feat: 将自定义范式从 localStorage 迁移到数据库存储

**数据库层 (src/db/index.js)**:
- 为 paradigms 表添加 4 个新字段:
  * specialized_prompt: 存储专业化提示词
  * expert_guidelines: 存储专家检查指令(JSON)
  * outline_template: 存储大纲模板
  * recommended_tags: 存储推荐标签(JSON)
- 新增数据库迁移函数 migrateDatabase(),自动为现有数据库添加新列
- 更新 getAllParadigms() 解析新字段
- 更新 addParadigm() 保存新字段
- 更新 updateParadigm() 支持新字段的更新

**Store 层 (src/stores/paradigm.js)**:
- 将存储从 localStorage 改为数据库(IndexedDB/SQLite)
- loadCustomParadigms() 现在从数据库加载,并自动迁移 localStorage 旧数据
- 新增 saveCustomParadigm(paradigm) 用于保存单个范式
- addCustomParadigm() 改为异步,调用数据库保存
- deleteCustomParadigm() 改为异步,调用数据库删除
- updateParadigmField() 改为异步,调用数据库更新
- clearAllCustomParadigms() 改为异步,调用数据库清空
- 迁移后自动清理 localStorage 中的旧数据

**优势**:
- 范式数据现在包含在数据导出中
- 统一的数据管理接口
- 更可靠的数据持久化
- 自动迁移现有数据,用户无感知

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-12 01:18:41 +08:00
parent 7f985ed317
commit 81b0e937b3
2 changed files with 179 additions and 33 deletions

View File

@@ -128,6 +128,10 @@ const initTables = async () => {
logic_paradigms TEXT, logic_paradigms TEXT,
auto_match_refs INTEGER DEFAULT 1, auto_match_refs INTEGER DEFAULT 1,
selected_refs TEXT, selected_refs TEXT,
specialized_prompt TEXT,
expert_guidelines TEXT,
outline_template TEXT,
recommended_tags TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_custom INTEGER DEFAULT 0 is_custom INTEGER DEFAULT 0
@@ -213,6 +217,45 @@ const initTables = async () => {
`) `)
console.log('✅ 数据表初始化完成') console.log('✅ 数据表初始化完成')
// 运行数据库迁移
migrateDatabase()
}
/**
* 数据库迁移:添加范式表的新字段
*/
const migrateDatabase = () => {
try {
// 检查是否已存在 specialized_prompt 列
const columns = query(`
SELECT name FROM pragma_table_info('paradigms')
`).map(row => row.name)
const neededColumns = ['specialized_prompt', 'expert_guidelines', 'outline_template', 'recommended_tags']
const missingColumns = neededColumns.filter(col => !columns.includes(col))
if (missingColumns.length > 0) {
console.log('🔄 检测到数据库需要迁移,添加新字段...')
if (missingColumns.includes('specialized_prompt')) {
execute('ALTER TABLE paradigms ADD COLUMN specialized_prompt TEXT')
}
if (missingColumns.includes('expert_guidelines')) {
execute('ALTER TABLE paradigms ADD COLUMN expert_guidelines TEXT')
}
if (missingColumns.includes('outline_template')) {
execute('ALTER TABLE paradigms ADD COLUMN outline_template TEXT')
}
if (missingColumns.includes('recommended_tags')) {
execute('ALTER TABLE paradigms ADD COLUMN recommended_tags TEXT')
}
console.log('✅ 数据库迁移完成')
}
} catch (error) {
console.error('❌ 数据库迁移失败:', error)
}
} }
// ============================================ // ============================================
@@ -516,6 +559,10 @@ export const getAllParadigms = () => {
customDimensions: p.custom_dimensions ? JSON.parse(p.custom_dimensions) : null, customDimensions: p.custom_dimensions ? JSON.parse(p.custom_dimensions) : null,
logicParadigms: p.logic_paradigms ? JSON.parse(p.logic_paradigms) : null, logicParadigms: p.logic_paradigms ? JSON.parse(p.logic_paradigms) : null,
selectedRefs: p.selected_refs ? JSON.parse(p.selected_refs) : [], selectedRefs: p.selected_refs ? JSON.parse(p.selected_refs) : [],
specializedPrompt: p.specialized_prompt || null,
expertGuidelines: p.expert_guidelines ? JSON.parse(p.expert_guidelines) : null,
outlineTemplate: p.outline_template || null,
recommendedTags: p.recommended_tags ? JSON.parse(p.recommended_tags) : [],
isCustom: p.is_custom === 1, isCustom: p.is_custom === 1,
autoMatchRefs: p.auto_match_refs === 1 autoMatchRefs: p.auto_match_refs === 1
})) }))
@@ -529,8 +576,9 @@ export const addParadigm = (paradigm) => {
execute(` execute(`
INSERT INTO paradigms (id, name, icon, description, tags, tag_class, system_constraints, INSERT INTO paradigms (id, name, icon, description, tags, tag_class, system_constraints,
dimension_set_id, custom_dimensions, logic_paradigms, auto_match_refs, selected_refs, is_custom) dimension_set_id, custom_dimensions, logic_paradigms, auto_match_refs, selected_refs,
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) specialized_prompt, expert_guidelines, outline_template, recommended_tags, is_custom)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [ `, [
id, id,
paradigm.name, paradigm.name,
@@ -544,6 +592,10 @@ export const addParadigm = (paradigm) => {
paradigm.logicParadigms ? JSON.stringify(paradigm.logicParadigms) : null, paradigm.logicParadigms ? JSON.stringify(paradigm.logicParadigms) : null,
paradigm.autoMatchRefs !== false ? 1 : 0, paradigm.autoMatchRefs !== false ? 1 : 0,
JSON.stringify(paradigm.selectedRefs || []), JSON.stringify(paradigm.selectedRefs || []),
paradigm.specializedPrompt || null,
paradigm.expertGuidelines ? JSON.stringify(paradigm.expertGuidelines) : null,
paradigm.outlineTemplate || null,
paradigm.recommendedTags ? JSON.stringify(paradigm.recommendedTags || []) : null,
paradigm.isCustom ? 1 : 0 paradigm.isCustom ? 1 : 0
]) ])
@@ -589,6 +641,22 @@ export const updateParadigm = (id, updates) => {
setClauses.push('selected_refs = ?') setClauses.push('selected_refs = ?')
params.push(JSON.stringify(updates.selectedRefs)) params.push(JSON.stringify(updates.selectedRefs))
} }
if (updates.specializedPrompt !== undefined) {
setClauses.push('specialized_prompt = ?')
params.push(updates.specializedPrompt)
}
if (updates.expertGuidelines !== undefined) {
setClauses.push('expert_guidelines = ?')
params.push(updates.expertGuidelines ? JSON.stringify(updates.expertGuidelines) : null)
}
if (updates.outlineTemplate !== undefined) {
setClauses.push('outline_template = ?')
params.push(updates.outlineTemplate)
}
if (updates.recommendedTags !== undefined) {
setClauses.push('recommended_tags = ?')
params.push(updates.recommendedTags ? JSON.stringify(updates.recommendedTags) : null)
}
setClauses.push('updated_at = CURRENT_TIMESTAMP') setClauses.push('updated_at = CURRENT_TIMESTAMP')
params.push(id) params.push(id)

View File

@@ -4,11 +4,15 @@ import { ref, computed } from 'vue'
/** /**
* 自定义范式管理 Store * 自定义范式管理 Store
* 用于管理用户通过需求文档生成的自定义范式 * 用于管理用户通过需求文档生成的自定义范式
* 现已迁移到数据库存储IndexedDB/SQLite
*/ */
export const useParadigmStore = defineStore('paradigm', () => { export const useParadigmStore = defineStore('paradigm', () => {
// 自定义范式列表(存储在 localStorage // 自定义范式列表(从数据库加载
const customParadigms = ref([]) const customParadigms = ref([])
// 数据库是否已初始化
const isDbInitialized = ref(false)
// 当前正在编辑的范式 // 当前正在编辑的范式
const editingParadigm = ref(null) const editingParadigm = ref(null)
@@ -19,14 +23,62 @@ export const useParadigmStore = defineStore('paradigm', () => {
const parsingProgress = ref('') const parsingProgress = ref('')
/** /**
* 从 localStorage 加载自定义范式 * 从数据库加载自定义范式
* 如果 localStorage 中有旧数据,自动迁移到数据库
*/ */
function loadCustomParadigms() { async function loadCustomParadigms() {
try { try {
const stored = localStorage.getItem('customParadigms') // 动态导入数据库模块(避免循环依赖)
if (stored) { const { getAllParadigms, addParadigm, updateParadigm } = await import('../db/index.js')
customParadigms.value = JSON.parse(stored)
// 从数据库加载所有范式
const allParadigms = getAllParadigms()
// 过滤出自定义范式
customParadigms.value = allParadigms.filter(p => p.isCustom)
// 检查 localStorage 中是否有旧数据需要迁移
const localStorageData = localStorage.getItem('customParadigms')
if (localStorageData && !isDbInitialized.value) {
try {
const oldParadigms = JSON.parse(localStorageData)
if (oldParadigms.length > 0) {
console.log(`🔄 检测到 localStorage 中有 ${oldParadigms.length} 个范式,开始迁移...`)
// 迁移每个范式到数据库
for (const oldParadigm of oldParadigms) {
// 检查数据库中是否已存在
const existing = customParadigms.value.find(p => p.id === oldParadigm.id)
if (existing) {
// 更新现有范式
updateParadigm(oldParadigm.id, oldParadigm)
} else {
// 添加新范式
addParadigm({
...oldParadigm,
isCustom: true,
autoMatchRefs: oldParadigm.autoMatchRefs !== false
})
}
}
// 重新加载从数据库
const updatedParadigms = getAllParadigms()
customParadigms.value = updatedParadigms.filter(p => p.isCustom)
// 清除 localStorage 中的旧数据
localStorage.removeItem('customParadigms')
console.log('✅ 范式迁移完成localStorage 已清理')
}
} catch (migrateError) {
console.error('❌ 迁移 localStorage 数据失败:', migrateError)
}
} }
isDbInitialized.value = true
} catch (error) { } catch (error) {
console.error('加载自定义范式失败:', error) console.error('加载自定义范式失败:', error)
customParadigms.value = [] customParadigms.value = []
@@ -34,11 +86,26 @@ export const useParadigmStore = defineStore('paradigm', () => {
} }
/** /**
* 保存自定义范式到 localStorage * 保存单个自定义范式到数据库(替换旧的批量保存)
*/ */
function saveCustomParadigms() { async function saveCustomParadigm(paradigm) {
try { try {
localStorage.setItem('customParadigms', JSON.stringify(customParadigms.value)) const { addParadigm, updateParadigm } = await import('../db/index.js')
// 检查是否已存在
const existing = customParadigms.value.find(p => p.id === paradigm.id)
if (existing) {
// 更新现有范式
updateParadigm(paradigm.id, paradigm)
} else {
// 添加新范式
addParadigm({
...paradigm,
isCustom: true,
autoMatchRefs: paradigm.autoMatchRefs !== false
})
}
} catch (error) { } catch (error) {
console.error('保存自定义范式失败:', error) console.error('保存自定义范式失败:', error)
} }
@@ -48,7 +115,7 @@ export const useParadigmStore = defineStore('paradigm', () => {
* 添加自定义范式 * 添加自定义范式
* @param {Object} paradigm - 范式对象 * @param {Object} paradigm - 范式对象
*/ */
function addCustomParadigm(paradigm) { async function addCustomParadigm(paradigm) {
// 检查是否已存在相同ID // 检查是否已存在相同ID
const index = customParadigms.value.findIndex(p => p.id === paradigm.id) const index = customParadigms.value.findIndex(p => p.id === paradigm.id)
if (index >= 0) { if (index >= 0) {
@@ -58,16 +125,21 @@ export const useParadigmStore = defineStore('paradigm', () => {
// 添加新范式 // 添加新范式
customParadigms.value.push(paradigm) customParadigms.value.push(paradigm)
} }
saveCustomParadigms() await saveCustomParadigm(paradigm)
} }
/** /**
* 删除自定义范式 * 删除自定义范式
* @param {string} paradigmId - 范式ID * @param {string} paradigmId - 范式ID
*/ */
function deleteCustomParadigm(paradigmId) { async function deleteCustomParadigm(paradigmId) {
customParadigms.value = customParadigms.value.filter(p => p.id !== paradigmId) try {
saveCustomParadigms() const { deleteParadigm } = await import('../db/index.js')
deleteParadigm(paradigmId)
customParadigms.value = customParadigms.value.filter(p => p.id !== paradigmId)
} catch (error) {
console.error('删除自定义范式失败:', error)
}
} }
/** /**
@@ -85,11 +157,11 @@ export const useParadigmStore = defineStore('paradigm', () => {
* @param {string} field - 字段名 * @param {string} field - 字段名
* @param {any} value - 新值 * @param {any} value - 新值
*/ */
function updateParadigmField(paradigmId, field, value) { async function updateParadigmField(paradigmId, field, value) {
const paradigm = getCustomParadigmById(paradigmId) const paradigm = getCustomParadigmById(paradigmId)
if (paradigm) { if (paradigm) {
paradigm[field] = value paradigm[field] = value
saveCustomParadigms() await saveCustomParadigm(paradigm)
} }
} }
@@ -170,9 +242,14 @@ export const useParadigmStore = defineStore('paradigm', () => {
/** /**
* 清空所有自定义范式(慎用) * 清空所有自定义范式(慎用)
*/ */
function clearAllCustomParadigms() { async function clearAllCustomParadigms() {
customParadigms.value = [] try {
saveCustomParadigms() const { execute } = await import('../db/index.js')
execute('DELETE FROM paradigms WHERE is_custom = 1')
customParadigms.value = []
} catch (error) {
console.error('清空自定义范式失败:', error)
}
} }
/** /**
@@ -234,7 +311,7 @@ export const useParadigmStore = defineStore('paradigm', () => {
return updatedParadigm return updatedParadigm
} }
// 初始化时加载 // 初始化时加载(异步)
loadCustomParadigms() loadCustomParadigms()
return { return {
@@ -243,6 +320,7 @@ export const useParadigmStore = defineStore('paradigm', () => {
editingParadigm, editingParadigm,
isParsing, isParsing,
parsingProgress, parsingProgress,
isDbInitialized,
// 计算属性 // 计算属性
customParadigmCount, customParadigmCount,
@@ -250,7 +328,7 @@ export const useParadigmStore = defineStore('paradigm', () => {
// 方法 // 方法
loadCustomParadigms, loadCustomParadigms,
saveCustomParadigms, saveCustomParadigm, // 单个保存(新)
addCustomParadigm, addCustomParadigm,
deleteCustomParadigm, deleteCustomParadigm,
getCustomParadigmById, getCustomParadigmById,