ソースを参照

feat(menu): 新增菜单项并优化相关功能

- 新增 manage_config、manage_log 和 manage_schedule 路由
- 更新菜单类型定义,增加按钮类型
- 优化登录逻辑,添加验证码功能
- 调整用户信息结构,增加用户名和手机号字段
- 重构菜单数据转换函数,支持多级菜单
- 更新国际化文案,支持新菜单项翻译
zhangtao 1 週間 前
コミット
17253d2892
43 ファイル変更3010 行追加314 行削除
  1. 1 1
      .env.test
  2. 4 4
      packages/hooks/src/use-table.ts
  3. 1 1
      src/components/custom/custom-icon-select.vue
  4. 100 43
      src/components/zt/Form/basic-form.vue
  5. 3 3
      src/components/zt/Form/helper.ts
  6. 6 9
      src/components/zt/Form/hooks/useForm.ts
  7. 1 0
      src/components/zt/Form/hooks/useFormValues.ts
  8. 19 3
      src/components/zt/Form/types/form.ts
  9. 7 0
      src/components/zt/Modal/basic-modal.vue
  10. 3 2
      src/constants/business.ts
  11. 6 1
      src/hooks/common/form.ts
  12. 7 6
      src/hooks/common/table.ts
  13. 1 1
      src/layouts/modules/global-header/components/user-avatar.vue
  14. 6 2
      src/locales/langs/en-us.ts
  15. 7 3
      src/locales/langs/zh-cn.ts
  16. 3 0
      src/router/elegant/imports.ts
  17. 27 0
      src/router/elegant/routes.ts
  18. 3 0
      src/router/elegant/transform.ts
  19. 25 9
      src/service/api/auth.ts
  20. 1 1
      src/service/api/route.ts
  21. 40 2
      src/service/api/system-manage.ts
  22. 4 3
      src/service/request/index.ts
  23. 1 1
      src/service/request/shared.ts
  24. 23 11
      src/store/modules/auth/index.ts
  25. 6 5
      src/store/modules/route/index.ts
  26. 2 2
      src/store/modules/theme/index.ts
  27. 4 1
      src/typings/api.d.ts
  28. 1 1
      src/typings/api/auth.d.ts
  29. 14 1
      src/typings/api/system-manage.d.ts
  30. 3 0
      src/typings/app.d.ts
  31. 1 0
      src/typings/components.d.ts
  32. 6 0
      src/typings/elegant-router.d.ts
  33. 1 0
      src/typings/global.d.ts
  34. 97 0
      src/utils/zt/index.ts
  35. 28 6
      src/views/_builtin/login/modules/pwd-login.vue
  36. 1 1
      src/views/home/modules/header-banner.vue
  37. 7 0
      src/views/manage/config/index.vue
  38. 7 0
      src/views/manage/log/index.vue
  39. 281 190
      src/views/manage/menu/index.vue
  40. 2133 0
      src/views/manage/menu/modules/icons.ts
  41. 1 1
      src/views/manage/menu/modules/menu-operate-modal.vue
  42. 111 0
      src/views/manage/menu/modules/shared.ts
  43. 7 0
      src/views/manage/schedule/index.vue

+ 1 - 1
.env.test

@@ -1,5 +1,5 @@
 # backend service base url, test environment
-VITE_SERVICE_BASE_URL=http://192.168.1.206:8081
+VITE_SERVICE_BASE_URL=http://192.168.1.206:8114
 # VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
 
 

+ 4 - 4
packages/hooks/src/use-table.ts

@@ -4,9 +4,9 @@ import useBoolean from './use-boolean';
 import useLoading from './use-loading';
 
 export interface PaginationData<T> {
-  data: T[];
-  pageNum: number;
-  pageSize: number;
+  records: T[];
+  pages: number;
+  size: number;
   total: number;
 }
 
@@ -125,7 +125,7 @@ function getTableData<ApiData, Pagination extends boolean>(
   pagination?: Pagination
 ) {
   if (pagination) {
-    return (data as PaginationData<ApiData>).data;
+    return (data as PaginationData<ApiData>).records;
   }
 
   return data as ApiData[];

+ 1 - 1
src/components/custom/custom-icon-select.vue

@@ -54,7 +54,7 @@ function handleChange(iconItem: string) {
     <template #header>
       <NInput v-model:value="searchValue" placeholder="搜索图标"></NInput>
     </template>
-    <div v-if="iconsList.length > 0" class="grid grid-cols-9 h-auto overflow-auto">
+    <div v-if="iconsList.length > 0" class="grid grid-cols-9 h-300px overflow-auto">
       <span v-for="iconItem in iconsList" :key="iconItem" @click="handleChange(iconItem)">
         <SvgIcon
           :icon="iconItem"

+ 100 - 43
src/components/zt/Form/basic-form.vue

@@ -5,6 +5,7 @@ import type { ButtonProps, FormItemRule } from 'naive-ui';
 import { DownOutlined, QuestionCircleOutlined, UpOutlined } from '@vicons/antd';
 import type { GridProps } from 'naive-ui/lib/grid';
 import { deepMerge } from '@/utils/zt';
+import { isBoolean, isFunction } from '@/utils/zt/is';
 import { createPlaceholderMessage } from './helper';
 import { useFormEvents } from './hooks/useFormEvents';
 import { useFormValues } from './hooks/useFormValues';
@@ -59,7 +60,7 @@ export default defineComponent({
       const isTime = component == 'NDatePicker';
       const newObj: Record<string, any> = {
         clearable: true,
-        placeholder: createPlaceholderMessage(unref(component)),
+        placeholder: createPlaceholderMessage(unref(component), String(schema.label)),
         ...compProps
       };
       if (isTime && !schema.componentProps?.valueFormat) {
@@ -73,7 +74,6 @@ export default defineComponent({
       const baseProps = props as FormProps;
       const refProps = unref(propsRef) as Partial<FormProps>;
 
-      // 使用 Object.assign 代替扩展运算符,有时能避免类型推断问题
       const rawProps: FormProps = { ...baseProps, ...refProps };
 
       const rulesObj: { rules: Record<string, any> } = {
@@ -87,7 +87,6 @@ export default defineComponent({
         }
       });
 
-      // 再次使用 Object.assign 合并最终结果
       return { ...rawProps, ...rulesObj };
     });
 
@@ -111,7 +110,7 @@ export default defineComponent({
       const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
       for (const schema of schemas) {
         const { defaultValue } = schema;
-        if (defaultValue) {
+        if (defaultValue !== undefined && defaultValue !== null) {
           schema.defaultValue = defaultValue;
         }
       }
@@ -122,18 +121,43 @@ export default defineComponent({
         return {
           required: true,
           message: `${schema.label}不能为空`,
-          trigger: ['blur', 'input']
+          trigger: ['blur', 'input'],
+          validator: () => {
+            if (
+              formModel[schema.field] === undefined ||
+              formModel[schema.field] === null ||
+              formModel[schema.field] === ''
+            ) {
+              return new Error(`${schema.label}不能为空`);
+            }
+            if (Array.isArray(formModel[schema.field]) && formModel[schema.field].length === 0) {
+              return new Error(`${schema.label}不能为空`);
+            }
+            return true;
+          }
         };
       }
       return undefined;
     };
-
+    function getShow(schema: FormSchema) {
+      if (isBoolean(schema.show)) {
+        return schema.show;
+      }
+      if (isFunction(schema.ifShow)) {
+        return schema.ifShow({
+          schema,
+          model: formModel,
+          field: schema.field,
+          values: formModel[schema.field]
+        });
+      }
+      return true;
+    }
     const { handleFormValues, initDefault } = useFormValues({
       defaultFormModel,
       getSchema,
       formModel
     });
-
     const { handleSubmit, validate, resetFields, getFieldsValue, clearValidate, setFieldsValue } = useFormEvents({
       emit,
       getProps,
@@ -153,6 +177,20 @@ export default defineComponent({
       propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
     }
 
+    function render(schema: FormSchema) {
+      // 如果 schema 中定义了 render 函数,则使用自定义 render 函数
+      if (schema.render && isFunction(schema.render)) {
+        return schema.render({
+          schema,
+          model: formModel,
+          field: schema.field,
+          values: formModel[schema.field]
+        });
+      }
+
+      // 如果没有定义 render 函数,返回 null 或 undefined
+      return null;
+    }
     const formActionType: Partial<FormActionType> = {
       getFieldsValue,
       setFieldsValue,
@@ -199,7 +237,9 @@ export default defineComponent({
       unfoldToggle,
       componentMap,
       getRule,
-      getComponentValue
+      getComponentValue,
+      getShow,
+      render
     };
   }
 });
@@ -209,7 +249,13 @@ export default defineComponent({
   <NForm v-bind="getBindValue" ref="formElRef" :model="formModel">
     <NGrid v-bind="getGrid">
       <NGi v-for="schema in getSchema" v-bind="schema.giProps" :key="schema.field">
-        <NFormItem :label="schema.label" :path="schema.field" :required="schema.required" :rule="getRule(schema)">
+        <NFormItem
+          v-if="getShow(schema)"
+          :label="schema.label"
+          :path="schema.field"
+          :required="schema.required"
+          :rule="getRule(schema)"
+        >
           <!--标签名右侧温馨提示-->
           <template v-if="schema.labelMessage" #label>
             {{ schema.label }}
@@ -222,44 +268,55 @@ export default defineComponent({
               {{ schema.labelMessage }}
             </NTooltip>
           </template>
-
-          <!--判断插槽-->
-          <template v-if="schema.slot">
-            <slot :name="schema.slot" :model="formModel" :field="schema.field" :value="formModel[schema.field]"></slot>
+          <template v-if="render(schema)">
+            <component :is="render(schema)" />
           </template>
+          <template v-else>
+            <!--判断插槽-->
+            <template v-if="schema.slot">
+              <slot
+                :name="schema.slot"
+                :model="formModel"
+                :field="schema.field"
+                :value="formModel[schema.field]"
+              ></slot>
+            </template>
 
-          <!--NCheckbox-->
-          <template v-else-if="schema.component === 'NCheckboxGroup'">
-            <NCheckboxGroup v-model:value="formModel[schema.field]">
-              <NSpace>
-                <NCheckbox
-                  v-for="item in schema.componentProps!.options"
-                  :key="item.value"
-                  :value="item.value"
-                  :label="item.label"
-                />
-              </NSpace>
-            </NCheckboxGroup>
-          </template>
+            <!--NCheckbox-->
+            <template v-else-if="schema.component === 'NCheckboxGroup'">
+              <NCheckboxGroup v-model:value="formModel[schema.field]">
+                <NSpace>
+                  <NCheckbox
+                    v-for="item in schema.componentProps!.options"
+                    :key="item.value"
+                    :value="item.value"
+                    :label="item.label"
+                  />
+                </NSpace>
+              </NCheckboxGroup>
+            </template>
 
-          <!--NRadioGroup-->
-          <template v-else-if="schema.component === 'NRadioGroup'">
-            <NRadioGroup v-model:value="formModel[schema.field]">
-              <NSpace>
-                <NRadio v-for="item in schema.componentProps!.options" :key="item.value" :value="item.value">
-                  {{ item.label }}
-                </NRadio>
-              </NSpace>
-            </NRadioGroup>
+            <!--NRadioGroup-->
+            <template v-else-if="schema.component === 'NRadioGroup'">
+              <NRadioGroup v-model:value="formModel[schema.field]">
+                <NSpace>
+                  <NRadio v-for="item in schema.componentProps!.options" :key="item.value" :value="item.value">
+                    {{ item.label }}
+                  </NRadio>
+                </NSpace>
+              </NRadioGroup>
+            </template>
+            <!--动态渲染表单组件-->
+            <template v-else>
+              <component
+                v-bind="getComponentProps(schema)"
+                :is="componentMap[schema.component]"
+                v-model:[`${getComponentValue(schema)}`]="formModel[schema.field]"
+                :class="{ isFull: schema.isFull != false && getProps.isFull }"
+              />
+            </template>
           </template>
-          <!--动态渲染表单组件-->
-          <component
-            v-bind="getComponentProps(schema)"
-            :is="componentMap[schema.component]"
-            v-else
-            v-model:[`${getComponentValue(schema)}`]="formModel[schema.field]"
-            :class="{ isFull: schema.isFull != false && getProps.isFull }"
-          />
+
           <!--组件后面的内容-->
           <template v-if="schema.suffix">
             <slot

+ 3 - 3
src/components/zt/Form/helper.ts

@@ -3,14 +3,14 @@ import type { ComponentMap } from './types/form';
 /**
  * @description: 生成placeholder
  */
-export function createPlaceholderMessage(component: keyof ComponentMap) {
-  if (component === 'NInput') return '请输入';
+export function createPlaceholderMessage(component: keyof ComponentMap, label: string) {
+  if (component === 'NInput') return `请输入${label}`;
   if (
     ['NPicker', 'NSelect', 'NCheckbox', 'NRadio', 'NSwitch', 'NDatePicker', 'NTimePicker', 'NCheckboxGroup'].includes(
       component
     )
   )
-    return '请选择';
+    return `请输入${label}`;
   return '';
 }
 

+ 6 - 9
src/components/zt/Form/hooks/useForm.ts

@@ -7,8 +7,6 @@ type Props = Partial<FormProps>;
 export function useForm(props?: Props): UseFormReturnType {
   const formRef = ref<Nullable<FormActionType>>(null);
   const loadedRef = ref<Nullable<boolean>>(false);
-  const isProdMode = import.meta.env.PROD;
-
   async function getForm() {
     const form = unref(formRef);
     if (!form) {
@@ -21,14 +19,12 @@ export function useForm(props?: Props): UseFormReturnType {
   }
 
   function register(instance: FormActionType) {
-    if (isProdMode) {
-      onUnmounted(() => {
-        formRef.value = null;
-        loadedRef.value = null;
-      });
-    }
+    onUnmounted(() => {
+      formRef.value = null;
+      loadedRef.value = null;
+    });
 
-    if (unref(loadedRef) && isProdMode && instance === unref(formRef)) return;
+    if (unref(loadedRef) && instance === unref(formRef)) return;
 
     formRef.value = instance;
     loadedRef.value = true;
@@ -70,6 +66,7 @@ export function useForm(props?: Props): UseFormReturnType {
 
     setFieldsValue: async <T extends Recordable<any>>(values: T) => {
       const form = await getForm();
+
       form.setFieldsValue(values);
     },
 

+ 1 - 0
src/components/zt/Form/hooks/useFormValues.ts

@@ -38,6 +38,7 @@ export function useFormValues({ defaultFormModel, getSchema, formModel }: UseFor
       if (!isNullOrUnDef(defaultValue)) {
         obj[item.field] = defaultValue;
         formModel[item.field] = defaultValue;
+        // console.log(formModel[item.field], 'formModel[item.field]');
       }
     });
     defaultFormModel.value = obj;

+ 19 - 3
src/components/zt/Form/types/form.ts

@@ -1,4 +1,4 @@
-import type { CSSProperties } from 'vue';
+import type { CSSProperties, VNode } from 'vue';
 import {
   type AutoCompleteProps,
   type CalendarProps,
@@ -91,10 +91,10 @@ export type RegisterFn = (formInstance: FormActionType) => void;
 export type UseFormReturnType = [RegisterFn, FormActionType];
 
 interface NCheckboxGroupProps extends CheckboxGroupProps {
-  options: { label: string; value: string }[];
+  options: { label: string; value: string | number }[];
 }
 interface NRadioGroupProps extends RadioGroupProps {
-  options: { label: string; value: string }[];
+  options: { label: string; value: string | number }[];
 }
 
 export type FormSchema =
@@ -119,6 +119,12 @@ export type FormSchema =
   | FormSchemaWithType<'NCalendar', CalendarProps>
   | FormSchemaWithType<'NDynamicTags', DynamicTagsProps>;
 
+export interface RenderCallbackParams {
+  schema: FormSchema;
+  values: Recordable;
+  model: Recordable;
+  field: string;
+}
 interface FormSchemaWithType<T extends keyof ComponentMap, P> {
   /**
    * 字段名
@@ -158,6 +164,16 @@ interface FormSchemaWithType<T extends keyof ComponentMap, P> {
   isFull?: boolean;
   suffix?: string;
   slot?: string;
+  /**
+   * 是否显示,渲染函数
+   */
+  ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
+  /**
+   * 自定义渲染
+   * @param renderCallbackParams
+   * @returns
+   */
+  render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
 }
 
 export const componentMap = {

+ 7 - 0
src/components/zt/Modal/basic-modal.vue

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

+ 3 - 2
src/constants/business.ts

@@ -15,8 +15,9 @@ export const userGenderRecord: Record<Api.SystemManage.UserGender, App.I18n.I18n
 export const userGenderOptions = transformRecordToOption(userGenderRecord);
 
 export const menuTypeRecord: Record<Api.SystemManage.MenuType, App.I18n.I18nKey> = {
-  '1': 'page.manage.menu.type.directory',
-  '2': 'page.manage.menu.type.menu'
+  '0': 'page.manage.menu.type.directory',
+  '1': 'page.manage.menu.type.menu',
+  '2': 'page.manage.menu.type.button'
 };
 
 export const menuTypeOptions = transformRecordToOption(menuTypeRecord);

+ 6 - 1
src/hooks/common/form.ts

@@ -30,6 +30,10 @@ export function useFormRules() {
       pattern: REG_EMAIL,
       message: $t('form.email.invalid'),
       trigger: 'change'
+    },
+    imageCode: {
+      message: $t('form.code.invalid'),
+      trigger: 'change'
     }
   } satisfies Record<string, App.Global.FormRule>;
 
@@ -38,7 +42,8 @@ export function useFormRules() {
     phone: [createRequiredRule($t('form.phone.required')), patternRules.phone],
     pwd: [createRequiredRule($t('form.pwd.required')), patternRules.pwd],
     code: [createRequiredRule($t('form.code.required')), patternRules.code],
-    email: [createRequiredRule($t('form.email.required')), patternRules.email]
+    email: [createRequiredRule($t('form.email.required')), patternRules.email],
+    imageCode: [createRequiredRule('请输入验证码'), patternRules.imageCode]
   } satisfies Record<string, App.Global.FormRule[]>;
 
   /** the default required rule */

+ 7 - 6
src/hooks/common/table.ts

@@ -233,22 +233,23 @@ export function defaultTransform<ApiData>(
   response: FlatResponseData<any, Api.Common.PaginatingQueryRecord<ApiData>>
 ): PaginationData<ApiData> {
   const { data, error } = response;
+  console.log(response, 'asdasdsa');
 
   if (!error) {
     const { records, current, size, total } = data;
 
     return {
-      data: records,
-      pageNum: current,
-      pageSize: size,
+      records,
+      pages: current,
+      size,
       total
     };
   }
 
   return {
-    data: [],
-    pageNum: 1,
-    pageSize: 10,
+    records: [],
+    pages: 1,
+    size: 10,
     total: 0
   };
 }

+ 1 - 1
src/layouts/modules/global-header/components/user-avatar.vue

@@ -82,7 +82,7 @@ function handleDropdown(key: DropdownKey) {
     <div>
       <ButtonIcon>
         <SvgIcon icon="ph:user-circle" class="text-icon-large" />
-        <span class="text-16px font-medium">{{ authStore.userInfo.userName }}</span>
+        <span class="text-16px font-medium">{{ authStore.userInfo.username }}</span>
       </ButtonIcon>
     </div>
   </NDropdown>

+ 6 - 2
src/locales/langs/en-us.ts

@@ -277,7 +277,10 @@ const local: App.I18n.Schema = {
     plugin_gantt_vtable: 'VTableGantt',
     plugin_typeit: 'Typeit',
     plugin_tables: 'Tables',
-    plugin_tables_vtable: 'VTable'
+    plugin_tables_vtable: 'VTable',
+    manage_config: '',
+    manage_log: '',
+    manage_schedule: ''
   },
   page: {
     login: {
@@ -607,7 +610,8 @@ const local: App.I18n.Schema = {
         addChildMenu: 'Add Child Menu',
         type: {
           directory: 'Directory',
-          menu: 'Menu'
+          menu: 'Menu',
+          button: 'button'
         },
         iconType: {
           iconify: 'Iconify Icon',

+ 7 - 3
src/locales/langs/zh-cn.ts

@@ -274,7 +274,10 @@ const local: App.I18n.Schema = {
     plugin_gantt_vtable: 'VTableGantt',
     plugin_typeit: '打字机',
     plugin_tables: '表格',
-    plugin_tables_vtable: 'VTable'
+    plugin_tables_vtable: 'VTable',
+    manage_config: '',
+    manage_log: '',
+    manage_schedule: ''
   },
   page: {
     login: {
@@ -290,7 +293,7 @@ const local: App.I18n.Schema = {
         back: '返回',
         validateSuccess: '验证成功',
         loginSuccess: '登录成功',
-        welcomeBack: '欢迎回来,{userName} !'
+        welcomeBack: '欢迎回来,{username} !'
       },
       pwdLogin: {
         title: '密码登录',
@@ -604,7 +607,8 @@ const local: App.I18n.Schema = {
         addChildMenu: '新增子菜单',
         type: {
           directory: '目录',
-          menu: '菜单'
+          menu: '菜单',
+          button: '按钮'
         },
         iconType: {
           iconify: 'iconify图标',

+ 3 - 0
src/router/elegant/imports.ts

@@ -22,8 +22,11 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
   login: () => import("@/views/_builtin/login/index.vue"),
   about: () => import("@/views/about/index.vue"),
   home: () => import("@/views/home/index.vue"),
+  manage_config: () => import("@/views/manage/config/index.vue"),
+  manage_log: () => import("@/views/manage/log/index.vue"),
   manage_menu: () => import("@/views/manage/menu/index.vue"),
   manage_role: () => import("@/views/manage/role/index.vue"),
+  manage_schedule: () => import("@/views/manage/schedule/index.vue"),
   manage_user: () => import("@/views/manage/user/index.vue"),
   plugin_barcode: () => import("@/views/plugin/barcode/index.vue"),
   plugin_charts_antv: () => import("@/views/plugin/charts/antv/index.vue"),

+ 27 - 0
src/router/elegant/routes.ts

@@ -98,6 +98,24 @@ export const generatedRoutes: GeneratedRoute[] = [
       roles: ['R_ADMIN']
     },
     children: [
+      {
+        name: 'manage_config',
+        path: '/manage/config',
+        component: 'view.manage_config',
+        meta: {
+          title: 'manage_config',
+          i18nKey: 'route.manage_config'
+        }
+      },
+      {
+        name: 'manage_log',
+        path: '/manage/log',
+        component: 'view.manage_log',
+        meta: {
+          title: 'manage_log',
+          i18nKey: 'route.manage_log'
+        }
+      },
       {
         name: 'manage_menu',
         path: '/manage/menu',
@@ -123,6 +141,15 @@ export const generatedRoutes: GeneratedRoute[] = [
           roles: ['R_SUPER']
         }
       },
+      {
+        name: 'manage_schedule',
+        path: '/manage/schedule',
+        component: 'view.manage_schedule',
+        meta: {
+          title: 'manage_schedule',
+          i18nKey: 'route.manage_schedule'
+        }
+      },
       {
         name: 'manage_user',
         path: '/manage/user',

+ 3 - 0
src/router/elegant/transform.ts

@@ -186,8 +186,11 @@ const routeMap: RouteMap = {
   "iframe-page": "/iframe-page/:url",
   "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
   "manage": "/manage",
+  "manage_config": "/manage/config",
+  "manage_log": "/manage/log",
   "manage_menu": "/manage/menu",
   "manage_role": "/manage/role",
+  "manage_schedule": "/manage/schedule",
   "manage_user": "/manage/user",
   "plugin": "/plugin",
   "plugin_barcode": "/plugin/barcode",

+ 25 - 9
src/service/api/auth.ts

@@ -1,26 +1,42 @@
 import { request } from '../request';
 
+// /**
+//  * Login
+//  *
+//  * @param userName User name
+//  * @param password Password
+//  */
+// export function fetchLogin(userName: string, password: string) {
+//   return request<Api.Auth.LoginToken>({
+//     url: '/platformLogin',
+//     // url: '/auth/login',
+//     method: 'post',
+//     data: {
+//       userName,
+//       password
+//     }
+//   });
+// }
+
 /**
  * Login
  *
- * @param userName User name
- * @param password Password
+ * @param principal User name
+ * @param credentials Password
+ * @param imageCode 验证码
  */
-export function fetchLogin(userName: string, password: string) {
+export function fetchLogin(data: { principal: string; credentials: string; imageCode: string; sessionUUID: string }) {
   return request<Api.Auth.LoginToken>({
-    url: '/api/user/login',
+    url: '/platformLogin',
     // url: '/auth/login',
     method: 'post',
-    data: {
-      userName,
-      password
-    }
+    data
   });
 }
 
 /** Get user info */
 export function fetchGetUserInfo() {
-  return request<Api.Auth.UserInfo>({ url: '/api/user/getInfo' });
+  return request<Api.Auth.UserInfo>({ url: '/sys/user/info' });
   // return request<Api.Auth.UserInfo>({ url: '/auth/getUserInfo' });
 }
 

+ 1 - 1
src/service/api/route.ts

@@ -9,7 +9,7 @@ export function fetchGetConstantRoutes() {
 /** get user routes */
 export function fetchGetUserRoutes() {
   // return request<Api.Route.UserRoute>({ url: '/route/getUserRoutes' });
-  return request<Api.Route.UserRoute>({ url: '/api/user/getUserMenuResources' });
+  return request<Api.Route.UserRoute>({ url: 'sys/menu/nav' });
 }
 
 /**

+ 40 - 2
src/service/api/system-manage.ts

@@ -3,7 +3,7 @@ import { request } from '../request';
 /** get role list */
 export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) {
   return request<Api.SystemManage.RoleList>({
-    url: '/systemManage/getRoleList',
+    url: '/sys/role/page',
     method: 'get',
     params
   });
@@ -33,7 +33,8 @@ export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) {
 /** get menu list */
 export function fetchGetMenuList() {
   return request<Api.SystemManage.MenuList>({
-    url: '/systemManage/getMenuList/v2',
+    // url: '/systemManage/getMenuList/v2',
+    url: '/sys/menu/table',
     method: 'get'
   });
 }
@@ -53,3 +54,40 @@ export function fetchGetMenuTree() {
     method: 'get'
   });
 }
+
+/**
+ * 新增菜单
+ * @param data
+ * @returns
+ */
+export function fetchAddMenu(data: Api.SystemManage.AddMenu) {
+  return request({
+    url: '/sys/menu',
+    method: 'post',
+    data
+  });
+}
+/**
+ * 编辑菜单
+ * @param data
+ * @returns
+ */
+export function fetchEditMenu(data: Api.SystemManage.AddMenu) {
+  return request({
+    url: '/sys/menu',
+    method: 'put',
+    data
+  });
+}
+
+/**
+ * 删除菜单
+ * @param id
+ * @returns
+ */
+export function fetchDelMenu(id: number) {
+  return request({
+    url: `/sys/menu/${id}`,
+    method: 'delete'
+  });
+}

+ 4 - 3
src/service/request/index.ts

@@ -22,7 +22,9 @@ export const request = createFlatRequest(
       errMsgStack: [],
       refreshTokenPromise: null
     } as RequestInstanceState,
+    //  AxiosResponse<App.Service.Response<any>>
     transform(response: AxiosResponse<App.Service.Response<any>>) {
+      // return response.data.data;
       return response.data.data;
     },
     async onRequest(config) {
@@ -34,12 +36,12 @@ export const request = createFlatRequest(
     isBackendSuccess(response) {
       // when the backend response code is "0000"(default), it means the request is success
       // to change this logic by yourself, you can modify the `VITE_SERVICE_SUCCESS_CODE` in `.env` file
-      return String(response.data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE;
+      // return String(response.data.code) === import.meta.env.VITE_SERVICE_SUCCESS_CODE;
+      return String(response.status) === import.meta.env.VITE_SERVICE_SUCCESS_CODE;
     },
     async onBackendFail(response, instance) {
       const authStore = useAuthStore();
       const responseCode = String(response.data.code);
-
       function handleLogout() {
         authStore.resetStore();
       }
@@ -100,7 +102,6 @@ export const request = createFlatRequest(
     },
     onError(error) {
       // when the request is fail, you can show error message
-
       let message = error.message;
       let backendErrorCode = '';
 

+ 1 - 1
src/service/request/shared.ts

@@ -5,7 +5,7 @@ import type { RequestInstanceState } from './type';
 
 export function getAuthorization() {
   const token = localStg.get('token');
-  const Authorization = token ? `${token}` : null;
+  const Authorization = token ? `bearer${token}` : null;
 
   return Authorization;
 }

+ 23 - 11
src/store/modules/auth/index.ts

@@ -21,11 +21,13 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 
   const token = ref(getToken());
 
-  const userInfo: Api.Auth.UserInfo = reactive({
+  let userInfo: Api.Auth.UserInfo = reactive({
     userId: '',
-    userName: '',
+    username: '',
     roles: [],
-    buttons: []
+    buttons: [],
+    email: '',
+    mobile: ''
   });
 
   /** is super role in static route */
@@ -96,12 +98,22 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
    * @param password Password
    * @param [redirect=true] Whether to redirect after login. Default is `true`
    */
-  async function login(userName: string, password: string, redirect = true) {
+  async function login(
+    model: { userName: string; password: string; imageCode: string; sessionUUID: string },
+    redirect = true
+  ) {
+    const { userName, password, imageCode, sessionUUID } = model;
     startLoading();
-
-    const { data: loginToken, error } = await fetchLogin(userName, password);
-    console.log('login', loginToken);
-    console.log('count:', error);
+    const { error, response } = await fetchLogin({
+      principal: userName,
+      credentials: password,
+      imageCode,
+      sessionUUID
+    });
+    const loginToken: Api.Auth.LoginToken = {
+      token: response.data.access_token,
+      refreshToken: response.data.refreshToken
+    };
     if (!error) {
       const pass = await loginByToken(loginToken);
 
@@ -118,7 +130,7 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 
         window.$notification?.success({
           title: $t('page.login.common.loginSuccess'),
-          content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
+          content: $t('page.login.common.welcomeBack', { username: userInfo.username }),
           duration: 4500
         });
       }
@@ -148,10 +160,10 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
 
   async function getUserInfo() {
     const { data: info, error } = await fetchGetUserInfo();
-
     if (!error) {
       // update store
-      Object.assign(userInfo, info);
+      // Object.assign(userInfo, info);
+      userInfo = info;
 
       return true;
     }

+ 6 - 5
src/store/modules/route/index.ts

@@ -5,6 +5,7 @@ import { useBoolean } from '@sa/hooks';
 import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types';
 import { router } from '@/router';
 import { fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api';
+import { convertMenuData } from '@/utils/zt';
 import { SetupStoreId } from '@/enum';
 import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes';
 import { ROOT_ROUTE } from '@/router/routes/builtin';
@@ -213,15 +214,15 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
   async function initDynamicAuthRoute() {
     const { data, error } = await fetchGetUserRoutes();
     if (!error) {
-      const { routes, home } = data;
-
-      addAuthRoutes(routes);
+      const menu = convertMenuData({ menuList: data?.menuList });
+      addAuthRoutes(menu);
+      console.log(menu, '请求成功menu');
 
       handleConstantAndAuthRoutes();
 
-      setRouteHome(home);
+      setRouteHome('home');
 
-      handleUpdateRootRouteRedirect(home);
+      handleUpdateRootRouteRedirect('home');
 
       setIsInitAuthRoute(true);
     } else {

+ 2 - 2
src/store/modules/theme/index.ts

@@ -73,8 +73,8 @@ export const useThemeStore = defineStore(SetupStoreId.Theme, () => {
   const watermarkContent = computed(() => {
     const { watermark } = settings.value;
 
-    if (watermark.enableUserName && authStore.userInfo.userName) {
-      return authStore.userInfo.userName;
+    if (watermark.enableUserName && authStore.userInfo.username) {
+      return authStore.userInfo.username;
     }
 
     if (watermark.enableTime) {

+ 4 - 1
src/typings/api.d.ts

@@ -61,9 +61,11 @@ declare namespace Api {
 
     interface UserInfo {
       userId: string;
-      userName: string;
+      username: string;
       roles: string[];
       buttons: string[];
+      email: string;
+      mobile: string;
     }
   }
 
@@ -80,6 +82,7 @@ declare namespace Api {
     }
 
     interface UserRoute {
+      [x: string]: any;
       routes: MenuRoute[];
       home: import('@elegant-router/types').LastLevelRouteKey;
     }

+ 1 - 1
src/typings/api/auth.d.ts

@@ -12,7 +12,7 @@ declare namespace Api {
 
     interface UserInfo {
       userId: string;
-      userName: string;
+      username: string;
       roles: string[];
       buttons: string[];
     }

+ 14 - 1
src/typings/api/system-manage.d.ts

@@ -67,7 +67,8 @@ declare namespace Api {
      * - "1": directory
      * - "2": menu
      */
-    type MenuType = '1' | '2';
+    // type MenuType = '1' | '2';
+    type MenuType = '0' | '1' | '2';
 
     type MenuButton = {
       /**
@@ -135,5 +136,17 @@ declare namespace Api {
       pId: number;
       children?: MenuTree[];
     };
+    type AddMenu = {
+      menuId: string;
+      type: number;
+      name: string;
+      parentId: string;
+      url: string;
+      perms: string;
+      orderNum: number;
+      icon: string;
+      component: string;
+      hideInMenu: number;
+    };
   }
 }

+ 3 - 0
src/typings/app.d.ts

@@ -793,6 +793,7 @@ declare namespace App {
             type: {
               directory: string;
               menu: string;
+              button: string;
             };
             iconType: {
               iconify: string;
@@ -879,6 +880,8 @@ declare namespace App {
 
     /** The backend service response data */
     type Response<T = unknown> = {
+      access_token: string;
+      refreshToken: string;
       /** The backend service response code */
       code: string;
       /** The backend service response message */

+ 1 - 0
src/typings/components.d.ts

@@ -121,6 +121,7 @@ declare module 'vue' {
     NThing: typeof import('naive-ui')['NThing']
     NTooltip: typeof import('naive-ui')['NTooltip']
     NTree: typeof import('naive-ui')['NTree']
+    NTreeSelect: typeof import('naive-ui')['NTreeSelect']
     NWatermark: typeof import('naive-ui')['NWatermark']
     PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
     ProCard: typeof import('pro-naive-ui')['ProCard']

+ 6 - 0
src/typings/elegant-router.d.ts

@@ -40,8 +40,11 @@ declare module "@elegant-router/types" {
     "iframe-page": "/iframe-page/:url";
     "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
     "manage": "/manage";
+    "manage_config": "/manage/config";
+    "manage_log": "/manage/log";
     "manage_menu": "/manage/menu";
     "manage_role": "/manage/role";
+    "manage_schedule": "/manage/schedule";
     "manage_user": "/manage/user";
     "plugin": "/plugin";
     "plugin_barcode": "/plugin/barcode";
@@ -158,8 +161,11 @@ declare module "@elegant-router/types" {
     | "login"
     | "about"
     | "home"
+    | "manage_config"
+    | "manage_log"
     | "manage_menu"
     | "manage_role"
+    | "manage_schedule"
     | "manage_user"
     | "plugin_barcode"
     | "plugin_charts_antv"

+ 1 - 0
src/typings/global.d.ts

@@ -1,4 +1,5 @@
 import type { ComponentPublicInstance, FunctionalComponent, VNodeChild, PropType as VuePropType } from 'vue';
+import type { JSX } from 'vue/jsx-runtime';
 
 export {};
 

+ 97 - 0
src/utils/zt/index.ts

@@ -126,3 +126,100 @@ export function lighten(color: string, amount: number) {
 export function isUrl(url: string) {
   return /^(http|https):\/\//g.test(url);
 }
+/**
+ * 获取uuid
+ */
+export function getUUID() {
+  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
+    const r = Math.random() * 16;
+    const v = c === 'x' ? Math.floor(r) : Math.floor((r * 4) % 4) + 8;
+    return v.toString(16);
+  });
+}
+interface OriginalMenuItem {
+  menuId: number;
+  parentId: number;
+  parentName: string | null;
+  name: string;
+  url: string;
+  perms: string;
+  type: number;
+  icon: string | null;
+  orderNum: number;
+  list: OriginalMenuItem[] | null;
+  component: string;
+  hideInMenu: boolean;
+}
+
+interface OriginalMenuData {
+  menuList: OriginalMenuItem[];
+}
+
+interface ConvertedMenuItem {
+  name: string;
+  path: string;
+  component: string;
+  meta: {
+    title: string;
+    // i18nKey: string;
+    icon?: string;
+    order?: number;
+    hideInMenu?: boolean;
+  };
+  children?: ConvertedMenuItem[];
+}
+
+export function convertMenuData(originalData: OriginalMenuData): ConvertedMenuItem[] {
+  // 首先过滤出顶级菜单 (parentId === 0)
+  const topLevelMenus = originalData.menuList.filter(menu => menu.parentId === 0);
+
+  return topLevelMenus.map(menu => {
+    return convertMenuItem(menu, originalData.menuList);
+  });
+}
+
+function convertMenuItem(menu: OriginalMenuItem, allMenus: OriginalMenuItem[]): ConvertedMenuItem {
+  // 生成路由名称 (使用小写字母和下划线)
+  const routeName = menu.url
+    .replace(/^\/+/, '') // 去掉开头的一个或多个 '/'
+    .replace(/[/\s]/g, '_') // 替换剩余的 '/' 和空格为 '_'
+    .toLowerCase();
+
+  // 生成路由路径
+  let routePath = `${routeName}`;
+  if (menu.url && menu.url.trim() !== '') {
+    routePath = `${menu.url}`;
+  }
+
+  // 转换基本信息
+  const converted: ConvertedMenuItem = {
+    name: routeName,
+    path: routePath,
+    component: menu.component,
+    meta: {
+      title: menu.name,
+      // i18nKey: `route.${routeName}`,
+      order: menu.orderNum,
+      hideInMenu: menu.hideInMenu,
+      icon: String(menu.icon)
+    }
+  };
+
+  // 处理子菜单
+  if (menu.list && menu.list.length > 0) {
+    converted.children = menu.list.map(child => convertMenuItem(child, allMenus));
+  } else {
+    // 如果没有直接子菜单,但可能有其他关联的子菜单
+    const children = allMenus.filter(m => m.parentId === menu.menuId);
+    if (children.length > 0) {
+      converted.children = children.map(child => convertMenuItem(child, allMenus));
+    }
+  }
+
+  return converted;
+}
+
+// 使用示例
+// const originalData = {...}; // 你的原始数据
+// const convertedData = convertMenuData(originalData);
+// console.log(convertedData);

+ 28 - 6
src/views/_builtin/login/modules/pwd-login.vue

@@ -1,7 +1,8 @@
 <script setup lang="ts">
-import { computed, reactive } from 'vue';
+import { computed, reactive, ref, watch } from 'vue';
 import { useAuthStore } from '@/store/modules/auth';
 import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { getUUID } from '@/utils/zt';
 import { $t } from '@/locales';
 
 defineOptions({
@@ -10,15 +11,17 @@ defineOptions({
 
 const authStore = useAuthStore();
 const { formRef, validate } = useNaiveForm();
-
+const imgPath = ref('');
 interface FormModel {
   userName: string;
   password: string;
+  imageCode: string;
 }
 
 const model: FormModel = reactive({
   userName: 'admin',
-  password: 'admin123'
+  password: 'root2025',
+  imageCode: ''
 });
 
 const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
@@ -27,14 +30,29 @@ const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
 
   return {
     userName: formRules.userName,
-    password: formRules.pwd
+    password: formRules.pwd,
+    imageCode: formRules.imageCode
   };
 });
-
+const sessionUUID = ref('');
 async function handleSubmit() {
   await validate();
-  await authStore.login(model.userName, model.password);
+  await authStore.login({ ...model, sessionUUID: sessionUUID.value });
 }
+async function getDataCode() {
+  const uuid = getUUID();
+  imgPath.value = `${import.meta.env.VITE_SERVICE_BASE_URL}/captcha.jpg?uuid=${uuid}`;
+  sessionUUID.value = uuid;
+}
+getDataCode();
+watch(
+  () => authStore.loginLoading,
+  () => {
+    if (!authStore.loginLoading) {
+      getDataCode();
+    }
+  }
+);
 </script>
 
 <template>
@@ -50,6 +68,10 @@ async function handleSubmit() {
         :placeholder="$t('page.login.common.passwordPlaceholder')"
       />
     </NFormItem>
+    <NFormItem path="imageCode">
+      <NInput v-model:value="model.imageCode" placeholder="请输入验证码" />
+      <img :src="imgPath" alt="" class="ml20px h40px w220px cursor-pointer" @click="getDataCode" />
+    </NFormItem>
     <NSpace vertical :size="24">
       <div class="flex-y-center justify-between">
         <NCheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</NCheckbox>

+ 1 - 1
src/views/home/modules/header-banner.vue

@@ -48,7 +48,7 @@ const statisticData = computed<StatisticData[]>(() => [
           </div>
           <div class="pl-12px">
             <h3 class="text-18px font-semibold">
-              {{ $t('page.home.greeting', { userName: authStore.userInfo.userName }) }}
+              {{ $t('page.home.greeting', { userName: authStore.userInfo.username }) }}
             </h3>
             <p class="text-#999 leading-30px">{{ $t('page.home.weatherDesc') }}</p>
           </div>

+ 7 - 0
src/views/manage/config/index.vue

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

+ 7 - 0
src/views/manage/log/index.vue

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

+ 281 - 190
src/views/manage/menu/index.vue

@@ -1,222 +1,210 @@
 <script setup lang="tsx">
-import { ref } from 'vue';
-import type { Ref } from 'vue';
+import { nextTick, ref } from 'vue';
 import { NButton, NPopconfirm, NTag } from 'naive-ui';
-import { useBoolean } from '@sa/hooks';
+import type { InternalRowData } from 'naive-ui/es/data-table/src/interface';
 import { yesOrNoRecord } from '@/constants/common';
-import { enableStatusRecord, menuTypeRecord } from '@/constants/business';
-import { fetchGetAllPages, fetchGetMenuList } from '@/service/api';
+import { menuTypeRecord } from '@/constants/business';
+import { fetchAddMenu, fetchDelMenu, fetchEditMenu, fetchGetMenuList } from '@/service/api';
 import { useAppStore } from '@/store/modules/app';
-import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
 import { $t } from '@/locales';
 import SvgIcon from '@/components/custom/svg-icon.vue';
-import MenuOperateModal, { type OperateType } from './modules/menu-operate-modal.vue';
-
+import { useForm } from '@/components/zt/Form/hooks/useForm';
+import { type OperateType } from './modules/menu-operate-modal.vue';
+import { formSchems } from './modules/shared';
 const appStore = useAppStore();
-
-const { bool: visible, setTrue: openModal } = useBoolean();
+const checkedRowKeys = ref([]);
 
 const wrapperRef = ref<HTMLElement | null>(null);
+const menusData = ref([]);
+const loading = ref(false);
+const showModal = ref(false);
+const formLoading = ref(false);
+
+const [registerForm, { setFieldsValue, validate, getFieldsValue }] = useForm({
+  schemas: formSchems,
+  showSubmitButton: false,
+  showAdvancedButton: false,
+  showResetButton: false,
+  labelWidth: 120,
+  layout: 'horizontal',
+  gridProps: {
+    cols: '1 xl:4 s:1 l:3',
+    itemResponsive: true
+  },
+  collapsedRows: 1
+});
 
-const { columns, columnChecks, data, loading, pagination, getData, getDataByPage } = useNaivePaginatedTable({
-  api: () => fetchGetMenuList(),
-  transform: response => defaultTransform(response),
-  columns: () => [
-    {
-      type: 'selection',
-      align: 'center',
-      width: 48
-    },
-    {
-      key: 'id',
-      title: $t('page.manage.menu.id'),
-      align: 'center'
-    },
-    {
-      key: 'menuType',
-      title: $t('page.manage.menu.menuType'),
-      align: 'center',
-      width: 80,
-      render: row => {
-        const tagMap: Record<Api.SystemManage.MenuType, NaiveUI.ThemeColor> = {
-          1: 'default',
-          2: 'primary'
-        };
-
-        const label = $t(menuTypeRecord[row.menuType]);
-
-        return <NTag type={tagMap[row.menuType]}>{label}</NTag>;
-      }
-    },
-    {
-      key: 'menuName',
-      title: $t('page.manage.menu.menuName'),
-      align: 'center',
-      minWidth: 120,
-      render: row => {
-        const { i18nKey, menuName } = row;
-
-        const label = i18nKey ? $t(i18nKey) : menuName;
-
-        return <span>{label}</span>;
-      }
-    },
-    {
-      key: 'icon',
-      title: $t('page.manage.menu.icon'),
-      align: 'center',
-      width: 60,
-      render: row => {
-        const icon = row.iconType === '1' ? row.icon : undefined;
-
-        const localIcon = row.iconType === '2' ? row.icon : undefined;
-
-        return (
-          <div class="flex-center">
-            <SvgIcon icon={icon} localIcon={localIcon} class="text-icon" />
-          </div>
-        );
-      }
-    },
-    {
-      key: 'routeName',
-      title: $t('page.manage.menu.routeName'),
-      align: 'center',
-      minWidth: 120
-    },
-    {
-      key: 'routePath',
-      title: $t('page.manage.menu.routePath'),
-      align: 'center',
-      minWidth: 120
-    },
-    {
-      key: 'status',
-      title: $t('page.manage.menu.menuStatus'),
-      align: 'center',
-      width: 80,
-      render: row => {
-        if (row.status === null) {
-          return null;
-        }
-
-        const tagMap: Record<Api.Common.EnableStatus, NaiveUI.ThemeColor> = {
-          1: 'success',
-          2: 'warning'
-        };
-
-        const label = $t(enableStatusRecord[row.status]);
-
-        return <NTag type={tagMap[row.status]}>{label}</NTag>;
-      }
-    },
-    {
-      key: 'hideInMenu',
-      title: $t('page.manage.menu.hideInMenu'),
-      align: 'center',
-      width: 80,
-      render: row => {
-        const hide: CommonType.YesOrNo = row.hideInMenu ? 'Y' : 'N';
-
-        const tagMap: Record<CommonType.YesOrNo, NaiveUI.ThemeColor> = {
-          Y: 'error',
-          N: 'default'
-        };
-
-        const label = $t(yesOrNoRecord[hide]);
-
-        return <NTag type={tagMap[hide]}>{label}</NTag>;
-      }
-    },
-    {
-      key: 'parentId',
-      title: $t('page.manage.menu.parentId'),
-      width: 90,
-      align: 'center'
-    },
-    {
-      key: 'order',
-      title: $t('page.manage.menu.order'),
-      align: 'center',
-      width: 60
-    },
-    {
-      key: 'operate',
-      title: $t('common.operate'),
-      align: 'center',
-      width: 230,
-      render: row => (
-        <div class="flex-center justify-end gap-8px">
-          {row.menuType === '1' && (
-            <NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
-              {$t('page.manage.menu.addChildMenu')}
-            </NButton>
-          )}
-          <NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}>
-            {$t('common.edit')}
-          </NButton>
-          <NPopconfirm onPositiveClick={() => handleDelete(row.id)}>
-            {{
-              default: () => $t('common.confirmDelete'),
-              trigger: () => (
-                <NButton type="error" ghost size="small">
-                  {$t('common.delete')}
-                </NButton>
-              )
-            }}
-          </NPopconfirm>
+const colums: NaiveUI.TableColumn<InternalRowData>[] = [
+  {
+    type: 'selection',
+    align: 'center',
+    width: 48
+  },
+  {
+    key: 'id',
+    title: $t('page.manage.menu.id'),
+    align: 'center'
+  },
+  {
+    key: 'type',
+    title: $t('page.manage.menu.menuType'),
+    align: 'center',
+    width: 80,
+    render: (row: InternalRowData) => {
+      const tagMap: Record<Api.SystemManage.MenuType, NaiveUI.ThemeColor> = {
+        0: 'default',
+        1: 'primary',
+        2: 'error'
+      };
+
+      const type = row.type as keyof typeof menuTypeRecord;
+      const label = $t(menuTypeRecord[type]);
+      return <NTag type={tagMap[type]}>{label}</NTag>;
+    }
+  },
+  {
+    key: 'name',
+    title: $t('page.manage.menu.menuName'),
+    align: 'center',
+    minWidth: 120,
+    render: (row: InternalRowData) => {
+      return <span>{row.name}</span>;
+    }
+  },
+  {
+    key: 'icon',
+    title: $t('page.manage.menu.icon'),
+    align: 'center',
+    width: 60,
+    render: (row: InternalRowData) => {
+      return (
+        <div class="flex-center">
+          <SvgIcon icon={String(row.icon)} class="text-icon" />
         </div>
-      )
+      );
     }
-  ]
-});
-
-const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, 'id', getData);
+  },
+  {
+    key: 'routeName',
+    title: $t('page.manage.menu.routeName'),
+    align: 'center',
+    minWidth: 120
+  },
+  {
+    key: 'url',
+    title: $t('page.manage.menu.routePath'),
+    align: 'center',
+    minWidth: 120
+  },
+  {
+    key: 'hideInMenu',
+    title: $t('page.manage.menu.hideInMenu'),
+    align: 'center',
+    width: 80,
+    render: (row: InternalRowData) => {
+      const hide: CommonType.YesOrNo = row.hideInMenu ? 'Y' : 'N';
+
+      const tagMap: Record<CommonType.YesOrNo, NaiveUI.ThemeColor> = {
+        Y: 'error',
+        N: 'default'
+      };
+
+      const label = $t(yesOrNoRecord[hide]);
+
+      return <NTag type={tagMap[hide]}>{label}</NTag>;
+    }
+  },
+  {
+    key: 'parentId',
+    title: $t('page.manage.menu.parentId'),
+    width: 90,
+    align: 'center'
+  },
+  {
+    key: 'order',
+    title: $t('page.manage.menu.order'),
+    align: 'center',
+    width: 60
+  },
+  {
+    key: 'operate',
+    title: $t('common.operate'),
+    align: 'center',
+    width: 230,
+    fixed: 'right',
+    render: (row: any) => (
+      <div class="flex-center justify-end gap-8px">
+        {row.type == '0' && (
+          <NButton type="primary" ghost size="small" onClick={() => handleAddChildMenu(row)}>
+            {$t('page.manage.menu.addChildMenu')}
+          </NButton>
+        )}
+        <NButton type="primary" ghost size="small" onClick={() => handleEdit(row)}>
+          {$t('common.edit')}
+        </NButton>
+        <NPopconfirm onPositiveClick={() => handleDelete(row.menuId)}>
+          {{
+            default: () => $t('common.confirmDelete'),
+            trigger: () => (
+              <NButton type="error" ghost size="small" loading={formLoading.value}>
+                {$t('common.delete')}
+              </NButton>
+            )
+          }}
+        </NPopconfirm>
+      </div>
+    )
+  }
+];
+
+// const { checkedRowKeys, onBatchDeleted, onDeleted } = useTableOperate(data, 'id', getData);
 
 const operateType = ref<OperateType>('add');
 
 function handleAdd() {
   operateType.value = 'add';
-  openModal();
+  showModal.value = true;
 }
 
 async function handleBatchDelete() {
   // request
   console.log(checkedRowKeys.value);
-
-  onBatchDeleted();
 }
 
-function handleDelete(id: number) {
+async function handleDelete(id: number) {
   // request
   console.log(id);
-
-  onDeleted();
+  formLoading.value = true;
+  await fetchDelMenu(id);
+  init();
+  formLoading.value = false;
 }
 
-/** the edit menu data or the parent menu data when adding a child menu */
-const editingData: Ref<Api.SystemManage.Menu | null> = ref(null);
-
 function handleEdit(item: Api.SystemManage.Menu) {
+  showModal.value = true;
   operateType.value = 'edit';
-  editingData.value = { ...item };
-
-  openModal();
+  nextTick(() => {
+    setFieldsValue(item);
+    console.log(getFieldsValue());
+  });
 }
 
-function handleAddChildMenu(item: Api.SystemManage.Menu) {
+function handleAddChildMenu(item: any) {
   operateType.value = 'addChild';
+  showModal.value = true;
+  console.log(item, 'asdasd');
 
-  editingData.value = { ...item };
-
-  openModal();
+  nextTick(() => {
+    setFieldsValue({ parentId: item.menuId });
+    console.log(getFieldsValue());
+  });
 }
 
-const allPages = ref<string[]>([]);
-
 async function getAllPages() {
-  const { data: pages } = await fetchGetAllPages();
-  allPages.value = pages || [];
+  const { data } = await fetchGetMenuList();
+  const menuData = buildMenuTree(data);
+  console.log(menuData, '数据');
+  menusData.value = menuData;
 }
 
 function init() {
@@ -225,6 +213,83 @@ function init() {
 
 // init
 init();
+function buildMenuTree(data: any) {
+  // 创建根节点数组和映射表
+  const rootNodes: any = [];
+  const nodeMap: any = {};
+
+  // 首先将所有节点存入映射表
+  data.forEach((item: any) => {
+    nodeMap[item.menuId] = { ...item };
+  });
+
+  // 构建树形结构
+  data.forEach((item: any) => {
+    const node = nodeMap[item.menuId];
+
+    if (item.parentId === 0) {
+      // 根节点
+      rootNodes.push(node);
+    } else {
+      // 子节点,找到父节点并添加到父节点的children中
+      const parent = nodeMap[item.parentId];
+      if (parent) {
+        if (!parent.children) {
+          parent.children = [];
+        }
+        parent.children.push(node);
+      } else {
+        // 如果父节点不存在,也作为根节点
+        rootNodes.push(node);
+      }
+    }
+  });
+
+  // 删除空children字段
+  const removeEmptyChildren = (nodes: any) => {
+    nodes.forEach((node: any) => {
+      if (node.children && node.children.length === 0) {
+        delete node.children;
+      } else if (node.children) {
+        removeEmptyChildren(node.children);
+      }
+    });
+    return nodes;
+  };
+
+  // 对根节点和子节点按orderNum排序
+  const sortByOrderNum = (nodes: any) => {
+    return nodes
+      .sort((a: any, b: any) => a.orderNum - b.orderNum)
+      .map((node: any) => {
+        if (node.children) {
+          node.children = sortByOrderNum(node.children);
+        }
+        return node;
+      });
+  };
+
+  return removeEmptyChildren(sortByOrderNum(rootNodes));
+}
+async function handleSubmit() {
+  console.log(getFieldsValue());
+
+  await validate();
+  formLoading.value = true;
+  const form = getFieldsValue();
+  if (operateType.value == 'add' || operateType.value == 'addChild') {
+    await fetchAddMenu(form as Api.SystemManage.AddMenu);
+    formLoading.value = false;
+    showModal.value = false;
+
+    init();
+  } else {
+    await fetchEditMenu(form as Api.SystemManage.AddMenu);
+    formLoading.value = false;
+    showModal.value = false;
+    init();
+  }
+}
 </script>
 
 <template>
@@ -232,34 +297,60 @@ init();
     <NCard :title="$t('page.manage.menu.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
       <template #header-extra>
         <TableHeaderOperation
-          v-model:columns="columnChecks"
           :disabled-delete="checkedRowKeys.length === 0"
           :loading="loading"
+          is-add
           @add="handleAdd"
           @delete="handleBatchDelete"
-          @refresh="getData"
+          @refresh="init"
         />
       </template>
       <NDataTable
         v-model:checked-row-keys="checkedRowKeys"
-        :columns="columns"
-        :data="data"
+        :columns="colums"
+        :data="menusData"
         size="small"
         :flex-height="!appStore.isMobile"
         :scroll-x="1088"
         :loading="loading"
-        :row-key="row => row.id"
+        :row-key="row => row.menuId"
         remote
-        :pagination="pagination"
         class="sm:h-full"
       />
-      <MenuOperateModal
+      <!--
+ <MenuOperateModal
         v-model:visible="visible"
         :operate-type="operateType"
         :row-data="editingData"
         :all-pages="allPages"
-        @submitted="getDataByPage"
       />
+
+-->
+      <NModal v-model:show="showModal">
+        <NCard class="w-800px">
+          <template #header>
+            <div>
+              {{ operateType == 'add' ? '新增菜单' : operateType == 'addChild' ? '新增子菜单' : ' 编辑菜单' }}
+            </div>
+          </template>
+          <BasicForm @register="registerForm">
+            <template #parentId="{ model, field }">
+              <NTreeSelect
+                v-model:value="model[field]"
+                :options="menusData"
+                label-field="name"
+                key-field="menuId"
+              ></NTreeSelect>
+            </template>
+          </BasicForm>
+          <template #footer>
+            <div class="flex justify-end">
+              <NButton class="mr-3" @click="showModal = false">取消</NButton>
+              <NButton type="primary" :loading="formLoading" @click="handleSubmit">确定</NButton>
+            </div>
+          </template>
+        </NCard>
+      </NModal>
     </NCard>
   </div>
 </template>

+ 2133 - 0
src/views/manage/menu/modules/icons.ts

@@ -0,0 +1,2133 @@
+export const icons = [
+  'tdesign:accessibility',
+  'tdesign:accessibility-filled',
+  'tdesign:activity',
+  'tdesign:activity-filled',
+  'tdesign:add',
+  'tdesign:add-and-subtract',
+  'tdesign:add-circle',
+  'tdesign:add-circle-filled',
+  'tdesign:add-rectangle',
+  'tdesign:add-rectangle-filled',
+  'tdesign:address-book',
+  'tdesign:address-book-filled',
+  'tdesign:adjustment',
+  'tdesign:adjustment-filled',
+  'tdesign:airplay-wave',
+  'tdesign:airplay-wave-filled',
+  'tdesign:alarm',
+  'tdesign:alarm-add',
+  'tdesign:alarm-add-filled',
+  'tdesign:alarm-filled',
+  'tdesign:alarm-off',
+  'tdesign:alarm-off-filled',
+  'tdesign:align-top',
+  'tdesign:align-vertical',
+  'tdesign:alpha',
+  'tdesign:analytics',
+  'tdesign:analytics-filled',
+  'tdesign:anchor',
+  'tdesign:angry',
+  'tdesign:angry-filled',
+  'tdesign:animation',
+  'tdesign:animation-1',
+  'tdesign:animation-1-filled',
+  'tdesign:animation-filled',
+  'tdesign:anticlockwise',
+  'tdesign:anticlockwise-filled',
+  'tdesign:api',
+  'tdesign:app',
+  'tdesign:app-filled',
+  'tdesign:apple',
+  'tdesign:apple-filled',
+  'tdesign:application',
+  'tdesign:application-filled',
+  'tdesign:architecture-hui-style',
+  'tdesign:architecture-hui-style-filled',
+  'tdesign:archway',
+  'tdesign:archway-1',
+  'tdesign:archway-1-filled',
+  'tdesign:archway-filled',
+  'tdesign:arrow-down',
+  'tdesign:arrow-down-circle',
+  'tdesign:arrow-down-circle-filled',
+  'tdesign:arrow-down-rectangle',
+  'tdesign:arrow-down-rectangle-filled',
+  'tdesign:arrow-left',
+  'tdesign:arrow-left-circle',
+  'tdesign:arrow-left-circle-filled',
+  'tdesign:arrow-left-down',
+  'tdesign:arrow-left-down-circle',
+  'tdesign:arrow-left-down-circle-filled',
+  'tdesign:arrow-left-right-1',
+  'tdesign:arrow-left-right-2',
+  'tdesign:arrow-left-right-3',
+  'tdesign:arrow-left-right-circle',
+  'tdesign:arrow-left-right-circle-filled',
+  'tdesign:arrow-left-up',
+  'tdesign:arrow-left-up-circle',
+  'tdesign:arrow-left-up-circle-filled',
+  'tdesign:arrow-right',
+  'tdesign:arrow-right-circle',
+  'tdesign:arrow-right-circle-filled',
+  'tdesign:arrow-right-down',
+  'tdesign:arrow-right-down-circle',
+  'tdesign:arrow-right-down-circle-filled',
+  'tdesign:arrow-right-up',
+  'tdesign:arrow-right-up-circle',
+  'tdesign:arrow-right-up-circle-filled',
+  'tdesign:arrow-triangle-down',
+  'tdesign:arrow-triangle-down-filled',
+  'tdesign:arrow-triangle-up',
+  'tdesign:arrow-triangle-up-filled',
+  'tdesign:arrow-up',
+  'tdesign:arrow-up-circle',
+  'tdesign:arrow-up-circle-filled',
+  'tdesign:arrow-up-down-1',
+  'tdesign:arrow-up-down-2',
+  'tdesign:arrow-up-down-3',
+  'tdesign:arrow-up-down-circle',
+  'tdesign:arrow-up-down-circle-filled',
+  'tdesign:artboard',
+  'tdesign:article',
+  'tdesign:article-filled',
+  'tdesign:assignment',
+  'tdesign:assignment-checked',
+  'tdesign:assignment-checked-filled',
+  'tdesign:assignment-code',
+  'tdesign:assignment-code-filled',
+  'tdesign:assignment-error',
+  'tdesign:assignment-error-filled',
+  'tdesign:assignment-filled',
+  'tdesign:assignment-user',
+  'tdesign:assignment-user-filled',
+  'tdesign:attach',
+  'tdesign:attic',
+  'tdesign:attic-1',
+  'tdesign:attic-1-filled',
+  'tdesign:attic-filled',
+  'tdesign:audio',
+  'tdesign:audio-filled',
+  'tdesign:awkward',
+  'tdesign:awkward-filled',
+  'tdesign:backtop',
+  'tdesign:backtop-rectangle',
+  'tdesign:backtop-rectangle-filled',
+  'tdesign:backup',
+  'tdesign:backup-filled',
+  'tdesign:backward',
+  'tdesign:backward-filled',
+  'tdesign:bad-laugh',
+  'tdesign:bad-laugh-filled',
+  'tdesign:bamboo-shoot',
+  'tdesign:bamboo-shoot-filled',
+  'tdesign:banana',
+  'tdesign:banana-filled',
+  'tdesign:barbecue',
+  'tdesign:barbecue-filled',
+  'tdesign:barcode',
+  'tdesign:barcode-1',
+  'tdesign:base-station',
+  'tdesign:battery',
+  'tdesign:battery-add',
+  'tdesign:battery-add-filled',
+  'tdesign:battery-charging',
+  'tdesign:battery-charging-filled',
+  'tdesign:battery-filled',
+  'tdesign:battery-low',
+  'tdesign:battery-low-filled',
+  'tdesign:bean',
+  'tdesign:bean-filled',
+  'tdesign:beer',
+  'tdesign:beer-filled',
+  'tdesign:beta',
+  'tdesign:bifurcate',
+  'tdesign:bifurcate-filled',
+  'tdesign:bill',
+  'tdesign:bill-filled',
+  'tdesign:blockchain',
+  'tdesign:bluetooth',
+  'tdesign:bone',
+  'tdesign:bone-filled',
+  'tdesign:book',
+  'tdesign:book-filled',
+  'tdesign:book-open',
+  'tdesign:book-open-filled',
+  'tdesign:book-unknown',
+  'tdesign:book-unknown-filled',
+  'tdesign:bookmark',
+  'tdesign:bookmark-add',
+  'tdesign:bookmark-add-filled',
+  'tdesign:bookmark-checked',
+  'tdesign:bookmark-checked-filled',
+  'tdesign:bookmark-double',
+  'tdesign:bookmark-double-filled',
+  'tdesign:bookmark-filled',
+  'tdesign:bookmark-minus',
+  'tdesign:bookmark-minus-filled',
+  'tdesign:braces',
+  'tdesign:brackets',
+  'tdesign:bread',
+  'tdesign:bread-filled',
+  'tdesign:bridge',
+  'tdesign:bridge-1',
+  'tdesign:bridge-1-filled',
+  'tdesign:bridge-2',
+  'tdesign:bridge-2-filled',
+  'tdesign:bridge-3',
+  'tdesign:bridge-4',
+  'tdesign:bridge-5',
+  'tdesign:bridge-5-filled',
+  'tdesign:bridge-6',
+  'tdesign:bridge-6-filled',
+  'tdesign:brightness',
+  'tdesign:brightness-1',
+  'tdesign:brightness-1-filled',
+  'tdesign:brightness-filled',
+  'tdesign:broccoli',
+  'tdesign:broccoli-filled',
+  'tdesign:browse',
+  'tdesign:browse-filled',
+  'tdesign:browse-gallery',
+  'tdesign:browse-gallery-filled',
+  'tdesign:browse-off',
+  'tdesign:browse-off-filled',
+  'tdesign:brush',
+  'tdesign:brush-filled',
+  'tdesign:bug',
+  'tdesign:bug-filled',
+  'tdesign:bug-report',
+  'tdesign:bug-report-filled',
+  'tdesign:building',
+  'tdesign:building-1',
+  'tdesign:building-1-filled',
+  'tdesign:building-2',
+  'tdesign:building-2-filled',
+  'tdesign:building-3',
+  'tdesign:building-3-filled',
+  'tdesign:building-4',
+  'tdesign:building-4-filled',
+  'tdesign:building-5',
+  'tdesign:building-5-filled',
+  'tdesign:building-filled',
+  'tdesign:bulletpoint',
+  'tdesign:button',
+  'tdesign:button-filled',
+  'tdesign:cabbage',
+  'tdesign:cabbage-filled',
+  'tdesign:cake',
+  'tdesign:cake-filled',
+  'tdesign:calculation',
+  'tdesign:calculation-1',
+  'tdesign:calculation-1-filled',
+  'tdesign:calculator',
+  'tdesign:calculator-1',
+  'tdesign:calculator-filled',
+  'tdesign:calendar',
+  'tdesign:calendar-1',
+  'tdesign:calendar-1-filled',
+  'tdesign:calendar-2',
+  'tdesign:calendar-2-filled',
+  'tdesign:calendar-edit',
+  'tdesign:calendar-edit-filled',
+  'tdesign:calendar-event',
+  'tdesign:calendar-event-filled',
+  'tdesign:calendar-filled',
+  'tdesign:call',
+  'tdesign:call-1',
+  'tdesign:call-1-filled',
+  'tdesign:call-cancel',
+  'tdesign:call-cancel-filled',
+  'tdesign:call-filled',
+  'tdesign:call-forwarded',
+  'tdesign:call-forwarded-filled',
+  'tdesign:call-incoming',
+  'tdesign:call-incoming-filled',
+  'tdesign:call-off',
+  'tdesign:call-off-filled',
+  'tdesign:calm',
+  'tdesign:calm-1',
+  'tdesign:calm-1-filled',
+  'tdesign:calm-filled',
+  'tdesign:camera',
+  'tdesign:camera-1',
+  'tdesign:camera-1-filled',
+  'tdesign:camera-2',
+  'tdesign:camera-2-filled',
+  'tdesign:camera-filled',
+  'tdesign:camera-off',
+  'tdesign:camera-off-filled',
+  'tdesign:candy',
+  'tdesign:candy-filled',
+  'tdesign:card',
+  'tdesign:card-filled',
+  'tdesign:cardmembership',
+  'tdesign:cardmembership-filled',
+  'tdesign:caret-down',
+  'tdesign:caret-down-small',
+  'tdesign:caret-left',
+  'tdesign:caret-left-small',
+  'tdesign:caret-right',
+  'tdesign:caret-right-small',
+  'tdesign:caret-up',
+  'tdesign:caret-up-small',
+  'tdesign:cart',
+  'tdesign:cart-add',
+  'tdesign:cart-add-filled',
+  'tdesign:cart-filled',
+  'tdesign:cast',
+  'tdesign:cast-filled',
+  'tdesign:castle',
+  'tdesign:castle-1',
+  'tdesign:castle-1-filled',
+  'tdesign:castle-2',
+  'tdesign:castle-2-filled',
+  'tdesign:castle-3',
+  'tdesign:castle-3-filled',
+  'tdesign:castle-4',
+  'tdesign:castle-4-filled',
+  'tdesign:castle-5',
+  'tdesign:castle-5-filled',
+  'tdesign:castle-6',
+  'tdesign:castle-6-filled',
+  'tdesign:castle-7',
+  'tdesign:castle-7-filled',
+  'tdesign:castle-filled',
+  'tdesign:cat',
+  'tdesign:cat-filled',
+  'tdesign:catalog',
+  'tdesign:catalog-filled',
+  'tdesign:cd',
+  'tdesign:cd-filled',
+  'tdesign:celsius',
+  'tdesign:center-focus-strong',
+  'tdesign:center-focus-strong-filled',
+  'tdesign:centimeter',
+  'tdesign:certificate',
+  'tdesign:certificate-1',
+  'tdesign:certificate-1-filled',
+  'tdesign:certificate-filled',
+  'tdesign:chart',
+  'tdesign:chart-3d',
+  'tdesign:chart-3d-filled',
+  'tdesign:chart-add',
+  'tdesign:chart-add-filled',
+  'tdesign:chart-analytics',
+  'tdesign:chart-area',
+  'tdesign:chart-area-filled',
+  'tdesign:chart-area-multi',
+  'tdesign:chart-area-multi-filled',
+  'tdesign:chart-bar',
+  'tdesign:chart-bar-filled',
+  'tdesign:chart-bubble',
+  'tdesign:chart-bubble-filled',
+  'tdesign:chart-column',
+  'tdesign:chart-column-filled',
+  'tdesign:chart-combo',
+  'tdesign:chart-combo-filled',
+  'tdesign:chart-filled',
+  'tdesign:chart-line',
+  'tdesign:chart-line-data',
+  'tdesign:chart-line-data-1',
+  'tdesign:chart-line-multi',
+  'tdesign:chart-maximum',
+  'tdesign:chart-median',
+  'tdesign:chart-minimum',
+  'tdesign:chart-pie',
+  'tdesign:chart-pie-filled',
+  'tdesign:chart-radar',
+  'tdesign:chart-radar-filled',
+  'tdesign:chart-radial',
+  'tdesign:chart-ring',
+  'tdesign:chart-ring-1',
+  'tdesign:chart-ring-1-filled',
+  'tdesign:chart-ring-filled',
+  'tdesign:chart-scatter',
+  'tdesign:chart-stacked',
+  'tdesign:chart-stacked-filled',
+  'tdesign:chat',
+  'tdesign:chat-add',
+  'tdesign:chat-add-filled',
+  'tdesign:chat-bubble',
+  'tdesign:chat-bubble-1',
+  'tdesign:chat-bubble-1-filled',
+  'tdesign:chat-bubble-add',
+  'tdesign:chat-bubble-add-filled',
+  'tdesign:chat-bubble-error',
+  'tdesign:chat-bubble-error-filled',
+  'tdesign:chat-bubble-filled',
+  'tdesign:chat-bubble-help',
+  'tdesign:chat-bubble-help-filled',
+  'tdesign:chat-bubble-history',
+  'tdesign:chat-bubble-history-filled',
+  'tdesign:chat-bubble-locked',
+  'tdesign:chat-bubble-locked-filled',
+  'tdesign:chat-bubble-smile',
+  'tdesign:chat-bubble-smile-filled',
+  'tdesign:chat-checked',
+  'tdesign:chat-checked-filled',
+  'tdesign:chat-clear',
+  'tdesign:chat-clear-filled',
+  'tdesign:chat-double',
+  'tdesign:chat-double-filled',
+  'tdesign:chat-error',
+  'tdesign:chat-error-filled',
+  'tdesign:chat-filled',
+  'tdesign:chat-heart',
+  'tdesign:chat-heart-filled',
+  'tdesign:chat-message',
+  'tdesign:chat-message-filled',
+  'tdesign:chat-off',
+  'tdesign:chat-off-filled',
+  'tdesign:chat-poll',
+  'tdesign:chat-poll-filled',
+  'tdesign:chat-setting',
+  'tdesign:chat-setting-filled',
+  'tdesign:check',
+  'tdesign:check-circle',
+  'tdesign:check-circle-filled',
+  'tdesign:check-double',
+  'tdesign:check-rectangle',
+  'tdesign:check-rectangle-filled',
+  'tdesign:cheese',
+  'tdesign:cheese-filled',
+  'tdesign:cherry',
+  'tdesign:cherry-filled',
+  'tdesign:chevron-down',
+  'tdesign:chevron-down-circle',
+  'tdesign:chevron-down-circle-filled',
+  'tdesign:chevron-down-double',
+  'tdesign:chevron-down-double-s',
+  'tdesign:chevron-down-rectangle',
+  'tdesign:chevron-down-rectangle-filled',
+  'tdesign:chevron-down-s',
+  'tdesign:chevron-left',
+  'tdesign:chevron-left-circle',
+  'tdesign:chevron-left-circle-filled',
+  'tdesign:chevron-left-double',
+  'tdesign:chevron-left-double-s',
+  'tdesign:chevron-left-rectangle',
+  'tdesign:chevron-left-rectangle-filled',
+  'tdesign:chevron-left-s',
+  'tdesign:chevron-right',
+  'tdesign:chevron-right-circle',
+  'tdesign:chevron-right-circle-filled',
+  'tdesign:chevron-right-double',
+  'tdesign:chevron-right-double-s',
+  'tdesign:chevron-right-rectangle',
+  'tdesign:chevron-right-rectangle-filled',
+  'tdesign:chevron-right-s',
+  'tdesign:chevron-up',
+  'tdesign:chevron-up-circle',
+  'tdesign:chevron-up-circle-filled',
+  'tdesign:chevron-up-double',
+  'tdesign:chevron-up-double-s',
+  'tdesign:chevron-up-rectangle',
+  'tdesign:chevron-up-rectangle-filled',
+  'tdesign:chevron-up-s',
+  'tdesign:chicken',
+  'tdesign:chili',
+  'tdesign:chili-filled',
+  'tdesign:chimney',
+  'tdesign:chimney-1',
+  'tdesign:chimney-1-filled',
+  'tdesign:chimney-2',
+  'tdesign:chimney-2-filled',
+  'tdesign:chimney-filled',
+  'tdesign:chinese-cabbage',
+  'tdesign:chinese-cabbage-filled',
+  'tdesign:church',
+  'tdesign:church-filled',
+  'tdesign:circle',
+  'tdesign:circle-filled',
+  'tdesign:city',
+  'tdesign:city-1',
+  'tdesign:city-1-filled',
+  'tdesign:city-10',
+  'tdesign:city-10-filled',
+  'tdesign:city-11',
+  'tdesign:city-11-filled',
+  'tdesign:city-12',
+  'tdesign:city-12-filled',
+  'tdesign:city-13',
+  'tdesign:city-13-filled',
+  'tdesign:city-14',
+  'tdesign:city-14-filled',
+  'tdesign:city-15',
+  'tdesign:city-15-filled',
+  'tdesign:city-2',
+  'tdesign:city-2-filled',
+  'tdesign:city-3',
+  'tdesign:city-3-filled',
+  'tdesign:city-4',
+  'tdesign:city-4-filled',
+  'tdesign:city-5',
+  'tdesign:city-5-filled',
+  'tdesign:city-6',
+  'tdesign:city-6-filled',
+  'tdesign:city-7',
+  'tdesign:city-7-filled',
+  'tdesign:city-8',
+  'tdesign:city-8-filled',
+  'tdesign:city-9',
+  'tdesign:city-9-filled',
+  'tdesign:city-ancient',
+  'tdesign:city-ancient-1',
+  'tdesign:city-ancient-1-filled',
+  'tdesign:city-ancient-2',
+  'tdesign:city-ancient-2-filled',
+  'tdesign:city-ancient-filled',
+  'tdesign:city-filled',
+  'tdesign:clear',
+  'tdesign:clear-filled',
+  'tdesign:clear-formatting',
+  'tdesign:clear-formatting-1',
+  'tdesign:clear-formatting-1-filled',
+  'tdesign:clear-formatting-filled',
+  'tdesign:close',
+  'tdesign:close-circle',
+  'tdesign:close-circle-filled',
+  'tdesign:close-octagon',
+  'tdesign:close-octagon-filled',
+  'tdesign:close-rectangle',
+  'tdesign:close-rectangle-filled',
+  'tdesign:cloud',
+  'tdesign:cloud-download',
+  'tdesign:cloud-filled',
+  'tdesign:cloud-upload',
+  'tdesign:cloudy-day',
+  'tdesign:cloudy-day-filled',
+  'tdesign:cloudy-night',
+  'tdesign:cloudy-night-filled',
+  'tdesign:cloudy-night-rain',
+  'tdesign:cloudy-night-rain-filled',
+  'tdesign:cloudy-rain',
+  'tdesign:cloudy-rain-filled',
+  'tdesign:cloudy-sunny',
+  'tdesign:cloudy-sunny-filled',
+  'tdesign:code',
+  'tdesign:code-1',
+  'tdesign:code-off',
+  'tdesign:cola',
+  'tdesign:cola-filled',
+  'tdesign:collage',
+  'tdesign:collage-filled',
+  'tdesign:collection',
+  'tdesign:collection-filled',
+  'tdesign:color-invert',
+  'tdesign:color-invert-filled',
+  'tdesign:combination',
+  'tdesign:combination-filled',
+  'tdesign:command',
+  'tdesign:compass',
+  'tdesign:compass-1',
+  'tdesign:compass-1-filled',
+  'tdesign:compass-filled',
+  'tdesign:component-breadcrumb',
+  'tdesign:component-breadcrumb-filled',
+  'tdesign:component-checkbox',
+  'tdesign:component-checkbox-filled',
+  'tdesign:component-divider-horizontal',
+  'tdesign:component-divider-horizontal-filled',
+  'tdesign:component-divider-vertical',
+  'tdesign:component-divider-vertical-filled',
+  'tdesign:component-dropdown',
+  'tdesign:component-dropdown-filled',
+  'tdesign:component-grid',
+  'tdesign:component-grid-filled',
+  'tdesign:component-input',
+  'tdesign:component-input-filled',
+  'tdesign:component-layout',
+  'tdesign:component-layout-filled',
+  'tdesign:component-radio',
+  'tdesign:component-space',
+  'tdesign:component-space-filled',
+  'tdesign:component-steps',
+  'tdesign:component-steps-filled',
+  'tdesign:component-switch',
+  'tdesign:component-switch-filled',
+  'tdesign:constraint',
+  'tdesign:contrast',
+  'tdesign:contrast-1',
+  'tdesign:contrast-1-filled',
+  'tdesign:contrast-filled',
+  'tdesign:control-platform',
+  'tdesign:control-platform-filled',
+  'tdesign:cooperate',
+  'tdesign:cooperate-filled',
+  'tdesign:coordinate-system',
+  'tdesign:coordinate-system-filled',
+  'tdesign:copy',
+  'tdesign:copy-filled',
+  'tdesign:copyright',
+  'tdesign:copyright-filled',
+  'tdesign:corn',
+  'tdesign:corn-filled',
+  'tdesign:coupon',
+  'tdesign:coupon-filled',
+  'tdesign:course',
+  'tdesign:course-filled',
+  'tdesign:cpu',
+  'tdesign:cpu-filled',
+  'tdesign:crack',
+  'tdesign:crack-filled',
+  'tdesign:creditcard',
+  'tdesign:creditcard-add',
+  'tdesign:creditcard-add-filled',
+  'tdesign:creditcard-filled',
+  'tdesign:creditcard-off',
+  'tdesign:creditcard-off-filled',
+  'tdesign:crooked-smile',
+  'tdesign:crooked-smile-filled',
+  'tdesign:cry-and-laugh',
+  'tdesign:cry-and-laugh-filled',
+  'tdesign:cry-loudly',
+  'tdesign:cry-loudly-filled',
+  'tdesign:css3',
+  'tdesign:css3-filled',
+  'tdesign:cucumber',
+  'tdesign:currency-exchange',
+  'tdesign:cursor',
+  'tdesign:cursor-filled',
+  'tdesign:curtain',
+  'tdesign:curtain-filled',
+  'tdesign:curve',
+  'tdesign:cut',
+  'tdesign:cut-1',
+  'tdesign:dam',
+  'tdesign:dam-1',
+  'tdesign:dam-1-filled',
+  'tdesign:dam-2',
+  'tdesign:dam-2-filled',
+  'tdesign:dam-3',
+  'tdesign:dam-3-filled',
+  'tdesign:dam-4',
+  'tdesign:dam-4-filled',
+  'tdesign:dam-5',
+  'tdesign:dam-5-filled',
+  'tdesign:dam-6',
+  'tdesign:dam-6-filled',
+  'tdesign:dam-7',
+  'tdesign:dam-7-filled',
+  'tdesign:dam-filled',
+  'tdesign:dart-board',
+  'tdesign:dart-board-filled',
+  'tdesign:dashboard',
+  'tdesign:dashboard-1',
+  'tdesign:dashboard-1-filled',
+  'tdesign:dashboard-filled',
+  'tdesign:data',
+  'tdesign:data-base',
+  'tdesign:data-base-filled',
+  'tdesign:data-checked',
+  'tdesign:data-checked-filled',
+  'tdesign:data-display',
+  'tdesign:data-error',
+  'tdesign:data-error-filled',
+  'tdesign:data-filled',
+  'tdesign:data-search',
+  'tdesign:data-search-filled',
+  'tdesign:delete',
+  'tdesign:delete-1',
+  'tdesign:delete-1-filled',
+  'tdesign:delete-filled',
+  'tdesign:delete-time',
+  'tdesign:delete-time-filled',
+  'tdesign:delta',
+  'tdesign:delta-filled',
+  'tdesign:depressed',
+  'tdesign:depressed-filled',
+  'tdesign:desktop',
+  'tdesign:desktop-1',
+  'tdesign:desktop-1-filled',
+  'tdesign:desktop-filled',
+  'tdesign:despise',
+  'tdesign:despise-filled',
+  'tdesign:device',
+  'tdesign:device-filled',
+  'tdesign:discount',
+  'tdesign:discount-filled',
+  'tdesign:dissatisfaction',
+  'tdesign:dissatisfaction-filled',
+  'tdesign:divide',
+  'tdesign:dividers',
+  'tdesign:dividers-1',
+  'tdesign:doge',
+  'tdesign:doge-filled',
+  'tdesign:double-storey',
+  'tdesign:double-storey-filled',
+  'tdesign:download',
+  'tdesign:download-1',
+  'tdesign:download-2',
+  'tdesign:download-2-filled',
+  'tdesign:downscale',
+  'tdesign:drag-drop',
+  'tdesign:drag-move',
+  'tdesign:drink',
+  'tdesign:drink-filled',
+  'tdesign:drumstick',
+  'tdesign:drumstick-filled',
+  'tdesign:dv',
+  'tdesign:dv-filled',
+  'tdesign:dvd',
+  'tdesign:dvd-filled',
+  'tdesign:earphone',
+  'tdesign:earphone-filled',
+  'tdesign:earth',
+  'tdesign:earth-filled',
+  'tdesign:edit',
+  'tdesign:edit-1',
+  'tdesign:edit-1-filled',
+  'tdesign:edit-2',
+  'tdesign:edit-2-filled',
+  'tdesign:edit-filled',
+  'tdesign:edit-off',
+  'tdesign:edit-off-filled',
+  'tdesign:education',
+  'tdesign:education-filled',
+  'tdesign:eggplant',
+  'tdesign:eggplant-filled',
+  'tdesign:ellipsis',
+  'tdesign:emo-emotional',
+  'tdesign:emo-emotional-filled',
+  'tdesign:enter',
+  'tdesign:equal',
+  'tdesign:error',
+  'tdesign:error-circle',
+  'tdesign:error-circle-filled',
+  'tdesign:error-triangle',
+  'tdesign:error-triangle-filled',
+  'tdesign:excited',
+  'tdesign:excited-1',
+  'tdesign:excited-1-filled',
+  'tdesign:excited-filled',
+  'tdesign:expand-down',
+  'tdesign:expand-down-filled',
+  'tdesign:expand-horizontal',
+  'tdesign:expand-up',
+  'tdesign:expand-up-filled',
+  'tdesign:expand-vertical',
+  'tdesign:explore',
+  'tdesign:explore-filled',
+  'tdesign:explore-off',
+  'tdesign:explore-off-filled',
+  'tdesign:exposure',
+  'tdesign:exposure-filled',
+  'tdesign:extension',
+  'tdesign:extension-filled',
+  'tdesign:extension-off',
+  'tdesign:extension-off-filled',
+  'tdesign:face-retouching',
+  'tdesign:face-retouching-filled',
+  'tdesign:fact-check',
+  'tdesign:fact-check-filled',
+  'tdesign:fahrenheit-scale',
+  'tdesign:feel-at-ease',
+  'tdesign:feel-at-ease-filled',
+  'tdesign:ferocious',
+  'tdesign:ferocious-filled',
+  'tdesign:ferris-wheel',
+  'tdesign:ferris-wheel-filled',
+  'tdesign:file',
+  'tdesign:file-1',
+  'tdesign:file-1-filled',
+  'tdesign:file-add',
+  'tdesign:file-add-1',
+  'tdesign:file-add-1-filled',
+  'tdesign:file-add-filled',
+  'tdesign:file-attachment',
+  'tdesign:file-attachment-filled',
+  'tdesign:file-blocked',
+  'tdesign:file-blocked-filled',
+  'tdesign:file-code',
+  'tdesign:file-code-1',
+  'tdesign:file-code-1-filled',
+  'tdesign:file-code-filled',
+  'tdesign:file-copy',
+  'tdesign:file-copy-filled',
+  'tdesign:file-download',
+  'tdesign:file-download-filled',
+  'tdesign:file-excel',
+  'tdesign:file-excel-filled',
+  'tdesign:file-export',
+  'tdesign:file-export-filled',
+  'tdesign:file-filled',
+  'tdesign:file-icon',
+  'tdesign:file-icon-filled',
+  'tdesign:file-image',
+  'tdesign:file-image-filled',
+  'tdesign:file-import',
+  'tdesign:file-import-filled',
+  'tdesign:file-locked',
+  'tdesign:file-locked-filled',
+  'tdesign:file-minus',
+  'tdesign:file-minus-filled',
+  'tdesign:file-music',
+  'tdesign:file-music-filled',
+  'tdesign:file-onenote',
+  'tdesign:file-onenote-filled',
+  'tdesign:file-outlook',
+  'tdesign:file-outlook-filled',
+  'tdesign:file-paste',
+  'tdesign:file-paste-filled',
+  'tdesign:file-pdf',
+  'tdesign:file-pdf-filled',
+  'tdesign:file-powerpoint',
+  'tdesign:file-powerpoint-filled',
+  'tdesign:file-restore',
+  'tdesign:file-restore-filled',
+  'tdesign:file-safety',
+  'tdesign:file-safety-filled',
+  'tdesign:file-search',
+  'tdesign:file-search-filled',
+  'tdesign:file-setting',
+  'tdesign:file-setting-filled',
+  'tdesign:file-teams',
+  'tdesign:file-teams-filled',
+  'tdesign:file-transmit',
+  'tdesign:file-transmit-double',
+  'tdesign:file-transmit-double-filled',
+  'tdesign:file-transmit-filled',
+  'tdesign:file-unknown',
+  'tdesign:file-unknown-filled',
+  'tdesign:file-unlocked',
+  'tdesign:file-unlocked-filled',
+  'tdesign:file-word',
+  'tdesign:file-word-filled',
+  'tdesign:file-zip',
+  'tdesign:file-zip-filled',
+  'tdesign:fill-color',
+  'tdesign:fill-color-1',
+  'tdesign:fill-color-1-filled',
+  'tdesign:fill-color-filled',
+  'tdesign:film',
+  'tdesign:film-1',
+  'tdesign:film-1-filled',
+  'tdesign:film-filled',
+  'tdesign:filter',
+  'tdesign:filter-1',
+  'tdesign:filter-1-filled',
+  'tdesign:filter-2',
+  'tdesign:filter-2-filled',
+  'tdesign:filter-3',
+  'tdesign:filter-3-filled',
+  'tdesign:filter-clear',
+  'tdesign:filter-clear-filled',
+  'tdesign:filter-filled',
+  'tdesign:filter-off',
+  'tdesign:filter-off-filled',
+  'tdesign:filter-sort',
+  'tdesign:filter-sort-filled',
+  'tdesign:fingerprint',
+  'tdesign:fingerprint-1',
+  'tdesign:fingerprint-2',
+  'tdesign:fingerprint-3',
+  'tdesign:fish',
+  'tdesign:fish-filled',
+  'tdesign:flag',
+  'tdesign:flag-1',
+  'tdesign:flag-1-filled',
+  'tdesign:flag-2',
+  'tdesign:flag-2-filled',
+  'tdesign:flag-3',
+  'tdesign:flag-3-filled',
+  'tdesign:flag-4',
+  'tdesign:flag-4-filled',
+  'tdesign:flag-filled',
+  'tdesign:flashlight',
+  'tdesign:flashlight-filled',
+  'tdesign:flight-landing',
+  'tdesign:flight-landing-filled',
+  'tdesign:flight-takeoff',
+  'tdesign:flight-takeoff-filled',
+  'tdesign:flip-smiling-face',
+  'tdesign:flip-smiling-face-filled',
+  'tdesign:flip-to-back',
+  'tdesign:flip-to-back-filled',
+  'tdesign:flip-to-front',
+  'tdesign:flip-to-front-filled',
+  'tdesign:focus',
+  'tdesign:focus-filled',
+  'tdesign:fog',
+  'tdesign:fog-filled',
+  'tdesign:fog-night',
+  'tdesign:fog-night-filled',
+  'tdesign:fog-sunny',
+  'tdesign:fog-sunny-filled',
+  'tdesign:folder',
+  'tdesign:folder-1',
+  'tdesign:folder-1-filled',
+  'tdesign:folder-add',
+  'tdesign:folder-add-1',
+  'tdesign:folder-add-1-filled',
+  'tdesign:folder-add-filled',
+  'tdesign:folder-blocked',
+  'tdesign:folder-blocked-filled',
+  'tdesign:folder-details',
+  'tdesign:folder-details-filled',
+  'tdesign:folder-export',
+  'tdesign:folder-export-filled',
+  'tdesign:folder-filled',
+  'tdesign:folder-import',
+  'tdesign:folder-import-filled',
+  'tdesign:folder-locked',
+  'tdesign:folder-locked-filled',
+  'tdesign:folder-minus',
+  'tdesign:folder-minus-filled',
+  'tdesign:folder-move',
+  'tdesign:folder-move-filled',
+  'tdesign:folder-off',
+  'tdesign:folder-off-filled',
+  'tdesign:folder-open',
+  'tdesign:folder-open-1',
+  'tdesign:folder-open-1-filled',
+  'tdesign:folder-open-filled',
+  'tdesign:folder-search',
+  'tdesign:folder-search-filled',
+  'tdesign:folder-setting',
+  'tdesign:folder-setting-filled',
+  'tdesign:folder-shared',
+  'tdesign:folder-shared-filled',
+  'tdesign:folder-unlocked',
+  'tdesign:folder-unlocked-filled',
+  'tdesign:folder-zip',
+  'tdesign:folder-zip-filled',
+  'tdesign:forest',
+  'tdesign:forest-filled',
+  'tdesign:fork',
+  'tdesign:fork-filled',
+  'tdesign:form',
+  'tdesign:form-filled',
+  'tdesign:format-horizontal-align-bottom',
+  'tdesign:format-horizontal-align-center',
+  'tdesign:format-horizontal-align-top',
+  'tdesign:format-vertical-align-center',
+  'tdesign:format-vertical-align-left',
+  'tdesign:format-vertical-align-right',
+  'tdesign:forward',
+  'tdesign:forward-filled',
+  'tdesign:frame',
+  'tdesign:frame-1',
+  'tdesign:frame-1-filled',
+  'tdesign:frame-filled',
+  'tdesign:fries',
+  'tdesign:fries-filled',
+  'tdesign:fullscreen',
+  'tdesign:fullscreen-1',
+  'tdesign:fullscreen-2',
+  'tdesign:fullscreen-exit',
+  'tdesign:fullscreen-exit-1',
+  'tdesign:function-curve',
+  'tdesign:functions',
+  'tdesign:functions-1',
+  'tdesign:gamepad',
+  'tdesign:gamepad-1',
+  'tdesign:gamepad-1-filled',
+  'tdesign:gamepad-filled',
+  'tdesign:gamma',
+  'tdesign:garlic',
+  'tdesign:garlic-filled',
+  'tdesign:gender-female',
+  'tdesign:gender-male',
+  'tdesign:gesture-applause',
+  'tdesign:gesture-applause-filled',
+  'tdesign:gesture-click',
+  'tdesign:gesture-click-filled',
+  'tdesign:gesture-down',
+  'tdesign:gesture-down-filled',
+  'tdesign:gesture-expansion',
+  'tdesign:gesture-expansion-filled',
+  'tdesign:gesture-left',
+  'tdesign:gesture-left-filled',
+  'tdesign:gesture-left-slip',
+  'tdesign:gesture-left-slip-filled',
+  'tdesign:gesture-open',
+  'tdesign:gesture-open-filled',
+  'tdesign:gesture-pray',
+  'tdesign:gesture-pray-1',
+  'tdesign:gesture-pray-filled',
+  'tdesign:gesture-press',
+  'tdesign:gesture-press-filled',
+  'tdesign:gesture-ranslation',
+  'tdesign:gesture-ranslation-1',
+  'tdesign:gesture-ranslation-filled',
+  'tdesign:gesture-right',
+  'tdesign:gesture-right-filled',
+  'tdesign:gesture-right-slip',
+  'tdesign:gesture-right-slip-filled',
+  'tdesign:gesture-slide-left-and-right',
+  'tdesign:gesture-slide-left-and-right-filled',
+  'tdesign:gesture-slide-up',
+  'tdesign:gesture-slide-up-filled',
+  'tdesign:gesture-typing',
+  'tdesign:gesture-typing-filled',
+  'tdesign:gesture-up',
+  'tdesign:gesture-up-2',
+  'tdesign:gesture-up-and-down',
+  'tdesign:gesture-up-and-down-filled',
+  'tdesign:gesture-up-filled',
+  'tdesign:gesture-wipe-down',
+  'tdesign:gesture-wipe-down-filled',
+  'tdesign:gift',
+  'tdesign:gift-filled',
+  'tdesign:giggle',
+  'tdesign:giggle-filled',
+  'tdesign:git-branch',
+  'tdesign:git-branch-filled',
+  'tdesign:git-commit',
+  'tdesign:git-commit-filled',
+  'tdesign:git-merge',
+  'tdesign:git-merge-filled',
+  'tdesign:git-pull-request',
+  'tdesign:git-pull-request-filled',
+  'tdesign:git-repository',
+  'tdesign:git-repository-commits',
+  'tdesign:git-repository-commits-filled',
+  'tdesign:git-repository-filled',
+  'tdesign:git-repository-private',
+  'tdesign:git-repository-private-filled',
+  'tdesign:gps',
+  'tdesign:gps-filled',
+  'tdesign:grape',
+  'tdesign:grape-filled',
+  'tdesign:greater-than',
+  'tdesign:greater-than-or-equal',
+  'tdesign:green-onion',
+  'tdesign:grid-add',
+  'tdesign:grid-add-filled',
+  'tdesign:grid-view',
+  'tdesign:grid-view-filled',
+  'tdesign:guitar',
+  'tdesign:guitar-filled',
+  'tdesign:hamburger',
+  'tdesign:hamburger-filled',
+  'tdesign:happy',
+  'tdesign:happy-filled',
+  'tdesign:hard-disk-storage',
+  'tdesign:hard-disk-storage-filled',
+  'tdesign:hard-drive',
+  'tdesign:hard-drive-filled',
+  'tdesign:hashtag',
+  'tdesign:hd',
+  'tdesign:hd-filled',
+  'tdesign:heart',
+  'tdesign:heart-filled',
+  'tdesign:help',
+  'tdesign:help-circle',
+  'tdesign:help-circle-filled',
+  'tdesign:help-rectangle',
+  'tdesign:help-rectangle-filled',
+  'tdesign:highlight',
+  'tdesign:highlight-1',
+  'tdesign:highlight-1-filled',
+  'tdesign:history',
+  'tdesign:history-setting',
+  'tdesign:home',
+  'tdesign:home-filled',
+  'tdesign:horizontal',
+  'tdesign:horizontal-filled',
+  'tdesign:hospital',
+  'tdesign:hospital-1',
+  'tdesign:hospital-1-filled',
+  'tdesign:hospital-filled',
+  'tdesign:hotspot-wave',
+  'tdesign:hotspot-wave-filled',
+  'tdesign:hourglass',
+  'tdesign:hourglass-filled',
+  'tdesign:houses',
+  'tdesign:houses-1',
+  'tdesign:houses-1-filled',
+  'tdesign:houses-2',
+  'tdesign:houses-2-filled',
+  'tdesign:houses-filled',
+  'tdesign:html5',
+  'tdesign:html5-filled',
+  'tdesign:https',
+  'tdesign:https-filled',
+  'tdesign:ice-cream',
+  'tdesign:ice-cream-filled',
+  'tdesign:icon',
+  'tdesign:icon-filled',
+  'tdesign:image',
+  'tdesign:image-1',
+  'tdesign:image-1-filled',
+  'tdesign:image-add',
+  'tdesign:image-add-filled',
+  'tdesign:image-edit',
+  'tdesign:image-edit-filled',
+  'tdesign:image-error',
+  'tdesign:image-error-filled',
+  'tdesign:image-filled',
+  'tdesign:image-off',
+  'tdesign:image-off-filled',
+  'tdesign:image-search',
+  'tdesign:image-search-filled',
+  'tdesign:indent-left',
+  'tdesign:indent-right',
+  'tdesign:indicator',
+  'tdesign:indicator-filled',
+  'tdesign:info-circle',
+  'tdesign:info-circle-filled',
+  'tdesign:ink',
+  'tdesign:ink-filled',
+  'tdesign:install',
+  'tdesign:install-desktop',
+  'tdesign:install-desktop-filled',
+  'tdesign:install-filled',
+  'tdesign:install-mobile',
+  'tdesign:install-mobile-filled',
+  'tdesign:institution',
+  'tdesign:institution-checked',
+  'tdesign:institution-checked-filled',
+  'tdesign:institution-filled',
+  'tdesign:internet',
+  'tdesign:internet-filled',
+  'tdesign:ipod',
+  'tdesign:ipod-filled',
+  'tdesign:joyful',
+  'tdesign:joyful-filled',
+  'tdesign:jump',
+  'tdesign:jump-double',
+  'tdesign:jump-off',
+  'tdesign:key',
+  'tdesign:key-filled',
+  'tdesign:keyboard',
+  'tdesign:keyboard-filled',
+  'tdesign:laptop',
+  'tdesign:laptop-filled',
+  'tdesign:layers',
+  'tdesign:layers-filled',
+  'tdesign:layout',
+  'tdesign:layout-filled',
+  'tdesign:leaderboard',
+  'tdesign:leaderboard-filled',
+  'tdesign:lemon',
+  'tdesign:lemon-filled',
+  'tdesign:lemon-slice',
+  'tdesign:lemon-slice-filled',
+  'tdesign:less-than',
+  'tdesign:less-than-or-equal',
+  'tdesign:letters-a',
+  'tdesign:letters-b',
+  'tdesign:letters-c',
+  'tdesign:letters-d',
+  'tdesign:letters-e',
+  'tdesign:letters-f',
+  'tdesign:letters-g',
+  'tdesign:letters-h',
+  'tdesign:letters-i',
+  'tdesign:letters-j',
+  'tdesign:letters-k',
+  'tdesign:letters-l',
+  'tdesign:letters-m',
+  'tdesign:letters-n',
+  'tdesign:letters-o',
+  'tdesign:letters-p',
+  'tdesign:letters-q',
+  'tdesign:letters-r',
+  'tdesign:letters-s',
+  'tdesign:letters-t',
+  'tdesign:letters-u',
+  'tdesign:letters-v',
+  'tdesign:letters-w',
+  'tdesign:letters-x',
+  'tdesign:letters-y',
+  'tdesign:letters-z',
+  'tdesign:lightbulb',
+  'tdesign:lightbulb-circle',
+  'tdesign:lightbulb-circle-filled',
+  'tdesign:lightbulb-filled',
+  'tdesign:lighthouse',
+  'tdesign:lighthouse-1',
+  'tdesign:lighthouse-1-filled',
+  'tdesign:lighthouse-2',
+  'tdesign:lighthouse-2-filled',
+  'tdesign:lighthouse-filled',
+  'tdesign:lighting-circle',
+  'tdesign:lighting-circle-filled',
+  'tdesign:line-height',
+  'tdesign:link',
+  'tdesign:link-1',
+  'tdesign:link-unlink',
+  'tdesign:liquor',
+  'tdesign:liquor-filled',
+  'tdesign:list',
+  'tdesign:list-numbered',
+  'tdesign:load',
+  'tdesign:loading',
+  'tdesign:location',
+  'tdesign:location-1',
+  'tdesign:location-1-filled',
+  'tdesign:location-enlargement',
+  'tdesign:location-enlargement-filled',
+  'tdesign:location-error',
+  'tdesign:location-error-filled',
+  'tdesign:location-filled',
+  'tdesign:location-parking-place',
+  'tdesign:location-parking-place-filled',
+  'tdesign:location-reduction',
+  'tdesign:location-reduction-filled',
+  'tdesign:location-setting',
+  'tdesign:location-setting-filled',
+  'tdesign:lock-off',
+  'tdesign:lock-off-filled',
+  'tdesign:lock-on',
+  'tdesign:lock-on-filled',
+  'tdesign:lock-time',
+  'tdesign:lock-time-filled',
+  'tdesign:login',
+  'tdesign:logo-adobe-illustrate',
+  'tdesign:logo-adobe-illustrate-filled',
+  'tdesign:logo-adobe-lightroom',
+  'tdesign:logo-adobe-lightroom-filled',
+  'tdesign:logo-adobe-photoshop',
+  'tdesign:logo-adobe-photoshop-filled',
+  'tdesign:logo-alipay',
+  'tdesign:logo-alipay-filled',
+  'tdesign:logo-android',
+  'tdesign:logo-android-filled',
+  'tdesign:logo-apple',
+  'tdesign:logo-apple-filled',
+  'tdesign:logo-behance',
+  'tdesign:logo-behance-filled',
+  'tdesign:logo-chrome',
+  'tdesign:logo-chrome-filled',
+  'tdesign:logo-cinema4d',
+  'tdesign:logo-cinema4d-filled',
+  'tdesign:logo-cnb',
+  'tdesign:logo-cnb-filled',
+  'tdesign:logo-codepen',
+  'tdesign:logo-codesandbox',
+  'tdesign:logo-dribbble',
+  'tdesign:logo-dribbble-filled',
+  'tdesign:logo-facebook',
+  'tdesign:logo-facebook-filled',
+  'tdesign:logo-figma',
+  'tdesign:logo-figma-filled',
+  'tdesign:logo-framer',
+  'tdesign:logo-framer-filled',
+  'tdesign:logo-github',
+  'tdesign:logo-github-filled',
+  'tdesign:logo-gitlab',
+  'tdesign:logo-gitlab-filled',
+  'tdesign:logo-ie',
+  'tdesign:logo-ie-filled',
+  'tdesign:logo-instagram',
+  'tdesign:logo-instagram-filled',
+  'tdesign:logo-miniprogram',
+  'tdesign:logo-miniprogram-filled',
+  'tdesign:logo-qq',
+  'tdesign:logo-qq-filled',
+  'tdesign:logo-twitter',
+  'tdesign:logo-twitter-filled',
+  'tdesign:logo-wechat',
+  'tdesign:logo-wechat-stroke',
+  'tdesign:logo-wechat-stroke-filled',
+  'tdesign:logo-wechatpay',
+  'tdesign:logo-wechatpay-filled',
+  'tdesign:logo-wecom',
+  'tdesign:logo-wecom-filled',
+  'tdesign:logo-windows',
+  'tdesign:logo-windows-filled',
+  'tdesign:logo-youtube',
+  'tdesign:logo-youtube-filled',
+  'tdesign:logout',
+  'tdesign:look-around',
+  'tdesign:look-around-filled',
+  'tdesign:loudspeaker',
+  'tdesign:loudspeaker-filled',
+  'tdesign:mail',
+  'tdesign:mail-filled',
+  'tdesign:map',
+  'tdesign:map-3d',
+  'tdesign:map-3d-filled',
+  'tdesign:map-add',
+  'tdesign:map-add-filled',
+  'tdesign:map-aiming',
+  'tdesign:map-aiming-filled',
+  'tdesign:map-blocked',
+  'tdesign:map-blocked-filled',
+  'tdesign:map-bubble',
+  'tdesign:map-bubble-filled',
+  'tdesign:map-cancel',
+  'tdesign:map-cancel-filled',
+  'tdesign:map-chat',
+  'tdesign:map-chat-filled',
+  'tdesign:map-checked',
+  'tdesign:map-checked-filled',
+  'tdesign:map-collection',
+  'tdesign:map-collection-filled',
+  'tdesign:map-connection',
+  'tdesign:map-connection-filled',
+  'tdesign:map-distance',
+  'tdesign:map-distance-filled',
+  'tdesign:map-double',
+  'tdesign:map-double-filled',
+  'tdesign:map-edit',
+  'tdesign:map-edit-filled',
+  'tdesign:map-filled',
+  'tdesign:map-grid',
+  'tdesign:map-grid-filled',
+  'tdesign:map-information',
+  'tdesign:map-information-1',
+  'tdesign:map-information-1-filled',
+  'tdesign:map-information-2',
+  'tdesign:map-information-2-filled',
+  'tdesign:map-information-filled',
+  'tdesign:map-location',
+  'tdesign:map-location-filled',
+  'tdesign:map-locked',
+  'tdesign:map-locked-filled',
+  'tdesign:map-marked',
+  'tdesign:map-marked-filled',
+  'tdesign:map-navigation',
+  'tdesign:map-navigation-filled',
+  'tdesign:map-outline',
+  'tdesign:map-outline-filled',
+  'tdesign:map-route-planning',
+  'tdesign:map-route-planning-filled',
+  'tdesign:map-ruler',
+  'tdesign:map-ruler-filled',
+  'tdesign:map-safety',
+  'tdesign:map-safety-filled',
+  'tdesign:map-search',
+  'tdesign:map-search-1',
+  'tdesign:map-search-1-filled',
+  'tdesign:map-search-filled',
+  'tdesign:map-setting',
+  'tdesign:map-setting-filled',
+  'tdesign:map-unlocked',
+  'tdesign:map-unlocked-filled',
+  'tdesign:mark-as-unread',
+  'tdesign:mark-as-unread-filled',
+  'tdesign:markup',
+  'tdesign:markup-filled',
+  'tdesign:mathematics',
+  'tdesign:mathematics-filled',
+  'tdesign:measurement',
+  'tdesign:measurement-1',
+  'tdesign:measurement-1-filled',
+  'tdesign:measurement-2',
+  'tdesign:measurement-2-filled',
+  'tdesign:measurement-filled',
+  'tdesign:meat-pepper',
+  'tdesign:meat-pepper-filled',
+  'tdesign:media-library',
+  'tdesign:media-library-filled',
+  'tdesign:member',
+  'tdesign:member-filled',
+  'tdesign:menu',
+  'tdesign:menu-application',
+  'tdesign:menu-filled',
+  'tdesign:menu-fold',
+  'tdesign:menu-unfold',
+  'tdesign:merge-cells',
+  'tdesign:merge-cells-filled',
+  'tdesign:microphone',
+  'tdesign:microphone-1',
+  'tdesign:microphone-1-filled',
+  'tdesign:microphone-2',
+  'tdesign:microphone-2-filled',
+  'tdesign:microphone-filled',
+  'tdesign:milk',
+  'tdesign:milk-filled',
+  'tdesign:minus',
+  'tdesign:minus-circle',
+  'tdesign:minus-circle-filled',
+  'tdesign:minus-rectangle',
+  'tdesign:minus-rectangle-filled',
+  'tdesign:mirror',
+  'tdesign:mirror-filled',
+  'tdesign:mobile',
+  'tdesign:mobile-blocked',
+  'tdesign:mobile-blocked-filled',
+  'tdesign:mobile-filled',
+  'tdesign:mobile-list',
+  'tdesign:mobile-list-filled',
+  'tdesign:mobile-navigation',
+  'tdesign:mobile-navigation-filled',
+  'tdesign:mobile-shortcut',
+  'tdesign:mobile-shortcut-filled',
+  'tdesign:mobile-vibrate',
+  'tdesign:mobile-vibrate-filled',
+  'tdesign:mode-dark',
+  'tdesign:mode-dark-filled',
+  'tdesign:mode-light',
+  'tdesign:mode-light-filled',
+  'tdesign:module',
+  'tdesign:module-filled',
+  'tdesign:money',
+  'tdesign:money-filled',
+  'tdesign:monument',
+  'tdesign:monument-filled',
+  'tdesign:moon',
+  'tdesign:moon-fall',
+  'tdesign:moon-fall-filled',
+  'tdesign:moon-filled',
+  'tdesign:moon-rising',
+  'tdesign:moon-rising-filled',
+  'tdesign:more',
+  'tdesign:mosque',
+  'tdesign:mosque-1',
+  'tdesign:mosque-1-filled',
+  'tdesign:mosque-filled',
+  'tdesign:mouse',
+  'tdesign:mouse-filled',
+  'tdesign:move',
+  'tdesign:move-1',
+  'tdesign:movie-clapper',
+  'tdesign:movie-clapper-filled',
+  'tdesign:multiply',
+  'tdesign:museum',
+  'tdesign:museum-1',
+  'tdesign:museum-1-filled',
+  'tdesign:museum-2',
+  'tdesign:museum-2-filled',
+  'tdesign:museum-filled',
+  'tdesign:mushroom',
+  'tdesign:mushroom-1',
+  'tdesign:mushroom-1-filled',
+  'tdesign:mushroom-filled',
+  'tdesign:music',
+  'tdesign:music-1',
+  'tdesign:music-1-filled',
+  'tdesign:music-2',
+  'tdesign:music-2-filled',
+  'tdesign:music-filled',
+  'tdesign:music-rectangle-add',
+  'tdesign:music-rectangle-add-filled',
+  'tdesign:navigation-arrow',
+  'tdesign:navigation-arrow-filled',
+  'tdesign:next',
+  'tdesign:next-filled',
+  'tdesign:no-expression',
+  'tdesign:no-expression-filled',
+  'tdesign:noodle',
+  'tdesign:noodle-filled',
+  'tdesign:notification',
+  'tdesign:notification-add',
+  'tdesign:notification-add-filled',
+  'tdesign:notification-circle',
+  'tdesign:notification-circle-filled',
+  'tdesign:notification-error',
+  'tdesign:notification-error-filled',
+  'tdesign:notification-filled',
+  'tdesign:numbers-0',
+  'tdesign:numbers-0-1',
+  'tdesign:numbers-1',
+  'tdesign:numbers-1-1',
+  'tdesign:numbers-2',
+  'tdesign:numbers-2-1',
+  'tdesign:numbers-3',
+  'tdesign:numbers-3-1',
+  'tdesign:numbers-4',
+  'tdesign:numbers-4-1',
+  'tdesign:numbers-5',
+  'tdesign:numbers-5-1',
+  'tdesign:numbers-6',
+  'tdesign:numbers-6-1',
+  'tdesign:numbers-7',
+  'tdesign:numbers-7-1',
+  'tdesign:numbers-8',
+  'tdesign:numbers-8-1',
+  'tdesign:numbers-9',
+  'tdesign:numbers-9-1',
+  'tdesign:nut',
+  'tdesign:nut-filled',
+  'tdesign:object-storage',
+  'tdesign:open-mouth',
+  'tdesign:open-mouth-filled',
+  'tdesign:opera',
+  'tdesign:opera-filled',
+  'tdesign:order-adjustment-column',
+  'tdesign:order-ascending',
+  'tdesign:order-descending',
+  'tdesign:outbox',
+  'tdesign:outbox-filled',
+  'tdesign:page-first',
+  'tdesign:page-head',
+  'tdesign:page-head-filled',
+  'tdesign:page-last',
+  'tdesign:palace',
+  'tdesign:palace-1',
+  'tdesign:palace-1-filled',
+  'tdesign:palace-2',
+  'tdesign:palace-2-filled',
+  'tdesign:palace-3',
+  'tdesign:palace-3-filled',
+  'tdesign:palace-4',
+  'tdesign:palace-4-filled',
+  'tdesign:palace-filled',
+  'tdesign:palette',
+  'tdesign:palette-1',
+  'tdesign:palette-1-filled',
+  'tdesign:palette-filled',
+  'tdesign:panorama-horizontal',
+  'tdesign:panorama-horizontal-filled',
+  'tdesign:panorama-vertical',
+  'tdesign:panorama-vertical-filled',
+  'tdesign:pantone',
+  'tdesign:pantone-filled',
+  'tdesign:parabola',
+  'tdesign:parentheses',
+  'tdesign:paste',
+  'tdesign:paste-filled',
+  'tdesign:patio',
+  'tdesign:patio-filled',
+  'tdesign:pause',
+  'tdesign:pause-circle',
+  'tdesign:pause-circle-filled',
+  'tdesign:pause-circle-stroke',
+  'tdesign:pause-circle-stroke-filled',
+  'tdesign:pea',
+  'tdesign:pea-filled',
+  'tdesign:peach',
+  'tdesign:peach-filled',
+  'tdesign:pear',
+  'tdesign:pear-filled',
+  'tdesign:pearl-of-the-orient',
+  'tdesign:pearl-of-the-orient-filled',
+  'tdesign:pen',
+  'tdesign:pen-ball',
+  'tdesign:pen-ball-filled',
+  'tdesign:pen-brush',
+  'tdesign:pen-brush-filled',
+  'tdesign:pen-filled',
+  'tdesign:pen-mark',
+  'tdesign:pen-mark-filled',
+  'tdesign:pen-quill',
+  'tdesign:pen-quill-filled',
+  'tdesign:pending',
+  'tdesign:pending-filled',
+  'tdesign:percent',
+  'tdesign:personal-information',
+  'tdesign:personal-information-filled',
+  'tdesign:phone-locked',
+  'tdesign:phone-locked-filled',
+  'tdesign:phone-search',
+  'tdesign:phone-search-filled',
+  'tdesign:pi',
+  'tdesign:piano',
+  'tdesign:piano-filled',
+  'tdesign:pin',
+  'tdesign:pin-filled',
+  'tdesign:play',
+  'tdesign:play-circle',
+  'tdesign:play-circle-filled',
+  'tdesign:play-circle-stroke',
+  'tdesign:play-circle-stroke-add',
+  'tdesign:play-circle-stroke-add-filled',
+  'tdesign:play-circle-stroke-filled',
+  'tdesign:play-demo',
+  'tdesign:play-demo-filled',
+  'tdesign:play-rectangle',
+  'tdesign:play-rectangle-filled',
+  'tdesign:plus',
+  'tdesign:popsicle',
+  'tdesign:popsicle-filled',
+  'tdesign:portrait',
+  'tdesign:portrait-filled',
+  'tdesign:pout',
+  'tdesign:pout-filled',
+  'tdesign:poweroff',
+  'tdesign:precise-monitor',
+  'tdesign:previous',
+  'tdesign:previous-filled',
+  'tdesign:print',
+  'tdesign:print-filled',
+  'tdesign:pumpkin',
+  'tdesign:pumpkin-filled',
+  'tdesign:pyramid',
+  'tdesign:pyramid-filled',
+  'tdesign:pyramid-maya',
+  'tdesign:pyramid-maya-filled',
+  'tdesign:qrcode',
+  'tdesign:quadratic',
+  'tdesign:questionnaire',
+  'tdesign:questionnaire-double',
+  'tdesign:questionnaire-double-filled',
+  'tdesign:questionnaire-filled',
+  'tdesign:queue',
+  'tdesign:queue-filled',
+  'tdesign:quote',
+  'tdesign:quote-filled',
+  'tdesign:radar',
+  'tdesign:radio-1',
+  'tdesign:radio-1-filled',
+  'tdesign:radio-2',
+  'tdesign:radio-2-filled',
+  'tdesign:radish',
+  'tdesign:radish-filled',
+  'tdesign:rain-heavy',
+  'tdesign:rain-light',
+  'tdesign:rain-light-filled',
+  'tdesign:rain-medium',
+  'tdesign:rainbow',
+  'tdesign:rectangle',
+  'tdesign:rectangle-filled',
+  'tdesign:refresh',
+  'tdesign:relation',
+  'tdesign:relativity',
+  'tdesign:relativity-filled',
+  'tdesign:remote-wave',
+  'tdesign:remote-wave-filled',
+  'tdesign:remove',
+  'tdesign:replay',
+  'tdesign:replay-filled',
+  'tdesign:rice',
+  'tdesign:rice-ball',
+  'tdesign:rice-ball-filled',
+  'tdesign:rice-filled',
+  'tdesign:roast',
+  'tdesign:roast-filled',
+  'tdesign:rocket',
+  'tdesign:rocket-filled',
+  'tdesign:rollback',
+  'tdesign:rollfront',
+  'tdesign:root-list',
+  'tdesign:root-list-filled',
+  'tdesign:rotate',
+  'tdesign:rotate-locked',
+  'tdesign:rotate-locked-filled',
+  'tdesign:rotation',
+  'tdesign:round',
+  'tdesign:round-filled',
+  'tdesign:router-wave',
+  'tdesign:router-wave-filled',
+  'tdesign:rss',
+  'tdesign:ruler',
+  'tdesign:ruler-filled',
+  'tdesign:sailing-hotel',
+  'tdesign:sailing-hotel-filled',
+  'tdesign:sandwich',
+  'tdesign:sandwich-filled',
+  'tdesign:saturation',
+  'tdesign:saturation-filled',
+  'tdesign:sausage',
+  'tdesign:sausage-filled',
+  'tdesign:save',
+  'tdesign:save-filled',
+  'tdesign:saving-pot',
+  'tdesign:saving-pot-filled',
+  'tdesign:scan',
+  'tdesign:screen-4k',
+  'tdesign:screen-4k-filled',
+  'tdesign:screencast',
+  'tdesign:screencast-filled',
+  'tdesign:screenshot',
+  'tdesign:scroll-bar',
+  'tdesign:scroll-bar-filled',
+  'tdesign:sd-card',
+  'tdesign:sd-card-1',
+  'tdesign:sd-card-1-filled',
+  'tdesign:sd-card-filled',
+  'tdesign:seal',
+  'tdesign:seal-filled',
+  'tdesign:search',
+  'tdesign:search-error',
+  'tdesign:search-error-filled',
+  'tdesign:search-filled',
+  'tdesign:secured',
+  'tdesign:secured-filled',
+  'tdesign:send',
+  'tdesign:send-cancel',
+  'tdesign:send-cancel-filled',
+  'tdesign:send-filled',
+  'tdesign:sensors',
+  'tdesign:sensors-1',
+  'tdesign:sensors-2',
+  'tdesign:sensors-off',
+  'tdesign:sequence',
+  'tdesign:sequence-filled',
+  'tdesign:serenity',
+  'tdesign:serenity-filled',
+  'tdesign:server',
+  'tdesign:server-filled',
+  'tdesign:service',
+  'tdesign:service-filled',
+  'tdesign:setting',
+  'tdesign:setting-1',
+  'tdesign:setting-1-filled',
+  'tdesign:setting-filled',
+  'tdesign:share',
+  'tdesign:share-1',
+  'tdesign:share-1-filled',
+  'tdesign:share-filled',
+  'tdesign:sharpness',
+  'tdesign:sharpness-filled',
+  'tdesign:shield-error',
+  'tdesign:shield-error-filled',
+  'tdesign:shimen',
+  'tdesign:shimen-filled',
+  'tdesign:shop',
+  'tdesign:shop-1',
+  'tdesign:shop-1-filled',
+  'tdesign:shop-2',
+  'tdesign:shop-2-filled',
+  'tdesign:shop-3',
+  'tdesign:shop-3-filled',
+  'tdesign:shop-4',
+  'tdesign:shop-4-filled',
+  'tdesign:shop-5',
+  'tdesign:shop-5-filled',
+  'tdesign:shop-filled',
+  'tdesign:shrimp',
+  'tdesign:shrimp-filled',
+  'tdesign:shrink-horizontal',
+  'tdesign:shrink-vertical',
+  'tdesign:shutter',
+  'tdesign:shutter-filled',
+  'tdesign:shutup',
+  'tdesign:shutup-filled',
+  'tdesign:sim-card',
+  'tdesign:sim-card-1',
+  'tdesign:sim-card-1-filled',
+  'tdesign:sim-card-2',
+  'tdesign:sim-card-2-filled',
+  'tdesign:sim-card-filled',
+  'tdesign:sinister-smile',
+  'tdesign:sinister-smile-filled',
+  'tdesign:sip',
+  'tdesign:sip-filled',
+  'tdesign:sitemap',
+  'tdesign:sitemap-filled',
+  'tdesign:slash',
+  'tdesign:sleep',
+  'tdesign:sleep-filled',
+  'tdesign:slice',
+  'tdesign:slice-filled',
+  'tdesign:slideshow',
+  'tdesign:slideshow-filled',
+  'tdesign:smile',
+  'tdesign:smile-filled',
+  'tdesign:sneer',
+  'tdesign:sneer-filled',
+  'tdesign:snowflake',
+  'tdesign:sonic',
+  'tdesign:sound',
+  'tdesign:sound-down',
+  'tdesign:sound-down-filled',
+  'tdesign:sound-filled',
+  'tdesign:sound-high',
+  'tdesign:sound-high-filled',
+  'tdesign:sound-low',
+  'tdesign:sound-low-filled',
+  'tdesign:sound-mute',
+  'tdesign:sound-mute-1',
+  'tdesign:sound-mute-1-filled',
+  'tdesign:sound-mute-filled',
+  'tdesign:sound-up',
+  'tdesign:sound-up-filled',
+  'tdesign:space',
+  'tdesign:speechless',
+  'tdesign:speechless-1',
+  'tdesign:speechless-1-filled',
+  'tdesign:speechless-filled',
+  'tdesign:star',
+  'tdesign:star-filled',
+  'tdesign:statue-of-jesus',
+  'tdesign:statue-of-jesus-filled',
+  'tdesign:sticky-note',
+  'tdesign:sticky-note-filled',
+  'tdesign:stop',
+  'tdesign:stop-circle',
+  'tdesign:stop-circle-filled',
+  'tdesign:stop-circle-stroke',
+  'tdesign:stop-circle-stroke-filled',
+  'tdesign:store',
+  'tdesign:store-filled',
+  'tdesign:street-road',
+  'tdesign:street-road-1',
+  'tdesign:street-road-1-filled',
+  'tdesign:street-road-filled',
+  'tdesign:subtitle',
+  'tdesign:subtitle-filled',
+  'tdesign:subway-line',
+  'tdesign:subway-line-filled',
+  'tdesign:sum',
+  'tdesign:sun-fall',
+  'tdesign:sun-fall-filled',
+  'tdesign:sun-rising',
+  'tdesign:sun-rising-filled',
+  'tdesign:sunny',
+  'tdesign:sunny-filled',
+  'tdesign:support',
+  'tdesign:support-filled',
+  'tdesign:surprised',
+  'tdesign:surprised-1',
+  'tdesign:surprised-1-filled',
+  'tdesign:surprised-filled',
+  'tdesign:swap',
+  'tdesign:swap-left',
+  'tdesign:swap-right',
+  'tdesign:swear-1',
+  'tdesign:swear-1-filled',
+  'tdesign:swear-2',
+  'tdesign:swear-2-filled',
+  'tdesign:system-2',
+  'tdesign:system-3',
+  'tdesign:system-3-filled',
+  'tdesign:system-application',
+  'tdesign:system-application-filled',
+  'tdesign:system-blocked',
+  'tdesign:system-blocked-filled',
+  'tdesign:system-code',
+  'tdesign:system-code-filled',
+  'tdesign:system-components',
+  'tdesign:system-components-filled',
+  'tdesign:system-coordinate',
+  'tdesign:system-coordinate-filled',
+  'tdesign:system-device',
+  'tdesign:system-device-filled',
+  'tdesign:system-interface',
+  'tdesign:system-interface-filled',
+  'tdesign:system-location',
+  'tdesign:system-location-filled',
+  'tdesign:system-locked',
+  'tdesign:system-locked-filled',
+  'tdesign:system-log',
+  'tdesign:system-log-filled',
+  'tdesign:system-marked',
+  'tdesign:system-marked-filled',
+  'tdesign:system-messages',
+  'tdesign:system-messages-filled',
+  'tdesign:system-regulation',
+  'tdesign:system-regulation-filled',
+  'tdesign:system-search',
+  'tdesign:system-search-filled',
+  'tdesign:system-setting',
+  'tdesign:system-setting-filled',
+  'tdesign:system-storage',
+  'tdesign:system-storage-filled',
+  'tdesign:system-sum',
+  'tdesign:system-unlocked',
+  'tdesign:system-unlocked-filled',
+  'tdesign:tab',
+  'tdesign:tab-filled',
+  'tdesign:table',
+  'tdesign:table-1',
+  'tdesign:table-1-filled',
+  'tdesign:table-2',
+  'tdesign:table-2-filled',
+  'tdesign:table-add',
+  'tdesign:table-add-filled',
+  'tdesign:table-filled',
+  'tdesign:table-split',
+  'tdesign:table-split-filled',
+  'tdesign:tag',
+  'tdesign:tag-filled',
+  'tdesign:tangerinr',
+  'tdesign:tangerinr-filled',
+  'tdesign:tape',
+  'tdesign:tape-filled',
+  'tdesign:task',
+  'tdesign:task-1',
+  'tdesign:task-1-filled',
+  'tdesign:task-add',
+  'tdesign:task-add-1',
+  'tdesign:task-add-filled',
+  'tdesign:task-checked',
+  'tdesign:task-checked-1',
+  'tdesign:task-checked-filled',
+  'tdesign:task-double',
+  'tdesign:task-double-filled',
+  'tdesign:task-error',
+  'tdesign:task-error-filled',
+  'tdesign:task-filled',
+  'tdesign:task-location',
+  'tdesign:task-location-filled',
+  'tdesign:task-marked',
+  'tdesign:task-marked-filled',
+  'tdesign:task-setting',
+  'tdesign:task-setting-filled',
+  'tdesign:task-time',
+  'tdesign:task-time-filled',
+  'tdesign:task-visible',
+  'tdesign:task-visible-filled',
+  'tdesign:tea',
+  'tdesign:tea-filled',
+  'tdesign:teahouse',
+  'tdesign:teahouse-filled',
+  'tdesign:template',
+  'tdesign:template-filled',
+  'tdesign:temple',
+  'tdesign:temple-filled',
+  'tdesign:terminal',
+  'tdesign:terminal-rectangle',
+  'tdesign:terminal-rectangle-1',
+  'tdesign:terminal-rectangle-1-filled',
+  'tdesign:terminal-rectangle-filled',
+  'tdesign:terminal-window',
+  'tdesign:terminal-window-filled',
+  'tdesign:textbox',
+  'tdesign:textbox-filled',
+  'tdesign:textformat-bold',
+  'tdesign:textformat-color',
+  'tdesign:textformat-italic',
+  'tdesign:textformat-strikethrough',
+  'tdesign:textformat-underline',
+  'tdesign:textformat-wrap',
+  'tdesign:theaters',
+  'tdesign:theaters-filled',
+  'tdesign:thumb-down',
+  'tdesign:thumb-down-1',
+  'tdesign:thumb-down-1-filled',
+  'tdesign:thumb-down-2',
+  'tdesign:thumb-down-2-filled',
+  'tdesign:thumb-down-filled',
+  'tdesign:thumb-up',
+  'tdesign:thumb-up-1',
+  'tdesign:thumb-up-1-filled',
+  'tdesign:thumb-up-2',
+  'tdesign:thumb-up-2-filled',
+  'tdesign:thumb-up-filled',
+  'tdesign:thunder',
+  'tdesign:thunderstorm',
+  'tdesign:thunderstorm-night',
+  'tdesign:thunderstorm-night-filled',
+  'tdesign:thunderstorm-sunny',
+  'tdesign:thunderstorm-sunny-filled',
+  'tdesign:ticket',
+  'tdesign:ticket-filled',
+  'tdesign:time',
+  'tdesign:time-filled',
+  'tdesign:tips',
+  'tdesign:tips-double',
+  'tdesign:tips-double-filled',
+  'tdesign:tips-filled',
+  'tdesign:tomato',
+  'tdesign:tomato-filled',
+  'tdesign:tools',
+  'tdesign:tools-circle',
+  'tdesign:tools-circle-filled',
+  'tdesign:tools-filled',
+  'tdesign:tornado',
+  'tdesign:tower',
+  'tdesign:tower-1',
+  'tdesign:tower-1-filled',
+  'tdesign:tower-2',
+  'tdesign:tower-2-filled',
+  'tdesign:tower-3',
+  'tdesign:tower-3-filled',
+  'tdesign:tower-clock',
+  'tdesign:tower-clock-filled',
+  'tdesign:tower-filled',
+  'tdesign:town',
+  'tdesign:town-filled',
+  'tdesign:traffic',
+  'tdesign:traffic-events',
+  'tdesign:traffic-events-filled',
+  'tdesign:traffic-filled',
+  'tdesign:transform',
+  'tdesign:transform-1',
+  'tdesign:transform-1-filled',
+  'tdesign:transform-2',
+  'tdesign:transform-3',
+  'tdesign:transform-filled',
+  'tdesign:translate',
+  'tdesign:translate-1',
+  'tdesign:tree-list',
+  'tdesign:tree-round-dot',
+  'tdesign:tree-round-dot-filled',
+  'tdesign:tree-round-dot-vertical',
+  'tdesign:tree-round-dot-vertical-filled',
+  'tdesign:tree-square-dot',
+  'tdesign:tree-square-dot-filled',
+  'tdesign:tree-square-dot-vertical',
+  'tdesign:tree-square-dot-vertical-filled',
+  'tdesign:trending-down',
+  'tdesign:trending-up',
+  'tdesign:tv',
+  'tdesign:tv-1',
+  'tdesign:tv-1-filled',
+  'tdesign:tv-2',
+  'tdesign:tv-2-filled',
+  'tdesign:tv-filled',
+  'tdesign:typography',
+  'tdesign:typography-filled',
+  'tdesign:uncomfortable',
+  'tdesign:uncomfortable-1',
+  'tdesign:uncomfortable-1-filled',
+  'tdesign:uncomfortable-2',
+  'tdesign:uncomfortable-2-filled',
+  'tdesign:uncomfortable-filled',
+  'tdesign:undertake',
+  'tdesign:undertake-delivery',
+  'tdesign:undertake-delivery-filled',
+  'tdesign:undertake-environment-protection',
+  'tdesign:undertake-environment-protection-filled',
+  'tdesign:undertake-filled',
+  'tdesign:undertake-hold-up',
+  'tdesign:undertake-hold-up-filled',
+  'tdesign:undertake-transaction',
+  'tdesign:undertake-transaction-filled',
+  'tdesign:unfold-less',
+  'tdesign:unfold-more',
+  'tdesign:unhappy',
+  'tdesign:unhappy-1',
+  'tdesign:unhappy-1-filled',
+  'tdesign:unhappy-filled',
+  'tdesign:uninstall',
+  'tdesign:uninstall-filled',
+  'tdesign:upload',
+  'tdesign:upload-1',
+  'tdesign:upscale',
+  'tdesign:usb',
+  'tdesign:usb-filled',
+  'tdesign:user',
+  'tdesign:user-1',
+  'tdesign:user-1-filled',
+  'tdesign:user-add',
+  'tdesign:user-add-filled',
+  'tdesign:user-arrow-down',
+  'tdesign:user-arrow-down-filled',
+  'tdesign:user-arrow-left',
+  'tdesign:user-arrow-left-filled',
+  'tdesign:user-arrow-right',
+  'tdesign:user-arrow-right-filled',
+  'tdesign:user-arrow-up',
+  'tdesign:user-arrow-up-filled',
+  'tdesign:user-avatar',
+  'tdesign:user-avatar-filled',
+  'tdesign:user-blocked',
+  'tdesign:user-blocked-filled',
+  'tdesign:user-business',
+  'tdesign:user-business-filled',
+  'tdesign:user-checked',
+  'tdesign:user-checked-1',
+  'tdesign:user-checked-1-filled',
+  'tdesign:user-checked-filled',
+  'tdesign:user-circle',
+  'tdesign:user-circle-filled',
+  'tdesign:user-clear',
+  'tdesign:user-clear-filled',
+  'tdesign:user-error-1',
+  'tdesign:user-error-1-filled',
+  'tdesign:user-filled',
+  'tdesign:user-invisible',
+  'tdesign:user-invisible-filled',
+  'tdesign:user-list',
+  'tdesign:user-list-filled',
+  'tdesign:user-locked',
+  'tdesign:user-locked-filled',
+  'tdesign:user-marked',
+  'tdesign:user-marked-filled',
+  'tdesign:user-password',
+  'tdesign:user-password-filled',
+  'tdesign:user-safety',
+  'tdesign:user-safety-filled',
+  'tdesign:user-search',
+  'tdesign:user-search-filled',
+  'tdesign:user-setting',
+  'tdesign:user-setting-filled',
+  'tdesign:user-talk',
+  'tdesign:user-talk-1',
+  'tdesign:user-talk-1-filled',
+  'tdesign:user-talk-filled',
+  'tdesign:user-talk-off-1',
+  'tdesign:user-talk-off-1-filled',
+  'tdesign:user-time',
+  'tdesign:user-time-filled',
+  'tdesign:user-transmit',
+  'tdesign:user-transmit-filled',
+  'tdesign:user-unknown',
+  'tdesign:user-unknown-filled',
+  'tdesign:user-unlocked',
+  'tdesign:user-unlocked-filled',
+  'tdesign:user-vip',
+  'tdesign:user-vip-filled',
+  'tdesign:user-visible',
+  'tdesign:user-visible-filled',
+  'tdesign:usercase',
+  'tdesign:usercase-filled',
+  'tdesign:usercase-link',
+  'tdesign:usercase-link-filled',
+  'tdesign:usergroup',
+  'tdesign:usergroup-add',
+  'tdesign:usergroup-add-filled',
+  'tdesign:usergroup-clear',
+  'tdesign:usergroup-clear-filled',
+  'tdesign:usergroup-filled',
+  'tdesign:vehicle',
+  'tdesign:vehicle-filled',
+  'tdesign:verified',
+  'tdesign:verified-filled',
+  'tdesign:verify',
+  'tdesign:verify-filled',
+  'tdesign:vertical',
+  'tdesign:vertical-filled',
+  'tdesign:video',
+  'tdesign:video-camera',
+  'tdesign:video-camera-1',
+  'tdesign:video-camera-1-filled',
+  'tdesign:video-camera-2',
+  'tdesign:video-camera-2-filled',
+  'tdesign:video-camera-3',
+  'tdesign:video-camera-3-filled',
+  'tdesign:video-camera-dollar',
+  'tdesign:video-camera-dollar-filled',
+  'tdesign:video-camera-filled',
+  'tdesign:video-camera-minus',
+  'tdesign:video-camera-minus-filled',
+  'tdesign:video-camera-music',
+  'tdesign:video-camera-music-filled',
+  'tdesign:video-camera-off',
+  'tdesign:video-camera-off-filled',
+  'tdesign:video-filled',
+  'tdesign:video-library',
+  'tdesign:video-library-filled',
+  'tdesign:view-agenda',
+  'tdesign:view-agenda-filled',
+  'tdesign:view-column',
+  'tdesign:view-in-ar',
+  'tdesign:view-in-ar-filled',
+  'tdesign:view-list',
+  'tdesign:view-module',
+  'tdesign:view-module-filled',
+  'tdesign:visual-recognition',
+  'tdesign:visual-recognition-filled',
+  'tdesign:wallet',
+  'tdesign:wallet-filled',
+  'tdesign:watch',
+  'tdesign:watch-filled',
+  'tdesign:watermelon',
+  'tdesign:watermelon-filled',
+  'tdesign:wave-bye',
+  'tdesign:wave-bye-filled',
+  'tdesign:wave-left',
+  'tdesign:wave-left-filled',
+  'tdesign:wave-right',
+  'tdesign:wave-right-filled',
+  'tdesign:wealth',
+  'tdesign:wealth-1',
+  'tdesign:wealth-1-filled',
+  'tdesign:wealth-filled',
+  'tdesign:widget',
+  'tdesign:widget-filled',
+  'tdesign:wifi',
+  'tdesign:wifi-1',
+  'tdesign:wifi-1-filled',
+  'tdesign:wifi-off',
+  'tdesign:wifi-off-1',
+  'tdesign:wifi-off-1-filled',
+  'tdesign:window',
+  'tdesign:window-1',
+  'tdesign:window-1-filled',
+  'tdesign:window-filled',
+  'tdesign:windy',
+  'tdesign:windy-rain',
+  'tdesign:wink',
+  'tdesign:wink-filled',
+  'tdesign:work',
+  'tdesign:work-filled',
+  'tdesign:work-history',
+  'tdesign:work-history-filled',
+  'tdesign:work-off',
+  'tdesign:work-off-filled',
+  'tdesign:wry-smile',
+  'tdesign:wry-smile-filled',
+  'tdesign:zoom-in',
+  'tdesign:zoom-in-filled',
+  'tdesign:zoom-out',
+  'tdesign:zoom-out-filled'
+];

+ 1 - 1
src/views/manage/menu/modules/menu-operate-modal.vue

@@ -136,7 +136,7 @@ const localIconOptions = localIcons.map<SelectOption>(item => ({
 
 const showLayout = computed(() => model.value.parentId === 0);
 
-const showPage = computed(() => model.value.menuType === '2');
+const showPage = computed(() => model.value.menuType === '1');
 
 const pageOptions = computed(() => {
   const allPages = [...props.allPages];

+ 111 - 0
src/views/manage/menu/modules/shared.ts

@@ -1,3 +1,7 @@
+import { h } from 'vue';
+import type { FormSchema } from '@/components/zt/Form/types/form';
+import CustomIconSelect from '@/components/custom/custom-icon-select.vue';
+import { icons } from './icons';
 const LAYOUT_PREFIX = 'layout.';
 const VIEW_PREFIX = 'view.';
 const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
@@ -77,3 +81,110 @@ export function getRoutePathWithParam(routePath: string, param: string) {
 
   return routePath;
 }
+
+export const formSchems: FormSchema[] = [
+  {
+    field: 'menuId',
+    label: '',
+    show: false,
+    component: 'NInput'
+  },
+  {
+    label: '菜单类型',
+    field: 'type',
+    component: 'NRadioGroup',
+    defaultValue: 1,
+    required: true,
+    componentProps: {
+      options: [
+        {
+          label: '目录',
+          value: 0
+        },
+        {
+          label: '菜单',
+          value: 1
+        },
+        {
+          label: '按钮',
+          value: 2
+        }
+      ]
+    }
+  },
+  {
+    label: '菜单名称',
+    field: 'name',
+    component: 'NInput',
+    required: true
+  },
+  {
+    label: '路由名称',
+    field: 'url',
+    component: 'NInput',
+    required: true,
+    ifShow({ model }) {
+      return model.type != 2;
+    }
+  },
+  {
+    label: '上级菜单',
+    field: 'parentId',
+    slot: 'parentId',
+    component: 'NTreeSelect',
+    defaultValue: 0
+  },
+  {
+    label: '排序',
+    field: 'orderNum',
+    component: 'NInputNumber',
+    defaultValue: 1,
+    required: true
+  },
+  {
+    label: '授权标识',
+    field: 'perms',
+    component: 'NInput'
+  },
+  {
+    label: '图标',
+    field: 'icon',
+    component: 'NSelect',
+    required: true,
+    defaultValue: '',
+    render({ model, field }) {
+      return h(CustomIconSelect, {
+        icons,
+        value: model[field],
+        'onUpdate:value': value => {
+          model[field] = value;
+        }
+      });
+    },
+    ifShow({ model }) {
+      return model.type != 2;
+    }
+  },
+  {
+    label: '隐藏菜单',
+    field: 'hideInMenu',
+    component: 'NRadioGroup',
+    required: true,
+    defaultValue: 0,
+    ifShow({ model }) {
+      return model.type != 2;
+    },
+    componentProps: {
+      options: [
+        {
+          label: '是',
+          value: 1
+        },
+        {
+          label: '否',
+          value: 0
+        }
+      ]
+    }
+  }
+];

+ 7 - 0
src/views/manage/schedule/index.vue

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