Files
cn-rdms-web/src/store/modules/dict/index.ts

280 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref } from 'vue';
import { defineStore } from 'pinia';
import { RDMS_OBJECT_DIRECTION_DICT_CODE, RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE } from '@/constants/dict';
import { fetchGetDictDataByCode, fetchGetFrontendDictCache } from '@/service/api';
import { SetupStoreId } from '@/enum';
type DictValue = string | number | null | undefined;
type DictFilterOptions = {
onlyEnabled?: boolean;
};
type DictLabelOptions = DictFilterOptions & {
fallback?: string;
};
type DictLabelsOptions = DictLabelOptions & {
separator?: string;
};
function sortDictData(list: Api.Dict.DictData[]) {
return list.slice().sort((left, right) => left.sort - right.sort || left.label.localeCompare(right.label, 'zh-CN'));
}
// hex 色值兜底校验:仅接受 #RRGGBB6 位);其他格式(含 #RGB 简写 / rgb())一律视为无效回落到默认渲染
const HEX_COLOR_PATTERN = /^#[0-9a-f]{6}$/i;
function normalizeColorType(raw: unknown): string | null {
if (typeof raw !== 'string') return null;
const trimmed = raw.trim().toLowerCase();
return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : null;
}
function normalizeFrontendDictData(
dictType: string,
list: Api.Dict.FrontendDictData[],
dictIndex: number
): Api.Dict.DictData[] {
const normalizedList = list.map((item, itemIndex) => ({
id: -((dictIndex + 1) * 100000 + itemIndex + 1),
label: item.label,
value: item.value,
dictType: item.dictType || dictType,
sort: item.sort,
status: item.status ?? 0,
colorType: normalizeColorType(item.colorType),
remark: item.remark ?? null,
createTime: 0
}));
return sortDictData(normalizedList);
}
function normalizeDictDataItem(item: Api.Dict.DictData, dictType: string): Api.Dict.DictData {
return {
...item,
value: String(item.value),
dictType: item.dictType || dictType,
status: item.status ?? 0,
colorType: normalizeColorType(item.colorType),
remark: item.remark ?? null
};
}
function normalizeFrontendDictCache(cache: Api.Dict.FrontendDictCache) {
const entries = Object.entries(cache);
return Object.fromEntries(
entries.map(([dictType, list], index) => [dictType, normalizeFrontendDictData(dictType, list, index)])
);
}
function applyDictTypeAliases(dictDataMap: Record<string, Api.Dict.DictData[]>) {
const nextDictDataMap = { ...dictDataMap };
// 兼容后端尚未切换完成的过渡期:旧编码仍返回时,前端统一映射到新编码。
if (!nextDictDataMap[RDMS_OBJECT_DIRECTION_DICT_CODE] && nextDictDataMap[RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE]) {
nextDictDataMap[RDMS_OBJECT_DIRECTION_DICT_CODE] = nextDictDataMap[RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE].map(
item => ({
...item,
dictType: RDMS_OBJECT_DIRECTION_DICT_CODE
})
);
}
return nextDictDataMap;
}
function createRuntimeDictTypes(dictDataMap: Record<string, Api.Dict.DictData[]>) {
return Object.keys(dictDataMap).map((dictType, index) => ({
id: -(index + 1),
name: dictType,
type: dictType,
status: 0 as const,
remark: null,
createTime: 0
}));
}
function findDictItem(list: Api.Dict.DictData[], value?: DictValue) {
if (value === null || value === undefined || value === '') {
return undefined;
}
return list.find(item => item.value === String(value));
}
export const useDictStore = defineStore(SetupStoreId.Dict, () => {
const loading = ref(false);
const initialized = ref(false);
const dictTypes = ref<Api.Dict.DictType[]>([]);
const dictDataMap = ref<Record<string, Api.Dict.DictData[]>>({});
const loadedAt = ref<number | null>(null);
let initPromise: Promise<boolean> | null = null;
const dictDataLoadPromises = new Map<string, Promise<boolean>>();
function resetDictCache() {
dictTypes.value = [];
dictDataMap.value = {};
loadedAt.value = null;
initialized.value = false;
initPromise = null;
dictDataLoadPromises.clear();
}
async function initDictCache(force = false) {
if (initialized.value && !force) {
return true;
}
if (initPromise && !force) {
return initPromise;
}
if (force) {
resetDictCache();
}
initPromise = (async () => {
loading.value = true;
const result = await fetchGetFrontendDictCache();
loading.value = false;
if (result.error) {
initPromise = null;
return false;
}
const normalizedDictDataMap = applyDictTypeAliases(normalizeFrontendDictCache(result.data || {}));
dictTypes.value = createRuntimeDictTypes(normalizedDictDataMap);
dictDataMap.value = normalizedDictDataMap;
loadedAt.value = Date.now();
initialized.value = true;
initPromise = null;
return true;
})();
return initPromise;
}
async function ensureDictData(dictType: string, force = false) {
if (!dictType) {
return false;
}
if (!initialized.value) {
await initDictCache();
}
if (!force && getDictData(dictType).length > 0) {
return true;
}
const pending = dictDataLoadPromises.get(dictType);
if (pending && !force) {
return pending;
}
const promise = (async () => {
const result = await fetchGetDictDataByCode(dictType);
if (result.error || !result.data?.list?.length) {
return false;
}
dictDataMap.value = {
...dictDataMap.value,
[dictType]: sortDictData(result.data.list.map(item => normalizeDictDataItem(item, dictType)))
};
dictTypes.value = createRuntimeDictTypes(dictDataMap.value);
return true;
})();
dictDataLoadPromises.set(dictType, promise);
try {
return await promise;
} finally {
if (dictDataLoadPromises.get(dictType) === promise) {
dictDataLoadPromises.delete(dictType);
}
}
}
function getDictData(dictType: string, onlyEnabled = false) {
if (!dictType) {
return [];
}
const list = dictDataMap.value[dictType] || [];
if (!onlyEnabled) {
return list;
}
return list.filter(item => item.status === 0);
}
function getDictOptions(dictType: string, onlyEnabled = true) {
return getDictData(dictType, onlyEnabled).map(item => ({
label: item.label,
value: item.value
}));
}
function getDictItem(dictType: string, value?: DictValue, options: DictFilterOptions = {}) {
return findDictItem(getDictData(dictType, options.onlyEnabled), value);
}
function getDictLabel(dictType: string, value?: DictValue, options: DictLabelOptions = {}) {
const { fallback = '--', onlyEnabled = false } = options;
if (value === null || value === undefined || value === '') {
return fallback;
}
const matched = getDictItem(dictType, value, { onlyEnabled });
return matched?.label || String(value);
}
function getDictLabels(dictType: string, values?: Array<DictValue> | null, options: DictLabelsOptions = {}) {
const { fallback = '--', separator = ' / ', onlyEnabled = false } = options;
if (!values?.length) {
return fallback;
}
const labels = values
.filter(value => value !== null && value !== undefined && value !== '')
.map(value => getDictLabel(dictType, value, { fallback: String(value), onlyEnabled }));
return labels.length ? labels.join(separator) : fallback;
}
function hasDictValue(dictType: string, value?: DictValue, options: DictFilterOptions = {}) {
return Boolean(getDictItem(dictType, value, options));
}
return {
loading,
initialized,
dictTypes,
dictDataMap,
loadedAt,
initDictCache,
ensureDictData,
resetDictCache,
getDictData,
getDictOptions,
getDictItem,
getDictLabel,
getDictLabels,
hasDictValue
};
});