验收评价完成

This commit is contained in:
2026-09-07 11:32:26 +08:00
parent b7cdde9263
commit 0212c254ac
2 changed files with 144 additions and 110 deletions
+4 -3
View File
@@ -151,8 +151,8 @@ const getAllFormData = () => {
let changeApplyForm = changeApplyRef.value?.form;
let changeRiskForm = riskAnalysisRef.value?.form;
let changeTrainingForm = changeTrainingRef.value?.form;
let pssrForm = pssrRef.value?.form;
let evaluationForm = evaluationRef.value?.form;
let pssrForm = pssrRef.value?.list;
let evaluationForm = evaluationRef.value?.list;
let closeConfirmForm = closeConfirmRef.value?.form;
return {
title: title.value,
@@ -352,7 +352,8 @@ onMounted(() => {
<div v-show="tab === 'evaluation'">
<Evaluation
ref="evaluationRef"
:type="tab"
:type="tab"
:orgTree="orgTree"
:isAiOpen="aiEnabled"
:title="title"
:currentId="currentId"
+140 -107
View File
@@ -1,28 +1,40 @@
<!-- 验收评价 -->
<script setup lang="ts">
import { ref } from 'vue';
import { h, ref } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { getColorWithOpacity } from "@/utils/common";
import Footer from './footer.vue';
const themeStore = useThemeStore();
const props = defineProps(['type','isAiOpen','title','currentId','formInfo'])
const props = defineProps(['type','orgTree','isAiOpen','title','currentId','formInfo'])
const emit = defineEmits(['save','submit','confirmTab']);
const form = ref<{ content: string }>({
content: ''
});
const editing = ref<any>(null);
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 list = ref<{id:number,item:string,basis:string,standard:string,method:string,org_id:number}[]>([]);
// 添加验收项
const addRow = () => {
list.value.push({ id: 0, item: '', basis: '', standard: '', method: '',org_id: 0 });
}
// 删除验收项
const deleteRow = (i:number) => {
list.value.splice(i, 1);
}
// 自定义渲染展开图标
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 saveDraft = () => {
@@ -37,7 +49,18 @@ const confirmTab = (tab:string, val:boolean) => {
emit('confirmTab', tab, val);
}
defineExpose({form})
const findNodeById = (tree:any,id:number) => {
for (const node of tree) {
if (node.id === id) return node;
if (node.children && node.children.length) {
const found:any = findNodeById(node.children,id);
if (found) return found;
}
}
return null; // 未找到
}
defineExpose({list})
</script>
<template>
@@ -74,115 +97,125 @@ defineExpose({form})
<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-12 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-46 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))"
<tr v-if="list.length===0">
<td colspan="6" class="py-4 px-2 text-center text-slate-400">暂无验收项目</td>
</tr>
<tr v-for="(row, i) in list" :key="i" 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 === `item-${i}-${row.item}`"
class="text-xs"
v-model:value="row.item"
type="textarea"
autofocus
rows="2"
@blur="editing = null"
/>
<button v-else @click="editing = `item-${i}-${row.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 === `basis-${i}-${row.basis}`"
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">
<NPopconfirm
positive-text="确定"
:positiveButtonProps="{ size: 'tiny' }"
:negativeButtonProps="{ size: 'tiny' }"
@positive-click="acceptRows = acceptRows.filter((x) => x.id !== row.id)"
>
<template #trigger>
<NButton text type="error" title="删除本验收项">
<Icon icon="iconamoon:trash" class="size-16px" />
</NButton>
</template>
确定删除吗?
</NPopconfirm>
</td>
size="small"
v-model:value="row.basis"
autofocus
@blur="editing = null"
/>
<button v-else @click="editing = `basis-${i}-${row.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 class="border-t px-3 py-2.5">
<NInput
v-if="editing === `standard-${i}-${row.standard}`"
class="text-xs"
v-model:value="row.standard"
type="textarea"
autofocus
rows="2"
@blur="editing = null"
/>
<button v-else @click="editing = `standard-${i}-${row.standard}`" 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.standard || '点击填写量化验收标准' }}
</button>
</td>
<td class="border-t px-3 py-2.5">
<NInput
v-if="editing === `method-${i}-${row.method}`"
class="text-xs"
v-model:value="row.method"
type="textarea"
autofocus
rows="2"
@blur="editing = null"
/>
<button v-else @click="editing = `method-${i}-${row.method}`" 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.method || '点击填写验收方式' }}
</button>
</td>
<td class="border-t px-3 py-2.5">
<NTreeSelect
v-if="editing === `org-${i}-${row.org_id}`"
size="small"
v-model:value="row.org_id"
default-expand-all
label-field="label"
key-field="id"
:options="props.orgTree"
:render-switcher-icon="renderSwitcherIcon"
placeholder="请选择组织层级"
@blur="editing = null"
/>
<button v-else @click="editing = `org-${i}-${row.org_id}`" 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]"
>
{{ findNodeById(orgTree,row.org_id)?.label || '点击选择组织层级' }}
</button>
</td>
<td class="border-t px-2 py-2.5 text-center">
<NPopconfirm
positive-text="确定"
:positiveButtonProps="{ size: 'tiny' }"
:negativeButtonProps="{ size: 'tiny' }"
@positive-click="deleteRow(i)"
>
<template #trigger>
<NButton text type="error" class="mt-1.5" title="删除本验收项">
<Icon icon="iconamoon:trash" class="size-16px" />
</NButton>
</template>
确定删除吗
</NPopconfirm>
</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 ghost class="text-xs" @click="addRow">
<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>