|
|
@@ -15,19 +15,6 @@
|
|
|
|
|
|
<!-- 主内容区 -->
|
|
|
<view class="main-content">
|
|
|
- <!-- 步骤指示器 -->
|
|
|
- <view class="steps-indicator">
|
|
|
- <view
|
|
|
- v-for="(step, index) in steps"
|
|
|
- :key="index"
|
|
|
- class="step-item"
|
|
|
- :class="{ active: currentStep >= index, current: currentStep === index }"
|
|
|
- >
|
|
|
- <view class="step-circle">{{ index + 1 }}</view>
|
|
|
- <text class="step-text">{{ step }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
<!-- 步骤1:基础设置 -->
|
|
|
<view v-if="currentStep === 0" class="card">
|
|
|
<view class="card-header">
|
|
|
@@ -37,26 +24,19 @@
|
|
|
<!-- 内容类型 -->
|
|
|
<view class="form-item">
|
|
|
<text class="form-label">内容类型</text>
|
|
|
- <view class="type-grid">
|
|
|
- <view
|
|
|
- v-for="cat in contentCategories"
|
|
|
- :key="cat.name"
|
|
|
- class="type-category"
|
|
|
- >
|
|
|
- <view class="category-header">{{ cat.name }}</view>
|
|
|
- <view class="type-items">
|
|
|
- <view
|
|
|
- v-for="type in cat.types"
|
|
|
- :key="type"
|
|
|
- class="type-item"
|
|
|
- :class="{ active: selectedType === type }"
|
|
|
- @click="selectedType = type"
|
|
|
- >
|
|
|
- {{ type }}
|
|
|
- </view>
|
|
|
+ <scroll-view scroll-x class="type-scroll">
|
|
|
+ <view class="type-list">
|
|
|
+ <view
|
|
|
+ v-for="type in allTypes"
|
|
|
+ :key="type"
|
|
|
+ class="type-item"
|
|
|
+ :class="{ active: selectedType === type }"
|
|
|
+ @click="selectedType = type"
|
|
|
+ >
|
|
|
+ {{ type }}
|
|
|
</view>
|
|
|
</view>
|
|
|
- </view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 行业选择 -->
|
|
|
@@ -77,14 +57,18 @@
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 主题输入 -->
|
|
|
+ <!-- 主题/需求输入 -->
|
|
|
<view class="form-item">
|
|
|
- <text class="form-label">创作主题</text>
|
|
|
- <input
|
|
|
- v-model="theme"
|
|
|
- class="theme-input"
|
|
|
- placeholder="输入创作主题,如:都市爱情、励志故事"
|
|
|
+ <text class="form-label">创作需求</text>
|
|
|
+ <textarea
|
|
|
+ v-model="prompt"
|
|
|
+ class="prompt-input"
|
|
|
+ :placeholder="getPromptPlaceholder()"
|
|
|
+ :maxlength="500"
|
|
|
/>
|
|
|
+ <view class="input-hint">
|
|
|
+ <text>{{ prompt.length }}/500 字</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 目标字数 -->
|
|
|
@@ -92,112 +76,48 @@
|
|
|
<text class="form-label">目标字数:{{ targetLength }} 字</text>
|
|
|
<slider
|
|
|
:value="targetLength"
|
|
|
- :min="500"
|
|
|
+ :min="100"
|
|
|
:max="10000"
|
|
|
- :step="500"
|
|
|
+ :step="100"
|
|
|
@change="(e: any) => targetLength = e.detail.value"
|
|
|
activeColor="#4F46E5"
|
|
|
backgroundColor="#e5e7eb"
|
|
|
block-size="20"
|
|
|
/>
|
|
|
<view class="slider-range">
|
|
|
- <text>500字</text>
|
|
|
+ <text>100字</text>
|
|
|
<text>10000字</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <button class="next-btn" :disabled="generatingOutline" @click="goToStep2">
|
|
|
- <text v-if="generatingOutline">生成大纲中...</text>
|
|
|
- <text v-else>下一步:生成大纲 →</text>
|
|
|
+ <button
|
|
|
+ class="generate-btn"
|
|
|
+ :disabled="!canGenerate || generating"
|
|
|
+ @click="startGenerate"
|
|
|
+ >
|
|
|
+ <text v-if="generating">{{ generatingProgress || '生成中...' }}</text>
|
|
|
+ <text v-else>🚀 开始生成</text>
|
|
|
</button>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 步骤2:大纲预览 -->
|
|
|
+ <!-- 步骤2:生成结果 -->
|
|
|
<view v-if="currentStep === 1" class="card">
|
|
|
<view class="card-header">
|
|
|
- <text class="card-title">📑 内容大纲</text>
|
|
|
- <text class="header-action" @click="regenerateOutline">🔄 重新生成</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view v-if="generatingOutline" class="loading-state">
|
|
|
- <text>正在生成大纲...</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view v-else class="outline-content">
|
|
|
- <view class="outline-stats">
|
|
|
- <text class="stat-item">总章节:{{ outline.length }}</text>
|
|
|
- <text class="stat-item">预计字数:{{ targetLength }}</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view
|
|
|
- v-for="(chapter, index) in outline"
|
|
|
- :key="index"
|
|
|
- class="chapter-item"
|
|
|
- >
|
|
|
- <view class="chapter-header">
|
|
|
- <text class="chapter-title">{{ chapter.title }}</text>
|
|
|
- <text class="chapter-words">{{ chapter.wordCount }}字</text>
|
|
|
- </view>
|
|
|
- <text class="chapter-desc">{{ chapter.description }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="btn-row">
|
|
|
- <button class="back-btn" @click="currentStep = 0">← 上一步</button>
|
|
|
- <button class="next-btn" :disabled="generating" @click="goToStep3">
|
|
|
- <text v-if="generatingCharacters">生成角色中...</text>
|
|
|
- <text v-else>下一步:生成内容 →</text>
|
|
|
- </button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 步骤3:角色设定 -->
|
|
|
- <view v-if="currentStep === 2" class="card">
|
|
|
- <view class="card-header">
|
|
|
- <text class="card-title">👥 角色设定</text>
|
|
|
- <text class="header-action" @click="generateCharacters">🔄 重新生成</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="characters-list">
|
|
|
- <view
|
|
|
- v-for="char in characters"
|
|
|
- :key="char.id"
|
|
|
- class="character-card"
|
|
|
- >
|
|
|
- <view class="char-avatar">{{ char.gender === '男' ? '👨' : '👩' }}</view>
|
|
|
- <view class="char-info">
|
|
|
- <text class="char-name">{{ char.name }}</text>
|
|
|
- <text class="char-detail">年龄:{{ char.age }}</text>
|
|
|
- <text class="char-detail">性格:{{ char.personality }}</text>
|
|
|
- <text class="char-detail">角色:{{ char.role }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="btn-row">
|
|
|
- <button class="back-btn" @click="currentStep = 1">← 上一步</button>
|
|
|
- <button class="next-btn" @click="currentStep = 3">下一步:生成内容 →</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 步骤4:内容生成 -->
|
|
|
- <view v-if="currentStep === 3" class="card">
|
|
|
- <view class="card-header">
|
|
|
- <text class="card-title">✍️ 内容生成</text>
|
|
|
+ <text class="card-title">📝 生成结果</text>
|
|
|
<view class="header-actions">
|
|
|
+ <text class="header-action" @click="copyContent">📋 复制</text>
|
|
|
<text class="header-action" @click="saveDraft">💾 保存</text>
|
|
|
- <text class="header-action" @click="showQualityScore">📊 评分</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 生成进度 -->
|
|
|
+ <!-- 进度显示 -->
|
|
|
<view v-if="generating" class="progress-section">
|
|
|
<view class="progress-header">
|
|
|
<text>{{ generatingProgress }}</text>
|
|
|
<text v-if="currentCharCount > 0">{{ currentCharCount }} 字</text>
|
|
|
</view>
|
|
|
<progress
|
|
|
- :percent="Math.round((currentChapter + 1) / outline.length * 100)"
|
|
|
+ :percent="Math.round(progressPercent)"
|
|
|
activeColor="#4F46E5"
|
|
|
backgroundColor="#e5e7eb"
|
|
|
stroke-width="8"
|
|
|
@@ -205,31 +125,22 @@
|
|
|
</view>
|
|
|
|
|
|
<!-- 生成的内容 -->
|
|
|
- <view class="generated-content">
|
|
|
- <view
|
|
|
- v-for="(chapter, index) in generatedChapters"
|
|
|
- :key="index"
|
|
|
- class="content-chapter"
|
|
|
- >
|
|
|
- <view class="chapter-badge">{{ index + 1 }}</view>
|
|
|
- <text class="chapter-text">{{ chapter }}</text>
|
|
|
- </view>
|
|
|
+ <view v-if="generatedContent" class="result-content">
|
|
|
+ <text class="content-text">{{ generatedContent }}</text>
|
|
|
</view>
|
|
|
|
|
|
- <view v-if="!generating && generatedChapters.length === 0" class="empty-state">
|
|
|
- <text>点击下方按钮开始生成内容</text>
|
|
|
+ <view class="result-footer">
|
|
|
+ <text class="word-count">{{ generatedContent?.length || 0 }} 字</text>
|
|
|
</view>
|
|
|
|
|
|
<view class="btn-row">
|
|
|
- <button class="back-btn" @click="currentStep = 2">← 上一步</button>
|
|
|
- <button class="generate-btn" :disabled="generating" @click="startGeneration">
|
|
|
- {{ generating ? '生成中...' : '🚀 开始生成' }}
|
|
|
- </button>
|
|
|
+ <button class="back-btn" @click="reset">← 重新生成</button>
|
|
|
+ <button class="primary-btn" @click="useContent">📝 使用此内容</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 步骤5:质量检测 -->
|
|
|
- <view v-if="currentStep === 4" class="card">
|
|
|
+ <!-- 步骤3:质量检测 -->
|
|
|
+ <view v-if="currentStep === 2" class="card">
|
|
|
<view class="card-header">
|
|
|
<text class="card-title">✅ 质量检测</text>
|
|
|
</view>
|
|
|
@@ -238,7 +149,9 @@
|
|
|
<view class="quality-item">
|
|
|
<view class="quality-header">
|
|
|
<text class="quality-title">🔍 敏感词检测</text>
|
|
|
- <text class="quality-status pass">通过</text>
|
|
|
+ <text class="quality-status" :class="sensitiveResult.isClean ? 'pass' : 'fail'">
|
|
|
+ {{ sensitiveResult.isClean ? '通过' : '有问题' }}
|
|
|
+ </text>
|
|
|
</view>
|
|
|
<view v-if="sensitiveResult.foundWords.length > 0" class="sensitive-words">
|
|
|
<text v-for="word in sensitiveResult.foundWords" :key="word" class="sensitive-tag">
|
|
|
@@ -251,7 +164,9 @@
|
|
|
<view class="quality-item">
|
|
|
<view class="quality-header">
|
|
|
<text class="quality-title">📋 合规检查</text>
|
|
|
- <text class="quality-status pass">通过</text>
|
|
|
+ <text class="quality-status" :class="complianceResult.passed ? 'pass' : 'fail'">
|
|
|
+ {{ complianceResult.passed ? '通过' : '有问题' }}
|
|
|
+ </text>
|
|
|
</view>
|
|
|
<view v-if="complianceResult.warnings.length > 0" class="compliance-warnings">
|
|
|
<text v-for="warn in complianceResult.warnings" :key="warn" class="warning-tag">
|
|
|
@@ -282,7 +197,7 @@
|
|
|
</view>
|
|
|
|
|
|
<view class="btn-row">
|
|
|
- <button class="back-btn" @click="currentStep = 3">← 上一步</button>
|
|
|
+ <button class="back-btn" @click="currentStep = 1">← 返回结果</button>
|
|
|
<button class="primary-btn" @click="useContent">📝 使用此内容</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -296,12 +211,17 @@
|
|
|
<text class="modal-close" @click="showHelp = false">✕</text>
|
|
|
</view>
|
|
|
<view class="modal-body">
|
|
|
- <text class="help-text">1. 选择内容类型和行业</text>
|
|
|
- <text class="help-text">2. 输入创作主题</text>
|
|
|
- <text class="help-text">3. 生成并预览大纲</text>
|
|
|
- <text class="help-text">4. 查看角色设定</text>
|
|
|
- <text class="help-text">5. 一键生成完整内容</text>
|
|
|
- <text class="help-text">6. 质量检测后使用</text>
|
|
|
+ <text class="help-title">支持的类型:</text>
|
|
|
+ <text class="help-text">• 创作类:小说、故事、剧本、诗歌、散文</text>
|
|
|
+ <text class="help-text">• 营销类:产品介绍、广告文案、朋友圈、小红书、抖音脚本</text>
|
|
|
+ <text class="help-text">• 教育类:课件、培训、教程、知识科普</text>
|
|
|
+ <text class="help-text">• 商务类:销售话术、客服话术、商务邮件</text>
|
|
|
+ <text class="help-text">• 媒体类:新闻播报、天气预报、体育解说</text>
|
|
|
+ <text class="help-title">使用流程:</text>
|
|
|
+ <text class="help-text">1. 选择内容类型</text>
|
|
|
+ <text class="help-text">2. 选择所属行业</text>
|
|
|
+ <text class="help-text">3. 描述您的需求</text>
|
|
|
+ <text class="help-text">4. 一键生成</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -310,40 +230,29 @@
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed } from 'vue';
|
|
|
-import { get, post } from '../../utils/request';
|
|
|
-
|
|
|
-const steps = ['基础设置', '大纲预览', '角色设定', '内容生成', '质量检测'];
|
|
|
-const currentStep = ref(0);
|
|
|
-
|
|
|
-// 基础设置
|
|
|
-const contentCategories = [
|
|
|
- { name: '创作类', types: ['小说', '故事', '剧本', '诗歌', '散文'] },
|
|
|
- { name: '营销类', types: ['产品介绍', '广告文案', '朋友圈', '小红书', '抖音脚本'] },
|
|
|
- { name: '教育类', types: ['课件', '培训', '教程', '知识科普'] },
|
|
|
- { name: '商务类', types: ['销售话术', '客服话术', '商务邮件'] },
|
|
|
- { name: '媒体类', types: ['新闻播报', '天气预报', '体育解说'] },
|
|
|
+import { post } from '../../utils/request';
|
|
|
+
|
|
|
+const allTypes = [
|
|
|
+ '小说', '故事', '剧本', '诗歌', '散文',
|
|
|
+ '产品介绍', '广告文案', '朋友圈', '小红书', '抖音脚本',
|
|
|
+ '课件', '培训', '教程', '知识科普',
|
|
|
+ '销售话术', '客服话术', '商务邮件',
|
|
|
+ '新闻播报', '天气预报', '体育解说',
|
|
|
];
|
|
|
|
|
|
const industries = ['通用', '医疗健康', '教育培训', '金融服务', '电子商务', '餐饮美食'];
|
|
|
+
|
|
|
+const currentStep = ref(0);
|
|
|
const selectedType = ref('小说');
|
|
|
const selectedIndustry = ref('通用');
|
|
|
-const theme = ref('');
|
|
|
+const prompt = ref('');
|
|
|
const targetLength = ref(2000);
|
|
|
|
|
|
-// 大纲
|
|
|
-const generatingOutline = ref(false);
|
|
|
-const outline = ref<{ title: string; description: string; wordCount: number }[]>([]);
|
|
|
-
|
|
|
-// 角色
|
|
|
-const characters = ref<any[]>([]);
|
|
|
-const generatingCharacters = ref(false);
|
|
|
-
|
|
|
-// 内容生成
|
|
|
+// 生成状态
|
|
|
const generating = ref(false);
|
|
|
-const currentChapter = ref(0);
|
|
|
-const generatedChapters = ref<string[]>([]);
|
|
|
-const generatingProgress = ref(''); // 当前进度
|
|
|
-const currentCharCount = ref(0); // 当前已生成字数
|
|
|
+const generatingProgress = ref('');
|
|
|
+const currentCharCount = ref(0);
|
|
|
+const generatedContent = ref('');
|
|
|
|
|
|
// 质量检测
|
|
|
const sensitiveResult = ref<{ isClean: boolean; foundWords: string[] }>({ isClean: true, foundWords: [] });
|
|
|
@@ -353,7 +262,14 @@ const qualityScore = ref<any>(null);
|
|
|
// UI状态
|
|
|
const showHelp = ref(false);
|
|
|
|
|
|
-const fullContent = computed(() => generatedChapters.value.join('\n\n'));
|
|
|
+const canGenerate = computed(() => {
|
|
|
+ return prompt.value.trim().length > 0 && !generating.value;
|
|
|
+});
|
|
|
+
|
|
|
+const progressPercent = computed(() => {
|
|
|
+ if (!generating.value) return 100;
|
|
|
+ return 50; // 生成中固定50%
|
|
|
+});
|
|
|
|
|
|
const dimensionNames: Record<string, string> = {
|
|
|
fluency: '流畅度',
|
|
|
@@ -367,101 +283,47 @@ function getDimensionName(key: string) {
|
|
|
return dimensionNames[key] || key;
|
|
|
}
|
|
|
|
|
|
+function getPromptPlaceholder() {
|
|
|
+ const placeholders: Record<string, string> = {
|
|
|
+ '小说': '描述你想要的故事类型,如:都市爱情、玄幻穿越、悬疑推理...',
|
|
|
+ '故事': '描述故事的主题和背景...',
|
|
|
+ '产品介绍': '介绍产品的名称、特点、优势...',
|
|
|
+ '广告文案': '描述产品卖点、活动内容...',
|
|
|
+ '朋友圈': '描述想表达的心情或内容...',
|
|
|
+ '培训': '描述培训主题和目标受众...',
|
|
|
+ '销售话术': '描述产品类型和目标客户...',
|
|
|
+ '新闻播报': '描述新闻事件内容...',
|
|
|
+ };
|
|
|
+ return placeholders[selectedType.value] || '描述您的创作需求...';
|
|
|
+}
|
|
|
+
|
|
|
function goBack() {
|
|
|
uni.navigateBack();
|
|
|
}
|
|
|
|
|
|
-async function goToStep2() {
|
|
|
- if (!theme.value.trim()) {
|
|
|
- uni.showToast({ title: '请输入创作主题', icon: 'none' });
|
|
|
- return;
|
|
|
- }
|
|
|
- uni.showLoading({ title: '正在生成大纲...' });
|
|
|
- await generateOutline();
|
|
|
- uni.hideLoading();
|
|
|
- if (outline.value.length > 0) {
|
|
|
- currentStep.value = 1;
|
|
|
- }
|
|
|
-}
|
|
|
+async function startGenerate() {
|
|
|
+ if (!canGenerate.value) return;
|
|
|
|
|
|
-async function goToStep3() {
|
|
|
- uni.showLoading({ title: '正在生成角色...' });
|
|
|
- await generateCharacters();
|
|
|
- uni.hideLoading();
|
|
|
- currentStep.value = 2;
|
|
|
-}
|
|
|
+ generating.value = true;
|
|
|
+ generatingProgress.value = '正在生成内容...';
|
|
|
+ currentCharCount.value = 0;
|
|
|
+ generatedContent.value = '';
|
|
|
|
|
|
-async function generateOutline() {
|
|
|
- generatingOutline.value = true;
|
|
|
try {
|
|
|
- const result = await post<any>('/ai-content/outline/generate', {
|
|
|
+ const result = await post<any>('/ai-content/generate', {
|
|
|
type: selectedType.value,
|
|
|
- theme: theme.value,
|
|
|
- chapters: Math.ceil(targetLength.value / 1500),
|
|
|
+ industry: selectedIndustry.value,
|
|
|
+ prompt: prompt.value,
|
|
|
+ targetLength: targetLength.value,
|
|
|
});
|
|
|
- outline.value = result.outline || [];
|
|
|
- } catch (e: any) {
|
|
|
- uni.showToast({ title: '生成大纲失败', icon: 'none' });
|
|
|
- } finally {
|
|
|
- generatingOutline.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-async function regenerateOutline() {
|
|
|
- await generateOutline();
|
|
|
-}
|
|
|
-
|
|
|
-async function generateCharacters() {
|
|
|
- generatingCharacters.value = true;
|
|
|
- try {
|
|
|
- const result = await post<any>('/ai-content/characters/generate', {
|
|
|
- type: selectedType.value,
|
|
|
- genre: theme.value,
|
|
|
- });
|
|
|
- characters.value = result.characters || [];
|
|
|
- } catch (e) {
|
|
|
- characters.value = [
|
|
|
- { id: 'char-1', name: '张三', age: 30, gender: '男', personality: '正直勇敢', role: '主角' },
|
|
|
- { id: 'char-2', name: '李四', age: 28, gender: '女', personality: '聪明机智', role: '女主' },
|
|
|
- ];
|
|
|
- } finally {
|
|
|
- generatingCharacters.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function startGeneration() {
|
|
|
- generating.value = true;
|
|
|
- currentChapter.value = 0;
|
|
|
- generatedChapters.value = [];
|
|
|
- generatingProgress.value = '准备开始...';
|
|
|
- currentCharCount.value = 0;
|
|
|
-
|
|
|
- try {
|
|
|
- for (let i = 0; i < outline.value.length; i++) {
|
|
|
- currentChapter.value = i;
|
|
|
- generatingProgress.value = `正在生成第 ${i + 1}/${outline.value.length} 章...`;
|
|
|
- currentCharCount.value = 0;
|
|
|
-
|
|
|
- const chapterTitle = outline.value[i]?.title || `第${i + 1}章`;
|
|
|
- const previousContent = i > 0 ? generatedChapters.value[i - 1] : '';
|
|
|
-
|
|
|
- const result = await post<any>('/ai-content/chunk/generate', {
|
|
|
- outlineId: `outline-${Date.now()}`,
|
|
|
- chapterIndex: i,
|
|
|
- chapterTitle,
|
|
|
- previousContent,
|
|
|
- });
|
|
|
-
|
|
|
- generatedChapters.value.push(result.content || `这是第${i + 1}章的内容...`);
|
|
|
- currentCharCount.value = result.wordCount || result.content?.length || 0;
|
|
|
- generatingProgress.value = `第 ${i + 1}/${outline.value.length} 章完成 (${currentCharCount.value}字)`;
|
|
|
- }
|
|
|
+ generatedContent.value = result.content || result.text || '生成内容为空';
|
|
|
+ currentCharCount.value = generatedContent.value.length;
|
|
|
+ generatingProgress.value = '生成完成';
|
|
|
|
|
|
// 自动进行质量检测
|
|
|
- generatingProgress.value = '进行质量检测...';
|
|
|
await checkQuality();
|
|
|
- currentStep.value = 4;
|
|
|
- generatingProgress.value = '';
|
|
|
+ currentStep.value = 1;
|
|
|
} catch (e: any) {
|
|
|
uni.showToast({ title: '生成失败: ' + (e.message || '未知错误'), icon: 'none' });
|
|
|
} finally {
|
|
|
@@ -470,18 +332,18 @@ async function startGeneration() {
|
|
|
}
|
|
|
|
|
|
async function checkQuality() {
|
|
|
- const content = fullContent.value;
|
|
|
+ if (!generatedContent.value) return;
|
|
|
|
|
|
// 敏感词检测
|
|
|
try {
|
|
|
- const sensitive = await post<any>('/ai-content/sensitive/check', { text: content });
|
|
|
+ const sensitive = await post<any>('/ai-content/sensitive/check', { text: generatedContent.value });
|
|
|
sensitiveResult.value = sensitive;
|
|
|
} catch (e) {}
|
|
|
|
|
|
// 合规检查
|
|
|
try {
|
|
|
const compliance = await post<any>('/ai-content/compliance/check', {
|
|
|
- text: content,
|
|
|
+ text: generatedContent.value,
|
|
|
industry: selectedIndustry.value,
|
|
|
});
|
|
|
complianceResult.value = compliance;
|
|
|
@@ -489,12 +351,17 @@ async function checkQuality() {
|
|
|
|
|
|
// 质量评分
|
|
|
try {
|
|
|
- qualityScore.value = await post<any>('/ai-content/quality/score', { text: content });
|
|
|
+ qualityScore.value = await post<any>('/ai-content/quality/score', { text: generatedContent.value });
|
|
|
} catch (e) {}
|
|
|
}
|
|
|
|
|
|
-function showQualityScore() {
|
|
|
- checkQuality();
|
|
|
+function copyContent() {
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: generatedContent.value,
|
|
|
+ success: () => {
|
|
|
+ uni.showToast({ title: '已复制', icon: 'success' });
|
|
|
+ },
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
function saveDraft() {
|
|
|
@@ -502,9 +369,20 @@ function saveDraft() {
|
|
|
}
|
|
|
|
|
|
function useContent() {
|
|
|
- uni.setStorageSync('ai_generated_text', fullContent.value);
|
|
|
+ uni.setStorageSync('ai_generated_text', generatedContent.value);
|
|
|
uni.navigateBack();
|
|
|
}
|
|
|
+
|
|
|
+function reset() {
|
|
|
+ currentStep.value = 0;
|
|
|
+ generatedContent.value = '';
|
|
|
+ generatingProgress.value = '';
|
|
|
+ currentCharCount.value = 0;
|
|
|
+}
|
|
|
+
|
|
|
+function showQualityScore() {
|
|
|
+ currentStep.value = 2;
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -556,52 +434,6 @@ function useContent() {
|
|
|
padding: 140rpx 32rpx 32rpx;
|
|
|
}
|
|
|
|
|
|
-/* 步骤指示器 */
|
|
|
-.steps-indicator {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- margin-bottom: 32rpx;
|
|
|
- padding: 24rpx;
|
|
|
- background: #fff;
|
|
|
- border-radius: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.step-item {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- flex: 1;
|
|
|
- opacity: 0.4;
|
|
|
-}
|
|
|
-
|
|
|
-.step-item.active {
|
|
|
- opacity: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.step-item.current .step-circle {
|
|
|
- background: #4F46E5;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.step-circle {
|
|
|
- width: 48rpx;
|
|
|
- height: 48rpx;
|
|
|
- border-radius: 50%;
|
|
|
- background: #e5e7eb;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 24rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #6b7280;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.step-text {
|
|
|
- font-size: 20rpx;
|
|
|
- color: #6b7280;
|
|
|
-}
|
|
|
-
|
|
|
.card {
|
|
|
background: #fff;
|
|
|
border-radius: 24rpx;
|
|
|
@@ -622,16 +454,16 @@ function useContent() {
|
|
|
color: #1f2937;
|
|
|
}
|
|
|
|
|
|
-.header-action {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #4F46E5;
|
|
|
-}
|
|
|
-
|
|
|
.header-actions {
|
|
|
display: flex;
|
|
|
gap: 24rpx;
|
|
|
}
|
|
|
|
|
|
+.header-action {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #4F46E5;
|
|
|
+}
|
|
|
+
|
|
|
/* 表单 */
|
|
|
.form-item {
|
|
|
margin-bottom: 32rpx;
|
|
|
@@ -644,176 +476,67 @@ function useContent() {
|
|
|
display: block;
|
|
|
}
|
|
|
|
|
|
-/* 内容类型网格 */
|
|
|
-.type-grid {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.type-category {
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.category-header {
|
|
|
- font-size: 26rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #4F46E5;
|
|
|
- margin-bottom: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.type-items {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.type-item {
|
|
|
- font-size: 24rpx;
|
|
|
- padding: 8rpx 16rpx;
|
|
|
- background: #fff;
|
|
|
- border-radius: 8rpx;
|
|
|
- color: #6b7280;
|
|
|
- border: 2rpx solid transparent;
|
|
|
-}
|
|
|
-
|
|
|
-.type-item.active {
|
|
|
- background: #4F46E5;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-/* 行业选择 */
|
|
|
-.industry-scroll {
|
|
|
+/* 类型选择 */
|
|
|
+.type-scroll, .industry-scroll {
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
-.industry-list {
|
|
|
- display: flex;
|
|
|
+.type-list, .industry-list {
|
|
|
+ display: inline-flex;
|
|
|
gap: 16rpx;
|
|
|
padding: 8rpx 0;
|
|
|
}
|
|
|
|
|
|
-.industry-item {
|
|
|
+.type-item, .industry-item {
|
|
|
font-size: 26rpx;
|
|
|
padding: 12rpx 24rpx;
|
|
|
background: #f3f4f6;
|
|
|
border-radius: 32rpx;
|
|
|
color: #6b7280;
|
|
|
white-space: nowrap;
|
|
|
+ border: 2rpx solid transparent;
|
|
|
}
|
|
|
|
|
|
-.industry-item.active {
|
|
|
+.type-item.active, .industry-item.active {
|
|
|
background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%);
|
|
|
color: #fff;
|
|
|
+ border-color: #4f46e5;
|
|
|
}
|
|
|
|
|
|
-/* 主题输入 */
|
|
|
-.theme-input {
|
|
|
+/* 输入框 */
|
|
|
+.prompt-input {
|
|
|
width: 100%;
|
|
|
- height: 80rpx;
|
|
|
+ min-height: 200rpx;
|
|
|
background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 0 24rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 24rpx;
|
|
|
font-size: 28rpx;
|
|
|
+ line-height: 1.6;
|
|
|
}
|
|
|
|
|
|
-/* 滑块 */
|
|
|
-.slider-range {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
+.input-hint {
|
|
|
+ text-align: right;
|
|
|
margin-top: 8rpx;
|
|
|
}
|
|
|
|
|
|
-.slider-range text {
|
|
|
+.input-hint text {
|
|
|
font-size: 22rpx;
|
|
|
color: #9ca3af;
|
|
|
}
|
|
|
|
|
|
-/* 大纲内容 */
|
|
|
-.outline-stats {
|
|
|
- display: flex;
|
|
|
- gap: 24rpx;
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-item {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #6b7280;
|
|
|
- background: #f3f4f6;
|
|
|
- padding: 8rpx 16rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.chapter-item {
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 20rpx;
|
|
|
- margin-bottom: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.chapter-header {
|
|
|
+/* 滑块 */
|
|
|
+.slider-range {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.chapter-title {
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
+ margin-top: 8rpx;
|
|
|
}
|
|
|
|
|
|
-.chapter-words {
|
|
|
+.slider-range text {
|
|
|
font-size: 22rpx;
|
|
|
color: #9ca3af;
|
|
|
}
|
|
|
|
|
|
-.chapter-desc {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #6b7280;
|
|
|
-}
|
|
|
-
|
|
|
-/* 角色列表 */
|
|
|
-.characters-list {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.character-card {
|
|
|
- display: flex;
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 16rpx;
|
|
|
- padding: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.char-avatar {
|
|
|
- font-size: 64rpx;
|
|
|
- margin-right: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.char-info {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.char-name {
|
|
|
- font-size: 30rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.char-detail {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #6b7280;
|
|
|
- display: block;
|
|
|
- margin-bottom: 4rpx;
|
|
|
-}
|
|
|
-
|
|
|
-/* 生成进度 */
|
|
|
+/* 进度 */
|
|
|
.progress-section {
|
|
|
background: #f9fafb;
|
|
|
border-radius: 12rpx;
|
|
|
@@ -832,40 +555,30 @@ function useContent() {
|
|
|
color: #6b7280;
|
|
|
}
|
|
|
|
|
|
-/* 生成的内容 */
|
|
|
-.generated-content {
|
|
|
- max-height: 500rpx;
|
|
|
+/* 结果 */
|
|
|
+.result-content {
|
|
|
+ background: #f9fafb;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 24rpx;
|
|
|
+ max-height: 600rpx;
|
|
|
overflow-y: auto;
|
|
|
- margin-bottom: 24rpx;
|
|
|
}
|
|
|
|
|
|
-.content-chapter {
|
|
|
- display: flex;
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 20rpx;
|
|
|
- margin-bottom: 16rpx;
|
|
|
+.content-text {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #374151;
|
|
|
+ line-height: 1.8;
|
|
|
+ white-space: pre-wrap;
|
|
|
}
|
|
|
|
|
|
-.chapter-badge {
|
|
|
- width: 40rpx;
|
|
|
- height: 40rpx;
|
|
|
- background: #4F46E5;
|
|
|
- color: #fff;
|
|
|
- border-radius: 50%;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 22rpx;
|
|
|
- margin-right: 16rpx;
|
|
|
- flex-shrink: 0;
|
|
|
+.result-footer {
|
|
|
+ margin-top: 16rpx;
|
|
|
+ text-align: right;
|
|
|
}
|
|
|
|
|
|
-.chapter-text {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #374151;
|
|
|
- line-height: 1.6;
|
|
|
- white-space: pre-wrap;
|
|
|
+.word-count {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #9ca3af;
|
|
|
}
|
|
|
|
|
|
/* 质量检测 */
|
|
|
@@ -900,6 +613,11 @@ function useContent() {
|
|
|
color: #059669;
|
|
|
}
|
|
|
|
|
|
+.quality-status.fail {
|
|
|
+ background: #fee2e2;
|
|
|
+ color: #dc2626;
|
|
|
+}
|
|
|
+
|
|
|
.quality-score {
|
|
|
font-size: 32rpx;
|
|
|
font-weight: 700;
|
|
|
@@ -966,7 +684,7 @@ function useContent() {
|
|
|
margin-top: 24rpx;
|
|
|
}
|
|
|
|
|
|
-.next-btn, .primary-btn {
|
|
|
+.generate-btn, .primary-btn {
|
|
|
flex: 1;
|
|
|
height: 88rpx;
|
|
|
background: linear-gradient(135deg, #4f46e5 0%, #818cf8 100%);
|
|
|
@@ -980,6 +698,11 @@ function useContent() {
|
|
|
color: #fff;
|
|
|
}
|
|
|
|
|
|
+.generate-btn[disabled] {
|
|
|
+ background: #e5e7eb;
|
|
|
+ color: #9ca3af;
|
|
|
+}
|
|
|
+
|
|
|
.back-btn {
|
|
|
flex: 1;
|
|
|
height: 88rpx;
|
|
|
@@ -993,34 +716,8 @@ function useContent() {
|
|
|
color: #6b7280;
|
|
|
}
|
|
|
|
|
|
-.generate-btn {
|
|
|
- flex: 1;
|
|
|
- height: 88rpx;
|
|
|
- background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
|
|
- border-radius: 16rpx;
|
|
|
+.generate-btn::after, .back-btn::after, .primary-btn::after {
|
|
|
border: none;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.generate-btn[disabled] {
|
|
|
- background: #e5e7eb;
|
|
|
- color: #9ca3af;
|
|
|
-}
|
|
|
-
|
|
|
-.next-btn::after, .back-btn::after, .generate-btn::after, .primary-btn::after {
|
|
|
- border: none;
|
|
|
-}
|
|
|
-
|
|
|
-.empty-state, .loading-state {
|
|
|
- text-align: center;
|
|
|
- padding: 48rpx;
|
|
|
- color: #9ca3af;
|
|
|
- font-size: 28rpx;
|
|
|
}
|
|
|
|
|
|
/* 弹窗 */
|
|
|
@@ -1039,9 +736,11 @@ function useContent() {
|
|
|
|
|
|
.modal-content {
|
|
|
width: 600rpx;
|
|
|
+ max-height: 80vh;
|
|
|
background: #fff;
|
|
|
border-radius: 24rpx;
|
|
|
padding: 32rpx;
|
|
|
+ overflow-y: auto;
|
|
|
}
|
|
|
|
|
|
.modal-header {
|
|
|
@@ -1064,12 +763,20 @@ function useContent() {
|
|
|
.modal-body {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- gap: 16rpx;
|
|
|
+ gap: 8rpx;
|
|
|
}
|
|
|
|
|
|
-.help-text {
|
|
|
+.help-title {
|
|
|
font-size: 28rpx;
|
|
|
- color: #374151;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1f2937;
|
|
|
+ margin-top: 16rpx;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.help-text {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #6b7280;
|
|
|
line-height: 1.6;
|
|
|
}
|
|
|
</style>
|