Files
ai-write/src/components/AnalysisPanel.vue
empty 67758a7fff feat: 实现范式库到工作台的数据流转机制
- 新增 src/config/paradigms.js:范式配置库,包含专家指令和评价量表
- 实现范式预设填充:点击卡片自动注入参考案例、标签、约束
- WriterPanel.vue:新增「专家指令」只读展示区(金色高亮)
- AnalysisPanel.vue:使用统一的范式配置,调用 loadParadigmPreset
- appStore.js:新增 activeParadigm、expertGuidelines、qualityReport 状态
- 支持民主生活会对照检查等6种范式的完整专家标准
2026-01-08 11:49:29 +08:00

231 lines
7.9 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<aside class="w-[400px] flex flex-col border-r border-slate-700 bg-slate-800 shrink-0 h-screen">
<!-- 头部 -->
<header class="p-4 border-b border-slate-700 flex items-center justify-between shrink-0">
<div class="flex items-center gap-4">
<h1 class="font-bold text-lg text-white flex items-center gap-2">
<span class="text-2xl"></span> 写作范式分析
</h1>
<button
@click="switchPage('writer')"
class="text-xs px-2 py-1 rounded bg-slate-700 text-slate-300 hover:bg-slate-600 transition"
>
返回写作
</button>
</div>
<span class="text-xs px-2 py-1 rounded bg-blue-900 text-blue-300 border border-blue-700">Pro版</span>
</header>
<!-- 内容区 - 添加 min-h-0 确保滚动正常 -->
<div class="flex-1 overflow-y-auto p-4 space-y-6 min-h-0">
<!-- 写作范式库 -->
<section>
<h3 class="text-sm font-medium text-slate-400 mb-4">📚 写作范式库</h3>
<div class="space-y-3">
<div
v-for="paradigm in paradigms"
:key="paradigm.id"
@click="selectParadigm(paradigm)"
:class="['bg-slate-700/50 rounded-lg p-4 border transition cursor-pointer',
selectedParadigm?.id === paradigm.id
? 'border-blue-500 bg-blue-900/20'
: 'border-slate-600 hover:border-blue-500']"
>
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium text-white">{{ paradigm.icon }} {{ paradigm.name }}</h4>
<button
v-if="selectedParadigm?.id === paradigm.id"
@click.stop="applyParadigm(paradigm)"
class="text-xs px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-500 transition"
>
应用到写作
</button>
</div>
<p class="text-xs text-slate-400 mb-2">{{ paradigm.description }}</p>
<div class="flex flex-wrap gap-1">
<span
v-for="tag in paradigm.tags"
:key="tag"
:class="['text-xs px-2 py-1 rounded', paradigm.tagClass]"
>
{{ tag }}
</span>
</div>
</div>
</div>
</section>
<!-- 范式分析工具 -->
<section>
<h3 class="text-sm font-medium text-slate-400 mb-4">🔍 范式分析工具</h3>
<textarea
v-model="analysisText"
class="w-full h-32 bg-slate-900 border border-slate-700 rounded-lg p-3 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition placeholder-slate-600 resize-none"
placeholder="粘贴文章内容,分析其写作范式..."
></textarea>
<div class="mt-2 flex gap-2">
<button
@click="analyzeArticle"
:disabled="isAnalyzing || !analysisText"
class="flex-1 bg-blue-600 hover:bg-blue-500 text-white py-2 rounded-lg text-sm font-medium transition disabled:opacity-50 disabled:cursor-not-allowed"
>
{{ isAnalyzing ? '正在分析...' : '分析文章范式' }}
</button>
<button
@click="clearAnalysis"
class="px-4 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded-lg text-sm font-medium transition"
>
清空
</button>
</div>
</section>
<!-- 分析历史 -->
<section v-if="analysisHistory.length > 0">
<h3 class="text-sm font-medium text-slate-400 mb-4">📝 分析历史</h3>
<div class="space-y-2">
<div
v-for="(item, index) in analysisHistory"
:key="index"
@click="loadHistoryItem(item)"
class="bg-slate-700/30 rounded p-3 cursor-pointer hover:bg-slate-700/50 transition text-xs"
>
<div class="flex justify-between items-center">
<span class="text-slate-300">{{ item.paradigm }}</span>
<span class="text-slate-500">{{ formatDate(item.timestamp) }}</span>
</div>
<p class="text-slate-500 mt-1 truncate">{{ item.preview }}</p>
</div>
</div>
</section>
</div>
</aside>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useAppStore } from '../stores/app'
import DeepSeekAPI from '../api/deepseek.js'
import { getParadigmList } from '../config/paradigms.js'
const appStore = useAppStore()
const { analysisText, isAnalyzing } = storeToRefs(appStore)
// 选中的范式
const selectedParadigm = ref(null)
// 分析历史
const analysisHistory = ref([])
// 从配置文件获取范式列表
const paradigms = getParadigmList()
// 选择范式
const selectParadigm = (paradigm) => {
selectedParadigm.value = paradigm
}
// 应用范式到写作(使用新的 loadParadigmPreset 方法)
const applyParadigm = (paradigm) => {
appStore.loadParadigmPreset(paradigm.id)
// 显示提示
alert(`已应用"${paradigm.name}"到写作任务`)
}
// 分析文章
const analyzeArticle = async () => {
try {
const result = await appStore.analyzeArticleAction(analysisText.value, detectParadigm)
// 添加到历史记录
addToHistory(result.paradigm, analysisText.value, result.content)
} catch (error) {
alert(error.message)
}
}
// 检测文章范式
const detectParadigm = (analysis) => {
const text = analysis.toLowerCase()
if (text.includes('民主生活会') || text.includes('对照检查') || text.includes('整改') || text.includes('党性')) {
return paradigms[4] // 民主生活会对照检查
} else if (text.includes('政府') || text.includes('工作报告') || text.includes('述职')) {
return paradigms[5] // 政府工作报告
} else if (text.includes('技术') || text.includes('代码') || text.includes('编程')) {
return paradigms[0] // 技术博客
} else if (text.includes('商业') || text.includes('市场') || text.includes('数据分析')) {
return paradigms[1] // 商业分析
} else if (text.includes('产品') || text.includes('营销') || text.includes('用户')) {
return paradigms[2] // 产品文案
} else if (text.includes('学术') || text.includes('研究') || text.includes('文献')) {
return paradigms[3] // 学术论文
}
return paradigms[0] // 默认技术博客
}
// 添加到历史记录
const addToHistory = (paradigm, text, analysis) => {
const historyItem = {
paradigm,
text,
analysis,
preview: text.substring(0, 50) + '...',
timestamp: new Date()
}
// 限制历史记录数量
analysisHistory.value.unshift(historyItem)
if (analysisHistory.value.length > 10) {
analysisHistory.value = analysisHistory.value.slice(0, 10)
}
// 保存到本地存储
localStorage.setItem('analysisHistory', JSON.stringify(analysisHistory.value))
}
// 加载历史记录项
const loadHistoryItem = (item) => {
analysisText.value = item.text
appStore.analysisResult = {
paradigm: item.paradigm,
analysis: item.analysis,
timestamp: item.timestamp
}
}
// 清空分析
const clearAnalysis = () => {
analysisText.value = ''
appStore.analysisResult = null
selectedParadigm.value = null
}
// 格式化日期
const formatDate = (date) => {
const now = new Date()
const diff = now - new Date(date)
const minutes = Math.floor(diff / 60000)
const hours = Math.floor(diff / 3600000)
const days = Math.floor(diff / 86400000)
if (minutes < 1) return '刚刚'
if (minutes < 60) return `${minutes}分钟前`
if (hours < 24) return `${hours}小时前`
if (days < 7) return `${days}天前`
return new Date(date).toLocaleDateString()
}
// 组件挂载时加载历史记录
onMounted(() => {
const saved = localStorage.getItem('analysisHistory')
if (saved) {
analysisHistory.value = JSON.parse(saved)
}
})
</script>