From e2c871b1be0ff8cfa61e55325095ee1c79932ddd Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期五, 30 一月 2026 17:01:19 +0800
Subject: [PATCH] tms 开发承运商运费结算模块
---
src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue | 2
src/api/inventoryManagement/CarrierManagement.js | 49 ++++++
3 files changed, 471 insertions(+), 1 deletions(-)
diff --git a/src/api/inventoryManagement/CarrierManagement.js b/src/api/inventoryManagement/CarrierManagement.js
index 40ec3b8..180a579 100644
--- a/src/api/inventoryManagement/CarrierManagement.js
+++ b/src/api/inventoryManagement/CarrierManagement.js
@@ -153,4 +153,53 @@
url: `/fakeWarehousing/deliveryTrack/${ids}`,
method: "delete",
});
+}
+
+// 杩愯垂缁撶畻鍗曠鐞�
+// 鍒嗛〉鏌ヨ杩愯垂缁撶畻鍗曞垪琛�
+// /fakeWarehousing/freightSettlement/list
+export const getFreightSettlementPage = (params) => {
+ return request({
+ url: "/fakeWarehousing/freightSettlement/list",
+ method: "get",
+ params,
+ });
+}
+
+// 鏍规嵁缁撶畻ID鏌ヨ
+// /fakeWarehousing/freightSettlement/{id}
+export const getFreightSettlementDetail = (id) => {
+ return request({
+ url: `/fakeWarehousing/freightSettlement/${id}`,
+ method: "get",
+ });
+}
+
+// 鏂板杩愯垂缁撶畻鍗�
+// /fakeWarehousing/freightSettlement/
+export const addFreightSettlement = (data) => {
+ return request({
+ url: "/fakeWarehousing/freightSettlement",
+ method: "post",
+ data,
+ });
+}
+
+// 淇敼杩愯垂缁撶畻鍗�
+// /fakeWarehousing/freightSettlement/
+export const updateFreightSettlement = (data) => {
+ return request({
+ url: "/fakeWarehousing/freightSettlement",
+ method: "put",
+ data,
+ });
+}
+
+// 鍒犻櫎杩愯垂缁撶畻鍗�
+// /fakeWarehousing/freightSettlement/{ids}
+export const deleteFreightSettlement = (ids) => {
+ return request({
+ url: `/fakeWarehousing/freightSettlement/${ids}`,
+ method: "delete",
+ });
}
\ No newline at end of file
diff --git a/src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue b/src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue
new file mode 100644
index 0000000..54f685f
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/freightSettlement/index.vue
@@ -0,0 +1,421 @@
+<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.settleNo"
+ style="width: 240px"
+ placeholder="缁撶畻鍗曞彿"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ <span class="search_title">鎵胯繍鍟�:</span>
+ <el-input
+ v-model="searchForm.carrierName"
+ style="width: 240px"
+ placeholder="鎵胯繍鍟�"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ <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="settleNo" width="180" show-overflow-tooltip />
+ <el-table-column label="鎵胯繍鍟�" prop="carrierName" width="200" show-overflow-tooltip />
+ <el-table-column label="鍙戠エ鍙风爜" prop="invoiceNo" width="160" show-overflow-tooltip />
+ <el-table-column label="鍙戠エ閲戦(鍏�)" prop="invoiceAmt" width="140" align="right">
+ <template #default="{ row }">{{ toMoney(row.invoiceAmt) }}</template>
+ </el-table-column>
+ <el-table-column label="寮�绁ㄦ棩鏈�" prop="invoiceDate" width="120" />
+ <el-table-column label="鏇存柊鏃堕棿" prop="updateTime" width="170" />
+
+ <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
+ <template #default="scope">
+ <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="settleNo">
+ <el-input v-model="editForm.settleNo" placeholder="濡� FS-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="invoiceNo">
+ <el-input v-model="editForm.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍙戠エ閲戦(鍏�)" prop="invoiceAmt">
+ <el-input-number v-model="editForm.invoiceAmt" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="寮�绁ㄦ棩鏈�" prop="invoiceDate">
+ <el-date-picker
+ v-model="editForm.invoiceDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%"
+ />
+ </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.settleNo }}</el-descriptions-item>
+ <el-descriptions-item label="鎵胯繍鍟�">{{ viewRow.carrierName }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戠エ鍙风爜">{{ viewRow.invoiceNo }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戠エ閲戦(鍏�)">{{ toMoney(viewRow.invoiceAmt) }}</el-descriptions-item>
+ <el-descriptions-item label="寮�绁ㄦ棩鏈�">{{ viewRow.invoiceDate }}</el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">{{ viewRow.createTime }}</el-descriptions-item>
+ <el-descriptions-item label="鏇存柊鏃堕棿">{{ viewRow.updateTime }}</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 pagination from "@/components/PIMTable/Pagination.vue";
+import dayjs from "dayjs";
+import {
+ getFreightSettlementPage,
+ getFreightSettlementDetail,
+ addFreightSettlement,
+ updateFreightSettlement,
+ deleteFreightSettlement,
+} 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 normalizeRow = (raw = {}) => {
+ return {
+ id: raw.id,
+ settleNo: raw.settleNo,
+ carrierName: raw.carrierName,
+ invoiceNo: raw.invoiceNo,
+ invoiceAmt: raw.invoiceAmt,
+ invoiceDate: raw.invoiceDate,
+ createTime: raw.createTime,
+ updateTime: raw.updateTime,
+ };
+};
+
+// ------------------ 椤甸潰鐘舵�� ------------------
+const searchForm = reactive({
+ keyword: "",
+ invoiceDateRange: [],
+ invoiceDateStart: undefined,
+ invoiceDateEnd: undefined,
+});
+
+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,
+ settleNo: searchForm.settleNo || undefined,
+ carrierName: searchForm.carrierName || undefined,
+ };
+};
+
+const changeDateRange = (date) => {
+ if (date && date.length === 2) {
+ searchForm.invoiceDateStart = date[0];
+ searchForm.invoiceDateEnd = date[1];
+ } else {
+ searchForm.invoiceDateStart = undefined;
+ searchForm.invoiceDateEnd = undefined;
+ }
+ getList();
+};
+
+const clearRange = () => {
+ searchForm.invoiceDateRange = [];
+ getList();
+};
+
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+const resetQuery = () => {
+ searchForm.keyword = "";
+ searchForm.invoiceDateRange = [];
+ searchForm.invoiceDateStart = undefined;
+ searchForm.invoiceDateEnd = undefined;
+ page.current = 1;
+ getList();
+};
+
+const paginationSearch = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+const getList = async () => {
+ tableLoading.value = true;
+ try {
+ const res = await getFreightSettlementPage(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 editVisible = ref(false);
+const viewVisible = ref(false);
+const editMode = ref("create");
+const saving = ref(false);
+const editFormRef = ref();
+
+const editForm = reactive({
+ id: undefined,
+ settleNo: "",
+ carrierName: "",
+ invoiceNo: "",
+ invoiceAmt: 0,
+ invoiceDate: "",
+});
+
+const rules = {
+ settleNo: [{ required: true, message: "璇疯緭鍏ョ粨绠楀崟鍙�", trigger: "blur" }],
+ carrierName: [{ required: true, message: "璇疯緭鍏ユ壙杩愬晢鍚嶇О", trigger: "blur" }],
+ invoiceNo: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄥ彿鐮�", trigger: "blur" }],
+ invoiceAmt: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄩ噾棰�", trigger: "change" },
+ {validator: (rule, value, callback) => {if (value <= 0) {callback(new Error("鍙戠エ閲戦蹇呴』澶т簬0"));} else {callback();}}, trigger: "change",}],
+ invoiceDate: [{ required: true, message: "璇烽�夋嫨寮�绁ㄦ棩鏈�", trigger: "change" }],
+};
+
+const openCreate = () => {
+ editMode.value = "create";
+ Object.assign(editForm, {
+ id: undefined,
+ settleNo: `FS-${dayjs().format("YYYYMMDD")}-${String(Math.floor(Math.random() * 9000 + 1000))}`,
+ carrierName: "",
+ invoiceNo: "",
+ invoiceAmt: 0,
+ invoiceDate: dayjs().format("YYYY-MM-DD"),
+ });
+ 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 getFreightSettlementDetail(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 getFreightSettlementDetail(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,
+ settleNo: editForm.settleNo,
+ carrierName: editForm.carrierName,
+ invoiceNo: editForm.invoiceNo,
+ invoiceAmt: editForm.invoiceAmt,
+ invoiceDate: editForm.invoiceDate,
+ };
+
+ if (editMode.value === "create") {
+ await addFreightSettlement(payload);
+ ElMessage.success("鍒涘缓鎴愬姛");
+ page.current = 1;
+ } else {
+ await updateFreightSettlement(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.settleNo}銆戯紵`, "鎻愮ず", {
+ confirmButtonText: "鍒犻櫎",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+
+ tableLoading.value = true;
+ try {
+ await deleteFreightSettlement(row.id);
+ ElMessage.success("鍒犻櫎鎴愬姛");
+
+ // 鍒犻櫎鍚庡鏋滃綋鍓嶉〉鏃犳暟鎹垯鍥為��
+ const res = await getFreightSettlementPage(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;
+ }
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.el-pagination {
+ width: 100%;
+ height: 55px;
+ display: flex;
+ justify-content: flex-end;
+ float: right;
+ flex-direction: row;
+ align-items: center;
+ background: #fff;
+ margin: -20px 0 0 0;
+ padding: 0 20px;
+}
+
+.pagination-container {
+ margin-top: 0;
+}
+</style>
diff --git a/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
index 2e8f994..3da8125 100644
--- a/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
+++ b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
@@ -17,7 +17,7 @@
gap: 10px;
"
>
- <span class="search_title">鍏抽敭瀛�:</span>
+ <span class="search_title">鍏抽敭瀛楁煡璇�:</span>
<el-input
v-model="searchForm.keyword"
style="width: 240px"
--
Gitblit v1.9.3