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/paymentOrder/index.vue | 684 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 684 insertions(+), 0 deletions(-)
diff --git a/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
new file mode 100644
index 0000000..3da8125
--- /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>
--
Gitblit v1.9.3