已添加8个文件
已修改6个文件
1880 ■■■■■ 文件已修改
src/api/qualityManagement/inspectItem.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/rawMaterial.js 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/formDia.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/New.vue 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Qualified.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Subtract.vue 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/InspectItem/index.vue 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/components/filesDia.vue 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/components/formDia.vue 372 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/components/inspectionFormDia.vue 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/components/itemSelect.vue 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/index.vue 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/inspectItem.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
import request from "@/utils/request";
export function qualityInspectItemListPage(data) {
  return request({
    url: "/qualityInspectItem/listPage",
    method: "post",
    data,
  });
}
export function qualityInspectItemSave(data) {
  return request({
    url: "/qualityInspectItem/save",
    method: "post",
    data,
  });
}
export function qualityInspectItemDelete(data) {
  return request({
    url: "/qualityInspectItem/delete",
    method: "post",
    data,
  });
}
src/api/qualityManagement/rawMaterial.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
import request from '@/utils/request'
// æŸ¥è¯¢åŽŸæ£€åˆ—è¡¨
export function findRawMaterialListPage(query) {
    return request({
        url: '/quality/rawMaterial/listPage',
        method: 'get',
        params: query,
    })
}
// æ–°å¢žåŽŸæ£€
export function createRawMaterial(data) {
    return request({
        url: '/quality/rawMaterial',
        method: 'post',
        data: data,
    })
}
// ä¿®æ”¹åŽŸæ£€
export function updateRawMaterial(data) {
    return request({
        url: '/quality/rawMaterial',
        method: 'put',
        data: data,
    })
}
// åˆ é™¤åŽŸæ£€
export function deleteRawMaterial(query) {
    return request({
        url: '/quality/rawMaterial',
        method: 'delete',
        data: query,
    })
}
src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -11,8 +11,19 @@
        <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="taskId">
                        <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                    <el-form-item label="任务名称" prop="taskName">
                        <el-input v-model="form.taskName" placeholder="请输入任务名称" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="deviceIds">
                        <el-select
                            v-model="form.deviceIds"
                            multiple
                            filterable
                            placeholder="请选择设备"
                            @change="handleDeviceChange"
                        >
                            <el-option
                                v-for="(item, index) in deviceOptions"
                                :key="index"
@@ -22,17 +33,7 @@
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="规格型号">
                        <el-input
                            v-model="form.deviceModel"
                            placeholder="请输入规格型号"
                            disabled
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="录入人" prop="inspector">
                        <el-select
@@ -151,7 +152,8 @@
const data = reactive({
    form: {
        taskId: undefined,
        taskName: undefined,
        deviceIds: [],
        taskName: "",
        // å½•入人:单选一个用户 id
        inspector: undefined,
        remarks: '',
@@ -163,7 +165,8 @@
        registrationDate: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        deviceIds: [{ required: true, message: "请选择设备", trigger: "change" }],
        taskName: [{ required: true, message: "请输入任务名称", trigger: "blur" }],
        inspector: [{ required: true, message: "请选择录入人", trigger: "blur" },],
        registrationDate: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
@@ -176,14 +179,32 @@
    deviceOptions.value = data;
};
// é€‰æ‹©è®¾å¤‡æ—¶ï¼Œå›žå¡«è®¾å¤‡åç§°(taskName)和规格型号(deviceModel)
const setDeviceModel = (id) => {
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskId = option.id;
        form.value.taskName = option.deviceName;
        form.value.deviceModel = option.deviceModel;
const normalizeIdList = (val) => {
    if (!val) return []
    if (Array.isArray(val)) return val
    if (typeof val === 'string') {
        const s = val.trim()
        // JSON å­—符串:"[1,2,3]"
        if (s.startsWith('[') && s.endsWith(']')) {
            try {
                const arr = JSON.parse(s)
                return Array.isArray(arr) ? arr : []
            } catch {
                return []
            }
        }
        // é€—号分隔:"1,2,3"
        return s.split(',').map(v => v.trim()).filter(Boolean)
    }
    return []
}
// é€‰æ‹©è®¾å¤‡æ—¶ï¼Œé¡ºå¸¦å›žå¡«è§„格型号(多选时取第一个设备的规格型号)
const handleDeviceChange = () => {
    const selectedIds = form.value.deviceIds || []
    const firstId = Array.isArray(selectedIds) ? selectedIds[0] : undefined
    const firstDevice = deviceOptions.value.find(d => String(d.id) === String(firstId))
    form.value.deviceModel = firstDevice?.deviceModel || firstDevice?.model || form.value.deviceModel
}
// æ‰“开弹框
@@ -203,16 +224,19 @@
    await loadDeviceName();
    
    if (type === 'edit' && row) {
        form.value = { ...row }
        form.value = { ...form.value, ...row }
        // ç¼–辑时用接口返回的 registrantId å›žæ˜¾å½•入人
        if (row.registrantId) {
            form.value.inspector = row.registrantId
        }
        // å¦‚果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
        // ç¼–辑回显:deviceIds å¯èƒ½æ˜¯ JSON å­—符串 / é€—号分隔 / æ•°ç»„
        const ids = normalizeIdList(row.deviceIds ?? row.taskIds)
        form.value.deviceIds = ids.map(v => {
            const n = Number(v)
            return Number.isNaN(n) ? v : n
        })
        handleDeviceChange()
    } else if (type === 'add') {
        // æ–°å¢žæ—¶è®¾ç½®ç™»è®°æ—¥æœŸä¸ºå½“天
        form.value.registrationDate = getCurrentDate();
@@ -236,8 +260,8 @@
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: undefined,
        deviceIds: [],
        taskName: "",
        inspector: undefined,
        remarks: '',
        frequencyType: '',
@@ -255,6 +279,8 @@
        if (valid) {
            try {
                const payload = { ...form.value }
                // åŽç«¯è¦æ±‚:deviceIds ä»¥ JSON.stringify([1,2,3]) å½¢å¼ä¼ å‚
                payload.deviceIds = JSON.stringify(form.value.deviceIds || [])
                // ä¸å†å‘后端传保养人字段,仅使用接口要求的 registrant / registrantId
                // æ ¹æ®é€‰æ‹©çš„"录入人"设置 registrant / registrantId
                if (payload.inspector) {
@@ -268,6 +294,7 @@
                }
                delete payload.inspector
                delete payload.inspectorIds
                delete payload.taskIds
                
                if (payload.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
src/views/equipmentManagement/upkeep/index.vue
@@ -16,7 +16,7 @@
                        :prefix-icon="Search"
                        @change="getScheduledTableData" />
            </el-form-item>
            <el-form-item label="任务状态">
            <!-- <el-form-item label="任务状态">
              <el-select v-model="scheduledFilters.status"
                         placeholder="请选择任务状态"
                         clearable
@@ -26,7 +26,7 @@
                <el-option label="停用"
                           value="0" />
              </el-select>
            </el-form-item>
            </el-form-item> -->
            <el-form-item>
              <el-button type="primary"
                         @click="getScheduledTableData">搜索</el-button>
@@ -313,10 +313,11 @@
  // å®šæ—¶ä»»åŠ¡ç®¡ç†è¡¨æ ¼åˆ—é…ç½®
  const scheduledColumns = ref([
    { prop: "taskName", label: "设备名称" },
    { prop: "taskName", label: "任务名称" },
    {
      label: "规格型号",
      prop: "deviceModel",
      prop: "deviceName",
      label: "设备",
      minWidth: 180
    },
    {
      prop: "frequencyType",
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -73,6 +73,18 @@
        <el-table-column label="入库数量"
                         prop="stockInNum"
                         show-overflow-tooltip/>
        <el-table-column label="车牌号"
                         prop="licensePlateNo"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="毛重(吨)"
                         prop="grossWeight"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="皮重(吨)"
                         prop="tareWeight"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="净重(吨)"
                         prop="netWeight"
                         v-if="type === '0'"
@@ -91,6 +103,10 @@
                         prop="weighingDate"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="过磅员"
                         prop="weighingOperator"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
src/views/inventoryManagement/stockManagement/New.vue
@@ -37,21 +37,57 @@
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <!-- productType === 0:原材料 -->
        <el-form-item
            label="库存数量"
            prop="qualitity"
            v-if="type === 'qualified' && formState.productType === 0"
            label="车牌号"
            prop="licensePlateNo"
        >
          <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" />
          <el-input v-model="formState.licensePlateNo" />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified'"
            label="库存预警数量"
            prop="warnNum"
            v-if="type === 'qualified' && formState.productType === 0"
            label="毛重(吨)"
            prop="grossWeight"
        >
          <el-input-number v-model="formState.warnNum" :step="1" :min="0" :max="formState.qualitity" style="width: 100%" />
          <el-input-number
              v-model="formState.grossWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <!-- productType === 0:原材料 -->
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="皮重(吨)"
            prop="tareWeight"
        >
          <el-input-number
              v-model="formState.tareWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="净重(吨)"
            prop="netWeight"
        >
          <el-input-number
              v-model="formState.netWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              disabled
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="过磅日期"
@@ -70,10 +106,10 @@
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="净重(吨)"
            prop="netWeight"
            label="过磅员"
            prop="weighingOperator"
        >
          <el-input-number v-model="formState.netWeight" :step="0.01" :min="0" style="width: 100%" />
          <el-input v-model="formState.weighingOperator" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
@@ -125,11 +161,14 @@
  productName: "",
  productModelName: "",
  unit: "",
  weighingDate: undefined,
  productType: undefined,
  qualitity: 0,
  warnNum: 0,
  // è¿‡ç£…相关字段(仅原材料合格品使用)
  licensePlateNo: "",
  grossWeight: undefined,
  tareWeight: undefined,
  netWeight: undefined,
  weighingDate: undefined,
  weighingOperator: "",
  remark: '',
});
@@ -161,6 +200,8 @@
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  formState.value.weighingDate = undefined;
  formState.value.grossWeight = undefined;
  formState.value.tareWeight = undefined;
  formState.value.netWeight = undefined;
  if (products && products.length > 0) {
    const product = products[0];
@@ -176,6 +217,19 @@
  }
};
// å‡€é‡ = æ¯›é‡ - çš®é‡
const computeNetWeight = () => {
  const { grossWeight, tareWeight } = formState.value;
  if (grossWeight != null && tareWeight != null) {
    const net = Number(grossWeight) - Number(tareWeight);
    // ä¿ç•™ä¸¤ä½å°æ•°ï¼Œä¸”不为负
    const safeNet = Number(net.toFixed(2));
    formState.value.netWeight = safeNet > 0 ? safeNet : 0;
  } else {
    formState.value.netWeight = undefined;
  }
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -29,7 +29,7 @@
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
        <el-table-column label="库存预警数量" prop="warnNum"  show-overflow-tooltip />
        <!-- <el-table-column label="库存预警数量" prop="warnNum"  show-overflow-tooltip /> -->
        <el-table-column label="净重(吨)" prop="netWeight"  show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
src/views/inventoryManagement/stockManagement/Subtract.vue
@@ -38,25 +38,71 @@
        </el-form-item>
        <el-form-item
            label="数量"
            prop="qualitity"
        >
          <el-input-number v-model="formState.qualitity" :step="1" :min="1" :max="maxQuality" style="width: 100%" />
        </el-form-item>
        <el-form-item
            v-if="isRawMaterial"
            label="净重"
            prop="netWeight"
        >
          <el-input-number v-model="formState.netWeight" :precision="2" :step="0.01" :min="0" style="width: 100%" />
        </el-form-item>
        <el-form-item
            label="车牌号"
            prop="licensePlateNo"
        >
          <el-input v-model="formState.licensePlateNo" />
        </el-form-item>
        <el-form-item
            label="毛重(吨)"
            prop="grossWeight"
        >
          <el-input-number
              v-model="formState.grossWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            label="皮重(吨)"
            prop="tareWeight"
        >
          <el-input-number
              v-model="formState.tareWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            label="净重(吨)"
            prop="netWeight"
        >
          <el-input-number
              v-model="formState.netWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              disabled
          />
        </el-form-item>
        <el-form-item
            label="过磅日期"
            prop="weighingDate"
        >
          <el-date-picker
              style="width: 100%"
              v-model="formState.weighingDate"
              value-format="YYYY-MM-DD HH:mm:ss"
              format="YYYY-MM-DD HH:mm:ss"
              type="datetime"
              placeholder="请选择过磅日期"
              clearable
          />
        </el-form-item>
        <el-form-item
            label="过磅员"
            prop="weighingOperator"
        >
          <el-input v-model="formState.weighingOperator" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
@@ -81,7 +127,7 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {subtractStockInventory} from "@/api/inventoryManagement/stockInventory.js";
import {subtractStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
@@ -108,10 +154,6 @@
  initFormData()
})
const maxQuality = computed(() => {
  return props.record.unLockedQuantity ? props.record.unLockedQuantity :  0;
})
const isRawMaterial = computed(() => {
  return props.record.parentName === '原材料';
})
@@ -131,9 +173,13 @@
  productName: "",
  model: "",
  unit: "",
  qualitity: 0,
  // è¿‡ç£…相关字段
  licensePlateNo: "",
  grossWeight: undefined,
  tareWeight: undefined,
  netWeight: undefined,
  licensePlateNo: undefined,
  weighingDate: undefined,
  weighingOperator: "",
  remark: '',
});
@@ -158,13 +204,29 @@
    productName: "",
    model: "",
    unit: "",
    qualitity: 0,
    licensePlateNo: "",
    grossWeight: undefined,
    tareWeight: undefined,
    netWeight: undefined,
    weighingDate: undefined,
    weighingOperator: "",
    remark: '',
  };
  isShow.value = false;
};
// å‡€é‡ = æ¯›é‡ - çš®é‡
const computeNetWeight = () => {
  const { grossWeight, tareWeight } = formState.value;
  if (grossWeight != null && tareWeight != null) {
    const net = Number(grossWeight) - Number(tareWeight);
    const safeNet = Number(net.toFixed(2));
    formState.value.netWeight = safeNet > 0 ? safeNet : 0;
  } else {
    formState.value.netWeight = undefined;
  }
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
src/views/qualityManagement/InspectItem/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,239 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div style="display: flex; flex-direction: row; align-items: center;">
        <span class="search_title">检测项目:</span>
        <el-input
          v-model="searchForm.name"
          style="width: 260px"
          placeholder="请输入检测项目名称"
          clearable
          :prefix-icon="Search"
          @change="handleQuery"
          @clear="handleQuery"
        />
        <el-button type="primary" style="margin-left: 10px" @click="handleQuery">搜索</el-button>
      </div>
      <div>
        <el-button type="primary" @click="openDialog('add')">新增</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        :tableLoading="tableLoading"
        :total="page.total"
        @selection-change="handleSelectionChange"
        @pagination="pagination"
      />
    </div>
    <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '新增检测项目' : '修改检测项目'" width="520px" @close="closeDialog">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px" class="dialog-form-center">
        <el-form-item label="检测项目" prop="name">
          <el-input v-model="form.name" placeholder="请输入检测项目名称" clearable style="width: 280px" />
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input v-model="form.unit" placeholder="请输入单位" clearable style="width: 280px" />
        </el-form-item>
        <el-form-item label="标准值" prop="standardValue">
          <el-input v-model="form.standardValue" placeholder="请输入标准值" clearable style="width: 280px" />
        </el-form-item>
        <el-form-item label="内控值" prop="internalControl">
          <el-input v-model="form.internalControl" placeholder="请输入内控值" clearable style="width: 280px" />
        </el-form-item>
        <el-form-item label="化验值" prop="testValue">
          <el-input v-model="form.testValue" placeholder="请输入化验值" clearable style="width: 280px" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { getCurrentInstance, reactive, ref, toRefs } from "vue";
import {
  qualityInspectItemDelete,
  qualityInspectItemListPage,
  qualityInspectItemSave,
} from "@/api/qualityManagement/inspectItem.js";
const { proxy } = getCurrentInstance();
const data = reactive({
  searchForm: {
    name: "",
  },
  form: {
    id: null,
    name: "",
    unit: "",
    standardValue: "",
    internalControl: "",
    testValue: "",
  },
  rules: {
    name: [{ required: true, message: "请输入检测项目名称", trigger: "blur" }],
    unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
  },
});
const { searchForm, form, rules } = toRefs(data);
const tableColumn = ref([
  { label: "检测项目", prop: "name" },
  { label: "单位", prop: "unit", width: 120 },
  { label: "标准值", prop: "standardValue", width: 160 },
  { label: "内控值", prop: "internalControl", width: 160 },
  { label: "化验值", prop: "testValue", width: 160 },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 100,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => openDialog("edit", row),
      },
    ],
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const operationType = ref("add");
const page = reactive({
  current: 1,
  size: 30,
  total: 0,
});
const resetFormData = () => {
  form.value = {
    id: null,
    name: "",
    unit: "",
    standardValue: "",
    internalControl: "",
    testValue: "",
  };
};
const getList = () => {
  tableLoading.value = true;
  const params = {
    name: searchForm.value.name || null,
    current: page.current,
    size: page.size,
  };
  qualityInspectItemListPage(params)
    .then((res) => {
      const records = res?.data?.records || [];
      tableData.value = records;
      page.total = res?.data?.total || 0;
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const openDialog = (type, row) => {
  operationType.value = type;
  resetFormData();
  if (type === "edit" && row) {
    form.value = {
      id: row.id || null,
      name: row.name || "",
      unit: row.unit || "",
      standardValue: row.standardValue || "",
      internalControl: row.internalControl || "",
      testValue: row.testValue || "",
    };
  }
  dialogVisible.value = true;
};
const closeDialog = () => {
  proxy.resetForm("formRef");
  resetFormData();
  dialogVisible.value = false;
};
const submitForm = () => {
  proxy.$refs.formRef.validate((valid) => {
    if (!valid) {
      return;
    }
    qualityInspectItemSave(form.value).then(() => {
      proxy.$modal.msgSuccess(operationType.value === "add" ? "新增成功" : "修改成功");
      closeDialog();
      getList();
    });
  });
};
const handleDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const ids = selectedRows.value.map((item) => item.id);
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      qualityInspectItemDelete(ids).then(() => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
getList();
</script>
<style scoped>
.dialog-form-center {
  padding-top: 12px;
  width: fit-content;
  margin: 0 auto;
}
</style>
src/views/qualityManagement/rawMaterial/components/filesDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        title="上传附件"
        width="50%"
        @close="closeDia"
    >
      <div style="margin-bottom: 10px;text-align: right">
        <el-upload
            v-model:file-list="fileList"
            class="upload-demo"
            :action="uploadUrl"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            name="file"
            :show-file-list="false"
            :headers="headers"
            style="display: inline;margin-right: 10px"
        >
          <el-button type="primary">上传附件</el-button>
        </el-upload>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :tableLoading="tableLoading"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          @pagination="paginationSearch"
          height="500"
      >
      </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
        <filePreview ref="filePreviewRef" />
  </div>
</template>
<script setup>
import {ref} from "vue";
import filePreview from '@/components/filePreview/index.vue'
import {ElMessageBox} from "element-plus";
import {getToken} from "@/utils/auth.js";
import {
  qualityInspectFileAdd,
  qualityInspectFileDel,
  qualityInspectFileListPage
} from "@/api/qualityManagement/qualityInspectFile.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const currentId = ref('')
const selectedRows = ref([]);
const tableColumn = ref([
  {
    label: "文件名称",
    prop: "name",
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    operation: [
      {
        name: "下载",
        type: "text",
        clickFun: (row) => {
          downLoadFile(row);
        },
      },
            {
                name: "预览",
                type: "text",
                clickFun: (row) => {
                    lookFile(row);
                },
            }
    ],
  },
]);
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
const tableData = ref([]);
const fileList = ref([]);
const tableLoading = ref(false);
const filePreviewRef = ref()
const headers = ref({
  Authorization: "Bearer " + getToken(),
});
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // ä¸Šä¼ çš„图片服务器地址
// æ‰“开弹框
const openDialog = (row) => {
  dialogFormVisible.value = true;
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
  qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => {
    tableData.value = res.data.records;
        page.total = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  dialogFormVisible.value = false;
  emit('close')
};
// ä¸Šä¼ æˆåŠŸå¤„ç†
function handleUploadSuccess(res, file) {
  // å¦‚果上传成功
  if (res.code == 200) {
    const fileRow = {}
    fileRow.name = res.data.originalName
    fileRow.url = res.data.tempPath
    uploadFile(fileRow)
  } else {
    proxy.$modal.msgError("文件上传失败");
  }
}
function uploadFile(file) {
  file.inspectId = currentId.value;
  qualityInspectFileAdd(file).then(res => {
    proxy.$modal.msgSuccess("文件上传成功");
    getList()
  })
}
// ä¸Šä¼ å¤±è´¥å¤„理
function handleUploadError() {
  proxy.$modal.msgError("文件上传失败");
}
// ä¸‹è½½é™„ä»¶
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
// é¢„览附件
const lookFile = (row) => {
    filePreviewRef.value.open(row.url)
}
// åˆ é™¤
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(() => {
    qualityInspectFileDel(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    });
  }).catch(() => {
    proxy.$modal.msg("已取消");
  });
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/qualityManagement/rawMaterial/components/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,372 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增原料' : '编辑原料'"
        width="70%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="产品名称:" prop="productId">
              <el-tree-select
                  v-model="form.productId"
                  placeholder="请选择"
                  clearable
                  check-strictly
                  @change="getModels"
                  :data="productOptions"
                  :render-after-expand="false"
                  :disabled="operationType === 'edit'"
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="form.productModelId" placeholder="请选择" clearable :disabled="operationType === 'edit'"
                         filterable readonly @change="handleChangeModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="form.unit" disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="批号:" prop="batchNo">
              <el-input style="width: 100%" v-model="form.batchNo" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验类型:" prop="checkType">
              <el-select v-model="form.checkType">
                <el-option label="入厂检" :value="0"/>
                <el-option label="车间检" :value="1"/>
                <el-option label="出厂检" :value="2"/>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测结果:" prop="checkResult">
              <el-select v-model="form.checkResult">
                <el-option label="合格" :value="1"/>
                <el-option label="不合格" :value="0"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-select v-model="form.checkName" placeholder="请选择" clearable filterable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                           :value="item.nickName"/>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测日期:" prop="checkTime">
              <el-date-picker
                  v-model="form.checkTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div style="margin-bottom: 10px">
        <el-button type="primary" @click="isShowItems = true">添加检测项目</el-button>
      </div>
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          height="400"
      >
        <template #slot="{ row }">
          <el-input v-model="row.testValue" clearable/>
        </template>
      </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <item-select v-model="isShowItems" @confirm="handleItemSelect" />
  </div>
</template>
<script setup>
import {ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {userListNoPage} from "@/api/system/user.js";
import {createRawMaterial, updateRawMaterial} from "@/api/qualityManagement/rawMaterial.js";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
const {proxy} = getCurrentInstance()
const emit = defineEmits(['close'])
const ItemSelect = defineAsyncComponent(() => import("@/views/qualityManagement/rawMaterial/components/itemSelect.vue"));
const dialogFormVisible = ref(false);
const operationType = ref('')
const isShowItems = ref(false)
const data = reactive({
  form: {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
    checkResult: "",
  },
  rules: {
    checkTime: [{required: true, message: "请输入", trigger: "blur"},],
    supplier: [{required: true, message: "请输入", trigger: "blur"}],
    checkName: [{required: false, message: "请输入", trigger: "blur"}],
    productId: [{required: true, message: "请输入", trigger: "blur"}],
    productModelId: [{required: true, message: "请选择产品型号", trigger: "change"}],
    batchNo: [{required: false, message: "请输入批次", trigger: "blur"}],
    unit: [{required: false, message: "请输入", trigger: "blur"}],
    quantity: [{required: true, message: "请输入", trigger: "blur"}],
    checkCompany: [{required: false, message: "请输入", trigger: "blur"}],
    checkResult: [{required: true, message: "请选择检测结果", trigger: "change"}],
  },
});
const tableColumn = ref([
  {
    label: "指标",
    prop: "parameterItem",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "标准值",
    prop: "standardValue",
  },
  {
    label: "内控值",
    prop: "controlValue",
  },
  {
    label: "化验值",
    prop: "testValue",
    dataType: 'slot',
    slot: 'slot',
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const {form, rules} = toRefs(data);
const userList = ref([]);
const productOptions = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
const modelOptions = ref([]);
// æ‰“开弹框
const openDialog = async (type, row) => {
  operationType.value = type;
  userListNoPage().then(res => {
    userList.value = res.data || [];
  })
  // å…ˆé‡ç½®è¡¨å•数据(保持字段完整,避免弹窗首次渲染时触发必填红框“闪一下”)
    form.value = {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
    checkResult: "",
  }
  testStandardOptions.value = [];
  tableData.value = [];
  // å…ˆç¡®ä¿äº§å“æ ‘已加载,否则编辑时产品/规格型号无法反显
  await getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
    currentProductId.value = row.productId || 0
    // å…³é”®ï¼šç¼–辑时加载规格型号下拉选项,才能反显 productModelId
    if (currentProductId.value) {
      try {
        const res = await modelList({ id: currentProductId.value });
        modelOptions.value = res || [];
        // åŒæ­¥å›žå¡« model / unit(有些接口返回的 row é‡Œå¯èƒ½æ²¡å¸¦å…¨ï¼‰
        if (form.value.productModelId) {
          handleChangeModel(form.value.productModelId);
        }
      } catch (e) {
        console.error("加载规格型号失败", e);
        modelOptions.value = [];
      }
    }
    // ç¼–辑模式下,先加载指标选项,然后加载参数列表
    if (currentProductId.value) {
    } else {
      getQualityInspectParamList(row.id);
    }
  }
  // æœ€åŽå†æ‰“开弹窗,并清理校验态,避免必填提示闪烁
  dialogFormVisible.value = true;
  nextTick(() => {
    proxy.$refs?.formRef?.clearValidate?.();
  });
}
const getProductOptions = () => {
  return productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
    return productOptions.value;
  });
};
const getModels = (value) => {
  form.value.productModelId = undefined;
  form.value.unit = undefined;
  modelOptions.value = [];
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  })
  if (currentProductId.value) {
    getList();
  }
};
const handleItemSelect = (value) => {
  tableData.value.push(...value)
}
const handleChangeModel = (value) => {
  form.value.model = modelOptions.value.find(item => item.id == value)?.model || '';
  form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || '';
}
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
};
function convertIdToValue(data) {
  return data.map((item) => {
    const {id, children, ...rest} = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 0
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
      const data = {...form.value, qualityInspectParams: tableData.value}
      if (operationType.value === "add") {
        createRawMaterial(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        updateRawMaterial(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      }
    }
  })
}
const getList = () => {
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
  let params = {
    productId: currentProductId.value,
  }
  qualityInspectDetailByProductId(params).then(res => {
    // æ¸…空表格数据,等待用户选择指标
    tableData.value = [];
  })
}
const getQualityInspectParamList = (id) => {
  qualityInspectParamInfo(id).then(res => {
    tableData.value = res.data;
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  dialogFormVisible.value = false;
  emit('close')
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/qualityManagement/rawMaterial/components/inspectionFormDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,139 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        title="填写检验记录"
        width="70%"
        @close="closeDia"
    >
      <div style="margin-bottom: 10px;text-align: right">
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="600"
      >
        <template #slot="{ row }">
          <el-input v-model="row.testValue" clearable/>
        </template>
      </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref} from "vue";
import {Search} from "@element-plus/icons-vue";
import {
  qualityInspectParamDel,
  qualityInspectParamInfo,
  qualityInspectParamUpdate
} from "@/api/qualityManagement/qualityInspectParam.js";
import {ElMessageBox} from "element-plus";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const currentId = ref('')
const selectedRows = ref([]);
const tableColumn = ref([
  {
    label: "指标",
    prop: "parameterItem",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "标准值",
    prop: "standardValue",
  },
  {
    label: "内控值",
    prop: "controlValue",
  },
  {
    label: "化验值",
    prop: "testValue",
    dataType: 'slot',
    slot: 'slot',
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    currentId.value = row.id;
    getList()
  }
}
const getList = () => {
  qualityInspectParamInfo(currentId.value).then(res => {
    tableData.value = res.data;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  qualityInspectParamUpdate(tableData.value).then(res => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  dialogFormVisible.value = false;
  emit('close')
};
// åˆ é™¤
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(() => {
        qualityInspectParamDel(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/qualityManagement/rawMaterial/components/itemSelect.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,185 @@
<template>
  <el-dialog v-model="visible" title="选择检测项目" width="900px" destroy-on-close :close-on-click-modal="false">
    <el-form :inline="true" :model="query" class="mb-2">
      <el-form-item label="检测项目名称">
        <el-input v-model="query.name" placeholder="输入检测项目名称" clearable @keyup.enter="onSearch" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="onReset">重置</el-button>
      </el-form-item>
    </el-form>
    <!-- åˆ—表 -->
    <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        :tableLoading="loading"
        :total="total"
        @selection-change="handleSelectionChange"
        @pagination="pagination"
        @select="handleSelect"
    />
    <template #footer>
      <el-button @click="close()">取消</el-button>
      <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm">
        ç¡®å®š
      </el-button>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
import { ElMessage } from "element-plus";
import { qualityInspectItemListPage } from "@/api/qualityManagement/inspectItem.js";
export type ProductRow = {
  id: number;
  productName: string;
  model: string;
  unit?: string;
};
const props = defineProps<{
  modelValue: boolean;
  single?: boolean; // æ˜¯å¦åªèƒ½é€‰æ‹©ä¸€ä¸ªï¼Œé»˜è®¤false(可选择多个)
}>();
const emit = defineEmits(['update:modelValue', 'confirm']);
const visible = computed({
  get: () => props.modelValue,
  set: (v) => emit("update:modelValue", v),
});
const query = reactive({
  name: "",
});
const page = reactive({
  pageNum: 1,
  pageSize: 10,
});
const loading = ref(false);
const tableData = ref<ProductRow[]>([]);
const total = ref(0);
const multipleSelection = ref<ProductRow[]>([]);
const tableRef = ref();
const tableColumn = ref([
  { label: "检测项目", prop: "name" },
  { label: "单位", prop: "unit", width: 120 },
  { label: "标准值", prop: "standardValue", width: 160 },
  { label: "内控值", prop: "internalControl", width: 160 },
  { label: "化验值", prop: "testValue", width: 160 },
]);
function close() {
  visible.value = false;
}
const handleSelectionChange = (val: ProductRow[]) => {
  if (props.single && val.length > 1) {
    // å¦‚果限制为单个选择,只保留最后一个选中的
    const lastSelected = val[val.length - 1];
    multipleSelection.value = [lastSelected];
    // æ¸…空表格选中状态,然后重新选中最后一个
    nextTick(() => {
      if (tableRef.value) {
        tableRef.value.clearSelection();
        tableRef.value.toggleRowSelection(lastSelected, true);
      }
    });
  } else {
    multipleSelection.value = val;
  }
}
// å¤„理单个选择
const handleSelect = (selection: ProductRow[], row: ProductRow) => {
  if (props.single) {
    // å¦‚果限制为单个,清空其他选择,只保留当前行
    if (selection.includes(row)) {
      // é€‰ä¸­å½“前行时,清空其他选中
      multipleSelection.value = [row];
      nextTick(() => {
        if (tableRef.value) {
          tableData.value.forEach((item) => {
            if (item.id !== row.id) {
              tableRef.value.toggleRowSelection(item, false);
            }
          });
        }
      });
    }
  }
}
function onSearch() {
  page.pageNum = 1;
  loadData();
}
function onReset() {
  query.name = "";
  page.pageNum = 1;
  loadData();
}
function onPageChange() {
  loadData();
}
const pagination = (obj) => {
  page.pageNum = obj.page;
  page.pageSize = obj.limit;
  loadData();
};
function onConfirm() {
  if (multipleSelection.value.length === 0) {
    ElMessage.warning("请选择一条项目");
    return;
  }
  if (props.single && multipleSelection.value.length > 1) {
    ElMessage.warning("只能选择一个项目");
    return;
  }
  emit("confirm", props.single ? [multipleSelection.value[0]] : multipleSelection.value);
  close();
}
async function loadData() {
  loading.value = true;
  try {
    multipleSelection.value = []; // ç¿»é¡µ/搜索后清空选择更符合预期
    const res: any = await qualityInspectItemListPage({
      name: query.name.trim(),
      current: page.pageNum,
      size: page.pageSize,
    });
    tableData.value = res.data.records;
    total.value = res.data.total;
  } finally {
    loading.value = false;
  }
}
// ç›‘听弹窗打开,重置选择
watch(() => props.modelValue, (visible) => {
  if (visible) {
    multipleSelection.value = [];
  }
});
onMounted(() => {
  loadData()
})
</script>
src/views/qualityManagement/rawMaterial/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,388 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商:</span>
        <el-input
            v-model="searchForm.supplier"
            style="width: 240px"
            placeholder="请输入供应商搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange"/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索
        </el-button
        >
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
    </div>
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
               @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="检验员:" prop="checkName">
          <el-select v-model="form.checkName" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                       :value="item.nickName"/>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {Search} from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import InspectionFormDia from "@/views/qualityManagement/rawMaterial/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/rawMaterial/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {
  downloadQualityInspect,
  qualityInspectUpdate,
  submitQualityInspect
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/rawMaterial/components/filesDia.vue";
import dayjs from "dayjs";
import {userListNoPage} from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import {deleteRawMaterial, findRawMaterialListPage} from "@/api/qualityManagement/rawMaterial.js";
const data = reactive({
  searchForm: {
    supplier: "",
    entryDate: undefined, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  rules: {
    checkName: [{required: true, message: "请选择", trigger: "change"}],
  },
});
const {searchForm, rules} = toRefs(data);
const tableColumn = ref([
  {
    label: "检测日期",
    prop: "checkTime",
    width: 120
  },
  {
    label: "检验员",
    prop: "checkName",
  },
  {
    label: "产品名称",
    prop: "productName",
  },
  {
    label: "规格型号",
    prop: "model",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "数量",
    prop: "quantity",
    width: 120
  },
  {
    label: "检验类型",
    prop: "checkTypeText",
    width: 120
  },
  {
    label: "检测结果",
    prop: "checkResult",
    dataType: "tag",
    formatType: (params) => {
      if (params === 1) {
        return "danger";
      } else if (params === 0) {
        return "success";
      } else {
        return null;
      }
    },
  },
  {
    label: "提交状态",
    prop: "inspectState",
    formatData: (params) => {
      if (params) {
        return "已提交";
      } else {
        return "未提交";
      }
    },
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => {
                    // å·²æäº¤åˆ™ç¦ç”¨
                    if (row.inspectState == 1) return true;
                    // å¦‚果检验员有值,只有当前登录用户能编辑
                    if (row.checkName) {
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          openFilesFormDia(row);
        },
      },
      {
        name: "提交",
        type: "text",
        clickFun: (row) => {
          submit(row.id);
        },
                disabled: (row) => {
                    // å·²æäº¤åˆ™ç¦ç”¨
                    if (row.inspectState == 1) return true;
                    // å¦‚果检验员有值,只有当前登录用户能提交
                    if (row.checkName) {
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
      },
      {
        name: "分配检验员",
        type: "text",
        clickFun: (row) => {
          if (!row.checkName) {
            open(row)
          } else {
            proxy.$modal.msgError("检验员已存在");
          }
        },
                disabled: (row) => {
                    return row.inspectState == 1 || row.checkName;
                }
      },
      {
        name: "下载",
        type: "text",
        clickFun: (row) => {
          downLoadFile(row);
        },
      },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const userList = ref([]);
const dialogFormVisible = ref(false);
const form = ref({
  checkName: ""
});
const page = reactive({
  current: 1,
  size: 100,
  total: 0
});
const currentRow = ref(null)
const formDia = ref()
const filesDia = ref()
const inspectionFormDia = ref()
const {proxy} = getCurrentInstance()
const userStore = useUserStore()
const changeDaterange = (value) => {
  searchForm.value.entryDateStart = undefined;
  searchForm.value.entryDateEnd = undefined;
  if (value) {
    searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  }
  getList();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const params = {...searchForm.value, ...page};
  params.entryDate = undefined
  findRawMaterialListPage({...params}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开弹框
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
};
// æ‰“开附件弹框
const openFilesFormDia = (type, row) => {
  nextTick(() => {
    filesDia.value?.openDialog(type, row)
  })
};
// åˆ é™¤
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(() => {
        deleteRawMaterial(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityInspect/export", {inspectType: 0}, "原材料检验.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// æä»·
const submit = async (id) => {
  const res = await submitQualityInspect({id: id})
  if (res.code === 200) {
    proxy.$modal.msgSuccess("提交成功");
    getList();
  }
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
const submitForm = () => {
  if (currentRow.value) {
    const data = {
      ...form.value,
      id: currentRow.value.id
    }
    qualityInspectUpdate(data).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    })
  }
};
const open = async (row) => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  currentRow.value = row
  dialogFormVisible.value = true
}
const downLoadFile = (row) => {
  downloadQualityInspect({ id: row.id }).then((blobData) => {
    const blob = new Blob([blobData], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })
    const downloadUrl = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = '原材料检验报告.docx'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(downloadUrl)
  })
};
onMounted(() => {
  getList();
});
</script>
<style scoped></style>