变更申请表板块修改

This commit is contained in:
2026-09-01 17:30:13 +08:00
parent 378829da21
commit 09dbd8ba2d
2 changed files with 121 additions and 96 deletions
+107 -76
View File
@@ -22,6 +22,7 @@ import Jsa from "./modules/jsa.vue";
import Scl from "./modules/scl.vue"; import Scl from "./modules/scl.vue";
import RiskCheck from "./modules/riskCheck.vue"; import RiskCheck from "./modules/riskCheck.vue";
import ChangeCompareTable from "./modules/ChangeCompareTable.vue"; import ChangeCompareTable from "./modules/ChangeCompareTable.vue";
import { localStg } from '@/utils/storage';
const themeStore = useThemeStore(); const themeStore = useThemeStore();
@@ -44,7 +45,6 @@ const form = ref<ApplyForm>({
type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null, type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null,
}); });
const confirmed = ref<Record<string, boolean>>({});
const blockedTabs = ref<string[]>([]); const blockedTabs = ref<string[]>([]);
// ---------- 变更预识别 ---------- // ---------- 变更预识别 ----------
@@ -209,30 +209,15 @@ const submitApply = () => {
// emit("openChange", order); // emit("openChange", order);
}; };
const PURPOSE_OPTIONS = [
"满足市场需求", "提高生产效能", "改善操作条件", "增加安全性能",
"达到环保要求", "法律合规整改", "减少能耗物损", "稳定系统运行",
"完善管理制度", "达到质量改进", "设备可靠性升级",
];
const DEPT_APPROVER_OPTS = ["李主任(一车间主任)", "赵主任(二车间主任)", "陈主任(三车间主任)"].map((x) => ({ label: x, value: x })); const DEPT_APPROVER_OPTS = ["李主任(一车间主任)", "赵主任(二车间主任)", "陈主任(三车间主任)"].map((x) => ({ label: x, value: x }));
const SIGNER_POOL = ["王仪表(技术部)", "王仪表(技术部 · 仪表)", "周设备(设备科 · 电气)", "王海燕(安全环保部)", "王海燕(质量部)"]; const SIGNER_POOL = ["王仪表(技术部)", "王仪表(技术部 · 仪表)", "周设备(设备科 · 电气)", "王海燕(安全环保部)", "王海燕(质量部)"];
const signerOpts = (cur: string) => [...new Set([cur, ...SIGNER_POOL])].map((x) => ({ label: x, value: x })); 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 recommendTools = () => {
const lv = form.value?.level === "重要" ? "重要" : "一般"; const lv = form.value?.level === "重要" ? "重要" : "一般";
tools.value = lv === "重要" ? ["HAZOP", "JSA", "检查表法", "通用检查表"] : ["JSA", "检查表法", "通用检查表"]; tools.value = lv === "重要" ? ["HAZOP", "JSA", "检查表法", "通用检查表"] : ["JSA", "检查表法", "通用检查表"];
window.$message?.success(`已按「${lv}变更」规则匹配风险分析工具,可继续手动重选`); window.$message?.success(`已按「${lv}变更」规则匹配风险分析工具,可继续手动重选`);
}; };
const files = ref<string[]>(["小试实验报告.pdf", "杂质初步分析.docx"]);
const previewFile = ref<string | null>(null); const previewFile = ref<string | null>(null);
const DISCIPLINE_SIGNER: Record<string, string> = { const DISCIPLINE_SIGNER: Record<string, string> = {
技术: "王仪表(技术部)", 仪表: "王仪表(技术部 · 仪表)", 电气: "周设备(设备科 · 电气)", 环保: "王海燕(安全环保部)", 质量: "王海燕(质量部)", 技术: "王仪表(技术部)", 仪表: "王仪表(技术部 · 仪表)", 电气: "周设备(设备科 · 电气)", 环保: "王海燕(安全环保部)", 质量: "王海燕(质量部)",
@@ -245,10 +230,6 @@ const toggleDiscipline = (d:any) => {
signers.value = {...result}; signers.value = {...result};
}; };
const TAB_NAME: Record<string, string> = {
pre: "变更预识别", form: "变更申请表", risk: "风险分析记录表",
train: "变更培训内容", pssr: "PSSR检查内容", accept: "验收评价", close: "变更关闭确认表",
};
const confirmTab = (t: string) => { const confirmTab = (t: string) => {
const v = !confirmed.value[t]; const v = !confirmed.value[t];
confirmed.value = { ...confirmed.value, [t]: v }; confirmed.value = { ...confirmed.value, [t]: v };
@@ -556,16 +537,6 @@ const uploadConfirm = () => {
const pluginDrawer = ref<boolean>(false); const pluginDrawer = ref<boolean>(false);
const currentPlugin = ref<string>(""); const currentPlugin = ref<string>("");
// 插件选择
// 注:风险核查(RISK_CHECK)的「AI 预填检查表」交互在 modules/riskCheck.vue 插件内部,
// 本页 toggleTool 仅负责打开插件抽屉,无独立 AI 预填入口,故不在此接 aiRiskCheck。
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") => { const exportAll = (items: any[], mode: "manual" | "ai") => {
riskRecords.value = [...riskRecords.value, ...items]; riskRecords.value = [...riskRecords.value, ...items];
@@ -583,10 +554,18 @@ const onBack = () => {
// **********************通用字段*********************** // **********************通用字段***********************
// 当前用户
const userInfo = ref<any>(localStg.get('userInfo'));
// 是否开启ai // 是否开启ai
const aiEnabled = ref<boolean>(true); const aiEnabled = ref<boolean>(true);
// 保存loading // 保存loading
const saveLoading = ref<boolean>(false); const saveLoading = ref<boolean>(false);
// 提交loading
const submitLoading = ref<boolean>(false);
// 下载loading
const downloadLoading = ref<boolean>(false);
// 重新生成loading
const regenerateLoading = ref<boolean>(false);
// 变更识别loading // 变更识别loading
const preLoading = ref<boolean>(false); const preLoading = ref<boolean>(false);
// 风险预分析loading // 风险预分析loading
@@ -594,11 +573,13 @@ const analyzing = ref<boolean>(false);
// 生成变更申请单loading // 生成变更申请单loading
const generateLoading = ref<boolean>(false); const generateLoading = ref<boolean>(false);
// btn disabled // btn disabled
const btnDisabled = computed(() => saveLoading.value || preLoading.value || analyzing.value || generateLoading.value); const btnDisabled = computed(() => saveLoading.value || preLoading.value || analyzing.value || generateLoading.value || submitLoading.value || downloadLoading.value || regenerateLoading.value);
// 变更名称 // 变更名称
const title = ref<string>(''); const title = ref<string>('');
// 当前id // 当前id
const currentId = ref<number>(0); const currentId = ref<number>(0);
// 是否确认
const confirmed = ref<Record<string, boolean>>({});
// **********************tab切换*********************** // **********************tab切换***********************
const tab = ref<string>("pre"); const tab = ref<string>("pre");
@@ -742,22 +723,67 @@ const genForm = async () => {
// --------------------- 变更申请表 --------------------- // --------------------- 变更申请表 ---------------------
// 表单 // 表单
const changForm = ref<{ const changForm = ref<{
name: string, change_type: number | null,
mainChanges: string[], change_level: number | null,
related: string, duration_type: number | null,
restore_deadline: number | null,
org_id: number | null,
urgent: number,
purposes: string[], purposes: string[],
effect: string, effect: string,
type: string, main_changes: string[],
duration: string related_changes: string[],
materials: string,
update_docs: string[],
disciplines: string[],
manage_dept: string,
plan_use_date: number | null,
risk_tools: string[],
}>({ }>({
name: '', change_type: null,
mainChanges: [], change_level: null,
related: '', duration_type: null,
restore_deadline: null,
org_id: null,
urgent: 0,
purposes: [], purposes: [],
effect: '', effect: '',
type: '', main_changes: [''],
duration: '' related_changes: [''],
materials: '',
update_docs: [],
disciplines: [],
manage_dept: '',
plan_use_date: null,
risk_tools: [],
}); });
// 上传的资料
const files = ref<string[]>(["小试实验报告.pdf", "杂质初步分析.docx"]);
// 变更目的选项
const PURPOSE_OPTIONS = [
"满足市场需求", "提高生产效能", "改善操作条件", "增加安全性能",
"达到环保要求", "法律合规整改", "减少能耗物损", "稳定系统运行",
"完善管理制度", "达到质量改进", "设备可靠性升级",
];
const levelScores = ref<number[]>([2, 2, 3, 1, 2, 3]);
const totalScore = computed(() => levelScores.value.reduce((a: number, b: number) => 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 toggleTool = (t: {key:string,label:string}) => {
const { risk_tools } = changForm.value;
changForm.value.risk_tools = risk_tools.includes(t.key) ? risk_tools.filter((key:string) => key !== t.key) : [...risk_tools, t.key];
};
const TAB_NAME: Record<string, string> = {
pre: "变更预识别", form: "变更申请表", risk: "风险分析记录表",
train: "变更培训内容", pssr: "PSSR检查内容", accept: "验收评价", close: "变更关闭确认表",
};
</script> </script>
@@ -884,18 +910,18 @@ const changForm = ref<{
<div class="grid gap-4 grid-cols-2"> <div class="grid gap-4 grid-cols-2">
<div class="flex gap-3"> <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="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="请输入申请部门" /> <NInput v-model:value="changForm.manage_dept" placeholder="请输入申请部门" />
</div> </div>
<div class="flex gap-3"> <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="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">申请人</div>
<NInput v-model:value="ROLES[0].person" disabled /> <NInput :value="userInfo?.real_name" disabled />
</div> </div>
</div> </div>
<!-- 变更目的 --> <!-- 变更目的 -->
<div class="flex gap-3"> <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="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"> <div class="rounded-lg border border-[#ACC6E5] bg-[#EAF0F9]/40 p-3">
<NCheckboxGroup v-model:value="form.purposes"> <NCheckboxGroup v-model:value="changForm.purposes">
<div class="flex flex-wrap gap-y-2 gap-x-4"> <div class="flex flex-wrap gap-y-2 gap-x-4">
<NCheckbox v-for="p in PURPOSE_OPTIONS" :key="p" :value="p" :label="p" /> <NCheckbox v-for="p in PURPOSE_OPTIONS" :key="p" :value="p" :label="p" />
</div> </div>
@@ -905,20 +931,20 @@ const changForm = ref<{
<!-- 预期效果 --> <!-- 预期效果 -->
<div class="flex gap-3"> <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="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="" /> <NInput type="textarea" v-model:value="changForm.effect" rows="2" placeholder="" />
</div> </div>
<!-- 主要变更内容 --> <!-- 主要变更内容 -->
<div class="flex gap-3"> <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="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 class="flex-1 space-y-2">
<div v-for="(m, i) in form.mainChanges" :key="i" class="flex items-center gap-2"> <div v-for="(_, i) in changForm.main_changes" :key="i" class="flex items-center gap-2">
<span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span> <span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span>
<NInput v-model:value="m as string" placeholder="" /> <NInput v-model:value="changForm.main_changes[i]" type="text" placeholder="" />
<NButton text type="error" @click="form = { ...form, mainChanges: form.mainChanges.filter((_, j) => j !== i) }"> <NButton v-if="changForm.main_changes.length > 1" text type="error" @click="changForm.main_changes = changForm.main_changes.splice(i, 1)">
<Icon icon="line-md:close-circle" class="size-16px" /> <Icon icon="line-md:close-circle" class="size-16px" />
</NButton> </NButton>
</div> </div>
<NButton text type="primary" class="text-xs" @click="form = { ...form, mainChanges: [...form.mainChanges, ''] }"> <NButton text type="primary" class="text-xs" @click="changForm.main_changes = [...changForm.main_changes, '']">
<Icon icon="ic:round-plus" class="size-14px" /> 添加变更点 <Icon icon="ic:round-plus" class="size-14px" /> 添加变更点
</NButton> </NButton>
</div> </div>
@@ -927,24 +953,25 @@ const changForm = ref<{
<div class="flex gap-3"> <div class="flex gap-3">
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">连带变更内容</div> <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">连带变更内容</div>
<div class="flex-1 space-y-2"> <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"> <div v-for="(_, i) in changForm.related_changes" :key="i" class="flex items-center gap-2">
<span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span> <span class="w-5 shrink-0 text-sm text-slate-400">{{ i + 1 }}.</span>
<NInput v-model:value="m as string" placeholder="连带变更点(如:更新操作规程)" /> <NInput v-model:value="changForm.related_changes[i]" placeholder="连带变更点(如:更新操作规程)" />
<NButton text type="error" @click="form = { ...form, related: (form.related ? form.related.split('、') : ['']).filter((_, j) => j !== i).join('、') }"> <NButton v-if="changForm.related_changes.length > 1" text type="error" @click="changForm.related_changes = changForm.related_changes.splice(i, 1)">
<Icon icon="line-md:close-circle" class="size-16px" /> <Icon icon="line-md:close-circle" class="size-16px" />
</NButton> </NButton>
</div> </div>
<NButton text type="primary" class="text-xs" @click="form = { ...form, related: form.related ? `${form.related}、` : '、' }"> <NButton text type="primary" class="text-xs" @click="changForm.related_changes = [...changForm.related_changes, '']">
<Icon icon="ic:round-plus" class="size-14px" /> 添加连带变更点 <Icon icon="ic:round-plus" class="size-14px" /> 添加连带变更点
</NButton> </NButton>
</div> </div>
</div> </div>
<!-- 变更类型 / 变更等级 --> <!-- 变更类型 / 变更等级 -->
<div class="grid gap-4 grid-cols-2"> <div class="grid gap-4 grid-cols-2">
<!-- 变更类型 -->
<div class="flex gap-3"> <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="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"> <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
<NRadioGroup v-model:value="form.type"> <NRadioGroup v-model:value="changForm.change_type">
<div class="flex gap-x-2"> <div class="flex gap-x-2">
<NRadio v-for="t in ['工艺', '设备', '管理']" :key="t" :value="t">{{t}}</NRadio> <NRadio v-for="t in ['工艺', '设备', '管理']" :key="t" :value="t">{{t}}</NRadio>
</div> </div>
@@ -955,10 +982,11 @@ const changForm = ref<{
</NButton> </NButton>
</div> </div>
</div> </div>
<!-- 变更等级 -->
<div class="flex gap-3"> <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="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"> <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
<NRadioGroup v-model:value="form.level"> <NRadioGroup v-model:value="changForm.change_level">
<div class="flex gap-x-2"> <div class="flex gap-x-2">
<NRadio v-for="t in ['一般', '重要']" :key="t" :value="t">{{t}}</NRadio> <NRadio v-for="t in ['一般', '重要']" :key="t" :value="t">{{t}}</NRadio>
</div> </div>
@@ -974,21 +1002,23 @@ const changForm = ref<{
<div class="flex gap-3"> <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="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"> <div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
<NRadioGroup v-model:value="form.duration"> <NRadioGroup v-model:value="changForm.duration_type">
<div class="flex gap-x-2"> <div class="flex gap-x-2">
<NRadio v-for="t in ['永久', '临时']" :key="t" :value="t">{{t}}</NRadio> <NRadio v-for="t in [0, 1]" :key="t" :value="t">
{{ t === 0 ? '永久' : '临时' }}
</NRadio>
</div> </div>
</NRadioGroup> </NRadioGroup>
<NCheckbox v-model:checked="form.urgent">紧急</NCheckbox> <NCheckbox v-model:checked="changForm.urgent" :checked-value="1" :unchecked-value="0">紧急</NCheckbox>
</div> </div>
</div> </div>
<div class="flex gap-3"> <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="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"> <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 辅助分析在各工具插件内使用" <NButton size="small" round v-for="t in RISK_TOOLS_FORM" :key="t.key" @click="toggleTool(t)" title="点击选用 / 取消;AI 辅助分析在各工具插件内使用"
class="text-sm" class="text-sm"
:ghost="tools.includes(t.key) ? false : true" :ghost="changForm.risk_tools.includes(t.key) ? false : true"
:type="tools.includes(t.key) ? 'primary' : 'default'" :type="changForm.risk_tools.includes(t.key) ? 'primary' : 'default'"
> >
{{ t.label }} {{ t.label }}
</NButton> </NButton>
@@ -1002,12 +1032,14 @@ const changForm = ref<{
<div class="flex gap-3"> <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="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"> <div class="flex flex-1 flex-wrap items-center gap-2">
<NDatePicker v-model:value="form.date" type="date" class="!max-w-[220px]" /> <NDatePicker v-model:value="changForm.plan_use_date" type="date" value-format="yyyy-MM-dd" class="!max-w-[220px]" />
<template v-if="form.duration === '临时'"> <!-- 临时 -->
<template v-if="changForm.duration_type === 1">
<span class="whitespace-nowrap text-sm text-slate-500">计划恢复时间 <span class="text-red-500">*</span></span> <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]" /> <NDatePicker v-model:value="changForm.restore_deadline" type="date" value-format="yyyy-MM-dd" class="!max-w-[220px]" />
<span class="text-xs text-amber-600">临时变更到期系统自动提醒恢复原状</span> <span class="text-xs text-amber-600">临时变更到期系统自动提醒恢复原状</span>
</template> </template>
<!-- 永久 -->
<span v-else class="whitespace-nowrap text-xs text-slate-300">勾选临时后在此并排填写计划恢复时间</span> <span v-else class="whitespace-nowrap text-xs text-slate-300">勾选临时后在此并排填写计划恢复时间</span>
</div> </div>
</div> </div>
@@ -1017,7 +1049,7 @@ const changForm = ref<{
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">所需材料</div> <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="flex-1 space-y-2">
<div class="text-[11px] text-slate-400">施工所需材料生成变更申请表时系统自动生成支持手动修改</div> <div class="text-[11px] text-slate-400">施工所需材料生成变更申请表时系统自动生成支持手动修改</div>
<NInput type="textarea" v-model:value="materialsText" rows="4" placeholder="点击「生成变更申请单」后由系统按变更内容自动生成,也可直接填写" /> <NInput type="textarea" v-model:value="changForm.materials" rows="4" placeholder="点击「生成变更申请单」后由系统按变更内容自动生成,也可直接填写" />
</div> </div>
</div> </div>
<!-- 资料上传 --> <!-- 资料上传 -->
@@ -1038,7 +1070,7 @@ const changForm = ref<{
<div class="flex gap-3"> <div class="flex gap-3">
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">需更新的资料</div> <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"> <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"> <NCheckboxGroup v-model:value="changForm.update_docs">
<div class="flex flex-wrap gap-y-2 gap-x-4"> <div class="flex flex-wrap gap-y-2 gap-x-4">
<NCheckbox v-for="p in ['PID图纸', '操作规程', '总图', '设备台账', '工艺卡片', '联锁台账', '应急预案']" :key="p" :value="p" :label="p" /> <NCheckbox v-for="p in ['PID图纸', '操作规程', '总图', '设备台账', '工艺卡片', '联锁台账', '应急预案']" :key="p" :value="p" :label="p" />
</div> </div>
@@ -1051,18 +1083,18 @@ const changForm = ref<{
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">涉及专业</div> <div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600">涉及专业</div>
<div class="flex-1"> <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"> <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"> <NCheckboxGroup v-model:value="changForm.disciplines" @update:value="toggleDiscipline">
<div class="flex flex-wrap gap-y-2 gap-x-4"> <div class="flex flex-wrap gap-y-2 gap-x-4">
<NCheckbox v-for="p in ['技术', '仪表', '电气', '环保', '质量']" :key="p" :value="p" :label="p" /> <NCheckbox v-for="p in ['技术', '仪表', '电气', '环保', '质量']" :key="p" :value="p" :label="p" />
</div> </div>
</NCheckboxGroup> </NCheckboxGroup>
<span class="self-center text-xs text-slate-400">多选决定审批流中专业会签/审批的路由范围</span> <span class="self-center text-xs text-slate-400">多选决定审批流中专业会签/审批的路由范围</span>
</div> </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"> <!-- <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" />已联动专业会签 <Icon icon="lucide:users" class="size-12px" />已联动专业会签
<NTag size="small" type="info" v-for="(p, d) in signers" :key="d">{{ d }} · {{ p }}</NTag> <NTag size="small" type="info" v-for="(p, d) in signers" :key="d">{{ d }} · {{ p }}</NTag>
<span class="text-slate-400">在底部审批流程设置中可调整人选</span> <span class="text-slate-400">在底部审批流程设置中可调整人选</span>
</div> </div> -->
</div> </div>
</div> </div>
@@ -1102,7 +1134,7 @@ const changForm = ref<{
</div> </div>
</div> </div>
<NButton size="small" class="text-xs" :type="pluginDone[t.key] !== undefined ? 'default' : tools.includes(t.key) ? 'primary' : 'default'" <NButton size="small" class="text-xs" :type="pluginDone[t.key] !== undefined ? 'default' : tools.includes(t.key) ? 'primary' : 'default'"
@click="toggleTool(t)"> >
使用 使用
</NButton> </NButton>
</div> </div>
@@ -1508,17 +1540,16 @@ const changForm = ref<{
<!-- 全局按钮下载 / 重新生成 / 确认完毕 --> <!-- 全局按钮下载 / 重新生成 / 确认完毕 -->
<div v-if="tab !== 'pre'" class="flex flex-wrap items-center justify-between gap-2 border-t p-4"> <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"> <div class="flex flex-wrap gap-2">
<NButton ghost type="success" @click="download"> <NButton ghost type="success" :disabled="btnDisabled" :loading="downloadLoading" @click="download">
<Icon icon="material-symbols:download" class="size-16px" /> 下载{{ TAB_NAME[tab] }} <Icon icon="material-symbols:download" class="size-16px" /> 下载{{ TAB_NAME[tab] }}
</NButton> </NButton>
<NButton ghost type="warning" @click="regenerate"> <NButton ghost type="warning" :disabled="btnDisabled" :loading="regenerateLoading" @click="regenerate">
<Icon icon="tdesign:refresh" class="size-14px" /> 重新生成{{ TAB_NAME[tab] }} <Icon icon="tdesign:refresh" class="size-14px" /> 重新生成{{ TAB_NAME[tab] }}
</NButton> </NButton>
<NButton ghost :type="confirmed[tab] ? 'success' : 'primary'" @click="confirmTab(tab)"> <NButton ghost :type="confirmed[tab] ? 'success' : 'primary'" :disabled="btnDisabled" @click="confirmTab(tab)">
<Icon icon="ix:success" class="size-16px" /> {{ confirmed[tab] ? '已确认完毕' : '本标签内容确认完毕' }} <Icon icon="ix:success" class="size-16px" /> {{ confirmed[tab] ? '已确认完毕' : '本标签内容确认完毕' }}
</NButton> </NButton>
</div> </div>
<span class="text-xs text-slate-400">审批人 / 保存 / 提交均在底部固定行滚动时保持可见</span>
</div> </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)]"> <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'"> <template v-if="tab === 'pre'">
@@ -1546,9 +1577,9 @@ const changForm = ref<{
</NButton> </NButton>
</div> </div>
<div class="ml-auto flex shrink-0 items-center gap-2"> <div class="ml-auto flex shrink-0 items-center gap-2">
<NButton size="small" type="primary" @click="saveDraft">保存</NButton> <NButton size="small" type="primary" :disabled="btnDisabled" :loading="saveLoading" @click="saveDraft">保存</NButton>
<span v-if="!form" class="flex items-center text-xs text-amber-600">请先在变更预识别页生成变更申请单</span> <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 ? '提交进入审批流' : '请先生成变更申请单'"> <NButton size="small" type="primary" @click="submitApply" :disabled="btnDisabled" :loading="submitLoading" :title="form ? '提交进入审批流' : '请先生成变更申请单'">
提交 <Icon icon="formkit:right" class="size-14px" /> 提交 <Icon icon="formkit:right" class="size-14px" />
</NButton> </NButton>
</div> </div>
+13 -19
View File
@@ -62,25 +62,19 @@ const progressPct = (p: string) => {
</div> </div>
<div class="space-y-2 scroll"> <div class="space-y-2 scroll">
<div v-for="d in drafts" :key="d.id" class="rounded-lg border px-4 py-3"> <div v-for="d in drafts" :key="d.id" class="rounded-lg border px-4 py-3">
<div class="flex flex-wrap items-start gap-3"> <div class="flex flex-wrap items-center gap-3">
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<div class="text-sm font-semibold text-slate-800 dark:text-white">{{ d.name }}</div> <div class="text-sm font-semibold text-slate-800 dark:text-white">{{ d.name }}</div>
<div class="mt-1 flex flex-wrap items-center gap-2 text-[11px] text-slate-400"> <div class="mt-1 flex flex-wrap items-center gap-2 text-[11px] text-slate-400">
<span class="font-mono">{{ d.id }}</span> <span class="font-mono">{{ d.id }}</span>
<span>保存于 {{ d.time }}</span> <span>保存于 {{ d.time }}</span>
<NTag type="primary" size="small" class="text-11px">{{ d.step }}</NTag> <NTag type="primary" size="small" class="text-11px">{{ d.step }}</NTag>
</div> </div>
<div class="mt-2 flex items-center gap-2"> </div>
<div class="h-1.5 w-40 overflow-hidden rounded-full bg-slate-100"> <div class="flex shrink-0 items-center gap-2 pt-2px">
<div class="h-full rounded-full bg-[#6E93C6]" :style="{ width: progressPct(d.progress) + '%' }" /> <NButton class="text-xs" text type="error" @click="remove(d)">删除</NButton>
</div> <NButton class="text-xs" text type="primary" @click="continueEdit(d)">继续编辑</NButton>
<span class="text-[11px] text-slate-400">编辑进度 {{ d.progress }}</span> </div>
</div>
</div>
<div class="flex shrink-0 items-center gap-2 pt-2px">
<NButton class="text-xs" text type="error" @click="remove(d)">删除</NButton>
<NButton class="text-xs" text type="primary" @click="continueEdit(d)">继续编辑</NButton>
</div>
</div> </div>
</div> </div>
<div v-if="drafts.length === 0" class="rounded-lg border border-dashed bg-white py-12 text-center"> <div v-if="drafts.length === 0" class="rounded-lg border border-dashed bg-white py-12 text-center">