Explorar o código

feat: 优化有声书创建流程界面交互

- 完善 create.vue 创建页面交互优化
- App.vue 全局调整

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MyFramework User hai 3 meses
pai
achega
fe78b3d74e
Modificáronse 2 ficheiros con 42 adicións e 44 borrados
  1. 4 4
      my-uniapp-vue3/src/App.vue
  2. 38 40
      my-uniapp-vue3/src/pages/book-generator/create.vue

+ 4 - 4
my-uniapp-vue3/src/App.vue

@@ -122,11 +122,11 @@ onLaunch(() => {
   initAnalytics();
 
   // 处理微信 OAuth 回调(必须在 initUser 之后)
-  handleWechatOAuthCallback();
-
-  // 全局自动登录检查(微信环境下,有 openid 但未登录时尝试自动登录)
   // #ifdef H5
-  checkAutoLogin();
+  handleWechatOAuthCallback().then(() => {
+    // OAuth 回调处理完成后,再执行自动登录检查
+    checkAutoLogin();
+  });
   // #endif
 });
 

+ 38 - 40
my-uniapp-vue3/src/pages/book-generator/create.vue

@@ -24,10 +24,10 @@
                 class="form-textarea"
                 :class="{ 'error': attemptedCreate && !newBook.description.trim() }"
                 placeholder="例如:教小学生学习编程的入门书,内容生动有趣,通过趣味案例引导孩子入门Python..."
-                :maxlength="500"
+                :maxlength="2000"
                 @blur="detectScaleFromDesc"
               />
-              <text class="char-count">{{ newBook.description.length }}/500</text>
+              <text class="char-count">{{ newBook.description.length }}/2000</text>
             </view>
           </view>
 
@@ -56,7 +56,7 @@
           <!-- 书籍规模 -->
           <view class="form-item">
             <view class="selector-field" @click="showScalePicker">
-              <text class="selector-value">{{ getScaleDisplay(newBook.bookScale) || '请选择书籍规模' }}</text>
+              <text class="selector-value">{{ getScaleDisplay(newBook.bookScale) || '书籍规模' }}</text>
               <text class="selector-arrow">▼</text>
             </view>
           </view>
@@ -82,8 +82,7 @@
           <!-- 面向人群 -->
           <view class="form-item">
             <view class="selector-field" @click="showAudiencePicker">
-              <text class="selector-label">面向人群</text>
-              <text class="selector-value">{{ getAudienceLabel(newBook.targetAudience) || '请选择' }}</text>
+              <text class="selector-value">{{ getAudienceLabel(newBook.targetAudience) || '面向人群' }}</text>
               <text class="selector-arrow">▼</text>
             </view>
           </view>
@@ -91,8 +90,7 @@
           <!-- 知识难度 -->
           <view class="form-item">
             <view class="selector-field" @click="showKnowledgeLevelPicker">
-              <text class="selector-label">知识难度</text>
-              <text class="selector-value">{{ getKnowledgeLevelLabel(newBook.knowledgeLevel) || '请选择' }}</text>
+              <text class="selector-value">{{ getKnowledgeLevelLabel(newBook.knowledgeLevel) || '知识难度' }}</text>
               <text class="selector-arrow">▼</text>
             </view>
           </view>
@@ -100,8 +98,7 @@
           <!-- 书籍类型 -->
           <view class="form-item">
             <view class="selector-field" @click="showBookTypePicker">
-              <text class="selector-label">书籍类型</text>
-              <text class="selector-value">{{ getBookTypeLabel(newBook.bookType) || '请选择' }}</text>
+              <text class="selector-value">{{ getBookTypeLabel(newBook.bookType) || '书籍类型' }}</text>
               <text class="selector-arrow">▼</text>
             </view>
           </view>
@@ -109,8 +106,7 @@
           <!-- 大纲层级 -->
           <view class="form-item">
             <view class="selector-field" @click="showOutlineLevelPicker">
-              <text class="selector-label">大纲层级</text>
-              <text class="selector-value">{{ getOutlineLevelLabel(newBook.outlineLevel) || '请选择' }}</text>
+              <text class="selector-value">{{ getOutlineLevelLabel(newBook.outlineLevel) || '大纲层级' }}</text>
               <text class="selector-arrow">▼</text>
             </view>
           </view>
@@ -127,8 +123,7 @@
             <!-- 快速模板 -->
             <view class="form-item">
               <view class="selector-field" @click="showTemplatePicker">
-                <text class="selector-label">快速模板</text>
-                <text class="selector-value">{{ selectedTemplateName || '请选择模板(可选)' }}</text>
+                <text class="selector-value">{{ selectedTemplateName || '快速模板' }}</text>
                 <text class="selector-arrow">▼</text>
               </view>
             </view>
@@ -136,8 +131,7 @@
             <!-- 写作风格 -->
             <view class="form-item">
               <view class="selector-field" @click="showStylePicker">
-                <text class="selector-label">写作风格</text>
-                <text class="selector-value">{{ newBook.style || '请选择' }}</text>
+                <text class="selector-value">{{ newBook.style || '写作风格' }}</text>
                 <text class="selector-arrow">▼</text>
               </view>
             </view>
@@ -145,8 +139,7 @@
             <!-- 行业领域 -->
             <view class="form-item">
               <view class="selector-field" @click="showIndustryPicker">
-                <text class="selector-label">行业领域</text>
-                <text class="selector-value">{{ getIndustryLabel(newBook.industry) || '请选择' }}</text>
+                <text class="selector-value">{{ getIndustryLabel(newBook.industry) || '行业领域' }}</text>
                 <text class="selector-arrow">▼</text>
               </view>
             </view>
@@ -154,8 +147,7 @@
             <!-- 特殊要求 (多选) -->
             <view class="form-item">
               <view class="selector-field" @click="showSpecialFeaturesPicker">
-                <text class="selector-label">特殊要求</text>
-                <text class="selector-value">{{ getSpecialFeaturesLabel() || '请选择' }}</text>
+                <text class="selector-value">{{ getSpecialFeaturesLabel() || '特殊要求' }}</text>
                 <text class="selector-arrow">▼</text>
               </view>
             </view>
@@ -435,9 +427,10 @@ function getSpecialFeaturesLabel() {
 // 弹出选择器
 function showAudiencePicker() {
   uni.showActionSheet({
-    itemList: audiences.map(a => a.label),
+    itemList: ['面向人群', ...audiences.map(a => a.label)],
     success: (res) => {
-      const selected = audiences[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = audiences[res.tapIndex - 1];
       newBook.value.targetAudience = selected.value;
       syncToDescription('面向人群', selected.value);
     }
@@ -446,9 +439,10 @@ function showAudiencePicker() {
 
 function showKnowledgeLevelPicker() {
   uni.showActionSheet({
-    itemList: knowledgeLevels.map(l => l.label),
+    itemList: ['知识难度', ...knowledgeLevels.map(l => l.label)],
     success: (res) => {
-      const selected = knowledgeLevels[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = knowledgeLevels[res.tapIndex - 1];
       newBook.value.knowledgeLevel = selected.value;
       syncToDescription('知识难度', selected.value);
     }
@@ -457,9 +451,10 @@ function showKnowledgeLevelPicker() {
 
 function showBookTypePicker() {
   uni.showActionSheet({
-    itemList: bookTypes.map(b => b.label),
+    itemList: ['书籍类型', ...bookTypes.map(b => b.label)],
     success: (res) => {
-      const selected = bookTypes[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = bookTypes[res.tapIndex - 1];
       newBook.value.bookType = selected.value;
       syncToDescription('书籍类型', selected.value);
     }
@@ -468,9 +463,10 @@ function showBookTypePicker() {
 
 function showOutlineLevelPicker() {
   uni.showActionSheet({
-    itemList: outlineLevelOptions.map(o => o.label),
+    itemList: ['大纲层级', ...outlineLevelOptions.map(o => o.label)],
     success: (res) => {
-      const selected = outlineLevelOptions[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = outlineLevelOptions[res.tapIndex - 1];
       newBook.value.outlineLevel = selected.value;
       syncToDescription('大纲层级', selected.value);
     }
@@ -479,9 +475,10 @@ function showOutlineLevelPicker() {
 
 function showStylePicker() {
   uni.showActionSheet({
-    itemList: styles,
+    itemList: ['写作风格', ...styles],
     success: (res) => {
-      const selected = styles[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = styles[res.tapIndex - 1];
       newBook.value.style = selected;
       syncToDescription('写作风格', selected);
     }
@@ -490,9 +487,10 @@ function showStylePicker() {
 
 function showIndustryPicker() {
   uni.showActionSheet({
-    itemList: industries.map(i => i.label),
+    itemList: ['行业领域', ...industries.map(i => i.label)],
     success: (res) => {
-      const selected = industries[res.tapIndex];
+      if (res.tapIndex === 0) return;
+      const selected = industries[res.tapIndex - 1];
       newBook.value.industry = selected.value;
       syncToDescription('行业领域', selected.label);
     }
@@ -506,9 +504,10 @@ function showSpecialFeaturesPicker() {
     return checked ? `${f.label} ✓` : f.label;
   });
   uni.showActionSheet({
-    itemList: items,
+    itemList: ['特殊要求', ...items],
     success: (res) => {
-      toggleSpecialFeature(specialFeatures[res.tapIndex].value);
+      if (res.tapIndex === 0) return;
+      toggleSpecialFeature(specialFeatures[res.tapIndex - 1].value);
     }
   });
 }
@@ -526,10 +525,11 @@ function showScalePicker() {
 function showTemplatePicker() {
   const templateNames = quickTemplates.map(t => t.name);
   uni.showActionSheet({
-    itemList: templateNames,
+    itemList: ['快速模板', ...templateNames],
     success: (res) => {
-      applyTemplate(quickTemplates[res.tapIndex]);
-      selectedTemplateName.value = quickTemplates[res.tapIndex].name;
+      if (res.tapIndex === 0) return;
+      applyTemplate(quickTemplates[res.tapIndex - 1]);
+      selectedTemplateName.value = quickTemplates[res.tapIndex - 1].name;
     }
   });
 }
@@ -901,7 +901,6 @@ onLoad(async (query: any) => {
 .card-title { font-size: 36rpx; font-weight: 700; color: #1f2937; }
 
 .form-item { margin-bottom: 32rpx; }
-.form-label { font-size: 28rpx; font-weight: 600; color: #374151; margin-bottom: 12rpx; display: block; }
 .form-hint { font-size: 24rpx; color: #9ca3af; margin-bottom: 16rpx; display: block; }
 .form-hint-small { font-size: 24rpx; color: #9ca3af; margin-bottom: 16rpx; display: block; }
 
@@ -914,8 +913,7 @@ onLoad(async (query: any) => {
   border-radius: 12rpx;
   border: 2rpx solid #e5e7eb;
 }
-.selector-label { font-size: 28rpx; color: #374151; font-weight: 600; }
-.selector-value { font-size: 28rpx; color: #9ca3af; flex: 1; text-align: right; margin-right: 12rpx; }
+.selector-value { font-size: 28rpx; color: #9ca3af; }
 .selector-arrow { font-size: 22rpx; color: #9ca3af; }
 
 .form-input {
@@ -930,7 +928,7 @@ onLoad(async (query: any) => {
 
 .form-textarea {
   width: 100%;
-  min-height: 200rpx;
+  min-height: 320rpx;
   padding: 24rpx;
   padding-bottom: 60rpx;
   background: #f9fafb;