吴羡
18 小时以前 6f90e7110190d146fde06cf1f5b2272fd49b53d6
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>