Ver Fonte

fix: 修复工作流编辑器显示问题 - 修复节点重叠、尺寸和画布高度

何文松 há 14 horas atrás
pai
commit
f06ac7f728

BIN
130应急事业部—电力建设企业安全生产标准化达标评审报告-数据规则清单V2.0-.xlsx


+ 326 - 0
_mock_rules_v2.py

@@ -0,0 +1,326 @@
+#!/usr/bin/env python3
+"""
+根据 anyuan_docs_maker 的 Dataset001Parser 真实逻辑,
+为 lingyue project 10 创建准确的规则映射。
+"""
+import requests, json
+
+BASE = "http://47.108.80.98:8001"
+PROJECT_ID = 10
+
+# ── 1. 登录 ─────────────────────────────────────────────────
+r = requests.post(f"{BASE}/api/v1/auth/login", json={"username": "admin", "password": "admin123"})
+TOKEN = r.json()["data"]["accessToken"]
+HDR = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
+
+# ── 2. lingyue 附件 ID 映射 ──────────────────────────────────
+# anyuan 附件编号 → lingyue attachment id + 名称
+ATT = {
+    "001001": (400, "核心要素评审情况记录表"),
+    "001002": (401, "现场评审分工表"),
+    "001003": (402, "安全生产标准化复审实施方案"),
+    "001004": (403, "材料真实性说明"),
+    "001005": (404, "在建项目一览表"),
+    "001006": (405, "安全管理制度清单"),
+    "001007": (406, "现场评审末次会签到表"),
+    "001008": (407, "工作方案"),
+    "001010": (408, "复审问题建议表"),
+}
+
+def inp(code):
+    """构建 inputs 数组"""
+    aid, name = ATT[code]
+    return [{"sourceNodeId": str(aid), "inputKey": "attachment", "inputType": "ATTACHMENT", "inputName": name}]
+
+def inps(*codes):
+    """多来源"""
+    result = []
+    for c in codes:
+        aid, name = ATT[c]
+        result.append({"sourceNodeId": str(aid), "inputKey": "attachment", "inputType": "ATTACHMENT", "inputName": name})
+    return result
+
+# ── 3. 规则定义(按 Dataset001Parser 解析阶段) ────────────────
+# 格式: (elementKey, ruleName, actionType, dslContent/描述, 来源附件编号列表)
+RULES = [
+    # ═══════════════════════════════════════════════
+    # 阶段2: 工作方案 (001008) → 评审对象、简称、日期、人员等
+    # ═══════════════════════════════════════════════
+    ("project.reviewObject",
+     "评审对象全称", "summary",
+     "从工作方案PDF中通过AI提取评审对象全称。解析工作方案的每个PDF文件,从中识别被评审企业的完整名称。",
+     ["001008"]),
+
+    ("project.reviewObjectAlias",
+     "评审对象简称", "summary",
+     "从工作方案PDF中通过AI提取评审对象简称。例如「中国电建集团成都勘测设计研究院有限公司」简称「成都院」。",
+     ["001008"]),
+
+    ("project.workStartAt",
+     "评审开始日期", "quote",
+     "从工作方案PDF中直接提取评审工作开始日期。格式为:yyyy年M月d日。多个工作方案时取最早的开始日期。",
+     ["001008"]),
+
+    ("project.workEndAt",
+     "评审结束日期", "quote",
+     "从工作方案PDF中直接提取评审工作结束日期。格式为:yyyy年M月d日。多个工作方案时取最晚的结束日期。",
+     ["001008"]),
+
+    ("project.reviewRange",
+     "复审范围", "summary",
+     "从工作方案PDF中提取所有复审项目(单位和在建项目),按类型排序后拼接。格式如:成都院本部、大邑地勘项目(简称:大邑项目)。",
+     ["001008"]),
+
+    ("project.workProcess",
+     "复审工作过程", "summary",
+     "根据工作方案中提取的评审开始/结束日期、评审对象简称、复审项目列表,按固定模板生成复审工作过程描述。包含评审组工作会议、现场评审、具体评审时间表等内容。",
+     ["001008"]),
+
+    ("+SPSRRReviewProject",
+     "现场复审项目", "table_extract",
+     "从工作方案PDF中提取复审项目列表,包含项目名称、简称、类型(单位/在建项目)、排序等信息。",
+     ["001008"]),
+
+    ("+SPSRRReviewer",
+     "现场复审人员", "table_extract",
+     "从工作方案PDF中提取评审人员列表,包含姓名、专业分工等。多个工作方案中的同名人员会合并专业。",
+     ["001008"]),
+
+    # ═══════════════════════════════════════════════
+    # 阶段4: 核心要素评审记录表 (001001) → 各核心要素得分与评审意见
+    # ═══════════════════════════════════════════════
+    ("project.resultScore",
+     "评审得分", "table_extract",
+     "从核心要素评审情况记录表(docx)中提取各评审项的标准分和实得分,汇总计算总评审得分。公式:总实得分/总标准分×100。",
+     ["001001"]),
+
+    ("project.resultLevel",
+     "评审结论级别", "table_extract",
+     "根据评审得分自动判定。≥90分为一级,≥80分为二级。",
+     ["001001"]),
+
+    ("project.target",
+     "目标", "summary",
+     "从核心要素评审记录表中提取评审代码5.1.1.1~5.1.1.3的评审意见(remark),经AI总结生成目标描述。",
+     ["001001"]),
+
+    ("project.duty",
+     "职责", "summary",
+     "从核心要素评审记录表中提取评审代码5.1.2.1的评审意见(remark),经AI总结生成职责描述。",
+     ["001001"]),
+
+    ("project.fullParticipation",
+     "全员参与", "summary",
+     "从核心要素评审记录表中提取评审代码5.1.3的评审意见(remark),经AI总结生成全员参与描述。",
+     ["001001"]),
+
+    ("project.safetyInvestment",
+     "安全投入", "summary",
+     "从核心要素评审记录表中提取评审代码5.1.4的评审意见(remark),经AI总结生成安全投入描述。",
+     ["001001"]),
+
+    ("project.safetyCulture",
+     "安全文化", "summary",
+     "从核心要素评审记录表中提取评审代码5.1.5的评审意见(remark),经AI总结生成安全文化描述。",
+     ["001001"]),
+
+    ("project.systematicManagement",
+     "体系化管理", "summary",
+     "从核心要素评审记录表中提取评审代码5.2.*的评审意见(remark),经AI总结生成体系化管理描述。",
+     ["001001"]),
+
+    ("project.employeeTraining",
+     "人员教育培训", "summary",
+     "从核心要素评审记录表中提取评审代码5.3.2.*的评审意见(remark),经AI总结生成人员教育培训描述。",
+     ["001001"]),
+
+    ("project.assetManagement",
+     "设备设施管理", "summary",
+     "从核心要素评审记录表中提取评审代码5.4.1.*的评审意见(remark),经AI总结生成设备设施管理描述。",
+     ["001001"]),
+
+    ("project.jobSafety",
+     "作业安全", "summary",
+     "从核心要素评审记录表中提取评审代码5.4.2.1的评审意见(remark),经AI总结生成作业安全描述。",
+     ["001001"]),
+
+    ("project.positionQualified",
+     "岗位达标", "summary",
+     "从核心要素评审记录表中提取评审代码5.4.2.3的评审意见(remark),经AI总结生成岗位达标描述。",
+     ["001001"]),
+
+    ("project.partner",
+     "相关方", "summary",
+     "从核心要素评审记录表中提取评审代码5.4.2.4的评审意见(remark),经AI总结生成相关方管理描述。",
+     ["001001"]),
+
+    ("project.occupationalHealth",
+     "职业健康", "summary",
+     "从核心要素评审记录表中提取评审代码5.4.3.1的评审意见(remark),经AI总结生成职业健康描述。",
+     ["001001"]),
+
+    ("project.riskAssessment",
+     "风险辨识与评价", "summary",
+     "从核心要素评审记录表中提取评审代码5.5.1.1和5.5.1.2的评审意见(remark),经AI总结生成风险辨识与评价描述。",
+     ["001001"]),
+
+    ("project.majorHazardManagement",
+     "重大危险源管理", "summary",
+     "从核心要素评审记录表中提取评审代码5.5.2.1的评审意见(remark),经AI总结生成重大危险源管理描述。",
+     ["001001"]),
+
+    ("project.hazardInspection",
+     "隐患排查", "summary",
+     "从核心要素评审记录表中提取评审代码5.5.3.1的评审意见(remark),经AI总结生成隐患排查描述。",
+     ["001001"]),
+
+    ("project.changeManagement",
+     "变更管理", "summary",
+     "从核心要素评审记录表中提取评审代码5.5.1.4的评审意见(remark),经AI总结生成变更管理描述。",
+     ["001001"]),
+
+    ("project.earlyWarning",
+     "预测预警", "summary",
+     "从核心要素评审记录表中提取评审代码5.5.4的评审意见(remark),经AI总结生成预测预警描述。",
+     ["001001"]),
+
+    ("project.emergencyResponse",
+     "应急救援", "summary",
+     "从核心要素评审记录表中提取评审代码5.6.1.1的评审意见(remark),经AI总结生成应急救援描述。",
+     ["001001"]),
+
+    ("project.incidentManagement",
+     "事故管理", "summary",
+     "从核心要素评审记录表中提取评审代码5.7.1的评审意见(remark),经AI总结生成事故管理描述。",
+     ["001001"]),
+
+    ("project.continuousImprovement",
+     "持续改进", "summary",
+     "从核心要素评审记录表中提取评审代码5.8.2.*的评审意见(remark),经AI总结生成持续改进描述。",
+     ["001001"]),
+
+    ("project.safetyStandardizationStatus",
+     "标准化建设运行情况", "summary",
+     "从核心要素评审记录表中提取评审代码5.8.1.*和5.8.2.*的评审意见(remark),经AI总结生成标准化建设运行情况描述。",
+     ["001001"]),
+
+    ("project.safetyHighlight",
+     "安全生产管理亮点", "summary",
+     "综合核心要素评审记录表中目标职责(5.1)各子项的评审意见,经AI提炼安全生产管理亮点。",
+     ["001001"]),
+
+    ("+target_responsibility",
+     "目标职责详情", "table_extract",
+     "从核心要素评审记录表中筛选评审代码为5.1.*的所有检查项,按模板渲染目标职责章节详情。包含各子项的评审标准、标准分、实得分、存在问题及扣分情况。",
+     ["001001"]),
+
+    ("+institutionalized_management",
+     "制度化管理详情", "table_extract",
+     "从核心要素评审记录表中筛选评审代码为5.2.*的所有检查项,按模板渲染制度化管理章节详情。",
+     ["001001"]),
+
+    ("+review_status",
+     "现场复审情况", "table_extract",
+     "从核心要素评审记录表中汇总所有8个核心要素的评审结果,渲染现场复审情况总览表。",
+     ["001001"]),
+
+    ("+review_result",
+     "复审结果", "table_extract",
+     "根据核心要素评审记录表的评分汇总,按8个核心要素计算实际评审项数、标准分、实得分、扣分项数等,生成复审结果表。",
+     ["001001"]),
+
+    ("+site_reaudit_score_analysis",
+     "现场复审得分分析", "table_extract",
+     "根据核心要素评审记录表的评分结果,计算各核心要素的得分率(实得分/标准分×100),生成得分分析表和分析文字描述。",
+     ["001001"]),
+
+    # ═══════════════════════════════════════════════
+    # 阶段6: 复审问题建议表 (001010) → 整改建议
+    # ═══════════════════════════════════════════════
+    ("+SPSRRSuggestion",
+     "现场复审发现的问题及整改", "table_extract",
+     "主体数据从核心要素评审记录表(001001)中提取有扣分的检查项(问题描述),整改建议从复审问题建议表(001010)中按评审代码匹配补充。",
+     ["001001", "001010"]),
+
+    # ═══════════════════════════════════════════════
+    # 阶段7: 安全生产标准化复审实施方案 (001003) → 自评过程、工作依据
+    # ═══════════════════════════════════════════════
+    ("project.reviewObjectSelfAssessmentProcess",
+     "自评过程", "summary",
+     "从安全生产标准化复审实施方案(PDF)中OCR提取全文,通过AI按参考模板总结自评过程内容。包含前期策划、专题培训、自查自纠、内审整改、报告撰写、持续改进6个阶段。",
+     ["001003"]),
+
+    ("project.moreWorkReference",
+     "其他工作依据", "quote",
+     "从安全生产标准化复审实施方案(PDF)中OCR提取文件编号和文件名称。格式如:《xxx管理办法》(蓉设安质〔2024〕20号)。",
+     ["001003"]),
+
+    # ═══════════════════════════════════════════════
+    # 用户输入字段(来源为用户手工填写的元数据,不依赖附件)
+    # ═══════════════════════════════════════════════
+    ("basicInfo.projectCode",
+     "项目编号", "use_entity_value",
+     "用户手工输入的项目编号,如 PRJ-2024-001。",
+     []),
+
+    ("basicInfo.requestLevel",
+     "申请级别", "use_entity_value",
+     "用户手工输入的申请标准化复审级别,如「一级」。",
+     []),
+
+    ("basicInfo.applyAt",
+     "复审申请日期", "use_entity_value",
+     "用户手工输入的复审申请日期。格式为:yyyy年M月d日。",
+     []),
+
+    ("basicInfo.reviewObjectCertificateGetAt",
+     "获证日期", "use_entity_value",
+     "用户手工输入的电力安全生产标准化企业证书获取日期。格式为:yyyy年M月d日。",
+     []),
+
+    ("basicInfo.reviewObjectCertificateCode",
+     "证书编号", "use_entity_value",
+     "用户手工输入的电力安全生产标准化企业证书编号。",
+     []),
+
+    ("basicInfo.reviewObjectCertificate2GetAt",
+     "通过评审日期", "use_entity_value",
+     "用户手工输入的国家能源局安全生产标准化达标证书获取日期。",
+     []),
+
+    # ═══════════════════════════════════════════════
+    # 渲染阶段:附件直接嵌入(附件1~7的原始内容嵌入到报告附录)
+    # ═══════════════════════════════════════════════
+    # 这些不需要创建规则,因为是直接将附件内容渲染到报告末尾
+]
+
+# ── 4. 批量创建 ──────────────────────────────────────────────
+print(f"共 {len(RULES)} 条规则待创建:\n")
+ok, fail = 0, 0
+for elem_key, rule_name, action_type, dsl, att_codes in RULES:
+    inputs = []
+    for c in att_codes:
+        aid, name = ATT[c]
+        inputs.append({"sourceNodeId": str(aid), "inputKey": "attachment", "inputType": "ATTACHMENT", "inputName": name})
+
+    payload = {
+        "elementKey": elem_key,
+        "ruleName": rule_name,
+        "ruleType": "auto",
+        "actionType": action_type,
+        "actionConfig": json.dumps({"description": dsl[:200]}, ensure_ascii=False),
+        "dslContent": dsl,
+        "description": f"来源:{', '.join(ATT[c][1] for c in att_codes) if att_codes else '用户输入'}",
+        "inputs": inputs,
+    }
+
+    resp = requests.post(f"{BASE}/api/v1/projects/{PROJECT_ID}/rules", headers=HDR, json=payload)
+    if resp.status_code == 200 and resp.json().get("code") == 200:
+        rid = resp.json()["data"].get("id", "?")
+        print(f"  ✅ [{rid:>5}] {action_type:18s} {elem_key:50s} ← {', '.join(ATT[c][1] for c in att_codes) if att_codes else '用户输入'}")
+        ok += 1
+    else:
+        print(f"  ❌ {elem_key:50s} → {resp.status_code} {resp.text[:120]}")
+        fail += 1
+
+print(f"\n完成:成功 {ok},失败 {fail}")

+ 436 - 0
_update_rules_config.py

@@ -0,0 +1,436 @@
+#!/usr/bin/env python3
+"""
+为现有规则补充内容定位配置(locatorType、chapterTitle、reviewCode、prompt)
+"""
+import requests, json
+
+BASE = "http://47.108.80.98:8001"
+PROJECT_ID = 10
+
+# ── 1. 登录 ─────────────────────────────────────────────────
+r = requests.post(f"{BASE}/api/v1/auth/login", json={"username": "admin", "password": "admin123"})
+TOKEN = r.json()["data"]["accessToken"]
+HDR = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
+
+# ── 2. 规则配置补充 ──────────────────────────────────────────
+# 格式: elementKey -> { locatorType, chapterTitle, reviewCode, tableSelector, prompt }
+RULE_CONFIGS = {
+    # ═══════════════════════════════════════════════
+    # 工作方案 (001008) → 全文提取
+    # ═══════════════════════════════════════════════
+    "project.reviewObject": {
+        "locatorType": "full_text",
+        "prompt": """你是一个专业的文档信息提取助手。请阅读以下内容,提取评审对象公司的全称。
+
+提取规则:
+1. 提取封面或标题第一行的受检单位全称
+2. 通常在"一、工作目的"中出现,如"XXX公司(以下简称:XXX)"
+3. 只输出公司全称,不要其他内容
+
+输出格式:直接输出公司全称文本"""
+    },
+    
+    "project.reviewObjectAlias": {
+        "locatorType": "chapter",
+        "chapterTitle": "工作目的",
+        "prompt": """从内容中提取评审对象公司的简称。
+
+提取规则:
+1. 寻找"以下简称"或"简称"后面的内容
+2. 例如"中国电建集团成都勘测设计研究院有限公司(以下简称:成都院)",简称为"成都院"
+3. 只输出简称,不要其他内容"""
+    },
+    
+    "project.workStartAt": {
+        "locatorType": "chapter",
+        "chapterTitle": "工作时间",
+        "prompt": """从内容中提取评审工作开始日期。
+
+提取规则:
+1. 寻找"工作时间阶段"或"评审时间"相关内容
+2. 格式通常为"yyyy年M月d日至yyyy年M月d日"
+3. 提取开始日期,格式为:yyyy年M月d日"""
+    },
+    
+    "project.workEndAt": {
+        "locatorType": "chapter",
+        "chapterTitle": "工作时间",
+        "prompt": """从内容中提取评审工作结束日期。
+
+提取规则:
+1. 寻找"工作时间阶段"或"评审时间"相关内容
+2. 格式通常为"yyyy年M月d日至yyyy年M月d日"
+3. 提取结束日期,格式为:yyyy年M月d日"""
+    },
+    
+    "project.reviewRange": {
+        "locatorType": "chapter",
+        "chapterTitle": "查评范围",
+        "prompt": """从内容中提取复审范围,包括所有复审项目(单位和在建项目)。
+
+提取规则:
+1. 提取所有被评审的单位和项目名称
+2. 按类型排序:先单位后项目
+3. 格式如:成都院本部、大邑地勘项目(简称:大邑项目)"""
+    },
+    
+    "project.workProcess": {
+        "locatorType": "full_text",
+        "prompt": """根据工作方案内容,生成复审工作过程描述。
+
+包含以下内容:
+1. 评审组工作会议召开情况
+2. 现场评审安排
+3. 具体评审时间表
+4. 评审组成员分工
+
+输出格式:按时间顺序描述复审工作过程"""
+    },
+    
+    # ═══════════════════════════════════════════════
+    # 核心要素评审记录表 (001001) → 评审代码定位
+    # ═══════════════════════════════════════════════
+    "project.resultScore": {
+        "locatorType": "table",
+        "tableSelector": "核心要素评审情况记录表",
+        "prompt": """从表格中提取各评审项的标准分和实得分,计算总评审得分。
+
+计算公式:总实得分/总标准分×100
+输出格式:保留一位小数的分数,如 92.5"""
+    },
+    
+    "project.resultLevel": {
+        "locatorType": "table",
+        "tableSelector": "核心要素评审情况记录表",
+        "prompt": """根据评审得分判定评审结论级别。
+
+判定规则:
+- ≥90分为一级
+- ≥80分为二级
+- <80分为三级
+
+输出格式:一级/二级/三级"""
+    },
+    
+    "project.target": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1.1",
+        "prompt": """从评审代码5.1.1.1~5.1.1.3的评审意见中,总结生成目标描述。
+
+要求:
+1. 提取各子项的评审意见(remark)
+2. 综合分析,生成100字以内的目标描述
+3. 突出企业安全生产目标的制定和落实情况"""
+    },
+    
+    "project.duty": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1.2.1",
+        "prompt": """从评审代码5.1.2.1的评审意见中,总结生成职责描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的职责描述
+3. 突出安全生产责任制的建立和落实情况"""
+    },
+    
+    "project.fullParticipation": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1.3",
+        "prompt": """从评审代码5.1.3的评审意见中,总结生成全员参与描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的全员参与描述
+3. 突出全员安全生产参与机制"""
+    },
+    
+    "project.safetyInvestment": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1.4",
+        "prompt": """从评审代码5.1.4的评审意见中,总结生成安全投入描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的安全投入描述
+3. 突出安全生产费用的提取和使用情况"""
+    },
+    
+    "project.safetyCulture": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1.5",
+        "prompt": """从评审代码5.1.5的评审意见中,总结生成安全文化描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的安全文化描述
+3. 突出企业安全文化建设情况"""
+    },
+    
+    "project.systematicManagement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.2",
+        "prompt": """从评审代码5.2.*的评审意见中,总结生成体系化管理描述。
+
+要求:
+1. 提取5.2下所有子项的评审意见
+2. 综合分析,生成150字以内的体系化管理描述
+3. 突出安全管理体系的建设和运行情况"""
+    },
+    
+    "project.employeeTraining": {
+        "locatorType": "review_code",
+        "reviewCode": "5.3.2",
+        "prompt": """从评审代码5.3.2.*的评审意见中,总结生成人员教育培训描述。
+
+要求:
+1. 提取5.3.2下所有子项的评审意见
+2. 综合分析,生成100字以内的人员教育培训描述
+3. 突出安全教育培训的开展情况"""
+    },
+    
+    "project.assetManagement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.4.1",
+        "prompt": """从评审代码5.4.1.*的评审意见中,总结生成设备设施管理描述。
+
+要求:
+1. 提取5.4.1下所有子项的评审意见
+2. 综合分析,生成100字以内的设备设施管理描述
+3. 突出设备设施的安全管理情况"""
+    },
+    
+    "project.jobSafety": {
+        "locatorType": "review_code",
+        "reviewCode": "5.4.2.1",
+        "prompt": """从评审代码5.4.2.1的评审意见中,总结生成作业安全描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的作业安全描述
+3. 突出作业现场安全管理情况"""
+    },
+    
+    "project.positionQualified": {
+        "locatorType": "review_code",
+        "reviewCode": "5.4.2.3",
+        "prompt": """从评审代码5.4.2.3的评审意见中,总结生成岗位达标描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的岗位达标描述
+3. 突出岗位安全达标情况"""
+    },
+    
+    "project.partner": {
+        "locatorType": "review_code",
+        "reviewCode": "5.4.2.4",
+        "prompt": """从评审代码5.4.2.4的评审意见中,总结生成相关方管理描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的相关方管理描述
+3. 突出相关方安全管理情况"""
+    },
+    
+    "project.occupationalHealth": {
+        "locatorType": "review_code",
+        "reviewCode": "5.4.3.1",
+        "prompt": """从评审代码5.4.3.1的评审意见中,总结生成职业健康描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的职业健康描述
+3. 突出职业健康管理情况"""
+    },
+    
+    "project.riskAssessment": {
+        "locatorType": "review_code",
+        "reviewCode": "5.5.1",
+        "prompt": """从评审代码5.5.1.1和5.5.1.2的评审意见中,总结生成风险辨识与评价描述。
+
+要求:
+1. 提取5.5.1.1和5.5.1.2的评审意见
+2. 综合分析,生成100字以内的风险辨识与评价描述
+3. 突出风险辨识和评价工作开展情况"""
+    },
+    
+    "project.majorHazardManagement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.5.2.1",
+        "prompt": """从评审代码5.5.2.1的评审意见中,总结生成重大危险源管理描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的重大危险源管理描述
+3. 突出重大危险源辨识和管控情况"""
+    },
+    
+    "project.hazardInspection": {
+        "locatorType": "review_code",
+        "reviewCode": "5.5.3.1",
+        "prompt": """从评审代码5.5.3.1的评审意见中,总结生成隐患排查描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的隐患排查描述
+3. 突出隐患排查治理工作开展情况"""
+    },
+    
+    "project.changeManagement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.5.1.4",
+        "prompt": """从评审代码5.5.1.4的评审意见中,总结生成变更管理描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的变更管理描述
+3. 突出变更管理制度和执行情况"""
+    },
+    
+    "project.earlyWarning": {
+        "locatorType": "review_code",
+        "reviewCode": "5.5.4",
+        "prompt": """从评审代码5.5.4的评审意见中,总结生成预测预警描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的预测预警描述
+3. 突出预测预警机制建设情况"""
+    },
+    
+    "project.emergencyResponse": {
+        "locatorType": "review_code",
+        "reviewCode": "5.6.1.1",
+        "prompt": """从评审代码5.6.1.1的评审意见中,总结生成应急救援描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的应急救援描述
+3. 突出应急预案和应急演练情况"""
+    },
+    
+    "project.incidentManagement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.7.1",
+        "prompt": """从评审代码5.7.1的评审意见中,总结生成事故管理描述。
+
+要求:
+1. 提取评审意见(remark)
+2. 生成100字以内的事故管理描述
+3. 突出事故报告和调查处理情况"""
+    },
+    
+    "project.continuousImprovement": {
+        "locatorType": "review_code",
+        "reviewCode": "5.8.2",
+        "prompt": """从评审代码5.8.2.*的评审意见中,总结生成持续改进描述。
+
+要求:
+1. 提取5.8.2下所有子项的评审意见
+2. 综合分析,生成100字以内的持续改进描述
+3. 突出安全生产标准化持续改进情况"""
+    },
+    
+    "project.safetyStandardizationStatus": {
+        "locatorType": "review_code",
+        "reviewCode": "5.8",
+        "prompt": """从评审代码5.8.1.*和5.8.2.*的评审意见中,总结生成标准化建设运行情况描述。
+
+要求:
+1. 提取5.8下所有子项的评审意见
+2. 综合分析,生成150字以内的标准化建设运行情况描述
+3. 突出安全生产标准化体系的建设和运行情况"""
+    },
+    
+    "project.safetyHighlight": {
+        "locatorType": "review_code",
+        "reviewCode": "5.1",
+        "prompt": """综合评审代码5.1下各子项的评审意见,提炼安全生产管理亮点。
+
+要求:
+1. 提取5.1下所有子项的评审意见
+2. 分析提炼企业安全生产管理的亮点和特色
+3. 生成150字以内的亮点描述"""
+    },
+    
+    # ═══════════════════════════════════════════════
+    # 安全生产标准化复审实施方案 (001003)
+    # ═══════════════════════════════════════════════
+    "project.reviewObjectSelfAssessmentProcess": {
+        "locatorType": "full_text",
+        "prompt": """从安全生产标准化复审实施方案中,总结自评过程内容。
+
+包含以下6个阶段:
+1. 前期策划
+2. 专题培训
+3. 自查自纠
+4. 内审整改
+5. 报告撰写
+6. 持续改进
+
+输出格式:按阶段描述自评过程,每个阶段50-100字"""
+    },
+    
+    "project.moreWorkReference": {
+        "locatorType": "chapter",
+        "chapterTitle": "工作依据",
+        "prompt": """从内容中提取其他工作依据文件。
+
+提取规则:
+1. 寻找文件编号和文件名称
+2. 格式如:《xxx管理办法》(蓉设安质〔2024〕20号)
+3. 多个文件用换行分隔"""
+    },
+}
+
+# ── 3. 获取现有规则并更新 ──────────────────────────────────────
+print("获取现有规则...")
+resp = requests.get(f"{BASE}/api/v1/projects/{PROJECT_ID}/rules", headers=HDR)
+if resp.status_code != 200:
+    print(f"获取规则失败: {resp.status_code} {resp.text}")
+    exit(1)
+
+rules = resp.json().get("data", [])
+print(f"共 {len(rules)} 条规则\n")
+
+# ── 4. 更新规则配置 ──────────────────────────────────────────
+ok, skip, fail = 0, 0, 0
+for rule in rules:
+    rule_id = rule.get("id")
+    element_key = rule.get("elementKey")
+    rule_name = rule.get("ruleName")
+    
+    if element_key not in RULE_CONFIGS:
+        print(f"  ⏭️  [{rule_id:>5}] {element_key:50s} - 无配置,跳过")
+        skip += 1
+        continue
+    
+    config = RULE_CONFIGS[element_key]
+    
+    # 合并现有 actionConfig
+    existing_config = {}
+    if rule.get("actionConfig"):
+        try:
+            existing_config = json.loads(rule["actionConfig"]) if isinstance(rule["actionConfig"], str) else rule["actionConfig"]
+        except:
+            pass
+    
+    # 更新配置
+    new_config = {**existing_config, **config}
+    
+    payload = {
+        "elementKey": element_key,
+        "ruleName": rule_name,
+        "actionConfig": json.dumps(new_config, ensure_ascii=False),
+    }
+    
+    resp = requests.put(f"{BASE}/api/v1/rules/{rule_id}", headers=HDR, json=payload)
+    if resp.status_code == 200 and resp.json().get("code") == 200:
+        print(f"  ✅ [{rule_id:>5}] {element_key:50s} - locatorType={config.get('locatorType')}")
+        ok += 1
+    else:
+        print(f"  ❌ [{rule_id:>5}] {element_key:50s} → {resp.status_code} {resp.text[:100]}")
+        fail += 1
+
+print(f"\n完成:成功 {ok},跳过 {skip},失败 {fail}")

+ 2 - 0
backend/lingyue-project/src/main/java/com/lingyue/project/rule/dto/RuleExecuteResultVO.java

@@ -11,4 +11,6 @@ public class RuleExecuteResultVO {
     private String elementKey;
     private Boolean valueUpdated;
     private Long duration;
+    private String error;
+    private String locatedContent;  // 定位到的原始内容
 }

+ 97 - 7
backend/lingyue-project/src/main/java/com/lingyue/project/rule/service/RuleService.java

@@ -10,9 +10,12 @@ import com.lingyue.graph.service.PropertyService;
 import com.lingyue.project.rule.dto.RuleCreateDTO;
 import com.lingyue.project.rule.dto.RuleExecuteResultVO;
 import com.lingyue.project.rule.dto.RuleVO;
+import com.lingyue.project.rule.util.ContentLocator;
 import com.lingyue.project.rule.viewmapper.RuleViewMapper;
 import com.lingyue.project.rule.viewmapper.VRule;
 import com.lingyue.project.rule.viewmapper.VRuleInput;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -33,6 +36,8 @@ public class RuleService {
     private final EdgeService edgeService;
     private final PropertyService propertyService;
     private final RuleViewMapper ruleViewMapper;
+    private final ContentLocator contentLocator;
+    private final ObjectMapper objectMapper = new ObjectMapper();
 
     public List<RuleVO> listRules(Long projectId) {
         List<VRule> rules = ruleViewMapper.selectByProjectId(projectId);
@@ -202,23 +207,108 @@ public class RuleService {
         }
 
         long startTime = System.currentTimeMillis();
+        log.info("执行规则: ruleId={}, ruleName={}, ruleType={}, actionType={}", 
+                ruleId, vr.getRuleName(), vr.getRuleType(), vr.getActionType());
 
-        // TODO: 根据ruleType和actionType执行规则逻辑
-        // 当前返回占位结果
-        log.info("执行规则: ruleId={}, ruleType={}, actionType={}", ruleId, vr.getRuleType(), vr.getActionType());
+        String outputText = "";
+        String status = "success";
+        String error = null;
+        boolean valueUpdated = false;
 
-        propertyService.setNodeProperty(ruleId, "last_run_status", "success");
+        try {
+            // 1. 解析 actionConfig 获取定位信息
+            String locatorType = "full_text";
+            String chapterTitle = null;
+            String reviewCode = null;
+            String tableSelector = null;
+            String prompt = null;
+
+            if (StringUtils.hasText(vr.getActionConfig())) {
+                try {
+                    JsonNode config = objectMapper.readTree(vr.getActionConfig());
+                    locatorType = config.has("locatorType") ? config.get("locatorType").asText() : "full_text";
+                    chapterTitle = config.has("chapterTitle") ? config.get("chapterTitle").asText() : null;
+                    reviewCode = config.has("reviewCode") ? config.get("reviewCode").asText() : null;
+                    tableSelector = config.has("tableSelector") ? config.get("tableSelector").asText() : null;
+                    prompt = config.has("prompt") ? config.get("prompt").asText() : null;
+                } catch (Exception e) {
+                    log.warn("解析 actionConfig 失败: {}", e.getMessage());
+                }
+            }
+
+            // 2. 获取输入资源内容
+            List<VRuleInput> inputs = ruleViewMapper.selectInputsByRuleId(ruleId);
+            StringBuilder inputContent = new StringBuilder();
+
+            for (VRuleInput input : inputs) {
+                if (input.getSourceNodeId() != null) {
+                    // 获取附件全文
+                    String fullText = propertyService.getNodePropertyString(input.getSourceNodeId(), "full_text");
+                    if (StringUtils.hasText(fullText)) {
+                        // 3. 根据定位类型提取内容
+                        String locatedContent = contentLocator.locateContent(
+                                fullText, locatorType, chapterTitle, reviewCode, tableSelector);
+                        
+                        if (StringUtils.hasText(locatedContent)) {
+                            inputContent.append("【来源: ").append(input.getSourceName()).append("】\n");
+                            inputContent.append(locatedContent).append("\n\n");
+                        }
+                        
+                        log.info("内容定位完成: sourceId={}, sourceName={}, locatorType={}, contentLength={}", 
+                                input.getSourceNodeId(), input.getSourceName(), locatorType, 
+                                locatedContent != null ? locatedContent.length() : 0);
+                    }
+                }
+            }
+
+            // 4. 根据 actionType 处理内容
+            String actionType = vr.getActionType();
+            if ("quote".equals(actionType) || "direct_entity".equals(actionType)) {
+                // 直接引用:使用定位到的内容
+                outputText = inputContent.toString().trim();
+            } else if ("ai_extract".equals(actionType) || "summary".equals(actionType)) {
+                // AI 提取/总结:需要调用 AI 服务
+                // TODO: 集成 AI 服务
+                outputText = "[待 AI 处理] " + inputContent.toString().trim();
+                log.info("AI 处理待实现: actionType={}, prompt={}, inputLength={}", 
+                        actionType, prompt, inputContent.length());
+            } else if ("table_extract".equals(actionType)) {
+                // 表格提取
+                outputText = inputContent.toString().trim();
+            } else {
+                outputText = inputContent.toString().trim();
+            }
+
+            // 5. 保存执行结果
+            if (StringUtils.hasText(outputText)) {
+                propertyService.setNodeProperty(ruleId, "last_output_text", outputText);
+                valueUpdated = true;
+            }
+
+        } catch (Exception e) {
+            log.error("规则执行失败: ruleId={}, error={}", ruleId, e.getMessage(), e);
+            status = "error";
+            error = e.getMessage();
+        }
+
+        propertyService.setNodeProperty(ruleId, "last_run_status", status);
         propertyService.setNodePropertyDate(ruleId, "last_run_time", LocalDateTime.now());
+        if (error != null) {
+            propertyService.setNodeProperty(ruleId, "last_run_error", error);
+        }
 
         long duration = System.currentTimeMillis() - startTime;
+        log.info("规则执行完成: ruleId={}, status={}, duration={}ms, outputLength={}", 
+                ruleId, status, duration, outputText.length());
 
         RuleExecuteResultVO result = new RuleExecuteResultVO();
         result.setRuleId(ruleId);
-        result.setStatus("success");
-        result.setOutputText(vr.getLastOutputText());
+        result.setStatus(status);
+        result.setOutputText(outputText);
         result.setElementKey(vr.getElementKey());
-        result.setValueUpdated(false);
+        result.setValueUpdated(valueUpdated);
         result.setDuration(duration);
+        result.setError(error);
         return result;
     }
 

+ 267 - 0
backend/lingyue-project/src/main/java/com/lingyue/project/rule/util/ContentLocator.java

@@ -0,0 +1,267 @@
+package com.lingyue.project.rule.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 内容定位工具类
+ * 根据不同的定位方式从附件全文中提取对应内容
+ */
+@Slf4j
+@Component
+public class ContentLocator {
+
+    /**
+     * 定位类型
+     */
+    public enum LocatorType {
+        FULL_TEXT,      // 全文
+        CHAPTER,        // 章节定位
+        REVIEW_CODE,    // 评审代码定位
+        TABLE           // 表格定位
+    }
+
+    /**
+     * 根据定位类型提取内容
+     *
+     * @param fullText      附件全文
+     * @param locatorType   定位类型
+     * @param chapterTitle  章节标题(CHAPTER 类型使用)
+     * @param reviewCode    评审代码(REVIEW_CODE 类型使用)
+     * @param tableSelector 表格选择器(TABLE 类型使用)
+     * @return 提取的内容
+     */
+    public String locateContent(String fullText, String locatorType, 
+                                String chapterTitle, String reviewCode, String tableSelector) {
+        if (fullText == null || fullText.isEmpty()) {
+            return "";
+        }
+
+        LocatorType type;
+        try {
+            type = LocatorType.valueOf(locatorType.toUpperCase());
+        } catch (Exception e) {
+            type = LocatorType.FULL_TEXT;
+        }
+
+        switch (type) {
+            case CHAPTER:
+                return getChapterBlock(fullText, chapterTitle);
+            case REVIEW_CODE:
+                return getReviewCodeContent(fullText, reviewCode);
+            case TABLE:
+                return getTableContent(fullText, tableSelector);
+            case FULL_TEXT:
+            default:
+                return fullText;
+        }
+    }
+
+    /**
+     * 根据章节标题提取内容
+     * 参考 001 项目的 LongTextUtil.getChapterBlock
+     */
+    public String getChapterBlock(String content, String targetTitle) {
+        if (content == null || content.isEmpty() || targetTitle == null || targetTitle.isEmpty()) {
+            return "";
+        }
+
+        String[] lines = content.replace("\r\n", "\n").split("\n");
+        
+        // 匹配章节标题的正则:例如 "4.2.3.1 短路电流计算" 或 "一、工作目的"
+        String startRegex = "^([\\d\\.]+|[一二三四五六七八九十]+[、.])\\s*" + Pattern.quote(targetTitle.trim());
+        Pattern pStart = Pattern.compile(startRegex);
+
+        StringBuilder sb = new StringBuilder();
+        boolean found = false;
+        String startHierarchy = "";
+
+        for (String line : lines) {
+            String trimmed = line.trim();
+            if (trimmed.isEmpty()) continue;
+
+            // 过滤掉纯数字(页码干扰)
+            if (trimmed.matches("^\\d+$")) {
+                if (found) sb.append(line).append("\n");
+                continue;
+            }
+
+            if (!found) {
+                if (pStart.matcher(trimmed).find()) {
+                    found = true;
+                    // 提取章节编号
+                    Matcher m = Pattern.compile("^([\\d\\.]+|[一二三四五六七八九十]+)").matcher(trimmed);
+                    if (m.find()) {
+                        startHierarchy = m.group(1);
+                    }
+                    sb.append(line).append("\n");
+                }
+            } else {
+                // 判定是否是下一个同级或更高级章节
+                if (isNextChapter(trimmed, startHierarchy)) {
+                    break;
+                }
+                sb.append(line).append("\n");
+            }
+        }
+
+        log.info("章节定位完成: title={}, found={}, contentLength={}", targetTitle, found, sb.length());
+        return sb.toString().trim();
+    }
+
+    /**
+     * 根据评审代码提取内容
+     * 从表格中找到对应评审代码的行
+     */
+    public String getReviewCodeContent(String content, String reviewCode) {
+        if (content == null || content.isEmpty() || reviewCode == null || reviewCode.isEmpty()) {
+            return "";
+        }
+
+        String[] lines = content.replace("\r\n", "\n").split("\n");
+        StringBuilder result = new StringBuilder();
+        boolean foundCode = false;
+        int contextLines = 0;
+
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i];
+            
+            // 检查是否包含评审代码
+            if (line.contains(reviewCode)) {
+                foundCode = true;
+                // 添加上下文(前后各 5 行)
+                int start = Math.max(0, i - 5);
+                int end = Math.min(lines.length, i + 10);
+                
+                for (int j = start; j < end; j++) {
+                    result.append(lines[j]).append("\n");
+                }
+                break;
+            }
+        }
+
+        // 如果没找到精确匹配,尝试模糊匹配
+        if (!foundCode) {
+            // 尝试匹配类似 "5.1.5" 的模式
+            Pattern codePattern = Pattern.compile("\\b" + Pattern.quote(reviewCode) + "\\b");
+            for (int i = 0; i < lines.length; i++) {
+                if (codePattern.matcher(lines[i]).find()) {
+                    int start = Math.max(0, i - 5);
+                    int end = Math.min(lines.length, i + 10);
+                    for (int j = start; j < end; j++) {
+                        result.append(lines[j]).append("\n");
+                    }
+                    foundCode = true;
+                    break;
+                }
+            }
+        }
+
+        log.info("评审代码定位完成: code={}, found={}, contentLength={}", reviewCode, foundCode, result.length());
+        return result.toString().trim();
+    }
+
+    /**
+     * 根据表格选择器提取表格内容
+     */
+    public String getTableContent(String content, String tableSelector) {
+        if (content == null || content.isEmpty() || tableSelector == null || tableSelector.isEmpty()) {
+            return "";
+        }
+
+        // 查找包含表格选择器的表格区域
+        // Markdown 表格通常以 | 开头
+        String[] lines = content.replace("\r\n", "\n").split("\n");
+        StringBuilder result = new StringBuilder();
+        boolean inTable = false;
+        boolean foundSelector = false;
+
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i].trim();
+            
+            // 检查是否是表格标题或包含选择器
+            if (line.contains(tableSelector)) {
+                foundSelector = true;
+                // 向后查找表格
+                for (int j = i; j < lines.length; j++) {
+                    String tableLine = lines[j];
+                    if (tableLine.trim().startsWith("|") || tableLine.contains("|")) {
+                        inTable = true;
+                        result.append(tableLine).append("\n");
+                    } else if (inTable && !tableLine.trim().isEmpty() && !tableLine.trim().startsWith("|")) {
+                        // 表格结束
+                        break;
+                    } else if (inTable && tableLine.trim().isEmpty()) {
+                        // 空行,可能表格结束
+                        continue;
+                    }
+                }
+                if (inTable) break;
+            }
+        }
+
+        // 如果没找到 Markdown 表格,尝试提取普通文本表格
+        if (!foundSelector || result.length() == 0) {
+            // 返回包含选择器的上下文
+            for (int i = 0; i < lines.length; i++) {
+                if (lines[i].contains(tableSelector)) {
+                    int start = Math.max(0, i - 2);
+                    int end = Math.min(lines.length, i + 50);
+                    for (int j = start; j < end; j++) {
+                        result.append(lines[j]).append("\n");
+                    }
+                    break;
+                }
+            }
+        }
+
+        log.info("表格定位完成: selector={}, found={}, contentLength={}", tableSelector, foundSelector, result.length());
+        return result.toString().trim();
+    }
+
+    /**
+     * 判断是否是下一个章节
+     */
+    private boolean isNextChapter(String line, String startHierarchy) {
+        if (startHierarchy == null || startHierarchy.isEmpty()) {
+            return false;
+        }
+
+        // 匹配数字编号章节
+        if (startHierarchy.matches("^[\\d\\.]+$")) {
+            Pattern p = Pattern.compile("^([\\d\\.]+)\\s+");
+            Matcher m = p.matcher(line);
+            if (m.find()) {
+                String currentHierarchy = m.group(1);
+                // 比较层级
+                int startLevel = startHierarchy.split("\\.").length;
+                int currentLevel = currentHierarchy.split("\\.").length;
+                
+                // 如果当前层级小于等于起始层级,说明是同级或更高级章节
+                if (currentLevel <= startLevel && !currentHierarchy.startsWith(startHierarchy)) {
+                    return true;
+                }
+            }
+        }
+        
+        // 匹配中文编号章节
+        if (startHierarchy.matches("^[一二三四五六七八九十]+$")) {
+            Pattern p = Pattern.compile("^([一二三四五六七八九十]+)[、.]");
+            Matcher m = p.matcher(line);
+            if (m.find()) {
+                String currentNum = m.group(1);
+                // 简单比较:如果是不同的中文数字,认为是下一章节
+                if (!currentNum.equals(startHierarchy)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}

+ 1500 - 0
frontend/vue-demo/package-lock.json

@@ -0,0 +1,1500 @@
+{
+  "name": "lingyue-zhibao-demo",
+  "version": "0.1.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "lingyue-zhibao-demo",
+      "version": "0.1.0",
+      "dependencies": {
+        "@element-plus/icons-vue": "^2.3.1",
+        "@vue-flow/background": "^1.3.2",
+        "@vue-flow/controls": "^1.1.3",
+        "@vue-flow/core": "^1.48.2",
+        "@vue-flow/minimap": "^1.5.4",
+        "axios": "^1.6.0",
+        "element-plus": "^2.4.4",
+        "jszip": "^3.10.1",
+        "marked": "^17.0.3",
+        "pinia": "^2.1.7",
+        "vue": "^3.4.0",
+        "vue-router": "^4.2.5"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^4.5.2",
+        "sass": "^1.69.5",
+        "vite": "^5.0.10"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.6.tgz",
+      "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.6"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.6.tgz",
+      "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
+      "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@element-plus/icons-vue": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
+      "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz",
+      "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.10"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.4.tgz",
+      "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/core": "^1.7.3",
+        "@floating-ui/utils": "^0.2.10"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.10",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz",
+      "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "license": "MIT"
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz",
+      "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "detect-libc": "^2.0.3",
+        "is-glob": "^4.0.3",
+        "node-addon-api": "^7.0.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher-android-arm64": "2.5.6",
+        "@parcel/watcher-darwin-arm64": "2.5.6",
+        "@parcel/watcher-darwin-x64": "2.5.6",
+        "@parcel/watcher-freebsd-x64": "2.5.6",
+        "@parcel/watcher-linux-arm-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm-musl": "2.5.6",
+        "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+        "@parcel/watcher-linux-arm64-musl": "2.5.6",
+        "@parcel/watcher-linux-x64-glibc": "2.5.6",
+        "@parcel/watcher-linux-x64-musl": "2.5.6",
+        "@parcel/watcher-win32-arm64": "2.5.6",
+        "@parcel/watcher-win32-ia32": "2.5.6",
+        "@parcel/watcher-win32-x64": "2.5.6"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-glibc": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+      "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher-linux-x64-musl": {
+      "version": "2.5.6",
+      "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+      "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "name": "@sxzz/popperjs-es",
+      "version": "2.11.7",
+      "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+      "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.56.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz",
+      "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.56.0",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz",
+      "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.17.23",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.23.tgz",
+      "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/lodash-es": {
+      "version": "4.17.12",
+      "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+      "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.20",
+      "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
+      "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
+      "license": "MIT"
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0 || ^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue-flow/background": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/@vue-flow/background/-/background-1.3.2.tgz",
+      "integrity": "sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue-flow/core": "^1.23.0",
+        "vue": "^3.3.0"
+      }
+    },
+    "node_modules/@vue-flow/controls": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/@vue-flow/controls/-/controls-1.1.3.tgz",
+      "integrity": "sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue-flow/core": "^1.23.0",
+        "vue": "^3.3.0"
+      }
+    },
+    "node_modules/@vue-flow/core": {
+      "version": "1.48.2",
+      "resolved": "https://registry.npmmirror.com/@vue-flow/core/-/core-1.48.2.tgz",
+      "integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vueuse/core": "^10.5.0",
+        "d3-drag": "^3.0.0",
+        "d3-interpolate": "^3.0.1",
+        "d3-selection": "^3.0.0",
+        "d3-zoom": "^3.0.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.3.0"
+      }
+    },
+    "node_modules/@vue-flow/minimap": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/@vue-flow/minimap/-/minimap-1.5.4.tgz",
+      "integrity": "sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==",
+      "license": "MIT",
+      "dependencies": {
+        "d3-selection": "^3.0.0",
+        "d3-zoom": "^3.0.0"
+      },
+      "peerDependencies": {
+        "@vue-flow/core": "^1.23.0",
+        "vue": "^3.3.0"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.27.tgz",
+      "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.27",
+        "entities": "^7.0.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz",
+      "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.27",
+        "@vue/shared": "3.5.27"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz",
+      "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.27",
+        "@vue/compiler-dom": "3.5.27",
+        "@vue/compiler-ssr": "3.5.27",
+        "@vue/shared": "3.5.27",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz",
+      "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.27",
+        "@vue/shared": "3.5.27"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.27.tgz",
+      "integrity": "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.27"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.27.tgz",
+      "integrity": "sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.27",
+        "@vue/shared": "3.5.27"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.27.tgz",
+      "integrity": "sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.27",
+        "@vue/runtime-core": "3.5.27",
+        "@vue/shared": "3.5.27",
+        "csstype": "^3.2.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.27.tgz",
+      "integrity": "sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.27",
+        "@vue/shared": "3.5.27"
+      },
+      "peerDependencies": {
+        "vue": "3.5.27"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.27.tgz",
+      "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==",
+      "license": "MIT"
+    },
+    "node_modules/@vueuse/core": {
+      "version": "10.11.1",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz",
+      "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.20",
+        "@vueuse/metadata": "10.11.1",
+        "@vueuse/shared": "10.11.1",
+        "vue-demi": ">=0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "10.11.1",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz",
+      "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "10.11.1",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz",
+      "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
+      "license": "MIT",
+      "dependencies": {
+        "vue-demi": ">=0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "license": "MIT"
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT"
+    },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "d3-selection": "2 - 3"
+      }
+    },
+    "node_modules/d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.19",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+      "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+      "license": "MIT"
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/element-plus": {
+      "version": "2.13.1",
+      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.13.1.tgz",
+      "integrity": "sha512-eG4BDBGdAsUGN6URH1PixzZb0ngdapLivIk1meghS1uEueLvQ3aljSKrCt5x6sYb6mUk8eGtzTQFgsPmLavQcA==",
+      "license": "MIT",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.3.2",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.17.20",
+        "@types/lodash-es": "^4.17.12",
+        "@vueuse/core": "^10.11.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.19",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.3",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.3.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
+      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+      "license": "MIT"
+    },
+    "node_modules/immutable": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.4.tgz",
+      "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "license": "MIT"
+    },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "license": "(MIT OR GPL-3.0-or-later)",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "license": "MIT",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.23",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
+      "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+      "license": "MIT"
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.23",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz",
+      "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash-unified": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
+      "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/lodash-es": "*",
+        "lodash": "*",
+        "lodash-es": "*"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/marked": {
+      "version": "17.0.3",
+      "resolved": "https://registry.npmmirror.com/marked/-/marked-17.0.3.tgz",
+      "integrity": "sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==",
+      "license": "MIT",
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
+      "license": "MIT"
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
+      "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/normalize-wheel-es": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+      "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "license": "(MIT AND Zlib)"
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz",
+      "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.4.4",
+        "vue": "^2.7.0 || ^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "license": "MIT"
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.56.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.56.0.tgz",
+      "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.56.0",
+        "@rollup/rollup-android-arm64": "4.56.0",
+        "@rollup/rollup-darwin-arm64": "4.56.0",
+        "@rollup/rollup-darwin-x64": "4.56.0",
+        "@rollup/rollup-freebsd-arm64": "4.56.0",
+        "@rollup/rollup-freebsd-x64": "4.56.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.56.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.56.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.56.0",
+        "@rollup/rollup-linux-arm64-musl": "4.56.0",
+        "@rollup/rollup-linux-loong64-gnu": "4.56.0",
+        "@rollup/rollup-linux-loong64-musl": "4.56.0",
+        "@rollup/rollup-linux-ppc64-gnu": "4.56.0",
+        "@rollup/rollup-linux-ppc64-musl": "4.56.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.56.0",
+        "@rollup/rollup-linux-riscv64-musl": "4.56.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.56.0",
+        "@rollup/rollup-linux-x64-gnu": "4.56.0",
+        "@rollup/rollup-linux-x64-musl": "4.56.0",
+        "@rollup/rollup-openbsd-x64": "4.56.0",
+        "@rollup/rollup-openharmony-arm64": "4.56.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.56.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.56.0",
+        "@rollup/rollup-win32-x64-gnu": "4.56.0",
+        "@rollup/rollup-win32-x64-msvc": "4.56.0",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT"
+    },
+    "node_modules/sass": {
+      "version": "1.97.3",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.97.3.tgz",
+      "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": "^4.0.0",
+        "immutable": "^5.0.2",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "optionalDependencies": {
+        "@parcel/watcher": "^2.4.1"
+      }
+    },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+      "license": "MIT"
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/vite": {
+      "version": "5.4.21",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+      "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.27",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.27.tgz",
+      "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.27",
+        "@vue/compiler-sfc": "3.5.27",
+        "@vue/runtime-dom": "3.5.27",
+        "@vue/server-renderer": "3.5.27",
+        "@vue/shared": "3.5.27"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.6.4",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
+      "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.0"
+      }
+    }
+  }
+}

+ 149 - 9
frontend/vue-demo/src/components/workflow/RuleWorkflow.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
+import { ref, computed, watch, onMounted, onUnmounted, nextTick, markRaw } from 'vue'
 import { VueFlow, useVueFlow } from '@vue-flow/core'
 import { Background } from '@vue-flow/background'
 import { Controls } from '@vue-flow/controls'
@@ -20,15 +20,18 @@ const props = defineProps({
   projectId: { type: Number, required: true },
   attachments: { type: Array, default: () => [] },
   elements: { type: Array, default: () => [] },
-  rules: { type: Array, default: () => [] }
+  rules: { type: Array, default: () => [] },
+  targetRule: { type: Object, default: null },  // 编辑的目标规则
+  targetElement: { type: Object, default: null }  // 目标要素(单要素模式)
 })
 
 const emit = defineEmits(['save', 'close'])
 
+// 使用 markRaw 避免组件被响应式化
 const nodeTypes = {
-  source: SourceNode,
-  action: ActionNode,
-  element: ElementNode
+  source: markRaw(SourceNode),
+  action: markRaw(ActionNode),
+  element: markRaw(ElementNode)
 }
 
 const nodes = ref([])
@@ -587,10 +590,29 @@ function handleKeydown(event) {
   }
 }
 
+// VueFlow 初始化完成后调用
+const flowInitialized = ref(false)
+function onFlowInit() {
+  console.log('VueFlow initialized, nodes:', nodes.value.length)
+  flowInitialized.value = true
+  // 延迟 fitView 确保节点已渲染
+  setTimeout(() => {
+    console.log('Calling fitView with nodes:', nodes.value.length)
+    if (nodes.value.length > 0) {
+      fitView({ padding: 0.2, maxZoom: 1, includeHiddenNodes: true })
+    }
+  }, 200)
+}
+
 onMounted(() => {
-  if (props.rules && props.rules.length > 0) {
-    loadRulesAsWorkflow(props.rules)
+  // 如果有目标规则,加载该规则的工作流
+  if (props.targetRule) {
+    loadSingleRuleAsWorkflow(props.targetRule)
+  } else if (props.targetElement) {
+    // 新建规则模式:预置目标要素节点
+    initWithTargetElement(props.targetElement)
   }
+  // 新建模式(无 targetRule 和 targetElement):空白画布,不加载任何规则
   
   // 保存初始状态
   saveHistory()
@@ -603,6 +625,124 @@ onUnmounted(() => {
   window.removeEventListener('keydown', handleKeydown)
 })
 
+// 加载单个规则为工作流(编辑模式)
+function loadSingleRuleAsWorkflow(rule) {
+  console.log('loadSingleRuleAsWorkflow:', rule)
+  const newNodes = []
+  const newEdges = []
+  const y = 150
+  
+  const elementId = `element-${rule.id}`
+  const sourceId = `source-${rule.id}`
+  const actionId = `action-${rule.id}`
+  
+  let lastNodeId = null
+  let xPos = 50
+  const nodeSpacing = 250  // 节点间距
+  
+  // 1. 添加来源节点(如果有 inputs)
+  if (rule.inputs && rule.inputs.length > 0) {
+    const input = rule.inputs[0]
+    newNodes.push({
+      id: sourceId,
+      type: 'source',
+      position: { x: xPos, y },
+      data: {
+        nodeType: 'source',
+        subType: input.inputType || 'attachment',
+        label: input.sourceName || input.inputName || '来源',
+        sourceNodeId: input.sourceNodeId,
+        sourceName: input.sourceName || input.inputName,
+        sourceText: input.sourceText
+      }
+    })
+    lastNodeId = sourceId
+    xPos += nodeSpacing
+  }
+  
+  // 2. 添加动作节点(如果不是 quote 类型)
+  if (rule.actionType && rule.actionType !== 'quote') {
+    let prompt = ''
+    try {
+      prompt = rule.actionConfig ? JSON.parse(rule.actionConfig).prompt : ''
+    } catch (e) {}
+    
+    newNodes.push({
+      id: actionId,
+      type: 'action',
+      position: { x: xPos, y },
+      data: {
+        nodeType: 'action',
+        subType: rule.actionType,
+        label: getActionLabel(rule.actionType),
+        actionType: rule.actionType,
+        prompt: prompt
+      }
+    })
+    
+    // 连接来源到动作
+    if (lastNodeId) {
+      newEdges.push({
+        id: `edge-${lastNodeId}-${actionId}`,
+        source: lastNodeId,
+        target: actionId,
+        animated: true,
+        style: { stroke: '#409eff', strokeWidth: 2 }
+      })
+    }
+    lastNodeId = actionId
+    xPos += nodeSpacing
+  }
+  
+  // 3. 添加输出节点(目标要素)
+  newNodes.push({
+    id: elementId,
+    type: 'element',
+    position: { x: xPos, y },
+    data: {
+      nodeType: 'element',
+      label: rule.elementKey,
+      elementKey: rule.elementKey,
+      elementName: getElementName(rule.elementKey)
+    }
+  })
+  
+  // 连接到输出节点
+  if (lastNodeId) {
+    newEdges.push({
+      id: `edge-${lastNodeId}-${elementId}`,
+      source: lastNodeId,
+      target: elementId,
+      animated: true,
+      style: { stroke: '#67c23a', strokeWidth: 2 }
+    })
+  }
+  
+  console.log('Setting nodes:', newNodes.length, 'edges:', newEdges.length)
+  nodes.value = newNodes
+  edges.value = newEdges
+  console.log('After set - nodes:', nodes.value.length, 'edges:', edges.value.length)
+}
+
+// 新建规则模式:预置目标要素节点
+function initWithTargetElement(element) {
+  const elementId = `element-new-${Date.now()}`
+  nodes.value = [{
+    id: elementId,
+    type: 'element',
+    position: { x: 400, y: 150 },
+    data: {
+      nodeType: 'element',
+      label: element.elementName || element.elementKey,
+      elementKey: element.elementKey,
+      elementName: element.elementName || element.elementKey
+    }
+  }]
+  edges.value = []
+  
+  setTimeout(() => fitView({ padding: 0.3 }), 100)
+}
+
 function loadRulesAsWorkflow(rules) {
   const newNodes = []
   const newEdges = []
@@ -808,8 +948,8 @@ defineExpose({
           :default-viewport="{ zoom: 1, x: 0, y: 0 }"
           :min-zoom="0.2"
           :max-zoom="2"
-          fit-view-on-init
           class="vue-flow-wrapper"
+          @init="onFlowInit"
         >
           <Background pattern-color="#aaa" :gap="16" />
           <Controls position="bottom-left" />
@@ -933,7 +1073,7 @@ defineExpose({
 .rule-workflow {
   display: flex;
   flex-direction: column;
-  height: 100%;
+  height: calc(100vh - 54px);  /* 弹窗高度减去 header */
   background: #f5f7fa;
 }
 

+ 9 - 1
frontend/vue-demo/src/components/workflow/nodes/ActionNode.vue

@@ -62,7 +62,8 @@ const borderColor = computed(() => {
 
 <style scoped>
 .workflow-node {
-  min-width: 120px;
+  min-width: 180px;
+  max-width: 240px;
   border-radius: 8px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
   transition: all 0.2s;
@@ -106,5 +107,12 @@ const borderColor = computed(() => {
   background: #f5f7fa;
   padding: 6px 8px;
   border-radius: 4px;
+  max-height: 40px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  line-clamp: 2;
+  -webkit-box-orient: vertical;
 }
 </style>

+ 2 - 1
frontend/vue-demo/src/components/workflow/nodes/ElementNode.vue

@@ -34,7 +34,8 @@ const displayKey = computed(() => {
 
 <style scoped>
 .workflow-node {
-  min-width: 140px;
+  min-width: 160px;
+  max-width: 200px;
   border-radius: 8px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
   transition: all 0.2s;

+ 2 - 1
frontend/vue-demo/src/components/workflow/nodes/SourceNode.vue

@@ -41,7 +41,8 @@ const nodeClass = computed(() => {
 
 <style scoped>
 .workflow-node {
-  min-width: 160px;
+  min-width: 180px;
+  max-width: 220px;
   border-radius: 8px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
   transition: all 0.2s;

+ 41 - 9
frontend/vue-demo/src/views/Editor.vue

@@ -710,10 +710,7 @@
           style="width: 280px"
           :prefix-icon="Search"
         />
-        <el-button size="small" :icon="Plus" @click="showNewRuleDialog = true">添加规则</el-button>
-        <el-button size="small" type="primary" @click="showRuleWorkflow = true; showRuleDialog = false">
-          🔀 工作流
-        </el-button>
+        <el-button size="small" :icon="Plus" @click="openWorkflowForNewRule">添加规则</el-button>
         <el-button
           v-if="rules.length > 0"
           type="success"
@@ -765,7 +762,7 @@
           :key="rule.id"
           class="fp-rule-item"
           :class="{ expanded: expandedRuleId === rule.id }"
-          @click="toggleRuleExpand(rule.id)"
+          @click="openWorkflowForRule(rule)"
         >
           <div class="rule-item-main">
             <span class="rule-action-badge" :class="'action-' + rule.actionType">{{ ruleActionLabel(rule.actionType) }}</span>
@@ -981,17 +978,20 @@
     <!-- 规则工作流弹窗 -->
     <el-dialog
       v-model="showRuleWorkflow"
-      title="规则工作流"
+      :title="workflowTargetRule ? `编辑规则 - ${workflowTargetElement?.elementName || workflowTargetRule.elementKey}` : '新建规则'"
       fullscreen
       :close-on-click-modal="false"
       class="rule-workflow-dialog"
     >
       <RuleWorkflow
         v-if="showRuleWorkflow && currentProjectId"
+        :key="workflowTargetRule?.id || 'new'"
         :project-id="currentProjectId"
         :attachments="attachments"
         :elements="elements"
         :rules="rules"
+        :target-rule="workflowTargetRule"
+        :target-element="workflowTargetElement"
         @save="handleWorkflowSave"
         @close="showRuleWorkflow = false"
       />
@@ -1204,6 +1204,8 @@ const showNewRuleDialog = ref(false)
 const showAttachmentDialog = ref(false)
 const showRuleDialog = ref(false)
 const showRuleWorkflow = ref(false)
+const workflowTargetRule = ref(null)  // 当前编辑的规则(null 表示新建)
+const workflowTargetElement = ref(null)  // 当前编辑的目标要素
 const ruleSearchQuery = ref('')
 const ruleFilterType = ref('all')
 const expandedRuleId = ref(null)
@@ -1422,6 +1424,36 @@ function toggleRuleExpand(ruleId) {
   expandedRuleId.value = expandedRuleId.value === ruleId ? null : ruleId
 }
 
+// 打开工作流编辑器 - 新建规则
+function openWorkflowForNewRule() {
+  workflowTargetRule.value = null
+  workflowTargetElement.value = null
+  showRuleDialog.value = false
+  showRuleWorkflow.value = true
+}
+
+// 打开工作流编辑器 - 编辑现有规则
+async function openWorkflowForRule(rule) {
+  try {
+    // 获取规则完整详情(包含 inputs)
+    const fullRule = await ruleApi.getById(rule.id)
+    workflowTargetRule.value = fullRule
+    // 找到对应的要素
+    const elem = elements.value.find(e => e.elementKey === fullRule.elementKey)
+    workflowTargetElement.value = elem || { elementKey: fullRule.elementKey, elementName: fullRule.elementKey }
+    showRuleDialog.value = false
+    showRuleWorkflow.value = true
+  } catch (e) {
+    console.error('获取规则详情失败:', e)
+    // 降级使用列表中的规则数据
+    workflowTargetRule.value = rule
+    const elem = elements.value.find(e => e.elementKey === rule.elementKey)
+    workflowTargetElement.value = elem || { elementKey: rule.elementKey, elementName: rule.elementKey }
+    showRuleDialog.value = false
+    showRuleWorkflow.value = true
+  }
+}
+
 function normalizeRuleSourceName(name) {
   const s = String(name || '').trim()
   if (!s) return ''
@@ -2592,10 +2624,10 @@ function highlightRunsWithElements(runs, shortMap) {
       const em = seg.em
       const isStatic = em.isStatic
       const borderStyle = isStatic
-        ? 'border:1px dashed #ccc;border-radius:3px;padding:0 2px;cursor:pointer;opacity:0.7;background:#f5f5f5;'
-        : `border:1.5px solid ${darkenColor(em.color)};border-radius:3px;padding:0 2px;cursor:pointer;background:${em.color};color:${darkenColor(em.color)};`
+        ? 'border:1px dashed #ccc;border-radius:4px;padding:2px 6px;cursor:pointer;opacity:0.7;background:#f5f5f5;'
+        : `border:1.5px solid ${darkenColor(em.color)};border-radius:4px;padding:2px 6px;cursor:pointer;background:${em.color};color:${darkenColor(em.color)};`
       const hlClass = isStatic ? 'elem-highlight elem-highlight-static' : 'elem-highlight'
-      html += `<span class="${hlClass}" data-elem-key="${em.elementKey}" data-value-id="${em.valueId || ''}" style="${borderStyle}" contenteditable="false" title="${escapeAttr(em.elementName)}">`
+      html += `<span class="${hlClass}" data-elem-key="${em.elementKey}" data-value-id="${em.valueId || ''}" style="${borderStyle}" title="${escapeAttr(em.elementName)}">`
       for (const g of groups) {
         const run = runs[g.runIdx]
         const slice = escapeHtml(run.text.substring(g.startOffset, g.endOffset))

+ 86 - 0
scripts/upload_attachments.sh

@@ -0,0 +1,86 @@
+#!/bin/bash
+# Upload local attachments to backend for persistent storage
+# Usage: ./upload_attachments.sh <project_id> <access_token>
+
+PROJECT_ID=${1:-10}
+TOKEN=${2:-""}
+API_BASE="${API_SERVER:-http://47.108.80.98:8001}/api/v1"
+ATTACHMENTS_DIR="/home/hws/workspace/GitLab/ay/lingyue-zhibao/frontend/vue-demo/public/attachments"
+
+# Attachment files with display names
+declare -A FILES=(
+  ["att01-核心要素评审情况记录表.docx"]="附件1 成都院核心要素评审情况记录表.docx"
+  ["att02-现场评审分工表.zip"]="附件2 成都院现场评审分工表.zip"
+  ["att03-安全生产标准化通知.pdf"]="附件3 安全生产标准化建设通知.pdf"
+  ["att04-材料真实性说明.png"]="附件4 成都院材料真实性说明.png"
+  ["att05-在建项目一览表.docx"]="附件5 成都院在建项目一览表.docx"
+  ["att06-安全管理制度清单.docx"]="附件6 成都院安全管理制度清单.docx"
+  ["att07-现场评审末次会签到表.png"]="附件7 成都院现场评审末次会签到表.png"
+  ["att08-工作方案.zip"]="附件8 工作方案.zip"
+  ["att09-复审问题建议表.docx"]="附件9 复审问题建议表.docx"
+)
+
+echo "Uploading attachments to project $PROJECT_ID..."
+echo "API Base: $API_BASE"
+echo ""
+
+for filename in "${!FILES[@]}"; do
+  displayName="${FILES[$filename]}"
+  filepath="$ATTACHMENTS_DIR/$filename"
+  
+  if [ ! -f "$filepath" ]; then
+    echo "❌ File not found: $filepath"
+    continue
+  fi
+  
+  echo "📤 Uploading: $displayName"
+  
+  if [ -n "$TOKEN" ]; then
+    response=$(curl -s -X POST "$API_BASE/projects/$PROJECT_ID/attachments/upload" \
+      -H "Authorization: Bearer $TOKEN" \
+      -F "file=@$filepath" \
+      -F "displayName=$displayName")
+  else
+    response=$(curl -s -X POST "$API_BASE/projects/$PROJECT_ID/attachments/upload" \
+      -F "file=@$filepath" \
+      -F "displayName=$displayName")
+  fi
+  
+  # Check response
+  code=$(echo "$response" | grep -o '"code":[0-9]*' | head -1 | cut -d: -f2)
+  if [ "$code" = "200" ]; then
+    id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
+    echo "   ✅ Success (id: $id)"
+  else
+    echo "   ❌ Failed: $response"
+  fi
+done
+
+echo ""
+echo "Done. Verifying attachments..."
+
+if [ -n "$TOKEN" ]; then
+  curl -s "$API_BASE/projects/$PROJECT_ID/attachments" -H "Authorization: Bearer $TOKEN" | python3 -c "
+import sys, json
+try:
+    data = json.load(sys.stdin)
+    atts = data.get('data', [])
+    print(f'Total attachments: {len(atts)}')
+    for a in atts:
+        print(f'  - [{a.get(\"id\")}] {a.get(\"displayName\")} (fileKey: {a.get(\"fileKey\", \"N/A\")})')
+except Exception as e:
+    print(f'Error parsing response: {e}')
+"
+else
+  curl -s "$API_BASE/projects/$PROJECT_ID/attachments" | python3 -c "
+import sys, json
+try:
+    data = json.load(sys.stdin)
+    atts = data.get('data', [])
+    print(f'Total attachments: {len(atts)}')
+    for a in atts:
+        print(f'  - [{a.get(\"id\")}] {a.get(\"displayName\")} (fileKey: {a.get(\"fileKey\", \"N/A\")})')
+except Exception as e:
+    print(f'Error parsing response: {e}')
+"
+fi

Diff do ficheiro suprimidas por serem muito extensas
+ 2222 - 0
附件1.md


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff