<script setup lang="ts">
import { computed, ref } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import {
  CHANGES, type ChangeLevel, type ChangeOrder, type ChangeStatus, type ChangeType,
} from "@/typings/model";
import { getColorWithOpacity } from "@/utils/common";
import HighlightText from "./modules/HighlightText.vue";
import Ed from "./modules/Ed.vue";
import Hazop from "./modules/hazop.vue";
import Jsa from "./modules/jsa.vue";
import Scl from "./modules/scl.vue";
import RiskCheck from "./modules/riskCheck.vue";

const themeStore = useThemeStore();

interface ApplyForm {
  name: string;
  dept: string;
  mainChanges: string[];
  related: string;
  purposes: string[];
  effect: string;
  type: string;
  level: string;
  duration: string;
  urgent: boolean;
  date: any;
  restoreDate: any;
}
const form = ref<ApplyForm>({
  name: "", dept: "", mainChanges: [""], related: "", purposes: [], effect: "",
  type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null,
});
const tab = ref<string>("risk");
const TABS = [
  { id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
  { id: "form", name: "变更申请表", icon: 'akar-icons:file' },
  { id: "risk", name: "风险分析记录表", icon: 'proicons:alert-triangle' },
  { id: "train", name: "变更培训内容", icon: 'lucide:graduation-cap' },
  { id: "pssr", name: "PSSR 检查内容", icon: 'tabler:clipboard-check' },
  { id: "accept", name: "验收评价", icon: 'material-symbols:check-circle-outline' },
  { id: "close", name: "变更关闭确认表", icon: 'quill:folder-open' },
];
const confirmed = ref<Record<string, boolean>>({});
const blockedTabs = ref<string[]>([]);

const aiEnabled = ref<boolean>(true);
const analyzing = ref<boolean>(false);
// ---------- 变更预识别 ----------
const desc = ref<string>(
  "某缩合反应现在控在80℃要反应8小时，车间反馈产能跟不上。我们小试做了几组，90℃下4小时转化率就能到98%，但有个未知杂质会从0.05%涨到0.12%。这个杂质结构还没定，不过不是基因毒性警示结构。想申请把温度提到90℃，同时把反应时间缩到4小时。",
);
const rows = ref<any>(null);
const AI_ROWS = [
  {
    id: 1, item: "工艺流程",
    before: "流程简述（含物料、设备）：某缩合反应，原料A与原料B在缩合反应釜中混合，加热至80℃进行保温反应8小时，反应完成后转入后处理工序。\n设计意图：通过缩合反应制得目标产物，保证转化率和杂质水平。",
    after: "流程简述（含物料、设备）：保温温度提升至90℃，反应时间缩短为4小时，其余流程不变。\n设计意图：在保持转化率98%的前提下缩短反应时间、提升产能。",
    hl: ["保温温度提升至90℃，反应时间缩短为4小时", "在保持转化率98%的前提下缩短反应时间、提升产能"],
  },
  {
    id: 2, item: "设备信息",
    before: "基本信息（名称、设计参数、材质、用途）：缩合反应釜，设计温度100℃，设计压力0.2MPa，材质SS304，用于缩合反应。\n使用信息（安装位置、连接方式、维护策略）：安装于反应工段，夹套加热，按预防性维护计划定期检查。",
    after: "无变化",
  },
  {
    id: 3, item: "运行条件",
    before: "运行参数：反应温度80±2℃，保温时间8小时，压力常压。\n管控措施：DCS温度高报警设定85℃，高高报警90℃并联锁切断加热介质。",
    after: "运行参数：反应温度改为90±2℃，保温时间改为4小时，压力不变。\n管控措施：DCS温度高报警需上调至95℃，高高报警及联锁值需上调至100℃。",
    hl: ["90±2℃", "4小时", "DCS温度高报警需上调至95℃，高高报警及联锁值需上调至100℃"],
  },
  { id: 4, item: "操作方式", before: "DCS自动化控制，远程操作，单人监控。", after: "无变化" },
];
// ---------- AI 变更识别 ----------
const runIdentify = () => {
  if (!desc.value.trim()) { window.$message?.warning("请先输入变更内容描述"); return; }
  analyzing.value = true;
  rows.value = null;
  setTimeout(() => {
    rows.value = AI_ROWS.map((x) => ({ ...x }));
    analyzing.value = false;
    window.$message?.success("AI 变更识别完成，共 4 项，请逐项人工确认（可直接修改）");
  }, 1200);
};

const editing = ref<any>(null);

// ---------- 风险预分析 ----------
const preRiskEdit = ref<any>(null);
const preRiskShown = ref<boolean>(false);
const preRiskText = ref({
  severity: "本次变更为缩合反应温度由80℃提升至90℃、保温时间由8h缩短至4h。变更前后反应体系与物料不变，不新增化学品，不存在新增燃爆、毒害、腐蚀等危害严重性变化；温度提升后仍在溶剂沸点与物料分解温度的安全裕度之内。综上，本次变更未显著增加潜在事件的危害严重程度，事故后果（人员伤亡、财产损失、环境污染）的严重等级与变更前基本一致；但杂质水平由0.05%升至0.12%，需关注长期运行对产品质量与设备结垢的累积影响。",
  probability: "变更引入了新的偏差场景。首先，反应温度更接近溶剂回流温度，温控失效或冷却水中断时，超温溜温概率上升，可能引发冲料；其次，保温时间缩短使反应终点判断窗口变窄，人为误判导致转化率不足或副反应增加的概率上升；再者，未知杂质含量升高（0.05%→0.12%），其热稳定性尚未定性，长期累积可能加速釜壁结垢与搅拌负荷异常；此外，DCS报警/联锁值上调后原有报警裕度改变，误报、漏报概率需重新评估。",
  protection: "变更主要影响与温度控制相关的保护层。原DCS温度高报警85℃、高高报警及联锁90℃已不适用于新工艺窗口，需同步上调至95℃/100℃并重新整定验证；安全阀起跳压力与泄放能力不受本次温度调整直接影响，仍然有效；操作规程与应急预案须修订后，方可继续作为有效的人为保护层。建议投用前完成联锁测试与报警验证，并将杂质含量纳入日常分析计划。",
});
const runRiskAnalysis = () => {
  analyzing.value = true;
  setTimeout(() => {
    analyzing.value = false;
    preRiskShown.value = true;
    window.$message?.success("AI 风险预分析报告已生成（危害严重性 / 事件概率 / 保护措施影响），内容支持手动修改");
  }, 1300);
};
const saveDraft = () => window.$message?.success("已保存为草稿，可在左侧「我的草稿」中继续编辑");

// ---------- 生成变更申请单 ----------
const formGen = ref<string>("idle");
const materialsText = ref("");
const updateDocs = ref<string[]>([]);
const disciplines = ref<string[]>([]);
const tools = ref<string[]>(["HAZOP","JSA", "SCL", "RISK_CHECK"]);
const genForm = () => {
  if (formGen.value === "generating") return;
  formGen.value = "generating";
  window.$message?.info("AI 正在生成变更申请单…（「变更申请表」标签已显示生成中标识）");
  setTimeout(() => {
    const main: string[] = [];
    for (const row of rows.value ?? []) {
      if (row.after && row.after !== "无变化" && row.item === "运行条件") {
        main.push("缩合反应温度从80±2℃提升至90±2℃");
        main.push("保温时间从8小时缩短至4小时");
        main.push("DCS温度高报警从85℃上调至95℃，高高报警及联锁值从90℃上调至100℃");
      }
    }
    form.value = {
      name: "缩合反应温度及时间优化",
      dept: "",
      mainChanges: main.length ? main : ["（请根据识别结果填写主要变更）"],
      related: "更新操作规程、DCS联锁设定值、工艺安全信息、岗位操作法",
      purposes: ["提高生产效能", "改善操作条件"],
      effect: "在保持转化率不低于98%的前提下，反应时间缩短50%，产能提升一倍",
      type: "工艺", level: "一般", duration: "永久", urgent: false, date: "", restoreDate: "",
    };
    materialsText.value = "1. 施工方案（含作业步骤、隔离与置换方案、作业许可）\n2. 保温材料（釜体保温更换，硅酸铝纤维毡，厚度按设计核算）\n3. 紧固件与垫片（法兰螺栓、金属缠绕垫，材质与原设计一致）\n4. 防腐涂料（釜体外表面除锈防腐）\n5. 脚手架搭拆材料及验收记录";
    updateDocs.value = ["PID图纸", "操作规程", "总图", "设备台账"];
    disciplines.value = ["技术", "仪表"];
    signers.value = { 技术: "王仪表（技术部）", 仪表: "王仪表（技术部 · 仪表）" };
    tools.value = ["HAZOP","JSA", "SCL", "通用检查表"];
    currentPlugin.value = "";
    formGen.value = "done";
    tab.value = "form";
    window.$message?.success("变更申请单已生成：AI 自动总结名称、预填内容，均可手动修改");
  }, 1400);
};
// ---------- 审批流程设置 ----------
const approvers = ref<Record<string, string>>({
  部门审核: "李主任（一车间主任）",
  最终批准: "李主任（部门负责人 · 固定）",
});
const signers = ref<Record<string, string>>({});
const sheet = ref<"type" | "level" | "approver" | null>(null);
let demoSeq = 43;
function nextChangeId() {
  return `MOC-2026-R01-${String(demoSeq++).padStart(4, "0")}`;
}
const today = () => new Date().toISOString().slice(0, 10);
interface LedgerRow {
  id: string;
  changeId?: string; // 关联 CHANGES，可打开完整详情
  title: string;
  type: ChangeType;
  level: ChangeLevel;
  duration: "永久" | "临时";
  urgent?: boolean;
  applyTime: string;   // 申请时间
  dept: string;        // 申请部门
  applicant: string;   // 申请人
  planUse: string;     // 计划投用时间
  status: ChangeStatus;
  statusDetail: string; // 当前环节说明
  statusTime: string;  // 状态更新时间
  overdueDays?: number;
  approveTime?: string; // 审批通过时间
  useTime?: string;     // 实际投用时间
  handlers: { name: string; done: boolean }[]; // 接班人（当前待办/已办）
}
const LEDGER_ROWS: LedgerRow[] = [
  { id: "MOC-2026-R01-0035", changeId: "MOC-2026-R01-0035", title: "R-201 反应釜搅拌器电机更换（55kW→75kW 防爆电机）", type: "设备", level: "重要", duration: "永久", applyTime: "2026-07-28 09:12", dept: "一车间", applicant: "张工艺", planUse: "2026-08-10", status: "审批中", statusDetail: "专业会签中（周设备、王仪表待签）", statusTime: "2026-07-30 14:20", handlers: [{ name: "王仪表", done: true }, { name: "周设备", done: false }, { name: "王仪表", done: false }] },
  { id: "MOC-2026-R01-0036", changeId: "MOC-2026-R01-0036", title: "聚合反应温度控制上限调整（78℃→82℃）", type: "工艺", level: "重要", duration: "永久", applyTime: "2026-07-25 10:05", dept: "一车间", applicant: "孙丽", planUse: "2026-08-05", status: "审批中", statusDetail: "安全审查（王海燕）", statusTime: "2026-07-29 16:40", handlers: [{ name: "王海燕", done: false }] },
  { id: "MOC-2026-R02-0038", changeId: "MOC-2026-R02-0038", title: "DCS 罐区液位报警值 LIA-301 调整", type: "工艺", level: "一般", duration: "永久", applyTime: "2026-07-20 08:30", dept: "一车间", applicant: "吴强", planUse: "2026-07-31", status: "待PSSR", statusDetail: "培训 / PSSR 资料已传 · 待确认投用", statusTime: "2026-07-27 11:15", approveTime: "2026-07-26 16:40", handlers: [{ name: "吴强", done: false }] },
  { id: "MOC-2026-R01-0039", changeId: "MOC-2026-R01-0039", title: "V-102 安全阀起跳压力临时调整（紧急补办）", type: "设备", level: "一般", duration: "临时", urgent: true, applyTime: "2026-07-15 19:40", dept: "一车间", applicant: "张工艺", planUse: "2026-07-16（已投用）", status: "待关闭", statusDetail: "待关闭确认（资料更新核查中）", statusTime: "2026-07-27 09:02", approveTime: "2026-07-15 21:05", useTime: "2026-07-16 06:30", overdueDays: 4, handlers: [{ name: "李主任", done: false }] },
  { id: "MOC-2026-R03-0040", changeId: "MOC-2026-R03-0040", title: "原料环己烷供应商变更（新增 B 供应商）", type: "管理", level: "重要", duration: "永久", applyTime: "2026-07-18 14:22", dept: "供应部", applicant: "刘芳", planUse: "2026-08-01", status: "实施中", statusDetail: "实施准备中 · 培训 / PSSR 资料待上传", statusTime: "2026-07-26 10:30", handlers: [{ name: "刘芳", done: false }] },
  { id: "MOC-2026-R02-0031", title: "冷冻盐水泵 P-103 叶轮材质变更", type: "设备", level: "一般", duration: "永久", applyTime: "2026-06-28 09:50", dept: "二车间", applicant: "周涛", planUse: "2026-07-15", status: "待验收", statusDetail: "变更验收（72h 运行验证）", statusTime: "2026-07-24 15:44", overdueDays: 7, handlers: [{ name: "李主任", done: false }] },
  { id: "MOC-2026-R01-0029", title: "操作规程修订（夏季工况）", type: "管理", level: "一般", duration: "临时", applyTime: "2026-06-20 11:26", dept: "一车间", applicant: "张工艺", planUse: "2026-07-01", status: "审批中", statusDetail: "部门审核（李主任）", statusTime: "2026-06-21 08:40", handlers: [{ name: "李主任", done: false }] },
  { id: "MOC-2026-R02-0033", title: "包装线贴标机控制程序升级", type: "设备", level: "一般", duration: "永久", applyTime: "2026-07-08 13:35", dept: "二车间", applicant: "吴强", planUse: "2026-08-02", status: "已批准", statusDetail: "待实施（计划 8 月 2 日投用）", statusTime: "2026-07-22 09:12", handlers: [{ name: "周设备", done: false }] },
  { id: "MOC-2026-R03-0027", title: "化验室通风橱整体更换", type: "设备", level: "一般", duration: "永久", applyTime: "2026-06-12 15:10", dept: "质量部", applicant: "陈静", planUse: "2026-06-25", status: "已关闭", statusDetail: "关闭确认完成，已归档", statusTime: "2026-07-02 10:18", handlers: [{ name: "钱峰", done: true }] },
  { id: "MOC-2026-R01-0024", title: "氮气管网压力分级调整", type: "工艺", level: "重要", duration: "永久", applyTime: "2026-05-30 09:00", dept: "一车间", applicant: "孙丽", planUse: "2026-06-20", status: "已关闭", statusDetail: "关闭确认完成，已归档", statusTime: "2026-06-28 16:30", handlers: [{ name: "李主任", done: true }, { name: "刘副总", done: true }] },
];
const ROLES = [
  { id: "applicant", name: "申请人", person: "张工艺", org: "一车间 · 工艺组", desc: "发起变更申请、AI 辅助判定、跟踪进度" },
  { id: "process", name: "专业会签（仪表）", person: "王仪表", org: "技术部 · 仪表科", desc: "专业会签、HAZOP 分析、确认连带变更" },
  { id: "process2", name: "专业会签（设备）", person: "周设备", org: "设备管理部 · 设备科", desc: "专业会签、设备完整性检查、行动项落实" },
  { id: "safety", name: "安全工程师", person: "王海燕", org: "安全环保部", desc: "安全审查、PSSR 检查表确认、风险分析组织" },
  { id: "dept", name: "车间主任", person: "李主任", org: "一车间", desc: "部门审核、现场验收、变更关闭确认" },
  { id: "vp", name: "分管领导", person: "刘副总", org: "公司领导", desc: "重要变更最终批准、移动端审批" },
  { id: "admin", name: "系统管理员", person: "刘敏", org: "信息化部", desc: "流程配置、字典维护、权限分配" },
  { id: "quality", name: "质量工程师", person: "陈质量", org: "质量部", desc: "质量会签、产品合格判定、检验方案确认" },
  { id: "viewer", name: "非审批人员", person: "吴新", org: "相关部门", desc: "以信息查看为主，不参与变更流程" },
];
function nowStr() {
  const d = new Date();
  const p = (n: number) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
}
type DemoListener = () => void;
const demoListeners = new Set<DemoListener>();
function emitDemo() { demoListeners.forEach((f) => f()); }
function submitChange(c: ChangeOrder) {
  CHANGES.unshift(c);
  const cur = c.flow.find((n) => n.status === "current");
  LEDGER_ROWS.unshift({
    id: c.id,
    changeId: c.id,
    title: c.title,
    type: c.type,
    level: c.level,
    duration: c.duration,
    urgent: c.urgent || undefined,
    applyTime: nowStr(),
    dept: c.org.split("·")[0].trim(),
    applicant: c.applicant,
    planUse: c.date || "待定",
    status: c.status,
    statusDetail: cur ? `${cur.name}（待处理）` : "审批中",
    statusTime: nowStr(),
    handlers: (cur?.roles ?? []).map((rid) => ({ name: ROLES.find((r) => r.id === rid)?.person ?? rid, done: false })),
  });
  emitDemo();
}
// ---------- 提交 ----------
const submitApply = () => {
  if (!desc.value.trim()) { window.$message?.warning("请先录入变更描述并完成 AI 识别"); return; }
  if (!form.value) { window.$message?.warning("请先在「变更预识别」页点击「生成变更申请单」，生成申请表后再提交"); return; }
  const unconfirmed = ["form", "risk", "train", "pssr", "accept", "close"].filter((t) => !confirmed.value[t]);
  if (unconfirmed.length) {
    blockedTabs.value = unconfirmed;
    window.$message?.warning(`全部标签页确认后方可提交，请先完成以下标签页的「内容确认完毕」（标签栏已用红点标出）：${unconfirmed.map((t) => TAB_NAME[t]).join("、")}`);
    return;
  }
  blockedTabs.value = [];
  const id = nextChangeId();
  const level: ChangeLevel = form.value.level === "重要" ? "重要" : "一般";
  const flow: any[] = level === "重要"
    ? [
        { name: "部门审核", kind: "审批", roles: ["dept"], status: "current" },
        { name: "专业会签", kind: "会签", roles: ["process", "process2"], status: "todo", locked: true, members: [{ name: "王仪表（仪表）", done: false }, { name: "周设备（设备）", done: false }] },
        { name: "安全审查", kind: "审批", roles: ["safety"], status: "todo", locked: true },
        { name: "分管领导最终批准", kind: "最终批准", roles: ["vp"], status: "todo", locked: true },
      ]
    : [
        { name: "部门审核", kind: "审批", roles: ["dept"], status: "current" },
        { name: "专业审批", kind: "审批", roles: ["process"], status: "todo", locked: true },
        { name: "部门负责人批准", kind: "最终批准", roles: ["dept"], status: "todo", locked: true },
      ];
  const order: ChangeOrder = {
    id,
    title: form.value.name || desc.value.slice(0, 24) || "未命名变更",
    type: (form.value.type || "工艺") as ChangeType,
    level,
    duration: form.value.duration === "临时" ? "临时" : "永久",
    urgent: !!form.value.urgent,
    status: "审批中",
    applicant: ROLES[0].person,
    org: form.value.dept || ROLES[0].org,
    device: "—",
    date: today(),
    purpose: form.value.purposes.join("、") || "提高生产效能",
    effect: form.value.effect || "—",
    before: rows.value?.map((x) => `${x.item}：${x.before}`).join("；") || desc.value.slice(0, 60),
    after: rows.value?.map((x) => `${x.item}：${x.after}`).join("；") || "—",
    related: form.value.related ? form.value.related.split("、") : updateDocs.value,
    materials: materialsText.value ? materialsText.value.split("\n").filter(Boolean) : [],
    riskTools: tools.value,
    flow,
  };
  submitChange(order);
  window.$message?.success(`已提交 ${id}，进入「部门审核」节点（台账 / 审批中心 / 角标已联动）`);
  // emit("openChange", order);
};

const PURPOSE_OPTIONS = [
  "满足市场需求", "提高生产效能", "改善操作条件", "增加安全性能",
  "达到环保要求", "法律合规整改", "减少能耗物损", "稳定系统运行",
  "完善管理制度", "达到质量改进", "设备可靠性升级",
];
const DEPT_APPROVER_OPTS = ["李主任（一车间主任）", "赵主任（二车间主任）", "陈主任（三车间主任）"].map((x) => ({ label: x, value: x }));
const SIGNER_POOL = ["王仪表（技术部）", "王仪表（技术部 · 仪表）", "周设备（设备科 · 电气）", "王海燕（安全环保部）", "王海燕（质量部）"];
const signerOpts = (cur: string) => [...new Set([cur, ...SIGNER_POOL])].map((x) => ({ label: x, value: x }));
const levelScores = ref<number[]>([2, 2, 3, 1, 2, 3]);
const totalScore = computed(() => levelScores.value.reduce((a, b) => a + b, 0));
const levelResult = computed(() => (totalScore.value >= 20 ? "重要变更" : "一般变更"));
const RISK_TOOLS_FORM = [
  { key: "HAZOP", label: "HAZOP" },
  { key: "JSA", label: "JSA" },
  { key: "SCL", label: "设备SCL" },
  { key: "RISK_CHECK", label: "风险检查表" },
];
const recommendTools = () => {
  const lv = form.value?.level === "重要" ? "重要" : "一般";
  tools.value = lv === "重要" ? ["HAZOP", "JSA", "检查表法", "通用检查表"] : ["JSA", "检查表法", "通用检查表"];
  window.$message?.success(`已按「${lv}变更」规则匹配风险分析工具，可继续手动重选`);
};

const files = ref<string[]>(["小试实验报告.pdf", "杂质初步分析.docx"]);
const previewFile = ref<string | null>(null);
const DISCIPLINE_SIGNER: Record<string, string> = {
  技术: "王仪表（技术部）", 仪表: "王仪表（技术部 · 仪表）", 电气: "周设备（设备科 · 电气）", 环保: "王海燕（安全环保部）", 质量: "王海燕（质量部）",
};
const toggleDiscipline = (d:any) => {
  const result = disciplines.value.reduce((acc: Record<string, string>, key: string) => {
    acc[key] = DISCIPLINE_SIGNER[key];  // 如果键不存在，值为 undefined
    return acc;
  }, {});
  signers.value = {...result};
};

const TAB_NAME: Record<string, string> = {
  pre: "变更预识别", form: "变更申请表", risk: "风险分析记录表",
  train: "变更培训内容", pssr: "PSSR检查内容", accept: "验收评价", close: "变更关闭确认表",
};
const confirmTab = (t: string) => {
  const v = !confirmed.value[t];
  confirmed.value = { ...confirmed.value, [t]: v };
  if (v) window.$message?.success(`已确认${TAB_NAME[t]}内容完毕（标签页栏已标记 ✓）`);
  else window.$message?.info(`已取消${TAB_NAME[t]}的确认标记`);
};
const regenerate = () => window.$message?.success(`演示：已根据变更描述重新生成${TAB_NAME[tab.value]}（本页内容已刷新）`);
const download = () => window.$message?.success(`演示：${TAB_NAME[tab.value]}已下载（Word/PDF）`);

const downloadPdf = () => window.$message?.success(`演示：${previewFile.value} 已下载`);
const LEVEL_DIMS = [
  {
    name: "1. 设备影响", note: "不合适的材质将会导致",
    options: [
      { label: "对设备带来一定的损害及腐蚀", score: 1 },
      { label: "对设备带来中等的损害及腐蚀", score: 2 },
      { label: "对设备带来明显的损害及腐蚀", score: 3 },
      { label: "对设备带来重大的损害及腐蚀", score: 4 },
    ],
  },
  {
    name: "2. 财务影响", note: "工艺设计错误将会导致",
    options: [
      { label: "一定损失，a<1万人民币", score: 1 },
      { label: "中等损失，直接损失1万≤a<10万人民币", score: 2 },
      { label: "大量损失，直接损失10万≤a<100万人民币", score: 3 },
      { label: "巨额损失，直接损失100万人民币≤a", score: 4 },
    ],
  },
  {
    name: "3. 质量影响", note: "",
    options: [
      { label: "对质量带来一定的风险（非关键工席、非关键设备设施的变更）", score: 1 },
      { label: "对质量带来中等风险（B类原料替代、生产辅助设备变更）", score: 2 },
      { label: "对质量带来明显的风险（主要工艺参数、中间体生产工艺变更）", score: 3 },
      { label: "对质量有很高风险（主要工艺路线、关键工艺参数变更）", score: 4 },
    ],
  },
  {
    name: "4. 设计的复杂程度", note: "工艺设计包含了",
    options: [
      { label: "少量的简单系统（单个设备或单个部件）", score: 1 },
      { label: "大量的简单系统（多个单类设备或部件）", score: 2 },
      { label: "至少一个复杂系统（涉及多设备/工艺相应调整）", score: 3 },
      { label: "大量的复杂系统（需要装置配套改造调整）", score: 4 },
    ],
  },
  {
    name: "5. 工艺设计的成熟度", note: "",
    options: [
      { label: "被频繁使用，且被证明为专利方设计", score: 1 },
      { label: "被少量使用，或被证明为专利方设计", score: 2 },
      { label: "被相关工业设计方法或参考文献认可（非专利方）", score: 3 },
      { label: "新的，未发表的", score: 4 },
    ],
  },
  {
    name: "6. 工艺危害", note: "物料性质和工艺条件",
    options: [
      { label: "无危害（常压、-10℃≤温度<40℃、无毒、无腐蚀不燃）", score: 1 },
      { label: "可燃/负压/低正压（0~0.3MPa）、40℃≤温度<80℃", score: 2 },
      { label: "易燃/中压（0.3~1.0MPa）、80℃≤温度<150℃", score: 3 },
      { label: "极度危害/高压（>1.0MPa）、温度≥150℃", score: 4 },
    ],
  },
];
const finishApprover = () => {
  sheet.value = null; 
  window.$message?.success('审批流程已更新（底部固定行摘要同步刷新）');
}
// 风险识别分析记录表
const TOOL_CARDS = [
  { key: "HAZOP" as const, name: "HAZOP 分析", desc: "节点-偏离结构化分析：原因 / 后果 / LS 风险矩阵 / 安全措施" },
  { key: "JSA" as const, name: "JSA 分析", desc: "作业步骤分解：危害因素识别与 LS 风险分级管控" },
  { key: "SCL" as const, name: "设备 SCL", desc: "设备 / 管理检查项目对照标准逐项核查" },
  { key: "RISK_CHECK" as const, name: "风险检查表", desc: "变更通用检查项：是 / 否 / 不涉及逐项判定，输出风险描述与管控措施" },
];
const pluginDone = ref<Record<string, number>>({});
const riskRecords = ref<any>([]);
const updRec = (i: number, patch: Partial<any>) => {
  riskRecords.value = riskRecords.value.map((x, j) => (j === i ? { ...x, ...patch } : x));
};
const lvlNext = (v: string) => (v === "高" ? "中" : v === "中" ? "低" : "高");
const pssrGroups = ref<{ g: string; items: any[] }[]>([
  { g: "一、风险分析行动项落实", items: [
    { c: "联锁跳车电流设定值校核并留存记录", p: "投用前" },
    { c: "电缆与开关容量校核升级完成", p: "投用前" },
    { c: "操作人员培训完成并考核合格", p: "投用前" },
  ] },
  { g: "二、现场设备安装检查确认", items: [
    { c: "电机安装就位，联轴器对中合格", p: "投用前" },
    { c: "地脚螺栓紧固，减振垫安装到位", p: "投用前" },
    { c: "电缆接线、防爆格兰头密封良好", p: "投用前" },
  ] },
  { g: "三、保护措施确认", items: [
    { c: "联锁跳车设定值与新电机额定电流匹配", p: "投用前" },
    { c: "电机外壳接地、防护罩完好", p: "投用前" },
    { c: "紧急停机按钮功能试验正常", p: "投用前" },
  ] },
  { g: "四、资料归档确认", items: [
    { c: "P&ID（图号 PID-R201-03）已更新并受控", p: "关闭前" },
    { c: "设备台账、备件清单已更新", p: "关闭前" },
    { c: "培训签到表、考核记录已归档", p: "关闭前" },
  ] },
]);
const toPssr = (i: number) => {
  const rec = riskRecords.value[i];
  if (!rec || rec.pssr) return;
  pssrGroups.value = pssrGroups.value.map((g:any, gi:any) => gi === 0
    ? { ...g, items: [...g.items, { c: `${rec.suggest}（来源：风险分析记录表 · ${rec.item}）`, p: "投用前" }] }
    : g);
  updRec(i, { pssr: true });
  window.$message?.success("建议措施已转为 PSSR 行动项（见「PSSR 检查内容」· 一、风险分析行动项落实）");
};
const toPssrAll = () => {
  const rest = riskRecords.value.map((x:any, i:any) => (x.pssr ? -1 : i)).filter((i:any) => i >= 0);
  if (!rest.length) { window.$message?.info("所有建议措施均已转为 PSSR 行动项"); return; }
  const items = rest.map((i:any) => ({ c: `${riskRecords.value[i].suggest}（来源：风险分析记录表 · ${riskRecords.value[i].item}）`, p: "投用前" }));
  pssrGroups.value = pssrGroups.value.map((g:any, gi:any) => gi === 0 ? { ...g, items: [...g.items, ...items] } : g);
  riskRecords.value = riskRecords.value.map((x:any) => ({ ...x, pssr: true }));
  window.$message?.success(`已将 ${rest.length} 条建议措施全部转为 PSSR 行动项（见「PSSR 检查内容」）`);
};
const aiOrganize = () => {
  const gen: any[] = [
    { item: "超温溜温冲料", scene: "反应温度提升至 90℃ 后更接近溶剂回流温度，温控失效或冷却水中断时反应溜温，釜压骤升可能导致冲料与物料喷溅", inherent: "高", existing: "DCS 温度高报 95℃、高高报及联锁 100℃（随变更上调并重新整定）；紧急冷却系统有效", suggest: "1. 投用前完成联锁传动测试并留存记录；2. 投用首周每班复核温度趋势，纳入交接班巡检", residual: "中", source: "AI 整理" },
    { item: "未知杂质累积影响", scene: "未知杂质含量由 0.05% 升至 0.12%，热稳定性尚未定性，长期累积加速釜壁结垢与搅拌负荷异常", inherent: "中", existing: "小试热稳定性筛查；安全阀泄放能力不受本次调整影响", suggest: "1. 将杂质含量纳入日常分析计划（每周 1 次，连续 8 周）；2. 跟踪结垢趋势，安排周期清洗", residual: "低", source: "AI 整理" },
    { item: "联锁 / 报警设定失效", scene: "报警 / 联锁值上调后（高报 85→95℃、联锁 90→100℃），原有报警裕度改变，存在误报、漏报风险", inherent: "中", existing: "参数修改执行申请-复核双人确认", suggest: "1. 联锁逻辑图与台账同步更新并归档；2. 联锁测试纳入 PSSR 必查项", residual: "低", source: "AI 整理" },
  ];
  riskRecords.value = [...riskRecords.value, ...gen];
  window.$message?.success("AI 已根据变更内容自动整理形成风险分析记录表（3 项），支持手动修改");
};
// ---------- 变更培训 ----------
const trainText = ref(
`培训对象：R-201 岗位操作人员（4 人）、维修班（2 人）
培训方式：现场讲解 + 操作演示

一、变更内容
M-201A 搅拌电机由 55kW 更换为 75kW 防爆电机（ExdⅡBT4 → ExdⅡCT4），额定电流 102A → 138A；电缆与开关容量同步升级，联锁跳车电流设定值同步校核调整。

二、变化的操作方式
1. 搅拌器启动：变更前直接启动、观察电流 ≤102A；变更后启动后核对电流 ≤138A，确认变频器参数已更新（注意：首次启动空载试运行 30 分钟）
2. 高粘度工况操作：变更前粘度 >3000cP 需降速运行；变更后粘度 ≤6000cP 可全速运行（注意：严禁超过 6000cP 上限）
3. 过载跳闸处置：变更前复位后直接重启；变更后先查明原因并记录，联锁复位需班长确认（注意：跳闸电流设定值已调整）

三、潜在风险与应对措施
1. 风险：电机 / 电缆过热 → 应对：投用首周每班测温并记录，红外点检复核
2. 风险：联锁设定错误导致拒动 / 误动 → 应对：投用前完成跳车试验并留存记录
3. 风险：防爆接合面或格兰头密封失效 → 应对：检查防爆接合面与格兰头，严禁带电开盖`
);
// ---------- PSSR 检查内容 ----------
const pssrDone = ref<Record<string, { by: string; at: string }>>({});
const pssrNa = ref<Record<string, boolean>>({});
const pssrActive = computed(() => pssrGroups.value.filter((g:any) => !pssrNa.value[g.g]));
const pssrTotal = computed(() => pssrActive.value.reduce((a:any, g:any) => a + g.items.length, 0));
const pssrDoneCount = computed(() => pssrActive.value.reduce((a:any, g:any) => a + g.items.filter((it:any) => pssrDone.value[it.c]).length, 0));
const pssrAllDone = computed(() => pssrDoneCount.value === pssrTotal.value && (pssrTotal.value > 0 || (pssrGroups.value.length > 0 && pssrGroups.value.every((g) => !!pssrNa.value[g.g]))));
const pssrFileRef = ref<any>(null);
const pssrSheet = ref<any>(null);
  const closeDocs = ref([
  { name: "PID 图纸（PID-R201-03，受控最新版）", cat: "图纸与规程类", scope: "是", archived: true },
  { name: "操作规程（含超温应急处置卡）", cat: "图纸与规程类", scope: "是", archived: false },
  { name: "应急预案修订", cat: "图纸与规程类", scope: "不涉及", archived: false },
  { name: "总图更新", cat: "图纸与规程类", scope: "否", archived: false },
  { name: "DCS 联锁台账（新报警 / 联锁值）", cat: "台账与制度类", scope: "是", archived: false },
  { name: "设备台账与备件清单", cat: "台账与制度类", scope: "是", archived: true },
  { name: "风险分析报告（HAZOP / 检查表法）", cat: "过程与结果文件类", scope: "是", archived: true },
  { name: "培训签到与考核记录", cat: "过程与结果文件类", scope: "是", archived: true },
  { name: "PSSR 检查记录（含现场照片）", cat: "过程与结果文件类", scope: "是", archived: false },
  { name: "验收报告与化验数据", cat: "过程与结果文件类", scope: "是", archived: false },
]);
const onPssrFile = (e: Event) => {
  const input = e.target as HTMLInputElement;
  const f = input.files?.[0]; input.value = "";
  if (!f) return;
  pssrSheet.value = f.name;
  const at = today();
  const n = { ...pssrDone.value };
  pssrGroups.value.forEach((g:any) => { if (!pssrNa.value[g.g]) g.items.forEach((it:any) => { n[it.c] = { by: "纸质确认表上传", at }; }); });
  pssrDone.value = n;
  const archGroup = pssrGroups.value[pssrGroups.value.length - 1];
  if (archGroup && !pssrNa.value[archGroup.g]) {
    closeDocs.value = closeDocs.value.map((d:any) => (d.name.includes("PID") || d.name.includes("设备台账") || d.name.includes("培训签到") ? { ...d, archived: true } : d));
  }
  window.$message?.success(`已上传 PSSR 纸质确认表「${f.name}」：不做内容识别，上传即视为全部检查项完成确认（「不涉及」组除外），原件归档至变更档案`);
};

// ---------- 验收评价 ----------
const acceptRows = ref([
  { id: 1, item: "反应转化率", basis: "转化率不低于 98%", standard: "转化率 ≥ 98%（连续 3 批化验合格）", method: "化验室逐批取样分析，出具检验报告", owner: "车间 · 张工艺", result: "待验收" },
  { id: 2, item: "单批反应时间 / 产能", basis: "反应时间缩短 50%、产能提升一倍", standard: "保温时间 4h±0.5h，产能提升 ≥ 80%", method: "DCS 批次记录统计，对比变更前 30 天均值", owner: "车间 · 李主任", result: "待验收" },
  { id: 3, item: "未知杂质含量", basis: "杂质水平受控、产品质量合格", standard: "杂质 ≤ 0.15%，产品质量指标全部合格", method: "每批化验跟踪，连续 5 批数据趋势评估", owner: "质量 · 王海燕", result: "待验收" },
  { id: 4, item: "温控与联锁有效性", basis: "温度窗口上调后保护层有效", standard: "温度波动 ≤ ±2℃，高报/联锁动作正确率 100%", method: "DCS 趋势核查 + 联锁测试记录复核", owner: "仪表 · 王仪表", result: "待验收" },
  { id: 5, item: "设备运行状态", basis: "负荷变化后设备长周期稳定", standard: "搅拌电流、釜壁温度无异常趋势，无泄漏", method: "设备巡检 + 振动/温度记录评估", owner: "设备 · 周设备", result: "待验收" },
]);
const acceptNote = ref("");

// ---------- 变更关闭确认表 ----------
const closeFold = ref<Record<string, boolean>>({});
const uploadDoc = ref<string | null>(null);
const uploadSel = ref<string | null>(null);
const UPDATE_DOC_KW: Record<string, string> = { PID图纸: "PID", 操作规程: "操作规程", 总图: "总图", 设备台账: "设备台账", 工艺卡片: "工艺卡片", 联锁台账: "联锁台账", 应急预案: "应急预案" };
const needUpdate = (name: string) => updateDocs.value.some((d:any) => name.includes(UPDATE_DOC_KW[d] ?? d));

const changeScope = (d: any, v: string) => {
  closeDocs.value = closeDocs.value.map((x:any) => (x.name === d.name ? { ...x, scope: v } : x)); 
  if (v !== d.scope) window.$message?.info(`演示：「${d.name}」改判为「${v}」，改判理由与操作人已留痕`);
}
const uploadDocInfo = computed(() => {
  if (!uploadDoc.value) return null;
  const doc = closeDocs.value.find((x) => x.name === uploadDoc.value);
  const base = uploadDoc.value.replace(/[（(].*$/, "");
  return {
    storePath: `\\\\PLANT-DMS\\变更管理\\2026\\MOC-2026-R01-0041\\${doc?.cat ?? ""}\\`,
    candidates: [`${base}_RevC_受控版.pdf`, `${base}_2026-08 更新版.docx`],
    base,
  };
});
const openDir = () => {
  window.$message?.info('演示：已在文档管理系统中打开该目录')
}
const uploadFiles = () => {
  uploadSel.value = `本地文件：${uploadDocInfo.value?.base}_最新版.pdf`; 
  window.$message?.info('演示：已接收拖拽 / 选择的本地文件');
}
const uploadConfirm = () => {
  closeDocs.value = closeDocs.value.map((x) => (x.name === uploadDoc.value ? { ...x, archived: true } : x)); 
  window.$message?.success(`「${uploadDoc.value}」已上传并归档（${uploadSel.value}，受控版本，哈希留痕）`); 
  uploadDoc.value = null;
}

const pluginDrawer = ref<boolean>(false);
const currentPlugin = ref<string>("");
// 插件选择
const toggleTool = (t: any) => {
  if (!tools.value.includes(t.key))
  tools.value = tools.value.includes(t.key) ? tools.value.filter((x) => x !== t.key) : [...tools.value, t.key];
  currentPlugin.value = t.key;
  pluginDrawer.value = true;
};


const exportAll = (items: any[], mode: "manual" | "ai") => {
  riskRecords.value = [...riskRecords.value, ...items];
  if (mode === "ai") {
    pluginDrawer.value = false;
    currentPlugin.value = '';
    window.$message?.success(`AI 已自动整理形成风险分析记录表（新增 ${items.length} 项），支持手动修改`);
  } else {
    window.$message?.success(`已选入 ${items.length} 条风险记录，可继续挑选或返回查看记录表`);
  }
}
const onBack = () => {
  pluginDrawer.value = false;
}


</script>

<template>
<NSpace vertical :size="16">
  <!-- 发起变更 -->
  <NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
    <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
      <!-- 顶部 -->
      <div 
        class="border-b bg-[var(--bg-color)]"
        :style="{
          '--bg-color': getColorWithOpacity(themeStore.themeColor, 0.1)
        }"
      >
        <div 
          class="flex flex-wrap items-center gap-x-3 gap-y-2 border-b px-4 py-2 border-[var(--border-color)]"
          :style="{'--border-color': getColorWithOpacity(themeStore.themeColor, 0.2)}"
        >
          <span class="whitespace-nowrap text-sm font-medium text-slate-600">变更名称 <span class="text-red-500">*</span></span>
          <NInput v-model:value="form.name" maxlength="28" show-count placeholder="AI 自动总结（28 字以内），可手动修改" class="!w-420px" />
          <span class="ml-auto whitespace-nowrap text-sm text-slate-500">变更编号</span>
          <NInput value="MOC-2026-R01-0041" disabled class="!w-44" />
        </div>
        <div class="flex items-center gap-1 overflow-x-auto px-3 pt-2">
          <div v-for="t in TABS" :key="t.id" @click="tab = t.id"
            class="flex items-center gap-1.5 whitespace-nowrap px-3 py-2 text-sm transition cursor-pointer border hover:bg-white/60 rounded-t-lg"
            :class="tab === t.id ? 'border-b-transparent border-[var(--border-color)] bg-white text-[var(--active-color)]':'border-transparent text-slate-500'"
            :style="{
              '--border-color': getColorWithOpacity(themeStore.themeColor, 0.3),
              '--active-color': themeStore.themeColor
              }"
          >
            <Icon :icon="t.icon" class="size-14px" />
            {{ t.name }}
            <span v-if="t.id === 'form' && formGen === 'generating'"
              class="flex items-center gap-1 rounded-full border border-violet-300 bg-violet-50 px-1.5 py-0.5 text-[10px] text-violet-600">
              <Icon icon="ri:loader-4-fill" class="size-14px animate-spin" />
              AI 生成中
            </span>
            <Icon v-if="confirmed[t.id]" icon="material-symbols:check-circle-outline" class="size-14px text-emerald-500" />
            <span v-if="!confirmed[t.id] && blockedTabs.includes(t.id)" class="h-1.5 w-1.5 rounded-full bg-red-500" title="本页尚未确认完毕" />
          </div>
        </div>
      </div>
      <!-- 中间 -->
      <div class="p-4">
        <div class="space-y-4" :class="tab === 'pre'?'scroll':'scroll2'">
          <!-- ===== 变更预识别 ===== -->
          <template v-if="tab === 'pre'">
            <!-- 变更内容描述 -->
            <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex items-center justify-between px-4 py-2 border-b">
                <div class="flex items-center">
                  <Icon icon="lucide:sparkles" class="size-16px text-violet-500" />
                  <span class="font-semibold text-base ml-2">变更内容描述</span>
                </div>
                <NButton v-if="aiEnabled" ghost type="primary" @click="runIdentify" :disabled="analyzing">
                  <Icon v-if="analyzing" icon="ri:loader-4-fill" class="size-16px animate-spin" />
                  <Icon v-else icon="fluent:wand-24-regular" class="size-16px" /> 
                  变更识别
                </NButton>
                <NTag type="info" v-else>AI 未启用</NTag>
              </div>
              <div class="p-4">
                <NInput type="textarea" v-model:value="desc" rows="4" placeholder="请输入变更内容描述，例如：将反应釜R-101的搅拌速度从120rpm提高至180rpm，同时将反应温度从80℃提高至95℃…" />
                <div v-if="!aiEnabled" class="mt-2 flex items-center gap-2 rounded-lg bg-amber-50 px-3 py-2 text-xs text-amber-700">
                  <Icon icon="proicons:alert-triangle" class="size-14px" />
                  AI 辅助功能未启用（可在「系统设置 → AI 辅助功能」开启），识别结果与申请表需手动填写。
                </div>
              </div>
            </div>
            <!-- 变更识别结果 -->
            <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex items-center justify-between px-4 py-2 border-b">
                <div class="flex items-center">
                  <Icon icon="lucide:list-checks" class="size-16px text-emerald-500" />
                  <span class="font-semibold text-base ml-2">{{rows ? `变更识别结果（${rows.length}项）` : '变更识别结果（空表 · 可手动录入，或由 AI 识别生成）'}}</span>
                </div>
                <span v-if="rows" class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                  ✨ AI 生成 · 需人工确认
                </span>
              </div>
              <div class="p-4">
                <div class="overflow-x-auto">
                  <table class="w-full min-w-[640px] border-collapse text-sm">
                    <thead>
                      <tr class="bg-slate-50 text-left text-slate-500">
                        <th class="w-28 border-b px-3 py-2 font-medium">变更项目</th>
                        <th class="border-b px-3 py-2 font-medium">变更前</th>
                        <th class="border-b px-3 py-2 font-medium">变更后</th>
                        <th class="w-10 border-b px-2 py-2" />
                      </tr>
                    </thead>
                    <tbody>
                      <tr v-for="row in rows ?? []" :key="row.id" class="align-top">
                        <td class="border-b px-3 py-3">
                          <NInput 
                            v-if="editing === `${row.id}-item`"
                            class="text-left"
                            type="textarea"
                            size="small"
                            autofocus
                            v-model:value="row.item" 
                            :autosize="{ minRows: 1 }"
                            placeholder="请输入"
                            @blur="editing = null"
                          />
                          <p v-else class="font-medium text-slate-700 hover:text-[#17407F] cursor-pointer"  @click="editing = `${row.id}-item`" title="点击修改项目名称">
                            {{ row.item }}
                          </p>
                        </td>
                        <td v-for="field in (['before', 'after'])" :key="field" class="border-b px-3 py-3">
                          <NInput 
                            v-if="editing === `${row.id}-${field}`"
                            class="text-left"
                            type="textarea"
                            size="small"
                            autofocus
                            v-model:value="row[field]" 
                            :autosize="{ minRows: 1 }"
                            placeholder="请输入"
                            @blur="editing = null"
                          />
                          <p v-else class="min-h-30px cursor-pointer rounded-lg border bg-slate-50/60 px-2.3 py-0.55 text-left text-sm leading-relaxed text-slate-700 transition hover:border-[#6E93C6] hover:bg-white" @click="editing = `${row.id}-${field}`" title="点击编辑">
                            <HighlightText v-if="field === 'after'" :text="row[field]" :hl="row.hl" />
                            <span v-else class="whitespace-pre-line">{{ row[field] }}</span>
                          </p>
                        </td>
                        <td class="border-b px-2 py-3 text-center">
                          <NButton text @click="rows = rows && rows.filter((x) => x.id !== row.id)" class="text-slate-300 hover:text-red-500" title="删除此行">
                            <Icon icon="line-md:close-circle" class="size-16px" />
                          </NButton>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                </div>
                <div class="mt-3">
                  <NButton ghost
                    @click="rows = [...(rows ?? []), { id: Date.now(), item: '新项目', before: '', after: '' }]">
                    <Icon icon="ic:round-plus" class="size-16px" /> 添加行
                  </NButton>
                </div>
              </div>
            </div>
            <!-- 风险预分析报告 -->
            <div v-if="preRiskShown" class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex items-center justify-between px-4 py-2 border-b">
                <div class="flex items-center">
                  <Icon icon="lucide:list-checks" class="size-16px text-emerald-500" />
                  <span class="font-semibold text-base ml-2">风险预分析报告</span>
                </div>
                <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                  ✨ AI 生成 · 需人工确认
                </span>
              </div>
              <div class="p-4">
                <div class="space-y-4">
                  <div v-for="s in ([{ key: 'severity', name: '危害严重性分析' }, { key: 'probability', name: '事件概率分析' }, { key: 'protection', name: '保护措施影响分析' }] as const)" :key="s.key" class="rounded-lg border">
                    <div class="flex items-center gap-2 border-b bg-slate-50/60 px-4 py-2">
                      <span class="text-sm font-semibold text-slate-700">{{ s.name }}</span>
                      <NButton text type="primary" class="ml-auto text-xs hover:underline" @click="preRiskEdit = preRiskEdit === s.key ? null : s.key">
                        {{ preRiskEdit === s.key ? "完成" : "编辑" }}
                      </NButton>
                    </div>
                    <NInput v-if="preRiskEdit === s.key" type="textarea" :bordered="false" v-model:value="preRiskText[s.key]" autofocus rows="6" />
                    <p v-else class="whitespace-pre-wrap px-3 py-1.5 text-sm leading-relaxed text-slate-700">{{ preRiskText[s.key] }}</p>
                  </div>
                  <div class="rounded-lg bg-amber-50 px-3 py-2 text-xs leading-5 text-amber-700">
                    AI 风险预分析仅供参考，用于申请阶段的快速研判；正式风险识别请前往「风险分析记录表」标签页，使用 HAZOP / JSA / 检查表法开展并留存记录。
                  </div>
                </div>
              </div>
            </div>
          </template>
          <!-- ===== 变更申请表 ===== -->
          <template v-if="tab === 'form' && form">
            <!-- 申请部门 / 申请人 -->
            <div class="grid gap-4 grid-cols-2">
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">申请部门 <span class="text-red-500">*</span></div>
                <NInput v-model:value="form.dept" placeholder="请输入申请部门" />
              </div>
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">申请人</div>
                <NInput v-model:value="ROLES[0].person" disabled />
              </div>
            </div>
            <!-- 变更目的 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">变更目的</div>
              <div class="rounded-lg border border-[#ACC6E5] bg-[#EAF0F9]/40 p-3">
                <NCheckboxGroup v-model:value="form.purposes">
                  <div class="flex flex-wrap gap-y-2 gap-x-4">
                    <NCheckbox v-for="p in PURPOSE_OPTIONS" :key="p" :value="p" :label="p" />
                  </div>
                </NCheckboxGroup>
              </div>
            </div>
            <!-- 预期效果 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">预期效果</div>
              <NInput type="textarea" v-model:value="form.effect" rows="2" placeholder="" />
            </div>
            <!-- 主要变更内容 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">主要变更内容 <span class="text-red-500">*</span></div>
              <div class="flex-1 space-y-2">
                <div v-for="(m, i) in form.mainChanges" :key="i" class="flex items-center gap-2">
                  <span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span>
                  <NInput v-model:value="m as string" placeholder="" />
                  <NButton text type="error" @click="form = { ...form, mainChanges: form.mainChanges.filter((_, j) => j !== i) }">
                    <Icon icon="line-md:close-circle" class="size-16px" />
                  </NButton>
                </div>
                <NButton text type="primary" class="text-xs" @click="form = { ...form, mainChanges: [...form.mainChanges, ''] }">
                  <Icon icon="ic:round-plus" class="size-14px" /> 添加变更点
                </NButton>
              </div>
            </div>
            <!-- 连带变更内容 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">连带变更内容</div>
              <div class="flex-1 space-y-2">
                <div v-for="(m, i) in (form.related ? form.related.split('、') : [''])" :key="i" class="flex items-center gap-2">
                  <span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span>
                  <NInput v-model:value="m as string" placeholder="连带变更点（如：更新操作规程）" />
                  <NButton text type="error" @click="form = { ...form, related: (form.related ? form.related.split('、') : ['']).filter((_, j) => j !== i).join('、') }">
                    <Icon icon="line-md:close-circle" class="size-16px" />
                  </NButton>
                </div>
                <NButton text type="primary" class="text-xs" @click="form = { ...form, related: form.related ? `${form.related}、` : '、' }">
                  <Icon icon="ic:round-plus" class="size-14px" /> 添加连带变更点
                </NButton>
              </div>
            </div>
            <!-- 变更类型 / 变更等级 -->
            <div class="grid gap-4 grid-cols-2">
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">变更类型 <span class="text-red-500">*</span></div>
                <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
                  <NRadioGroup v-model:value="form.type">
                    <div class="flex gap-x-2">
                      <NRadio v-for="t in ['工艺', '设备', '管理']" :key="t" :value="t">{{t}}</NRadio>
                    </div>
                  </NRadioGroup>
                  <NButton text type="primary" class="ml-auto text-xs hover:underline" @click="sheet = 'type'">
                    <Icon icon="lucide:sparkles" class="size-12px" />
                    AI 判定说明
                  </NButton>
                </div>
              </div>
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">变更等级 <span class="text-red-500">*</span></div>
                <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
                  <NRadioGroup v-model:value="form.level">
                    <div class="flex gap-x-2">
                      <NRadio v-for="t in ['一般', '重要']" :key="t" :value="t">{{t}}</NRadio>
                    </div>
                  </NRadioGroup>
                  <NButton text type="primary" class="ml-auto text-xs hover:underline" @click="sheet = 'level'">
                    <Icon icon="lucide:list-checks" class="size-12px" /> 等级判定表 ({{ totalScore }}分) →
                  </NButton>
                </div>
              </div>
            </div>
            <!-- 变更时限 / 风险分析 -->
            <div class="grid gap-4 grid-cols-2">
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">变更时限 <span class="text-red-500">*</span></div>
                <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
                  <NRadioGroup v-model:value="form.duration">
                    <div class="flex gap-x-2">
                      <NRadio v-for="t in ['永久', '临时']" :key="t" :value="t">{{t}}</NRadio>
                    </div>
                  </NRadioGroup>
                  <NCheckbox v-model:checked="form.urgent">紧急</NCheckbox>
                </div>
              </div>
              <div class="flex gap-3">
                <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">风险分析</div>
                <div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg bg-slate-50 px-3 py-2">
                  <NButton size="small" round v-for="t in RISK_TOOLS_FORM" :key="t.key" @click="toggleTool(t.key)" title="点击选用 / 取消；AI 辅助分析在各工具插件内使用"
                    class="text-sm"
                    :ghost="tools.includes(t.key) ? false : true"
                    :type="tools.includes(t.key) ? 'primary' : 'default'"
                  >
                    {{ t.label }}
                  </NButton>
                  <NButton type="primary" ghost dashed size="small" round @click="recommendTools" class="text-xs">
                    按等级重新推荐
                  </NButton>
                </div>
              </div>
            </div>
            <!-- 计划投用时间 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">计划投用时间</div>
              <div class="flex flex-1 flex-wrap items-center gap-2">
                <NDatePicker v-model:value="form.date" type="date" class="!max-w-[220px]" />
                <template v-if="form.duration === '临时'">
                  <span class="whitespace-nowrap text-sm text-slate-500">计划恢复时间 <span class="text-red-500">*</span></span>
                  <NDatePicker v-model:value="form.restoreDate" type="date" class="!max-w-[220px]" />
                  <span class="text-xs text-amber-600">临时变更到期系统自动提醒恢复原状</span>
                </template>
                <span v-else class="whitespace-nowrap text-xs text-slate-300">（勾选「临时」后在此并排填写计划恢复时间）</span>
              </div>
            </div>
            <div v-if="form.urgent" class="text-xs text-red-500">紧急变更为单独标记：走快速通道，事后限期补办风险分析与完整审批</div>
            <!-- 所需材料 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">所需材料</div>
              <div class="flex-1 space-y-2">
                <div class="text-[11px] text-slate-400">施工所需材料（生成变更申请表时系统自动生成，支持手动修改）</div>
                <NInput type="textarea" v-model:value="materialsText" rows="4" placeholder="点击「生成变更申请单」后由系统按变更内容自动生成，也可直接填写" />
              </div>
            </div>
            <!-- 资料上传 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">资料上传</div>
              <div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg border border-dashed p-3">
                <NButton size="small" class="text-xs" v-for="f in files" :key="f" @click="previewFile = f" title="点击在线预览">
                  <Icon icon="akar-icons:file" class="size-14px" />
                  {{ f }}
                </NButton>
                <NButton @click="files = [...files, `资料_${files.length + 1}.pdf`]">
                  <Icon icon="material-symbols:upload" class="size-16px" /> 资料上传
                </NButton>
                <span class="text-xs text-slate-400">（点击附件名弹窗预览，支持 PDF / Word / 图片在线查看）</span>
              </div>
            </div>
            <!-- 需更新的资料 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">需更新的资料</div>
              <div class="flex flex-1 flex-wrap items-center gap-x-4 gap-y-1.5 rounded-lg bg-slate-50 px-3 py-2">
                <NCheckboxGroup v-model:value="updateDocs">
                  <div class="flex flex-wrap gap-y-2 gap-x-4">
                    <NCheckbox v-for="p in ['PID图纸', '操作规程', '总图', '设备台账', '工艺卡片', '联锁台账', '应急预案']" :key="p" :value="p" :label="p" />
                  </div>
                </NCheckboxGroup>
                <span class="self-center text-xs text-slate-400">（多选，变更关闭前逐项确认上传最新版本）</span>
              </div>
            </div>
            <!-- 涉及专业 -->
            <div class="flex gap-3">
              <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">涉及专业</div>
              <div class="flex-1">
                <div class="flex flex-wrap items-center gap-x-4 gap-y-1.5 rounded-lg bg-slate-50 px-3 py-2">
                  <NCheckboxGroup v-model:value="disciplines" @update:value="toggleDiscipline">
                    <div class="flex flex-wrap gap-y-2 gap-x-4">
                      <NCheckbox v-for="p in ['技术', '仪表', '电气', '环保', '质量']" :key="p" :value="p" :label="p" />
                    </div>
                  </NCheckboxGroup>
                  <span class="self-center text-xs text-slate-400">（多选，决定审批流中专业会签/审批的路由范围）</span>
                </div>
                <div v-if="Object.keys(signers).length > 0" class="mt-1.5 flex flex-wrap items-center gap-1.5 text-xs text-slate-500">
                  <Icon icon="lucide:users" class="size-12px" />已联动专业会签：
                  <NTag size="small" type="info" v-for="(p, d) in signers" :key="d">{{ d }} · {{ p }}</NTag>
                  <span class="text-slate-400">（在底部「审批流程设置」中可调整人选）</span>
                </div>
              </div>
            </div>

          </template>
          <!-- ===== 风险分析记录表 ===== -->
          <template v-if="tab === 'risk'">
            <div class="border p-3" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex flex-wrap items-center gap-2">
                <span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
                  <Icon icon="lucide:shield-alert" class="size-16px" />
                  风险识别工具（按变更等级匹配）
                </span>
                <NTag size="small" :type="form?.level === '重要' ? 'error' : 'default'">
                  {{ form?.level === '重要' ? '重要变更' : '一般变更' }}
                </NTag>
                <span class="text-xs text-slate-400">
                  {{ form?.level === '重要' ? '规则：HAZOP 必做，同时开展 JSA 与检查表法（SCL）' : '规则：JSA + 检查表法（SCL），涉及工艺安全边界的应升级 HAZOP' }}；可在申请表「风险分析」栏手动重选
                </span>
              </div>
              <div class="grid gap-2 grid-cols-4 mt-3">
                <div v-for="t in TOOL_CARDS" :key="t.key" :title="t.desc"
                  class="flex items-center gap-2 rounded-lg border px-3 py-2 transition"
                  :class="pluginDone[t.key] !== undefined ? 'border-[var(--theme-color)] bg-[var(--theme-color)]' : tools.includes(t.key) ? 'border-[var(--theme-color)] bg-white' : 'border-slate-200 bg-white'"
                  :style="{'--theme-color': themeStore.themeColor}"
                >
                  <div class="min-w-0 flex-1">
                    <div 
                      class="flex items-center gap-1.5 text-xs font-semibold"
                      :class="pluginDone[t.key] !== undefined ? 'text-white' : tools.includes(t.key) ? 'text-[var(--theme-color)]' : 'text-slate-500'"
                      :style="{'--theme-color': themeStore.themeColor}"
                    >
                      <Icon icon="fluent:wand-24-regular" class="size-16px" />
                      {{ t.name }}
                    </div>
                    <div class="mt-0.5 text-[10px]" :class="pluginDone[t.key] !== undefined ? 'text-white/75' : 'text-slate-400'">
                      {{ pluginDone[t.key] !== undefined ? `已使用并分析了 ${pluginDone[t.key]} 条内容` : '未使用此工具分析' }}
                    </div>
                  </div>
                  <NButton size="small" class="text-xs" :type="pluginDone[t.key] !== undefined ? 'default' : tools.includes(t.key) ? 'primary' : 'default'" 
                    @click="toggleTool(t)">
                    使用
                  </NButton>
                </div>
              </div>
            </div>

            <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex flex-wrap items-center gap-2 bg-slate-100 px-3 py-2">
                <span class="flex items-center gap-1.5 text-sm font-medium text-slate-700">
                  <Icon icon="proicons:alert-triangle" class="size-16px text-amber-500" />
                  风险分析记录表
                </span>
                <span class="text-[11px] text-slate-400">共 {{ riskRecords.length }} 项</span>
                <span class="ml-auto hidden text-[11px] text-slate-400 lg:block">插件结果「入表」或 AI 自动整理形成；单元格点击修改，风险等级点击切换</span>
                <NButton ghost size="small" color="#7c3aed" class="text-xs" @click="toPssrAll">
                  <Icon icon="tabler:clipboard-check" class="size-14px" />
                  全部转 PSSR 行动项
                </NButton>
                <NButton ghost size="small" type="primary" class="text-xs" @click="aiOrganize">
                  <Icon icon="lucide:sparkles" class="size-12px" />
                  AI 自动整理
                </NButton>
              </div>
              <div v-if="riskRecords.length === 0" class="px-3 py-6 text-center text-xs text-slate-400">
                尚无风险记录：调用上方插件完成分析后逐条「入表」，或点击「AI 自动整理」形成记录表
              </div>
              <div v-else class="overflow-x-auto p-3">
                <table class="w-full min-w-[960px] border-collapse text-xs">
                  <thead>
                    <tr>
                      <th v-for="h in ['风险项', '场景描述', '固有风险', '现有措施', '建议措施', '剩余风险', '来源', '操作']" :key="h"
                        class="border bg-slate-50 px-2 py-1.5 text-[11px] font-medium text-slate-500">{{ h }}</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr v-for="(rec, i) in riskRecords" :key="i">
                      <td class="border px-1.5 py-1 align-top font-medium text-slate-700">
                        <Ed :v="rec.item" @update="(x: string) => updRec(i, { item: x })" />
                      </td>
                      <td class="border px-1.5 py-1 align-top"><Ed :v="rec.scene" @update="(x: string) => updRec(i, { scene: x })" /></td>
                      <td class="border px-1.5 py-1 text-center align-top">
                        <NTag v-if="rec.inherent==='高'" type="error" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { inherent: lvlNext(rec.inherent) as '高' | '中' | '低' })">
                          {{ rec.inherent }}
                        </NTag>
                        <NTag v-if="rec.inherent==='中'" type="warning" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { inherent: lvlNext(rec.inherent) as '高' | '中' | '低' })">
                          {{ rec.inherent }}
                        </NTag>
                        <NTag v-if="rec.inherent==='低'" type="success" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { inherent: lvlNext(rec.inherent) as '高' | '中' | '低' })">
                          {{ rec.inherent }}
                        </NTag>
                      </td>
                      <td class="border px-1.5 py-1 align-top"><Ed :v="rec.existing" @update="(x: string) => updRec(i, { existing: x })" /></td>
                      <td class="border px-1.5 py-1 align-top"><Ed :v="rec.suggest" @update="(x: string) => updRec(i, { suggest: x })" /></td>
                      <td class="border px-1.5 py-1 text-center align-top">
                        <NTag v-if="rec.residual==='高'" type="error" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { residual: lvlNext(rec.residual) as '高' | '中' | '低' })">
                          {{ rec.residual }}
                        </NTag>
                        <NTag v-if="rec.residual==='中'" type="warning" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { residual: lvlNext(rec.residual) as '高' | '中' | '低' })">
                          {{ rec.residual }}
                        </NTag>
                        <NTag v-if="rec.residual==='低'" type="success" size="small" class="cursor-pointer" title="点击切换等级" @click="updRec(i, { residual: lvlNext(rec.residual) as '高' | '中' | '低' })">
                          {{ rec.residual }}
                        </NTag>
                      </td>
                      <td class="border px-1.5 py-1 text-center align-top">
                        <NTag size="small" type="info">{{ rec.source }}</NTag>
                      </td>
                      <td class="border px-1.5 py-1 text-center align-top">
                        <NTag v-if="rec.pssr" size="small" :color="{ color: '#f5f3ff', textColor: '#7c3aed', borderColor: '#f5f3ff' }">已转 PSSR</NTag>
                        <NButton text size="tiny" color="#7c3aed" v-else class="hover:underline" @click="toPssr(i)">转 PSSR</NButton>
                        <NButton text type="error" size="tiny" class="mt-2" @click="riskRecords = riskRecords.filter((_, j) => j !== i)">删除</NButton>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
          </template>
          <!-- 变更培训内容 -->
          <template v-if="tab === 'train'">
            <div class="flex flex-wrap items-center gap-3 mb-3">
              <span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
                <Icon icon="lucide:graduation-cap" class="size-16px text-violet-500" />
                变更培训内容
              </span>
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <span class="ml-auto text-xs text-slate-400">AI 生成内容均可点击修改自定义；审批通过后，相关人员在 PC / 移动端完成学习与签到确认</span>
            </div>
            <!-- 培训内容 -->
            <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex flex-wrap items-center gap-2 bg-slate-100 px-3 py-2">
                <span class="text-sm font-medium text-slate-700">培训内容</span>
                <span class="text-[11px] text-slate-400">含培训对象、培训方式、变更内容、操作方式、潜在风险与应对措施，一个文本框自动分段换行</span>
                <span class="ml-auto text-[11px] text-slate-400">内容支持直接修改</span>
              </div>
              <NInput v-model:value="trainText" type="textarea" :bordered="false" :autosize="{minRows: 4}" class="font-mono text-sm" placeholder="" />
            </div>
          </template>
          <!-- PSSR 检查内容 -->
          <template v-if="tab === 'pssr'">
            <div class="flex flex-wrap items-center gap-3">
              <span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
                <Icon icon="tabler:clipboard-check" class="size-16px text-violet-500" />
                PSSR 投用前安全检查内容
              </span>
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <NTag size="small" :type="pssrAllDone ? 'success' : 'info'">
                已完成 {{ pssrDoneCount }}/{{ pssrTotal }} 项
              </NTag>
              <span class="flex items-center gap-2">
                <NButton ghost size="small" class="text-xs" title="上传 PSSR 纸质确认表照片 / 扫描件（JPG / PNG / PDF），系统不做内容识别，上传即视为全部检查项完成确认" @click="pssrFileRef?.click()">
                  <Icon icon="material-symbols:upload" class="size-14px" />
                  上传纸质确认表
                </NButton>
                <input ref="pssrFileRef" type="file" accept="image/*,.pdf" class="hidden" @change="onPssrFile" />
                <span class="hidden text-xs text-slate-400 xl:inline">申请阶段：点击条目内容直接编辑，可增删项，完成时点互斥选择；审批通过后逐项点击确认（PC / 移动端均可），全部完成自动推送属地负责人</span>
              </span>
            </div>
            <div v-if="pssrSheet" class="flex items-center gap-2 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-2 text-xs text-emerald-700">
              <Icon icon="ix:success" class="size-16px" />
              已上传 PSSR 纸质确认表「{{ pssrSheet }}」：上传即视为全部检查项完成确认（未做内容识别），原件已归档至变更档案
            </div>
            <div v-if="pssrAllDone" class="flex items-center gap-2 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-2.5 text-xs text-emerald-700">
              <Icon icon="ix:success" class="size-16px" />
              全部 PSSR 检查项已确认完成，「具备投用条件」信息已自动推送属地负责人（车间 / 装置主任），可进行投用确认
            </div>

            <div v-for="(g, gi) in pssrGroups" :key="g.g" class="border" :class="pssrNa[g.g] ? 'opacity-70' : ''" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex items-center bg-slate-100 px-3 py-2 text-sm font-medium text-slate-700">
                {{ g.g }}
                <NTag v-if="pssrNa[g.g]" size="small" class="ml-2">不涉及</NTag>
                <span v-else class="ml-2 text-xs font-normal text-slate-400">{{ g.items.filter((it) => pssrDone[it.c]).length }}/{{ g.items.length }} 已确认</span>
                <NButton text type="primary" class="ml-auto text-xs font-normal hover:underline"
                  :title="pssrNa[g.g] ? '恢复本组检查清单' : '本次变更不涉及本组内容时点击标记，标记后不计入完成统计'"
                  @click="pssrNa = { ...pssrNa, [g.g]: !pssrNa[g.g] }">
                  {{ pssrNa[g.g] ? '恢复清单' : '标记不涉及' }}
                </NButton>
                <NButton text type="primary" v-if="!pssrNa[g.g]" class="ml-3 text-xs font-normal hover:underline"
                  @click="pssrGroups = pssrGroups.map((x, xi) => xi === gi ? { ...x, items: [...x.items, { c: '新检查项（点击直接编辑内容）', p: '投用前' }] } : x)">
                  <Icon icon="ic:round-plus" class="size-14px" /> 添加检查项
                </NButton>
              </div>
              <div v-if="pssrNa[g.g]" class="border-t bg-slate-50 px-3 py-3 text-xs text-slate-400">
                本组内容经评估本次变更不涉及（如：无现场安装作业 / 不改变保护措施 / 无需归档资料），已不计入 PSSR 完成统计；如需恢复检查清单，点击右上角「恢复清单」。
              </div>
              <template v-else>
                <div v-for="(it, i) in g.items" :key="`${gi}-${i}`" class="flex flex-wrap items-center gap-3 border-t px-3 py-2.5 text-sm">
                  <span class="w-5 text-slate-400">{{ i + 1 }}</span>
                  <NInput 
                    v-if="editing === `pssr-c-${gi}-${i}`"
                    class="flex-1"
                    v-model:value="it.c"
                    type="textarea"
                    autofocus 
                    rows="2"
                    @blur="editing = null"
                  />
                  <button v-else @click="editing = `pssr-c-${gi}-${i}`" title="点击编辑内容"
                    class="min-w-0 flex-1 rounded-md px-1 text-left text-slate-800 transition hover:bg-[#EAF0F9] bg-transparent"
                  >
                    {{ it.c }}
                  </button>
                  <span class="flex items-center gap-1 py-1">
                    <NRadio :checked="it.p === '投用前'" :value="'投用前'" label="投用前完成"
                      @change="pssrGroups = pssrGroups.map((x, xi) => xi === gi ? { ...x, items: x.items.map((y, yi) => yi === i ? { ...y, p: '投用前' } : y) } : x)" />
                    <NRadio :checked="it.p === '关闭前'" :value="'关闭前'" label="关闭前完成"
                      @change="pssrGroups = pssrGroups.map((x, xi) => xi === gi ? { ...x, items: x.items.map((y, yi) => yi === i ? { ...y, p: '关闭前' } : y) } : x)" />
                  </span>
                  <NButton text type="error" title="删除该项"
                    @click="pssrGroups = pssrGroups.map((x, xi) => xi === gi ? { ...x, items: x.items.filter((_, yi) => yi !== i) } : x)"
                  >
                    <Icon icon="iconamoon:trash" class="size-16px" />
                  </NButton>
                </div>
              </template>
            </div>
            <div class="rounded-lg bg-violet-50/70 px-4 py-2.5 text-xs text-violet-700">
              完成时点说明：「投用前完成 / 关闭前完成」为互斥选择，点击即可切换。投用前完成的项未确认时属地负责人无法确认投用；关闭前完成的项不阻塞投用，但纳入关闭校验清单，未完成时变更主管部门专责无法确认关闭。
            </div>
            <div class="rounded-lg bg-violet-50/70 px-4 py-2.5 text-xs text-violet-700">
              清单由 AI 依据变更类型与风险分析自动生成，专业人员审核后随审批发布；确认方式不作硬性要求——可在 PC 端直接点击确认，也可在移动端拍照上传作为佐证（系统自动记录确认人与日期）；资料归档组确认后自动同步「变更关闭确认表」显示已归档。
            </div>
          </template>
          <!-- 验收评价 -->
          <template v-if="tab === 'accept'">
            <div class="flex flex-wrap items-center gap-3">
              <span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
                <Icon icon="ix:success" class="size-18px text-emerald-500" />
                验收评价（依据预期效果设定可量化验收标准）
              </span>
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <span class="ml-auto text-xs text-slate-400">申请阶段由 AI 依据「预期效果」生成量化标准与验收方式，可手动修改；投用运行后由车间和各专业按标准验收并给出评估结论</span>
            </div>
            <div 
              class="space-y-1.5 rounded-lg border border-[var(--border-color)] bg-[var(--bg-color)] px-4 py-3 text-xs leading-6 text-slate-700"
              :style="{
                '--bg-color': getColorWithOpacity(themeStore.themeColor, 0.1),
                '--border-color': getColorWithOpacity(themeStore.themeColor, 0.2)
              }"
            >
              <div><b :style="{color: themeStore.themeColor}">变更目的：</b>{{ form?.purposes?.join('、') || '提高生产效能、改善操作条件' }}</div>
              <div><b :style="{color: themeStore.themeColor}">预期效果：</b>{{ form?.effect || '在保持转化率不低于98%的前提下，反应时间缩短50%，产能提升一倍' }}</div>
              <div 
                class="border-t border-[var(--border-color)] pt-1.5 text-slate-500"
                :style="{'--border-color': getColorWithOpacity(themeStore.themeColor, 0.2)}"
              >
                <b :style="{color: themeStore.themeColor}">对应说明：</b>验收项目由预期效果逐条拆解设定，各项目对应的预期效果来源见表格「验收项目」列下方灰色小字（点击可修改）；验收时须逐项对照预期效果判定达成程度。
              </div>
            </div>

            <div class="overflow-x-auto border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <table class="w-full min-w-[900px] border-collapse text-sm">
                <thead>
                  <tr class="bg-slate-100 text-left text-slate-600">
                    <th class="w-8 px-3 py-2 font-medium">#</th>
                    <th class="w-56 px-3 py-2 font-medium">验收项目（含对应预期效果）</th>
                    <th class="px-3 py-2 font-medium">量化验收标准</th>
                    <th class="px-3 py-2 font-medium">验收方式（可执行）</th>
                    <th class="w-32 px-3 py-2 font-medium">责任单位/人</th>
                    <th class="px-3 py-2 font-medium">评估结论</th>
                    <th class="w-12 px-2 py-2 font-medium text-center">操作</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="(row, i) in acceptRows" :key="row.id" class="align-top">
                    <td class="border-t px-3 py-2.5 text-slate-400">{{ i + 1 }}</td>
                    <td class="border-t px-3 py-2.5">
                      <NInput
                        v-if="editing === `acc-${row.id}-item`"
                        class="text-xs"
                        v-model:value="row.item"
                        type="textarea"
                        autofocus
                        rows="2"
                        @blur="editing = null"
                      />
                      <button v-else @click="editing = `acc-${row.id}-item`" title="点击修改"
                        class="bg-transparent block w-full rounded-md px-2 py-1 text-left text-xs font-medium leading-relaxed text-slate-700 transition hover:bg-[#EAF0F9]"
                      >
                        {{ row.item }}
                      </button>
                      <NInput 
                        v-if="editing === `acc-${row.id}-basis`"
                        class="text-[11px]"
                        size="small"
                        v-model:value="row.basis"
                        autofocus
                        @blur="editing = null"
                      />
                      <button v-else @click="editing = `acc-${row.id}-basis`" title="本项目对应的预期效果来源，点击修改"
                        class="bg-transparent mt-0.5 block w-full rounded px-2 text-left text-[11px] text-slate-400 transition hover:bg-[#EAF0F9] hover:text-slate-500"
                      >
                        对应预期：{{ row.basis || '（点击填写对应预期效果）' }}
                      </button>
                    </td>
                    <td v-for="field in (['standard', 'method', 'owner'])" :key="field" class="border-t px-3 py-2.5">
                      <NInput
                        v-if="editing === `acc-${row.id}-${field}`"
                        class="text-xs"
                        v-model:value="row[field]"
                        type="textarea"
                        autofocus
                        rows="2"
                        @blur="editing = null"
                      />
                      <button v-else @click="editing = `acc-${row.id}-${field}`" title="点击修改"
                        class="bg-transparent block w-full rounded-md px-2 py-1 text-left text-xs leading-relaxed text-slate-700 transition hover:bg-[#EAF0F9]"
                      >
                        {{ row[field] }}
                      </button>
                    </td>
                    <td class="border-t px-3 py-2.5">
                      <div class="flex flex-nowrap items-center gap-1">
                        <NButton v-for="v in (['完全达到', '基本达到', '未达成'])" :key="v"
                          size="small"
                          round
                          @click="acceptRows = acceptRows.map((x) => (x.id === row.id ? { ...x, result: x.result === v ? '待验收' : v } : x))"
                          class="text-[11px]"
                          :type="row.result === v
                            ? v === '未达成' ? 'error' : v === '基本达到' ? 'warning' : 'success'
                            : 'default'">
                          {{ v }}
                        </NButton>
                      </div>
                    </td>
                    <td class="border-t px-2 py-2.5 text-center">
                      <NButton text type="error" title="删除本验收项" @click="acceptRows = acceptRows.filter((x) => x.id !== row.id)">
                        <Icon icon="iconamoon:trash" class="size-16px" />
                      </NButton>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
            <div class="flex items-center gap-3">
              <NButton ghost class="text-xs"
                @click="acceptRows = [...acceptRows, { id: Date.now(), item: '新验收项目', basis: '', standard: '', method: '', owner: '', result: '待验收' }]">
                <Icon icon="ic:round-plus" class="size-16px" />添加验收项
              </NButton>
              <span class="text-xs text-slate-400">评估结论在验收阶段由责任单位填写：点击选择「完全达到 / 基本达到 / 未达成」，再次点击取消；行尾可删除验收项</span>
            </div>
            <div class="rounded-lg border">
              <div class="border-b bg-slate-100 px-3 py-2 text-sm font-medium text-slate-700">综合验收结论（验收阶段由车间会同各专业评估填写）</div>
              <div class="space-y-3 p-3">
                <div class="flex flex-wrap items-center gap-2">
                  <span v-for="v in (['完全达到', '基本达到', '未达成'])" :key="v">
                    <NRadio :checked="acceptNote !== '' && acceptNote.startsWith(v)" :value="'v'" :label="v" @change="acceptNote = v + '：'"></NRadio>
                  </span>
                  <span class="text-xs text-slate-400">「基本达到 / 未达成」须说明偏差原因与处置措施，并纳入变更关闭审核</span>
                </div>
                <NInput v-model:value="acceptNote" type="textarea" rows="2" placeholder="验收意见：总体评价、偏差说明、后续跟踪措施…（验收阶段填写）" />
              </div>
            </div>
            <div class="rounded-lg bg-emerald-50/80 px-4 py-2.5 text-xs leading-5 text-emerald-700">
              流程说明：投用运行满验收周期后，系统向车间（属地）与涉及专业推送验收任务；各责任单位按上表逐项给出评估结论并上传佐证（数据报表 / 化验报告 / 现场照片），全部完成后汇总至综合验收结论，结论与佐证资料一并纳入变更关闭校验，验收不通过时退回整改或评估恢复原状。
            </div>
          </template>
          <!-- 变更关闭确认表 -->
          <template v-if="tab === 'close'">
            <div class="flex flex-wrap items-center gap-3">
              <span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
                <Icon icon="quill:folder-open" class="size-16px" />
                变更关闭确认表（资料归档清单）
              </span>
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <NTag size="small" class="text-xs">AI 辅助判定一次 · 支持人工改判留痕</NTag>
              <span class="ml-auto text-xs text-slate-400">判定为「是」的资料全部归档后，变更主管部门专责方可确认关闭</span>
            </div>
            <div class="border" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
              <div class="flex items-center gap-3 bg-slate-100 px-3 py-2 text-xs font-medium text-slate-500">
                <span class="w-5" />
                <span class="flex-1">资料名称（与申请表「需更新的资料」勾选联动，勾选资料标记「需更新版本」）</span>
                <span class="w-48 text-center">是否涉及（AI 判定，可改判）</span>
                <span class="w-32 text-center">归档状态</span>
              </div>
              <template v-for="cat in (['图纸与规程类', '台账与制度类', '过程与结果文件类'])" :key="cat">
                <div v-if="closeDocs.filter((d) => d.cat === cat).length">
                  <button @click="closeFold = { ...closeFold, [cat]: !closeFold[cat] }"
                    class="flex w-full items-center gap-2 border-t bg-slate-50 px-3 py-2 text-left text-xs font-semibold text-slate-600 transition hover:bg-[#EAF0F9]">
                    <Icon icon="formkit:right" class="size-14px text-slate-400 transition" :class="closeFold[cat] ? '' : 'rotate-90'" />
                    {{ cat }}
                    <span class="font-normal text-slate-400">
                      {{ closeDocs.filter((d) => d.cat === cat).length }} 项 · 已归档
                      {{ closeDocs.filter((d) => d.cat === cat && d.scope === '是' && d.archived).length }}/{{ closeDocs.filter((d) => d.cat === cat && d.scope === '是').length }}
                    </span>
                    <span class="ml-auto text-[11px] font-normal text-slate-400">{{ closeFold[cat] ? '点击展开' : '点击折叠' }}</span>
                  </button>
                  <template v-if="!closeFold[cat]">
                    <div v-for="d in closeDocs.filter((x) => x.cat === cat)" :key="d.name" class="flex flex-wrap items-center gap-3 border-t px-3 py-2.5 text-sm">
                      <span class="w-5 text-slate-400">{{ closeDocs.indexOf(d) + 1 }}</span>
                      <span class="min-w-0 flex-1 text-slate-800">
                        {{ d.name }}
                        <NTag v-if="needUpdate(d.name)" size="small" type="info" class="ml-2">需更新版本</NTag>
                      </span>
                      <span class="flex w-48 shrink-0 justify-center gap-1.5">
                        <NButton size="tiny" round v-for="v in (['是', '否', '不涉及'])" :key="v"
                          @click="changeScope(d, v)"
                          class="text-[11px]"
                          :type="d.scope === v ? 'primary' : 'default'">
                          {{ v }}
                        </NButton>
                      </span>
                      <span class="flex w-32 shrink-0 items-center justify-center gap-2">
                        <template v-if="d.scope === '是'">
                          <NTag v-if="d.archived" size="small" type="success">已归档</NTag>
                          <template v-else>
                            <NTag size="small" type="warning">待上传</NTag>
                            <NButton text type="primary" class="text-xs hover:underline" @click="uploadDoc = d.name; uploadSel = null;">上传</NButton>
                          </template>
                        </template>
                        <NTag v-else size="small">{{ d.scope }}</NTag>
                      </span>
                    </div>
                  </template>
                </div>
              </template>
            </div>
            <div class="flex flex-wrap items-center gap-4 text-xs text-slate-500">
              <span>
                统计：涉及 <b class="text-[#17407F]">{{ closeDocs.filter((d) => d.scope === '是').length }}</b> 项 ·
                已归档 <b class="text-emerald-600">{{ closeDocs.filter((d) => d.scope === '是' && d.archived).length }}</b> 项 ·
                待上传 <b class="text-amber-600">{{ closeDocs.filter((d) => d.scope === '是' && !d.archived).length }}</b> 项 ·
                需更新版本 <b class="text-[#1D4E9C]">{{ closeDocs.filter((d) => needUpdate(d.name)).length }}</b> 项（联动申请表勾选）
              </span>
              <span class="text-slate-400">资料上传支持 PC / 移动端（拍照上传），版本受控</span>
            </div>
            <div class="rounded-lg bg-[#EAF0F9] px-4 py-2.5 text-xs leading-5 text-[#17407F]">
              关闭校验说明：判定为「是」的资料项全部归档后，系统将全部流程完毕信号推送变更主管部门专责，专责确认后点击关闭，系统记录变更关闭时间；「否 / 不涉及」由 AI 辅助判定一次，人工改判须填写理由并留痕。临时变更恢复完成后，恢复操作票与参数核对记录自动追加至本清单。
            </div>
          </template>

        </div>
      </div>
      <!-- 底部 -->
      <!-- 全局按钮：下载 / 重新生成 / 确认完毕 -->
      <div v-if="tab !== 'pre'" class="flex flex-wrap items-center justify-between gap-2 border-t p-4">
        <div class="flex flex-wrap gap-2">
          <NButton ghost type="success" @click="download">
            <Icon icon="material-symbols:download" class="size-16px" /> 下载{{ TAB_NAME[tab] }}
          </NButton>
          <NButton ghost type="warning" @click="regenerate">
            <Icon icon="tdesign:refresh" class="size-14px" /> 重新生成{{ TAB_NAME[tab] }}
          </NButton>
          <NButton ghost :type="confirmed[tab] ? 'success' : 'primary'" @click="confirmTab(tab)">
            <Icon icon="ix:success" class="size-16px" /> {{ confirmed[tab] ? '已确认完毕' : '本标签内容确认完毕' }}
          </NButton>
        </div>
        <span class="text-xs text-slate-400">审批人 / 保存 / 提交均在底部固定行，滚动时保持可见</span>
      </div>
      <div class="flex flex-wrap items-center gap-x-4 gap-y-2 bg-[#EAF0F9] px-4 py-2.5 shadow-[0_-2px_8px_rgba(0,0,0,0.05)]">
        <template v-if="tab === 'pre'">
          <span class="text-xs text-slate-400">预识别阶段不设审批人与提交：完成识别与预分析后，请切换至「变更申请表」选择审批人并提交</span>
          <div class="ml-auto flex shrink-0 gap-2">
            <NButton ghost type="success" size="small" @click="genForm">
              <Icon icon="akar-icons:file" class="size-14px" /> 生成变更申请单
            </NButton>
            <NButton ghost type="warning" size="small" @click="runRiskAnalysis" :disabled="analyzing">
              <Icon v-if="analyzing" icon="ri:loader-4-fill" class="size-14px animate-spin" />
              <Icon v-else icon="mdi:shield-alert-outline" class="size-14px" />风险预分析
            </NButton>
            <NButton type="primary" size="small" @click="saveDraft">保存</NButton>
          </div>
        </template>
        <template v-else>
          <div class="flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-slate-500">
            <Icon icon="lucide:users" class="size-14px" />
            <span class="min-w-0 truncate">
              部门审核：{{ approvers.部门审核 }} → 专业会签：{{ Object.keys(signers).length ? Object.values(signers).join('、') : '未涉及专业 · 免会签' }} → 最终批准：{{ approvers.最终批准 }}
            </span>
            <NButton ghost size="small" type="primary" @click="sheet = 'approver'">
              <Icon icon="octicon:sliders-24" class="size-14px" />
              审批流程设置
            </NButton>
          </div>
          <div class="ml-auto flex shrink-0 items-center gap-2">
            <NButton size="small" type="primary" @click="saveDraft">保存</NButton>
            <span v-if="!form" class="flex items-center text-xs text-amber-600">请先在「变更预识别」页生成变更申请单</span>
            <NButton size="small" type="primary" @click="submitApply" :disabled="!form" :title="form ? '提交进入审批流' : '请先生成变更申请单'">
              提交 <Icon icon="formkit:right" class="size-14px" />
            </NButton>
          </div>
        </template>
      </div>
    </div>
    <!-- 附件在线预览弹窗 -->
    <NModal
      :show="previewFile !== null"
      preset="card"
      :auto-focus="false"
      :style="{ width: '700px', height: 'auto' }"
      :segmented="{ content: true, footer: true }"
      @update:show="(v: boolean) => !v && (previewFile = null)"
    >
      <template #header>
        <div class="flex items-center gap-2 text-base font-semibold text-slate-800">
          <Icon icon="akar-icons:file" class="size-18px" /> {{ previewFile }}
        </div>
      </template>
      <template #default>
        <div class="space-y-3">
          <div class="flex flex-wrap gap-x-4 gap-y-1 text-xs text-slate-400">
            <span>上传人：{{ ROLES[0].person }}</span><span>上传时间：{{ today() }}</span><span>版本：V1（受控）</span>
          </div>
          <div class="flex h-72 flex-col items-center justify-center gap-2 rounded-lg border bg-slate-50 text-slate-400">
            <Icon icon="akar-icons:file" class="size-42px" />
            <span class="text-sm">文件在线预览区（演示）</span>
            <span class="text-xs">正式环境由文档预览服务渲染 PDF / Word / 图片内容，支持缩放、翻页与移动端查看</span>
          </div>
        </div>
      </template>
      <template #footer>
        <div class="flex justify-end gap-[10px]">
          <NButton ghost size="small" @click="downloadPdf">
            <Icon icon="material-symbols:download" class="size-16px" /> 下载
          </NButton>
          <NButton size="small" type="primary" @click="previewFile = null">关闭</NButton>
        </div>
      </template>
    </NModal>
    <!-- 侧边抽屉：AI 类型判定 / 等级判定表 / 审批流程设置 -->
    <NDrawer :show="sheet !== null" @update:show="(v: boolean) => !v && (sheet = null)" placement="right" :width="sheet === 'level' ? '45rem' : '28rem'">
      <NDrawerContent>
        <template #header>
          <div v-if="sheet === 'type'" class="text-base font-semibold text-slate-800">AI 变更类型判定说明</div>
          <div v-if="sheet === 'level'" class="text-base font-semibold text-slate-800">变更等级判定表</div>
          <div v-if="sheet === 'approver'" class="text-base font-semibold text-slate-800">审批流程设置</div>
        </template>
        <div class="h-full space-y-4 overflow-y-auto">
          <!-- AI 变更类型判定说明 -->
          <template v-if="sheet === 'type'">
            <div class="flex items-center gap-2">
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <NTag type="info" size="small">判定：工艺</NTag>
              <NTag size="small">置信度：高</NTag>
            </div>
            <div class="space-y-3 text-sm leading-relaxed text-slate-700">
              <p><b>判定结论：</b>本次变更涉及反应温度（80℃→90℃）、保温时间（8h→4h）等工艺参数调整及 DCS 报警/联锁值修改，未涉及设备本体更换或材质变更，依据变更类型规则判定为「工艺」类变更。</p>
              <p><b>判定依据：</b></p>
              <ul class="list-disc space-y-1 pl-5">
                <li>命中规则 2.1：操作参数超出原设计控制范围 → 工艺类</li>
                <li>命中规则 2.4：DCS 报警值/联锁值调整 → 工艺类（仪表联动）</li>
                <li>排除规则 3.2：无设备规格型号变化 → 不构成设备类</li>
              </ul>
              <p><b>知识库引用：</b>工艺卡片 R-101（Rev B）、DCS 联锁台账、《变更管理制度》第 4.2 条。</p>
              <p class="rounded-lg bg-amber-50 p-3 text-xs text-amber-700">AI 判定结果仅供参考，最终类型以人工确认为准；如调整为其他类型，系统将记录修改留痕。</p>
            </div>
          </template>
  
          <!-- 变更等级判定表 -->
          <template v-if="sheet === 'level'">
            <div class="flex items-center gap-2">
              <span class="inline-flex items-center gap-1 rounded-full border border-violet-200 bg-violet-100 px-2 py-0.5 text-xs font-medium text-violet-700">
                ✨ AI 生成 · 需人工确认
              </span>
              <NTag size="small" :type="totalScore >= 20 ? 'error' : 'warning'">
                {{ levelResult }}
              </NTag>
              <span class="text-xs text-slate-400">合计得分 <b class="text-[#17407F]">{{ totalScore }}</b> 分（&lt;20 分为一般，≥20 分为重要）</span>
            </div>
            <div class="grid gap-3 grid-cols-2">
              <div v-for="(dim, di) in LEVEL_DIMS" :key="dim.name" class="rounded-lg border p-3">
                <div class="mb-2 flex items-baseline justify-between gap-2">
                  <span class="text-sm font-semibold text-slate-700">
                    {{ dim.name }}
                    <span v-if="dim.note" class="ml-1 text-xs font-normal text-slate-400">（{{ dim.note }}）</span>
                  </span>
                  <span class="text-sm text-slate-500">得分 <b class="text-[#17407F]">{{ levelScores[di] }}</b></span>
                </div>
                <div class="space-y-1.5">
                  <button v-for="op in dim.options" :key="op.score" @click="levelScores = levelScores.map((s, i) => (i === di ? op.score : s))"
                    class="flex w-full items-start gap-2 rounded-lg px-2 py-1.5 text-left text-xs transition"
                    :class="levelScores[di] === op.score ? 'bg-[#EAF0F9] text-[var(--theme-color)]' : 'text-slate-600 hover:bg-slate-50 bg-white'"
                    :style="{'--theme-color': themeStore.themeColor}"
                  >
                    <span 
                      class="mt-0.5 flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full border" 
                      :class="levelScores[di] === op.score ? 'border-[var(--theme-color)]' : 'border-slate-300'"
                      :style="{'--theme-color': themeStore.themeColor}"
                    >
                      <span 
                        v-if="levelScores[di] === op.score" 
                        class="h-1.5 w-1.5 rounded-full bg-[var(--theme-color)]"
                        :style="{'--theme-color': themeStore.themeColor}"
                      />
                    </span>
                    <span class="flex-1 leading-snug">{{ op.label }}（{{ op.score }}分）</span>
                  </button>
                </div>
              </div>
            </div>
            <div class="rounded-lg border border-violet-200 bg-violet-50/50 p-4">
              <div class="mb-2 flex items-center gap-2 text-sm font-semibold text-violet-700">
                <Icon icon="lucide:sparkles" class="size-14px" />
                AI 评分分析过程
              </div>
              <div class="space-y-3 text-xs leading-relaxed text-slate-600">
                <p>我们分析变更内容：缩合反应温度从80℃提高到90℃，反应时间从8小时缩短到4小时。设备材质SS304，设计温度100℃，设计压力0.2MPa，压力不变（常压）。物料为原料A与原料B，未具体说明毒性、腐蚀性。逐维度评分如下。</p>
                <p><b class="text-[#17407F]">1. 设备影响：</b>温度从80℃升至90℃，仍在设计温度100℃范围内，但接近上限。SS304 在 80→90℃ 下腐蚀速率可能略有增加，但变化不大；保守起见给 2 分（中等损害）。</p>
                <p><b class="text-[#17407F]">2. 财务影响：</b>变更目的是提升产能，但若设计错误可能导致反应失败、批次报废。温度提高10℃、时间缩短一半，控制不好可能影响产品质量，损失可能中等（1万~10万），取 2 分。</p>
                <p><b class="text-[#17407F]">3. 质量影响：</b>涉及主要工艺参数变更且有未知杂质（0.05%→0.12%）结构未定，对质量带来明显风险，取 3 分。</p>
                <p class="text-slate-400">（各维度得分均可人工改判，修改留痕；改判后等级自动回填至申请表）</p>
              </div>
            </div>
          </template>
  
          <!-- 审批流程设置 -->
          <template v-if="sheet === 'approver'">
            <p class="text-xs leading-relaxed text-slate-400">审批链：部门审核 → 专业会签 → 最终批准。专业会签人由申请表「涉及专业」勾选自动增减，可在此调整人选；重要变更自动追加安全审查与分管领导终批。</p>
            <div class="rounded-lg border p-3">
              <div class="mb-2 flex items-center gap-2 text-sm font-semibold text-slate-700">
                <NTag type="info" size="small">1</NTag> 部门审核
              </div>
              <NSelect :value="approvers.部门审核" :options="DEPT_APPROVER_OPTS"
                @update:value="(v: string) => approvers = { ...approvers, 部门审核: v }" />
            </div>
            <div class="rounded-lg border p-3">
              <div class="mb-2 flex items-center gap-2 text-sm font-semibold text-slate-700">
                <NTag type="info" size="small">2</NTag> 专业会签
                <span class="text-[11px] font-normal text-slate-400">（由「涉及专业」勾选联动）</span>
              </div>
              <p v-if="Object.keys(signers).length === 0" class="text-xs text-slate-400">未勾选涉及专业，本环节免会签；如需会签，请回申请表勾选「涉及专业」。</p>
              <div v-else class="space-y-2">
                <div v-for="(p, d) in signers" :key="d" class="flex items-center gap-2">
                  <NTag size="small">{{ d }}</NTag>
                  <NSelect class="flex-1" :value="p" :options="signerOpts(p)"
                    @update:value="(v: string) => signers = { ...signers, [d]: v }" />
                </div>
              </div>
            </div>
            <div class="rounded-lg border p-3">
              <div class="mb-2 flex items-center gap-2 text-sm font-semibold text-slate-700">
                <NTag type="info" size="small">3</NTag> 最终批准
                <NTag size="small" class="text-11px">制度固定</NTag>
              </div>
              <NInput :value="approvers.最终批准" disabled />
              <p class="mt-1.5 text-[11px] text-slate-400">重要变更：自动追加「安全审查（安全环保部）」与「分管领导终批」节点，无需手动设置。</p>
            </div>
            <NButton type="primary" class="w-full" @click="finishApprover">完成设置</NButton>
          </template>
        </div>
      </NDrawerContent>
    </NDrawer>
    <!-- 资料上传弹窗 -->
    <NModal 
      :show="uploadDoc !== null"
      preset="card"
      :auto-focus="false"
      :style="{ width: '550px', height: 'auto' }"
      :segmented="{ content: true, footer: true }"
      @update:show="(v: boolean) => !v && (uploadDoc = null)"
    >
      <template #header>
        <div class="flex items-center gap-2 text-base font-semibold text-slate-800">
          <Icon icon="material-symbols:upload" class="size-20px" /> 资料上传归档 — {{ uploadDoc }}
        </div>
      </template>
      <div class="space-y-3">
        <div>
          <div class="mb-1 text-xs font-medium text-slate-500">系统文档存储地址（受控目录）</div>
          <div class="flex items-center gap-2 rounded-lg border bg-slate-50 px-3 py-2">
            <Icon icon="quill:folder-open" class="size-16px" />
            <code class="min-w-0 flex-1 truncate text-xs text-slate-600">{{ uploadDocInfo?.storePath }}</code>
            <NButton text type="primary" class="text-xs hover:underline" @click="openDir">打开目录</NButton>
          </div>
        </div>
        <div>
          <div class="mb-1 text-xs font-medium text-slate-500">方式一：从存储地址选择资料</div>
          <div class="space-y-1.5">
            <p v-for="f in uploadDocInfo?.candidates" :key="f" @click="uploadSel = f"
              class="flex w-full items-center gap-2 rounded-lg border px-3 py-2 text-left text-xs transition cursor-pointer"
              :class="uploadSel === f ? 'border-[var(--theme-color)] bg-[var(--theme-color)] text-[var(--theme-color)] text-white' : 'bg-white text-slate-600 hover:border-[var(--theme-color)] hover:text-[var(--theme-color)]'"
              :style="{'--theme-color': themeStore.themeColor}"
            >
              <Icon icon="akar-icons:file" class="size-14px mr-1" /> {{ f }}
                <Icon v-if="uploadSel === f" icon="ix:success" class="ml-auto size-16px" />
            </p>
          </div>
        </div>
        <div>
          <div class="mb-1 text-xs font-medium text-slate-500">方式二：拖拽本地文件到区域</div>
          <button @click="uploadFiles"
            class="flex h-20 w-full flex-col items-center justify-center gap-1 rounded-lg border-2 border-dashed px-3 text-xs transition"
            :class="uploadSel?.startsWith('本地文件') ? 'border-[var(--theme-color)] bg-[#EAF0F9] text-[var(--theme-color)]' : 'border-slate-300 text-slate-400 hover:border-[var(--theme-color)] hover:text-[var(--theme-color)]'"
            :style="{'--theme-color': themeStore.themeColor}"
          >
            <Icon icon="material-symbols:upload" class="size-24px" />
            {{ uploadSel?.startsWith('本地文件') ? uploadSel : '将资料文件拖拽到此处，或点击选择本地文件' }}
          </button>
        </div>
      </div>
      <template #footer>
        <div class="flex justify-end gap-2">
          <NButton @click="uploadDoc = null">取消</NButton>
          <NButton type="primary" :disabled="!uploadSel" @click="uploadConfirm">
            确认上传归档
          </NButton>
        </div>
      </template>
    </NModal>
    <!-- 插件抽屉 -->
    <NDrawer v-model:show="pluginDrawer" :width="1200" placement="right">
      <NDrawerContent>
        <template v-if="currentPlugin === 'HAZOP'">
          <Hazop @back="onBack" @export="exportAll" />
        </template>
        <template v-else-if="currentPlugin === 'JSA'">
          <Jsa @back="onBack" @export="exportAll" />
        </template>
        <template v-else-if="currentPlugin === 'SCL'">
          <Scl @back="onBack" @export="exportAll" />
        </template>
        <template v-else-if="currentPlugin === 'RISK_CHECK'">
          <RiskCheck @back="onBack" />
        </template>
      </NDrawerContent>
  </NDrawer>
  </NCard>
</NSpace>
</template>

<style scoped lang="scss">
.scroll {
  height: calc(100vh - 326px);
  overflow-y: auto;
}
.scroll2 {
  height: calc(100vh - 393px);
  overflow-y: auto;
}
</style>
