zhangwencui
8 小时以前 77f82fbb8542b59de772164aa2c317b5729f6946
feat(components,qualityManagement): 新增PIMTreeSelect通用组件并优化质检管理模块代码

- 新建通用树形选择组件,封装Element Plus el-tree-select,支持v-model、多选、搜索等常用功能
- 在metricBinding页面替换原生el-tree-select为该组件,提升代码复用性
- 为processInspection、nonconformingManagement的产品选择树选框添加搜索功能
- 统一格式化质检管理模块多个文件的代码风格,修正缩进、引号与语法细节
已添加1个文件
已修改5个文件
3386 ■■■■ 文件已修改
src/components/PIMTreeSelect/index.vue 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 859 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/metricBinding/index.vue 979 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/formDia.vue 486 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 950 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTreeSelect/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
<template>
  <el-tree-select
    v-model="innerValue"
    :data="data"
    :multiple="multiple"
    :filterable="filterable"
    :collapse-tags="collapseTags"
    :collapse-tags-tooltip="collapseTagsTooltip"
    :placeholder="placeholder"
    :clearable="clearable"
    :check-strictly="checkStrictly"
    :render-after-expand="renderAfterExpand"
    :disabled="disabled"
    v-bind="$attrs"
    @change="handleChange"
  />
</template>
<script setup>
  import { computed } from "vue";
  const props = defineProps({
    modelValue: {
      type: [String, Number, Array, Object],
      default: undefined,
    },
    data: {
      type: Array,
      default: () => [],
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    filterable: {
      type: Boolean,
      default: true,
    },
    collapseTags: {
      type: Boolean,
      default: false,
    },
    collapseTagsTooltip: {
      type: Boolean,
      default: false,
    },
    placeholder: {
      type: String,
      default: "请选择",
    },
    clearable: {
      type: Boolean,
      default: true,
    },
    checkStrictly: {
      type: Boolean,
      default: true,
    },
    renderAfterExpand: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  });
  const emit = defineEmits(["update:modelValue", "change"]);
  const innerValue = computed({
    get() {
      return props.modelValue;
    },
    set(val) {
      emit("update:modelValue", val);
    },
  });
  const handleChange = val => {
    emit("change", val);
  };
</script>
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -1,67 +1,86 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增出厂检验' : operationType === 'view' ? '查看出厂检验' : '编辑出厂检验'"
        width="70%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增出厂检验' : operationType === 'view' ? '查看出厂检验' : '编辑出厂检验'"
               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="isViewMode || operationType === 'edit'"
                  style="width: 100%"
              />
            <el-form-item label="产品名称:"
                          prop="productId">
              <el-tree-select v-model="form.productId"
                              placeholder="请选择"
                              filterable
                              clearable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              :disabled="isViewMode || operationType === 'edit'"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="form.productModelId" placeholder="请选择" clearable :disabled="isViewMode || operationType === 'edit'"
                         filterable readonly @change="handleChangeModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="form.productModelId"
                         placeholder="请选择"
                         clearable
                         :disabled="isViewMode || 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="testStandardId">
              <el-select
                v-model="form.testStandardId"
                placeholder="请选择指标"
                clearable
                @change="handleTestStandardChange"
                style="width: 100%"
                :disabled="isViewMode"
              >
                <el-option
                  v-for="item in testStandardOptions"
                  :key="item.id"
                  :label="item.standardName || item.standardNo"
                  :value="item.id"
                />
            <el-form-item label="指标选择:"
                          prop="testStandardId">
              <el-select v-model="form.testStandardId"
                         placeholder="请选择指标"
                         clearable
                         @change="handleTestStandardChange"
                         style="width: 100%"
                         :disabled="isViewMode">
                <el-option v-for="item in testStandardOptions"
                           :key="item.id"
                           :label="item.standardName || item.standardNo"
                           :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" placeholder="请输入" disabled/>
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="form.unit"
                        placeholder="请输入"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="isViewMode || processQuantityDisabled"/>
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.01"
                               :min="0"
                               style="width: 100%"
                               v-model="form.quantity"
                               placeholder="请输入"
                               clearable
                               :precision="2"
                               :disabled="isViewMode || processQuantityDisabled" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -97,63 +116,79 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检测单位:" prop="checkCompany">
              <el-input v-model="form.checkCompany" placeholder="请输入" clearable :disabled="isViewMode"/>
            <el-form-item label="检测单位:"
                          prop="checkCompany">
              <el-input v-model="form.checkCompany"
                        placeholder="请输入"
                        clearable
                        :disabled="isViewMode" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测结果:" prop="checkResult">
              <el-select v-model="form.checkResult" :disabled="isViewMode">
                <el-option label="合格" value="合格" />
                <el-option label="不合格" value="不合格" />
                <el-option label="部分合格" value="部分合格" />
            <el-form-item label="检测结果:"
                          prop="checkResult">
              <el-select v-model="form.checkResult"
                         :disabled="isViewMode">
                <el-option label="合格"
                           value="合格" />
                <el-option label="不合格"
                           value="不合格" />
                <el-option label="部分合格"
                           value="部分合格" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-select v-model="form.checkName" placeholder="请选择" clearable :disabled="isViewMode">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                           :value="item.nickName"/>
            <el-form-item label="检验员:"
                          prop="checkName">
              <el-select v-model="form.checkName"
                         placeholder="请选择"
                         clearable
                         :disabled="isViewMode">
                <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%"
                  :disabled="isViewMode"
              />
            <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%"
                              :disabled="isViewMode" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :tableLoading="tableLoading"
                height="400"
            >
                <template #slot="{ row }">
                    <el-input v-model="row.testValue" clearable :disabled="isViewMode"/>
                </template>
            </PIMTable>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :tableLoading="tableLoading"
                height="400">
        <template #slot="{ row }">
          <el-input v-model="row.testValue"
                    clearable
                    :disabled="isViewMode" />
        </template>
      </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <template v-if="!isViewMode">
            <el-button type="primary" @click="submitForm">确认</el-button>
            <el-button type="primary"
                       @click="submitForm">确认</el-button>
            <el-button @click="closeDia">取消</el-button>
          </template>
          <el-button v-else @click="closeDia">关闭</el-button>
          <el-button v-else
                     @click="closeDia">关闭</el-button>
        </div>
      </template>
    </el-dialog>
@@ -161,336 +196,370 @@
</template>
<script setup>
import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue";
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
  import {
    ref,
    reactive,
    toRefs,
    computed,
    getCurrentInstance,
    nextTick,
  } from "vue";
  import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import {
    qualityInspectAdd,
    qualityInspectUpdate,
  } from "@/api/qualityManagement/rawMaterialInspection.js";
  import { userListNoPage } from "@/api/system/user.js";
  import {
    qualityInspectDetailByProductId,
    getQualityTestStandardParamByTestStandardId,
  } from "@/api/qualityManagement/metricMaintenance.js";
  import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js";
  const { proxy } = getCurrentInstance();
  const emit = defineEmits(["close"]);
const dialogFormVisible = ref(false);
const operationType = ref("");
const data = reactive({
  form: {
    checkTime: "",
    process: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    testStandardId: "",
    unit: "",
    quantity: "",
    qualifiedQuantity: "",
    unqualifiedQuantity: "",
    checkCompany: "",
    checkResult: "",
  },
  rules: {
    checkTime: [{ required: true, message: "请输入", trigger: "blur" }],
    process: [{ required: true, message: "请输入", trigger: "blur" }],
    checkName: [{ required: false, message: "请输入", trigger: "blur" }],
    productId: [{ required: true, message: "请输入", trigger: "blur" }],
    productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    testStandardId: [{ required: false, message: "请选择指标", trigger: "change" }],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    qualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    unqualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: true, message: "请输入", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
// æ˜¯å¦ä¸ºæŸ¥çœ‹æ¨¡å¼
const isViewMode = computed(() => operationType.value === 'view');
// ç¼–辑时:productMainId æˆ– purchaseLedgerId ä»»ä¸€æœ‰å€¼åˆ™å·¥åºã€æ•°é‡ç½®ç°
const processQuantityDisabled = computed(() => {
  const v = form.value || {};
  return !!(v.productMainId != null || v.purchaseLedgerId != null);
});
const supplierList = ref([]);
const productOptions = 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 userList = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
const modelOptions = ref([]);
// æ‰“开弹框
const openDialog = async (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  // å…ˆæ¸…空表单验证状态,避免闪烁
  await nextTick();
  proxy.$refs.formRef?.clearValidate();
  // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
  const [userListsRes] = await Promise.all([
    userListNoPage(),
    getProductOptions(),
    getOptions().then((res) => {
      supplierList.value = res.data;
    })
  const dialogFormVisible = ref(false);
  const operationType = ref("");
  const data = reactive({
    form: {
      checkTime: "",
      process: "",
      checkName: "",
      productName: "",
      productId: "",
      productModelId: "",
      model: "",
      testStandardId: "",
      unit: "",
      quantity: "",
      qualifiedQuantity: "",
      unqualifiedQuantity: "",
      checkCompany: "",
      checkResult: "",
    },
    rules: {
      checkTime: [{ required: true, message: "请输入", trigger: "blur" }],
      process: [{ required: true, message: "请输入", trigger: "blur" }],
      checkName: [{ required: false, message: "请输入", trigger: "blur" }],
      productId: [{ required: true, message: "请输入", trigger: "blur" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      testStandardId: [
        { required: false, message: "请选择指标", trigger: "change" },
      ],
      unit: [{ required: false, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      qualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
      unqualifiedQuantity: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
      checkResult: [{ required: true, message: "请输入", trigger: "change" }],
    },
  });
  const { form, rules } = toRefs(data);
  // æ˜¯å¦ä¸ºæŸ¥çœ‹æ¨¡å¼
  const isViewMode = computed(() => operationType.value === "view");
  // ç¼–辑时:productMainId æˆ– purchaseLedgerId ä»»ä¸€æœ‰å€¼åˆ™å·¥åºã€æ•°é‡ç½®ç°
  const processQuantityDisabled = computed(() => {
    const v = form.value || {};
    return !!(v.productMainId != null || v.purchaseLedgerId != null);
  });
  const supplierList = ref([]);
  const productOptions = ref([]);
  const tableColumn = ref([
    {
      label: "指标",
      prop: "parameterItem",
    },
    {
      label: "单位",
      prop: "unit",
    },
    {
      label: "标准值",
      prop: "standardValue",
    },
    {
      label: "内控值",
      prop: "controlValue",
    },
    {
      label: "检验值",
      prop: "testValue",
      dataType: "slot",
      slot: "slot",
    },
  ]);
  userList.value = userListsRes.data;
  const tableData = ref([]);
  const tableLoading = ref(false);
  const userList = ref([]);
  const currentProductId = ref(0);
  const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
  const modelOptions = ref([]);
  form.value = {}
  testStandardOptions.value = [];
  tableData.value = [];
  // æ‰“开弹框
  const openDialog = async (type, row) => {
    operationType.value = type;
    dialogFormVisible.value = true;
    // å…ˆæ¸…空表单验证状态,避免闪烁
    await nextTick();
    proxy.$refs.formRef?.clearValidate();
  if (operationType.value === 'edit' || operationType.value === 'view') {
    // å…ˆä¿å­˜ testStandardId,避免被清空
    const savedTestStandardId = row.testStandardId;
    // å…ˆè®¾ç½®è¡¨å•数据,但暂时清空 testStandardId,等选项加载完成后再设置
    form.value = {...row, testStandardId: ''}
    currentProductId.value = row.productId || 0
    // æ¸…空验证状态,避免数据加载过程中的校验闪烁
    nextTick(() => {
      proxy.$refs.formRef?.clearValidate();
    });
    // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
    const [userListsRes] = await Promise.all([
      userListNoPage(),
      getProductOptions(),
      getOptions().then(res => {
        supplierList.value = res.data;
      }),
    ]);
    userList.value = userListsRes.data;
    // ç¼–辑模式下,并行加载规格型号和指标选项
    if (currentProductId.value) {
      // è®¾ç½®äº§å“åç§°
      form.value.productName = findNodeById(productOptions.value, currentProductId.value);
      // å¹¶è¡ŒåŠ è½½è§„æ ¼åž‹å·å’ŒæŒ‡æ ‡é€‰é¡¹
      const params = {
        productId: currentProductId.value,
        inspectType: 2
      };
      Promise.all([
        modelList({ id: currentProductId.value }),
        qualityInspectDetailByProductId(params)
      ]).then(([modelRes, testStandardRes]) => {
        // è®¾ç½®è§„格型号选项
        modelOptions.value = modelRes || [];
        // å¦‚果表单中已有 productModelId,设置对应的 model å’Œ unit
        if (form.value.productModelId && modelOptions.value.length > 0) {
          const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId);
          if (selectedModel) {
            form.value.model = selectedModel.model || '';
            form.value.unit = selectedModel.unit || '';
          }
        }
        // è®¾ç½®æŒ‡æ ‡é€‰é¡¹
        testStandardOptions.value = testStandardRes.data || [];
        // è®¾ç½® testStandardId å¹¶åŠ è½½å‚æ•°åˆ—è¡¨
        nextTick(() => {
          if (savedTestStandardId) {
            // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
            const matchedOption = testStandardOptions.value.find(item =>
              item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
            );
            if (matchedOption) {
              // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
              form.value.testStandardId = matchedOption.id;
            } else {
              // å¦‚果找不到匹配项,尝试直接使用原值
              console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value);
              form.value.testStandardId = savedTestStandardId;
            }
          }
          // ç¼–辑场景保留已有检验值,直接拉取原参数数据
          getQualityInspectParamList(row.id);
        });
      });
    } else {
      getQualityInspectParamList(row.id);
    }
  }
}
const getProductOptions = () => {
  return productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
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 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 handleQualifiedQuantityChange = (value) => {
  if (value === null || value === undefined) {
    form.value.qualifiedQuantity = 0;
    return;
  }
  const quantity = parseFloat(form.value.quantity) || 0;
  const qualified = parseFloat(value) || 0;
  form.value.qualifiedQuantity = qualified > quantity?quantity:qualified;
  form.value.unqualifiedQuantity = Math.max(0, quantity - qualified);
};
const handleUnqualifiedQuantityChange = (value) => {
  if (value === null || value === undefined) {
    form.value.unqualifiedQuantity = 0;
    return;
  }
  const quantity = parseFloat(form.value.quantity) || 0;
  const unqualified = parseFloat(value) || 0;
  form.value.unqualifiedQuantity = unqualified > quantity?quantity:unqualified;
  form.value.qualifiedQuantity = Math.max(0, quantity - unqualified);
};
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 = 2;
      if (operationType.value === "add") {
        tableData.value.forEach((item) => {
          delete item.id;
        });
      }
      const data = { ...form.value, qualityInspectParams: tableData.value };
      if (operationType.value === "add") {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        });
      } else {
        qualityInspectUpdate(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        });
      }
    }
  });
}
const getList = () => {
  if (!currentProductId.value) {
    form.value = {};
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
  let params = {
    productId: currentProductId.value,
    inspectType: 2
  };
  qualityInspectDetailByProductId(params).then(res => {
    // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
    testStandardOptions.value = res.data || [];
    // æ¸…空表格数据,等待用户选择指标
    tableData.value = [];
    // æ¸…空指标选择
    form.value.testStandardId = '';
  });
}
// æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
const handleTestStandardChange = (testStandardId) => {
  if (!testStandardId) {
    tableData.value = [];
    return;
    if (operationType.value === "edit" || operationType.value === "view") {
      // å…ˆä¿å­˜ testStandardId,避免被清空
      const savedTestStandardId = row.testStandardId;
      // å…ˆè®¾ç½®è¡¨å•数据,但暂时清空 testStandardId,等选项加载完成后再设置
      form.value = { ...row, testStandardId: "" };
      currentProductId.value = row.productId || 0;
      // æ¸…空验证状态,避免数据加载过程中的校验闪烁
      nextTick(() => {
        proxy.$refs.formRef?.clearValidate();
      });
      // ç¼–辑模式下,并行加载规格型号和指标选项
      if (currentProductId.value) {
        // è®¾ç½®äº§å“åç§°
        form.value.productName = findNodeById(
          productOptions.value,
          currentProductId.value
        );
        // å¹¶è¡ŒåŠ è½½è§„æ ¼åž‹å·å’ŒæŒ‡æ ‡é€‰é¡¹
        const params = {
          productId: currentProductId.value,
          inspectType: 2,
        };
        Promise.all([
          modelList({ id: currentProductId.value }),
          qualityInspectDetailByProductId(params),
        ]).then(([modelRes, testStandardRes]) => {
          // è®¾ç½®è§„格型号选项
          modelOptions.value = modelRes || [];
          // å¦‚果表单中已有 productModelId,设置对应的 model å’Œ unit
          if (form.value.productModelId && modelOptions.value.length > 0) {
            const selectedModel = modelOptions.value.find(
              item => item.id == form.value.productModelId
            );
            if (selectedModel) {
              form.value.model = selectedModel.model || "";
              form.value.unit = selectedModel.unit || "";
            }
          }
          // è®¾ç½®æŒ‡æ ‡é€‰é¡¹
          testStandardOptions.value = testStandardRes.data || [];
          // è®¾ç½® testStandardId å¹¶åŠ è½½å‚æ•°åˆ—è¡¨
          nextTick(() => {
            if (savedTestStandardId) {
              // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
              const matchedOption = testStandardOptions.value.find(
                item =>
                  item.id == savedTestStandardId ||
                  String(item.id) === String(savedTestStandardId)
              );
              if (matchedOption) {
                // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                form.value.testStandardId = matchedOption.id;
              } else {
                // å¦‚果找不到匹配项,尝试直接使用原值
                console.warn(
                  "未找到匹配的指标选项,testStandardId:",
                  savedTestStandardId,
                  "可用选项:",
                  testStandardOptions.value
                );
                form.value.testStandardId = savedTestStandardId;
              }
            }
            // ç¼–辑场景保留已有检验值,直接拉取原参数数据
            getQualityInspectParamList(row.id);
          });
        });
      } else {
        getQualityInspectParamList(row.id);
      }
    }
  };
  const getProductOptions = () => {
    return productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
    });
  };
  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 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 handleQualifiedQuantityChange = value => {
    if (value === null || value === undefined) {
      form.value.qualifiedQuantity = 0;
      return;
    }
    const quantity = parseFloat(form.value.quantity) || 0;
    const qualified = parseFloat(value) || 0;
    form.value.qualifiedQuantity = qualified > quantity ? quantity : qualified;
    form.value.unqualifiedQuantity = Math.max(0, quantity - qualified);
  };
  const handleUnqualifiedQuantityChange = value => {
    if (value === null || value === undefined) {
      form.value.unqualifiedQuantity = 0;
      return;
    }
    const quantity = parseFloat(form.value.quantity) || 0;
    const unqualified = parseFloat(value) || 0;
    form.value.unqualifiedQuantity =
      unqualified > quantity ? quantity : unqualified;
    form.value.qualifiedQuantity = Math.max(0, quantity - unqualified);
  };
  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;
    });
  }
  tableLoading.value = true;
  getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
    tableData.value = res.data || [];
    tableData.value = tableData.value.map(item => ({
      ...item,
      id: null
    }));
  }).catch(error => {
    console.error('获取标准参数失败:', error);
  // æäº¤äº§å“è¡¨å•
  const submitForm = () => {
    proxy.$refs.formRef.validate(valid => {
      if (valid) {
        form.value.inspectType = 2;
        if (operationType.value === "add") {
          tableData.value.forEach(item => {
            delete item.id;
          });
        }
        const data = { ...form.value, qualityInspectParams: tableData.value };
        if (operationType.value === "add") {
          qualityInspectAdd(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        } else {
          qualityInspectUpdate(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        }
      }
    });
  };
  const getList = () => {
    if (!currentProductId.value) {
      testStandardOptions.value = [];
      tableData.value = [];
      return;
    }
    let params = {
      productId: currentProductId.value,
      inspectType: 2,
    };
    qualityInspectDetailByProductId(params).then(res => {
      // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
      testStandardOptions.value = res.data || [];
      // æ¸…空表格数据,等待用户选择指标
      tableData.value = [];
      // æ¸…空指标选择
      form.value.testStandardId = "";
    });
  };
  // æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
  const handleTestStandardChange = testStandardId => {
    if (!testStandardId) {
      tableData.value = [];
      return;
    }
    tableLoading.value = true;
    getQualityTestStandardParamByTestStandardId(testStandardId)
      .then(res => {
        tableData.value = res.data || [];
        tableData.value = tableData.value.map(item => ({
          ...item,
          id: null,
        }));
      })
      .catch(error => {
        console.error("获取标准参数失败:", error);
        tableData.value = [];
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const getQualityInspectParamList = id => {
    qualityInspectParamInfo(id).then(res => {
      tableData.value = res.data;
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    tableData.value = [];
  }).finally(() => {
    tableLoading.value = false;
    testStandardOptions.value = [];
    form.value.testStandardId = "";
    dialogFormVisible.value = false;
    emit("close");
  };
  defineExpose({
    openDialog,
  });
}
const getQualityInspectParamList = (id) => {
  qualityInspectParamInfo(id).then(res => {
    tableData.value = res.data;
  });
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
  dialogFormVisible.value = false;
  emit('close');
}
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/qualityManagement/metricBinding/index.vue
@@ -1,149 +1,167 @@
<template>
  <div class="app-container metric-binding">
    <el-row :gutter="16" class="metric-binding-row">
    <el-row :gutter="16"
            class="metric-binding-row">
      <!-- å·¦ä¾§ï¼šæ£€æµ‹æ ‡å‡†åˆ—表 -->
      <el-col :xs="24" :sm="24" :md="12" :lg="14" :xl="14" class="left-col">
      <el-col :xs="24"
              :sm="24"
              :md="12"
              :lg="14"
              :xl="14"
              class="left-col">
        <div class="panel left-panel">
      <PIMTable
        rowKey="id"
        :column="standardColumns"
        :tableData="standardTableData"
        :page="page"
        :isSelection="false"
        :rowClassName="rowClassNameCenter"
        :tableLoading="tableLoading"
        :rowClick="handleTableRowClick"
        @pagination="handlePagination"
        :total="page.total"
      >
        <template #standardNoCell="{ row }">
          <span class="clickable-link" @click="handleStandardRowClick(row)">
            {{ row.standardNo }}
          </span>
        </template>
        <!-- è¡¨å¤´æœç´¢ -->
        <template #standardNoHeader>
          <el-input
            v-model="searchForm.standardNo"
            placeholder="标准编号"
            clearable
            size="small"
            @change="handleQuery"
            @clear="handleQuery"
          />
        </template>
        <template #standardNameHeader>
          <el-input
            v-model="searchForm.standardName"
            placeholder="标准名称"
            clearable
            size="small"
            @change="handleQuery"
            @clear="handleQuery"
          />
        </template>
        <template #inspectTypeHeader>
          <el-select
            v-model="searchForm.inspectType"
            placeholder="类别"
            clearable
            size="small"
            style="width: 120px"
            @change="handleQuery"
            @clear="handleQuery"
          >
            <el-option label="原材料检验" value="0" />
            <el-option label="过程检验" value="1" />
            <el-option label="出厂检验" value="2" />
          </el-select>
        </template>
        <template #stateHeader>
          <el-select
            v-model="searchForm.state"
            placeholder="状态"
            clearable
            size="small"
            style="width: 110px"
            @change="handleQuery"
            @clear="handleQuery"
          >
            <el-option label="草稿" value="0" />
            <el-option label="通过" value="1" />
            <el-option label="撤销" value="2" />
          </el-select>
        </template>
      </PIMTable>
          <PIMTable rowKey="id"
                    :column="standardColumns"
                    :tableData="standardTableData"
                    :page="page"
                    :isSelection="false"
                    :rowClassName="rowClassNameCenter"
                    :tableLoading="tableLoading"
                    :rowClick="handleTableRowClick"
                    @pagination="handlePagination"
                    :total="page.total">
            <template #standardNoCell="{ row }">
              <span class="clickable-link"
                    @click="handleStandardRowClick(row)">
                {{ row.standardNo }}
              </span>
            </template>
            <!-- è¡¨å¤´æœç´¢ -->
            <template #standardNoHeader>
              <el-input v-model="searchForm.standardNo"
                        placeholder="标准编号"
                        clearable
                        size="small"
                        @change="handleQuery"
                        @clear="handleQuery" />
            </template>
            <template #standardNameHeader>
              <el-input v-model="searchForm.standardName"
                        placeholder="标准名称"
                        clearable
                        size="small"
                        @change="handleQuery"
                        @clear="handleQuery" />
            </template>
            <template #inspectTypeHeader>
              <el-select v-model="searchForm.inspectType"
                         placeholder="类别"
                         clearable
                         size="small"
                         style="width: 120px"
                         @change="handleQuery"
                         @clear="handleQuery">
                <el-option label="原材料检验"
                           value="0" />
                <el-option label="过程检验"
                           value="1" />
                <el-option label="出厂检验"
                           value="2" />
              </el-select>
            </template>
            <template #stateHeader>
              <el-select v-model="searchForm.state"
                         placeholder="状态"
                         clearable
                         size="small"
                         style="width: 110px"
                         @change="handleQuery"
                         @clear="handleQuery">
                <el-option label="草稿"
                           value="0" />
                <el-option label="通过"
                           value="1" />
                <el-option label="撤销"
                           value="2" />
              </el-select>
            </template>
          </PIMTable>
        </div>
      </el-col>
      <!-- å³ä¾§ï¼šç»‘定列表 -->
      <el-col :xs="24" :sm="24" :md="12" :lg="10" :xl="10" class="right-col">
      <el-col :xs="24"
              :sm="24"
              :md="12"
              :lg="10"
              :xl="10"
              class="right-col">
        <div class="panel right-panel">
      <div class="right-header">
        <div class="title">绑定关系</div>
        <div class="desc" v-if="currentStandard">
          å½“前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span>
        </div>
        <div class="desc" v-else>请选择左侧检测标准</div>
      </div>
      <div class="right-toolbar">
        <el-button type="primary" :disabled="!currentStandard" @click="openBindingDialog">添加绑定</el-button>
        <el-button type="danger" plain :disabled="!currentStandard" @click="handleBatchUnbind">删除</el-button>
      </div>
      <el-table
        v-loading="bindingLoading"
        :data="bindingTableData"
        border
        :row-class-name="() => 'row-center'"
        class="center-table"
        style="width: 100%"
        height="calc(100vh - 220px)"
        @selection-change="handleBindingSelectionChange"
      >
        <el-table-column type="selection" width="48" align="center" />
        <el-table-column type="index" label="序号" width="60" align="center" />
        <el-table-column prop="productName" label="产品名称" min-width="140" />
        <el-table-column label="操作" width="120" fixed="right" align="center">
          <template #default="{ row }">
            <el-button link type="danger" size="small" @click="handleUnbind(row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
          <div class="right-header">
            <div class="title">绑定关系</div>
            <div class="desc"
                 v-if="currentStandard">
              å½“前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span>
            </div>
            <div class="desc"
                 v-else>请选择左侧检测标准</div>
          </div>
          <div class="right-toolbar">
            <el-button type="primary"
                       :disabled="!currentStandard"
                       @click="openBindingDialog">添加绑定</el-button>
            <el-button type="danger"
                       plain
                       :disabled="!currentStandard"
                       @click="handleBatchUnbind">删除</el-button>
          </div>
          <el-table v-loading="bindingLoading"
                    :data="bindingTableData"
                    border
                    :row-class-name="() => 'row-center'"
                    class="center-table"
                    style="width: 100%"
                    height="calc(100vh - 220px)"
                    @selection-change="handleBindingSelectionChange">
            <el-table-column type="selection"
                             width="48"
                             align="center" />
            <el-table-column type="index"
                             label="序号"
                             width="60"
                             align="center" />
            <el-table-column prop="productName"
                             label="产品名称"
                             min-width="140" />
            <el-table-column label="操作"
                             width="120"
                             fixed="right"
                             align="center">
              <template #default="{ row }">
                <el-button link
                           type="danger"
                           size="small"
                           @click="handleUnbind(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-col>
    </el-row>
    <!-- æ·»åŠ ç»‘å®šå¼¹æ¡† -->
    <el-dialog
      v-model="bindingDialogVisible"
      title="添加绑定"
      width="520px"
      @close="closeBindingDialog"
    >
    <el-dialog v-model="bindingDialogVisible"
               title="添加绑定"
               width="520px"
               @close="closeBindingDialog">
      <el-form label-width="100px">
        <el-form-item label="产品">
          <el-tree-select
            v-model="selectedProductIds"
            multiple
            filterable
            collapse-tags
            collapse-tags-tooltip
            placeholder="请选择产品(可多选)"
            clearable
            check-strictly
            :data="productOptions"
            :render-after-expand="false"
            style="width: 100%"
          />
          <PIMTreeSelect v-model="selectedProductIds"
                         multiple
                         filterable
                         collapse-tags
                         collapse-tags-tooltip
                         placeholder="请选择产品(可多选)"
                         clearable
                         check-strictly
                         :data="productOptions"
                         :render-after-expand="false"
                         style="width: 100%" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="closeBindingDialog">取消</el-button>
          <el-button type="primary" @click="submitBinding">确定</el-button>
          <el-button type="primary"
                     @click="submitBinding">确定</el-button>
        </span>
      </template>
    </el-dialog>
@@ -151,387 +169,404 @@
</template>
<script setup>
import { Search } from '@element-plus/icons-vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from 'element-plus'
import PIMTable from '@/components/PIMTable/PIMTable.vue'
import { productTreeList } from '@/api/basicData/product.js'
import {
  qualityTestStandardListPage
} from '@/api/qualityManagement/metricMaintenance.js'
import { productProcessListPage } from '@/api/basicData/productProcess.js'
import {
  qualityTestStandardBindingList,
  qualityTestStandardBindingAdd,
  qualityTestStandardBindingDel
} from '@/api/qualityManagement/qualityTestStandardBinding.js'
  import { Search } from "@element-plus/icons-vue";
  import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
  import { ElMessageBox } from "element-plus";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import PIMTreeSelect from "@/components/PIMTreeSelect/index.vue";
  import { productTreeList } from "@/api/basicData/product.js";
  import { qualityTestStandardListPage } from "@/api/qualityManagement/metricMaintenance.js";
  import { productProcessListPage } from "@/api/basicData/productProcess.js";
  import {
    qualityTestStandardBindingList,
    qualityTestStandardBindingAdd,
    qualityTestStandardBindingDel,
  } from "@/api/qualityManagement/qualityTestStandardBinding.js";
const { proxy } = getCurrentInstance()
  const { proxy } = getCurrentInstance();
// å·¦ä¾§æ ‡å‡†åˆ—表:整行内容居中(配合样式)
const rowClassNameCenter = () => 'row-center'
  // å·¦ä¾§æ ‡å‡†åˆ—表:整行内容居中(配合样式)
  const rowClassNameCenter = () => "row-center";
const data = reactive({
  searchForm: {
    standardNo: '',
    standardName: '',
    state: '',
    inspectType: ''
  const data = reactive({
    searchForm: {
      standardNo: "",
      standardName: "",
      state: "",
      inspectType: "",
    },
  });
  const { searchForm } = toRefs(data);
  // å·¦ä¾§
  const standardTableData = ref([]);
  const tableLoading = ref(false);
  const page = reactive({ current: 1, size: 10, total: 0 });
  // å·¥åºä¸‹æ‹‰ï¼ˆç”¨äºŽåˆ—表回显)
  const processOptions = ref([]);
  const getProcessList = async () => {
    try {
      const res = await productProcessListPage({ current: 1, size: 1000 });
      if (res?.code === 200) {
        const records = res?.data?.records || [];
        processOptions.value = records.map(item => ({
          label: item.processName || item.name || item.label,
          value: item.id || item.processId || item.value,
        }));
      }
    } catch (error) {
      console.error("获取工序列表失败:", error);
    }
  };
  const standardColumns = ref([
    {
      label: "标准编号",
      prop: "standardNo",
      dataType: "slot",
      slot: "standardNoCell",
      minWidth: 160,
      align: "center",
      headerSlot: "standardNoHeader",
    },
    {
      label: "标准名称",
      prop: "standardName",
      minWidth: 180,
      align: "center",
      headerSlot: "standardNameHeader",
    },
    {
      label: "类别",
      prop: "inspectType",
      headerSlot: "inspectTypeHeader",
      align: "center",
      dataType: "tag",
      formatData: val => {
        const map = { 0: "原材料检验", 1: "过程检验", 2: "出厂检验" };
        return map[val] || val;
      },
    },
    {
      label: "工序",
      prop: "processId",
      align: "center",
      dataType: "tag",
      formatData: val => {
        const target = processOptions.value.find(
          item => String(item.value) === String(val)
        );
        return target?.label || val;
      },
    },
    {
      label: "备注",
      prop: "remark",
      minWidth: 160,
      align: "center",
    },
    // {
    //   label: '状态',
    //   prop: 'state',
    //   headerSlot: 'stateHeader',
    //   dataType: 'tag',
    //   formatData: (val) => {
    //     const map = { 0: '草稿', 1: '通过', 2: '撤销' }
    //     return map[val] || val
    //   },
    //   formatType: (val) => {
    //     if (val == 1) return 'success'
    //     if (val == 2) return 'warning'
    //     return 'info'
    //   }
    // }
  ]);
  const currentStandard = ref(null);
  // å³ä¾§ç»‘定
  const bindingTableData = ref([]);
  const bindingLoading = ref(false);
  const bindingSelectedRows = ref([]);
  const bindingDialogVisible = ref(false);
  // äº§å“æ ‘(用于绑定选择)
  const productOptions = ref([]);
  const selectedProductIds = ref([]);
  const getProductOptions = async () => {
    // é¿å…é‡å¤è¯·æ±‚
    if (productOptions.value?.length) return;
    const res = await productTreeList();
    productOptions.value = convertIdToValue(Array.isArray(res) ? res : []);
  };
  function convertIdToValue(data) {
    return (data || []).map(item => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
        value: id,
      };
      if (children && children.length > 0) {
        newItem.children = convertIdToValue(children);
      }
      return newItem;
    });
  }
})
const { searchForm } = toRefs(data)
// å·¦ä¾§
const standardTableData = ref([])
const tableLoading = ref(false)
const page = reactive({ current: 1, size: 10, total: 0 })
  const handleQuery = () => {
    page.current = 1;
    getStandardList();
  };
// å·¥åºä¸‹æ‹‰ï¼ˆç”¨äºŽåˆ—表回显)
const processOptions = ref([])
  const handlePagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getStandardList();
  };
const getProcessList = async () => {
  try {
    const res = await productProcessListPage({ current: 1, size: 1000 })
    if (res?.code === 200) {
      const records = res?.data?.records || []
      processOptions.value = records.map((item) => ({
        label: item.processName || item.name || item.label,
        value: item.id || item.processId || item.value
      }))
    }
  } catch (error) {
    console.error('获取工序列表失败:', error)
  }
}
const standardColumns = ref([
  { label: '标准编号', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, align: 'center', headerSlot: 'standardNoHeader' },
  { label: '标准名称', prop: 'standardName', minWidth: 180, align: 'center', headerSlot: 'standardNameHeader' },
  {
    label: '类别',
    prop: 'inspectType',
    headerSlot: 'inspectTypeHeader',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const map = { 0: '原材料检验', 1: '过程检验', 2: '出厂检验' }
      return map[val] || val
    }
  },
  {
    label: '工序',
    prop: 'processId',
    align: 'center',
    dataType: 'tag',
    formatData: (val) => {
      const target = processOptions.value.find(
        (item) => String(item.value) === String(val)
      )
      return target?.label || val
    }
  },
  {
    label: '备注',
    prop: 'remark',
    minWidth: 160,
    align: 'center'
  }
  // {
  //   label: '状态',
  //   prop: 'state',
  //   headerSlot: 'stateHeader',
  //   dataType: 'tag',
  //   formatData: (val) => {
  //     const map = { 0: '草稿', 1: '通过', 2: '撤销' }
  //     return map[val] || val
  //   },
  //   formatType: (val) => {
  //     if (val == 1) return 'success'
  //     if (val == 2) return 'warning'
  //     return 'info'
  //   }
  // }
])
const currentStandard = ref(null)
// å³ä¾§ç»‘定
const bindingTableData = ref([])
const bindingLoading = ref(false)
const bindingSelectedRows = ref([])
const bindingDialogVisible = ref(false)
// äº§å“æ ‘(用于绑定选择)
const productOptions = ref([])
const selectedProductIds = ref([])
const getProductOptions = async () => {
  // é¿å…é‡å¤è¯·æ±‚
  if (productOptions.value?.length) return
  const res = await productTreeList()
  productOptions.value = convertIdToValue(Array.isArray(res) ? res : [])
}
function convertIdToValue(data) {
  return (data || []).map((item) => {
    const { id, children, ...rest } = item
    const newItem = {
      ...rest,
      value: id
    }
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children)
    }
    return newItem
  })
}
const handleQuery = () => {
  page.current = 1
  getStandardList()
}
const handlePagination = (obj) => {
  page.current = obj.page
  page.size = obj.limit
  getStandardList()
}
const getStandardList = () => {
  tableLoading.value = true
  qualityTestStandardListPage({
    ...searchForm.value,
    current: page.current,
    size: page.size,
    state: 1
  })
    .then((res) => {
      const records = res?.data?.records || []
      standardTableData.value = records
      page.total = res?.data?.total || records.length
  const getStandardList = () => {
    tableLoading.value = true;
    qualityTestStandardListPage({
      ...searchForm.value,
      current: page.current,
      size: page.size,
      state: 1,
    })
    .finally(() => {
      tableLoading.value = false
    })
}
      .then(res => {
        const records = res?.data?.records || [];
        standardTableData.value = records;
        page.total = res?.data?.total || records.length;
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
// è¡¨æ ¼è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨
const handleTableRowClick = (row) => {
  currentStandard.value = row
  loadBindingList()
}
  // è¡¨æ ¼è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨
  const handleTableRowClick = row => {
    currentStandard.value = row;
    loadBindingList();
  };
// å·¦ä¾§è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨ï¼ˆä¿ç•™ç”¨äºŽæ ‡å‡†ç¼–å·åˆ—çš„ç‚¹å‡»ï¼‰
const handleStandardRowClick = (row) => {
  currentStandard.value = row
  loadBindingList()
}
  // å·¦ä¾§è¡Œç‚¹å‡»ï¼ŒåŠ è½½å³ä¾§ç»‘å®šåˆ—è¡¨ï¼ˆä¿ç•™ç”¨äºŽæ ‡å‡†ç¼–å·åˆ—çš„ç‚¹å‡»ï¼‰
  const handleStandardRowClick = row => {
    currentStandard.value = row;
    loadBindingList();
  };
const loadBindingList = () => {
  if (!currentStandard.value?.id) {
    bindingTableData.value = []
    return
  }
  bindingLoading.value = true
  qualityTestStandardBindingList({ testStandardId: currentStandard.value.id })
    .then((res) => {
      const base = res?.data || []
      // å°†å½“前标准的工序和备注带到绑定列表中展示
      bindingTableData.value = base.map((item) => ({
        ...item,
        processId: currentStandard.value?.processId,
        remark: currentStandard.value?.remark
      }))
    })
    .finally(() => {
      bindingLoading.value = false
    })
}
  const loadBindingList = () => {
    if (!currentStandard.value?.id) {
      bindingTableData.value = [];
      return;
    }
    bindingLoading.value = true;
    qualityTestStandardBindingList({ testStandardId: currentStandard.value.id })
      .then(res => {
        const base = res?.data || [];
        // å°†å½“前标准的工序和备注带到绑定列表中展示
        bindingTableData.value = base.map(item => ({
          ...item,
          processId: currentStandard.value?.processId,
          remark: currentStandard.value?.remark,
        }));
      })
      .finally(() => {
        bindingLoading.value = false;
      });
  };
const handleBindingSelectionChange = (selection) => {
  bindingSelectedRows.value = selection
}
  const handleBindingSelectionChange = selection => {
    bindingSelectedRows.value = selection;
  };
const openBindingDialog = () => {
  if (!currentStandard.value?.id) return
  selectedProductIds.value = []
  getProductOptions()
  bindingDialogVisible.value = true
}
  const openBindingDialog = () => {
    if (!currentStandard.value?.id) return;
    selectedProductIds.value = [];
    getProductOptions();
    bindingDialogVisible.value = true;
  };
const closeBindingDialog = () => {
  bindingDialogVisible.value = false
}
  const closeBindingDialog = () => {
    bindingDialogVisible.value = false;
  };
const submitBinding = async () => {
  const testStandardId = currentStandard.value?.id
  if (!testStandardId) return
  const ids = (selectedProductIds.value || []).filter(Boolean)
  if (!ids.length) {
    proxy.$message.warning('请选择产品')
    return
  }
  const payload = ids.map((pid) => ({
    productId: pid,
    testStandardId
  }))
  await qualityTestStandardBindingAdd(payload)
  proxy.$message.success('添加成功')
  bindingDialogVisible.value = false
  loadBindingList()
}
  const submitBinding = async () => {
    const testStandardId = currentStandard.value?.id;
    if (!testStandardId) return;
    const ids = (selectedProductIds.value || []).filter(Boolean);
    if (!ids.length) {
      proxy.$message.warning("请选择产品");
      return;
    }
    const payload = ids.map(pid => ({
      productId: pid,
      testStandardId,
    }));
    await qualityTestStandardBindingAdd(payload);
    proxy.$message.success("添加成功");
    bindingDialogVisible.value = false;
    loadBindingList();
  };
const handleUnbind = async (row) => {
  const id = row?.id ?? row?.qualityTestStandardBindingId
  if (id == null || id === '') return
  try {
    await ElMessageBox.confirm('确认删除该绑定?', '提示', { type: 'warning' })
  } catch {
    return
  }
  try {
    await qualityTestStandardBindingDel([id])
    proxy.$message.success('删除成功')
    loadBindingList()
  } catch (err) {
    console.error('删除绑定失败:', err)
    proxy.$message?.error(err?.message || '删除失败')
  }
}
  const handleUnbind = async row => {
    const id = row?.id ?? row?.qualityTestStandardBindingId;
    if (id == null || id === "") return;
    try {
      await ElMessageBox.confirm("确认删除该绑定?", "提示", { type: "warning" });
    } catch {
      return;
    }
    try {
      await qualityTestStandardBindingDel([id]);
      proxy.$message.success("删除成功");
      loadBindingList();
    } catch (err) {
      console.error("删除绑定失败:", err);
      proxy.$message?.error(err?.message || "删除失败");
    }
  };
const handleBatchUnbind = async () => {
  if (!bindingSelectedRows.value.length) {
    proxy.$message.warning('请选择数据')
    return
  }
  const ids = bindingSelectedRows.value
    .map((i) => i?.id ?? i?.qualityTestStandardBindingId)
    .filter((id) => id != null && id !== '')
  if (!ids.length) {
    proxy.$message.warning('选中数据缺少有效 id')
    return
  }
  try {
    await ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { type: 'warning' })
  } catch {
    return
  }
  try {
    await qualityTestStandardBindingDel(ids)
    proxy.$message.success('删除成功')
    loadBindingList()
  } catch (err) {
    console.error('批量删除绑定失败:', err)
    proxy.$message?.error(err?.message || '删除失败')
  }
}
  const handleBatchUnbind = async () => {
    if (!bindingSelectedRows.value.length) {
      proxy.$message.warning("请选择数据");
      return;
    }
    const ids = bindingSelectedRows.value
      .map(i => i?.id ?? i?.qualityTestStandardBindingId)
      .filter(id => id != null && id !== "");
    if (!ids.length) {
      proxy.$message.warning("选中数据缺少有效 id");
      return;
    }
    try {
      await ElMessageBox.confirm(
        "选中的内容将被删除,是否确认删除?",
        "删除提示",
        { type: "warning" }
      );
    } catch {
      return;
    }
    try {
      await qualityTestStandardBindingDel(ids);
      proxy.$message.success("删除成功");
      loadBindingList();
    } catch (err) {
      console.error("批量删除绑定失败:", err);
      proxy.$message?.error(err?.message || "删除失败");
    }
  };
onMounted(() => {
  getStandardList()
  getProcessList()
})
  onMounted(() => {
    getStandardList();
    getProcessList();
  });
</script>
<style scoped>
.metric-binding-row {
  width: 100%;
}
  .metric-binding-row {
    width: 100%;
  }
.metric-binding-row .left-col,
.metric-binding-row .right-col {
  margin-bottom: 16px;
}
  .metric-binding-row .left-col,
  .metric-binding-row .right-col {
    margin-bottom: 16px;
  }
.metric-binding-row .panel {
  background: #ffffff;
  padding: 16px;
  box-sizing: border-box;
  height: 100%;
  min-height: 400px;
}
  .metric-binding-row .panel {
    background: #ffffff;
    padding: 16px;
    box-sizing: border-box;
    height: 100%;
    min-height: 400px;
  }
.left-panel,
.right-panel {
  height: 100%;
}
  .left-panel,
  .right-panel {
    height: 100%;
  }
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}
  .toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
  }
.toolbar-right {
  flex-shrink: 0;
}
  .toolbar-right {
    flex-shrink: 0;
  }
.right-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 10px;
}
  .right-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 10px;
  }
.right-header .title {
  font-size: 16px;
  font-weight: 600;
}
  .right-header .title {
    font-size: 16px;
    font-weight: 600;
  }
.right-header .desc {
  font-size: 13px;
  color: #666;
}
  .right-header .desc {
    font-size: 13px;
    color: #666;
  }
.right-toolbar {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-bottom: 10px;
}
  .right-toolbar {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-bottom: 10px;
  }
.link-text {
  color: #409eff;
  cursor: default;
}
  .link-text {
    color: #409eff;
    cursor: default;
  }
.clickable-link {
  color: #409eff;
  cursor: pointer;
}
  .clickable-link {
    color: #409eff;
    cursor: pointer;
  }
.clickable-link:hover {
  text-decoration: underline;
}
  .clickable-link:hover {
    text-decoration: underline;
  }
:deep(.row-center td) {
  text-align: center !important;
}
  :deep(.row-center td) {
    text-align: center !important;
  }
/* el-table è¡¨å¤´/内容统一居中(row-class-name ä¸ä½œç”¨äºŽè¡¨å¤´ï¼‰ */
:deep(.center-table .el-table__header-wrapper th .cell) {
  text-align: center !important;
}
:deep(.center-table .el-table__body-wrapper td .cell) {
  text-align: center !important;
}
  /* el-table è¡¨å¤´/内容统一居中(row-class-name ä¸ä½œç”¨äºŽè¡¨å¤´ï¼‰ */
  :deep(.center-table .el-table__header-wrapper th .cell) {
    text-align: center !important;
  }
  :deep(.center-table .el-table__body-wrapper td .cell) {
    text-align: center !important;
  }
/* PIMTable è¡¨å¤´å±…中 */
:deep(.lims-table .pim-table-header-cell) {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
  /* PIMTable è¡¨å¤´å±…中 */
  :deep(.lims-table .pim-table-header-cell) {
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
:deep(.lims-table .pim-table-header-title) {
  text-align: center;
  width: 100%;
}
  :deep(.lims-table .pim-table-header-title) {
    text-align: center;
    width: 100%;
  }
:deep(.lims-table .pim-table-header-extra) {
  width: 100%;
  margin-top: 4px;
}
  :deep(.lims-table .pim-table-header-extra) {
    width: 100%;
    margin-top: 4px;
  }
</style>
src/views/qualityManagement/nonconformingManagement/components/formDia.vue
@@ -1,121 +1,168 @@
<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-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="inspectType">
            <el-form-item label="类别:"
                          prop="inspectType">
              <el-select v-model="form.inspectType">
                <el-option label="原材料检验" :value="0" />
                <el-option label="过程检验" :value="1" />
                <el-option label="出厂检验" :value="2" />
                <el-option label="原材料检验"
                           :value="0" />
                <el-option label="过程检验"
                           :value="1" />
                <el-option label="出厂检验"
                           :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <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"
                  style="width: 100%"
              />
            <el-form-item label="产品名称:"
                          prop="productId">
              <el-tree-select v-model="form.productId"
                              placeholder="请选择"
                              filterable
                              clearable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="model">
              <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-form-item label="规格型号:"
                          prop="model">
              <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" placeholder="请输入" clearable/>
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="form.unit"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2"/>
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.01"
                               :min="0"
                               style="width: 100%"
                               v-model="form.quantity"
                               placeholder="请输入"
                               clearable
                               :precision="2" />
            </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 style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
            <el-form-item label="检验员:"
                          prop="checkName">
              <el-select v-model="form.checkName"
                         placeholder="请选择"
                         clearable
                         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 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-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="不合格现象:" prop="defectivePhenomena">
              <el-input v-model="form.defectivePhenomena" placeholder="请输入" clearable/>
            <el-form-item label="不合格现象:"
                          prop="defectivePhenomena">
              <el-input v-model="form.defectivePhenomena"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="处理结果:" prop="dealResult">
              <el-select v-model="form.dealResult" placeholder="请选择" clearable>
                <el-option :label="item.label" :value="item.value" v-for="item in rejection_handling" :key="item.value" />
            <el-form-item label="处理结果:"
                          prop="dealResult">
              <el-select v-model="form.dealResult"
                         placeholder="请选择"
                         clearable>
                <el-option :label="item.label"
                           :value="item.value"
                           v-for="item in rejection_handling"
                           :key="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="处理人:" prop="dealName">
              <el-select v-model="form.dealName" placeholder="请选择" clearable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
            <el-form-item label="处理人:"
                          prop="dealName">
              <el-select v-model="form.dealName"
                         placeholder="请选择"
                         clearable
                         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="dealTime">
              <el-date-picker
                  v-model="form.dealTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            <el-form-item label="处理日期:"
                          prop="dealTime">
              <el-date-picker v-model="form.dealTime"
                              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>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
@@ -124,173 +171,172 @@
</template>
<script setup>
import {ref, reactive, toRefs} from "vue";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {
  getQualityUnqualifiedInfo,
  qualityUnqualifiedAdd,
  qualityUnqualifiedUpdate
} from "@/api/qualityManagement/nonconformingManagement.js";
import {userListNoPage} from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
const emit = defineEmits(['close'])
  import { ref, reactive, toRefs } from "vue";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import {
    getQualityUnqualifiedInfo,
    qualityUnqualifiedAdd,
    qualityUnqualifiedUpdate,
  } from "@/api/qualityManagement/nonconformingManagement.js";
  import { userListNoPage } from "@/api/system/user.js";
  import useUserStore from "@/store/modules/user";
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const emit = defineEmits(["close"]);
const dialogFormVisible = ref(false);
const operationType = ref('')
const { rejection_handling } = proxy.useDict("rejection_handling")
const data = reactive({
  form: {
    checkTime: "",
    process: "",
    checkName: "",
    productName: "",
    productId: "",
    model: "",
    unit: "",
    quantity: undefined,
    checkCompany: "",
    checkResult: "",
    inspectType: '',
    defectivePhenomena: '',
    dealResult: '',
    dealName: '',
    dealTime: '',
    productModelId: undefined,
  },
  rules: {
    checkTime: [{ required: false, message: "请输入", trigger: "blur" },],
    process: [{ required: true, message: "请输入", trigger: "blur" }],
    checkName: [{ required: true, message: "请选择检验员", trigger: "change" }],
    productId: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    dealName: [{ required: true, message: "请选择处理人", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const modelOptions = ref([]);
const userList = ref([]); // æ£€éªŒå‘˜/处理人下拉列表
// æ‰“开弹框
const openDialog = async (type, row) => {
  operationType.value = type;
  try {
    const userRes = await userListNoPage();
    userList.value = userRes.data || [];
  } catch (e) {
    console.error("加载用户列表失败", e);
    userList.value = [];
  }
  dialogFormVisible.value = true;
  if (operationType.value === 'add') {
    form.value = {
      checkName: userStore.nickName || '',
      dealName: '',
      dealTime: '',
      dealResult: '',
      defectivePhenomena: '',
      inspectType: '',
      checkTime: '',
      productId: '',
      model: '',
      unit: '',
  const dialogFormVisible = ref(false);
  const operationType = ref("");
  const { rejection_handling } = proxy.useDict("rejection_handling");
  const data = reactive({
    form: {
      checkTime: "",
      process: "",
      checkName: "",
      productName: "",
      productId: "",
      model: "",
      unit: "",
      quantity: undefined,
      productName: '',
      checkCompany: "",
      checkResult: "",
      inspectType: "",
      defectivePhenomena: "",
      dealResult: "",
      dealName: "",
      dealTime: "",
      productModelId: undefined,
    };
  } else {
    form.value = {};
  }
  getProductOptions();
  if (operationType.value === 'edit') {
    getQualityUnqualifiedInfo(row.id).then(res => {
      const { inspectState, ...rest } = (res.data || {})
      form.value = { ...rest }
    })
  }
}
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
    },
    rules: {
      checkTime: [{ required: false, message: "请输入", trigger: "blur" }],
      process: [{ required: true, message: "请输入", trigger: "blur" }],
      checkName: [{ required: true, message: "请选择检验员", trigger: "change" }],
      productId: [{ required: true, message: "请输入", trigger: "blur" }],
      model: [{ required: true, message: "请输入", trigger: "blur" }],
      unit: [{ required: false, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
      checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
      dealName: [{ required: true, message: "请选择处理人", trigger: "change" }],
    },
  });
};
const getModels = (value) => {
  form.value.productName = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  })
};
const handleChangeModel = (value) => {
  const selectedModel = modelOptions.value.find(item => item.id === value);
  if (selectedModel) {
    form.value.model = selectedModel.model;
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
  const { form, rules } = toRefs(data);
  const productOptions = ref([]);
  const modelOptions = ref([]);
  const userList = ref([]); // æ£€éªŒå‘˜/处理人下拉列表
  // æ‰“开弹框
  const openDialog = async (type, row) => {
    operationType.value = type;
    try {
      const userRes = await userListNoPage();
      userList.value = userRes.data || [];
    } catch (e) {
      console.error("加载用户列表失败", e);
      userList.value = [];
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
    dialogFormVisible.value = true;
    if (operationType.value === "add") {
      form.value = {
        checkName: userStore.nickName || "",
        dealName: "",
        dealTime: "",
        dealResult: "",
        defectivePhenomena: "",
        inspectType: "",
        checkTime: "",
        productId: "",
        model: "",
        unit: "",
        quantity: undefined,
        productName: "",
        productModelId: undefined,
      };
    } else {
      form.value = {};
    }
    getProductOptions();
    if (operationType.value === "edit") {
      getQualityUnqualifiedInfo(row.id).then(res => {
        const { inspectState, ...rest } = res.data || {};
        form.value = { ...rest };
      });
    }
  };
  const getProductOptions = () => {
    productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
    });
  };
  const getModels = value => {
    form.value.productName = findNodeById(productOptions.value, value);
    modelList({ id: value }).then(res => {
      modelOptions.value = res;
    });
  };
  const handleChangeModel = value => {
    const selectedModel = modelOptions.value.find(item => item.id === value);
    if (selectedModel) {
      form.value.model = selectedModel.model;
    }
  };
  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) {
      // çŠ¶æ€å­—æ®µä¸åœ¨è¡¨å•å¡«å†™ï¼Œä¹Ÿä¸ä¼ ç»™åŽç«¯
      const { inspectState, ...payload } = (form.value || {})
      if (operationType.value === "add") {
        qualityUnqualifiedAdd(payload).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        qualityUnqualifiedUpdate(payload).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
    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);
      }
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')
};
defineExpose({
  openDialog,
});
      return newItem;
    });
  }
  // æäº¤äº§å“è¡¨å•
  const submitForm = () => {
    proxy.$refs.formRef.validate(valid => {
      if (valid) {
        // çŠ¶æ€å­—æ®µä¸åœ¨è¡¨å•å¡«å†™ï¼Œä¹Ÿä¸ä¼ ç»™åŽç«¯
        const { inspectState, ...payload } = form.value || {};
        if (operationType.value === "add") {
          qualityUnqualifiedAdd(payload).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        } else {
          qualityUnqualifiedUpdate(payload).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        }
      }
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
    emit("close");
  };
  defineExpose({
    openDialog,
  });
</script>
<style scoped>
</style>
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -30,6 +30,7 @@
                          prop="productId">
              <el-tree-select v-model="form.productId"
                              placeholder="请选择"
                              filterable
                              clearable
                              check-strictly
                              @change="getModels"
@@ -141,7 +142,8 @@
          <el-col :span="12">
            <el-form-item label="检测结果:"
                          prop="checkResult">
              <el-select v-model="form.checkResult" :disabled="isViewMode">
              <el-select v-model="form.checkResult"
                         :disabled="isViewMode">
                <el-option label="合格"
                           value="合格" />
                <el-option label="不合格"
@@ -200,7 +202,8 @@
                       @click="submitForm">确认</el-button>
            <el-button @click="closeDia">取消</el-button>
          </template>
          <el-button v-else @click="closeDia">关闭</el-button>
          <el-button v-else
                     @click="closeDia">关闭</el-button>
        </div>
      </template>
    </el-dialog>
@@ -258,11 +261,15 @@
      checkName: [{ required: false, message: "请输入", trigger: "blur" }],
      productId: [{ required: true, message: "请输入", trigger: "blur" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      testStandardId: [{ required: false, message: "请选择指标", trigger: "change" }],
      testStandardId: [
        { required: false, message: "请选择指标", trigger: "change" },
      ],
      unit: [{ required: false, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      qualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
      unqualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
      unqualifiedQuantity: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
      checkResult: [{ required: true, message: "请输入", trigger: "change" }],
    },
@@ -270,7 +277,7 @@
  const userList = ref([]);
  const { form, rules } = toRefs(data);
  // æ˜¯å¦ä¸ºæŸ¥çœ‹æ¨¡å¼
  const isViewMode = computed(() => operationType.value === 'view');
  const isViewMode = computed(() => operationType.value === "view");
  // ç¼–辑时:productMainId æˆ– purchaseLedgerId ä»»ä¸€æœ‰å€¼åˆ™å·¥åºã€æ•°é‡ç½®ç°
  const processQuantityDisabled = computed(() => {
    const v = form.value || {};
@@ -445,25 +452,26 @@
      modelOptions.value.find(item => item.id == value)?.unit || "";
  };
  const handleQualifiedQuantityChange = (value) => {
  const handleQualifiedQuantityChange = value => {
    if (value === null || value === undefined) {
      form.value.qualifiedQuantity = 0;
      return;
    }
    const quantity = parseFloat(form.value.quantity) || 0;
    const qualified = parseFloat(value) || 0;
    form.value.qualifiedQuantity = qualified > quantity?quantity:qualified;
    form.value.qualifiedQuantity = qualified > quantity ? quantity : qualified;
    form.value.unqualifiedQuantity = Math.max(0, quantity - qualified);
  };
  const handleUnqualifiedQuantityChange = (value) => {
  const handleUnqualifiedQuantityChange = value => {
    if (value === null || value === undefined) {
      form.value.unqualifiedQuantity = 0;
      return;
    }
    const quantity = parseFloat(form.value.quantity) || 0;
    const unqualified = parseFloat(value) || 0;
    form.value.unqualifiedQuantity = unqualified > quantity?quantity:unqualified;
    form.value.unqualifiedQuantity =
      unqualified > quantity ? quantity : unqualified;
    form.value.qualifiedQuantity = Math.max(0, quantity - unqualified);
  };
@@ -571,7 +579,7 @@
        tableData.value = res.data || [];
        tableData.value = tableData.value.map(item => ({
          ...item,
          id: null
          id: null,
        }));
      })
      .catch(error => {
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -1,166 +1,211 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增原材料检验' : operationType === 'view' ? '查看原材料检验' : '编辑原材料检验'"
        width="70%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增原材料检验' : operationType === 'view' ? '查看原材料检验' : '编辑原材料检验'"
               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="supplier">
              <el-select
                  v-model="form.supplier"
                  placeholder="请选择"
                  clearable
                  :disabled="isViewMode || supplierQuantityDisabled"
              >
                <el-option
                    v-for="item in supplierList"
                    :key="item.id"
                    :label="item.supplierName"
                    :value="item.supplierName"
                />
            <el-form-item label="供应商:"
                          prop="supplier">
              <el-select v-model="form.supplier"
                         placeholder="请选择"
                         clearable
                         :disabled="isViewMode || supplierQuantityDisabled">
                <el-option v-for="item in supplierList"
                           :key="item.id"
                           :label="item.supplierName"
                           :value="item.supplierName" />
              </el-select>
            </el-form-item>
          </el-col>
          <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="isViewMode || operationType === 'edit'"
                  style="width: 100%"
              />
            <el-form-item label="产品名称:"
                          prop="productId">
              <el-tree-select v-model="form.productId"
                              placeholder="请选择"
                              filterable
                              clearable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              :disabled="isViewMode || 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="isViewMode || operationType === 'edit'"
                         filterable readonly @change="handleChangeModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="form.productModelId"
                         placeholder="请选择"
                         clearable
                         :disabled="isViewMode || 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-col :span="12">
            <el-form-item label="指标选择:" prop="testStandardId">
              <el-select
                v-model="form.testStandardId"
                placeholder="请选择指标"
                clearable
                @change="handleTestStandardChange"
                style="width: 100%"
                :disabled="isViewMode"
              >
                <el-option
                  v-for="item in testStandardOptions"
                  :key="item.id"
                  :label="item.standardName || item.standardNo"
                  :value="item.id"
                />
            <el-form-item label="指标选择:"
                          prop="testStandardId">
              <el-select v-model="form.testStandardId"
                         placeholder="请选择指标"
                         clearable
                         @change="handleTestStandardChange"
                         style="width: 100%"
                         :disabled="isViewMode">
                <el-option v-for="item in testStandardOptions"
                           :key="item.id"
                           :label="item.standardName || item.standardNo"
                           :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 label="单位:"
                          prop="unit">
              <el-input v-model="form.unit"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入"
                               clearable :precision="2" :disabled="isViewMode || supplierQuantityDisabled"/>
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.01"
                               :min="0"
                               style="width: 100%"
                               v-model="form.quantity"
                               placeholder="请输入"
                               clearable
                               :precision="2"
                               :disabled="isViewMode || supplierQuantityDisabled" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="合格数量:" prop="qualifiedQuantity">
              <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%"
                               v-model="form.qualifiedQuantity" placeholder="请输入" :precision="2"
                               @change="onQualifiedChange" :disabled="isViewMode"/>
            <el-form-item label="合格数量:"
                          prop="qualifiedQuantity">
              <el-input-number :step="0.01"
                               :min="0"
                               :max="form.quantity || 0"
                               style="width: 100%"
                               v-model="form.qualifiedQuantity"
                               placeholder="请输入"
                               :precision="2"
                               @change="onQualifiedChange"
                               :disabled="isViewMode" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不合格数量:" prop="unqualifiedQuantity">
              <el-input-number :step="0.01" :min="0" :max="form.quantity || 0" style="width: 100%"
                               v-model="form.unqualifiedQuantity" placeholder="请输入" :precision="2"
                               @change="onUnqualifiedChange" :disabled="isViewMode"/>
            <el-form-item label="不合格数量:"
                          prop="unqualifiedQuantity">
              <el-input-number :step="0.01"
                               :min="0"
                               :max="form.quantity || 0"
                               style="width: 100%"
                               v-model="form.unqualifiedQuantity"
                               placeholder="请输入"
                               :precision="2"
                               @change="onUnqualifiedChange"
                               :disabled="isViewMode" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检测单位:" prop="checkCompany">
              <el-input v-model="form.checkCompany" placeholder="请输入" clearable :disabled="isViewMode"/>
            <el-form-item label="检测单位:"
                          prop="checkCompany">
              <el-input v-model="form.checkCompany"
                        placeholder="请输入"
                        clearable
                        :disabled="isViewMode" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测结果:" prop="checkResult">
              <el-select v-model="form.checkResult" :disabled="isViewMode">
                <el-option label="合格" value="合格"/>
                <el-option label="不合格" value="不合格"/>
                <el-option label="部分合格" value="部分合格"/>
            <el-form-item label="检测结果:"
                          prop="checkResult">
              <el-select v-model="form.checkResult"
                         :disabled="isViewMode">
                <el-option label="合格"
                           value="合格" />
                <el-option label="不合格"
                           value="不合格" />
                <el-option label="部分合格"
                           value="部分合格" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-select v-model="form.checkName" placeholder="请选择" clearable style="width: 100%" :disabled="isViewMode">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
            <el-form-item label="检验员:"
                          prop="checkName">
              <el-select v-model="form.checkName"
                         placeholder="请选择"
                         clearable
                         style="width: 100%"
                         :disabled="isViewMode">
                <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%"
                  :disabled="isViewMode"
              />
            <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%"
                              :disabled="isViewMode" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
<!--      <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"
          height="400"
      >
      <!--      <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"
                height="400">
        <template #slot="{ row }">
          <el-input v-model="row.testValue" clearable :disabled="isViewMode"/>
          <el-input v-model="row.testValue"
                    clearable
                    :disabled="isViewMode" />
        </template>
      </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <template v-if="!isViewMode">
            <el-button type="primary" @click="submitForm">确认</el-button>
            <el-button type="primary"
                       @click="submitForm">确认</el-button>
            <el-button @click="closeDia">取消</el-button>
          </template>
          <el-button v-else @click="closeDia">关闭</el-button>
          <el-button v-else
                     @click="closeDia">关闭</el-button>
        </div>
      </template>
    </el-dialog>
@@ -168,358 +213,393 @@
</template>
<script setup>
import {ref, reactive, toRefs, computed, getCurrentInstance, nextTick} from "vue";
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {qualityInspectParamDel, qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js";
import {userListNoPage} from "@/api/system/user.js";
  import {
    ref,
    reactive,
    toRefs,
    computed,
    getCurrentInstance,
    nextTick,
  } from "vue";
  import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import {
    qualityInspectAdd,
    qualityInspectUpdate,
  } from "@/api/qualityManagement/rawMaterialInspection.js";
  import {
    qualityInspectParamDel,
    qualityInspectParamInfo,
  } from "@/api/qualityManagement/qualityInspectParam.js";
  import {
    qualityInspectDetailByProductId,
    getQualityTestStandardParamByTestStandardId,
  } from "@/api/qualityManagement/metricMaintenance.js";
  import { userListNoPage } from "@/api/system/user.js";
const {proxy} = getCurrentInstance()
const emit = defineEmits(['close'])
  const { proxy } = getCurrentInstance();
  const emit = defineEmits(["close"]);
const dialogFormVisible = ref(false);
const operationType = ref('')
const data = reactive({
  form: {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    testStandardId: "",
    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"}],
    testStandardId: [{required: false, message: "请选择指标", trigger: "change"}],
    unit: [{required: false, message: "请输入", trigger: "blur"}],
    quantity: [{required: true, message: "请输入", trigger: "blur"}],
    qualifiedQuantity: [{required: true, message: "请输入", trigger: "blur"}],
    unqualifiedQuantity: [{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 dialogFormVisible = ref(false);
  const operationType = ref("");
  const data = reactive({
    form: {
      checkTime: "",
      supplier: "",
      checkName: "",
      productName: "",
      productId: "",
      productModelId: "",
      model: "",
      testStandardId: "",
      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" },
      ],
      testStandardId: [
        { required: false, message: "请选择指标", trigger: "change" },
      ],
      unit: [{ required: false, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      qualifiedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
      unqualifiedQuantity: [
        { 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 supplierList = ref([]);
const productOptions = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
const modelOptions = ref([]);
const userList = ref([]); // æ£€éªŒå‘˜ä¸‹æ‹‰åˆ—表
  const { form, rules } = toRefs(data);
  const supplierList = ref([]);
  const productOptions = ref([]);
  const currentProductId = ref(0);
  const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
  const modelOptions = ref([]);
  const userList = ref([]); // æ£€éªŒå‘˜ä¸‹æ‹‰åˆ—表
// æ˜¯å¦ä¸ºæŸ¥çœ‹æ¨¡å¼
const isViewMode = computed(() => operationType.value === 'view');
  // æ˜¯å¦ä¸ºæŸ¥çœ‹æ¨¡å¼
  const isViewMode = computed(() => operationType.value === "view");
// ç¼–辑时:productMainId æˆ– purchaseLedgerId ä»»ä¸€æœ‰å€¼åˆ™ä¾›åº”商、数量置灰
const supplierQuantityDisabled = computed(() => {
  const v = form.value || {};
  return !!(v.productMainId != null || v.purchaseLedgerId != null);
});
// æ‰“开弹框
const openDialog = async (type, row) => {
  operationType.value = type;
  getOptions().then((res) => {
    supplierList.value = res.data;
  // ç¼–辑时:productMainId æˆ– purchaseLedgerId ä»»ä¸€æœ‰å€¼åˆ™ä¾›åº”商、数量置灰
  const supplierQuantityDisabled = computed(() => {
    const v = form.value || {};
    return !!(v.productMainId != null || v.purchaseLedgerId != null);
  });
  try {
    const userRes = await userListNoPage();
    userList.value = userRes.data || [];
  } catch (e) {
    console.error("加载检验员列表失败", e);
    userList.value = [];
  }
  // å…ˆé‡ç½®è¡¨å•数据(保持字段完整,避免弹窗首次渲染时触发必填红框“闪一下”)
    form.value = {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    testStandardId: "",
    unit: "",
    quantity: "",
    checkCompany: "",
    checkResult: "",
  }
  testStandardOptions.value = [];
  tableData.value = [];
  // å…ˆç¡®ä¿äº§å“æ ‘已加载,否则编辑时产品/规格型号无法反显
  await getProductOptions();
  if (operationType.value === 'edit' || operationType.value === 'view') {
    // å…ˆä¿å­˜ testStandardId,避免被清空
    const savedTestStandardId = row.testStandardId;
    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) {
      // å…ˆåŠ è½½æŒ‡æ ‡é€‰é¡¹
      let params = {
        productId: currentProductId.value,
        inspectType: 0
      }
      qualityInspectDetailByProductId(params).then(res => {
        testStandardOptions.value = res.data || [];
        // ä½¿ç”¨ nextTick å’Œ setTimeout ç¡®ä¿é€‰é¡¹å·²ç»æ¸²æŸ“到 DOM
        nextTick(() => {
          setTimeout(() => {
            // å¦‚果编辑数据中有 testStandardId,则设置并加载对应的参数
            if (savedTestStandardId) {
              // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
              const matchedOption = testStandardOptions.value.find(item =>
                item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
              );
              if (matchedOption) {
                // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                form.value.testStandardId = matchedOption.id;
                // ç¼–辑保留原检验值,直接拉取原参数数据
                getQualityInspectParamList(row.id);
              } else {
                // å¦‚果找不到匹配项,尝试直接使用原值
                console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value);
                form.value.testStandardId = savedTestStandardId;
                getQualityInspectParamList(row.id);
              }
            } else {
              // å¦åˆ™ä½¿ç”¨æ—§çš„逻辑
              getQualityInspectParamList(row.id);
            }
          }, 100);
        });
      });
    } 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 openDialog = async (type, row) => {
    operationType.value = type;
    getOptions().then(res => {
      supplierList.value = res.data;
    });
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; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
    try {
      const userRes = await userListNoPage();
      userList.value = userRes.data || [];
    } catch (e) {
      console.error("加载检验员列表失败", e);
      userList.value = [];
    }
    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
    // å…ˆé‡ç½®è¡¨å•数据(保持字段完整,避免弹窗首次渲染时触发必填红框“闪一下”)
    form.value = {
      checkTime: "",
      supplier: "",
      checkName: "",
      productName: "",
      productId: "",
      productModelId: "",
      model: "",
      testStandardId: "",
      unit: "",
      quantity: "",
      checkCompany: "",
      checkResult: "",
    };
    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") {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        qualityInspectUpdate(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      }
    }
  })
}
const getList = () => {
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
  let params = {
    productId: currentProductId.value,
    inspectType: 0
  }
  qualityInspectDetailByProductId(params).then(res => {
    // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
    testStandardOptions.value = res.data || [];
    // æ¸…空表格数据,等待用户选择指标
    tableData.value = [];
    // æ¸…空指标选择
    form.value.testStandardId = '';
  })
}
// æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
const handleTestStandardChange = (testStandardId) => {
  if (!testStandardId) {
    tableData.value = [];
    return;
  }
  tableLoading.value = true;
  getQualityTestStandardParamByTestStandardId(testStandardId).then(res => {
    tableData.value = res.data || [];
    tableData.value = tableData.value.map(item => ({
      ...item,
      id: null
    }));
  }).catch(error => {
    console.error('获取标准参数失败:', error);
    tableData.value = [];
  }).finally(() => {
    tableLoading.value = false;
  })
}
const getQualityInspectParamList = (id) => {
  qualityInspectParamInfo(id).then(res => {
    tableData.value = res.data;
  })
}
// è‡ªåŠ¨è®¡ç®—åˆæ ¼æ•°é‡å˜åŒ–æ—¶çš„ä¸åˆæ ¼æ•°é‡
const onQualifiedChange = (value) => {
  if (form.value.quantity !== undefined && form.value.quantity !== null) {
    const maxUnqualified = form.value.quantity - value;
    if (maxUnqualified >= 0) {
      form.value.unqualifiedQuantity = maxUnqualified;
    } else {
      form.value.qualifiedQuantity = form.value.quantity;
      form.value.unqualifiedQuantity = 0;
    // å…ˆç¡®ä¿äº§å“æ ‘已加载,否则编辑时产品/规格型号无法反显
    await getProductOptions();
    if (operationType.value === "edit" || operationType.value === "view") {
      // å…ˆä¿å­˜ testStandardId,避免被清空
      const savedTestStandardId = row.testStandardId;
      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) {
        // å…ˆåŠ è½½æŒ‡æ ‡é€‰é¡¹
        let params = {
          productId: currentProductId.value,
          inspectType: 0,
        };
        qualityInspectDetailByProductId(params).then(res => {
          testStandardOptions.value = res.data || [];
          // ä½¿ç”¨ nextTick å’Œ setTimeout ç¡®ä¿é€‰é¡¹å·²ç»æ¸²æŸ“到 DOM
          nextTick(() => {
            setTimeout(() => {
              // å¦‚果编辑数据中有 testStandardId,则设置并加载对应的参数
              if (savedTestStandardId) {
                // ç¡®ä¿ç±»åž‹åŒ¹é…ï¼ˆitem.id å¯èƒ½æ˜¯æ•°å­—或字符串)
                const matchedOption = testStandardOptions.value.find(
                  item =>
                    item.id == savedTestStandardId ||
                    String(item.id) === String(savedTestStandardId)
                );
                if (matchedOption) {
                  // ç¡®ä¿ä½¿ç”¨åŒ¹é…é¡¹çš„ id(保持类型一致)
                  form.value.testStandardId = matchedOption.id;
                  // ç¼–辑保留原检验值,直接拉取原参数数据
                  getQualityInspectParamList(row.id);
                } else {
                  // å¦‚果找不到匹配项,尝试直接使用原值
                  console.warn(
                    "未找到匹配的指标选项,testStandardId:",
                    savedTestStandardId,
                    "可用选项:",
                    testStandardOptions.value
                  );
                  form.value.testStandardId = savedTestStandardId;
                  getQualityInspectParamList(row.id);
                }
              } else {
                // å¦åˆ™ä½¿ç”¨æ—§çš„逻辑
                getQualityInspectParamList(row.id);
              }
            }, 100);
          });
        });
      } else {
        getQualityInspectParamList(row.id);
      }
    }
  }
};
// è‡ªåŠ¨è®¡ç®—ä¸åˆæ ¼æ•°é‡å˜åŒ–æ—¶çš„åˆæ ¼æ•°é‡
const onUnqualifiedChange = (value) => {
  if (form.value.quantity !== undefined && form.value.quantity !== null) {
    const maxQualified = form.value.quantity - value;
    if (maxQualified >= 0) {
      form.value.qualifiedQuantity = maxQualified;
    } else {
      form.value.unqualifiedQuantity = form.value.quantity;
      form.value.qualifiedQuantity = 0;
    // æœ€åŽå†æ‰“开弹窗,并清理校验态,避免必填提示闪烁
    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 closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  form.value.testStandardId = '';
  dialogFormVisible.value = false;
  emit('close')
};
defineExpose({
  openDialog,
});
  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") {
          qualityInspectAdd(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        } else {
          qualityInspectUpdate(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
          });
        }
      }
    });
  };
  const getList = () => {
    if (!currentProductId.value) {
      testStandardOptions.value = [];
      tableData.value = [];
      return;
    }
    let params = {
      productId: currentProductId.value,
      inspectType: 0,
    };
    qualityInspectDetailByProductId(params).then(res => {
      // ä¿å­˜ä¸‹æ‹‰æ¡†é€‰é¡¹æ•°æ®
      testStandardOptions.value = res.data || [];
      // æ¸…空表格数据,等待用户选择指标
      tableData.value = [];
      // æ¸…空指标选择
      form.value.testStandardId = "";
    });
  };
  // æŒ‡æ ‡é€‰æ‹©å˜åŒ–处理
  const handleTestStandardChange = testStandardId => {
    if (!testStandardId) {
      tableData.value = [];
      return;
    }
    tableLoading.value = true;
    getQualityTestStandardParamByTestStandardId(testStandardId)
      .then(res => {
        tableData.value = res.data || [];
        tableData.value = tableData.value.map(item => ({
          ...item,
          id: null,
        }));
      })
      .catch(error => {
        console.error("获取标准参数失败:", error);
        tableData.value = [];
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const getQualityInspectParamList = id => {
    qualityInspectParamInfo(id).then(res => {
      tableData.value = res.data;
    });
  };
  // è‡ªåŠ¨è®¡ç®—åˆæ ¼æ•°é‡å˜åŒ–æ—¶çš„ä¸åˆæ ¼æ•°é‡
  const onQualifiedChange = value => {
    if (form.value.quantity !== undefined && form.value.quantity !== null) {
      const maxUnqualified = form.value.quantity - value;
      if (maxUnqualified >= 0) {
        form.value.unqualifiedQuantity = maxUnqualified;
      } else {
        form.value.qualifiedQuantity = form.value.quantity;
        form.value.unqualifiedQuantity = 0;
      }
    }
  };
  // è‡ªåŠ¨è®¡ç®—ä¸åˆæ ¼æ•°é‡å˜åŒ–æ—¶çš„åˆæ ¼æ•°é‡
  const onUnqualifiedChange = value => {
    if (form.value.quantity !== undefined && form.value.quantity !== null) {
      const maxQualified = form.value.quantity - value;
      if (maxQualified >= 0) {
        form.value.qualifiedQuantity = maxQualified;
      } else {
        form.value.unqualifiedQuantity = form.value.quantity;
        form.value.qualifiedQuantity = 0;
      }
    }
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    tableData.value = [];
    testStandardOptions.value = [];
    form.value.testStandardId = "";
    dialogFormVisible.value = false;
    emit("close");
  };
  defineExpose({
    openDialog,
  });
</script>
<style scoped>
</style>