From c4d25912d11ab9059f8165c25a161634bb9b5e97 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 六月 2026 09:45:33 +0800
Subject: [PATCH] proapp 1.工作台分类修改

---
 src/pages/oa/ApproveManage/approve-list/apply.vue |  302 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 279 insertions(+), 23 deletions(-)

diff --git a/src/pages/oa/ApproveManage/approve-list/apply.vue b/src/pages/oa/ApproveManage/approve-list/apply.vue
index 3e873a8..b529db3 100644
--- a/src/pages/oa/ApproveManage/approve-list/apply.vue
+++ b/src/pages/oa/ApproveManage/approve-list/apply.vue
@@ -17,7 +17,7 @@
       </view>
       <template v-else-if="detail">
         <up-form :model="form"
-                 label-width="88"
+                 label-width="100"
                  input-align="right">
           <u-cell-group title="鍩烘湰淇℃伅"
                         class="form-section">
@@ -53,13 +53,14 @@
           </view>
           <up-form v-if="formConfigData.fields.length"
                    :model="formValues"
-                   label-width="88"
+                   label-width="100"
                    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"
+                          :label-position="formItemLabelPosition(field)"
                           :class="formItemClass(field)">
               <up-textarea v-if="isTextareaField(field)"
                            v-model="formValues[field.key]"
@@ -120,6 +121,66 @@
           </up-form>
           <view v-else
                 class="empty-hint">璇ユā鏉挎殏鏃犲~鎶ラ」</view>
+
+          <!-- 璇峰亣锛氬亣鏈熶綑棰� + 鏃堕暱鑷姩璁$畻 -->
+          <view v-if="isLeaveModule"
+                class="module-extra-block">
+            <up-form :model="extraForm"
+                     label-width="100"
+                     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="100"
+                     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="100"
+                     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 +261,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 +273,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 +312,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 +326,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());
@@ -290,6 +387,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);
 
   const selectSheetTitle = computed(
@@ -313,6 +435,12 @@
     if (isDatetimerangeField(field)) return "form-item-daterange";
     if (isSelectField(field) || isDateLikeField(field)) return "form-item-select";
     return "form-item-inline";
+  };
+
+  /** 澶氳鏂囨湰銆佹棩鏈熻寖鍥达細鏍囩缃《锛岄伩鍏嶉暱鏂囨鍦ㄧ獎鍒楀唴鏂 */
+  const formItemLabelPosition = field => {
+    if (isTextareaField(field) || isDatetimerangeField(field)) return "top";
+    return "left";
   };
 
   const getRangePartDisplay = (field, part) => {
@@ -414,7 +542,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 +581,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 +708,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 +726,9 @@
       return;
     }
     instanceRow.value = row;
+    if (!moduleKey.value) {
+      moduleKey.value = inferModuleKeyFromRow(row);
+    }
     templateId.value = row.templateId;
     form.title = row.title || "";
 
@@ -587,11 +737,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 +818,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();
@@ -771,15 +985,42 @@
     justify-content: flex-end !important;
   }
 
-  :deep(.form-item-textarea .u-form-item__body) {
+  :deep(.form-item-textarea .u-form-item__body),
+  :deep(.form-item-daterange .u-form-item__body) {
     flex-direction: column !important;
     align-items: stretch !important;
     padding: 10px 0 12px !important;
   }
 
-  :deep(.form-item-textarea .u-form-item__content) {
+  :deep(.form-item-textarea .u-form-item__body__left),
+  :deep(.form-item-daterange .u-form-item__body__left) {
+    width: 100% !important;
+    max-width: 100% !important;
+    margin-bottom: 8px !important;
+    padding-right: 0 !important;
+  }
+
+  :deep(.form-item-textarea .u-form-item__body__left__content__label),
+  :deep(.form-item-daterange .u-form-item__body__left__content__label) {
+    white-space: normal !important;
+    line-height: 1.45 !important;
+    font-size: 14px !important;
+  }
+
+  :deep(.form-item-textarea .u-form-item__body__right),
+  :deep(.form-item-daterange .u-form-item__body__right) {
+    width: 100% !important;
+    flex: none !important;
+  }
+
+  :deep(.form-item-textarea .u-form-item__content),
+  :deep(.form-item-daterange .u-form-item__content) {
     width: 100% !important;
     justify-content: stretch !important;
+  }
+
+  :deep(.dynamic-form .u-form-item__body__left__content__label) {
+    white-space: nowrap;
   }
 
   .field-trigger {
@@ -799,16 +1040,6 @@
   :deep(.field-trigger .u-input__content__field-wrapper__field) {
     text-align: right !important;
     font-size: 15px !important;
-  }
-
-  :deep(.form-item-daterange .u-form-item__body) {
-    flex-direction: column !important;
-    align-items: stretch !important;
-  }
-
-  :deep(.form-item-daterange .u-form-item__content) {
-    width: 100% !important;
-    justify-content: stretch !important;
   }
 
   .daterange-fill {
@@ -1015,4 +1246,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