From 8c79a8e7451a101e2769d637f5b8a14d9c67623a Mon Sep 17 00:00:00 2001 From: ywyonui Date: Fri, 12 Sep 2025 19:44:12 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=8B=86=E5=88=86=E7=82=B9=E5=90=8D?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=202=E3=80=81=E9=BB=98=E8=AE=A4=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E7=8F=AD=E4=B8=BB=E4=BB=BB=E5=92=8C=E5=89=AF=E7=8F=AD?= =?UTF-8?q?=E4=B8=BB=E4=BB=BB=E9=99=AA=E9=A4=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/base/jsApi.ts | 7 + src/components/BasicJsPicker/Picker.vue | 134 ++- src/components/BasicSpCsMgr/index.vue | 4 +- .../view/hr/jsQj/components/jsQjDkEdit.vue | 6 +- src/pages/view/routine/jc/bzList.vue | 5 +- src/pages/view/routine/jc/components/dm.vue | 944 ++---------------- src/pages/view/routine/jc/components/dmJs.vue | 278 ++++++ src/pages/view/routine/jc/components/dmXs.vue | 588 +++++++++++ src/pages/view/routine/jc/detail.vue | 46 +- src/utils/debounce.ts | 107 ++ 10 files changed, 1169 insertions(+), 950 deletions(-) create mode 100644 src/api/base/jsApi.ts create mode 100644 src/pages/view/routine/jc/components/dmJs.vue create mode 100644 src/pages/view/routine/jc/components/dmXs.vue create mode 100644 src/utils/debounce.ts diff --git a/src/api/base/jsApi.ts b/src/api/base/jsApi.ts new file mode 100644 index 0000000..915856b --- /dev/null +++ b/src/api/base/jsApi.ts @@ -0,0 +1,7 @@ +// 食堂巡查相关API接口 +import { get, post } from "@/utils/request"; + +// 查询某个班级的班主任和副班主任 +export const findNjJsListApi = async (params: any) => { + return await get("/api/bj/findNjJsList", params); +}; diff --git a/src/components/BasicJsPicker/Picker.vue b/src/components/BasicJsPicker/Picker.vue index 22a6721..89933c2 100644 --- a/src/components/BasicJsPicker/Picker.vue +++ b/src/components/BasicJsPicker/Picker.vue @@ -8,12 +8,12 @@ {{ getShowSelectedName() }} - {{ placeholder }} + {{ placeholder }} - - + + 取消 @@ -21,12 +21,12 @@ 确定 - + - - + + {{ item.label }} @@ -44,7 +44,7 @@ const { getAllJsBasicInfoVo } = useCommonStore(); // 接收外部传入属性 const props = withDefaults(defineProps<{ - defualtValue?: any, + defaultValue?: any, parentData?: any, multiple?: boolean, // 排除id列表 @@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{ // 自定义触发器 customTrigger?: boolean }>(), { - defualtValue: null, + defaultValue: null, parentData: null, multiple: false, excludeIds: [], @@ -79,13 +79,16 @@ let searchKey = ""; const selectedList = ref([]); const getShowSelectedName = () => { - return selectedList.value.map((item: any) => item.label).join(","); + if (selectedList.value && selectedList.value.length > 0) { + return selectedList.value.map((item: any) => item.label).join(","); + } + return ''; }; const handleSearch = (value: any) => { searchKey = value; rebuildJsList(); - return + return }; const handleSelect = (item: any) => { @@ -134,47 +137,81 @@ const showPicker = () => { }); }; -const rebuildJsList = () => { +const rebuildJsList = () => { jsList.value = []; if (!jsListAll.value || !Array.isArray(jsListAll.value)) { return; } - // 使用for...of确保同步执行 for (const item of jsListAll.value) { // 检查item是否有效 if (!item || !item.id || !item.jsxm) { continue; } - + // 检查是否在排除列表中 if (props.excludeIds && Array.isArray(props.excludeIds) && props.excludeIds.includes(item.id)) { continue; } - + // 判断教师姓名jsxm包含搜索关键词 if (!searchKey || item.jsxm.includes(searchKey)) { + const isSelected = selectedList.value.some((selected: any) => selected.id === item.id); jsList.value.push({ ...item, label: item.jsxm, value: item.id, - selected: false + selected: isSelected, }); } } }; -// 暴露方法给父组件 -defineExpose({ - showPicker -}); +const clearValue = () => { + selectedList.value = []; + for (const item of jsList.value) { + item.selected = false; + } +}; + +const setValue = (val: any) => { + // 设置默认值 + if (val) { + if (props.multiple) { + if (Array.isArray(val) && val.length > 0) { + selectedList.value = []; + for (const item of jsList.value) { + if (val.includes(item.value)) { + item.selected = true; + selectedList.value.push(item); + } else { + item.selected = false; + } + } + } else { + clearValue(); + } + } else { + for (const item of jsList.value) { + if (item.value === val) { + item.selected = true; + selectedList.value = [item]; + } else { + item.selected = false; + } + } + } + } else { + clearValue(); + } +}; onMounted(async () => { try { const res = await getAllJsBasicInfoVo() if (res && res.result && Array.isArray(res.result)) { jsListAll.value = res.result; - + // 确保rebuildJsList完全执行完成 await new Promise((resolve) => { rebuildJsList(); @@ -183,34 +220,7 @@ onMounted(async () => { resolve(); }); }); - - // 设置默认值 - if (props.defualtValue) { - if (props.multiple) { - if (Array.isArray(props.defualtValue) && props.defualtValue.length > 0) { - selectedList.value = []; - // 使用for...of确保同步执行 - for (const item of jsList.value) { - if (props.defualtValue.includes(item.value)) { - item.selected = true; - selectedList.value.push(item); - } else { - item.selected = false; - } - } - } - } else { - // 使用for...of确保同步执行 - for (const item of jsList.value) { - if (item.value === props.defualtValue) { - item.selected = true; - selectedList.value = [item]; - } else { - item.selected = false; - } - } - } - } + setValue(props.defaultValue); } else { console.warn('JsPicker: 获取教师数据失败或数据格式不正确'); } @@ -218,11 +228,18 @@ onMounted(async () => { console.error('JsPicker初始化失败:', error); } }); + +// 暴露方法给父组件 +defineExpose({ + showPicker, + setValue +}); \ No newline at end of file diff --git a/src/pages/view/routine/jc/components/dmJs.vue b/src/pages/view/routine/jc/components/dmJs.vue new file mode 100644 index 0000000..5be47f4 --- /dev/null +++ b/src/pages/view/routine/jc/components/dmJs.vue @@ -0,0 +1,278 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/jc/components/dmXs.vue b/src/pages/view/routine/jc/components/dmXs.vue new file mode 100644 index 0000000..59e76f2 --- /dev/null +++ b/src/pages/view/routine/jc/components/dmXs.vue @@ -0,0 +1,588 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/jc/detail.vue b/src/pages/view/routine/jc/detail.vue index 4b2595e..5a931f1 100644 --- a/src/pages/view/routine/jc/detail.vue +++ b/src/pages/view/routine/jc/detail.vue @@ -41,25 +41,17 @@ class="teacher-item bg-white r-md p-12" > - + - + {{ teacher.jsXm }} - - - {{ getTeacherStatusText(teacher.pcZt) }} - - @@ -151,7 +143,7 @@ @@ -226,13 +218,23 @@ const loadDetail = async () => { if (response.result) { dmDetail.value = response.result; - dmXsList.value = response.result.dmXsList || []; - dmXsList.value = sortChinese(dmXsList.value, 'xsXm'); - unBmXsList.value = response.result.unBmXsList || []; - unBmXsList.value = sortChinese(unBmXsList.value, 'xm'); - + let srcList = response.result.dmXsList || []; + srcList = sortChinese(srcList, 'xsXm'); + dmXsList.value = []; + unBmXsList.value = []; + for (let i = 0; i < srcList.length; i++) { + const xs = srcList[i]; + switch (xs.jcZt) { + case 'E': { + unBmXsList.value.push(xs); + } break; + default: { + dmXsList.value.push(xs); + } + } + } rsData.value = { - zrs: unBmXsList.value.length + dmXsList.value.length, + zrs: srcList.length, bmRs: dmXsList.value.length, unBmRs: unBmXsList.value.length, } @@ -474,7 +476,13 @@ onMounted(() => { margin-bottom: 30rpx; } -.teacher-grid, .xs-grid { +.teacher-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20rpx; +} + +.xs-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20rpx; diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 0000000..c3a2905 --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,107 @@ +// src/utils/debounce.ts +import { ref } from 'vue'; + +/** + * 通用防抖函数 + * @param func 需要防抖的函数 + * @param delay 延迟时间(毫秒) + * @param immediate 是否立即执行 + * @returns 防抖后的函数 + */ +export function debounce(func: Function, wait: number, immediate: boolean = false) { + let timeout: NodeJS.Timeout | null; + + return function (this: any, ...args: any[]) { + const context = this; + const later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + const callNow = immediate && !timeout; + if (timeout) clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} + +/** + * 防抖状态管理类 + * 用于管理多个防抖状态 + */ +export class DebounceManager { + private states: Map = new Map(); + + /** + * 设置防抖状态 + * @param key 状态标识 + * @param value 状态值 + * @param duration 状态持续时间(毫秒) + */ + setState(key: string, value: boolean, duration?: number): void { + this.states.set(key, value); + + if (value && duration) { + setTimeout(() => { + this.states.set(key, false); + }, duration); + } + } + + /** + * 获取防抖状态 + * @param key 状态标识 + * @returns 状态值 + */ + getState(key: string): boolean { + return this.states.get(key) || false; + } + + /** + * 重置所有状态 + */ + reset(): void { + this.states.clear(); + } +} + +/** + * Vue组合式API防抖函数 + * @param delay 延迟时间(毫秒) + * @returns 包含防抖状态和控制函数的对象 + */ +export function useDebounce(delay: number = 1000) { + const isProcessing = ref(false); + + const debounce = Promise>( + func: T + ): ((...args: Parameters) => Promise | void>) => { + return async (...args: Parameters): Promise | void> => { + // 如果正在处理中,则阻止新的调用 + if (isProcessing.value) { + return; + } + + isProcessing.value = true; + + try { + const result = await func(...args); + return result; + } finally { + // 延迟重置状态,防止快速重复点击 + setTimeout(() => { + isProcessing.value = false; + }, delay); + } + }; + }; + + const reset = () => { + isProcessing.value = false; + }; + + return { + isProcessing, + debounce, + reset + }; +} \ No newline at end of file