gaoluyang
3 天以前 fbad57aa29d1600dcb0c08817dc3d91522e1afb6
浪潮
1.仓储物流添加新增功能以及bug修改
已添加9个文件
已修改11个文件
4438 ■■■■ 文件已修改
src/api/inventoryManagement/stockIn.js 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockManage.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockOut.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockReport.js 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockWarning.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/index.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/index.vue 352 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/index.vue 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDia.vue 401 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDiaProduct.vue 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 436 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/components/FormDiaManual.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/components/FormDiaProduction.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/components/FormDiaPurchase.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 365 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockReport/index.vue 713 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockWarning/index.vue 431 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentHistory/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockIn.js
@@ -9,6 +9,41 @@
    });
};
// æŸ¥è¯¢ç”Ÿäº§å…¥åº“信息列表
export const getStockInPageByProduction = (params) => {
    return request({
        url: "/stockin/listPageByProduction",
        method: "get",
        params,
    });
};
// å‡ºåº“台账-查询自定义入库信息列表
export const getStockInPageByCustom = (params) => {
    return request({
        url: "/stockmanagement/listPageByCustom",
        method: "get",
        params,
    });
};
// å…¥åº“管理-查询自定义入库信息列表
export const getInPageByCustom = (params) => {
    return request({
        url: "/stockin/listPageByCustom",
        method: "get",
        params,
    });
};
// å‡ºåº“台账-查询生产出库信息列表
export const getStockInPageByProduct = (params) => {
    return request({
        url: "/stockmanagement/listPageByProduct",
        method: "get",
        params,
    });
};
// ä¿®æ”¹å…¥åº“存信息
export const updateStockIn = (data) => {
    return request({
@@ -26,6 +61,14 @@
        data,
    });
};
// ä¿®æ”¹ææ–™åº“存信息
export const updateManagementByCustom = (data) => {
    return request({
        url: "/stockin/updateManagementByCustom ",
        method: "post",
        data,
    });
};
// æ–°å¢žå•†å“å…¥åº“信息
export function addSutockIn(data) {
@@ -36,6 +79,32 @@
    })
}
// æ–°å¢žè‡ªå®šä¹‰å…¥åº“信息
export function addStockInCustom(data) {
    return request({
        url: '/stockin/addCustom',
        method: 'post',
        data: data
    })
}
// ç¼–辑自定义入库信息
export function updateStockInCustom(data) {
    return request({
        url: '/stockin/updateCustom',
        method: 'post',
        data: data
    })
}
// ç¼–辑成品入库信息
export function updateProduct(data) {
    return request({
        url: '/stockin/update',
        method: 'post',
        data: data
    })
}
// åˆ é™¤å…¥åº“信息
export function delStockIn(ids) {
    return request({
@@ -45,6 +114,15 @@
    })
}
// åˆ é™¤è‡ªå®šä¹‰å…¥åº“信息
export function delStockInCustom(ids) {
    return request({
        url: '/stockin/delteCustom',
        method: 'post',
        data: ids
    })
}
// å¯¼å‡ºå…¥åº“信息
export function exportStockIn(query) {
    return request({
src/api/inventoryManagement/stockManage.js
@@ -9,6 +9,24 @@
    });
};
// æŸ¥è¯¢ç”Ÿäº§å…¥åº“库存信息列表
export const getStockManagePageByProduction = (params) => {
    return request({
        url: "/stockin/listPageCopyByProduction",
        method: "get",
        params,
    });
};
// æŸ¥è¯¢è‡ªå®šä¹‰å…¥åº“库存信息列表
export const getStockManagePageByCustom = (params) => {
    return request({
        url: "/stockin/listPageCopyByCustom",
        method: "get",
        params,
    });
};
// ä¿®æ”¹åº“存信息
export const updateStockManage = (data) => {
@@ -38,7 +56,7 @@
    })
}
//出库接口
// å‡ºåº“管理-领用接口
export const stockOut = (data) => {
    return request({
        url: '/stockmanagement/stockout',
src/api/inventoryManagement/stockOut.js
@@ -1,6 +1,6 @@
import request from "@/utils/request";
//查询出库列表
// å‡ºåº“台账-采购出库查询出库列表
export const getStockOutPage = (params) => {
    return request({
        url: "/stockmanagement/listPage",
@@ -33,15 +33,5 @@
        url: '/stockmanagement/del',
        method: 'post',
        data: ids
    })
}
//导出出库信息
export const exportStockOut = (query) => {
    return request({
        url: '/stockmanagement/export',
        method: 'get',
        params: query,
        responseType: 'blob'
    })
}
src/api/inventoryManagement/stockReport.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
import request from "@/utils/request";
// èŽ·å–åº“å­˜æ—¥æŠ¥ç»Ÿè®¡
export const getStockDailyReport = (params) => {
    return request({
        url: "/stockin/getReportList",
        method: "get",
        params,
    });
};
// èŽ·å–åº“å­˜æœˆæŠ¥ç»Ÿè®¡
export const getStockMonthlyReport = (params) => {
    return request({
        url: "/stockin/getReportList",
        method: "get",
        params,
    });
};
// èŽ·å–ä½œä¸šæŠ¥è¡¨ç»Ÿè®¡
export const getWorkReport = (params) => {
    return request({
        url: "/stockin/getReportList",
        method: "get",
        params,
    });
};
// èŽ·å–åº“å­˜è¿›å‡ºå­˜ç»Ÿè®¡
export const getStockInOutReport = (params) => {
    return request({
        url: "/stockin/getReportList",
        method: "get",
        params,
    });
};
// å¯¼å‡ºåº“存报表
export const exportStockReport = (params) => {
    return request({
        url: "/stockin/exportCopy",
        method: "post",
        params,
        responseType: 'blob'
    });
};
// èŽ·å–åº“å­˜è¶‹åŠ¿æ•°æ®
export const getStockTrendData = (params) => {
    return request({
        url: "/stockreport/trend",
        method: "get",
        params,
    });
};
src/api/inventoryManagement/stockWarning.js
@@ -1,11 +1,14 @@
import request from "@/utils/request";
// æŸ¥è¯¢å‚¨æ°”罐预警列表
export const getStockWarningPage = (params) => {
export const getStockWarningPage = (page, params) => {
    return request({
        url: "/gasTankWarning/listPage",
        method: "get",
        params,
        params: {
            ...page,
            ...params
        },
    });
};
@@ -14,7 +17,7 @@
    return request({
        url: "/gasTankWarning/add",
        method: "post",
        data,
        data: data,
    });
};
@@ -22,8 +25,8 @@
export const updateStockWarning = (data) => {
    return request({
        url: "/gasTankWarning/update",
        method: "put",
        data,
        method: "post",
        data: data,
    });
};
@@ -32,7 +35,7 @@
    return request({
        url: "/gasTankWarning/delete",
        method: "delete",
        data: { ids },
        data: ids,
    });
};
src/utils/index.js
@@ -396,3 +396,16 @@
export function isEqual(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}
/**
 * èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
 * @returns {string} æ ¼å¼åŒ–的日期字符串
 */
export function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
src/views/inventoryManagement/dispatchLog/index.vue
@@ -2,21 +2,26 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
                <span class="search_title ml10">发料日期:</span>
                <el-date-picker
                    v-model="searchForm.timeStr"
                    type="date"
                    placeholder="请选择日期"
                    value-format="YYYY-MM-DD"
                    format="YYYY-MM-DD"
                    clearable
                    @change="handleQuery"
                />
                <span class="search_title ml10">产品大类:</span>
        <el-input
          v-model="searchForm.supplierName"
                    v-model="searchForm.productCategory"
          style="width: 240px"
          placeholder="请输入"
          @change="handleQuery"
          clearable
          prefix-icon="Search"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
          >搜索</el-button
        >
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <!-- <el-button type="primary" @click="openForm('add')">新增</el-button> -->
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
@@ -33,92 +38,19 @@
        style="width: 100%"
        :summary-method="summarizeMainTable"
        height="calc(100vh - 18.5em)"
        stripe
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="出库日期"
          prop="createTime"
          min-width="130"
          show-overflow-tooltip
        />
        <el-table-column
          label="供应商名称"
          prop="supplierName"
          width="250"
          show-overflow-tooltip
        />
        <el-table-column
          label="产品大类"
          prop="productCategory"
          width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="规格型号"
          prop="specificationModel"
          width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="单位"
          prop="unit"
          width="80"
          show-overflow-tooltip
        />
        <el-table-column
          label="出库数量"
          prop="inboundNum"
          width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="含税单价(元)"
          prop="taxInclusiveUnitPrice"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="含税总价(元)"
          prop="taxInclusiveTotalPrice"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="税率(%)"
          prop="taxRate"
          width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="不含税总价(元)"
          prop="taxExclusiveTotalPrice"
          width="180"
          show-overflow-tooltip
        />
        <el-table-column
          label="出库人"
          prop="createBy"
          width="80"
          show-overflow-tooltip
        />
        <!-- <el-table-column
          fixed="right"
          label="操作"
          min-width="60"
          align="center"
        >
          <template #default="scope">
            <el-button
              link
              type="primary"
              size="small"
              @click="openForm('edit', scope.row)"
              >编辑</el-button
            >
          </template>
        </el-table-column> -->
                <el-table-column label="发料日期" prop="createTime" show-overflow-tooltip width="130"/>
                <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
                <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
                <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
                <el-table-column label="单位" prop="unit" show-overflow-tooltip />
                <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
                <el-table-column label="发料数量" prop="inboundNum" show-overflow-tooltip />
                <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
                <el-table-column label="发料人" prop="createBy" show-overflow-tooltip />
      </el-table>
      <pagination
        v-show="total > 0"
@@ -134,54 +66,32 @@
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref } from "vue";
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import {
  getStockOutPage,
  delStockOut,
} from "@/api/inventoryManagement/stockOut.js";
const userStore = useUserStore();
import {
    getStockInPageByCustom,
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
const productList = ref([])
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
// æŸ¥è¯¢è¡¨å•数据
const data = reactive({
  searchForm: {
    supplierName: "",
  },
  form: {
    supplierId: null,
    supplierName: '',
    productId: null,
    productName: '',
    userId: userStore.userId,
    nickname: '',
    model: '',
    productModelId: null,
    unit: '',
    productrecordId: null,
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: '',
    taxRate: '',
    taxExclusiveTotalPrice: '',
    inboundTime: '',
    inboundBatch: '',
    inboundQuantity: ''
        customerName: "",
        productCategory:'',
        timeStr: getCurrentDate(),
  },
});
const { searchForm } = toRefs(data);
@@ -199,12 +109,25 @@
};
const getList = () => {
  tableLoading.value = true;
  getStockOutPage({ ...searchForm.value, ...page })
    const params = { ...page }
    params.supplierName = searchForm.value.supplierName
    params.timeStr = searchForm.value.timeStr
    params.productCategory = searchForm.value.productCategory
    // ææ–™å‡ºåº“:调用自定义出库记录接口
    const apiCall = getStockInPageByCustom(params)
    apiCall
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      tableData.value.map((item) => {
        item.children = [];
                // å‰ç«¯è®¡ç®—总价
                const inboundNum = Number(item.inboundNum) || 0;
                // ææ–™å‡ºåº“:总价 = taxInclusiveUnitPrice Ã— inboundNum
                const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0;
                item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2);
      });
      total.value = res.data.total;
    })
@@ -238,7 +161,9 @@
    type: "warning",
  })
    .then(() => {
      proxy.download("/stockmanagement/export", {}, "出库台账.xlsx");
            // æ ¹æ®ä¸åŒçš„ tab ç±»åž‹è°ƒç”¨ä¸åŒçš„导出接口
            const exportUrl = "/stockmanagement/exportTwo"
            proxy.download(exportUrl, {}, "出库台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
@@ -269,17 +194,178 @@
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.print-preview-dialog {
    .el-dialog__body {
        padding: 0;
        max-height: 80vh;
        overflow-y: auto;
    }
}
.print-preview-container {
    .print-preview-header {
        padding: 15px;
        border-bottom: 1px solid #e4e7ed;
        text-align: center;
        .el-button {
            margin: 0 10px;
        }
    }
    .print-preview-content {
        padding: 20px;
        background-color: #f5f5f5;
        min-height: 400px;
    }
}
.print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
}
.delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
}
.header {
    text-align: center;
    margin-bottom: 8px;
    .company-name {
        font-size: 18px;
        font-weight: bold;
        margin-bottom: 4px;
    }
    .document-title {
        font-size: 16px;
        font-weight: bold;
    }
}
.info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .info-row {
        line-height: 20px;
        .label {
            font-weight: bold;
            width: 60px;
            font-size: 14px;
        }
        .value {
            margin-right: 20px;
            min-width: 80px;
            font-size: 14px;
        }
    }
}
.table-section {
    margin-bottom: 4px;
    flex: 1;
    .product-table {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid #000;
        th, td {
            border: 1px solid #000;
            padding: 6px;
            text-align: center;
            font-size: 14px;
            line-height: 1.4;
        }
        th {
            font-weight: bold;
        }
        .total-label {
            text-align: right;
            font-weight: bold;
        }
        .total-value {
            font-weight: bold;
        }
    }
}
.footer-section {
    .footer-row {
        display: flex;
        margin-bottom: 3px;
        line-height: 20px;
        justify-content: space-between;
        .footer-item {
            display: flex;
            margin-right: 20px;
            .label {
                font-weight: bold;
                width: 80px;
                font-size: 14px;
            }
            .value {
                min-width: 80px;
                font-size: 14px;
            }
            &.address-item {
                .address-value {
                    min-width: 200px;
                }
            }
        }
    }
}
@media print {
    .app-container {
        display: none;
    }
    .print-page {
        box-shadow: none;
        margin: 0;
        padding: 10mm;
        padding-left: 20mm;
        page-break-inside: avoid;
        page-break-after: always;
    }
    .print-page:last-child {
        page-break-after: avoid;
    }
}
</style>
src/views/inventoryManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,309 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <div>
                <span class="search_title">发放季度:</span>
                <el-select
                    style="width: 200px;"
                    @change="handleQuery"
                    v-model="searchForm.season"
                    placeholder="请选择"
                    :clearable="false"
                >
                    <el-option :label="item.label" :value="item.value" v-for="(item,index) in jidu" :key="item.value" />
                </el-select>
                <span class="search_title ml10">员工名称:</span>
                <el-input
                    v-model="searchForm.staffName"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search"
                />
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
                >搜索</el-button
                >
            </div>
            <div>
                <el-button type="primary" @click="add" icon="Plus"> æ–°å¢ž </el-button>
                <el-button @click="handleOut" icon="download">导出</el-button>
                <el-button
                    type="danger"
                    icon="Delete"
                    :disabled="multipleList.length <= 0"
                    @click="deleteRow(multipleList.map((item) => item.id))"
                >
                    æ‰¹é‡åˆ é™¤
                </el-button>
            </div>
        </div>
        <div class="table_list">
            <el-table
                ref="tableRef"
                v-loading="tableLoading"
                :data="tableData"
                border
                height="calc(100vh - 21em)"
                :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
                style="width: 100%"
                @selection-change="handleSelectionChange"
            >
                <!-- é€‰æ‹©åˆ— -->
                <el-table-column
                    align="center"
                    type="selection"
                    width="55"
                    fixed="left"
                />
                <!-- åºå·åˆ— -->
                <el-table-column
                    align="center"
                    label="序号"
                    type="index"
                    width="60"
                    fixed="left"
                />
                <!-- å›ºå®šåˆ—:姓名 -->
                <el-table-column
                    label="姓名"
                    prop="staffName"
                    width="100"
                    show-overflow-tooltip
                    align="center"
                    fixed="left"
                />
                <!-- å›ºå®šåˆ—:工号 -->
                <el-table-column
                    label="工号"
                    prop="staffNo"
                    width="100"
                    show-overflow-tooltip
                    align="center"
                    fixed="left"
                />
                <!-- åŠ¨æ€åˆ—ï¼šæ ¹æ®å­—å…¸æ¸²æŸ“ -->
                <el-table-column
                    v-for="(dictItem, index) in sys_lavor_issue"
                    :key="dictItem.value"
                    :label="dictItem.label"
                    :prop="dictItem.value"
                    show-overflow-tooltip
                >
                </el-table-column>
                <!-- æ“ä½œåˆ— -->
                <el-table-column
                    label="操作"
                    width="150"
                    align="center"
                    fixed="right"
                >
                    <template #default="scope">
                        <el-button
                            type="primary"
                            link
                            size="small"
                            @click="edit(scope.row)"
                        >
                            ç¼–辑
                        </el-button>
                        <el-button
                            type="danger"
                            link
                            size="small"
                            :disabled="!!scope.row.adoptedDate"
                            @click="adopted(scope.row)"
                        >
                            é¢†ç”¨
                        </el-button>
                    </template>
                </el-table-column>
            </el-table>
            <pagination :total="total" layout="total, sizes, prev, pager, next, jumper"
                                    :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
        <!-- <Modal ref="modalRef" @success="handleQuery"></Modal> -->
        <!-- <files-dia ref="filesDia"></files-dia> -->
    </div>
</template>
<script setup>
import { ref, onMounted, reactive, toRefs, nextTick, getCurrentInstance } from 'vue'
import dayjs from "dayjs";
// import Modal from "./Modal.vue";
// import FilesDia from "./filesDia.vue";
import Pagination from "@/components/Pagination/index.vue";
import {listPage, deleteLedger, update} from "@/api/lavorissce/ledger.js";
import {ElMessageBox, ElMessage} from "element-plus";
const { proxy } = getCurrentInstance();
import { getCurrentMonth } from "@/utils/util"
const page = ref({
    current: 1,
    size: 100,
})
const total = ref(0)
// å“åº”式数据
const tableRef = ref(null)
const tableData = ref([])
const tableLoading = ref(false)
const { sys_lavor_issue } = proxy.useDict("sys_lavor_issue")
const data = reactive({
    searchForm: {
        season: "",
        staffName: "",
    },
});
const { searchForm } = toRefs(data);
const modalRef = ref();
// const filesDia = ref();
const multipleList = ref([]);
const jidu = ref([
    {
        value: '1',
        label: '第一季度'
    },
    {
        value: '2',
        label: '第二季度'
    },
    {
        value: '3',
        label: '第三季度'
    },
    {
        value: '4',
        label: '第四季度'
    }
])
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.value.current = 1;
    getList();
};
// èŽ·å–å­—å…¸æ•°æ®
const getList = async () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page.value };
    listPage(params).then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        total.value = res.data.total;
    }).catch(err => {
        tableLoading.value = false;
    })
}
const add = () => {
    modalRef.value.openModal();
};
const edit = (row) => {
    modalRef.value.loadForm(row);
};
const deleteRow = (id) => {
    ElMessageBox.confirm("此操作将永久删除该数据, æ˜¯å¦ç»§ç»­?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
    }).then(async () => {
        const { code } = await deleteLedger(id);
        if (code == 200) {
            ElMessage({
                type: "success",
                message: "删除成功",
            });
            await getList();
        }
    });
};
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download(`/lavorIssue/exportCopy`, {season: searchForm.value.season}, "劳保台账.xlsx");
        })
        .catch(() => {
            ElMessage.info("已取消");
        });
};
const adopted = (row) => {
    ElMessageBox.confirm("是否确认领用?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
    }).then(async () => {
        const params = {
            id: row.id,
            adoptedDate: dayjs().format("YYYY-MM-DD")
        }
        const { code } = await update(params);
        if (code == 200) {
            ElMessage({
                type: "success",
                message: "领用成功",
            });
            await getList();
        }
    })
}
// æ‰“开附件弹框
// const openFilesFormDia = (row) => {
//     nextTick(() => {
//         filesDia.value?.openDialog( row,'收入')
//     })
// };
// äº‹ä»¶å¤„理函数
const handleSelectionChange = (selection) => {
    multipleList.value = selection;
}
const paginationChange = (pagination) => {
    page.value.current = pagination.page;
    page.value.size = pagination.limit;
    getList();
}
// ç»„件挂载时加载字典数据
onMounted(() => {
    handleQuery()
})
</script>
<style scoped>
.dynamic-table-container {
    width: 100%;
}
.pagination-container {
    margin-top: 20px;
    display: flex;
    justify-content: flex-end;
}
:deep(.el-table .el-table__header-wrapper th) {
    background-color: #F0F1F5 !important;
    color: #333333;
    font-weight: 600;
}
:deep(.el-table .el-table__body-wrapper td) {
    padding: 8px 0;
}
:deep(.el-select) {
    width: 100%;
}
:deep(.el-input) {
    width: 100%;
}
</style>
src/views/inventoryManagement/issueManagement/index.vue
@@ -2,56 +2,70 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
        <span class="search_title ml10">产品大类:</span>
        <el-input
          v-model="searchForm.productCategory"
          style="width: 240px"
          placeholder="请输入"
          clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <!-- <el-button type="primary" @click="openForm('add')">新增出库</el-button> -->
        <el-button @click="handleOut">导出</el-button>
        <!-- <el-button type="danger" plain @click="handleDelete">删除</el-button> -->
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="row => row.id"
        show-summary
        style="width: 100%"
        :summary-method="summarizeMainTable"
        height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="入库批次" prop="inboundBatches" width="160" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="80" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm(scope.row);">领用</el-button>
            <el-button link type="primary" size="small" @click="openForm(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" />
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="'新增出库'" width="40%" @close="closeDia">
    <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="40%" @close="closeDia" draggable>
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="出库数量:" prop="salesContractNo">
          <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
            <div>{{getAvailableQuantityText()}}:{{currentRowNum}}</div>
        <el-form-item :label="getQuantityLabel()" prop="salesContractNo">
          <el-input-number :step="0.01" :min="0" :max="currentRowNum" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="出库日期:" prop="projectName">
        <el-form-item :label="getDateLabel()" prop="projectName">
          <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
            type="date" placeholder="请选择" clearable />
        </el-form-item>
        <el-form-item label="出库人:" prop="entryPerson">
          <el-select v-model="form.nickName" placeholder="请选择" clearable>
        <el-form-item :label="getPersonLabel()" prop="entryPerson">
          <el-select v-model="form.nickName"
                                         filterable
                     default-first-option
                     :reserve-keyword="false" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
          </el-select>
        </el-form-item>
@@ -68,18 +82,18 @@
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import {
  getStockInPage
    getInPageByCustom
} from "@/api/inventoryManagement/stockIn.js";
import {
  getStockManagePage,
    delStockManage,
    stockOut,
} from "@/api/inventoryManagement/stockManage.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
@@ -98,11 +112,12 @@
const dialogFormVisible = ref(false)
const data = reactive({
  searchForm: {
    supplierName: '',
    inboundQuantity:'',
    inboundTime:'',
    nickName: '',
    userId: '',
    productCategory:'',
    // timeStr: getCurrentDate(),
  },
  form: {
    productrecordId: '',
@@ -128,11 +143,14 @@
}
const getList = () => {
  tableLoading.value = true
  getStockInPage({ ...searchForm.value, ...page }).then(res => {
  const params = { ...page }
  params.productCategory = searchForm.value.productCategory
  params.flag = true
  const apiCall = getInPageByCustom(params)
  apiCall.then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    console.log('res', res.data.records)
  }).catch(() => {
    tableLoading.value = false
  })
@@ -176,37 +194,52 @@
  currentRowNum.value = row.inboundNum0
  salesLedgerProductId.value = row.salesLedgerProductId
  form.value = {}
  // åˆå§‹åŒ–表单数据
  form.value = {
    productrecordId: '',
    inboundQuantity: '', // å‡ºåº“数量清空
    inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
    nickName: '', // é»˜è®¤å½“前用户
  }
  console.log('form',form.value)
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  try {
    const userLists = await userListNoPageByTenantId()
    userList.value = userLists.data
    // ææ–™å‡ºåº“默认发料人
    const defaultPersonName = '吴玉梅'
    const defaultPerson = userList.value.find(user => user.nickName === defaultPersonName)
    const defaultUserId = defaultPerson ? defaultPerson.userId : ''
    // åˆå§‹åŒ–表单数据
    form.value = {
      productrecordId: '',
      inboundQuantity: currentRowNum.value, // ä½¿ç”¨currentRowNum作为默认值
      inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
      nickName: defaultUserId, // æ ¹æ®tab类型设置默认发货人
    }
    console.log('form',form.value)
  } catch (error) {
    console.error('加载用户列表失败:', error)
    // å¦‚果加载失败,使用空值初始化
    form.value = {
      productrecordId: '',
      inboundQuantity: currentRowNum.value,
      inboundTime: getCurrentDate(),
      nickName: '',
    }
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
  let num = Number(form.value.inboundQuantity)
  if(num < 1 || num > currentRowNum.value){
  if(num <= 0 || num > currentRowNum.value){
    return proxy.$modal.msgWarning("请填入有效数字")
  }
  proxy.$refs["formRef"].validate(valid => {
    if (valid && currentRowId.value) {
      const outData = {
        id: currentRowId.value, // åŽŸå§‹è®°å½•ID
        salesLedgerProductId: salesLedgerProductId.value,
        salesLedgerProductId: 0,
        quantity: form.value.inboundQuantity, // å‡ºåº“数量
        time: form.value.inboundTime, // å‡ºåº“æ—¶é—´
        userId: form.value.nickName // æ“ä½œäºº
        userId: form.value.nickName, // æ“ä½œäºº
        type: 3 // å‡ºåº“类型:自定义/材料
      }
      console.log(outData)
@@ -236,7 +269,8 @@
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/stockin/export", {}, '入库台账.xlsx')
    const exportUrl = "/stockin/exportTwo"
    proxy.download(exportUrl, {}, '出库台账.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
@@ -266,17 +300,38 @@
    proxy.$modal.msg("已取消")
  })
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
// æ ¹æ®tab类型获取弹框标题
const getDialogTitle = () => {
  return '新增发料';
};
// æ ¹æ®tab类型获取可出库数量文本
const getAvailableQuantityText = () => {
  return '可发料数量';
};
// æ ¹æ®tab类型获取数量字段标签
const getQuantityLabel = () => {
  return '发料数量:';
};
// æ ¹æ®tab类型获取日期字段标签
const getDateLabel = () => {
  return '发料日期:';
};
// æ ¹æ®tab类型获取人员字段标签
const getPersonLabel = () => {
  return '发料人:';
};
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/components/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,401 @@
<template>
  <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="70%"
    @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-form-item label="采购订单号" prop="purchaseContractNumber">
        <el-select
          v-model="form.purchaseContractNumber"
          placeholder="请选择采购订单号"
          clearable
          filterable
          :loading="loadingPurchaseOptions"
          @change="handlePurchaseChange"
          :disabled="operationType === 'edit'"
          style="width: 100%"
        >
          <el-option
            v-for="item in purchaseOptions"
            :key="item.purchaseContractNumber"
            :label="formatPurchaseOption(item)"
            :value="item.purchaseContractNumber"
          />
        </el-select>
      </el-form-item>
      <el-table
        :data="productList"
        border
        v-loading="loadingProducts"
        @selection-change="handleSelectionChange"
      >
        <el-table-column align="center" type="selection" width="55" />
        <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="supplierName" width="100" /> -->
        <el-table-column label="采购数量" prop="quantity" width="100" />
        <el-table-column label="待入库数量" prop="quantity0" width="100" />
        <el-table-column label="本次入库数量" prop="quantityStock" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="税率(%)" prop="taxRate" width="120" />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150">
                    <template #default="scope">
                        <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" :disabled="operationType === 'edit'"/>
                    </template>
                </el-table-column>
        <el-table-column
          label="总价(元)"
                    :formatter="formattedNumber"
          prop="taxInclusiveTotalPrice"
          width="150"
        >
        </el-table-column>
      </el-table>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
import useUserStore from '@/store/modules/user'
import {
  updateStockIn,
  addSutockIn,
  selectProductRecordListByPuechaserId
} from "@/api/inventoryManagement/stockIn.js";
import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close', 'success'])
const operationType = ref('')// æ“ä½œç±»åž‹: 'add' æˆ– 'edit'
const dialogFormVisible = ref(false)// å¼¹æ¡†æ˜¾ç¤ºçŠ¶æ€
const productList = ref([]);// äº§å“åˆ—表数据
const loadingProducts = ref(false);// äº§å“åŠ è½½çŠ¶æ€
const selectedRows = ref([]) // äº§å“è¡¨æ ¼é€‰ä¸­è¡Œ
const purchaseOptions = ref([])
const loadingPurchaseOptions = ref(false)
const loading = ref(false);
const data = reactive({
  form: {
    id: null,
    purchaseContractNumber: '', // é‡‡è´­è®¢å•号
    supplierId: null,       // ä¾›åº”商ID
    supplierName: '',       // ä¾›åº”商名称
    inboundTime: '',        // å…¥åº“æ—¶é—´
    inboundBatch: '',       // å…¥åº“批次
    recorderId: userStore.userId, // å½•入人ID
    recorderName: userStore.name, // å½•入人姓名
    entryDate: getCurrentDate(),  // å½•入日期
    remark: '',             // å¤‡æ³¨
  },
  rules: {
    purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
    supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
    inboundTime: [{ required: true, message: "请选择入库时间", trigger: "change" }],
    inboundBatch: [{ required: true, message: "请输入入库批次", trigger: "blur" }]
  }
})
const { form, rules } = toRefs(data)
// åŠ¨æ€è®¡ç®—å¯¹è¯æ¡†æ ‡é¢˜
const getDialogTitle = () => {
  return operationType.value === 'add' ? '新增入库' : '编辑入库'
}
const formatPurchaseOption = (item = {}) => {
  const contract = item.purchaseContractNumber || '--';
  const supplier = item.supplierName ? ` Â· ${item.supplierName}` : '';
  return `${contract}${supplier}`;
};
const loadPurchaseOptions = async (keyword = '') => {
  try {
    loadingPurchaseOptions.value = true;
    const res = await purchaseListPage({
      current: -1,
      size: -1,
      purchaseContractNumber: keyword,
    });
    const records = res.data?.records || [];
    purchaseOptions.value = records;
    if (
      form.value.purchaseContractNumber &&
      !purchaseOptions.value.find(
        (item) => item.purchaseContractNumber === form.value.purchaseContractNumber
      )
    ) {
      purchaseOptions.value.push({
        purchaseContractNumber: form.value.purchaseContractNumber,
        supplierName: form.value.supplierName,
        supplierId: form.value.supplierId,
      });
    }
  } finally {
    loadingPurchaseOptions.value = false;
  }
};
const handlePurchaseChange = (value) => {
  form.value.purchaseContractNumber = value || '';
  const matched = purchaseOptions.value.find(
    (item) => item.purchaseContractNumber === value
  );
  if (matched) {
    form.value.supplierName = matched.supplierName || form.value.supplierName;
    form.value.supplierId = matched.supplierId || form.value.supplierId;
  }
  if (!value) {
    productList.value = [];
    return;
  }
  fetchProductsByContract();
};
const exceedsAddLimit = (product) => {
  const stock = Number(product?.quantityStock ?? 0);
  const waiting = Number(product?.quantity0 ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(waiting)) {
    return false;
  }
  return stock > waiting;
};
const exceedsEditLimit = (product) => {
  const stock = Number(product?.quantityStock ?? 0);
  const waiting = Number(product?.quantity0 ?? 0);
  const original = Number(product?.originalQuantityStock ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(waiting) || !Number.isFinite(original)) {
    return false;
  }
  return stock > waiting + original;
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
// è®¡ç®—总价
const calculateTotalPrice = (row) => {
  const quantityStock = Number(row?.quantityStock ?? 0);
  const taxInclusiveUnitPrice = Number(row?.taxInclusiveUnitPrice ?? 0);
  if (Number.isFinite(quantityStock) && Number.isFinite(taxInclusiveUnitPrice)) {
    row.taxInclusiveTotalPrice = quantityStock * taxInclusiveUnitPrice;
  } else {
    row.taxInclusiveTotalPrice = 0;
  }
};
const fetchProductsByContract = async () => {
  if (!form.value.purchaseContractNumber) {
    proxy.$modal.msgWarning('请选择合同号')
    return
  }
  try {
    loadingProducts.value = true
    const productRes = await selectProductRecordListByPuechaserId({
      purchaseContractNumber: form.value.purchaseContractNumber
    });
    if (!productRes.data || productRes.data.length === 0) {
      proxy.$modal.msgWarning('该合同下没有产品记录')
      productList.value = [];
      return
    }
    productList.value = productRes.data.map(item => {
      const quantityStock = Number(item?.quantity0 ?? 0);
      const taxInclusiveUnitPrice = Number(item?.taxInclusiveUnitPrice ?? 0);
      return {
        ...item,
        quantityStock,
        taxInclusiveUnitPrice,
        taxInclusiveTotalPrice: quantityStock * taxInclusiveUnitPrice,
        originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? 0),
      };
    })
  } catch (error) {
    console.error('查询产品记录失败:', error)
    proxy.$modal.msgError('查询产品记录失败')
    productList.value = [];
  } finally {
    loadingProducts.value = false
  }
}
const updatePro = async () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning('请先选择产品');
    return;
  }
  const target = selectedRows.value[0];
  const stock = Number(target?.quantityStock ?? 0);
  if (!Number.isFinite(stock) || stock <= 0) {
    proxy.$modal.msgWarning('请填写有效的入库数量');
    return;
  }
  if (exceedsEditLimit(target)) {
    proxy.$modal.msgError('本次入库数量不能超过原入库数量与待入库数量之和');
    return;
  }
  const stockInData = {
    id: selectedRows.value[0].recordId,
    quantityStock: Number(selectedRows.value[0].quantityStock),
  };
  await updateStockIn(stockInData)
  proxy.$modal.msgSuccess('修改入库成功')
  closeDia()
  emit('success')
}
const submitForm = async () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning('请先选择采购合同并选择产品')
    return
  }
  if(operationType.value !== 'add'){
    await updatePro()
    return
  }
  try {
    await proxy.$refs.formRef.validate()
    const invalidProducts = selectedRows.value.filter((product) => {
        const stock = Number(product?.quantityStock ?? 0);
        if (!Number.isFinite(stock) || stock <= 0) {
          return true;
        }
        return exceedsAddLimit(product);
    })
    if (invalidProducts.length > 0) {
      proxy.$modal.msgError('本次入库数量需大于0,且不能超过待入库数量')
      return
    }
    const stockInData = {
      ...form.value,
      inboundTime: formatDateTime(form.value.inboundTime),
      nickName: userStore.nickName,
      details: selectedRows.value.map(product => ({
        id: product.id,
        inboundQuantity: Number(product.quantityStock),
                taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice),
                taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice)
      })),
    };
    loading.value = true
    await addSutockIn(stockInData)
    proxy.$modal.msgSuccess('新增入库成功')
    closeDia()
    emit('success')
  } catch (error) {
    console.error('提交失败:', error)
    if (!error.errors) {
      proxy.$modal.msgError('操作失败,请重试')
    }
  } finally {
    loading.value = false
  }
}
const closeDia = () => {
  proxy.$refs.formRef.resetFields()
  dialogFormVisible.value = false
  emit('close')
}
const handleSelectionChange = (selection) => {
  selectedRows.value = selection.filter(item => item.id);
}
function formatDateTime(date = new Date(), includeTime = true) {
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  if (!includeTime) {
    return `${year}-${month}-${day}`;
  }
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const openDialog = async (type, row) => {
  operationType.value = type
  dialogFormVisible.value = true
  selectedRows.value = []
  await loadPurchaseOptions();
  if (type === 'add') {
    form.value = {
      id: null,
      purchaseContractNumber: '',
      supplierId: null,
      supplierName: '',
      inboundTime: '',
      inboundBatch: '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: ''
    }
    productList.value = []
  } else {
    form.value = JSON.parse(JSON.stringify(row))
    try {
      loadingProducts.value = true
      const res = await selectProductRecordListByPuechaserId({
        purchaseContractNumber: form.value.purchaseContractNumber,
        id: row.id
      });
      productList.value = res.data.map(item => ({
        ...item,
        quantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
        taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0),
        taxInclusiveTotalPrice: Number(item?.quantityStock ?? 0) * Number(item?.taxInclusiveUnitPrice ?? 0),
        originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
      }))
      selectedRows.value = productList.value
    } catch (error) {
      console.error('加载产品失败:', error)
      proxy.$modal.msgError('加载产品失败')
      productList.value = []
    } finally {
      loadingProducts.value = false
    }
  }
}
defineExpose({
  openDialog,
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,310 @@
<template>
  <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增自定义入库' : '编辑自定义入库'" width="70%"
    @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <div style="margin-bottom: 10px;" v-if="operationType === 'add'">
        <el-button type="primary" @click="addProductRow">新增</el-button>
      </div>
      <el-table
        :data="productList"
        border
        v-loading="loadingProducts"
      >
        <el-table-column
          align="center"
          label="序号"
          type="index"
          width="60"
        />
        <el-table-column label="产品大类" prop="productCategory" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.productCategory" placeholder="请输入产品大类" />
          </template>
        </el-table-column>
        <el-table-column label="规格型号" prop="specificationModel" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.specificationModel" placeholder="请输入规格型号" />
          </template>
        </el-table-column>
        <el-table-column label="单位" prop="unit" width="100">
          <template #default="scope">
            <el-input v-model="scope.row.unit" placeholder="请输入单位" />
          </template>
        </el-table-column>
        <el-table-column label="供应商" prop="supplierName" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.supplierName" placeholder="请输入供应商" />
          </template>
        </el-table-column>
        <el-table-column label="物品类型" prop="itemType" width="150">
          <template #default="scope">
            <el-input v-model="scope.row.itemType" placeholder="请输入物品类型" />
          </template>
        </el-table-column>
        <el-table-column label="入库数量" prop="inboundNum" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="入库日期" prop="inboundDate" width="180">
          <template #default="scope">
            <el-date-picker
              v-model="scope.row.inboundDate"
              type="date"
              placeholder="请选择入库日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              style="width: 100%"
            />
          </template>
        </el-table-column>
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column
           label="总价(元)"
           prop="taxInclusiveTotalPrice"
           width="150"
         >
        </el-table-column>
        <el-table-column label="操作" width="80" v-if="operationType === 'add'">
          <template #default="scope">
            <el-button type="danger" size="small" @click="removeProductRow(scope.$index)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
import useUserStore from '@/store/modules/user'
import {
  addStockInCustom,
  updateStockInCustom,
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close', 'success'])
const operationType = ref('')// æ“ä½œç±»åž‹: 'add' æˆ– 'edit'
const dialogFormVisible = ref(false)// å¼¹æ¡†æ˜¾ç¤ºçŠ¶æ€
const productList = ref([]);// äº§å“åˆ—表数据
const loadingProducts = ref(false);// äº§å“åŠ è½½çŠ¶æ€
const loading = ref(false);
function formatDateTime(date = new Date(), includeTime = true) {
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  if (!includeTime) {
    return `${year}-${month}-${day}`;
  }
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const taxRateOptions = [
  { label: '1', value: 1 },
  { label: '6', value: 6 },
  { label: '13', value: 13 },
]
const data = reactive({
  form: {
    id: null,
    supplierId: null,       // ä¾›åº”商ID
    supplierName: '',       // ä¾›åº”商名称
    recorderId: userStore.userId, // å½•入人ID
    recorderName: userStore.name, // å½•入人姓名
    entryDate: getCurrentDate(),  // å½•入日期
    remark: '',             // å¤‡æ³¨
  },
  rules: {
    supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }]
  }
})
const { form, rules } = toRefs(data)
// æ–°å¢žäº§å“è¡Œ
const addProductRow = () => {
  productList.value.push({
    id: null,
    productCategory: '',
    specificationModel: '',
    unit: '',
    supplierName: form.value.supplierName || '',
    itemType: '',
    inboundNum: 0,
    inboundDate: getCurrentDate(), // é»˜è®¤å½“天日期
    // quantityStock: 0,
    taxInclusiveUnitPrice: 0,
    taxInclusiveTotalPrice: 0,
    taxRate: null,
    taxExclusiveTotalPrice: 0,
  });
};
// åˆ é™¤äº§å“è¡Œ
const removeProductRow = (index) => {
  productList.value.splice(index, 1);
};
// è®¡ç®—总价(根据数量、单价和含税单价)
const calculateTotalPrice = (row) => {
  // è®¡ç®—普通总价:inboundNum * taxInclusiveUnitPrice
  const quantity = Number(row.inboundNum || 0);
  const taxInclusiveUnitPrice = Number(row.taxInclusiveUnitPrice || 0);
  row.taxInclusiveTotalPrice = quantity * taxInclusiveUnitPrice;
  calculateExclusivePrice(row);
};
// è®¡ç®—不含税总价(根据含税总价和税率)
const calculateExclusivePrice = (row) => {
  const taxInclusiveTotalPrice = Number(row.taxInclusiveTotalPrice || 0);
  const taxRate = Number(row.taxRate || 0);
  row.taxExclusiveTotalPrice = taxInclusiveTotalPrice / (1 + taxRate / 100);
};
const submitForm = async () => {
  try {
    await proxy.$refs.formRef.validate()
    if (!productList.value.length) {
      proxy.$modal.msgError('请至少添加一条产品数据')
      return
    }
    // éªŒè¯è‡ªå®šä¹‰æ·»åŠ çš„æ•°æ®å¿…å¡«å­—æ®µ
    for (let i = 0; i < productList.value.length; i++) {
      const product = productList.value[i];
      if (!product.productCategory || !product.specificationModel || !product.unit) {
        proxy.$modal.msgError(`第${i + 1}行产品数据未填写完整(产品大类、规格型号、单位为必填)`)
        return
      }
      if (!product.itemType) {
        proxy.$modal.msgError(`第${i + 1}行请选择物品类型`)
        return
      }
      if (!product.inboundDate) {
        proxy.$modal.msgError(`第${i + 1}行请选择入库日期`)
        return
      }
      const stock = Number(product?.inboundNum ?? 0);
      if (!Number.isFinite(stock) || stock <= 0) {
        proxy.$modal.msgError(`第${i + 1}行本次入库数量需大于0`)
        return
      }
    }
    const payloadList = productList.value.map(product => ({
      id: product.id ?? null,
            inboundNum: Number(product.inboundNum),
      productCategory: product.productCategory,
      specificationModel: product.specificationModel,
      unit: product.unit,
      supplierName: product.supplierName || form.value.supplierName,
      itemType: product.itemType,
      inboundDate: formatDateTime(product.inboundDate, false),
      taxRate: Number(product.taxRate || 0),
      taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0),
            taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice || 0),
            taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice || 0),
    }));
    loading.value = true
    if (operationType.value === 'edit') {
      const editPayload = payloadList[0]
      await updateStockInCustom(editPayload)
    } else {
      await addStockInCustom(payloadList)
    }
    proxy.$modal.msgSuccess(operationType.value === 'edit' ? '编辑自定义入库成功' : '新增自定义入库成功')
    closeDia()
    emit('success')
  } catch (error) {
    console.error('提交失败:', error)
    if (!error.errors) {
      proxy.$modal.msgError('操作失败,请重试')
    }
  } finally {
    loading.value = false
  }
}
const closeDia = () => {
  proxy.$refs.formRef.resetFields()
  dialogFormVisible.value = false
  productList.value = []
  emit('close')
}
const openDialog = async (type, row) => {
  operationType.value = type
  dialogFormVisible.value = true
  if (type === 'add') {
    form.value = {
      id: null,
      supplierId: null,
      supplierName: '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: ''
    }
    productList.value = []
  } else {
    // ç¼–辑模式:将行数据填充到表格中以支持修改
    form.value = {
      id: row?.id ?? null,
      supplierId: row?.supplierId ?? null,
      supplierName: row?.supplierName ?? '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: row?.remark ?? ''
    }
    productList.value = [{
      id: row?.id ?? null,
      productCategory: row?.productCategory ?? '',
      specificationModel: row?.specificationModel ?? '',
      unit: row?.unit ?? '',
      supplierName: row?.supplierName ?? '',
      itemType: row?.itemType ?? '',
      inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0),
      inboundDate: row?.inboundDate ?? row?.createTime ?? '',
      taxRate: Number(row?.taxRate ?? 0),
      taxInclusiveUnitPrice: Number(row?.taxInclusiveUnitPrice ?? 0),
      taxInclusiveTotalPrice: Number(row?.taxInclusiveTotalPrice ?? 0),
      taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0),
    }]
  }
}
defineExpose({
  openDialog,
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/components/formDiaProduct.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,300 @@
<template>
  <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增自定义入库' : '编辑自定义入库'" width="70%"
    @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <div style="margin-bottom: 10px;" v-if="operationType === 'add'">
        <el-button type="primary" @click="addProductRow">新增</el-button>
      </div>
      <el-table
        :data="productList"
        border
        v-loading="loadingProducts"
      >
        <el-table-column
          align="center"
          label="序号"
          type="index"
          width="60"
        />
        <el-table-column label="产品大类" prop="productCategory" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.productCategory" placeholder="请输入产品大类" />
          </template>
        </el-table-column>
        <el-table-column label="规格型号" prop="specificationModel" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.specificationModel" placeholder="请输入规格型号" />
          </template>
        </el-table-column>
        <el-table-column label="单位" prop="unit" width="100">
          <template #default="scope">
            <el-input v-model="scope.row.unit" placeholder="请输入单位" />
          </template>
        </el-table-column>
        <el-table-column label="入库数量" prop="inboundNum" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="入库日期" prop="inboundDate" width="180">
          <template #default="scope">
            <el-date-picker
              v-model="scope.row.inboundDate"
              type="date"
              placeholder="请选择入库日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              style="width: 100%"
            />
          </template>
        </el-table-column>
        <el-table-column label="单价(元)" prop="unitPrice" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.unitPrice" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column
           label="总价(元)"
           prop="totalPrice"
           width="150"
         >
        </el-table-column>
        <el-table-column label="操作" width="80" v-if="operationType === 'add'">
          <template #default="scope">
            <el-button type="danger" size="small" @click="removeProductRow(scope.$index)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
import useUserStore from '@/store/modules/user'
import {
    addStockInCustom, updateProduct
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close', 'success'])
const operationType = ref('')// æ“ä½œç±»åž‹: 'add' æˆ– 'edit'
const dialogFormVisible = ref(false)// å¼¹æ¡†æ˜¾ç¤ºçŠ¶æ€
const productList = ref([]);// äº§å“åˆ—表数据
const loadingProducts = ref(false);// äº§å“åŠ è½½çŠ¶æ€
const loading = ref(false);
function formatDateTime(date = new Date(), includeTime = true) {
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  if (!includeTime) {
    return `${year}-${month}-${day}`;
  }
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const itemTypeOptions = [
  { label: '物料', value: '物料' },
  { label: '原料', value: '原料' },
  { label: '成品', value: '成品' },
  { label: '其他', value: '其他' },
]
const taxRateOptions = [
  { label: '1', value: 1 },
  { label: '6', value: 6 },
  { label: '13', value: 13 },
]
const data = reactive({
  form: {
    id: null,
    supplierId: null,       // ä¾›åº”商ID
    supplierName: '',       // ä¾›åº”商名称
    recorderId: userStore.userId, // å½•入人ID
    recorderName: userStore.name, // å½•入人姓名
    entryDate: getCurrentDate(),  // å½•入日期
    remark: '',             // å¤‡æ³¨
  },
  rules: {
    supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }]
  }
})
const { form, rules } = toRefs(data)
// æ–°å¢žäº§å“è¡Œ
const addProductRow = () => {
  productList.value.push({
    id: null,
    productCategory: '',
    specificationModel: '',
    unit: '',
    supplierName: form.value.supplierName || '',
    itemType: '',
    inboundNum: 0,
    inboundDate: '',
    quantityStock: 0,
    unitPrice: 0,
    totalPrice: 0,
    taxRate: null,
    taxExclusiveTotalPrice: 0,
  });
};
// åˆ é™¤äº§å“è¡Œ
const removeProductRow = (index) => {
  productList.value.splice(index, 1);
};
// è®¡ç®—总价(根据数量、单价和含税单价)
const calculateTotalPrice = (row) => {
  // è®¡ç®—普通总价:inboundNum * unitPrice
  const quantity = Number(row.inboundNum || 0);
  const unitPrice = Number(row.unitPrice || 0);
  row.totalPrice = quantity * unitPrice;
  calculateExclusivePrice(row);
};
// è®¡ç®—不含税总价(根据含税总价和税率)
const calculateExclusivePrice = (row) => {
  const totalPrice = Number(row.totalPrice || 0);
  const taxRate = Number(row.taxRate || 0);
  row.taxExclusiveTotalPrice = totalPrice / (1 + taxRate / 100);
};
const submitForm = async () => {
  try {
    await proxy.$refs.formRef.validate()
    if (!productList.value.length) {
      proxy.$modal.msgError('请至少添加一条产品数据')
      return
    }
    // éªŒè¯è‡ªå®šä¹‰æ·»åŠ çš„æ•°æ®å¿…å¡«å­—æ®µ
    for (let i = 0; i < productList.value.length; i++) {
      const product = productList.value[i];
      if (!product.productCategory || !product.specificationModel || !product.unit) {
        proxy.$modal.msgError(`第${i + 1}行产品数据未填写完整(产品大类、规格型号、单位为必填)`)
        return
      }
      if (!product.inboundDate) {
        proxy.$modal.msgError(`第${i + 1}行请选择入库日期`)
        return
      }
      const stock = Number(product?.inboundNum ?? 0);
      if (!Number.isFinite(stock) || stock <= 0) {
        proxy.$modal.msgError(`第${i + 1}行本次入库数量需大于0`)
        return
      }
    }
    const payloadList = productList.value.map(product => ({
      id: product.id ?? null,
            inboundNum: Number(product.inboundNum),
      productCategory: product.productCategory,
      specificationModel: product.specificationModel,
      unit: product.unit,
      supplierName: product.supplierName || form.value.supplierName,
      itemType: product.itemType,
      inboundDate: formatDateTime(product.inboundDate, false),
      taxRate: Number(product.taxRate || 0),
      taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0),
            unitPrice: Number(product.unitPrice || 0),
    }));
    loading.value = true
    if (operationType.value === 'edit') {
      const editPayload = payloadList[0]
      await updateProduct(editPayload)
    } else {
      await addStockInCustom(payloadList)
    }
    proxy.$modal.msgSuccess(operationType.value === 'edit' ? '编辑自定义入库成功' : '新增自定义入库成功')
    closeDia()
    emit('success')
  } catch (error) {
    console.error('提交失败:', error)
    if (!error.errors) {
      proxy.$modal.msgError('操作失败,请重试')
    }
  } finally {
    loading.value = false
  }
}
const closeDia = () => {
  proxy.$refs.formRef.resetFields()
  dialogFormVisible.value = false
  productList.value = []
  emit('close')
}
const openDialog = async (type, row) => {
  operationType.value = type
  dialogFormVisible.value = true
  if (type === 'add') {
    form.value = {
      id: null,
      supplierId: null,
      supplierName: '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: ''
    }
    productList.value = []
  } else {
    // ç¼–辑模式:将行数据填充到表格中以支持修改
    form.value = {
      id: row?.id ?? null,
      supplierId: row?.supplierId ?? null,
      supplierName: row?.supplierName ?? '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: row?.remark ?? ''
    }
    productList.value = [{
      id: row?.id ?? null,
      productCategory: row?.productCategory ?? '',
      specificationModel: row?.specificationModel ?? '',
      unit: row?.unit ?? '',
      supplierName: row?.supplierName ?? '',
      itemType: row?.itemType ?? '',
      inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0),
      inboundDate: row?.inboundDate ?? row?.createTime ?? '',
      taxRate: Number(row?.taxRate ?? 0),
      unitPrice: Number(row?.unitPrice ?? 0),
      taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0),
    }]
  }
}
defineExpose({
  openDialog,
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/index.vue
@@ -2,9 +2,23 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker
          v-model="searchForm.timeStr"
          type="date"
          placeholder="请选择日期"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          clearable
          @change="handleQuery"
        />
        <span class="search_title ml10">产品大类:</span>
        <el-input
          v-model="searchForm.productCategory"
          style="width: 240px"
          placeholder="请输入"
          clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
@@ -14,132 +28,70 @@
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="row => row.id"
        show-summary
        style="width: 100%"
        :summary-method="summarizeMainTable"
        height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="入库批次" prop="inboundBatches" width="160" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="入库时间" prop="inboundDate" width="100" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="80" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" show-overflow-tooltip />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150" />
        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150" />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row);" :disabled="scope.row.createUser !== userStore.id">编辑</el-button>
            <el-button link type="primary" size="small" @click="openForm('edit', 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" />
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增入库' : '编辑入库'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="采购订单号" prop="salesContractNo">
              <el-input
                  v-model="form.purchaseContractNumber"
                  placeholder="请输入合同号"
                  clearable
                  @change="searchByContractNo"
                  :disabled="operationType === 'edit'"
              >
                <template #append>
                  <el-button
                      icon="Search"
                      @click="searchByContractNo"
                      :loading="loadingProducts"
                  />
                </template>
              </el-input>
            </el-form-item>
        <el-table
          :data="productList"
          border
          v-loading="loadingProducts"
          @selection-change="handleSelectionChange"
          stripe
        >
          <el-table-column align="center" type="selection" width="55" />
          <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="supplierName" width="100" />
          <el-table-column label="采购数量" prop="quantity" width="100" />
          <el-table-column label="待入库数量" prop="quantity0" width="100" />
          <el-table-column label="本次入库数量" prop="quantityStock" width="150">
            <template #default="scope">
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" :max="scope.row.quantity0" />
            </template>
          </el-table-column>
          <el-table-column label="税率(%)" prop="taxRate" width="120" />
          <el-table-column
            label="含税单价(元)"
            prop="taxInclusiveUnitPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column
            label="含税总价(元)"
            prop="taxInclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column
            label="不含税总价(元)"
            prop="taxExclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
          />
        </el-table>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <form-dia-manual ref="formDiaManual" @close="handleQuery" @success="handleQuery"></form-dia-manual>
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance, nextTick } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import dayjs from 'dayjs'
import {
  getStockInPage,
  updateStockIn,
  addSutockIn,
  delStockIn,
  selectProductRecordListByPuechaserId
    delStockInCustom,
  getInPageByCustom,
} from "@/api/inventoryManagement/stockIn.js";
import FormDiaManual from './components/formDiaManual.vue'
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const userList = ref([])
const loading = ref(false);
const tableLoading = ref(false)
const formDiaManual = ref()
const page = reactive({
  current: 1,
@@ -147,37 +99,13 @@
})
const total = ref(0)
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref('')// æ“ä½œç±»åž‹: 'add' æˆ– 'edit'
const dialogFormVisible = ref(false)// å¼¹æ¡†æ˜¾ç¤ºçŠ¶æ€
const productList = ref([]);// äº§å“åˆ—表数据
const loadingProducts = ref(false);// äº§å“åŠ è½½çŠ¶æ€
const productSelectedRows = ref([]) // äº§å“è¡¨æ ¼é€‰ä¸­è¡Œ
const data = reactive({
  searchForm: {
    supplierName: '',
    productCategory:'',
    timeStr: getCurrentDate(),
  },
  form: {
    id: null,
    purchaseContractNumber: '', // é‡‡è´­è®¢å•号
    supplierId: null,       // ä¾›åº”商ID
    supplierName: '',       // ä¾›åº”商名称
    inboundTime: '',        // å…¥åº“æ—¶é—´
    inboundBatch: '',       // å…¥åº“批次
    recorderId: userStore.userId, // å½•入人ID
    recorderName: userStore.name, // å½•入人姓名
    entryDate: getCurrentDate(),  // å½•入日期
    remark: '',             // å¤‡æ³¨
  },
  rules: {
    purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
    supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
    inboundTime: [{ required: true, message: "请选择入库时间", trigger: "change" }],
    inboundBatch: [{ required: true, message: "请输入入库批次", trigger: "blur" }]
  }
})
const { searchForm, form, rules } = toRefs(data)
const { searchForm } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
@@ -191,258 +119,87 @@
}
const getList = () => {
  tableLoading.value = true
  getStockInPage({ ...searchForm.value, ...page }).then(res => {
  const params = { ...page }
  params.timeStr = searchForm.value.timeStr
  params.productCategory = searchForm.value.productCategory
    params.flag = false
  const apiCall = getInPageByCustom(params)
  apiCall.then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    // å‰ç«¯è®¡ç®—含税总价:含税总价 = taxInclusiveUnitPrice * å…¥åº“数量
    tableData.value = tableData.value.map(item => {
      const inboundNum = Number(item.inboundNum) || 0
      const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0
      item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2)
      return item
    })
    total.value = res.data.total
    console.log('tableData:', tableData.value)
  }).catch(() => {
    tableLoading.value = false
  })
}
// è°ƒç”¨selectProductRecordListByPuechaserId这个方法根据合同查询到id,再调用getProductRecordByhetong这个方法根据id查询到产品订单记录
// æ–°å¢žæ ¹æ®åˆåŒå·æŸ¥è¯¢äº§å“è®°å½•的方法
const searchByContractNo = async () =>
{
  if (!form.value.purchaseContractNumber) {
    proxy.$modal.msgWarning('请输入合同号')
    return
  }
  try {
    loadingProducts.value = true
    // æ ¹æ®åˆåŒæŸ¥è¯¢äº§å“è®°å½•
    const productRes = await selectProductRecordListByPuechaserId({
      purchaseContractNumber: form.value.purchaseContractNumber
    });
    console.log('productRes:', productRes)
    if (!productRes.data || productRes.data.length === 0) {
      proxy.$modal.msgWarning('该合同下没有产品记录')
      productList.value = [];
      return
    }
    // å¤„理产品数据,添加本次入库数量字段
    productList.value = productRes.data.map(item => ({
      ...item,
      quantityStock: 0 // åˆå§‹åŒ–本次入库数量为0
    }))
  } catch (error) {
    console.error('查询产品记录失败:', error)
    proxy.$modal.msgError('查询产品记录失败')
    productList.value = [];
  } finally {
    loadingProducts.value = false
  }
}
// æ‰“开弹框
  const openForm = async (type, row) => {
    operationType.value = type
    dialogFormVisible.value = true
    selectedRows.value = []
    if (type === 'add') {
      // æ–°å¢žæ—¶åˆå§‹åŒ–表单
      form.value = {
        id: null,
        purchaseContractNumber: '',
        supplierId: null,
        supplierName: '',
        inboundTime: '',
        inboundBatch: '',
        recorderId: userStore.userId,
        recorderName: userStore.name,
        entryDate: getCurrentDate(),
        remark: ''
      }
      productList.value = [] // æ¸…空产品列表
    } else {
      form.value = JSON.parse(JSON.stringify(row))
      try {
        loadingProducts.value = true
        // æ ¹æ®åˆåŒå·åŠ è½½å¯¹åº”çš„äº§å“åˆ—è¡¨ï¼ˆå‡è®¾ getProductByContract æ˜¯å¯ç”¨æŽ¥å£ï¼‰
        const res = await selectProductRecordListByPuechaserId({
          purchaseContractNumber: form.value.purchaseContractNumber,
          id: row.id
        });
        productList.value = res.data.map(item => ({
          ...item,
          quantityStock: row.inboundNum // å¦‚果已有入库数量则保留
        }))
        selectedRows.value = productList.value
      } catch (error) {
        console.error('加载产品失败:', error)
        proxy.$modal.msgError('加载产品失败')
        productList.value = []
      } finally {
        loadingProducts.value = false
      }
    }
  await nextTick(() => {
    formDiaManual.value?.openDialog(type, row)
  })
  }
  const updatePro = async () => {
    // å‡†å¤‡æäº¤æ•°æ®
    // å‡†å¤‡æäº¤æ•°æ® - ä¿®æ”¹ä¸ºåŽç«¯éœ€è¦çš„æ ¼å¼
    const stockInData = {
      id: selectedRows.value[0].recordId,
      quantityStock: Number(selectedRows.value[0].quantityStock),// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
    };
    await updateStockIn(stockInData)
    proxy.$modal.msgSuccess('修改入库成功')
    closeDia()
    getList() // åˆ·æ–°åˆ—表
  }
// æäº¤è¡¨å•
  const submitForm = async () => {
    // éªŒè¯è‡³å°‘选择了一个产品
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgError('请先查询并选择产品')
      return
    }
    if(operationType.value !== 'add'){
      await updatePro()
      return
    }
    try {
      await proxy.$refs.formRef.validate()
      // éªŒè¯å…¥åº“数量
      const invalidProducts = selectedRows.value.filter(
          product => product.quantityStock <= 0 || product.quantityStock > product.quantity0
      )
      if (invalidProducts.length > 0) {
        proxy.$modal.msgError('请为所有产品输入有效的入库数量')
        return
      }
      // å‡†å¤‡æäº¤æ•°æ®
      // å‡†å¤‡æäº¤æ•°æ® - ä¿®æ”¹ä¸ºåŽç«¯éœ€è¦çš„æ ¼å¼
      const stockInData = {
        // å…¥åº“单基本信息
        ...form.value,
        inboundTime: formatDateTime(form.value.inboundTime),
        nickName: userStore.nickName,// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
        details: selectedRows.value.map(product => ({
          id: product.id,
          inboundQuantity: Number(product.quantityStock)
        })),
      };
      console.log('准备提交的数据:', JSON.parse(JSON.stringify(stockInData)));
      // è°ƒç”¨API
      loading.value = true
      await addSutockIn(stockInData)
      proxy.$modal.msgSuccess('新增入库成功')
      closeDia()
      getList() // åˆ·æ–°åˆ—表
    } catch (error) {
      console.error('提交失败:', error)
      if (!error.errors) {
        proxy.$modal.msgError('操作失败,请重试')
      }
    } finally {
      loading.value = false
    }
  }
// å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.$refs.formRef.resetFields()
    dialogFormVisible.value = false
  }
// è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = (selection) => {
    // è¿‡æ»¤æŽ‰å­æ•°æ®
    selectedRows.value = selection.filter(item => item.id);
    console.log('selection', selectedRows.value)
  selectedRows.value = selection.filter(item => item.id)
  }
  const expandedRowKeys = ref([])
// ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = (param) => {
    return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']);
  };
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice'])
}
// å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm(
        '是否确认导出?',
        '导出', {
  ElMessageBox.confirm('是否确认导出?', '导出', {
          confirmButtonText: '确认',
          cancelButtonText: '取消',
          type: 'warning',
        }
    ).then(() => {
      proxy.download("/stockin/export", {}, '入库台账.xlsx')
  }).then(() => {
    const exportUrl = "/stockin/exportTwo"
    proxy.download(exportUrl, {}, '入库台账.xlsx')
    }).catch(() => {
      proxy.$modal.msg("已取消")
    })
  }
// åˆ é™¤
  const handleDelete = () => {
    let ids = []
    if (selectedRows.value.length > 0) {
            // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
            const unauthorizedData = selectedRows.value.filter(item => item.createUser !== userStore.id);
            if (unauthorizedData.length > 0) {
                proxy.$modal.msgWarning("不可删除他人维护的数据");
                return;
            }
      ids = selectedRows.value.map(item => item.id);
    } else {
  if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning('请选择数据')
      return
    }
    ElMessageBox.confirm(
        '选中的内容将被删除,是否确认删除?',
        '导出', {
  const ids = selectedRows.value.map(item => item.id)
  ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除', {
          confirmButtonText: '确认',
          cancelButtonText: '取消',
          type: 'warning',
        }
    ).then(() => {
      delStockIn({ids:ids}).then(res => {
  }).then(() => {
    // ææ–™å…¥åº“删除
    delStockInCustom(ids).then(() => {
        proxy.$modal.msgSuccess("删除成功")
        getList()
    }).catch(() => {
      proxy.$modal.msgError("删除失败")
      })
    }).catch(() => {
      proxy.$modal.msg("已取消")
    })
  }
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
// ä¿®æ”¹ä¸ºæ›´é€šç”¨çš„æ—¥æœŸæ—¶é—´æ ¼å¼åŒ–函数
function formatDateTime(date = new Date(), includeTime = true) {
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  if (!includeTime) {
    return `${year}-${month}-${day}`; // ä¿æŒåŽŸæœ‰ getCurrentDate åŠŸèƒ½
  }
  // æ–°å¢žæ—¶é—´éƒ¨åˆ†æ ¼å¼åŒ–
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// ä¿æŒåŽŸæœ‰ getCurrentDate çš„兼容性
function getCurrentDate() {
  return formatDateTime(new Date(), false);
}
  onMounted(() => {
    getList()
@@ -450,3 +207,6 @@
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/stockManagement/components/FormDiaManual.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,154 @@
<template>
  <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? '新增材料库存' : '编辑材料库存'" width="70%"
    @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="产品大类:" prop="productCategory">
            <el-input disabled v-model="form.productCategory" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="规格型号:" prop="specificationModel">
            <el-input disabled v-model="form.specificationModel" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="单位:" prop="unit">
            <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="物品类型:" prop="itemType">
            <el-input disabled v-model="form.itemType" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="入库时间:" prop="createTime">
            <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                            type="date" placeholder="请选择" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="库存数量:" prop="inboundNum">
            <el-input v-model="form.inboundNum" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="已出库数量:" prop="totalInboundNum">
            <el-input disabled v-model="form.totalInboundNum" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="待出库数量:" prop="inboundNum0">
            <el-input disabled v-model="form.inboundNum0" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="单价(元):" prop="taxInclusiveUnitPrice">
            <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="总价(元):" prop="taxInclusiveTotalPrice">
            <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="自动计算" clearable />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, watch } from 'vue'
const props = defineProps({
  dialogFormVisible: Boolean,
  operationType: String,
  formData: Object
})
const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close'])
const formRef = ref()
const data = reactive({
  form: {
    productCategory: '',
    specificationModel: '',
    unit: '',
    itemType: '',
    createTime: '',
    inboundNum: '',
    totalInboundNum: '',
    inboundNum0: '',
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: ''
  },
  rules: {
    productCategory: [{ required: true, message: '请输入产品大类', trigger: 'blur' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    itemType: [{ required: true, message: '请输入物品类型', trigger: 'blur' }],
    createTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundNum: [{ required: true, message: '请输入库存数量', trigger: 'blur' }],
    taxInclusiveUnitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }]
  }
})
const { form, rules } = toRefs(data)
// è®¡ç®—总价:总价 = å•ä»· Ã— å‰©ä½™åº“å­˜
const calculateTotalPrice = () => {
  const unitPrice = parseFloat(form.value.taxInclusiveUnitPrice) || 0
  const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åº“存数量
  const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²å‡ºåº“数量
  const remainingStock = stockQuantity - outboundQuantity // å‰©ä½™åº“å­˜
  form.value.taxInclusiveTotalPrice = (unitPrice * remainingStock).toFixed(2)
}
// ç›‘听formData变化
watch(() => props.formData, (newVal) => {
  if (newVal) {
    form.value = { ...newVal }
    // æ•°æ®å˜åŒ–后重新计算总价
    calculateTotalPrice()
  }
}, { immediate: true })
// æäº¤è¡¨å•
const submitForm = () => {
  formRef.value.validate(valid => {
    if (valid) {
      emit('submit', form.value)
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  emit('close')
  emit('update:dialogFormVisible', false)
}
</script>
<style scoped lang="scss">
.dialog-footer {
  text-align: center;
}
</style>
src/views/inventoryManagement/stockManagement/components/FormDiaProduction.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
<template>
  <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? '新增成品库存' : '编辑成品库存'" width="70%"
    @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="产品大类:" prop="productCategory">
            <el-input disabled v-model="form.productCategory" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="规格型号:" prop="specificationModel">
            <el-input disabled v-model="form.specificationModel" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="单位:" prop="unit">
            <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入库时间:" prop="createTime">
            <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                            type="date" placeholder="请选择" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="库存数量:" prop="inboundNum">
            <el-input v-model="form.inboundNum" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="已出库数量:" prop="totalInboundNum">
            <el-input disabled v-model="form.totalInboundNum" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="待出库数量:" prop="inboundNum0">
            <el-input disabled v-model="form.inboundNum0" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="单价(元):" prop="unitPrice">
            <el-input v-model="form.unitPrice" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="总价(元):" prop="totalPrice">
            <el-input disabled v-model="form.totalPrice" placeholder="自动计算" clearable />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, watch } from 'vue'
const props = defineProps({
  dialogFormVisible: Boolean,
  operationType: String,
  formData: Object
})
const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close'])
const formRef = ref()
const data = reactive({
  form: {
    productCategory: '',
    specificationModel: '',
    unit: '',
    createTime: '',
    inboundNum: '',
    totalInboundNum: '',
    inboundNum0: '',
    unitPrice: '',
    totalPrice: ''
  },
  rules: {
    productCategory: [{ required: true, message: '请输入产品大类', trigger: 'blur' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    createTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundNum: [{ required: true, message: '请输入库存数量', trigger: 'blur' }],
    unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }]
  }
})
const { form, rules } = toRefs(data)
// è®¡ç®—总价:总价 = å•ä»· Ã— å‰©ä½™åº“å­˜
const calculateTotalPrice = () => {
  const unitPrice = parseFloat(form.value.unitPrice) || 0
  const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åº“存数量
  const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²å‡ºåº“数量
  const remainingStock = stockQuantity - outboundQuantity // å‰©ä½™åº“å­˜
  form.value.totalPrice = (unitPrice * remainingStock).toFixed(2)
}
// ç›‘听formData变化
watch(() => props.formData, (newVal) => {
  if (newVal) {
    form.value = { ...newVal }
    // æ•°æ®å˜åŒ–后重新计算总价
    calculateTotalPrice()
  }
}, { immediate: true })
// æäº¤è¡¨å•
const submitForm = () => {
  formRef.value.validate(valid => {
    if (valid) {
      emit('submit', form.value)
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  emit('close')
  emit('update:dialogFormVisible', false)
}
</script>
<style scoped lang="scss">
.dialog-footer {
  text-align: center;
}
</style>
src/views/inventoryManagement/stockManagement/components/FormDiaPurchase.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
<template>
  <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? '新增原料库存' : '编辑原料库存'" width="70%"
    @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="产品大类:" prop="productCategory">
            <el-input disabled v-model="form.productCategory" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="规格型号:" prop="specificationModel">
            <el-input disabled v-model="form.specificationModel" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="单位:" prop="unit">
            <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入库时间:" prop="createTime">
            <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                            type="date" placeholder="请选择" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="库存数量:" prop="inboundNum">
            <el-input v-model="form.inboundNum" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="已出库数量:" prop="totalInboundNum">
            <el-input disabled v-model="form.totalInboundNum" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="待出库数量:" prop="inboundNum0">
            <el-input disabled v-model="form.inboundNum0" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
            <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
            <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="自动计算" clearable />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, watch } from 'vue'
const props = defineProps({
  dialogFormVisible: Boolean,
  operationType: String,
  formData: Object
})
const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close'])
const formRef = ref()
const data = reactive({
  form: {
    productCategory: '',
    specificationModel: '',
    unit: '',
    createTime: '',
    inboundNum: '',
    totalInboundNum: '',
    inboundNum0: '',
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: ''
  },
  rules: {
    productCategory: [{ required: true, message: '请输入产品大类', trigger: 'blur' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    createTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundNum: [{ required: true, message: '请输入库存数量', trigger: 'blur' }],
    taxInclusiveUnitPrice: [{ required: true, message: '请输入含税单价', trigger: 'blur' }]
  }
})
const { form, rules } = toRefs(data)
// è®¡ç®—总价:含税总价 = å«ç¨Žå•ä»· Ã— å‰©ä½™åº“å­˜
const calculateTotalPrice = () => {
  const unitPrice = parseFloat(form.value.taxInclusiveUnitPrice) || 0
  const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åº“存数量
  const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²å‡ºåº“数量
  const remainingStock = stockQuantity - outboundQuantity // å‰©ä½™åº“å­˜
  form.value.taxInclusiveTotalPrice = (unitPrice * remainingStock).toFixed(2)
}
// ç›‘听formData变化
watch(() => props.formData, (newVal) => {
  if (newVal) {
    form.value = { ...newVal }
    // æ•°æ®å˜åŒ–后重新计算总价
    calculateTotalPrice()
  }
}, { immediate: true })
// æäº¤è¡¨å•
const submitForm = () => {
  formRef.value.validate(valid => {
    if (valid) {
      emit('submit', form.value)
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  emit('close')
  emit('update:dialogFormVisible', false)
}
</script>
<style scoped lang="scss">
.dialog-footer {
  text-align: center;
}
</style>
src/views/inventoryManagement/stockManagement/index.vue
@@ -2,155 +2,101 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker
          v-model="searchForm.timeStr"
          type="date"
          placeholder="请选择日期"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          clearable
          @change="handleQuery"
        />
        <span class="search_title ml10">产品大类:</span>
        <el-input
          v-model="searchForm.productCategory"
          style="width: 240px"
          placeholder="请输入"
          clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <!-- <el-button type="primary" @click="openForm('add')">新增</el-button> -->
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="row => row.id"
        show-summary
        style="width: 100%"
        :row-class-name="tableRowClassName"
        :summary-method="summarizeMainTable"
        height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="库存日期" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="入库日期" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="入库日期" prop="inboundDate" width="100" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="100" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="100" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
        <el-table-column label="物品类型" prop="itemType" width="120" show-overflow-tooltip />
        <el-table-column label="已发料数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150" />
        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150" />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row);" :disabled="scope.row.createUser !== userStore.id">编辑</el-button>
            <el-button link type="primary" size="small" @click="openForm('edit', 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" />
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增库存' : '编辑库存'" width="70%"
      @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierName">
              <el-input disabled v-model="form.supplierName" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="产品大类:" prop="productId">
              <el-select disabled v-model="form.productCategory" placeholder="请选择" clearable filterable @change="handleProductChange">
                <el-option v-for="item in productList" :key="item.id" :label="item.productName"
                           :value="item.productName" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productManageId">
              <el-select disabled v-model="form.specificationModel" placeholder="请先选择产品大类" clearable filterable :disabled="!form.productCategory"
                         @change="handleModelChange">
                <el-option v-for="item in productModelList" :key="item.id" :label="item.model"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item  label="单位:" prop="customerId">
              <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="库存时间:" prop="projectName">
              <el-date-picker style="width: 100%" v-model="form.updateTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="入库时间:" prop="projectName">
              <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item  label="含税单价:" prop="customerId">
              <el-input disabled v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item  label="含税总价:" prop="customerContractNo">
              <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item  label="税率:" prop="customerId">
              <el-input disabled v-model="form.taxRate" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价:" prop="entryDate">
              <el-input disabled v-model="form.taxExclusiveTotalPrice" placeholder="请输入" clearable />
            </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.createUser" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- ææ–™åº“存弹框 -->
    <FormDiaManual
      v-model:dialogFormVisible="manualDialogVisible"
      :operationType="operationType"
      :formData="form"
      @submit="submitForm"
      @close="closeDia"
    />
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { productTreeList,modelList } from "@/api/basicData/product.js"
import {
  getStockManagePage ,
  updateStockManage,
  getStockManagePageByCustom,
  delStockManage,
  exportStockManage
} from "@/api/inventoryManagement/stockManage.js";
import {
  updateManagement
    updateManagement, updateManagementByCustom, updateStockIn
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
// å¯¼å…¥ææ–™åº“存弹框组件
import FormDiaManual from './components/FormDiaManual.vue'
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
@@ -167,17 +113,22 @@
})
const total = ref(0)
const fileList = ref([])
const loading = ref(false);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref('')
const dialogFormVisible = ref(false)
// ææ–™åº“存弹框显示状态
const manualDialogVisible = ref(false)
const data = reactive({
  searchForm: {
    supplierName: '',
    // supplierName: '',
    productCategory:'',
    customerName: '',
    timeStr: getCurrentDate(),
  },
  form: {
    supplierId: null,
    supplierName: '',
    productId: null,
    productName: '',
    userId: userStore.userId,
@@ -194,20 +145,24 @@
    inboundBatch: '',
    stockQuantity: '',
    boundTime: '',
        warnNum: '', // æ–°å¢žæœ€ä½Žåº“存字段
    salesLedgerProductId: null,
  },
  rules: {
    supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
    // supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
    productCategory: [{ required: true, message: '请选择产品大类', trigger: 'change' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    stockQuantity: [{ required: true, message: '请输入出库数量', trigger: 'blur' }],
    unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }], // æ·»åŠ æˆå“åº“å­˜å•ä»·çš„éªŒè¯è§„åˆ™
    taxInclusiveUnitPrice: [{ required: true, message: '请输入含税单价', trigger: 'blur' }],
    taxInclusiveTotalPrice: [{ required: true, message: '请输入含税总价', trigger: 'blur' }],
    taxRate: [{ required: true, message: '请输入税率', trigger: 'blur' }],
    taxExclusiveTotalPrice: [{ required: true, message: '请输入不含税总价', trigger: 'blur' }],
    boundTime: [{ required: true, message: '请选择库存时间', trigger: 'change' }],
    inboundTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundPerson: [{ required: true, message: '请选择出库人', trigger: 'change' }]
    inboundPerson: [{ required: true, message: '请选择出库人', trigger: 'change' }],
        warnNum: [{ required: true, message: '请输入最低库存', trigger: 'blur' }],
  }
})
const { searchForm, form, rules } = toRefs(data)
@@ -223,16 +178,39 @@
  page.size = obj.limit;
  getList()
}
const buildQueryParams = () => {
  const params = {
    ...page,
    timeStr: searchForm.value.timeStr,
  }
  params.productCategory = searchForm.value.productCategory
  return params
}
const getList = () => {
  tableLoading.value = true
  getStockManagePage({ ...searchForm.value, ...page }).then(res => {
  const params = buildQueryParams()
  getStockManagePageByCustom(params).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    console.log('res', res)
    // tableData.value.map(item => {
    //   item.children = []
    // })
    // ä¸ºè¡¨æ ¼æ•°æ®è‡ªåŠ¨è®¡ç®—æ€»ä»·
    tableData.value = tableData.value.map(item => {
      // è®¡ç®—剩余库存
      const stockQuantity = parseFloat(item.inboundNum) || 0
      const outboundQuantity = parseFloat(item.totalInboundNum) || 0
      const remainingStock = Math.max(stockQuantity - outboundQuantity, 0)
      // ææ–™åº“存:含税总价 = å«ç¨Žå•ä»· Ã— å‰©ä½™åº“å­˜
      const taxInclusiveUnitPrice = parseFloat(item.taxInclusiveUnitPrice) || 0
      item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * remainingStock).toFixed(2)
      return item
    })
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
  }).catch(() => {
    tableLoading.value = false
  })
@@ -252,6 +230,16 @@
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']);
};
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  const stock = Number(row?.inboundNum0 ?? 0);
  const warn = Number(row?.warnNum ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
    return '';
  }
  return stock < warn ? 'row-low-stock' : '';
};
// æ‰“开弹框
const openForm = async (type, row) => {
  operationType.value = type
@@ -259,11 +247,6 @@
  productData.value = []
  let userLists = await userListNoPageByTenantId()
  userList.value = userLists.data
  // customerList().then(res => {
  //   customerOption.value = res
  // })
  // console.log('userStore.id', userStore.id)
  // form.value.entryPerson = userStore.id
  if (type === 'edit') {
    form.value = { ...row }
    productTreeList().then(res =>{
@@ -276,48 +259,65 @@
        }
      })
    })
    // getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
    //   form.value.entryPerson = Number(res.entryPerson)
    //   productData.value = form.value.productData
    //   fileList.value = form.value.salesLedgerFiles
    // })
  }
  form.value.entryDate = getCurrentDate() // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  dialogFormVisible.value = true
  // ä»…材料库存弹框
  manualDialogVisible.value = true
}
// æäº¤è¡¨å•
const submitForm = () => {
  console.log(form.value)
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // if (productData.value.length > 0) {
      //   form.value.productData = proxy.HaveJson(productData.value)
      // } else {
      //   proxy.$modal.msgWarning('请添加产品信息')
      //   return
      // }
      // let tempFileIds = []
      // if (fileList.value.length > 0) {
      //   tempFileIds = fileList.value.map(item => item.tempId)
      // }
      // form.value.tempFileIds = tempFileIds
      // form.value.type = 1
      updateManagement(form.value).then(res => {
const submitForm = (submittedData) => {
  console.log('子组件提交的数据:', submittedData)
  // ä½¿ç”¨å­ç»„件提交的数据,而不是父组件的form对象
  const submitData = { ...submittedData }
  // ææ–™åº“存:移除含税总价字段
  delete submitData.taxInclusiveTotalPrice
  // ç§»é™¤å…¶ä»–可能的总价字段
  delete submitData.taxExclusiveTotalPrice
  console.log('提交给后端的数据(已移除总价字段):', submitData)
  // ææ–™åº“存使用 updateManagementByCustom æŽ¥å£
  updateManagementByCustom(submitData).then(res => {
        proxy.$modal.msgSuccess("提交成功")
        closeDia()
        getList()
    // æäº¤åŽæ£€æŸ¥åº“存并尝试创建请购单
    // checkStockAndCreatePurchase();
  }).catch(error => {
    console.error('提交失败:', error)
    proxy.$modal.msgError("提交失败,请重试")
      })
    }
  })
}
// æ£€æŸ¥åº“存并创建请购单
// const checkStockAndCreatePurchase = async () => {
//   const stockList = tableData.value;
//   // handList()
//   for (const item of stockList) {
//     if (item.inboundNum0 < item.warnNum) {
//       try {
//                 const stockInData = {
//                     id: item.id,
//                     quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
//                 };
//                 loading.value = true
//                 await updateStockIn(stockInData)
//                 proxy.$modal.msgSuccess(`产品 ${item.productCategory} ä¿®æ”¹å…¥åº“成功`)
//                 loading.value = false
//       } catch (error) {
//         proxy.$modal.msgError(`产品 ${item.productCategory} ç”Ÿæˆè¯·è´­å•失败,请手动处理`);
//
//       }
//     }
//   }
// };
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef")
  dialogFormVisible.value = false
  manualDialogVisible.value = false
}
// å¯¼å‡º
@@ -330,7 +330,9 @@
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/stockin/exportCopy", {}, '库存信息.xlsx')
    const exportParams = buildQueryParams()
    const exportUrl = "/stockin/exportCopyTwo"
    proxy.download(exportUrl, exportParams, '库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
@@ -339,12 +341,6 @@
const handleDelete = () => {
  let ids = []
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.createUser !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning('请选择数据')
@@ -366,17 +362,26 @@
    proxy.$modal.msg("已取消")
  })
}
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList()
  // checkStockAndCreatePurchase();
    // æ¯å°æ—¶æ£€æŸ¥ä¸€æ¬¡åº“å­˜
    // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000);
// onUnmounted(() => {
//   // ç»„件卸载时清除定时器
//   clearInterval(intervalId);
// });
})
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
</style>
src/views/inventoryManagement/stockReport/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,713 @@
<template>
  <div class="app-container">
    <!-- æœç´¢è¡¨å• -->
    <div class="search_form">
      <div class="search_left">
        <span class="search_title">报表类型:</span>
        <el-select
          v-model="searchForm.reportType"
          style="width: 150px;"
          placeholder="请选择"
          @change="handleReportTypeChange"
        >
          <el-option label="日报" value="daily" />
          <el-option label="月报" value="monthly" />
          <el-option label="作业报表" value="work" />
          <el-option label="进出存报表" value="inout" />
        </el-select>
        <span class="search_title ml10">时间范围:</span>
         <el-date-picker
           v-if="searchForm.reportType === 'daily'"
           v-model="searchForm.singleDate"
           type="date"
           placeholder="请选择日期"
           format="YYYY-MM-DD"
           value-format="YYYY-MM-DD"
           style="width: 200px;"
         />
        <el-date-picker
          v-else-if="searchForm.reportType === 'monthly'"
          v-model="searchForm.monthRange"
          type="monthrange"
          range-separator="至"
          start-placeholder="开始月份"
          end-placeholder="结束月份"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD"
          style="width: 240px;"
        />
        <el-date-picker
          v-else
          v-model="searchForm.dateRange"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD"
          style="width: 240px;"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
          æŸ¥è¯¢
        </el-button>
        <el-button @click="handleReset">重置</el-button>
      </div>
      <div class="search_right">
        <el-button type="success" @click="handleExport" icon="Download">
          å¯¼å‡ºæŠ¥è¡¨
        </el-button>
      </div>
    </div>
    <!-- ç»Ÿè®¡å¡ç‰‡ -->
    <div class="stats_cards" v-if="reportData.summary">
      <el-row :gutter="20">
        <el-col :span="6">
          <el-card class="stats_card">
            <div class="stats_content">
              <div class="stats_icon in">
                <el-icon><TrendCharts /></el-icon>
              </div>
              <div class="stats_info">
                <div class="stats_value">{{ reportData.summary.totalIn || 0 }}</div>
                <div class="stats_label">总入库量</div>
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="stats_card">
            <div class="stats_content">
              <div class="stats_icon out">
                <el-icon><TrendCharts /></el-icon>
              </div>
              <div class="stats_info">
                <div class="stats_value">{{ reportData.summary.totalOut || 0 }}</div>
                <div class="stats_label">总出库量</div>
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="stats_card">
            <div class="stats_content">
              <div class="stats_icon stock">
                <el-icon><Box /></el-icon>
              </div>
              <div class="stats_info">
                <div class="stats_value">{{ reportData.summary.currentStock || 0 }}</div>
                <div class="stats_label">当前库存</div>
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card class="stats_card">
            <div class="stats_content">
              <div class="stats_icon turnover">
                <el-icon><Refresh /></el-icon>
              </div>
              <div class="stats_info">
                <div class="stats_value">{{ reportData.summary.turnoverRate || 0 }}%</div>
                <div class="stats_label">周转率</div>
              </div>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <!-- å›¾è¡¨åŒºåŸŸ -->
    <div class="chart_section" v-if="reportData.chartData">
      <el-row :gutter="20">
        <el-col :span="12">
          <el-card>
            <template #header>
              <span>库存趋势图</span>
            </template>
            <div ref="trendChart" style="height: 300px;"></div>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card>
            <template #header>
              <span>进出库对比</span>
            </template>
            <div ref="comparisonChart" style="height: 300px;"></div>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <!-- è¯¦ç»†æ•°æ®è¡¨æ ¼ -->
    <div class="table_section">
      <el-card>
        <template #header>
          <span>{{ getTableTitle() }}</span>
        </template>
         <el-table
           v-loading="tableLoading"
           :data="reportData.tableData"
           border
           height="400"
           style="width: 100%"
           :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
         >
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
           <el-table-column
             v-if="searchForm.reportType === 'daily'"
             label="日期"
             prop="createTime"
             width="100"
             align="center"
           />
           <el-table-column
             v-if="searchForm.reportType === 'monthly'"
             label="月份"
             prop="createTime"
             width="100"
             align="center"
           />
           <el-table-column
             label="入库时间"
             prop="createTime"
             width="100"
             show-overflow-tooltip
           />
           <el-table-column
             label="入库批次"
             prop="inboundBatches"
             width="160"
             show-overflow-tooltip
           />
           <el-table-column
             label="产品大类"
             prop="productCategory"
             width="100"
             show-overflow-tooltip
           />
           <el-table-column
             label="规格型号"
             prop="specificationModel"
             min-width="200"
             show-overflow-tooltip
           />
           <el-table-column
             label="单位"
             prop="unit"
             width="70"
             show-overflow-tooltip
           />
           <!-- <el-table-column
             label="期初库存"
             prop="beginStock"
             width="100"
             align="center"
           /> -->
           <el-table-column
             label="入库数量"
             prop="inboundNum"
             width="100"
             align="center"
           />
           <!-- <el-table-column
             label="出库数量"
             prop=""
             width="100"
             align="center"
           /> -->
           <el-table-column
             label="现在库存"
             prop="inboundNum0"
             width="100"
             align="center"
           />
           <el-table-column
             label="含税单价"
             prop="taxInclusiveUnitPrice"
             width="100"
             show-overflow-tooltip
           />
           <el-table-column
             label="含税总价"
             prop="taxInclusiveTotalPrice"
             width="100"
             show-overflow-tooltip
           />
           <el-table-column
             label="税率(%)"
             prop="taxRate"
             width="80"
             show-overflow-tooltip
           />
           <el-table-column
             label="不含税总价"
             prop="taxExclusiveTotalPrice"
             width="100"
             show-overflow-tooltip
           />
           <el-table-column
             label="入库人"
             prop="createBy"
             width="80"
             show-overflow-tooltip
           />
           <el-table-column
             v-if="searchForm.reportType === 'work'"
             label="操作人员"
             prop="operator"
             width="80"
             align="center"
           />
           <el-table-column
             v-if="searchForm.reportType === 'work'"
             label="操作时间"
             prop="operateTime"
             width="150"
             align="center"
           />
        </el-table>
      </el-card>
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import * as echarts from 'echarts'
import {
  getStockDailyReport,
  getStockMonthlyReport,
  getWorkReport,
  getStockInOutReport,
  exportStockReport
} from '@/api/inventoryManagement/stockReport'
const { proxy } = getCurrentInstance()
// å“åº”式数据
const tableLoading = ref(false)
const trendChart = ref(null)
const comparisonChart = ref(null)
const searchForm = reactive({
  reportType: 'daily',
  singleDate: '',
  dateRange: [],
  monthRange: []
})
const reportData = ref({
  summary: null,
  chartData: null,
  tableData: []
})
// èŽ·å–è¡¨æ ¼æ ‡é¢˜
const getTableTitle = () => {
  const typeMap = {
    daily: '日报详细数据',
    monthly: '月报详细数据',
    work: '作业报表详细数据',
    inout: '进出存报表详细数据'
  }
  return typeMap[searchForm.reportType] || '报表详细数据'
}
// æŠ¥è¡¨ç±»åž‹æ”¹å˜
const handleReportTypeChange = () => {
  reportData.value = {
    summary: null,
    chartData: null,
    tableData: []
  }
}
// æŸ¥è¯¢æ•°æ®
const handleQuery = async () => {
  if (!validateSearchForm()) {
    return
  }
  tableLoading.value = true
  try {
    const params = getQueryParams()
    let response
    switch (searchForm.reportType) {
      case 'daily':
        response = await getStockDailyReport(params)
        break
      case 'monthly':
        response = await getStockMonthlyReport(params)
        break
      case 'work':
        response = await getWorkReport(params)
        break
      case 'inout':
        response = await getStockInOutReport(params)
        break
      default:
        throw new Error('未知的报表类型')
    }
    if (response.code === 200) {
      // generateMockData()
      reportData.value.tableData = response.data.tableData
      reportData.value.summary = response.data.summary
      reportData.value.chartData = response.data.chartData
      nextTick(() => {
        initCharts()
      })
    }
  } catch (error) {
    ElMessage.error('查询失败:' + error.message)
  } finally {
    tableLoading.value = false
  }
}
// // ç”Ÿæˆå‡æ•°æ®
// const generateMockData = () => {
//   // ç”Ÿæˆç»Ÿè®¡å¡ç‰‡å‡æ•°æ®
//   const summary = {
//     totalIn: 1000,
//     totalOut: 600,
//     currentStock: 400,
//     turnoverRate: 30
//   }
//   // ç”Ÿæˆå›¾è¡¨å‡æ•°æ®
//   const trendDates = ['2025-09-15', '2025-09-16', '2025-09-17', '2025-09-18', '2025-09-19']
//   const trendValues = [300, 350, 400, 380, 420]
//   const comparisonDates = ['2025-09-15', '2025-09-16', '2025-09-17']
//   const inValues = [100, 150, 200]
//   const outValues = [80, 120, 100]
//   const chartData = {
//     trendDates,
//     trendValues,
//     comparisonDates,
//     inValues,
//     outValues
//   }
//   reportData.value = {
//     summary,
//     chartData,
//     tableData: []
//   }
// }
// éªŒè¯æœç´¢è¡¨å•
const validateSearchForm = () => {
  if (searchForm.reportType === 'daily') {
    if (!searchForm.singleDate) {
      ElMessage.warning('请选择日期')
      return false
    }
  } else if (searchForm.reportType === 'work' || searchForm.reportType === 'inout') {
    if (!searchForm.dateRange || searchForm.dateRange.length !== 2) {
      ElMessage.warning('请选择日期范围')
      return false
    }
  } else if (searchForm.reportType === 'monthly') {
    if (!searchForm.monthRange || searchForm.monthRange.length !== 2) {
      ElMessage.warning('请选择月份范围')
      return false
    }
  }
  return true
}
// èŽ·å–æŸ¥è¯¢å‚æ•°
const getQueryParams = () => {
  const params = {
    reportType: searchForm.reportType,
    reportDate: "",
    startMonth: "",
    endMonth: "",
    startDate: "",
    endDate: ""
  }
  if (searchForm.reportType === 'daily') {
    params.reportDate = searchForm.singleDate
  } else if (searchForm.reportType === 'monthly') {
    params.startMonth = searchForm.monthRange[0]
    params.endMonth = searchForm.monthRange[1]
  } else {
    params.startDate = searchForm.dateRange[0]
    params.endDate = searchForm.dateRange[1]
  }
  return params
}
// é‡ç½®æœç´¢
const handleReset = () => {
  searchForm.reportType = 'daily'
  searchForm.singleDate = ''
  searchForm.dateRange = []
  searchForm.monthRange = []
  reportData.value = {
    summary: null,
    chartData: null,
    tableData: []
  }
}
// å¯¼å‡ºæŠ¥è¡¨
const handleExport = async () => {
  if (!validateSearchForm()) {
    return
  }
  try {
    const params = getQueryParams()
    // const response = await exportStockReport(params)
    proxy.download("/stockin/exportCopy", params, '库存报表.xlsx')
    // åˆ›å»ºä¸‹è½½é“¾æŽ¥
    // const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
    // const url = window.URL.createObjectURL(blob)
    // const link = document.createElement('a')
    // link.href = url
    // link.download = `${getTableTitle()}_${new Date().getTime()}.xlsx`
    // document.body.appendChild(link)
    // link.click()
    // document.body.removeChild(link)
    // window.URL.revokeObjectURL(url)
    // ElMessage.success('导出成功')
  } catch (error) {
    ElMessage.error('导出失败:' + error.message)
  }
}
// åˆå§‹åŒ–图表
const initCharts = () => {
  if (!reportData.value.chartData) return
  initTrendChart()
  initComparisonChart()
}
// åˆå§‹åŒ–趋势图
const initTrendChart = () => {
  if (!trendChart.value) return
  const chart = echarts.init(trendChart.value)
  const option = {
    title: {
      text: '库存变化趋势',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['库存量'],
      top: 30
    },
    xAxis: {
      type: 'category',
      data: reportData.value.chartData.trendDates || []
    },
    yAxis: {
      type: 'value'
    },
    series: [{
      name: '库存量',
      type: 'line',
      data: reportData.value.chartData.trendValues || [],
      smooth: true,
      itemStyle: {
        color: '#409EFF'
      }
    }]
  }
  chart.setOption(option)
}
// åˆå§‹åŒ–对比图
const initComparisonChart = () => {
  if (!comparisonChart.value) return
  const chart = echarts.init(comparisonChart.value)
  const option = {
    title: {
      text: '进出库对比',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['入库', '出库'],
      top: 30
    },
    xAxis: {
      type: 'category',
      data: reportData.value.chartData.comparisonDates || []
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        name: '入库',
        type: 'bar',
        data: reportData.value.chartData.inValues || [],
        itemStyle: {
          color: '#67C23A'
        }
      },
      {
        name: '出库',
        type: 'bar',
        data: reportData.value.chartData.outValues || [],
        itemStyle: {
          color: '#F56C6C'
        }
      }
    ]
  }
  chart.setOption(option)
}
// ç»„件挂载时设置默认时间
onMounted(() => {
  const today = new Date()
  searchForm.singleDate = today.toISOString().split('T')[0]
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
  searchForm.dateRange = [
    yesterday.toISOString().split('T')[0],
    today.toISOString().split('T')[0]
  ]
})
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.search_form {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 20px;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.search_left {
  display: flex;
  align-items: center;
}
.search_title {
  font-weight: 500;
  color: #333;
  margin-right: 8px;
}
.ml10 {
  margin-left: 10px;
}
.stats_cards {
  margin-bottom: 20px;
}
.stats_card {
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.stats_content {
  display: flex;
  align-items: center;
  padding: 10px 0;
}
.stats_icon {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 15px;
  font-size: 24px;
  color: #fff;
}
.stats_icon.in {
  background: linear-gradient(135deg, #67C23A, #85CE61);
}
.stats_icon.out {
  background: linear-gradient(135deg, #F56C6C, #F78989);
}
.stats_icon.stock {
  background: linear-gradient(135deg, #409EFF, #66B1FF);
}
.stats_icon.turnover {
  background: linear-gradient(135deg, #E6A23C, #EEBE77);
}
.stats_info {
  flex: 1;
}
.stats_value {
  font-size: 24px;
  font-weight: bold;
  color: #333;
  line-height: 1;
  margin-bottom: 5px;
}
.stats_label {
  font-size: 14px;
  color: #666;
}
.chart_section {
  margin-bottom: 20px;
}
.table_section {
  margin-bottom: 20px;
}
:deep(.el-card__header) {
  background: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
  font-weight: 500;
}
:deep(.el-table .el-table__header-wrapper th) {
  background-color: #F0F1F5 !important;
  color: #333333;
  font-weight: 600;
}
:deep(.el-table .el-table__body-wrapper td) {
  padding: 8px 0;
}
</style>
src/views/inventoryManagement/stockWarning/index.vue
@@ -41,7 +41,7 @@
      <!-- æ“ä½œæŒ‰é’® -->
      <div class="table-operations">
        <el-button type="primary" @click="handleAdd">新增预警规则</el-button>
        <el-button type="success" @click="handleBatchProcess">批量处理</el-button>
        <!-- <el-button type="success" @click="handleBatchProcess">批量处理@</el-button> -->
        <el-button @click="handleExport">导出</el-button>
      </div>
      <el-table 
@@ -51,7 +51,6 @@
        @selection-change="handleSelectionChange"
        style="width: 100%"
        height="calc(100vh - 280px)"
        stripe
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
@@ -121,7 +120,7 @@
        <el-table-column fixed="right" label="操作" width="200" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button link type="success" size="small" @click="handleProcess(scope.row)">处理</el-button>
<!--            <el-button link type="success" size="small" @click="handleProcess(scope.row)">处理@</el-button>-->
            <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
@@ -383,15 +382,15 @@
import { WarningFilled } from '@element-plus/icons-vue'
import pagination from '@/components/PIMTable/Pagination.vue'
// æ³¨é‡ŠæŽ‰API导入,使用假数据
// import {
//   getStockWarningPage,
//   addStockWarning,
//   updateStockWarning,
//   deleteStockWarning,
//   batchProcessStockWarning,
//   exportStockWarning,
//   toggleStockWarningStatus
// } from '@/api/inventoryManagement/stockWarning.js'
import {
  getStockWarningPage,
  addStockWarning,
  updateStockWarning,
  deleteStockWarning,
  batchProcessStockWarning,
  exportStockWarning,
  toggleStockWarningStatus
} from '@/api/inventoryManagement/stockWarning.js'
const { proxy } = getCurrentInstance()
@@ -411,7 +410,8 @@
// åˆ†é¡µå‚æ•°
const page = reactive({
  current: 1,
  size: 10
  size: 10,
  total: 0
})
// æœç´¢è¡¨å•
@@ -538,236 +538,20 @@
  // è¿™é‡Œå¯ä»¥è°ƒç”¨å¤„理API
}
// ç”Ÿæˆå‡æ•°æ®
const generateMockData = () => {
  const mockData = [
    {
      id: 1,
      tankCode: 'TANK001',
      tankName: '液化气储罐A',
      tankType: '液化气储罐',
      specificationModel: 'LPG-5000L',
      volume: 5000,
      currentGasLevel: 15,
      safetyGasLevel: 30,
      minGasLevel: 10,
      maxGasLevel: 95,
      currentPressure: 2.5,
      warningType: '气体不足',
      warningLevel: '紧急',
      warningThreshold: 20,
      isEnabled: true,
      warningTime: '2024-01-15 08:30:00',
      warningDuration: 3,
      lastUpdateTime: '2024-01-15 10:00:00',
      expectedRefillTime: '2024-01-16 14:00:00',
      expectedShortageTime: '2024-01-15 18:30:00', // ä»Šå¤©ä¸‹åˆ6:30缺气
      warningRule: '当气体量低于20%时触发预警'
    },
    {
      id: 2,
      tankCode: 'TANK002',
      tankName: '压缩气储罐B',
      tankType: '压缩气储罐',
      specificationModel: 'COMP-3000L',
      volume: 3000,
      currentGasLevel: 45,
      safetyGasLevel: 25,
      minGasLevel: 15,
      maxGasLevel: 90,
      currentPressure: 8.2,
      warningType: '压力异常',
      warningLevel: '重要',
      warningThreshold: 10,
      isEnabled: true,
      warningTime: '2024-01-14 16:20:00',
      warningDuration: 2,
      lastUpdateTime: '2024-01-15 09:15:00',
      expectedRefillTime: '2024-01-17 09:00:00',
      expectedShortageTime: '2024-01-18 12:00:00', // 3天后缺气
      warningRule: '当压力超过8MPa时触发预警'
    },
    {
      id: 3,
      tankCode: 'TANK003',
      tankName: '天然气储罐C',
      tankType: '天然气储罐',
      specificationModel: 'NG-8000L',
      volume: 8000,
      currentGasLevel: 75,
      safetyGasLevel: 20,
      minGasLevel: 10,
      maxGasLevel: 95,
      currentPressure: 4.8,
      warningType: '温度异常',
      warningLevel: '一般',
      warningThreshold: 5,
      isEnabled: true,
      warningTime: '2024-01-13 11:45:00',
      warningDuration: 1,
      lastUpdateTime: '2024-01-15 08:45:00',
      expectedRefillTime: '2024-01-20 10:00:00',
      expectedShortageTime: '2024-01-22 15:30:00', // 7天后缺气
      warningRule: '当温度超过60°C时触发预警'
    },
    {
      id: 4,
      tankCode: 'TANK004',
      tankName: '氧气储罐D',
      tankType: '氧气储罐',
      specificationModel: 'O2-2000L',
      volume: 2000,
      currentGasLevel: 8,
      safetyGasLevel: 25,
      minGasLevel: 5,
      maxGasLevel: 90,
      currentPressure: 6.5,
      warningType: '泄漏预警',
      warningLevel: '紧急',
      warningThreshold: 15,
      isEnabled: true,
      warningTime: '2024-01-15 07:15:00',
      warningDuration: 4,
      lastUpdateTime: '2024-01-15 11:30:00',
      expectedRefillTime: '2024-01-15 16:00:00',
      expectedShortageTime: '2024-01-15 14:00:00', // ä»Šå¤©ä¸‹åˆ2点缺气
      warningRule: '当检测到气体泄漏时触发预警'
    },
    {
      id: 5,
      tankCode: 'TANK005',
      tankName: '液化气储罐E',
      tankType: '液化气储罐',
      specificationModel: 'LPG-6000L',
      volume: 6000,
      currentGasLevel: 35,
      safetyGasLevel: 30,
      minGasLevel: 15,
      maxGasLevel: 95,
      currentPressure: 3.2,
      warningType: '气体不足',
      warningLevel: '重要',
      warningThreshold: 20,
      isEnabled: false,
      warningTime: '2024-01-14 14:30:00',
      warningDuration: 2,
      lastUpdateTime: '2024-01-15 09:00:00',
      expectedRefillTime: '2024-01-19 08:00:00',
      expectedShortageTime: '2024-01-21 10:00:00', // 6天后缺气
      warningRule: '当气体量低于20%时触发预警'
    },
    {
      id: 6,
      tankCode: 'TANK006',
      tankName: '压缩气储罐F',
      tankType: '压缩气储罐',
      specificationModel: 'COMP-4000L',
      volume: 4000,
      currentGasLevel: 85,
      safetyGasLevel: 20,
      minGasLevel: 10,
      maxGasLevel: 90,
      currentPressure: 7.8,
      warningType: '压力异常',
      warningLevel: '一般',
      warningThreshold: 8,
      isEnabled: true,
      warningTime: '2024-01-12 09:20:00',
      warningDuration: 1,
      lastUpdateTime: '2024-01-15 08:30:00',
      expectedRefillTime: '2024-01-25 14:00:00',
      expectedShortageTime: '2024-01-28 16:00:00', // 13天后缺气
      warningRule: '当压力超过8MPa时触发预警'
    },
    {
      id: 7,
      tankCode: 'TANK007',
      tankName: '天然气储罐G',
      tankType: '天然气储罐',
      specificationModel: 'NG-10000L',
      volume: 10000,
      currentGasLevel: 92,
      safetyGasLevel: 15,
      minGasLevel: 8,
      maxGasLevel: 95,
      currentPressure: 5.2,
      warningType: '温度异常',
      warningLevel: '重要',
      warningThreshold: 6,
      isEnabled: true,
      warningTime: '2024-01-11 16:45:00',
      warningDuration: 1,
      lastUpdateTime: '2024-01-15 07:45:00',
      expectedRefillTime: '2024-01-30 09:00:00',
      expectedShortageTime: '2024-02-05 12:00:00', // 21天后缺气
      warningRule: '当温度超过60°C时触发预警'
    },
    {
      id: 8,
      tankCode: 'TANK008',
      tankName: '氧气储罐H',
      tankType: '氧气储罐',
      specificationModel: 'O2-1500L',
      volume: 1500,
      currentGasLevel: 12,
      safetyGasLevel: 30,
      minGasLevel: 8,
      maxGasLevel: 90,
      currentPressure: 4.5,
      warningType: '泄漏预警',
      warningLevel: '紧急',
      warningThreshold: 12,
      isEnabled: true,
      warningTime: '2024-01-15 06:30:00',
      warningDuration: 5,
      lastUpdateTime: '2024-01-15 12:15:00',
      expectedRefillTime: '2024-01-15 20:00:00',
      expectedShortageTime: '2024-01-15 17:30:00', // ä»Šå¤©ä¸‹åˆ5:30缺气
      warningRule: '当检测到气体泄漏时触发预警'
    }
  ]
  // æ ¹æ®æœç´¢æ¡ä»¶è¿‡æ»¤æ•°æ®
  let filteredData = mockData.filter(item => {
    if (searchForm.tankName && !item.tankName.includes(searchForm.tankName)) return false
    if (searchForm.tankType && item.tankType !== searchForm.tankType) return false
    if (searchForm.warningType && item.warningType !== searchForm.warningType) return false
    if (searchForm.warningLevel && item.warningLevel !== searchForm.warningLevel) return false
    return true
  })
  // åˆ†é¡µå¤„理
  const start = (page.current - 1) * page.size
  const end = start + page.size
  const paginatedData = filteredData.slice(start, end)
  return {
    records: paginatedData,
    total: filteredData.length
  }
}
// èŽ·å–åˆ—è¡¨æ•°æ®
const getList = async () => {
  tableLoading.value = true
  try {
    // æ¨¡æ‹Ÿç½‘络延迟
    await new Promise(resolve => setTimeout(resolve, 500))
  getStockWarningPage(page, searchForm)
  .then(res => {
    
    const result = generateMockData()
    tableData.value = result.records
    total.value = result.total
    tableData.value = res.data.records
    page.value.total = res.data.total;
    tableLoading.value = false;
    // æ£€æŸ¥ç¼ºæ°”预警
    checkShortageWarnings()
  } catch (error) {
    console.error('获取列表失败:', error)
    ElMessage.error('获取列表失败')
  } finally {
    tableLoading.value = false
  }
  }).catch(err => {
    tableLoading.value = false;
  })
}
// æœç´¢
@@ -799,7 +583,7 @@
// æ–°å¢ž
const handleAdd = () => {
  operationType.value = 'add'
  resetForm()
  // resetForm()
  dialogFormVisible.value = true
}
@@ -816,7 +600,7 @@
    // æ¨¡æ‹ŸAPI调用延迟
    await new Promise(resolve => setTimeout(resolve, 300))
    ElMessage.success(`正在处理预警:${row.tankName}`)
    getList()
    // getList()
  } catch (error) {
    ElMessage.error('处理预警失败')
  }
@@ -830,11 +614,21 @@
      cancelButtonText: '取消',
      type: 'warning'
    })
    // æ¨¡æ‹ŸAPI调用延迟
    await new Promise(resolve => setTimeout(resolve, 300))
    ElMessage.success('删除成功')
    getList()
    let ids = [];
    ids.push(row.id);
    deleteStockWarning(ids).then(res => {
      if(res.code == 200){
        ElMessage.success("删除成功");
        ids.value = [];
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // // æ¨¡æ‹ŸAPI调用延迟
    // await new Promise(resolve => setTimeout(resolve, 300))
    // ElMessage.success('删除成功')
    // getList()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('删除失败')
@@ -861,75 +655,65 @@
// å¯¼å‡º
const handleExport = async () => {
  try {
    // æ¨¡æ‹ŸAPI调用延迟
    await new Promise(resolve => setTimeout(resolve, 800))
  //   if (selectedRows.value.length === 0) {
  //     exportStockWarning().then(res => {
  //       // // åˆ›å»ºä¸‹è½½é“¾æŽ¥
  //       // const blob = new Blob([res.data], { type: 'text/csv;charset=utf-8;' })
  //       // const url = window.URL.createObjectURL(blob)
  //       // const link = document.createElement('a')
  //       // link.href = url
  //       // link.download = `储气罐预警数据_${new Date().getTime()}.csv`
  //       // link.click()
  //       // window.URL.revokeObjectURL(url)
  //     }).catch(err => {
  //       ElMessage.error(err.msg);
  //     })
  // }else{
  //     let ids = [];
  //     selectedRows.value.forEach(item => {
  //       ids.push(item.id);
  //     })
  //     exportStockWarning(ids).then(res => {
  //       // // åˆ›å»ºä¸‹è½½é“¾æŽ¥
  //       // const blob = new Blob([res.data], { type: 'text/csv;charset=utf-8;' })
  //       // const url = window.URL.createObjectURL(blob)
  //       // const link = document.createElement('a')
  //       // link.href = url
  //       // link.download = `储气罐预警数据_${new Date().getTime()}.csv`
  //       // link.click()
  //       // window.URL.revokeObjectURL(url)
  //     }).catch(err => {
  //       ElMessage.error(err.msg);
  //     })
  // }
    
    // ç”Ÿæˆå¯¼å‡ºæ•°æ®
    const exportData = generateMockData().records
    const csvContent = generateCSV(exportData)
    // åˆ›å»ºä¸‹è½½é“¾æŽ¥
    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
    const url = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = `储气罐预警数据_${new Date().getTime()}.csv`
    link.click()
    window.URL.revokeObjectURL(url)
    ElMessage.success('导出成功')
  } catch (error) {
    ElMessage.error('导出失败')
  }
}
// ç”ŸæˆCSV内容
const generateCSV = (data) => {
  const headers = [
    '储气罐编码', '储气罐名称', '储气罐类型', '规格型号', '容积(m³)',
    '当前气体量(%)', '安全气体量(%)', '最低气体量(%)', '最高气体量(%)',
    '当前压力(MPa)', '预警类型', '预警级别', '预警阈值', '是否启用',
    '预警时间', '预警持续天数', '最后更新时间', '预计充装时间', '预计缺气时间', '预警规则描述'
  ]
  const csvRows = [headers.join(',')]
  data.forEach(item => {
    const row = [
      item.tankCode,
      item.tankName,
      item.tankType,
      item.specificationModel,
      item.volume,
      item.currentGasLevel,
      item.safetyGasLevel,
      item.minGasLevel,
      item.maxGasLevel,
      item.currentPressure,
      item.warningType,
      item.warningLevel,
      item.warningThreshold,
      item.isEnabled ? '是' : '否',
      item.warningTime,
      item.warningDuration,
      item.lastUpdateTime,
      item.expectedRefillTime,
      item.expectedShortageTime,
      item.warningRule
    ]
    csvRows.push(row.join(','))
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
  return csvRows.join('\n')
    .then(() => {
      proxy.download("/gasTankWarning/export", {ids: selectedRows.value.map(item => item.id)}, "储气罐预警.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
}
// å¯ç”¨çŠ¶æ€å˜åŒ–
// // å¯ç”¨çŠ¶æ€å˜åŒ–
const handleEnableChange = async (row) => {
  try {
    // æ¨¡æ‹ŸAPI调用延迟
    await new Promise(resolve => setTimeout(resolve, 200))
    ElMessage.success(`${row.tankName} çš„启用状态已更新`)
    updateStockWarning(row).then(res => {
      if(res.code == 200){
        ElMessage.success(`${row.tankName} çš„启用状态已更新`);
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
  } catch (error) {
    ElMessage.error('状态更新失败')
    // æ¢å¤åŽŸçŠ¶æ€
@@ -943,16 +727,37 @@
    await proxy.$refs.formRef.validate()
    
    // æ¨¡æ‹ŸAPI调用延迟
    await new Promise(resolve => setTimeout(resolve, 500))
    // await new Promise(resolve => setTimeout(resolve, 500))
    
    if (operationType.value === 'add') {
      ElMessage.success('新增成功')
      addStockWarning(form).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
          dialogFormVisible.value = false
          getList()
          resetForm()
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
      // ElMessage.success('新增成功')
    } else {
      ElMessage.success('编辑成功')
      updateStockWarning(form).then(res => {
        if(res.code == 200){
          ElMessage.success("更新成功");
          dialogFormVisible.value = false
          getList()
          resetForm()
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
      // ElMessage.success('编辑成功')
    }
    
    closeDialog()
    getList()
    // closeDialog()
    // getList()
  } catch (error) {
    if (!error.errors) {
      ElMessage.error(operationType.value === 'add' ? '新增失败' : '编辑失败')
@@ -963,7 +768,7 @@
// å…³é—­å¼¹çª—
const closeDialog = () => {
  dialogFormVisible.value = false
  resetForm()
  // resetForm()
}
// é‡ç½®è¡¨å•
src/views/salesManagement/receiptPaymentHistory/index.vue
@@ -109,19 +109,6 @@
  {
    label: "回款方式",
    prop: "receiptPaymentType",
    dataType: "tag",
    formatData: (params) => {
      if (params == 0) {
        return "电汇";
      } else if (params == 1) {
        return "承兑";
      } else {
        return null;
      }
    },
    formatType: (params) => {
      return "info";
    },
  },
  {
    label: "登记人",