import { ref } from 'vue'; import { listDictData } from '@/api/system/dict-data'; import { listCollege } from '@/api/gxmu/college'; import { listClassInfo } from '@/api/gxmu/class'; import type { ClassInfo } from '@/api/gxmu/class/model'; export interface SelectOption { label: string; value: string; } export interface ClassCascaderOption { label: string; value: string; children?: ClassCascaderOption[]; } export interface ClassCascadeSelection { classId?: number; className: string; collegeId?: number; collegeName: string; path: string[]; combinedLabel: string; } const NATION_DICT_ID = 173; const POLITICS_DICT_ID = 172; const genderOptions: SelectOption[] = [ { label: '男', value: '男' }, { label: '女', value: '女' } ]; let nationCache: SelectOption[] | null = null; let politicsCache: SelectOption[] | null = null; let collegeCache: SelectOption[] | null = null; let classCache: SelectOption[] | null = null; let classCascaderCache: ClassCascaderOption[] | null = null; let classCascadeSelectionCache: ClassCascadeSelection[] | null = null; const normalizeOptions = (values: string[]) => values .map((item) => item.trim()) .filter((item, index, arr) => !!item && arr.indexOf(item) === index) .map((item) => ({ label: item, value: item })); const normalizeText = (value?: string) => String(value || '') .trim() .replace(/[\/|,,、]+/g, ' ') .replace(/\s+/g, ' '); const compactText = (value?: string) => normalizeText(value).replace(/\s+/g, '').toLowerCase(); const buildCombinedLabel = (collegeName?: string, className?: string) => [String(collegeName || '').trim(), String(className || '').trim()] .filter(Boolean) .join(' '); const buildClassCascadeData = (list: ClassInfo[]) => { const collegeMap = new Map(); const classSelections: ClassCascadeSelection[] = []; list.forEach((item, index) => { const className = String(item.className || '').trim(); const collegeName = String(item.collegeName || '').trim(); if (!className || !collegeName) { return; } const collegeValue = `college:${ item.collegeId ?? `${collegeName}:${index}` }`; const classValue = `class:${ item.id ?? `${collegeValue}:${className}:${index}` }`; let collegeOption = collegeMap.get(collegeValue); if (!collegeOption) { collegeOption = { label: collegeName, value: collegeValue, children: [] }; collegeMap.set(collegeValue, collegeOption); } if ( !collegeOption.children?.some( (child) => child.label === className && child.value === classValue ) ) { collegeOption.children?.push({ label: className, value: classValue }); } classSelections.push({ classId: item.id, className, collegeId: item.collegeId, collegeName, path: [collegeValue, classValue], combinedLabel: buildCombinedLabel(collegeName, className) }); }); return { classOptions: normalizeOptions( classSelections.map((item) => item.className) ), cascaderOptions: Array.from(collegeMap.values()).filter( (item) => item.children?.length ), classSelections }; }; const ensureClassDataLoaded = async () => { if (classCache && classCascaderCache && classCascadeSelectionCache) { return; } const list = await listClassInfo({ status: 1, limit: 1000 }); const { classOptions, cascaderOptions, classSelections } = buildClassCascadeData(list); classCache = classOptions; classCascaderCache = cascaderOptions; classCascadeSelectionCache = classSelections; }; export const filterSelectOption = (input: string, option: SelectOption) => { return String(option?.label || '') .toLowerCase() .includes(input.toLowerCase()); }; export const useGxmuFormSelectOptions = () => { const loading = ref(false); const loaded = ref(false); const nationOptions = ref(nationCache ?? []); const politicsOptions = ref(politicsCache ?? []); const collegeOptions = ref(collegeCache ?? []); const classOptions = ref(classCache ?? []); const classCascaderOptions = ref( classCascaderCache ?? [] ); const loadNationOptions = async () => { if (nationCache) { nationOptions.value = nationCache; return nationCache; } const result = await listDictData({ dictId: NATION_DICT_ID }); nationCache = result .filter((item) => !!item.dictDataName) .map((item) => ({ label: String(item.dictDataName), value: String(item.dictDataName) })); nationOptions.value = nationCache; return nationCache; }; const loadCollegeOptions = async () => { if (collegeCache) { collegeOptions.value = collegeCache; return collegeCache; } const list = await listCollege({ status: 1, limit: 500 }); collegeCache = normalizeOptions( list.map((item) => item.collegeName || item.shortName || '') ); collegeOptions.value = collegeCache; return collegeCache; }; const loadPoliticsOptions = async () => { if (politicsCache) { politicsOptions.value = politicsCache; return politicsCache; } const result = await listDictData({ dictId: POLITICS_DICT_ID }); politicsCache = result .filter((item) => !!item.dictDataName) .map((item) => ({ label: String(item.dictDataName), value: String(item.dictDataName) })); politicsOptions.value = politicsCache; return politicsCache; }; const loadClassOptions = async () => { if (classCache) { classOptions.value = classCache; return classCache; } await ensureClassDataLoaded(); classOptions.value = classCache; return classCache; }; const loadClassCascaderOptions = async () => { if (classCascaderCache) { classCascaderOptions.value = classCascaderCache; return classCascaderCache; } await ensureClassDataLoaded(); classCascaderOptions.value = classCascaderCache; return classCascaderCache; }; const findClassCascadeSelection = ( value?: string, mode: 'class' | 'collegeClass' = 'class' ) => { const source = classCascadeSelectionCache ?? []; const normalizedValue = normalizeText(value); const compactValue = compactText(value); if (!normalizedValue) { return void 0; } if (mode === 'collegeClass') { return ( source.find( (item) => normalizeText(item.combinedLabel) === normalizedValue ) ?? source.find( (item) => compactText(item.combinedLabel) === compactValue ) ?? source.find((item) => { return ( compactValue.includes(compactText(item.collegeName)) && compactValue.includes(compactText(item.className)) ); }) ); } return ( source.find( (item) => normalizeText(item.className) === normalizedValue ) ?? source.find((item) => compactText(item.className) === compactValue) ?? source.find( (item) => normalizeText(item.combinedLabel) === normalizedValue ) ?? source.find((item) => compactText(item.combinedLabel) === compactValue) ); }; const getClassCascadeSelectionByPath = (path?: string[]) => { if (!path?.length) { return void 0; } const key = path.join('>'); return (classCascadeSelectionCache ?? []).find( (item) => item.path.join('>') === key ); }; const loadAll = async () => { if (loaded.value) { return; } loading.value = true; try { await Promise.all([ loadNationOptions(), loadPoliticsOptions(), loadCollegeOptions(), loadClassOptions(), loadClassCascaderOptions() ]); loaded.value = true; } finally { loading.value = false; } }; return { loading, loaded, genderOptions, nationOptions, politicsOptions, collegeOptions, classOptions, classCascaderOptions, loadAll, loadNationOptions, loadPoliticsOptions, loadCollegeOptions, loadClassOptions, loadClassCascaderOptions, findClassCascadeSelection, getClassCascadeSelectionByPath, buildCombinedLabel }; };