feat: 初始化AI写作工坊项目
- 实现基于Vue 3 + Vite的模块化架构 - 集成DeepSeek API进行内容生成 - 支持写作范式分析功能 - 添加环境变量配置支持 - 完整的项目结构和文档
This commit is contained in:
302
src/components/AnalysisPanel.vue
Normal file
302
src/components/AnalysisPanel.vue
Normal file
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<aside class="w-[400px] flex flex-col border-r border-slate-700 bg-slate-800 shrink-0">
|
||||
<!-- 头部 -->
|
||||
<header class="p-4 border-b border-slate-700 flex items-center justify-between">
|
||||
<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>
|
||||
|
||||
<!-- 内容区 -->
|
||||
<div class="flex-1 overflow-y-auto p-4 space-y-6">
|
||||
<!-- 写作范式库 -->
|
||||
<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.type"
|
||||
@click="selectParadigm(paradigm)"
|
||||
:class="['bg-slate-700/50 rounded-lg p-4 border transition cursor-pointer',
|
||||
selectedParadigm?.type === paradigm.type
|
||||
? '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?.type === paradigm.type"
|
||||
@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'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const { analysisText, isAnalyzing, selectedTags, inputTask } = storeToRefs(appStore)
|
||||
|
||||
// 选中的范式
|
||||
const selectedParadigm = ref(null)
|
||||
|
||||
// 分析历史
|
||||
const analysisHistory = ref([])
|
||||
|
||||
const paradigms = [
|
||||
{
|
||||
type: 'tech',
|
||||
name: '技术博客范式',
|
||||
icon: '📝',
|
||||
description: '适用于技术分享、教程类文章',
|
||||
tags: ['问题引入', '解决方案', '代码示例', '总结'],
|
||||
tagClass: 'bg-blue-900/30 text-blue-300',
|
||||
prompt: '请按照技术博客的范式写作,包含:1) 问题背景引入 2) 具体解决方案 3) 代码示例说明 4) 总结与最佳实践',
|
||||
constraints: ['Markdown格式', '总分总结构', '数据支撑']
|
||||
},
|
||||
{
|
||||
type: 'business',
|
||||
name: '商业分析范式',
|
||||
icon: '📊',
|
||||
description: '适用于行业分析、市场报告',
|
||||
tags: ['背景介绍', '数据支撑', '趋势分析', '建议'],
|
||||
tagClass: 'bg-green-900/30 text-green-300',
|
||||
prompt: '请按照商业分析的范式写作,包含:1) 行业背景介绍 2) 数据分析与支撑 3) 趋势预测 4) 战略建议',
|
||||
constraints: ['Markdown格式', '数据支撑', '引用权威来源']
|
||||
},
|
||||
{
|
||||
type: 'marketing',
|
||||
name: '产品文案范式',
|
||||
icon: '🎯',
|
||||
description: '适用于产品介绍、营销文案',
|
||||
tags: ['痛点切入', '价值主张', '功能亮点', '行动号召'],
|
||||
tagClass: 'bg-purple-900/30 text-purple-300',
|
||||
prompt: '请按照产品文案的范式写作,包含:1) 用户痛点切入 2) 核心价值主张 3) 产品功能亮点 4) 明确的行动号召',
|
||||
constraints: ['语气幽默', '严禁被动语态']
|
||||
},
|
||||
{
|
||||
type: 'academic',
|
||||
name: '学术论文范式',
|
||||
icon: '📖',
|
||||
description: '适用于学术写作、研究报告',
|
||||
tags: ['摘要', '引言', '文献综述', '方法论', '结果'],
|
||||
tagClass: 'bg-orange-900/30 text-orange-300',
|
||||
prompt: '请按照学术论文的范式写作,包含:1) 摘要 2) 引言 3) 文献综述 4) 研究方法论 5) 结果与讨论 6) 结论',
|
||||
constraints: ['引用权威来源', '严禁被动语态']
|
||||
}
|
||||
]
|
||||
|
||||
// 选择范式
|
||||
const selectParadigm = (paradigm) => {
|
||||
selectedParadigm.value = paradigm
|
||||
}
|
||||
|
||||
// 应用范式到写作
|
||||
const applyParadigm = (paradigm) => {
|
||||
// 更新写作任务
|
||||
inputTask.value = paradigm.prompt
|
||||
|
||||
// 更新选中的约束标签
|
||||
selectedTags.value = [...paradigm.constraints]
|
||||
|
||||
// 切换到写作页面
|
||||
appStore.switchPage('writer')
|
||||
|
||||
// 显示提示
|
||||
alert(`已应用"${paradigm.name}"到写作任务`)
|
||||
}
|
||||
|
||||
// 分析文章
|
||||
const analyzeArticle = async () => {
|
||||
if (!analysisText.value.trim()) {
|
||||
alert('请输入要分析的文章内容')
|
||||
return
|
||||
}
|
||||
|
||||
isAnalyzing.value = true
|
||||
appStore.analysisResult = null
|
||||
|
||||
try {
|
||||
const api = new DeepSeekAPI({
|
||||
url: appStore.apiUrl,
|
||||
key: appStore.apiKey
|
||||
})
|
||||
|
||||
const result = await api.analyzeContent(analysisText.value)
|
||||
|
||||
// 解析分析结果,提取范式类型
|
||||
const analysisContent = result.choices[0].message.content
|
||||
const detectedParadigm = detectParadigm(analysisContent)
|
||||
|
||||
appStore.analysisResult = {
|
||||
paradigm: detectedParadigm.name,
|
||||
paradigmType: detectedParadigm.type,
|
||||
analysis: analysisContent,
|
||||
timestamp: new Date()
|
||||
}
|
||||
|
||||
// 添加到历史记录
|
||||
addToHistory(detectedParadigm.name, analysisText.value, analysisContent)
|
||||
} catch (error) {
|
||||
appStore.analysisResult = {
|
||||
error: true,
|
||||
message: error.message
|
||||
}
|
||||
} finally {
|
||||
isAnalyzing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 检测文章范式
|
||||
const detectParadigm = (analysis) => {
|
||||
const text = analysis.toLowerCase()
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user