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/DeliveryTrackingManagement/index.vue |  372 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 372 insertions(+), 0 deletions(-)

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>

--
Gitblit v1.9.3