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/paymentHistory/index.vue | 512 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 512 insertions(+), 0 deletions(-)
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>
--
Gitblit v1.9.3