本文适用于希望将RuoYi-Vue3项目转化为TypeScript版本,或希望将‘字典’功能模块从RuoYi-Vue3中独立出来,并在其他TypeScript项目中使用的同学。
文章感谢 Ruoyi团队 原代码地址项目 RuoYi-Cloud-Vue3
文章环境说明
| pagekage | version | 备注 |
|---|---|---|
| vue | ^3.4.21 | 这个都懂得吧 |
| typescript | ^5.5.4 | 同上 |
| pinia | ^2.2.2 | 状态管理库 |
| pinia-plugin-persistedstate | ^3.2.3 | 状态管理库持久化/不需要可以不安装 |
文章不讲解 pinia 和 pinia-plugin-persistedstate 使用方法,可以点击查看官方文档
先写一个判空函数
如果你已经定义了自己的判空函数,那么可以跳过此段落。如果你还没有定义判空函数,可以将以下代码保存为一个新文件,并放置在您希望的位置,比如我将它放在了
'/src/utils/assert'路径下。在接下来的内容中,我们将主要使用isEmpty函数,您也可以根据自己的需要将isEmpty替换为您自己的判空函数。
// '@/utils/assert'
// 采取类型
const takeType = (data: unknown) => Object.prototype.toString.call(data).slice(8, -1);
// 是否是假值
export const isFakeValue = <T = unknown>(data: T | undefined | null | ''): data is undefined | null | '' =>
['', null, undefined].includes(data as any);
// 是不是数组
export const isArr = <T = any>(data: unknown): data is T[] =>
typeof Array.isArray === 'undefined' ? takeType(data) === 'Array' : Array.isArray(data);
// 是不是对象
export const isObj = <T extends Record<string, any> = Record<string, any>>(data: unknown): data is T =>
takeType(data) === 'Object';
// 是不是空对象
export const isEmptyObj = (data: unknown): data is {} => isObj(data) && Object.keys(data).length === 0;
// 是不是空数组
export const isEmptyArr = (data: unknown): data is any[] => isArr(data) && data.length === 0;
// 是不是空,主要使用函数
export const isEmpty = (data: unknown): boolean => {
if (isFake(data)) return true;
const type = takeType(data);
switch (type) {
case 'String':
return (data as string).length === 0;
case 'Number':
return (data as number) === 0;
case 'Object':
return isEmptyObj(data);
case 'Array':
return isEmptyArr(data);
case 'Boolean':
return data as boolean;
case 'Map':
return (data as Map<any, any>).size === 0;
case 'Set':
return (data as Set<any>).size === 0;
default:
return false;
}
};
创建 dictionaryStore
在你所在 store 目录下创建 dictionaryStore.ts,不多说了看注释吧,
记得替加入你的请求函数!!!!
import { isEmpty } from '#utils/assert'; // 导入空函数 可以替换你的判空函数
import { acceptHMRUpdate, defineStore } from 'pinia';
// 以下类型根据你的项目需求扩展
// 字典的 key 和 value 类型定义
export interface IDictionaryValue {
value: string;
label: string;
tagType?: string;
tagClass?: string;
}
// 字典类型定义,用于存储字典的 key 和 value
interface IDictionaries {
key: string;
value: IDictionaryValue[];
}
// State 类型定义,用于在组件中使用 store 的状态和函数
export interface IDictionaryState {
dictionaries: IDictionaries[];
}
// 获取返回的类型
interface GetDictionaryResults {
dictLabel: string;
dictValue: string;
listClass?: string;
cssClass?: string;
}
export const useDictionaryStore = defineStore('dictionaryStore', {
// 初始化状态
state: (): IDictionaryState => ({
dictionaries: []
}),
// 定义 actions 方法
actions: {
// 获取字典
getDict(key: string) {
if (isEmpty(key)) return null;
try {
const results = this.dictionaries.find((item) => item.key === key);
if (!results) return null;
return results.value;
} catch {
return null;
}
},
// 设置字典
setDict(key: string, value: IDictionaryValue[]) {
if (isEmpty(key)) return;
this.dictionaries.push({ key, value });
},
// 移除字典
removeDict(key: string) {
if (isEmpty(key)) return false;
try {
const index = this.dictionaries.findIndex((item) => item.key === key);
if (index === -1) {
return false;
} else {
this.dictionaries.splice(index, 1);
return true;
}
} catch {
return false;
}
},
// 清空字典列表(重置)
clearDict() {
this.dictionaries = [];
},
// 获取字典数据(异步)
async fetchDict(key: string): Promise<IDictionaryValue[] | null> {
try {
////// 此处替换为实际获取字典的方法 请求方法 //////
////// 此处替换为实际获取字典的方法 请求方法//////
////// 此处替换为实际获取字典的方法 请求方法 //////
////// 此处替换为实际获取字典的方法 请求方法 //////
const value = [] as GetDictionaryResults[];
const v: IDictionaryValue[] = value.map((p) => ({
label: p.dictLabel,
value: p.dictValue,
elTagType: p.listClass,
elTagClass: p.cssClass,
}));
this.setDict(key, v);
return v;
} catch {
return null;
}
}
},
// 持久化配置,启用 pinia-plugin-persistedstate 插件后可用 如果不需要就删掉
persist: true
});
// HMR 热更新
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useDictionaryStore, import.meta.hot));
}
创建 useDictionary
在你的习惯存放hook的位置创建 useDictionary,博主的存放的位置是
src/hook/下。不多说了看代码吧
import { ref, type ToRefs, toRefs } from 'vue';
import { type IDictionaryValue, useDictionaryStore } from '#store/modules/dictionaryStore';
import { isEmpty } from '#utils/assert';
interface IUseDictionaryOptions {
// 重置本次字典?(重置后需要重新拉取字典)
reset?: boolean;
}
// 字典结果类型,使用泛型约束字典key的类型,便于ts的类型推断
type DictionaryResults<T extends string> = {
[key in T]: IDictionaryValue[];
};
/**
* 获取数据字典 useDictionary
* @param dictKey 字典key
* @param options 配置
* @returns ToRefs<DictionaryResults<T>>
*/
export default function useDictionary<T extends string>(
dictKey: T[],
options?: IUseDictionaryOptions,
): ToRefs<DictionaryResults<T>> {
// 定义一个响应式变量res,初始值为空对象,并指定其类型为DictionaryResults<T>
const res = ref<DictionaryResults<T>>({} as DictionaryResults<T>);
// 调用useDictionaryStore钩子函数,获取字典存储Store对象
const store = useDictionaryStore();
// 重置本次字典?(重置后需要重新拉取字典)
if (options?.reset) {
// 遍历dictKey数组,对每个字典类型执行store.removeDict操作
dictKey.forEach((dictType: string) => store.removeDict(dictType));
}
return (() => {
// 遍历dictKey数组,对每个字典类型执行异步操作
dictKey.forEach(async (dictType: string) => {
// 将res对象中对应字典类型的值初始化为空数组
res.value[dictType as keyof DictionaryResults<T>] = [];
// 从store中获取对应字典类型的值
const dict = store.getDict(dictType);
// 判断获取到的字典是否为空
if (isEmpty(dict)) {
// 如果字典为空,则从服务器异步拉取字典数据
const data = await store.fetchDict(dictType);
// 如果拉取到的数据不为空,则将其赋值给res对象中对应字典类型的值
if (data) {
res.value[dictType as keyof DictionaryResults<T>] = data;
}
} else {
// 如果字典不为空,则直接将其赋值给res对象中对应字典类型的值
res.value[dictType as keyof DictionaryResults<T>] = dict;
}
});
// 使用 toRefs 将其返回,让它解构是还有响应式
return toRefs<DictionaryResults<T>>(res.value);
})();
}
看看效果
以上基本完成,看看效果 根据输入dictkey值返回响应对象

image.png

image.png

image.png