From b44addf784a1638bac1102799ed415645a373a55 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期四, 21 五月 2026 14:29:30 +0800
Subject: [PATCH] 转正申请/调岗申请/工作交接/请假申请/加班申请页面画页面,接口联调和web端保持一致 日报

---
 src/pages/oa/ApproveManage/approve-list/apply.vue |  250 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 241 insertions(+), 9 deletions(-)

diff --git a/src/pages/oa/ApproveManage/approve-list/apply.vue b/src/pages/oa/ApproveManage/approve-list/apply.vue
index 3e873a8..7d6e107 100644
--- a/src/pages/oa/ApproveManage/approve-list/apply.vue
+++ b/src/pages/oa/ApproveManage/approve-list/apply.vue
@@ -56,7 +56,7 @@
                    label-width="88"
                    input-align="right"
                    class="dynamic-form">
-            <up-form-item v-for="field in formConfigData.fields"
+            <up-form-item v-for="field in displayTemplateFields"
                           :key="field.key"
                           :label="field.label"
                           :required="!!field.required"
@@ -120,6 +120,66 @@
           </up-form>
           <view v-else
                 class="empty-hint">璇ユā鏉挎殏鏃犲~鎶ラ」</view>
+
+          <!-- 璇峰亣锛氬亣鏈熶綑棰� + 鏃堕暱鑷姩璁$畻 -->
+          <view v-if="isLeaveModule"
+                class="module-extra-block">
+            <up-form :model="extraForm"
+                     label-width="88"
+                     input-align="right"
+                     class="dynamic-form">
+              <up-form-item label="鍋囨湡浣欓"
+                            required
+                            class="form-item-inline">
+                <up-input v-model="extraForm.leaveBalanceDays"
+                          type="digit"
+                          placeholder="璇疯緭鍏ュぉ鏁�"
+                          clearable />
+              </up-form-item>
+              <up-form-item label="璇峰亣鏃堕暱"
+                            class="form-item-inline">
+                <view class="readonly-with-unit">
+                  <up-input :model-value="leaveDurationText"
+                            readonly
+                            placeholder="鏍规嵁璇峰亣鏃堕棿鑷姩璁$畻" />
+                  <text class="unit-text">澶�</text>
+                </view>
+              </up-form-item>
+            </up-form>
+          </view>
+
+          <!-- 鍔犵彮锛氭椂闀胯嚜鍔ㄨ绠� -->
+          <view v-if="isOvertimeModule"
+                class="module-extra-block">
+            <up-form label-width="88"
+                     input-align="right"
+                     class="dynamic-form">
+              <up-form-item label="鍔犵彮鏃堕暱"
+                            class="form-item-inline">
+                <view class="readonly-with-unit">
+                  <up-input :model-value="overtimeHoursText"
+                            readonly
+                            placeholder="鏍规嵁鍔犵彮鏃堕棿鑷姩璁$畻" />
+                  <text class="unit-text">灏忔椂</text>
+                </view>
+              </up-form-item>
+            </up-form>
+          </view>
+
+          <!-- 璋冨矖锛氬師宀椾綅鑷姩甯﹀嚭 -->
+          <view v-if="isTransferModule"
+                class="module-extra-block">
+            <up-form label-width="88"
+                     input-align="right"
+                     class="dynamic-form">
+              <up-form-item label="鍘熷矖浣�"
+                            class="form-item-readonly">
+                <up-input :model-value="extraForm.originalPostName"
+                          readonly
+                          placeholder="閫夋嫨鐢宠浜哄悗鑷姩甯﹀嚭" />
+              </up-form-item>
+            </up-form>
+          </view>
         </view>
 
         <view class="section-card">
@@ -200,7 +260,7 @@
 </template>
 
 <script setup>
-  import { computed, reactive, ref } from "vue";
+  import { computed, reactive, ref, watch } from "vue";
   import { onLoad } from "@dcloudio/uni-app";
   import PageHeader from "@/components/PageHeader.vue";
   import FooterButtons from "@/components/FooterButtons.vue";
@@ -212,7 +272,25 @@
   import useUserStore from "@/store/modules/user";
   import { parseTime } from "@/utils/ruoyi";
   import { getDept } from "@/api/collaborativeApproval/approvalProcess.js";
+  import { findPostOptions } from "@/api/system/post.js";
   import { userListNoPageByTenantId } from "@/api/system/user";
+  import { APPROVAL_MODULE_KEYS } from "../../_utils/approvalModuleRegistry.js";
+  import {
+    computeLeaveDurationDisplay,
+    computeOvertimeHoursDisplay,
+    displayTemplateFieldsByModule,
+    findApplicantTemplateField,
+    findLeaveTimeTemplateField,
+    findOvertimeTimeTemplateField,
+    inferModuleKeyFromRow,
+    loadModuleExtrasFromRow,
+    resolveOriginalPostName,
+    syncModuleExtrasToFormValues,
+    unwrapUserArray,
+    userById,
+    validateModuleExtras,
+    buildPostIdToNameMap,
+  } from "../../_utils/approvalModuleApplyExtras.js";
   import {
     formatDatetimerangeDisplay,
     formatFieldDateValue,
@@ -233,10 +311,12 @@
     parseFieldDateToTs,
   } from "../../_utils/approvalFormField.js";
 
-  const EDIT_STORAGE_KEY = "oa_approve_instance_edit_row";
+  import { EDIT_STORAGE_KEY } from "../../_utils/approveListUtils.js";
+
   const LEVEL_TEXT = ["", "涓�", "浜�", "涓�", "鍥�", "浜�", "鍏�", "涓�", "鍏�", "涔�", "鍗�"];
 
   const userStore = useUserStore();
+  const moduleKey = ref("");
   const templateId = ref("");
   const instanceId = ref("");
   const instanceRow = ref(null);
@@ -245,6 +325,22 @@
   const submitting = ref(false);
   const formValues = reactive({});
   const form = reactive({ title: "" });
+  const extraForm = reactive({
+    leaveBalanceDays: undefined,
+    originalPostName: "",
+  });
+  const postIdToName = ref({});
+  const transferUserPool = ref([]);
+
+  const isLeaveModule = computed(
+    () => moduleKey.value === APPROVAL_MODULE_KEYS.LEAVE
+  );
+  const isOvertimeModule = computed(
+    () => moduleKey.value === APPROVAL_MODULE_KEYS.OVERTIME
+  );
+  const isTransferModule = computed(
+    () => moduleKey.value === APPROVAL_MODULE_KEYS.TRANSFER
+  );
 
   const showDatePicker = ref(false);
   const datePickerTs = ref(Date.now());
@@ -288,6 +384,31 @@
       );
     }
     return parseApprovalFormConfig(detail.value?.formConfig);
+  });
+
+  const displayTemplateFields = computed(() =>
+    displayTemplateFieldsByModule(moduleKey.value, formConfigData.value.fields)
+  );
+
+  const leaveDurationText = computed(() => {
+    if (!isLeaveModule.value) return "";
+    const fields = formConfigData.value.fields;
+    const timeField = findLeaveTimeTemplateField(fields);
+    if (timeField?.key) void formValues[timeField.key];
+    return computeLeaveDurationDisplay(fields, formValues);
+  });
+
+  const overtimeHoursText = computed(() => {
+    if (!isOvertimeModule.value) return "";
+    const fields = formConfigData.value.fields;
+    const timeField = findOvertimeTimeTemplateField(fields);
+    if (timeField?.key) void formValues[timeField.key];
+    return computeOvertimeHoursDisplay(fields, formValues);
+  });
+
+  const applicantPickerValue = computed(() => {
+    const f = findApplicantTemplateField(formConfigData.value.fields);
+    return f?.key ? formValues[f.key] : undefined;
   });
 
   const levelLabel = n => LEVEL_TEXT[Number(n)] || String(n);
@@ -414,7 +535,7 @@
       uni.showToast({ title: "璇疯緭鍏ュ鎵规爣棰�", icon: "none" });
       return false;
     }
-    for (const field of formConfigData.value.fields) {
+    for (const field of displayTemplateFields.value) {
       if (!field.required) continue;
       const val = formValues[field.key];
       if (val === undefined || val === null || String(val).trim() === "") {
@@ -453,17 +574,35 @@
       uni.showToast({ title: "妯℃澘鏈厤缃鎵规祦绋�", icon: "none" });
       return false;
     }
+    const moduleMsg = validateModuleExtras(
+      moduleKey.value,
+      formConfigData.value.fields,
+      formValues,
+      extraForm
+    );
+    if (moduleMsg) {
+      uni.showToast({ title: moduleMsg, icon: "none" });
+      return false;
+    }
     return true;
   };
 
-  const buildFormConfigPayload = () =>
-    JSON.stringify({
+  const buildFormConfigPayload = () => {
+    syncModuleExtrasToFormValues(
+      moduleKey.value,
+      formValues,
+      extraForm,
+      formConfigData.value.fields
+    );
+    const allFields = formConfigData.value.fields || [];
+    return JSON.stringify({
       prompt: formConfigData.value.prompt,
-      fields: formConfigData.value.fields.map(field => ({
+      fields: allFields.map(field => ({
         ...field,
         value: formValues[field.key] ?? "",
       })),
     });
+  };
 
   const buildSavePayload = () => ({
     templateId: detail.value.id,
@@ -562,7 +701,8 @@
     try {
       await loadTemplateDetail();
       if (!detail.value) return;
-      initFormValues(formConfigData.value.fields);
+      initFormValues(displayTemplateFields.value);
+      resetModuleExtras();
       if (!form.title && detail.value.templateName) {
         form.title = `${detail.value.templateName}鐢宠`;
       }
@@ -579,6 +719,9 @@
       return;
     }
     instanceRow.value = row;
+    if (!moduleKey.value) {
+      moduleKey.value = inferModuleKeyFromRow(row);
+    }
     templateId.value = row.templateId;
     form.title = row.title || "";
 
@@ -587,11 +730,65 @@
     try {
       await loadTemplateDetail();
       if (!detail.value) return;
-      initFormValues(formConfigData.value.fields);
+      initFormValues(displayTemplateFields.value);
+      applyModuleExtrasFromRow();
+      if (isTransferModule.value) {
+        await ensureTransferLookupData();
+        syncOriginalPostFromApplicant(applicantPickerValue.value);
+      }
     } finally {
       loading.value = false;
     }
   };
+
+  function resetModuleExtras() {
+    extraForm.leaveBalanceDays = undefined;
+    extraForm.originalPostName = "";
+  }
+
+  function applyModuleExtrasFromRow() {
+    const loaded = loadModuleExtrasFromRow(
+      moduleKey.value,
+      instanceRow.value,
+      formValues
+    );
+    if (loaded.leaveBalanceDays != null) {
+      extraForm.leaveBalanceDays = loaded.leaveBalanceDays;
+    }
+    if (loaded.originalPostName) {
+      extraForm.originalPostName = loaded.originalPostName;
+    }
+  }
+
+  async function ensureTransferLookupData() {
+    if (!transferUserPool.value.length) {
+      try {
+        const res = await userListNoPageByTenantId();
+        transferUserPool.value = unwrapUserArray(res);
+      } catch {
+        transferUserPool.value = [];
+      }
+    }
+    if (!Object.keys(postIdToName.value).length) {
+      try {
+        const res = await findPostOptions();
+        const rows = res?.data ?? res?.rows ?? [];
+        postIdToName.value = buildPostIdToNameMap(Array.isArray(rows) ? rows : []);
+      } catch {
+        postIdToName.value = {};
+      }
+    }
+  }
+
+  function syncOriginalPostFromApplicant(uid) {
+    if (!isTransferModule.value) return;
+    if (!uid) {
+      extraForm.originalPostName = "";
+      return;
+    }
+    const user = userById(transferUserPool.value, uid);
+    extraForm.originalPostName = resolveOriginalPostName(user, postIdToName.value);
+  }
 
   const goBack = () => {
     uni.navigateBack();
@@ -614,8 +811,18 @@
       });
   };
 
+  watch(applicantPickerValue, async uid => {
+    if (!isTransferModule.value) return;
+    await ensureTransferLookupData();
+    syncOriginalPostFromApplicant(uid);
+  });
+
   onLoad(options => {
+    moduleKey.value = options?.moduleKey || "";
     loadPickerSourceData();
+    if (isTransferModule.value) {
+      ensureTransferLookupData();
+    }
     if (options?.id) {
       instanceId.value = options.id;
       loadForEdit();
@@ -1015,4 +1222,29 @@
   .empty-wrap {
     padding: 48px 20px;
   }
+
+  .module-extra-block {
+    margin-top: 8px;
+    padding-top: 8px;
+    border-top: 1px dashed #e8ecf0;
+  }
+
+  .readonly-with-unit {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    width: 100%;
+    justify-content: flex-end;
+  }
+
+  .readonly-with-unit :deep(.u-input) {
+    flex: 1;
+    min-width: 0;
+  }
+
+  .unit-text {
+    flex-shrink: 0;
+    font-size: 14px;
+    color: $text-muted;
+  }
 </style>

--
Gitblit v1.9.3