feat: 封装数据字典

This commit is contained in:
xlsea 2024-09-04 09:11:04 +08:00
parent 0130688265
commit 3d426fb8e1
20 changed files with 421 additions and 24 deletions

2
.env
View File

@ -12,7 +12,7 @@ VITE_ICON_PREFIX=icon
VITE_ICON_LOCAL_PREFIX=icon-local
# auth route mode: static dynamic
VITE_AUTH_ROUTE_MODE=dynamic
VITE_AUTH_ROUTE_MODE=static
# static auth route home
VITE_ROUTE_HOME=home

View File

@ -0,0 +1,64 @@
import { fetchGetDictDataByType } from '@/service/api';
import { useDictStore } from '@/store/modules/dict';
export function useDict() {
const dictStore = useDictStore();
async function getDictData(...args: Array<string>) {
const dictData: { [key: string]: Array<Api.System.DictData> } = {};
const promises = args.map(async dictType => {
dictData[dictType] = [];
const dicts = dictStore.getDict(dictType);
if (dicts) {
dictData[dictType] = dicts;
return;
}
const { data, error } = await fetchGetDictDataByType(dictType);
if (error) return;
dictData[dictType] = data;
dictStore.setDict(dictType, data);
});
await Promise.all(promises);
return dictData;
}
async function getDictRecord(...args: Array<string>) {
const dictRecord: { [key: string]: { [key: string]: string } } = {};
const dictData = await getDictData(...args);
Object.keys(dictData).forEach(dictType => {
const data: { [key: string]: string } = {};
Object.keys(dictData);
dictData[dictType].forEach(dict => {
data[dict.dictValue!] = dict.dictLabel!;
});
dictRecord[dictType] = data;
});
return dictRecord;
}
async function getDictOptions(...args: Array<string>) {
const dictOptions: { [key: string]: Array<CommonType.Option> } = {};
const dictData = await getDictData(...args);
Object.keys(dictData).forEach(dictType => {
dictOptions[dictType] = dictData[dictType].map(dict => ({ label: dict.dictLabel!, value: dict.dictValue! }));
});
return dictOptions;
}
async function transformDictByCode(type: string, code: string) {
const dictData = await getDictData(type);
return transformDictByOptions(code, dictData[type]);
}
function transformDictByOptions(code: string, options: Array<Api.System.DictData>) {
return options.find(item => item.dictValue === code)?.dictLabel;
}
return {
getDictData,
getDictRecord,
getDictOptions,
transformDictByCode,
transformDictByOptions
};
}

View File

@ -157,7 +157,9 @@ const local: App.I18n.Schema = {
'iframe-page': 'Iframe',
home: 'Home',
system: 'System Management',
system_menu: 'Menu Management'
system_menu: 'Menu Management',
tool: 'System Tools',
tool_gen: 'Code Generation'
},
page: {
login: {

View File

@ -157,7 +157,9 @@ const local: App.I18n.Schema = {
'iframe-page': '外链页面',
home: '首页',
system: '系统管理',
system_menu: '菜单管理'
system_menu: '菜单管理',
tool: '系统工具',
tool_gen: '代码生成'
},
page: {
login: {

View File

@ -22,4 +22,5 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
login: () => import("@/views/_builtin/login/index.vue"),
home: () => import("@/views/home/index.vue"),
system_menu: () => import("@/views/system/menu/index.vue"),
tool_gen: () => import("@/views/tool/gen/index.vue"),
};

View File

@ -98,5 +98,29 @@ export const generatedRoutes: GeneratedRoute[] = [
}
}
]
},
{
name: 'tool',
path: '/tool',
component: 'layout.base',
meta: {
title: 'tool',
i18nKey: 'route.tool',
localIcon: 'menu-tool',
order: 4
},
children: [
{
name: 'tool_gen',
path: '/tool/gen',
component: 'view.tool_gen',
meta: {
title: 'tool_gen',
i18nKey: 'route.tool_gen',
localIcon: 'menu-code',
order: 2
}
}
]
}
];

View File

@ -170,7 +170,9 @@ const routeMap: RouteMap = {
"iframe-page": "/iframe-page/:url",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"system": "/system",
"system_menu": "/system/menu"
"system_menu": "/system/menu",
"tool": "/tool",
"tool_gen": "/tool/gen"
};
/**

View File

@ -1,3 +1,4 @@
export * from './auth';
export * from './route';
export * from './system';
export * from './tool';

View File

@ -0,0 +1,9 @@
import { request } from '@/service/request';
/** 根据字典类型查询字典数据信息 */
export function fetchGetDictDataByType(dictType: string) {
return request<Array<Api.System.DictData>>({
url: `/system/dict/data/type/${dictType}`,
method: 'get'
});
}

View File

@ -1 +1,2 @@
export * from './menu';
export * from './dict';

View File

@ -0,0 +1,42 @@
import { request } from '@/service/request';
/** 查询代码生成列表 */
export function fetchGetGenTableList(params?: Api.Tool.GenTableSearchParams) {
return request<Api.Tool.GenTableList>({
url: '/tool/gen/list',
method: 'get',
params
});
}
/**
*
*
* @param tables
* @param dataName
*/
export function fetchImportGenTable(tables: Array<string>, dataName: string) {
return request<boolean>({
url: '/tool/gen/importTable',
method: 'post',
data: { tables, dataName }
});
}
/** 修改代码生成 */
export function fetchUpdateGenTable(data: Api.System.MenuOperateParams) {
return request<boolean>({
url: '/tool/gen',
method: 'put',
data
});
}
/** 批量删除代码生成 */
export function fetchBatchDeleteGenTable(tableIds: Array<CommonType.IdType>) {
const ids = tableIds.join(',');
return request<boolean>({
url: `/system/menu/${ids}`,
method: 'delete'
});
}

View File

@ -0,0 +1 @@
export * from './gen';

View File

@ -0,0 +1,35 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useDictStore = defineStore('dict', () => {
const dictData = ref<{ [key: string]: Array<Api.System.DictData> }>({});
const getDict = (key: string) => {
return dictData.value[key];
};
const setDict = (key: string, dict: Array<Api.System.DictData>) => {
dictData.value[key] = dict;
};
const removeDict = (key: string) => {
if (key in dictData.value) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete dictData.value[key];
}
};
const cleanDict = () => {
dictData.value = {};
};
return {
dictData,
getDict,
setDict,
removeDict,
cleanDict
};
});
export default useDictStore;

View File

@ -14,20 +14,28 @@ declare namespace Api {
/** role */
type Role = Common.CommonRecord<{
roleId: CommonType.IdType;
roleName: string;
roleKey: string;
roleSort: number;
dataScope: string;
menuCheckStrictly: boolean;
deptCheckStrictly: boolean;
status: string;
delFlag: string;
remark?: any;
flag: boolean;
menuIds?: Array<CommonType.IdType>;
deptIds?: Array<CommonType.IdType>;
admin: boolean;
/** 数据范围1全部数据权限 2自定数据权限 3本部门数据权限 4本部门及以下数据权限 */
dataScope?: string;
/** 部门树选择项是否关联显示 */
deptCheckStrictly?: boolean;
/** 用户是否存在此角色标识 默认不存在 */
flag?: boolean;
/** 菜单树选择项是否关联显示 */
menuCheckStrictly?: boolean;
/** 备注 */
remark?: string;
/** 角色ID */
roleId?: number;
/** 角色权限字符串 */
roleKey?: string;
/** 角色名称 */
roleName?: string;
/** 显示顺序 */
roleSort?: number;
/** 角色状态0正常 1停用 */
status?: string;
/** 是否管理员 */
superAdmin?: boolean;
}>;
/** role search params */
@ -176,11 +184,11 @@ declare namespace Api {
/** 父菜单名称 */
parentName?: string;
/** 子菜单 */
children?: Menu[];
children?: MenuList;
}>;
/** menu list */
type MenuList = Menu[];
type MenuList = Array<Menu>;
/** menu search params */
type MenuSearchParams = CommonType.RecordNullable<Pick<Menu, 'menuName' | 'status' | 'menuType' | 'parentId'>>;
@ -204,5 +212,27 @@ declare namespace Api {
| 'icon'
| 'remark'
>;
/** 字典数据 */
export type DictData = Common.CommonRecord<{
/** 样式属性(其他样式扩展) */
cssClass?: string;
/** 字典编码 */
dictCode?: number;
/** 字典标签 */
dictLabel?: string;
/** 字典排序 */
dictSort?: number;
/** 字典类型 */
dictType?: string;
/** 字典键值 */
dictValue?: string;
/** 是否默认Y是 N否 */
isDefault?: string;
/** 表格回显样式 */
listClass?: string;
/** 备注 */
remark?: string;
}>;
}
}

145
src/typings/api/tool.api.d.ts vendored Normal file
View File

@ -0,0 +1,145 @@
/**
* Namespace Api
*
* All backend api type
*/
declare namespace Api {
/**
* namespace Tool
*
* backend api module: "tool"
*/
namespace Tool {
/** 代码生成业务表 */
export type GenTable = {
/** 生成业务名 */
businessName: string;
/** 实体类名称(首字母大写) */
className: string;
/** 表列信息 */
columns?: GenTableColumn[];
/** 是否单表(增删改查) */
crud?: boolean;
/** 数据源名称 */
dataName: string;
/** 生成作者 */
functionAuthor: string;
/** 生成功能名 */
functionName: string;
/** 生成路径(不填默认项目路径) */
genPath?: string;
/** 生成代码方式0zip压缩包 1自定义路径 */
genType?: string;
/** 菜单 id 列表 */
menuIds?: CommonType.IdType[];
/** 生成模块名 */
moduleName: string;
/** 其它生成选项 */
options?: string;
/** 生成包路径 */
packageName: string;
/** 上级菜单ID字段 */
parentMenuId?: string;
/** 上级菜单名称字段 */
parentMenuName?: string;
/** 主键信息 */
pkColumn?: GenTableColumn;
/** 备注 */
remark?: string;
/** 本表关联父表的外键名 */
subTableFkName?: string;
/** 关联父表的表名 */
subTableName?: string;
/** 表描述 */
tableComment: string;
/** 编号 */
tableId?: number;
/** 表名称 */
tableName: string;
/** 使用的模板crud单表操作 tree树表操作 sub主子表操作 */
tplCategory?: string;
/** 是否tree树表操作 */
tree?: boolean;
/** 树编码字段 */
treeCode?: string;
/** 树名称字段 */
treeName?: string;
/** 树父编码字段 */
treeParentCode?: string;
};
/** 代码生成业务字段 */
export type GenTableColumn = {
/** 列描述 */
columnComment?: string;
/** 编号 */
columnId?: number;
/** 列名称 */
columnName?: string;
/** 列类型 */
columnType?: string;
/** 字典类型 */
dictType?: string;
/** 是否编辑字段1是 */
edit?: boolean;
/** 显示类型input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件 */
htmlType?: string;
/** 是否自增1是 */
increment?: boolean;
/** 是否为插入字段1是 */
insert?: boolean;
/** 是否编辑字段1是 */
isEdit?: string;
/** 是否自增1是 */
isIncrement?: string;
/** 是否为插入字段1是 */
isInsert?: string;
/** 是否列表字段1是 */
isList?: string;
/** 是否主键1是 */
isPk?: string;
/** 是否查询字段1是 */
isQuery?: string;
/** 是否必填1是 */
isRequired?: string;
/** JAVA字段名 */
javaField: string;
/** JAVA类型 */
javaType?: string;
/** 是否列表字段1是 */
list?: boolean;
/** 是否主键1是 */
pk?: boolean;
/** 是否查询字段1是 */
query?: boolean;
/** 查询方式EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围 */
queryType?: string;
/** 是否必填1是 */
required?: boolean;
/** 排序 */
sort?: number;
/** 是否基类字段 */
superColumn?: boolean;
/** 归属表编号 */
tableId?: number;
/** 可用字段 */
usableColumn?: boolean;
};
/** gen table search params */
type GenTableSearchParams = CommonType.RecordNullable<
Pick<GenTable, 'dataName' | 'tableName' | 'tableComment'> &
CommonType.RecordNullable<
Common.CommonSearchParams & {
params: {
beginTime: string;
endTime: string;
};
}
>
>;
/** gen table list */
type GenTableList = Common.PaginatingQueryRecord<GenTable>;
}
}

View File

@ -16,6 +16,9 @@ declare namespace CommonType {
*/
type Option<K = string> = { value: K; label: string };
/** The record type */
type Record<K extends string | number = string> = { [key in K]: string };
type YesOrNo = 'Y' | 'N';
/** add null to all properties */

View File

@ -25,6 +25,8 @@ declare module "@elegant-router/types" {
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
"system": "/system";
"system_menu": "/system/menu";
"tool": "/tool";
"tool_gen": "/tool/gen";
};
/**
@ -63,6 +65,7 @@ declare module "@elegant-router/types" {
| "iframe-page"
| "login"
| "system"
| "tool"
>;
/**
@ -86,6 +89,7 @@ declare module "@elegant-router/types" {
| "login"
| "home"
| "system_menu"
| "tool_gen"
>;
/**

View File

@ -11,6 +11,7 @@ import ButtonIcon from '@/components/custom/button-icon.vue';
import { $t } from '@/locales';
import { handleMenuTree } from '@/utils/ruoyi';
import StatusTag from '@/components/common/status-tag.vue';
import { useDict } from '@/hooks/business/dict';
import MenuOperateDrawer from './modules/menu-operate-drawer.vue';
const appStore = useAppStore();
@ -258,6 +259,18 @@ const btnColumns: DataTableColumns<Api.System.Menu> = [
}
}
];
const enableStatusRecord = ref<CommonType.Record>({});
const showHideRecord = ref<CommonType.Record>({});
async function initDictData() {
const { getDictRecord } = useDict();
const { sys_show_hide, sys_normal_disable } = await getDictRecord('sys_show_hide', 'sys_normal_disable');
enableStatusRecord.value = sys_normal_disable;
showHideRecord.value = sys_show_hide;
}
initDictData();
</script>
<template>
@ -381,7 +394,7 @@ const btnColumns: DataTableColumns<Api.System.Menu> = [
</NDescriptionsItem>
<NDescriptionsItem label="显示状态">
<NTag v-if="currentMenu.visible" size="small" :type="tagMap[currentMenu.visible]">
{{ currentMenu.visible === '0' ? '显示' : '隐藏' }}
{{ showHideRecord[currentMenu.visible] }}
</NTag>
</NDescriptionsItem>
<NDescriptionsItem v-if="currentMenu.menuType === 'C'" label="是否缓存">

View File

@ -5,10 +5,11 @@ import { NTooltip } from 'naive-ui';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
import { fetchCreateMenu, fetchUpdateMenu } from '@/service/api';
import { enableStatusOptions, menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
import { menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
import SvgIcon from '@/components/custom/svg-icon.vue';
import { getLocalMenuIcons } from '@/utils/icon';
import { humpToLine, isNotNull } from '@/utils/common';
import { useDict } from '@/hooks/business/dict';
defineOptions({
name: 'MenuOperateDrawer'
@ -216,6 +217,7 @@ watch(visible, () => {
if (visible.value) {
handleInitModel();
restoreValidation();
initDictData();
}
});
@ -249,6 +251,16 @@ const FormTipComponent = defineComponent({
);
}
});
const enableStatusOptions = ref<Array<CommonType.Option>>([]);
const showHideOptions = ref<Array<CommonType.Option>>([]);
async function initDictData() {
const { getDictOptions } = useDict();
const { sys_show_hide, sys_normal_disable } = await getDictOptions('sys_show_hide', 'sys_normal_disable');
enableStatusOptions.value = sys_normal_disable;
showHideOptions.value = sys_show_hide;
}
</script>
<template>
@ -398,8 +410,7 @@ const FormTipComponent = defineComponent({
</template>
<NRadioGroup v-model:value="model.visible">
<NSpace>
<NRadio value="0" label="显示" />
<NRadio value="1" label="隐藏" />
<NRadio v-for="item in showHideOptions" :key="item.value" :value="item.value" :label="item.label" />
</NSpace>
</NRadioGroup>
</NFormItemGi>

View File

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<style scoped></style>