- 新增 src/config/paradigms.js:范式配置库,包含专家指令和评价量表 - 实现范式预设填充:点击卡片自动注入参考案例、标签、约束 - WriterPanel.vue:新增「专家指令」只读展示区(金色高亮) - AnalysisPanel.vue:使用统一的范式配置,调用 loadParadigmPreset - appStore.js:新增 activeParadigm、expertGuidelines、qualityReport 状态 - 支持民主生活会对照检查等6种范式的完整专家标准
231 lines
7.9 KiB
Vue
231 lines
7.9 KiB
Vue
<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>
|