From 4762afb6cb043a3e539ed7ec8da5bde997bfaf65 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 29 五月 2026 11:51:41 +0800
Subject: [PATCH] 英泽防锈 1.车辆管理页面开发联调

---
 src/views/officeProcessAutomation/ApproveManage/approve-template/components/FormConfigEditor.vue  |   13 
 src/views/productionManagement/productionProcess/index.vue                                        |   41 -
 src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue                            |    6 
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalTemplateBindingUtils.js    |    2 
 src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue     |   11 
 src/views/productionManagement/processRoute/processRouteItem/index.vue                            |   14 
 src/views/productionManagement/productionCosting/index.vue                                        |   20 
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js |   13 
 src/api/officeProcessAutomation/vehicle.js                                                        |  179 ++++++
 src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js            |   36 +
 src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js               |   54 +
 src/views/productionManagement/productionProcess/Edit.vue                                         |   25 
 src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js                    |   48 +
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js          |   16 
 src/views/officeProcessAutomation/ApproveManage/vehicle-apply/index.vue                           | 1066 ++++++++++++++++++++++++++++++++++++++
 src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js        |    4 
 src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js              |   10 
 src/views/inventoryManagement/vehicleManagement/index.vue                                         |   78 ++
 src/views/productionManagement/productionProcess/New.vue                                          |   23 
 19 files changed, 1,537 insertions(+), 122 deletions(-)

diff --git a/src/api/officeProcessAutomation/vehicle.js b/src/api/officeProcessAutomation/vehicle.js
new file mode 100644
index 0000000..d76dc8d
--- /dev/null
+++ b/src/api/officeProcessAutomation/vehicle.js
@@ -0,0 +1,179 @@
+import request from "@/utils/request";
+
+/**
+ * 杞﹁締绠$悊API
+ */
+
+// ==================== 杞﹁締涓绘。鎺ュ彛 ====================
+
+/**
+ * 鍒嗛〉鏌ヨ杞﹁締鍒楄〃
+ * @param {Object} params - 鏌ヨ鍙傛暟
+ * @param {number} params.current - 褰撳墠椤电爜
+ * @param {number} params.size - 姣忛〉鏉℃暟
+ * @param {string} params.plateNumber - 杞︾墝鍙凤紙妯$硦鏌ヨ锛�
+ * @param {string} params.status - 浣跨敤鐘舵��
+ */
+export function listVehiclePage(params) {
+  return request({
+    url: "/approve/vehicle/listPage",
+    method: "get",
+    params,
+  });
+}
+
+/**
+ * 鏂板杞﹁締
+ * @param {Object} data - 杞﹁締鏁版嵁
+ * @param {string} data.plateNumber - 杞︾墝鍙�
+ * @param {number} data.mileage - 杞﹁締鍏噷鏁�
+ * @param {string} data.status - 浣跨敤鐘舵�侊細IDLE / IN_USE / MAINTENANCE / SCRAPPED
+ */
+export function saveVehicle(data) {
+  return request({
+    url: "/approve/vehicle/save",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 杞﹁締璇︽儏
+ * @param {number} id - 杞﹁締ID
+ */
+export function getVehicleDetail(id) {
+  return request({
+    url: "/approve/vehicle/detail",
+    method: "get",
+    params: { id },
+  });
+}
+
+/**
+ * 淇敼杞﹁締
+ * @param {Object} data - 杞﹁締鏁版嵁
+ */
+export function updateVehicle(data) {
+  return request({
+    url: "/approve/vehicle/update",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 鍒犻櫎杞﹁締
+ * @param {Array<number>} ids - 杞﹁締ID鏁扮粍
+ */
+export function deleteVehicle(ids) {
+  return request({
+    url: "/approve/vehicle/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// ==================== 鍊熷嚭璁板綍鎺ュ彛 ====================
+
+/**
+ * 鍒嗛〉鏌ヨ鍊熷嚭璁板綍
+ * @param {Object} params - 鏌ヨ鍙傛暟
+ * @param {number} params.current - 褰撳墠椤电爜
+ * @param {number} params.size - 姣忛〉鏉℃暟
+ * @param {string} params.borrowNo - 鍊熷嚭鍗曞彿
+ * @param {string} params.vehiclePlateNumber - 杞︾墝鍙�
+ * @param {string} params.applicantName - 鐢宠浜�
+ * @param {string} params.borrowStatus - 鍊熷嚭鐘舵��
+ * @param {string} params.extendStatus - 寤舵湡鐘舵��
+ */
+export function listBorrowPage(params) {
+  return request({
+    url: "/approve/vehicle/borrow/listPage",
+    method: "get",
+    params,
+  });
+}
+
+/**
+ * 鏂板鍊熷嚭鐢宠
+ * @param {Object} data - 鍊熷嚭鏁版嵁
+ * @param {number} data.vehicleId - 杞﹁締ID
+ * @param {string} data.borrowReason - 鍊熷嚭鍘熷洜
+ * @param {string} data.borrowStartTime - 鍊熷嚭寮�濮嬫椂闂�
+ * @param {string} data.plannedReturnTime - 璁″垝褰掕繕鏃堕棿
+ * @param {string} data.borrowStatus - 鍊熷嚭鐘舵�侊細DRAFT / IN_APPROVAL
+ * @param {number} data.approvalTemplateId - 瀹℃壒妯℃澘ID
+ */
+export function saveBorrow(data) {
+  return request({
+    url: "/approve/vehicle/borrow/save",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 鍊熷嚭璁板綍璇︽儏
+ * @param {number} id - 鍊熷嚭璁板綍ID
+ */
+export function getBorrowDetail(id) {
+  return request({
+    url: "/approve/vehicle/borrow/detail",
+    method: "get",
+    params: { id },
+  });
+}
+
+/**
+ * 淇敼鍊熷嚭鐢宠
+ * @param {Object} data - 鍊熷嚭鏁版嵁
+ */
+export function updateBorrow(data) {
+  return request({
+    url: "/approve/vehicle/borrow/update",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 鍒犻櫎鍊熷嚭璁板綍
+ * @param {Array<number>} ids - 鍊熷嚭璁板綍ID鏁扮粍
+ */
+export function deleteBorrow(ids) {
+  return request({
+    url: "/approve/vehicle/borrow/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+/**
+ * 褰掕繕杞﹁締
+ * @param {Object} data - 褰掕繕鏁版嵁
+ * @param {number} data.id - 鍊熷嚭璁板綍ID
+ * @param {string} data.actualReturnTime - 瀹為檯褰掕繕鏃堕棿
+ */
+export function returnVehicle(data) {
+  return request({
+    url: "/approve/vehicle/borrow/return",
+    method: "post",
+    data,
+  });
+}
+
+/**
+ * 鍙戣捣寤舵湡鐢宠
+ * @param {Object} data - 寤舵湡鏁版嵁
+ * @param {number} data.id - 鍊熷嚭璁板綍ID
+ * @param {string} data.extendTargetReturnTime - 寤舵湡鍚庣殑褰掕繕鏃堕棿
+ * @param {string} data.extendReason - 寤舵湡鍘熷洜
+ * @param {number} data.approvalTemplateId - 寤舵湡瀹℃壒妯℃澘ID
+ */
+export function delayBorrow(data) {
+  return request({
+    url: "/approve/vehicle/borrow/delay",
+    method: "post",
+    data,
+  });
+}
diff --git a/src/views/inventoryManagement/vehicleManagement/index.vue b/src/views/inventoryManagement/vehicleManagement/index.vue
index 1e383c6..bba4f19 100644
--- a/src/views/inventoryManagement/vehicleManagement/index.vue
+++ b/src/views/inventoryManagement/vehicleManagement/index.vue
@@ -136,6 +136,14 @@
           width="120"
           show-overflow-tooltip
         />
+        <el-table-column label="褰撳墠鍏噷鏁�" prop="currentMileage" width="120" align="center" />
+        <el-table-column label="浣跨敤鐘舵��" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="usageStatusTagType(scope.row.usageStatus)">
+              {{ usageStatusLabel(scope.row.usageStatus) }}
+            </el-tag>
+          </template>
+        </el-table-column>
         <el-table-column label="鐘舵��" width="100" align="center">
           <template #default="scope">
             <el-tag :type="statusTagType(scope.row.status)">
@@ -303,6 +311,30 @@
               />
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="褰撳墠鍏噷鏁帮細" prop="currentMileage">
+              <el-input-number
+                v-model="form.currentMileage"
+                :min="0"
+                :precision="1"
+                :step="0.1"
+                style="width: 100%"
+                placeholder="璇疯緭鍏ュ綋鍓嶅叕閲屾暟"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="浣跨敤鐘舵�侊細" prop="usageStatus">
+              <el-select v-model="form.usageStatus" placeholder="璇烽�夋嫨浣跨敤鐘舵��" style="width: 100%">
+                <el-option label="闂茬疆" value="idle" />
+                <el-option label="浣跨敤涓�" value="in_use" />
+                <el-option label="缁翠慨涓�" value="repair" />
+                <el-option label="淇濆吇涓�" value="maintenance" />
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
       <template #footer>
@@ -333,6 +365,10 @@
     licenseExpireDate: "2026-03-10",
     status: "鍦ㄧ敤",
     archived: false,
+    currentMileage: 12580.5,
+    lastRecordMileage: 12000.0,
+    mileagePhoto: "",
+    usageStatus: "idle",
   },
   {
     id: 2,
@@ -346,6 +382,10 @@
     licenseExpireDate: "2025-07-28",
     status: "缁翠慨",
     archived: false,
+    currentMileage: 8920.0,
+    lastRecordMileage: 8920.0,
+    mileagePhoto: "",
+    usageStatus: "repair",
   },
   {
     id: 3,
@@ -359,6 +399,10 @@
     licenseExpireDate: "2024-05-18",
     status: "闂茬疆",
     archived: false,
+    currentMileage: 45600.0,
+    lastRecordMileage: 45600.0,
+    mileagePhoto: "",
+    usageStatus: "idle",
   },
   {
     id: 4,
@@ -372,6 +416,10 @@
     licenseExpireDate: "2023-11-08",
     status: "鍦ㄧ敤",
     archived: true,
+    currentMileage: 67890.5,
+    lastRecordMileage: 67890.5,
+    mileagePhoto: "",
+    usageStatus: "archived",
   },
 ]);
 
@@ -394,6 +442,7 @@
   { label: "鍦ㄧ敤", value: "鍦ㄧ敤" },
   { label: "闂茬疆", value: "闂茬疆" },
   { label: "缁翠慨", value: "缁翠慨" },
+  { label: "浣跨敤涓�", value: "浣跨敤涓�" },
 ];
 
 // 鏌ヨ琛ㄥ崟
@@ -425,6 +474,10 @@
   licenseExpireDate: "",
   status: "鍦ㄧ敤",
   archived: false,
+  currentMileage: 0,
+  lastRecordMileage: 0,
+  mileagePhoto: "",
+  usageStatus: "idle",
 });
 
 const rules = {
@@ -569,8 +622,33 @@
   if (status === "鍦ㄧ敤") return "success";
   if (status === "闂茬疆") return "info";
   if (status === "缁翠慨") return "warning";
+  if (status === "浣跨敤涓�") return "primary";
   return "default";
 };
+
+// 浣跨敤鐘舵�佹爣绛炬牱寮�
+const usageStatusTagType = (usageStatus) => {
+  const typeMap = {
+    idle: "info",
+    in_use: "primary",
+    repair: "warning",
+    maintenance: "warning",
+    archived: "info",
+  };
+  return typeMap[usageStatus] || "default";
+};
+
+// 浣跨敤鐘舵�佹爣绛炬枃瀛�
+const usageStatusLabel = (usageStatus) => {
+  const labelMap = {
+    idle: "闂茬疆",
+    in_use: "浣跨敤涓�",
+    repair: "缁翠慨涓�",
+    maintenance: "淇濆吇涓�",
+    archived: "宸插綊妗�",
+  };
+  return labelMap[usageStatus] || usageStatus;
+};
 </script>
 
 <style scoped lang="scss">
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
index 3251f0c..12cf082 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -19,6 +19,9 @@
   { value: "quotation", label: "鎶ヤ环瀹℃壒", cellBg: "#f4ecfc", cellColor: "#9b59b6" },
   { value: "shipment", label: "鍙戣揣瀹℃壒", cellBg: "#e8faf6", cellColor: "#1abc9c" },
   { value: "enterprise_news", label: "浼佷笟鏂伴椈", cellBg: "#ecf5ff", cellColor: "#409eff" },
+  { value: "vehicle", label: "杞﹁締瀹℃壒", cellBg: "#fff7e6", cellColor: "#fa8c16" },
+  { value: "vehicle_return", label: "杞﹁締杩樿溅瀹℃壒", cellBg: "#e6fffb", cellColor: "#13c2c2" },
+  { value: "vehicle_extend", label: "杞﹁締寤舵湡瀹℃壒", cellBg: "#f6ffed", cellColor: "#52c41a" },
 ];
 
 /** 鍒楄〃鏌ヨ锛氬鎵圭姸鎬侊紙涓庡悗绔� status 鏋氫妇涓�鑷达級 */
@@ -516,12 +519,12 @@
   return APPROVAL_STATUS_OPTIONS.find(x => x.value === key)?.label || "鈥�";
 }
 
-/** 涓氬姟鐢宠椤电姸鎬佹枃妗堬細PENDING鈫掕繘琛屼腑 APPROVED鈫掑凡瀹屾垚 REJECTED鈫掑凡椹冲洖 */
+/** 涓氬姟鐢宠椤电姸鎬佹枃妗堬細PENDING鈫掑鎵逛腑 APPROVED鈫掑凡閫氳繃 REJECTED鈫掑凡椹冲洖 */
 export function businessApprovalStatusLabel(v) {
   const key = normalizeApprovalStatusKey(v);
   if (key === "draft") return "鑽夌";
-  if (key === "pending") return "杩涜涓�";
-  if (key === "approved") return "宸插畬鎴�";
+  if (key === "pending") return "瀹℃壒涓�";
+  if (key === "approved") return "宸查�氳繃";
   if (key === "rejected") return "宸查┏鍥�";
   if (key === "cancelled") return "宸叉挙閿�";
   return "鈥�";
@@ -600,6 +603,7 @@
     flowNodes,
     templateAttachments: tpl?.storageBlobDTOs ? JSON.parse(JSON.stringify(tpl.storageBlobDTOs)) : [],
     storageBlobDTOs: [],
+    formConfig: null,
   };
 }
 
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
index 7933db5..18e5387 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
@@ -11,7 +11,7 @@
         v-for="field in fields"
         :key="field.key"
         :label="field.label"
-        :span="field.type === 'textarea' || field.type === 'datetimerange' ? 2 : 1"
+        :span="field.type === 'textarea' || field.type === 'datetimerange' || field.type === 'datetime' ? 2 : 1"
       >
         <span class="field-value">{{ displayValue(field) }}</span>
       </el-descriptions-item>
@@ -62,6 +62,15 @@
           style="width: 100%"
         />
         <el-date-picker
+          v-else-if="field.type === 'datetime'"
+          v-model="formPayload[field.key]"
+          type="datetime"
+          :placeholder="`璇烽�夋嫨${field.label}`"
+          format="YYYY-MM-DD HH:mm:ss"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          style="width: 100%"
+        />
+        <el-date-picker
           v-else-if="field.type === 'datetimerange'"
           v-model="formPayload[field.key]"
           type="datetimerange"
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index bbfa56a..078fd45 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -48,6 +48,7 @@
         <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
       </div>
       <div class="search_actions">
+        <el-button type="danger" :disabled="!selectedRows?.length" @click="batchDelete">鎵归噺鍒犻櫎</el-button>
         <el-button type="primary" :icon="Plus" @click="openSubmitDialog">鎻愪氦瀹℃壒</el-button>
       </div>
     </div>
@@ -58,10 +59,11 @@
         :column="tableColumn"
         :tableData="tableData"
         :page="page"
-        :isSelection="false"
+        :isSelection="true"
         :tableLoading="tableLoading"
         :total="page.total"
         @pagination="pagination"
+        @selection-change="(rows) => selectedRows = rows"
       >
         <template #approveType="{ row }">
           <span class="approve-type-cell" :style="approvalTypeStyle(row.approvalType)">
@@ -395,6 +397,8 @@
   submitApprove,
   openDetail,
   openApprove,
+  selectedRows,
+  batchDelete,
 } = al;
 
 const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index 67b9213..68a3a3e 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -98,6 +98,9 @@
   const submitFormRef = ref();
   const submitSaving = ref(false);
 
+  // 鎵归噺鍒犻櫎鐩稿叧
+  const selectedRows = ref([]);
+
   const isSubmitEdit = computed(() => submitDialog.mode === "edit");
   const submitDialogTitle = computed(() => {
     if (submitDialog.mode === "edit") {
@@ -569,6 +572,49 @@
     return "寰呭鐞�";
   }
 
+  // 鎵归噺鍒犻櫎瀹℃壒瀹炰緥
+  async function batchDelete() {
+    if (!selectedRows.value?.length) {
+      ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑璁板綍");
+      return;
+    }
+    try {
+      await ElMessageBox.confirm(
+        `纭畾鍒犻櫎閫変腑鐨� ${selectedRows.value.length} 鏉″鎵硅褰曞悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+        "鍒犻櫎纭",
+        {
+          type: "warning",
+          confirmButtonText: "纭畾鍒犻櫎",
+          cancelButtonText: "鍙栨秷",
+          distinguishCancelAndClose: true,
+          autofocus: false,
+        }
+      );
+    } catch {
+      return;
+    }
+    const ids = selectedRows.value.map((row) => row.id).filter(Boolean);
+    if (!ids.length) {
+      ElMessage.warning("鏃犳硶鍒犻櫎锛氱己灏戝鎵瑰疄渚� ID");
+      return;
+    }
+    try {
+      await deleteApprovalInstance(ids);
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      selectedRows.value = [];
+      // 鍏抽棴鍙兘鎵撳紑鐨勮鎯呭脊绐�
+      if (detailDialog.visible) {
+        detailDialog.visible = false;
+      }
+      if (approveDialog.visible) {
+        approveDialog.visible = false;
+      }
+      await fetchApprovalList();
+    } catch {
+      /* 閿欒鐢辨嫤鎴櫒鎻愮ず */
+    }
+  }
+
   return {
     Search,
     APPROVAL_TYPE_OPTIONS,
@@ -624,5 +670,7 @@
     openDetail,
     openApprove,
     fetchApprovalList,
+    selectedRows,
+    batchDelete,
   };
 }
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
index d6bec18..96e5fb8 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
@@ -82,19 +82,23 @@
  * 涓氬姟鐢宠涓昏〃鍒楋細鍥哄畾鍒� + formConfig 鍔ㄦ�佸垪 + 瀹℃壒鐘舵�� + 鎿嶄綔
  */
 export function buildInstanceTableColumns(tableDataRef, buildTableActions, options = {}) {
-  const { moduleKey, excludeKeys = DEFAULT_EXCLUDE_KEYS, beforeFormColumns = [], extraColumns = [], afterFormColumns = [], actionWidth = 260 } = options;
+  const { moduleKey, excludeKeys = DEFAULT_EXCLUDE_KEYS, beforeFormColumns = [], extraColumns = [], afterFormColumns = [], actionWidth = 260, showCreateTime = true } = options;
 
   const leadingCols = moduleKey && INSTANCE_NO_SEARCH_MODULE_KEYS.has(moduleKey) ? [INSTANCE_NO_TABLE_COLUMN] : [];
 
   return computed(() => {
     const formCols = getFormConfigFieldColumns(tableDataRef.value?.[0], { excludeKeys });
-    return [
+    const cols = [
       ...leadingCols,
       ...beforeFormColumns,
       ...formCols,
       ...extraColumns,
       ...afterFormColumns,
-      { label: "鍒涘缓鏃堕棿", prop: "createTime", width: 170 },
+    ];
+    if (showCreateTime !== false) {
+      cols.push({ label: "鍒涘缓鏃堕棿", prop: "createTime", width: 170 });
+    }
+    cols.push(
       {
         label: "瀹℃壒鐘舵��",
         prop: "approvalStatus",
@@ -111,6 +115,7 @@
         width: actionWidth,
         operation: buildTableActions(),
       },
-    ];
+    );
+    return cols;
   });
 }
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
index fd56eca..b5cd8c3 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
@@ -12,6 +12,8 @@
   TRAVEL_REIMBURSE: "travel_reimburse",
   COST_REIMBURSE: "cost_reimburse",
   ENTERPRISE_NEWS: "enterprise_news",
+  VEHICLE: "vehicle",
+  VEHICLE_DELAY: "vehicle_delay",
 };
 
 /** 瀹℃壒瀹炰緥 listPage / 淇濆瓨 浣跨敤鐨� businessType 鏋氫妇 */
@@ -24,6 +26,8 @@
   [APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE]: 16,
   [APPROVAL_MODULE_KEYS.COST_REIMBURSE]: 17,
   [APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS]: 18,
+  [APPROVAL_MODULE_KEYS.VEHICLE]: 19,
+  [APPROVAL_MODULE_KEYS.VEHICLE_DELAY]: 20,
 };
 
 /** @type {Record<string, import('./approvalModuleRegistry.js').ApprovalModuleConfig>} */
@@ -81,6 +85,18 @@
     businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS],
     typeLabels: ["浼佷笟鏂伴椈", "鏂伴椈", "鏂伴椈鍙戝竷"],
   },
+  [APPROVAL_MODULE_KEYS.VEHICLE]: {
+    label: "杞﹁締瀹℃壒",
+    approvalType: "vehicle",
+    businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.VEHICLE],
+    typeLabels: ["杞﹁締", "杞﹁締瀹℃壒", "鐢ㄨ溅鐢宠", "杞﹁締浣跨敤"],
+  },
+  [APPROVAL_MODULE_KEYS.VEHICLE_DELAY]: {
+    label: "杞﹁締寤舵湡瀹℃壒",
+    approvalType: "vehicle_delay",
+    businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.VEHICLE_DELAY],
+    typeLabels: ["杞﹁締寤舵湡", "寤舵湡鐢宠", "杞﹁締寤舵湡瀹℃壒"],
+  },
 };
 
 /**
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalTemplateBindingUtils.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalTemplateBindingUtils.js
index d68016f..c11a541 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalTemplateBindingUtils.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalTemplateBindingUtils.js
@@ -40,6 +40,7 @@
     flowNodes: base.flowNodes,
     templateAttachments: JSON.parse(JSON.stringify(templateAttachments)),
     storageBlobDTOs: [],
+    formConfig: mapped.formConfig, // 鍘熷 formConfig JSON 瀛楃涓�
   };
 }
 
@@ -80,6 +81,7 @@
     flowNodes: "flowNodes",
     templateAttachments: "templateAttachments",
     storageBlobDTOs: "storageBlobDTOs",
+    formConfig: "formConfig",
     ...fieldMap,
   };
   Object.entries(map).forEach(([srcKey, destKey]) => {
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/components/FormConfigEditor.vue b/src/views/officeProcessAutomation/ApproveManage/approve-template/components/FormConfigEditor.vue
index 6880f3f..3160c24 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/components/FormConfigEditor.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/components/FormConfigEditor.vue
@@ -239,6 +239,18 @@
               @change="emitOut"
             />
             <el-date-picker
+              v-else-if="field.type === 'datetime'"
+              v-model="field.defaultValue"
+              type="datetime"
+              placeholder="閫夊~"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              style="width: 100%"
+              :disabled="isFieldLocked(field)"
+              clearable
+              @change="emitOut"
+            />
+            <el-date-picker
               v-else-if="field.type === 'datetimerange'"
               v-model="field.defaultValue"
               type="datetimerange"
@@ -490,6 +502,7 @@
 function resetDefaultValueForType(field) {
   if (field.type === "number") field.defaultValue = undefined;
   else if (field.type === "datetimerange") field.defaultValue = [];
+  else if (field.type === "datetime") field.defaultValue = "";
   else field.defaultValue = "";
 }
 
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
index c1f66bd..f18c0d2 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/formConfigUtils.js
@@ -13,6 +13,7 @@
   { value: "textarea", label: "澶氳鏂囨湰" },
   { value: "number", label: "鏁板瓧" },
   { value: "date", label: "鏃ユ湡" },
+  { value: "datetime", label: "鏃ユ湡鏃堕棿" },
   { value: "datetimerange", label: "鏃ユ湡鏃堕棿鑼冨洿" },
   { value: "select", label: "涓嬫媺閫夋嫨" },
 ];
@@ -59,6 +60,56 @@
       { key: "dateRange", label: "璇峰亣鏃堕棿", type: "datetimerange", required: true },
     ],
   },
+  {
+    key: "vehicle",
+    label: "杞﹁締瀹℃壒",
+    summaryPlaceholder: "璇峰~鍐欒溅杈嗕娇鐢ㄧ敵璇蜂俊鎭�",
+    fields: [
+      { key: "vehicleNo", label: "杞︾墝鍙�", type: "select", required: true, optionSource: "vehicle_list", placeholder: "璇烽�夋嫨杞﹁締" },
+      { key: "driver", label: "椹鹃┒鍛�", type: "text", required: true },
+      { key: "purpose", label: "鐢ㄨ溅浜嬬敱", type: "textarea", required: true, rows: 2 },
+      { key: "useDateRange", label: "杞﹁締浣跨敤鏃堕棿", type: "datetimerange", required: true },
+      { key: "destination", label: "鐩殑鍦�", type: "text", required: true },
+      { key: "passengers", label: "涔樿溅浜烘暟", type: "number", required: false, min: 1, precision: 0 },
+      { key: "startMileage", label: "璧峰鍏噷鏁�", type: "number", required: true, min: 0, precision: 1 },
+      { key: "startMileagePhoto", label: "璧峰鍏噷鏁扮収鐗�", type: "image", required: false },
+      { key: "estimatedEndMileage", label: "棰勮缁撴潫鍏噷鏁�", type: "number", required: false, min: 0, precision: 1 },
+      { key: "remark", label: "澶囨敞", type: "textarea", required: false, rows: 2 },
+    ],
+  },
+  {
+    key: "vehicle_return",
+    label: "杞﹁締杩樿溅瀹℃壒",
+    summaryPlaceholder: "璇峰~鍐欒溅杈嗚繕杞︿俊鎭�",
+    fields: [
+      { key: "vehicleNo", label: "杞︾墝鍙�", type: "select", required: true, optionSource: "vehicle_list", placeholder: "璇烽�夋嫨杞﹁締" },
+      { key: "driver", label: "椹鹃┒鍛�", type: "text", required: true },
+      { key: "originalApprovalNo", label: "鍘熷鎵瑰崟鍙�", type: "text", required: true },
+      { key: "returnDate", label: "杩樿溅鏃ユ湡", type: "date", required: true },
+      { key: "endMileage", label: "缁撴潫鍏噷鏁�", type: "number", required: true, min: 0, precision: 1 },
+      { key: "endMileagePhoto", label: "缁撴潫鍏噷鏁扮収鐗�", type: "image", required: false },
+      { key: "actualMileage", label: "瀹為檯琛岄┒鍏噷鏁�", type: "number", required: false, min: 0, precision: 1 },
+      { key: "extendDays", label: "寤舵湡澶╂暟", type: "number", required: false, min: 0, precision: 0 },
+      { key: "extendReason", label: "寤舵湡鍘熷洜", type: "textarea", required: false, rows: 2 },
+      { key: "vehicleStatus", label: "杞﹁締鐘舵��", type: "select", required: true, options: [{ label: "瀹屽ソ", value: "good" }, { label: "杞诲井鎹熷潖", value: "minor_damage" }, { label: "闇�瑕佺淮淇�", value: "need_repair" }] },
+      { key: "remark", label: "澶囨敞", type: "textarea", required: false, rows: 2 },
+    ],
+  },
+  {
+    key: "vehicle_extend",
+    label: "杞﹁締寤舵湡瀹℃壒",
+    summaryPlaceholder: "杞﹁締鍒版湡鍚庣敵璇峰欢鏈熶娇鐢�",
+    fields: [
+      { key: "vehicleNo", label: "杞︾墝鍙�", type: "select", required: true, optionSource: "vehicle_list", placeholder: "璇烽�夋嫨杞﹁締" },
+      { key: "driver", label: "椹鹃┒鍛�", type: "text", required: true },
+      { key: "originalApprovalNo", label: "鍘熷鎵瑰崟鍙�", type: "text", required: true },
+      { key: "originalEndDate", label: "鍘熷埌鏈熸棩鏈�", type: "date", required: true },
+      { key: "extendDays", label: "寤舵湡澶╂暟", type: "number", required: true, min: 1, precision: 0 },
+      { key: "newEndDate", label: "鏂板埌鏈熸棩鏈�", type: "date", required: true },
+      { key: "extendReason", label: "寤舵湡鍘熷洜", type: "textarea", required: true, rows: 3 },
+      { key: "remark", label: "澶囨敞", type: "textarea", required: false, rows: 2 },
+    ],
+  },
 ];
 
 function newFieldUid() {
@@ -88,6 +139,7 @@
   if (dv === undefined || dv === null || dv === "") {
     if (type === "number") return undefined;
     if (type === "datetimerange") return [];
+    if (type === "datetime") return "";
     return "";
   }
   if (type === "number") {
@@ -106,6 +158,7 @@
   if (dv === undefined || dv === null) return false;
   if (type === "number") return dv !== "" && !Number.isNaN(Number(dv));
   if (type === "datetimerange") return Array.isArray(dv) && dv.length === 2;
+  if (type === "datetime") return String(dv).trim() !== "";
   if (type === "select") return dv !== "";
   return String(dv).trim() !== "";
 }
@@ -143,6 +196,7 @@
   if (f.defaultValue === undefined || f.defaultValue === null) {
     if (type === "number") return undefined;
     if (type === "datetimerange") return [];
+    if (type === "datetime") return "";
     return "";
   }
   if (type === "datetimerange" && Array.isArray(f.defaultValue)) {
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
index 99706b4..1e19bb6 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
@@ -1,16 +1,19 @@
 import { deptTreeSelect, userListNoPageByTenantId } from "@/api/system/user.js";
+import { listVehiclePage } from "@/api/officeProcessAutomation/vehicle.js";
 
 /** 涓嬫媺閫夐」鏉ユ簮锛堝啓鍏� formConfig锛屾彁浜ら〉鎸夋潵婧愭媺鍙栨暟鎹級 */
 export const SELECT_OPTION_SOURCE = {
   STATIC: "static",
   USER: "user",
   DEPT: "dept",
+  VEHICLE: "vehicle",
 };
 
 export const SELECT_OPTION_SOURCE_OPTIONS = [
   { value: SELECT_OPTION_SOURCE.STATIC, label: "鎵嬪姩閰嶇疆", desc: "鍦ㄦā鏉夸腑鑷畾涔夐�夐」鏂囨湰涓庡��" },
   { value: SELECT_OPTION_SOURCE.USER, label: "浜哄憳鍒楄〃", desc: "浠庣郴缁熺敤鎴蜂腑閫夋嫨锛屽�间负鐢ㄦ埛 ID" },
   { value: SELECT_OPTION_SOURCE.DEPT, label: "閮ㄩ棬鍒楄〃", desc: "浠庣粍缁囨灦鏋勪腑閫夋嫨锛屽�间负閮ㄩ棬 ID" },
+  { value: SELECT_OPTION_SOURCE.VEHICLE, label: "杞﹁締鍒楄〃", desc: "浠庤溅杈嗙鐞嗕腑閫夋嫨锛屽�间负杞︾墝鍙�" },
 ];
 
 export function selectOptionSourceLabel(source) {
@@ -18,7 +21,7 @@
 }
 
 export function isDynamicOptionSource(source) {
-  return source === SELECT_OPTION_SOURCE.USER || source === SELECT_OPTION_SOURCE.DEPT;
+  return source === SELECT_OPTION_SOURCE.USER || source === SELECT_OPTION_SOURCE.DEPT || source === SELECT_OPTION_SOURCE.VEHICLE;
 }
 
 function unwrapArray(payload) {
@@ -69,6 +72,14 @@
   });
 }
 
+/** 杞﹁締 鈫� 涓嬫媺 option */
+export function mapVehicleToSelectOption(v) {
+  return {
+    label: v.plateNumber || `杞﹁締${v.id}`,
+    value: v.plateNumber || String(v.id),
+  };
+}
+
 /** 鎸夊瓧娈甸厤缃В鏋愪笅鎷� options锛堥渶浼犲叆宸插姞杞界殑缂撳瓨锛� */
 export function resolveFieldSelectOptions(field, caches = {}) {
   const source = field?.optionSource || SELECT_OPTION_SOURCE.STATIC;
@@ -77,6 +88,9 @@
   }
   if (source === SELECT_OPTION_SOURCE.DEPT) {
     return caches.deptOptions || [];
+  }
+  if (source === SELECT_OPTION_SOURCE.VEHICLE) {
+    return (caches.vehicles || []).map(mapVehicleToSelectOption);
   }
   return (field?.options || []).filter((o) => o.value !== "" && o.value != null);
 }
@@ -89,13 +103,14 @@
   return hit?.label || String(val);
 }
 
-/** 鍔犺浇浜哄憳 / 閮ㄩ棬缂撳瓨锛堝澶勫鐢級 */
+/** 鍔犺浇浜哄憳 / 閮ㄩ棬 / 杞﹁締缂撳瓨锛堝澶勫鐢級 */
 export async function fetchSelectOptionCaches(sources = []) {
   const needUser = sources.includes(SELECT_OPTION_SOURCE.USER);
   const needDept = sources.includes(SELECT_OPTION_SOURCE.DEPT);
-  const caches = { users: [], deptOptions: [] };
+  const needVehicle = sources.includes(SELECT_OPTION_SOURCE.VEHICLE);
+  const caches = { users: [], deptOptions: [], vehicles: [] };
 
-  if (!needUser && !needDept) return caches;
+  if (!needUser && !needDept && !needVehicle) return caches;
 
   const tasks = [];
   if (needUser) {
@@ -123,6 +138,19 @@
         })
     );
   }
+  if (needVehicle) {
+    tasks.push(
+      listVehiclePage({ current: 1, size: 1000 })
+        .then((res) => {
+          // 杞﹁締鎺ュ彛杩斿洖鏍煎紡: { data: { records: [...], total: ... } }
+          const records = res?.data?.records;
+          caches.vehicles = Array.isArray(records) ? records : [];
+        })
+        .catch(() => {
+          caches.vehicles = [];
+        })
+    );
+  }
 
   await Promise.all(tasks);
   return caches;
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
index 8397288..98da303 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
@@ -6,12 +6,13 @@
   resolveSelectDisplayLabel,
 } from "./selectOptionSource.js";
 
-/** 涓嬫媺鍔ㄦ�侀�夐」锛氫汉鍛� / 閮ㄩ棬缂撳瓨涓庤В鏋� */
+/** 涓嬫媺鍔ㄦ�侀�夐」锛氫汉鍛� / 閮ㄩ棬 / 杞﹁締缂撳瓨涓庤В鏋� */
 export function useSelectOptionSources() {
   const loading = ref(false);
   const caches = reactive({
     users: [],
     deptOptions: [],
+    vehicles: [],
   });
 
   async function ensureForFields(fields) {
@@ -22,6 +23,7 @@
       const next = await fetchSelectOptionCaches(sources);
       caches.users = next.users;
       caches.deptOptions = next.deptOptions;
+      caches.vehicles = next.vehicles;
     } finally {
       loading.value = false;
     }
diff --git a/src/views/officeProcessAutomation/ApproveManage/vehicle-apply/index.vue b/src/views/officeProcessAutomation/ApproveManage/vehicle-apply/index.vue
new file mode 100644
index 0000000..3d18260
--- /dev/null
+++ b/src/views/officeProcessAutomation/ApproveManage/vehicle-apply/index.vue
@@ -0,0 +1,1066 @@
+<!--OA妯″潡锛氳溅杈嗗鎵圭鐞�-->
+<template>
+  <div class="app-container">
+    <!-- 杞﹁締绠$悊鍖哄煙 -->
+    <el-card class="vehicle-manage-card mb20">
+      <template #header>
+        <div class="card-header">
+          <span>杞﹁締绠$悊</span>
+          <el-button type="primary" size="small" @click="openVehicleDialog()">鏂板杞﹁締</el-button>
+        </div>
+      </template>
+      <el-table :data="vehicleList" border size="small" style="width: 100%" v-loading="vehicleLoading">
+        <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+        <el-table-column prop="plateNumber" label="杞︾墝鍙�" min-width="120" align="center" />
+        <el-table-column label="浣跨敤鐘舵��" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="vehicleStatusTagType(scope.row.status)" size="small">
+              {{ vehicleStatusLabel(scope.row.status) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="currentMileage" label="褰撳墠鍏噷鏁�" width="120" align="center" />
+        <el-table-column label="鎿嶄綔" width="150" align="center" fixed="right">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="openVehicleDialog(scope.row)">缂栬緫</el-button>
+            <el-button link type="danger" size="small" @click="handleDeleteVehicle(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="pagination-container" style="margin-top: 10px;">
+        <el-pagination
+          v-model:current-page="vehiclePage.current"
+          v-model:page-size="vehiclePage.size"
+          :page-sizes="[10, 20, 50]"
+          layout="total, sizes, prev, pager, next"
+          :total="vehiclePage.total"
+          @size-change="fetchVehicleList"
+          @current-change="fetchVehicleList"
+          size="small"
+        />
+      </div>
+    </el-card>
+
+    <!-- 鍊熷嚭璁板綍鍒楄〃 -->
+    <div class="search_form mb20">
+      <div>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
+        <el-input
+          v-model="searchForm.applicantKeyword"
+          style="width: 220px"
+          placeholder="濮撳悕鎴栫紪鍙�"
+          clearable
+          :prefix-icon="Search"
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">杞︾墝鍙凤細</span>
+        <el-select
+          v-model="searchForm.vehiclePlateNumber"
+          style="width: 180px"
+          placeholder="璇烽�夋嫨杞︾墝鍙�"
+          clearable
+          filterable
+        >
+          <el-option
+            v-for="item in vehicleList"
+            :key="item.id"
+            :label="item.plateNumber"
+            :value="item.plateNumber"
+          />
+        </el-select>
+        <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="resetSearch">閲嶇疆</el-button>
+      </div>
+      <div>
+        <el-button type="danger" @click="batchDeleteBorrow" :disabled="!selectedBorrowRows?.length">鎵归噺鍒犻櫎</el-button>
+        <el-button type="primary" @click="openAddWithTemplate">鏂板鍊熷嚭鐢宠</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable
+        rowKey="id"
+        :column="borrowTableColumn"
+        :tableData="borrowTableData"
+        :page="borrowPage"
+        :isSelection="true"
+        :tableLoading="borrowTableLoading"
+        @pagination="onBorrowPagination"
+        @selection-change="(rows) => selectedBorrowRows = rows"
+        :total="borrowPage.total"
+      />
+    </div>
+
+    <!-- 鍊熷嚭鐢宠鎻愪氦瀵硅瘽妗� -->
+    <ApprovalInstanceSubmitDialog
+      v-model="submitDialog.visible"
+      :title="submitDialogTitle"
+      :form="submitForm"
+      :rules="submitFormRules"
+      :fields="submitFormFields"
+      :active-template="activeTemplate"
+      :user-options="flowUserOptions"
+      :is-edit="isSubmitEdit"
+      :saving="submitSaving"
+      :form-ref="submitFormRef"
+      flow-attachments-only
+      @submit="onSubmit"
+    >
+      <template #before="{ form, fields }">
+        <FormPayloadFields :fields="displayTemplateFields(fields)" :form-payload="form.formPayload" />
+        <!-- 杞﹁締浣跨敤淇℃伅灞曠ず -->
+        <el-row :gutter="24" v-if="vehicleUseInfoDisplay(form)">
+          <el-col :span="24">
+            <el-form-item label="杞﹁締鐘舵��">
+              <el-tag :type="vehicleStatusType(form.formPayload?.vehicleNo)">
+                {{ vehicleStatusText(form.formPayload?.vehicleNo) }}
+              </el-tag>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </template>
+    </ApprovalInstanceSubmitDialog>
+
+    <ApprovalTemplateBindDialog
+      v-model:visible="templateBindVisible"
+      :module-key="APPROVAL_MODULE_KEYS.VEHICLE"
+      skip-form-confirm
+      @confirm="onTemplateBound"
+      @closed="onTemplateBindClosed"
+    />
+
+    <ApprovalInstanceDetailDialog
+      v-model="detailDialog.visible"
+      title="杞﹁締瀹℃壒璇︽儏"
+      :row="detailRow"
+      @edit="openEditFromDetail"
+    />
+
+    <!-- 杞﹁締绠$悊瀵硅瘽妗� -->
+    <el-dialog
+      v-model="vehicleDialog.visible"
+      :title="vehicleDialog.isEdit ? '缂栬緫杞﹁締' : '鏂板杞﹁締'"
+      width="500px"
+      :close-on-click-modal="false"
+    >
+      <el-form :model="vehicleForm" :rules="vehicleRules" ref="vehicleFormRef" label-width="100px">
+        <el-form-item label="杞︾墝鍙�" prop="plateNumber">
+          <el-input v-model="vehicleForm.plateNumber" placeholder="璇疯緭鍏ヨ溅鐗屽彿" />
+        </el-form-item>
+        <el-form-item label="浣跨敤鐘舵��" prop="status">
+          <el-select v-model="vehicleForm.status" placeholder="璇烽�夋嫨浣跨敤鐘舵��" style="width: 100%">
+            <el-option label="闂茬疆" value="idle" />
+            <el-option label="浣跨敤涓�" value="in_use" />
+            <el-option label="缁翠慨涓�" value="repair" />
+            <el-option label="淇濆吇涓�" value="maintenance" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="褰撳墠鍏噷鏁�" prop="currentMileage">
+          <el-input-number
+            v-model="vehicleForm.currentMileage"
+            :min="0"
+            :precision="1"
+            :step="0.1"
+            controls-position="right"
+            style="width: 100%"
+            placeholder="璇疯緭鍏ュ綋鍓嶅叕閲屾暟"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="vehicleDialog.visible = false">鍙栨秷</el-button>
+        <el-button type="primary" :loading="vehicleSaveLoading" @click="saveVehicle">淇濆瓨</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 寤舵湡鐢宠瀵硅瘽妗嗭紙浣跨敤瀹℃壒妯℃澘锛� -->
+    <ApprovalInstanceSubmitDialog
+      v-model="extendSubmitDialog.visible"
+      :title="extendSubmitDialogTitle"
+      :form="extendSubmitForm"
+      :rules="extendSubmitFormRules"
+      :fields="extendSubmitFormFields"
+      :active-template="extendActiveTemplate"
+      :user-options="flowUserOptions"
+      :is-edit="false"
+      :saving="extendSubmitSaving"
+      :form-ref="extendSubmitFormRef"
+      flow-attachments-only
+      @submit="onExtendSubmit"
+    >
+      <template #before="{ form, fields }">
+        <FormPayloadFields :fields="fields" :form-payload="form.formPayload" />
+        <!-- 寤舵湡淇℃伅灞曠ず -->
+        <el-row :gutter="24">
+          <el-col :span="12">
+            <el-form-item label="鍘熷鎵瑰崟鍙�">
+              <el-input v-model="extendSourceRow.instanceNo" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="杞︾墝鍙�">
+              <el-input v-model="extendSourceRow.vehiclePlateNumber" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </template>
+    </ApprovalInstanceSubmitDialog>
+
+    <ApprovalTemplateBindDialog
+      v-model:visible="extendTemplateBindVisible"
+      :module-key="APPROVAL_MODULE_KEYS.VEHICLE_DELAY"
+      skip-form-confirm
+      @confirm="onExtendTemplateBound"
+      @closed="onExtendTemplateBindClosed"
+    />
+
+    <!-- 褰掕繕杞﹁締瀵硅瘽妗� -->
+    <el-dialog
+      v-model="returnDialog.visible"
+      title="褰掕繕杞﹁締"
+      width="600px"
+      :close-on-click-modal="false"
+    >
+      <el-form :model="returnForm" :rules="returnRules" ref="returnFormRef" label-width="120px">
+        <el-form-item label="鍊熷嚭鍗曞彿">
+          <el-input v-model="returnForm.instanceNo" disabled />
+        </el-form-item>
+        <el-form-item label="杞︾墝鍙�">
+          <el-input v-model="returnForm.vehiclePlateNumber" disabled />
+        </el-form-item>
+        <el-form-item label="瀹為檯褰掕繕鏃堕棿" prop="actualReturnTime" required>
+          <el-date-picker
+            v-model="returnForm.actualReturnTime"
+            type="datetime"
+            placeholder="璇烽�夋嫨瀹為檯褰掕繕鏃堕棿"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="闄勪欢">
+          <AttachmentUploadImage
+            v-model:fileList="returnForm.returnStorageBlobDTOs"
+            :limit="5"
+            button-text="涓婁紶褰掕繕闄勪欢"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" :loading="returnLoading" @click="submitReturn">纭褰掕繕</el-button>
+        <el-button @click="returnDialog.visible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import dayjs from "dayjs";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { computed, onMounted, reactive, ref } from "vue";
+import AttachmentUploadImage from "@/components/AttachmentUpload/image/index.vue";
+import {
+  deleteVehicle,
+  listVehiclePage,
+  saveVehicle as saveVehicleApi,
+  updateVehicle,
+  saveBorrow,
+  listBorrowPage,
+  deleteBorrow,
+} from "@/api/officeProcessAutomation/vehicle.js";
+import FormPayloadFields from "../approve-list/components/FormPayloadFields.vue";
+import ApprovalInstanceDetailDialog from "../approve-shared/components/ApprovalInstanceDetailDialog.vue";
+import ApprovalInstanceSubmitDialog from "../approve-shared/components/ApprovalInstanceSubmitDialog.vue";
+import ApprovalTemplateBindDialog from "../approve-shared/components/ApprovalTemplateBindDialog.vue";
+import { buildInstanceTableColumns } from "../approve-shared/approvalInstanceFormConfigTable.js";
+import { APPROVAL_MODULE_KEYS } from "../approve-shared/approvalModuleRegistry.js";
+import { useApprovalInstanceModule } from "../approve-shared/useApprovalInstanceModule.js";
+import { useFlowUserOptions } from "../approve-shared/useFlowUserOptions.js";
+
+// ==================== 杞﹁締绠$悊 ====================
+const vehicleList = ref([]);
+const vehicleLoading = ref(false);
+const vehicleSaveLoading = ref(false);
+const vehiclePage = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+
+const vehicleDialog = reactive({
+  visible: false,
+  isEdit: false,
+});
+const vehicleFormRef = ref(null);
+const vehicleForm = reactive({
+  id: null,
+  plateNumber: "",
+  status: "idle",
+  currentMileage: 0,
+});
+const vehicleRules = {
+  plateNumber: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿", trigger: "blur" }],
+  status: [{ required: true, message: "璇烽�夋嫨浣跨敤鐘舵��", trigger: "change" }],
+};
+
+// 鑾峰彇杞﹁締鍒楄〃
+async function fetchVehicleList() {
+  vehicleLoading.value = true;
+  try {
+    const res = await listVehiclePage({
+      current: vehiclePage.current,
+      size: vehiclePage.size,
+    });
+    if (res.code === 200) {
+      vehicleList.value = (res.data?.records || []).map((item) => ({
+        id: item.id,
+        plateNumber: item.plateNumber,
+        status: mapVehicleStatusFromApi(item.status),
+        currentMileage: item.mileage || 0,
+      }));
+      vehiclePage.total = res.data?.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error("鑾峰彇杞﹁締鍒楄〃澶辫触");
+  } finally {
+    vehicleLoading.value = false;
+  }
+}
+
+// 鐘舵�佹槧灏�
+function mapVehicleStatusFromApi(apiStatus) {
+  const statusMap = {
+    IDLE: "idle",
+    IN_USE: "in_use",
+    MAINTENANCE: "repair",
+    SCRAPPED: "archived",
+  };
+  return statusMap[apiStatus] || "idle";
+}
+
+function mapVehicleStatusToApi(frontendStatus) {
+  const statusMap = {
+    idle: "IDLE",
+    in_use: "IN_USE",
+    repair: "MAINTENANCE",
+    archived: "SCRAPPED",
+  };
+  return statusMap[frontendStatus] || "IDLE";
+}
+
+function vehicleStatusTagType(status) {
+  const typeMap = {
+    idle: "success",
+    in_use: "warning",
+    repair: "danger",
+    maintenance: "info",
+  };
+  return typeMap[status] || "info";
+}
+
+function vehicleStatusLabel(status) {
+  const labelMap = {
+    idle: "闂茬疆",
+    in_use: "浣跨敤涓�",
+    repair: "缁翠慨涓�",
+    maintenance: "淇濆吇涓�",
+  };
+  return labelMap[status] || status;
+}
+
+// 杞﹁締绠$悊瀵硅瘽妗�
+function openVehicleDialog(row = null) {
+  vehicleDialog.isEdit = !!row;
+  if (row) {
+    vehicleForm.id = row.id;
+    vehicleForm.plateNumber = row.plateNumber;
+    vehicleForm.status = row.status;
+    vehicleForm.currentMileage = row.currentMileage;
+  } else {
+    vehicleForm.id = null;
+    vehicleForm.plateNumber = "";
+    vehicleForm.status = "idle";
+    vehicleForm.currentMileage = 0;
+  }
+  vehicleDialog.visible = true;
+}
+
+// 淇濆瓨杞﹁締
+async function saveVehicle() {
+  if (!vehicleFormRef.value) return;
+  await vehicleFormRef.value.validate(async (valid) => {
+    if (!valid) return;
+    vehicleSaveLoading.value = true;
+    try {
+      const apiData = {
+        plateNumber: vehicleForm.plateNumber,
+        mileage: vehicleForm.currentMileage,
+        status: mapVehicleStatusToApi(vehicleForm.status),
+      };
+      
+      if (vehicleDialog.isEdit) {
+        await updateVehicle({ ...apiData, id: vehicleForm.id });
+        ElMessage.success("杞﹁締淇℃伅宸叉洿鏂�");
+      } else {
+        await saveVehicleApi(apiData);
+        ElMessage.success("杞﹁締宸叉坊鍔�");
+      }
+      vehicleDialog.visible = false;
+      fetchVehicleList();
+    } catch (error) {
+      ElMessage.error(error?.message || "淇濆瓨澶辫触");
+    } finally {
+      vehicleSaveLoading.value = false;
+    }
+  });
+}
+
+// 鍒犻櫎杞﹁締
+async function handleDeleteVehicle(row) {
+  try {
+    await ElMessageBox.confirm(`纭畾瑕佸垹闄よ溅杈� ${row.plateNumber} 鍚楋紵`, "鎻愮ず", {
+      type: "warning",
+    });
+    await deleteVehicle([row.id]);
+    ElMessage.success("杞﹁締宸插垹闄�");
+    fetchVehicleList();
+  } catch {
+    // 鐢ㄦ埛鍙栨秷
+  }
+}
+
+// ==================== 鍊熷嚭鐢宠锛堜娇鐢ㄥ鎵规ā鏉匡級 ====================
+
+// 鏌ユ壘杞﹁締浣跨敤鏃堕棿瀛楁锛堟敮鎸� datetimerange 鍜� datetime 绫诲瀷锛�
+function findVehicleUseTimeField(fields = []) {
+  return (
+    fields.find((f) => (f?.type === "datetimerange" || f?.type === "datetime") && String(f?.label || "").includes("杞﹁締浣跨敤鏃堕棿")) ||
+    fields.find((f) => f?.type === "datetimerange" && f?.key === "useDateRange") ||
+    fields.find((f) => f?.type === "datetimerange") ||
+    null
+  );
+}
+
+// 鏌ユ壘杞︾墝鍙峰瓧娈�
+function findVehicleNoField(fields = []) {
+  return (
+    fields.find((f) => String(f?.label || "").includes("杞︾墝鍙�")) ||
+    fields.find((f) => f?.key === "vehicleNo") ||
+    null
+  );
+}
+
+// 瑙f瀽鏃堕棿鑼冨洿
+function resolveTimeRange(payload, timeField) {
+  if (!timeField?.key) return { start: "", end: "" };
+  const val = payload?.[timeField.key];
+  if (!Array.isArray(val) || val.length < 2) return { start: "", end: "" };
+  return { start: val[0] || "", end: val[1] || "" };
+}
+
+// 璁$畻澶╂暟
+function computeDays(startStr, endStr) {
+  if (!startStr || !endStr) return null;
+  const t0 = dayjs(startStr);
+  const t1 = dayjs(endStr);
+  if (!t0.isValid() || !t1.isValid() || !t1.isAfter(t0)) return null;
+  const days = t1.diff(t0, "millisecond") / (24 * 60 * 60 * 1000);
+  return Math.round(days * 100) / 100;
+}
+
+// 鏄剧ず妯℃澘瀛楁锛堣繃婊ゆ帀宸插湪鑷畾涔夊尯鍩熸樉绀虹殑瀛楁锛�
+function displayTemplateFields(fields = []) {
+  return (fields || []).filter((f) => {
+    const label = String(f?.label || "");
+    return !label.includes("棰勮浣跨敤澶╂暟") && !label.includes("杞﹁締鐘舵��");
+  });
+}
+
+// 杞﹁締浣跨敤淇℃伅鏄剧ず
+function vehicleUseInfoDisplay(form) {
+  const vehicleNoField = findVehicleNoField(form.formFieldDefs);
+  return !!vehicleNoField?.key && !!form.formPayload?.[vehicleNoField?.key];
+}
+
+// 杞﹁締浣跨敤鏃堕暱鏄剧ず
+function vehicleDurationDisplay(form) {
+  const useTimeField = findVehicleUseTimeField(form.formFieldDefs);
+  const { start, end } = resolveTimeRange(form.formPayload, useTimeField);
+  const d = computeDays(start, end);
+  return d == null ? "" : String(d);
+}
+
+// 杞﹁締鐘舵�佹爣绛剧被鍨�
+function vehicleStatusType(vehicleNo) {
+  const vehicle = vehicleList.value.find((v) => v.plateNumber === vehicleNo);
+  if (!vehicle) return "info";
+  const typeMap = {
+    idle: "success",
+    in_use: "warning",
+    repair: "danger",
+    maintenance: "info",
+  };
+  return typeMap[vehicle.status] || "info";
+}
+
+// 杞﹁締鐘舵�佹爣绛炬枃瀛�
+function vehicleStatusText(vehicleNo) {
+  const vehicle = vehicleList.value.find((v) => v.plateNumber === vehicleNo);
+  if (!vehicle) return "鏈煡";
+  const labelMap = {
+    idle: "闂茬疆",
+    in_use: "浣跨敤涓�",
+    repair: "缁翠慨涓�",
+    maintenance: "淇濆吇涓�",
+  };
+  return labelMap[vehicle.status] || vehicle.status;
+}
+
+// 楠岃瘉杞﹁締浣跨敤鏃堕棿锛堢洰鍓嶄笉鍋氭椂闂村厛鍚庢牎楠岋紝鐢卞悗绔鐞嗭級
+function validateVehicleUseTime() {
+  // 鏃堕棿鏍¢獙宸茬Щ闄�
+}
+
+const searchForm = reactive({
+  instanceNo: "",
+  applicantKeyword: "",
+  vehiclePlateNumber: "",
+});
+
+// ==================== 鍊熷嚭璁板綍鍒楄〃鏌ヨ ====================
+const borrowTableData = ref([]);
+const borrowTableLoading = ref(false);
+const borrowPage = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+const selectedBorrowRows = ref([]);
+
+// 鏌ヨ鍊熷嚭璁板綍鍒楄〃
+async function fetchBorrowList() {
+  borrowTableLoading.value = true;
+  try {
+    const res = await listBorrowPage({
+      current: borrowPage.current,
+      size: borrowPage.size,
+      borrowNo: searchForm.instanceNo,
+      applicantName: searchForm.applicantKeyword,
+      vehiclePlateNumber: searchForm.vehiclePlateNumber,
+    });
+    if (res.code === 200) {
+      borrowTableData.value = res.data?.records || [];
+      borrowPage.total = res.data?.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error(error?.message || "鏌ヨ澶辫触");
+  } finally {
+    borrowTableLoading.value = false;
+  }
+}
+
+// 鍊熷嚭璁板綍鍒嗛〉
+function onBorrowPagination(obj) {
+  borrowPage.current = obj.page;
+  borrowPage.size = obj.limit;
+  fetchBorrowList();
+}
+
+// 鎵归噺鍒犻櫎鍊熷嚭璁板綍
+async function batchDeleteBorrow() {
+  if (!selectedBorrowRows.value?.length) {
+    ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑璁板綍");
+    return;
+  }
+  try {
+    await ElMessageBox.confirm("纭畾鍒犻櫎閫変腑鐨勫�熷嚭璁板綍鍚楋紵", "鎻愮ず", {
+      type: "warning",
+    });
+    const ids = selectedBorrowRows.value.map(row => row.id);
+    const res = await deleteBorrow(ids);
+    if (res.code === 200) {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      selectedBorrowRows.value = [];
+      fetchBorrowList();
+    } else {
+      ElMessage.error(res.msg || "鍒犻櫎澶辫触");
+    }
+  } catch (error) {
+    if (error !== "cancel") {
+      ElMessage.error(error?.message || "鍒犻櫎澶辫触");
+    }
+  }
+}
+
+const mod = useApprovalInstanceModule({
+  moduleKey: APPROVAL_MODULE_KEYS.VEHICLE,
+  beforeSave: validateVehicleUseTime,
+});
+
+const {
+  tableData,
+  tableLoading,
+  page,
+  detailDialog,
+  detailRow,
+  submitDialog,
+  submitForm,
+  submitFormRef,
+  submitSaving,
+  isSubmitEdit,
+  activeTemplate,
+  submitFormFields,
+  submitFormRules,
+  submitDialogTitle,
+  templateBindVisible,
+  handleQuery,
+  initModuleList,
+  pagination,
+  openAddWithTemplate,
+  onTemplateBound,
+  onTemplateBindClosed,
+  openEditFromDetail,
+  submitInstanceForm,
+  buildTableActions,
+} = mod;
+
+const { flowUserOptions } = useFlowUserOptions();
+
+// 鍒ゆ柇鏄惁杩囦簡璁″垝褰掕繕鏃堕棿锛堟槸鍚﹀彲浠ュ欢鏈燂級
+function canExtend(row) {
+  if (!["pending", "approved"].includes(row?.approvalStatus) || row?.businessType !== 19) {
+    return false;
+  }
+  // 浠庤溅杈嗕娇鐢ㄦ椂闂村瓧娈佃幏鍙栬鍒掑綊杩樻椂闂�
+  const useTimeField = findVehicleUseTimeField(row.formFieldDefs || []);
+  if (useTimeField?.key && row.formPayload?.[useTimeField.key]) {
+    const timeRange = row.formPayload[useTimeField.key];
+    if (Array.isArray(timeRange) && timeRange.length >= 2) {
+      const plannedReturnTime = dayjs(timeRange[1]);
+      // 杩囦簡璁″垝褰掕繕鏃堕棿鎵嶈兘寤舵湡
+      return dayjs().isAfter(plannedReturnTime);
+    }
+  }
+  return false;
+}
+
+// 鍊熷嚭鐘舵�佹爣绛剧被鍨�
+function borrowStatusTagType(status) {
+  const typeMap = {
+    DRAFT: "info",
+    IN_APPROVAL: "warning",
+    BORROWING: "success",
+    RETURNED: "",
+    REJECTED: "danger",
+  };
+  return typeMap[status] || "info";
+}
+
+// 鍊熷嚭鐘舵�佹枃鏈�
+function borrowStatusLabel(status) {
+  const labelMap = {
+    DRAFT: "鑽夌",
+    IN_APPROVAL: "瀹℃壒涓�",
+    BORROWING: "鍊熷嚭涓�",
+    RETURNED: "宸插綊杩�",
+    REJECTED: "宸查┏鍥�",
+  };
+  return labelMap[status] || status;
+}
+
+// 寤舵湡鐘舵�佹爣绛剧被鍨�
+function extendStatusTagType(status) {
+  const typeMap = {
+    NONE: "info",
+    PENDING: "warning",
+    APPROVED: "success",
+    REJECTED: "danger",
+  };
+  return typeMap[status] || "info";
+}
+
+// 寤舵湡鐘舵�佹枃鏈�
+function extendStatusLabel(status) {
+  const labelMap = {
+    NONE: "鏈敵璇�",
+    PENDING: "瀹℃壒涓�",
+    APPROVED: "宸查�氳繃",
+    REJECTED: "宸查┏鍥�",
+  };
+  return labelMap[status] || status;
+}
+
+// 鍒ゆ柇鏄惁鍙互寤舵湡锛堝�熷嚭涓笖鏈敵璇峰欢鏈熸垨寤舵湡宸查┏鍥烇級
+function canExtendBorrow(row) {
+  if (row.borrowStatus !== "BORROWING") return false;
+  if (row.extendStatus === "PENDING" || row.extendStatus === "APPROVED") return false;
+  // 杩囦簡璁″垝褰掕繕鏃堕棿鎵嶈兘寤舵湡
+  if (row.plannedReturnTime) {
+    return dayjs().isAfter(dayjs(row.plannedReturnTime));
+  }
+  return false;
+}
+
+// 鍒ゆ柇鏄惁鍙互褰掕繕锛堝�熷嚭涓級
+function canReturnBorrow(row) {
+  return row.borrowStatus === "BORROWING";
+}
+
+// 鏋勫缓鍊熷嚭璁板綍琛ㄦ牸鍒�
+const borrowTableColumn = computed(() => [
+  { label: "鍊熷嚭鍗曞彿", prop: "borrowNo", minWidth: 160, align: "center" },
+  { label: "杞︾墝鍙�", prop: "vehiclePlateNumber", minWidth: 120, align: "center" },
+  { label: "鐢宠浜�", prop: "applicantName", minWidth: 100, align: "center" },
+  { label: "鍊熷嚭鍘熷洜", prop: "borrowReason", minWidth: 150, align: "center", showOverflowTooltip: true },
+  { label: "鍊熷嚭鏃堕棿", prop: "borrowStartTime", width: 160, align: "center" },
+  { label: "璁″垝褰掕繕", prop: "plannedReturnTime", width: 160, align: "center" },
+  { label: "瀹為檯褰掕繕", prop: "actualReturnTime", width: 160, align: "center" },
+  {
+    label: "鍊熷嚭鐘舵��",
+    prop: "borrowStatus",
+    width: 100,
+    align: "center",
+    dataType: "tag",
+    formatData: borrowStatusLabel,
+    formatType: borrowStatusTagType,
+  },
+  {
+    label: "寤舵湡鐘舵��",
+    prop: "extendStatus",
+    width: 100,
+    align: "center",
+    dataType: "tag",
+    formatData: extendStatusLabel,
+    formatType: extendStatusTagType,
+  },
+  { label: "鍒涘缓鏃堕棿", prop: "createTime", width: 160, align: "center" },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: "right",
+    width: 150,
+    operation: [
+      {
+        name: "寤舵湡",
+        type: "text",
+        disabled: (row) => !canExtendBorrow(row),
+        clickFun: (row) => openExtendDialog(row),
+      },
+      {
+        name: "褰掕繕",
+        type: "text",
+        disabled: (row) => !canReturnBorrow(row),
+        clickFun: (row) => openReturnDialog(row),
+      },
+    ],
+  },
+]);
+
+async function onSearch() {
+  // 鍚屾椂鍒锋柊杞﹁締鍒楄〃锛堢敤浜庤溅鐗屽彿涓嬫媺閫夋嫨锛�
+  await fetchVehicleList();
+  // 浣跨敤鍊熷嚭璁板綍鏌ヨ鎺ュ彛
+  fetchBorrowList();
+}
+
+function resetSearch() {
+  searchForm.instanceNo = "";
+  searchForm.applicantKeyword = "";
+  searchForm.vehiclePlateNumber = "";
+  onSearch();
+}
+
+function onPagination(obj) {
+  pagination(obj, searchForm);
+}
+
+async function onSubmit() {
+  // 鏂板鍊熷嚭鐢宠锛屽彧璋冪敤 saveBorrow 鎺ュ彛
+  if (!isSubmitEdit.value) {
+    try {
+      // 浠庤〃鍗曟暟鎹腑鑾峰彇鍊熷嚭淇℃伅
+      const formPayload = submitForm.formPayload || {};
+      const fields = submitForm.formFieldDefs || [];
+      
+      // 鏌ユ壘杞﹁締閫夋嫨瀛楁锛堝厛灏濊瘯浣跨敤椤圭洰涓凡鏈夌殑鏌ユ壘鏂瑰紡锛屽啀鍏煎鐩存帴鍙栧�硷級
+      const vehicleField = findVehicleNoField(fields);
+      
+      // 杞﹁締閫夋嫨瀛楁鐨勫�兼槸杞︾墝鍙凤紝闇�瑕佷粠vehicleList涓牴鎹溅鐗屽彿鏌ユ壘瀵瑰簲鐨勮溅杈咺D
+      // 浼樺厛浠� formPayload.vehiclePlateNumber 鑾峰彇锛屽鏋滀笉瀛樺湪鍒欓�氳繃瀛楁key鑾峰彇
+      const selectedPlateNumber = formPayload.vehiclePlateNumber || 
+        (vehicleField?.key ? formPayload[vehicleField.key] : null);
+      
+      let vehicleId = null;
+      if (selectedPlateNumber) {
+        const selectedVehicle = vehicleList.value.find(v => v.plateNumber === selectedPlateNumber);
+        vehicleId = selectedVehicle?.id;
+      }
+      
+      // 鐩存帴浣跨敤琛ㄥ崟涓殑鏃堕棿瀛楁
+      const borrowStartTime = formPayload.borrowStartTime;
+      const plannedReturnTime = formPayload.plannedReturnTime;
+      
+      if (!vehicleId) {
+        ElMessage.warning("璇烽�夋嫨杞﹁締");
+        return;
+      }
+      if (!borrowStartTime || !plannedReturnTime) {
+        ElMessage.warning("璇烽�夋嫨杞﹁締浣跨敤鏃堕棿");
+        return;
+      }
+      
+      // 鍙皟鐢� saveBorrow 鎺ュ彛锛屼笉鍐嶈皟鐢ㄥ鎵瑰疄渚嬫彁浜�
+      // formConfig 鏄ā鏉胯鎯呮帴鍙h繑鍥炵殑鍘熷 JSON 瀛楃涓�
+      console.log('=== submitForm debug ===', {
+        templateId: submitForm.templateId,
+        templateKey: submitForm.templateKey,
+        templateSnapshotTemplateId: submitForm.templateSnapshot?.templateId,
+        formConfig: submitForm.formConfig || submitForm.templateSnapshot?.formConfig || activeTemplate.value?.formConfig,
+        'formConfig type': typeof submitForm.formConfig,
+      });
+      const submitData = {
+        vehicleId: vehicleId,
+        borrowReason: formPayload.borrowReason || formPayload.reason || '',
+        borrowStartTime: borrowStartTime,
+        plannedReturnTime: plannedReturnTime,
+        borrowStatus: 'IN_APPROVAL',
+        approvalTemplateId: submitForm.templateId || submitForm.templateSnapshot?.templateId || submitForm.templateKey,
+        borrowStorageBlobDTOs: submitForm.storageBlobDTOs || [],
+        formConfig: submitForm.formConfig,
+      };
+      console.log('=== saveBorrow submitData ===', JSON.parse(JSON.stringify(submitData)));
+      const borrowRes = await saveBorrow(submitData);
+      
+      if (borrowRes.code !== 200) {
+        ElMessage.error(borrowRes.msg || "淇濆瓨鍊熷嚭璁板綍澶辫触");
+        return;
+      }
+      
+      ElMessage.success("鎻愪氦鎴愬姛");
+      submitDialog.visible = false;
+      fetchBorrowList();
+    } catch (error) {
+      ElMessage.error(error?.message || "淇濆瓨鍊熷嚭璁板綍澶辫触");
+    }
+  } else {
+    // 缂栬緫妯″紡锛岃皟鐢ㄥ鎵瑰疄渚嬫彁浜�
+    const ok = await submitInstanceForm({ skipValidate: true });
+    if (ok) ElMessage.success("淇敼鎴愬姛");
+  }
+}
+
+// ==================== 寤舵湡鐢宠锛堜娇鐢ㄥ鎵规ā鏉匡級 ====================
+const extendTemplateBindVisible = ref(false);
+const extendSubmitDialog = reactive({
+  visible: false,
+});
+const extendSubmitFormRef = ref(null);
+const extendSubmitSaving = ref(false);
+const extendSubmitForm = reactive({
+  templateId: null,
+  templateName: "",
+  formPayload: {},
+  flowNodes: [],
+  storageBlobDTOs: [],
+  formFieldDefs: [],
+});
+const extendSubmitFormRules = reactive({});
+const extendSubmitFormFields = ref([]);
+const extendActiveTemplate = ref({});
+const extendSubmitDialogTitle = computed(() => "杞﹁締寤舵湡鐢宠");
+const extendSourceRow = reactive({
+  id: null,
+  instanceNo: "",
+  vehiclePlateNumber: "",
+  originalEndDate: "",
+});
+
+// 鎵撳紑寤舵湡鐢宠瀵硅瘽妗嗭紙鍏堥�夋嫨妯℃澘锛�
+function openExtendDialog(row) {
+  // 淇濆瓨鍘熷鎵逛俊鎭�
+  extendSourceRow.id = row.id;
+  extendSourceRow.instanceNo = row.instanceNo || "";
+  // 灏濊瘯浠庡涓彲鑳界殑瀛楁鍚嶄腑鑾峰彇杞︾墝鍙�
+  const payload = row.formPayload || {};
+  extendSourceRow.vehiclePlateNumber = payload.vehicleNo || payload.plateNumber || payload.vehiclePlateNumber || payload.carNo || "";
+  // 浠庤溅杈嗕娇鐢ㄦ椂闂磋绠楀師鍒版湡鏃ユ湡
+  const useTimeField = findVehicleUseTimeField(row.formFieldDefs || []);
+  if (useTimeField?.key && row.formPayload?.[useTimeField.key]) {
+    const timeRange = row.formPayload[useTimeField.key];
+    if (Array.isArray(timeRange) && timeRange.length >= 2) {
+      extendSourceRow.originalEndDate = timeRange[1];
+    }
+  }
+  // 鎵撳紑妯℃澘閫夋嫨瀵硅瘽妗�
+  extendTemplateBindVisible.value = true;
+}
+
+// 寤舵湡妯℃澘缁戝畾纭
+function onExtendTemplateBound(payload) {
+  const { templateId, templateName, formFieldDefs, formPayload, flowNodes, templateAttachments, storageBlobDTOs } = payload;
+  extendActiveTemplate.value = { id: templateId, name: templateName, fields: formFieldDefs };
+  extendSubmitForm.templateId = templateId;
+  extendSubmitForm.templateName = templateName;
+  extendSubmitForm.formFieldDefs = formFieldDefs || [];
+  extendSubmitFormFields.value = formFieldDefs || [];
+  // 鍒濆鍖栬〃鍗曟暟鎹�
+  extendSubmitForm.formPayload = formPayload || {};
+  extendSubmitForm.flowNodes = flowNodes || [];
+  extendSubmitForm.templateAttachments = templateAttachments || [];
+  extendSubmitForm.storageBlobDTOs = storageBlobDTOs || [];
+  // 鎵撳紑鎻愪氦瀵硅瘽妗�
+  extendSubmitDialog.visible = true;
+}
+
+// 寤舵湡妯℃澘缁戝畾鍏抽棴
+function onExtendTemplateBindClosed() {
+  // 娓呯悊鐘舵��
+}
+
+// 鎻愪氦寤舵湡鐢宠
+async function onExtendSubmit() {
+  if (!extendSubmitFormRef.value) return;
+
+  extendSubmitSaving.value = true;
+  try {
+    // 鏋勫缓鎻愪氦鏁版嵁
+    // formConfig 鏄ā鏉胯鎯呮帴鍙h繑鍥炵殑鍘熷 JSON 瀛楃涓�
+    const payload = {
+      ...extendSubmitForm.formPayload,
+      originalInstanceNo: extendSourceRow.instanceNo,
+      vehiclePlateNumber: extendSourceRow.vehiclePlateNumber,
+      originalEndDate: extendSourceRow.originalEndDate,
+      formConfig: extendSubmitForm.formConfig,
+    };
+
+    // 杩欓噷璋冪敤鍒涘缓寤舵湡瀹℃壒瀹炰緥鐨凙PI
+    // await createVehicleDelayApproval({
+    //   templateId: extendSubmitForm.templateId,
+    //   formPayload: payload,
+    //   flowNodes: extendSubmitForm.flowNodes,
+    //   storageBlobDTOs: extendSubmitForm.storageBlobDTOs,
+    //   formConfig: extendSubmitForm.formFieldDefs || [],
+    // });
+
+    ElMessage.success("寤舵湡鐢宠宸叉彁浜�");
+    extendSubmitDialog.visible = false;
+    onSearch();
+  } catch (error) {
+    ElMessage.error(error?.message || "鎻愪氦澶辫触");
+  } finally {
+    extendSubmitSaving.value = false;
+  }
+}
+
+// ==================== 褰掕繕杞﹁締 ====================
+const returnDialog = reactive({
+  visible: false,
+});
+const returnLoading = ref(false);
+const returnFormRef = ref(null);
+const returnForm = reactive({
+  id: null,
+  instanceNo: "",
+  vehiclePlateNumber: "",
+  actualReturnTime: "",
+  returnStorageBlobDTOs: [],
+});
+const returnRules = {
+  actualReturnTime: [{ required: true, message: "璇烽�夋嫨瀹為檯褰掕繕鏃堕棿", trigger: "change" }],
+};
+
+// 鎵撳紑褰掕繕杞﹁締瀵硅瘽妗�
+function openReturnDialog(row) {
+  returnForm.id = row.id;
+  returnForm.instanceNo = row.instanceNo || "";
+  // 灏濊瘯浠庡涓彲鑳界殑瀛楁鍚嶄腑鑾峰彇杞︾墝鍙�
+  const payload = row.formPayload || {};
+  returnForm.vehiclePlateNumber = payload.vehicleNo || payload.plateNumber || payload.vehiclePlateNumber || payload.carNo || "";
+  returnForm.actualReturnTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
+  returnForm.returnStorageBlobDTOs = [];
+  returnDialog.visible = true;
+}
+
+// 鎻愪氦褰掕繕
+async function submitReturn() {
+  if (!returnFormRef.value) return;
+  await returnFormRef.value.validate(async (valid) => {
+    if (!valid) return;
+    
+    returnLoading.value = true;
+    try {
+      // 杩欓噷璋冪敤褰掕繕杞﹁締鐨凙PI
+      // 1. 鏇存柊杞﹁締鍏噷鏁�
+      const vehicle = vehicleList.value.find((v) => v.plateNumber === returnForm.vehiclePlateNumber);
+      if (vehicle) {
+        vehicle.currentMileage += returnForm.mileage;
+        vehicle.status = returnForm.vehicleStatus === "good" ? "idle" : "repair";
+      }
+      
+      // 2. 鍒涘缓褰掕繕瀹℃壒璁板綍锛堝疄闄呴」鐩腑璋冪敤API锛�
+      // await createVehicleReturnApproval({...});
+      
+      ElMessage.success("杞﹁締褰掕繕鎴愬姛");
+      returnDialog.visible = false;
+      onSearch();
+    } catch (error) {
+      ElMessage.error(error?.message || "褰掕繕澶辫触");
+    } finally {
+      returnLoading.value = false;
+    }
+  });
+}
+
+onMounted(() => {
+  fetchVehicleList();
+  // 浣跨敤鍊熷嚭璁板綍鏌ヨ鎺ュ彛
+  fetchBorrowList();
+});
+</script>
+
+<style scoped>
+.search_form {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 12px;
+}
+
+.search_title {
+  font-size: 14px;
+  color: #606266;
+}
+
+.mb20 {
+  margin-bottom: 20px;
+}
+
+.table_list {
+  margin-top: 10px;
+}
+
+.vehicle-manage-card {
+  margin-bottom: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.pagination-container {
+  display: flex;
+  justify-content: flex-end;
+}
+</style>
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 3c52410..141dc9a 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -140,13 +140,7 @@
       <el-table-column label="鍗曚綅"
                        prop="unit"
                        width="100" />
-      <el-table-column label="璁¤垂绫诲瀷"
-                       prop="type"
-                       width="100">
-        <template #default="scope">
-          {{scope.row.type==0 ? "璁℃椂" : "璁′欢"}}
-        </template>
-      </el-table-column>
+
       <el-table-column label="鏄惁璐ㄦ"
                        prop="isQuality"
                        width="100">
@@ -217,9 +211,7 @@
                   {{ item.model }}
                   <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
                 </div>
-                <el-tag class="product-tag"
-                        :type="item.type == 1 ? 'primary' : 'success'"
-                        style="margin-left: 8px;">{{ item.type==0?'璁℃椂':'璁′欢' }}</el-tag>
+
                 <el-tag type="primary"
                         class="product-tag"
                         style="margin-left: 8px;"
@@ -476,7 +468,7 @@
                       v-else>
           <span>{{ form.unit }}</span>
         </el-form-item>
-        <el-form-item label="璁¤垂绫诲瀷"
+        <el-form-item label=""
                       prop="type">
           <el-radio-group v-model="form.type">
             <el-radio :label="0">璁℃椂</el-radio>
diff --git a/src/views/productionManagement/productionCosting/index.vue b/src/views/productionManagement/productionCosting/index.vue
index 3e79b93..4de0dea 100644
--- a/src/views/productionManagement/productionCosting/index.vue
+++ b/src/views/productionManagement/productionCosting/index.vue
@@ -139,11 +139,11 @@
       prop: "workHours",
       minWidth: 100,
     },
-    {
-      label: "宸ヨ祫",
-      prop: "wages",
-      minWidth: 100,
-    },
+    // {
+    //   label: "宸ヨ祫",
+    //   prop: "wages",
+    //   minWidth: 100,
+    // },
   ]);
 
   // 宸︿晶姹囨�诲彴璐﹀垪锛堢敓浜т汉銆佷骇閲忋�佸伐璧勩�佸悎鏍肩巼锛�
@@ -158,11 +158,11 @@
       prop: "finishedNum",
       minWidth: 100,
     },
-    {
-      label: "宸ヨ祫",
-      prop: "wages",
-      minWidth: 100,
-    },
+    // {
+    //   label: "宸ヨ祫",
+    //   prop: "wages",
+    //   minWidth: 100,
+    // },
     {
       label: "鍚堟牸鐜�",
       prop: "outputRate",
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index 28077b6..a32c5a9 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -25,24 +25,7 @@
         <el-form-item label="宸ュ簭缂栧彿" prop="no">
           <el-input v-model="formState.no"  />
         </el-form-item>
-        <el-form-item
-            label="宸ュ簭绫诲瀷"
-            prop="type"
-            :rules="[
-                {
-                required: true,
-                message: '璇烽�夋嫨宸ュ簭绫诲瀷',
-              }
-            ]"
-        >
-          <el-select v-model="formState.type" placeholder="璇烽�夋嫨宸ュ簭绫诲瀷">
-            <el-option label="璁℃椂" :value="0" />
-            <el-option label="璁′欢" :value="1" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
-          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
-        </el-form-item>
+
         <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
           <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
         </el-form-item>
@@ -88,10 +71,8 @@
 const formState = ref({
   id: props.record.id,
   name: props.record.name,
-  type: props.record.type,
   no: props.record.no,
   remark: props.record.remark,
-  salaryQuota: props.record.salaryQuota,
   isQuality: props.record.isQuality,
   inbound: props.record.inbound,
   reportWork: props.record.reportWork,
@@ -113,9 +94,7 @@
       id: newRecord.id,
       name: newRecord.name || '',
       no: newRecord.no || '',
-      type: newRecord.type,
       remark: newRecord.remark || '',
-      salaryQuota: newRecord.salaryQuota || '',
       isQuality: props.record.isQuality,
       inbound: newRecord.inbound,
       reportWork: newRecord.reportWork,
@@ -130,9 +109,7 @@
       id: props.record.id,
       name: props.record.name || '',
       no: props.record.no || '',
-      type: props.record.type,
       remark: props.record.remark || '',
-      salaryQuota: props.record.salaryQuota || '',
       isQuality: props.record.isQuality,
       inbound: props.record.inbound,
       reportWork: props.record.reportWork,
diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
index 0b3fd47..3e9a823 100644
--- a/src/views/productionManagement/productionProcess/New.vue
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -25,26 +25,7 @@
         <el-form-item label="宸ュ簭缂栧彿" prop="no">
           <el-input v-model="formState.no"  />
         </el-form-item>
-        <el-form-item
-            label="宸ュ簭绫诲瀷"
-            prop="type"
-            :rules="[
-                {
-                required: true,
-                message: '璇烽�夋嫨宸ュ簭绫诲瀷',
-              }
-            ]"
-        >
-          <el-select v-model="formState.type" placeholder="璇烽�夋嫨宸ュ簭绫诲瀷">
-            <el-option label="璁℃椂" :value="0" />
-            <el-option label="璁′欢" :value="1" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
-          <el-input v-model="formState.salaryQuota" type="number" :step="0.001">
-            <template #append>鍏�</template>
-          </el-input>
-        </el-form-item>
+
         <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
           <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
         </el-form-item>
@@ -84,9 +65,7 @@
 // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
 const formState = ref({
   name: '',
-  type: undefined,
   remark: '',
-  salaryQuota:  '',
   isQuality: false,
   inbound: false,
   reportWork: false,
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index ee49657..6c960ab 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -57,14 +57,7 @@
                         :type="process.isProduction ? 'warning' : 'info'">
                   {{ process.isProduction ? '鐢熶骇' : '涓嶇敓浜�' }}
                 </el-tag>
-                <el-tag v-if="process.type !== null && process.type !== undefined"
-                        size="small"
-                        :type="process.type == 1 ? 'primary' : 'success'"
-                        style="margin-left: 8px">
-                  {{ process.type == 0 ? '璁℃椂' : '璁′欢' }}
-                </el-tag>
               </div>
-              <span class="param-count">宸ヨ祫瀹氶: 楼{{ process.salaryQuota || 0 }}</span>
             </div>
           </div>
         </div>
@@ -118,12 +111,6 @@
           <el-input v-model="processForm.name"
                     placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
         </el-form-item>
-        <el-form-item label="宸ヨ祫瀹氶"
-                      prop="salaryQuota">
-          <el-input v-model="processForm.salaryQuota"
-                    type="number"
-                    :step="0.001" />
-        </el-form-item>
         <el-form-item label="鏄惁璐ㄦ"
                       prop="isQuality">
           <el-switch v-model="processForm.isQuality" />
@@ -131,13 +118,6 @@
         <el-form-item label="鏄惁鐢熶骇"
                       prop="isProduction">
           <el-switch v-model="processForm.isProduction" />
-        </el-form-item>
-        <el-form-item label="璁¤垂绫诲瀷"
-                      prop="type">
-          <el-radio-group v-model="processForm.type">
-            <el-radio :label="0">璁℃椂</el-radio>
-            <el-radio :label="1">璁′欢</el-radio>
-          </el-radio-group>
         </el-form-item>
         <el-form-item label="鍏宠仈璁惧"
                       prop="deviceLedgerId">
@@ -341,34 +321,17 @@
     id: null,
     no: "",
     name: "",
-    salaryQuota: null,
     isQuality: false,
     isProduction: false,
     remark: "",
     deviceLedgerId: null,
-    type: 0,
   });
   const processRules = {
     no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
     name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
-    salaryQuota: [
-      {
-        required: false,
-        message: "璇疯緭鍏ュ伐璧勫畾棰�",
-        trigger: "blur",
-        validator: (rule, value, callback) => {
-          if (isNaN(value) || value < 0) {
-            callback(new Error("宸ヨ祫瀹氶蹇呴』鏄潪璐熸暟瀛�"));
-          } else {
-            callback();
-          }
-        },
-      },
-    ],
     deviceLedgerId: [
       { required: false, message: "璇烽�夋嫨璁惧", trigger: "change" },
     ],
-    type: [{ required: false, message: "璇烽�夋嫨璁¤垂绫诲瀷", trigger: "change" }],
   };
 
   // 鍙傛暟瀵硅瘽妗�
@@ -552,12 +515,10 @@
     processForm.id = null;
     processForm.no = "";
     processForm.name = "";
-    processForm.salaryQuota = null;
     processForm.isQuality = false;
     processForm.isProduction = false;
     processForm.remark = "";
     processForm.deviceLedgerId = null;
-    processForm.type = 0;
     processDialogVisible.value = true;
   };
 
@@ -566,7 +527,6 @@
     processForm.id = process.id;
     processForm.no = process.no;
     processForm.name = process.name;
-    processForm.salaryQuota = process.salaryQuota;
     processForm.isQuality = !!process.isQuality;
     processForm.isProduction = !!process.isProduction;
     processForm.remark = process.remark || "";
@@ -574,7 +534,6 @@
     const deviceId = Number(process.deviceLedgerId);
     const hasDevice = deviceOptions.value.some(item => item.id === deviceId);
     processForm.deviceLedgerId = deviceId && hasDevice ? deviceId : null;
-    processForm.type = process.type;
     processDialogVisible.value = true;
   };
 

--
Gitblit v1.9.3