Merge remote-tracking branch 'origin/dev_长治_健齿齿科器材' into dev_长治_健齿齿科器材
已修改24个文件
964 ■■■■ 文件已修改
src/api/inventoryManagement/stockInventory.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/nonconformingManagement.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/New.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Qualified.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Unqualified.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockReport/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/MaterialRequisitionDialog.vue 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/Input.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/formDia.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/index.vue 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/returnOrder/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 331 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockInventory.js
@@ -65,4 +65,13 @@
        url: "/stockInventory/getMaterials",
        method: "get",
    });
}
}
// 获取库存树(产品->规格/型号->批号->供应商)
export const getStockInventoryAll = (params = {}) => {
    return request({
        url: "/stockInventory/getStockInventoryAll",
        method: "get",
        params,
    });
};
src/api/qualityManagement/nonconformingManagement.js
@@ -48,3 +48,13 @@
    data: query,
  });
}
// 下载返工附件
export function downloadReturnRecord(id) {
  return request({
    url: "/quality/qualityUnqualified/downloadReturnRecord",
    method: "get",
    params: { id },
    responseType: "blob",
  });
}
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -29,7 +29,7 @@
            <div>
                <el-button @click="handleOut">导出</el-button>
                <el-button type="danger" plain @click="handleDelete">删除</el-button>
                <el-button type="primary" plain @click="handlePrint">打印</el-button>
                <!-- <el-button type="primary" plain @click="handlePrint">打印</el-button> -->
            </div>
        </div>
        <div class="table_list">
@@ -45,12 +45,30 @@
            >
                <el-table-column align="center" type="selection" width="55" />
                <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
        <!-- <el-table-column
            label="出库批次"
            prop="outboundBatches"
            min-width="100"
            show-overflow-tooltip
        />
        /> -->
        <el-table-column
            label="批号"
            min-width="120"
            show-overflow-tooltip
        >
          <template #default="scope">
            {{ scope.row.batchNo || "" }}
          </template>
        </el-table-column>
        <el-table-column
            label="供应商"
            min-width="160"
            show-overflow-tooltip
        >
          <template #default="scope">
            {{ scope.row.supplierName || scope.row.customer || "" }}
          </template>
        </el-table-column>
                <el-table-column
                    label="出库日期"
                    prop="createTime"
@@ -424,8 +442,14 @@
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.code || ''}</span>
              <div>
                <span class="label">批号:</span>
                <span class="value">${item.batchNo || item.outboundBatches || ''}</span>
              </div>
              <div>
                <span class="label">单号:</span>
                <span class="value">${item.code || ''}</span>
              </div>
            </div>
          </div>
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -54,10 +54,24 @@
                         label="序号"
                         type="index"
                         width="60"/>
        <el-table-column label="入库批次"
        <!-- <el-table-column label="入库批次"
                         prop="inboundBatches"
                         width="280"
                         show-overflow-tooltip/>
                         show-overflow-tooltip/> -->
        <el-table-column label="批号"
                         min-width="160"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ scope.row.batchNo || '' }}
          </template>
        </el-table-column>
        <el-table-column label="供应商"
                         min-width="200"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ scope.row.supplierName || scope.row.customer || '' }}
          </template>
        </el-table-column>
        <el-table-column label="入库时间"
                         prop="createTime"
                         show-overflow-tooltip/>
src/views/inventoryManagement/stockManagement/New.vue
@@ -37,6 +37,35 @@
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <el-form-item label="批号" prop="batchNo" :rules="[{ required: true, message: '请输入批号', trigger: 'blur' }]">
          <el-input v-model="formState.batchNo" placeholder="请输入批号" clearable />
        </el-form-item>
        <el-form-item
          label="供应商"
          prop="customer"
          :rules="[{ required: true, message: '请选择供应商', trigger: 'change' }]"
        >
          <el-select
            v-model="formState.customer"
            placeholder="请选择供应商"
            filterable
            clearable
            allow-create
            :reserve-keyword="true"
            :default-first-option="false"
          >
            <el-option
              v-for="item in supplierList"
              :key="item.id"
              :label="item.supplierName"
              :value="item.supplierName"
            >
              {{ item.supplierName}}
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item
            label="库存数量"
            prop="qualitity"
@@ -74,10 +103,11 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import {ref, computed, getCurrentInstance, watch} from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {createStockInventory} from "@/api/inventoryManagement/stockInventory.js";
import {createStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
import {getOptions as getSupplierOptions} from "@/api/procurementManagement/procurementLedger.js";
const props = defineProps({
  visible: {
@@ -101,6 +131,8 @@
  productName: "",
  productModelName: "",
  unit: "",
  batchNo: "",
  customer: "",
  qualitity: 0,
  warnNum: 0,
  remark: '',
@@ -126,11 +158,39 @@
    productModelId: undefined,
    productName: "",
    productModelName: "",
    description: '',
    unit: "",
    batchNo: "",
    customer: "",
    qualitity: 0,
    warnNum: 0,
    remark: '',
  };
  isShow.value = false;
};
const supplierList = ref([]);
const loadSuppliers = async () => {
  try {
    const res = await getSupplierOptions();
    // 复用采购台账筛选逻辑:isWhite=0 的供应商
    supplierList.value = (res?.data || []).filter(item => item.isWhite === 0);
  } catch (e) {
    console.error("获取供应商列表失败:", e);
    supplierList.value = [];
  }
};
watch(
  () => props.visible,
  (val) => {
    if (val) {
      loadSuppliers();
    }
  },
  { immediate: true }
);
// 产品选择处理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -2,9 +2,29 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <span class="search_title ml10">产品类型:</span>
        <el-radio-group v-model="productScope" class="qualified-product-scope" @change="onProductScopeChange">
          <el-radio-button label="成品">成品</el-radio-button>
          <el-radio-button label="其他产品">其他产品</el-radio-button>
        </el-radio-group>
        <!-- <span class="search_title ml10" style="margin-left: 20px">产品大类:</span>
        <el-input v-model="searchForm.productName"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/> -->
        <span class="search_title ml10" style="margin-left: 20px">规格型号:</span>
        <el-input v-model="searchForm.model"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10" style="margin-left: 20px">UID码:</span>
        <el-input v-model="searchForm.uidNo"
                  style="width: 200px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10" style="margin-left: 20px">批次号:</span>
        <el-input v-model="searchForm.batchNo"
                  style="width: 200px"
                  placeholder="请输入"
                  clearable/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
@@ -26,6 +46,8 @@
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="UID码" prop="uidNo" show-overflow-tooltip />
        <el-table-column label="批号" prop="batchNo" show-overflow-tooltip />
        <el-table-column label="供应商" prop="customer" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
@@ -70,7 +92,7 @@
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js";
const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
@@ -100,9 +122,27 @@
const data = reactive({
  searchForm: {
    productName: '',
    model: '',
    uidNo: '',
    batchNo: '',
  }
})
const { searchForm } = toRefs(data)
// 成品(2) / 其他产品(原材料1、半成品3),与产品类型字典一致;分页接口需支持 productType 或 productTypes
const productScope = ref('成品')
const getProductScopeParams = () => {
  if (productScope.value === '成品') {
    return { productType: 2 }
  }
  return { productTypes: '1,3' }
}
const onProductScopeChange = () => {
  page.current = 1
  getList()
}
// 查询列表
/** 搜索按钮操作 */
@@ -117,7 +157,7 @@
}
const getList = () => {
  tableLoading.value = true
  getStockInventoryListPage({ ...searchForm.value, ...page }).then(res => {
  getStockInventoryListPage({ ...searchForm.value, ...page, ...getProductScopeParams() }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
@@ -187,7 +227,7 @@
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/stockInventory/exportStockInventory", {}, '合格库存信息.xlsx')
    proxy.download("/stockInventory/exportStockInventory", { ...searchForm.value, ...getProductScopeParams() }, '合格库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
@@ -199,6 +239,10 @@
</script>
<style scoped lang="scss">
.qualified-product-scope {
  vertical-align: middle;
}
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
src/views/inventoryManagement/stockManagement/Unqualified.vue
@@ -7,6 +7,21 @@
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10" style="margin-left: 20px">规格型号:</span>
        <el-input v-model="searchForm.model"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10" style="margin-left: 20px">UID码:</span>
        <el-input v-model="searchForm.uidNo"
                  style="width: 200px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10" style="margin-left: 20px">批次号:</span>
        <el-input v-model="searchForm.batchNo"
                  style="width: 200px"
                  placeholder="请输入"
                  clearable/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
@@ -23,6 +38,8 @@
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="UID码" prop="uidNo" show-overflow-tooltip />
        <el-table-column label="批号" prop="batchNo" show-overflow-tooltip />
        <el-table-column label="供应商" prop="customer" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
@@ -89,6 +106,9 @@
const data = reactive({
  searchForm: {
    productName: '',
    model: '',
    uidNo: '',
    batchNo: '',
  }
})
const { searchForm } = toRefs(data)
src/views/inventoryManagement/stockReport/index.vue
@@ -168,13 +168,27 @@
             show-overflow-tooltip
             v-if="searchForm.reportType !== 'inout'"
           />
           <el-table-column
             label="入库批次"
             prop="inboundBatches"
             width="240"
             show-overflow-tooltip
             v-if="searchForm.reportType !== 'inout'"
           />
          <el-table-column
            label="批号"
            width="240"
            show-overflow-tooltip
            v-if="searchForm.reportType !== 'inout'"
          >
            <template #default="scope">
              {{ scope.row.batchNo || scope.row.inboundBatches || "" }}
            </template>
          </el-table-column>
          <el-table-column
            label="供应商"
            prop="supplierName"
            min-width="200"
            show-overflow-tooltip
            v-if="searchForm.reportType !== 'inout'"
          >
            <template #default="scope">
              {{ scope.row.supplierName || scope.row.customer || "" }}
            </template>
          </el-table-column>
           <el-table-column
             label="产品大类"
             prop="productName"
src/views/procurementManagement/procurementLedger/index.vue
@@ -86,6 +86,7 @@
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="UID码" prop="uidNo" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="数量"
@@ -120,10 +121,10 @@
                         prop="supplierName"
                          width="160"
                         show-overflow-tooltip />
        <el-table-column label="项目名称"
        <!-- <el-table-column label="项目名称"
                         prop="projectName"
                         width="320"
                         show-overflow-tooltip />
                         show-overflow-tooltip /> -->
        <el-table-column label="审批状态"
                         prop="approvalStatus"
                         width="100"
@@ -238,23 +239,6 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目名称"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="签订日期:"
                          prop="executionDate">
              <el-date-picker style="width: 100%"
@@ -266,6 +250,24 @@
                              clearable />
            </el-form-item>
          </el-col>
          <!-- <el-col :span="12">
            <el-form-item label="项目名称"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row :gutter="30">
          <!-- <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -409,6 +411,7 @@
                           prop="unit"
                           width="70" />
          <el-table-column label="UID码" prop="uidNo" />
          <el-table-column label="批次号" prop="batchNo" />
          <el-table-column label="数量"
                           prop="quantity"
                           width="70" />
@@ -579,6 +582,17 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="批次号:"
                          prop="batchNo">
              <el-input v-model="productForm.batchNo"
                        placeholder="请输入"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:"
@@ -963,6 +977,7 @@
      productCategory: "",
      productModelId: "",
      uidNo: "",
        batchNo: "",
      specificationModel: "",
      unit: "",
      quantity: "",
@@ -977,6 +992,7 @@
    productRules: {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      batchNo: [{ required: true, message: "请输入批次号", trigger: "blur" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
src/views/productionManagement/productionOrder/MaterialRequisitionDialog.vue
@@ -2,7 +2,7 @@
  <el-dialog
    v-model="visible"
    title="领料"
    width="1000px"
    width="1400px"
    top="3vh"
    :close-on-click-modal="false"
    destroy-on-close
@@ -19,11 +19,8 @@
            <el-table-column prop="productName" label="产品名称" min-width="150" />
            <el-table-column prop="model" label="型号" min-width="150" />
            <el-table-column prop="unit" label="单位" width="80" align="center" />
            <!-- <el-table-column prop="qualitity" label="可领用数量" width="100" align="center">
              <template #default="{ row }">
                {{ row.qualitity || 0 }}
              </template>
            </el-table-column> -->
            <el-table-column prop="customer" label="供应商" min-width="160" show-overflow-tooltip />
            <el-table-column prop="batchNo" label="批号" min-width="180" show-overflow-tooltip />
            <el-table-column prop="requisitionQty" label="领用数量" width="120" align="center">
              <template #default="{ row }">
                <el-input-number
@@ -61,13 +58,43 @@
    <el-dialog
      v-model="addDialogVisible"
      title="选择原材料"
      width="800px"
      width="1000px"
      top="5vh"
      :close-on-click-modal="false"
      append-to-body
    >
      <div class="material-filter" style="margin-bottom: 20px;">
        <el-select
          v-model="filterSupplier"
          placeholder="供应商"
          clearable
          filterable
          style="width: 220px"
        >
          <el-option
            v-for="opt in supplierFilterOptions"
            :key="opt"
            :label="opt"
            :value="opt"
          />
        </el-select>
        <el-select
          v-model="filterBatchNo"
          placeholder="批号"
          clearable
          filterable
          style="width: 220px; margin-left: 12px"
        >
          <el-option
            v-for="opt in batchFilterOptions"
            :key="opt"
            :label="opt"
            :value="opt"
          />
        </el-select>
      </div>
      <el-table
        :data="availableMaterials"
        :data="filteredMaterials"
        border
        style="width: 100%"
        height="50vh"
@@ -78,6 +105,8 @@
        <el-table-column prop="productName" label="产品名称" min-width="150" />
        <el-table-column prop="model" label="型号" min-width="150" />
        <el-table-column prop="unit" label="单位" width="80" align="center" />
        <el-table-column prop="customer" label="供应商" min-width="160" show-overflow-tooltip />
        <el-table-column prop="batchNo" label="批号" min-width="180" show-overflow-tooltip />
        <!-- <el-table-column prop="qualitity" label="可领用数量" width="100" align="center">
          <template #default="{ row }">
            {{ row.qualitity || 0 }}
@@ -129,6 +158,45 @@
const availableMaterials = ref([]);
const selectedMaterials = ref([]);
// 选择弹窗筛选条件(供应商/批号)
const filterSupplier = ref('');
const filterBatchNo = ref('');
// 将后端可能返回的字段做一下归一化:供应商/批号字段名可能不一致
const normalizeMaterial = (m) => {
  return {
    ...m,
    customer: m.customer ?? m.supplierName ?? '',
    batchNo: m.batchNo ?? m.batchNumber ?? m.batch_number ?? m.lotNo ?? '',
  };
};
const supplierFilterOptions = computed(() => {
  return Array.from(new Set(availableMaterials.value.map((m) => m.customer).filter(Boolean)));
});
const batchFilterOptions = computed(() => {
  const list = filterSupplier.value
    ? availableMaterials.value.filter((m) => m.customer === filterSupplier.value)
    : availableMaterials.value;
  return Array.from(new Set(list.map((m) => m.batchNo).filter(Boolean)));
});
const filteredMaterials = computed(() => {
  return availableMaterials.value.filter((m) => {
    if (filterSupplier.value && m.customer !== filterSupplier.value) return false;
    if (filterBatchNo.value && m.batchNo !== filterBatchNo.value) return false;
    return true;
  });
});
watch(filterSupplier, () => {
  // 如果当前“批号”不属于所选供应商,则清空
  if (filterBatchNo.value && !batchFilterOptions.value.includes(filterBatchNo.value)) {
    filterBatchNo.value = '';
  }
});
// 监听弹框打开,加载数据
watch(() => props.modelValue, (val) => {
  if (val && props.orderData) {
@@ -146,7 +214,7 @@
  if (bomId) {
    try {
      const res = await getMaterials({ bomId });
      materialsFromApi = res.data || [];
      materialsFromApi = (res.data || []).map(normalizeMaterial);
    } catch (error) {
      console.error('查询原材料列表失败:', error);
    }
@@ -164,7 +232,9 @@
        return {
          ...savedItem,
          qualitity: apiItem?.qualitity ?? savedItem.qualitity ?? 0,
          requisitionQty: savedItem.requisitionQty || 0
          requisitionQty: savedItem.requisitionQty || 0,
          customer: savedItem.customer ?? savedItem.supplierName ?? apiItem?.customer ?? '',
          batchNo: savedItem.batchNo ?? savedItem.batchNumber ?? apiItem?.batchNo ?? '',
        };
      });
    } catch (e) {
@@ -190,7 +260,9 @@
    const res = await getMaterials({ bomId });
    console.log('getMaterials返回数据:', res.data);
    // 直接展示所有数据,不过滤
    availableMaterials.value = res.data || [];
    availableMaterials.value = (res.data || []).map(normalizeMaterial);
    filterSupplier.value = '';
    filterBatchNo.value = '';
    selectedMaterials.value = [];
    addDialogVisible.value = true;
  } catch (error) {
@@ -222,7 +294,7 @@
    .map(item => ({
      ...item,
      requisitionQty: 0,
      remark: ''
      remark: '',
    }));
  if (newItems.length === 0) {
@@ -270,6 +342,7 @@
    visible.value = false;
    materialList.value = [];
    activeTab.value = 'material';
    emit('confirm');
  } catch (error) {
    console.error('保存领料失败:', error);
    ElMessage.error('保存领料失败');
src/views/productionManagement/productionOrder/New.vue
@@ -166,10 +166,7 @@
    const product = products[0];
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    const productNameArr = product.productName.split('-');
    if (productNameArr.length === 3 && productNameArr[0] && productNameArr[1] !== '') {
      formState.value.manufacturingTeam = productNameArr[1].charAt(0) + '类车间';
    }
    formState.value.manufacturingTeam = product.parentName.charAt(0) + '类车间';
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
src/views/productionManagement/productionOrder/index.vue
@@ -107,6 +107,7 @@
    <material-requisition-dialog
      v-model="materialRequisitionVisible"
      :order-data="currentOrderData"
      @confirm="handleQuery"
    />
  </div>
</template>
@@ -158,6 +159,11 @@
    width: '120px',
  },
  {
    label: "批号",
    prop: "batchNo",
    width: '120px',
  },
  {
    label: "工艺路线编号",
    prop: "processRouteCode",
    width: '200px',
src/views/productionManagement/productionReporting/Input.vue
@@ -71,6 +71,14 @@
    prop: 'uidNo',
  },
  {
    label: '供应商',
    prop: 'customer',
  },
  {
    label: '批号',
    prop: 'batchNo',
  },
  {
    label: '投入数量',
    prop: 'quantity',
  },
src/views/productionManagement/workOrder/index.vue
@@ -173,6 +173,13 @@
               label-width="120px">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="产品名称">
              <el-input v-model="reportForm.productName"
                        readonly
                        style="width: 300px" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="待生产数量">
              <el-input v-model="reportForm.planQuantity"
                        readonly
@@ -212,18 +219,6 @@
                    @input="handleScrapQtyInput" />
        </el-form-item></el-col>
        <el-col :span="12">
          <el-form-item label="检品数量"
                      prop="inspectedQuantity">
          <el-input v-model.number="reportForm.inspectedQuantity"
                    type="number"
                    min="0"
                    step="1"
                    style="width: 300px"
                    placeholder="请输入检品数量"
                    @input="handleInspectedQuantity"/>
        </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="班组信息">
          <el-select v-model="reportForm.userId"
                     style="width: 300px"
@@ -262,6 +257,8 @@
            <el-table-column prop="productName" label="产品名称" min-width="160" />
            <el-table-column prop="model" label="型号" min-width="150" />
            <el-table-column prop="unit" label="单位" width="90" align="center" />
            <el-table-column prop="customer" label="供应商" min-width="160" show-overflow-tooltip />
            <el-table-column prop="batchNo" label="批号" min-width="180" show-overflow-tooltip />
            <el-table-column prop="reportQty" label="领用数量" width="160" align="center">
              <template #default="{ row }">
                <el-input-number
@@ -269,8 +266,8 @@
                  :min="0"
                  :precision="2"
                  :controls="false"
                  :max="row.qualitity || 0"
                  :disabled="!row.qualitity"
                  :max="row.requisitionQty || 0"
                  :disabled="!row.requisitionQty"
                  style="width: 100%"
                />
              </template>
@@ -300,7 +297,7 @@
    <el-dialog
      v-model="addMaterialDialogVisible"
      title="选择原材料"
      width="1000px"
      width="1400px"
      top="5vh"
      :close-on-click-modal="false"
      append-to-body
@@ -320,7 +317,9 @@
          <el-table-column prop="productName" label="产品名称" min-width="160" />
          <el-table-column prop="model" label="型号" min-width="150" />
          <el-table-column prop="unit" label="单位" width="90" align="center" />
          <el-table-column prop="qualitity" label="可领用数量" width="140" align="center" />
          <el-table-column prop="customer" label="供应商" min-width="160" show-overflow-tooltip />
          <el-table-column prop="batchNo" label="批号" min-width="180" show-overflow-tooltip />
          <el-table-column prop="requisitionQty" label="可领用数量" width="140" align="center" />
        </el-table>
        <!-- 已选择明细展示放在报工弹框下方的 reportForm.drawMaterialList 表格里 -->
@@ -488,6 +487,8 @@
  const userOptions = ref([]);
  const deviceOptions = ref([]);
  const reportForm = reactive({
    // 报工弹框里“产品名称”只读回显
    productName: "",
    planQuantity: 0,
    totalInvestment: 0,
    quantity: null,
@@ -901,6 +902,8 @@
  const showReportDialog = async row => {
    currentReportRowData.value = row;
    processParamList.value = await getProcessParamList(row)
    // 兼容后端/表格字段命名:优先 row.productName,其次 row.productCategory
    reportForm.productName = row.productName ?? row.productCategory ?? ''
    reportForm.planQuantity = row.planQuantity;
    reportForm.totalInvestment = row.totalInvestment;
    reportForm.quantity =
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -50,6 +50,23 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="form.unit" placeholder="请输入" disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="批号:" prop="batchNo">
              <el-input
                v-model="form.batchNo"
                placeholder="请输入"
                clearable
                :disabled="operationType === 'edit'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="UID码:" prop="uidNo">
              <el-input v-model="form.uidNo" placeholder="请输入" disabled/>
            </el-form-item>
@@ -57,8 +74,8 @@
        </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="inspectedQuantity">
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inspectedQuantity" placeholder="请输入" clearable :precision="2"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -66,6 +83,9 @@
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2" :disabled="quantityDisabled"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -227,6 +247,15 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
const validateBatchNo = (rule, value, callback) => {
  if (value === undefined || value === null || String(value).trim() === '') {
    callback(new Error('请输入批号'));
    return;
  }
  callback();
};
const data = reactive({
  form: {
    checkTime: "",
@@ -239,6 +268,8 @@
    testStandardId: "",
    unit: "",
    uidNo: "",
    batchNo: "",
    inspectedQuantity: "",
    quantity: "",
    inspectedQuantity: "",
    inspectMaterialCondition: "",
@@ -255,6 +286,7 @@
    productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    testStandardId: [{required: false, message: "请选择指标", trigger: "change"}],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    inspectedQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    inspectedQuantity: [
      { required: true, message: "请输入检品数量", trigger: "blur" },
@@ -276,6 +308,7 @@
      }
    ],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }],
    checkResult: [{ required: true, message: "请输入", trigger: "change" }],
  },
});
@@ -439,6 +472,7 @@
  form.value.productModelId = undefined;
  form.value.unit = undefined;
  form.value.uidNo = undefined;
  form.value.batchNo = "";
  modelOptions.value = [];
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
src/views/qualityManagement/nonconformingManagement/components/formDia.vue
@@ -49,6 +49,18 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="批号:" prop="batchNo" :required="operationType === 'add'">
              <el-input
                v-model="form.batchNo"
                placeholder="请输入"
                clearable
                :disabled="operationType === 'edit'"
              />
            </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>
@@ -125,7 +137,7 @@
</template>
<script setup>
import {ref, reactive, toRefs} from "vue";
import {ref, reactive, toRefs, getCurrentInstance} from "vue";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {
  getQualityUnqualifiedInfo,
@@ -140,6 +152,19 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
const validateBatchNo = (rule, value, callback) => {
  if (operationType.value !== 'add') {
    callback();
    return;
  }
  if (value === undefined || value === null || String(value).trim() === '') {
    callback(new Error('请输入批号'));
    return;
  }
  callback();
};
const { rejection_handling } = proxy.useDict("rejection_handling")
const data = reactive({
  form: {
@@ -150,6 +175,7 @@
    productId: "",
    model: "",
    uidNo: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
@@ -171,6 +197,7 @@
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    dealName: [{ required: true, message: "请选择处理人", trigger: "change" }],
    batchNo: [{ validator: validateBatchNo, trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
@@ -201,6 +228,7 @@
      productId: '',
      model: '',
      uidNo: '',
      batchNo: '',
      unit: '',
      quantity: '',
      productName: '',
@@ -233,6 +261,7 @@
  form.value.model = undefined;
  form.value.unit = undefined;
  form.value.uidNo = undefined;
  form.value.batchNo = "";
  modelOptions.value = [];
  form.value.productName = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
src/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue
@@ -54,6 +54,13 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="批号:" prop="batchNo">
              <el-input v-model="form.batchNo" placeholder="—" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-input v-model="form.checkName" placeholder="请输入" clearable disabled/>
            </el-form-item>
@@ -121,7 +128,7 @@
</template>
<script setup>
import {ref, reactive, toRefs, computed} from "vue";
import {ref, reactive, toRefs, computed, getCurrentInstance} from "vue";
import {productTreeList} from "@/api/basicData/product.js";
import {
  getQualityUnqualifiedInfo,
@@ -142,6 +149,7 @@
    productName: "",
    productId: "",
    model: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
src/views/qualityManagement/nonconformingManagement/index.vue
@@ -63,7 +63,7 @@
import {onMounted, ref, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
import FormDia from "@/views/qualityManagement/nonconformingManagement/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {qualityUnqualifiedDel, qualityUnqualifiedListPage} from "@/api/qualityManagement/nonconformingManagement.js";
import {qualityUnqualifiedDel, qualityUnqualifiedListPage, downloadReturnRecord} from "@/api/qualityManagement/nonconformingManagement.js";
import InspectionFormDia from "@/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue";
import dayjs from "dayjs";
@@ -148,6 +148,11 @@
    prop: "uidNo",
  },
  {
    label: "批号",
    prop: "batchNo",
    width: 140,
  },
  {
    label: "单位",
    prop: "unit",
  },
@@ -181,7 +186,7 @@
    label: "操作",
    align: "center",
    fixed: "right",
    width: 100,
    width: 180,
    operation: [
      {
        name: "处理",
@@ -190,6 +195,14 @@
          openInspectionForm("edit", row);
        },
        disabled: (row) => row.inspectState === 1,
      },
      {
        name: "下载返工附件",
        type: "text",
        clickFun: (row) => {
          handleDownloadReturnRecord(row);
        },
        show: (row) => row.dealResult === "返工",
      },
    ],
  },
@@ -264,6 +277,33 @@
  })
};
// 下载返工附件
const handleDownloadReturnRecord = async (row) => {
  try {
    const blobData = await downloadReturnRecord(row.id);
    // 构建 Blob
    const blob = new Blob([blobData], {
      type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    });
    // 下载
    const url = window.URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = `返工附件.docx`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    window.URL.revokeObjectURL(url);
    proxy.$modal.msgSuccess("下载成功");
  } catch (error) {
    console.error("下载返工附件失败:", error);
    proxy.$modal.msgError("下载失败");
  }
};
// 删除
const handleDelete = () => {
  let ids = [];
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -122,6 +122,18 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="批号:" prop="batchNo">
              <el-input
                v-model="form.batchNo"
                placeholder="请输入"
                clearable
                :disabled="operationType === 'edit'"
              />
            </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/>
            </el-form-item>
@@ -134,6 +146,8 @@
              </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>
@@ -237,6 +251,15 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
const validateBatchNo = (rule, value, callback) => {
  if (value === undefined || value === null || String(value).trim() === '') {
    callback(new Error('请输入批号'));
    return;
  }
  callback();
};
const data = reactive({
  form: {
    checkTime: "",
@@ -247,6 +270,7 @@
    productModelId: "",
    model: "",
    uidNo: "",
    batchNo: "",
    testStandardId: "",
    unit: "",
    quantity: "",
@@ -286,6 +310,7 @@
      }
    ],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }],
    checkResult: [{ required: true, message: "请输入", trigger: "change" }],
  },
});
@@ -381,6 +406,7 @@
        testStandardId: "",
        unit: "",
        uidNo: "",
        batchNo: "",
        quantity: "",
        inspectedQuantity: "",
        inspectMaterialCondition: "",
@@ -470,6 +496,7 @@
  form.value.productModelId = undefined;
  form.value.unit = undefined;
  form.value.uidNo = undefined;
  form.value.batchNo = "";
  modelOptions.value = [];
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
src/views/qualityManagement/processInspection/index.vue
@@ -122,6 +122,11 @@
    prop: "uidNo",
  },
  {
    label: "批号",
    prop: "batchNo",
    width: 140,
  },
  {
    label: "单位",
    prop: "unit",
  },
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -134,12 +134,22 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测单位:" prop="checkCompany">
              <el-input v-model="form.checkCompany" placeholder="请输入" clearable/>
            <el-form-item label="批号:" prop="batchNo">
              <el-input
                v-model="form.batchNo"
                placeholder="请输入"
                clearable
                :disabled="operationType === 'edit'"
              />
            </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/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测结果:" prop="checkResult">
              <el-select v-model="form.checkResult">
@@ -148,6 +158,8 @@
              </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%">
@@ -251,6 +263,15 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
const validateBatchNo = (rule, value, callback) => {
  if (value === undefined || value === null || String(value).trim() === '') {
    callback(new Error('请输入批号'));
    return;
  }
  callback();
};
const data = reactive({
  form: {
    checkTime: "",
@@ -261,6 +282,7 @@
    productModelId: "",
    model: "",
    uidNo: "",
    batchNo: "",
    testStandardId: "",
    unit: "",
    quantity: "",
@@ -300,6 +322,7 @@
      }
    ],
    checkCompany: [{required: false, message: "请输入", trigger: "blur"}],
    batchNo: [{ required: true, validator: validateBatchNo, trigger: "blur" }],
    checkResult: [{required: true, message: "请选择检测结果", trigger: "change"}],
  },
});
@@ -393,6 +416,7 @@
    productModelId: "",
    model: "",
    uidNo: "",
    batchNo: "",
    testStandardId: "",
    unit: "",
    quantity: "",
@@ -482,6 +506,7 @@
  form.value.productModelId = undefined;
  form.value.unit = undefined;
  form.value.uidNo = undefined;
  form.value.batchNo = "";
  modelOptions.value = [];
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -124,6 +124,11 @@
    prop: "uidNo",
  },
  {
    label: "批号",
    prop: "batchNo",
    width: 140,
  },
  {
    label: "单位",
    prop: "unit",
  },
src/views/salesManagement/returnOrder/index.vue
@@ -145,7 +145,7 @@
  { label: "销售单号", prop: "salesContractNo", minWidth: 160 },
  { label: "业务员", prop: "salesman", minWidth: 120 },
  { label: "关联出库单号", prop: "shippingNo", minWidth: 170 },
  { label: "项目名称", prop: "projectName", minWidth: 180 },
  // { label: "项目名称", prop: "projectName", minWidth: 180 },
  { label: "项目阶段", prop: "projectStage", minWidth: 120 },
  { label: "制单人", prop: "maker", minWidth: 120 },
  { label: "结算人", prop: "settler", minWidth: 120 },
src/views/salesManagement/salesLedger/index.vue
@@ -47,6 +47,7 @@
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="批号" prop="batchNo" />
              <el-table-column label="UID码" prop="uidNo" />
              <el-table-column label="单位" prop="unit" />
                            <el-table-column label="产品状态"
                                                             width="100px"
@@ -111,8 +112,8 @@
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <!-- <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip /> -->
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
@@ -172,24 +173,12 @@
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="项目名称:" prop="projectName">
                            <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
                                                            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="付款方式">
                            <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
@@ -375,8 +364,25 @@
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="批号:" prop="batchNo">
              <el-select v-model="productForm.batchNo" placeholder="请选择" clearable filterable>
              <el-select v-model="productForm.batchNo"
                          placeholder="请选择"
                          clearable
                          filterable
                          @change="handleBatchNoChange">
                <el-option v-for="item in batchNoOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="供应商:" prop="customer">
              <el-select v-model="productForm.customer"
                          placeholder="请选择"
                          clearable
                          filterable
                          :disabled="!supplierOptions.length">
                <el-option v-for="item in supplierOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
@@ -533,6 +539,7 @@
                                        <th>产品名称</th>
                                        <th>规格型号</th>
                                        <th>单位</th>
                                        <th>UID码</th>
                                        <th>单价</th>
                                        <th>零售数量</th>
                                        <th>零售金额</th>
@@ -543,6 +550,7 @@
                                        <td>{{ product.productCategory || '' }}</td>
                                        <td>{{ product.specificationModel || '' }}</td>
                                        <td>{{ product.unit || '' }}</td>
                                        <td>{{ product.uidNo || '' }}</td>
                                        <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                                        <td>{{ product.quantity || '0' }}</td>
                                        <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
@@ -696,11 +704,11 @@
  delProduct,
  delLedgerFile, getProductInventory, saleOutboundExport,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import { getStockInventoryAll } from "@/api/inventoryManagement/stockInventory.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
import {getProductOrderBatchNoOptions} from "@/api/productionManagement/productionOrder.js";
// 由 /stockInventory/getStockInventoryAll 驱动“批号/供应商”联动
import {safeTrainingExport} from "@/api/safeProduction/safetyTrainingAssessment.js";
const userStore = useUserStore();
@@ -713,6 +721,7 @@
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const supplierOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
    current: 1,
@@ -761,6 +770,7 @@
const productFormData = reactive({
    productForm: {
        productCategory: "",
        customer: "",
        specificationModel: "",
    uidNo: "",
        unit: "",
@@ -776,6 +786,7 @@
        productCategory: [{ required: true, message: "请选择", trigger: "change" }],
        productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    batchNo: [{ required: true, message: "请选择", trigger: "change" }],
        customer: [{ required: true, message: "请选择", trigger: "change" }],
        specificationModel: [
            { required: true, message: "请选择", trigger: "change" },
        ],
@@ -944,64 +955,203 @@
            tableLoading.value = false;
        });
};
// 获取产品大类tree数据
const getProductOptions = () => {
let stockInventoryAllTree = [];
let batchNodeByBatchNo = new Map();
const normalizeStockInventoryTree = (nodes = []) => {
    const normalizeNodeValue = (node) => {
        // 后端有时会出现 id=null 的层级,这里给一个可用的 key
        if (node?.id !== null && node?.id !== undefined) return String(node.id);
        if (node?.nodeType === "batch") return String(node.batchNo ?? node.label ?? "");
        if (node?.nodeType === "customer") return String(node.customer ?? node.label ?? "");
        if (node?.nodeType === "model") return String(node.productModelId ?? node.model ?? node.label ?? "");
        return String(node.productName ?? node.label ?? "");
    };
    const normalized = (list) =>
        (list || []).map((n) => {
            const value = normalizeNodeValue(n);
            const label = n.label ?? n.productName ?? n.model ?? n.batchNo ?? n.customer ?? "";
            return {
                ...n,
                value,
                label,
                children: normalized(n.children),
            };
        });
    return normalized(nodes);
};
// 仅展示最多 3 个层级:第 1 层(product) -> 第 2 层(model) -> 第 3 层(batch),更深的节点不展示
const filterStockInventoryFirst3Levels = (nodes = []) => {
    const MAX_LEVEL = 3;
    const cloneAndFilterByLevel = (list = [], level = 1) => {
        return (list || [])
            .map((n) => {
                // 后续层级里如果还有 customer,直接剔除
                if (n.nodeType === "customer") return null;
                // 到达展示深度后,不再向下挂子节点
                if (level >= MAX_LEVEL) {
                    return { ...n, children: [] };
                }
                // 特例:batch 节点本身也不再展示 children(保持与接口节点语义一致)
                if (n.nodeType === "batch") {
                    return { ...n, children: [] };
                }
                return { ...n, children: cloneAndFilterByLevel(n.children, level + 1) };
            })
            .filter(Boolean);
    };
    return cloneAndFilterByLevel(nodes, 1);
};
const findNodeObjByValue = (nodes = [], value) => {
    for (let i = 0; i < (nodes || []).length; i++) {
        const node = nodes[i];
        if (String(node?.value) === String(value)) return node;
        const children = node?.children || [];
        if (children.length) {
            const found = findNodeObjByValue(children, value);
            if (found) return found;
        }
    }
    return null;
};
// 获取库存树(用于产品大类/规格型号联动)
const getProductOptions = async () => {
    // 返回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value;
    });
    const res = await getStockInventoryAll();
    const data = res?.data || [];
    stockInventoryAllTree = normalizeStockInventoryTree(data);
    productOptions.value = filterStockInventoryFirst3Levels(stockInventoryAllTree);
    return productOptions.value;
};
const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
};
// 获取tree子数据
// 获取tree子数据(先选产品,再选规格型号)
const getModels = (value) => {
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then((res) => {
        modelOptions.value = res;
    });
    const node = findNodeObjByValue(stockInventoryAllTree, value);
    if (!node) return;
    if (node.nodeType !== "product") return;
    // 选择产品后,重置下游字段
    productForm.value.productCategory = node.label;
    modelOptions.value = (node.children || [])
        .filter((c) => c.nodeType === "model")
        .map((m) => ({
            id: m.value,
            model: m.model ?? m.label ?? "",
            unit: m.unit ?? "",
            uidNo: m.uidNo ?? m.identifierCode ?? "",
        }));
    productForm.value.productModelId = null;
    productForm.value.specificationModel = "";
    productForm.value.uidNo = "";
    productForm.value.unit = "";
    productForm.value.batchNo = "";
    productForm.value.customer = "";
    productForm.value.taxInclusiveUnitPrice = "";
    productForm.value.taxInclusiveTotalPrice = "";
    productForm.value.taxExclusiveTotalPrice = "";
    modelOptions.value = modelOptions.value || [];
    batchNoOptions.value = [];
    supplierOptions.value = [];
    batchNodeByBatchNo = new Map();
};
// 规格型号选择后:回显 UID,并生成“批号下拉”
const getProductModel = (value) => {
    const index = modelOptions.value.findIndex((item) => item.id === value);
    if (index !== -1) {
        productForm.value.specificationModel = modelOptions.value[index].model;
        productForm.value.unit = modelOptions.value[index].unit;
    productForm.value.uidNo = modelOptions.value[index].uidNo || "";
    const modelNode = findNodeObjByValue(stockInventoryAllTree, value);
    if (!modelNode || modelNode.nodeType !== "model") return;
    const prevBatchNo = productForm.value.batchNo;
    const prevCustomer = productForm.value.customer;
    productForm.value.productModelId = modelNode.value;
    productForm.value.specificationModel = modelNode.model ?? modelNode.label ?? "";
    // 有些接口/树数据里可能不包含 unit,这种情况下不要覆盖编辑时已回显的值
    const nextUnit = modelNode.unit ?? "";
    if (nextUnit !== null && nextUnit !== undefined && String(nextUnit).trim() !== "") {
        productForm.value.unit = nextUnit;
    }
    // 有些接口/树数据里可能不包含 uidNo,这种情况下不要覆盖编辑时已回显的值
    const nextUidNo = modelNode.uidNo ?? modelNode.identifierCode ?? "";
    if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
        productForm.value.uidNo = nextUidNo;
    }
    const batchNodes = (modelNode.children || []).filter((b) => b.nodeType === "batch");
    batchNodeByBatchNo = new Map(
        batchNodes.map((b) => {
            const key = String(b.batchNo ?? b.value ?? b.label ?? "").trim();
            return [key, b];
        })
    );
    batchNoOptions.value = batchNodes.map((b) => ({
        label: String(b.batchNo ?? b.label ?? "").trim(),
        value: String(b.batchNo ?? b.value ?? b.label ?? "").trim(),
    }));
    // 批号不再属于新规格时,清空
    const batchValues = new Set(batchNoOptions.value.map((x) => x.value));
    if (!prevBatchNo || !batchValues.has(prevBatchNo)) {
        productForm.value.batchNo = "";
    }
    // 需要供应商:批号回显后再生成
    productForm.value.customer = "";
    supplierOptions.value = [];
    if (productForm.value.batchNo) {
        handleBatchNoChange(productForm.value.batchNo, prevCustomer);
    }
};
const handleBatchNoChange = (batchNo, prevCustomer) => {
    const safeBatchNo = String(batchNo ?? "").trim();
    if (!safeBatchNo || !batchNodeByBatchNo.size) {
        productForm.value.customer = "";
        supplierOptions.value = [];
        return;
    }
    const batchNode = batchNodeByBatchNo.get(String(safeBatchNo));
    if (!batchNode) {
        productForm.value.customer = "";
        supplierOptions.value = [];
        return;
    }
    // UID码可能来源于 batch 节点(不同接口字段名不一致时尽量兜底)
    const nextUidNo = batchNode.uidNo ?? batchNode.identifierCode ?? batchNode.uid ?? "";
    if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
        productForm.value.uidNo = nextUidNo;
    }
    const customers = (batchNode.children || [])
        .filter((c) => c.nodeType === "customer")
        .map((c) => c.customer ?? c.label ?? "")
        .filter(Boolean);
    const uniq = Array.from(new Set(customers));
    supplierOptions.value = uniq.map((s) => ({ label: s, value: s }));
    // 编辑场景尽量回显;新增场景不回显
    if (prevCustomer && uniq.includes(prevCustomer)) {
        productForm.value.customer = prevCustomer;
    } else {
        productForm.value.specificationModel = null;
        productForm.value.unit = null;
        productForm.value.uidNo = null;
        productForm.value.customer = "";
    }
};
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;
    });
}
// 根据名称反查产品大类 id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
    if (!label) return null;
@@ -1195,8 +1345,9 @@
        return {
            // 台账字段
            productCategory: p.product || p.productName || "",
            productModelId: p.productModelId || "",
            specificationModel: p.specification || "",
      uidNo: p.uidNo || "",
            uidNo: p.uidNo || "",
            unit: p.unit || "",
            quantity: quantity,
            taxRate: taxRate,
@@ -1275,10 +1426,6 @@
};
const batchNoOptions = ref([]);
const fetchBatchNoOptions = async () => {
    const res = await getProductOrderBatchNoOptions();
    batchNoOptions.value = res.data;
};
// 关闭弹框
const closeDia = () => {
    proxy.resetForm("formRef");
@@ -1302,25 +1449,44 @@
        productIndex.value = index;
        // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
        try {
            const options = productOptions.value && productOptions.value.length > 0
                ? productOptions.value
                : await getProductOptions();
            const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
            if (categoryId) {
                const models = await modelList({ id: categoryId });
                modelOptions.value = models || [];
                // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
                const currentModel = (modelOptions.value || []).find(
                    (m) => m.model === productForm.value.specificationModel
                );
            if (!productOptions.value || productOptions.value.length === 0) {
                await getProductOptions();
            }
            // 回显:根据“产品大类”反查产品节点
            const categoryKey = findNodeIdByLabel(productOptions.value, productForm.value.productCategory);
            if (categoryKey) {
                const categoryNode = findNodeObjByValue(stockInventoryAllTree, categoryKey);
                const models = (categoryNode?.children || [])
                    .filter((n) => n.nodeType === "model")
                    .map((m) => ({
                        id: m.value,
                        model: m.model ?? m.label ?? "",
                        unit: m.unit ?? "",
                        uidNo: m.uidNo ?? m.identifierCode ?? "",
                    }));
                modelOptions.value = models;
                // 根据当前规格型号回显
                const targetSpec = String(productForm.value.specificationModel ?? "").trim();
                const currentModel =
                    (models || []).find((m) => String(m.model ?? "").trim() === targetSpec) ||
                    (models || []).find((m) => String(m.model ?? "").trim().includes(targetSpec)) ||
                    (models || []).find((m) => targetSpec.includes(String(m.model ?? "").trim()));
                if (currentModel) {
                    productForm.value.customer = productForm.value.customer || row.customer || row.supplierName || "";
                    productForm.value.productModelId = currentModel.id;
                    getProductModel(currentModel.id);
                }
            }
        } catch (e) {
            // 加载失败时保持可编辑,不中断弹窗
            console.error("加载产品规格型号失败", e);
        }
        // 最终兜底:如果中途被重置清空,至少回显行数据里的 UID
        productForm.value.uidNo = row.uidNo ?? productForm.value.uidNo ?? "";
        // 最终兜底:同样保证单位不会因树数据缺失而被覆盖为空
        productForm.value.unit = row.unit ?? productForm.value.unit ?? "";
    } else {
        getProductOptions()
    }
@@ -1732,6 +1898,7 @@
                  <th>产品名称</th>
                  <th>规格型号</th>
                  <th>单位</th>
                  <th>UID码</th>
                  <th>单价</th>
                  <th>零售数量</th>
                  <th>零售金额</th>
@@ -1744,6 +1911,7 @@
                      <td>${product.productCategory || ''}</td>
                      <td>${product.specificationModel || ''}</td>
                      <td>${product.unit || ''}</td>
                      <td>${product.uidNo || ''}</td>
                      <td>${product.taxInclusiveUnitPrice || '0'}</td>
                      <td>${product.quantity || '0'}</td>
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
@@ -2250,7 +2418,6 @@
onMounted(() => {
    getList();
  fetchBatchNoOptions();
    userListNoPage().then(res => {
        userList.value = res.data;
    })