From 960b6c627f93e1f475c2b30cdc0baa5fb5f8da36 Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期五, 30 一月 2026 15:27:55 +0800
Subject: [PATCH] tms 开发承运商管理模块 tms 开发承运合同模块 tms 开发承运订单模块 tms 开发承运跟踪管理模块

---
 src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue   |   62 +
 src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue |   87 +
 src/views/inventoryManagement/procurementManagement/DeliveryTrackingManagement/index.vue           |  372 ++++++
 src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue             |  372 ++++++
 src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue     |  196 +++
 src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue                         |  684 ++++++++++++
 src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue                 |   67 +
 src/views/inventoryManagement/procurementManagement/paymentHistory/index.vue                       |  512 +++++++++
 src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue          |  313 +++++
 src/api/inventoryManagement/CarrierManagement.js                                                   |  156 ++
 src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue                    |  528 +++++++++
 11 files changed, 3,349 insertions(+), 0 deletions(-)

diff --git a/src/api/inventoryManagement/CarrierManagement.js b/src/api/inventoryManagement/CarrierManagement.js
new file mode 100644
index 0000000..40ec3b8
--- /dev/null
+++ b/src/api/inventoryManagement/CarrierManagement.js
@@ -0,0 +1,156 @@
+import request from "@/utils/request";
+
+// 鏌ヨ鍏ュ簱淇℃伅鍒楄〃
+export const getStockInPage = (params) => {
+    return request({
+        url: "/stockin/listPage",
+        method: "get",
+        params,
+    });
+};
+
+// 鍒嗛〉鏌ヨ鎵胯繍鍚堝悓鍒楄〃
+// /fakeWarehousing/list
+export const getCarrierContractPage = (params) => {
+    return request({
+        url: "/fakeWarehousing/list",
+        method: "get",
+        params,
+    });
+};
+
+// 鏍规嵁ID鏌ヨ鎵胯繍鍚堝悓璇︽儏
+// /fakeWarehousing/{id}
+export const getCarrierContractDetail = (id) => {
+    return request({
+        url: `/fakeWarehousing/${id}`,
+        method: "get",
+    });
+};
+
+// 鏂板鎵胯繍鍚堝悓
+// /fakeWarehousing/
+export const addCarrierContract = (data) => {
+    return request({
+        url: "/fakeWarehousing",
+        method: "post",
+        data,
+    });
+};
+
+// 淇敼鎵胯繍鍚堝悓
+// /fakeWarehousing/
+export const updateCarrierContract = (data) => {
+    return request({
+        url: "/fakeWarehousing",
+        method: "put",
+        data,
+    });
+}
+
+// 鎵胯繍鍚堝悓绠$悊-鍒犻櫎ids
+// /fakeWarehousing/{ids}
+export const deleteCarrierContract = (ids) => {
+    return request({
+        url: `/fakeWarehousing/${ids}`,
+        method: "delete",
+    });
+};
+
+
+// 鍒嗛〉鏌ヨ鎵胯繍璁㈠崟鍒楄〃
+// /fakeWarehousing/order/list
+export const getCarrierOrderPage = (params) => {
+    return request({
+        url: "/fakeWarehousing/order/list",
+        method: "get",
+        params,
+    });
+};
+
+// 鏍规嵁ID鏌ヨ鎵胯繍璁㈠崟璇︽儏
+// /fakeWarehousing/order/{id}
+export const getCarrierOrderDetail = (id) => {
+    return request({
+        url: `/fakeWarehousing/order/${id}`,
+        method: "get",
+    });
+};
+
+// 鏂板鎵胯繍璁㈠崟
+// /fakeWarehousing/order/
+export const addCarrierOrder = (data) => {
+    return request({
+        url: "/fakeWarehousing/order",
+        method: "post",
+        data,
+    });
+};
+
+// 淇敼鎵胯繍璁㈠崟
+// /fakeWarehousing/order/
+export const updateCarrierOrder = (data) => {
+    return request({
+        url: "/fakeWarehousing/order",
+        method: "put",
+        data,
+    });
+}
+
+// 鍒犻櫎鎵胯繍璁㈠崟
+// /fakeWarehousing/order/{ids}
+export const deleteCarrierOrder = (ids) => {
+    return request({
+        url: `/fakeWarehousing/order/${ids}`,
+        method: "delete",
+    });
+};
+
+// 鏂板鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/
+export const addDeliveryTrack = (data) => {
+    return request({
+        url: "/fakeWarehousing/deliveryTrack",
+        method: "post",
+        data,
+    });
+}
+
+// 鍙戣揣璺熻釜绠$悊鎺ュ彛
+// 鍒嗛〉鏌ヨ鍙戣揣璺熻釜鍒楄〃
+// /fakeWarehousing/deliveryTrack/list
+export const getDeliveryTrackPage = (params) => {
+    return request({
+        url: "/fakeWarehousing/deliveryTrack/list",
+        method: "get",
+        params,
+    });
+}
+
+// 鏍规嵁ID鏌ヨ鍙戣揣璺熻釜璇︽儏
+// /fakeWarehousing/deliveryTrack/{id}
+export const getDeliveryTrackDetail = (id) => {
+    return request({
+        url: `/fakeWarehousing/deliveryTrack/${id}`,
+        method: "get",
+    });
+}
+
+// 淇敼鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/
+export const updateDeliveryTrack = (data) => {
+    return request({
+        url: "/fakeWarehousing/deliveryTrack",
+        method: "put",
+        data,
+    });
+}
+
+// 鍒犻櫎鍙戣揣璺熻釜璁板綍
+// /fakeWarehousing/deliveryTrack/{ids}
+export const deleteDeliveryTrack = (ids) => {
+    return request({
+        url: `/fakeWarehousing/deliveryTrack/${ids}`,
+        method: "delete",
+    });
+}
\ No newline at end of file
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>
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>
diff --git a/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue b/src/views/inventoryManagement/procurementManagement/paymentOrder/index.vue
new file mode 100644
index 0000000..2e8f994
--- /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>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
new file mode 100644
index 0000000..6290804
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
@@ -0,0 +1,196 @@
+<template>
+	<el-form :model="form">
+		<el-row :gutter="20">
+			<el-col :span="12">
+				<el-form-item label="閲囪喘鍚堝悓鍙凤細">
+					<el-tag size="large">{{ form.purchaseContractNumber }}</el-tag>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="閿�鍞悎鍚屽彿锛�">
+					<el-text>{{ form.salesContractNo }}</el-text>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鍚◣鍗曚环(鍏�)锛�">
+					<el-text type="primary">{{ form.taxInclusiveUnitPrice }}</el-text>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鍒涘缓鏃堕棿锛�">
+					<el-text>{{ form.createdAt }}</el-text>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鍙戠エ鍙凤細">
+					<el-input v-model="form.invoiceNumber" />
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鏉ョエ鏁帮細">
+					<el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsNum" @change="inputTicketsNum" :precision="2"/>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鏈鏉ョエ閲戦(鍏�)锛�">
+					<el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsAmount" @change="inputTicketsAmount" :precision="2"/>
+				</el-form-item>
+			</el-col>
+			<el-col :span="12">
+				<el-form-item label="鏈潵绁ㄦ暟锛�">
+					<el-text type="success">{{ form.futureTickets }}</el-text>
+				</el-form-item>
+			</el-col>
+		</el-row>
+	</el-form>
+</template>
+
+<script setup>
+import useFormData from "@/hooks/useFormData";
+import { getProductRecordById } from "@/api/procurementManagement/procurementInvoiceLedger";
+const { proxy } = getCurrentInstance()
+
+defineOptions({
+	name: "鏉ョエ鍙拌处琛ㄥ崟",
+});
+const temFutureTickets = ref(0) // 鍒濆鏈潵绁ㄦ暟
+const initialTicketsNum = ref(0) // 鍒濆鏉ョエ鏁�
+const initialTicketsAmount = ref(0) // 鍒濆鏉ョエ閲戦
+const quantity = ref(0) // 鎬绘暟閲�
+const { form, resetForm } = useFormData({
+	id: undefined,
+	purchaseContractNumber: undefined, // 閲囪喘鍚堝悓鍙�
+	salesContractNo: undefined, // 閿�鍞悎鍚屽彿
+	createdAt: undefined, // 鍒涘缓鏃堕棿
+	invoiceNumber: undefined, // 鍙戠エ鍙�
+	ticketsNum: undefined, // 鏉ョエ鏁�
+	ticketsAmount: undefined, // 鏉ョエ閲戦
+	taxInclusiveUnitPrice: undefined, // 鍚◣鍗曚环
+	ticketRegistrationId: undefined, // 鍚◣鍗曚环
+});
+
+const load = async (id) => {
+	const { code, data } = await getProductRecordById({ id });
+	if (code === 200) {
+		form.id = data.id;
+		form.purchaseContractNumber = data.purchaseContractNumber;
+		form.salesContractNo = data.salesContractNo;
+		form.createdAt = data.createdAt;
+		form.invoiceNumber = data.invoiceNumber;
+		form.ticketsNum = data.ticketsNum;
+		form.ticketsAmount = data.ticketsAmount ? Number(data.ticketsAmount).toFixed(2) : 0;
+		form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice;
+		form.futureTickets = data.futureTickets;
+		temFutureTickets.value = data.futureTickets;
+		initialTicketsNum.value = data.ticketsNum || 0;
+		initialTicketsAmount.value = data.ticketsAmount || 0;
+		form.ticketRegistrationId = data.ticketRegistrationId;
+		// 鑾峰彇鎬绘暟閲忥紝濡傛灉鏁版嵁涓湁 quantity 瀛楁鍒欎娇鐢紝鍚﹀垯浣跨敤鏉ョエ鏁�+鏈潵绁ㄦ暟
+		quantity.value = data.quantity || (Number(data.ticketsNum || 0) + Number(data.futureTickets || 0));
+	}
+};
+
+const inputTicketsNum = (val) => {
+	// 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+	if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) {
+		proxy.$modal.msgWarning("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+		return;
+	}
+	
+	const newTicketsNum = Number(form.ticketsNum) || 0;
+	const currentTicketsNum = Number(initialTicketsNum.value) || 0;
+	
+	// 璁$畻鏂板鐨勬潵绁ㄦ暟
+	const addedTicketsNum = newTicketsNum - currentTicketsNum;
+	
+	// 璁$畻鏂扮殑鏈潵绁ㄦ暟 = 鍒濆鏈潵绁ㄦ暟 - 鏂板鐨勬潵绁ㄦ暟
+	const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
+	
+	// 楠岃瘉锛氭柊鐨勬潵绁ㄦ暟 + 鏂扮殑鏈潵绁ㄦ暟 鈮� quantity
+	if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
+		proxy.$modal.msgWarning(`鏉ョエ鏁�+鏈潵绁ㄦ暟涓嶈兘澶т簬鎬绘暟閲�(${quantity.value})`);
+		// 闄愬埗鏉ョエ鏁帮紝浣垮叾婊¤冻锛氭潵绁ㄦ暟 + 鏈潵绁ㄦ暟 鈮� quantity
+		// 鏈�澶ф潵绁ㄦ暟 = quantity - 鍒濆鏈潵绁ㄦ暟 + 鍒濆鏉ョエ鏁�
+		const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
+		form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
+		// 閲嶆柊璁$畻
+		const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
+		const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
+		form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
+		const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice);
+		form.ticketsAmount = Number(ticketsAmount.toFixed(2));
+		return;
+	}
+	
+	// 妫�鏌ユ柊澧炵殑鏉ョエ鏁版槸鍚﹀ぇ浜庡垵濮嬫湭鏉ョエ鏁�
+	if (addedTicketsNum > Number(temFutureTickets.value)) {
+		proxy.$modal.msgWarning("鏂板寮�绁ㄦ暟涓嶅緱澶т簬鏈紑绁ㄦ暟");
+		form.ticketsNum = Number(initialTicketsNum.value) + Number(temFutureTickets.value);
+	}
+	
+	// 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+	const finalTicketsNum = Number(form.ticketsNum) || 0;
+	const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
+	const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
+	const ticketsAmount = finalTicketsNum * Number(form.taxInclusiveUnitPrice);
+	form.futureTickets = Number(finalFutureTickets.toFixed(2));
+	form.ticketsAmount = Number(ticketsAmount.toFixed(2));
+};
+const inputTicketsAmount = (val) => {
+	// 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+	if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) {
+		proxy.$modal.msgWarning("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+		return;
+	}
+	
+	const newTicketsAmount = Number(val) || 0;
+	
+	// 璁$畻鏂扮殑鏉ョエ鏁�
+	const newTicketsNum = newTicketsAmount / Number(form.taxInclusiveUnitPrice);
+	const currentTicketsNum = Number(initialTicketsNum.value) || 0;
+	
+	// 璁$畻鏂板鐨勬潵绁ㄦ暟
+	const addedTicketsNum = newTicketsNum - currentTicketsNum;
+	
+	// 璁$畻鏂扮殑鏈潵绁ㄦ暟 = 鍒濆鏈潵绁ㄦ暟 - 鏂板鐨勬潵绁ㄦ暟
+	const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
+	
+	// 楠岃瘉锛氭柊鐨勬潵绁ㄦ暟 + 鏂扮殑鏈潵绁ㄦ暟 鈮� quantity
+	if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
+		proxy.$modal.msgWarning(`鏉ョエ鏁�+鏈潵绁ㄦ暟涓嶈兘澶т簬鎬绘暟閲�(${quantity.value})`);
+		// 闄愬埗鏉ョエ鏁帮紝浣垮叾婊¤冻锛氭潵绁ㄦ暟 + 鏈潵绁ㄦ暟 鈮� quantity
+		const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
+		form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
+		form.ticketsAmount = Number((form.ticketsNum * Number(form.taxInclusiveUnitPrice)).toFixed(2));
+		const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
+		const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
+		form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
+		return;
+	}
+	
+	// 妫�鏌ユ柊澧炵殑鏉ョエ閲戦鏄惁澶т簬鍒濆鏈潵绁ㄦ暟瀵瑰簲鐨勯噾棰�
+	const maxAddedAmount = Number(temFutureTickets.value * form.taxInclusiveUnitPrice);
+	if (addedTicketsNum > 0 && addedTicketsNum * Number(form.taxInclusiveUnitPrice) > maxAddedAmount) {
+		proxy.$modal.msgWarning("鏂板鏉ョエ閲戦涓嶅緱澶т簬鏈紑绁ㄩ噾棰�");
+		form.ticketsAmount = Number((initialTicketsAmount.value + maxAddedAmount).toFixed(2));
+		form.ticketsNum = Number((currentTicketsNum + Number(temFutureTickets.value)).toFixed(2));
+		form.futureTickets = 0;
+		return;
+	}
+	
+	// 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+	const finalTicketsNum = Number(newTicketsNum.toFixed(2));
+	const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
+	const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
+	form.ticketsNum = finalTicketsNum;
+	form.futureTickets = Number(finalFutureTickets.toFixed(2));
+};
+
+defineExpose({
+	load,
+	form,
+	resetForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue
new file mode 100644
index 0000000..82b4164
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/EditModal.vue
@@ -0,0 +1,62 @@
+<template>
+  <el-dialog :title="modalOptions.title" v-model="visible" @close="close">
+    <EditForm ref="editFormRef" />
+    <template #footer>
+			<el-button type="primary" :loading="loading" @click="sendForm">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "@/hooks/useModal";
+import EditForm from "../Form/EditForm.vue";
+import { updateRegistration } from "@/api/procurementManagement/procurementInvoiceLedger";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "鏉ョエ鍙拌处缂栬緫",
+});
+const emits = defineEmits(["success"]);
+
+const saleLedgerProjectId = ref('')
+const editFormRef = ref();
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "鏉ョエ鍙拌处" });
+
+const open = async (row) => {
+  openModal(row.id);
+	saleLedgerProjectId.value = row.saleLedgerProjectId;
+  await nextTick();
+  editFormRef.value.load(row.id);
+};
+
+const close = () => {
+  editFormRef.value.resetForm();
+  closeModal();
+};
+
+const sendForm = async () => {
+  const form = editFormRef.value.form;
+	form.saleLedgerProjectId = saleLedgerProjectId.value;
+  const { code } = await updateRegistration(form);
+  if (code === 200) {
+    emits("success");
+    ElMessage({ message: "鎿嶄綔鎴愬姛", type: "success" });
+    close();
+  }
+};
+
+defineExpose({
+  open,
+});
+</script>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue
new file mode 100644
index 0000000..b82bd10
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/Modal/UploadModal.vue
@@ -0,0 +1,87 @@
+<template>
+  <el-dialog v-model="upload.open" :title="upload.title" :width="500">
+    <FileUpload
+      ref="fileUploadRef"
+      accept=".xlsx, .xls, .pdf"
+      :headers="upload.headers"
+      :autoUpload="true"
+      :action="upload.url"
+      :disabled="upload.isUploading"
+      :showTip="false"
+      :limit="10"
+      @success="handleFileSuccess"
+      @remove="removeFile"
+    />
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+        <el-button @click="upload.open = false">鍙� 娑�</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { reactive } from "vue";
+import { getToken } from "@/utils/auth.js";
+import { FileUpload } from "@/components/Upload";
+import { ElMessage } from "element-plus";
+import { ref } from "vue";
+import useFormData from "@/hooks/useFormData";
+
+defineOptions({
+  name: "鏉ョエ鍙拌处闄勪欢琛ュ厖",
+});
+
+const { form, resetForm } = useFormData({
+  id: undefined,
+  tempFileIds: [],
+});
+const emits = defineEmits(["uploadSuccess"]);
+const fileUploadRef = ref();
+const upload = reactive({
+  // 鏄惁鏄剧ず寮瑰嚭灞傦紙渚涘簲鍟嗗鍏ワ級
+  open: false,
+  // 寮瑰嚭灞傛爣棰橈紙渚涘簲鍟嗗鍏ワ級
+  title: "",
+  // 鏄惁绂佺敤涓婁紶
+  isUploading: false,
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+});
+// 鐐瑰嚮瀵煎叆
+const handleImport = (id) => {
+  form.id = id;
+  upload.open = true;
+  upload.title = "鏉ョエ鍙拌处闄勪欢琛ュ厖";
+};
+
+const submitFileForm = () => {
+  emits("uploadSuccess", form);
+  resetForm();
+  upload.open = false;
+  // 娓呯┖鏂囦欢鍒楄〃
+  fileUploadRef.value.fileList = [];
+};
+
+const handleFileSuccess = (response) => {
+  if (response.code == 200) {
+		form.tempFileIds.push(response.data.tempId);
+		console.log('form',form);
+    ElMessage({ message: "瀵煎叆鎴愬姛", type: "success" });
+  } else {
+    ElMessage({ message: response.msg, type: "error" });
+  }
+};
+
+const removeFile = (file) => {
+  const { tempId } = file.response.data;
+  form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
+};
+
+defineExpose({
+  handleImport,
+});
+</script>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue
new file mode 100644
index 0000000..8ad8799
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/index.vue
@@ -0,0 +1,372 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="閲囪喘鍚堝悓鍙�">
+        <el-input
+          v-model="filters.purchaseContractNumber"
+          style="width: 240px"
+          placeholder="璇疯緭鍏�"
+          clearable
+          :prefix-icon="Search"
+          @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�">
+        <el-input
+          v-model="filters.supplierName"
+          style="width: 240px"
+          placeholder="璇疯緭鍏�"
+          clearable
+          :prefix-icon="Search"
+          @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鏉ョエ鏃ユ湡">
+        <el-date-picker
+          style="width: 240px"
+          v-model="filters.createdAt"
+          value-format="YYYY-MM-DD"
+          format="YYYY-MM-DD"
+          type="daterange"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          clearable
+          @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters"> 閲嶇疆 </el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableLoading="loading"
+        :tableData="dataList"
+        :isSelection="true"
+        height="calc(100vh - 19.5em)"
+        :isShowSummary="true"
+        :summaryMethod="summarizeMainTable"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <template #commonFilesRef="{ row }">
+          <el-dropdown @command="(command) => handleCommand(command, row)">
+            <el-button link :icon="Files" type="danger"> 闄勪欢 </el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item
+                  v-if="row.commonFiles.length !== 0"
+                  :icon="Download"
+                  command="download"
+                >
+                  涓嬭浇
+                </el-dropdown-item>
+                <el-dropdown-item :icon="Upload" command="upload">
+                  涓婁紶
+                </el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template #operation="{ row }">
+          <el-button
+            type="primary"
+            text
+            @click="openEdit(row)"
+						:disabled="row.issUerId !== userStore.id"
+          >
+            缂栬緫
+          </el-button>
+          <el-button
+            type="primary"
+            text
+						:disabled="row.issUerId !== userStore.id"
+            @click="handleDelete(row)"
+          >
+            鍒犻櫎
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <UploadModal ref="modalRef" @uploadSuccess="uploadSuccess"></UploadModal>
+    <EditModal ref="editmodalRef" @success="getTableData"></EditModal>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import { usePaginationApi } from "@/hooks/usePaginationApi";
+import {
+  Files,
+  Download,
+  Search,
+  Upload,
+  EditPen,
+} from "@element-plus/icons-vue";
+import {
+	delRegistration,
+	productRecordPage,
+	productUploadFile,
+} from "@/api/procurementManagement/procurementInvoiceLedger.js";
+import { onMounted } from "vue";
+import { ElMessageBox } from "element-plus";
+import UploadModal from "./Modal/UploadModal.vue";
+import EditModal from "./Modal/EditModal.vue";
+import useUserStore from "@/store/modules/user.js";
+import {delInvoiceLedgerByRegProductId} from "@/api/salesManagement/invoiceLedger.js";
+const userStore = useUserStore();
+
+defineOptions({
+  name: "鏉ョエ鍙拌处",
+});
+
+const modalRef = ref();
+const editmodalRef = ref();
+
+const { proxy } = getCurrentInstance();
+const multipleVal = ref([]);
+const {
+  loading,
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(
+  productRecordPage,
+  {
+    purchaseContractNumber: undefined, // 閲囪喘鍚堝悓鍙�
+    supplierName: undefined, // 渚涘簲鍟�
+    createdAt: [], // 鏉ョエ鏃ユ湡
+  },
+  [
+    {
+      label: "閲囪喘鍚堝悓鍙�",
+      prop: "purchaseContractNumber",
+      width: 150,
+    },
+    {
+      label: "閿�鍞悎鍚屽彿",
+      prop: "salesContractNo",
+      width: 150,
+    },
+    {
+      label: "椤圭洰鍚嶇О",
+      prop: "projectName",
+      width: 240,
+    },
+    {
+      label: "渚涘簲鍟嗗悕绉�",
+      prop: "supplierName",
+      width: 240,
+    },
+    {
+      label: "浜у搧澶х被",
+      prop: "productCategory",
+      width: 150,
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      prop: "specificationModel",
+      width: 150,
+    },
+    {
+      label: "鍙戠エ鍙�",
+      prop: "invoiceNumber",
+      width: 200,
+    },
+    {
+      label: "鍚堝悓閲戦(鍏�)",
+      prop: "taxInclusiveTotalPrice",
+      width: 200,
+      formatData: (cell) => {
+        return cell ? parseFloat(cell).toFixed(2) : 0;
+      },
+    },
+    {
+      label: "鏈鏉ョエ鏁�",
+      prop: "ticketsNum",
+      width: 110,
+    },
+    {
+      label: "鏉ョエ鏃ユ湡",
+      prop: "createdAt",
+      width: 110,
+    },
+    {
+      label: "鏉ョエ閲戦(鍏�)",
+      prop: "ticketsAmount",
+      width: 200,
+      formatData: (cell) => {
+        return cell ? parseFloat(cell).toFixed(2) : 0;
+      },
+    },
+    {
+      label: "涓嶅惈绋庨噾棰�",
+      prop: "unTicketsPrice",
+      width: 200,
+      formatData: (cell) => {
+        return cell ? parseFloat(cell).toFixed(2) : 0;
+      },
+    },
+    {
+      label: "澧炲�肩◣",
+      prop: "invoiceAmount",
+      width: 200,
+    },
+    {
+      label: "褰曞叆浜�",
+      prop: "issUer",
+      width: 200,
+    },
+    {
+      label: "闄勪欢",
+      align: "center",
+      prop: "commonFiles",
+      dataType: "slot",
+			fixed: "right",
+      slot: "commonFilesRef",
+      width: 120,
+    },
+    {
+      fixed: "right",
+      width: 150,
+      label: "鎿嶄綔",
+      dataType: "slot",
+      slot: "operation",
+      align: "center",
+    },
+  ],
+  {},
+  {
+    createdAt: (aim) => ({
+      createdAtStart: aim ? aim[0] : undefined,
+      createdAtEnd: aim ? aim[1] : undefined,
+    }),
+  }
+);
+
+// 涓昏〃鍚堣鏂规硶
+const summarizeMainTable = (param) => {
+  return proxy.summarizeTable(
+    param,
+    [
+      "taxInclusiveTotalPrice",
+      "ticketsAmount",
+      "unTicketsPrice",
+      "invoiceAmount",
+    ],
+    {
+      ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+      futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+    }
+  );
+};
+
+const handleSelectionChange = (val) => {
+  multipleVal.value = val;
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download("/purchase/registration/export", {}, "鏉ョエ鐧昏.xlsx");
+    })
+    .catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+};
+
+const handleFiles = (fileList) => {
+  fileList.forEach((e) => {
+    proxy.$download.name(e.url);
+  });
+};
+
+const changePage = ({ page, limit }) => {
+  pagination.currentPage = page;
+	pagination.pageSize = limit;
+  onCurrentChange(page);
+};
+
+const handleCommand = (command, row) => {
+  switch (command) {
+    case "download":
+      handleFiles(row.commonFiles);
+      break;
+    case "upload":
+      console.log(row.commonFiles);
+      openUoload(row.ticketRegistrationId);
+      break;
+  }
+};
+
+const openUoload = (id) => {
+  modalRef.value.handleImport(id);
+};
+
+const openEdit = (row) => {
+  editmodalRef.value.open(row);
+};
+
+// 涓婁紶鎴愬姛鍚庡仛浠�涔�
+const uploadSuccess = async (data) => {
+  const { code } = await productUploadFile({
+    ticketRegistrationId: data.id,
+    tempFileIds: data.tempFileIds,
+  });
+  if (code === 200) {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    getTableData();
+  }
+};
+// 鍒犻櫎
+const handleDelete = (row) => {
+	let ids = [];
+	ids.push(row.id);
+	ElMessageBox.confirm("璇ュ紑绁ㄥ彴璐﹀皢琚垹闄�,鏄惁纭鍒犻櫎", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			loading.value = true;
+			delRegistration(ids).then((res) => {
+				getTableData();
+			});
+			loading.value = false;
+		})
+		.catch(() => {
+			proxy.$modal.msg("宸插彇娑�");
+		});
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.tagBox {
+  margin-top: 4px;
+}
+</style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue
new file mode 100644
index 0000000..af38ab1
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementInvoiceLedger/indexOld.vue
@@ -0,0 +1,313 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">閲囪喘鍚堝悓鍙凤細</span>
+        <el-input
+          v-model="searchForm.purchaseContractNumber"
+          style="width: 240px"
+          placeholder="璇疯緭鍏�"
+          @change="handleQuery"
+          clearable
+          :prefix-icon="Search"
+        />
+        <span class="search_title" style="margin-left: 10px">渚涘簲鍟嗭細</span>
+        <el-input
+          v-model="searchForm.supplierName"
+          style="width: 240px"
+          placeholder="璇疯緭鍏�"
+          @change="handleQuery"
+          clearable
+          :prefix-icon="Search"
+        />
+        <span class="search_title" style="margin-left: 10px">鏉ョエ鏃ユ湡锛�</span>
+        <el-date-picker
+          style="width: 240px"
+          v-model="searchForm.issueDate"
+          value-format="YYYY-MM-DD"
+          format="YYYY-MM-DD"
+          type="daterange"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          clearable
+          @change="changeDateRange"
+          @clear="clearRange"
+        />
+        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+          >鎼滅储</el-button
+        >
+      </div>
+      <div>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <el-table
+        :data="tableData"
+        border
+        v-loading="tableLoading"
+        :expand-row-keys="expandedRowKeys"
+        :row-key="(row) => row.id"
+        show-summary
+        :summary-method="summarizeMainTable"
+        @expand-change="expandChange"
+        height="calc(100vh - 18.5em)"
+        stripe
+      >
+        <el-table-column align="center" label="搴忓彿" type="index" width="55" />
+        <el-table-column type="expand">
+          <template #default="props">
+            <el-table
+              :data="props.row.children"
+              border
+              show-summary
+              :summary-method="summarizeChildrenTable"
+              stripe
+            >
+              <el-table-column
+                align="center"
+                label="搴忓彿"
+                type="index"
+                width="60"
+              />
+              <el-table-column label="浜у搧澶х被" prop="productCategory" />
+              <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+              <el-table-column label="鍗曚綅" prop="unit" />
+              <el-table-column label="鏁伴噺" prop="quantity" />
+              <el-table-column label="绋庣巼(%)" prop="taxRate" />
+              <el-table-column
+                label="鍚◣鍗曚环(鍏�)"
+                prop="taxInclusiveUnitPrice"
+                :formatter="formattedNumber"
+              />
+              <el-table-column
+                label="鍚◣鎬讳环(鍏�)"
+                prop="taxInclusiveTotalPrice"
+                :formatter="formattedNumber"
+              />
+              <el-table-column
+                label="涓嶅惈绋庢�讳环(鍏�)"
+                prop="taxExclusiveTotalPrice"
+                :formatter="formattedNumber"
+              />
+              <el-table-column label="鏈鏉ョエ鏁�" prop="ticketsNum" />
+              <el-table-column
+                label="鏈鏉ョエ閲戦(鍏�)"
+                prop="ticketsAmount"
+                :formatter="formattedNumber"
+              />
+              <el-table-column label="鏈潵绁ㄦ暟" prop="futureTickets" />
+              <el-table-column
+                label="鏈潵绁ㄩ噾棰�(鍏�)"
+                prop="futureTicketsAmount"
+                :formatter="formattedNumber"
+              />
+            </el-table>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="閲囪喘鍚堝悓鍙�"
+          prop="purchaseContractNumber"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="閿�鍞悎鍚屽彿"
+          prop="salesContractNo"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="渚涘簲鍟嗗悕绉�"
+          prop="supplierName"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="鍙戠エ鍙�"
+          prop="invoiceNumber"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="鍚堝悓閲戦(鍏�)"
+          prop="invoiceAmount"
+          show-overflow-tooltip
+          :formatter="formattedNumber"
+        />
+        <el-table-column label="寮�绁ㄤ汉" prop="issUer" show-overflow-tooltip />
+        <el-table-column
+          label="寮�绁ㄦ棩鏈�"
+          prop="issueDate"
+          show-overflow-tooltip
+        />
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current"
+        :limit="page.size"
+        @pagination="paginationChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import pagination from "@/components/PIMTable/Pagination.vue";
+import { ref } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessageBox } from "element-plus";
+import {
+  invoiceListPage,
+  productRecordList,
+} from "@/api/procurementManagement/procurementInvoiceLedger.js";
+import dayjs from "dayjs";
+
+const { proxy } = getCurrentInstance();
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+  current: 1,
+  size: 100,
+});
+const total = ref(0);
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const data = reactive({
+  searchForm: {
+    purchaseContractNumber: "",
+    supplierName: "",
+    issueDate: [
+      dayjs().startOf("month").format("YYYY-MM-DD"),
+      dayjs().endOf("month").format("YYYY-MM-DD"),
+    ],
+    issueDateStart: dayjs().startOf("month").format("YYYY-MM-DD"),
+    issueDateEnd: dayjs().endOf("month").format("YYYY-MM-DD"),
+  },
+  form: {
+    issueDate: "", // 寮�绁ㄦ棩鏈�
+    purchaseLedgerId: "",
+    purchaseLedgerNo: "",
+    issUerId: "", // 寮�绁ㄤ汉id
+    issUer: "", // 寮�绁ㄤ汉濮撳悕
+  },
+  rules: {
+    purchaseLedgerId: [
+      { required: true, message: "璇烽�夋嫨", trigger: "change" },
+    ],
+  },
+});
+const { searchForm } = toRefs(data);
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  page.current = 1;
+  getList();
+};
+const paginationChange = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList();
+};
+const getList = () => {
+  tableLoading.value = true;
+  const { issueDate, ...rest } = searchForm.value;
+  invoiceListPage({ ...rest, ...page })
+    .then((res) => {
+      tableLoading.value = false;
+      tableData.value = res.records;
+      tableData.value.map((item) => {
+        item.children = [];
+      });
+      total.value = res.total;
+      expandedRowKeys.value = [];
+    })
+    .catch(() => {
+      tableLoading.value = false;
+    });
+};
+const formattedNumber = (row, column, cellValue) => {
+  return parseFloat(cellValue).toFixed(2);
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const expandedRowKeys = ref([]);
+// 灞曞紑琛�
+const expandChange = (row, expandedRows) => {
+  if (expandedRows.length > 0) {
+    expandedRowKeys.value = [];
+    try {
+      productRecordList({ id: row.id }).then((res) => {
+        const index = tableData.value.findIndex((item) => item.id === row.id);
+        if (index > -1) {
+          tableData.value[index].children = res;
+        }
+        expandedRowKeys.value.push(row.id);
+      });
+    } catch (error) {
+      console.log(error);
+    }
+  } else {
+    expandedRowKeys.value = [];
+  }
+};
+// 涓昏〃鍚堣鏂规硶
+const summarizeMainTable = (param) => {
+  return proxy.summarizeTable(param, ["invoiceAmount"], {
+    ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+    futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+  });
+};
+// 瀛愯〃鍚堣鏂规硶
+const summarizeChildrenTable = (param) => {
+  return proxy.summarizeTable(
+    param,
+    [
+      "taxInclusiveUnitPrice",
+      "taxInclusiveTotalPrice",
+      "taxExclusiveTotalPrice",
+      "ticketsNum",
+      "ticketsAmount",
+      "futureTickets",
+      "futureTicketsAmount",
+    ],
+    {
+      ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+      futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+    }
+  );
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download("/purchase/registration/export", {}, "鏉ョエ鐧昏.xlsx");
+    })
+    .catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+};
+
+const changeDateRange = (date) => {
+  if (date) {
+    searchForm.value.receiptPaymentDateStart = date[0];
+    searchForm.value.receiptPaymentDateEnd = date[1];
+    getList();
+  }
+};
+
+const clearRange = () => {
+  searchForm.value.issueDate = [];
+  searchForm.value.issueDateStart = undefined;
+  searchForm.value.issueDateEnd = undefined;
+  getList();
+};
+
+onMounted(() => {
+  getList();
+});
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue b/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue
new file mode 100644
index 0000000..fb392c5
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementLedger/fileList.vue
@@ -0,0 +1,67 @@
+<template>
+  <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
+    <el-table :data="tableData" border height="40vh">
+      <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
+      <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+        <template #default="scope">
+          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+          <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
+          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-dialog>
+  <filePreview ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
+import filePreview from '@/components/filePreview/index.vue'
+import { delCommonFile } from '@/api/publicApi/commonFile.js'
+
+const dialogVisible = ref(false)
+const tableData = ref([])
+const { proxy } = getCurrentInstance();
+const filePreviewRef = ref()
+const handleClose = () => {
+  dialogVisible.value = false
+}
+const open = (list) => {
+  dialogVisible.value = true
+  tableData.value = list
+}
+const downLoadFile = (row) => {
+  proxy.$download.name(row.url);
+
+}
+const lookFile = (row) => {
+  filePreviewRef.value.open(row.url)
+}
+// 鍒犻櫎闄勪欢
+const handleDelete = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎闄勪欢"${row.name}"鍚楋紵`, '鍒犻櫎纭', {
+    confirmButtonText: '纭',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning',
+  }).then(() => {
+    delCommonFile([row.id]).then(() => {
+      ElMessage.success('鍒犻櫎鎴愬姛')
+      // 浠庡垪琛ㄤ腑绉婚櫎宸插垹闄ょ殑闄勪欢
+      const index = tableData.value.findIndex(item => item.id === row.id)
+      if (index !== -1) {
+        tableData.value.splice(index, 1)
+      }
+    }).catch(() => {
+      ElMessage.error('鍒犻櫎澶辫触')
+    })
+  }).catch(() => {
+    proxy.$modal.msg('宸插彇娑堝垹闄�')
+  })
+}
+defineExpose({
+  open
+})
+</script>
+
+<style></style>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue b/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue
new file mode 100644
index 0000000..21a3fc5
--- /dev/null
+++ b/src/views/inventoryManagement/procurementManagement/procurementLedger/index.vue
@@ -0,0 +1,528 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">鎵胯繍鍟嗘。妗堬細</span>
+        <el-input
+            v-model="searchForm.supplierName"
+            style="width: 240px"
+            placeholder="杈撳叆鎵胯繍鍟嗗悕绉版悳绱�"
+            @change="handleQuery"
+            clearable
+            :prefix-icon="Search"
+        />
+        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+        >鎼滅储</el-button
+        >
+      </div>
+      <div>
+        <el-button type="primary" @click="openForm('add')"
+        >鏂板鎵胯繍鍟�</el-button
+        >
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+        <el-button type="info" plain icon="Upload" @click="handleImport"
+        >瀵煎叆</el-button
+        >
+        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable
+          rowKey="id"
+          :column="tableColumn"
+          :tableData="tableData"
+          :page="page"
+          :isSelection="true"
+          @selection-change="handleSelectionChange"
+          :tableLoading="tableLoading"
+          @pagination="pagination"
+      ></PIMTable>
+    </div>
+    <el-dialog
+        v-model="dialogFormVisible"
+        :title="operationType === 'add' ? '鏂板鎵胯繍鍟嗕俊鎭�' : '缂栬緫鎵胯繍鍟嗕俊鎭�'"
+        width="70%"
+        @close="closeDia"
+    >
+      <el-form
+          :model="form"
+          label-width="140px"
+          label-position="top"
+          :rules="rules"
+          ref="formRef"
+      >
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鎵胯繍鍟嗗悕绉帮細" prop="supplierName">
+              <el-input
+                  v-model="form.supplierName"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item
+                label="绾崇◣浜鸿瘑鍒彿锛�"
+                prop="taxpayerIdentificationNum"
+            >
+              <el-input
+                  v-model="form.taxpayerIdentificationNum"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍏徃鍦板潃锛�" prop="companyAddress">
+              <el-input
+                  v-model="form.companyAddress"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏徃鐢佃瘽锛�" prop="companyPhone">
+              <el-input
+                  v-model="form.companyPhone"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="寮�鎴疯锛�" prop="bankAccountName">
+              <el-input
+                  v-model="form.bankAccountName"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璐﹀彿锛�" prop="bankAccountNum">
+              <el-input
+                  v-model="form.bankAccountNum"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴浜猴細" prop="contactUserName">
+              <el-input
+                  v-model="form.contactUserName"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactUserPhone">
+              <el-input
+                  v-model="form.contactUserPhone"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="缁存姢浜猴細" prop="maintainUserId">
+              <el-select
+                  v-model="form.maintainUserId"
+                  placeholder="璇烽�夋嫨"
+                  clearable
+                  disabled
+              >
+                <el-option
+                    v-for="item in userList"
+                    :key="item.nickName"
+                    :label="item.nickName"
+                    :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="缁存姢鏃堕棿锛�" prop="maintainTime">
+              <el-date-picker
+                  style="width: 100%"
+                  v-model="form.maintainTime"
+                  value-format="YYYY-MM-DD"
+                  format="YYYY-MM-DD"
+                  type="date"
+                  placeholder="璇烽�夋嫨"
+                  clearable
+                  disabled
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 鎵胯繍鍟嗗鍏ュ璇濇 -->
+    <el-dialog
+        :title="upload.title"
+        v-model="upload.open"
+        width="400px"
+        append-to-body
+    >
+      <el-upload
+          ref="uploadRef"
+          :limit="1"
+          accept=".xlsx, .xls"
+          :headers="upload.headers"
+          :action="upload.url + '?updateSupport=' + upload.updateSupport"
+          :disabled="upload.isUploading"
+          :on-progress="handleFileUploadProgress"
+          :on-success="handleFileSuccess"
+          :auto-upload="false"
+          drag
+      >
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+            <!-- <el-link
+              type="primary"
+              :underline="false"
+              style="font-size: 12px; vertical-align: baseline"
+              @click="importTemplate"
+              >涓嬭浇妯℃澘</el-link
+            > -->
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+          <el-button @click="upload.open = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { delSupplier } from "@/api/basicData/supplierManageFile.js";
+import { ElMessageBox } from "element-plus";
+import { userListNoPage } from "@/api/system/user.js";
+import {
+  addSupplier,
+  getSupplier,
+  listSupplier,
+  updateSupplier,
+} from "@/api/basicData/supplierManageFile.js";
+import useUserStore from "@/store/modules/user";
+import { getToken } from "@/utils/auth.js";
+const { proxy } = getCurrentInstance();
+const userStore = useUserStore();
+
+const tableColumn = ref([
+  {
+    label: "鎵胯繍鍟嗗悕绉�",
+    prop: "supplierName",
+    width: 250,
+  },
+  {
+    label: "绾崇◣浜鸿瘑鍒彿",
+    prop: "taxpayerIdentificationNum",
+    width: 230,
+  },
+  {
+    label: "鍏徃鍦板潃",
+    prop: "companyAddress",
+    width: 220,
+  },
+  {
+    label: "鑱旂郴鏂瑰紡",
+    prop: "companyPhone",
+    width:150
+  },
+  {
+    label: "寮�鎴疯",
+    prop: "bankAccountName",
+    width: 220,
+  },
+  {
+    label: "璐﹀彿",
+    prop: "bankAccountNum",
+    width: 220,
+  },
+  {
+    label: "鑱旂郴浜�",
+    prop: "contactUserName",
+  },
+  {
+    label: "鑱旂郴鐢佃瘽",
+    prop: "contactUserPhone",
+    width: 150,
+  },
+  {
+    label: "缁存姢浜�",
+    prop: "maintainUserName",
+  },
+
+  {
+    label: "缁存姢鏃堕棿",
+    prop: "maintainTime",
+    width:100
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: 'right',
+    operation: [
+      {
+        name: "缂栬緫",
+        type: "text",
+        clickFun: (row) => {
+          openForm("edit", row);
+        },
+        disabled: (row) => {
+          return row.maintainUserName !== userStore.nickName
+        }
+      },
+    ],
+  },
+]);
+const tableData = ref([]);
+const selectedRows = ref([]);
+const userList = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+  current: 1,
+  size: 100,
+  total: 0,
+});
+
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const operationType = ref("");
+const dialogFormVisible = ref(false);
+const data = reactive({
+  searchForm: {
+    supplierName: "",
+  },
+  form: {
+    supplierName: "",
+    taxpayerIdentificationNum: "",
+    companyAddress: "",
+    companyPhone: "",
+    bankAccountName: "",
+    bankAccountNum: "",
+    contactUserName: "",
+    contactUserPhone: "",
+    maintainUserId: "",
+    maintainTime: "",
+  },
+  rules: {
+    supplierName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    taxpayerIdentificationNum: [
+      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+    ],
+    companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    bankAccountName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    bankAccountNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    contactUserName: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+    contactUserPhone: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+    maintainUserId: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+    maintainTime: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+  },
+});
+const { searchForm, form, rules } = toRefs(data);
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  page.current = 1;
+  getList();
+};
+const pagination = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList();
+};
+/** 鎻愪氦涓婁紶鏂囦欢 */
+function submitFileForm() {
+  console.log(upload.url + '?updateSupport=' + upload.updateSupport)
+  proxy.$refs["uploadRef"].submit();
+}
+const getList = () => {
+  tableLoading.value = true;
+  listSupplier({ ...searchForm.value, ...page }).then((res) => {
+    tableLoading.value = false;
+    tableData.value = res.data.records;
+    page.total = res.data.total;
+  });
+};
+const upload = reactive({
+  // 鏄惁鏄剧ず寮瑰嚭灞傦紙鎵胯繍鍟嗗鍏ワ級
+  open: false,
+  // 寮瑰嚭灞傛爣棰橈紙鎵胯繍鍟嗗鍏ワ級
+  title: "",
+  // 鏄惁绂佺敤涓婁紶
+  isUploading: false,
+  // 鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�
+  updateSupport: 1,
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
+});
+/** 瀵煎叆鎸夐挳鎿嶄綔 */
+function handleImport() {
+  upload.title = "鎵胯繍鍟嗗鍏�";
+  upload.open = true;
+}
+
+/**鏂囦欢涓婁紶涓鐞� */
+const handleFileUploadProgress = (event, file, fileList) => {
+  upload.isUploading = true;
+};
+
+/** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
+const handleFileSuccess = (response, file, fileList) => {
+  upload.open = false;
+  upload.isUploading = false;
+  proxy.$refs["uploadRef"].handleRemove(file);
+  getList();
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+  operationType.value = type;
+  form.value = {};
+  form.value.maintainUserId = userStore.id;
+  form.value.maintainTime = getCurrentDate();
+  userListNoPage().then((res) => {
+    userList.value = res.data;
+  });
+  if (type === "edit") {
+    getSupplier(row.id).then((res) => {
+      form.value = { ...res.data };
+    });
+  }
+  dialogFormVisible.value = true;
+};
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+  proxy.$refs["formRef"].validate((valid) => {
+    if (valid) {
+      if (operationType.value === "edit") {
+        submitEdit();
+      } else {
+        submitAdd();
+      }
+    }
+  });
+};
+// 鎻愪氦鏂板
+const submitAdd = () => {
+  addSupplier(form.value).then((res) => {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    closeDia();
+    getList();
+  });
+};
+// 鎻愪氦淇敼
+const submitEdit = () => {
+  updateSupplier(form.value).then((res) => {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    closeDia();
+    getList();
+  });
+};
+// 鍏抽棴寮规
+const closeDia = () => {
+  proxy.resetForm("formRef");
+  dialogFormVisible.value = false;
+};
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+      .then(() => {
+        proxy.download("/system/supplier/export", {}, "鎵胯繍鍟嗘。妗�.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+};
+// 鍒犻櫎
+const handleDelete = () => {
+  let ids = [];
+  if (selectedRows.value.length > 0) {
+    // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+    const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName);
+    if (unauthorizedData.length > 0) {
+      proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+      return;
+    }
+    ids = selectedRows.value.map((item) => item.id);
+  } else {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+      .then(() => {
+        tableLoading.value = true;
+        delSupplier(ids)
+            .then((res) => {
+              proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+              getList();
+            })
+            .finally(() => {
+              tableLoading.value = false;
+            });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+};
+
+// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+function getCurrentDate() {
+  const today = new Date();
+  const year = today.getFullYear();
+  const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+  const day = String(today.getDate()).padStart(2, "0");
+  return `${year}-${month}-${day}`;
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
+
+<style scoped lang="scss"></style>

--
Gitblit v1.9.3