diff --git a/.env b/.env index 1c0c09b4..324f0f7b 100644 --- a/.env +++ b/.env @@ -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 diff --git a/src/hooks/business/dict.ts b/src/hooks/business/dict.ts new file mode 100644 index 00000000..369e5efa --- /dev/null +++ b/src/hooks/business/dict.ts @@ -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) { + const dictData: { [key: string]: Array } = {}; + 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) { + 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) { + const dictOptions: { [key: string]: Array } = {}; + 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) { + return options.find(item => item.dictValue === code)?.dictLabel; + } + + return { + getDictData, + getDictRecord, + getDictOptions, + transformDictByCode, + transformDictByOptions + }; +} diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts index 42fae758..c6cddf50 100644 --- a/src/locales/langs/en-us.ts +++ b/src/locales/langs/en-us.ts @@ -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: { diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts index 4b0d4e2d..34f79652 100644 --- a/src/locales/langs/zh-cn.ts +++ b/src/locales/langs/zh-cn.ts @@ -157,7 +157,9 @@ const local: App.I18n.Schema = { 'iframe-page': '外链页面', home: '首页', system: '系统管理', - system_menu: '菜单管理' + system_menu: '菜单管理', + tool: '系统工具', + tool_gen: '代码生成' }, page: { login: { diff --git a/src/router/elegant/imports.ts b/src/router/elegant/imports.ts index 84bbf5a5..11674f07 100644 --- a/src/router/elegant/imports.ts +++ b/src/router/elegant/imports.ts @@ -22,4 +22,5 @@ export const views: Record Promise 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"), }; diff --git a/src/router/elegant/routes.ts b/src/router/elegant/routes.ts index 225bfef6..4800fdd4 100644 --- a/src/router/elegant/routes.ts +++ b/src/router/elegant/routes.ts @@ -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 + } + } + ] } ]; diff --git a/src/router/elegant/transform.ts b/src/router/elegant/transform.ts index a4c55ae5..e080f322 100644 --- a/src/router/elegant/transform.ts +++ b/src/router/elegant/transform.ts @@ -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" }; /** diff --git a/src/service/api/index.ts b/src/service/api/index.ts index f89c5603..3a7184f5 100644 --- a/src/service/api/index.ts +++ b/src/service/api/index.ts @@ -1,3 +1,4 @@ export * from './auth'; export * from './route'; export * from './system'; +export * from './tool'; diff --git a/src/service/api/system/dict.ts b/src/service/api/system/dict.ts new file mode 100644 index 00000000..dd67b7eb --- /dev/null +++ b/src/service/api/system/dict.ts @@ -0,0 +1,9 @@ +import { request } from '@/service/request'; + +/** 根据字典类型查询字典数据信息 */ +export function fetchGetDictDataByType(dictType: string) { + return request>({ + url: `/system/dict/data/type/${dictType}`, + method: 'get' + }); +} diff --git a/src/service/api/system/index.ts b/src/service/api/system/index.ts index 8267df70..e4e64d22 100644 --- a/src/service/api/system/index.ts +++ b/src/service/api/system/index.ts @@ -1 +1,2 @@ export * from './menu'; +export * from './dict'; diff --git a/src/service/api/tool/gen.ts b/src/service/api/tool/gen.ts new file mode 100644 index 00000000..f64bbff6 --- /dev/null +++ b/src/service/api/tool/gen.ts @@ -0,0 +1,42 @@ +import { request } from '@/service/request'; + +/** 查询代码生成列表 */ +export function fetchGetGenTableList(params?: Api.Tool.GenTableSearchParams) { + return request({ + url: '/tool/gen/list', + method: 'get', + params + }); +} + +/** + * 导入表结构 + * + * @param tables 表名称 + * @param dataName 数据源名称 + */ +export function fetchImportGenTable(tables: Array, dataName: string) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + data: { tables, dataName } + }); +} + +/** 修改代码生成 */ +export function fetchUpdateGenTable(data: Api.System.MenuOperateParams) { + return request({ + url: '/tool/gen', + method: 'put', + data + }); +} + +/** 批量删除代码生成 */ +export function fetchBatchDeleteGenTable(tableIds: Array) { + const ids = tableIds.join(','); + return request({ + url: `/system/menu/${ids}`, + method: 'delete' + }); +} diff --git a/src/service/api/tool/index.ts b/src/service/api/tool/index.ts new file mode 100644 index 00000000..8dfed38c --- /dev/null +++ b/src/service/api/tool/index.ts @@ -0,0 +1 @@ +export * from './gen'; diff --git a/src/store/modules/dict/index.ts b/src/store/modules/dict/index.ts new file mode 100644 index 00000000..cbd273a1 --- /dev/null +++ b/src/store/modules/dict/index.ts @@ -0,0 +1,35 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; + +export const useDictStore = defineStore('dict', () => { + const dictData = ref<{ [key: string]: Array }>({}); + + const getDict = (key: string) => { + return dictData.value[key]; + }; + + const setDict = (key: string, dict: Array) => { + 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; diff --git a/src/typings/api/system.api.d.ts b/src/typings/api/system.api.d.ts index 4a999777..d8f49ae5 100644 --- a/src/typings/api/system.api.d.ts +++ b/src/typings/api/system.api.d.ts @@ -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; - deptIds?: Array; - 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 search params */ type MenuSearchParams = CommonType.RecordNullable>; @@ -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; + }>; } } diff --git a/src/typings/api/tool.api.d.ts b/src/typings/api/tool.api.d.ts new file mode 100644 index 00000000..4fe137b2 --- /dev/null +++ b/src/typings/api/tool.api.d.ts @@ -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 & + CommonType.RecordNullable< + Common.CommonSearchParams & { + params: { + beginTime: string; + endTime: string; + }; + } + > + >; + + /** gen table list */ + type GenTableList = Common.PaginatingQueryRecord; + } +} diff --git a/src/typings/common.d.ts b/src/typings/common.d.ts index 9e84e1c6..bb95d3b4 100644 --- a/src/typings/common.d.ts +++ b/src/typings/common.d.ts @@ -16,6 +16,9 @@ declare namespace CommonType { */ type Option = { value: K; label: string }; + /** The record type */ + type Record = { [key in K]: string }; + type YesOrNo = 'Y' | 'N'; /** add null to all properties */ diff --git a/src/typings/elegant-router.d.ts b/src/typings/elegant-router.d.ts index 49dbf198..15c63960 100644 --- a/src/typings/elegant-router.d.ts +++ b/src/typings/elegant-router.d.ts @@ -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" >; /** diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue index 304eb71d..d635b76a 100644 --- a/src/views/system/menu/index.vue +++ b/src/views/system/menu/index.vue @@ -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 = [ } } ]; + +const enableStatusRecord = ref({}); +const showHideRecord = ref({}); + +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();