验收评价完成
This commit is contained in:
@@ -31,7 +31,7 @@ const form = ref<ApplyForm>({
|
||||
name: "", dept: "", mainChanges: [""], related: "", purposes: [], effect: "",
|
||||
type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null,
|
||||
});
|
||||
const tab = ref<string>("train");
|
||||
const tab = ref<string>("accept");
|
||||
const TABS = [
|
||||
{ id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
|
||||
{ id: "form", name: "变更申请表", icon: 'akar-icons:file' },
|
||||
@@ -480,6 +480,52 @@ M-201A 搅拌电机由 55kW 更换为 75kW 防爆电机(ExdⅡBT4 → ExdⅡCT
|
||||
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("");
|
||||
|
||||
</script>
|
||||
|
||||
@@ -981,11 +1027,235 @@ M-201A 搅拌电机由 55kW 更换为 75kW 防爆电机(ExdⅡBT4 → ExdⅡCT
|
||||
<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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 -->
|
||||
<!-- 全局按钮:下载 / 重新生成 / 确认完毕 -->
|
||||
<div v-if="tab !== 'pre'" class="mt-4 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">
|
||||
<NButton ghost type="success" @click="download">
|
||||
<Icon icon="material-symbols:download" class="size-16px" /> 下载{{ TAB_NAME[tab] }}
|
||||
@@ -1204,7 +1474,7 @@ M-201A 搅拌电机由 55kW 更换为 75kW 防爆电机(ExdⅡBT4 → ExdⅡCT
|
||||
overflow-y: auto;
|
||||
}
|
||||
.scroll2 {
|
||||
height: calc(100vh - 409px);
|
||||
height: calc(100vh - 393px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user