feat: 添加文稿管理、素材库、设置页面及对照检查重写功能
- 新增 DocumentsPanel.vue 文稿管理页面 - 新增 MaterialsPanel.vue 素材库管理页面 - 新增 SettingsPanel.vue 设置页面 - 新增 DocumentSelectorModal.vue 文稿选择弹窗 - 新增 MaterialSelectorModal.vue 素材选择弹窗 - 集成 SQLite 数据库持久化 (sql.js) - 对照检查页面支持从文稿库选取内容 - 对照检查页面新增一键重写及差异对比功能 - 修复对照检查页面布局问题 - MainContent 支持文稿编辑功能
This commit is contained in:
@@ -20,7 +20,15 @@
|
||||
<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="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-medium text-slate-400">📚 写作范式库</h3>
|
||||
<button
|
||||
@click="openAddModal"
|
||||
class="text-xs px-2 py-1 bg-green-600 text-white rounded hover:bg-green-500 transition flex items-center gap-1"
|
||||
>
|
||||
<span>+</span> 新增范式
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
@@ -38,14 +46,34 @@
|
||||
<span v-if="paradigm.isNew" class="text-[10px] px-1.5 py-0.5 rounded bg-orange-500 text-white font-bold animate-pulse">
|
||||
NEW
|
||||
</span>
|
||||
<span v-if="paradigm.isCustom" class="text-[10px] px-1.5 py-0.5 rounded bg-purple-500 text-white font-bold">
|
||||
自定义
|
||||
</span>
|
||||
</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 class="flex items-center gap-1">
|
||||
<button
|
||||
@click.stop="openEditModal(paradigm)"
|
||||
class="text-xs px-2 py-1 bg-slate-600 text-white rounded hover:bg-slate-500 transition"
|
||||
title="编辑范式"
|
||||
>
|
||||
✏️
|
||||
</button>
|
||||
<button
|
||||
v-if="paradigm.isCustom"
|
||||
@click.stop="deleteParadigm(paradigm)"
|
||||
class="text-xs px-2 py-1 bg-red-600 text-white rounded hover:bg-red-500 transition"
|
||||
title="删除范式"
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
<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>
|
||||
</div>
|
||||
<p class="text-xs text-slate-400 mb-2">{{ paradigm.description }}</p>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@@ -105,27 +133,628 @@
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 编辑/新增范式弹窗 -->
|
||||
<div
|
||||
v-if="showEditModal"
|
||||
class="fixed inset-0 bg-black/60 flex items-center justify-center z-50"
|
||||
@click.self="closeEditModal"
|
||||
>
|
||||
<div class="bg-slate-800 rounded-lg w-[500px] max-h-[80vh] overflow-y-auto border border-slate-600 shadow-xl">
|
||||
<div class="p-4 border-b border-slate-700 flex items-center justify-between">
|
||||
<h3 class="font-medium text-white">{{ isAddMode ? '新增写作范式' : '编辑写作范式' }}</h3>
|
||||
<button @click="closeEditModal" class="text-slate-400 hover:text-white transition">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 space-y-4">
|
||||
<!-- 图标选择 -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">图标</label>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<button
|
||||
v-for="icon in iconOptions"
|
||||
:key="icon"
|
||||
@click="editForm.icon = icon"
|
||||
:class="['w-10 h-10 rounded-lg text-xl flex items-center justify-center transition',
|
||||
editForm.icon === icon ? 'bg-blue-600' : 'bg-slate-700 hover:bg-slate-600']"
|
||||
>
|
||||
{{ icon }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 名称 -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">范式名称</label>
|
||||
<input
|
||||
v-model="editForm.name"
|
||||
class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition"
|
||||
placeholder="如:技术博客范式"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 描述 -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">描述</label>
|
||||
<textarea
|
||||
v-model="editForm.description"
|
||||
rows="2"
|
||||
class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition resize-none"
|
||||
placeholder="简要描述此范式的适用场景"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">标签(逗号分隔)</label>
|
||||
<input
|
||||
v-model="editForm.tagsInput"
|
||||
class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition"
|
||||
placeholder="如:问题引入,解决方案,代码示例"
|
||||
/>
|
||||
<div class="flex flex-wrap gap-1 mt-2" v-if="editForm.tagsInput">
|
||||
<span
|
||||
v-for="tag in editForm.tagsInput.split(',').map(t => t.trim()).filter(t => t)"
|
||||
:key="tag"
|
||||
:class="['text-xs px-2 py-1 rounded', editForm.tagClass]"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 标签颜色 -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">标签颜色</label>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<button
|
||||
v-for="color in colorOptions"
|
||||
:key="color.class"
|
||||
@click="editForm.tagClass = color.class"
|
||||
:class="['px-3 py-1 rounded text-xs transition', color.class,
|
||||
editForm.tagClass === color.class ? 'ring-2 ring-white' : '']"
|
||||
>
|
||||
{{ color.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 写作约束(高级) -->
|
||||
<div>
|
||||
<label class="block text-sm text-slate-400 mb-2">写作约束(每行一条)</label>
|
||||
<textarea
|
||||
v-model="editForm.constraintsInput"
|
||||
rows="4"
|
||||
class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition resize-none font-mono"
|
||||
placeholder="如: 开篇必须明确阐述问题 结尾需总结核心要点"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- 维度配置(新架构) -->
|
||||
<div class="border-t border-slate-700 pt-4 mt-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm text-slate-400 font-medium">📐 维度配置(高级)</label>
|
||||
<button
|
||||
@click="toggleDimensionEditor"
|
||||
class="text-xs text-blue-400 hover:text-blue-300"
|
||||
>
|
||||
{{ showDimensionEditor ? '收起' : '展开' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="showDimensionEditor" class="space-y-3">
|
||||
<!-- 维度集选择 -->
|
||||
<div>
|
||||
<label class="block text-xs text-slate-500 mb-1">选择维度集模板</label>
|
||||
<select
|
||||
v-model="editForm.dimensionSetId"
|
||||
@change="onDimensionSetChange"
|
||||
class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
>
|
||||
<option :value="null">不使用维度集</option>
|
||||
<option v-for="ds in dimensionSetOptions" :key="ds.id" :value="ds.id">
|
||||
{{ ds.name }} - {{ ds.description }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 维度列表编辑 -->
|
||||
<div v-if="editForm.dimensions.length > 0">
|
||||
<label class="block text-xs text-slate-500 mb-2">维度列表(可编辑)</label>
|
||||
<div class="space-y-2 max-h-60 overflow-y-auto">
|
||||
<div
|
||||
v-for="(dim, index) in editForm.dimensions"
|
||||
:key="dim.id || index"
|
||||
class="bg-slate-900/50 rounded p-2 border border-slate-700"
|
||||
>
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<input
|
||||
v-model="dim.name"
|
||||
class="flex-1 bg-slate-800 border border-slate-600 rounded px-2 py-1 text-xs"
|
||||
placeholder="维度名称"
|
||||
/>
|
||||
<button
|
||||
@click="removeDimension(index)"
|
||||
class="text-red-400 hover:text-red-300 text-xs px-1"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="dim.focus"
|
||||
class="w-full bg-slate-800 border border-slate-600 rounded px-2 py-1 text-xs text-slate-400"
|
||||
placeholder="关注重点(如:理想信念、宗旨意识)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="addDimension"
|
||||
class="mt-2 text-xs text-green-400 hover:text-green-300 flex items-center gap-1"
|
||||
>
|
||||
<span>+</span> 添加维度
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 快速添加维度 -->
|
||||
<div v-else class="text-center py-4 text-slate-500 text-xs">
|
||||
<p>选择一个维度集模板,或手动添加维度</p>
|
||||
<button
|
||||
@click="addDimension"
|
||||
class="mt-2 text-green-400 hover:text-green-300"
|
||||
>
|
||||
+ 添加自定义维度
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 素材库配置(新增) -->
|
||||
<div class="border-t border-slate-700 pt-4 mt-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm text-slate-400 font-medium">📚 素材库配置</label>
|
||||
<button
|
||||
@click="toggleReferenceEditor"
|
||||
class="text-xs text-blue-400 hover:text-blue-300"
|
||||
>
|
||||
{{ showReferenceEditor ? '收起' : '展开' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="showReferenceEditor" class="space-y-3">
|
||||
<!-- 自动匹配开关 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-slate-500">自动匹配相关素材</span>
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" v-model="editForm.autoMatchRefs" class="sr-only peer">
|
||||
<div class="w-9 h-5 bg-slate-600 peer-focus:ring-2 peer-focus:ring-blue-500 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-blue-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- 素材类型筛选 -->
|
||||
<div>
|
||||
<label class="block text-xs text-slate-500 mb-2">手动选择素材(按类型)</label>
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<button
|
||||
v-for="type in referenceTypes"
|
||||
:key="type.id"
|
||||
@click="filterReferencesByType(type.id)"
|
||||
:class="['text-xs px-2 py-1 rounded transition',
|
||||
selectedRefType === type.id
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-slate-700 text-slate-300 hover:bg-slate-600']"
|
||||
>
|
||||
{{ type.icon }} {{ type.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 素材列表 -->
|
||||
<div class="max-h-40 overflow-y-auto space-y-1">
|
||||
<div
|
||||
v-for="ref in filteredReferences"
|
||||
:key="ref.id"
|
||||
@click="toggleReference(ref.id)"
|
||||
:class="['p-2 rounded cursor-pointer border transition text-xs',
|
||||
editForm.selectedRefs.includes(ref.id)
|
||||
? 'bg-blue-900/30 border-blue-500'
|
||||
: 'bg-slate-900/50 border-slate-700 hover:border-slate-500']"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="font-medium text-white">{{ ref.title }}</span>
|
||||
<span v-if="editForm.selectedRefs.includes(ref.id)" class="text-green-400">✓</span>
|
||||
</div>
|
||||
<div class="text-slate-500 text-[10px] mt-1">{{ ref.excerptCount }} 条可引用内容</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已选素材摘要 -->
|
||||
<div v-if="editForm.selectedRefs.length > 0" class="text-xs text-slate-400">
|
||||
已选择 {{ editForm.selectedRefs.length }} 个素材
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 border-t border-slate-700 flex justify-end gap-2">
|
||||
<button
|
||||
@click="closeEditModal"
|
||||
class="px-4 py-2 bg-slate-700 text-white rounded-lg text-sm hover:bg-slate-600 transition"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
@click="saveParadigm"
|
||||
:disabled="!editForm.name"
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm hover:bg-blue-500 transition disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{{ isAddMode ? '添加' : '保存' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, computed, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useDatabaseStore } from '../stores/database.js'
|
||||
import DeepSeekAPI from '../api/deepseek.js'
|
||||
import { getParadigmList } from '../config/paradigms.js'
|
||||
import { getParadigmList, getParadigmDimensions } from '../config/paradigms.js'
|
||||
import { getDimensionSetList, getDimensionSetById } from '../config/dimensionSets.js'
|
||||
import { getLogicParadigmList } from '../config/logicParadigms.js'
|
||||
import { REFERENCE_TYPES } from '../config/references.js'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const { analysisText, isAnalyzing } = storeToRefs(appStore)
|
||||
|
||||
// 数据库 Store
|
||||
const dbStore = useDatabaseStore()
|
||||
const { references: dbReferences, isInitialized: dbInitialized } = storeToRefs(dbStore)
|
||||
|
||||
// 选中的范式
|
||||
const selectedParadigm = ref(null)
|
||||
|
||||
// 分析历史
|
||||
const analysisHistory = ref([])
|
||||
|
||||
// 从配置文件获取范式列表
|
||||
const paradigms = getParadigmList()
|
||||
// 范式列表(响应式,支持编辑)
|
||||
const paradigms = ref([])
|
||||
|
||||
// 编辑弹窗状态
|
||||
const showEditModal = ref(false)
|
||||
const isAddMode = ref(false)
|
||||
const editingParadigmId = ref(null)
|
||||
|
||||
// 编辑表单
|
||||
const editForm = reactive({
|
||||
icon: '📝',
|
||||
name: '',
|
||||
description: '',
|
||||
tagsInput: '',
|
||||
tagClass: 'bg-blue-900/30 text-blue-300',
|
||||
constraintsInput: '',
|
||||
// 新架构:维度配置
|
||||
dimensionSetId: null,
|
||||
dimensions: [],
|
||||
// 素材库配置
|
||||
autoMatchRefs: true,
|
||||
selectedRefs: []
|
||||
})
|
||||
|
||||
// 维度编辑器状态
|
||||
const showDimensionEditor = ref(false)
|
||||
|
||||
// 素材编辑器状态
|
||||
const showReferenceEditor = ref(false)
|
||||
const selectedRefType = ref('all')
|
||||
|
||||
// 使用数据库中的素材数据
|
||||
const allReferences = computed(() => {
|
||||
return dbReferences.value.map(ref => ({
|
||||
id: ref.id,
|
||||
type: ref.type,
|
||||
title: ref.title,
|
||||
source: ref.source,
|
||||
tags: ref.tags,
|
||||
excerptCount: ref.excerpts?.length || 0
|
||||
}))
|
||||
})
|
||||
|
||||
const filteredReferences = computed(() => {
|
||||
if (selectedRefType.value === 'all') {
|
||||
return allReferences.value
|
||||
}
|
||||
return allReferences.value.filter(ref => ref.type === selectedRefType.value)
|
||||
})
|
||||
|
||||
// 素材类型选项
|
||||
const referenceTypes = [
|
||||
{ id: 'all', name: '全部', icon: '📚' },
|
||||
{ id: REFERENCE_TYPES.POLICY, name: '政策文件', icon: '📄' },
|
||||
{ id: REFERENCE_TYPES.SPEECH, name: '领导讲话', icon: '🎙️' },
|
||||
{ id: REFERENCE_TYPES.CASE, name: '典型案例', icon: '📌' },
|
||||
{ id: REFERENCE_TYPES.QUOTE, name: '金句警句', icon: '✨' },
|
||||
{ id: REFERENCE_TYPES.REGULATION, name: '党规党纪', icon: '📖' }
|
||||
]
|
||||
|
||||
// 维度集选项
|
||||
const dimensionSetOptions = getDimensionSetList()
|
||||
|
||||
// 图标选项
|
||||
const iconOptions = ['📝', '💻', '📊', '🚀', '📚', '🏛️', '🔥', '🏢', '💡', '🎯', '📋', '✨']
|
||||
|
||||
// 颜色选项
|
||||
const colorOptions = [
|
||||
{ label: '蓝色', class: 'bg-blue-900/30 text-blue-300' },
|
||||
{ label: '绿色', class: 'bg-green-900/30 text-green-300' },
|
||||
{ label: '红色', class: 'bg-red-900/30 text-red-300' },
|
||||
{ label: '紫色', class: 'bg-purple-900/30 text-purple-300' },
|
||||
{ label: '橙色', class: 'bg-orange-900/30 text-orange-300' },
|
||||
{ label: '青色', class: 'bg-cyan-900/30 text-cyan-300' }
|
||||
]
|
||||
|
||||
// 初始化范式列表
|
||||
const initParadigms = () => {
|
||||
// 先加载默认范式
|
||||
const defaultParadigms = getParadigmList()
|
||||
|
||||
// 从本地存储加载自定义修改
|
||||
const savedCustomizations = localStorage.getItem('paradigmCustomizations')
|
||||
const customizations = savedCustomizations ? JSON.parse(savedCustomizations) : {}
|
||||
|
||||
// 从本地存储加载自定义范式
|
||||
const savedCustomParadigms = localStorage.getItem('customParadigms')
|
||||
const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : []
|
||||
|
||||
// 合并默认范式和自定义修改
|
||||
const mergedParadigms = defaultParadigms.map(p => {
|
||||
if (customizations[p.id]) {
|
||||
return { ...p, ...customizations[p.id] }
|
||||
}
|
||||
return p
|
||||
})
|
||||
|
||||
// 添加自定义范式
|
||||
paradigms.value = [...mergedParadigms, ...customParadigms]
|
||||
}
|
||||
|
||||
// 打开新增弹窗
|
||||
const openAddModal = () => {
|
||||
isAddMode.value = true
|
||||
editingParadigmId.value = null
|
||||
resetEditForm()
|
||||
showEditModal.value = true
|
||||
}
|
||||
|
||||
// 打开编辑弹窗
|
||||
const openEditModal = (paradigm) => {
|
||||
isAddMode.value = false
|
||||
editingParadigmId.value = paradigm.id
|
||||
|
||||
editForm.icon = paradigm.icon || '📝'
|
||||
editForm.name = paradigm.name || ''
|
||||
editForm.description = paradigm.description || ''
|
||||
editForm.tagsInput = (paradigm.tags || []).join(', ')
|
||||
editForm.tagClass = paradigm.tagClass || 'bg-blue-900/30 text-blue-300'
|
||||
editForm.constraintsInput = (paradigm.systemConstraints || []).join('\n')
|
||||
|
||||
// 加载维度配置
|
||||
editForm.dimensionSetId = paradigm.dimensionSetId || null
|
||||
if (paradigm.dimensionSetId) {
|
||||
const dimSet = getDimensionSetById(paradigm.dimensionSetId)
|
||||
editForm.dimensions = dimSet ? JSON.parse(JSON.stringify(dimSet.dimensions)) : []
|
||||
} else if (paradigm.customDimensions) {
|
||||
editForm.dimensions = JSON.parse(JSON.stringify(paradigm.customDimensions))
|
||||
} else {
|
||||
editForm.dimensions = []
|
||||
}
|
||||
|
||||
// 加载素材配置
|
||||
editForm.autoMatchRefs = paradigm.autoMatchRefs !== false // 默认为true
|
||||
editForm.selectedRefs = paradigm.selectedRefs ? [...paradigm.selectedRefs] : []
|
||||
|
||||
showDimensionEditor.value = false
|
||||
showReferenceEditor.value = false
|
||||
showEditModal.value = true
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
const closeEditModal = () => {
|
||||
showEditModal.value = false
|
||||
resetEditForm()
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetEditForm = () => {
|
||||
editForm.icon = '📝'
|
||||
editForm.name = ''
|
||||
editForm.description = ''
|
||||
editForm.tagsInput = ''
|
||||
editForm.tagClass = 'bg-blue-900/30 text-blue-300'
|
||||
editForm.constraintsInput = ''
|
||||
editForm.dimensionSetId = null
|
||||
editForm.dimensions = []
|
||||
// 素材配置重置
|
||||
editForm.autoMatchRefs = true
|
||||
editForm.selectedRefs = []
|
||||
showDimensionEditor.value = false
|
||||
showReferenceEditor.value = false
|
||||
}
|
||||
|
||||
// 切换维度编辑器显示
|
||||
const toggleDimensionEditor = () => {
|
||||
showDimensionEditor.value = !showDimensionEditor.value
|
||||
}
|
||||
|
||||
// 维度集变更时更新维度列表
|
||||
const onDimensionSetChange = () => {
|
||||
if (editForm.dimensionSetId) {
|
||||
const dimSet = getDimensionSetById(editForm.dimensionSetId)
|
||||
if (dimSet) {
|
||||
// 深拷贝维度列表,允许用户编辑
|
||||
editForm.dimensions = JSON.parse(JSON.stringify(dimSet.dimensions))
|
||||
}
|
||||
} else {
|
||||
editForm.dimensions = []
|
||||
}
|
||||
}
|
||||
|
||||
// 添加自定义维度
|
||||
const addDimension = () => {
|
||||
editForm.dimensions.push({
|
||||
id: `custom-dim-${Date.now()}`,
|
||||
name: '',
|
||||
focus: ''
|
||||
})
|
||||
}
|
||||
|
||||
// 删除维度
|
||||
const removeDimension = (index) => {
|
||||
editForm.dimensions.splice(index, 1)
|
||||
}
|
||||
|
||||
// 切换素材编辑器显示
|
||||
const toggleReferenceEditor = () => {
|
||||
showReferenceEditor.value = !showReferenceEditor.value
|
||||
}
|
||||
|
||||
// 按类型筛选素材(通过修改selectedRefType触发computed重新计算)
|
||||
const filterReferencesByType = (typeId) => {
|
||||
selectedRefType.value = typeId
|
||||
}
|
||||
|
||||
// 切换素材选中状态
|
||||
const toggleReference = (refId) => {
|
||||
const index = editForm.selectedRefs.indexOf(refId)
|
||||
if (index === -1) {
|
||||
editForm.selectedRefs.push(refId)
|
||||
} else {
|
||||
editForm.selectedRefs.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 保存范式
|
||||
const saveParadigm = () => {
|
||||
const tags = editForm.tagsInput.split(',').map(t => t.trim()).filter(t => t)
|
||||
const constraints = editForm.constraintsInput.split('\n').map(c => c.trim()).filter(c => c)
|
||||
|
||||
// 处理维度配置
|
||||
const dimensionConfig = {
|
||||
dimensionSetId: editForm.dimensionSetId,
|
||||
customDimensions: editForm.dimensions.length > 0 ? editForm.dimensions : null
|
||||
}
|
||||
|
||||
// 处理素材配置
|
||||
const referenceConfig = {
|
||||
autoMatchRefs: editForm.autoMatchRefs,
|
||||
selectedRefs: editForm.selectedRefs
|
||||
}
|
||||
|
||||
if (isAddMode.value) {
|
||||
// 新增自定义范式
|
||||
const newParadigm = {
|
||||
id: `custom-${Date.now()}`,
|
||||
icon: editForm.icon,
|
||||
name: editForm.name,
|
||||
description: editForm.description,
|
||||
tags,
|
||||
tagClass: editForm.tagClass,
|
||||
systemConstraints: constraints,
|
||||
isCustom: true,
|
||||
// 新架构:维度配置
|
||||
...dimensionConfig,
|
||||
// 素材配置
|
||||
...referenceConfig,
|
||||
logicParadigms: {
|
||||
problemSection: 'progressive-problem',
|
||||
analysisSection: 'deep-attribution',
|
||||
remediationSection: 'remediation'
|
||||
}
|
||||
}
|
||||
|
||||
paradigms.value.push(newParadigm)
|
||||
|
||||
// 保存到本地存储
|
||||
const savedCustomParadigms = localStorage.getItem('customParadigms')
|
||||
const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : []
|
||||
customParadigms.push(newParadigm)
|
||||
localStorage.setItem('customParadigms', JSON.stringify(customParadigms))
|
||||
} else {
|
||||
// 编辑现有范式
|
||||
const index = paradigms.value.findIndex(p => p.id === editingParadigmId.value)
|
||||
if (index !== -1) {
|
||||
const updatedParadigm = {
|
||||
...paradigms.value[index],
|
||||
icon: editForm.icon,
|
||||
name: editForm.name,
|
||||
description: editForm.description,
|
||||
tags,
|
||||
tagClass: editForm.tagClass,
|
||||
systemConstraints: constraints,
|
||||
// 新架构:维度配置
|
||||
...dimensionConfig,
|
||||
// 素材配置
|
||||
...referenceConfig
|
||||
}
|
||||
|
||||
paradigms.value[index] = updatedParadigm
|
||||
|
||||
// 根据是否是自定义范式决定存储位置
|
||||
if (updatedParadigm.isCustom) {
|
||||
// 更新自定义范式
|
||||
const savedCustomParadigms = localStorage.getItem('customParadigms')
|
||||
const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : []
|
||||
const customIndex = customParadigms.findIndex(p => p.id === editingParadigmId.value)
|
||||
if (customIndex !== -1) {
|
||||
customParadigms[customIndex] = updatedParadigm
|
||||
localStorage.setItem('customParadigms', JSON.stringify(customParadigms))
|
||||
}
|
||||
} else {
|
||||
// 保存对默认范式的自定义修改
|
||||
const savedCustomizations = localStorage.getItem('paradigmCustomizations')
|
||||
const customizations = savedCustomizations ? JSON.parse(savedCustomizations) : {}
|
||||
customizations[editingParadigmId.value] = {
|
||||
icon: editForm.icon,
|
||||
name: editForm.name,
|
||||
description: editForm.description,
|
||||
tags,
|
||||
tagClass: editForm.tagClass,
|
||||
systemConstraints: constraints,
|
||||
// 新架构:维度配置
|
||||
...dimensionConfig,
|
||||
// 素材配置
|
||||
...referenceConfig
|
||||
}
|
||||
localStorage.setItem('paradigmCustomizations', JSON.stringify(customizations))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closeEditModal()
|
||||
}
|
||||
|
||||
// 删除自定义范式
|
||||
const deleteParadigm = (paradigm) => {
|
||||
if (!paradigm.isCustom) return
|
||||
|
||||
if (!confirm(`确定要删除"${paradigm.name}"吗?`)) return
|
||||
|
||||
// 从列表中移除
|
||||
paradigms.value = paradigms.value.filter(p => p.id !== paradigm.id)
|
||||
|
||||
// 从本地存储中移除
|
||||
const savedCustomParadigms = localStorage.getItem('customParadigms')
|
||||
const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : []
|
||||
const filtered = customParadigms.filter(p => p.id !== paradigm.id)
|
||||
localStorage.setItem('customParadigms', JSON.stringify(filtered))
|
||||
|
||||
// 如果正在选中该范式,清除选中状态
|
||||
if (selectedParadigm.value?.id === paradigm.id) {
|
||||
selectedParadigm.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 选择范式
|
||||
const selectParadigm = (paradigm) => {
|
||||
@@ -154,22 +783,26 @@ const analyzeArticle = async () => {
|
||||
// 检测文章范式
|
||||
const detectParadigm = (analysis) => {
|
||||
const text = analysis.toLowerCase()
|
||||
const list = paradigms.value
|
||||
|
||||
// 根据 id 查找范式
|
||||
const findById = (id) => list.find(p => p.id === id)
|
||||
|
||||
if (text.includes('民主生活会') || text.includes('对照检查') || text.includes('整改') || text.includes('党性')) {
|
||||
return paradigms[4] // 民主生活会对照检查
|
||||
return findById('party-review') || list[0]
|
||||
} else if (text.includes('政府') || text.includes('工作报告') || text.includes('述职')) {
|
||||
return paradigms[5] // 政府工作报告
|
||||
return findById('gov-report') || list[0]
|
||||
} else if (text.includes('技术') || text.includes('代码') || text.includes('编程')) {
|
||||
return paradigms[0] // 技术博客
|
||||
return findById('tech') || list[0]
|
||||
} else if (text.includes('商业') || text.includes('市场') || text.includes('数据分析')) {
|
||||
return paradigms[1] // 商业分析
|
||||
return findById('business') || list[0]
|
||||
} else if (text.includes('产品') || text.includes('营销') || text.includes('用户')) {
|
||||
return paradigms[2] // 产品文案
|
||||
return findById('marketing') || list[0]
|
||||
} else if (text.includes('学术') || text.includes('研究') || text.includes('文献')) {
|
||||
return paradigms[3] // 学术论文
|
||||
return findById('academic') || list[0]
|
||||
}
|
||||
|
||||
return paradigms[0] // 默认技术博客
|
||||
return list[0] // 默认第一个范式
|
||||
}
|
||||
|
||||
// 添加到历史记录
|
||||
@@ -225,8 +858,12 @@ const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString()
|
||||
}
|
||||
|
||||
// 组件挂载时加载历史记录
|
||||
// 组件挂载时加载历史记录和范式列表
|
||||
onMounted(() => {
|
||||
// 初始化范式列表
|
||||
initParadigms()
|
||||
|
||||
// 加载分析历史
|
||||
const saved = localStorage.getItem('analysisHistory')
|
||||
if (saved) {
|
||||
analysisHistory.value = JSON.parse(saved)
|
||||
|
||||
Reference in New Issue
Block a user