zhangwencui
22 小时以前 7630c6eddd23a8ea456b3dc64a49c43e41803d8a
隐患排查上报
已添加2个文件
已修改1个文件
1355 ■■■■■ 文件已修改
src/api/safeProduction/dangerInvestigation.js 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/dangerInvestigation/index.vue 1288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safeQualifications/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/safeProduction/dangerInvestigation.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
// å‘货台账页面接口
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function dangerInvestigationListPage(query) {
  return request({
    url: "/safeHidden/page",
    method: "get",
    params: query,
  });
}
// æ–°å¢žå®‰å…¨è§„程与资质管理
export function safeHiddenAdd(query) {
    return request({
        url: '/safeHidden',
        method: 'post',
        data: query
    })
}
// ä¿®æ”¹å®‰å…¨è§„程与资质管理
export function safeHiddenUpdate(query) {
    return request({
        url: '/safeHidden',
        method: 'put',
        data: query
    })
}
// åˆ é™¤å®‰å…¨è§„程与资质管理
export function safeHiddenDel(ids) {
    return request({
        url: '/safeHidden/' + ids,
        method: 'delete',
        data: ids
    })
}
// æŸ¥è¯¢é™„件列表
export function fileListPage(query) {
  return request({
    url: "/safeHiddenFile/listPage",
    method: "get",
    params: query,
  });
}
// æ·»åР附件
export function safeHiddenFileAdd(query) {
    return request({
        url: '/safeHiddenFile/add',
        method: 'post',
        data: query
    })
}
// åˆ é™¤é™„ä»¶
export function safeHiddenFileDel(ids) {
    return request({
        url: '/safeHiddenFile/del',
        method: 'delete',
        data: ids
    })
}
src/views/safeProduction/dangerInvestigation/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1288 @@
<template>
  <div class="app-container">
    <!-- <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="隐患编号:">
          <el-input v-model="searchForm.hiddenCode"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
      </el-form>
    </div> -->
    <div class="table_list">
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary"
                     @click="openForm('add')">
            æ–°å¢žé𐿂£
          </el-button>
          <!-- <el-button type="primary"
                     plain
                     @click="handleImport">导入</el-button>
          <el-button @click="handleOut">导出</el-button> -->
          <el-button type="danger"
                     plain
                     @click="handleDelete">删除</el-button>
          <!-- <el-button type="primary"
                     plain
                     @click="handlePrint">打印</el-button> -->
        </div>
      </div>
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                :row-class-name="getRowClass"
                style="width: 100%"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55"
                         fixed="left" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="隐患编号"
                         prop="hiddenCode"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="隐患描述"
                         prop="hiddenDesc"
                         show-overflow-tooltip />
        <el-table-column label="隐患具体位置"
                         prop="location"
                         show-overflow-tooltip />
        <el-table-column label="上报人"
                         prop="createUserName"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="上报时间"
                         prop="createTime"
                         show-overflow-tooltip />
        <el-table-column label="整改完成期限"
                         prop="rectifyTime"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="整改责任人"
                         prop="rectifyUserName"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="整改责任人联系方式"
                         prop="rectifyUserMobile"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="整改具体措施"
                         prop="rectifyMeasures"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="实际整改完成时间"
                         prop="rectifyActualTime"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="验收意见"
                         prop="verifyRemark"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="验收时间"
                         prop="verifyTime"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="验收结果"
                         prop="verifyResult"
                         width="120"
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag v-if="scope.row.verifyResult"
                    :type="scope.row.verifyResult === '通过' ? 'success' : 'danger'">
              {{ scope.row.verifyResult }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column fixed="right"
                         label="操作"
                         min-width="250"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button link
                       type="primary"
                       size="small"
                       @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit2', scope.row)">整改</el-button>
            <el-button link
                       type="primary"
                       size="small"
                       :disabled="!scope.row.rectifyActualTime"
                       @click="openForm('edit3', scope.row)">验收</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible"
                :title="getTitle(operationType)"
                :width="'70%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
               v-if="operationType === 'add' || operationType === 'edit'"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="隐患编号:"
                          prop="hiddenCode">
              <el-input v-model="form.hiddenCode"
                        placeholder="自动生成"
                        disabled
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="隐患具体位置:"
                          prop="location">
              <el-input v-model="form.location"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="隐患描述:"
                      prop="hiddenDesc">
          <el-input v-model="form.hiddenDesc"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入"
                    clearable />
        </el-form-item>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="隐患类型:"
                          prop="type">
              <el-select v-model="form.type"
                         placeholder="请选择"
                         clearable>
                <el-option v-for="item in typeList"
                           :key="item.value"
                           :label="item.label"
                           :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="隐患风险等级:"
                          prop="riskLevel">
              <el-select v-model="form.riskLevel"
                         placeholder="请选择"
                         clearable>
                <el-option v-for="item in riskLevelList"
                           :key="item.value"
                           :label="item.label"
                           :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="上报人:"
                          prop="createUser">
              <el-select v-model="form.createUser"
                         placeholder="请选择"
                         @change="handleChange"
                         clearable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="上报时间:"
                          prop="createTime">
              <el-date-picker style="width: 100%"
                              readonly
                              v-model="form.createTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="整改责任人:"
                          prop="rectifyUserId">
              <el-select v-model="form.rectifyUserId"
                         placeholder="请选择"
                         @change="handleChange2"
                         clearable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="整改完成期限"
                          prop="rectifyTime">
              <el-date-picker style="width: 100%"
                              v-model="form.rectifyTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <el-descriptions :column="2"
                       style="margin-bottom: 20px;"
                       v-if="operationType === 'edit2' || operationType === 'edit3'"
                       title="隐患详情"
                       border>
        <el-descriptions-item label="隐患编号">
          <span class="detail-title">{{ form.hiddenCode }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="隐患具体位置">
          <span class="detail-title">{{ form.location }}</span>
        </el-descriptions-item>
        <el-descriptions-item :span="2"
                              label="隐患描述">
          <span class="detail-title">{{ form.hiddenDesc }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="隐患类型">
          <span class="detail-title">{{ form.type }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="隐患风险等级">
          <span class="detail-title">{{ form.riskLevel }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="上报人">
          <span class="detail-title">{{ form.createUserName }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="上报时间">
          <span class="detail-title">{{ form.createTime }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="整改责任人">
          <span class="detail-title">{{ form.rectifyUserName }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="整改责任人联系方式">
          <span class="detail-title">{{ form.rectifyUserMobile }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="整改完成期限">
          <span class="detail-title">{{ form.rectifyTime }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-descriptions :column="2"
                       style="margin-bottom: 20px;"
                       v-if="operationType === 'edit3'"
                       title="整改详情"
                       border>
        <el-descriptions-item label="整改具体措施"
                              :span="2">
          <span class="detail-title">{{ form2.rectifyMeasures }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="实际整改完成时间">
          <span class="detail-title">{{ form2.rectifyActualTime }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-form :model="form2"
               v-if="operationType === 'edit2'"
               label-width="140px"
               label-position="top"
               :rules="rules2"
               ref="formRef2">
        <el-form-item label="整改具体措施:"
                      prop="rectifyMeasures">
          <el-input v-model="form2.rectifyMeasures"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入整改具体措施"
                    clearable />
        </el-form-item>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="实际整改完成时间:"
                          prop="rectifyActualTime">
              <el-date-picker style="width: 100%"
                              v-model="form2.rectifyActualTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable
                              :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <el-form :model="form3"
               v-if="operationType === 'edit3'"
               label-width="140px"
               label-position="top"
               :rules="rules3"
               ref="formRef3">
        <el-form-item label="验收意见:"
                      prop="verifyRemark">
          <el-input v-model="form3.verifyRemark"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入验收意见"
                    clearable />
        </el-form-item>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="验收时间:"
                          prop="verifyTime">
              <el-date-picker style="width: 100%"
                              disabled
                              v-model="form3.verifyTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="验收结果:"
                          prop="verifyResult">
              <el-select v-model="form3.verifyResult"
                         placeholder="请选择"
                         clearable>
                <el-option label="通过"
                           value="通过" />
                <el-option label="不通过"
                           value="不通过" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="验收人:"
                          prop="verifyUserId">
              <el-select v-model="form3.verifyUserId"
                         disabled
                         placeholder="请选择"
                         clearable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
    <!-- é™„件列表弹窗 -->
    <FileListDialog ref="fileListRef"
                    v-model="fileListDialogVisible"
                    :show-upload-button="true"
                    :show-delete-button="true"
                    :upload-method="handleUpload"
                    :delete-method="handleFileDelete"
                    title="附件列表" />
  </div>
</template>
<script setup>
  import { getToken } from "@/utils/auth";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { onMounted, ref, getCurrentInstance } from "vue";
  import { ElMessageBox, ElMessage } from "element-plus";
  import useUserStore from "@/store/modules/user";
  import { userListNoPage } from "@/api/system/user.js";
  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
  import {
    dangerInvestigationListPage,
    safeHiddenAdd,
    safeHiddenUpdate,
    safeHiddenDel,
    fileListPage,
    safeHiddenFileAdd,
    safeHiddenFileDel,
  } from "@/api/safeProduction/dangerInvestigation.js";
  import useFormData from "@/hooks/useFormData.js";
  import request from "@/utils/request";
  import dayjs from "dayjs";
  import { get } from "@vueuse/core";
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const selectedRows = ref([]);
  const userList = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const getTitle = type => {
    if (type === "add") {
      return "新增隐患";
    } else if (type === "edit") {
      return "修改隐患";
    } else if (type === "edit2") {
      return "整改页面";
    } else if (type === "edit3") {
      return "验收页面";
    }
  };
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      hiddenCode: "", // é𐿂£ç¼–号
    },
    form: {
      hiddenCode: "", // é𐿂£ç¼–号
      location: "", // é𐿂£ä½ç½®
      hiddenDesc: "", // é𐿂£æè¿°
      createUser: "", // ä¸ŠæŠ¥äºº
      createUserName: "",
      createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // ä¸ŠæŠ¥æ—¶é—´
      rectifyUserId: "", // æ•´æ”¹è´£ä»»äºº
      rectifyUserName: "",
      rectifyTime: "", // æ•´æ”¹å®ŒæˆæœŸé™
      rectifyUserMobile: "", // æ•´æ”¹è´£ä»»äººæ‰‹æœºå·
      riskLevel: "", // é𐿂£é£Žé™©ç­‰çº§
      type: "", // é𐿂£ç±»åž‹
    },
    rules: {
      location: [{ required: true, message: "请输入", trigger: "blur" }],
      hiddenDesc: [{ required: true, message: "请输入", trigger: "blur" }],
      riskLevel: [{ required: true, message: "请选择", trigger: "change" }],
      type: [{ required: true, message: "请选择", trigger: "change" }],
      createUser: [{ required: true, message: "请选择", trigger: "change" }],
      rectifyUserId: [{ required: true, message: "请选择", trigger: "change" }],
      rectifyTime: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const rules2 = {
    rectifyActualTime: [{ required: true, message: "请选择", trigger: "blur" }],
    rectifyMeasures: [{ required: true, message: "请输入", trigger: "blur" }],
  };
  const rules3 = {
    verifyTime: [{ required: true, message: "请选择", trigger: "blur" }],
    verifyRemark: [{ required: true, message: "请输入", trigger: "blur" }],
    verifyResult: [{ required: true, message: "请选择", trigger: "change" }],
    acceptDesc: [{ required: true, message: "请输入", trigger: "blur" }],
  };
  const typeList = ref([
    {
      value: "设备安全",
      label: "设备安全",
    },
    {
      value: "人员操作",
      label: "人员操作",
    },
    {
      value: "环境风险",
      label: "环境风险",
    },
    {
      value: "物料管控",
      label: "物料管控",
    },
    {
      value: "其他",
      label: "其他",
    },
  ]);
  const form2 = ref({
    rectifyActualTime: "", // å®žé™…整改完成时间
    rectifyMeasures: "", // æ•´æ”¹å…·ä½“措施
  });
  const form3 = ref({
    verifyTime: "", // éªŒæ”¶æ—¶é—´
    verifyRemark: "", // éªŒæ”¶å¤‡æ³¨
    acceptDesc: "", // éªŒæ”¶æè¿°
    verifyUserId: "", // éªŒæ”¶äºº
    verifyUserName: "",
    verifyResult: "", // éªŒæ”¶ç»“æžœ
  });
  const riskLevelList = ref([
    {
      value: "重大风险",
      label: "重大风险",
    },
    {
      value: "较大风险",
      label: "较大风险",
    },
    {
      value: "一般风险",
      label: "一般风险",
    },
    {
      value: "低风险",
      label: "低风险",
    },
  ]);
  // é𐿂£ç±»åž‹é€‰é¡¹
  const { type_qualification } = proxy.useDict("type_qualification");
  const { form, rules } = toRefs(data);
  const { form: searchForm } = useFormData(data.searchForm);
  // äº§å“è¡¨å•弹框数据
  const productFormVisible = ref(false);
  const quotationLoading = ref(false);
  const quotationList = ref([]);
  const quotationSearchForm = reactive({
    quotationNo: "",
    customer: "",
  });
  const handleChange = userId => {
    const selectedUser = userList.value.find(user => user.userId === userId);
    if (selectedUser) {
      form.value.createUserName = selectedUser.nickName;
    }
  };
  const handleChange2 = userId => {
    const selectedUser = userList.value.find(user => user.userId === userId);
    if (selectedUser) {
      form.value.rectifyUserName = selectedUser.nickName;
      form.value.rectifyUserMobile = selectedUser.phonenumber;
    }
  };
  // å¯¼å…¥ç›¸å…³
  const importUploadRef = ref(null);
  const importUpload = reactive({
    title: "导入销售台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: file => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
        proxy.$modal.msgError("上传文件只能是 xlsx/xls æ ¼å¼!");
        return false;
      }
      if (!isLt10M) {
        proxy.$modal.msgError("上传文件大小不能超过 10MB!");
        return false;
      }
      return true;
    },
    onChange: (file, fileList) => {
      console.log("文件状态改变", file, fileList);
    },
    onProgress: (event, file, fileList) => {
      console.log("上传中...", event.percent);
    },
    onSuccess: (response, file, fileList) => {
      console.log("上传成功", response, file, fileList);
      importUpload.isUploading = false;
      if (response.code === 200) {
        proxy.$modal.msgSuccess("导入成功");
        importUpload.open = false;
        if (importUploadRef.value) {
          importUploadRef.value.clearFiles();
        }
        getList();
      } else {
        proxy.$modal.msgError(response.msg || "导入失败");
      }
    },
    onError: (error, file, fileList) => {
      console.error("上传失败", error, file, fileList);
      importUpload.isUploading = false;
      proxy.$modal.msgError("导入失败,请重试");
    },
  });
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    // åªæœ‰åœ¨ç‚¹å‡»æœç´¢æŒ‰é’®æ—¶æ‰é‡ç½®é¡µç åˆ°ç¬¬ä¸€é¡µ
    // é¿å…è¡¨å•字段change事件干扰分页
    if (arguments.length === 0) {
      page.current = 1;
    }
    expandedRowKeys.value = [];
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
    const params = { ...rest, ...page };
    // ç§»é™¤å½•入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    return dangerInvestigationListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        total.value = res.data.total;
        return res;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
        return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
        }
      }
    }
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
    console.log("selection", selectedRows.value);
  };
  const expandedRowKeys = ref([]);
  // æ‰“开弹框
  const openForm = async (type, row) => {
    console.log("row", row);
    operationType.value = type;
    form3.value = {
      verifyTime: "", // éªŒæ”¶æ—¶é—´
      verifyRemark: "", // éªŒæ”¶å¤‡æ³¨
      verifyResult: "", // éªŒæ”¶æè¿°
      verifyUserId: "", // éªŒæ”¶äºº
    };
    form2.value = {
      rectifyActualTime: "", // å®žé™…整改完成时间
      rectifyMeasures: "", // æ•´æ”¹å…·ä½“措施
    };
    if (type === "add") {
      form.value = {
        hiddenCode: "", // é𐿂£ç¼–号
        location: "", // é𐿂£ä½ç½®
        hiddenDesc: "", // é𐿂£æè¿°
        createUser: "", // ä¸ŠæŠ¥äºº
        createUserName: "",
        createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // ä¸ŠæŠ¥æ—¶é—´
        rectifyUserId: "", // æ•´æ”¹è´£ä»»äºº
        rectifyUserName: "",
        rectifyTime: "", // æ•´æ”¹å®ŒæˆæœŸé™
        rectifyUserMobile: "", // æ•´æ”¹è´£ä»»äººæ‰‹æœºå·
        riskLevel: "", // é𐿂£é£Žé™©ç­‰çº§
        type: "", // é𐿂£ç±»åž‹
      };
    } else if (type === "edit") {
      form.value = {
        id: row.id,
        hiddenCode: row.hiddenCode, // é𐿂£ç¼–号
        location: row.location, // é𐿂£ä½ç½®
        hiddenDesc: row.hiddenDesc, // é𐿂£æè¿°
        createUser: row.createUser, // ä¸ŠæŠ¥äºº
        createUserName: row.createUserName,
        createTime: row.createTime, // ä¸ŠæŠ¥æ—¶é—´
        rectifyUserId: row.rectifyUserId, // æ•´æ”¹è´£ä»»äºº
        rectifyUserName: row.rectifyUserName,
        rectifyTime: row.rectifyTime, // æ•´æ”¹å®ŒæˆæœŸé™
        rectifyUserMobile: row.rectifyUserMobile, // æ•´æ”¹è´£ä»»äººæ‰‹æœºå·
        riskLevel: row.riskLevel, // é𐿂£é£Žé™©ç­‰çº§
        type: row.type, // é𐿂£ç±»åž‹
      };
    } else if (type === "edit2") {
      form.value = {
        id: row.id,
        hiddenCode: row.hiddenCode, // é𐿂£ç¼–号
        location: row.location, // é𐿂£ä½ç½®
        hiddenDesc: row.hiddenDesc, // é𐿂£æè¿°
        createUser: row.createUser, // ä¸ŠæŠ¥äºº
        createUserName: row.createUserName,
        createTime: row.createTime, // ä¸ŠæŠ¥æ—¶é—´
        rectifyUserId: row.rectifyUserId, // æ•´æ”¹è´£ä»»äºº
        rectifyUserName: row.rectifyUserName,
        rectifyTime: row.rectifyTime, // æ•´æ”¹å®ŒæˆæœŸé™
        rectifyUserMobile: row.rectifyUserMobile, // æ•´æ”¹è´£ä»»äººæ‰‹æœºå·
        riskLevel: row.riskLevel, // é𐿂£é£Žé™©ç­‰çº§
        type: row.type, // é𐿂£ç±»åž‹
      };
      form2.value = {
        rectifyActualTime: row.rectifyActualTime, // å®žé™…整改完成时间
        rectifyMeasures: row.rectifyMeasures, // æ•´æ”¹å…·ä½“措施
      };
    } else if (type === "edit3") {
      form.value = {
        id: row.id,
        hiddenCode: row.hiddenCode, // é𐿂£ç¼–号
        location: row.location, // é𐿂£ä½ç½®
        hiddenDesc: row.hiddenDesc, // é𐿂£æè¿°
        createUser: row.createUser, // ä¸ŠæŠ¥äºº
        createUserName: row.createUserName,
        createTime: row.createTime, // ä¸ŠæŠ¥æ—¶é—´
        rectifyUserId: row.rectifyUserId, // æ•´æ”¹è´£ä»»äºº
        rectifyUserName: row.rectifyUserName,
        rectifyTime: row.rectifyTime, // æ•´æ”¹å®ŒæˆæœŸé™
        rectifyUserMobile: row.rectifyUserMobile, // æ•´æ”¹è´£ä»»äººæ‰‹æœºå·
        riskLevel: row.riskLevel, // é𐿂£é£Žé™©ç­‰çº§
        type: row.type, // é𐿂£ç±»åž‹
      };
      form2.value = {
        rectifyActualTime: row.rectifyActualTime, // å®žé™…整改完成时间
        rectifyMeasures: row.rectifyMeasures, // æ•´æ”¹å…·ä½“措施
      };
      form3.value = {
        verifyTime: row.verifyTime, // éªŒæ”¶æ—¶é—´
        verifyRemark: row.verifyRemark, // éªŒæ”¶å¤‡æ³¨
        verifyResult: row.verifyResult, // éªŒæ”¶æè¿°
        verifyUserId: row.verifyUserId, // éªŒæ”¶äºº
      };
      console.log("form3.value", form3.value);
      if (!form3.value.verifyUserId || form3.value.verifyUserId === "null") {
        form3.value.verifyUserId = Number(currentUserId.value); // éªŒæ”¶äºº
      }
      if (!form3.value.verifyTime || form3.value.verifyTime === "null") {
        form3.value.verifyTime = dayjs().format("YYYY-MM-DD"); // éªŒæ”¶æè¿°
      }
    }
    dialogFormVisible.value = true;
  };
  const getCurrentUserInfo = () => {
    getInfo;
  };
  const fetchQuotationList = async () => {
    quotationLoading.value = true;
    try {
      const params = {
        // å…¼å®¹åŽç«¯åˆ†é¡µå­—段:这里沿用报价页面已有可用的字段命名
        currentPage: 1,
        pageSize: 100,
        ...quotationSearchForm,
        status: "通过",
      };
      const res = await getQuotationList(params);
      quotationList.value = res?.data?.records || [];
    } finally {
      quotationLoading.value = false;
    }
  };
  // æäº¤è¡¨å•
  const submitForm = () => {
    console.log("operationType.value", operationType.value);
    if (operationType.value == "add") {
      proxy.$refs["formRef"].validate(valid => {
        if (valid) {
          safeHiddenAdd(form.value).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
          });
        }
      });
    } else if (operationType.value == "edit") {
      proxy.$refs["formRef"].validate(valid => {
        if (valid) {
          safeHiddenUpdate(form.value).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
          });
        }
      });
    } else if (operationType.value == "edit2") {
      console.log("form2.value", form2.value);
      proxy.$refs["formRef2"].validate(valid => {
        if (valid) {
          safeHiddenUpdate({ ...form2.value, ...form.value }).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
          });
        }
      });
    } else if (operationType.value == "edit3") {
      proxy.$refs["formRef3"].validate(valid => {
        if (valid) {
          safeHiddenUpdate({
            ...form3.value,
            ...form2.value,
            ...form.value,
          }).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
          });
        }
      });
    }
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    proxy.resetForm("formRef2");
    dialogFormVisible.value = false;
  };
  // å…³é—­äº§å“å¼¹æ¡†
  const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        safeHiddenDel(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  /**
   * åˆ¤æ–­æ˜¯å¦å¯ä»¥å‘è´§
   * åªæœ‰åœ¨äº§å“çŠ¶æ€æ˜¯å……è¶³ï¼Œå‘è´§çŠ¶æ€æ˜¯å¾…å‘è´§å’Œå®¡æ ¸æ‹’ç»çš„æ—¶å€™æ‰å¯ä»¥å‘è´§
   * @param row è¡Œæ•°æ®
   */
  const canShip = row => {
    // äº§å“çŠ¶æ€å¿…é¡»æ˜¯å……è¶³ï¼ˆapproveStatus === 1)
    if (row.approveStatus !== 1) {
      return false;
    }
    // èŽ·å–å‘è´§çŠ¶æ€
    const shippingStatus = row.shippingStatus;
    // å¦‚果已发货(有发货日期或车牌号),不能再次发货
    if (row.shippingDate || row.shippingCarNumber) {
      return false;
    }
    // å‘货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
    return statusStr === "待发货" || statusStr === "审核拒绝";
  };
  /**
   * ä¸‹è½½æ–‡ä»¶
   *
   * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
   */
  const fileListRef = ref(null);
  const fileListDialogVisible = ref(false);
  const currentFileRow = ref(null);
  const downLoadFile = row => {
    currentFileRow.value = row;
    fileListPage({ safeHiddenId: row.id }).then(res => {
      if (fileListRef.value) {
        fileListRef.value.open(res.data.records);
      }
    });
  };
  const currentUserId = ref("");
  const getCurrentFactoryName = async () => {
    let res = await userStore.getInfo();
    currentUserId.value = res.user.userId;
  };
  /**
   * èŽ·å–è¡Œç±»åï¼Œç”¨äºŽåˆ¤æ–­æ˜¯å¦è¿‡æœŸæœªæ•´æ”¹
   * @param row è¡Œæ•°æ®
   * @returns ç±»å
   */
  const getRowClass = ({ row }) => {
    const now = new Date();
    // æ£€æŸ¥æ˜¯å¦è¶…过整改期限且未实际整改
    if (row.rectifyTime && !row.rectifyActualTime) {
      const rectifyTime = new Date(row.rectifyTime);
      if (now > rectifyTime) {
        return "overdue-row";
      }
    }
    return "";
  };
  onMounted(() => {
    getList();
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    getCurrentFactoryName();
  });
  // ä¸Šä¼ é™„ä»¶
  const handleUpload = async () => {
    if (!currentFileRow.value) {
      proxy.$modal.msgWarning("请先选择数据");
      return null;
    }
    return new Promise(resolve => {
      // åˆ›å»ºä¸€ä¸ªéšè—çš„æ–‡ä»¶è¾“入元素
      const input = document.createElement("input");
      input.type = "file";
      input.style.display = "none";
      input.onchange = async e => {
        const file = e.target.files[0];
        if (!file) {
          resolve(null);
          return;
        }
        try {
          // ä½¿ç”¨ FormData ä¸Šä¼ æ–‡ä»¶
          const formData = new FormData();
          formData.append("file", file);
          const uploadRes = await request({
            url: "/file/upload",
            method: "post",
            data: formData,
            headers: {
              "Content-Type": "multipart/form-data",
              Authorization: `Bearer ${getToken()}`,
            },
          });
          if (uploadRes.code === 200) {
            // ä¿å­˜é™„件信息
            const fileData = {
              safeHiddenId: currentFileRow.value.id,
              name: uploadRes.data.originalName || file.name,
              url: uploadRes.data.tempPath || uploadRes.data.url,
            };
            const saveRes = await safeHiddenFileAdd(fileData);
            if (saveRes.code === 200) {
              proxy.$modal.msgSuccess("文件上传成功");
              // é‡æ–°åŠ è½½æ–‡ä»¶åˆ—è¡¨
              const listRes = await fileListPage({
                safeHiddenId: currentFileRow.value.id,
              });
              if (listRes.code === 200 && fileListRef.value) {
                const fileList = (listRes.data?.records || []).map(item => ({
                  name: item.name,
                  url: item.url,
                  id: item.id,
                  ...item,
                }));
                fileListRef.value.setList(fileList);
              }
              // è¿”回新文件信息
              resolve({
                name: fileData.name,
                url: fileData.url,
                id: saveRes.data?.id,
              });
            } else {
              proxy.$modal.msgError(saveRes.msg || "文件保存失败");
              resolve(null);
            }
          } else {
            proxy.$modal.msgError(uploadRes.msg || "文件上传失败");
            resolve(null);
          }
        } catch (error) {
          proxy.$modal.msgError("文件上传失败");
          resolve(null);
        } finally {
          document.body.removeChild(input);
        }
      };
      document.body.appendChild(input);
      input.click();
    });
  };
  // åˆ é™¤é™„ä»¶
  const handleFileDelete = async row => {
    try {
      const res = await safeHiddenFileDel([row.id]);
      if (res.code === 200) {
        proxy.$modal.msgSuccess("删除成功");
        // é‡æ–°åŠ è½½æ–‡ä»¶åˆ—è¡¨
        if (currentFileRow.value && fileListRef.value) {
          const listRes = await fileListPage({
            safeHiddenId: currentFileRow.value.id,
          });
          if (listRes.code === 200) {
            const fileList = (listRes.data?.records || []).map(item => ({
              name: item.name,
              url: item.url,
              id: item.id,
              ...item,
            }));
            fileListRef.value.setList(fileList);
          }
        }
        return true; // è¿”回 true è¡¨ç¤ºåˆ é™¤æˆåŠŸï¼Œç»„ä»¶ä¼šæ›´æ–°åˆ—è¡¨
      } else {
        proxy.$modal.msgError(res.msg || "删除失败");
        return false;
      }
    } catch (error) {
      proxy.$modal.msgError("删除失败");
      return false;
    }
  };
</script>
<style scoped lang="scss">
  .ml-10 {
    margin-left: 10px;
  }
  .table_list {
    margin-top: unset;
  }
  :deep(.warning-row) {
    background-color: #fef0f0 !important;
  }
  :deep(.warning-row td) {
    // color: #cf1322 !important;
  }
  :deep(.overdue-row) {
    background-color: #ffffff !important;
  }
  :deep(.overdue-row td) {
    color: #e1707a !important;
  }
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
  }
  .print-preview-dialog {
    .el-dialog__body {
      padding: 0;
      max-height: 80vh;
      overflow-y: auto;
    }
  }
  .print-preview-container {
    .print-preview-header {
      padding: 15px;
      border-bottom: 1px solid #e4e7ed;
      text-align: center;
      .el-button {
        margin: 0 10px;
      }
    }
    .print-preview-content {
      padding: 20px;
      background-color: #f5f5f5;
      min-height: 400px;
    }
  }
  .print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
  }
  .delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
  }
  .header {
    text-align: center;
    margin-bottom: 8px;
    .company-name {
      font-size: 18px;
      font-weight: bold;
      margin-bottom: 4px;
    }
    .document-title {
      font-size: 16px;
      font-weight: bold;
    }
  }
  .info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .info-row {
      line-height: 20px;
      .label {
        font-weight: bold;
        width: 60px;
        font-size: 14px;
      }
      .value {
        margin-right: 20px;
        min-width: 80px;
        font-size: 14px;
      }
    }
  }
  .table-section {
    margin-bottom: 4px;
    flex: 1;
    .product-table {
      width: 100%;
      border-collapse: collapse;
      border: 1px solid #000;
      th,
      td {
        border: 1px solid #000;
        padding: 6px;
        text-align: center;
        font-size: 14px;
        line-height: 1.4;
      }
      th {
        font-weight: bold;
      }
      .total-label {
        text-align: right;
        font-weight: bold;
      }
      .total-value {
        font-weight: bold;
      }
    }
  }
  .footer-section {
    .footer-row {
      display: flex;
      margin-bottom: 3px;
      line-height: 20px;
      justify-content: space-between;
      .footer-item {
        display: flex;
        margin-right: 20px;
        .label {
          font-weight: bold;
          width: 80px;
          font-size: 14px;
        }
        .value {
          min-width: 80px;
          font-size: 14px;
        }
        &.address-item {
          .address-value {
            min-width: 200px;
          }
        }
      }
    }
  }
  @media print {
    .app-container {
      display: none;
    }
    .print-page {
      box-shadow: none;
      margin: 0;
      padding: 10mm;
      padding-left: 20mm;
      page-break-inside: avoid;
      page-break-after: always;
    }
    .print-page:last-child {
      page-break-after: avoid;
    }
  }
</style>
src/views/safeProduction/safeQualifications/index.vue
@@ -82,7 +82,11 @@
                         show-overflow-tooltip />
        <el-table-column label="规程资质类型"
                         prop="type"
                         show-overflow-tooltip />
                         show-overflow-tooltip>
          <template #default="scope">
            {{ type_qualification.find(item => item.value === scope.row.type)?.label || '-' }}
          </template>
        </el-table-column>
        <el-table-column label="版本号"
                         prop="version"
                         width="180"