feat: 封装数据字典
This commit is contained in:
parent
0130688265
commit
3d426fb8e1
2
.env
2
.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
|
||||
|
64
src/hooks/business/dict.ts
Normal file
64
src/hooks/business/dict.ts
Normal 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
|
||||
};
|
||||
}
|
@ -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: {
|
||||
|
@ -157,7 +157,9 @@ const local: App.I18n.Schema = {
|
||||
'iframe-page': '外链页面',
|
||||
home: '首页',
|
||||
system: '系统管理',
|
||||
system_menu: '菜单管理'
|
||||
system_menu: '菜单管理',
|
||||
tool: '系统工具',
|
||||
tool_gen: '代码生成'
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
|
@ -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"),
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './auth';
|
||||
export * from './route';
|
||||
export * from './system';
|
||||
export * from './tool';
|
||||
|
9
src/service/api/system/dict.ts
Normal file
9
src/service/api/system/dict.ts
Normal 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'
|
||||
});
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './menu';
|
||||
export * from './dict';
|
||||
|
42
src/service/api/tool/gen.ts
Normal file
42
src/service/api/tool/gen.ts
Normal 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'
|
||||
});
|
||||
}
|
1
src/service/api/tool/index.ts
Normal file
1
src/service/api/tool/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './gen';
|
35
src/store/modules/dict/index.ts
Normal file
35
src/store/modules/dict/index.ts
Normal 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;
|
62
src/typings/api/system.api.d.ts
vendored
62
src/typings/api/system.api.d.ts
vendored
@ -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
145
src/typings/api/tool.api.d.ts
vendored
Normal 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>;
|
||||
}
|
||||
}
|
3
src/typings/common.d.ts
vendored
3
src/typings/common.d.ts
vendored
@ -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 */
|
||||
|
4
src/typings/elegant-router.d.ts
vendored
4
src/typings/elegant-router.d.ts
vendored
@ -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"
|
||||
>;
|
||||
|
||||
/**
|
||||
|
@ -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="是否缓存">
|
||||
|
@ -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>
|
||||
|
7
src/views/tool/gen/index.vue
Normal file
7
src/views/tool/gen/index.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
Loading…
Reference in New Issue
Block a user