我发起的变更页面完成

This commit is contained in:
2026-09-20 10:59:19 +08:00
parent ace1401089
commit ebfb52d1db
3 changed files with 260 additions and 535 deletions
+238 -518
View File
@@ -1,441 +1,19 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, h } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { useRouterPush } from '@/hooks/common/router';
import { tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
import { systemConfigApi } from '@/service/api/system';
import { orgListApi } from '@/service/api/user';
import { changeLedgerListApi } from '@/service/api/changeLedger';
const { routerPushByKey } = useRouterPush();
const themeStore = useThemeStore();
const revokeModal = ref(false);
const revokeSaving = ref<boolean>(false);
const progressDrawer = ref<boolean>(false);
const dept = ref("全部组织");
const openOnly = ref(false);
const kw = ref("");
const typeFilter = ref("全部");
const statusFilter = ref("全部");
const durFilter = ref("全部");
const customFrom = ref<any>(null);
const customTo = ref<any>(null);
const sort = ref<{ k: string; dir: "asc" | "desc" }>({ k: "applyTime", dir: "desc" });
const me = "张工艺";
const DEPTS = ["全部组织", "一车间", "二车间", "供应部", "质量部"];
const deptOpts = DEPTS.map((d) => ({ label: d, value: d }));
const LEDGER_ROWS = [
{ 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 REF_NOW = new Date("2026-08-03T12:00:00");
const PERIOD_DAYS: Record<string, number> = { 近一年: 365, 近半年: 183, 近三个月: 92, 近一个月: 31 };
const rows = computed(() => {
let list = LEDGER_ROWS.slice();
if (dept.value !== "全部组织") list = list.filter((r) => r.dept === dept.value);
if (openOnly.value) list = list.filter((r) => r.status !== "已关闭");
if (typeFilter.value !== "全部") list = list.filter((r) => r.type === typeFilter.value);
if (statusFilter.value !== "全部") list = list.filter((r) => r.status === statusFilter.value);
if (durFilter.value !== "全部") list = list.filter((r) => r.duration === durFilter.value);
if (period.value !== "自定义") {
const from = new Date(REF_NOW.getTime() - PERIOD_DAYS[period.value] * 86400000);
list = list.filter((r) => new Date(r.applyTime.replace(" ", "T")) >= from);
} else {
if (customFrom.value) list = list.filter((r) => r.applyTime.slice(0, 10) >= customFrom.value);
if (customTo.value) list = list.filter((r) => r.applyTime.slice(0, 10) <= customTo.value);
}
if (kw.value.trim()) list = list.filter((r) => r.title.includes(kw.value.trim()) || r.id.includes(kw.value.trim()));
list.sort((a:any, b:any) => {
const va = a[sort.value.k], vb = b[sort.value.k];
return sort.value.dir === "asc" ? va.localeCompare(vb) : vb.localeCompare(va);
});
return list;
});
const navPreset = ref<string>("mine");
const finalRows = computed(() => {
switch (navPreset.value) {
case "mine": return rows.value.filter((r:any) => r.applicant === me);
case "important": return rows.value.filter((r:any) => r.level === "重要");
case "temp": return rows.value.filter((r:any) => r.duration === "临时");
case "overdue": return rows.value.filter((r:any) => r.overdueDays);
case "highrisk": return rows.value.filter((r:any) => r.level === "重要" && r.status !== "已关闭");
default: return rows.value;
}
});
interface LedgerExtra {
special?: ("延期" | "临时转永久" | "未实施" | "未投用")[];
actualUse?: string; planRestore?: string; extRestore?: string; closeTime?: string;
}
const LEDGER_EXTRA: Record<string, LedgerExtra> = {
"MOC-2026-R01-0039": { special: ["延期"], actualUse: "2026-07-16 08:30", planRestore: "2026-07-27", extRestore: "2026-08-03" },
"MOC-2026-R01-0029": { special: ["临时转永久"], planRestore: "2026-08-01" },
"MOC-2026-R02-0033": { special: ["未实施"] },
"MOC-2026-R03-0040": { special: ["未投用"] },
"MOC-2026-R02-0031": { actualUse: "2026-07-15 09:20" },
"MOC-2026-R03-0027": { actualUse: "2026-06-25 10:00", closeTime: "2026-07-02 10:18" },
"MOC-2026-R01-0024": { actualUse: "2026-06-20 08:10", closeTime: "2026-06-28 16:30" },
};
const SPECIAL_COLOR: Record<string, string> = {
延期: "bg-amber-50 text-amber-700 border-amber-300",
临时转永久: "bg-[#DFE9F6] text-[#17407F] border-[#6E93C6]/50",
未实施: "bg-red-50 text-red-600 border-red-200",
未投用: "bg-red-50 text-red-600 border-red-200",
};
const STATUS_COLOR: Record<string, string> = {
审批中: "bg-amber-100 text-amber-700 border-amber-200",
已批准: "bg-[#DFE9F6] text-[#17407F] border-[#ACC6E5]",
实施中: "bg-[#1D4E9C]/10 text-[#1D4E9C] border-[#1D4E9C]/30",
待PSSR: "bg-violet-100 text-violet-700 border-violet-200",
待验收: "bg-emerald-50 text-emerald-600 border-emerald-200",
待关闭: "bg-[#E15555]/10 text-[#E15555] border-[#E15555]/30",
已关闭: "bg-emerald-100 text-emerald-700 border-emerald-200",
};
function opsOf(r: any, me: string, role?: string): { label: string; act: string; danger?: boolean }[] {
const ops: { label: string; act: string; danger?: boolean }[] = [];
if (r.status === "审批中" && r.applicant === me) ops.push({ label: "撤回", act: "withdraw", danger: true });
if (r.status === "已批准" || r.status === "待PSSR") ops.push({ label: "未实施情况说明", act: "unimpl" });
if (r.status === "实施中") ops.push({ label: "未投用情况说明", act: "unuse" });
if (role === "dept" && (r.overdueDays ?? 0) > 0) ops.push({ label: "催办", act: "urge" });
ops.push({ label: "详情", act: "detail" }, { label: "进度", act: "progress" });
if (r.status === "已批准") ops.push({ label: "下载申请表", act: "download" });
if (r.status === "待关闭") ops.push({ label: "下载", act: "download" });
if (r.status === "已关闭") ops.push({ label: "下载归档包", act: "download" });
return ops;
}
const FLOW_IMPORTANT = [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "done" },
{
name: "专业会签",
kind: "会签",
roles: ["process", "process2", "quality"],
status: "current",
members: [
{ name: "王仪表(仪表)", done: true },
{ name: "周设备(设备)", done: false },
{ name: "陈质量(质量)", done: false },
],
},
{ name: "安全审查", kind: "审批", roles: ["safety"], status: "todo", locked: true },
{ name: "分管领导最终批准", kind: "最终批准", roles: ["vp"], status: "todo", locked: true },
];
const FLOW_SIMPLE = [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "done" },
{ name: "专业审批", kind: "审批", roles: ["process"], status: "current" },
{ name: "部门负责人批准", kind: "最终批准", roles: ["dept"], status: "todo", locked: true },
];
const CHANGES = [
{
id: "MOC-2026-R01-0035",
title: "R-201 反应釜搅拌器电机更换(55kW→75kW 防爆电机)",
type: "设备",
level: "重要",
duration: "永久",
urgent: false,
status: "审批中",
applicant: "张工艺",
org: "一车间 · 工艺组",
device: "R-201 反应釜 / M-201A 搅拌电机",
date: "2026-07-28",
purpose: "原 55kW 电机在高粘度工况下频繁过载跳闸,影响反应均匀性与产品质量,需更换为 75kW 防爆电机。",
effect: "消除过载跳闸(月均 3 次降至 0),搅拌电流裕量提升至 25%,产品批次合格率预计提升 1.5%。",
before: "M-201A 搅拌电机:55kWExdⅡBT4,额定电流 102A,原设计工况粘度 ≤3000cP。",
after: "更换为 75kW 防爆电机:ExdⅡCT4,额定电流 138A,适配粘度 ≤6000cP;电缆与开关容量同步校核升级。",
related: ["更新 P&ID(图号 PID-R201-03", "修订电气图纸与电缆清册", "校核联锁跳车电流设定", "更新设备台账与备件清单", "操作人员培训"],
materials: ["设备数据表", "防爆合格证与材质证明", "电气负荷校核计算书", "更新后的 P&ID", "制造商安装维护手册"],
riskTools: ["AI 分析", "JSA", "检查表法"],
flow: FLOW_IMPORTANT,
},
{
id: "MOC-2026-R01-0036",
title: "聚合反应温度控制上限调整(78℃→82℃)",
type: "工艺",
level: "重要",
duration: "永久",
urgent: false,
status: "审批中",
applicant: "孙丽",
org: "一车间 · 工艺组",
device: "R-101 聚合釜 / TIC-101",
date: "2026-07-25",
purpose: "提高反应温度上限以缩短聚合时间、提升装置产能,满足新增订单需求。",
effect: "单釜反应周期缩短约 40 分钟,年产能预计提升 8%。",
before: "聚合反应温度控制范围 6578℃,TIC-101 高报 78℃、联锁 80℃。",
after: "控制范围调整为 6582℃,TIC-101 高报 82℃、联锁值维持 85℃ 安全边界不变。",
related: ["修订工艺卡片与操作规程", "DCS 参数与报警值修改", "更新 HAZOP 分析报告", "岗位人员再培训"],
materials: ["工艺计算书", "热平衡与飞温风险分析", "HAZOP 分析报告", "更新后的工艺卡片"],
riskTools: ["AI 分析", "HAZOP"],
flow: [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "done" },
{
name: "专业会签",
kind: "会签",
roles: ["process", "quality"],
status: "done",
members: [
{ name: "王仪表(仪表)", done: true },
{ name: "陈质量(质量)", done: true },
],
},
{ name: "安全审查", kind: "审批", roles: ["safety"], status: "current", locked: true },
{ name: "分管领导最终批准", kind: "最终批准", roles: ["vp"], status: "todo", locked: true },
],
},
{
id: "MOC-2026-R02-0038",
title: "DCS 罐区液位报警值 LIA-301 调整",
type: "工艺",
level: "一般",
duration: "永久",
urgent: false,
status: "待PSSR",
applicant: "吴强",
org: "一车间 · 罐区",
device: "V-301 储罐 / LIA-301",
date: "2026-07-20",
purpose: "原高报值偏保守,频繁误报导致操作疲劳,按最新容积表校准报警值。",
effect: "误报次数由每周 5 次降至 0,报警响应有效性提升。",
before: "LIA-301 高报 80%(对应 7.2m)。",
after: "高报调整为 85%(对应 7.65m),联锁值 90% 不变。",
related: ["DCS 组态修改", "更新报警台账", "岗位告知培训"],
materials: ["储罐容积表", "DCS 组态修改记录", "报警台账"],
riskTools: ["AI 分析", "检查表法"],
flow: FLOW_SIMPLE.map((n) => ({ ...n, status: "done" as const })),
},
{
id: "MOC-2026-R01-0039",
title: "紧急变更:V-102 安全阀起跳压力临时调整(补办手续)",
type: "设备",
level: "一般",
duration: "临时",
urgent: true,
status: "待关闭",
applicant: "张工艺",
org: "一车间 · 工艺组",
device: "V-102 缓冲罐 / PSV-102",
date: "2026-07-15",
purpose: "上游工况波动导致安全阀频繁起跳,经紧急批准临时调整起跳压力并限期恢复原设定。",
effect: "装置维持连续运行,临时期限内风险受控。",
before: "PSV-102 起跳压力 0.8MPa。",
after: "临时调整为 0.88MPa(不超过设计压力 1.0MPa 的 90%),期限 30 天,到期恢复 0.8MPa。",
related: ["恢复性检查", "更新安全阀台账", "事后风险分析补办"],
materials: ["安全阀校验报告", "紧急变更审批单", "事后 JSA 分析"],
riskTools: ["AI 分析", "JSA"],
flow: FLOW_SIMPLE.map((n) => ({ ...n, status: "done" as const })),
overdueDays: 4,
},
{
id: "MOC-2026-R03-0040",
title: "原料环己烷供应商变更(新增 B 供应商)",
type: "管理",
level: "重要",
duration: "永久",
urgent: false,
status: "实施中",
applicant: "刘芳",
org: "供应部",
device: "原料罐区 / TK-201",
date: "2026-07-18",
purpose: "引入第二供应商保障原料供应安全,降低单一来源断供风险。",
effect: "供应保障能力提升,采购成本预计下降 3%。",
before: "环己烷单一供应商 A,纯度 ≥99.5%。",
after: "新增供应商 B(纯度 ≥99.8%,已提供 MSDS 与质检报告),双源供应。",
related: ["更新合格供应商名录", "修订进厂检验规程", "更新 MSDS 档案", "卸料操作培训"],
materials: ["新供应商资质文件", "产品质检报告与 MSDS", "进厂检验规程"],
riskTools: ["AI 分析", "检查表法"],
flow: FLOW_IMPORTANT.map((n) => ({ ...n, status: "done" as const })),
},
{
id: "MOC-2026-R01-0029",
title: "操作规程修订(夏季工况)",
type: "管理",
level: "一般",
duration: "临时",
urgent: false,
status: "审批中",
applicant: "张工艺",
org: "一车间 · 工艺组",
device: "全装置",
date: "2026-06-20",
purpose: "夏季高温工况下循环水温度升高,原操作规程冷却参数不适用,需修订夏季操作参数与巡检频次。",
effect: "夏季工况下装置运行参数受控,避免超温降负荷,预计减少非计划降量 2 次/年。",
before: "操作规程(2025 版)未区分冬夏季工况,冷却参数单一。",
after: "增加夏季工况章节:循环水温度 >28℃ 时启用备用冷却塔,巡检频次由 4h 调整为 2h。",
related: ["操作规程升版发布", "岗位人员培训"],
materials: ["修订后操作规程", "参数核算表"],
riskTools: ["AI 分析", "检查表法"],
flow: [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "current" },
{ name: "专业审批", kind: "审批", roles: ["process"], status: "todo", locked: true },
{ name: "部门负责人批准", kind: "最终批准", roles: ["dept"], status: "todo", locked: true },
],
overdueDays: 3,
},
{
id: "MOC-2026-R02-0041",
title: "公用工程氮气减压阀组改造",
type: "设备",
level: "一般",
duration: "永久",
urgent: false,
status: "审批中",
applicant: "周涛",
org: "公用工程车间",
device: "氮气总管 / PV-208 减压阀组",
date: "2026-07-29",
purpose: "氮气减压阀组单路运行,检修时需停氮气,影响装置密封气供应,需改造为双路并联。",
effect: "减压阀组可在线切换检修,氮气供应连续性提升至 100%。",
before: "PV-208 单路减压,无备用回路。",
after: "新增并联减压回路 PV-208B,双路一用一备,可在线切换。",
related: ["更新 P&ID", "修订氮气系统操作规程", "更新设备台账"],
materials: ["阀门数据表", "管道单线图", "施工方案"],
riskTools: ["AI 分析", "JSA"],
flow: [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "done" },
{ name: "专业审批", kind: "审批", roles: ["process"], status: "current" },
{ name: "部门负责人批准", kind: "最终批准", roles: ["dept"], status: "todo", locked: true },
],
overdueDays: 3,
},
{
id: "MOC-2026-R03-0042",
title: "三车间自动化和安全隐患整改项目变更",
type: "管理",
level: "重要",
duration: "永久",
urgent: false,
status: "审批中",
applicant: "刘芳",
org: "三车间",
device: "三车间装置区",
date: "2026-07-31",
purpose: "落实安全隐患整改要求,对三车间自动化控制系统升级改造,消除加料互串与超温隐患。",
effect: "隐患整改闭环率 100%,自动化水平提升,误操作风险显著降低。",
before: "加料系统手动控制,存在互串与超温隐患(隐患编号 YH-2026-017)。",
after: "改为 DCS 自动控制 + 加料顺序联锁,增设独立超温切断。",
related: ["修订操作规程", "DCS 组态修改", "更新 HAZOP 分析", "岗位再培训"],
materials: ["隐患整改方案", "HAZOP 分析报告", "联锁因果表"],
riskTools: ["AI 分析", "HAZOP"],
flow: [
{ name: "部门审核", kind: "审批", roles: ["dept"], status: "done" },
{
name: "专业会签",
kind: "会签",
roles: ["process", "process2", "quality"],
status: "done",
members: [
{ name: "王仪表(仪表)", done: true },
{ name: "周设备(设备)", done: true },
{ name: "陈质量(质量)", done: true },
],
},
{ name: "安全审查", kind: "审批", roles: ["safety"], status: "done" },
{ name: "分管领导最终批准", kind: "最终批准", roles: ["vp"], status: "current" },
],
},
];
const progressRow = ref<any>(null);
const ledgerAct = ref<{ id: string; act: "withdraw" | "unimpl" | "unuse" } | null>(null);
const ledgerReason = ref("");
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()); }
interface UrgeRecord { id: string; changeId: string; title: string; applicant: string; by: string; time: string; }
let urgeSeq = 1;
const URGE_RECORDS: UrgeRecord[] = [];
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 urgeChange(changeId: string, byRole: string) {
const row = LEDGER_ROWS.find((r) => r.id === changeId || r.changeId === changeId);
const c = CHANGES.find((x) => x.id === changeId);
const by = ROLES.find((r:any) => r.id === byRole)?.person ?? byRole;
URGE_RECORDS.unshift({
id: `URGE-${urgeSeq++}`,
changeId,
title: row?.title ?? c?.title ?? changeId,
applicant: row?.applicant ?? c?.applicant ?? "",
by,
time: nowStr(),
});
emitDemo();
}
const stages = ["提交申请", "审批", "实施与投用前确认", "投用", "验收", "关闭"];
const curIdx = computed(() => ({ 审批中: 1, 已批准: 2, 实施中: 2, 待PSSR: 2, 待验收: 4, 待关闭: 5, 已关闭: 6 } as Record<string, number>)[progressRow.value?.status ?? ""] ?? 1);
const closed = computed(() => progressRow.value?.status === "已关闭");
const timeOf = (i: number) => (i === 0 ? progressRow.value?.applyTime : i === 3 ? progressRow.value?.actualUse : i === 5 ? progressRow.value?.closeTime : undefined);
const role = ref<string>("applicant");
const doOp = (r: any, act: string) => {
if (act === "detail") {
const c = CHANGES.find((x) => x.id === r.changeId);
if (c) {
// 跳转详情页面
// emit("openChange", c);
}else {
window.$message?.info(`演示:打开变更详情页 ${r.id}`);
}
return;
}
if (act === "progress") {
progressRow.value = r;
progressDrawer.value = true;
return;
}
if (act === "withdraw" || act === "unimpl" || act === "unuse") {
ledgerAct.value = { id: r.id, act };
ledgerReason.value = "";
revokeModal.value = true;
return;
}
if (act === "urge") {
urgeChange(r.id, role.value ?? "dept");
window.$message?.success(`已催办 ${r.id} 申请人 ${r.applicant}(系统消息 + 企业微信),催办记录已留痕并抄送变更主管部门,申请人操作台将出现催办提示`);
return;
}
window.$message?.info(`演示:下载 ${r.id} 变更申请表 / 归档包(PDF`);
};
// 撤回弹框取消
const revokeCancel = () => {
ledgerAct.value = null;
ledgerReason.value = "";
revokeModal.value = false;
}
// 撤回弹框提交
const revokeSave = () => {
if (!ledgerReason.value) {
window.$message?.warning("请输入撤回理由");
return;
}
window.$message?.success(ledgerAct.value?.act === 'withdraw' ? `已撤回 ${ledgerAct.value?.id},申请退回草稿箱,审批记录保留可查` : `${ledgerAct.value?.id} 情况说明已提交,变更转入关闭流程(主管部门确认后记录关闭时间)`);
revokeCancel();
}
const loading = ref<boolean>(false);
// 组织树
const orgTree = ref<any[]>([]);
// 筛选条件
const formInfo = ref<any>({
kw: '',
@@ -459,14 +37,14 @@ const typeList = ref<any[]>([{ label: "全部", value: null }]);
const levelList = ref<any[]>([{ label: "全部", value: null }]);
// 状态选项
const STATUS_OPTIONS = [
{ label: "全部", value: "" },
{ label: "审批中", value: "APPROVING" },
{ label: "待实施", value: "APPROVED" },
{ label: "待验收", value: "IMPLEMENTED" },
{ label: "待关闭", value: "ACCEPTED" },
{ label: "已关闭", value: "CLOSED" },
{ label: "已撤回", value: "WITHDRAWN" },
{ label: "已驳回", value: "REJECTED" },
{ label: "全部", value: "", type:'all' },
{ label: "审批中", value: "APPROVING", type:'warning' },
{ label: "已通过", value: "APPROVED", type:'success' },
{ label: "待验收", value: "IMPLEMENTED", type:'success' },
{ label: "待关闭", value: "ACCEPTED", type:'error' },
{ label: "已关闭", value: "CLOSED", type:'success' },
{ label: "已撤回", value: "WITHDRAWN", type:'error' },
{ label: "已驳回", value: "REJECTED", type:'error' },
];
// 排序选项
const SORT_OPTS = [
@@ -495,7 +73,15 @@ const TIME_OPTIONS = [
const period = ref<string>("year");
// 是否显示全部
const isAll = ref<boolean>(true);
// 当前选中行
const currentRow = ref<any>(null);
// 进度抽屉
const progressDrawer = ref<boolean>(false);
// 撤回/未实施情况说明弹框
const revokeModal = ref<boolean>(false);
const modalType = ref<string>('');
const reason = ref<string>('');
const revokeSaving = ref<boolean>(false);
// 列表
const list = ref<any[]>([]);
@@ -516,6 +102,35 @@ const exportChanges = () => {
window.$message?.info('还差接口');
}
// 自定义渲染展开图标
const renderSwitcherIcon = (node:any) => {
// 如果节点没有 children 或 children 为空数组,则不显示图标
if (!node.option.children || node.option.children.length === 0) {
return null; // 返回 null 表示不渲染任何图标
}
// 有子节点:返回 undefined 表示使用默认展开图标,也可自定义
return h(Icon,{
icon: node.option.expanded ? 'bi:caret-down-fill' : 'bi:caret-right-fill',
class: 'size-12px',
});
}
// 匹配对象
const matchObj= (list:any[],val:any) => {
const item = list.find((item:any) => item.value == val);
return item || null;
}
// 搜索
const searchHandle = () => {
pagination.value.page = 1;
getList();
}
// 组织选择改变
const orgChange = (val:any) => {
formInfo.value.org_id = val;
searchHandle();
}
// 显示全部改变
const showAll = () => {
isAll.value = !isAll.value;
@@ -524,39 +139,80 @@ const showAll = () => {
}else{
formInfo.value.status = 'APPROVING,APPROVED,IMPLEMENTED,ACCEPTED,WITHDRAWN,REJECTED';
}
pagination.value.page = 1;
getList();
searchHandle();
}
// 类型选择
const typeChoose = (val:any) => {
formInfo.value.type = val;
pagination.value.page = 1;
getList();
searchHandle();
}
// 时限选择
const durChoose = (val:any) => {
formInfo.value.duration = val;
pagination.value.page = 1;
getList();
searchHandle();
}
// 时间范围选择
const timeChoose = (val:string) => {
period.value = val;
pagination.value.page = 1;
getList();
searchHandle();
}
// 状态选择
const statusChoose = (val:string) => {
formInfo.value.status = val;
pagination.value.page = 1;
getList();
searchHandle();
}
// 排序选择
const sortChoose = (val:string) => {
formInfo.value.sort = val;
pagination.value.page = 1;
getList();
searchHandle();
}
// 跳转详情
const jumpTo = (r:any) => {
routerPushByKey("details_index",{ query: { id: r?.id?.toString(),status:r?.status?.toString() || '',key:'changeledger_mychanges' } });
}
// 打开进度
const openProgress = (r: any) => {
currentRow.value = r;
progressDrawer.value = true;
};
// 撤回/未实施情况说明
const openModal = (r: any,type:string) => {
currentRow.value = r;
modalType.value = type;
revokeModal.value = true;
}
// 撤回/未实施情况说明提交
const revokeSave = async () => {
if (!reason.value) {
return window.$message?.warning(modalType.value === "withdraw" ? "请输入撤回理由" : "请输入未实施情况说明");
}
revokeSaving.value = true;
setTimeout(() => {
revokeSaving.value = false;
}, 1000);
}
// 下载文件
const downloadFiles = (r:any,type:string) => {
// 下载申请表
if(type==='apply'){
}
// 下载已验收待关闭
if(type==='accepted'){
}
// 下载归档包
if(type==='closed'){
}
window.$message?.info('还差接口');
}
// 催办
const urge = (r:any) => {
window.$message?.info('还差接口');
}
// 分页改变
@@ -581,7 +237,6 @@ const getList = async () => {
page_size: pagination.value.pageSize,
});
if (!error) {
console.log(data)
list.value = data?.list ?? [];
pagination.value.itemCount = data?.total ?? 0;
}
@@ -604,9 +259,22 @@ const getConfig = async () => {
}
}
onMounted(() => {
getConfig();
// // 获取组织列表
const getOrgList = async () => {
loading.value = true;
const {data,error} = await orgListApi();
if(!error){
orgTree.value = data;
if(orgTree.value.length > 0){
getList();
}
}
loading.value = false;
}
onMounted(async () => {
await getConfig();
getOrgList();
});
</script>
@@ -622,10 +290,20 @@ onMounted(() => {
>
<span class="flex items-center gap-1.5 text-sm font-semibold text-slate-700">
<Icon icon="akar-icons:file" class="size-16px" :style="{ color: themeStore.themeColor }" /> 全部变更
<span class="rounded bg-slate-100 px-1.5 text-xs text-slate-500">{{ finalRows.length }}</span>
<span class="rounded bg-slate-100 px-1.5 text-xs text-slate-500">{{ pagination.itemCount }}</span>
</span>
<NSelect size="small" class="!w-32" :value="formInfo.org_id" :options="deptOpts" @update:value="(v: string) => formInfo.org_id = v" />
<NTreeSelect
class="!w-200px"
:value="formInfo.org_id"
:options="orgTree"
size="small"
default-expand-all
label-field="label"
key-field="id"
:render-switcher-icon="renderSwitcherIcon"
placeholder="请选择组织层级"
@update:value="orgChange"
/>
<NRadio
size="small"
:checked="isAll"
@@ -645,9 +323,9 @@ onMounted(() => {
显示未关闭
</NRadio>
<div class="relative ml-auto">
<NInput v-model:value="kw" size="small" type="text" clearable placeholder="搜索变更名称 / 编号">
<NInput v-model:value="formInfo.kw" size="small" type="text" clearable placeholder="搜索变更名称 / 编号" @clear="searchHandle">
<template #suffix>
<Icon icon="akar-icons:search" class="size-14px text-slate-400 cursor-pointer" />
<Icon icon="akar-icons:search" class="size-14px text-slate-400 cursor-pointer" @click="searchHandle" />
</template>
</NInput>
</div>
@@ -695,52 +373,95 @@ onMounted(() => {
</div>
<div class="space-y-3 scroll">
<!-- 列表 -->
<div v-for="(r, i) in finalRows" :key="r.id" class="rounded-lg border bg-white px-4 py-3">
<div
v-for="(r, i) in list" :key="r.id"
class="rounded-lg border bg-white px-4 py-3"
:style="{
'--theme-color': themeStore.themeColor
}"
>
<div class="flex flex-wrap items-start gap-3">
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<span class="text-xs text-slate-300">{{ String(i + 1).padStart(2, '0') }}</span>
<NButton text class="text-left text-sm font-semibold text-slate-800 hover:text-[#1D4E9C] hover:underline" @click="doOp(r, 'detail')">{{ r.title }}</NButton>
<span class="text-xs text-slate-300">{{ Number(i)+1 }}</span>
<p class="cursor-pointer text-left text-sm font-semibold text-slate-800 hover:text-[var(--theme-color)] hover:underline" @click="jumpTo(r)">
{{ r.title }}
</p>
<NTag size="small" v-if="r.urgent" class="animate-breathe border-[#E15555] bg-[#E15555] text-white">紧急</NTag>
<span class="font-mono text-[11px] text-slate-400">{{ r.id }}</span>
<span class="font-mono text-[11px] text-slate-400">{{ r.change_no }}</span>
</div>
<div class="mt-1.5 flex flex-wrap items-center gap-1.5">
<NTag size="small" class="border-slate-200 bg-white text-slate-600">{{ r.type }}</NTag>
<NTag size="small" :class="r.level === '重要' ? 'border-red-200 bg-red-100 text-red-700' : 'border-slate-200 bg-slate-100 text-slate-600'">{{ r.level }}</NTag>
<NTag size="small" :class="r.duration === '临时' ? 'border-[#FAAD14] bg-[#FAAD14] text-white' : 'border-[#1D4E9C] bg-[#1D4E9C] text-white'">{{ r.duration }}</NTag>
<NTag size="small" v-for="s in LEDGER_EXTRA[r.id]?.special ?? []" :key="s" :class="SPECIAL_COLOR[s]">{{ s }}</NTag>
<!-- 类型 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(typeList,r.change_type)?.color),
textColor: tagTextColor(matchObj(typeList,r.change_type)?.color),
borderColor: tagBorderColor(matchObj(typeList,r.change_type)?.color),
}"
>
{{ matchObj(typeList,r.change_type)?.label }}
</NTag>
<!-- 等级 -->
<NTag
size="small"
:color="{
color: tagBgColor(matchObj(levelList,r.change_level)?.color),
textColor: tagTextColor(matchObj(levelList,r.change_level)?.color),
borderColor: tagBorderColor(matchObj(levelList,r.change_level)?.color),
}"
>
{{ matchObj(levelList,r.change_level)?.label }}
</NTag>
<NTag v-if="r.duration_type===2" size="small" type="warning">临时</NTag>
<NTag v-if="r.duration_type===1" size="small" type="info">永久</NTag>
<NTag v-if="r.extend_count" size="small" type="warning">延期</NTag>
</div>
<div class="mt-1.5 flex flex-wrap gap-x-4 gap-y-0.5 text-[11px] text-slate-500">
<span>申请 {{ r.applyTime }}</span>
<span>{{ r.dept }} · {{ r.applicant }}</span>
<span>计划投用 {{ r.planUse }}</span>
<span v-if="LEDGER_EXTRA[r.id]?.actualUse">实际投用 {{ LEDGER_EXTRA[r.id].actualUse }}</span>
<span v-if="LEDGER_EXTRA[r.id]?.planRestore">计划恢复 {{ LEDGER_EXTRA[r.id].planRestore }}</span>
<span v-if="LEDGER_EXTRA[r.id]?.extRestore" class="text-amber-600">延期后恢复 {{ LEDGER_EXTRA[r.id].extRestore }}</span>
<span v-if="LEDGER_EXTRA[r.id]?.closeTime">关闭 {{ LEDGER_EXTRA[r.id].closeTime }}</span>
<span>状态更新 {{ r.statusTime }}</span>
<span v-if="r.apply_time">申请 {{ r.apply_time?.slice(0,16) }}</span>
<span>{{ r.org_name }} · {{ r.applicant_name }}</span>
<span v-if="r.plan_use_date">计划投用 {{ r.plan_use_date?.slice(0,16) }}</span>
<span v-if="r.use_time">实际投用 {{ r.use_time?.slice(0,16) }}</span>
<span v-if="r.restore_deadline">计划恢复 {{ r.restore_deadline?.slice(0,16) }}</span>
<span v-if="r.close_time">关闭 {{ r.close_time?.slice(0,16) }}</span>
<span v-if="r.status_changed_at">状态更新 {{ r.status_changed_at?.slice(0,16) }}</span>
</div>
</div>
<div class="flex shrink-0 flex-col items-end gap-1.5">
<div class="flex flex-wrap items-center justify-end gap-2">
<span class="text-[11px] text-slate-400">{{ r.statusDetail }}</span>
<NTag size="small" :class="STATUS_COLOR[r.status]">{{ r.status }}</NTag>
<span v-if="r.overdueDays" class="text-[11px] font-medium text-red-500">超期 {{ r.overdueDays }} </span>
<!-- 状态标签 -->
<NTag v-if="r.status==='APPROVING'" size="small" type="warning">审批中</NTag>
<NTag v-if="r.status==='APPROVED'" size="small" type="success">已通过</NTag>
<NTag v-if="r.status==='IMPLEMENTED'" size="small" type="success">待验收</NTag>
<NTag v-if="r.status==='ACCEPTED'" size="small" type="error">待关闭</NTag>
<NTag v-if="r.status==='CLOSED'" size="small" type="success">已关闭</NTag>
<NTag v-if="r.status==='WITHDRAWN'" size="small" type="error">已撤回</NTag>
<NTag v-if="r.status==='REJECTED'" size="small" type="error">已驳回</NTag>
<!-- 超期 -->
<span v-if="r.overdue_status===2" class="text-[11px] font-medium text-red-500">超期 {{ r.overdue_days }} </span>
</div>
<!-- 操作按钮 -->
<div class="flex flex-wrap items-center justify-end gap-1 pt-0.5">
<NButton size="small" v-for="op in opsOf(r, me, role)" :key="op.label"
class="text-xs"
:ghost="op.act === 'detail' ? false : true"
:type="op.act === 'detail' ? 'primary' : op.danger ? 'error' : 'default'"
@click="doOp(r, op.act)">
{{ op.label }}
</NButton>
<!-- 审批中 -->
<NButton v-if="r.status==='APPROVING'" size="small" ghost type="error" class="text-xs" @click="openModal(r,'withdraw')">撤回</NButton>
<!-- 已批准待实施 -->
<NButton v-if="r.status==='APPROVED'" size="small" ghost type="primary" class="text-xs" @click="openModal(r,'unimplemented')">未实施情况说明</NButton>
<NButton v-if="r.status==='APPROVED'" size="small" ghost type="primary" class="text-xs" @click="downloadFiles(r,'apply')">下载申请表</NButton>
<!-- 已验收待关闭 -->
<NButton v-if="r.status==='ACCEPTED'" size="small" ghost type="error" class="text-xs" @click="downloadFiles(r,'accepted')">下载</NButton>
<!-- 已关闭 -->
<NButton v-if="r.status==='CLOSED'" size="small" ghost type="success" class="text-xs" @click="downloadFiles(r,'closed')">下载归档包</NButton>
<!-- 催办(超期) -->
<NButton v-if="r.overdue_days" size="small" ghost type="error" class="text-xs" @click="urge(r)">催办</NButton>
<NButton size="small" type="primary" class="text-xs" @click="jumpTo(r)">详情</NButton>
<NButton size="small" type="default" class="text-xs" @click="openProgress(r)">进度</NButton>
</div>
<div v-if="r.duration === '临时'" class="text-right text-[10px] leading-4 text-slate-400">临时变更恢复 / 延期 / 转永久请在详情页中操作</div>
<!-- 临时变更 -->
<div v-if="r.duration_type === 2" class="text-right text-[10px] leading-4 text-slate-400">临时变更恢复 / 延期 / 转永久请在详情页中操作</div>
</div>
</div>
</div>
<div v-if="finalRows.length === 0" class="rounded-lg border border-dashed bg-white py-12 text-center text-sm text-slate-400">当前筛选条件下暂无变更记录</div>
<div v-if="list.length === 0" class="rounded-lg border border-dashed bg-white py-12 text-center text-sm text-slate-400">当前筛选条件下暂无变更记录</div>
</div>
<div class="flex justify-end mt-3">
<NPagination
@@ -764,73 +485,72 @@ onMounted(() => {
>
<template #header>
<h3 class="text-sm font-bold text-slate-800">
<span v-if="ledgerAct?.act === 'withdraw'">撤回变更申请</span>
<span v-else-if="ledgerAct?.act === 'unuse'">未投用情况说明</span>
<span v-if="modalType === 'withdraw'">撤回变更申请</span>
<span v-else-if="modalType === 'unuse'">未投用情况说明</span>
<span v-else>未实施情况说明</span>
{{ledgerAct?.id}}
{{currentRow?.change_no}}
</h3>
</template>
<template #default>
<div class="rounded-lg bg-slate-50 px-3 py-2 text-xs leading-5 text-slate-500">
{{ ledgerAct?.act === 'withdraw'
{{ modalType === 'withdraw'
? '撤回后申请退回草稿状态,已产生的审批记录保留可查;修改内容后可重新提交。仅本人发起的审批中变更可撤回。'
: '变更审批通过 / 投用后未实施或未投用的,由申请人填写情况说明,提交后变更转入关闭流程,由变更主管部门专责确认关闭并记录关闭时间。' }}
</div>
<NInput
class="mt-3"
v-model:value="ledgerReason"
v-model:value="reason"
type="textarea"
:rows="3"
:placeholder="ledgerAct?.act === 'withdraw' ? '撤回理由(必填)…' : '情况说明(必填):原因、现场当前状态、后续处置安排…'"
:placeholder="modalType === 'withdraw' ? '撤回理由(必填)…' : '情况说明(必填):原因、现场当前状态、后续处置安排…'"
/>
</template>
<template #footer>
<div class="flex justify-end gap-[10px]">
<NButton size="small" @click="revokeCancel">取消</NButton>
<NButton size="small" @click="revokeModal = false">取消</NButton>
<NButton size="small" type="primary" :loading="revokeSaving" @click="revokeSave">提交</NButton>
</div>
</template>
</NModal>
<!-- 进度抽屉 -->
<!-- 进度弹框 -->
<NDrawer v-model:show="progressDrawer" :width="502" placement="right">
<NDrawerContent title="变更进度">
<div class="rounded-lg border bg-slate-50/60 px-3 py-2.5">
<div class="text-sm font-semibold text-slate-800">{{ progressRow?.title }}</div>
<div class="text-sm font-semibold text-slate-800">{{ currentRow?.title }}</div>
<div class="mt-1.5 flex flex-wrap items-center gap-1.5 text-[11px] text-slate-400">
<span class="font-mono">{{ progressRow?.id }}</span>
<NTag v-if="progressRow?.status==='审批中'" size="small" type="warning">{{progressRow.status}}</NTag>
<NTag v-if="progressRow?.status==='已批准'" size="small" type="primary">{{progressRow.status}}</NTag>
<NTag v-if="progressRow?.status==='待验收'" size="small" type="success">{{progressRow.status}}</NTag>
<NTag v-if="progressRow?.status==='待关闭'" size="small" type="error">{{progressRow.status}}</NTag>
<span v-if="progressRow?.overdue" class="font-medium text-red-500">超期 {{ progressRow?.day }} </span>
<span class="font-mono">{{ currentRow?.change_no }}</span>
<NTag v-if="currentRow?.status==='APPROVING'" size="small" type="warning">审批中</NTag>
<NTag v-if="currentRow?.status==='APPROVED'" size="small" type="success">已通过</NTag>
<NTag v-if="currentRow?.status==='IMPLEMENTED'" size="small" type="warning">待验收</NTag>
<NTag v-if="currentRow?.status==='ACCEPTED'" size="small" type="error">待关闭</NTag>
<NTag v-if="currentRow?.status==='CLOSED'" size="small" type="error">已关闭</NTag>
<NTag v-if="currentRow?.status==='WITHDRAWN'" size="small" type="error">已撤回</NTag>
<NTag v-if="currentRow?.status==='REJECTED'" size="small" type="error">已驳回</NTag>
<span v-if="currentRow?.overdue_status===2" class="font-medium text-red-500">超期 {{ currentRow?.overdue_days }} </span>
</div>
<div v-if="progressRow?.dept || progressRow?.applicant" class="mt-1 text-[11px] text-slate-400">{{ [progressRow?.dept, progressRow?.applicant].filter(Boolean).join(' · ') }}</div>
<div v-if="progressRow?.meta" class="mt-1 text-[11px] text-slate-400">{{ progressRow?.meta }}</div>
<div v-if="currentRow?.dept || currentRow?.applicant" class="mt-1 text-[11px] text-slate-400">{{ [currentRow?.dept, currentRow?.applicant].filter(Boolean).join(' · ') }}</div>
<div v-if="currentRow?.meta" class="mt-1 text-[11px] text-slate-400">{{ currentRow?.meta }}</div>
</div>
<div class="mt-4">
<div v-for="(s, i) in stages" :key="s" class="flex gap-3">
<div v-for="(s, i) in currentRow?.approval_records" :key="s.id" class="flex gap-3">
<div class="flex flex-col items-center">
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border-2"
:class="i < curIdx || closed ? 'border-[#7CC242] bg-[#7CC242] text-white' : i === curIdx && !closed ? 'border-[#1D4E9C] bg-[#1D4E9C] text-white' : 'border-slate-200 bg-white text-slate-300'">
<Icon v-if="i < curIdx || closed" icon="ix:success" />
<Icon v-else-if="i === curIdx" icon="tabler:clock" />
<span v-else class="text-[10px]">{{ i + 1 }}</span>
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border-2 border-[#7CC242] bg-[#7CC242] text-white">
<Icon icon="ix:success" />
</span>
<span v-if="i < stages.length - 1" class="w-0.5 flex-1" :class="i < curIdx ? 'bg-[#7CC242]' : 'bg-slate-200'" />
<span
v-if="Number(i) < Number(currentRow?.approval_records?.length) - 1"
class="w-0.5 flex-1"
:class="Number(i) < Number(currentRow?.approval_records?.length) ? 'bg-[#7CC242]' : 'bg-slate-200'"
></span>
</div>
<div class="pb-5">
<div class="text-sm" :class="i === curIdx && !closed ? 'font-semibold text-[#1D4E9C]' : i < curIdx || closed ? 'font-medium text-slate-700' : 'text-slate-400'">
{{ s }}{{ i === curIdx && !closed ? '(当前)' : '' }}
</div>
<div v-if="timeOf(i)" class="mt-0.5 text-[11px] text-slate-400">{{ timeOf(i) }}</div>
<div v-if="i === curIdx && !closed && (progressRow?.detail || progressRow?.updateTime)" class="mt-0.5 text-[11px] text-slate-400">
{{ [progressRow?.detail, progressRow?.updateTime ? `更新 ${progressRow?.updateTime}` : ''].filter(Boolean).join(' · ') }}
<p class="text-sm">{{ s.action }}</p>
<span class="mt-0.5 text-[11px] text-slate-400">{{s.finished_at?.slice(0, 16)}}</span>
</div>
</div>
</div>
</div>
<div v-if="progressRow?.dur === '临时'" class="rounded-lg border border-amber-200 bg-amber-50/50 px-3 py-2 text-[11px] leading-5 text-amber-700">
临时变更计划恢复 {{ progressRow?.deadline ?? '' }}{{ progressRow?.extended ? ` · 延期后恢复 ${progressRow?.deadline}` : '' }} · 恢复 / 延期 / 转永久请在详情页中操作
<div v-if="currentRow?.duration_type === 2" class="rounded-lg border border-amber-200 bg-amber-50/50 px-3 py-2 text-[11px] leading-5 text-amber-700">
临时变更计划恢复 {{ currentRow?.restore_deadline ?? '' }} · 恢复 / 延期 / 转永久请在详情页中操作
</div>
</NDrawerContent>
</NDrawer>
+6 -1
View File
@@ -15,6 +15,8 @@ const { routerPushByKey } = useRouterPush();
const route = useRoute();
const loading = ref<boolean>(false);
// 返回键
const backKey = ref<string>('');
// 判定说明
const typeAiLoading = ref<boolean>(false);
const typeAi = ref<{ type: string; confidence: number; conclusion: string; hit_rules: string[]; exclude_rules: string[]; knowledge_refs: string[] } | null>(null);
@@ -233,7 +235,7 @@ const openTypeSheet = async () => {
// 返回
const back = () => {
routerPushByKey('my_mytask');
routerPushByKey(backKey.value as any);
};
// 导出
@@ -381,6 +383,9 @@ onMounted(async () => {
if(route.query.status){
currentStatus.value = route.query.status.toString();
}
if(route.query.key){
backKey.value = route.query.key.toString();
}
}
})
</script>
+4 -4
View File
@@ -67,23 +67,23 @@ const tabChange = (k: string) => {
// 处置申请跳转
const jumpTo = (act: string) => {
routerPushByKey("details_disposal",{ query: { id: currentRow.value?.change_id.toString(),type:act } });
routerPushByKey("details_disposal",{ query: { id: currentRow.value?.change_id?.toString(),type:act } });
}
// 打开详情、修改重报
const openDetail = (c: any,type:string = '') => {
console.log(c)
if(type==='detail'){
routerPushByKey("details_index",{ query: { id: c.change_id.toString(),status:c.status.toString() || '' } });
routerPushByKey("details_index",{ query: { id: c?.change_id?.toString(),status:c?.status?.toString() || '',key:'my_mytask' } });
}
if(type==='edit'){
routerPushByKey("my_initiatechange",{ query: { id: c.change_id.toString() } });
routerPushByKey("my_initiatechange",{ query: { id: c?.change_id?.toString() } });
}
};
// 打开资料关闭
const openDocClose = (c: any) => {
currentRow.value = c;
routerPushByKey("details_closedoc",{ query: { id: c.change_id.toString() } });
routerPushByKey("details_closedoc",{ query: { id: c?.change_id?.toString() } });
};
// 打开处置申请
const openDisposal = (c: any,) => {