新疆马铃薯
1.首页问题:挪新系统ui,需要确认一下页面数据完整。
2.协同办公:挪新系统
3.营销管理:客户往来取消回款金额字段,改为点击左侧客户时显示与该客户的所有订单信息,以及发货情况。销售可以选好对应的采购订单方便质量追溯。
4.采购管理:供应商往来同上逻辑,显示是否收货,也加上采购退货和采购报表功能。
5.采购加上设备备件选项,设备备件入库到备件库存。设备,仓储不足时做采购提醒。
6.仓储物流:得区分成品库和原料库(不存在半成品,成品只有一个产品,很好确认),原材料需要有批号,采集原料库需要做好仓库字段,让他们可以区分哪个仓库,然后把数采设备信息做一个实时的显示。总库存显示好当前存在的批次信息。
7.质量:只有不通过才需要填写对应的数据信息。在外侧做好选择通过不通过。过程,出厂检验无法对应到生产订单,那就对应到销售订单。
8.决策分析:基础数据分析和进销存分析,质量数据分析需要重新设计
已添加5个文件
已修改13个文件
2129 ■■■■ 文件已修改
src/api/procurementManagement/invoiceEntry.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/paymentEntry.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementInvoiceLedger.js 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/rawMaterialInspection.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/spareParts/index.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/New.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 202 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 289 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/receivingManagement/index.vue 320 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/receivingManagement/modal/ReceiptForm.vue 338 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/index.vue 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/dataDashboard/index.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentLedger/index.vue 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/invoiceEntry.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
// é‡‡è´­-来票登记接口
import request from "@/utils/request";
// æŸ¥è¯¢é‡‡è´­åˆåŒå·
export function getProduct(query) {
  return request({
    url: "/purchase/ledger/getProduct",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢id采购合同号
export function getPurchaseNoById(query) {
  return request({
    url: "/purchase/ledger/getPurchaseNoById",
    method: "get",
    params: query,
  });
}
// æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢è¯¦ç»†ä¿¡æ¯
export function getInfo(query) {
  return request({
    url: "/purchase/ledger/getInfo",
    method: "get",
    params: query,
  });
}
// ä¸»åˆ—表查询
export function gePurchaseList(query) {
  return request({
    url: "/purchase/ledger/list",
    method: "get",
    params: query,
  });
}
// ä¸»åˆ—表查询
export function getRegistrationById(query) {
  return request({
    url: "/purchase/registration/getRegistrationById",
    method: "get",
    params: query,
  });
}
// æ–°å¢žç¼–辑来票登记
export function addOrUpdateRegistration(query) {
  return request({
    url: "/purchase/registration/addOrUpdateRegistration",
    method: "post",
    data: query,
  });
}
// åˆ é™¤æ¥ç¥¨ç™»è®°
export function delRegistration(query) {
  return request({
    url: "/purchase/registration/delRegistration",
    method: "delete",
    data: query,
  });
}
// ä¸»åˆ—表查询
export function gePurchaseListPage(query) {
  return request({
    url: "/purchase/ledger/listPage",
    method: "get",
    params: query,
  });
}
src/api/procurementManagement/paymentEntry.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
// é‡‡è´­ä»˜æ¬¾ç™»è®°é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function registrationList(query) {
  return request({
    url: "/purchase/paymentRegistration/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
export function registrationInfo(query) {
  return request({
    url: "/purchase/paymentRegistration/" + query,
    method: "get",
  });
}
// æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢è¯¦æƒ…
export function byPurchaseId(query) {
  return request({
    url: "/purchase/paymentRegistration/byPurchaseId/" + query,
    method: "get",
  });
}
// æŸ¥è¯¢é‡‡è´­åˆåŒå·
export function getPurchaseNo() {
  return request({
    url: "/purchase/ledger/getPurchaseNo",
    method: "get",
  });
}
// æ–°å¢ž
export function paymentRegistrationAdd(query) {
  return request({
    url: "/purchase/paymentRegistration",
    method: "post",
    data: query,
  });
}
// ä¿®æ”¹
export function paymentRegistrationEdit(query) {
  return request({
    url: "/purchase/paymentRegistration",
    method: "put",
    data: query,
  });
}
// åˆ é™¤
export function paymentRegistrationDel(query) {
  return request({
    url: "/purchase/paymentRegistration/delete",
    method: "delete",
    data: query,
  });
}
// èŽ·å–å‘ç¥¨å·å’Œå‘ç¥¨é‡‘é¢
export function getTicketNo(query) {
  return request({
    url: "/purchase/registration/getTicketNo",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentHistoryList(query) {
  return request({
    url: "/purchase/paymentRegistration/paymentHistoryList",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentHistoryListPage(query) {
  return request({
    url: "/purchase/paymentRegistration/paymentHistoryListPage",
    method: "get",
    params: query,
  });
}
src/api/procurementManagement/procurementInvoiceLedger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
// é‡‡è´­-来票台账接口
import request from "@/utils/request";
// æŸ¥è¯¢åˆ—表
export function invoiceList(query) {
  return request({
    url: "/purchase/registration/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
// export function getInvoiceById(query) {
//   return request({
//     url: "/purchase/registration/getRegistrationById",
//     method: "get",
//     params: query,
//   });
// }
// æ–°å¢žã€ç¼–辑
export function addOrUpdateInvoice(query) {
  return request({
    url: "/purchase/invoice/addOrUpdateInvoice",
    method: "post",
    data: query,
  });
}
// åˆ é™¤
export function delInvoice(query) {
  return request({
    url: "/purchase/invoice/delInvoice",
    method: "delete",
    data: query,
  });
}
// åˆ é™¤æ¥ç¥¨å°è´¦
export function delRegistration(query) {
  return request({
    url: "/purchase/registration/delRegistration",
    method: "delete",
    data: query,
  });
}
// åˆ é™¤é™„ä»¶
export function delCommonFile(query) {
  return request({
    url: "/commonFile/delCommonFile",
    method: "delete",
    data: query,
  });
}
// å­è¡¨æ ¼æŸ¥è¯¢
export function productRecordList(query) {
  return request({
    url: "/purchase/registration/productRecordList",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢åˆ—表
export function invoiceListPage(query) {
  return request({
    url: "/sales/product/listPagePurchaseLedger",
    method: "get",
    params: query,
  });
}
export function productRecordPage(query) {
  return request({
    url: "/purchase/registration/productRecordPage",
    method: "get",
    params: query,
  });
}
export function productUploadFile(data) {
  return request({
    url: "/file/uploadFile",
    method: "post",
    data: data,
  });
}
// export function getProductRecordById(params) {
//   return request({
//     url: "/purchase/registration/getProductRecordById",
//     method: "get",
//     params: params,
//   });
// }
export function getProductRecordById(data) {
  return request({
    url: "/purchase/registration/getProductRecordById",
     method: "post",
    data: data,
  });
}
export function updateRegistration(data) {
  return request({
    url: "/purchase/registration/updateRegistration",
    method: "post",
    data: data,
  });
}
// æŸ¥è¯¢ä»˜æ¬¾ç™»è®°å­åˆ—表
export function registrationListPageGetById(query) {
  return request({
    url: "/purchase/registration/getById",
    method: "get",
    params: query,
  });
}
// ä¿®æ”¹ä»˜æ¬¾æµæ°´
export function updatePaymentRegistration(query) {
  return request({
    url: "/purchase/registration/updatePaymentRegistration",
    method: "put",
    data: query,
  });
}
// åˆ é™¤ä»˜æ¬¾æµæ°´
export function delPaymentRegistration(query) {
  return request({
    url: "/purchase/registration/delPaymentRegistration",
    method: "delete",
    data: query,
  });
}
src/api/qualityManagement/rawMaterialInspection.js
@@ -55,3 +55,12 @@
    })
}
// æ‰¹é‡å¿«é€Ÿæ£€éªŒ
export function batchQuickInspect(data) {
    return request({
        url: '/quality/qualityInspect/batchQuickInspect',
        method: 'post',
        data: data,
    })
}
src/views/equipmentManagement/spareParts/index.vue
@@ -75,6 +75,32 @@
            <el-form-item label="描述" prop="description">
              <el-input v-model="form.description"></el-input>
            </el-form-item>
            <el-form-item label="库存预警数量" prop="warnNum">
              <el-input-number
                v-model="form.warnNum"
                placeholder="请输入库存预警数量"
                :min="0"
                :step="0.1"
                :precision="2"
                style="width: 100%"
              ></el-input-number>
            </el-form-item>
            <el-form-item label="预警通知人" prop="notifyPersonId">
              <el-select
                v-model="form.notifyPersonId"
                placeholder="请选择预警通知人"
                filterable
                clearable
                style="width: 100%"
              >
                <el-option
                  v-for="user in userOptions"
                  :key="user.userId"
                  :label="user.nickName || user.userName"
                  :value="user.userId"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="ä»·æ ¼" prop="price">
              <el-input-number
                v-model="form.price"
@@ -88,8 +114,8 @@
          </el-form>
          <template #footer>
            <span class="dialog-footer">
              <el-button @click="dialogVisible = false" :disabled="formLoading">取消</el-button>
              <el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button>
              <el-button @click="dialogVisible = false" :disabled="formLoading">取消</el-button>
            </span>
          </template>
        </el-dialog>
@@ -134,6 +160,7 @@
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { userListNoPage } from "@/api/system/user.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage";
@@ -153,6 +180,7 @@
const operationType = ref('add')
// è®¾å¤‡é€‰é¡¹
const deviceOptions = ref([]);
const userOptions = ref([]);
// è¡¨å•引用
const formRef = ref(null);
// æŸ¥è¯¢å‚æ•°
@@ -222,6 +250,14 @@
    prop: "quantity",
  },
  {
    label: "库存预警数量",
    prop: "warnNum",
  },
  {
    label: "预警通知人",
    prop: "notifyPersonName",
  },
  {
    label: "描述",
    prop: "description",
  },
@@ -256,7 +292,9 @@
  status: '',
  description: '',
  deviceLedgerIds: [],
  price: null
  price: null,
  warnNum: null,
  notifyPersonId: null
});
// è¡¨å•验证规则
@@ -414,9 +452,20 @@
  }
};
// åŠ è½½ç”¨æˆ·åˆ—è¡¨
const loadUserOptions = async () => {
  try {
    const res = await userListNoPage();
    userOptions.value = res.data || [];
  } catch (error) {
    ElMessage.error('获取用户列表失败');
  }
};
// æ–°å¢žåˆ†ç±»
const addCategory = async () => {
  await loadDeviceName();
  await loadUserOptions();
  form.id = '';
  form.name = '';
  form.sparePartsNo = '';
@@ -425,6 +474,8 @@
  form.deviceLedgerIds = [];
  form.quantity = undefined;
  form.price = null;
  form.warnNum = null;
  form.notifyPersonId = null;
  operationType.value = 'add'
  dialogVisible.value = true;
};
@@ -432,6 +483,7 @@
// ç¼–辑分类
const editCategory = async (row) => {
  await loadDeviceName();
  await loadUserOptions();
  Object.assign(form, row);
  // å¦‚果后端返回的是 deviceIds å­—符串,需要转换为数组
  if (row.deviceIds && typeof row.deviceIds === 'string') {
src/views/inventoryManagement/stockManagement/New.vue
@@ -70,6 +70,15 @@
                           :max="formState.qualitity"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="入库时间"
                      prop="createTime">
          <el-date-picker v-model="formState.createTime"
                          type="datetime"
                          placeholder="请选择入库时间"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          format="YYYY-MM-DD HH:mm:ss"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="formState.remark"
@@ -123,6 +132,7 @@
    qualitity: 0,
    batchNo: null,
    warnNum: 0,
    createTime: "",
    remark: "",
  });
@@ -161,6 +171,7 @@
      qualitity: 0,
      batchNo: null,
      warnNum: 0,
      createTime: "",
      remark: "",
    };
    isShow.value = false;
src/views/procurementManagement/paymentLedger/index.vue
@@ -17,7 +17,7 @@
      <div></div>
    </div>
    <el-row :gutter="20">
      <el-col :span="14">
      <el-col :span="8">
        <div class="table_list">
          <el-table ref="multipleTable"
                    border
@@ -42,10 +42,6 @@
                             prop="contractAmounts"
                             show-overflow-tooltip
                             :formatter="formattedNumber" />
            <el-table-column label="付款金额(元)"
                             prop="paymentAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber" />
            <el-table-column label="应付金额(元)"
                             prop="payableAmount"
                             show-overflow-tooltip>
@@ -64,25 +60,54 @@
                      :limit="page.size" />
        </div>
      </el-col>
      <el-col :span="10">
      <el-col :span="16">
        <div class="table_list">
          <PIMTable rowKey="id"
                    :column="tableColumnSon"
                    :tableData="originalTableDataSon"
                    :isSelection="false"
                    :isShowPagination="true"
                    :page="sonPage"
                    :tableLoading="tableLoadingSon"
                    :isShowSummary="isShowSummarySon"
                    :summaryMethod="summarizeMainTable1"
                    height="calc(100vh - 18.5em)"
                    @pagination="sonPaginationSearch">
            <template #payableAmountSlot="{ row }">
              <el-text type="danger">
                {{ parseFloat(row.payableAmount).toFixed(2) }}
              </el-text>
          <div class="table-header" v-if="selectedSupplierName">
            <span class="supplier-title">供应商:{{ selectedSupplierName }}</span>
          </div>
          <el-table border
                    v-loading="tableLoadingSon"
                    :data="originalTableDataSon"
                    :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
                    height="calc(100vh - 20em)"
                    style="width: 100%"
                    tooltip-effect="dark"
                    :show-summary="isShowSummarySon"
                    :summary-method="summarizeMainTable1">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="合同签订日期"
                             prop="executionDate"
                             show-overflow-tooltip/>
            <el-table-column label="采购合同号"
                             prop="purchaseContractNumber"
                             show-overflow-tooltip/>
            <el-table-column label="项目名称"
                             prop="projectName"
                             show-overflow-tooltip/>
            <el-table-column label="合同金额(元)"
                             prop="contractAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber" />
            <el-table-column label="收货状态"
                             prop="status"
                             show-overflow-tooltip
                             width="100">
              <template #default="{ row }">
                <el-tag :type="getReceiptStatusType(row.status)" size="small">
                  {{ receiptStatusText[row.status] || '未知状态' }}
                </el-tag>
            </template>
          </PIMTable>
            </el-table-column>
          </el-table>
          <pagination v-show="sonPage.total > 0"
                      @pagination="sonPaginationSearch"
                      :total="sonPage.total"
                      :layout="sonPage.layout"
                      :page="sonPage.current"
                      :limit="sonPage.size" />
        </div>
      </el-col>
    </el-row>
@@ -90,12 +115,12 @@
</template>
<script setup>
  import { ref, toRefs } from "vue";
  import { ref, toRefs, reactive, getCurrentInstance } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import {
    paymentLedgerList,
    paymentRecordList,
  } from "@/api/procurementManagement/paymentLedger.js";
  import { gePurchaseListPage } from "@/api/procurementManagement/invoiceEntry.js";
  import Pagination from "../../../components/PIMTable/Pagination.vue";
  const tableData = ref([]);
@@ -119,48 +144,13 @@
  const isShowSummary = ref(true);
  const { searchForm } = toRefs(data);
  const currentSupplierId = ref("");
  const selectedSupplierName = ref("");
  const rowClick = row => {
    currentSupplierId.value = row.supplierId;
    selectedSupplierName.value = row.supplierName;
    sonPage.current = 1;
    getPaymenRecordtList(row.supplierId);
    getPurchaseList(row.supplierName);
  };
  // å­æ¨¡å—
  const tableColumnSon = ref([
    {
      label: "合同签订日期",
      prop: "executionDate",
      width: 110,
    },
    {
      label: "采购合同号",
      prop: "purchaseContractNumber",
      width: 150,
    },
    {
      label: "合同金额(元)",
      prop: "contractAmount",
      width: 200,
      formatData: params => {
        return params ? parseFloat(params).toFixed(2) : 0;
      },
    },
    {
      label: "付款金额(元)",
      prop: "paymentAmount",
      width: 200,
      formatData: params => {
        return params ? parseFloat(params).toFixed(2) : 0;
      },
    },
    {
      label: "应付金额(元)",
      dataType: "slot",
      width: 200,
      prop: "payableAmount",
      slot: "payableAmountSlot",
    },
  ]);
  const tableDataSon = ref([]);
  const originalTableDataSon = ref([]);
  const tableLoadingSon = ref(false);
  const isShowSummarySon = ref(true);
@@ -170,32 +160,23 @@
  const summarizeMainTable = param => {
    return proxy.summarizeTable(
      param,
      ["contractAmounts", "paymentAmount", "payableAmount"],
      ["contractAmounts", "payableAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        ticketsNum: { noDecimal: true },
        futureTickets: { noDecimal: true },
      }
    );
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable1 = param => {
    let summarizeTable = proxy.summarizeTable(
    return proxy.summarizeTable(
      param,
      ["contractAmount", "invoiceAmount", "paymentAmount"],
      ["contractAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        ticketsNum: { noDecimal: true },
        futureTickets: { noDecimal: true },
      }
    );
    if (originalTableDataSon.value.length > 0) {
      summarizeTable[summarizeTable.length - 1] =
        originalTableDataSon.value[
          originalTableDataSon.value.length - 1
        ].payableAmount.toFixed(2);
    } else {
      summarizeTable[summarizeTable.length - 1] = 0.0;
    }
    return summarizeTable;
  };
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
@@ -219,16 +200,20 @@
      total.value = result.total || 0;
      if (tableData.value.length > 0) {
        currentSupplierId.value = tableData.value[0].supplierId;
        selectedSupplierName.value = tableData.value[0].supplierName;
        sonPage.current = 1;
        getPaymenRecordtList(tableData.value[0].supplierId);
        getPurchaseList(tableData.value[0].supplierName);
      } else {
        originalTableDataSon.value = [];
        selectedSupplierName.value = "";
      }
    });
  };
  const getPaymenRecordtList = supplierId => {
  const getPurchaseList = supplierName => {
    tableLoadingSon.value = true;
    paymentRecordList({
      supplierId: supplierId,
    gePurchaseListPage({
      supplierName: supplierName,
      current: sonPage.current,
      size: sonPage.size,
    })
@@ -236,9 +221,8 @@
        tableLoadingSon.value = false;
        let result = res.data;
        if (Array.isArray(result)) {
          tableDataSon.value = result;
          originalTableDataSon.value = result;
          sonPage.total = result.length;
          handlePagination({ page: sonPage.current, limit: sonPage.size });
        } else {
          originalTableDataSon.value = result.records || [];
          sonPage.total = result.total || 0;
@@ -248,30 +232,37 @@
        tableLoadingSon.value = false;
      });
  };
  const handlePagination = ({ page, limit }) => {
    console.log(page, limit);
    sonPage.current = page;
    sonPage.size = limit;
    const start = (page - 1) * limit;
    const end = start + limit;
    originalTableDataSon.value = tableDataSon.value.slice(start, end);
  };
  const sonPaginationSearch = pagination => {
    // æŽ¥æ”¶åˆ†é¡µå™¨å‚æ•° { page, limit }
    sonPage.current = pagination.page;
    sonPage.size = pagination.limit;
    getPaymenRecordtList(currentSupplierId.value);
    getPurchaseList(selectedSupplierName.value);
  };
  const formattedNumber = (row, column, cellValue) => {
    if (column.property !== "supplierName") {
    if (cellValue !== undefined && cellValue !== null && !isNaN(cellValue)) {
      return parseFloat(cellValue).toFixed(2);
    } else {
      return cellValue;
    }
    return "0.00";
  };
  // æ”¶è´§çŠ¶æ€æ–‡æœ¬æ˜ å°„
  const receiptStatusText = {
    1: '待收货',
    2: '收货中',
    3: '已收货'
  };
  // æ”¶è´§çŠ¶æ€æ ‡ç­¾ç±»åž‹
  const getReceiptStatusType = (status) => {
    const typeMap = {
      1: 'info',
      2: 'warning',
      3: 'success'
    };
    return typeMap[status] || 'info';
  };
  getList();
</script>
@@ -291,4 +282,17 @@
  .pagination-container {
    margin-top: 0;
  }
  .table-header {
    margin-bottom: 10px;
    padding: 10px;
    background-color: #f5f7fa;
    border-radius: 4px;
    .supplier-title {
      font-weight: bold;
      font-size: 14px;
      color: #303133;
    }
  }
</style>
src/views/procurementManagement/procurementLedger/index.vue
@@ -66,9 +66,9 @@
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="success"
                   plain
                   @click="handleBatchGenerate">批量生成数据</el-button>
<!--        <el-button type="success"-->
<!--                   plain-->
<!--                   @click="handleBatchGenerate">批量生成数据</el-button>-->
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="primary"
@@ -103,12 +103,33 @@
                               label="序号"
                               type="index"
                               width="60" />
              <el-table-column label="类型"
                               prop="productType"
                               width="100">
                <template #default="scope">
                  <el-tag :type="scope.row.productType === 2 ? 'warning' : 'success'" size="small">
                    {{ scope.row.productType === 2 ? '设备备件' : '产品' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
                               prop="specificationModel"
                               width="120">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">/</span>
                  <span v-else>{{ scope.row.specificationModel || '--' }}</span>
                </template>
              </el-table-column>
              <el-table-column label="单位"
                               prop="unit" />
                               prop="unit"
                               width="100">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ scope.row.unit || '--' }}</span>
                </template>
              </el-table-column>
              <el-table-column label="入库审核状态"
                               prop="stockInApprovalStatus"
                               width="120">
@@ -120,22 +141,59 @@
                </template>
              </el-table-column>
              <el-table-column label="数量"
                               prop="quantity" />
                               prop="quantity"
                               width="100" />
              <el-table-column label="可用数量"
                               prop="availableQuality" />
                               prop="availableQuality"
                               width="100">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ scope.row.availableQuality }}</span>
                </template>
              </el-table-column>
              <el-table-column label="退货数量"
                               prop="returnQuality" />
                               prop="returnQuality"
                               width="100">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ scope.row.returnQuality }}</span>
                </template>
              </el-table-column>
              <el-table-column label="税率(%)"
                               prop="taxRate" />
                               prop="taxRate"
                               width="100">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ scope.row.taxRate }}</span>
                </template>
              </el-table-column>
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               width="130">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               width="130">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveTotalPrice) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               width="140">
                <template #default="scope">
                  <span v-if="scope.row.productType === 2">--</span>
                  <span v-else>{{ formattedNumber(null, null, scope.row.taxExclusiveTotalPrice) }}</span>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
@@ -403,40 +461,86 @@
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="类型"
                           prop="productType"
                           width="100">
            <template #default="scope">
              <el-tag :type="scope.row.productType === 2 ? 'warning' : 'success'" size="small">
                {{ scope.row.productType === 2 ? '设备备件' : '产品' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="产品大类"
                           prop="productCategory" />
          <el-table-column label="规格型号"
                           prop="specificationModel" />
                           prop="specificationModel"
                           width="120">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">/</span>
              <span v-else>{{ scope.row.specificationModel || '--' }}</span>
            </template>
          </el-table-column>
          <el-table-column label="单位"
                           prop="unit"
                           width="70" />
                           width="70">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ scope.row.unit || '--' }}</span>
            </template>
          </el-table-column>
          <el-table-column label="数量"
                           prop="quantity"
                           width="70" />
          <el-table-column label="库存预警数量"
                           prop="warnNum"
                           width="120"
                           show-overflow-tooltip />
                           show-overflow-tooltip>
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ scope.row.warnNum }}</span>
            </template>
          </el-table-column>
          <el-table-column label="税率(%)"
                           prop="taxRate"
                           width="80" />
                           width="80">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ scope.row.taxRate }}</span>
            </template>
          </el-table-column>
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="130">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="130">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveTotalPrice) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="140">
            <template #default="scope">
              <span v-if="scope.row.productType === 2">--</span>
              <span v-else>{{ formattedNumber(null, null, scope.row.taxExclusiveTotalPrice) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="是否质检"
                           prop="isChecked"
                           width="150">
                           width="100">
            <template #default="scope">
              <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
              <span v-if="scope.row.productType === 2">--</span>
              <el-tag v-else :type="scope.row.isChecked ? 'success' : 'info'">
                {{ scope.row.isChecked ? '是' : '否' }}
              </el-tag>
            </template>
@@ -533,9 +637,21 @@
               ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="类型:"
                          prop="productType">
              <el-radio-group v-model="productForm.productType" @change="handleProductTypeChange">
                <el-radio :label="1">产品</el-radio>
                <el-radio :label="2">设备备件</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:"
                          prop="productId">
              <el-tree-select v-model="productForm.productId"
              <el-tree-select v-if="productForm.productType !== 2"
                              v-model="productForm.productId"
                              placeholder="请选择"
                              clearable
                              filterable
@@ -544,10 +660,22 @@
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
              <el-select v-else
                         v-model="productForm.productId"
                         placeholder="请选择设备备件"
                         filterable
                         clearable
                         @change="handleSparePartsChange"
                         style="width: 100%">
                <el-option v-for="item in sparePartsOptions"
                           :key="item.id"
                           :label="item.name"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30" v-if="productForm.productType !== 2">
          <el-col :span="24">
            <el-form-item label="规格型号:"
                          prop="productModelId">
@@ -564,7 +692,7 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30" v-if="productForm.productType !== 2">
          <el-col :span="12">
            <el-form-item label="单位:"
                          prop="unit">
@@ -588,7 +716,7 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30" v-if="productForm.productType !== 2">
          <el-col :span="12">
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice">
@@ -615,7 +743,21 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30" v-if="productForm.productType === 2">
          <el-col :span="24">
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
                               clearable
                               :precision="2"
                               :min="0"
                               style="width: 100%"
                               v-model="productForm.quantity"
                               placeholder="请输入" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30" v-if="productForm.productType !== 2">
          <el-col :span="12">
            <el-form-item label="含税总价(元):"
                          prop="taxInclusiveTotalPrice">
@@ -655,7 +797,7 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          <el-col :span="12" v-if="productForm.productType !== 2">
            <el-form-item label="库存预警数量:"
                          prop="warnNum">
              <el-input-number v-model="productForm.warnNum"
@@ -667,7 +809,7 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="30" v-if="productForm.productType !== 2">
          <el-col :span="12">
            <el-form-item label="是否质检:"
                          prop="isChecked">
@@ -740,6 +882,7 @@
  const modelOptions = ref([]);
  const userList = ref([]);
  const productOptions = ref([]);
  const sparePartsOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const tableLoading = ref(false);
@@ -753,6 +896,7 @@
  const fileList = ref([]);
  import useUserStore from "@/store/modules/user";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import { getSparePartsList } from "@/api/equipmentManagement/spareParts.js";
  import dayjs from "dayjs";
  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
@@ -956,6 +1100,7 @@
  const currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productType: 1,
      productId: "",
      productCategory: "",
      productModelId: "",
@@ -987,10 +1132,27 @@
        { required: true, message: "请输入", trigger: "blur" },
      ],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
      isChecked: [{ required: true, message: "请选择", trigger: "change" }],
      // isChecked é»˜è®¤ä¼ å¦ï¼Œä¸éœ€è¦éªŒè¯
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  const { productForm } = toRefs(productFormData);
  // åŠ¨æ€è®¡ç®—éªŒè¯è§„åˆ™
  const productRules = computed(() => {
    const isSpareParts = productForm.value.productType === 2;
    return {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: isSpareParts ? [] : [{ required: true, message: "请选择", trigger: "change" }],
      unit: isSpareParts ? [] : [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: isSpareParts ? [] : [{ required: true, message: "请输入", trigger: "blur" }],
      taxRate: isSpareParts ? [] : [{ required: true, message: "请选择", trigger: "change" }],
      warnNum: isSpareParts ? [] : [{ required: true, message: "请选择", trigger: "change" }],
      taxInclusiveTotalPrice: isSpareParts ? [] : [{ required: true, message: "请输入", trigger: "blur" }],
      taxExclusiveTotalPrice: isSpareParts ? [] : [{ required: true, message: "请输入", trigger: "blur" }],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
    };
  });
  const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
@@ -1472,16 +1634,27 @@
    await nextTick();
    if (type === "add") {
      productForm.value.isChecked = false;
      productForm.value.isChecked = false; // æ˜¯å¦è´¨æ£€é»˜è®¤ä¼ å¦
      productForm.value.productType = "product";
    }
    if (type === "edit") {
      // å¤åˆ¶è¡Œæ•°æ®
      productForm.value = { ...row };
      // å¦‚果没有 productType,默认为 product (1)
      if (!productForm.value.productType) {
        productForm.value.productType = 1;
      }
      // å¦‚果是设备备件类型,加载设备备件列表
      if (productForm.value.productType === 2) {
        await getSparePartsOptions();
      }
      // å¦‚果是从模板加载的数据,可能没有 productId å’Œ productModelId
      // éœ€è¦æ ¹æ® productCategory å’Œ specificationModel æ¥æŸ¥æ‰¾å¯¹åº”çš„ ID
      if (!productForm.value.productId && productForm.value.productCategory) {
      if (!productForm.value.productId && productForm.value.productCategory && productForm.value.productType !== 2) {
        // æ ¹æ® productCategory æŸ¥æ‰¾ productId
        const findProductIdByCategory = (nodes, categoryName) => {
          for (let i = 0; i < nodes.length; i++) {
@@ -1549,6 +1722,45 @@
      return res;
    });
  };
  // èŽ·å–è®¾å¤‡å¤‡ä»¶åˆ—è¡¨
  const getSparePartsOptions = () => {
    return getSparePartsList({ current: -1, size: -1 }).then(res => {
      if (res.data && Array.isArray(res.data.rows)) {
        sparePartsOptions.value = res.data.rows;
      } else if (res.data && Array.isArray(res.data.records)) {
        sparePartsOptions.value = res.data.records;
      } else {
        sparePartsOptions.value = [];
      }
      return res;
    });
  };
  // å¤„理类型变化
  const handleProductTypeChange = (type) => {
    productForm.value.productId = "";
    productForm.value.productCategory = "";
    productForm.value.productModelId = "";
    productForm.value.specificationModel = "";
    modelOptions.value = [];
    if (type === 2) {
      getSparePartsOptions();
    }
  };
  // å¤„理设备备件选择
  const handleSparePartsChange = (value) => {
    const selectedSparePart = sparePartsOptions.value.find(item => item.id === value);
    if (selectedSparePart) {
      productForm.value.productCategory = selectedSparePart.name;
      productForm.value.specificationModel = "/";
    } else {
      productForm.value.productCategory = "";
      productForm.value.specificationModel = "";
    }
  };
  const getModels = value => {
    if (value) {
      productForm.value.productCategory =
@@ -1605,6 +1817,19 @@
  // æäº¤äº§å“è¡¨å•
  const submitProduct = () => {
    // å¦‚果是设备备件,先设置默认值,再验证
    if (productForm.value.productType === 2) {
      productForm.value.specificationModel = "/";
      productForm.value.productModelId = null;
      productForm.value.unit = "";
      productForm.value.taxRate = "";
      productForm.value.taxInclusiveUnitPrice = 0;
      productForm.value.taxInclusiveTotalPrice = 0;
      productForm.value.taxExclusiveTotalPrice = 0;
      productForm.value.warnNum = 0;
      productForm.value.isChecked = false; // æ˜¯å¦è´¨æ£€é»˜è®¤ä¼ å¦
    }
    proxy.$refs["productFormRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
src/views/procurementManagement/receivingManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,320 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="供应商名称:">
          <el-input
            v-model="searchForm.supplierName"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="采购合同号:">
          <el-input
            v-model="searchForm.purchaseContractNumber"
            style="width: 240px"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input
            v-model="searchForm.salesContractNo"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input
            v-model="searchForm.projectName"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker
            v-model="searchForm.entryDate"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            type="daterange"
            placeholder="请选择"
            clearable
            @change="changeDaterange"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        show-summary
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        height="calc(100vh - 21.5em)"
      >
        <el-table-column type="expand">
          <template #default="props">
            <el-table
              :data="props.row.children"
              border
              show-summary
              :summary-method="summarizeChildrenTable"
            >
              <el-table-column align="center" label="序号" type="index" width="60" />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="可用数量" prop="availableQuality" />
              <el-table-column label="退货数量" prop="returnQuality" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="采购合同号" prop="purchaseContractNumber" width="160" show-overflow-tooltip />
        <el-table-column label="销售合同号" prop="salesContractNo" width="160" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="160" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="320" show-overflow-tooltip />
        <el-table-column label="收货状态" prop="status" width="100" show-overflow-tooltip>
          <template #default="scope">
            <el-tag :type="getReceiptStatusType(scope.row.status)" size="small">
              {{ receiptStatusText[scope.row.status] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="审批状态" prop="approvalStatus" width="100" show-overflow-tooltip>
          <template #default="scope">
            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="签订日期" prop="executionDate" width="100" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" width="100" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="200" show-overflow-tooltip :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="recorderName" width="120" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="100" show-overflow-tooltip />
        <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="120" align="center">
          <template #default="scope">
            <el-button
              link
              type="primary"
              :disabled="isConfirmed(scope.row)"
              @click="confirmReceipt(scope.row)"
            >
              {{ isConfirmed(scope.row) ? '已收货' : '确认收货' }}
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
  </div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import dayjs from 'dayjs'
import Pagination from '@/components/PIMTable/Pagination.vue'
import { productList, purchaseListPage, updateApprovalStatus } from '@/api/procurementManagement/procurementLedger.js'
const tableData = ref([])
const tableLoading = ref(false)
const total = ref(0)
const expandedRowKeys = ref([])
const receiptResultMap = ref({})
const page = reactive({
  current: 1,
  size: 100
})
const searchForm = reactive({
  supplierName: '',
  purchaseContractNumber: '',
  salesContractNo: '',
  projectName: '',
  entryDate: [],
  entryDateStart: undefined,
  entryDateEnd: undefined
})
const approvalStatusText = {
  1: '待审核',
  2: '审批中',
  3: '审批通过',
  4: '审批失败'
}
const receiptStatusText = {
  1: '待收货',
  2: '收货中',
  3: '已收货'
}
const getApprovalStatusType = (status) => {
  const typeMap = {
    1: 'info',
    2: 'warning',
    3: 'success',
    4: 'danger'
  }
  return typeMap[status]
}
const getReceiptStatusType = (status) => {
  const typeMap = {
    1: 'info',
    2: 'warning',
    3: 'success'
  }
  return typeMap[status]
}
const formattedNumber = (_row, _column, cellValue) => {
  const value = Number(cellValue)
  return Number.isFinite(value) ? value.toFixed(2) : '0.00'
}
const createSummary = (columns, data, sumFields) => {
  const sums = []
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = '合计'
      return
    }
    if (!sumFields.includes(column.property)) {
      sums[index] = ''
      return
    }
    const totalValue = data.reduce((sum, item) => sum + Number(item?.[column.property] || 0), 0)
    sums[index] = Number.isFinite(totalValue) ? totalValue.toFixed(2) : '0.00'
  })
  return sums
}
const summarizeMainTable = ({ columns, data }) => createSummary(columns, data, ['contractAmount'])
const summarizeChildrenTable = ({ columns, data }) =>
  createSummary(columns, data, ['quantity', 'availableQuality', 'returnQuality', 'taxInclusiveUnitPrice', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice'])
const isConfirmed = (row) => Number(row?.status) === 3 || Boolean(receiptResultMap.value[row?.id])
const handleQuery = () => {
  page.current = 1
  getList()
}
const changeDaterange = (value) => {
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format('YYYY-MM-DD')
    searchForm.entryDateEnd = dayjs(value[1]).format('YYYY-MM-DD')
  } else {
    searchForm.entryDateStart = undefined
    searchForm.entryDateEnd = undefined
  }
  handleQuery()
}
const paginationChange = (obj) => {
  page.current = obj.page
  page.size = obj.limit
  getList()
}
const expandChange = async (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = []
    try {
      const res = await productList({ salesLedgerId: row.id, type: 2 })
      const index = tableData.value.findIndex((item) => item.id === row.id)
      if (index > -1) {
        tableData.value[index].children = res.data || []
        expandedRowKeys.value.push(row.id)
      }
    } catch (_error) {
      ElMessage.error('加载产品列表失败')
      const index = expandedRows.findIndex((item) => item.id === row.id)
      if (index > -1) {
        expandedRows.splice(index, 1)
      }
    }
  } else {
    expandedRowKeys.value = []
  }
}
const getList = () => {
  tableLoading.value = true
  const { entryDate, ...rest } = searchForm
  purchaseListPage({ ...rest, ...page, approvalStatus: 3 })
    .then((res) => {
      tableData.value = (res.data?.records || []).map((record) => ({
        ...record,
        children: []
      }))
      total.value = res.data?.total || 0
      expandedRowKeys.value = []
    })
    .finally(() => {
      tableLoading.value = false
    })
}
const confirmReceipt = async (row) => {
  try {
    await ElMessageBox.confirm('是否确认收货?', '确认收货', {
      type: 'warning',
      confirmButtonText: '确认',
      cancelButtonText: '取消'
    })
    await updateApprovalStatus({
      id: row.id,
      status: 3
    })
    receiptResultMap.value[row.id] = true
    ElMessage.success('确认收货成功')
    getList()
  } catch (error) {
    if (error !== 'cancel' && error !== 'close') {
      ElMessage.error('确认收货失败')
    }
  }
}
onMounted(() => {
  getList()
})
</script>
src/views/procurementManagement/receivingManagement/modal/ReceiptForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,338 @@
<template>
  <FormDialog
    v-model="dialogVisible"
    title="确认收货"
    width="70%"
    operation-type="edit"
    @close="handleClose"
    @cancel="handleClose"
    @confirm="handleConfirm"
  >
    <el-form ref="formRef" :model="detailData" label-width="140px" label-position="top">
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="采购合同号:">
            <el-input :model-value="detailData.purchaseContractNumber || ''" disabled />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="销售合同号:">
            <el-input :model-value="detailData.salesContractNo || ''" disabled />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="供应商名称:">
            <el-input :model-value="detailData.supplierName || ''" disabled />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="项目名称:">
            <el-input :model-value="detailData.projectName || ''" disabled />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="付款方式:">
            <el-input :model-value="detailData.paymentMethod || ''" disabled />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="签订日期:">
            <el-input :model-value="detailData.executionDate || ''" disabled />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="录入人:">
            <el-input :model-value="detailData.recorderName || ''" disabled />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="录入日期:">
            <el-input :model-value="detailData.entryDate || ''" disabled />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="审批人选择:">
            <div class="approver-nodes-container">
              <div v-for="(name, index) in approverNames" :key="`${name}-${index}`" class="approver-node-item">
                <div class="approver-node-header">
                  <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                </div>
                <el-input :model-value="name" disabled />
              </div>
              <el-empty v-if="!approverNames.length" description="暂无审批人信息" />
            </div>
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="产品信息:">
        <el-table :data="detailData.productData" border show-summary :summary-method="summarizeProductTable">
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" width="70" />
          <el-table-column label="数量" prop="quantity" width="90" />
          <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip />
          <el-table-column label="税率(%)" prop="taxRate" width="80" />
          <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" width="150" :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" width="150" :formatter="formattedNumber" />
          <el-table-column label="是否质检" prop="isChecked" width="100">
            <template #default="scope">
              <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
                {{ scope.row.isChecked ? '是' : '否' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="是否合格" min-width="180">
            <template #default="scope">
              <el-form-item
                :prop="`productData.${scope.$index}.isQualified`"
                :rules="[{ required: true, message: '请选择是否合格', trigger: 'change' }]"
                class="inline-form-item"
              >
                <el-radio-group v-model="scope.row.isQualified">
                  <el-radio :label="1">合格</el-radio>
                  <el-radio :label="2">不合格</el-radio>
                </el-radio-group>
              </el-form-item>
            </template>
          </el-table-column>
          <el-table-column label="不合格原因" min-width="220">
            <template #default="scope">
              <el-form-item
                :prop="`productData.${scope.$index}.reason`"
                :rules="[{
                  validator: (_rule, value, callback) => validateReason(scope.row, value, callback),
                  trigger: 'blur'
                }]"
                class="inline-form-item"
              >
                <el-input
                  v-model="scope.row.reason"
                  :disabled="scope.row.isQualified !== 2"
                  placeholder="不合格时请填写原因"
                  clearable
                />
              </el-form-item>
            </template>
          </el-table-column>
        </el-table>
      </el-form-item>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="备注:">
            <el-input :model-value="detailData.remarks || ''" type="textarea" :rows="2" disabled />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="附件材料:">
            <div class="file-list">
              <el-tag v-for="file in detailData.salesLedgerFiles" :key="file.id || file.fileName" class="file-tag">
                {{ file.fileName || file.name }}
              </el-tag>
              <el-empty v-if="!detailData.salesLedgerFiles?.length" description="暂无附件" />
            </div>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import FormDialog from '@/components/Dialog/FormDialog.vue'
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  record: {
    type: Object,
    default: () => ({})
  }
})
const emit = defineEmits(['update:modelValue', 'submit'])
const formRef = ref()
const dialogVisible = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})
const createDefaultDetail = () => ({
  id: '',
  purchaseContractNumber: '',
  salesContractNo: '',
  supplierName: '',
  projectName: '',
  paymentMethod: '',
  executionDate: '',
  recorderName: '',
  entryDate: '',
  remarks: '',
  approveUserIds: '',
  approverNames: [],
  salesLedgerFiles: [],
  productData: []
})
const detailData = reactive(createDefaultDetail())
const approverNames = computed(() => detailData.approverNames || [])
const formattedNumber = (_row, _column, cellValue) => {
  const value = Number(cellValue)
  return Number.isFinite(value) ? value.toFixed(2) : '0.00'
}
const summarizeProductTable = ({ columns, data }) => {
  const sumFields = ['quantity', 'taxInclusiveUnitPrice', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']
  const sums = []
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = '合计'
      return
    }
    if (!sumFields.includes(column.property)) {
      sums[index] = ''
      return
    }
    const total = data.reduce((sum, item) => sum + Number(item?.[column.property] || 0), 0)
    sums[index] = Number.isFinite(total) ? total.toFixed(2) : '0.00'
  })
  return sums
}
const validateReason = (row, value, callback) => {
  if (row.isQualified === 2 && (!value || !value.trim())) {
    callback(new Error('不合格时必须填写原因'))
    return
  }
  callback()
}
watch(
  () => props.record,
  (value) => {
    const nextData = createDefaultDetail()
    Object.assign(nextData, value || {})
    nextData.productData = (value?.productData || []).map((item) => ({
      ...item,
      isQualified: item.isQualified ?? undefined,
      reason: item.reason || ''
    }))
    nextData.salesLedgerFiles = value?.salesLedgerFiles || []
    nextData.approverNames = value?.approverNames || []
    Object.assign(detailData, nextData)
  },
  { immediate: true, deep: true }
)
const handleClose = () => {
  dialogVisible.value = false
}
const handleConfirm = async () => {
  if (!formRef.value) {
    return
  }
  try {
    await formRef.value.validate()
    const unqualifiedRows = detailData.productData.filter((item) => item.isQualified === 2)
    emit('submit', {
      id: detailData.id,
      purchaseContractNumber: detailData.purchaseContractNumber,
      supplierName: detailData.supplierName,
      projectName: detailData.projectName,
      productData: detailData.productData.map((item) => ({
        id: item.id,
        specificationModel: item.specificationModel,
        quantity: item.quantity,
        isQualified: item.isQualified,
        reason: item.reason || ''
      })),
      unqualifiedQuantity: unqualifiedRows.reduce((sum, item) => sum + Number(item.quantity || 0), 0)
    })
    dialogVisible.value = false
  } catch (_error) {
    ElMessage.warning('请先完成每条产品的收货判断')
  }
}
</script>
<style scoped lang="scss">
.approver-nodes-container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  width: 100%;
}
.approver-node-item {
  flex: 0 0 calc(33.333% - 12px);
  min-width: 220px;
  padding: 12px;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
}
.approver-node-header {
  margin-bottom: 8px;
}
.approver-node-label {
  font-size: 13px;
  font-weight: 500;
  color: #606266;
}
.inline-form-item {
  margin-bottom: 0;
}
.file-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  width: 100%;
}
.file-tag {
  margin-right: 0;
}
@media (max-width: 1200px) {
  .approver-node-item {
    flex: 0 0 calc(50% - 8px);
  }
}
@media (max-width: 768px) {
  .approver-node-item {
    flex: 0 0 100%;
  }
}
</style>
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -9,6 +9,28 @@
      <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="salesLedgerId">
              <el-select
                v-model="form.salesLedgerId"
                placeholder="请选择销售订单"
                clearable
                filterable
                :disabled="isViewMode"
                style="width: 100%"
                @change="handleSalesLedgerChange"
              >
                <el-option
                  v-for="item in salesLedgerOptions"
                  :key="item.id"
                  :label="item.salesContractNo"
                  :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="productId">
              <el-tree-select
                  v-model="form.productId"
@@ -168,6 +190,7 @@
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
import {ledgerListNoPage} from "@/api/salesManagement/salesLedger.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -189,6 +212,8 @@
    unqualifiedQuantity: "",
    checkCompany: "",
    checkResult: "",
    salesLedgerId: "",
    salesContractNo: "",
  },
  rules: {
    checkTime: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -245,6 +270,7 @@
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
const modelOptions = ref([]);
const salesLedgerOptions = ref([]); // é”€å”®è®¢å•下拉框数据
// æ‰“开弹框
const openDialog = async (type, row) => {
@@ -255,11 +281,14 @@
  proxy.$refs.formRef?.clearValidate();
  // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
  const [userListsRes] = await Promise.all([
  const [userListsRes, salesLedgerRes] = await Promise.all([
    userListNoPage(),
    getProductOptions(),
    getOptions().then((res) => {
      supplierList.value = res.data;
    }),
    ledgerListNoPage({ current: -1, size: -1 }).then((res) => {
      salesLedgerOptions.value = res.data || [];
    })
  ]);
  userList.value = userListsRes.data;
@@ -357,6 +386,16 @@
  form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || '';
}
// é”€å”®è®¢å•选择变化处理
const handleSalesLedgerChange = (value) => {
  const selectedItem = salesLedgerOptions.value.find(item => item.id == value);
  if (selectedItem) {
    form.value.salesContractNo = selectedItem.salesContractNo || '';
  } else {
    form.value.salesContractNo = '';
  }
}
const handleQualifiedQuantityChange = (value) => {
  if (value === null || value === undefined) {
    form.value.qualifiedQuantity = 0;
src/views/qualityManagement/finalInspection/index.vue
@@ -5,16 +5,15 @@
               :model="searchForm"
               class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
          <el-col :span="6">
            <el-form-item label="产品名称"
                          prop="productName">
              <el-input v-model="searchForm.productName"
                        style="width: 240px"
                        placeholder="请输入产品名称搜索"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="6">
          <el-col :span="8">
            <el-form-item label="检测日期"
                          prop="entryDate">
              <el-date-picker v-model="searchForm.entryDate"
@@ -26,21 +25,12 @@
                              @change="changeDaterange" />
            </el-form-item>
          </el-col>
          <el-col :span="4">
          <el-col :span="6">
            <el-form-item label="销售单号"
                          prop="salesContractNo">
              <el-input v-model="searchForm.salesContractNo"
                        style="width: 240px"
                        placeholder="请输入销售单号搜索"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="生产工单号"
                          prop="workOrderNo">
              <el-input v-model="searchForm.workOrderNo"
                        style="width: 240px"
                        placeholder="请输入生产工单号搜索"
                        clearable />
            </el-form-item>
          </el-col>
@@ -55,16 +45,14 @@
          </el-col>
        </el-row>
      </el-form>
      <div class="actions">
        <el-button type="primary"
                   @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <div style="margin-bottom: 20px; text-align: right;">
      <el-button type="primary" @click="openQuickCheck">快速检验</el-button>
      <el-button type="primary" @click="openForm('add')">新增</el-button>
      <el-button @click="handleOut">导出</el-button>
      <el-button type="danger" plain @click="handleDelete">删除</el-button>
    </div>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
@@ -110,6 +98,8 @@
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -132,6 +122,7 @@
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
    batchQuickInspect,
  } from "@/api/qualityManagement/rawMaterialInspection.js";
  import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue";
  import dayjs from "dayjs";
@@ -152,6 +143,7 @@
    },
  });
  const { searchForm } = toRefs(data);
  const tableColumn = ref([
    {
      label: "检测日期",
@@ -161,11 +153,6 @@
    {
      label: "销售单号",
      prop: "salesContractNo",
      width: 120,
    },
    {
      label: "生产工单号",
      prop: "workOrderNo",
      width: 120,
    },
    {
@@ -399,6 +386,50 @@
      formDia.value?.openDialog(type, row);
    });
  };
  // æ‰“开快速检验确认框
  const openQuickCheck = () => {
    // æ£€æŸ¥æ˜¯å¦é€‰æ‹©äº†æ•°æ®
    if (!selectedRows.value || selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请先选择要检验的数据");
      return;
    }
    // è¿‡æ»¤å‡ºæœªæäº¤çš„æ•°æ®
    const unSubmittedRows = selectedRows.value.filter(item => item.inspectState !== 1);
    if (unSubmittedRows.length === 0) {
      proxy.$modal.msgWarning("选中的数据已全部提交,无需重复检验");
      return;
    }
    const totalCount = selectedRows.value.length;
    const submittedCount = totalCount - unSubmittedRows.length;
    let confirmMessage = `已选择 ${totalCount} æ¡æ£€éªŒå•`;
    if (submittedCount > 0) {
      confirmMessage += `(其中 ${submittedCount} æ¡å·²æäº¤ï¼Œå°†è‡ªåŠ¨è·³è¿‡ï¼‰`;
    }
    confirmMessage += `\n\n确认后将自动:\n· æ£€éªŒç»“果设为"合格"\n· åˆæ ¼æ•°é‡è®¾ä¸ºæ€»æ•°\n· ä¸åˆæ ¼æ•°é‡è®¾ä¸º 0\n· æäº¤å¹¶å…¥åº“`;
    ElMessageBox.confirm(confirmMessage, "快速检验", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
      dangerouslyUseHTMLString: false,
    })
      .then(() => {
        // è°ƒç”¨æ‰¹é‡å¿«é€Ÿæ£€éªŒæŽ¥å£
        const ids = unSubmittedRows.map(item => item.id);
        batchQuickInspect(ids).then(res => {
          proxy.$modal.msgSuccess(res.msg || "快速检验完成");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // æ‰“开新增检验弹框
  const openInspectionForm = (type, row) => {
    nextTick(() => {
src/views/qualityManagement/processInspection/index.vue
@@ -30,16 +30,14 @@
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button type="primary"
                   @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
       <div style="margin-bottom: 20px; text-align: right;">
      <el-button type="primary" @click="openQuickCheck">快速检验</el-button>
      <el-button type="primary" @click="openForm('add')">新增</el-button>
      <el-button @click="handleOut">导出</el-button>
      <el-button type="danger" plain @click="handleDelete">删除</el-button>
    </div>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
@@ -85,6 +83,8 @@
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -107,6 +107,7 @@
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
    batchQuickInspect,
  } from "@/api/qualityManagement/rawMaterialInspection.js";
  import FilesDia from "@/views/qualityManagement/processInspection/components/filesDia.vue";
  import dayjs from "dayjs";
@@ -126,15 +127,11 @@
    },
  });
  const { searchForm } = toRefs(data);
  const tableColumn = ref([
    {
      label: "检测日期",
      prop: "checkTime",
      width: 120,
    },
    {
      label: "生产工单号",
      prop: "workOrderNo",
      width: 120,
    },
    {
@@ -372,6 +369,50 @@
      formDia.value?.openDialog(type, row);
    });
  };
  // æ‰“开快速检验确认框
  const openQuickCheck = () => {
    // æ£€æŸ¥æ˜¯å¦é€‰æ‹©äº†æ•°æ®
    if (!selectedRows.value || selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请先选择要检验的数据");
      return;
    }
    // è¿‡æ»¤å‡ºæœªæäº¤çš„æ•°æ®
    const unSubmittedRows = selectedRows.value.filter(item => item.inspectState !== 1);
    if (unSubmittedRows.length === 0) {
      proxy.$modal.msgWarning("选中的数据已全部提交,无需重复检验");
      return;
    }
    const totalCount = selectedRows.value.length;
    const submittedCount = totalCount - unSubmittedRows.length;
    let confirmMessage = `已选择 ${totalCount} æ¡æ£€éªŒå•`;
    if (submittedCount > 0) {
      confirmMessage += `(其中 ${submittedCount} æ¡å·²æäº¤ï¼Œå°†è‡ªåŠ¨è·³è¿‡ï¼‰`;
    }
    confirmMessage += `\n\n确认后将自动:\n· æ£€éªŒç»“果设为"合格"\n· åˆæ ¼æ•°é‡è®¾ä¸ºæ€»æ•°\n· ä¸åˆæ ¼æ•°é‡è®¾ä¸º 0\n· æäº¤å¹¶å…¥åº“`;
    ElMessageBox.confirm(confirmMessage, "快速检验", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
      dangerouslyUseHTMLString: false,
    })
      .then(() => {
        // è°ƒç”¨æ‰¹é‡å¿«é€Ÿæ£€éªŒæŽ¥å£
        const ids = unSubmittedRows.map(item => item.id);
        batchQuickInspect(ids).then(res => {
          proxy.$modal.msgSuccess(res.msg || "快速检验完成");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // æ‰“开新增检验弹框
  const openInspectionForm = (type, row) => {
    nextTick(() => {
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -31,16 +31,14 @@
                   style="margin-left: 10px">搜索
        </el-button>
      </div>
      <div>
        <el-button type="primary"
                   @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
       <div style="margin-bottom: 20px; text-align: right;">
      <el-button type="primary" @click="openQuickCheck">快速检验</el-button>
      <el-button type="primary" @click="openForm('add')">新增</el-button>
      <el-button @click="handleOut">导出</el-button>
      <el-button type="danger" plain @click="handleDelete">删除</el-button>
    </div>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
@@ -86,6 +84,8 @@
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -108,6 +108,7 @@
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
    batchQuickInspect,
  } from "@/api/qualityManagement/rawMaterialInspection.js";
  import FilesDia from "@/views/qualityManagement/rawMaterialInspection/components/filesDia.vue";
  import dayjs from "dayjs";
@@ -127,6 +128,7 @@
    },
  });
  const { searchForm, rules } = toRefs(data);
  const tableColumn = ref([
    {
      label: "检测日期",
@@ -378,6 +380,50 @@
      formDia.value?.openDialog(type, row);
    });
  };
  // æ‰“开快速检验确认框
  const openQuickCheck = () => {
    // æ£€æŸ¥æ˜¯å¦é€‰æ‹©äº†æ•°æ®
    if (!selectedRows.value || selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请先选择要检验的数据");
      return;
    }
    // è¿‡æ»¤å‡ºæœªæäº¤çš„æ•°æ®
    const unSubmittedRows = selectedRows.value.filter(item => item.inspectState !== 1);
    if (unSubmittedRows.length === 0) {
      proxy.$modal.msgWarning("选中的数据已全部提交,无需重复检验");
      return;
    }
    const totalCount = selectedRows.value.length;
    const unSubmittedCount = unSubmittedRows.length;
    const submittedCount = totalCount - unSubmittedCount;
    let confirmMessage = `已选择 ${totalCount} æ¡æ£€éªŒå•`;
    if (submittedCount > 0) {
      confirmMessage += `(其中 ${submittedCount} æ¡å·²æäº¤ï¼Œå°†è‡ªåŠ¨è·³è¿‡ï¼‰`;
    }
    confirmMessage += `\n\n确认后将自动:\n· æ£€éªŒç»“果设为"合格"\n· åˆæ ¼æ•°é‡è®¾ä¸ºæ€»æ•°\n· ä¸åˆæ ¼æ•°é‡è®¾ä¸º 0\n· æäº¤å¹¶å…¥åº“`;
    ElMessageBox.confirm(confirmMessage, "快速检验", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
      dangerouslyUseHTMLString: false,
    })
      .then(() => {
        // è°ƒç”¨æ‰¹é‡å¿«é€Ÿæ£€éªŒæŽ¥å£
        const ids = unSubmittedRows.map(item => item.id);
        batchQuickInspect(ids).then(res => {
          proxy.$modal.msgSuccess(res.msg || "快速检验完成");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // æ‰“开附件弹框
  const openFilesFormDia = (type, row) => {
    nextTick(() => {
src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
@@ -5,18 +5,6 @@
      <div class="stat-card">
        <img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" />
        <div class="card-content">
          <span class="card-label">员工总数</span>
          <span class="card-value">{{ totalStaff }}</span>
          <div class="card-compare" :class="compareClass(staffYoY)">
            <span>同比</span>
            <span class="compare-value">{{ formatPercent(staffYoY) }}</span>
            <span class="compare-icon">{{ staffYoY >= 0 ? '↑' : '↓' }}</span>
          </div>
        </div>
      </div>
      <div class="stat-card">
        <img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" />
        <div class="card-content">
          <span class="card-label">客户总数</span>
          <span class="card-value">{{ totalCustomers }}</span>
          <div class="card-compare" :class="compareClass(customersYoY)">
@@ -118,11 +106,9 @@
import { measuringInstrumentListPage } from '@/api/equipmentManagement/measurementEquipment.js'
// ç»Ÿè®¡æ•°æ®
const totalStaff = ref(0)
const totalCustomers = ref(0)
const totalSuppliers = ref(0)
// åŒæ¯”
const staffYoY = ref(0)
const customersYoY = ref(0)
const suppliersYoY = ref(0)
const equipmentNum = ref(0)
@@ -141,11 +127,9 @@
const compareClass = (val) => (val >= 0 ? 'compare-up' : 'compare-down')
// èŽ·å–å‘˜å·¥ã€å®¢æˆ·ã€ä¾›åº”å•†æ•°é‡
// èŽ·å–å®¢æˆ·ã€ä¾›åº”å•†æ•°é‡
const getNum = () => {
  summaryStatistics().then((res) => {
    totalStaff.value = res.data.totalStaff
    staffYoY.value = res.data.staffGrowthRate
    totalCustomers.value = res.data.totalCustomer
    customersYoY.value = res.data.customerGrowthRate
    totalSuppliers.value = res.data.totalSupplier
@@ -388,6 +372,7 @@
  padding: 18px;
  height: 240px;
  padding-top: 0px;
  margin-bottom: 20px;
}
.equipment-header {
@@ -459,7 +444,8 @@
  background-repeat: no-repeat;
  padding: 20px;
  padding-top: 10px;
  height: 186px;
  height: 480px;
  flex: 1;
}
.event-header {
@@ -483,7 +469,7 @@
  list-style: none;
  padding: 0;
  margin: 0;
  height: 120px; /* æŒ‰ç”¨æˆ·è¦æ±‚调整高度 */
  height: 210px;
  overflow: hidden;
  font-size: 15px;
}
src/views/reportAnalysis/dataDashboard/index.vue
@@ -28,8 +28,6 @@
    <!-- ä¸­é—´åŒºåŸŸ -->
    <div class="center-panel">
      <CenterTop />
      <CenterBottom />
    </div>
    <!-- å³ä¾§åŒºåŸŸ -->
@@ -49,7 +47,6 @@
import LeftTop from './components/basic/left-top.vue'
import LeftBottom from './components/basic/left-bottom.vue'
import CenterTop from './components/basic/center-top.vue'
import CenterBottom from './components/basic/center-bottom.vue'
import RightTop from './components/basic/right-top.vue'
import RightBottom from './components/basic/right-bottom.vue'
import useUserStore from '@/store/modules/user'
src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -16,7 +16,8 @@
      </div>
    </div>
    <el-row :gutter="20">
      <el-col :span="12">
      <!-- å·¦ä¾§å®¢æˆ·åˆ—表 -->
      <el-col :span="8">
        <div class="table_list"
             style="width: 100%">
          <el-table :data="tableData"
@@ -40,22 +41,17 @@
                             prop="contractAmounts"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="回款金额(元)"
                             prop="receiptPaymentAmount"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="应收金额(元)"
                             prop="receiptableAmount"
                             show-overflow-tooltip
                             width="200">
              <template #default="{ row, column }">
                <el-text type="danger">
                  {{ formattedNumber(row, column, row.receiptableAmount) }}
                </el-text>
              </template>
            </el-table-column>
                             width="150" />
<!--            <el-table-column label="应收金额(元)"-->
<!--                             prop="receiptableAmount"-->
<!--                             show-overflow-tooltip-->
<!--                             width="150">-->
<!--              <template #default="{ row, column }">-->
<!--                <el-text type="danger">-->
<!--                  {{ formattedNumber(row, column, row.receiptableAmount) }}-->
<!--                </el-text>-->
<!--              </template>-->
<!--            </el-table-column>-->
          </el-table>
          <pagination v-show="total > 0"
                      :total="total"
@@ -65,57 +61,85 @@
                      @pagination="paginationChange" />
        </div>
      </el-col>
      <el-col :span="12">
      <!-- å³ä¾§è®¢å•信息 -->
      <el-col :span="16">
        <div class="table_list"
             style="width: 100%">
          <el-table :data="receiptRecord"
          <el-table :data="orderRecord"
                    border
                    :row-key="(row) => row.id"
                    show-summary
                    :summary-method="summarizeMainTable1"
                    height="calc(100vh - 18.5em)">
                    v-loading="orderLoading"
                    height="calc(100vh - 20em)">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="合同签订日期"
                             prop="executionDate"
                             show-overflow-tooltip
                             width="110" />
            <el-table-column label="销售合同号"
            <el-table-column label="销售订单"
                             prop="salesContractNo"
                             show-overflow-tooltip
                             width="200" />
            <el-table-column label="合同金额(元)"
                             prop="contractAmount"
                             width="150" />
            <!-- <el-table-column label="发货订单号"
                             prop="shippingNo"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="回款金额(元)"
                             prop="receiptPaymentAmount"
                             width="150" /> -->
            <el-table-column label="客户名称"
                             prop="customerName"
                             show-overflow-tooltip
                             :formatter="formattedNumber"
                             width="200" />
            <el-table-column label="应收金额(元)"
                             prop="receiptableAmount"
                             width="150" />
            <el-table-column label="产品名称"
                             prop="productName"
                             show-overflow-tooltip
                             width="200">
              <template #default="{ row, column }">
                <el-text type="danger">
                  {{ formattedNumber(row, column, row.receiptableAmount) }}
                </el-text>
                             width="150" />
            <el-table-column label="规格型号"
                             prop="specificationModel"
                             show-overflow-tooltip
                             width="120" />
            <el-table-column label="发货时间"
                             prop="shippingDate"
                             show-overflow-tooltip
                             width="110" />
            <el-table-column label="发货数量"
                             prop="totalQuantity"
                             show-overflow-tooltip
                             width="100" />
            <!-- <el-table-column label="发货车牌号"
                             prop="shippingCarNumber"
                             show-overflow-tooltip
                             width="120" />
            <el-table-column label="快递公司"
                             prop="expressCompany"
                             show-overflow-tooltip
                             width="120" />
            <el-table-column label="快递单号"
                             prop="expressNumber"
                             show-overflow-tooltip
                             width="150" /> -->
            <el-table-column label="发货状态"
                             prop="status"
                             align="center"
                             width="100">
              <template #default="{ row }">
                <el-tag :type="getApprovalStatusType(row.status)">
                  {{ getApprovalStatusText(row.status) }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="出库单号"
                             prop="outboundBatches"
                             show-overflow-tooltip
                             width="130" />
          </el-table>
          <pagination v-show="recordTotal > 0"
                      :total="recordTotal"
          <pagination v-show="orderTotal > 0"
                      :total="orderTotal"
                      layout="total, sizes, prev, pager, next, jumper"
                      :page="recordPage.current"
                      :limit="recordPage.size"
                      @pagination="recordPaginationChange" />
                      :page="orderPage.current"
                      :limit="orderPage.size"
                      @pagination="orderPaginationChange" />
        </div>
      </el-col>
    </el-row>
  </div>
</template>
@@ -123,23 +147,25 @@
  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
  import {
    customewTransactions,
    customewTransactionsDetails,
  } from "@/api/salesManagement/indicatorStats.js";
  import { deliveryLedgerListPage } from "@/api/salesManagement/deliveryLedger.js";
  import Pagination from "../../../components/PIMTable/Pagination.vue";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const receiptRecord = ref([]);
  const orderRecord = ref([]);
  const tableLoading = ref(false);
  const orderLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const recordPage = reactive({
  const orderPage = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const recordTotal = ref(0);
  const orderTotal = ref(0);
  const data = reactive({
    searchForm: {
      searchText: "",
@@ -148,7 +174,7 @@
  });
  const customerId = ref("");
  const { searchForm } = toRefs(data);
  const originReceiptRecord = ref([]);
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
@@ -167,9 +193,12 @@
      tableData.value = res.data.records;
      total.value = res.data.total;
      if (tableData.value.length > 0) {
        recordPage.current = 1;
        orderPage.current = 1;
        customerId.value = tableData.value[0].customerId;
        receiptPaymentList(customerId.value);
        getOrderList(customerId.value);
      } else {
        orderRecord.value = [];
        customerId.value = "";
      }
    });
  };
@@ -180,64 +209,82 @@
  const summarizeMainTable = param => {
    return proxy.summarizeTable(
      param,
      ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"],
      ["contractAmounts", "receiptableAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        ticketsNum: { noDecimal: true },
        futureTickets: { noDecimal: true },
      }
    );
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable1 = param => {
    var summarizeTable = proxy.summarizeTable(
      param,
      ["contractAmount", "receiptPaymentAmount", "receiptableAmount"],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
    return summarizeTable;
  };
  const receiptPaymentList = id => {
    const param = {
  // èŽ·å–è®¢å•åˆ—è¡¨ï¼ˆæ ¹æ®å®¢æˆ·ID查询发货台账)
  const getOrderList = async (id) => {
    orderLoading.value = true;
    try {
      // ä½¿ç”¨ deliveryLedgerListPage æŽ¥å£æ ¹æ® customerId æŸ¥è¯¢
      const res = await deliveryLedgerListPage({
      customerId: id,
      current: recordPage.current,
      size: recordPage.size,
    };
    customewTransactionsDetails(param).then(res => {
      if (Array.isArray(res.data)) {
        originReceiptRecord.value = res.data;
        recordTotal.value = res.data.length;
        handlePagination({ page: 1, limit: recordPage.size });
      } else {
        receiptRecord.value = res.data.records;
        recordTotal.value = res.data.total;
      }
        current: 1,
        size: 1000,
    });
      let orders = [];
      if (res.data) {
        if (Array.isArray(res.data)) {
          orders = res.data;
        } else if (res.data.records && Array.isArray(res.data.records)) {
          orders = res.data.records;
        }
      }
      orderTotal.value = orders.length;
      handleOrderPagination({ page: orderPage.current, limit: orderPage.size }, orders);
    } catch (error) {
      console.error('获取订单列表失败:', error);
      orderRecord.value = [];
    } finally {
      orderLoading.value = false;
    }
  };
  // æ±‡æ¬¾è®°å½•列表分页
  const recordPaginationChange = pagination => {
    recordPage.current = pagination.page;
    recordPage.size = pagination.limit;
    receiptPaymentList(customerId.value);
  // è®¢å•列表分页
  const orderPaginationChange = pagination => {
    orderPage.current = pagination.page;
    orderPage.size = pagination.limit;
    getOrderList(customerId.value);
  };
  const handleOrderPagination = ({ page, limit }, allOrders) => {
    const start = (page - 1) * limit;
    const end = start + limit;
    orderRecord.value = allOrders.slice(start, end);
  };
  const rowClickMethod = row => {
    customerId.value = row.customerId;
    receiptPaymentList(customerId.value);
    orderPage.current = 1;
    getOrderList(customerId.value);
  };
  const handlePagination = ({ page, limit }) => {
    recordPage.current = page;
    recordPage.size = limit;
  // å®¡æ ¸çŠ¶æ€æ ‡ç­¾ç±»åž‹
  const getApprovalStatusType = (status) => {
    const statusMap = {
      '已发货': 'success',
      '审核通过': 'success',
      '审核中': 'warning',
      '审核不通过': 'danger',
    };
    return statusMap[status] || 'info';
  };
    const start = (page - 1) * limit;
    const end = start + limit;
    receiptRecord.value = originReceiptRecord.value.slice(start, end);
  // å®¡æ ¸çŠ¶æ€æ–‡æœ¬
  const getApprovalStatusText = (status) => {
    const statusMap = {
      '已发货': '已发货',
      '审核通过': '审核通过',
      '审核中': '审核中',
      '审核不通过': '审核不通过',
    };
    return statusMap[status] || status || '--';
  };
  onMounted(() => {
@@ -247,6 +294,7 @@
<style scoped lang="scss">
  .table_list {
    width: 50%;
    width: 100%;
  }
</style>
src/views/salesManagement/salesLedger/index.vue
@@ -419,6 +419,41 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="采购合同号:" prop="purchaseContractNumber">
              <el-select
                v-model="form.purchaseContractNumber"
                placeholder="请选择采购合同号"
                clearable
                filterable
                style="width: 100%"
                :disabled="operationType === 'view'"
              >
                <el-option
                  v-for="item in purchaseContractOptions"
                  :key="item.purchaseContractNumber"
                  :label="item.purchaseContractNumber + ' - ' + item.supplierName"
                  :value="item.purchaseContractNumber"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="entryDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.deliveryDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                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="entryPerson">
              <el-select
                v-model="form.entryPerson"
@@ -444,22 +479,6 @@
              <el-date-picker
                style="width: 100%"
                v-model="form.entryDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                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="entryDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.deliveryDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
@@ -527,13 +546,14 @@
            prop="taxExclusiveTotalPrice"
            :formatter="formattedNumber"
          />
          <el-table-column label="是否生产" prop="isProduction" width="150">
          <!-- æ˜¯å¦ç”Ÿäº§åˆ—已隐藏,默认值为 false -->
          <!-- <el-table-column label="是否生产" prop="isProduction" width="150">
            <template #default="scope">
              <el-tag :type="scope.row.isProduction ? 'success' : 'info'">
                {{ scope.row.isProduction ? "是" : "否" }}
              </el-tag>
            </template>
          </el-table-column>
          </el-table-column> -->
          <el-table-column
            fixed="right"
            label="操作"
@@ -823,14 +843,15 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          <!-- æ˜¯å¦ç”Ÿäº§å­—段已隐藏,默认值为 false -->
          <!-- <el-col :span="12">
            <el-form-item label="是否生产:" prop="isProduction">
              <el-radio-group v-model="productForm.isProduction">
                <el-radio label="是" :value="true" />
                <el-radio label="否" :value="false" />
              </el-radio-group>
            </el-form-item>
          </el-col>
          </el-col> -->
        </el-row>
      </el-form>
    </FormDialog>
@@ -1180,6 +1201,7 @@
} from "@/api/salesManagement/salesLedger.js";
import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import { purchaseList } from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
@@ -1204,6 +1226,7 @@
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const purchaseContractOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
@@ -1237,6 +1260,7 @@
    executionDate: "",
    hasProductionRecord: false,
    createTime: "",
    purchaseContractNumber: "",
  },
  rules: {
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
@@ -1264,7 +1288,7 @@
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
    isProduction: false,
    isProduction: false, // é»˜è®¤å€¼ä¸º false(否)
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
@@ -1375,22 +1399,9 @@
const getDeliveryBatchNoList = async (productModelId) => {
  if (!productModelId) return [];
  const res = await getStockInventoryByModelId(productModelId);
  const rawList = Array.isArray(res?.data)
    ? res.data
    : res?.data?.records || res?.data?.rows || [];
  const rawList = res.data || [];
  const seenIds = new Set();
  return rawList
    .filter((item) => {
      if (!item?.id || !item?.batchNo || seenIds.has(item.id)) {
        return false;
      }
      seenIds.add(item.id);
      return true;
    })
    .map((item) => ({
      ...item,
      deliveryQuantity: 0,
    }));
};
const validateDeliveryShippingCarNumber = (_rule, value, callback) => {
  if (deliveryForm.value.type === "货车" && !value) {
@@ -1742,6 +1753,8 @@
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
    customerOption.value = res.data.records;
  });
  // èŽ·å–é‡‡è´­åˆåŒåˆ—è¡¨
  await getPurchaseContractList();
  form.value.entryPerson = userStore.id;
  if (type === "add") {
    // æ–°å¢žæ—¶è®¾ç½®å½•入日期为当天
@@ -1871,7 +1884,7 @@
      taxInclusiveTotalPrice: taxInclusiveTotalPrice,
      taxExclusiveTotalPrice: taxExclusiveTotalPrice,
      invoiceType: "增普票",
      isProduction: true,
      isProduction: false, // é»˜è®¤å€¼ä¸º false(否)
      productId: p.productId,
      productModelId: p.productModelId,
    };
@@ -1927,7 +1940,7 @@
  productOperationType.value = type;
  productForm.value = {};
  if (type === "add") {
    productForm.value.isProduction = true;
    productForm.value.isProduction = false; // é»˜è®¤å€¼ä¸º false(否)
  }
  proxy.resetForm("productFormRef");
  if (type === "edit") {
@@ -1963,6 +1976,17 @@
  }
  productFormVisible.value = true;
};
// èŽ·å–é‡‡è´­åˆåŒåˆ—è¡¨
const getPurchaseContractList = async () => {
  try {
    const res = await purchaseList({ current: -1, size: -1 });
    purchaseContractOptions.value = res.rows;
  } catch (error) {
    console.error("获取采购合同列表失败", error);
    purchaseContractOptions.value = [];
  }
};
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {