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