From e9a0f130fd9a076518009cb35fdea24edf1aaac7 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期三, 29 七月 2026 17:48:32 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' into dev_pro_山西长治
---
multiple/config.json | 38 +++
src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js | 77 +++++
src/views/officeProcessAutomation/HrManage/work-handover/index.vue | 1
src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue | 151 ++++++++---
src/views/officeProcessAutomation/HrManage/regular-apply/index.vue | 1
multiple/assets/favicon/QYHYfavicon.ico | 0
multiple/assets/favicon/XJGM.ico | 0
src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue | 1
multiple/assets/logo/XJGMLogo.png | 0
multiple/assets/favicon/HJGCfavicon.ico | 0
multiple/assets/logo/OLDLogo.png | 0
multiple/assets/logo/HJGCLogo.png | 0
multiple/multiple-build.js | 8
src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue | 17
multiple/assets/favicon/RBHB.ico | 0
multiple/assets/logo/RBHBLogo.png | 0
deploy.bat | 10
src/views/login.vue | 20
src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js | 13
src/views/personnelManagement/classsSheduling/index.vue | 270 +++++++++++++++++++++-
multiple/assets/favicon/OLD.ico | 0
src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue | 93 +++++--
src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue | 2
src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue | 2
src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue | 2
multiple/assets/logo/QYHYLogo.png | 0
26 files changed, 581 insertions(+), 125 deletions(-)
diff --git a/deploy.bat b/deploy.bat
index 17c8665..7586c2d 100644
--- a/deploy.bat
+++ b/deploy.bat
@@ -3,12 +3,12 @@
setlocal enabledelayedexpansion
:: ================= Config =================
-set "USER=dell"
-set "PASS=xd123456@.."
-set "IP=1.15.17.182"
-set "PORT=10000"
+set "USER=root"
+set "PASS=Xdrjkj@10086#"
+set "IP=36.213.180.217
+set "PORT=65022"
set "DIST_DIR=dist"
-set "REMOTE_DIR=/home/vue/newpro-product"
+set "REMOTE_DIR=/data/project/pim/vue/xjgm"
set "SSH_OPTS=-o StrictHostKeyChecking=no -o ConnectTimeout=10"
:: ===========================================
diff --git a/multiple/assets/favicon/HJGCfavicon.ico b/multiple/assets/favicon/HJGCfavicon.ico
new file mode 100644
index 0000000..7dfa6db
--- /dev/null
+++ b/multiple/assets/favicon/HJGCfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/OLD.ico b/multiple/assets/favicon/OLD.ico
new file mode 100644
index 0000000..236aae2
--- /dev/null
+++ b/multiple/assets/favicon/OLD.ico
Binary files differ
diff --git a/multiple/assets/favicon/QYHYfavicon.ico b/multiple/assets/favicon/QYHYfavicon.ico
new file mode 100644
index 0000000..5e6bb8f
--- /dev/null
+++ b/multiple/assets/favicon/QYHYfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/RBHB.ico b/multiple/assets/favicon/RBHB.ico
new file mode 100644
index 0000000..a41f7ec
--- /dev/null
+++ b/multiple/assets/favicon/RBHB.ico
Binary files differ
diff --git a/multiple/assets/favicon/XJGM.ico b/multiple/assets/favicon/XJGM.ico
new file mode 100644
index 0000000..acff53d
--- /dev/null
+++ b/multiple/assets/favicon/XJGM.ico
Binary files differ
diff --git a/multiple/assets/logo/HJGCLogo.png b/multiple/assets/logo/HJGCLogo.png
new file mode 100644
index 0000000..9a4de9c
--- /dev/null
+++ b/multiple/assets/logo/HJGCLogo.png
Binary files differ
diff --git a/multiple/assets/logo/OLDLogo.png b/multiple/assets/logo/OLDLogo.png
new file mode 100644
index 0000000..8cdd10d
--- /dev/null
+++ b/multiple/assets/logo/OLDLogo.png
Binary files differ
diff --git a/multiple/assets/logo/QYHYLogo.png b/multiple/assets/logo/QYHYLogo.png
new file mode 100644
index 0000000..4f9b687
--- /dev/null
+++ b/multiple/assets/logo/QYHYLogo.png
Binary files differ
diff --git a/multiple/assets/logo/RBHBLogo.png b/multiple/assets/logo/RBHBLogo.png
new file mode 100644
index 0000000..605f429
--- /dev/null
+++ b/multiple/assets/logo/RBHBLogo.png
Binary files differ
diff --git a/multiple/assets/logo/XJGMLogo.png b/multiple/assets/logo/XJGMLogo.png
new file mode 100644
index 0000000..84cbc8a
--- /dev/null
+++ b/multiple/assets/logo/XJGMLogo.png
Binary files differ
diff --git a/multiple/config.json b/multiple/config.json
index b3351e2..e9dc9b3 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -258,6 +258,42 @@
"logo": "logo/JXSMLogo.png",
"favicon": "favicon/JXSMico.ico"
},
+ "HYZC": {
+ "env": {
+ "VITE_APP_TITLE": "灞辫タ鍗庝嚎浼楁垚寤烘潗鏈夐檺鍏徃",
+ "VITE_BASE_API": "http://36.213.180.217:9009",
+ "VITE_JAVA_API": "http://36.213.180.217:9008"
+ },
+ "logo": "logo/HYZCLogo.png",
+ "favicon": "favicon/HYZCfavicon.ico"
+ },
+ "OLD": {
+ "env": {
+ "VITE_APP_TITLE": "闈掓捣娆ц幈寰锋柊鏉愭枡绉戞妧鏈夐檺鍏徃",
+ "VITE_BASE_API": "http://223.221.37.139:9000",
+ "VITE_JAVA_API": "http://223.221.37.139:9000"
+ },
+ "logo": "logo/OLDLogo.png",
+ "favicon": "favicon/OLD.ico"
+ },
+ "XJGM": {
+ "env": {
+ "VITE_APP_TITLE": "鍏嬫媺鐜涗緷甯傛柊鑱氬伐璐告湁闄愯矗浠诲叕鍙�",
+ "VITE_BASE_API": "http://36.213.180.217:9015",
+ "VITE_JAVA_API": "http://36.213.180.217:9016"
+ },
+ "logo": "logo/XJGMLogo.png",
+ "favicon": "favicon/XJGM.ico"
+ },
+ "RBHB": {
+ "env": {
+ "VITE_APP_TITLE": "鍏嬫媺鐜涗緷铻嶉偊鐜繚绉戞妧鏈夐檺鍏徃",
+ "VITE_BASE_API": "http://36.213.180.217:9017",
+ "VITE_JAVA_API": "http://36.213.180.217:9018"
+ },
+ "logo": "logo/RBHBLogo.png",
+ "favicon": "favicon/RBHB.ico"
+ },
"logo": "/src/assets/logo/logo.png",
"favicon": "/public/favicon.ico"
-}
\ No newline at end of file
+}
diff --git a/multiple/multiple-build.js b/multiple/multiple-build.js
index afcd4d5..933090c 100644
--- a/multiple/multiple-build.js
+++ b/multiple/multiple-build.js
@@ -60,7 +60,9 @@
const backupFile = path.join(replacePath, config[key]);
const replaceFile = path.join(resourcePath, companyMap[key]);
- await copyFileWithOverwrite(originFile, backupFile);
+ if (fsSync.existsSync(originFile)) {
+ await copyFileWithOverwrite(originFile, backupFile);
+ }
await copyFileWithOverwrite(replaceFile, originFile);
}
@@ -82,7 +84,9 @@
const originFile = path.join(rootPath, config[key]);
const backupFile = path.join(replacePath, config[key]);
- await copyFileWithOverwrite(backupFile, originFile);
+ if (fsSync.existsSync(backupFile)) {
+ await copyFileWithOverwrite(backupFile, originFile);
+ }
}
await fs.rm(replacePath, { recursive: true, force: true });
console.log(`馃棏锔� 宸插垹闄� ${replacePath}`);
diff --git a/src/views/login.vue b/src/views/login.vue
index c7db15a..d044465 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -2,16 +2,16 @@
<div class="login-page">
<main class="page">
<section class="factory">
- <div class="brand hero-brand">
- <div class="logo hero-logo">
- <img
- :src="brandLogoUrl"
- :alt="`${companyName} logo`"
- class="logo-image hero-logo-image"
- @error="handleLogoError"
- />
- </div>
- </div>
+<!-- <div class="brand hero-brand">-->
+<!-- <div class="logo hero-logo">-->
+<!-- <img-->
+<!-- :src="brandLogoUrl"-->
+<!-- :alt="`${companyName} logo`"-->
+<!-- class="logo-image hero-logo-image"-->
+<!-- @error="handleLogoError"-->
+<!-- />-->
+<!-- </div>-->
+<!-- </div>-->
<div class="hero">
<div class="chip">鏁板瓧宸ュ巶 路 鏅鸿兘鎺掍骇 路 璁惧浜掕仈 路 璐ㄩ噺杩芥函</div>
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
index 7933db5..817a007 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/FormPayloadFields.vue
@@ -80,12 +80,12 @@
clearable
filterable
>
- <el-option
- v-for="o in getOptions(field)"
- :key="String(o.value)"
- :label="o.label"
- :value="o.value"
- />
+ <el-option
+ v-for="o in getOptions(field, { moduleKey: props.moduleKey })"
+ :key="String(o.value)"
+ :label="o.label"
+ :value="o.value"
+ />
</el-select>
<span v-else class="field-value">{{ displayValue(field) }}</span>
</el-form-item>
@@ -103,13 +103,14 @@
fields: { type: Array, default: () => [] },
formPayload: { type: Object, default: () => ({}) },
readonly: { type: Boolean, default: false },
+ moduleKey: { type: String, default: "" },
});
const { loading: optionSourceLoading, ensureForFields, getOptions, getDisplayLabel } =
useSelectOptionSources();
async function loadOptionCaches() {
- await ensureForFields(props.fields);
+ await ensureForFields(props.fields, { moduleKey: props.moduleKey });
}
onMounted(() => {
@@ -127,7 +128,7 @@
function displayValue(field) {
const val = props.formPayload?.[field.key];
if (field.type === "select" && field.optionSource && field.optionSource !== "static") {
- return getDisplayLabel(field, val);
+ return getDisplayLabel(field, val, { moduleKey: props.moduleKey });
}
return formatFieldDisplayValue(field, val);
}
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue
index 53de9e1..0f9646c 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue
@@ -24,6 +24,7 @@
v-model:attachments="form.storageBlobDTOs"
:template-attachments="form.templateAttachments"
:user-options="userOptions"
+ :module-key="moduleKey"
:show-template-name="!isEdit"
:allow-change-template="false"
:flow-attachments-only="flowAttachmentsOnly"
@@ -56,6 +57,7 @@
fields: { type: Array, default: () => [] },
activeTemplate: { type: Object, default: null },
userOptions: { type: Array, default: () => [] },
+ moduleKey: { type: String, default: "" },
isEdit: { type: Boolean, default: false },
saving: { type: Boolean, default: false },
formRef: { type: Object, default: null },
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue
index 409dd41..6d56084 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue
@@ -4,7 +4,7 @@
鐢ㄦ硶锛�
<ApprovalTemplateBindDialog
v-model:visible="visible"
- module-key="regular"
+ :module-key="APPROVAL_MODULE_KEYS.REGULAR"
@confirm="onTemplateBound"
/>
-->
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue
index d6e7073..ebc5ac4 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/components/ApprovalTemplateFormSection.vue
@@ -15,6 +15,7 @@
v-if="!hideFormFields && !flowAttachmentsOnly && !flowOnly"
:fields="fields"
:form-payload="formPayload"
+ :module-key="moduleKey"
/>
<el-form-item label="瀹℃壒娴佺▼" required>
@@ -87,6 +88,7 @@
uploadLimit: { type: Number, default: 10 },
/** 涓� true 鏃跺彲缂栬緫妯℃澘棰勭疆鐨勫鎵逛汉锛堜粎瀹℃壒妯℃澘绠$悊椤典娇鐢級 */
flowEditable: { type: Boolean, default: false },
+ moduleKey: { type: String, default: "" },
});
const emit = defineEmits(["update:flowNodes", "update:attachments", "change-template"]);
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
index 99706b4..3dbc6f0 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/selectOptionSource.js
@@ -1,30 +1,42 @@
import { deptTreeSelect, userListNoPageByTenantId } from "@/api/system/user.js";
+import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js";
/** 涓嬫媺閫夐」鏉ユ簮锛堝啓鍏� formConfig锛屾彁浜ら〉鎸夋潵婧愭媺鍙栨暟鎹級 */
export const SELECT_OPTION_SOURCE = {
STATIC: "static",
USER: "user",
DEPT: "dept",
+ EMPLOYEE: "employee",
};
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.EMPLOYEE, label: "鍛樺伐鍒楄〃", desc: "浠庡湪鑱屽憳宸ヤ腑閫夋嫨锛屽�间负鍛樺伐 ID" },
];
+
+const EMPLOYEE_APPLICANT_MODULE_KEYS = new Set(["regular"]);
export function selectOptionSourceLabel(source) {
return SELECT_OPTION_SOURCE_OPTIONS.find((x) => x.value === source)?.label || "鈥�";
}
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.EMPLOYEE
+ );
}
function unwrapArray(payload) {
if (Array.isArray(payload)) return payload;
+ if (Array.isArray(payload?.records)) return payload.records;
if (payload?.data && Array.isArray(payload.data)) return payload.data;
+ if (Array.isArray(payload?.data?.records)) return payload.data.records;
if (payload?.rows && Array.isArray(payload.rows)) return payload.rows;
+ if (Array.isArray(payload?.data?.rows)) return payload.data.rows;
return [];
}
@@ -39,6 +51,15 @@
const value = u.userId ?? u.id;
return {
label: u.nickName || u.userName || `鐢ㄦ埛${value}`,
+ value,
+ };
+}
+
+/** 鍦ㄨ亴鍛樺伐 鈫� 涓嬫媺 option */
+export function mapEmployeeToSelectOption(u) {
+ const value = u.id ?? u.staffOnJobId ?? u.staffId;
+ return {
+ label: u.staffName || u.name || u.userName || `鍛樺伐${value}`,
value,
};
}
@@ -70,21 +91,41 @@
}
/** 鎸夊瓧娈甸厤缃В鏋愪笅鎷� options锛堥渶浼犲叆宸插姞杞界殑缂撳瓨锛� */
-export function resolveFieldSelectOptions(field, caches = {}) {
+function isApplicantField(field) {
+ const key = String(field?.key || "").toLowerCase();
+ const label = String(field?.label || "");
+ return (
+ key === "applicant" ||
+ key === "applicantname" ||
+ label.includes("鐢宠浜�")
+ );
+}
+
+export function resolveFieldSelectOptions(field, caches = {}, context = {}) {
const source = field?.optionSource || SELECT_OPTION_SOURCE.STATIC;
+ if (
+ source === SELECT_OPTION_SOURCE.USER &&
+ EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) &&
+ isApplicantField(field)
+ ) {
+ return (caches.employees || []).map(mapEmployeeToSelectOption);
+ }
if (source === SELECT_OPTION_SOURCE.USER) {
return (caches.users || []).map(mapUserToSelectOption);
}
if (source === SELECT_OPTION_SOURCE.DEPT) {
return caches.deptOptions || [];
}
+ if (source === SELECT_OPTION_SOURCE.EMPLOYEE) {
+ return (caches.employees || []).map(mapEmployeeToSelectOption);
+ }
return (field?.options || []).filter((o) => o.value !== "" && o.value != null);
}
/** 鏍规嵁宸茶В鏋愮殑 options 鍙嶆煡灞曠ず鏂囨湰 */
-export function resolveSelectDisplayLabel(field, val, caches = {}) {
+export function resolveSelectDisplayLabel(field, val, caches = {}, context = {}) {
if (val == null || val === "") return "鈥�";
- const options = resolveFieldSelectOptions(field, caches);
+ const options = resolveFieldSelectOptions(field, caches, context);
const hit = options.find((o) => String(o.value) === String(val));
return hit?.label || String(val);
}
@@ -93,9 +134,10 @@
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 needEmployee = sources.includes(SELECT_OPTION_SOURCE.EMPLOYEE);
+ const caches = { users: [], deptOptions: [], employees: [] };
- if (!needUser && !needDept) return caches;
+ if (!needUser && !needDept && !needEmployee) return caches;
const tasks = [];
if (needUser) {
@@ -123,17 +165,36 @@
})
);
}
+ if (needEmployee) {
+ tasks.push(
+ staffOnJobListPage({ current: -1, size: -1, staffState: 1 })
+ .then((res) => {
+ caches.employees = unwrapArray(res);
+ })
+ .catch(() => {
+ caches.employees = [];
+ })
+ );
+ }
await Promise.all(tasks);
return caches;
}
/** 浠庡瓧娈靛垪琛ㄦ敹闆嗛渶瑕侀鍔犺浇鐨勫姩鎬佹潵婧� */
-export function collectOptionSourcesFromFields(fields) {
+export function collectOptionSourcesFromFields(fields, context = {}) {
const set = new Set();
(fields || []).forEach((f) => {
if (f?.type === "select" && isDynamicOptionSource(f.optionSource)) {
- set.add(f.optionSource);
+ if (
+ EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) &&
+ f.optionSource === SELECT_OPTION_SOURCE.USER &&
+ isApplicantField(f)
+ ) {
+ set.add(SELECT_OPTION_SOURCE.EMPLOYEE);
+ } else {
+ set.add(f.optionSource);
+ }
}
});
return [...set];
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
index 8397288..5d246e1 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useSelectOptionSources.js
@@ -14,25 +14,26 @@
deptOptions: [],
});
- async function ensureForFields(fields) {
- const sources = collectOptionSourcesFromFields(fields);
+ async function ensureForFields(fields, context = {}) {
+ const sources = collectOptionSourcesFromFields(fields, context);
if (!sources.length) return;
loading.value = true;
try {
const next = await fetchSelectOptionCaches(sources);
caches.users = next.users;
caches.deptOptions = next.deptOptions;
+ caches.employees = next.employees || [];
} finally {
loading.value = false;
}
}
- function getOptions(field) {
- return resolveFieldSelectOptions(field, caches);
+ function getOptions(field, context = {}) {
+ return resolveFieldSelectOptions(field, caches, context);
}
- function getDisplayLabel(field, val) {
- return resolveSelectDisplayLabel(field, val, caches);
+ function getDisplayLabel(field, val, context = {}) {
+ return resolveSelectDisplayLabel(field, val, caches, context);
}
return {
diff --git a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
index 291d57d..df70f4f 100644
--- a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
@@ -48,6 +48,7 @@
:fields="submitFormFields"
:active-template="activeTemplate"
:user-options="flowUserOptions"
+ :module-key="APPROVAL_MODULE_KEYS.REGULAR"
:is-edit="isSubmitEdit"
:saving="submitSaving"
:form-ref="submitFormRef"
diff --git a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
index f731d57..8b43805 100644
--- a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
@@ -58,6 +58,7 @@
:fields="submitFormFields"
:active-template="activeTemplate"
:user-options="flowUserOptions"
+ :module-key="APPROVAL_MODULE_KEYS.TRANSFER"
:is-edit="isSubmitEdit"
:saving="submitSaving"
:form-ref="submitFormRef"
diff --git a/src/views/officeProcessAutomation/HrManage/work-handover/index.vue b/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
index a9b96ae..37ff223 100644
--- a/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/work-handover/index.vue
@@ -58,6 +58,7 @@
:fields="submitFormFields"
:active-template="activeTemplate"
:user-options="flowUserOptions"
+ :module-key="APPROVAL_MODULE_KEYS.WORK_HANDOVER"
:is-edit="isSubmitEdit"
:saving="submitSaving"
:form-ref="submitFormRef"
diff --git a/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue b/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
index cbbb8af..0ddea67 100644
--- a/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
+++ b/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
@@ -37,11 +37,17 @@
<el-option v-for="item in shifts_list"
:key="item.value"
:label="item.label"
- :value="item.value" />
+ :value="item.value"
+ :disabled="
+ operationType === 'add' &&
+ (String(item.label || '').includes('鍋�') ||
+ String(item.label || '').includes('浼�'))
+ " />
</el-select>
</el-form-item>
<el-form-item label="鎵撳崱鑼冨洿(m)"
- prop="radius">
+ prop="radius"
+ v-show="!hideClockFields">
<el-input-number v-model="form.radius"
:min="10"
:max="1000"
@@ -51,7 +57,8 @@
</el-form-item>
<!-- 楂樺痉鍦板浘閫夋嫨 -->
<el-form-item label="鎵撳崱浣嶇疆"
- prop="longitude">
+ prop="longitude"
+ v-show="!hideClockFields">
<div class="map-container">
<div class="map-header"
style="margin-bottom: 10px">
@@ -82,14 +89,16 @@
</div>
</el-form-item>
<el-form-item label="鍦扮偣鍚嶇О"
- prop="locationName">
+ prop="locationName"
+ v-show="!hideClockFields">
<el-input v-model="form.locationName"
:disabled="operationType === 'view'"
placeholder="璇疯緭鍏ュ湴鐐瑰悕绉�" />
</el-form-item>
<!-- 涓婁笅鐝椂闂� -->
<el-form-item label="涓婄彮鏃堕棿"
- prop="startAt">
+ prop="startAt"
+ v-show="!hideClockFields">
<el-time-picker v-model="form.startAt"
format="HH:mm"
value-format="HH:mm"
@@ -97,7 +106,8 @@
:disabled="operationType === 'view'" />
</el-form-item>
<el-form-item label="涓嬬彮鏃堕棿"
- prop="endAt">
+ prop="endAt"
+ v-show="!hideClockFields">
<el-time-picker v-model="form.endAt"
format="HH:mm"
value-format="HH:mm"
@@ -110,7 +120,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button type="primary" @click="submitForm" v-if="operationType !== 'view'">纭畾</el-button>
+ <el-button type="primary"
+ @click="submitForm"
+ v-if="operationType !== 'view'">纭畾</el-button>
<el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
@@ -157,6 +169,21 @@
// 鑾峰彇鐝瀛楀吀鍊�
const { shifts_list } = useDict("shifts_list");
+ const selectedShiftLabel = computed(() => {
+ const item = shifts_list.value?.find(
+ s => String(s.value) === String(form.shift)
+ );
+ return item?.label || "";
+ });
+
+ const hideClockFields = computed(() => {
+ if (props.operationType === "view") return false;
+ return (
+ selectedShiftLabel.value.includes("鍋�") ||
+ selectedShiftLabel.value.includes("浼�")
+ );
+ });
+
// 琛ㄥ崟鏁版嵁
const formRef = ref();
const form = reactive({
@@ -172,44 +199,53 @@
});
// 琛ㄥ崟楠岃瘉瑙勫垯
- const rules = {
- sysDeptId: [{ required: true, message: "璇烽�夋嫨閮ㄩ棬", trigger: "change" }],
- locationName: [
- { required: true, message: "璇疯緭鍏ュ湴鐐瑰悕绉�", trigger: "blur" },
- ],
- longitude: [{ required: true, message: "璇烽�夋嫨鎵撳崱浣嶇疆", trigger: "blur" }],
- latitude: [{ required: true, message: "璇烽�夋嫨鎵撳崱浣嶇疆", trigger: "blur" }],
- shift: [{ required: true, message: "璇烽�夋嫨鐝", trigger: "change" }],
- radius: [{ required: true, message: "璇疯緭鍏ユ墦鍗¤寖鍥�", trigger: "blur" }],
- startAt: [{ required: true, message: "璇烽�夋嫨涓婄彮鏃堕棿", trigger: "change" }],
- endAt: [
- { required: true, message: "璇烽�夋嫨涓嬬彮鏃堕棿", trigger: "change" },
- {
- validator: (rule, value, callback) => {
- // 濡傛灉鏄鐝紝涓嶉檺鍒舵椂闂撮『搴�
- if (form.shift && ['澶滅彮', '澶�'].includes(form.shift)) {
- callback();
- return;
- }
- if (form.startAt && value) {
- const startParts = form.startAt.split(":");
- const endParts = value.split(":");
- const startTime =
- parseInt(startParts[0]) * 60 + parseInt(startParts[1]);
- const endTime = parseInt(endParts[0]) * 60 + parseInt(endParts[1]);
- if (endTime <= startTime) {
- callback(new Error("涓嬬彮鏃堕棿涓嶈兘鏃╀簬涓婄彮鏃堕棿"));
+ const rules = computed(() => {
+ const base = {
+ sysDeptId: [{ required: true, message: "璇烽�夋嫨閮ㄩ棬", trigger: "change" }],
+ shift: [{ required: true, message: "璇烽�夋嫨鐝", trigger: "change" }],
+ };
+
+ if (hideClockFields.value) {
+ return base;
+ }
+
+ return {
+ ...base,
+ locationName: [
+ { required: true, message: "璇疯緭鍏ュ湴鐐瑰悕绉�", trigger: "blur" },
+ ],
+ longitude: [{ required: true, message: "璇烽�夋嫨鎵撳崱浣嶇疆", trigger: "blur" }],
+ latitude: [{ required: true, message: "璇烽�夋嫨鎵撳崱浣嶇疆", trigger: "blur" }],
+ radius: [{ required: true, message: "璇疯緭鍏ユ墦鍗¤寖鍥�", trigger: "blur" }],
+ startAt: [{ required: true, message: "璇烽�夋嫨涓婄彮鏃堕棿", trigger: "change" }],
+ endAt: [
+ { required: true, message: "璇烽�夋嫨涓嬬彮鏃堕棿", trigger: "change" },
+ {
+ validator: (rule, value, callback) => {
+ if (form.shift && ["澶滅彮", "澶�"].includes(form.shift)) {
+ callback();
+ return;
+ }
+ if (form.startAt && value) {
+ const startParts = form.startAt.split(":");
+ const endParts = value.split(":");
+ const startTime =
+ parseInt(startParts[0]) * 60 + parseInt(startParts[1]);
+ const endTime = parseInt(endParts[0]) * 60 + parseInt(endParts[1]);
+ if (endTime <= startTime) {
+ callback(new Error("涓嬬彮鏃堕棿涓嶈兘鏃╀簬涓婄彮鏃堕棿"));
+ } else {
+ callback();
+ }
} else {
callback();
}
- } else {
- callback();
- }
+ },
+ trigger: "change",
},
- trigger: "change",
- },
- ],
- };
+ ],
+ };
+ });
// 閮ㄩ棬閫夐」
const deptOptions = ref([]);
@@ -393,8 +429,8 @@
watch(
() => form.radius,
newValue => {
- if (circle) {
- circle.setRadius(newValue);
+ if (circle && newValue != null && newValue !== "") {
+ circle.setRadius(Number(newValue));
}
}
);
@@ -405,6 +441,29 @@
() => {
if (formRef.value && form.endAt) {
formRef.value.validateField("endAt");
+ }
+ }
+ );
+
+ watch(
+ () => hideClockFields.value,
+ val => {
+ if (!val) return;
+ form.radius = null;
+ form.longitude = "";
+ form.latitude = "";
+ form.locationName = "";
+ form.startAt = "";
+ form.endAt = "";
+ if (formRef.value) {
+ formRef.value.clearValidate([
+ "radius",
+ "longitude",
+ "latitude",
+ "locationName",
+ "startAt",
+ "endAt",
+ ]);
}
}
);
@@ -442,7 +501,9 @@
// 鍒濆鍖栧湴鍥�
setTimeout(() => {
- initMap();
+ if (!hideClockFields.value) {
+ initMap();
+ }
}, 100);
}
}
@@ -513,4 +574,4 @@
.mt8 {
margin-top: 8px;
}
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue b/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
index c47dd2e..9658b1f 100644
--- a/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
+++ b/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
@@ -13,30 +13,37 @@
</el-button>
</div>
</div>
- <!-- 鏌ヨ鏉′欢 -->
- <!-- <el-form :model="searchForm"
+ <el-form :model="searchForm"
:inline="true"
class="search-form mb16">
<el-form-item label="閮ㄩ棬锛�"
- prop="countId">
- <el-tree-select v-model="searchForm.countId"
+ prop="sysDeptId">
+ <el-tree-select v-model="searchForm.sysDeptId"
:data="deptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="璇烽�夋嫨閮ㄩ棬"
check-strictly
+ clearable
style="width: 200px" />
</el-form-item>
- <el-form-item label="鍦扮偣锛�"
- prop="locationName">
- <el-input v-model="searchForm.locationName"
- placeholder="璇疯緭鍏ュ湴鐐瑰悕绉�"
- clearable
- style="width: 200px" />
+ <el-form-item label="鐝锛�"
+ prop="shift">
+ <el-select v-model="searchForm.shift"
+ placeholder="璇烽�夋嫨鐝"
+ clearable
+ style="width: 200px">
+ <el-option
+ v-for="item in shifts_list"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
</el-form-item>
<el-form-item>
<el-button type="primary"
- @click="fetchData">
+ @click="handleSearch">
<el-icon>
<Search />
</el-icon>
@@ -49,7 +56,7 @@
閲嶇疆
</el-button>
</el-form-item>
- </el-form> -->
+ </el-form>
<!-- 鐝鍒楄〃 -->
<el-card shadow="never"
class="mb16">
@@ -74,23 +81,39 @@
</template>
</el-table-column>
<el-table-column prop="locationName"
- label="鍦扮偣鍚嶇О" />
+ label="鍦扮偣鍚嶇О">
+ <template #default="scope">
+ {{ scope.row.locationName || "-" }}
+ </template>
+ </el-table-column>
<el-table-column prop="longitude"
- label="缁忓害" />
+ label="缁忓害">
+ <template #default="scope">
+ {{ scope.row.longitude ?? "-" }}
+ </template>
+ </el-table-column>
<el-table-column prop="latitude"
- label="绾害" />
+ label="绾害">
+ <template #default="scope">
+ {{ scope.row.latitude ?? "-" }}
+ </template>
+ </el-table-column>
<el-table-column prop="radius"
- label="鎵撳崱鑼冨洿(m)" />
+ label="鎵撳崱鑼冨洿(m)">
+ <template #default="scope">
+ {{ scope.row.radius ?? "-" }}
+ </template>
+ </el-table-column>
<el-table-column prop="startAt"
label="涓婄彮鏃堕棿">
<template #default="scope">
- {{ scope.row.startAt }}
+ {{ scope.row.startAt || "-" }}
</template>
</el-table-column>
<el-table-column prop="endAt"
label="涓嬬彮鏃堕棿">
<template #default="scope">
- {{ scope.row.endAt }}
+ {{ scope.row.endAt || "-" }}
</template>
</el-table-column>
<el-table-column label="鎿嶄綔"
@@ -100,9 +123,11 @@
<template #default="scope">
<el-button type="primary"
link
+ :disabled="isRuleLocked(scope.row)"
@click="openForm('edit', scope.row)">缂栬緫</el-button>
<el-button type="danger"
link
+ :disabled="isRuleLocked(scope.row)"
@click="handleDelete(scope.row.id)">鍒犻櫎</el-button>
</template>
</el-table-column>
@@ -124,7 +149,7 @@
</template>
<script setup>
- import { ref, reactive, onMounted } from "vue";
+ import { ref, reactive, onMounted, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
Plus,
@@ -158,8 +183,8 @@
// 鏌ヨ琛ㄥ崟
const searchForm = reactive({
- countId: "",
- locationName: "",
+ sysDeptId: "",
+ shift: "",
});
// 閮ㄩ棬閫夐」
@@ -187,6 +212,10 @@
if (!value) return "";
const shift = shifts_list.value.find(item => item.value === value);
return shift ? shift.label : value;
+ };
+
+ const isRuleLocked = row => {
+ return Boolean(row?.usageLocked || row?.canEdit === false || row?.canDelete === false);
};
// 鑾峰彇閮ㄩ棬鍒楄〃
@@ -232,12 +261,18 @@
tableLoading.value = true;
getAttendanceRules({ ...page, ...searchForm })
.then(res => {
- tableData.value = res.data.records;
+ const records = Array.isArray(res?.data?.records) ? res.data.records : [];
+ tableData.value = records.filter(r => String(r?.id) !== "999");
page.total = res.data.total;
})
.finally(() => {
tableLoading.value = false;
});
+ };
+
+ const handleSearch = () => {
+ page.current = 1;
+ fetchData();
};
// 鍒嗛〉鍙樻洿
@@ -249,13 +284,18 @@
// 閲嶇疆鎼滅储
const resetSearch = () => {
- searchForm.countId = "";
- searchForm.locationName = "";
+ searchForm.sysDeptId = "";
+ searchForm.shift = "";
+ page.current = 1;
fetchData();
};
// 鎵撳紑琛ㄥ崟
const openForm = (type, row = {}) => {
+ if (type === "edit" && isRuleLocked(row)) {
+ ElMessage.warning("璇ョ彮娆¢厤缃凡琚帓鐝苟浜х敓鎵撳崱璁板綍锛屼笉鑳界紪杈�");
+ return;
+ }
operationType.value = type;
currentRow.value = row;
dialogVisible.value = true;
@@ -263,6 +303,11 @@
// 鍒犻櫎鐝
const handleDelete = id => {
+ const row = tableData.value.find(item => String(item.id) === String(id));
+ if (isRuleLocked(row)) {
+ ElMessage.warning("瀛樺湪宸叉帓鐝笖宸叉湁鎵撳崱璁板綍鐨勭彮娆¢厤缃紝涓嶈兘鍒犻櫎");
+ return;
+ }
ElMessageBox.confirm("纭畾瑕佸垹闄よ繖鏉$彮娆″悧锛�", "鍒犻櫎纭", {
confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
diff --git a/src/views/personnelManagement/classsSheduling/index.vue b/src/views/personnelManagement/classsSheduling/index.vue
index 140ffba..59e65b4 100644
--- a/src/views/personnelManagement/classsSheduling/index.vue
+++ b/src/views/personnelManagement/classsSheduling/index.vue
@@ -142,19 +142,20 @@
:key="'d' + i">
<el-dropdown trigger="click"
placement="bottom"
- @command="(e) => handleCommand(e, m)"
+ :disabled="isCellBlockedByLeaveDate(item, m)"
+ @command="(e) => handleCommand(e, item, m)"
class="shift-dropdown">
<div class="shift-box"
:class="{
'shift-box-early': m.shift === '鏃╃彮',
'shift-box-mid': m.shift === '涓彮',
'shift-box-night': m.shift === '澶滅彮',
- 'shift-box-rest': m.shift === '浼戞伅',
+ 'shift-box-rest': !m.shift || m.shift === '浼戞伅',
'shift-box-leave': m.shift === '璇峰亣',
'shift-box-other': m.shift === '澶�11',
'shift-box-business': m.shift === '澶�12',
}">
- <span class="shift-text">{{ getShiftNameByValue(m.shift) || '鈥�' }}</span>
+ <span class="shift-text">{{ getShiftNameByValue(m.shift) || '浼戞伅' }}</span>
</div>
<template #dropdown>
<el-dropdown-menu>
@@ -199,7 +200,7 @@
</div> -->
<div class="user-total">
<span class="total-label">鍚堣鍑哄嫟:</span>
- <span class="total-value">{{ item.work_time }}澶�</span>
+ <span class="total-value">{{ item.year.totalYearAttendance }}澶�</span>
</div>
</div>
</div>
@@ -271,6 +272,23 @@
<div class="search_thing">
<div class="search_label"
style="width: 90px">
+ <span style="color: red; margin-right: 4px">*</span>閮ㄩ棬锛�
+ </div>
+ <div class="search_input">
+ <el-tree-select v-model="schedulingQuery.deptId"
+ :data="deptOptions"
+ :props="{ value: 'id', label: 'label', children: 'children' }"
+ value-key="id"
+ placeholder="璇烽�夋嫨閮ㄩ棬"
+ check-strictly
+ clearable
+ @change="onSchedulingDeptChange"
+ style="width: 100%" />
+ </div>
+ </div>
+ <div class="search_thing">
+ <div class="search_label"
+ style="width: 90px">
<span style="color: red; margin-right: 4px">*</span>浜哄憳鍚嶇О锛�
</div>
<div class="search_input">
@@ -279,7 +297,8 @@
style="width: 100%"
multiple
clearable
- collapse-tags>
+ collapse-tags
+ :disabled="!schedulingQuery.deptId">
<el-option v-for="item in personList"
:key="item.id"
:label="item.staffName"
@@ -296,8 +315,9 @@
<div class="search_input">
<el-select v-model="schedulingQuery.shift"
placeholder="璇烽�夋嫨"
- style="width: 100%">
- <el-option v-for="item in classType"
+ style="width: 100%"
+ :disabled="!schedulingQuery.deptId">
+ <el-option v-for="item in schedulingClassType"
:key="item.id"
:label="getShiftNameByValue(item.shift)"
:value="item.id">
@@ -318,7 +338,14 @@
</template>
<script setup>
- import { ref, reactive, onMounted, getCurrentInstance } from "vue";
+ import {
+ ref,
+ reactive,
+ computed,
+ watch,
+ onMounted,
+ getCurrentInstance,
+ } from "vue";
import { useRouter } from "vue-router";
import {
page,
@@ -328,6 +355,7 @@
update,
staffOnJobListPage,
} from "@/api/personnelManagement/class";
+ import { findStaffLeaveListPage } from "@/api/personnelManagement/staffLeave.js";
import { deptTreeSelect } from "@/api/system/user.js";
import { getAttendanceRules } from "@/api/personnelManagement/attendanceRules.js";
import { useDict } from "@/utils/dict";
@@ -376,6 +404,8 @@
// 浜哄憳鍒楄〃
const personList = ref([]);
+ const personListAll = ref([]);
+ const leaveDateMap = ref({});
// 鍔犺浇鐘舵��
const loading = ref(false);
@@ -383,8 +413,18 @@
// 鎺掔彮鏌ヨ鏉′欢
const schedulingQuery = reactive({
week: "",
+ deptId: "",
userId: null,
shift: "",
+ });
+
+ const schedulingClassType = computed(() => {
+ if (!schedulingQuery.deptId) return classType.value || [];
+ return (classType.value || []).filter(item => {
+ if (["998", "999"].includes(String(item?.id))) return true;
+ const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id;
+ return String(itemDeptId) === String(schedulingQuery.deptId);
+ });
});
// 鍒楄〃鏁版嵁
@@ -593,6 +633,10 @@
proxy.$modal.msgError("璇烽�夋嫨鍛ㄦ");
return;
}
+ if (!schedulingQuery.deptId) {
+ proxy.$modal.msgError("璇烽�夋嫨閮ㄩ棬");
+ return;
+ }
let time = schedulingQuery.week.getTime();
// 鏍煎紡鍖栨棩鏈熶负 YYYY-MM-DD 鏍煎紡
@@ -616,6 +660,15 @@
proxy.$modal.msgError("璇烽�夋嫨鐝");
return;
}
+ const blockedEmployees = (personList.value || []).filter(item => {
+ const leaveDate = getEmployeeLeaveDate(item?.id);
+ const weekEndDate = getSchedulingWeekEndDate();
+ return leaveDate && weekEndDate && weekEndDate.getTime() > leaveDate.getTime();
+ });
+ if (blockedEmployees.length) {
+ proxy.$modal.msgError("瀛樺湪宸茶秴杩囩鑱屾棩鏈熺殑浜哄憳锛屼笉鑳界户缁帓鐝�");
+ return;
+ }
loading.value = true;
add({
startWeek,
@@ -628,8 +681,10 @@
proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
schedulingVisible.value = false;
schedulingQuery.week = "";
+ schedulingQuery.deptId = "";
schedulingQuery.userId = null;
schedulingQuery.shift = "";
+ personList.value = [];
refresh();
})
.catch(err => {
@@ -683,17 +738,20 @@
}
proxy.$download.saveAs(blob, fileName + ".xlsx");
})
- .catch(err => {
- })
+ .catch(err => {})
.finally(() => {
downLoading.value = false;
});
};
// 澶勭悊鍛戒护
- const handleCommand = (e, m) => {
+ const handleCommand = (e, row, cell) => {
+ if (isCellBlockedByLeaveDate(row, cell)) {
+ proxy.$modal.msgError("璇ュ憳宸ュ凡瓒呭嚭绂昏亴鏃ユ湡锛屼笉鑳界户缁帓鐝�");
+ return;
+ }
// if (e != m.shift) {
update({
- id: m.id,
+ id: cell.id,
personalAttendanceLocationConfigId: e,
}).then(res => {
proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
@@ -709,7 +767,18 @@
// 鏌ヨ瑙勫垯鍒楄〃
const fetchData = () => {
getAttendanceRules({ current: -1, size: -1 }).then(res => {
- classType.value = res.data.records;
+ const records = Array.isArray(res?.data?.records) ? res.data.records : [];
+ const appendList = [
+ { id: 998, shift: "浼戞伅" },
+ { id: 999, shift: "璇峰亣" },
+ ];
+ const exists = new Set(records.map(r => String(r?.id)));
+ appendList.forEach(item => {
+ if (!exists.has(String(item.id))) {
+ records.push(item);
+ }
+ });
+ classType.value = records;
});
};
// 鑾峰彇鐢ㄦ埛
@@ -724,8 +793,139 @@
staffState: 1,
}).then(res => {
let arr = res.data.records;
- personList.value = arr;
+ personListAll.value = arr;
+ updatePersonListByDept();
});
+ };
+
+ const getLeaveDates = () => {
+ findStaffLeaveListPage({
+ current: -1,
+ size: -1,
+ }).then(res => {
+ const records = Array.isArray(res?.data?.records) ? res.data.records : [];
+ const map = {};
+ records.forEach(item => {
+ const staffId = item?.staffOnJobId ?? item?.staff_on_job_id;
+ const leaveDate = item?.leaveDate ?? item?.leave_date;
+ if (!staffId || !leaveDate) return;
+ const key = String(staffId);
+ const prev = map[key];
+ if (!prev || String(leaveDate) > String(prev)) {
+ map[key] = leaveDate;
+ }
+ });
+ leaveDateMap.value = map;
+ updatePersonListByDept();
+ syncPersonListSelection();
+ });
+ };
+
+ const findDeptNodeById = (deptId, deptList = deptOptions.value) => {
+ const list = Array.isArray(deptList) ? deptList : [];
+ for (const node of list) {
+ if (String(node.id) === String(deptId)) return node;
+ if (node.children && node.children.length) {
+ const found = findDeptNodeById(deptId, node.children);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+
+ const collectDeptIds = node => {
+ const ids = [];
+ if (!node) return ids;
+ ids.push(String(node.id));
+ if (node.children && node.children.length) {
+ node.children.forEach(child => {
+ ids.push(...collectDeptIds(child));
+ });
+ }
+ return ids;
+ };
+
+ const updatePersonListByDept = () => {
+ if (!schedulingQuery.deptId) {
+ personList.value = [];
+ return;
+ }
+ const node = findDeptNodeById(schedulingQuery.deptId);
+ const deptIds = new Set(collectDeptIds(node));
+ personList.value = (personListAll.value || []).filter(item => {
+ const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id;
+ return deptIds.has(String(itemDeptId)) && !isEmployeeBlockedByLeaveDate(item);
+ });
+ };
+
+ const parseDateOnly = value => {
+ if (!value) return null;
+ if (value instanceof Date) {
+ const d = new Date(value);
+ d.setHours(0, 0, 0, 0);
+ return d;
+ }
+ const normalized = String(value).trim().slice(0, 10);
+ if (!normalized) return null;
+ const d = new Date(normalized.replace(/-/g, "/"));
+ if (Number.isNaN(d.getTime())) return null;
+ d.setHours(0, 0, 0, 0);
+ return d;
+ };
+
+ const getSchedulingWeekEndDate = () => {
+ if (!schedulingQuery.week) return null;
+ const start = new Date(schedulingQuery.week);
+ start.setHours(0, 0, 0, 0);
+ return new Date(start.getTime() + 24 * 60 * 60 * 1000 * 5);
+ };
+
+ const getCellDate = cell => parseDateOnly(cell?.time);
+
+ const getEmployeeLeaveDate = staffId => {
+ const leaveDate = leaveDateMap.value[String(staffId)];
+ return parseDateOnly(leaveDate);
+ };
+
+ const isEmployeeBlockedByLeaveDate = item => {
+ const leaveDate = getEmployeeLeaveDate(item?.id);
+ if (!leaveDate) return false;
+ const weekEndDate = getSchedulingWeekEndDate();
+ if (!weekEndDate) return false;
+ return weekEndDate.getTime() > leaveDate.getTime();
+ };
+
+ const isCellBlockedByLeaveDate = (row, cell) => {
+ const staffId = row?.userId ?? row?.user_id ?? row?.id;
+ const leaveDate = getEmployeeLeaveDate(staffId);
+ const cellDate = getCellDate(cell);
+ if (!leaveDate || !cellDate) return false;
+ return cellDate.getTime() > leaveDate.getTime();
+ };
+
+ const syncPersonListSelection = () => {
+ if (!Array.isArray(schedulingQuery.userId) || !schedulingQuery.userId.length) return;
+ const validIds = new Set((personList.value || []).map(item => String(item.id)));
+ const nextIds = schedulingQuery.userId.filter(id => validIds.has(String(id)));
+ if (nextIds.length !== schedulingQuery.userId.length) {
+ schedulingQuery.userId = nextIds;
+ proxy.$modal.msgWarning("宸茶嚜鍔ㄥ墧闄よ秴鍑虹鑱屾棩鏈熺殑浜哄憳");
+ }
+ };
+
+ const setDefaultShiftByDept = () => {
+ if (!schedulingQuery.deptId) {
+ schedulingQuery.shift = "";
+ return;
+ }
+ const first = schedulingClassType.value?.[0];
+ schedulingQuery.shift = first ? first.id : "";
+ };
+
+ const onSchedulingDeptChange = () => {
+ schedulingQuery.userId = null;
+ updatePersonListByDept();
+ setDefaultShiftByDept();
};
// 鏍规嵁瀛楀吀鑾峰彇鏃ユ湡
@@ -749,6 +949,7 @@
onMounted(() => {
fetchData();
getUsers();
+ getLeaveDates();
fetchDeptOptions();
if (query.month) {
init();
@@ -761,6 +962,45 @@
}
monthList.value.reverse();
});
+
+ watch(
+ () => classType.value,
+ () => {
+ if (schedulingVisible.value && schedulingQuery.deptId) {
+ setDefaultShiftByDept();
+ }
+ }
+ );
+
+ watch(
+ () => schedulingQuery.week,
+ () => {
+ if (!schedulingVisible.value) return;
+ updatePersonListByDept();
+ syncPersonListSelection();
+ }
+ );
+
+ watch(
+ () => schedulingQuery.deptId,
+ () => {
+ if (!schedulingVisible.value) return;
+ updatePersonListByDept();
+ syncPersonListSelection();
+ }
+ );
+
+ watch(
+ () => schedulingVisible.value,
+ val => {
+ if (!val) return;
+ schedulingQuery.week = "";
+ schedulingQuery.deptId = "";
+ schedulingQuery.userId = null;
+ schedulingQuery.shift = "";
+ personList.value = [];
+ }
+ );
</script>
<style scoped>
@@ -973,7 +1213,7 @@
.user-stats {
/* display: flex; */
/* flex-wrap: wrap;
- gap: 10px; */
+ gap: 10px; */
margin-bottom: 4px;
}
--
Gitblit v1.9.3