|
@@ -17,177 +17,156 @@
|
|
|
<!-- 主内容区 -->
|
|
<!-- 主内容区 -->
|
|
|
<view class="main-content">
|
|
<view class="main-content">
|
|
|
<view class="card">
|
|
<view class="card">
|
|
|
- <view class="card-header">
|
|
|
|
|
- <text class="card-title">📖 创建新书籍</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
|
|
|
- <!-- 简洁模式提示 -->
|
|
|
|
|
- <view class="simple-mode-tip">
|
|
|
|
|
- <view class="tip-header">
|
|
|
|
|
- <text class="tip-icon">💡</text>
|
|
|
|
|
- <text class="tip-title">简洁模式</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="tip-content">
|
|
|
|
|
- 先填写基本信息创建书籍,高级选项可在创建后修改。
|
|
|
|
|
- </text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 书名:由AI根据描述自动生成,无需手动输入 -->
|
|
|
|
|
-
|
|
|
|
|
- <!-- 描述/主题 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <text class="form-label">内容描述 *</text>
|
|
|
|
|
- <textarea
|
|
|
|
|
- v-model="newBook.description"
|
|
|
|
|
- class="form-textarea"
|
|
|
|
|
- placeholder="描述这本书的内容、主题、写作目的..."
|
|
|
|
|
- :maxlength="500"
|
|
|
|
|
- @blur="detectScaleFromDesc"
|
|
|
|
|
- />
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 面向人群 -->
|
|
|
|
|
- <view class="form-item highlight-item">
|
|
|
|
|
- <view class="label-with-badge">
|
|
|
|
|
- <text class="form-label">面向人群</text>
|
|
|
|
|
- <view class="required-badge">重要</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="form-hint">选择目标读者,AI会根据人群调整语言风格和表达方式</text>
|
|
|
|
|
- <view class="chip-group">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="audience in audiences"
|
|
|
|
|
- :key="audience.value"
|
|
|
|
|
- :class="['chip', 'audience-chip', newBook.targetAudience === audience.value ? 'active' : '']"
|
|
|
|
|
- @click="newBook.targetAudience = audience.value"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="chip-icon">{{ audience.icon }}</text>
|
|
|
|
|
- <text class="chip-text">{{ audience.label }}</text>
|
|
|
|
|
|
|
+ <!-- ========== 基础信息区 ========== -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <text class="section-title">基础信息</text>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 内容描述 + 字数计数 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">内容描述 *</text>
|
|
|
|
|
+ <view class="textarea-wrapper">
|
|
|
|
|
+ <textarea
|
|
|
|
|
+ v-model="newBook.description"
|
|
|
|
|
+ class="form-textarea"
|
|
|
|
|
+ :class="{ 'error': attemptedCreate && !newBook.description.trim() }"
|
|
|
|
|
+ placeholder="描述这本书的内容、主题、写作目的..."
|
|
|
|
|
+ :maxlength="500"
|
|
|
|
|
+ @blur="detectScaleFromDesc"
|
|
|
|
|
+ />
|
|
|
|
|
+ <text class="char-count">{{ newBook.description.length }}/500</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
|
|
|
|
|
- <!-- 书籍类型 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <view class="label-with-badge">
|
|
|
|
|
- <text class="form-label">书籍类型</text>
|
|
|
|
|
- <view class="required-badge" style="background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);">影响大纲结构</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="form-hint-small">选择类型决定大纲层级。不选则由AI自动判断</text>
|
|
|
|
|
- <view class="chip-group" style="margin-top: 8rpx;">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="bt in bookTypes"
|
|
|
|
|
- :key="bt.value"
|
|
|
|
|
- :class="['chip', newBook.bookType === bt.value ? 'active' : '']"
|
|
|
|
|
- @click="newBook.bookType = bt.value"
|
|
|
|
|
|
|
+ <!-- AI自动优化描述按钮 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="btn-smart-recommend"
|
|
|
|
|
+ :disabled="isRecommending || !newBook.description.trim()"
|
|
|
|
|
+ @click="requestSmartRecommend"
|
|
|
>
|
|
>
|
|
|
- <text class="chip-icon">{{ bt.icon }}</text>
|
|
|
|
|
- <text class="chip-text">{{ bt.label }}</text>
|
|
|
|
|
- <text class="chip-desc" style="font-size: 20rpx; margin-left: 4rpx;">{{ bt.desc }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <text class="recommend-icon">{{ isRecommending ? '⏳' : '✨' }}</text>
|
|
|
|
|
+ <text class="recommend-text">{{ isRecommending ? '推荐中...' : 'AI自动优化描述' }}</text>
|
|
|
|
|
+ </button>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
|
|
|
|
|
- <!-- 书籍规模 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <text class="form-label">书籍规模 *</text>
|
|
|
|
|
- <view class="scale-picker">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="scale in bookScales"
|
|
|
|
|
- :key="scale.value"
|
|
|
|
|
- :class="['scale-option', { active: newBook.bookScale === scale.value }]"
|
|
|
|
|
- @click="selectBookScale(scale.value)"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="scale-words">{{ scale.words }}</text>
|
|
|
|
|
- <text class="scale-label">{{ scale.label }} {{ scale.chapters }}</text>
|
|
|
|
|
- <text class="scale-audio">{{ scale.audio }}</text>
|
|
|
|
|
|
|
+ <!-- 书籍规模 chip 选择 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">书籍规模 *</text>
|
|
|
|
|
+ <view class="scale-chips">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="scale in commonScales"
|
|
|
|
|
+ :key="scale.value"
|
|
|
|
|
+ :class="['chip', newBook.bookScale === scale.value ? 'active' : '']"
|
|
|
|
|
+ @click="onScaleChipClick(scale.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ scale.label }}
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <picker
|
|
|
|
|
+ mode="selector"
|
|
|
|
|
+ :range="bookScales"
|
|
|
|
|
+ range-key="label"
|
|
|
|
|
+ :value="bookScales.findIndex(s => s.value === newBook.bookScale)"
|
|
|
|
|
+ @change="onBookScaleChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="more-scales">更多规模 ▼</text>
|
|
|
|
|
+ </picker>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
|
|
|
|
|
- <!-- 知识难度 -->
|
|
|
|
|
- <view class="form-item highlight-item">
|
|
|
|
|
- <view class="label-with-badge">
|
|
|
|
|
- <text class="form-label">知识难度 *</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="chip-group">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="level in knowledgeLevels"
|
|
|
|
|
- :key="level.value"
|
|
|
|
|
- :class="['chip', 'level-chip', newBook.knowledgeLevel === level.value ? 'active' : '']"
|
|
|
|
|
- @click="newBook.knowledgeLevel = level.value"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="chip-icon">{{ level.icon }}</text>
|
|
|
|
|
- <text class="chip-text">{{ level.label }}</text>
|
|
|
|
|
|
|
+ <!-- 预估信息(选择规模后显示在最上方) -->
|
|
|
|
|
+ <view v-if="bookEstimate && bookEstimate.words && bookEstimate.audioMinutes" class="estimate-card">
|
|
|
|
|
+ <view class="estimate-row">
|
|
|
|
|
+ <text class="estimate-label">📊 预估:</text>
|
|
|
|
|
+ <text class="estimate-value">{{ bookEstimate.words.min }}~{{ bookEstimate.words.max }}字</text>
|
|
|
|
|
+ <text class="estimate-sep">|</text>
|
|
|
|
|
+ <text class="estimate-value">{{ bookEstimate.audioMinutes.min }}~{{ bookEstimate.audioMinutes.max }}分钟</text>
|
|
|
|
|
+ <text class="estimate-sep">|</text>
|
|
|
|
|
+ <text class="estimate-value">约{{ bookEstimate.estimatedChapters }}章</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view v-if="quotaCheck" class="quota-row">
|
|
|
|
|
+ <text v-if="quotaCheck.allowed" class="quota-ok">✅ 额度充足</text>
|
|
|
|
|
+ <text v-else class="quota-warn">⚠️ {{ quotaCheck.reason }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 智能推荐按钮 -->
|
|
|
|
|
- <view class="form-item smart-recommend-section">
|
|
|
|
|
- <button
|
|
|
|
|
- class="btn-smart-recommend"
|
|
|
|
|
- :disabled="isRecommending || !newBook.description.trim()"
|
|
|
|
|
- @click="requestSmartRecommend"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="recommend-icon">{{ isRecommending ? '⏳' : '✨' }}</text>
|
|
|
|
|
- <text class="recommend-text">{{ isRecommending ? '推荐中...' : '智能推荐' }}</text>
|
|
|
|
|
- </button>
|
|
|
|
|
- <text class="form-hint-small">根据内容描述,AI推荐难度/人群/风格/领域</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <!-- ========== 读者配置区 ========== -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <text class="section-title">读者配置</text>
|
|
|
|
|
|
|
|
- <!-- 高级选项折叠区 -->
|
|
|
|
|
- <view class="advanced-section">
|
|
|
|
|
- <view class="advanced-header" @click="showAdvancedOptions = !showAdvancedOptions">
|
|
|
|
|
- <text class="advanced-title">高级选项</text>
|
|
|
|
|
- <text class="advanced-arrow">{{ showAdvancedOptions ? '▲' : '▼' }}</text>
|
|
|
|
|
|
|
+ <!-- 面向人群 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">面向人群</text>
|
|
|
|
|
+ <view class="chip-group">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="audience in audiences"
|
|
|
|
|
+ :key="audience.value"
|
|
|
|
|
+ :class="['chip', newBook.targetAudience === audience.value ? 'active' : '']"
|
|
|
|
|
+ @click="newBook.targetAudience = audience.value; syncToDescription('面向人群', audience.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="chip-icon">{{ audience.icon }}</text>
|
|
|
|
|
+ <text class="chip-text">{{ audience.label }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <!-- 折叠内容 -->
|
|
|
|
|
- <view v-if="showAdvancedOptions" class="advanced-content">
|
|
|
|
|
- <!-- 副标题 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <text class="form-label">副标题</text>
|
|
|
|
|
- <input
|
|
|
|
|
- v-model="newBook.subtitle"
|
|
|
|
|
- class="form-input"
|
|
|
|
|
- placeholder="例如:一本写给青少年的科普书"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <!-- 知识难度 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">知识难度</text>
|
|
|
|
|
+ <view class="chip-group">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="level in knowledgeLevels"
|
|
|
|
|
+ :key="level.value"
|
|
|
|
|
+ :class="['chip', newBook.knowledgeLevel === level.value ? 'active' : '']"
|
|
|
|
|
+ @click="newBook.knowledgeLevel = level.value; syncToDescription('知识难度', level.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="chip-icon">{{ level.icon }}</text>
|
|
|
|
|
+ <text class="chip-text">{{ level.label }}</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
|
|
|
- <!-- 快速模板 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <text class="form-label">📋 快速模板</text>
|
|
|
|
|
- <text class="form-hint-small">点击模板自动填充所有配置,也可手动修改</text>
|
|
|
|
|
- <view class="template-grid">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="template in quickTemplates"
|
|
|
|
|
- :key="template.name"
|
|
|
|
|
- class="template-card"
|
|
|
|
|
- @click="applyTemplate(template)"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="template-icon">{{ template.icon }}</text>
|
|
|
|
|
- <text class="template-name">{{ template.name }}</text>
|
|
|
|
|
- <text class="template-desc">{{ template.desc }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
+ <!-- 书籍类型 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">书籍类型</text>
|
|
|
|
|
+ <view class="chip-group">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="bt in bookTypes"
|
|
|
|
|
+ :key="bt.value"
|
|
|
|
|
+ :class="['chip', newBook.bookType === bt.value ? 'active' : '']"
|
|
|
|
|
+ @click="newBook.bookType = bt.value; syncToDescription('书籍类型', bt.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="chip-icon">{{ bt.icon }}</text>
|
|
|
|
|
+ <text class="chip-text">{{ bt.label }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
|
|
|
- <!-- 重要提示 -->
|
|
|
|
|
- <view class="important-tip">
|
|
|
|
|
- <view class="tip-header">
|
|
|
|
|
- <text class="tip-icon">💡</text>
|
|
|
|
|
- <text class="tip-title">重要提示</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="tip-content">
|
|
|
|
|
- 知识难度和面向人群会显著影响生成内容的风格和深度。同样的知识,面向不同人群,表达方式完全不同:
|
|
|
|
|
- </text>
|
|
|
|
|
- <view class="tip-examples">
|
|
|
|
|
- <text class="tip-example">• 面向小学生:形象、简单、通俗易懂</text>
|
|
|
|
|
- <text class="tip-example">• 面向大学生:系统、专业、有理论深度</text>
|
|
|
|
|
- <text class="tip-example">• 面向研究生:前沿、深入、有研究价值</text>
|
|
|
|
|
|
|
+ <!-- 大纲层级 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">大纲层级</text>
|
|
|
|
|
+ <view class="chip-group">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="opt in outlineLevelOptions"
|
|
|
|
|
+ :key="opt.value"
|
|
|
|
|
+ :class="['chip', newBook.outlineLevel === opt.value ? 'active' : '']"
|
|
|
|
|
+ @click="newBook.outlineLevel = opt.value; syncToDescription('大纲层级', opt.value)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="chip-text">{{ opt.label }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- ========== 高级定制区(默认展开) ========== -->
|
|
|
|
|
+ <view class="section">
|
|
|
|
|
+ <view class="section-header" @click="showAdvancedOptions = !showAdvancedOptions">
|
|
|
|
|
+ <text class="section-title">高级定制</text>
|
|
|
|
|
+ <text class="section-arrow">{{ showAdvancedOptions ? '▲' : '▼' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
|
|
|
|
|
+ <view v-if="showAdvancedOptions" class="section-content">
|
|
|
<!-- 写作风格 -->
|
|
<!-- 写作风格 -->
|
|
|
<view class="form-item">
|
|
<view class="form-item">
|
|
|
<text class="form-label">写作风格</text>
|
|
<text class="form-label">写作风格</text>
|
|
@@ -196,7 +175,7 @@
|
|
|
v-for="style in styles"
|
|
v-for="style in styles"
|
|
|
:key="style"
|
|
:key="style"
|
|
|
:class="['chip', newBook.style === style ? 'active' : '']"
|
|
:class="['chip', newBook.style === style ? 'active' : '']"
|
|
|
- @click="newBook.style = style"
|
|
|
|
|
|
|
+ @click="newBook.style = style; syncToDescription('写作风格', style)"
|
|
|
>
|
|
>
|
|
|
{{ style }}
|
|
{{ style }}
|
|
|
</view>
|
|
</view>
|
|
@@ -206,13 +185,12 @@
|
|
|
<!-- 行业领域 -->
|
|
<!-- 行业领域 -->
|
|
|
<view class="form-item">
|
|
<view class="form-item">
|
|
|
<text class="form-label">行业领域</text>
|
|
<text class="form-label">行业领域</text>
|
|
|
- <text class="form-hint-small">选择行业领域,AI会使用相关的案例和术语</text>
|
|
|
|
|
<view class="chip-group">
|
|
<view class="chip-group">
|
|
|
<view
|
|
<view
|
|
|
v-for="industry in industries"
|
|
v-for="industry in industries"
|
|
|
:key="industry.value"
|
|
:key="industry.value"
|
|
|
:class="['chip', newBook.industry === industry.value ? 'active' : '']"
|
|
:class="['chip', newBook.industry === industry.value ? 'active' : '']"
|
|
|
- @click="newBook.industry = industry.value"
|
|
|
|
|
|
|
+ @click="newBook.industry = industry.value; syncToDescription('行业领域', industry.label)"
|
|
|
>
|
|
>
|
|
|
<text class="chip-icon">{{ industry.icon }}</text>
|
|
<text class="chip-icon">{{ industry.icon }}</text>
|
|
|
<text class="chip-text">{{ industry.label }}</text>
|
|
<text class="chip-text">{{ industry.label }}</text>
|
|
@@ -223,12 +201,11 @@
|
|
|
<!-- 特殊要求 (多选) -->
|
|
<!-- 特殊要求 (多选) -->
|
|
|
<view class="form-item">
|
|
<view class="form-item">
|
|
|
<text class="form-label">特殊要求</text>
|
|
<text class="form-label">特殊要求</text>
|
|
|
- <text class="form-hint-small">可多选,AI会在生成时包含这些元素</text>
|
|
|
|
|
<view class="chip-group">
|
|
<view class="chip-group">
|
|
|
<view
|
|
<view
|
|
|
v-for="feature in specialFeatures"
|
|
v-for="feature in specialFeatures"
|
|
|
:key="feature.value"
|
|
:key="feature.value"
|
|
|
- :class="['chip', 'feature-chip', newBook.specialFeatures.includes(feature.value) ? 'active' : '']"
|
|
|
|
|
|
|
+ :class="['chip', newBook.specialFeatures.includes(feature.value) ? 'active' : '']"
|
|
|
@click="toggleSpecialFeature(feature.value)"
|
|
@click="toggleSpecialFeature(feature.value)"
|
|
|
>
|
|
>
|
|
|
<text class="chip-icon">{{ feature.icon }}</text>
|
|
<text class="chip-icon">{{ feature.icon }}</text>
|
|
@@ -236,77 +213,28 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 大纲层级 -->
|
|
|
|
|
- <view class="form-item">
|
|
|
|
|
- <view class="label-with-badge">
|
|
|
|
|
- <text class="form-label">大纲层级</text>
|
|
|
|
|
- <view class="required-badge" style="background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);">高级</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="form-hint-small">控制章节结构的详细程度,默认"自动"会根据书籍规模智能选择</text>
|
|
|
|
|
- <view class="chip-group" style="margin-top: 8rpx;">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="opt in outlineLevelOptions"
|
|
|
|
|
- :key="opt.value"
|
|
|
|
|
- :class="['chip', 'level-chip', newBook.outlineLevel === opt.value ? 'active' : '']"
|
|
|
|
|
- @click="newBook.outlineLevel = opt.value"
|
|
|
|
|
- >
|
|
|
|
|
- <text class="chip-text">{{ opt.label }}</text>
|
|
|
|
|
- <text class="chip-desc" style="font-size: 20rpx; color: #9ca3af; margin-left: 4rpx;">{{ opt.desc }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 预估信息(选择规模后显示) -->
|
|
|
|
|
- <view v-if="bookEstimate && bookEstimate.words && bookEstimate.audioMinutes" class="estimate-card">
|
|
|
|
|
- <view class="estimate-header">
|
|
|
|
|
- <text class="estimate-title">📊 生成预估</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="estimate-row">
|
|
|
|
|
- <text class="estimate-label">预估字数:</text>
|
|
|
|
|
- <text class="estimate-value">{{ bookEstimate.words.min }}~{{ bookEstimate.words.max }}字</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="estimate-row">
|
|
|
|
|
- <text class="estimate-label">预估时长:</text>
|
|
|
|
|
- <text class="estimate-value">{{ bookEstimate.audioMinutes.min }}~{{ bookEstimate.audioMinutes.max }}分钟</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="estimate-row">
|
|
|
|
|
- <text class="estimate-label">预估章节:</text>
|
|
|
|
|
- <text class="estimate-value">约{{ bookEstimate.estimatedChapters }}章</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-if="bookEstimate.genLevelInfo" class="estimate-row genlevel-row">
|
|
|
|
|
- <text class="estimate-label">大纲层级:</text>
|
|
|
|
|
- <view class="genlevel-tag" :class="'level-' + bookEstimate.defaultGenLevel">
|
|
|
|
|
- <text class="genlevel-label">{{ bookEstimate.genLevelInfo.label }}</text>
|
|
|
|
|
- <text class="genlevel-desc">{{ bookEstimate.genLevelInfo.desc }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 配额检查结果 -->
|
|
|
|
|
- <view v-if="quotaCheck" class="quota-check">
|
|
|
|
|
- <view v-if="quotaCheck.allowed" class="quota-ok">
|
|
|
|
|
- <text class="quota-icon">✅</text>
|
|
|
|
|
- <text class="quota-text">额度充足,可生成</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view v-else class="quota-warning">
|
|
|
|
|
- <text class="quota-icon">⚠️</text>
|
|
|
|
|
- <text class="quota-text">{{ quotaCheck.reason }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
|
|
|
|
|
- <!-- 详细配额信息 -->
|
|
|
|
|
- <view class="quota-detail">
|
|
|
|
|
- <text class="quota-info">您当前额度:{{ quotaCheck.quota.remainingMinutes }}分钟 / {{ quotaCheck.quota.totalMinutes }}分钟</text>
|
|
|
|
|
- <view v-if="quotaCheck.costEstimate.overageMinutes > 0" class="quota-overage">
|
|
|
|
|
- <text>预计超出:{{ quotaCheck.costEstimate.overageMinutes }}分钟</text>
|
|
|
|
|
- <text class="quota-price">额外费用:¥{{ quotaCheck.costEstimate.estimatedPrice }}</text>
|
|
|
|
|
|
|
+ <!-- 快速模板 -->
|
|
|
|
|
+ <view class="form-item">
|
|
|
|
|
+ <text class="form-label">📋 快速模板</text>
|
|
|
|
|
+ <view class="template-grid">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="template in quickTemplates"
|
|
|
|
|
+ :key="template.name"
|
|
|
|
|
+ class="template-card"
|
|
|
|
|
+ @click="applyTemplate(template)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="template-icon">{{ template.icon }}</text>
|
|
|
|
|
+ <text class="template-name">{{ template.name }}</text>
|
|
|
|
|
+ <text class="template-desc">{{ template.desc }}</text>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
- <view class="btn-group">
|
|
|
|
|
|
|
+ <!-- 提交按钮(固定底部) -->
|
|
|
|
|
+ <view class="btn-group-fixed">
|
|
|
<button class="btn-cancel" @click="goBack">取消</button>
|
|
<button class="btn-cancel" @click="goBack">取消</button>
|
|
|
<button
|
|
<button
|
|
|
class="btn-primary"
|
|
class="btn-primary"
|
|
@@ -331,8 +259,9 @@ const BASE_URL = getApiBaseUrl();
|
|
|
|
|
|
|
|
// 创建表单
|
|
// 创建表单
|
|
|
const creating = ref(false);
|
|
const creating = ref(false);
|
|
|
-const showAdvancedOptions = ref(false);
|
|
|
|
|
|
|
+const showAdvancedOptions = ref(true); // 默认展开高级选项
|
|
|
const isRecommending = ref(false);
|
|
const isRecommending = ref(false);
|
|
|
|
|
+const attemptedCreate = ref(false);
|
|
|
const newBook = ref({
|
|
const newBook = ref({
|
|
|
title: '',
|
|
title: '',
|
|
|
subtitle: '',
|
|
subtitle: '',
|
|
@@ -494,20 +423,27 @@ const quickTemplates = [
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const styles = ['不填', '通俗易懂', '专业严谨', '轻松幽默', '诗意优美', '故事化'];
|
|
const styles = ['不填', '通俗易懂', '专业严谨', '轻松幽默', '诗意优美', '故事化'];
|
|
|
|
|
+const commonScales = [
|
|
|
|
|
+ { value: '1000', label: '1千字' },
|
|
|
|
|
+ { value: '3000', label: '3千字' },
|
|
|
|
|
+ { value: '5000', label: '5千字' },
|
|
|
|
|
+ { value: '130000', label: '13万字' },
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
const bookScales = [
|
|
const bookScales = [
|
|
|
- { value: '340000', label: '上2', words: '约34万字', chapters: '170章', audio: '约28小时' },
|
|
|
|
|
- { value: '210000', label: '上1', words: '约21万字', chapters: '105章', audio: '约17小时' },
|
|
|
|
|
- { value: '130000', label: '标准', words: '约13万字', chapters: '65章', audio: '约11小时' },
|
|
|
|
|
- { value: '80000', label: '下1', words: '约8万字', chapters: '40章', audio: '约7小时' },
|
|
|
|
|
- { value: '50000', label: '下2', words: '约5万字', chapters: '25章', audio: '约4小时' },
|
|
|
|
|
- { value: '31000', label: '下3', words: '约3.1万字', chapters: '16章', audio: '约2.5小时' },
|
|
|
|
|
- { value: '19000', label: '下4', words: '约1.9万字', chapters: '10章', audio: '约1.5小时' },
|
|
|
|
|
- { value: '12000', label: '下5', words: '约1.2万字', chapters: '6章', audio: '约1小时' },
|
|
|
|
|
- { value: '7000', label: '下6', words: '约7千字', chapters: '4章', audio: '约35分钟' },
|
|
|
|
|
- { value: '4000', label: '下7', words: '约4千字', chapters: '2章', audio: '约20分钟' },
|
|
|
|
|
- { value: '3000', label: '下8', words: '约3千字', chapters: '2章', audio: '约15分钟' },
|
|
|
|
|
- { value: '2000', label: '下9', words: '约2千字', chapters: '1章', audio: '约10分钟' },
|
|
|
|
|
- { value: '1000', label: '下10', words: '约1千字', chapters: '1章', audio: '约5分钟' },
|
|
|
|
|
|
|
+ { value: '340000', label: '上2 | 34万字 170章', words: '约34万字', chapters: '170章', audio: '约28小时' },
|
|
|
|
|
+ { value: '210000', label: '上1 | 21万字 105章', words: '约21万字', chapters: '105章', audio: '约17小时' },
|
|
|
|
|
+ { value: '130000', label: '标准 | 13万字 65章', words: '约13万字', chapters: '65章', audio: '约11小时' },
|
|
|
|
|
+ { value: '80000', label: '下1 | 8万字 40章', words: '约8万字', chapters: '40章', audio: '约7小时' },
|
|
|
|
|
+ { value: '50000', label: '下2 | 5万字 25章', words: '约5万字', chapters: '25章', audio: '约4小时' },
|
|
|
|
|
+ { value: '31000', label: '下3 | 3.1万字 16章', words: '约3.1万字', chapters: '16章', audio: '约2.5小时' },
|
|
|
|
|
+ { value: '19000', label: '下4 | 1.9万字 10章', words: '约1.9万字', chapters: '10章', audio: '约1.5小时' },
|
|
|
|
|
+ { value: '12000', label: '下5 | 1.2万字 6章', words: '约1.2万字', chapters: '6章', audio: '约1小时' },
|
|
|
|
|
+ { value: '7000', label: '下6 | 7千字 4章', words: '约7千字', chapters: '4章', audio: '约35分钟' },
|
|
|
|
|
+ { value: '4000', label: '下7 | 4千字 2章', words: '约4千字', chapters: '2章', audio: '约20分钟' },
|
|
|
|
|
+ { value: '3000', label: '下8 | 3千字 2章', words: '约3千字', chapters: '2章', audio: '约15分钟' },
|
|
|
|
|
+ { value: '2000', label: '下9 | 2千字 1章', words: '约2千字', chapters: '1章', audio: '约10分钟' },
|
|
|
|
|
+ { value: '1000', label: '下10 | 1千字 1章', words: '约1千字', chapters: '1章', audio: '约5分钟' },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
// 书籍类型选项
|
|
// 书籍类型选项
|
|
@@ -545,6 +481,7 @@ function toggleSpecialFeature(feature: string) {
|
|
|
} else {
|
|
} else {
|
|
|
features.push(feature);
|
|
features.push(feature);
|
|
|
}
|
|
}
|
|
|
|
|
+ syncToDescription('特殊要求', newBook.value.specialFeatures.join('、'));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 应用模板
|
|
// 应用模板
|
|
@@ -565,7 +502,7 @@ function applyTemplate(template: typeof quickTemplates[0]) {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 智能推荐
|
|
|
|
|
|
|
+// AI自动优化描述(直接应用)
|
|
|
async function requestSmartRecommend() {
|
|
async function requestSmartRecommend() {
|
|
|
if (!newBook.value.description.trim() || isRecommending.value) return;
|
|
if (!newBook.value.description.trim() || isRecommending.value) return;
|
|
|
|
|
|
|
@@ -583,20 +520,32 @@ async function requestSmartRecommend() {
|
|
|
const res = response.data as any;
|
|
const res = response.data as any;
|
|
|
if (res.code === 0 && res.data) {
|
|
if (res.code === 0 && res.data) {
|
|
|
const rec = res.data;
|
|
const rec = res.data;
|
|
|
|
|
+ const parts: string[] = [];
|
|
|
|
|
+
|
|
|
if (rec.knowledgeLevel) {
|
|
if (rec.knowledgeLevel) {
|
|
|
newBook.value.knowledgeLevel = rec.knowledgeLevel;
|
|
newBook.value.knowledgeLevel = rec.knowledgeLevel;
|
|
|
|
|
+ parts.push(`知识难度:${rec.knowledgeLevel}`);
|
|
|
}
|
|
}
|
|
|
if (rec.targetAudience) {
|
|
if (rec.targetAudience) {
|
|
|
newBook.value.targetAudience = rec.targetAudience;
|
|
newBook.value.targetAudience = rec.targetAudience;
|
|
|
|
|
+ parts.push(`面向人群:${rec.targetAudience}`);
|
|
|
}
|
|
}
|
|
|
if (rec.style) {
|
|
if (rec.style) {
|
|
|
newBook.value.style = rec.style;
|
|
newBook.value.style = rec.style;
|
|
|
|
|
+ parts.push(`写作风格:${rec.style}`);
|
|
|
}
|
|
}
|
|
|
if (rec.industry) {
|
|
if (rec.industry) {
|
|
|
newBook.value.industry = rec.industry;
|
|
newBook.value.industry = rec.industry;
|
|
|
|
|
+ parts.push(`行业领域:${rec.industry}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 自动展开高级选项显示推荐结果
|
|
|
|
|
|
|
+ // 追加到description textarea
|
|
|
|
|
+ if (parts.length > 0) {
|
|
|
|
|
+ const separator = newBook.value.description.trim() ? '\n' : '';
|
|
|
|
|
+ newBook.value.description = newBook.value.description.trim() + separator + parts.join('\n');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 自动展开高级选项
|
|
|
showAdvancedOptions.value = true;
|
|
showAdvancedOptions.value = true;
|
|
|
|
|
|
|
|
uni.showToast({
|
|
uni.showToast({
|
|
@@ -610,7 +559,7 @@ async function requestSmartRecommend() {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
} catch (e: any) {
|
|
} catch (e: any) {
|
|
|
- console.error('智能推荐失败:', e);
|
|
|
|
|
|
|
+ console.error('AI自动优化描述失败:', e);
|
|
|
uni.showToast({
|
|
uni.showToast({
|
|
|
title: '推荐服务暂不可用',
|
|
title: '推荐服务暂不可用',
|
|
|
icon: 'none',
|
|
icon: 'none',
|
|
@@ -620,9 +569,36 @@ async function requestSmartRecommend() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// scale chip 点击处理
|
|
|
|
|
+function onScaleChipClick(value: string) {
|
|
|
|
|
+ onBookScaleChange({ detail: { value: bookScales.findIndex(s => s.value === value) } });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 将选项同步到description textarea
|
|
|
|
|
+function syncToDescription(label: string, value: string) {
|
|
|
|
|
+ if (!value || value === 'auto' || value === '不填') return;
|
|
|
|
|
+ const line = `${label}:${value}`;
|
|
|
|
|
+ const desc = newBook.value.description;
|
|
|
|
|
+ // 检查是否已存在该标签
|
|
|
|
|
+ if (desc.includes(`${label}:`)) {
|
|
|
|
|
+ // 替换已有行
|
|
|
|
|
+ newBook.value.description = desc.replace(new RegExp(`${label}:[^\\n]*`), line);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 追加新行
|
|
|
|
|
+ newBook.value.description = desc.trim() ? `${desc.trim()}\n${line}` : line;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 选择书籍规模时加载预估
|
|
// 选择书籍规模时加载预估
|
|
|
-async function selectBookScale(scale: string) {
|
|
|
|
|
|
|
+function getScaleDisplay(value: string): string {
|
|
|
|
|
+ return bookScales.find(s => s.value === value)?.label || '';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function onBookScaleChange(e: any) {
|
|
|
|
|
+ const index = e.detail.value;
|
|
|
|
|
+ const scale = bookScales[index].value;
|
|
|
newBook.value.bookScale = scale;
|
|
newBook.value.bookScale = scale;
|
|
|
|
|
+ syncToDescription('书籍规模', scale);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
const bt = newBook.value.bookType;
|
|
const bt = newBook.value.bookType;
|
|
@@ -697,6 +673,7 @@ function goBack() {
|
|
|
|
|
|
|
|
// 创建新书籍
|
|
// 创建新书籍
|
|
|
async function createNewBook() {
|
|
async function createNewBook() {
|
|
|
|
|
+ attemptedCreate.value = true;
|
|
|
if (!canCreateBook.value) return;
|
|
if (!canCreateBook.value) return;
|
|
|
|
|
|
|
|
const configSummary: string[] = [];
|
|
const configSummary: string[] = [];
|
|
@@ -840,33 +817,24 @@ onMounted(() => {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
min-height: 200rpx;
|
|
min-height: 200rpx;
|
|
|
padding: 24rpx;
|
|
padding: 24rpx;
|
|
|
|
|
+ padding-bottom: 60rpx;
|
|
|
background: #f9fafb;
|
|
background: #f9fafb;
|
|
|
border: 2rpx solid #e5e7eb;
|
|
border: 2rpx solid #e5e7eb;
|
|
|
border-radius: 12rpx;
|
|
border-radius: 12rpx;
|
|
|
font-size: 28rpx;
|
|
font-size: 28rpx;
|
|
|
line-height: 1.6;
|
|
line-height: 1.6;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.label-with-badge { display: flex; align-items: center; gap: 12rpx; margin-bottom: 12rpx; }
|
|
|
|
|
-.required-badge {
|
|
|
|
|
- padding: 4rpx 12rpx;
|
|
|
|
|
- background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
|
|
|
- color: white;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-}
|
|
|
|
|
-.optional-badge {
|
|
|
|
|
- padding: 4rpx 12rpx;
|
|
|
|
|
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
|
|
|
|
- color: white;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
|
|
+.form-textarea.error { border-color: #ef4444; }
|
|
|
|
|
+.textarea-wrapper { position: relative; }
|
|
|
|
|
+.char-count {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 24rpx;
|
|
|
|
|
+ bottom: 16rpx;
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: #9ca3af;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.highlight-item { padding: 24rpx; background: rgba(79, 70, 229, 0.03); border-radius: 12rpx; border: 1rpx solid rgba(79, 70, 229, 0.1); }
|
|
|
|
|
-
|
|
|
|
|
.chip-group { display: flex; flex-wrap: wrap; gap: 12rpx; }
|
|
.chip-group { display: flex; flex-wrap: wrap; gap: 12rpx; }
|
|
|
.chip {
|
|
.chip {
|
|
|
display: flex;
|
|
display: flex;
|
|
@@ -890,82 +858,32 @@ onMounted(() => {
|
|
|
border: 2rpx solid #e5e7eb;
|
|
border: 2rpx solid #e5e7eb;
|
|
|
border-radius: 12rpx;
|
|
border-radius: 12rpx;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
|
|
+ transition: all 0.2s;
|
|
|
}
|
|
}
|
|
|
|
|
+.template-card:active { transform: scale(1.02); box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.1); }
|
|
|
.template-icon { font-size: 40rpx; display: block; margin-bottom: 8rpx; }
|
|
.template-icon { font-size: 40rpx; display: block; margin-bottom: 8rpx; }
|
|
|
.template-name { font-size: 26rpx; font-weight: 600; color: #1f2937; display: block; }
|
|
.template-name { font-size: 26rpx; font-weight: 600; color: #1f2937; display: block; }
|
|
|
.template-desc { font-size: 22rpx; color: #9ca3af; display: block; margin-top: 4rpx; }
|
|
.template-desc { font-size: 22rpx; color: #9ca3af; display: block; margin-top: 4rpx; }
|
|
|
|
|
|
|
|
-.important-tip { padding: 24rpx; background: rgba(79, 70, 229, 0.05); border-radius: 12rpx; margin: 24rpx 0; }
|
|
|
|
|
-.tip-header { display: flex; align-items: center; gap: 8rpx; margin-bottom: 12rpx; }
|
|
|
|
|
-.tip-icon { font-size: 28rpx; }
|
|
|
|
|
-.tip-title { font-size: 28rpx; font-weight: 600; color: #4f46e5; }
|
|
|
|
|
-.tip-content { font-size: 24rpx; color: #6b7280; line-height: 1.6; display: block; margin-bottom: 12rpx; }
|
|
|
|
|
-.tip-examples { display: flex; flex-direction: column; gap: 8rpx; }
|
|
|
|
|
-.tip-example { font-size: 24rpx; color: #9ca3af; }
|
|
|
|
|
|
|
+.scale-chips { display: flex; flex-wrap: wrap; gap: 12rpx; margin-bottom: 16rpx; }
|
|
|
|
|
+.more-scales { font-size: 24rpx; color: #6366f1; padding: 12rpx 0; }
|
|
|
|
|
|
|
|
-.scale-picker { display: flex; flex-wrap: wrap; gap: 12rpx; }
|
|
|
|
|
-.scale-option {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-width: 200rpx;
|
|
|
|
|
- padding: 16rpx;
|
|
|
|
|
- background: #f9fafb;
|
|
|
|
|
- border: 2rpx solid #e5e7eb;
|
|
|
|
|
|
|
+/* 预估卡片 */
|
|
|
|
|
+.estimate-card {
|
|
|
|
|
+ background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
|
|
border-radius: 12rpx;
|
|
border-radius: 12rpx;
|
|
|
- text-align: center;
|
|
|
|
|
-}
|
|
|
|
|
-.scale-option.active { border-color: #4f46e5; background: rgba(79, 70, 229, 0.05); }
|
|
|
|
|
-.scale-words { font-size: 28rpx; font-weight: 600; color: #1f2937; display: block; }
|
|
|
|
|
-.scale-label { font-size: 24rpx; color: #6b7280; display: block; margin-top: 4rpx; }
|
|
|
|
|
-.scale-audio { font-size: 22rpx; color: #9ca3af; display: block; margin-top: 4rpx; }
|
|
|
|
|
-
|
|
|
|
|
-.estimate-card { padding: 24rpx; background: rgba(16, 185, 129, 0.05); border-radius: 12rpx; margin-top: 24rpx; }
|
|
|
|
|
-.estimate-header { margin-bottom: 16rpx; }
|
|
|
|
|
-.estimate-title { font-size: 28rpx; font-weight: 600; color: #059669; }
|
|
|
|
|
-.estimate-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8rpx; }
|
|
|
|
|
-.estimate-label { font-size: 24rpx; color: #6b7280; }
|
|
|
|
|
-.estimate-value { font-size: 24rpx; font-weight: 500; color: #1f2937; }
|
|
|
|
|
-.genlevel-row { margin-top: 8rpx; padding-top: 8rpx; border-top: 1rpx solid rgba(16, 185, 129, 0.15); }
|
|
|
|
|
-.genlevel-tag { display: flex; align-items: center; gap: 6rpx; padding: 4rpx 12rpx; border-radius: 8rpx; font-size: 22rpx; }
|
|
|
|
|
-.genlevel-tag.level-1 { background: rgba(239, 68, 68, 0.1); }
|
|
|
|
|
-.genlevel-tag.level-2 { background: rgba(245, 158, 11, 0.1); }
|
|
|
|
|
-.genlevel-tag.level-3 { background: rgba(79, 70, 229, 0.1); }
|
|
|
|
|
-.genlevel-label { font-weight: 600; color: #1f2937; font-size: 22rpx; }
|
|
|
|
|
-.genlevel-desc { color: #9ca3af; font-size: 20rpx; }
|
|
|
|
|
-
|
|
|
|
|
-.quota-check { margin-top: 16rpx; padding-top: 16rpx; border-top: 1rpx solid rgba(16, 185, 129, 0.2); }
|
|
|
|
|
-.quota-ok { display: flex; align-items: center; gap: 8rpx; margin-bottom: 12rpx; }
|
|
|
|
|
-.quota-icon { font-size: 24rpx; }
|
|
|
|
|
-.quota-text { font-size: 24rpx; color: #059669; }
|
|
|
|
|
-.quota-warning { display: flex; align-items: center; gap: 8rpx; margin-bottom: 12rpx; }
|
|
|
|
|
-.quota-detail { margin-top: 8rpx; }
|
|
|
|
|
-.quota-info { font-size: 22rpx; color: #9ca3af; display: block; }
|
|
|
|
|
-.quota-overage { margin-top: 8rpx; display: flex; justify-content: space-between; }
|
|
|
|
|
-.quota-price { font-size: 22rpx; color: #ef4444; }
|
|
|
|
|
-
|
|
|
|
|
-.btn-group { display: flex; gap: 16rpx; margin-top: 32rpx; }
|
|
|
|
|
-.btn-cancel, .btn-primary {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- height: 88rpx;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- border: none;
|
|
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ margin-bottom: 24rpx;
|
|
|
}
|
|
}
|
|
|
-.btn-cancel { background: #f3f4f6; color: #6b7280; }
|
|
|
|
|
-.btn-primary { background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; }
|
|
|
|
|
-.btn-primary[disabled] { opacity: 0.5; }
|
|
|
|
|
-
|
|
|
|
|
-/* 简洁模式提示 */
|
|
|
|
|
-.simple-mode-tip { padding: 24rpx; background: rgba(16, 185, 129, 0.08); border-radius: 12rpx; margin-bottom: 32rpx; }
|
|
|
|
|
-.simple-mode-tip .tip-header { display: flex; align-items: center; gap: 8rpx; margin-bottom: 8rpx; }
|
|
|
|
|
-.simple-mode-tip .tip-icon { font-size: 28rpx; }
|
|
|
|
|
-.simple-mode-tip .tip-title { font-size: 28rpx; font-weight: 600; color: #059669; }
|
|
|
|
|
-.simple-mode-tip .tip-content { font-size: 24rpx; color: #6b7280; line-height: 1.5; }
|
|
|
|
|
-
|
|
|
|
|
-/* 智能推荐按钮 */
|
|
|
|
|
-.smart-recommend-section { margin-top: 24rpx; }
|
|
|
|
|
|
|
+.estimate-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8rpx; }
|
|
|
|
|
+.estimate-label { font-size: 24rpx; color: #065f46; }
|
|
|
|
|
+.estimate-value { font-size: 24rpx; font-weight: 500; color: #065f46; }
|
|
|
|
|
+.estimate-sep { font-size: 24rpx; color: #6b7280; }
|
|
|
|
|
+.quota-row { margin-top: 8rpx; }
|
|
|
|
|
+.quota-ok { font-size: 24rpx; color: #059669; }
|
|
|
|
|
+.quota-warn { font-size: 24rpx; color: #dc2626; }
|
|
|
|
|
+
|
|
|
|
|
+/* AI自动优化描述按钮 */
|
|
|
.btn-smart-recommend {
|
|
.btn-smart-recommend {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
@@ -976,21 +894,40 @@ onMounted(() => {
|
|
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
|
|
border-radius: 12rpx;
|
|
border-radius: 12rpx;
|
|
|
border: none;
|
|
border: none;
|
|
|
- margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
}
|
|
|
.btn-smart-recommend[disabled] { opacity: 0.5; }
|
|
.btn-smart-recommend[disabled] { opacity: 0.5; }
|
|
|
.recommend-icon { font-size: 32rpx; }
|
|
.recommend-icon { font-size: 32rpx; }
|
|
|
.recommend-text { font-size: 28rpx; font-weight: 600; color: white; }
|
|
.recommend-text { font-size: 28rpx; font-weight: 600; color: white; }
|
|
|
|
|
|
|
|
-/* 高级选项折叠区 */
|
|
|
|
|
-.advanced-section { margin-top: 32rpx; border-top: 1rpx solid #e5e7eb; padding-top: 24rpx; }
|
|
|
|
|
-.advanced-header {
|
|
|
|
|
|
|
+/* 区域样式 */
|
|
|
|
|
+.section { margin-bottom: 32rpx; padding-bottom: 32rpx; border-bottom: 1rpx solid #e5e7eb; }
|
|
|
|
|
+.section:last-of-type { border-bottom: none; }
|
|
|
|
|
+.section-title { font-size: 32rpx; font-weight: 600; color: #1f2937; display: block; margin-bottom: 24rpx; }
|
|
|
|
|
+.section-header { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0; }
|
|
|
|
|
+.section-arrow { font-size: 24rpx; color: #9ca3af; }
|
|
|
|
|
+.section-content { margin-top: 16rpx; }
|
|
|
|
|
+
|
|
|
|
|
+/* 按钮组固定底部 */
|
|
|
|
|
+.btn-group-fixed {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 16rpx;
|
|
|
|
|
+ padding: 24rpx 0;
|
|
|
|
|
+ position: sticky;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.05);
|
|
|
|
|
+}
|
|
|
|
|
+.btn-cancel, .btn-primary {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 88rpx;
|
|
|
|
|
+ border-radius: 16rpx;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
- padding: 16rpx 0;
|
|
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ border: none;
|
|
|
}
|
|
}
|
|
|
-.advanced-title { font-size: 28rpx; font-weight: 600; color: #4b5563; }
|
|
|
|
|
-.advanced-arrow { font-size: 24rpx; color: #9ca3af; }
|
|
|
|
|
-.advanced-content { margin-top: 16rpx; }
|
|
|
|
|
|
|
+.btn-cancel { background: #f3f4f6; color: #6b7280; }
|
|
|
|
|
+.btn-primary { background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; }
|
|
|
|
|
+.btn-primary[disabled] { opacity: 0.5; }
|
|
|
</style>
|
|
</style>
|