From a563ea879ef5fb6897e76d2df661e465dce2ab9b Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 01 六月 2026 15:02:27 +0800
Subject: [PATCH] Merge branch 'dev_新疆_大罗素马铃薯new' of http://114.132.189.42:9002/r/product-inventory-management into dev_新疆_大罗素马铃薯new
---
src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 325 insertions(+), 0 deletions(-)
diff --git a/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
new file mode 100644
index 0000000..4d800df
--- /dev/null
+++ b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
@@ -0,0 +1,325 @@
+<!--OA妯″潡锛氳鍋囩敵璇�-->
+<template>
+ <div class="app-container">
+ <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"
+ />
+ <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </div>
+ <div>
+ <el-button type="primary" @click="openAddWithTemplate">鏂板璇峰亣鐢宠</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="false"
+ :tableLoading="tableLoading"
+ @pagination="onPagination"
+ :total="page.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">
+ <el-col :span="12">
+ <el-form-item label="鍋囨湡浣欓" prop="leaveBalanceDays">
+ <el-input-number
+ v-model="form.leaveBalanceDays"
+ :min="0"
+ :max="999"
+ :precision="2"
+ :step="0.5"
+ controls-position="right"
+ placeholder="澶�"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璇峰亣鏃堕暱">
+ <el-input :model-value="leaveDurationDisplay(form)" readonly placeholder="鏍规嵁妯℃澘涓鍋囨椂闂磋嚜鍔ㄨ绠�">
+ <template #append>澶�</template>
+ </el-input>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </template>
+ </ApprovalInstanceSubmitDialog>
+
+ <ApprovalTemplateBindDialog
+ v-model:visible="templateBindVisible"
+ :module-key="APPROVAL_MODULE_KEYS.LEAVE"
+ skip-form-confirm
+ @confirm="onTemplateBound"
+ @closed="onTemplateBindClosed"
+ />
+
+ <ApprovalInstanceDetailDialog
+ v-model="detailDialog.visible"
+ title="璇峰亣鐢宠璇︽儏"
+ :row="detailRow"
+ @edit="openEditFromDetail"
+ />
+ </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import dayjs from "dayjs";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { ElMessage } from "element-plus";
+import { computed, onMounted, reactive, ref, watch } from "vue";
+import FormPayloadFields from "../../ApproveManage/approve-list/components/FormPayloadFields.vue";
+import ApprovalInstanceDetailDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceDetailDialog.vue";
+import ApprovalInstanceSubmitDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue";
+import ApprovalTemplateBindDialog from "../../ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue";
+import { buildInstanceTableColumns } from "../../ApproveManage/approve-shared/approvalInstanceFormConfigTable.js";
+import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
+import { useApprovalInstanceModule } from "../../ApproveManage/approve-shared/useApprovalInstanceModule.js";
+import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
+import { SELECT_OPTION_SOURCE } from "../../ApproveManage/approve-template/selectOptionSource.js";
+
+const LEAVE_TYPE_OPTIONS = [
+ { label: "骞村亣", value: "annual" },
+ { label: "鐥呭亣", value: "sick" },
+ { label: "浜嬪亣", value: "personal" },
+ { label: "濠氬亣", value: "marriage" },
+ { label: "浜у亣", value: "maternity" },
+ { label: "鍝轰钩鍋�", value: "nursing" },
+ { label: "鎱板攣鍋�", value: "condolence" },
+ { label: "璋冧紤", value: "compensatory" },
+];
+
+function isLeaveBalanceField(field) {
+ const label = String(field?.label || "");
+ return label.includes("鍋囨湡浣欓") || field?.key === "leaveBalanceDays";
+}
+
+function isLeaveDurationField(field) {
+ const label = String(field?.label || "");
+ return label.includes("璇峰亣鏃堕暱") || field?.key === "leaveDurationDays";
+}
+
+function displayTemplateFields(fields = []) {
+ return (fields || []).filter((f) => !isLeaveBalanceField(f) && !isLeaveDurationField(f));
+}
+
+function findLeaveTimeTemplateField(fields = []) {
+ return (
+ fields.find((f) => f?.type === "datetimerange" && String(f?.label || "").includes("璇峰亣鏃堕棿")) ||
+ fields.find((f) => f?.type === "datetimerange" && f?.key === "dateRange") ||
+ fields.find((f) => f?.type === "datetimerange") ||
+ null
+ );
+}
+
+function findApplicantTemplateField(fields = []) {
+ return (
+ fields.find((f) => String(f?.label || "").includes("鐢宠浜�")) ||
+ fields.find((f) => f?.type === "select" && f?.optionSource === SELECT_OPTION_SOURCE.USER) ||
+ null
+ );
+}
+
+function resolveLeaveTimeRange(payload, leaveTimeField) {
+ if (!leaveTimeField?.key) return { start: "", end: "" };
+ const val = payload?.[leaveTimeField.key];
+ if (!Array.isArray(val) || val.length < 2) return { start: "", end: "" };
+ return { start: val[0] || "", end: val[1] || "" };
+}
+
+function computeLeaveDays(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 leaveDurationDisplay(form) {
+ const leaveTimeField = findLeaveTimeTemplateField(form.formFieldDefs);
+ const { start, end } = resolveLeaveTimeRange(form.formPayload, leaveTimeField);
+ const d = computeLeaveDays(start, end);
+ return d == null ? "" : String(d);
+}
+
+const searchForm = reactive({
+ instanceNo: "",
+ applicantKeyword: "",
+});
+
+function validateLeaveBeforeSave() {
+ const leaveTimeField = findLeaveTimeTemplateField(submitForm.formFieldDefs);
+ const { start, end } = resolveLeaveTimeRange(submitForm.formPayload, leaveTimeField);
+ if (computeLeaveDays(start, end) == null) {
+ ElMessage.warning("璇锋鏌ユā鏉夸腑鐨勮鍋囨椂闂达紝缁撴潫鏃堕棿椤绘櫄浜庡紑濮嬫椂闂�");
+ throw new Error("invalid leave time");
+ }
+}
+
+const mod = useApprovalInstanceModule({
+ moduleKey: APPROVAL_MODULE_KEYS.LEAVE,
+ beforeSave: validateLeaveBeforeSave,
+ extraFormRules: {
+ leaveBalanceDays: [{ required: true, message: "璇峰~鍐欏亣鏈熶綑棰�", trigger: "blur" }],
+ },
+});
+
+const {
+ tableData,
+ tableLoading,
+ page,
+ detailDialog,
+ detailRow,
+ submitDialog,
+ submitEditRow,
+ submitForm,
+ submitFormRef,
+ submitSaving,
+ isSubmitEdit,
+ activeTemplate,
+ submitFormFields,
+ submitFormRules,
+ submitDialogTitle,
+ templateBindVisible,
+ handleQuery,
+ initModuleList,
+ pagination,
+ openAddWithTemplate,
+ onTemplateBound,
+ onTemplateBindClosed,
+ openEditFromDetail,
+ submitInstanceForm,
+ buildTableActions,
+} = mod;
+
+const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
+const allUsersCache = ref([]);
+
+const applicantTemplateField = computed(() =>
+ findApplicantTemplateField(submitForm.formFieldDefs)
+);
+
+function unwrapArray(payload) {
+ if (Array.isArray(payload)) return payload;
+ if (payload && Array.isArray(payload.data)) return payload.data;
+ return [];
+}
+
+async function loadUserPool() {
+ try {
+ const res = await userListNoPageByTenantId();
+ allUsersCache.value = unwrapArray(res);
+ } catch {
+ allUsersCache.value = [];
+ }
+}
+
+watch(
+ () => submitDialog.visible,
+ (v) => {
+ if (!v) return;
+ if (submitForm.leaveBalanceDays == null && isSubmitEdit.value) {
+ submitForm.leaveBalanceDays =
+ submitEditRow.value?.formPayload?.leaveBalanceDays ??
+ submitEditRow.value?.leaveBalanceDays;
+ }
+ if (submitForm.leaveBalanceDays == null && !isSubmitEdit.value) {
+ submitForm.leaveBalanceDays = undefined;
+ }
+ }
+);
+
+watch(
+ () => {
+ const key = applicantTemplateField.value?.key;
+ return key ? submitForm.formPayload[key] : undefined;
+ },
+ async (uid) => {
+ if (!applicantTemplateField.value || !uid) return;
+ if (!allUsersCache.value.length) await loadUserPool();
+ }
+);
+
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+ moduleKey: APPROVAL_MODULE_KEYS.LEAVE,
+});
+
+function onSearch() {
+ handleQuery(searchForm);
+}
+
+function resetSearch() {
+ searchForm.instanceNo = "";
+ searchForm.applicantKeyword = "";
+ onSearch();
+}
+
+function onPagination(obj) {
+ pagination(obj, searchForm);
+}
+
+async function onSubmit() {
+ const ok = await submitInstanceForm({ skipValidate: true });
+ if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "鎻愪氦鎴愬姛");
+}
+
+onMounted(async () => {
+ loadFlowUsers();
+ await initModuleList(searchForm);
+});
+</script>
+
+<style scoped>
+.mb20 {
+ margin-bottom: 20px;
+}
+.search_form {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+.search_title {
+ font-size: 14px;
+ color: var(--el-text-color-regular);
+}
+</style>
--
Gitblit v1.9.3