312 lines
8.2 KiB
TypeScript
312 lines
8.2 KiB
TypeScript
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<string, ClassCascaderOption>();
|
|
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<SelectOption[]>(nationCache ?? []);
|
|
const politicsOptions = ref<SelectOption[]>(politicsCache ?? []);
|
|
const collegeOptions = ref<SelectOption[]>(collegeCache ?? []);
|
|
const classOptions = ref<SelectOption[]>(classCache ?? []);
|
|
const classCascaderOptions = ref<ClassCascaderOption[]>(
|
|
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
|
|
};
|
|
};
|