diff --git a/src/components/ArticleRewritePanel.vue b/src/components/ArticleRewritePanel.vue
index a996ba3..a28552e 100644
--- a/src/components/ArticleRewritePanel.vue
+++ b/src/components/ArticleRewritePanel.vue
@@ -141,9 +141,14 @@
-
-
↻
-
AI 正在检查中...
+
+
+ ↻
+ AI 正在检查中...
+
+
+ {{ checkProgress }}
+
↻
AI 正在重写中...
+
+ {{ rewriteProgress }}
+
{{ rewriteStreamContent }}
@@ -295,6 +303,7 @@
:visible="showParadigmSelector"
@close="showParadigmSelector = false"
@select="handleParadigmSelect"
+ @create-custom="handleCreateCustomParadigm"
/>
@@ -303,18 +312,28 @@
@close="showDocSelector = false"
@select="handleDocSelect"
/>
+
+
+
diff --git a/src/stores/paradigm.js b/src/stores/paradigm.js
new file mode 100644
index 0000000..05aee0e
--- /dev/null
+++ b/src/stores/paradigm.js
@@ -0,0 +1,267 @@
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+
+/**
+ * 自定义范式管理 Store
+ * 用于管理用户通过需求文档生成的自定义范式
+ */
+export const useParadigmStore = defineStore('paradigm', () => {
+ // 自定义范式列表(存储在 localStorage)
+ const customParadigms = ref([])
+
+ // 当前正在编辑的范式
+ const editingParadigm = ref(null)
+
+ // 是否正在解析需求文档
+ const isParsing = ref(false)
+
+ // 解析进度信息
+ const parsingProgress = ref('')
+
+ /**
+ * 从 localStorage 加载自定义范式
+ */
+ function loadCustomParadigms() {
+ try {
+ const stored = localStorage.getItem('customParadigms')
+ if (stored) {
+ customParadigms.value = JSON.parse(stored)
+ }
+ } catch (error) {
+ console.error('加载自定义范式失败:', error)
+ customParadigms.value = []
+ }
+ }
+
+ /**
+ * 保存自定义范式到 localStorage
+ */
+ function saveCustomParadigms() {
+ try {
+ localStorage.setItem('customParadigms', JSON.stringify(customParadigms.value))
+ } catch (error) {
+ console.error('保存自定义范式失败:', error)
+ }
+ }
+
+ /**
+ * 添加自定义范式
+ * @param {Object} paradigm - 范式对象
+ */
+ function addCustomParadigm(paradigm) {
+ // 检查是否已存在相同ID
+ const index = customParadigms.value.findIndex(p => p.id === paradigm.id)
+ if (index >= 0) {
+ // 更新现有范式
+ customParadigms.value[index] = paradigm
+ } else {
+ // 添加新范式
+ customParadigms.value.push(paradigm)
+ }
+ saveCustomParadigms()
+ }
+
+ /**
+ * 删除自定义范式
+ * @param {string} paradigmId - 范式ID
+ */
+ function deleteCustomParadigm(paradigmId) {
+ customParadigms.value = customParadigms.value.filter(p => p.id !== paradigmId)
+ saveCustomParadigms()
+ }
+
+ /**
+ * 根据ID获取自定义范式
+ * @param {string} paradigmId - 范式ID
+ * @returns {Object|null}
+ */
+ function getCustomParadigmById(paradigmId) {
+ return customParadigms.value.find(p => p.id === paradigmId) || null
+ }
+
+ /**
+ * 更新范式的某个字段
+ * @param {string} paradigmId - 范式ID
+ * @param {string} field - 字段名
+ * @param {any} value - 新值
+ */
+ function updateParadigmField(paradigmId, field, value) {
+ const paradigm = getCustomParadigmById(paradigmId)
+ if (paradigm) {
+ paradigm[field] = value
+ saveCustomParadigms()
+ }
+ }
+
+ /**
+ * 获取所有范式(包括内置和自定义)
+ * @param {Object} builtInParadigms - 内置范式对象
+ * @returns {Array}
+ */
+ function getAllParadigms(builtInParadigms = {}) {
+ const builtIn = Object.values(builtInParadigms).map(p => ({
+ ...p,
+ type: 'builtin'
+ }))
+
+ return [
+ ...builtIn,
+ ...customParadigms.value
+ ]
+ }
+
+ /**
+ * 计算属性:自定义范式数量
+ */
+ const customParadigmCount = computed(() => customParadigms.value.length)
+
+ /**
+ * 计算属性:最近使用的范式(按创建时间排序)
+ */
+ const recentCustomParadigms = computed(() => {
+ return [...customParadigms.value]
+ .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
+ .slice(0, 5)
+ })
+
+ /**
+ * 导出范式为JSON
+ * @param {string} paradigmId - 范式ID
+ * @returns {string|null} JSON字符串
+ */
+ function exportParadigm(paradigmId) {
+ const paradigm = getCustomParadigmById(paradigmId)
+ if (!paradigm) return null
+
+ try {
+ return JSON.stringify(paradigm, null, 2)
+ } catch (error) {
+ console.error('导出范式失败:', error)
+ return null
+ }
+ }
+
+ /**
+ * 从JSON导入范式
+ * @param {string} jsonString - JSON字符串
+ * @returns {boolean} 是否成功
+ */
+ function importParadigm(jsonString) {
+ try {
+ const paradigm = JSON.parse(jsonString)
+
+ // 基本验证
+ if (!paradigm.id || !paradigm.name || !paradigm.specializedPrompt) {
+ throw new Error('范式格式不正确')
+ }
+
+ // 生成新ID避免冲突
+ paradigm.id = `custom-imported-${Date.now()}`
+ paradigm.createdAt = new Date().toISOString()
+
+ addCustomParadigm(paradigm)
+ return true
+ } catch (error) {
+ console.error('导入范式失败:', error)
+ return false
+ }
+ }
+
+ /**
+ * 清空所有自定义范式(慎用)
+ */
+ function clearAllCustomParadigms() {
+ customParadigms.value = []
+ saveCustomParadigms()
+ }
+
+ /**
+ * 智能推断 guideline 的 scope
+ * @param {string|Object} guideline - 检查指令
+ * @returns {string} scope ('sentence' | 'paragraph' | 'document')
+ */
+ function inferScope(guideline) {
+ const text = typeof guideline === 'string' ? guideline : (guideline.description || guideline.title || '')
+
+ // 全文级关键词
+ if (/篇幅|章节|完整性|结构.*完整|会前准备|典型案例|巡视|巡察/.test(text)) {
+ return 'document'
+ }
+
+ // 段落级关键词
+ if (/递进|逻辑|段落|承接|前后.*呼应|层次|论述|因果/.test(text)) {
+ return 'paragraph'
+ }
+
+ // 默认句子级
+ return 'sentence'
+ }
+
+ /**
+ * 为现有范式补充 scope 字段(返回新对象,不修改原对象)
+ * @param {Object} paradigm - 范式对象
+ * @returns {Object} 更新后的范式(新对象)
+ */
+ function ensureGuidelinesHasScope(paradigm) {
+ if (!paradigm.expertGuidelines || !Array.isArray(paradigm.expertGuidelines)) {
+ return paradigm
+ }
+
+ // 创建深拷贝,避免修改原对象
+ const updatedParadigm = {
+ ...paradigm,
+ expertGuidelines: paradigm.expertGuidelines.map(g => {
+ if (typeof g === 'string') {
+ // 字符串格式:转换为对象格式并推断 scope
+ // 提取前面的关键词作为标题(最多15个字符)
+ const title = g.length > 15 ? g.substring(0, 15) : g
+ return {
+ title: title,
+ description: g,
+ scope: inferScope(g)
+ }
+ } else if (!g.scope) {
+ // 对象格式但缺少 scope:推断并补充
+ return {
+ ...g,
+ scope: inferScope(g)
+ }
+ }
+ return g
+ })
+ }
+
+ return updatedParadigm
+ }
+
+ // 初始化时加载
+ loadCustomParadigms()
+
+ return {
+ // 状态
+ customParadigms,
+ editingParadigm,
+ isParsing,
+ parsingProgress,
+
+ // 计算属性
+ customParadigmCount,
+ recentCustomParadigms,
+
+ // 方法
+ loadCustomParadigms,
+ saveCustomParadigms,
+ addCustomParadigm,
+ deleteCustomParadigm,
+ getCustomParadigmById,
+ updateParadigmField,
+ getAllParadigms,
+ exportParadigm,
+ importParadigm,
+ clearAllCustomParadigms,
+
+ // Scope 工具函数
+ inferScope,
+ ensureGuidelinesHasScope
+ }
+})
diff --git a/src/utils/requirementParser.js b/src/utils/requirementParser.js
new file mode 100644
index 0000000..7078e71
--- /dev/null
+++ b/src/utils/requirementParser.js
@@ -0,0 +1,199 @@
+/**
+ * 需求文档解析工具
+ * 将需求文档转换为范式配置
+ */
+
+/**
+ * 构建需求解析的 Prompt
+ */
+export function buildRequirementParserPrompt(requirementText) {
+ return `你是一位专业的文档分析专家,擅长提取文档中的核心要求并将其转化为结构化的写作范式配置。
+
+【任务】
+分析以下需求文档,提取关键要求并生成"范式配置",用于指导AI检查和润色文稿。
+
+【需求文档】
+${requirementText}
+
+【输出要求】
+请生成以下三个部分,使用JSON格式输出:
+
+1. **specializedPrompt** (string): 系统提示词,包含:
+ - 会议主题/文档目标
+ - 核心要求(列表形式)
+ - 论述规范(结构要求、术语规范、语气要求等)
+ - 维度要求(如五维度溯源等)
+ 长度:300-500字
+
+2. **expertGuidelines** (array): 专家检查指令,每条指令应该:
+ - 针对需求文档中的具体要求
+ - 可直接用于检查文稿是否符合标准
+ - 清晰、可执行
+ - **包含 scope 字段**(指定检查粒度):
+ * "sentence" - 句子级检查(如术语规范、语气分寸、表达方式)
+ * "paragraph" - 段落级检查(如逻辑结构、递进关系、论述层次)
+ * "document" - 全文级检查(如章节完整性、篇幅占比、结构要求)
+ 数量:8-12条
+
+3. **metadata** (object): 元数据,包含:
+ - name (string): 范式名称(简短,10字以内)
+ - description (string): 范式描述(30字以内)
+ - keyRequirements (array): 核心要求关键词(3-5个)
+
+【输出格式】
+\`\`\`json
+{
+ "specializedPrompt": "你是一位资深的...",
+ "expertGuidelines": [
+ {"title": "党内术语规范", "description": "检查是否使用...", "scope": "sentence"},
+ {"title": "递进式结构", "description": "检查段落是否符合...", "scope": "paragraph"},
+ {"title": "章节完整性", "description": "检查是否包含会前准备...", "scope": "document"},
+ ...
+ ],
+ "metadata": {
+ "name": "范式名称",
+ "description": "范式描述",
+ "keyRequirements": ["要求1", "要求2", "要求3"]
+ }
+}
+\`\`\`
+
+【注意事项】
+1. specializedPrompt 应该完整、系统,涵盖所有关键要求
+2. expertGuidelines 应该具体、可操作,每条针对一个检查点
+3. 保留原文中的专业术语和标准表述
+4. 如果需求文档提到篇幅要求、格式要求等,务必在 specializedPrompt 中明确体现
+5. **scope 分配原则**:
+ - sentence 级:适用于任何句子片段(1-5句)
+ - paragraph 级:需要段落上下文(6-20句)
+ - document 级:需要完整文档或大段落(21+句)
+
+请开始分析并生成范式配置。`;
+}
+
+/**
+ * 解析AI返回的范式配置
+ * @param {string} aiResponse - AI返回的文本
+ * @returns {Object|null} 解析后的范式配置,失败返回null
+ */
+export function parseParadigmConfig(aiResponse) {
+ try {
+ // 调试:输出原始响应
+ console.log('AI 原始响应长度:', aiResponse.length);
+ console.log('AI 原始响应内容(前500字符):', aiResponse.substring(0, 500));
+
+ // 检查响应是否为空
+ if (!aiResponse || aiResponse.trim().length === 0) {
+ console.error('AI 返回内容为空');
+ return null;
+ }
+
+ // 尝试提取 JSON 代码块
+ const jsonMatch = aiResponse.match(/```json\s*([\s\S]*?)\s*```/);
+ let jsonText = jsonMatch ? jsonMatch[1] : aiResponse;
+
+ console.log('提取的 JSON 文本(前200字符):', jsonText.substring(0, 200));
+
+ // 移除可能的注释
+ jsonText = jsonText.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*/g, '');
+
+ const config = JSON.parse(jsonText);
+
+ // 验证必需字段
+ if (!config.specializedPrompt || !Array.isArray(config.expertGuidelines) || !config.metadata) {
+ console.error('配置缺少必需字段:', {
+ hasPrompt: !!config.specializedPrompt,
+ hasGuidelines: Array.isArray(config.expertGuidelines),
+ hasMetadata: !!config.metadata
+ });
+ throw new Error('缺少必需字段');
+ }
+
+ return config;
+ } catch (error) {
+ console.error('解析范式配置失败:', error);
+ console.error('完整的 AI 响应:', aiResponse);
+ return null;
+ }
+}
+
+/**
+ * 将解析的配置转换为完整的范式对象
+ * @param {Object} parsedConfig - 解析后的配置
+ * @param {string} sourceDocPath - 源需求文档路径(可选)
+ * @returns {Object} 完整的范式对象
+ */
+export function buildParadigmObject(parsedConfig, sourceDocPath = null) {
+ const timestamp = Date.now();
+ const id = `custom-${timestamp}`;
+
+ return {
+ id,
+ name: parsedConfig.metadata.name,
+ description: parsedConfig.metadata.description,
+ type: 'custom', // 标记为自定义范式
+ createdAt: new Date().toISOString(),
+ sourceDoc: sourceDocPath,
+
+ specializedPrompt: parsedConfig.specializedPrompt,
+ expertGuidelines: parsedConfig.expertGuidelines,
+
+ // 可选:继承默认的逻辑范式和维度集
+ logicParadigms: null, // 由使用者决定是否继承
+ dimensionSetId: null,
+ defaultReference: null,
+
+ // 元数据
+ metadata: {
+ ...parsedConfig.metadata,
+ customGenerated: true
+ }
+ };
+}
+
+/**
+ * 验证范式配置的完整性
+ * @param {Object} paradigm - 范式对象
+ * @returns {Object} 验证结果 { valid: boolean, errors: string[] }
+ */
+export function validateParadigm(paradigm) {
+ const errors = [];
+
+ if (!paradigm.name || paradigm.name.trim().length === 0) {
+ errors.push('范式名称不能为空');
+ }
+
+ if (!paradigm.specializedPrompt || paradigm.specializedPrompt.length < 100) {
+ errors.push('specializedPrompt 内容过短,建议至少300字');
+ }
+
+ if (!Array.isArray(paradigm.expertGuidelines) || paradigm.expertGuidelines.length < 5) {
+ errors.push('expertGuidelines 至少需要5条指令');
+ }
+
+ if (paradigm.expertGuidelines) {
+ paradigm.expertGuidelines.forEach((guideline, index) => {
+ // 兼容字符串和对象格式
+ if (typeof guideline === 'string') {
+ if (guideline.trim().length === 0) {
+ errors.push(`第${index + 1}条指令为空`);
+ }
+ } else if (typeof guideline === 'object') {
+ // 对象格式:检查 description 字段
+ if (!guideline.description || guideline.description.trim().length === 0) {
+ errors.push(`第${index + 1}条指令的描述为空`);
+ }
+ if (!guideline.scope || !['sentence', 'paragraph', 'document'].includes(guideline.scope)) {
+ errors.push(`第${index + 1}条指令的 scope 字段无效`);
+ }
+ } else {
+ errors.push(`第${index + 1}条指令格式错误`);
+ }
+ });
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors
+ };
+}