From 960b6c627f93e1f475c2b30cdc0baa5fb5f8da36 Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期五, 30 一月 2026 15:27:55 +0800
Subject: [PATCH] tms 开发承运商管理模块 tms 开发承运合同模块 tms 开发承运订单模块 tms 开发承运跟踪管理模块
---
src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue | 62 +
src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue | 87 +
src/views/inventoryManagement/procurementManagement/DeliveryTrackingManagement/index.vue | 372 ++++++
src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue | 372 ++++++
src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue | 196 +++
src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue | 684 ++++++++++++
src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue | 67 +
src/views/inventoryManagement/procurementManagement/paymentHistory/index.vue | 512 +++++++++
src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue | 313 +++++
src/api/inventoryManagement/CarrierManagement.js | 156 ++
src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue | 528 +++++++++
11 files changed, 3,349 insertions(+), 0 deletions(-)
diff --git a/src/api/inventoryManagement/CarrierManagement.js b/src/api/inventoryManagement/CarrierManagement.js
new file mode 100644
index 0000000..40ec3b8
--- /dev/null
+++ b/src/api/inventoryManagement/CarrierManagement.js
@@ -0,0 +1,156 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍏ュ簱淇℃伅鍒楄〃
+export const getStockInPage = (params) => {
+ return request({
+ url: "/stockin/listPage",
+ method: "get",
+ params,
+ });
+};
+
+// 鍒嗛〉鏌ヨ鎵胯繍鍚堝悓鍒楄〃
+// /fakeWarehousing/list
+export const getCarrierContractPage = (params) => {
+ return request({
+ url: "/fakeWarehousing/list",
+ method: "get",
+ params,
+ });
+};
+
+// 鏍规嵁ID鏌ヨ鎵胯繍鍚堝悓璇︽儏
+// /fakeWarehousing/{id}
+export const getCarrierContractDetail = (id) => {
+ return request({
+ url: `/fakeWarehousing/${id}`,
+ method: "get",
+ });
+};
+
+// 鏂板鎵胯繍鍚堝悓
+// /fakeWarehousing/
+export const addCarrierContract = (data) => {
+ return request({
+ url: "/fakeWarehousing",
+ method: "post",
+ data,
+ });
+};
+
+// 淇敼鎵胯繍鍚堝悓
+// /fakeWarehousing/
+export const updateCarrierContract = (data) => {
+ return request({
+ url: "/fakeWarehousing",
+ method: "put",
+ data,
+ });
+}
+
+// 鎵胯繍鍚堝悓绠$悊-鍒犻櫎ids
+// /fakeWarehousing/{ids}
+export const deleteCarrierContract = (ids) => {
+ return request({
+ url: `/fakeWarehousing/${ids}`,
+ method: "delete",
+ });
+};
+
+
+// 鍒嗛〉鏌ヨ鎵胯繍璁㈠崟鍒楄〃
+// /fakeWarehousing/order/list
+export const getCarrierOrderPage = (params) => {
+ return request({
+ url: "/fakeWarehousing/order/list",
+ method: "get",
+ params,
+ });
+};
+
+// 鏍规嵁ID鏌ヨ鎵胯繍璁㈠崟璇︽儏
+// /fakeWarehousing/order/{id}
+export const getCarrierOrderDetail = (id) => {
+ return request({
+ url: `/fakeWarehousing/order/${id}`,
+ method: "get",
+ });
+};
+
+// 鏂板鎵胯繍璁㈠崟
+// /fakeWarehousing/order/
+export const addCarrierOrder = (data) => {
+ return request({
+ url: "/fakeWarehousing/order",
+ method: "post",
+ data,
+ });
+};
+
+// 淇敼鎵胯繍璁㈠崟
+// /fakeWarehousing/order/
+export const updateCarrierOrder = (data) => {
+ return request({
+ url: "/fakeWarehousing/order",
+ method: "put",
+ data,
+ });
+}
+
+// 鍒犻櫎鎵胯繍璁㈠崟
+// /fakeWarehousing/order/{ids}
+export const deleteCarrierOrder = (ids) => {
+ return request({
+ url: `/fakeWarehousing/order/${ids}`,
+ method: "delete",
+ });
+};
+
+// 鏂板鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/
+export const addDeliveryTrack = (data) => {
+ return request({
+ url: "/fakeWarehousing/deliveryTrack",
+ method: "post",
+ data,
+ });
+}
+
+// 鍙戣揣璺熻釜绠$悊鎺ュ彛
+// 鍒嗛〉鏌ヨ鍙戣揣璺熻釜鍒楄〃
+// /fakeWarehousing/deliveryTrack/list
+export const getDeliveryTrackPage = (params) => {
+ return request({
+ url: "/fakeWarehousing/deliveryTrack/list",
+ method: "get",
+ params,
+ });
+}
+
+// 鏍规嵁ID鏌ヨ鍙戣揣璺熻釜璇︽儏
+// /fakeWarehousing/deliveryTrack/{id}
+export const getDeliveryTrackDetail = (id) => {
+ return request({
+ url: `/fakeWarehousing/deliveryTrack/${id}`,
+ method: "get",
+ });
+}
+
+// 淇敼鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/
+export const updateDeliveryTrack = (data) => {
+ return request({
+ url: "/fakeWarehousing/deliveryTrack",
+ method: "put",
+ data,
+ });
+}
+
+// 鍒犻櫎鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/{ids}
+export const deleteDeliveryTrack = (ids) => {
+ return request({
+ url: `/fakeWarehousing/deliveryTrack/${ids}`,
+ method: "delete",
+ });
+}
\ No newline at end of file
diff --git a/src/views/inventoryManagement/procurementManagement/DeliveryTrackingManagement/index.vue b/src/views/inventoryManagement/procurementManagement/DeliveryTrackingManagement/index.vue
new file mode 100644
index 0000000..31e6b0a
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/DeliveryTrackingManagement/index.vue
@@ -0,0 +1,372 @@
+<template>
+ <div class="app-container">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="鍏抽敭瀛�">
+ <el-input
+ v-model="searchForm.keyword"
+ style="width: 240px"
+ placeholder="璁㈠崟鍙�/鎵胯繍鍟�/杞︾墝"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="鍙戣揣鏃ユ湡">
+ <el-date-picker
+ v-model="searchForm.shipDateRange"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ clearable
+ @change="changeDateRange"
+ @clear="clearRange"
+ />
+ </el-form-item>
+
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
+ 鎼滅储
+ </el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="false"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"
+ >
+ <template #statusSlot="{ row }">
+ <el-tag :type="trackStatusTagType(row.trackStatus)">
+ {{ trackStatusText(row.trackStatus) }}
+ </el-tag>
+ </template>
+ </PIMTable>
+ </div>
+
+ <!-- 杞ㄨ抗鏌ョ湅寮圭獥 -->
+ <el-dialog
+ v-model="trackVisible"
+ title="鍦ㄩ�旇建杩规煡璇�"
+ width="760px"
+ :close-on-click-modal="false"
+ destroy-on-close
+ >
+ <div style="margin-bottom: 10px; color: #606266">
+ <div>璁㈠崟鍙凤細{{ currentRow.orderCode }}</div>
+ <div>鎵胯繍鍟嗭細{{ currentRow.carrierName }}銆�杞︾墝锛歿{ currentRow.vehicleNo }}</div>
+ <div>鍙戣揣鏃堕棿锛歿{ currentRow.shipTime || "-" }}</div>
+ </div>
+
+ <el-empty v-if="trackLoading" description="鍔犺浇杞ㄨ抗涓�..." />
+ <el-empty v-else-if="trackList.length === 0" description="鏆傛棤杞ㄨ抗" />
+
+ <el-timeline v-else>
+ <el-timeline-item
+ v-for="item in trackList"
+ :key="item.id"
+ :timestamp="item.time"
+ :type="item.type"
+ :hollow="false"
+ >
+ <div style="font-weight: 600">{{ item.title }}</div>
+ <div style="color: #909399">{{ item.remark }}</div>
+ </el-timeline-item>
+ </el-timeline>
+
+ <template #footer>
+ <el-button @click="trackVisible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import {
+ getDeliveryTrackPage,
+ getDeliveryTrackDetail,
+ deleteDeliveryTrack,
+} from "@/api/inventoryManagement/CarrierManagement";
+
+// ------------------ 杞ㄨ抗锛堟殏鏃跺亣鏁版嵁锛氱瓑鍚庣杞ㄨ抗鎺ュ彛瀛楁纭畾鍚庢浛鎹級 ------------------
+const pad2 = (n) => String(n).padStart(2, "0");
+const formatDateTime = (d = new Date()) => {
+ const yyyy = d.getFullYear();
+ const mm = pad2(d.getMonth() + 1);
+ const dd = pad2(d.getDate());
+ const hh = pad2(d.getHours());
+ const mi = pad2(d.getMinutes());
+ const ss = pad2(d.getSeconds());
+ return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`;
+};
+
+const trackStatusText = (s) => {
+ const map = { 0: "寰呭彂杞�", 1: "杩愯緭涓�", 2: "宸插埌杈�", 3: "寮傚父" };
+ return map[s] ?? "-";
+};
+
+const trackStatusTagType = (s) => {
+ const map = { 0: "info", 1: "warning", 2: "success", 3: "danger" };
+ return map[s] ?? "info";
+};
+
+const buildMockTrack = (row) => {
+ const now = Date.now();
+ const isArrived = Number(row.trackStatus) === 2;
+ const isAbnormal = Number(row.trackStatus) === 3;
+
+ const base = [
+ {
+ id: 1,
+ time: formatDateTime(new Date(now - 1000 * 60 * 60 * 18)),
+ title: "宸插垱寤哄彂璐у崟",
+ location: row.origin || "-",
+ remark: "宸插畬鎴愯杞�",
+ type: "primary",
+ },
+ {
+ id: 2,
+ time: formatDateTime(new Date(now - 1000 * 60 * 60 * 12)),
+ title: "杞﹁締宸插彂杞�",
+ location: row.origin || "-",
+ remark: "浠庝粨搴撳嚭鍙�",
+ type: "success",
+ },
+ {
+ id: 3,
+ time: formatDateTime(new Date(now - 1000 * 60 * 60 * 6)),
+ title: isAbnormal ? "杩愯緭寮傚父" : "閫旂粡鑺傜偣",
+ location: "涓浆绔�",
+ remark: isAbnormal ? "杞﹁締鏁呴殰锛岀瓑寰呭鐞�" : "姝e父",
+ type: isAbnormal ? "danger" : "warning",
+ },
+ ];
+
+ if (isArrived) {
+ base.push({
+ id: 4,
+ time: formatDateTime(new Date(now - 1000 * 60 * 60)),
+ title: "宸插埌杈�",
+ location: row.destination || "-",
+ remark: "宸茬鏀�",
+ type: "success",
+ });
+ }
+
+ return base;
+};
+
+// ------------------ 鍒楄〃鏌ヨ锛堟潵鑷悗绔� /fakeWarehousing/deliveryTrack/list锛� ------------------
+// 鍚庣鏍蜂緥瀛楁锛�
+// id, orderId, orderCode, carrierName, shipTime, vehicleNo, driverName, driverPhone, remark,
+// createTime, updateTime, createUser, updateUser, tenantId
+const normalizeRow = (raw = {}) => {
+ const id = raw.id ?? raw.trackId ?? raw.deliveryTrackId;
+
+ return {
+ id,
+ orderId: raw.orderId ?? null,
+ orderCode: raw.orderCode ?? raw.orderNo ?? "-",
+ carrierName: raw.carrierName ?? raw.carrier ?? "-",
+ shipTime: raw.shipTime ?? raw.shipDate ?? "-",
+ vehicleNo: raw.vehicleNo ?? raw.carNo ?? "-",
+ driverName: raw.driverName ?? raw.driver ?? "-",
+ driverPhone: raw.driverPhone ?? raw.driverTel ?? "-",
+ remark: raw.remark ?? "",
+ createTime: raw.createTime ?? "-",
+ updateTime: raw.updateTime ?? raw.modifyTime ?? "-",
+ };
+};
+
+const tableData = ref([]);
+const tableLoading = ref(false);
+
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ layout: "total, sizes, prev, pager, next, jumper",
+});
+
+const searchForm = reactive({
+ keyword: "",
+ trackStatus: "",
+ shipDateRange: [],
+ shipDateStart: undefined,
+ shipDateEnd: undefined,
+});
+
+const tableColumn = ref([
+ { label: "璁㈠崟鍙�", prop: "orderCode", width: 180 },
+ { label: "鎵胯繍鍟�", prop: "carrierName", width: 180 },
+ { label: "杞︾墝鍙�", prop: "vehicleNo", width: 130 },
+ { label: "鍙戣揣鏃堕棿", prop: "shipTime", width: 170 },
+ { label: "鍙告満", prop: "driverName", width: 120 },
+ { label: "鍙告満鐢佃瘽", prop: "driverPhone", width: 140 },
+ { label: "鏇存柊鏃堕棿", prop: "updateTime", width: 170 },
+ {
+ label: "鎿嶄綔",
+ prop: "action",
+ dataType: "action",
+ fixed: "right",
+ width: 120,
+ operation: [
+ { name: "鏌ョ湅杞ㄨ抗", clickFun: (row) => openTrack(row) },
+ { name: "鍒犻櫎", clickFun: (row) => handleDelete(row) },
+ ],
+ },
+]);
+
+const buildListParams = () => {
+ return {
+ current: page.current,
+ size: page.size,
+ keyword: searchForm.keyword || undefined,
+ orderCode: searchForm.keyword || undefined,
+ carrierName: searchForm.keyword || undefined,
+ vehicleNo: searchForm.keyword || undefined,
+ trackStatus:
+ searchForm.trackStatus === "" || searchForm.trackStatus === null || searchForm.trackStatus === undefined
+ ? undefined
+ : searchForm.trackStatus,
+ startDate: searchForm.shipDateStart || undefined,
+ endDate: searchForm.shipDateEnd || undefined,
+ };
+};
+
+const getList = async () => {
+ tableLoading.value = true;
+ try {
+ const res = await getDeliveryTrackPage(buildListParams());
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ const total = data?.total ?? data?.count ?? 0;
+
+ tableData.value = (records || []).map(normalizeRow);
+ page.total = total;
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍔犺浇澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+const resetQuery = () => {
+ searchForm.keyword = "";
+ searchForm.trackStatus = "";
+ searchForm.shipDateRange = [];
+ searchForm.shipDateStart = undefined;
+ searchForm.shipDateEnd = undefined;
+ page.current = 1;
+ getList();
+};
+
+const pagination = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+const changeDateRange = (date) => {
+ if (date && date.length === 2) {
+ searchForm.shipDateStart = date[0];
+ searchForm.shipDateEnd = date[1];
+ } else {
+ searchForm.shipDateStart = undefined;
+ searchForm.shipDateEnd = undefined;
+ }
+ getList();
+};
+
+const clearRange = () => {
+ searchForm.shipDateRange = [];
+ searchForm.shipDateStart = undefined;
+ searchForm.shipDateEnd = undefined;
+ getList();
+};
+
+// ------------------ 杞ㄨ抗寮圭獥锛堣鎯呰皟鐢ㄥ悗绔� /fakeWarehousing/deliveryTrack/{id} 锛� ------------------
+const trackVisible = ref(false);
+const trackLoading = ref(false);
+const trackList = ref([]);
+const currentRow = reactive({});
+
+const openTrack = async (row) => {
+ Object.assign(currentRow, row);
+ trackVisible.value = true;
+ trackLoading.value = true;
+
+ try {
+ const res = await getDeliveryTrackDetail(row.id);
+ const detail = res?.data ?? res;
+ Object.assign(currentRow, normalizeRow(detail));
+
+ // 杞ㄨ抗鏄庣粏瀛楁灏氭湭纭畾锛氭殏鐢ㄦā鎷熻建杩瑰睍绀�
+ trackList.value = buildMockTrack(currentRow);
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "杞ㄨ抗鍔犺浇澶辫触");
+ trackList.value = buildMockTrack(currentRow);
+ } finally {
+ trackLoading.value = false;
+ }
+};
+
+// 鍒犻櫎锛堟壒閲� ids 鍚庣涔熸敮鎸侊細杩欓噷鍗曟潯鍒犻櫎锛�
+const handleDelete = async (row) => {
+ await ElMessageBox.confirm(`纭鍒犻櫎鍙戣揣璁板綍銆�${row.orderCode || "-"}銆戯紵`, "鎻愮ず", {
+ confirmButtonText: "鍒犻櫎",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+
+ tableLoading.value = true;
+ try {
+ await deleteDeliveryTrack(row.id);
+ ElMessage.success("鍒犻櫎鎴愬姛");
+
+ // 鍒犻櫎鍚庡鏋滃綋鍓嶉〉涓虹┖鍒欏洖閫�
+ const res = await getDeliveryTrackPage(buildListParams());
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ if ((records || []).length === 0 && page.current > 1) {
+ page.current -= 1;
+ }
+
+ await getList();
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍒犻櫎澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+// 棰勭暀锛氭柊澧�/淇敼鎺ュ彛宸叉帴鍏ワ紙鍚庣画鍔犲脊绐楀嵆鍙級
+// addDeliveryTrack / updateDeliveryTrack
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.table_list {
+ margin-top: unset;
+}
+</style>
diff --git a/src/views/inventoryManagement/procurementManagement/paymentHistory/index.vue b/src/views/inventoryManagement/procurementManagement/paymentHistory/index.vue
new file mode 100644
index 0000000..2fb870d
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/paymentHistory/index.vue
@@ -0,0 +1,512 @@
+<template>
+ <div class="app-container">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="鎵胯繍鍟�">
+ <el-input
+ v-model="searchForm.carrierName"
+ style="width: 240px"
+ placeholder="杈撳叆鎵胯繍鍟嗗悕绉�"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ </el-form-item>
+
+ <el-form-item label="鍚堝悓缂栧彿">
+ <el-input
+ v-model="searchForm.contractCode"
+ style="width: 240px"
+ placeholder="杈撳叆鍚堝悓缂栧彿"
+ clearable
+ @keyup.enter="handleQuery"
+ />
+ </el-form-item>
+
+ <el-form-item label="鍚堝悓鐘舵��">
+ <el-select
+ v-model="searchForm.contractStatus"
+ style="width: 160px"
+ placeholder="鍏ㄩ儴"
+ clearable
+ >
+ <el-option label="鏈夋晥" :value="1" />
+ <el-option label="鏃犳晥" :value="0" />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="鍚堝悓鏃堕棿">
+ <el-date-picker
+ v-model="searchForm.timeRange"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ format="YYYY-MM-DD"
+ type="datetimerange"
+ start-placeholder="寮�濮嬫椂闂�"
+ end-placeholder="缁撴潫鏃堕棿"
+ clearable
+ @change="changeDateRange"
+ @clear="clearRange"
+ />
+ </el-form-item>
+
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
+ 鎼滅储
+ </el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <div class="table_list">
+ <div style="display: flex; justify-content: flex-end; margin-bottom: 12px">
+ <el-button type="primary" @click="openCreate">鍒涘缓鍚堝悓</el-button>
+ </div>
+
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="false"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"
+ >
+ <template #statusSlot="{ row }">
+ <el-tag :type="row.contractStatus === 1 ? 'success' : 'info'">
+ {{ row.contractStatus === 1 ? '鏈夋晥' : '鏃犳晥' }}
+ </el-tag>
+ </template>
+ <template #versionSlot="{ row }">
+ <el-tag type="warning">v{{ row.version }}</el-tag>
+ </template>
+ </PIMTable>
+ </div>
+
+ <!-- 鍒涘缓/缁存姢(缂栬緫)寮圭獥 -->
+ <el-dialog
+ v-model="editVisible"
+ :title="editMode === 'create' ? '鍒涘缓鎵胯繍鍚堝悓' : '鍚堝悓缁存姢'"
+ width="720px"
+ :close-on-click-modal="false"
+ destroy-on-close
+ >
+ <el-form ref="editFormRef" :model="editForm" :rules="rules" label-width="120px">
+ <el-row :gutter="12">
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓缂栧彿" prop="contractCode">
+ <el-input v-model="editForm.contractCode" placeholder="濡� HT-2026-0001" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓鍚嶇О" prop="contractName">
+ <el-input v-model="editForm.contractName" placeholder="濡� 閿�鍞悎鍚�" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎵胯繍鍟嗗悕绉�" prop="carrierName">
+ <el-input v-model="editForm.carrierName" placeholder="璇疯緭鍏ユ壙杩愬晢鍚嶇О" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓寮�濮嬫椂闂�" prop="startTime">
+ <el-date-picker
+ v-model="editForm.startTime"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ format="YYYY-MM-DD HH:mm:ss"
+ type="datetime"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓缁撴潫鏃堕棿" prop="endTime">
+ <el-date-picker
+ v-model="editForm.endTime"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ format="YYYY-MM-DD HH:mm:ss"
+ type="datetime"
+ placeholder="鍙��"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓鐘舵��" prop="contractStatus">
+ <el-select v-model="editForm.contractStatus" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option label="鏈夋晥" :value="1" />
+ <el-option label="鏃犳晥" :value="0" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="24">
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="editForm.remark" type="textarea" :rows="2" placeholder="鍙��" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+
+ <template #footer>
+ <el-button @click="editVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" :loading="saving" @click="submitEdit">纭畾</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 鏌ョ湅寮圭獥 -->
+ <el-dialog v-model="viewVisible" title="鍚堝悓淇℃伅鏌ョ湅" width="720px" destroy-on-close>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鍚堝悓缂栧彿">{{ viewRow.contractCode }}</el-descriptions-item>
+ <el-descriptions-item label="鎵胯繍鍟�">{{ viewRow.carrierName }}</el-descriptions-item>
+ <el-descriptions-item label="鐘舵��">{{ viewRow.contractStatus === 1 ? '鏈夋晥' : '鏃犳晥' }}</el-descriptions-item>
+ <el-descriptions-item label="寮�濮嬫椂闂�">{{ viewRow.startTime }}</el-descriptions-item>
+ <el-descriptions-item label="缁撴潫鏃堕棿">{{ viewRow.endTime || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">{{ viewRow.createTime }}</el-descriptions-item>
+ <el-descriptions-item label="淇敼鏃堕棿">{{ viewRow.updateTime }}</el-descriptions-item>
+ <el-descriptions-item label="澶囨敞" :span="2">{{ viewRow.remark || '-' }}</el-descriptions-item>
+ </el-descriptions>
+ <template #footer>
+ <el-button @click="viewVisible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import useFormData from "@/hooks/useFormData";
+import dayjs from "dayjs";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import {
+ getCarrierContractPage,
+ getCarrierContractDetail,
+ addCarrierContract,
+ updateCarrierContract,
+ deleteCarrierContract,
+} from "@/api/inventoryManagement/CarrierManagement";
+
+// ------------------ 鏁版嵁閫傞厤锛堝吋瀹瑰悗绔繑鍥炵粨鏋勶級 ------------------
+const normalizeRow = (raw = {}) => {
+ // 鍚庣瀛楁锛歩d/contractCode/carrierId/carrierName/contractName/contractStatus/startTime/endTime/remark/createUser/createTime/updateUser/updateTime/tenantId
+ return {
+ id: raw.id,
+ contractCode: raw.contractCode,
+ carrierId: raw.carrierId,
+ carrierName: raw.carrierName,
+ contractName: raw.contractName,
+ contractStatus: raw.contractStatus,
+ startTime: raw.startTime,
+ endTime: raw.endTime,
+ remark: raw.remark,
+ createUser: raw.createUser,
+ createTime: raw.createTime,
+ updateUser: raw.updateUser,
+ updateTime: raw.updateTime,
+ tenantId: raw.tenantId,
+ // version 闈炲悗绔瓧娈碉細椤甸潰灞曠ず鍏滃簳
+ version: raw.version ?? 1,
+ };
+};
+
+// ------------------ 椤甸潰鐘舵�� ------------------
+const tableData = ref([]);
+const selectedRows = ref([]);
+const tableLoading = ref(false);
+
+const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0,
+ layout: "total, sizes, prev, pager, next, jumper",
+});
+
+const { form: searchForm } = useFormData({
+ carrierName: "",
+ contractCode: "",
+ contractName: "",
+ contractStatus: "",
+ timeRange: [],
+ startTime: undefined,
+ endTime: undefined,
+});
+
+const tableColumn = ref([
+ { label: "鍚堝悓缂栧彿", prop: "contractCode", width: 160 },
+ { label: "鍚堝悓鍚嶇О", prop: "contractName", width: 200 },
+ { label: "鎵胯繍鍟�", prop: "carrierName", width: 200 },
+ { label: "鐘舵��", prop: "contractStatus", dataType: "slot", slot: "statusSlot", width: 90 },
+ { label: "寮�濮嬫椂闂�", prop: "startTime", width: 170 },
+ { label: "缁撴潫鏃堕棿", prop: "endTime", width: 170 },
+ { label: "淇敼鏃堕棿", prop: "updateTime", width: 170 },
+ {
+ label: "鎿嶄綔",
+ prop: "action",
+ dataType: "action",
+ fixed: "right",
+ width: 140,
+ operation: [
+ { name: "鏌ョ湅", clickFun: (row) => openView(row) },
+ { name: "缂栬緫", clickFun: (row) => openMaintain(row) },
+ { name: "鍒犻櫎", clickFun: (row) => handleDelete(row) },
+ ],
+ },
+]);
+
+const getList = async () => {
+ tableLoading.value = true;
+ try {
+ const { timeRange, ...rest } = searchForm;
+ const params = {
+ ...rest,
+ current: page.current,
+ size: page.size,
+ };
+
+ const res = await getCarrierContractPage(params);
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ const total = data?.total ?? data?.count ?? 0;
+
+ tableData.value = (records || []).map(normalizeRow);
+ page.total = total;
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍔犺浇澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+const resetQuery = () => {
+ searchForm.carrierName = "";
+ searchForm.contractCode = "";
+ searchForm.contractName = "";
+ searchForm.contractStatus = "";
+ searchForm.timeRange = [];
+ searchForm.startTime = undefined;
+ searchForm.endTime = undefined;
+ page.current = 1;
+ getList();
+};
+
+const pagination = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+};
+
+const changeDateRange = (date) => {
+ if (date && date.length === 2) {
+ searchForm.startTime = date[0];
+ searchForm.endTime = date[1];
+ } else {
+ searchForm.startTime = undefined;
+ searchForm.endTime = undefined;
+ }
+ getList();
+};
+
+const clearRange = () => {
+ searchForm.timeRange = [];
+ searchForm.startTime = undefined;
+ searchForm.endTime = undefined;
+ getList();
+};
+
+// ------------------ 寮圭獥锛氭煡鐪�/缁存姢 ------------------
+const viewVisible = ref(false);
+const viewRow = reactive({});
+
+const openView = async (row) => {
+ // 鍏堝睍绀鸿鏁版嵁
+ Object.assign(viewRow, row);
+ viewVisible.value = true;
+
+ // 鍐嶆媺璇︽儏琛ュ叏
+ if (!row?.id) return;
+ try {
+ const res = await getCarrierContractDetail(row.id);
+ const detail = res?.data ?? res;
+ Object.assign(viewRow, normalizeRow(detail));
+ } catch {
+ // 璇︽儏澶辫触涓嶉樆鏂煡鐪�
+ }
+};
+
+const editVisible = ref(false);
+const editMode = ref("create"); // create | maintain
+const editFormRef = ref();
+const saving = ref(false);
+const editForm = reactive({
+ id: undefined,
+ contractCode: "",
+ carrierId: undefined,
+ carrierName: "",
+ contractName: "",
+ contractStatus: 1,
+ startTime: "",
+ endTime: "",
+ remark: "",
+});
+
+const rules = {
+ contractCode: [{ required: true, message: "璇疯緭鍏ュ悎鍚岀紪鍙�", trigger: "blur" }],
+ carrierName: [{ required: true, message: "璇疯緭鍏ユ壙杩愬晢鍚嶇О", trigger: "blur" }],
+ contractName: [{ required: true, message: "璇疯緭鍏ュ悎鍚屽悕绉�", trigger: "blur" }],
+ contractStatus: [{ required: true, message: "璇烽�夋嫨鍚堝悓鐘舵��", trigger: "change" }],
+ startTime: [{ required: true, message: "璇烽�夋嫨鍚堝悓寮�濮嬫椂闂�", trigger: "change" }],
+ endTime:[{ required: true, message: "璇烽�夋嫨鍚堝悓寮�濮嬫椂闂�", trigger: "change" }]
+};
+
+const openCreate = () => {
+ editMode.value = "create";
+ Object.assign(editForm, {
+ id: undefined,
+ contractCode: "",
+ carrierId: undefined,
+ carrierName: "",
+ contractName: "",
+ contractStatus: 1,
+ startTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+ endTime: "",
+ remark: "",
+ });
+ editVisible.value = true;
+ queueMicrotask(() => editFormRef.value?.clearValidate?.());
+};
+
+const openMaintain = async (row) => {
+ editMode.value = "maintain";
+ Object.assign(editForm, {
+ id: row.id,
+ contractCode: row.contractCode,
+ carrierId: row.carrierId,
+ carrierName: row.carrierName,
+ contractName: row.contractName,
+ contractStatus: row.contractStatus,
+ startTime: row.startTime,
+ endTime: row.endTime,
+ remark: row.remark,
+ });
+
+ // 鎷夎鎯咃紝閬垮厤鍒楄〃瀛楁涓嶅叏
+ if (row?.id) {
+ try {
+ const res = await getCarrierContractDetail(row.id);
+ const detail = res?.data ?? res;
+ const d = normalizeRow(detail);
+ Object.assign(editForm, {
+ id: d.id,
+ contractCode: d.contractCode,
+ carrierId: d.carrierId,
+ carrierName: d.carrierName,
+ contractName: d.contractName,
+ contractStatus: d.contractStatus,
+ startTime: d.startTime,
+ endTime: d.endTime,
+ remark: d.remark,
+ });
+ } catch {
+ // ignore
+ }
+ }
+
+ editVisible.value = true;
+ queueMicrotask(() => editFormRef.value?.clearValidate?.());
+};
+
+const submitEdit = async () => {
+ saving.value = true;
+ try {
+ await editFormRef.value?.validate?.();
+
+ const payload = {
+ id: editForm.id,
+ contractCode: editForm.contractCode,
+ carrierId: editForm.carrierId,
+ carrierName: editForm.carrierName,
+ contractName: editForm.contractName,
+ contractStatus: editForm.contractStatus,
+ startTime: editForm.startTime,
+ endTime: editForm.endTime,
+ remark: editForm.remark,
+ };
+
+ if (editMode.value === "create") {
+ await addCarrierContract(payload);
+ ElMessage.success("鍒涘缓鎴愬姛");
+ page.current = 1;
+ } else {
+ await updateCarrierContract(payload);
+ ElMessage.success("鏇存柊鎴愬姛");
+ }
+
+ editVisible.value = false;
+ await getList();
+ } catch (e) {
+ if (e?.message) ElMessage.error(e.message);
+ } finally {
+ saving.value = false;
+ }
+};
+
+const handleDelete = async (row) => {
+ await ElMessageBox.confirm(`纭鍒犻櫎鍚堝悓銆�${row.contractCode}銆戯紵`, "鎻愮ず", {
+ confirmButtonText: "鍒犻櫎",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+
+ tableLoading.value = true;
+ try {
+ await deleteCarrierContract(row.id);
+ ElMessage.success("鍒犻櫎鎴愬姛");
+
+ // 鍒犻櫎鍚庤嫢褰撳墠椤垫病鏁版嵁锛屽洖閫�涓�椤�
+ const res = await getCarrierContractPage({
+ ...searchForm,
+ current: page.current,
+ size: page.size,
+ });
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ if ((records || []).length === 0 && page.current > 1) {
+ page.current -= 1;
+ }
+
+ await getList();
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍒犻櫎澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+// ------------------ 鏃х殑鈥滅敓鎴愭柊鐗堟湰鈥濆姛鑳斤細鍚庣鏆傛棤鎺ュ彛锛屾殏涓嶅惎鐢� ------------------
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.table_list {
+ margin-top: unset;
+}
+</style>
diff --git a/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
new file mode 100644
index 0000000..2e8f994
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
@@ -0,0 +1,684 @@
+<template>
+ <div class="app-container">
+ <div
+ class="search_form"
+ style="
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+ align-items: flex-start;
+ "
+ >
+ <div
+ style="
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 10px;
+ "
+ >
+ <span class="search_title">鍏抽敭瀛�:</span>
+ <el-input
+ v-model="searchForm.keyword"
+ style="width: 240px"
+ placeholder="璁㈠崟鍙�/鎵胯繍鍟�/鍙戣揣鍦�/鏀惰揣鍦�"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+
+ <span class="search_title">鐘舵��:</span>
+ <el-select
+ v-model="searchForm.orderStatus"
+ style="width: 160px"
+ clearable
+ placeholder="鍏ㄩ儴"
+ >
+ <el-option label="寰呭彂璐�" :value="1" />
+ <el-option label="宸插彂璐�" :value="2" />
+ </el-select>
+
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </div>
+
+ <div>
+ <el-button type="primary" @click="openCreate">鍒涘缓璁㈠崟</el-button>
+ </div>
+ </div>
+
+ <el-row :gutter="20">
+ <!-- 宸︿晶锛氳鍗曞垪琛� -->
+ <el-col :span="24">
+ <div class="table_list">
+ <el-table
+ border
+ v-loading="tableLoading"
+ :data="tableData"
+ :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+ height="calc(100vh - 18.5em)"
+ :highlight-current-row="true"
+ style="width: 100%"
+ stripe
+ tooltip-effect="dark"
+ class="lims-table"
+ >
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column
+ label="鎵胯繍璁㈠崟鍙�"
+ prop="orderCode"
+ width="160"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鎵胯繍鍟�"
+ prop="carrierName"
+ width="180"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍙戣揣鍦�"
+ prop="origin"
+ width="160"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鏀惰揣鍦�"
+ prop="destination"
+ width="160"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="閲嶉噺(kg)"
+ prop="weight"
+ width="110"
+ align="right"
+ />
+ <el-table-column
+ label="浣撶Н(m鲁)"
+ prop="volume"
+ width="110"
+ align="right"
+ />
+ <el-table-column
+ label="棰勮璐圭敤(鍏�)"
+ prop="estimatedFee"
+ width="140"
+ align="right"
+ >
+ <template #default="{ row }">{{ toMoney(row.estimatedFee) }}</template>
+ </el-table-column>
+ <el-table-column label="鐘舵��" prop="orderStatus" width="110">
+ <template #default="{ row }">
+ <el-tag :type="statusTagType(row.orderStatus)">
+ {{ statusText(row.orderStatus) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏇存柊鏃堕棿" prop="updateTime" width="170" />
+
+ <el-table-column fixed="right" label="鎿嶄綔" width="260" align="center">
+ <template #default="scope">
+ <el-button
+ link
+ type="success"
+ size="small"
+ :disabled="scope.row.orderStatus !== 1"
+ @click.stop="openDelivery(scope.row)"
+ >鍙戣揣</el-button
+ >
+ <el-button
+ link
+ type="primary"
+ size="small"
+ @click.stop="openView(scope.row)"
+ >鏌ョ湅</el-button
+ >
+ <el-button
+ link
+ type="primary"
+ size="small"
+ @click.stop="openEdit(scope.row)"
+ >缂栬緫</el-button
+ >
+ <el-button
+ link
+ type="danger"
+ size="small"
+ @click.stop="handleDelete(scope.row)"
+ >鍒犻櫎</el-button
+ >
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination
+ v-show="total > 0"
+ @pagination="paginationSearch"
+ :total="total"
+ :layout="page.layout"
+ :page="page.current"
+ :limit="page.size"
+ />
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鍒涘缓/缂栬緫寮圭獥 -->
+ <el-dialog
+ v-model="editVisible"
+ :title="editMode === 'create' ? '鍒涘缓鎵胯繍璁㈠崟' : '缂栬緫鎵胯繍璁㈠崟'"
+ width="720px"
+ :close-on-click-modal="false"
+ destroy-on-close
+ >
+ <el-form
+ ref="editFormRef"
+ :model="editForm"
+ :rules="rules"
+ label-width="120px"
+ >
+ <el-row :gutter="12">
+ <el-col :span="12">
+ <el-form-item label="鎵胯繍璁㈠崟鍙�" prop="orderCode">
+ <el-input
+ v-model="editForm.orderCode"
+ placeholder="濡� TO-20260130-0001"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎵胯繍鍟嗗悕绉�" prop="carrierName">
+ <el-input
+ v-model="editForm.carrierName"
+ placeholder="璇疯緭鍏ユ壙杩愬晢鍚嶇О"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍙戣揣鍦�" prop="origin">
+ <el-input v-model="editForm.origin" placeholder="璇疯緭鍏ュ彂璐у湴" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏀惰揣鍦�" prop="destination">
+ <el-input
+ v-model="editForm.destination"
+ placeholder="璇疯緭鍏ユ敹璐у湴"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="閲嶉噺(kg)" prop="weight">
+ <el-input-number
+ v-model="editForm.weight"
+ :min="0"
+ :precision="2"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="浣撶Н(m鲁)" prop="volume">
+ <el-input-number
+ v-model="editForm.volume"
+ :min="0"
+ :precision="3"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="棰勮璐圭敤(鍏�)" prop="estimatedFee">
+ <el-input-number
+ v-model="editForm.estimatedFee"
+ :min="0"
+ :precision="2"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="24">
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input
+ v-model="editForm.remark"
+ type="textarea"
+ :rows="2"
+ placeholder="鍙��"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+
+ <template #footer>
+ <el-button @click="editVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" :loading="saving" @click="submitEdit"
+ >纭畾</el-button
+ >
+ </template>
+ </el-dialog>
+
+ <!-- 鏌ョ湅寮圭獥 -->
+ <el-dialog v-model="viewVisible" title="璁㈠崟淇℃伅鏌ョ湅" width="720px" destroy-on-close>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鎵胯繍璁㈠崟鍙�">{{ viewRow.orderCode }}</el-descriptions-item>
+ <el-descriptions-item label="鐘舵��">{{ statusText(viewRow.orderStatus) }}</el-descriptions-item>
+ <el-descriptions-item label="鎵胯繍鍟�">{{ viewRow.carrierName }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戣揣鍦�">{{ viewRow.origin }}</el-descriptions-item>
+ <el-descriptions-item label="鏀惰揣鍦�">{{ viewRow.destination }}</el-descriptions-item>
+ <el-descriptions-item label="閲嶉噺(kg)">{{ viewRow.weight }}</el-descriptions-item>
+ <el-descriptions-item label="浣撶Н(m鲁)">{{ viewRow.volume }}</el-descriptions-item>
+ <el-descriptions-item label="棰勮璐圭敤(鍏�)">{{ toMoney(viewRow.estimatedFee) }}</el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">{{ viewRow.createTime }}</el-descriptions-item>
+ <el-descriptions-item label="鏇存柊鏃堕棿">{{ viewRow.updateTime }}</el-descriptions-item>
+ <el-descriptions-item label="澶囨敞" :span="2">{{ viewRow.remark || "-" }}</el-descriptions-item>
+ </el-descriptions>
+ <template #footer>
+ <el-button @click="viewVisible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 鍙戣揣寮圭獥锛堟殏鐢ㄥ亣鎻愪氦锛岀瓑鍚庣鎺ュ彛锛� -->
+ <el-dialog
+ v-model="deliveryVisible"
+ title="鍙戣揣"
+ width="720px"
+ :close-on-click-modal="false"
+ destroy-on-close
+ >
+ <el-form ref="deliveryFormRef" :model="deliveryForm" :rules="deliveryRules" label-width="120px">
+ <el-row :gutter="12">
+ <el-col :span="24">
+ <el-alert
+ type="info"
+ :closable="false"
+ :title="`璁㈠崟鍙凤細${deliveryForm.orderCode || '-'}锛堟壙杩愬晢锛�${deliveryForm.carrierName || '-'}锛塦"
+ style="margin-bottom: 12px"
+ />
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍙戣揣鏃堕棿" prop="shipTime">
+ <el-date-picker
+ v-model="deliveryForm.shipTime"
+ type="datetime"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ format="YYYY-MM-DD HH:mm:ss"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="杞︾墝鍙�" prop="vehicleNo">
+ <el-input v-model="deliveryForm.vehicleNo" placeholder="濡� 娌狝12345" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍙告満濮撳悕" prop="driverName">
+ <el-input v-model="deliveryForm.driverName" placeholder="璇疯緭鍏�" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="鍙告満鐢佃瘽" prop="driverPhone">
+ <el-input v-model="deliveryForm.driverPhone" placeholder="璇疯緭鍏�" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="24">
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="deliveryForm.remark" type="textarea" :rows="2" placeholder="鍙��" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+
+ <template #footer>
+ <el-button @click="deliveryVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" :loading="deliverySubmitting" @click="submitDelivery">纭畾鍙戣揣</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import pagination from "@/components/PIMTable/Pagination.vue";
+import dayjs from "dayjs";
+import {
+ getCarrierOrderPage,
+ getCarrierOrderDetail,
+ addCarrierOrder,
+ updateCarrierOrder,
+ deleteCarrierOrder, addDeliveryTrack,
+} from "@/api/inventoryManagement/CarrierManagement";
+
+const toMoney = (v) => {
+ if (v === undefined || v === null || v === "") return "0.00";
+ const n = Number(v);
+ return Number.isFinite(n) ? n.toFixed(2) : "0.00";
+};
+
+const statusText = (s) => {
+ const map = {
+ 0: "鑽夌",
+ 1: "寰呭彂璐�",
+ 2: "宸插彂璐�",
+ };
+ return map[s] ?? "-";
+};
+
+const statusTagType = (s) => {
+ const map = {
+ 0: "info",
+ 1: "success",
+ 2: "warning",
+ 3: "success",
+ 4: "danger",
+ };
+ return map[s] ?? "info";
+};
+
+// 閫傞厤鍚庣杩斿洖瀛楁锛堜綘鍚庣瀛楁鏈渶缁堢‘瀹氭椂鍏堝仛鍏滃簳鏄犲皠锛�
+const normalizeRow = (raw = {}) => {
+ return {
+ orderId: raw.id ?? raw.orderId,
+ orderCode: raw.orderCode ?? raw.code,
+ carrierId: raw.carrierId,
+ carrierName: raw.carrierName,
+ origin: raw.origin ?? raw.startAddress ?? raw.fromAddress,
+ destination: raw.destination ?? raw.endAddress ?? raw.toAddress,
+ weight: raw.weight ?? 0,
+ volume: raw.volume ?? 0,
+ estimatedFee: raw.estimatedFee ?? raw.fee ?? 0,
+ orderStatus: raw.orderStatus ?? raw.status ?? 0,
+ remark: raw.remark ?? "",
+ createTime: raw.createTime,
+ updateTime: raw.updateTime,
+ };
+};
+
+// ------------------ 椤甸潰鐘舵�� ------------------
+const searchForm = reactive({
+ keyword: "",
+ orderStatus: "",
+});
+
+const page = reactive({
+ current: 1,
+ size: 10,
+ layout: "total, sizes, prev, pager, next, jumper",
+});
+
+const total = ref(0);
+const tableData = ref([]);
+const tableLoading = ref(false);
+
+const buildListParams = () => {
+ return {
+ current: page.current,
+ size: page.size,
+ keyword: searchForm.keyword || undefined,
+ orderCode: searchForm.keyword || undefined,
+ carrierName: searchForm.keyword || undefined,
+ origin: searchForm.keyword || undefined,
+ destination: searchForm.keyword || undefined,
+ orderStatus:
+ searchForm.orderStatus === "" || searchForm.orderStatus === null || searchForm.orderStatus === undefined
+ ? undefined
+ : searchForm.orderStatus,
+ };
+};
+
+const getList = async () => {
+ tableLoading.value = true;
+ try {
+ const res = await getCarrierOrderPage(buildListParams());
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ const t = data?.total ?? data?.count ?? 0;
+
+ tableData.value = (records || []).map(normalizeRow);
+ total.value = t;
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍔犺浇澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+const resetQuery = () => {
+ searchForm.keyword = "";
+ searchForm.orderStatus = "";
+ page.current = 1;
+ getList();
+};
+
+const paginationSearch = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+// ------------------ 鍒涘缓/缂栬緫/鏌ョ湅/鍒犻櫎 ------------------
+const editVisible = ref(false);
+const viewVisible = ref(false);
+const editMode = ref("create");
+const saving = ref(false);
+const editFormRef = ref();
+
+const editForm = reactive({
+ orderId: undefined,
+ orderCode: "",
+ carrierId: undefined,
+ carrierName: "",
+ origin: "",
+ destination: "",
+ weight: 0,
+ volume: 0,
+ estimatedFee: 0,
+ orderStatus: 0,
+ remark: "",
+});
+
+const rules = {
+ orderCode: [{ required: true, message: "璇疯緭鍏ユ壙杩愯鍗曞彿", trigger: "blur" }],
+ carrierName: [{ required: true, message: "璇疯緭鍏ユ壙杩愬晢鍚嶇О", trigger: "blur" }],
+ origin: [{ required: true, message: "璇疯緭鍏ュ彂璐у湴", trigger: "blur" }],
+ destination: [{ required: true, message: "璇疯緭鍏ユ敹璐у湴", trigger: "blur" }],
+};
+
+const openCreate = () => {
+ editMode.value = "create";
+ Object.assign(editForm, {
+ orderId: undefined,
+ orderCode: `TO-${dayjs().format("YYYYMMDD")}-${String(Math.floor(Math.random() * 9000 + 1000))}`,
+ carrierId: undefined,
+ carrierName: "",
+ origin: "",
+ destination: "",
+ weight: 0,
+ volume: 0,
+ estimatedFee: 0,
+ orderStatus: 0,
+ remark: "",
+ });
+ editVisible.value = true;
+ queueMicrotask(() => editFormRef.value?.clearValidate?.());
+};
+
+const openEdit = async (row) => {
+ editMode.value = "edit";
+ Object.assign(editForm, { ...row });
+
+ // 鎷夎鎯呰ˉ鍏�
+ if (row?.id) {
+ try {
+ const res = await getCarrierOrderDetail(row.id);
+ const detail = res?.data ?? res;
+ Object.assign(editForm, normalizeRow(detail));
+ } catch {
+ // ignore
+ }
+ }
+
+ editVisible.value = true;
+ queueMicrotask(() => editFormRef.value?.clearValidate?.());
+};
+
+const viewRow = reactive({});
+const openView = async (row) => {
+ Object.assign(viewRow, row);
+ viewVisible.value = true;
+
+ if (row?.id) {
+ try {
+ const res = await getCarrierOrderDetail(row.id);
+ const detail = res?.data ?? res;
+ Object.assign(viewRow, normalizeRow(detail));
+ } catch {
+ // ignore
+ }
+ }
+};
+
+const submitEdit = async () => {
+ saving.value = true;
+ try {
+ await editFormRef.value?.validate?.();
+
+ const payload = {
+ id: editForm.id,
+ orderCode: editForm.orderCode,
+ carrierId: editForm.carrierId,
+ carrierName: editForm.carrierName,
+ origin: editForm.origin,
+ destination: editForm.destination,
+ weight: editForm.weight,
+ volume: editForm.volume,
+ estimatedFee: editForm.estimatedFee,
+ orderStatus: editForm.orderStatus,
+ remark: editForm.remark,
+ };
+
+ if (editMode.value === "create") {
+ await addCarrierOrder(payload);
+ ElMessage.success("鍒涘缓鎴愬姛");
+ page.current = 1;
+ } else {
+ await updateCarrierOrder(payload);
+ ElMessage.success("鏇存柊鎴愬姛");
+ }
+
+ editVisible.value = false;
+ await getList();
+ } catch (e) {
+ if (e?.message) ElMessage.error(e.message);
+ } finally {
+ saving.value = false;
+ }
+};
+
+const handleDelete = async (row) => {
+ await ElMessageBox.confirm(`纭鍒犻櫎璁㈠崟銆�${row.orderCode}銆戯紵`, "鎻愮ず", {
+ confirmButtonText: "鍒犻櫎",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+
+ tableLoading.value = true;
+ try {
+ await deleteCarrierOrder(row.id);
+ ElMessage.success("鍒犻櫎鎴愬姛");
+
+ // 鍒犻櫎鍚庡鏋滃綋鍓嶉〉鏃犳暟鎹垯鍥為��
+ const res = await getCarrierOrderPage(buildListParams());
+ const data = res?.data ?? res;
+ const records = data?.records ?? data?.rows ?? data?.list ?? [];
+ if ((records || []).length === 0 && page.current > 1) {
+ page.current -= 1;
+ }
+
+ await getList();
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鍒犻櫎澶辫触");
+ } finally {
+ tableLoading.value = false;
+ }
+};
+
+// ------------------ 鍙戣揣锛堟殏鐢ㄥ亣鎻愪氦锛岀瓑鍚庣鎺ュ彛锛� ------------------
+const deliveryVisible = ref(false);
+const deliverySubmitting = ref(false);
+const deliveryFormRef = ref();
+const deliveryForm = reactive({
+ orderId: undefined,
+ orderCode: "",
+ carrierName: "",
+ shipTime: "",
+ vehicleNo: "",
+ driverName: "",
+ driverPhone: "",
+ remark: "",
+});
+
+const deliveryRules = {
+ shipTime: [{ required: true, message: "璇烽�夋嫨鍙戣揣鏃堕棿", trigger: "change" }],
+ vehicleNo: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿", trigger: "blur" }],
+};
+
+const openDelivery = (row) => {
+ console.log(row)
+ Object.assign(deliveryForm, {
+ orderId: row.id || row.orderId,
+ orderCode: row.orderCode,
+ carrierName: row.carrierName,
+ shipTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+ vehicleNo: "",
+ driverName: "",
+ driverPhone: "",
+ remark: "",
+ });
+
+ deliveryVisible.value = true;
+ queueMicrotask(() => deliveryFormRef.value?.clearValidate?.());
+};
+
+const submitDelivery = async () => {
+ deliverySubmitting.value = true;
+ try {
+ await deliveryFormRef.value?.validate?.();
+ console.log(deliveryForm)
+ let res = await addDeliveryTrack(deliveryForm)
+ if(res?.data){
+ ElMessage.success("鎻愪氦鎴愬姛");
+ }
+
+ deliveryVisible.value = false;
+ await getList();
+ } catch (e) {
+ ElMessage.error(e?.message || e?.msg || "鎻愪氦澶辫触");
+ } finally {
+ deliverySubmitting.value = false;
+ }
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
new file mode 100644
index 0000000..6290804
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
@@ -0,0 +1,196 @@
+<template>
+ <el-form :model="form">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="閲囪喘鍚堝悓鍙凤細">
+ <el-tag size="large">{{ form.purchaseContractNumber }}</el-tag>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="閿�鍞悎鍚屽彿锛�">
+ <el-text>{{ form.salesContractNo }}</el-text>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚◣鍗曚环(鍏�)锛�">
+ <el-text type="primary">{{ form.taxInclusiveUnitPrice }}</el-text>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍒涘缓鏃堕棿锛�">
+ <el-text>{{ form.createdAt }}</el-text>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍙戠エ鍙凤細">
+ <el-input v-model="form.invoiceNumber" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏉ョエ鏁帮細">
+ <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsNum" @change="inputTicketsNum" :precision="2"/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈鏉ョエ閲戦(鍏�)锛�">
+ <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsAmount" @change="inputTicketsAmount" :precision="2"/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈潵绁ㄦ暟锛�">
+ <el-text type="success">{{ form.futureTickets }}</el-text>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+</template>
+
+<script setup>
+import useFormData from "@/hooks/useFormData";
+import { getProductRecordById } from "@/api/procurementManagement/procurementInvoiceLedger";
+const { proxy } = getCurrentInstance()
+
+defineOptions({
+ name: "鏉ョエ鍙拌处琛ㄥ崟",
+});
+const temFutureTickets = ref(0) // 鍒濆鏈潵绁ㄦ暟
+const initialTicketsNum = ref(0) // 鍒濆鏉ョエ鏁�
+const initialTicketsAmount = ref(0) // 鍒濆鏉ョエ閲戦
+const quantity = ref(0) // 鎬绘暟閲�
+const { form, resetForm } = useFormData({
+ id: undefined,
+ purchaseContractNumber: undefined, // 閲囪喘鍚堝悓鍙�
+ salesContractNo: undefined, // 閿�鍞悎鍚屽彿
+ createdAt: undefined, // 鍒涘缓鏃堕棿
+ invoiceNumber: undefined, // 鍙戠エ鍙�
+ ticketsNum: undefined, // 鏉ョエ鏁�
+ ticketsAmount: undefined, // 鏉ョエ閲戦
+ taxInclusiveUnitPrice: undefined, // 鍚◣鍗曚环
+ ticketRegistrationId: undefined, // 鍚◣鍗曚环
+});
+
+const load = async (id) => {
+ const { code, data } = await getProductRecordById({ id });
+ if (code === 200) {
+ form.id = data.id;
+ form.purchaseContractNumber = data.purchaseContractNumber;
+ form.salesContractNo = data.salesContractNo;
+ form.createdAt = data.createdAt;
+ form.invoiceNumber = data.invoiceNumber;
+ form.ticketsNum = data.ticketsNum;
+ form.ticketsAmount = data.ticketsAmount ? Number(data.ticketsAmount).toFixed(2) : 0;
+ form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice;
+ form.futureTickets = data.futureTickets;
+ temFutureTickets.value = data.futureTickets;
+ initialTicketsNum.value = data.ticketsNum || 0;
+ initialTicketsAmount.value = data.ticketsAmount || 0;
+ form.ticketRegistrationId = data.ticketRegistrationId;
+ // 鑾峰彇鎬绘暟閲忥紝濡傛灉鏁版嵁涓湁 quantity 瀛楁鍒欎娇鐢紝鍚﹀垯浣跨敤鏉ョエ鏁�+鏈潵绁ㄦ暟
+ quantity.value = data.quantity || (Number(data.ticketsNum || 0) + Number(data.futureTickets || 0));
+ }
+};
+
+const inputTicketsNum = (val) => {
+ // 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+ if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) {
+ proxy.$modal.msgWarning("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+ return;
+ }
+
+ const newTicketsNum = Number(form.ticketsNum) || 0;
+ const currentTicketsNum = Number(initialTicketsNum.value) || 0;
+
+ // 璁$畻鏂板鐨勬潵绁ㄦ暟
+ const addedTicketsNum = newTicketsNum - currentTicketsNum;
+
+ // 璁$畻鏂扮殑鏈潵绁ㄦ暟 = 鍒濆鏈潵绁ㄦ暟 - 鏂板鐨勬潵绁ㄦ暟
+ const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
+
+ // 楠岃瘉锛氭柊鐨勬潵绁ㄦ暟 + 鏂扮殑鏈潵绁ㄦ暟 鈮� quantity
+ if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
+ proxy.$modal.msgWarning(`鏉ョエ鏁�+鏈潵绁ㄦ暟涓嶈兘澶т簬鎬绘暟閲�(${quantity.value})`);
+ // 闄愬埗鏉ョエ鏁帮紝浣垮叾婊¤冻锛氭潵绁ㄦ暟 + 鏈潵绁ㄦ暟 鈮� quantity
+ // 鏈�澶ф潵绁ㄦ暟 = quantity - 鍒濆鏈潵绁ㄦ暟 + 鍒濆鏉ョエ鏁�
+ const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
+ form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
+ // 閲嶆柊璁$畻
+ const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
+ const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
+ form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
+ const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice);
+ form.ticketsAmount = Number(ticketsAmount.toFixed(2));
+ return;
+ }
+
+ // 妫�鏌ユ柊澧炵殑鏉ョエ鏁版槸鍚﹀ぇ浜庡垵濮嬫湭鏉ョエ鏁�
+ if (addedTicketsNum > Number(temFutureTickets.value)) {
+ proxy.$modal.msgWarning("鏂板寮�绁ㄦ暟涓嶅緱澶т簬鏈紑绁ㄦ暟");
+ form.ticketsNum = Number(initialTicketsNum.value) + Number(temFutureTickets.value);
+ }
+
+ // 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+ const finalTicketsNum = Number(form.ticketsNum) || 0;
+ const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
+ const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
+ const ticketsAmount = finalTicketsNum * Number(form.taxInclusiveUnitPrice);
+ form.futureTickets = Number(finalFutureTickets.toFixed(2));
+ form.ticketsAmount = Number(ticketsAmount.toFixed(2));
+};
+const inputTicketsAmount = (val) => {
+ // 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+ if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) {
+ proxy.$modal.msgWarning("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+ return;
+ }
+
+ const newTicketsAmount = Number(val) || 0;
+
+ // 璁$畻鏂扮殑鏉ョエ鏁�
+ const newTicketsNum = newTicketsAmount / Number(form.taxInclusiveUnitPrice);
+ const currentTicketsNum = Number(initialTicketsNum.value) || 0;
+
+ // 璁$畻鏂板鐨勬潵绁ㄦ暟
+ const addedTicketsNum = newTicketsNum - currentTicketsNum;
+
+ // 璁$畻鏂扮殑鏈潵绁ㄦ暟 = 鍒濆鏈潵绁ㄦ暟 - 鏂板鐨勬潵绁ㄦ暟
+ const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
+
+ // 楠岃瘉锛氭柊鐨勬潵绁ㄦ暟 + 鏂扮殑鏈潵绁ㄦ暟 鈮� quantity
+ if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
+ proxy.$modal.msgWarning(`鏉ョエ鏁�+鏈潵绁ㄦ暟涓嶈兘澶т簬鎬绘暟閲�(${quantity.value})`);
+ // 闄愬埗鏉ョエ鏁帮紝浣垮叾婊¤冻锛氭潵绁ㄦ暟 + 鏈潵绁ㄦ暟 鈮� quantity
+ const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
+ form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
+ form.ticketsAmount = Number((form.ticketsNum * Number(form.taxInclusiveUnitPrice)).toFixed(2));
+ const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
+ const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
+ form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
+ return;
+ }
+
+ // 妫�鏌ユ柊澧炵殑鏉ョエ閲戦鏄惁澶т簬鍒濆鏈潵绁ㄦ暟瀵瑰簲鐨勯噾棰�
+ const maxAddedAmount = Number(temFutureTickets.value * form.taxInclusiveUnitPrice);
+ if (addedTicketsNum > 0 && addedTicketsNum * Number(form.taxInclusiveUnitPrice) > maxAddedAmount) {
+ proxy.$modal.msgWarning("鏂板鏉ョエ閲戦涓嶅緱澶т簬鏈紑绁ㄩ噾棰�");
+ form.ticketsAmount = Number((initialTicketsAmount.value + maxAddedAmount).toFixed(2));
+ form.ticketsNum = Number((currentTicketsNum + Number(temFutureTickets.value)).toFixed(2));
+ form.futureTickets = 0;
+ return;
+ }
+
+ // 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+ const finalTicketsNum = Number(newTicketsNum.toFixed(2));
+ const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
+ const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
+ form.ticketsNum = finalTicketsNum;
+ form.futureTickets = Number(finalFutureTickets.toFixed(2));
+};
+
+defineExpose({
+ load,
+ form,
+ resetForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue
new file mode 100644
index 0000000..82b4164
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue
@@ -0,0 +1,62 @@
+<template>
+ <el-dialog :title="modalOptions.title" v-model="visible" @close="close">
+ <EditForm ref="editFormRef" />
+ <template #footer>
+ <el-button type="primary" :loading="loading" @click="sendForm">
+ {{ modalOptions.confirmText }}
+ </el-button>
+ <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "@/hooks/useModal";
+import EditForm from "../Form/EditForm.vue";
+import { updateRegistration } from "@/api/procurementManagement/procurementInvoiceLedger";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+ name: "鏉ョエ鍙拌处缂栬緫",
+});
+const emits = defineEmits(["success"]);
+
+const saleLedgerProjectId = ref('')
+const editFormRef = ref();
+const {
+ id,
+ visible,
+ loading,
+ openModal,
+ modalOptions,
+ handleConfirm,
+ closeModal,
+} = useModal({ title: "鏉ョエ鍙拌处" });
+
+const open = async (row) => {
+ openModal(row.id);
+ saleLedgerProjectId.value = row.saleLedgerProjectId;
+ await nextTick();
+ editFormRef.value.load(row.id);
+};
+
+const close = () => {
+ editFormRef.value.resetForm();
+ closeModal();
+};
+
+const sendForm = async () => {
+ const form = editFormRef.value.form;
+ form.saleLedgerProjectId = saleLedgerProjectId.value;
+ const { code } = await updateRegistration(form);
+ if (code === 200) {
+ emits("success");
+ ElMessage({ message: "鎿嶄綔鎴愬姛", type: "success" });
+ close();
+ }
+};
+
+defineExpose({
+ open,
+});
+</script>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue
new file mode 100644
index 0000000..b82bd10
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue
@@ -0,0 +1,87 @@
+<template>
+ <el-dialog v-model="upload.open" :title="upload.title" :width="500">
+ <FileUpload
+ ref="fileUploadRef"
+ accept=".xlsx, .xls, .pdf"
+ :headers="upload.headers"
+ :autoUpload="true"
+ :action="upload.url"
+ :disabled="upload.isUploading"
+ :showTip="false"
+ :limit="10"
+ @success="handleFileSuccess"
+ @remove="removeFile"
+ />
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+ <el-button @click="upload.open = false">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { reactive } from "vue";
+import { getToken } from "@/utils/auth.js";
+import { FileUpload } from "@/components/Upload";
+import { ElMessage } from "element-plus";
+import { ref } from "vue";
+import useFormData from "@/hooks/useFormData";
+
+defineOptions({
+ name: "鏉ョエ鍙拌处闄勪欢琛ュ厖",
+});
+
+const { form, resetForm } = useFormData({
+ id: undefined,
+ tempFileIds: [],
+});
+const emits = defineEmits(["uploadSuccess"]);
+const fileUploadRef = ref();
+const upload = reactive({
+ // 鏄惁鏄剧ず寮瑰嚭灞傦紙渚涘簲鍟嗗鍏ワ級
+ open: false,
+ // 寮瑰嚭灞傛爣棰橈紙渚涘簲鍟嗗鍏ワ級
+ title: "",
+ // 鏄惁绂佺敤涓婁紶
+ isUploading: false,
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+});
+// 鐐瑰嚮瀵煎叆
+const handleImport = (id) => {
+ form.id = id;
+ upload.open = true;
+ upload.title = "鏉ョエ鍙拌处闄勪欢琛ュ厖";
+};
+
+const submitFileForm = () => {
+ emits("uploadSuccess", form);
+ resetForm();
+ upload.open = false;
+ // 娓呯┖鏂囦欢鍒楄〃
+ fileUploadRef.value.fileList = [];
+};
+
+const handleFileSuccess = (response) => {
+ if (response.code == 200) {
+ form.tempFileIds.push(response.data.tempId);
+ console.log('form',form);
+ ElMessage({ message: "瀵煎叆鎴愬姛", type: "success" });
+ } else {
+ ElMessage({ message: response.msg, type: "error" });
+ }
+};
+
+const removeFile = (file) => {
+ const { tempId } = file.response.data;
+ form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
+};
+
+defineExpose({
+ handleImport,
+});
+</script>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue
new file mode 100644
index 0000000..8ad8799
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue
@@ -0,0 +1,372 @@
+<template>
+ <div class="app-container">
+ <el-form :model="filters" :inline="true">
+ <el-form-item label="閲囪喘鍚堝悓鍙�">
+ <el-input
+ v-model="filters.purchaseContractNumber"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item label="渚涘簲鍟�">
+ <el-input
+ v-model="filters.supplierName"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item label="鏉ョエ鏃ユ湡">
+ <el-date-picker
+ style="width: 240px"
+ v-model="filters.createdAt"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ start-placeholder="寮�濮嬫椂闂�"
+ end-placeholder="缁撴潫鏃堕棿"
+ clearable
+ @change="getTableData"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+ <el-button @click="resetFilters"> 閲嶇疆 </el-button>
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ </el-form-item>
+ </el-form>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="columns"
+ :tableLoading="loading"
+ :tableData="dataList"
+ :isSelection="true"
+ height="calc(100vh - 19.5em)"
+ :isShowSummary="true"
+ :summaryMethod="summarizeMainTable"
+ :page="{
+ current: pagination.currentPage,
+ size: pagination.pageSize,
+ total: pagination.total,
+ }"
+ @selection-change="handleSelectionChange"
+ @pagination="changePage"
+ >
+ <template #commonFilesRef="{ row }">
+ <el-dropdown @command="(command) => handleCommand(command, row)">
+ <el-button link :icon="Files" type="danger"> 闄勪欢 </el-button>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <el-dropdown-item
+ v-if="row.commonFiles.length !== 0"
+ :icon="Download"
+ command="download"
+ >
+ 涓嬭浇
+ </el-dropdown-item>
+ <el-dropdown-item :icon="Upload" command="upload">
+ 涓婁紶
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ </template>
+ <template #operation="{ row }">
+ <el-button
+ type="primary"
+ text
+ @click="openEdit(row)"
+ :disabled="row.issUerId !== userStore.id"
+ >
+ 缂栬緫
+ </el-button>
+ <el-button
+ type="primary"
+ text
+ :disabled="row.issUerId !== userStore.id"
+ @click="handleDelete(row)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </PIMTable>
+ </div>
+ <UploadModal ref="modalRef" @uploadSuccess="uploadSuccess"></UploadModal>
+ <EditModal ref="editmodalRef" @success="getTableData"></EditModal>
+ </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import { usePaginationApi } from "@/hooks/usePaginationApi";
+import {
+ Files,
+ Download,
+ Search,
+ Upload,
+ EditPen,
+} from "@element-plus/icons-vue";
+import {
+ delRegistration,
+ productRecordPage,
+ productUploadFile,
+} from "@/api/procurementManagement/procurementInvoiceLedger.js";
+import { onMounted } from "vue";
+import { ElMessageBox } from "element-plus";
+import UploadModal from "./Modal/UploadModal.vue";
+import EditModal from "./Modal/EditModal.vue";
+import useUserStore from "@/store/modules/user.js";
+import {delInvoiceLedgerByRegProductId} from "@/api/salesManagement/invoiceLedger.js";
+const userStore = useUserStore();
+
+defineOptions({
+ name: "鏉ョエ鍙拌处",
+});
+
+const modalRef = ref();
+const editmodalRef = ref();
+
+const { proxy } = getCurrentInstance();
+const multipleVal = ref([]);
+const {
+ loading,
+ filters,
+ columns,
+ dataList,
+ pagination,
+ getTableData,
+ resetFilters,
+ onCurrentChange,
+} = usePaginationApi(
+ productRecordPage,
+ {
+ purchaseContractNumber: undefined, // 閲囪喘鍚堝悓鍙�
+ supplierName: undefined, // 渚涘簲鍟�
+ createdAt: [], // 鏉ョエ鏃ユ湡
+ },
+ [
+ {
+ label: "閲囪喘鍚堝悓鍙�",
+ prop: "purchaseContractNumber",
+ width: 150,
+ },
+ {
+ label: "閿�鍞悎鍚屽彿",
+ prop: "salesContractNo",
+ width: 150,
+ },
+ {
+ label: "椤圭洰鍚嶇О",
+ prop: "projectName",
+ width: 240,
+ },
+ {
+ label: "渚涘簲鍟嗗悕绉�",
+ prop: "supplierName",
+ width: 240,
+ },
+ {
+ label: "浜у搧澶х被",
+ prop: "productCategory",
+ width: 150,
+ },
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "specificationModel",
+ width: 150,
+ },
+ {
+ label: "鍙戠エ鍙�",
+ prop: "invoiceNumber",
+ width: 200,
+ },
+ {
+ label: "鍚堝悓閲戦(鍏�)",
+ prop: "taxInclusiveTotalPrice",
+ width: 200,
+ formatData: (cell) => {
+ return cell ? parseFloat(cell).toFixed(2) : 0;
+ },
+ },
+ {
+ label: "鏈鏉ョエ鏁�",
+ prop: "ticketsNum",
+ width: 110,
+ },
+ {
+ label: "鏉ョエ鏃ユ湡",
+ prop: "createdAt",
+ width: 110,
+ },
+ {
+ label: "鏉ョエ閲戦(鍏�)",
+ prop: "ticketsAmount",
+ width: 200,
+ formatData: (cell) => {
+ return cell ? parseFloat(cell).toFixed(2) : 0;
+ },
+ },
+ {
+ label: "涓嶅惈绋庨噾棰�",
+ prop: "unTicketsPrice",
+ width: 200,
+ formatData: (cell) => {
+ return cell ? parseFloat(cell).toFixed(2) : 0;
+ },
+ },
+ {
+ label: "澧炲�肩◣",
+ prop: "invoiceAmount",
+ width: 200,
+ },
+ {
+ label: "褰曞叆浜�",
+ prop: "issUer",
+ width: 200,
+ },
+ {
+ label: "闄勪欢",
+ align: "center",
+ prop: "commonFiles",
+ dataType: "slot",
+ fixed: "right",
+ slot: "commonFilesRef",
+ width: 120,
+ },
+ {
+ fixed: "right",
+ width: 150,
+ label: "鎿嶄綔",
+ dataType: "slot",
+ slot: "operation",
+ align: "center",
+ },
+ ],
+ {},
+ {
+ createdAt: (aim) => ({
+ createdAtStart: aim ? aim[0] : undefined,
+ createdAtEnd: aim ? aim[1] : undefined,
+ }),
+ }
+);
+
+// 涓昏〃鍚堣鏂规硶
+const summarizeMainTable = (param) => {
+ return proxy.summarizeTable(
+ param,
+ [
+ "taxInclusiveTotalPrice",
+ "ticketsAmount",
+ "unTicketsPrice",
+ "invoiceAmount",
+ ],
+ {
+ ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ }
+ );
+};
+
+const handleSelectionChange = (val) => {
+ multipleVal.value = val;
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/purchase/registration/export", {}, "鏉ョエ鐧昏.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+
+const handleFiles = (fileList) => {
+ fileList.forEach((e) => {
+ proxy.$download.name(e.url);
+ });
+};
+
+const changePage = ({ page, limit }) => {
+ pagination.currentPage = page;
+ pagination.pageSize = limit;
+ onCurrentChange(page);
+};
+
+const handleCommand = (command, row) => {
+ switch (command) {
+ case "download":
+ handleFiles(row.commonFiles);
+ break;
+ case "upload":
+ console.log(row.commonFiles);
+ openUoload(row.ticketRegistrationId);
+ break;
+ }
+};
+
+const openUoload = (id) => {
+ modalRef.value.handleImport(id);
+};
+
+const openEdit = (row) => {
+ editmodalRef.value.open(row);
+};
+
+// 涓婁紶鎴愬姛鍚庡仛浠�涔�
+const uploadSuccess = async (data) => {
+ const { code } = await productUploadFile({
+ ticketRegistrationId: data.id,
+ tempFileIds: data.tempFileIds,
+ });
+ if (code === 200) {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ getTableData();
+ }
+};
+// 鍒犻櫎
+const handleDelete = (row) => {
+ let ids = [];
+ ids.push(row.id);
+ ElMessageBox.confirm("璇ュ紑绁ㄥ彴璐﹀皢琚垹闄�,鏄惁纭鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ loading.value = true;
+ delRegistration(ids).then((res) => {
+ getTableData();
+ });
+ loading.value = false;
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+
+onMounted(() => {
+ getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+ margin-top: unset;
+}
+.tagBox {
+ margin-top: 4px;
+}
+</style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue
new file mode 100644
index 0000000..af38ab1
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue
@@ -0,0 +1,313 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <span class="search_title">閲囪喘鍚堝悓鍙凤細</span>
+ <el-input
+ v-model="searchForm.purchaseContractNumber"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <span class="search_title" style="margin-left: 10px">渚涘簲鍟嗭細</span>
+ <el-input
+ v-model="searchForm.supplierName"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <span class="search_title" style="margin-left: 10px">鏉ョエ鏃ユ湡锛�</span>
+ <el-date-picker
+ style="width: 240px"
+ v-model="searchForm.issueDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ start-placeholder="寮�濮嬫椂闂�"
+ end-placeholder="缁撴潫鏃堕棿"
+ clearable
+ @change="changeDateRange"
+ @clear="clearRange"
+ />
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+ >鎼滅储</el-button
+ >
+ </div>
+ <div>
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ :expand-row-keys="expandedRowKeys"
+ :row-key="(row) => row.id"
+ show-summary
+ :summary-method="summarizeMainTable"
+ @expand-change="expandChange"
+ height="calc(100vh - 18.5em)"
+ stripe
+ >
+ <el-table-column align="center" label="搴忓彿" type="index" width="55" />
+ <el-table-column type="expand">
+ <template #default="props">
+ <el-table
+ :data="props.row.children"
+ border
+ show-summary
+ :summary-method="summarizeChildrenTable"
+ stripe
+ >
+ <el-table-column
+ align="center"
+ label="搴忓彿"
+ type="index"
+ width="60"
+ />
+ <el-table-column label="浜у搧澶х被" prop="productCategory" />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+ <el-table-column label="鍗曚綅" prop="unit" />
+ <el-table-column label="鏁伴噺" prop="quantity" />
+ <el-table-column label="绋庣巼(%)" prop="taxRate" />
+ <el-table-column
+ label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ :formatter="formattedNumber"
+ />
+ <el-table-column
+ label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ :formatter="formattedNumber"
+ />
+ <el-table-column
+ label="涓嶅惈绋庢�讳环(鍏�)"
+ prop="taxExclusiveTotalPrice"
+ :formatter="formattedNumber"
+ />
+ <el-table-column label="鏈鏉ョエ鏁�" prop="ticketsNum" />
+ <el-table-column
+ label="鏈鏉ョエ閲戦(鍏�)"
+ prop="ticketsAmount"
+ :formatter="formattedNumber"
+ />
+ <el-table-column label="鏈潵绁ㄦ暟" prop="futureTickets" />
+ <el-table-column
+ label="鏈潵绁ㄩ噾棰�(鍏�)"
+ prop="futureTicketsAmount"
+ :formatter="formattedNumber"
+ />
+ </el-table>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="閲囪喘鍚堝悓鍙�"
+ prop="purchaseContractNumber"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="閿�鍞悎鍚屽彿"
+ prop="salesContractNo"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="渚涘簲鍟嗗悕绉�"
+ prop="supplierName"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍙戠エ鍙�"
+ prop="invoiceNumber"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍚堝悓閲戦(鍏�)"
+ prop="invoiceAmount"
+ show-overflow-tooltip
+ :formatter="formattedNumber"
+ />
+ <el-table-column label="寮�绁ㄤ汉" prop="issUer" show-overflow-tooltip />
+ <el-table-column
+ label="寮�绁ㄦ棩鏈�"
+ prop="issueDate"
+ show-overflow-tooltip
+ />
+ </el-table>
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import pagination from "@/components/PIMTable/Pagination.vue";
+import { ref } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessageBox } from "element-plus";
+import {
+ invoiceListPage,
+ productRecordList,
+} from "@/api/procurementManagement/procurementInvoiceLedger.js";
+import dayjs from "dayjs";
+
+const { proxy } = getCurrentInstance();
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+ current: 1,
+ size: 100,
+});
+const total = ref(0);
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const data = reactive({
+ searchForm: {
+ purchaseContractNumber: "",
+ supplierName: "",
+ issueDate: [
+ dayjs().startOf("month").format("YYYY-MM-DD"),
+ dayjs().endOf("month").format("YYYY-MM-DD"),
+ ],
+ issueDateStart: dayjs().startOf("month").format("YYYY-MM-DD"),
+ issueDateEnd: dayjs().endOf("month").format("YYYY-MM-DD"),
+ },
+ form: {
+ issueDate: "", // 寮�绁ㄦ棩鏈�
+ purchaseLedgerId: "",
+ purchaseLedgerNo: "",
+ issUerId: "", // 寮�绁ㄤ汉id
+ issUer: "", // 寮�绁ㄤ汉濮撳悕
+ },
+ rules: {
+ purchaseLedgerId: [
+ { required: true, message: "璇烽�夋嫨", trigger: "change" },
+ ],
+ },
+});
+const { searchForm } = toRefs(data);
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+const paginationChange = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+const getList = () => {
+ tableLoading.value = true;
+ const { issueDate, ...rest } = searchForm.value;
+ invoiceListPage({ ...rest, ...page })
+ .then((res) => {
+ tableLoading.value = false;
+ tableData.value = res.records;
+ tableData.value.map((item) => {
+ item.children = [];
+ });
+ total.value = res.total;
+ expandedRowKeys.value = [];
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+};
+const formattedNumber = (row, column, cellValue) => {
+ return parseFloat(cellValue).toFixed(2);
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const expandedRowKeys = ref([]);
+// 灞曞紑琛�
+const expandChange = (row, expandedRows) => {
+ if (expandedRows.length > 0) {
+ expandedRowKeys.value = [];
+ try {
+ productRecordList({ id: row.id }).then((res) => {
+ const index = tableData.value.findIndex((item) => item.id === row.id);
+ if (index > -1) {
+ tableData.value[index].children = res;
+ }
+ expandedRowKeys.value.push(row.id);
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ } else {
+ expandedRowKeys.value = [];
+ }
+};
+// 涓昏〃鍚堣鏂规硶
+const summarizeMainTable = (param) => {
+ return proxy.summarizeTable(param, ["invoiceAmount"], {
+ ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ });
+};
+// 瀛愯〃鍚堣鏂规硶
+const summarizeChildrenTable = (param) => {
+ return proxy.summarizeTable(
+ param,
+ [
+ "taxInclusiveUnitPrice",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ "ticketsNum",
+ "ticketsAmount",
+ "futureTickets",
+ "futureTicketsAmount",
+ ],
+ {
+ ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ }
+ );
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/purchase/registration/export", {}, "鏉ョエ鐧昏.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+
+const changeDateRange = (date) => {
+ if (date) {
+ searchForm.value.receiptPaymentDateStart = date[0];
+ searchForm.value.receiptPaymentDateEnd = date[1];
+ getList();
+ }
+};
+
+const clearRange = () => {
+ searchForm.value.issueDate = [];
+ searchForm.value.issueDateStart = undefined;
+ searchForm.value.issueDateEnd = undefined;
+ getList();
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue b/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue
new file mode 100644
index 0000000..fb392c5
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue
@@ -0,0 +1,67 @@
+<template>
+ <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
+ <el-table :data="tableData" border height="40vh">
+ <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
+ <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+ <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
+ <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-dialog>
+ <filePreview ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
+import filePreview from '@/components/filePreview/index.vue'
+import { delCommonFile } from '@/api/publicApi/commonFile.js'
+
+const dialogVisible = ref(false)
+const tableData = ref([])
+const { proxy } = getCurrentInstance();
+const filePreviewRef = ref()
+const handleClose = () => {
+ dialogVisible.value = false
+}
+const open = (list) => {
+ dialogVisible.value = true
+ tableData.value = list
+}
+const downLoadFile = (row) => {
+ proxy.$download.name(row.url);
+
+}
+const lookFile = (row) => {
+ filePreviewRef.value.open(row.url)
+}
+// 鍒犻櫎闄勪欢
+const handleDelete = (row) => {
+ ElMessageBox.confirm(`纭鍒犻櫎闄勪欢"${row.name}"鍚楋紵`, '鍒犻櫎纭', {
+ confirmButtonText: '纭',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning',
+ }).then(() => {
+ delCommonFile([row.id]).then(() => {
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ // 浠庡垪琛ㄤ腑绉婚櫎宸插垹闄ょ殑闄勪欢
+ const index = tableData.value.findIndex(item => item.id === row.id)
+ if (index !== -1) {
+ tableData.value.splice(index, 1)
+ }
+ }).catch(() => {
+ ElMessage.error('鍒犻櫎澶辫触')
+ })
+ }).catch(() => {
+ proxy.$modal.msg('宸插彇娑堝垹闄�')
+ })
+}
+defineExpose({
+ open
+})
+</script>
+
+<style></style>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue b/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue
new file mode 100644
index 0000000..21a3fc5
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue
@@ -0,0 +1,528 @@
+<template>
+ <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"
+ />
+ <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="info" plain icon="Upload" @click="handleImport"
+ >瀵煎叆</el-button
+ >
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ ></PIMTable>
+ </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
+ v-model="form.supplierName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item
+ label="绾崇◣浜鸿瘑鍒彿锛�"
+ prop="taxpayerIdentificationNum"
+ >
+ <el-input
+ v-model="form.taxpayerIdentificationNum"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍏徃鍦板潃锛�" prop="companyAddress">
+ <el-input
+ v-model="form.companyAddress"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍏徃鐢佃瘽锛�" prop="companyPhone">
+ <el-input
+ v-model="form.companyPhone"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="寮�鎴疯锛�" prop="bankAccountName">
+ <el-input
+ v-model="form.bankAccountName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璐﹀彿锛�" prop="bankAccountNum">
+ <el-input
+ v-model="form.bankAccountNum"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴浜猴細" prop="contactUserName">
+ <el-input
+ v-model="form.contactUserName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactUserPhone">
+ <el-input
+ v-model="form.contactUserPhone"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="缁存姢浜猴細" prop="maintainUserId">
+ <el-select
+ v-model="form.maintainUserId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ disabled
+ >
+ <el-option
+ v-for="item in userList"
+ :key="item.nickName"
+ :label="item.nickName"
+ :value="item.userId"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="缁存姢鏃堕棿锛�" prop="maintainTime">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.maintainTime"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable
+ disabled
+ />
+ </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>
+
+ <!-- 鎵胯繍鍟嗗鍏ュ璇濇 -->
+ <el-dialog
+ :title="upload.title"
+ v-model="upload.open"
+ width="400px"
+ append-to-body
+ >
+ <el-upload
+ ref="uploadRef"
+ :limit="1"
+ accept=".xlsx, .xls"
+ :headers="upload.headers"
+ :action="upload.url + '?updateSupport=' + upload.updateSupport"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :auto-upload="false"
+ drag
+ >
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <template #tip>
+ <div class="el-upload__tip text-center">
+ <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+ <!-- <el-link
+ type="primary"
+ :underline="false"
+ style="font-size: 12px; vertical-align: baseline"
+ @click="importTemplate"
+ >涓嬭浇妯℃澘</el-link
+ > -->
+ </div>
+ </template>
+ </el-upload>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+ <el-button @click="upload.open = false">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { delSupplier } from "@/api/basicData/supplierManageFile.js";
+import { ElMessageBox } from "element-plus";
+import { userListNoPage } from "@/api/system/user.js";
+import {
+ addSupplier,
+ getSupplier,
+ listSupplier,
+ updateSupplier,
+} from "@/api/basicData/supplierManageFile.js";
+import useUserStore from "@/store/modules/user";
+import { getToken } from "@/utils/auth.js";
+const { proxy } = getCurrentInstance();
+const userStore = useUserStore();
+
+const tableColumn = ref([
+ {
+ label: "鎵胯繍鍟嗗悕绉�",
+ prop: "supplierName",
+ width: 250,
+ },
+ {
+ label: "绾崇◣浜鸿瘑鍒彿",
+ prop: "taxpayerIdentificationNum",
+ width: 230,
+ },
+ {
+ label: "鍏徃鍦板潃",
+ prop: "companyAddress",
+ width: 220,
+ },
+ {
+ label: "鑱旂郴鏂瑰紡",
+ prop: "companyPhone",
+ width:150
+ },
+ {
+ label: "寮�鎴疯",
+ prop: "bankAccountName",
+ width: 220,
+ },
+ {
+ label: "璐﹀彿",
+ prop: "bankAccountNum",
+ width: 220,
+ },
+ {
+ label: "鑱旂郴浜�",
+ prop: "contactUserName",
+ },
+ {
+ label: "鑱旂郴鐢佃瘽",
+ prop: "contactUserPhone",
+ width: 150,
+ },
+ {
+ label: "缁存姢浜�",
+ prop: "maintainUserName",
+ },
+
+ {
+ label: "缁存姢鏃堕棿",
+ prop: "maintainTime",
+ width:100
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: 'right',
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openForm("edit", row);
+ },
+ disabled: (row) => {
+ return row.maintainUserName !== userStore.nickName
+ }
+ },
+ ],
+ },
+]);
+const tableData = ref([]);
+const selectedRows = ref([]);
+const userList = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0,
+});
+
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const operationType = ref("");
+const dialogFormVisible = ref(false);
+const data = reactive({
+ searchForm: {
+ supplierName: "",
+ },
+ form: {
+ supplierName: "",
+ taxpayerIdentificationNum: "",
+ companyAddress: "",
+ companyPhone: "",
+ bankAccountName: "",
+ bankAccountNum: "",
+ contactUserName: "",
+ contactUserPhone: "",
+ maintainUserId: "",
+ maintainTime: "",
+ },
+ rules: {
+ supplierName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxpayerIdentificationNum: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ bankAccountName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ bankAccountNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ contactUserName: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ contactUserPhone: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ maintainUserId: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+ maintainTime: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+});
+const { searchForm, form, rules } = toRefs(data);
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+const pagination = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+/** 鎻愪氦涓婁紶鏂囦欢 */
+function submitFileForm() {
+ console.log(upload.url + '?updateSupport=' + upload.updateSupport)
+ proxy.$refs["uploadRef"].submit();
+}
+const getList = () => {
+ tableLoading.value = true;
+ listSupplier({ ...searchForm.value, ...page }).then((res) => {
+ tableLoading.value = false;
+ tableData.value = res.data.records;
+ page.total = res.data.total;
+ });
+};
+const upload = reactive({
+ // 鏄惁鏄剧ず寮瑰嚭灞傦紙鎵胯繍鍟嗗鍏ワ級
+ open: false,
+ // 寮瑰嚭灞傛爣棰橈紙鎵胯繍鍟嗗鍏ワ級
+ title: "",
+ // 鏄惁绂佺敤涓婁紶
+ isUploading: false,
+ // 鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�
+ updateSupport: 1,
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
+});
+/** 瀵煎叆鎸夐挳鎿嶄綔 */
+function handleImport() {
+ upload.title = "鎵胯繍鍟嗗鍏�";
+ upload.open = true;
+}
+
+/**鏂囦欢涓婁紶涓鐞� */
+const handleFileUploadProgress = (event, file, fileList) => {
+ upload.isUploading = true;
+};
+
+/** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
+const handleFileSuccess = (response, file, fileList) => {
+ upload.open = false;
+ upload.isUploading = false;
+ proxy.$refs["uploadRef"].handleRemove(file);
+ getList();
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+};
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = {};
+ form.value.maintainUserId = userStore.id;
+ form.value.maintainTime = getCurrentDate();
+ userListNoPage().then((res) => {
+ userList.value = res.data;
+ });
+ if (type === "edit") {
+ getSupplier(row.id).then((res) => {
+ form.value = { ...res.data };
+ });
+ }
+ dialogFormVisible.value = true;
+};
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ proxy.$refs["formRef"].validate((valid) => {
+ if (valid) {
+ if (operationType.value === "edit") {
+ submitEdit();
+ } else {
+ submitAdd();
+ }
+ }
+ });
+};
+// 鎻愪氦鏂板
+const submitAdd = () => {
+ addSupplier(form.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ getList();
+ });
+};
+// 鎻愪氦淇敼
+const submitEdit = () => {
+ updateSupplier(form.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ getList();
+ });
+};
+// 鍏抽棴寮规
+const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+};
+// 瀵煎嚭
+const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/system/supplier/export", {}, "鎵胯繍鍟嗘。妗�.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+// 鍒犻櫎
+const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+ const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName);
+ if (unauthorizedData.length > 0) {
+ proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+ return;
+ }
+ ids = selectedRows.value.map((item) => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ delSupplier(ids)
+ .then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ 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>
--
Gitblit v1.9.3