From 8690e8f223d07cc4aedc3f7c1a38316f223b20cc Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期一, 19 十月 2026 17:11:42 +0800
Subject: [PATCH] feat(quality-monitor): 重构质量监控计划模块并集成检验任务流程

---
 src/components/Table/lims-table.vue                                                                  |   10 
 src/views/CNAS/process/ensureResults/ensureResultsValidity/index.vue                                 |  788 ++++++++++++++++++++-------------
 src/api/cnas/process/ensureResults/qualityMonitor.js                                                 |   32 +
 src/views/CNAS/process/method/methodVerification/component/calibrationsFileDia.vue                   |   25 
 src/views/CNAS/process/method/methodVerification/component/wordFormDIa.vue                           |   13 
 src/api/cnas/process/method/methodVerification.js                                                    |    9 
 src/views/CNAS/process/ensureResults/ensureResultsValidity/components/inspectionEvaluationDialog.vue |  233 ++++++++++
 src/views/CNAS/process/ensureResults/ensureResultsValidity/components/detailFormDialog.vue           |  136 ++++
 src/views/business/inspectionTask/inspection.vue                                                     |   17 
 src/views/business/inspectionTask/index.vue                                                          |   73 ++
 10 files changed, 980 insertions(+), 356 deletions(-)

diff --git a/src/api/cnas/process/ensureResults/qualityMonitor.js b/src/api/cnas/process/ensureResults/qualityMonitor.js
index 55bb995..73883b6 100644
--- a/src/api/cnas/process/ensureResults/qualityMonitor.js
+++ b/src/api/cnas/process/ensureResults/qualityMonitor.js
@@ -1,6 +1,38 @@
 // 璐ㄩ噺鐩戞帶璁″垝椤甸潰鐩稿叧鎺ュ彛
 import request from '@/utils/request'
 
+export function createAnnualPlan(data) {
+  return request({ url: '/qualityMonitor/createAnnualPlan', method: 'post', data })
+}
+
+export function getAnnualPlan(params) {
+  return request({ url: '/qualityMonitor/getAnnualPlan', method: 'get', params })
+}
+
+export function submitAnnualPlan(qualityMonitorId) {
+  return request({ url: '/qualityMonitor/submitAnnualPlan', method: 'post', data: { qualityMonitorId } })
+}
+
+export function dispatchInspectionTask(qualityMonitorDetailsId) {
+  return request({ url: '/qualityMonitor/dispatchInspectionTask', method: 'post', data: { qualityMonitorDetailsId } })
+}
+
+export function cancelInspectionTask(qualityMonitorDetailsId) {
+  return request({ url: '/qualityMonitor/cancelInspectionTask', method: 'post', data: { qualityMonitorDetailsId } })
+}
+
+export function getInspectionEvaluation(qualityMonitorDetailsId) {
+  return request({ url: '/qualityMonitor/getInspectionEvaluation', method: 'get', params: { qualityMonitorDetailsId } })
+}
+
+export function submitInspectionEvaluation(data) {
+  return request({ url: '/qualityMonitor/submitInspectionEvaluation', method: 'post', data })
+}
+
+export function approveInspectionEvaluation(data) {
+  return request({ url: '/qualityMonitor/approveInspectionEvaluation', method: 'post', data })
+}
+
 // 鐩戞帶骞村害璁″垝鍒楄〃
 export function pageQualityMonitor(query) {
   return request({
diff --git a/src/api/cnas/process/method/methodVerification.js b/src/api/cnas/process/method/methodVerification.js
index 582953e..e0658e8 100644
--- a/src/api/cnas/process/method/methodVerification.js
+++ b/src/api/cnas/process/method/methodVerification.js
@@ -47,6 +47,15 @@
   })
 }
 
+// 鎸夊綋鍓嶆墍閫夎澶囧疄鏃惰鍙栨牎鍑嗚瘉涔�
+export function getCurrentCalibrationFiles(query) {
+  return request({
+    url: '/processMethodVerify/getCurrentCalibrationFiles',
+    method: 'get',
+    params: query,
+  })
+}
+
 // 淇敼鏍囧噯鏂规硶楠岃瘉
 export function updateMethodVerify(data) {
   return request({
diff --git a/src/components/Table/lims-table.vue b/src/components/Table/lims-table.vue
index 8b7a584..70539e5 100644
--- a/src/components/Table/lims-table.vue
+++ b/src/components/Table/lims-table.vue
@@ -35,7 +35,7 @@
         :label="item.label"
         :prop="item.prop"
         :show-overflow-tooltip="item.dataType === 'action' || item.dataType === 'slot' ? false : true"
-        :min-width="columnWidth(item)"
+        :min-width="columnMinWidth(item)"
         :sortable="item.sortable ? true : false"
         :type="item.type"
         :width="columnWidth(item)"
@@ -214,6 +214,14 @@
 })
 
 function columnWidth(item) {
+  if (item.minWidth) return undefined
+  return item.dataType === 'action' ? actionColumnWidth.value : item.width
+}
+
+// 閰嶇疆 minWidth 鏃跺彧绾︽潫鏈�灏忓搴︼紝鎶婂墿浣欑┖闂翠氦缁� Element Plus 鑷姩鍒嗛厤銆�
+// 鏈厤缃� minWidth 鐨勬棫椤甸潰缁х画娌跨敤鍘熸湁鍥哄畾 width 琛屼负銆�
+function columnMinWidth(item) {
+  if (item.minWidth) return item.minWidth
   return item.dataType === 'action' ? actionColumnWidth.value : item.width
 }
 
diff --git a/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/detailFormDialog.vue b/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/detailFormDialog.vue
index 62415a7..6341f59 100644
--- a/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/detailFormDialog.vue
+++ b/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/detailFormDialog.vue
@@ -1,45 +1,73 @@
 <template>
   <div>
-    <el-dialog title="缂栬緫" v-model="formDia"
+    <el-dialog :title="operationType === 'add' ? '鏂板璐ㄦ帶椤圭洰' : '缂栬緫璐ㄦ帶椤圭洰'" v-model="formDia"
                :close-on-click-modal="false"
                :close-on-press-escape="false"
                width="80%" @close="closeDia">
       <el-form :model="form" :rules="rules" ref="form" label-width="140px">
         <el-col :span="12">
-          <el-form-item label="鐩戞帶鐩殑" prop="monitorPurpose">
-            <el-input v-model="form.monitorPurpose" size="small" clearable></el-input>
+          <el-form-item label="搴忓彿" prop="sortNo">
+            <el-input-number v-model="form.sortNo" :min="1" style="width:100%" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="璁″垝寮�灞曟椂闂�" prop="plannedTime">
-            <el-input v-model="form.plannedTime" size="small" clearable></el-input>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="鐩戞帶椤圭洰" prop="monitorProject">
+          <el-form-item label="璐ㄦ帶椤圭洰" prop="monitorProject">
             <el-input v-model="form.monitorProject" size="small" clearable></el-input>
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="鍙傚姞鍗曚綅锛堜汉鍛橈級" prop="participant">
-            <el-input v-model="form.participant" size="small" clearable></el-input>
+          <el-form-item label="鎶�鏈爣鍑�" prop="technicalStandardText">
+            <el-input v-model="form.technicalStandardText" size="small" clearable placeholder="鏍囧噯缂栧彿鍙婂悕绉帮紱闈炴娴嬬被鍙~鍐欑▼搴忔枃浠舵垨/" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="棰勭畻锛堝厓锛�" prop="budget">
-            <el-input v-model="form.budget" size="small" clearable></el-input>
+          <el-form-item label="璐ㄦ帶鏂瑰紡" prop="monitorWay">
+            <el-input v-model="form.monitorWay" size="small" clearable placeholder="濡傜洸鏍锋娴嬨�佷汉鍛樻瘮瀵广�佺洃鐫f鏌�" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="缁勭粐浜哄憳" prop="organization">
-            <el-input v-model="form.organization" size="small" clearable></el-input>
+          <el-form-item label="瀹屾垚鏃堕檺" prop="completionMonth">
+            <el-date-picker v-model="form.completionMonth" type="month" value-format="YYYY-MM" format="YYYY.MM" style="width:100%" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="鐩戞帶鏂瑰紡" prop="monitorWay">
-            <el-input v-model="form.monitorWay" size="small" clearable></el-input>
+          <el-form-item label="璐熻矗浜�" prop="responsibleUserId">
+            <el-select v-model="form.responsibleUserId" filterable style="width:100%">
+              <el-option v-for="item in userOptions" :key="item.id" :value="item.id" :label="item.name" />
+            </el-select>
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="浜嬮」绫诲埆" prop="itemKind">
+            <el-select v-model="form.itemKind" style="width: 100%">
+              <el-option label="闈炴娴嬬被" value="OTHER" />
+              <el-option label="妫�娴嬬被" value="INSPECTION" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <template v-if="form.itemKind === 'INSPECTION'">
+          <el-col :span="12"><el-form-item label="妫�娴嬫爣鍑�" prop="standardMethodId">
+            <el-select v-model="form.standardMethodId" filterable style="width: 100%" placeholder="璇烽�夋嫨妫�娴嬫爣鍑�" @change="handleStandardChange">
+              <el-option v-for="item in standardOptions" :key="item.id" :value="item.id" :label="`${item.code || ''} ${item.name || ''}`" />
+            </el-select>
+          </el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鍘熷璁板綍妯℃澘" prop="templateIds">
+            <el-select v-model="form.templateIds" multiple collapse-tags filterable style="width: 100%" placeholder="鍙�夋嫨澶氫釜妯℃澘">
+              <el-option v-for="item in templateOptions" :key="item.id" :value="item.id" :label="item.name" />
+            </el-select>
+          </el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鏍峰搧鍚嶇О"><el-input v-model="form.sampleName" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鏍峰搧瑙勬牸"><el-input v-model="form.sampleSpec" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="妫�娴嬪憳"><el-select v-model="form.inspectorUserId" filterable style="width: 100%">
+            <el-option v-for="item in userOptions" :key="item.id" :value="item.id" :label="item.name" />
+          </el-select></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="璇勪环浜�"><el-select v-model="form.evaluatorUserId" filterable style="width: 100%">
+            <el-option v-for="item in userOptions" :key="item.id" :value="item.id" :label="item.name" />
+          </el-select></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="浠诲姟鎴鏃�">
+            <el-date-picker v-model="form.dueDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
+          </el-form-item></el-col>
+        </template>
       </el-form>
       <template #footer><span  class="dialog-footer">
 					<el-button @click="closeDia">鍙� 娑�</el-button>
@@ -54,6 +82,9 @@
   addQualityMonitorDetail,
   updateQualityMonitorDetail
 } from "@/api/cnas/process/ensureResults/qualityMonitor";
+import { selectStandardMethodList } from '@/api/cnas/process/method/standardMethod';
+import { selectStandardTemplatePageList } from '@/api/standard/model';
+import { selectUserCondition } from '@/api/performance/class';
 
 export default {
   name: 'detailFormDialog',
@@ -66,6 +97,10 @@
       formDia: false,
       form: {
         qualityMonitorDetailsId: '',
+        sortNo: null,
+        technicalStandardText: '',
+        completionMonth: '',
+        responsibleUserId: null,
         monitorPurpose: '',
         plannedTime: '',
         monitorProject: '',
@@ -73,10 +108,26 @@
         budget: '',
         organization: '',
         monitorWay: '',
+        itemKind: 'OTHER',
+        standardMethodId: null,
+        templateId: null,
+        templateIds: [],
+        sampleName: '',
+        sampleSpec: '',
+        inspectorUserId: null,
+        evaluatorUserId: null,
+        dueDate: null,
       },
+      standardOptions: [],
+      templateOptions: [],
+      userOptions: [],
       rules: {
-        monitorPurpose: [{ required: true, message: '璇疯緭鍏ョ洃鎺х洰鐨�', trigger: 'blur' }],
-        monitorProject: [{ required: true, message: '璇疯緭鍏ョ洃鎺ч」鐩�', trigger: 'blur' }],
+        monitorProject: [{ required: true, message: '璇疯緭鍏ヨ川鎺ч」鐩�', trigger: 'blur' }],
+        technicalStandardText: [{ required: true, message: '璇疯緭鍏ユ妧鏈爣鍑�', trigger: 'blur' }],
+        monitorWay: [{ required: true, message: '璇疯緭鍏ヨ川鎺ф柟寮�', trigger: 'blur' }],
+        completionMonth: [{ required: true, message: '璇烽�夋嫨瀹屾垚鏃堕檺', trigger: 'change' }],
+        responsibleUserId: [{ required: true, message: '璇烽�夋嫨璐熻矗浜�', trigger: 'change' }],
+        templateIds: [{ required: true, type: 'array', min: 1, message: '璇疯嚦灏戦�夋嫨涓�涓師濮嬭褰曟ā鏉�', trigger: 'change' }],
       },
       upLoad: false,
       operationType: '',
@@ -86,18 +137,59 @@
   methods: {
     openDia(type, row) {
       this.formDia = true
+      selectStandardMethodList({ current: 1, size: 1000 }).then(res => { this.standardOptions = res.data?.records || res.data || [] })
+      selectStandardTemplatePageList({ current: 1, size: 1000 }).then(res => { this.templateOptions = res.data?.records || res.data || [] })
+      selectUserCondition({ type: 1 }).then(res => { this.userOptions = res.data || [] })
       this.operationType = type
       if (type === 'edit') {
         this.searchInfo(row)
       }
     },
     searchInfo (row) {
-      this.form = {...row}
+      const templateIds = row.templateIds
+        ? String(row.templateIds).split(',').filter(Boolean).map(id => Number(id))
+        : (row.templateId ? [Number(row.templateId)] : [])
+      this.form = {...row, itemKind: row.itemKind || 'OTHER', templateIds}
+    },
+    handleStandardChange(id) {
+      const standard = this.standardOptions.find(item => Number(item.id) === Number(id))
+      if (standard) this.form.technicalStandardText = `${standard.code || ''} ${standard.name || ''}`.trim()
+      this.form.templateIds = []
+      this.form.templateId = null
+    },
+    buildEntity () {
+      const entity = this.HaveJson(this.form)
+      const templateIds = Array.isArray(this.form.templateIds)
+        ? this.form.templateIds.map(id => Number(id)).filter(id => Number.isInteger(id) && id > 0)
+        : []
+      entity.templateIds = templateIds.join(',') || null
+      entity.templateId = templateIds.length ? templateIds[0] : null
+      entity.reviewerUserId = null
+      return entity
     },
     // 鎻愪氦琛ㄥ崟
     submitForm () {
       this.$refs['form'].validate((valid) => {
         if (valid) {
+          if (this.form.itemKind === 'INSPECTION') {
+            const required = ['standardMethodId','sampleName','inspectorUserId','evaluatorUserId','dueDate']
+            if (required.some(key => !this.form[key])) {
+              this.$message.warning('璇疯ˉ榻愭娴嬫爣鍑嗐�佹ā鏉裤�佹牱鍝併�佹娴嬪憳銆佽瘎浠蜂汉鍜屼换鍔℃埅姝㈡棩')
+              return
+            }
+            if (!Array.isArray(this.form.templateIds) || !this.form.templateIds.length) {
+              this.$message.warning('璇疯嚦灏戦�夋嫨涓�涓師濮嬭褰曟ā鏉�')
+              return
+            }
+            if (this.form.inspectorUserId === this.form.evaluatorUserId) {
+              this.$message.warning('妫�娴嬪憳涓嶈兘鍚屾椂鎷呬换璇勪环浜�')
+              return
+            }
+            if (this.form.dueDate.slice(0, 7) > this.form.completionMonth) {
+              this.$message.warning('浠诲姟鎴鏃ヤ笉鑳芥櫄浜庤鍒掑畬鎴愭湀浠�')
+              return
+            }
+          }
           if (this.operationType === 'add') {
             this.handleAdd()
           } else {
@@ -108,7 +200,7 @@
     },
     // 鎻愪氦鏂板
     handleAdd () {
-      let entity = this.HaveJson(this.form)
+      let entity = this.buildEntity()
       entity.qualityMonitorId = this.qualityMonitorId
       this.upLoad = true
       addQualityMonitorDetail(entity).then(res => {
@@ -122,7 +214,7 @@
     },
     // 鎻愪氦淇敼
     handleEdit () {
-      const entity = this.HaveJson(this.form)
+      const entity = this.buildEntity()
       this.upLoad = true
       updateQualityMonitorDetail(entity).then(res => {
         this.upLoad = false
diff --git a/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/inspectionEvaluationDialog.vue b/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/inspectionEvaluationDialog.vue
new file mode 100644
index 0000000..110092e
--- /dev/null
+++ b/src/views/CNAS/process/ensureResults/ensureResultsValidity/components/inspectionEvaluationDialog.vue
@@ -0,0 +1,233 @@
+<template>
+  <el-dialog v-model="visible" title="璐ㄩ噺鎺у埗璇勪环璁板綍" width="min(960px, 94vw)" @closed="$emit('close')">
+    <div class="record-code">璁板綍缂栧彿锛歀MSY-CX30-JL03</div>
+    <el-alert v-if="row"
+              :title="`鐩戞帶浠诲姟 ${row.inspectionTaskCode || row.entrustCode || ''} 路 ${row.monitorProject || ''}`"
+              type="info"
+              :closable="false"/>
+    <el-alert v-if="!canEdit" :title="editNotice" :type="loaded ? 'warning' : 'info'"
+              :closable="false" style="margin-top:8px"/>
+    <el-form :model="form" label-width="110px" style="margin-top: 16px">
+      <el-row :gutter="12">
+        <el-col :span="12">
+          <el-form-item label="妫�娴嬮」鐩�">
+            <el-input :model-value="row?.monitorProject" disabled/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="妫�娴嬫椂闂�">
+            <el-input :model-value="inspectionDateDisplay" readonly placeholder="鍘熷璁板綍澶嶆牳鍚庤嚜鍔ㄥ甫鍏�"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="妫�娴嬫牱鍝�">
+            <el-input :model-value="row?.sampleName || row?.sample" disabled/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鎵�鐢ㄨ澶�">
+            <el-input :model-value="form.rawEquipmentDescription || ''" type="textarea"
+                      :autosize="{ minRows: 1, maxRows: 6 }" readonly placeholder="鍘熷璁板綍澶嶆牳鍚庤嚜鍔ㄥ甫鍏�"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="璁惧瑙勬牸">
+            <el-input :model-value="form.rawEquipmentSpec || ''" type="textarea" :autosize="{ minRows: 1, maxRows: 6 }"
+                      readonly/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="璁惧缂栧彿">
+            <el-input :model-value="form.rawEquipmentCode || ''" type="textarea" :autosize="{ minRows: 1, maxRows: 6 }"
+                      readonly/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鐩戠潱鍛�">
+            <el-select v-model="form.supervisorUserId" filterable style="width: 100%" :disabled="!canEdit">
+              <el-option v-for="person in supervisorOptions" :key="person.id" :value="person.id" :label="person.name"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="琚洃鐫d汉鍛�">
+            <el-input :model-value="supervisedPersonName" disabled/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-form-item label="妫�娴嬫儏鍐靛強璇勪环">
+        <el-input v-model="form.evaluationConclusion" type="textarea" :rows="4" :disabled="!canEdit"/>
+      </el-form-item>
+      <el-table :data="form.resultRows" border style="width:100%">
+        <el-table-column label="浜哄憳濮撳悕" min-width="120">
+          <template #default>{{ supervisedPersonName }}</template>
+        </el-table-column>
+        <el-table-column label="褰撴棩妫�娴嬫暟鎹�" min-width="180">
+          <template #default="{ row: item }"><span :title="item.itemName || ''">{{
+              item.measuredValue
+            }} {{ item.unit === '/' ? '' : item.unit }}</span></template>
+        </el-table-column>
+        <el-table-column label="鐩叉牱鍊�" min-width="180">
+          <template #default="{ row: item }">
+            <el-input v-model="item.referenceValue" :disabled="!canEdit" placeholder="璇勪环浜哄~鍐�"/>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-checkbox v-if="canEdit" v-model="form.measurementConfirmed" style="margin:12px 0">
+        宸叉牳瀵瑰綋鏃ユ娴嬫暟鎹笌宸插鏍稿師濮嬭褰曚竴鑷�
+      </el-checkbox>
+      <el-form-item label="璇勪环浜哄憳绛惧瓧">
+        <el-input
+            :model-value="form.reviewUser ? `${form.reviewUser} ${form.reviewTime || ''}` : '鎻愪氦鍚庤褰曡瘎浠蜂汉鍙婃棩鏈�'"
+            disabled/>
+      </el-form-item>
+      <el-form-item label="绾犳鎺柦鍙婇闃叉帾鏂�">
+        <el-input v-model="form.correctiveAction" type="textarea" :rows="2" :disabled="!canEdit"/>
+      </el-form-item>
+      <el-row :gutter="12">
+        <el-col :span="12">
+          <el-form-item label="绾犳璐d换浜�">
+            <el-select v-model="form.correctiveResponsibleUserId" filterable clearable style="width:100%"
+                       :disabled="!canEdit">
+              <el-option v-for="person in people" :key="person.id" :value="person.id" :label="person.name"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="瀹屾垚鏃ユ湡">
+            <el-date-picker v-model="form.correctiveDueDate" type="date" value-format="YYYY-MM-DD" style="width:100%"
+                            :disabled="!canEdit"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-form-item label="楠岃瘉鎯呭喌">
+        <el-input v-model="form.verificationResult" type="textarea" :rows="2" :disabled="!canEdit"/>
+      </el-form-item>
+      <el-row :gutter="12">
+        <el-col :span="12">
+          <el-form-item label="楠岃瘉璐d换浜�">
+            <el-select v-model="form.verificationResponsibleUserId" filterable clearable style="width:100%"
+                       :disabled="!canEdit">
+              <el-option v-for="person in people" :key="person.id" :value="person.id" :label="person.name"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="楠岃瘉鏃ユ湡">
+            <el-date-picker v-model="form.verificationDate" type="date" value-format="YYYY-MM-DD" style="width:100%"
+                            :disabled="!canEdit"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-form-item v-if="form.ratifyOpinion" label="鎵瑰噯鎰忚">
+        <el-input :model-value="form.ratifyOpinion" disabled/>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="visible = false">鍏抽棴</el-button>
+      <el-button v-if="canEdit" type="primary" :loading="loading" @click="save">鎻愪氦璇勪环</el-button>
+      <template v-if="canApprove">
+        <el-button :loading="loading" @click="approve(false)">閫�鍥炶瘎浠�</el-button>
+        <el-button type="primary" :loading="loading" @click="approve(true)">鎵瑰噯褰掓。</el-button>
+      </template>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import {mapGetters} from '@/utils/store-compat';
+import {selectUserCondition} from '@/api/performance/class';
+import {
+  getInspectionEvaluation,
+  submitInspectionEvaluation,
+  approveInspectionEvaluation
+} from '@/api/cnas/process/ensureResults/qualityMonitor';
+
+export default {
+  name: 'InspectionEvaluationDialog',
+  emits: ['close'],
+  data() {
+    return {visible: false, loading: false, loaded: false, loadError: '', row: null, people: [], form: {resultRows: []}}
+  },
+  computed: {
+    ...mapGetters(['userId']),
+    canEdit() {
+      return this.loaded && this.form.evaluationEditable === true
+    },
+    inspectionDateDisplay() {
+      const dates = this.form.rawInspectionDates || []
+      if (!dates.length) return ''
+      return dates.length === 1 ? dates[0] : `${dates[0]} 锝� ${dates[dates.length - 1]}`
+    },
+    editNotice() {
+      if (!this.loaded) return '姝e湪璇诲彇璇勪环璁板綍鍜岀紪杈戞潈闄愨��'
+      return this.loadError || this.form.evaluationEditReason || '褰撳墠璐﹀彿鍙兘鏌ョ湅璇勪环璁板綍'
+    },
+    canApprove() {
+      return !!this.form.detailsEvaluateId && this.form.isFinish !== 1 && Number(this.userId) === Number(this.row?.planRatifyUserId)
+    },
+    supervisedPersonName() {
+      const id = this.form.inspectorUserId || this.row?.inspectorUserId
+      return this.people.find(person => Number(person.id) === Number(id))?.name || ''
+    },
+    supervisorOptions() {
+      const inspectorId = Number(this.form.inspectorUserId || this.row?.inspectorUserId)
+      const evaluatorId = Number(this.row?.evaluatorUserId)
+      return this.people.filter(person => Number(person.id) !== inspectorId && Number(person.id) !== evaluatorId)
+    }
+  },
+  methods: {
+    open(row) {
+      this.row = row
+      this.loaded = false
+      this.loadError = ''
+      this.form = {
+        qualityMonitorDetailsId: row.qualityMonitorDetailsId,
+        inspectorUserId: row.inspectorUserId,
+        resultRows: [],
+        measurementConfirmed: false
+      }
+      this.visible = true
+      selectUserCondition({type: 1}).then(res => {
+        this.people = res.data || []
+      })
+      getInspectionEvaluation(row.qualityMonitorDetailsId).then(res => {
+        if (res.data) this.form = {...res.data, resultRows: res.data.resultRows || [], measurementConfirmed: false}
+      }).catch(() => {
+        this.loadError = '璇勪环璁板綍璇诲彇澶辫触锛岃鍒锋柊鍚庨噸璇�'
+      }).finally(() => {
+        this.loaded = true
+      })
+    },
+    save() {
+      if (!this.form.supervisorUserId || !this.form.evaluationConclusion?.trim() || !this.form.resultRows?.length || this.form.resultRows.some(item => !item.measuredValue || !item.referenceValue?.trim()) || !this.form.measurementConfirmed) {
+        this.$message.warning('璇烽�夋嫨鐩戠潱鍛橈紝濉啓妫�娴嬫儏鍐靛強璇勪环銆佺洸鏍峰�硷紝骞舵牳瀵瑰師濮嬭褰曟娴嬫暟鎹�')
+        return
+      }
+      this.loading = true
+      submitInspectionEvaluation(this.form).then(() => {
+        this.$message.success('璇勪环宸叉彁浜�')
+        this.visible = false
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    approve(approved) {
+      this.$prompt('璇疯緭鍏ユ壒鍑嗘垨閫�鍥炴剰瑙�', approved ? '鎵瑰噯璇勪环' : '閫�鍥炶瘎浠�', {inputValue: this.form.ratifyOpinion || ''}).then(({value}) => {
+        this.loading = true
+        approveInspectionEvaluation({
+          qualityMonitorDetailsId: this.row.qualityMonitorDetailsId,
+          approved,
+          opinion: value
+        }).then(() => {
+          this.$message.success(approved ? '璇勪环宸插綊妗�' : '璇勪环宸查��鍥�')
+          this.visible = false
+        }).finally(() => {
+          this.loading = false
+        })
+      }).catch(() => {
+      })
+    }
+  }
+}
+</script>
diff --git a/src/views/CNAS/process/ensureResults/ensureResultsValidity/index.vue b/src/views/CNAS/process/ensureResults/ensureResultsValidity/index.vue
index 4589e7b..224c51e 100644
--- a/src/views/CNAS/process/ensureResults/ensureResultsValidity/index.vue
+++ b/src/views/CNAS/process/ensureResults/ensureResultsValidity/index.vue
@@ -1,38 +1,80 @@
 <template>
   <!--  7.7璐ㄩ噺鐩戞帶璁″垝-->
   <div class="app-container">
-    <div class="table-card">
-      <div style="display: flex;justify-content: space-between">
-        <div style="display: flex;">
-          <div style="margin-bottom: 18px;margin-right: 10px;display: flex;align-items: center;line-height: 32px;">
-            <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">璁″垝鍚嶇О</span>
-            <el-input size="small" placeholder="璇疯緭鍏�" clearable v-model="yearForm.monitorName"
-                      @keyup.enter="getYearPlanList"></el-input>
-          </div>
-          <div style="line-height: 30px;">
-            <el-button type="primary" size="small" @click="getYearPlanList">鏌ヨ</el-button>
-            <el-button size="small" @click="clearYear">閲嶇疆</el-button>
-          </div>
+    <section v-if="pageMode === 'list'" class="plan-list-card">
+      <div class="plan-list-toolbar">
+        <div class="plan-list-filters">
+          <span class="annual-plan-label">璁″垝骞村害</span>
+          <el-date-picker v-model="selectedYear" type="year" value-format="YYYY" format="YYYY骞�" :clearable="false"
+            size="small" style="width: 130px" @change="handleYearChange" />
+          <span class="filter-label">璁″垝鍚嶇О</span>
+          <el-input v-model="yearForm.monitorName" clearable placeholder="璇疯緭鍏�" size="small" style="width: 220px"
+            @keyup.enter="getYearPlanList" />
+          <el-button type="primary" size="small" @click="getYearPlanList">鏌ヨ</el-button>
+          <el-button size="small" @click="clearYear">閲嶇疆</el-button>
         </div>
-        <div style="line-height: 30px;">
-          <el-button size="small" type="primary" @click="leadInto">瀵煎叆</el-button>
+        <div class="plan-list-actions">
+          <el-button size="small" @click="leadInto">瀵煎叆</el-button>
+          <el-button type="primary" size="small" @click="openCreatePlan">鏂板缓骞村害璁″垝</el-button>
         </div>
       </div>
-      <lims-table :tableData="yearTableData" :column="yearColumnData" :page="yearPage" :tableLoading="yearLoading"
-                  :height="'calc(100vh - 34em)'" @pagination="pagination" :rowClick="rowClick" key="yearTableData"></lims-table>
+      <lims-table :tableData="yearTableData" :column="yearColumnData" :page="yearPage"
+        :tableLoading="yearLoading" :height="'calc(100vh - 11em)'" @pagination="pagination" key="yearTableData">
+        <template #planActions="{ row }">
+          <div class="detail-actions">
+            <el-button link type="primary" size="small" @click="openPlan(row)">鏌ョ湅璁″垝</el-button>
+            <el-button v-if="canReviewRow(row)" link type="primary" size="small" @click="examinePlan(row)">瀹℃牳</el-button>
+            <el-button v-if="canApproveRow(row)" link type="primary" size="small" @click="approvalPlan(row)">鎵瑰噯</el-button>
+            <el-dropdown trigger="click" @command="command => handlePlanCommand(command, row)">
+              <el-button link type="primary" size="small">鏇村鈱�</el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item command="export" :disabled="row.ratifyStatus !== 1">瀵煎嚭骞村害璁″垝</el-dropdown-item>
+                  <el-dropdown-item command="delete" divided :disabled="!canDeleteRow(row)">鍒犻櫎</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </div>
+        </template>
+      </lims-table>
+    </section>
+
+    <template v-else>
+    <div class="annual-plan-header">
+      <div class="annual-plan-main">
+        <el-button link type="primary" size="small" @click="backToPlanList">鈫� 杩斿洖骞村害璁″垝</el-button>
+        <span class="annual-plan-label">{{ selectedYear }}骞村害</span>
+        <template v-if="currentPlan">
+          <strong>{{ currentPlan.monitorName }}</strong>
+          <el-tag :type="planStatusType">{{ planStatusText }}</el-tag>
+          <span>缂栧埗锛歿{ currentPlan.writeName || '-' }} {{ formatDate(currentPlan.writeTime) }}</span>
+          <span>瀹℃牳锛歿{ currentPlan.examineName || '-' }}</span>
+          <span>鎵瑰噯锛歿{ currentPlan.ratifyName || '-' }}</span>
+        </template>
+        <el-tag v-else type="info">璇ュ勾搴﹀皻鏈缓绔嬭鍒�</el-tag>
+      </div>
+      <div class="annual-plan-actions">
+        <el-button v-if="canEditPlan" type="primary" size="small" @click="submitPlan">鎻愪氦瀹℃牳</el-button>
+        <el-button v-if="canReviewPlan" type="primary" size="small" @click="examinePlan(currentPlan)">瀹℃牳</el-button>
+        <el-button v-if="canApprovePlan" type="primary" size="small" @click="approvalPlan(currentPlan)">鎵瑰噯</el-button>
+        <el-button size="small" @click="leadInto">瀵煎叆</el-button>
+        <el-button v-if="currentPlan" size="small" :disabled="currentPlan.ratifyStatus !== 1" @click="handleDown(currentPlan)">瀵煎嚭骞村害璁″垝</el-button>
+      </div>
     </div>
-    <div style="margin-top: 20px">
-      <div style="display: flex;justify-content: space-between">
+    <div class="plan-detail-card">
+      <div class="plan-detail-toolbar">
         <div style="display: flex;">
+          <span class="section-title">璐ㄦ帶椤圭洰</span>
           <div style="margin-bottom: 18px;margin-right: 10px;display: flex;align-items: center;line-height: 32px;">
-            <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">鐩戞帶鐩殑</span>
-            <el-input v-model="yearDetailForm.monitorPurpose" placeholder="璇疯緭鍏�" size="small"
+            <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">璐ㄦ帶椤圭洰</span>
+            <el-input v-model="yearDetailForm.monitorProject" placeholder="璇疯緭鍏�" size="small"
                       @keyup.enter="getYearDetailPlanList"></el-input>
           </div>
           <div style="margin-bottom: 18px;margin-right: 10px;display: flex;align-items: center;line-height: 32px;">
-            <span style="width: 88px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">鐩戞帶椤圭洰</span>
-            <el-input v-model="yearDetailForm.monitorProject" placeholder="璇疯緭鍏�" size="small"
-                      @keyup.enter="getYearDetailPlanList"></el-input>
+            <span style="width: 70px;font-size: 14px;font-weight: 700;color: var(--lims-text-2);">璐熻矗浜�</span>
+            <el-select v-model="yearDetailForm.responsibleUserId" clearable filterable size="small" style="width: 160px">
+              <el-option v-for="item in responsibleOptions" :key="item.id" :label="item.name" :value="item.id" />
+            </el-select>
           </div>
           <div style="line-height: 30px;">
             <el-button type="primary" size="small" @click="getYearDetailPlanList">鏌ヨ</el-button>
@@ -40,14 +82,65 @@
           </div>
         </div>
         <div style="line-height: 30px;">
-          <el-button size="small" type="primary" @click="exportSignatureTemplate">瀵煎嚭绛惧悕妯℃澘</el-button>
-          <el-button size="small" type="primary" @click="showDialog('add')">鏂板</el-button>
+<!--          <el-button size="small" type="primary" @click="exportSignatureTemplate">瀵煎嚭绛惧悕妯℃澘</el-button>-->
+          <el-button size="small" type="primary" :disabled="!canEditPlan" @click="showDialog('add')">鏂板璐ㄦ帶椤圭洰</el-button>
         </div>
       </div>
       <lims-table :tableData="yearDetailTableData" :column="yearDetailColumnData" :page="yearDetailPage"
-        :tableLoading="yearDetailLoading" :height="'calc(100vh - 34em)'" @pagination="pagination1" ref="yearDetailTableData"
-        key="yearDetailTableData"></lims-table>
+        :tableLoading="yearDetailLoading" :height="'calc(100vh - 15em)'" @pagination="pagination1" ref="yearDetailTableData"
+        key="yearDetailTableData">
+        <template #detailActions="{ row }">
+          <div class="detail-actions">
+            <el-button link type="primary" size="small" :disabled="!canEditPlan || !!row.inspectionTaskId"
+              @click="showDialog('edit', row)">缂栬緫</el-button>
+
+            <el-button v-if="row.itemKind !== 'INSPECTION'" link type="primary" size="small"
+              @click="carryOut(row)">瀹炴柦</el-button>
+            <el-button v-else-if="!row.inspectionTaskId" link type="primary" size="small"
+              :disabled="row.planRatifyStatus !== 1" @click="dispatchTask(row)">涓嬪彂浠诲姟</el-button>
+            <el-button v-else link type="primary" size="small" @click="evaluate(row)">
+              {{ row.inspectionTaskState === 5 ? '璇勪环璁板綍' : '璇勪环瑕佹眰' }}
+            </el-button>
+
+            <el-dropdown trigger="click" @command="command => handleDetailCommand(command, row)">
+              <el-button link type="primary" size="small">鏇村鈱�</el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item v-if="row.itemKind !== 'INSPECTION'" command="complete">瀹屾垚鎶ュ憡</el-dropdown-item>
+                  <el-dropdown-item v-if="row.itemKind !== 'INSPECTION'" command="evaluate">璇勪环</el-dropdown-item>
+                  <el-dropdown-item v-if="row.itemKind === 'INSPECTION' && !row.inspectionTaskId" command="requirement">璇勪环瑕佹眰</el-dropdown-item>
+                  <el-dropdown-item v-if="row.itemKind === 'INSPECTION' && row.inspectionTaskId" command="cancel"
+                    :disabled="row.inspectionTaskOrderState !== 1 || row.inspectionTaskState !== 0">鍙栨秷浠诲姟</el-dropdown-item>
+                  <el-dropdown-item command="attachment">闄勪欢涓婁紶</el-dropdown-item>
+                  <el-dropdown-item command="export">瀵煎嚭</el-dropdown-item>
+                  <el-dropdown-item command="delete" divided
+                    :disabled="!canEditPlan || !!row.inspectionTaskId">鍒犻櫎</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </div>
+        </template>
+      </lims-table>
     </div>
+    </template>
+    <el-dialog v-model="createPlanDialog" title="鏂板缓骞村害璐ㄩ噺鎺у埗璁″垝" width="520px">
+      <el-form :model="planForm" label-width="90px">
+        <el-form-item label="骞村害">
+          <el-date-picker v-model="planForm.monitorYear" type="year" value-format="YYYY" format="YYYY骞�"
+            :clearable="false" style="width: 100%" placeholder="璇烽�夋嫨骞村害" />
+        </el-form-item>
+        <el-form-item label="璁″垝鍚嶇О"><el-input v-model="planForm.monitorName" /></el-form-item>
+        <el-form-item label="瀹℃牳浜�">
+          <el-select v-model="planForm.examineUserId" filterable style="width:100%">
+            <el-option v-for="item in responsibleOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="createPlanDialog=false">鍙栨秷</el-button>
+        <el-button type="primary" :loading="createPlanLoading" @click="createPlan">纭畾</el-button>
+      </template>
+    </el-dialog>
     <!--鏂板淇敼寮规-->
     <detail-form-dialog v-if="formDia" ref="formDia" :qualityMonitorId="qualityMonitorId"
       @closeDia="closeDia"></detail-form-dialog>
@@ -56,6 +149,8 @@
       @closeCarryOutDia="closeCarryOutDia"></carry-out-dialog>
     <!--璇勪环娴佺▼寮规-->
     <evaluate-dialog v-if="evaluateDialog" ref="evaluateDialog" @closeEvaDia="closeEvaDia"></evaluate-dialog>
+    <inspection-evaluation-dialog v-if="inspectionEvaluateDialog" ref="inspectionEvaluateDialog"
+      @close="closeInspectionEvaluateDialog" />
     <el-dialog v-model="examineDialog" title="瀹℃牳" width="30%" @close="closeExamineDia">
       <span>
         瀹℃牳澶囨敞锛�
@@ -170,17 +265,20 @@
 
 <script>
 import DetailFormDialog from './components/detailFormDialog.vue';
+import InspectionEvaluationDialog from './components/inspectionEvaluationDialog.vue';
 import CarryOutDialog from './components/carryOutDialog.vue';
 import EvaluateDialog from './components/evaluateDialog.vue';
 import filePreview from "@/components/Preview/filePreview.vue";
 import limsTable from "@/components/Table/lims-table.vue";
 import {
+  createAnnualPlan, getAnnualPlan, submitAnnualPlan,
   delQualityMonitorDetail,
   delQualitySupervise,
   examineQualityMonitor, exportQualityMonitorDetail, exportQualityMonitorEvaluate, exportQualityMonitorRatify,
   pageQualityMonitor, pageQualityMonitorDetail, ratifyFinishReport,
   ratifyQualityMonitor
 } from "@/api/cnas/process/ensureResults/qualityMonitor";
+import { dispatchInspectionTask, cancelInspectionTask } from "@/api/cnas/process/ensureResults/qualityMonitor";
 import { selectUserCondition } from "@/api/performance/class";
 import { mapGetters } from "@/utils/store-compat";
 import ViewRecord from "./components/ViewRecord.vue";
@@ -188,11 +286,19 @@
 export default {
   name: 'EnsureResultsValidity',
   // import 寮曞叆鐨勭粍浠堕渶瑕佹敞鍏ュ埌瀵硅薄涓墠鑳戒娇鐢�
-  components: {ViewRecord, limsTable, filePreview, EvaluateDialog, CarryOutDialog, DetailFormDialog },
+  components: {ViewRecord, limsTable, filePreview, EvaluateDialog, CarryOutDialog, DetailFormDialog, InspectionEvaluationDialog },
   data() {
     // 杩欓噷瀛樻斁鏁版嵁
     return {
       ViewRecord: false,
+      inspectionEvaluateDialog: false,
+      pageMode: 'list',
+      selectedYear: String(new Date().getFullYear()),
+      currentPlan: null,
+      selectedPlanId: null,
+      createPlanDialog: false,
+      createPlanLoading: false,
+      planForm: { monitorYear: String(new Date().getFullYear()), monitorName: '', examineUserId: null },
       examineUserId: '',
       responsibleOptions: [],
       yearForm: {
@@ -207,247 +313,92 @@
         {
           label: '璁″垝鍚嶇О',
           prop: 'monitorName',
-          width: '150px'
+          minWidth: '220px'
+        }, {
+          label: '璁″垝骞村害',
+          prop: 'monitorYear',
+          width: '100px'
+        }, {
+          dataType: 'tag',
+          label: '璁″垝鐘舵��',
+          prop: 'workflowStatus',
+          width: '110px',
+          formatData: (value, row) => this.planWorkflowText(value),
+          formatType: value => this.planWorkflowType(value)
         }, {
           label: '缂栧埗浜�',
           prop: 'writeName',
-          width: '100'
+          minWidth: '100px'
         }, {
           label: '缂栧埗鏃ユ湡',
           prop: 'writeTime',
-          width: '160'
-        }, {
-          dataType: 'tag',
-          label: '瀹℃牳鐘舵��',
-          prop: 'examineStatus',
-          width: '100',
-          formatData: (params) => {
-            if (params === 0) {
-              return '涓嶉�氳繃';
-            } else if (params === 1) {
-              return '閫氳繃';
-            } else {
-              return null;
-            }
-          },
-          formatType: (params) => {
-            if (params === 0) {
-              return 'danger';
-            } else if (params === 1) {
-              return 'success';
-            } else {
-              return null;
-            }
-          }
-        }, {
-          label: '瀹℃牳鍐呭',
-          prop: 'examineRemark',
-          width: '100'
+          minWidth: '150px',
+          formatData: value => this.formatDate(value)
         }, {
           label: '瀹℃牳浜�',
           prop: 'examineName',
-          width: '100'
+          minWidth: '100px'
         }, {
           label: '瀹℃牳鏃ユ湡',
           prop: 'examineTime',
-          width: '160'
-        }, {
-          dataType: 'tag',
-          label: '鎵瑰噯鐘舵��',
-          prop: 'ratifyStatus',
-          width: '100',
-          formatData: (params) => {
-            if (params === 0) {
-              return '涓嶆壒鍑�';
-            } else if (params === 1) {
-              return '鎵瑰噯';
-            } else {
-              return null;
-            }
-          },
-          formatType: (params) => {
-            if (params === 0) {
-              return 'danger';
-            } else if (params === 1) {
-              return 'success';
-            } else {
-              return null;
-            }
-          }
-        }, {
-          label: '鎵瑰噯鍐呭',
-          prop: 'ratifyRemark',
-          width: '100'
+          minWidth: '150px',
+          formatData: value => this.formatDate(value)
         }, {
           label: '鎵瑰噯浜�',
           prop: 'ratifyName',
-          width: '100'
+          minWidth: '100px'
         }, {
           label: '鎵瑰噯鏃ユ湡',
           prop: 'ratifyTime',
-          width: '160'
+          minWidth: '150px',
+          formatData: value => this.formatDate(value)
         }, {
-          dataType: 'action',
+          dataType: 'slot',
+          slot: 'planActions',
           label: '鎿嶄綔',
           fixed: 'right',
-          operation: [
-            {
-              name: '瀹℃牳',
-              type: 'text',
-              disabled: (row) => {
-                if (row.examineStatus === 1 || this.userId != row.examineUserId) {
-                  return true
-                } else {
-                  return false
-                }
-              },
-              clickFun: (row) => {
-                this.examinePlan(row)
-              }
-            },
-            {
-              name: '鎵瑰噯',
-              type: 'text',
-              disabled: (row) => {
-                if (row.ratifyStatus === 1 || row.examineStatus !== 1 || this.userId != row.ratifyUserId) {
-                  return true
-                } else {
-                  return false
-                }
-              },
-              clickFun: (row) => {
-                this.approvalPlan(row)
-              }
-            },
-            {
-              name: '瀵煎嚭',
-              type: 'text',
-              clickFun: (row) => {
-                this.handleDown(row)
-              },
-              disabled: (row) => {
-                if (row.ratifyStatus !== 1) {
-                  return true
-                } else {
-                  return false
-                }
-              },
-            },
-            {
-              name: '鍒犻櫎',
-              type: 'text',
-              color: 'var(--el-color-danger)',
-              clickFun: (row) => {
-                this.delPlan(row)
-              },
-              disabled: (row) => {
-                if (row.examineStatus === 1) {
-                  return true;
-                } else {
-                  return false;
-                }
-              },
-            }
-          ]
+          width: '220px'
         }],
       yearTableData: [],  // 骞磋〃
       yearLoading: false,
       yearDetailForm: {
-        monitorPurpose: '',
         monitorProject: '',
+        responsibleUserId: null,
       },
       yearDetailColumnData: [
         {
-          label: '鐩戞帶鐩殑',
-          prop: 'monitorPurpose',
-          width: '150px',
-          showOverflowTooltip: true,
-        }, {
-          label: '璁″垝寮�灞曟椂闂�',
-          prop: 'plannedTime',
-          width: '150px'
-        }, {
-          label: '鐩戞帶椤圭洰',
+          label: '璐ㄦ帶椤圭洰',
           prop: 'monitorProject',
-          width: '150px'
+          minWidth: '140px'
         }, {
-          label: '鍙傚姞鍗曚綅锛堜汉鍛橈級',
-          prop: 'participant',
-          width: '190px'
+          label: '鎶�鏈爣鍑�',
+          prop: 'technicalStandardText',
+          minWidth: '220px'
         }, {
-          label: '棰勭畻锛堝厓锛�',
-          prop: 'budget',
-          width: '150px'
-        }, {
-          label: '缁勭粐浜哄憳',
-          prop: 'organization',
-          width: '150px'
-        }, {
-          label: '鐩戞帶鏂瑰紡',
+          label: '璐ㄦ帶鏂瑰紡',
           prop: 'monitorWay',
-          width: '150px'
-        },{
-          dataType: 'tag',
-          label: '瀹炴柦鐘舵��',
-          prop: 'detailsRatifyStatus',
-          width: '100',
-          formatData: (params) => {
-            if (params === 0) {
-              return '鏈紑濮�';
-            } else if (params === 1) {
-              return '寰呰瘎浠�';
-            }  else if (params === 2) {
-              return '涓嶆壒鍑�';
-            } else if (params === 3) {
-              return '宸叉壒鍑�';
-            } else {
-              return null;
-            }
-          },
-          formatType: (params) => {
-            if (params === 0) {
-              return '';
-            } else if (params === 1) {
-              return 'warning';
-            } else if (params === 2) {
-              return 'danger';
-            }  else if (params === 3) {
-              return 'success';
-            } else {
-              return null;
-            }
-          }
-        },{
-          dataType: 'tag',
-          label: '鎶ュ憡鐘舵��',
-          prop: 'reportStatus',
-          width: '100',
-          formatData: (params) => {
-            if (params === 0) {
-              return '鏈紑濮�';
-            } else if (params === 1) {
-              return '寰呰瘎浠�';
-            }  else if (params === 2) {
-              return '涓嶆壒鍑�';
-            } else if (params === 3) {
-              return '宸叉壒鍑�';
-            } else {
-              return null;
-            }
-          },
-          formatType: (params) => {
-            if (params === 0) {
-              return '';
-            } else if (params === 1) {
-              return 'warning';
-            } else if (params === 2) {
-              return 'danger';
-            }  else if (params === 3) {
-              return 'success';
-            } else {
-              return null;
-            }
-          }
+          minWidth: '120px'
+        }, {
+          label: '瀹屾垚鏃堕檺',
+          prop: 'completionMonth',
+          width: '110px',
+          formatData: value => value ? value.replace('-', '.') : ''
+        }, {
+          label: '璐熻矗浜�',
+          prop: 'responsibleName',
+          minWidth: '100px'
+        }, {
+          label: '璇勪环浜�',
+          prop: 'evaluatorName',
+          minWidth: '100px'
+        }, {
+          label: '浜嬮」绫诲埆', prop: 'itemKind', width: '100px',
+          formatData: value => value === 'INSPECTION' ? '妫�娴嬬被' : '闈炴娴嬬被'
+        }, {
+          label: '鐩戞帶浠诲姟缂栧彿', prop: 'inspectionTaskCode', minWidth: '150px'
+        }, {
+          label: '浠诲姟鐘舵��', prop: 'inspectionTaskState', width: '100px',
+          formatData: value => this.inspectionTaskStatus(value)
         },{
           dataType: 'tag',
           label: '璇勪环鐘舵��',
@@ -480,61 +431,11 @@
             }
           }
         }, {
-          dataType: 'action',
+          dataType: 'slot',
+          slot: 'detailActions',
           label: '鎿嶄綔',
           fixed: 'right',
-          operation: [
-            {
-              name: '缂栬緫',
-              type: 'text',
-              clickFun: (row) => {
-                this.showDialog('edit', row)
-              }
-            },
-            {
-              name: '瀹炴柦',
-              type: 'text',
-              clickFun: (row) => {
-                this.carryOut(row)
-              }
-            },
-            {
-              name: '瀹屾垚鎶ュ憡',
-              type: 'text',
-              clickFun: (row) => {
-                this.record(row)
-              },
-            },
-            {
-              name: '璇勪环',
-              type: 'text',
-              clickFun: (row) => {
-                this.evaluate(row)
-              }
-            },
-            {
-              name: '闄勪欢涓婁紶',
-              type: 'text',
-              clickFun: (row) => {
-                this.viewTestRecord(row)
-              }
-            },
-            {
-              name: '瀵煎嚭',
-              type: 'text',
-              clickFun: (row) => {
-                this.downLoadPost(row);
-              },
-            },
-            {
-              name: '鍒犻櫎',
-              type: 'text',
-              color: 'var(--el-color-danger)',
-              clickFun: (row) => {
-                this.delYearPlanDetail(row)
-              }
-            }
-          ]
+          width: '220px'
         }
       ],
       yearDetailTableData: [], // 骞存槑缁嗚〃
@@ -574,31 +475,91 @@
     };
   },
   mounted() {
+    this.getUserList()
     this.getYearPlanList()
   },
   // 鏂规硶闆嗗悎
   methods: {
     // 鏌ヨ骞村害璁″垝琛�
     getYearPlanList() {
-      const entity = {
-        monitorName: this.yearForm.monitorName,
-      }
-      const page = this.yearPage
       this.yearLoading = true
-      pageQualityMonitor({ ...entity, ...page }).then(res => {
-        this.yearLoading = false
-        this.yearTableData = res.data.records
-        this.yearPage.total = res.data.total
-        if (this.yearTableData.length > 0) {
-          this.rowClick(this.yearTableData[0])
-        }
-      }).catch(err => {
-        console.log('err---', err);
+      const entity = {
+        monitorYear: this.selectedYear,
+        monitorName: this.yearForm.monitorName
+      }
+      return pageQualityMonitor({ ...entity, ...this.yearPage }).then(res => {
+        this.yearTableData = res.data?.records || []
+        this.yearPage.total = res.data?.total || 0
+      }).finally(() => {
         this.yearLoading = false
       })
     },
+    loadAnnualPlan() {
+      if (!this.selectedPlanId) return Promise.resolve()
+      return this.loadPlanVersion()
+    },
+    loadPlanVersion() {
+      if (!this.selectedPlanId) return Promise.resolve()
+      return getAnnualPlan({ monitorYear: this.selectedYear, qualityMonitorId: this.selectedPlanId }).then(res => {
+        this.currentPlan = res.data || null
+        this.qualityMonitorId = this.currentPlan?.qualityMonitorId || ''
+        this.yearDetailPage.current = 1
+        return this.getYearDetailPlanList()
+      })
+    },
+    openPlan(row, addFirstDetail = false) {
+      this.selectedYear = String(row.monitorYear || this.selectedYear)
+      this.selectedPlanId = row.qualityMonitorId
+      this.pageMode = 'detail'
+      return this.loadPlanVersion().then(() => {
+        if (addFirstDetail) this.showDialog('add')
+      })
+    },
+    backToPlanList() {
+      this.pageMode = 'list'
+      this.currentPlan = null
+      this.qualityMonitorId = ''
+      this.yearDetailForm = { monitorProject: '', responsibleUserId: null }
+      this.getYearPlanList()
+    },
+    handleYearChange() {
+      this.yearPage.current = 1
+      this.getYearPlanList()
+    },
+    openCreatePlan() {
+      this.planForm = {
+        monitorYear: String(new Date().getFullYear()),
+        monitorName: '',
+        examineUserId: null
+      }
+      this.createPlanDialog = true
+    },
+    createPlan() {
+      if (!this.planForm.monitorYear || !this.planForm.monitorName || !this.planForm.examineUserId) {
+        this.$message.warning('璇烽�夋嫨骞村害銆佸~鍐欒鍒掑悕绉板苟閫夋嫨瀹℃牳浜�')
+        return
+      }
+      this.createPlanLoading = true
+      createAnnualPlan({ ...this.planForm }).then(res => {
+        this.createPlanDialog = false
+        this.selectedYear = String(res.data.monitorYear)
+        this.yearPage.current = 1
+        this.$message.success('骞村害璁″垝宸插垱寤猴紝璇峰~鍐欑涓�涓川鎺ч」鐩�')
+        return this.getYearPlanList().then(() => this.openPlan(res.data, true))
+      }).finally(() => { this.createPlanLoading = false })
+    },
+    submitPlan() {
+      this.$confirm('鎻愪氦鍚庤川鎺ч」鐩皢閿佸畾锛岀‘璁ゆ彁浜ゅ鏍革紵', '鎻愪氦瀹℃牳').then(() => {
+        submitAnnualPlan(this.qualityMonitorId).then(() => {
+          this.$message.success('宸叉彁浜ゅ鏍�')
+          this.loadPlanVersion()
+          this.getYearPlanList()
+        })
+      }).catch(() => {})
+    },
     clearYear() {
       this.yearForm.monitorName = ''
+      this.yearPage.current = 1
       this.getYearPlanList()
     },
     pagination({ page, limit }) {
@@ -609,10 +570,11 @@
     pagination1({ page, limit }) {
       this.yearDetailPage.current = page;
       this.yearDetailPage.size = limit;
-      this.getYearPlanList();
+      this.getYearDetailPlanList();
     },
     leadInto() {
       this.uploadDia = true
+      this.planYear = this.selectedYear
       this.getUserList()
     },
     // 瀵煎叆娴佺▼
@@ -636,7 +598,7 @@
         this.timer = setTimeout(() => {
           this.uploadDia = false
           this.fileList = []
-          this.planYear = ''
+          this.planYear = this.selectedYear
           this.examineUserId = ''
           this.getYearPlanList()
         }, 1000)
@@ -650,7 +612,7 @@
         return;
       }
       if (!this.examineUserId) {
-        this.$message.warning('璇烽�夋嫨鎵瑰噯浜�')
+        this.$message.warning('璇烽�夋嫨瀹℃牳浜�')
         return;
       }
       this.$refs.upload.submit();
@@ -678,7 +640,7 @@
     closeExamineDia() {
       this.examineDialog = false
       this.examineInfo.examineRemark = ''
-      this.getYearPlanList()
+      this.refreshPlanData()
     },
     // 鎵瑰噯
     approvalPlan(row) {
@@ -701,7 +663,14 @@
     closeRatifyDia() {
       this.ratifyDialog = false
       this.ratifyInfo.ratifyRemark = ''
-      this.getYearPlanList()
+      this.refreshPlanData()
+    },
+    refreshPlanData() {
+      return this.getYearPlanList().then(() => {
+        if (this.pageMode === 'detail' && this.selectedPlanId) {
+          return this.loadPlanVersion()
+        }
+      })
     },
     // 鎵瑰噯鎶ュ憡
     handleApproval(status) {
@@ -762,8 +731,8 @@
     getYearDetailPlanList() {
       const entity = {
         qualityMonitorId: this.qualityMonitorId,
-        monitorPurpose: this.yearDetailForm.monitorPurpose,
         monitorProject: this.yearDetailForm.monitorProject,
+        responsibleUserId: this.yearDetailForm.responsibleUserId,
       }
       const page = this.yearDetailPage
       this.yearDetailLoading = true
@@ -771,7 +740,7 @@
         this.yearDetailLoading = false
         this.yearDetailTableData = res.data.records
         this.yearDetailPage.total = res.data.total
-        this.$refs.yearDetailTableData.$refs.multipleTable.doLayout()
+        this.$nextTick(() => this.$refs.yearDetailTableData?.$refs?.multipleTable?.doLayout())
       }).catch(err => {
         console.log('err---', err);
         this.yearDetailLoading = false
@@ -780,8 +749,8 @@
     // 閲嶇疆鏄庣粏琛�
     clearDetail() {
       this.yearDetailForm = {
-        monitorPurpose: '',
-        monitorProject: ''
+        monitorProject: '',
+        responsibleUserId: null
       }
       this.getYearDetailPlanList()
     },
@@ -862,6 +831,11 @@
     // end
     // 鎵撳紑璇勪环寮规
     evaluate(row) {
+      if (row.itemKind === 'INSPECTION') {
+        this.inspectionEvaluateDialog = true
+        this.$nextTick(() => this.$refs.inspectionEvaluateDialog.open(row))
+        return
+      }
       this.evaluateDialog = true
       this.$nextTick(() => {
         this.$refs.evaluateDialog.openDia(row)
@@ -870,6 +844,26 @@
     closeEvaDia() {
       this.evaluateDialog = false
       this.getYearDetailPlanList()
+    },
+    closeInspectionEvaluateDialog() {
+      this.inspectionEvaluateDialog = false
+      this.getYearDetailPlanList()
+    },
+    dispatchTask(row) {
+      this.$confirm(`纭涓嬪彂鈥�${row.monitorProject}鈥濈殑璐ㄩ噺鐩戞帶妫�楠屼换鍔★紵`, '涓嬪彂浠诲姟').then(() => {
+        dispatchInspectionTask(row.qualityMonitorDetailsId).then(res => {
+          this.$message.success(`宸蹭笅鍙戠洃鎺т换鍔★細${res.data.entrustCode}`)
+          this.getYearDetailPlanList()
+        })
+      }).catch(() => {})
+    },
+    cancelTask(row) {
+      this.$confirm(`纭鍙栨秷鐩戞帶浠诲姟鈥�${row.inspectionTaskCode}鈥濓紵宸插彇娑堜换鍔′細淇濈暀鍘嗗彶璁板綍銆俙, '鍙栨秷浠诲姟').then(() => {
+        cancelInspectionTask(row.qualityMonitorDetailsId).then(() => {
+          this.$message.success('浠诲姟宸插彇娑�')
+          this.getYearDetailPlanList()
+        })
+      }).catch(() => {})
     },
     // 闄勪欢涓婁紶
     viewTestRecord (row) {
@@ -885,6 +879,14 @@
     },
     // 鎵撳紑骞村害鏄庣粏鏂板銆佷慨鏀瑰脊妗�
     showDialog(type, row) {
+      if (!this.qualityMonitorId) {
+        this.$message.warning('璇峰厛鏂板缓骞村害璐ㄩ噺鎺у埗璁″垝')
+        return
+      }
+      if (!this.canEditPlan) {
+        this.$message.warning('褰撳墠璁″垝鐘舵�佷笉鑳界淮鎶よ川鎺ч」鐩�')
+        return
+      }
       this.formDia = true
       this.$nextTick(() => {
         this.$refs.formDia.openDia(type, row)
@@ -949,11 +951,93 @@
       link.target = '_blank';
       document.body.appendChild(link);
       link.click();
+    },
+    formatDate(value) {
+      return value ? String(value).slice(0, 10) : '-'
+    },
+    planWorkflowText(value) {
+      return ({
+        DRAFT: '鑽夌',
+        PENDING_REVIEW: '寰呭鏍�',
+        REVIEW_REJECTED: '瀹℃牳閫�鍥�',
+        PENDING_APPROVAL: '寰呮壒鍑�',
+        APPROVAL_REJECTED: '鎵瑰噯閫�鍥�',
+        APPROVED: '宸叉壒鍑�'
+      })[value] || '鍘嗗彶璁″垝'
+    },
+    planWorkflowType(value) {
+      return ({
+        DRAFT: 'info',
+        PENDING_REVIEW: 'warning',
+        REVIEW_REJECTED: 'danger',
+        PENDING_APPROVAL: 'warning',
+        APPROVAL_REJECTED: 'danger',
+        APPROVED: 'success'
+      })[value] || 'info'
+    },
+    canReviewRow(row) {
+      return row.workflowStatus === 'PENDING_REVIEW'
+        && Number(this.userId) === Number(row.examineUserId)
+    },
+    canApproveRow(row) {
+      return row.workflowStatus === 'PENDING_APPROVAL'
+        && Number(this.userId) === Number(row.ratifyUserId)
+    },
+    canDeleteRow(row) {
+      return Number(this.userId) === Number(row.writeUserId)
+        && ['DRAFT', 'REVIEW_REJECTED', 'APPROVAL_REJECTED'].includes(row.workflowStatus || 'DRAFT')
+    },
+    handlePlanCommand(command, row) {
+      if (command === 'export') this.handleDown(row)
+      if (command === 'delete') this.delPlan(row)
+    },
+    inspectionTaskStatus(value) {
+      const map = { 0: '寰呮娴�', 1: '妫�娴嬩腑', 2: '妫�娴嬩腑', 3: '寰呭鏍�', 4: '澶嶆牳閫�鍥�', 5: '寰呰瘎浠�' }
+      return value === null || value === undefined ? '鏈笅鍙�' : (map[value] || '澶勭悊涓�')
+    },
+    handleDetailCommand(command, row) {
+      const handlers = {
+        complete: () => this.record(row),
+        evaluate: () => this.evaluate(row),
+        requirement: () => this.evaluate(row),
+        cancel: () => this.cancelTask(row),
+        attachment: () => this.viewTestRecord(row),
+        export: () => this.downLoadPost(row),
+        delete: () => this.delYearPlanDetail(row)
+      }
+      handlers[command]?.()
     }
   },
   // 鐢ㄤ簬涓婁紶鏂囦欢鐨勪俊鎭�
   computed: {
     ...mapGetters(["userId", "nickName"]),
+    effectivePlanStatus() {
+      if (!this.currentPlan) return ''
+      if (this.currentPlan.workflowStatus) return this.currentPlan.workflowStatus
+      if (this.currentPlan.ratifyStatus === 1) return 'APPROVED'
+      if (this.currentPlan.ratifyStatus === 0) return 'APPROVAL_REJECTED'
+      if (this.currentPlan.examineStatus === 1) return 'PENDING_APPROVAL'
+      if (this.currentPlan.examineStatus === 0) return 'REVIEW_REJECTED'
+      return 'DRAFT'
+    },
+    planStatusText() {
+      return ({DRAFT:'鑽夌',PENDING_REVIEW:'寰呭鏍�',REVIEW_REJECTED:'瀹℃牳閫�鍥�',PENDING_APPROVAL:'寰呮壒鍑�',APPROVAL_REJECTED:'鎵瑰噯閫�鍥�',APPROVED:'宸叉壒鍑�'})[this.effectivePlanStatus] || '鍘嗗彶璁″垝'
+    },
+    planStatusType() {
+      return ({DRAFT:'info',PENDING_REVIEW:'warning',REVIEW_REJECTED:'danger',PENDING_APPROVAL:'warning',APPROVAL_REJECTED:'danger',APPROVED:'success'})[this.effectivePlanStatus] || 'info'
+    },
+    canEditPlan() {
+      return !!this.currentPlan && Number(this.userId) === Number(this.currentPlan.writeUserId)
+        && ['DRAFT','REVIEW_REJECTED','APPROVAL_REJECTED'].includes(this.effectivePlanStatus)
+    },
+    canReviewPlan() {
+      return !!this.currentPlan && this.effectivePlanStatus === 'PENDING_REVIEW'
+        && Number(this.userId) === Number(this.currentPlan.examineUserId)
+    },
+    canApprovePlan() {
+      return !!this.currentPlan && this.effectivePlanStatus === 'PENDING_APPROVAL'
+        && Number(this.userId) === Number(this.currentPlan.ratifyUserId)
+    },
     action() {
       return this.javaApi + '/qualityMonitor/importQualityMonitor'
     },
@@ -967,4 +1051,84 @@
 };
 </script>
 
-<style scoped></style>
+<style scoped>
+.plan-list-card {
+  min-width: 0;
+  padding: 14px 16px;
+  background: var(--lims-card-bg, var(--el-bg-color));
+  border: 1px solid var(--el-border-color-light);
+  border-radius: 4px;
+}
+.plan-list-toolbar,
+.plan-list-filters,
+.plan-list-actions {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+.plan-list-toolbar {
+  justify-content: space-between;
+  margin-bottom: 14px;
+}
+.filter-label {
+  margin-left: 8px;
+  font-size: 14px;
+  font-weight: 700;
+  color: var(--lims-text-2);
+  white-space: nowrap;
+}
+.annual-plan-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  padding: 14px 16px;
+  margin-bottom: 16px;
+  background: var(--lims-card-bg, var(--el-bg-color));
+  border: 1px solid var(--el-border-color-light);
+  border-radius: 4px;
+}
+.annual-plan-main, .annual-plan-actions {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+.annual-plan-label { font-weight: 700; color: var(--lims-text-2); }
+.plan-detail-card {
+  min-width: 0;
+  background: var(--lims-card-bg, var(--el-bg-color));
+}
+.plan-detail-toolbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+.section-title {
+  display: inline-flex;
+  align-items: center;
+  margin-right: 18px;
+  font-size: 15px;
+  font-weight: 700;
+  color: var(--lims-text-1, var(--el-text-color-primary));
+  white-space: nowrap;
+}
+.detail-actions {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  white-space: nowrap;
+}
+@media (max-width: 1200px) {
+  .annual-plan-header {
+    align-items: flex-start;
+    flex-direction: column;
+  }
+  .annual-plan-actions {
+    width: 100%;
+  }
+}
+</style>
diff --git a/src/views/CNAS/process/method/methodVerification/component/calibrationsFileDia.vue b/src/views/CNAS/process/method/methodVerification/component/calibrationsFileDia.vue
index cccee81..04ca9c0 100644
--- a/src/views/CNAS/process/method/methodVerification/component/calibrationsFileDia.vue
+++ b/src/views/CNAS/process/method/methodVerification/component/calibrationsFileDia.vue
@@ -2,6 +2,8 @@
   <div>
     <el-dialog v-model="filesDialogVisible" title="鏌ョ湅鏍″噯璇佷功" width="80%" @closed="closeFilesLook">
       <div>
+        <el-alert v-if="!tableLoading && tableData.length === 0" type="info" :closable="false"
+                  title="鎵�閫夎澶囨殏鏃犳牎鍑嗚瘉涔�"/>
         <limsTable
           ref="yearTable"
           :column="columnData"
@@ -18,7 +20,7 @@
       fullscreen
       title="鏌ョ湅闄勪欢" top="5vh" width="800px">
       <filePreview v-if="lookDialogVisible" :currentFile="{}"
-                   :fileUrl="javaApi+'/img/'+currentInfo.fileUrl" style="max-height: 90vh;overflow-y: auto;"/>
+                   :fileUrl="fileUrl(currentInfo)" style="max-height: 90vh;overflow-y: auto;"/>
     </el-dialog>
   </div>
 </template>
@@ -27,6 +29,7 @@
 import filePreview from '@/components/Preview/filePreview.vue';
 import limsTable from '@/components/Table/lims-table.vue'
 import file from '@/utils/file';
+import { getCurrentCalibrationFiles } from '@/api/cnas/process/method/methodVerification'
 
 export default {
   name: 'calibrationsFileDia',
@@ -88,10 +91,24 @@
   },
   // 鏂规硶闆嗗悎
   methods: {
-    openDia(row) {
+    fileUrl(row) {
+      const image = /\.(png|jpe?g|gif|bmp|webp)$/i.test(row.fileUrl || '')
+      return this.javaApi + (image ? '/img/' : '/word/') + row.fileUrl
+    },
+    openDia(row = {}) {
       this.filesDialogVisible = true
       this.info = row
-      this.tableData = this.info.calibrationsFileList
+      this.tableData = row.calibrationsFileList || []
+      if (row.useCurrentDevices) {
+        const deviceIds = [...new Set((row.machineAttachmentList || []).map(device => device.deviceId).filter(Boolean))]
+        if (!deviceIds.length) return
+        this.tableLoading = true
+        getCurrentCalibrationFiles({deviceIds: deviceIds.join(',')}).then(res => {
+          if (res.code === 200) this.tableData = res.data || []
+        }).finally(() => {
+          this.tableLoading = false
+        })
+      }
     },
     closeFilesLook () {
       this.filesDialogVisible = false
@@ -104,7 +121,7 @@
     // 涓嬭浇
     upload (row) {
       let url = '';
-      if(row.type==1){
+      if (/\.(png|jpe?g|gif|bmp|webp)$/i.test(row.fileUrl || '')) {
         url = this.javaApi+'/img/'+row.fileUrl
         file.downloadIamge(url,row.fileName)
       }else{
diff --git a/src/views/CNAS/process/method/methodVerification/component/wordFormDIa.vue b/src/views/CNAS/process/method/methodVerification/component/wordFormDIa.vue
index 21df974..9fa3579 100644
--- a/src/views/CNAS/process/method/methodVerification/component/wordFormDIa.vue
+++ b/src/views/CNAS/process/method/methodVerification/component/wordFormDIa.vue
@@ -57,11 +57,11 @@
                 <el-select v-model="form.confirmUser" multiple :multiple-limit="5" placeholder="璇烽�夋嫨妫�娴嬩汉鍛�" :disabled="isView">
                   <el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id"/>
                 </el-select>
-                <el-link v-if="operationType !== 'add'" type="primary" @click="viewWorkPermit">鏌ョ湅涓婂矖璇�</el-link>
+                <el-link type="primary" @click="viewWorkPermit">鏌ョ湅涓婂矖璇�</el-link>
               </div>
               <div v-if="item.key === 'equipment'" class="row-extra">
                 <el-link type="primary" @click="viewDevice">鏌ョ湅璁惧</el-link>
-                <el-link v-if="operationType !== 'add'" type="primary" @click="viewCalibrationsFileDia">鏌ョ湅鏍″噯璇佷功
+                <el-link type="primary" @click="viewCalibrationsFileDia">鏌ョ湅鏍″噯璇佷功
                 </el-link>
               </div>
             </td>
@@ -237,7 +237,14 @@
       this.openChild('deviceVisible', 'device', this.form)
     },
     viewCalibrationsFileDia() {
-      this.openChild('calibrationVisible', 'calibration', this.form)
+      if (this.operationType === 'add' && !(this.form.machineAttachmentList || []).some(device => device.deviceId)) {
+        this.$message.warning('璇峰厛閫夋嫨璁惧')
+        return
+      }
+      this.openChild('calibrationVisible', 'calibration', {
+        ...this.form,
+        useCurrentDevices: this.operationType === 'add'
+      })
     },
     viewTestRecord() {
       this.openChild('recordVisible', 'record', this.info)
diff --git a/src/views/business/inspectionTask/index.vue b/src/views/business/inspectionTask/index.vue
index 0e712b9..9131551 100644
--- a/src/views/business/inspectionTask/index.vue
+++ b/src/views/business/inspectionTask/index.vue
@@ -3,7 +3,7 @@
     <div style="height: 100%">
       <div class="search">
         <el-form :model="queryParams" ref="queryParams" size="small" :inline="true">
-          <el-form-item label="濮旀墭缂栧彿" prop="entrustCode">
+          <el-form-item :label="taskSource === 'QUALITY_MONITOR' ? '鐩戞帶浠诲姟缂栧彿' : '濮旀墭缂栧彿'" prop="entrustCode">
             <el-input v-model="queryParams.entrustCode" clearable placeholder="璇疯緭鍏�" size="small"
                       @keyup.enter="refreshTable()"></el-input>
           </el-form-item>
@@ -29,7 +29,7 @@
       <div class="center" v-loading="tableLoading">
         <div class="center-options">
           <div style="display: flex; align-items: center">
-            <span style="font-size: 14px">璇曢獙瀹�:</span>
+            <span style="font-size: 14px">浠诲姟鏉ユ簮:</span>
             <ul class="tab">
               <li v-for="(m, i) in tabList" :key="i" :class="{ active: i == tabIndex }" @click="handleTab(m, i)">
                 {{ m.label.replace("璇曢獙瀹�", "") }}
@@ -45,7 +45,7 @@
             <span>{{ page.total }}</span>
           </div>
         </div>
-        <lims-table :tableData="tableData" :column="column" :page="page"
+        <lims-table :tableData="tableData" :column="displayColumns" :page="page"
           :rowClassName="rowClassName" :height="'calc(100vh - 300px)'" @pagination="pagination"
           key="tableData0">
           <template #action="scope"><div>
@@ -56,7 +56,7 @@
                   (scope.row.userName && !scope.row.userName.includes(nickName))
                 )"
                        @click="handleInspection(scope.row)">妫�楠�</el-button>
-            <el-button link type="primary" size="small" :disabled="(
+            <el-button v-if="scope.row.taskSource !== 'QUALITY_MONITOR'" link type="primary" size="small" :disabled="(
                   scope.row.userName == null ||
                   scope.row.insState == 5 ||
                   scope.row.insState == 3 ||
@@ -64,12 +64,13 @@
                 )"
                        @click="handleConnect(scope.row)">浜ゆ帴</el-button>
             <el-button link type="primary" size="small" @click="viewInspectInfo(scope.row)">鍘熷璁板綍</el-button>
-            <el-popover placement="bottom" trigger="hover" style="margin-left: 6px" :disabled="(scope.row.insState != 3 || scope.row.userName == null ||(scope.row.userName && !scope.row.userName.includes(nickName)))">
+            <el-button v-if="scope.row.taskSource === 'QUALITY_MONITOR'" link type="primary" size="small" @click="showMonitorRequirement(scope.row)">{{ Number(scope.row.insState) === 5 ? '璇勪环璁板綍' : '璇勪环瑕佹眰' }}</el-button>
+            <el-popover v-if="scope.row.taskSource !== 'QUALITY_MONITOR'" placement="bottom" trigger="hover" style="margin-left: 6px" :disabled="(scope.row.insState != 3 || scope.row.userName == null ||(scope.row.userName && !scope.row.userName.includes(nickName)))">
               <template #reference>
-                <el-button link type="primary" size="small" :disabled="(scope.row.insState != 3 || scope.row.userName == null ||
+                <el-button link type="primary" size="small" :disabled="(scope.row.taskSource === 'QUALITY_MONITOR' || scope.row.insState != 3 || scope.row.userName == null ||
                   (scope.row.userName && !scope.row.userName.includes(nickName)))">鏇村</el-button>
               </template>
-              <div>
+              <div v-if="scope.row.taskSource !== 'QUALITY_MONITOR'">
                 <el-button :disabled="(scope.row.insState != 3 || scope.row.userName == null ||
                   (scope.row.userName && !scope.row.userName.includes(nickName)))" style="margin-left: 10px" link type="primary" size="small" @click="download(scope.row)">涓嬭浇鎶ュ憡</el-button>
                 <el-button :disabled="(scope.row.insState != 3 || scope.row.userName == null ||
@@ -80,6 +81,23 @@
         </lims-table>
       </div>
     </div>
+    <el-dialog v-model="monitorRequirementVisible" title="璐ㄩ噺鐩戞帶璇勪环瑕佹眰" width="560px">
+      <el-descriptions :column="1" border>
+        <el-descriptions-item label="鐩戞帶浠诲姟缂栧彿">{{ monitorRequirement.entrustCode }}</el-descriptions-item>
+        <el-descriptions-item label="鐩戞帶椤圭洰">{{ monitorRequirement.monitorProject }}</el-descriptions-item>
+        <el-descriptions-item label="鐩戞帶鐩殑">{{ monitorRequirement.monitorPurpose }}</el-descriptions-item>
+        <el-descriptions-item label="鐩戞帶鏂瑰紡">{{ monitorRequirement.monitorWay }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬫爣鍑�">{{ monitorRequirement.standardCode }}</el-descriptions-item>
+        <el-descriptions-item label="鏍峰搧鍙婅鏍�">{{ monitorRequirement.sample }} {{ monitorRequirement.sampleSpec }}</el-descriptions-item>
+        <el-descriptions-item label="瀹屾垚鏈熼檺">{{ monitorRequirement.dueDate }}</el-descriptions-item>
+      </el-descriptions>
+      <p>璇峰湪鈥滃師濮嬭褰曗�濅腑濉啓妫�娴嬫暟鎹紝澶嶆牳閫氳繃鍚庣敱璇勪环浜哄憳濉啓璐ㄩ噺鎺у埗璇勪环璁板綍銆�</p>
+      <template #footer>
+        <el-button @click="monitorRequirementVisible=false">鍏抽棴</el-button>
+        <el-button v-if="Number(monitorRequirement.insState) === 5" type="primary" @click="openMonitorEvaluation">鎵撳紑璇勪环璁板綍</el-button>
+      </template>
+    </el-dialog>
+    <inspection-evaluation-dialog v-if="inspectionEvaluateDialog" ref="inspectionEvaluateDialog" @close="inspectionEvaluateDialog=false" />
     <el-dialog v-model="claimVisible" title="鎻愮ず" width="400px">
       鏄惁璁ら濮旀墭缂栧彿<span style="color: #33c130">{{
         sampleUserForm.entrustCode
@@ -210,10 +228,11 @@
 import {getRetestResult} from "@/api/business/rawMaterialOrder";
 import onlyoffice from "@/components/Onlyoffice/onlyoffice.vue";
 import filePreview from "@/components/Preview/filePreview.vue";
+import InspectionEvaluationDialog from "@/views/CNAS/process/ensureResults/ensureResultsValidity/components/inspectionEvaluationDialog.vue";
 export default {
   name: 'InspectionTask',
   components: {
-    filePreview, onlyoffice,
+    filePreview, onlyoffice, InspectionEvaluationDialog,
     limsTable,
     viewManHourDia,
     UnPassRetestResult
@@ -221,10 +240,18 @@
   dicts: ["urgency_level", "inspection_task_state","material_prop_type"],
   computed: {
     ...mapGetters(["nickName", "userId"]),
+    displayColumns() {
+      return this.column.filter(item => this.taskSource === 'QUALITY_MONITOR'
+        ? !['typeSource', 'orderType'].includes(item.prop)
+        : !['monitorProject', 'dueDate'].includes(item.prop))
+    },
   },
   data() {
     return {
       issuedVisible: false,
+      monitorRequirementVisible: false,
+      monitorRequirement: {},
+      inspectionEvaluateDialog: false,
       fullscreen: false,
       lookDialogVisible: false,
       option: null,
@@ -245,7 +272,8 @@
       },
       claimVisible: false,
       tabList: [
-        { label: "濮旀墭", value: -1 },
+        { label: "濮旀墭", value: "ENTRUSTED" },
+        { label: "璐ㄩ噺鐩戞帶", value: "QUALITY_MONITOR" },
         // { label: "鍘熸潗鏂�", value: 1 },
         // { label: "澶栬喘", value: 0 },
       ],
@@ -255,6 +283,7 @@
       insStateList: [],
       state: 0, // 0:鍙拌处椤碉紝1锛氭楠岄〉闈�,2妫�楠岄〉闈�(澶嶆牳)锛岄粯璁や负0,3鏁版嵁鏌ョ湅
       typeSource: -1, // 0:澶栬喘鎴愬搧涓嬪崟锛�1锛氬師鏉愭枡涓嬪崟
+      taskSource: 'ENTRUSTED',
       sonLaboratory: null, // 0:濮旀墭锛�1锛氬師鏉愭枡
       activeFace: 0, // 1锛氫笅鍗曪紝2锛氭煡鐪嬶紝3锛氬鏍革紝榛樿涓�0
       currentId: null,
@@ -328,12 +357,14 @@
       tableData: [],
       column: [
         {
-          label: "濮旀墭缂栧彿",
+          label: "浠诲姟缂栧彿",
           prop: "entrustCode",
           width: "160px",
           dataType: "link",
           linkMethod: "selectAllByOne",
         },
+        { label: "鐩戞帶椤圭洰", prop: "monitorProject", width: "150px" },
+        { label: "璁″垝鏈熼檺", prop: "dueDate", width: "120px" },
         { label: "鏍峰搧鍚嶇О", prop: "sample", width: "160px" },
         {
           label: "涓嬪崟绫诲埆",
@@ -445,6 +476,7 @@
     this.getAuthorizedPerson();
     this.queryParams.userId = this.userId;
     this.queryParams.typeSource = this.typeSource
+    this.queryParams.taskSource = this.taskSource
     this.currentTime = getYearAndMonthAndDays();
     this.getDicts("urgency_level").then((response) => {
       this.urgencyLevel = this.dictToValue(response.data);
@@ -464,9 +496,19 @@
       this.inspectionTaskState = this.dictToValue(response.data);
     });
     this.queryParams.typeSource = this.typeSource
+    this.queryParams.taskSource = this.taskSource
     this.refreshTable();
   },
   methods: {
+    showMonitorRequirement(row) {
+      this.monitorRequirement = row
+      this.monitorRequirementVisible = true
+    },
+    openMonitorEvaluation() {
+      this.monitorRequirementVisible = false
+      this.inspectionEvaluateDialog = true
+      this.$nextTick(() => this.$refs.inspectionEvaluateDialog.open(this.monitorRequirement))
+    },
     getList() {
       this.tableLoading = true;
       let param = { ...this.queryParams, ...this.page };
@@ -505,7 +547,8 @@
     refreshTable(e) {
       this.page.current = 1;
       this.queryParams.tabLabel = this.tabLabel;
-      this.queryParams.typeSource = this.typeSource;
+      this.queryParams.taskSource = this.taskSource;
+      this.queryParams.typeSource = this.taskSource === 'ENTRUSTED' ? -1 : null;
       this.getList();
     },
     // 涓嬭浇鎶ュ憡
@@ -576,11 +619,15 @@
     },
     handleTab(m, i) {
       this.tabIndex = i;
-      this.typeSource =m.value;
+      this.taskSource = m.value;
       this.queryParams.sonLaboratory = "";
       this.refreshTable();
     },
     selectAllByOne(row) {
+      if (row.taskSource === 'QUALITY_MONITOR') {
+        this.viewInspectInfo(row)
+        return
+      }
       this.isCopper = row.isCopper;
       this.customsInspection = row;
       this.activeFace = 2;
@@ -647,6 +694,7 @@
           sonLaboratory: this.sonLaboratory,
           state: this.state,
           typeSource: this.typeSource,
+          taskSource: row.taskSource,
           orderId: this.orderId
         },
       });
@@ -673,6 +721,7 @@
           sonLaboratory: this.sonLaboratory,
           state: this.state,
           typeSource: this.typeSource,
+          taskSource: row.taskSource,
           orderId: this.orderId,
         },
       });
diff --git a/src/views/business/inspectionTask/inspection.vue b/src/views/business/inspectionTask/inspection.vue
index 0150dfc..1031214 100644
--- a/src/views/business/inspectionTask/inspection.vue
+++ b/src/views/business/inspectionTask/inspection.vue
@@ -396,7 +396,7 @@
         </div>
         <div class="search_input" style="width: 100%">
           <el-select v-model="verifyUser" clearable filterable placeholder="璇烽�夋嫨" size="small" style="width: 100%">
-            <el-option v-for="(item, i) in personList" :key="i" :label="item.label" :value="item.value">
+            <el-option v-for="(item, i) in reviewerOptions" :key="i" :label="item.label" :value="item.value">
             </el-option>
           </el-select>
         </div>
@@ -554,6 +554,7 @@
       state: null,
       inspectorList: null,
       typeSource: null,
+      taskSource: null,
       sagData: [],
       sagForm: {
         sampleCode: null,
@@ -727,6 +728,11 @@
   // 鐢ㄤ簬涓婁紶鏂囦欢鐨勪俊鎭�
   computed: {
     ...mapGetters(["userId"]),
+    reviewerOptions() {
+      return this.taskSource === 'QUALITY_MONITOR'
+        ? this.personList.filter(item => Number(item.value) !== Number(this.userId))
+        : this.personList;
+    },
     action() {
       return this.javaApi + "/insOrderPlan/uploadFile";
     },
@@ -831,14 +837,16 @@
   },
   methods: {
     syncRouteContext() {
-      const { sonLaboratory, orderId, state, inspectorList, typeSource } = this.$route.query;
+      const { sonLaboratory, orderId, state, inspectorList, typeSource, taskSource } = this.$route.query;
       const orderChanged = this.id !== orderId;
       this.sonLaboratory = sonLaboratory;
       this.orderId = orderId;
       this.state = state;
       this.inspectorList = inspectorList;
       this.typeSource = typeSource;
+      this.taskSource = taskSource;
       if (orderChanged) {
+        if (taskSource === 'QUALITY_MONITOR') this.verifyUser = null;
         this.tableLists = [];
         this.tableList = [];
         this.sampleProduct = [];
@@ -1000,6 +1008,7 @@
         laboratory: this.sonLaboratory,
       }).then(async (res) => {
         this.insOrder = res.data.insOrder;
+        this.taskSource = this.insOrder.taskSource || this.taskSource;
         if(res.data.insOrder.ifsOrderType==='02wg'){
           //鏌ヨ闆朵欢灞炴��
           this.getPartProps(res.data.insOrder.ifsInventoryId)
@@ -1759,6 +1768,10 @@
       }
     },
     openAddCheck() {
+      if (this.taskSource === 'QUALITY_MONITOR') {
+        this.upInsReview(1);
+        return;
+      }
       this.addCheck = true;
     },
     // 鏀堕泦妯℃澘绾ф娴嬫潯浠剁殑缂哄け椤癸紙姣忎釜妯℃澘閮借濉叏锛�

--
Gitblit v1.9.3