Prechádzať zdrojové kódy

feat:质量分析报告

luoxiao 4 dní pred
rodič
commit
b2a14331c9

+ 2 - 0
.env.development

@@ -15,3 +15,5 @@ VITE_APP_UPLOAD_URL = 'http://113.44.0.55:8010'
 
 # 是否启用 Mock 服务
 VITE_MOCK_DEV_SERVER = false
+
+VITE_APP_MES_FILE_URL = 'http://192.168.102.86:9000'

+ 1 - 0
.env.production

@@ -6,3 +6,4 @@ VITE_APP_UPLOAD_URL = ''
 # 代理前缀
 VITE_APP_BASE_API = '/spc-server'
 
+VITE_APP_MES_FILE_URL = 'http://192.168.102.86:9000'

+ 44 - 0
src/api/statistic/inspectionReport.ts

@@ -0,0 +1,44 @@
+import request from "@/utils/request";
+
+export function getInspectionData(data: object) {
+  return request({
+    url: "/api/v1/inspectionReport/page",
+    method: "post",
+    data,
+  });
+}
+
+//处理反馈表
+
+export function dealFeedback(data: object) {
+  return request({
+    url: "/api/v1/inspectionReport/handle",
+    method: "post",
+    data,
+  });
+}
+
+export function updateInspectionData(data: any) {
+  return request({
+    url: `/api/v1/inspectionReport/update`,
+    method: "post",
+    data: data,
+  });
+}
+
+export function getInspectionDetail(id: number) {
+  return request({
+    url: `/api/v1/inspectionReport/get/${id}`,
+    method: "get",
+  });
+}
+
+export function getSignaturebyName(name: string) {
+  return request({
+    url: `/api/v1/inspectionReport/getSignature`,
+    method: "get",
+    params: {
+      name: name,
+    },
+  });
+}

+ 298 - 0
src/views/statistic/inspectionReport/DetailCom.vue

@@ -0,0 +1,298 @@
+<template>
+  <div>
+    <div class="sale-header">检验报告</div>
+    <table border-collapse="collapse">
+      <!-- 定义6列布局 -->
+      <colgroup>
+        <col style="width: 16.6%" />
+        <!-- 标签列 -->
+        <col style="width: 16.6%" />
+        <!-- 内容列 -->
+        <col style="width: 16.6%" />
+        <!-- 标签列 -->
+        <col style="width: 16.6%" />
+        <!-- 内容列 -->
+        <col style="width: 16.6%" />
+        <!-- 日期标签 -->
+        <col style="width: 16.6%" />
+        <!-- 日期内容 -->
+      </colgroup>
+
+      <!-- 产品信息 -->
+      <tr>
+        <td>产品名称</td>
+        <td colspan="2">{{ saleModel.materialName }}</td>
+        <td>规格型号</td>
+        <td colspan="2">{{ saleModel.materialModel }}</td>
+      </tr>
+
+      <!-- 生产信息 -->
+      <tr>
+        <td>生产厂家/部门</td>
+        <td colspan="2">微电子车间</td>
+        <td>材料/生产批号</td>
+        <td colspan="2">{{ saleModel.workOrderCode }}</td>
+      </tr>
+
+      <!-- 检验日期 -->
+      <tr>
+        <td></td>
+        <td colspan="2"></td>
+        <td>检验日期</td>
+        <td colspan="2">{{ saleModel.created }}</td>
+      </tr>
+
+      <!-- 数量信息 -->
+      <tr>
+        <td>批 量</td>
+        <td colspan="2">{{ saleModel.batchNum }}</td>
+        <td>抽样数量</td>
+        <td colspan="2">{{ saleModel.seqNum }}</td>
+      </tr>
+
+      <!-- 环境信息 -->
+      <tr>
+        <td>温 度</td>
+        <td colspan="2">{{ saleModel.temperature }}</td>
+        <td>湿 度</td>
+        <td colspan="2">{{ saleModel.humidity }}</td>
+      </tr>
+
+      <!-- 检验依据 -->
+      <tr>
+        <td>检验依据/技术要求</td>
+        <td colspan="5">{{ saleModel.pursuant }}</td>
+      </tr>
+
+      <!-- 测试设备 -->
+      <tr>
+        <td>主要测试仪器及设备</td>
+        <td colspan="5">{{ saleModel.device }}</td>
+      </tr>
+
+      <!-- 检验情况 -->
+      <tr>
+        <td colspan="6" style="text-align: left">检验情况:</td>
+      </tr>
+      <tr>
+        <td style="height: 70px">检验项目</td>
+        <td colspan="2" style="height: 70px">检验要求</td>
+        <td colspan="3" style="height: 70px">检验结果/实测数据</td>
+      </tr>
+      <tr>
+        <!-- 检验项目列 -->
+        <td style="height: 200px; white-space: pre-line; vertical-align: top">
+          <div v-for="(item, index) in projectItems" :key="'project-' + index">
+            项目{{ index + 1 }}:{{ item.checkProject }}
+          </div>
+          <div v-if="projectItems.length === 0" style="color: #999">无数据</div>
+        </td>
+
+        <!-- 检验要求列 -->
+        <td
+          colspan="2"
+          style="height: 200px; white-space: pre-line; vertical-align: top"
+        >
+          <div v-for="(item, index) in projectItems" :key="'required-' + index">
+            项目{{ index + 1 }}:{{ item.checkRequired }}
+          </div>
+        </td>
+
+        <!-- 检验结果列 -->
+        <td
+          colspan="3"
+          style="height: 200px; white-space: pre-line; vertical-align: top"
+        >
+          <div v-for="(item, index) in projectItems" :key="'result-' + index">
+            项目{{ index + 1 }}:{{ item.checkResult }}
+          </div>
+        </td>
+      </tr>
+
+      <!-- 签名区域 -->
+      <tr>
+        <td colspan="2" style="text-align: left">
+          使用人员意见及签字(需要时)
+        </td>
+        <td colspan="4"></td>
+      </tr>
+      <tr>
+        <td colspan="2" style="text-align: left">
+          检验人员意见及签字(需要时)
+        </td>
+        <td colspan="4"></td>
+      </tr>
+
+      <!-- 检验结论 -->
+      <tr v-if="contentArray[0]?.content">
+        <td>检验结论</td>
+        <td>{{ contentArray[0].content }}</td>
+        <td>检验人</td>
+        <td>{{ contentArray[0].user1 }}</td>
+        <td>日期</td>
+        <td>{{ contentArray[0].time }}</td>
+      </tr>
+
+      <!-- 备注 -->
+      <tr v-if="contentArray[0]?.content">
+        <td>备 注</td>
+        <td colspan="5">{{ contentArray[0].notes }}</td>
+      </tr>
+
+      <!-- 审核批准 -->
+      <tr
+        v-if="
+          (contentArray[1]?.content || contentArray[2]?.content) &&
+          saleModel.state != 2
+        "
+      >
+        <td style="height: 70px">审 核</td>
+        <td>{{ contentArray[1]?.user }}</td>
+        <td>批准</td>
+        <td>{{ contentArray[2]?.user }}</td>
+        <td colspan="2" style="position: relative; height: 70px">
+          <span
+            style="
+              position: absolute;
+              left: 50%;
+              top: 50%;
+              transform: translate(-50%, -50%);
+              z-index: 1;
+              font-size: 18px;
+            "
+            >(盖章)</span
+          >
+          <img
+            v-if="contentArray[2]?.content"
+            :src="contentArray[2].sinature"
+            style="
+              position: absolute;
+              left: 50%;
+              top: -70px; /* 上移 */
+              transform: translateX(-50%);
+              width: 250px; /* 放大 */
+              height: 150px; /* 放大 */
+              object-fit: contain;
+              opacity: 0.7; /* 半透明 */
+            "
+          />
+        </td>
+      </tr>
+    </table>
+  </div>
+</template>
+
+<script setup>
+const saleModel = ref({});
+
+const contentArray = ref([]);
+const projectItems = ref([]);
+
+const refreshView = (row) => {
+  saleModel.value = row;
+
+  contentArray.value = [];
+
+  // 解析projectInfo
+  if (row.projectInfo) {
+    try {
+      projectItems.value = JSON.parse(row.projectInfo);
+    } catch (e) {
+      console.error("解析projectInfo失败:", e);
+      projectItems.value = [];
+    }
+  } else {
+    projectItems.value = [];
+  }
+
+  if (row.remark3 && JSON.parse(row.remark3).content) {
+    let remark3 = JSON.parse(row.remark3);
+    contentArray.value.push({
+      ...remark3,
+    });
+  }
+  if (row.remark4 && JSON.parse(row.remark4).content) {
+    let remark4 = JSON.parse(row.remark4);
+    contentArray.value.push({
+      ...remark4,
+    });
+  }
+  if (row.remark5 && JSON.parse(row.remark5).content) {
+    let remark5 = JSON.parse(row.remark5);
+    contentArray.value.push({
+      ...remark5,
+    });
+  }
+};
+
+const handleDownload = (item) => {
+  let url = import.meta.env.VITE_APP_UPLOAD_URL + item.fileUrl;
+
+  // 创建一个 <a> 元素
+  var link = document.createElement("a");
+  // 设置文件的 URL
+  link.href = url;
+  // 设置下载的文件名
+  link.download = item.fileName;
+
+  // 将 <a> 元素添加到文档中(虽然不需要在页面上可见)
+  document.body.appendChild(link);
+
+  // 触发点击事件
+  link.click();
+
+  // 移除 <a> 元素
+  document.body.removeChild(link);
+};
+
+defineExpose({ refreshView });
+</script>
+
+<style scoped lang="scss">
+.sale-header {
+  text-align: center;
+  font-size: 20px;
+  //font-weight: bold;
+  margin-bottom: 10px;
+}
+.sale-right {
+  text-align: right;
+  width: 800px;
+}
+table {
+  width: 800px;
+  margin: 0 auto;
+  border: 1px solid #000000;
+  border-collapse: collapse;
+}
+
+th,
+td {
+  border: 1px solid #000000;
+  text-align: center;
+  padding: 3px;
+}
+
+.sale-box {
+  .sale-title {
+    font-size: 16px;
+    margin-bottom: 5px;
+    text-align: start;
+  }
+  .sale-content {
+    font-size: 16px;
+    min-height: 60px;
+    margin-bottom: 10px;
+    text-align: start;
+    padding: 0 8px;
+  }
+  .sale-bottom {
+    display: flex;
+    justify-content: flex-end;
+    margin-top: 10px;
+  }
+  .desc {
+    margin-right: 20px;
+  }
+}
+</style>

+ 451 - 0
src/views/statistic/inspectionReport/FillCom.vue

@@ -0,0 +1,451 @@
+<template>
+  <div>
+    <el-scrollbar>
+      <el-drawer
+        v-model="drawerVisible"
+        size="800"
+        :close-on-click-modal="false"
+      >
+        <template #header>
+          <h4>填报检验报告</h4>
+        </template>
+        <template #default>
+          <el-form
+            ref="baseInfoRef"
+            :model="saleForm"
+            label-width="120"
+            :rules="rules"
+            style="max-width: 600px"
+          >
+            <el-form-item label="抽样数量" prop="seqNum">
+              <el-input v-model="saleForm.seqNum" />
+            </el-form-item>
+            <el-form-item label="温度" prop="temperature">
+              <el-input v-model="saleForm.temperature"></el-input>
+            </el-form-item>
+            <el-form-item label="湿度" prop="humidity">
+              <el-input v-model="saleForm.humidity" />
+            </el-form-item>
+            <el-form-item label="检验依据/技术要求" prop="pursuant">
+              <el-input v-model="saleForm.pursuant" />
+            </el-form-item>
+            <el-form-item label="主要测试仪器及设备" prop="device">
+              <el-input v-model="saleForm.device" />
+            </el-form-item>
+
+            <!-- 动态增删的表单部分 -->
+            <div class="dynamic-form-section">
+              <div
+                v-for="(item, index) in projectInfo"
+                :key="index"
+                class="dynamic-item"
+              >
+                <el-form-item
+                  :label="'检验项目' + (index + 1)"
+                  :prop="'projectInfo.' + index + '.checkProject'"
+                >
+                  <el-input
+                    v-model="item.checkProject"
+                    placeholder="请输入检验项目"
+                  />
+                </el-form-item>
+
+                <el-form-item
+                  :label="'检验要求' + (index + 1)"
+                  :prop="'projectInfo.' + index + '.checkRequired'"
+                >
+                  <el-input
+                    v-model="item.checkRequired"
+                    placeholder="请输入检验要求"
+                  />
+                </el-form-item>
+
+                <el-form-item
+                  :label="'检验结果/实测数据' + (index + 1)"
+                  :prop="'projectInfo.' + index + '.checkResult'"
+                >
+                  <el-input
+                    v-model="item.checkResult"
+                    placeholder="请输入检验结果"
+                  />
+                </el-form-item>
+
+                <div class="item-actions">
+                  <el-button
+                    type="danger"
+                    @click="removeItem(index)"
+                    :disabled="projectInfo.length <= 1"
+                    size="small"
+                  >
+                    删除
+                  </el-button>
+                  <el-button
+                    type="primary"
+                    @click="addItem"
+                    v-if="index === projectInfo.length - 1"
+                    size="small"
+                  >
+                    添加
+                  </el-button>
+                </div>
+              </div>
+            </div>
+          </el-form>
+
+          <el-form
+            ref="remark1Ref"
+            :model="remark1"
+            label-width="120"
+            :rules="rules1"
+            style="max-width: 600px"
+          >
+            <el-form-item label="检验结论" prop="content">
+              <el-input v-model="remark1.content" type="textarea" />
+            </el-form-item>
+            <el-form-item label="填表人" prop="user">
+              <el-tree-select
+                v-model="remark1.user"
+                :data="userList"
+                filterable
+              />
+            </el-form-item>
+            <el-form-item label="检验人" prop="user1">
+              <el-tree-select
+                v-model="remark1.user1"
+                :data="userList"
+                filterable
+              />
+            </el-form-item>
+            <el-form-item label="日期" prop="time">
+              <el-date-picker
+                v-model="remark1.time"
+                type="date"
+                placeholder="请选择日期"
+                clearable
+                format="YYYY年MM月DD日"
+                value-format="YYYY年MM月DD日"
+                style="width: 100%"
+              />
+            </el-form-item>
+            <el-form-item label="备注" prop="notes">
+              <el-input v-model="remark1.notes" type="textarea" />
+            </el-form-item>
+            <el-form-item label="下一步处理人" prop="nextRemarkUser">
+              <el-tree-select
+                v-model="remark1.nextRemarkUser"
+                :data="userList"
+                filterable
+              />
+            </el-form-item>
+          </el-form>
+        </template>
+        <template #footer>
+          <div class="drawer-bottom">
+            <el-button @click="cancelClick">取消</el-button>
+            <el-button type="primary" @click="confirmClick">提交</el-button>
+          </div>
+        </template>
+      </el-drawer>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script setup>
+import {
+  updateInspectionData,
+  getInspectionDetail,
+} from "@/api/statistic/inspectionReport";
+import { getOrderList } from "@/api/sales/index";
+import { getUserTree } from "@/api/user/index";
+import { useDictionaryStore } from "@/store";
+import { ref, reactive } from "vue";
+
+const drawerVisible = ref(false);
+const disabledDate = (time) => {
+  return time.getTime() < Date.now() - 8.64e7;
+};
+const disabledDate1 = (time) => {
+  return time.getTime() > Date.now() - 8.64e7;
+};
+
+const { dicts } = useDictionaryStore();
+const auditTypes = dicts.unqualified_audit_type;
+const levelTypes = ref([
+  { dictValue: "0", dictLabel: "一级" },
+  { dictValue: "1", dictLabel: "二级" },
+]);
+
+const baseInfoRef = ref(null);
+const saleForm = reactive({
+  id: "",
+  seqNum: "",
+  temperature: "",
+  humidity: "",
+  pursuant: "",
+  device: "",
+});
+
+// 动态表单数据
+const projectInfo = ref([
+  {
+    checkProject: "",
+    checkRequired: "",
+    checkResult: "",
+  },
+]);
+
+// 动态表单验证规则
+const dynamicRules = {
+  checkProject: [
+    { required: true, message: "请输入检验项目", trigger: "blur" },
+  ],
+  checkRequired: [
+    { required: true, message: "请输入检验要求", trigger: "blur" },
+  ],
+  checkResult: [{ required: true, message: "请输入检验结果", trigger: "blur" }],
+};
+
+// 添加项目
+const addItem = () => {
+  projectInfo.value.push({
+    checkProject: "",
+    checkRequired: "",
+    checkResult: "",
+  });
+};
+
+// 删除项目
+const removeItem = (index) => {
+  if (projectInfo.value.length > 1) {
+    projectInfo.value.splice(index, 1);
+  }
+};
+
+// 顾客投诉或建议
+const remark1Ref = ref(null);
+
+const remark1 = reactive({
+  content: "",
+  user: "",
+  user1: "",
+  time: "",
+  notes: "",
+  nextRemarkUser: "",
+});
+
+const rules = {
+  seqNum: [{ required: true, message: "请输入抽样数量", trigger: "blur" }],
+  temperature: [{ required: true, message: "请输入温度", trigger: "blur" }],
+  humidity: [{ required: true, message: "请输入湿度", trigger: "blur" }],
+  pursuant: [{ required: true, message: "请输入检验依据", trigger: "blur" }],
+  device: [
+    { required: true, message: "请输入测试仪器及设备", trigger: "blur" },
+  ],
+};
+
+const rules1 = {
+  content: [
+    {
+      required: true,
+      message: "请输入不合格现象(质量问题)描述",
+      trigger: "blur",
+    },
+  ],
+  user: [{ required: true, message: "请输入姓名", trigger: "blur" }],
+  user1: [{ required: true, message: "检验人员不能为空", trigger: "blur" }],
+  time: [{ required: true, message: "请选择日期", trigger: "change" }],
+  nextRemarkUser: [
+    { required: true, message: "请选择处理人", trigger: "change" },
+  ],
+};
+
+// 订单相关信息
+const orders = ref([]);
+const opOptions = ref([...dicts.spc_operation]);
+const typeList = dicts.sales_info_type;
+const selectedOrder = ref({});
+const selectOrderCode = ref("");
+const getOrders = async () => {
+  const res = await getOrderList({
+    pageNo: 0,
+    pageSize: 200,
+  });
+  orders.value = res.data.records;
+  if (orders.value.length > 0) {
+    // selectOrderCode.value = orders.value[0].orderCode;
+    // selectedOrder.value = orders.value[0];
+  }
+};
+const orderSelect = (value) => {
+  selectedOrder.value = value;
+  seqList.value = selectedOrder.value.seqs.map((item, index) => ({
+    value: item,
+    label: item,
+  }));
+  saleForm.seqs = [];
+};
+
+const seqList = ref([]);
+
+// 用户信息
+const userInfo = ref({});
+const userList = ref([]);
+const getUserInfo = async () => {
+  getUserTree().then((data) => {
+    userList.value = data.data;
+  });
+};
+
+const showDrawer = (row) => {
+  getUserInfo();
+  getInspectionDetail(row.id).then((res) => {
+    saleForm.id = res.data.id;
+    saleForm.batchNum = res.data.batchNum;
+    saleForm.seqNum = res.data.seqNum;
+    saleForm.temperature = res.data.temperature;
+    saleForm.humidity = res.data.humidity;
+    saleForm.pursuant = res.data.pursuant;
+    saleForm.device = res.data.device;
+
+    // 处理动态表单数据
+    if (res.data.projectInfo) {
+      projectInfo.value = JSON.parse(res.data.projectInfo);
+    } else {
+      // 兼容旧数据
+      projectInfo.value = [
+        {
+          checkProject: res.data.checkProject || "",
+          checkRequired: res.data.checkRequired || "",
+          checkResult: res.data.checkResult || "",
+        },
+      ];
+    }
+
+    remark1.content = res.data.remark3
+      ? JSON.parse(res.data.remark3).content
+      : "";
+    remark1.user = res.data.remark3 ? JSON.parse(res.data.remark3).user : "";
+    remark1.user1 = res.data.remark3 ? JSON.parse(res.data.remark3).user1 : "";
+    remark1.time = res.data.remark3 ? JSON.parse(res.data.remark3).time : "";
+    remark1.notes = res.data.remark3 ? JSON.parse(res.data.remark3).notes : "";
+    remark1.nextRemarkUser = res.data.remark4
+      ? JSON.parse(res.data.remark4).user
+      : "";
+  });
+  drawerVisible.value = true;
+};
+
+defineExpose({ showDrawer });
+
+const cancelClick = () => {
+  drawerVisible.value = false;
+  baseInfoRef.value?.resetFields();
+  remark1Ref.value?.resetFields();
+  selectedOrder.value = {};
+  // 重置动态表单
+  projectInfo.value = [
+    {
+      checkProject: "",
+      checkRequired: "",
+      checkResult: "",
+    },
+  ];
+};
+
+const feedBackEmit = defineEmits(["finish"]);
+
+const confirmClick = () => {
+  // 验证基础表单
+  baseInfoRef.value.validate((valid) => {
+    if (!valid) {
+      return false;
+    }
+
+    // 验证动态表单
+    let dynamicValid = true;
+    projectInfo.value.forEach((item, index) => {
+      if (!item.checkProject || !item.checkRequired || !item.checkResult) {
+        dynamicValid = false;
+      }
+    });
+
+    if (!dynamicValid) {
+      ElMessage.error("请填写完整的检验项目信息");
+      return;
+    }
+
+    // 验证备注表单
+    remark1Ref.value.validate((valid) => {
+      if (valid) {
+        let remark1Copy = {
+          content: remark1.content,
+          user: remark1.user,
+          user1: remark1.user1,
+          time: remark1.time,
+          notes: remark1.notes,
+        };
+        let remark2 = {
+          content: "",
+          user2: remark1.nextRemarkUser,
+          time: "",
+        };
+
+        let p = {
+          ...saleForm,
+          state: 1, // 待处理
+          currentUserName: remark1.nextRemarkUser,
+          remark3: JSON.stringify(remark1Copy),
+          remark4: JSON.stringify(remark2),
+          projectInfo: JSON.stringify(projectInfo.value), // 保存动态表单数据
+        };
+
+        updateInspectionData(p)
+          .then(() => {
+            ElMessage.success("提交成功");
+            feedBackEmit("finish");
+            cancelClick();
+          })
+          .catch((err) => {
+            ElMessage.error("提交失败: " + err.message);
+          });
+      }
+    });
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.drawer-bottom {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  margin-top: 20px;
+}
+
+.dynamic-form-section {
+  margin: 20px 0;
+}
+
+.dynamic-item {
+  padding: 15px;
+  margin-bottom: 15px;
+  border-radius: 4px;
+  position: relative;
+
+  &:hover {
+    border-color: #c0c4cc;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  }
+}
+
+.item-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  margin-top: 10px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 18px;
+}
+</style>

+ 39 - 0
src/views/statistic/inspectionReport/detail.vue

@@ -0,0 +1,39 @@
+<template>
+  <el-drawer v-model="drawerVisible" size="1000">
+    <template #header>
+      <div><el-button type="primary" v-print="'#print'">打印</el-button></div>
+    </template>
+
+    <div id="print">
+      <DetailCom ref="detailComRef"></DetailCom>
+    </div>
+  </el-drawer>
+</template>
+
+<script setup>
+import DetailCom from "@/views/statistic/inspectionReport/DetailCom.vue";
+
+const drawerVisible = ref(false);
+const detailComRef = ref(null);
+
+const showDrawer = (row) => {
+  drawerVisible.value = true;
+
+  nextTick(() => {
+    detailComRef.value && detailComRef.value.refreshView(row);
+  });
+};
+
+defineExpose({ showDrawer });
+</script>
+
+<style scoped lang="scss">
+@media print {
+  #print {
+    position: absolute; /* 或 absolute, fixed, 根据需要调整 */
+    top: 20px; /* 调整顶部位置 */
+    margin: 0; /* 重置边距 */
+    width: 1000px;
+  }
+}
+</style>

+ 171 - 0
src/views/statistic/inspectionReport/handle4.vue

@@ -0,0 +1,171 @@
+<template>
+  <el-drawer v-model="drawerVisible" size="1000">
+    <div>
+      <DetailCom ref="detailComRef"></DetailCom>
+    </div>
+
+    <el-form
+      ref="remarkRef"
+      :model="remark"
+      label-width="150"
+      :rules="rules1"
+      style="max-width: 600px; margin-left: 80px; margin-top: 20px"
+    >
+      <!--      <el-form-item label="责任部门" prop="user">
+        <el-tree-select
+            v-model="remark.user"
+            :data="userList"
+            filterable
+        />
+      </el-form-item>-->
+      <el-form-item
+        label="下一步处理人"
+        prop="nextRemarkUser"
+        v-if="remark.state == 0"
+      >
+        <el-tree-select
+          v-model="remark.nextRemarkUser"
+          :data="userList"
+          filterable
+        />
+      </el-form-item>
+      <el-form-item label="是否通过" prop="state">
+        <el-radio-group v-model="remark.state">
+          <el-radio :value="0">同意</el-radio>
+          <el-radio :value="1">不同意</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <div class="drawer-bottom">
+        <el-button @click="cancelClick">取消</el-button>
+        <el-button type="primary" @click="confirmClick">提交</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup>
+import DetailCom from "@/views/statistic/inspectionReport/DetailCom.vue";
+import { dealFeedback } from "@/api/statistic/inspectionReport";
+import { getUserTree } from "@/api/user/index";
+import { useUserStore } from "@/store";
+import FilesUpload from "@/components/Upload/FilesUpload.vue";
+const drawerVisible = ref(false);
+const detailComRef = ref(null);
+const saleModel = ref({});
+const userStore = useUserStore();
+const disabledDate = (time) => {
+  //选择今天以及今天之后的日期
+  return time.getTime() < Date.now() - 8.64e7; //如果没有后面的-8.64e7就是不可以选择
+  //选择今天以及今天之前的日期
+  //return time.getTime() > Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择今天的
+};
+const showDrawer = (row) => {
+  drawerVisible.value = true;
+  saleModel.value = row;
+
+  nextTick(() => {
+    detailComRef.value && detailComRef.value.refreshView(row);
+  });
+
+  getUserInfo();
+};
+
+const rules1 = {
+  content: [
+    { required: true, message: "请输入投诉或建议处置意见", trigger: "blur" },
+    { min: 2, max: 200, message: "长度在 2 到 200 个字符", trigger: "blur" },
+  ],
+  user: [
+    { required: true, message: "请输入姓名", trigger: "blur" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+  time: [{ required: true, message: "请选择日期", trigger: "change" }],
+  nextRemarkUser: [
+    { required: true, message: "请选择处理人", trigger: "change" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+};
+
+defineExpose({ showDrawer });
+
+const remarkRef = ref(null);
+
+const remark = reactive({
+  content: "",
+  user: "",
+  time: "",
+  nextRemarkUser: "",
+  state: 0,
+});
+const fileNameList = ref([]);
+
+const cancelClick = () => {
+  drawerVisible.value = false;
+
+  remarkRef.value.resetFields();
+};
+
+const feedBackEmit = defineEmits(["finish"]);
+
+// 用户信息
+const userInfo = ref({});
+const userList = ref([]);
+const getUserInfo = async () => {
+  getUserTree().then((data) => {
+    userList.value = data.data;
+  });
+};
+
+const confirmClick = () => {
+  const date = new Date();
+  const now =
+    date.getFullYear() + "年" + date.getMonth() + "月" + date.getDate() + "日";
+  remark.user = userStore.user.username;
+  remarkRef.value.validate((valid) => {
+    if (valid) {
+      let remark4Copy = {
+        content: remark.state == 0 ? "同意" : "",
+        user: remark.user,
+        time: now,
+        state: remark.state,
+      };
+      let remark5 = {
+        content: "",
+        user: remark.nextRemarkUser, // qinhaibo和dengyu说是user1234的
+        time: "",
+      };
+
+      let p = {
+        id: saleModel.value.id,
+        remark4: JSON.stringify(remark4Copy),
+        remark5: JSON.stringify(remark5),
+        currentUserName: remark.nextRemarkUser,
+        state: remark.state,
+        step: 2, //步骤 2 审核人 3 批准人
+      };
+
+      dealFeedback(p).then(() => {
+        feedBackEmit("finish");
+        cancelClick();
+      });
+    } else {
+      console.log("remarkRef invalid");
+      return false;
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+@media print {
+  #print {
+    position: absolute; /* 或 absolute, fixed, 根据需要调整 */
+    top: 20px; /* 调整顶部位置 */
+    margin: 0; /* 重置边距 */
+    width: 1000px;
+  }
+}
+</style>

+ 170 - 0
src/views/statistic/inspectionReport/handle5.vue

@@ -0,0 +1,170 @@
+<template>
+  <el-drawer v-model="drawerVisible" size="1000">
+    <div>
+      <DetailCom ref="detailComRef"></DetailCom>
+    </div>
+
+    <el-form
+      ref="remarkRef"
+      :model="remark"
+      label-width="150"
+      :rules="rules1"
+      style="max-width: 600px; margin-left: 80px; margin-top: 20px"
+    >
+      <el-form-item label="是否通过" prop="state">
+        <el-radio-group v-model="remark.state">
+          <el-radio :value="0">同意</el-radio>
+          <el-radio :value="1">不同意</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <div class="drawer-bottom">
+        <el-button @click="cancelClick">取消</el-button>
+        <el-button type="primary" @click="confirmClick">提交</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup>
+import DetailCom from "@/views/statistic/inspectionReport/DetailCom.vue";
+import {
+  dealFeedback,
+  getSignaturebyName,
+} from "@/api/statistic/inspectionReport";
+import { useUserStore } from "@/store";
+import FilesUpload from "@/components/Upload/FilesUpload.vue";
+const drawerVisible = ref(false);
+const detailComRef = ref(null);
+const saleModel = ref({});
+const userStore = useUserStore();
+const disabledDate = (time) => {
+  //选择今天以及今天之后的日期
+  return time.getTime() < Date.now() - 8.64e7; //如果没有后面的-8.64e7就是不可以选择
+  //选择今天以及今天之前的日期
+  //return time.getTime() > Date.now() - 8.64e7;//如果没有后面的-8.64e7就是不可以选择今天的
+};
+const showDrawer = (row) => {
+  drawerVisible.value = true;
+  saleModel.value = row;
+
+  nextTick(() => {
+    detailComRef.value && detailComRef.value.refreshView(row);
+  });
+
+  getUserInfo();
+};
+
+const getSignature = async (name) => {
+  const res = await getSignaturebyName(name);
+  return res.data;
+};
+
+const rules1 = {
+  content: [
+    { required: true, message: "请输入投诉或建议处置意见", trigger: "blur" },
+    { min: 2, max: 200, message: "长度在 2 到 200 个字符", trigger: "blur" },
+  ],
+  user: [
+    { required: true, message: "请输入姓名", trigger: "blur" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+  time: [{ required: true, message: "请选择日期", trigger: "change" }],
+  nextRemarkUser: [
+    { required: true, message: "请选择处理人", trigger: "change" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+};
+
+defineExpose({ showDrawer });
+
+const remarkRef = ref(null);
+
+const remark = reactive({
+  content: "",
+  user: "",
+  time: "",
+  nextRemarkUser: "",
+  state: 0,
+  fileUrl: "",
+});
+const fileNameList = ref([]);
+
+const cancelClick = () => {
+  drawerVisible.value = false;
+
+  remarkRef.value.resetFields();
+};
+
+const feedBackEmit = defineEmits(["finish"]);
+
+// 用户信息
+// const userInfo = ref({});
+// const userList = ref([]);
+// const getUserInfo = async () => {
+//   getUserTree().then((data) => {
+//     userList.value = data.data;
+//   });
+// };
+
+const confirmClick = async () => {
+  // 改为async函数
+  const date = new Date();
+  const now =
+    date.getFullYear() +
+    "年" +
+    (date.getMonth() + 1) +
+    "月" +
+    date.getDate() +
+    "日";
+  remark.user = userStore.user.username;
+
+  try {
+    const valid = await remarkRef.value.validate(); // 使用await验证表单
+    if (!valid) {
+      console.log("remarkRef invalid");
+      return false;
+    }
+
+    let queryName = "质量检验签章";
+    let prefix = import.meta.env.VITE_APP_MES_FILE_URL.replace(/'/g, "");
+
+    let remark5Copy = {
+      content: remark.state == 0 ? "同意" : "",
+      user: remark.user,
+      time: now,
+      state: remark.state,
+      sinature:
+        remark.state == 0 ? prefix + (await getSignature(queryName)) : "",
+    };
+
+    let p = {
+      id: saleModel.value.id,
+      remark5: JSON.stringify(remark5Copy),
+      state: remark.state,
+      currentUserName: remark.nextRemarkUser,
+      step: 3, //步骤 2 审核人 3 批准人
+    };
+
+    // 等待反馈处理完成
+    await dealFeedback(p);
+    feedBackEmit("finish");
+    cancelClick();
+  } catch (error) {
+    console.error("处理过程中出错:", error);
+  }
+};
+</script>
+
+<style scoped lang="scss">
+@media print {
+  #print {
+    position: absolute; /* 或 absolute, fixed, 根据需要调整 */
+    top: 20px; /* 调整顶部位置 */
+    margin: 0; /* 重置边距 */
+    width: 1000px;
+  }
+}
+</style>

+ 269 - 85
src/views/statistic/inspectionReport/index.vue

@@ -1,99 +1,283 @@
 <template>
-  <div class="mainContentBox">
-    <avue-crud
-      ref="crudRef"
-      v-model:search="search"
-      v-model="form"
-      :data="data"
-      :option="option"
-      v-model:page="page"
-      @row-save="createRow"
-      @row-update="updateRow"
-      @row-del="deleteRow"
-      @search-change="searchChange"
-      @search-reset="resetChange"
-      @size-change="dataList"
-      @current-change="dataList"
-      @selection-change="selectionChange"
-    >
-      <template #menu="{ row }">
-        <el-button link icon="el-icon-copy-document" @click="onClickEdit(row)"
-          >填报</el-button
+  <div class="container2" id="shijian">
+    <el-tabs v-model="activeName" type="card">
+      <el-tab-pane label="待处理" name="no" />
+      <el-tab-pane label="审核完成" name="ok" />
+    </el-tabs>
+    <div class="header">
+      <Search
+        :searchOptions="searchForm"
+        :keepState="true"
+        ref="searchRef"
+        @dataList="getData"
+      />
+    </div>
+    <div class="table">
+      <el-table :data="tableData" border>
+        <el-table-column prop="workOrderCode" label="工单编码" />
+        <el-table-column prop="materialModel" label="产品规格" />
+        <el-table-column prop="orderCode" label="订单编码" />
+        <el-table-column prop="orderName" label="订单名称" />
+        <el-table-column prop="materialCode" label="物料编号" />
+        <el-table-column prop="materialName" label="物料名称" />
+        <el-table-column
+          prop="currentUserName"
+          label="审核人"
+          overhidden="true"
+        />
+        <el-table-column prop="form_type" label="随工单类型" />
+        <el-table-column prop="state" label="审核状态">
+          <template #default="{ row }">
+            <div :style="{ color: getColorByState(row.state) }">
+              {{ getTextByState(row.state) }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="created" label="创建时间" />
+        <el-table-column
+          align="center"
+          width="160"
+          prop=""
+          label="操作"
+          id="opear"
         >
-      </template>
-    </avue-crud>
-
-    <IRTableDialog ref="IRTableDialogRef"></IRTableDialog>
+          <template #default="{ row }">
+            <el-button
+              v-if="row.state === 0 || row.state === 2"
+              type="primary"
+              link
+              @click="handleFill(row)"
+              >填报</el-button
+            >
+            <el-button
+              link
+              v-if="
+                row.state === 1 &&
+                userStore.user.username == row.currentUserName
+              "
+              @click="handle(row)"
+              type="primary"
+              >处理</el-button
+            >
+            <el-button link @click="toDetail(row)" type="primary"
+              >详情</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <Pagination
+        :total="currentOption.total"
+        :page="currentOption.page"
+        :limit="currentOption.limit"
+        :pageSizes="currentOption.pageSizes"
+        v-model:page="currentOption.page"
+        v-model:limit="currentOption.limit"
+        @pagination="getData"
+      />
+      <FillCom ref="fillRef" @finish="getData"></FillCom>
+      <Detail ref="detailRef"></Detail>
+      <Handle4 ref="handle4Ref" @finish="getData"></Handle4>
+      <Handle5 ref="handle5Ref" @finish="getData"></Handle5>
+    </div>
   </div>
 </template>
 
 <script setup>
-import { ref, getCurrentInstance } from "vue";
-import { useCrud } from "@/hooks/userCrud";
-import IRTableDialog from "@/views/statistic/inspectionReport/IRTableDialog.vue";
+import Search from "@/components/Search/index.vue";
+import { useSpcStore, useUserStore, useDictionaryStore } from "@/store";
+const { dicts } = useDictionaryStore();
+import { getInspectionData } from "@/api/statistic/inspectionReport.ts";
+import Detail from "@/views/statistic/inspectionReport/detail.vue";
+import FillCom from "@/views/statistic/inspectionReport/FillCom.vue";
+import Handle4 from "@/views/statistic/inspectionReport/handle4.vue";
+import Handle5 from "@/views/statistic/inspectionReport/handle5.vue";
 
-// 传入一个url,后面不带/
-const { form, data, option, search, page, toDeleteIds, Methords, Utils } =
-  useCrud({
-    src: "/api/v1/api/v1/inspectionReport",
-  });
-const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } =
-  Methords; //增删改查
-const { selectionChange, multipleDelete } = Methords; //选中和批量删除事件
-const { checkBtnPerm, downloadTemplate, exportData } = Utils; //按钮权限等工具
-const crudRef = ref(null); //crudRef.value 获取avue-crud对象
+defineOptions({
+  name: "SPCInspectionReport",
+});
+const userStore = useUserStore();
+const auditTypes = dicts.unqualified_audit_type;
+const fillRef = ref(null);
 
-onMounted(() => {
-  // console.log("crudRef", crudRef)
-  dataList();
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
 });
+const tableData = ref([]);
+const searchRef = ref(null);
+const searchForm = [
+  {
+    label: "工单号",
+    prop: "workOrderCode",
+    type: "input",
+  },
+  {
+    label: "订单号",
+    prop: "orderCode",
+    type: "input",
+  },
+  {
+    label: "物料型号",
+    prop: "materialModel",
+    type: "input",
+  },
+];
+
+const getData = async (obj) => {
+  const { data, code } = await getInspectionData({
+    ...searchRef.value.searchForm,
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+  });
+  if (code == "200") {
+    tableData.value = data.records;
+    currentOption.total = data.totalCount;
+  }
+};
 
-const IRTableDialogRef = ref(null);
-const onClickEdit = (row) => {
-  IRTableDialogRef.value && IRTableDialogRef.value.openPrintDialog(row);
+const handleFill = (row) => {
+  fillRef.value && fillRef.value.showDrawer(row); // 填报
 };
 
-// 设置表格列或者其他自定义的option
-option.value = Object.assign(option.value, {
-  searchEnter: true,
-  viewBtn: false,
-  addBtn: false,
-  editBtn: false,
-  delBtn: false,
-  column: [
-    {
-      label: "工单编码",
-      prop: "workOrderCode",
-    },
-    {
-      label: "产品规格",
-      prop: "materialModel",
-    },
-    {
-      label: "订单编码",
-      prop: "orderCode",
-    },
-    {
-      label: "订单名称",
-      prop: "orderName",
-    },
-    {
-      label: "物料编号",
-      prop: "materialCode",
-    },
-    {
-      label: "物料名称",
-      prop: "materialName",
-    },
-    {
-      label: "随工单类型",
-      prop: "formName",
-    },
-    {
-      label: "创建日期",
-      prop: "created",
-      type: "datetime",
-    },
-  ],
+// const toAdd = () => {
+//   addRef.value && addRef.value.showDrawer();
+// };
+
+// const toDelete = async (row) => {
+//   const res = await deleteFeedback(row.id);
+//   currentOption.page = 1;
+//   getData();
+// const { code } = await deleteData({ id });
+// if (code == "200") {
+//   ElMessage.success("操作成功");
+//   getData();
+// }
+// };
+
+// 处理相关
+const handle4Ref = ref(null);
+const handle5Ref = ref(null);
+
+const handle = (row) => {
+  console.log("row:", row);
+  if (row.remark4 && !JSON.parse(row.remark4).content) {
+    handle4Ref.value && handle4Ref.value.showDrawer(row); // 处理
+  } else if (row.remark5 && !JSON.parse(row.remark5).content) {
+    handle5Ref.value && handle5Ref.value.showDrawer(row); // 处理
+  } else {
+    ElMessage.info("该工单正在处理中。。。");
+  }
+};
+
+// 详情
+const detailRef = ref(null);
+const toDetail = (row) => {
+  detailRef.value && detailRef.value.showDrawer(row); // 详情
+};
+const getTextByAuditType = (state) => {
+  if (state) {
+    const item = auditTypes.filter((v) => {
+      console.log(
+        "dictValue:" +
+          v.dictValue +
+          ",state:" +
+          state +
+          "..." +
+          (v.dictValue === state)
+      );
+      return v.dictValue === state;
+    });
+    if (item.length > 0) {
+      return item[0].dictLabel;
+    }
+  }
+};
+// 审核状态0待处理 1 审核中 2 审核驳回 3 审核完成
+const getTextByState = (state) => {
+  switch (state) {
+    case 0:
+      return "待处理";
+    case 1:
+      return "审核中";
+    case 2:
+      return "审核驳回";
+    case 3:
+      return "审核完成";
+    default:
+      return "";
+  }
+};
+const getColorByState = (state) => {
+  switch (state) {
+    case 0:
+      return "#59acbb";
+    case 1:
+      return "#bba559";
+    case 2:
+      return "#d96868";
+    case 3:
+      return "#67c23a";
+    default:
+      return "";
+  }
+};
+const activeName = ref("no");
+
+onMounted(() => {
+  searchRef.value.searchForm.state = "0";
+  getData();
 });
+watch(
+  () => activeName.value,
+  (val) => {
+    if (val == "ok") {
+      searchRef.value.searchForm.state = "3";
+    } else {
+      searchRef.value.searchForm.state = "0";
+    }
+    getData();
+  }
+);
 </script>
+
+<style scoped lang="scss">
+.container2 {
+  background-color: white;
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  .header {
+    width: 100%;
+    height: auto;
+  }
+  .table {
+    flex: 1;
+    padding-bottom: 20px;
+  }
+  .formView {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .formTitle {
+      text-align: center;
+      font-size: 20px;
+      margin-bottom: 20px;
+    }
+    .formStyle {
+      width: 400px;
+    }
+  }
+}
+</style>
+<style lang="scss">
+#shijian {
+  .is-active {
+    background-color: #f5f7fa;
+  }
+}
+</style>