gongchunyi
3 天以前 7a40bd827a87152998a36694da6155b690008d66
feat: 质检绑定支持规格型号
已修改4个文件
127 ■■■■ 文件已修改
src/views/qualityManagement/finalInspection/components/formDia.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/metricBinding/index.vue 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -344,6 +344,7 @@
      // 并行加载规格型号和指标选项
      const params = {
        productId: currentProductId.value,
        productModelId: form.value.productModelId || undefined,
        inspectType: 2
      };
@@ -412,6 +413,8 @@
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 || '';
  // 规格型号变化后按 产品+规格型号 重新过滤可选指标
  getList(value);
}
const handleQualifiedQuantityChange = (value) => {
@@ -489,7 +492,7 @@
    }
  });
}
const getList = () => {
const getList = (modelId) => {
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
@@ -497,6 +500,7 @@
  }
  let params = {
    productId: currentProductId.value,
    productModelId: modelId ?? form.value.productModelId ?? undefined,
    inspectType: 2
  };
  qualityInspectDetailByProductId(params).then(res => {
src/views/qualityManagement/metricBinding/index.vue
@@ -106,6 +106,11 @@
        <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 prop="model" label="规格型号" min-width="120">
          <template #default="{ row }">
            {{ row.model || '—' }}
          </template>
        </el-table-column>
        <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>
@@ -126,11 +131,14 @@
      <div class="binding-dialog">
        <el-input
          v-model="productSearchKeyword"
          placeholder="搜索产品"
          placeholder="搜索产品或规格型号"
          clearable
          prefix-icon="Search"
          class="binding-search"
        />
        <div class="binding-tip">
          勾选产品(大类或具体产品)表示该类下所有规格通用;勾选规格型号表示只对该型号生效,两种可以同时选择。
        </div>
        <el-tree
          ref="productTreeRef"
          :key="productTreeKey"
@@ -138,7 +146,7 @@
          :data="productTreeData"
          show-checkbox
          check-strictly
          node-key="id"
          node-key="treeKey"
          :props="{ label: 'label', children: 'children', disabled: 'disabled' }"
          :filter-node-method="filterProductNode"
          class="product-binding-tree"
@@ -173,7 +181,7 @@
import { ref, reactive, toRefs, computed, watch, nextTick, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from 'element-plus'
import PIMTable from '@/components/PIMTable/PIMTable.vue'
import { productTreeList } from '@/api/basicData/product.js'
import { productTreeList, modelList } from '@/api/basicData/product.js'
import {
  qualityTestStandardListPage
} from '@/api/qualityManagement/metricMaintenance.js'
@@ -287,21 +295,14 @@
const productSearchKeyword = ref('')
const selectedProductIds = ref([])
const markParentNodesDisabled = (nodes) => {
  return (nodes || []).map((node) => {
    const children = node.children?.length ? markParentNodesDisabled(node.children) : []
    return {
      ...node,
      children,
      disabled: children.length > 0
    }
  })
}
// 树节点唯一 key:产品节点用 id,规格型号节点用 model-{id},避免与产品 id 冲突
const nodeKey = (node) => (node.isModel ? `model-${node.id}` : node.id)
const buildProductLabelMap = (nodes, map = {}) => {
  ;(nodes || []).forEach((node) => {
    if (node.id != null) {
      map[node.id] = node.label
    const key = nodeKey(node)
    if (key != null) {
      map[key] = node.label
    }
    if (node.children?.length) {
      buildProductLabelMap(node.children, map)
@@ -328,12 +329,42 @@
  productTreeRef.value?.filter(val)
})
// 为产品节点挂载规格型号子节点;无规格型号的产品保持可选
const attachProductModels = async (nodes) => {
  if (!Array.isArray(nodes)) return []
  const list = await Promise.all(
    nodes.map(async (node) => {
      const children = await attachProductModels(node.children)
      let modelChildren = []
      try {
        const res = await modelList({ id: node.id })
        modelChildren = (res || []).map((model) => ({
          id: model.id,
          label: model.model || model.productCode || `型号${model.id}`,
          isModel: true,
          productId: node.id,
          treeKey: `model-${model.id}`
        }))
      } catch (error) {
        console.error('获取产品规格型号失败:', error)
      }
      return {
        ...node,
        treeKey: nodeKey(node),
        children: [...children, ...modelChildren]
      }
    })
  )
  return list
}
const getProductTreeData = async () => {
  if (productTreeData.value?.length) return
  productTreeLoading.value = true
  try {
    const res = await productTreeList()
    productTreeData.value = markParentNodesDisabled(Array.isArray(res) ? res : [])
    const tree = Array.isArray(res) ? res : []
    productTreeData.value = await attachProductModels(tree)
  } catch (error) {
    console.error('获取产品树失败:', error)
  } finally {
@@ -416,6 +447,23 @@
  bindingSelectedRows.value = selection
}
// 勾选节点 -> 绑定项:产品节点绑定整个产品,规格型号节点绑定到具体型号
const checkedNodesToBindings = () => {
  const nodes = productTreeRef.value?.getCheckedNodes(true) || []
  const seen = new Set()
  return nodes
    .map((node) => (node.isModel
      ? { productId: node.productId, productModelId: node.id }
      : { productId: node.id, productModelId: null }))
    .filter((item) => item.productId != null)
    .filter((item) => {
      const key = `${item.productId}_${item.productModelId ?? ''}`
      if (seen.has(key)) return false
      seen.add(key)
      return true
    })
}
const openBindingDialog = () => {
  if (!currentStandard.value?.id) return
  selectedProductIds.value = []
@@ -438,13 +486,14 @@
const submitBinding = async () => {
  const testStandardId = currentStandard.value?.id
  if (!testStandardId) return
  const ids = (selectedProductIds.value || []).filter(Boolean)
  if (!ids.length) {
    proxy.$message.warning('请选择产品')
  const items = checkedNodesToBindings()
  if (!items.length) {
    proxy.$message.warning('请选择产品或规格型号')
    return
  }
  const payload = ids.map((pid) => ({
    productId: pid,
  const payload = items.map((item) => ({
    productId: item.productId,
    productModelId: item.productModelId ?? null,
    testStandardId
  }))
  await qualityTestStandardBindingAdd(payload)
@@ -618,6 +667,12 @@
  width: 100%;
}
.binding-tip {
  font-size: 12px;
  color: #909399;
  line-height: 18px;
}
.product-binding-tree {
  max-height: 360px;
  overflow-y: auto;
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -411,7 +411,11 @@
          modelOptions.value = res || [];
          // 同步回填 model / unit(有些接口返回的 row 里可能没带全)
          if (form.value.productModelId) {
            handleChangeModel(form.value.productModelId);
            const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId);
            if (selectedModel) {
              form.value.model = selectedModel.model || "";
              form.value.unit = selectedModel.unit || "";
            }
          }
        } catch (e) {
          console.error("加载规格型号失败", e);
@@ -423,6 +427,7 @@
        // 先加载指标选项
        let params = {
          productId: currentProductId.value,
          productModelId: form.value.productModelId || undefined,
          inspectType: 1,
          process: form.value.process || "",
        };
@@ -497,6 +502,8 @@
      modelOptions.value.find(item => item.id == value)?.model || "";
    form.value.unit =
      modelOptions.value.find(item => item.id == value)?.unit || "";
    // 规格型号变化后按 产品+规格型号 重新过滤可选指标
    getList(value);
  };
  const handleQualifiedQuantityChange = (value) => {
@@ -591,7 +598,7 @@
      }
    });
  };
  const getList = () => {
  const getList = (modelId) => {
    if (!currentProductId.value) {
      testStandardOptions.value = [];
      tableData.value = [];
@@ -600,6 +607,7 @@
    const processName = form.value.process || "";
    let params = {
      productId: currentProductId.value,
      productModelId: modelId ?? form.value.productModelId ?? undefined,
      inspectType: 1,
      process: processName,
    };
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -349,7 +349,11 @@
        modelOptions.value = res || [];
        // 同步回填 model / unit(有些接口返回的 row 里可能没带全)
        if (form.value.productModelId) {
          handleChangeModel(form.value.productModelId);
          const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId);
          if (selectedModel) {
            form.value.model = selectedModel.model || '';
            form.value.unit = selectedModel.unit || '';
          }
        }
      } catch (e) {
        console.error("加载规格型号失败", e);
@@ -361,6 +365,7 @@
      // 先加载指标选项
      let params = {
        productId: currentProductId.value,
        productModelId: form.value.productModelId || undefined,
        inspectType: 0
      }
      qualityInspectDetailByProductId(params).then(res => {
@@ -425,6 +430,8 @@
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 || '';
  // 规格型号变化后按 产品+规格型号 重新过滤可选指标
  getList(value);
}
const findNodeById = (nodes, productId) => {
@@ -483,7 +490,7 @@
  })
}
const getList = () => {
const getList = (modelId) => {
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
@@ -491,6 +498,7 @@
  }
  let params = {
    productId: currentProductId.value,
    productModelId: modelId ?? form.value.productModelId ?? undefined,
    inspectType: 0
  }
  qualityInspectDetailByProductId(params).then(res => {