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