Selaa lähdekoodia

把图片创作界面和音乐创作界面上传

lalalashen 3 kuukautta sitten
vanhempi
commit
82cc1e78b0

+ 0 - 5
pages.json

@@ -243,11 +243,6 @@
 			"style": {
 				"navigationBarTitleText": "音乐制作详情"
 			}
-		}, {
-			"path": "pages/articleDetail/imgDetail",
-			"style": {
-				"navigationBarTitleText": "帖子"
-			}
 		}
 	],
 	"globalStyle": {

+ 213 - 0
pages/makedetail/makeImgDetail.scss

@@ -0,0 +1,213 @@
+.makedetail-container {
+  min-height: 100vh;
+  background: #f5f5f5;
+  padding: 0 20rpx;
+
+  .header {
+    display: flex;
+    align-items: center;
+    height: 88rpx;
+    padding: 0 20rpx;
+    
+    .back-icon {
+      font-size: 40rpx;
+    }
+    
+    .title {
+      flex: 1;
+      text-align: center;
+      font-size: 32rpx;
+      font-weight: 500;
+    }
+    
+    .right-info {
+      display: flex;
+      align-items: center;
+      
+      .coin, .diamond {
+        margin-left: 20rpx;
+      }
+    }
+  }
+
+  .preview-section {
+    margin: 20rpx 0;
+    
+    .preview-card {
+      height: 400rpx;
+      background: linear-gradient(to bottom right, #87CEEB, #90EE90);
+      border-radius: 20rpx;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      
+      .loading-text {
+        color: #fff;
+        font-size: 32rpx;
+        margin-bottom: 10rpx;
+      }
+      
+      .sub-text {
+        color: rgba(255,255,255,0.8);
+        font-size: 24rpx;
+      }
+    }
+  }
+
+  .section-title {
+    font-size: 28rpx;
+    font-weight: 500;
+    margin-bottom: 20rpx;
+    
+    .required {
+      color: #ff4d4f;
+    }
+  }
+
+  .input-area {
+    width: 100%;
+    height: 200rpx;
+    background: #fff;
+    border-radius: 12rpx;
+    padding: 20rpx;
+    font-size: 28rpx;
+  }
+
+  .input-box {
+    width: 100%;
+    height: 80rpx;
+    background: #fff;
+    border-radius: 12rpx;
+    padding: 0 20rpx;
+    margin-bottom: 20rpx;
+  }
+
+  .tag-group {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20rpx;
+    
+    .tag {
+      padding: 10rpx 30rpx;
+      background: #fff;
+      border-radius: 30rpx;
+      font-size: 26rpx;
+    }
+  }
+
+  .style-scroll {
+    white-space: nowrap;
+    
+    .style-item {
+      display: inline-block;
+      margin-right: 20rpx;
+      text-align: center;
+      flex-direction: column;
+      
+      image {
+        width: 160rpx;
+        height: 160rpx;
+        border-radius: 12rpx;
+        margin-bottom: 8rpx;
+      }
+      
+      text {
+        display: block;
+        font-size: 24rpx;
+        color: #333;
+      }
+    }
+  }
+
+  .bottom-button {
+    padding: 40rpx 0;
+    
+    .generate-btn {
+      width: 100%;
+      height: 88rpx;
+      background: #333;
+      color: #fff;
+      border-radius: 44rpx;
+      font-size: 32rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    
+    .promotion-link {
+      text-align: center;
+      margin-top: 20rpx;
+      color: #2b85e4;
+      font-size: 24rpx;
+    }
+  }
+
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20rpx;
+
+    .clear-text {
+      display: flex;
+      align-items: center;
+      font-size: 24rpx;
+      color: #666;
+
+      image {
+        width: 28rpx;
+        height: 28rpx;
+        margin-right: 8rpx;
+      }
+    }
+  }
+
+  .preview-title {
+    font-size: 28rpx;
+    font-weight: 500;
+    margin-bottom: 20rpx;
+  }
+
+  .loading-icon {
+    width: 60rpx;
+    height: 60rpx;
+    margin-bottom: 20rpx;
+  }
+
+  .word-count {
+    text-align: right;
+    font-size: 24rpx;
+    color: #999;
+    margin-top: 10rpx;
+  }
+
+  .style-item {
+    &.active {
+      image {
+        border: 4rpx solid #2b85e4;
+      }
+      text {
+        color: #2b85e4;
+      }
+    }
+  }
+
+  .right-info {
+    .coin, .diamond {
+      display: flex;
+      align-items: center;
+      
+      image {
+        width: 40rpx;
+        height: 40rpx;
+        margin-right: 8rpx;
+      }
+      
+      text {
+        font-size: 28rpx;
+        color: #333;
+      }
+    }
+  }
+} 

+ 196 - 0
pages/makedetail/makeImgDetail.vue

@@ -0,0 +1,196 @@
+<template>
+  <view class="makedetail-container">
+    <!-- 顶部导航 -->
+    <view class="header">
+      <view class="back-icon" @click="goBack">
+        <text class="iconfont icon-left">&#xe8ef;</text>
+      </view>
+      <view class="right-info">
+        <view class="coin">
+          <image src="/static/coin.png" mode="aspectFit"></image>
+          <text>999</text>
+        </view>
+        <view class="diamond">
+          <image src="/static/diamond.png" mode="aspectFit"></image>
+          <text>2</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 创作预览区域 -->
+    <view class="preview-section">
+      <view class="preview-title">创作预览</view>
+      <view class="preview-card">
+        <image class="loading-icon" src="/static/loading.png" mode="aspectFit"></image>
+        <text class="loading-text">生成中 0%</text>
+        <text class="sub-text">请耐心等待制作完成</text>
+      </view>
+    </view>
+
+    <!-- 创作描述输入区 -->
+    <view class="description-section">
+      <view class="section-header">
+        <text class="section-title">创作描述<text class="required">*</text></text>
+        <view class="clear-text" @click="clearDescription">
+          <image src="/static/clear.png" mode="aspectFit"></image>
+          <text>清空文本</text>
+        </view>
+      </view>
+      <textarea 
+        class="input-area" 
+        v-model="description"
+        placeholder="请输入描述语,例如:穿着白色运动服,外表俊朗..."
+        maxlength="1000"
+        @input="onDescriptionInput"
+      ></textarea>
+      <view class="word-count">{{descriptionLength}}/1000</view>
+    </view>
+
+    <!-- 行为动作选择区 -->
+    <view class="action-section">
+      <view class="section-title">行为动作</view>
+      <input 
+        class="input-box" 
+        v-model="action"
+        placeholder="可输入也可点击推荐词" 
+      />
+      <view class="tag-group">
+        <text 
+          class="tag" 
+          v-for="(item, index) in actionTags" 
+          :key="index"
+          @click="selectAction(item)"
+        >{{item}}</text>
+      </view>
+    </view>
+
+    <!-- 主体环境选择区 -->
+    <view class="environment-section">
+      <view class="section-title">主体环境</view>
+      <input 
+        class="input-box" 
+        v-model="environment"
+        placeholder="可输入也可点击推荐词" 
+      />
+      <view class="tag-group">
+        <text 
+          class="tag" 
+          v-for="(item, index) in environmentTags" 
+          :key="index"
+          @click="selectEnvironment(item)"
+        >{{item}}</text>
+      </view>
+    </view>
+
+    <!-- 主体形象选择区 -->
+    <view class="image-section">
+      <view class="section-title">主体形象</view>
+      <input 
+        class="input-box" 
+        v-model="image"
+        placeholder="可输入也可点击推荐词" 
+      />
+      <view class="tag-group">
+        <text 
+          class="tag" 
+          v-for="(item, index) in imageTags" 
+          :key="index"
+          @click="selectImage(item)"
+        >{{item}}</text>
+      </view>
+    </view>
+
+    <!-- 参考风格选择区 -->
+    <view class="style-section">
+      <view class="section-title">选择参考风格<text class="required">*</text></view>
+      <scroll-view class="style-scroll" scroll-x>
+        <view 
+          class="style-item" 
+          v-for="(item, index) in styleList" 
+          :key="index"
+          :class="{'active': selectedStyle === index}"
+          @click="selectStyle(index)"
+        >
+          <image :src="item.image" mode="aspectFill"></image>
+          <text>{{item.name}}</text>
+        </view>
+      </scroll-view>
+    </view>
+
+    <!-- 底部按钮 -->
+    <view class="bottom-button">
+      <button class="generate-btn" @click="generateImage">立即生成(消耗10贡献)</button>
+      <view class="promotion-link">
+        <text>> 即刻开通订阅,获取各种福利! <</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      description: '',
+      descriptionLength: 0,
+      action: '',
+      environment: '',
+      image: '',
+      selectedStyle: -1,
+      actionTags: ['跳舞', '开枪', '喝咖啡', '看书', '运动'],
+      environmentTags: ['都市大道', '大树底下', '办公室', '厨房'],
+      imageTags: ['戴着墨镜', '戴着耳机', '戴着帽子', '手持冲浪板'],
+      styleList: [
+        { name: '自然共生', image: '/static/style1.png' },
+        { name: '国风新锋', image: '/static/style2.png' },
+        { name: '萌系治愈', image: '/static/style3.png' }
+      ]
+    }
+  },
+  methods: {
+    goBack() {
+      uni.navigateBack()
+    },
+    clearDescription() {
+      this.description = ''
+      this.descriptionLength = 0
+    },
+    onDescriptionInput(e) {
+      this.descriptionLength = e.detail.value.length
+    },
+    selectAction(tag) {
+      this.action = tag
+    },
+    selectEnvironment(tag) {
+      this.environment = tag
+    },
+    selectImage(tag) {
+      this.image = tag
+    },
+    selectStyle(index) {
+      this.selectedStyle = index
+    },
+    generateImage() {
+      if (!this.description) {
+        uni.showToast({
+          title: '请输入创作描述',
+          icon: 'none'
+        })
+        return
+      }
+      if (this.selectedStyle === -1) {
+        uni.showToast({
+          title: '请选择参考风格',
+          icon: 'none'
+        })
+        return
+      }
+      // TODO: 调用生成接口
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+@import './makeImgDetail.scss';
+</style> 

+ 162 - 0
pages/makedetail/makeMusicDetail.scss

@@ -0,0 +1,162 @@
+.make-music-detail {
+  width: 100%;
+  min-height: 100vh;
+  background-color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .nav-header {
+    padding: 44px 15px 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    border-bottom: 1px solid #f5f5f5;
+
+    .back-icon {
+      width: 24px;
+    }
+
+    .right-info {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      gap: 10px;
+
+      .coin-box, .diamond-box {
+        display: flex;
+        align-items: center;
+        background: #f5f5f5;
+        border-radius: 15px;
+        padding: 2px 8px;
+
+        image {
+          width: 16px;
+          height: 16px;
+          margin-right: 4px;
+        }
+
+        text {
+          font-size: 14px;
+        }
+      }
+    }
+  }
+
+  .content {
+    flex: 1;
+    padding: 20px;
+
+    .input-section {
+      margin-bottom: 20px;
+
+      .label {
+        font-size: 16px;
+        font-weight: 500;
+        margin-bottom: 10px;
+        display: block;
+      }
+
+      .input-field {
+        width: 100%;
+        height: 44px;
+        border-radius: 8px;
+        border: 1px solid #eee;
+        padding: 0 15px;
+        font-size: 14px;
+      }
+
+      .textarea-field {
+        width: 100%;
+        height: 120px;
+        border-radius: 8px;
+        border: 1px solid #eee;
+        padding: 15px;
+        font-size: 14px;
+      }
+
+      .textarea-footer {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 8px;
+
+        .ai-btn {
+          color: #007AFF;
+          font-size: 14px;
+        }
+      }
+
+      .count {
+        font-size: 12px;
+        color: #999;
+        text-align: right;
+      }
+    }
+
+    .style-section {
+      .tabs {
+        display: flex;
+        gap: 20px;
+        margin-bottom: 15px;
+
+        text {
+          font-size: 14px;
+          color: #666;
+          &.active {
+            color: #333;
+            font-weight: 500;
+          }
+        }
+      }
+
+      .tags {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 10px;
+
+        .tag {
+          padding: 6px 15px;
+          background: #f5f5f5;
+          border-radius: 20px;
+          font-size: 14px;
+
+          &.active {
+            background: #333;
+            color: #fff;
+          }
+        }
+      }
+    }
+  }
+
+  .bottom-section {
+    padding: 20px;
+
+    .submit-btn {
+      width: 100%;
+      height: 44px;
+      background: #333;
+      color: #fff;
+      border-radius: 22px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .small {
+        font-size: 12px;
+        margin-left: 5px;
+      }
+    }
+
+    .promotion-text {
+      text-align: center;
+      margin-top: 10px;
+
+      .link-text {
+        color: #007AFF;
+        font-size: 12px;
+      }
+    }
+  }
+} 

+ 140 - 0
pages/makedetail/makeMusicDetail.vue

@@ -0,0 +1,140 @@
+<template>
+  <view class="make-music-detail">
+    <!-- 顶部导航 -->
+    <view class="nav-header">
+      <view class="back-icon">
+        <text class="iconfont icon-left"></text>
+      </view>
+      <view class="right-info">
+        <view class="coin-box">
+          <image src="/static/images/coin.png" mode="aspectFit" class="coin-icon" />
+          <text>999</text>
+        </view>
+        <view class="diamond-box">
+          <image src="/static/images/diamond.png" mode="aspectFit" class="diamond-icon" />
+          <text>2</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 主要内容区 -->
+    <view class="content">
+      <!-- 歌曲名称输入 -->
+      <view class="input-section">
+        <text class="label">歌曲名称</text>
+        <input type="text" placeholder="请输入名称..." class="input-field" maxlength="30" v-model="songName" />
+        <text class="count">{{songName.length}}/30</text>
+      </view>
+
+      <!-- 创作歌词输入 -->
+      <view class="input-section">
+        <text class="label">创作歌词</text>
+        <textarea 
+          placeholder="请在此处输入您想要进行联想的内容或者歌词" 
+          class="textarea-field"
+          maxlength="800"
+          v-model="lyrics"
+          :style="{height: textareaHeight + 'px'}"
+          @input="onTextareaInput"
+        />
+        <view class="textarea-footer">
+          <text class="count">{{lyrics.length}}/800</text>
+          <text class="ai-btn">AI润词</text>
+        </view>
+      </view>
+
+      <!-- 音乐风格选择 -->
+      <view class="style-section">
+        <text class="label">音乐风格</text>
+        <view class="tabs">
+          <text :class="{'active': selectedTab === 'emotion'}" @click="selectTab('emotion')">情感</text>
+          <text :class="{'active': selectedTab === 'genre'}" @click="selectTab('genre')">流派</text>
+          <text :class="{'active': selectedTab === 'era'}" @click="selectTab('era')">年代</text>
+          <text :class="{'active': selectedTab === 'instrument'}" @click="selectTab('instrument')">乐器</text>
+        </view>
+        <view class="tags">
+          <text 
+            v-for="(tag, index) in currentTags" 
+            :key="index"
+            :class="['tag', { active: selectedTags.includes(tag) }]"
+            @click="toggleTag(tag)"
+          >
+            {{tag}}
+          </text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 底部按钮 -->
+    <view class="bottom-section">
+      <button class="submit-btn">立即生成<text class="small">(需消耗10枚豆)</text></button>
+      <view class="promotion-text">
+        <text class="link-text">>即刻开通订阅,我取各种福利<</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'MakeMusicDetail',
+  data() {
+    return {
+      songName: '',
+      lyrics: '',
+      selectedTags: [],
+      textareaHeight: 120,
+      minHeight: 120,
+      selectedTab: 'emotion',
+      tagOptions: {
+        emotion: ['欢快', '悲伤', '积极', '浪漫', '忧郁', '华丽', '闪耀', '神秘', '惊怒', '紧张', '恐怖', '平静'],
+        genre: ['流行', '摇滚', '民谣', '电子', 'R&B', '嘻哈', '古典', '爵士'],
+        era: ['80年代', '90年代', '00年代', '10年代', '20年代'],
+        instrument: ['钢琴', '吉他', '贝斯', '鼓', '小提琴', '萨克斯', '电子合成器']
+      }
+    }
+  },
+  computed: {
+    currentTags() {
+      return this.selectedTab ? this.tagOptions[this.selectedTab] : [];
+    }
+  },
+  methods: {
+    onTextareaInput(e) {
+      const lineHeight = 20; // 假设每行高度为20px
+      const padding = 30; // 上下padding各15px
+      const value = e.detail.value;
+      const lines = value.split('\n').length;
+      // 计算每行的平均字符数
+      const avgCharsPerLine = 30; // 根据实际输入框宽度调整
+      const textLines = Math.ceil(value.length / avgCharsPerLine);
+      
+      // 取行数的最大值,确保有足够空间显示
+      const totalLines = Math.max(lines, textLines);
+      const newHeight = Math.max(totalLines * lineHeight + padding, this.minHeight);
+      
+      this.textareaHeight = newHeight;
+    },
+    selectTab(tab) {
+      if (this.selectedTab === tab) {
+        this.selectedTab = '';
+      } else {
+        this.selectedTab = tab;
+      }
+      // 切换标签时清空已选择的标签
+      this.selectedTags = [];
+    },
+    toggleTag(tag) {
+      if (this.selectedTags.includes(tag)) {
+        this.selectedTags = this.selectedTags.filter(t => t !== tag);
+      } else {
+        this.selectedTags.push(tag);
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+@import './makeMusicDetail.scss';
+</style>