feat: 字典功能

This commit is contained in:
dap
2024-08-08 14:05:08 +08:00
parent 7f38933e37
commit 66100d44b2
12 changed files with 707 additions and 2 deletions

View File

@@ -0,0 +1,55 @@
import type { DictData } from '#/api/system/dict/dict-data-model';
import { dictDataInfo } from '#/api/system/dict/dict-data';
import { type Option, useDictStore } from '#/store/dict';
// todo 重复代码的封装
/**
* 添加一个字典请求状态的缓存
*
* 主要解决多次请求重复api的问题(不能用abortController 会导致除了第一个其他的获取的全为空)
* 比如在一个页面 index表单 modal drawer总共会请求三次 但是获取的都是一样的数据
*/
const dictRequestCache = new Map<string, Promise<DictData[] | void>>();
export function getDict(dictName: string): DictData[] {
const { getDict, setDictInfo } = useDictStore();
// 这里拿到
const dictList = getDict(dictName);
if (
dictList.length === 0 && // 检查请求状态缓存
!dictRequestCache.has(dictName)
) {
dictRequestCache.set(
dictName,
dictDataInfo(dictName).then((resp) => {
// 缓存到store 这样就不用重复获取了
// 内部处理了push的逻辑 这里不用push
setDictInfo(dictName, resp);
// 移除请求状态缓存
dictRequestCache.delete(dictName);
}),
);
}
return dictList;
}
export function getDictOptions(dictName: string): Option[] {
const { getDictOptions, setDictInfo } = useDictStore();
const dictOptionList = getDictOptions(dictName);
if (
dictOptionList.length === 0 && // 检查请求状态缓存
!dictRequestCache.has(dictName)
) {
dictRequestCache.set(
dictName,
dictDataInfo(dictName).then((resp) => {
// 缓存到store 这样就不用重复获取了
// 内部处理了push的逻辑 这里不用push
setDictInfo(dictName, resp);
// 移除请求状态缓存
dictRequestCache.delete(dictName);
}),
);
}
return dictOptionList;
}

View File

@@ -0,0 +1,180 @@
import type {
RouteLocationNormalized,
RouteRecordNormalized,
} from 'vue-router';
import type { App, Component } from 'vue';
import { unref } from 'vue';
import {
intersectionWith,
isArray,
isEqual,
isObject,
mergeWith,
unionWith,
} from 'lodash-es';
export const noop = () => {};
/**
* @description: Set ui mount node
*/
export function getPopupContainer(node?: HTMLElement): HTMLElement {
return (node?.parentNode as HTMLElement) ?? document.body;
}
/**
* Add the object as a parameter to the URL
* @param baseUrl url
* @param obj
* @returns {string}
* eg:
* let obj = {a: '3', b: '4'}
* setObjToUrlParams('www.baidu.com', obj)
* ==>www.baidu.com?a=3&b=4
*/
export function setObjToUrlParams(baseUrl: string, obj: any): string {
let parameters = '';
for (const key in obj) {
parameters += `${key}=${encodeURIComponent(obj[key])}&`;
}
parameters = parameters.replace(/&$/, '');
return /\?$/.test(baseUrl)
? baseUrl + parameters
: baseUrl.replace(/\/?$/, '?') + parameters;
}
/**
* Recursively merge two objects.
* 递归合并两个对象。
*
* @param source The source object to merge from. 要合并的源对象。
* @param target The target object to merge into. 目标对象,合并后结果存放于此。
* @param mergeArrays How to merge arrays. Default is "replace".
* 如何合并数组。默认为replace。
* - "union": Union the arrays. 对数组执行并集操作。
* - "intersection": Intersect the arrays. 对数组执行交集操作。
* - "concat": Concatenate the arrays. 连接数组。
* - "replace": Replace the source array with the target array. 用目标数组替换源数组。
* @returns The merged object. 合并后的对象。
*/
export function deepMerge<
T extends null | object | undefined,
U extends null | object | undefined,
>(
source: T,
target: U,
mergeArrays: 'concat' | 'intersection' | 'replace' | 'union' = 'replace',
): T & U {
if (!target) {
return source as T & U;
}
if (!source) {
return target as T & U;
}
return mergeWith({}, source, target, (sourceValue, targetValue) => {
if (isArray(targetValue) && isArray(sourceValue)) {
switch (mergeArrays) {
case 'concat': {
return [...sourceValue, ...targetValue];
}
case 'intersection': {
return intersectionWith(sourceValue, targetValue, isEqual);
}
case 'replace': {
return targetValue;
}
case 'union': {
return unionWith(sourceValue, targetValue, isEqual);
}
default: {
throw new Error(
`Unknown merge array strategy: ${mergeArrays as string}`,
);
}
}
}
if (isObject(targetValue) && isObject(sourceValue)) {
return deepMerge(sourceValue, targetValue, mergeArrays);
}
});
}
export function openWindow(
url: string,
opt?: {
noopener?: boolean;
noreferrer?: boolean;
target?: '_blank' | '_self' | string;
},
) {
const { noopener = true, noreferrer = true, target = '__blank' } = opt || {};
const feature: string[] = [];
noopener && feature.push('noopener=yes');
noreferrer && feature.push('noreferrer=yes');
window.open(url, target, feature.join(','));
}
// dynamic use hook props
export function getDynamicProps<T extends Record<string, unknown>, U>(
props: T,
): Partial<U> {
const ret: Record<string, any> = {};
Object.keys(props).forEach((key) => {
ret[key] = unref((props as Record<string, any>)[key]);
});
return ret as Partial<U>;
}
export function getRawRoute(
route: RouteLocationNormalized,
): RouteLocationNormalized {
if (!route) return route;
const { matched, ...opt } = route;
return {
...opt,
matched: (matched
? matched.map((item) => ({
meta: item.meta,
name: item.name,
path: item.path,
}))
: undefined) as RouteRecordNormalized[],
};
}
// https://github.com/vant-ui/vant/issues/8302
interface EventShim {
new (...args: any[]): {
$props: {
onClick?: (...args: any[]) => void;
};
};
}
export type WithInstall<T> = {
install(app: App): void;
} & EventShim &
T;
export type CustomComponent = { displayName?: string } & Component;
export const withInstall = <T extends CustomComponent>(
component: T,
alias?: string,
) => {
(component as Record<string, unknown>).install = (app: App) => {
const compName = component.name || component.displayName;
if (!compName) return;
app.component(compName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as WithInstall<T>;
};