mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 15:53:43 +08:00 
			
		
		
		
	feat(projects): add menu route field
This commit is contained in:
		@@ -355,12 +355,15 @@ const local: App.I18n.Schema = {
 | 
			
		||||
        localIcon: 'Local Icon',
 | 
			
		||||
        iconTypeTitle: 'Icon Type',
 | 
			
		||||
        order: 'Order',
 | 
			
		||||
        constant: 'Constant',
 | 
			
		||||
        keepAlive: 'Keep Alive',
 | 
			
		||||
        href: 'Href',
 | 
			
		||||
        hideInMenu: 'Hide In Menu',
 | 
			
		||||
        activeMenu: 'Active Menu',
 | 
			
		||||
        multiTab: 'Multi Tab',
 | 
			
		||||
        fixedIndexInTab: 'Fixed Index In Tab',
 | 
			
		||||
        roles: 'Roles',
 | 
			
		||||
        query: 'Query Params',
 | 
			
		||||
        button: 'Button',
 | 
			
		||||
        buttonCode: 'Button Code',
 | 
			
		||||
        buttonDesc: 'Button Desc',
 | 
			
		||||
@@ -380,10 +383,13 @@ const local: App.I18n.Schema = {
 | 
			
		||||
          keepAlive: 'Please select whether to cache route',
 | 
			
		||||
          href: 'Please enter href',
 | 
			
		||||
          hideInMenu: 'Please select whether to hide menu',
 | 
			
		||||
          activeMenu: 'Please enter the route name of the highlighted menu',
 | 
			
		||||
          activeMenu: 'Please select route name of the highlighted menu',
 | 
			
		||||
          multiTab: 'Please select whether to support multiple tabs',
 | 
			
		||||
          fixedInTab: 'Please select whether to fix in the tab',
 | 
			
		||||
          fixedIndexInTab: 'Please enter the index fixed in the tab',
 | 
			
		||||
          roles: 'Please select roles',
 | 
			
		||||
          queryKey: 'Please enter route parameter Key',
 | 
			
		||||
          queryValue: 'Please enter route parameter Value',
 | 
			
		||||
          button: 'Please select whether it is a button',
 | 
			
		||||
          buttonCode: 'Please enter button code',
 | 
			
		||||
          buttonDesc: 'Please enter button description',
 | 
			
		||||
 
 | 
			
		||||
@@ -355,12 +355,15 @@ const local: App.I18n.Schema = {
 | 
			
		||||
        localIcon: '本地图标',
 | 
			
		||||
        iconTypeTitle: '图标类型',
 | 
			
		||||
        order: '排序',
 | 
			
		||||
        constant: '常量路由',
 | 
			
		||||
        keepAlive: '缓存路由',
 | 
			
		||||
        href: '外链',
 | 
			
		||||
        hideInMenu: '隐藏菜单',
 | 
			
		||||
        activeMenu: '高亮的菜单',
 | 
			
		||||
        multiTab: '支持多页签',
 | 
			
		||||
        fixedIndexInTab: '固定在页签中的序号',
 | 
			
		||||
        roles: '角色',
 | 
			
		||||
        query: '路由参数',
 | 
			
		||||
        button: '按钮',
 | 
			
		||||
        buttonCode: '按钮编码',
 | 
			
		||||
        buttonDesc: '按钮描述',
 | 
			
		||||
@@ -380,10 +383,13 @@ const local: App.I18n.Schema = {
 | 
			
		||||
          keepAlive: '请选择是否缓存路由',
 | 
			
		||||
          href: '请输入外链',
 | 
			
		||||
          hideInMenu: '请选择是否隐藏菜单',
 | 
			
		||||
          activeMenu: '请输入高亮的菜单的路由名称',
 | 
			
		||||
          activeMenu: '请选择高亮的菜单的路由名称',
 | 
			
		||||
          multiTab: '请选择是否支持多标签',
 | 
			
		||||
          fixedInTab: '请选择是否固定在页签中',
 | 
			
		||||
          fixedIndexInTab: '请输入固定在页签中的序号',
 | 
			
		||||
          roles: '请选择角色',
 | 
			
		||||
          queryKey: '请输入路由参数Key',
 | 
			
		||||
          queryValue: '请输入路由参数Value',
 | 
			
		||||
          button: '请选择是否按钮',
 | 
			
		||||
          buttonCode: '请输入按钮编码',
 | 
			
		||||
          buttonDesc: '请输入按钮描述',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								src/typings/api.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								src/typings/api.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -198,6 +198,12 @@ declare namespace Api {
 | 
			
		||||
      order: number;
 | 
			
		||||
      /** whether to cache the route */
 | 
			
		||||
      keepAlive?: boolean;
 | 
			
		||||
      /**
 | 
			
		||||
       * Is constant route
 | 
			
		||||
       *
 | 
			
		||||
       * Does not need to login, and the route is defined in the front-end
 | 
			
		||||
       */
 | 
			
		||||
      constant?: boolean;
 | 
			
		||||
      /** outer link */
 | 
			
		||||
      href?: string;
 | 
			
		||||
      /** whether to hide the route in the menu */
 | 
			
		||||
@@ -215,8 +221,16 @@ declare namespace Api {
 | 
			
		||||
      multiTab?: boolean;
 | 
			
		||||
      /** If set, the route will be fixed in tabs, and the value is the order of fixed tabs */
 | 
			
		||||
      fixedIndexInTab?: number;
 | 
			
		||||
      /** if set query parameters, it will be automatically carried when entering the route */
 | 
			
		||||
      query: Record<string, string>;
 | 
			
		||||
      /** menu buttons */
 | 
			
		||||
      buttons?: MenuButton[];
 | 
			
		||||
      /**
 | 
			
		||||
       * Roles of the route
 | 
			
		||||
       *
 | 
			
		||||
       * Route can be accessed if the current user has at least one of the roles
 | 
			
		||||
       */
 | 
			
		||||
      roles?: string[];
 | 
			
		||||
      /** children menu */
 | 
			
		||||
      children?: Menu[];
 | 
			
		||||
    }>;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								src/typings/app.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -539,12 +539,15 @@ declare namespace App {
 | 
			
		||||
            localIcon: string;
 | 
			
		||||
            iconTypeTitle: string;
 | 
			
		||||
            order: string;
 | 
			
		||||
            constant: string;
 | 
			
		||||
            keepAlive: string;
 | 
			
		||||
            href: string;
 | 
			
		||||
            hideInMenu: string;
 | 
			
		||||
            activeMenu: string;
 | 
			
		||||
            multiTab: string;
 | 
			
		||||
            fixedIndexInTab: string;
 | 
			
		||||
            roles: string;
 | 
			
		||||
            query: string;
 | 
			
		||||
            button: string;
 | 
			
		||||
            buttonCode: string;
 | 
			
		||||
            buttonDesc: string;
 | 
			
		||||
@@ -568,6 +571,9 @@ declare namespace App {
 | 
			
		||||
              multiTab: string;
 | 
			
		||||
              fixedInTab: string;
 | 
			
		||||
              fixedIndexInTab: string;
 | 
			
		||||
              roles: string;
 | 
			
		||||
              queryKey: string;
 | 
			
		||||
              queryValue: string;
 | 
			
		||||
              button: string;
 | 
			
		||||
              buttonCode: string;
 | 
			
		||||
              buttonDesc: string;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -19,9 +19,11 @@ declare module 'vue' {
 | 
			
		||||
    IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
 | 
			
		||||
    IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
 | 
			
		||||
    IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
 | 
			
		||||
    'IconIc:roundPlus': typeof import('~icons/ic/round-plus')['default']
 | 
			
		||||
    IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
 | 
			
		||||
    IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
 | 
			
		||||
    IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
 | 
			
		||||
    IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default']
 | 
			
		||||
    IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
 | 
			
		||||
    IconLocalBanner: typeof import('~icons/local/banner')['default']
 | 
			
		||||
    IconLocalLogo: typeof import('~icons/local/logo')['default']
 | 
			
		||||
@@ -49,6 +51,7 @@ declare module 'vue' {
 | 
			
		||||
    NDrawer: typeof import('naive-ui')['NDrawer']
 | 
			
		||||
    NDrawerContent: typeof import('naive-ui')['NDrawerContent']
 | 
			
		||||
    NDropdown: typeof import('naive-ui')['NDropdown']
 | 
			
		||||
    NDynamicInput: typeof import('naive-ui')['NDynamicInput']
 | 
			
		||||
    NEmpty: typeof import('naive-ui')['NEmpty']
 | 
			
		||||
    NForm: typeof import('naive-ui')['NForm']
 | 
			
		||||
    NFormItem: typeof import('naive-ui')['NFormItem']
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,20 @@
 | 
			
		||||
<script setup lang="tsx">
 | 
			
		||||
import { computed, reactive, watch } from 'vue';
 | 
			
		||||
import type { Ref } from 'vue';
 | 
			
		||||
import { computed, reactive, ref, watch } from 'vue';
 | 
			
		||||
import type { SelectOption } from 'naive-ui';
 | 
			
		||||
import type { LastLevelRouteKey } from '@elegant-router/types';
 | 
			
		||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
 | 
			
		||||
import { $t } from '@/locales';
 | 
			
		||||
import { enableStatusOptions, menuIconTypeOptions, menuTypeOptions } from '@/constants/business';
 | 
			
		||||
import SvgIcon from '@/components/custom/svg-icon.vue';
 | 
			
		||||
import { getLocalIcons } from '@/utils/icon';
 | 
			
		||||
import { getLayoutAndPage, transformLayoutAndPageToComponent } from './shared';
 | 
			
		||||
import { fetchGetAllRoles } from '@/service/api';
 | 
			
		||||
import {
 | 
			
		||||
  getLayoutAndPage,
 | 
			
		||||
  transformLayoutAndPageToComponent,
 | 
			
		||||
  transformToKeyValuePairs,
 | 
			
		||||
  transformToQueryObject
 | 
			
		||||
} from './shared';
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: 'MenuOperateDrawer'
 | 
			
		||||
@@ -51,6 +59,7 @@ type Model = Pick<
 | 
			
		||||
  Api.SystemManage.Menu,
 | 
			
		||||
  | 'menuType'
 | 
			
		||||
  | 'menuName'
 | 
			
		||||
  | 'i18nKey'
 | 
			
		||||
  | 'icon'
 | 
			
		||||
  | 'iconType'
 | 
			
		||||
  | 'routeName'
 | 
			
		||||
@@ -58,8 +67,17 @@ type Model = Pick<
 | 
			
		||||
  | 'component'
 | 
			
		||||
  | 'status'
 | 
			
		||||
  | 'hideInMenu'
 | 
			
		||||
  | 'activeMenu'
 | 
			
		||||
  | 'order'
 | 
			
		||||
  | 'parentId'
 | 
			
		||||
  | 'constant'
 | 
			
		||||
  | 'keepAlive'
 | 
			
		||||
  | 'href'
 | 
			
		||||
  | 'multiTab'
 | 
			
		||||
  | 'fixedIndexInTab'
 | 
			
		||||
  | 'roles'
 | 
			
		||||
  | 'buttons'
 | 
			
		||||
  | 'query'
 | 
			
		||||
> & {
 | 
			
		||||
  layout: string;
 | 
			
		||||
  page: string;
 | 
			
		||||
@@ -71,16 +89,26 @@ function createDefaultModel(): Model {
 | 
			
		||||
  return {
 | 
			
		||||
    menuType: '1',
 | 
			
		||||
    menuName: '',
 | 
			
		||||
    i18nKey: '' as App.I18n.I18nKey,
 | 
			
		||||
    icon: '',
 | 
			
		||||
    iconType: '1',
 | 
			
		||||
    routeName: '',
 | 
			
		||||
    routePath: '',
 | 
			
		||||
    layout: '',
 | 
			
		||||
    page: '',
 | 
			
		||||
    status: null,
 | 
			
		||||
    status: '1',
 | 
			
		||||
    hideInMenu: false,
 | 
			
		||||
    activeMenu: '' as LastLevelRouteKey,
 | 
			
		||||
    order: 0,
 | 
			
		||||
    parentId: 0
 | 
			
		||||
    parentId: 0,
 | 
			
		||||
    constant: false,
 | 
			
		||||
    keepAlive: false,
 | 
			
		||||
    href: '',
 | 
			
		||||
    multiTab: false,
 | 
			
		||||
    fixedIndexInTab: 0,
 | 
			
		||||
    roles: [],
 | 
			
		||||
    buttons: [],
 | 
			
		||||
    query: {}
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -136,6 +164,29 @@ const layoutOptions: CommonType.Option[] = [
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const dynamicQueryKeyValuePairs: Ref<Record<string, string>[]> = ref([
 | 
			
		||||
  {
 | 
			
		||||
    key: '',
 | 
			
		||||
    value: ''
 | 
			
		||||
  }
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
/** the enabled role options */
 | 
			
		||||
const roleOptions = ref<CommonType.Option<string>[]>([]);
 | 
			
		||||
 | 
			
		||||
async function getRoleOptions() {
 | 
			
		||||
  const { error, data } = await fetchGetAllRoles();
 | 
			
		||||
 | 
			
		||||
  if (!error) {
 | 
			
		||||
    const options = data.map(item => ({
 | 
			
		||||
      label: item.roleName,
 | 
			
		||||
      value: item.roleCode
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    roleOptions.value = [...options];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleUpdateModel() {
 | 
			
		||||
  if (props.operateType === 'add') {
 | 
			
		||||
    Object.assign(model, createDefaultModel());
 | 
			
		||||
@@ -150,11 +201,12 @@ function handleUpdateModel() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (props.operateType === 'edit' && props.rowData) {
 | 
			
		||||
    const { component, ...rest } = props.rowData;
 | 
			
		||||
    const { component, query, ...rest } = props.rowData;
 | 
			
		||||
 | 
			
		||||
    const { layout, page } = getLayoutAndPage(component);
 | 
			
		||||
 | 
			
		||||
    Object.assign(model, rest, { layout, page });
 | 
			
		||||
    dynamicQueryKeyValuePairs.value = transformToKeyValuePairs(query);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -166,6 +218,12 @@ async function handleSubmit() {
 | 
			
		||||
  await validate();
 | 
			
		||||
 | 
			
		||||
  model.component = transformLayoutAndPageToComponent(model.layout, model.page);
 | 
			
		||||
  model.query = transformToQueryObject(dynamicQueryKeyValuePairs.value);
 | 
			
		||||
 | 
			
		||||
  // model.buttons = [];
 | 
			
		||||
  // Need: Get buttons based on roles
 | 
			
		||||
 | 
			
		||||
  console.log('model:', model);
 | 
			
		||||
 | 
			
		||||
  // request
 | 
			
		||||
  window.$message?.success($t('common.updateSuccess'));
 | 
			
		||||
@@ -177,28 +235,81 @@ watch(visible, () => {
 | 
			
		||||
  if (visible.value) {
 | 
			
		||||
    handleUpdateModel();
 | 
			
		||||
    restoreValidation();
 | 
			
		||||
    getRoleOptions();
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="360">
 | 
			
		||||
  <NDrawer v-model:show="visible" display-directive="show" :width="400">
 | 
			
		||||
    <NDrawerContent :title="title" :native-scrollbar="false" closable>
 | 
			
		||||
      <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" :label-width="80">
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.menuType')" path="menuType">
 | 
			
		||||
        <NGrid>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.menuType')" path="menuType">
 | 
			
		||||
            <NRadioGroup v-model:value="model.menuType" :disabled="disabledMenuType">
 | 
			
		||||
              <NRadio v-for="item in menuTypeOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.menuName')" path="menuName">
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.order')" path="order">
 | 
			
		||||
            <NInputNumber v-model:value="model.order" :placeholder="$t('page.manage.menu.form.order')" />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.menuName')" path="menuName">
 | 
			
		||||
            <NInput v-model:value="model.menuName" :placeholder="$t('page.manage.menu.form.menuName')" />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.iconTypeTitle')" path="iconType">
 | 
			
		||||
          <NRadioGroup v-model:value="model.iconType">
 | 
			
		||||
            <NRadio v-for="item in menuIconTypeOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.i18nKey')" path="i18nKey">
 | 
			
		||||
            <NInput v-model:value="model.i18nKey" :placeholder="$t('page.manage.menu.form.i18nKey')" />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.menuStatus')" path="status">
 | 
			
		||||
            <NRadioGroup v-model:value="model.status">
 | 
			
		||||
              <NRadio
 | 
			
		||||
                v-for="item in enableStatusOptions"
 | 
			
		||||
                :key="item.value"
 | 
			
		||||
                :value="item.value"
 | 
			
		||||
                :label="$t(item.label)"
 | 
			
		||||
              />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.icon')" path="icon">
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.hideInMenu')" path="hideInMenu">
 | 
			
		||||
            <NRadioGroup v-model:value="model.hideInMenu">
 | 
			
		||||
              <NRadio value :label="$t('common.yesOrNo.yes')" />
 | 
			
		||||
              <NRadio :value="false" :label="$t('common.yesOrNo.no')" />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.keepAlive')" path="keepAlive">
 | 
			
		||||
            <NRadioGroup v-model:value="model.keepAlive">
 | 
			
		||||
              <NRadio value :label="$t('common.yesOrNo.yes')" />
 | 
			
		||||
              <NRadio :value="false" :label="$t('common.yesOrNo.no')" />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.constant')" path="constant">
 | 
			
		||||
            <NRadioGroup v-model:value="model.constant">
 | 
			
		||||
              <NRadio value :label="$t('common.yesOrNo.yes')" />
 | 
			
		||||
              <NRadio :value="false" :label="$t('common.yesOrNo.no')" />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.multiTab')" path="multiTab">
 | 
			
		||||
            <NRadioGroup v-model:value="model.multiTab">
 | 
			
		||||
              <NRadio value :label="$t('common.yesOrNo.yes')" />
 | 
			
		||||
              <NRadio :value="false" :label="$t('common.yesOrNo.no')" />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="12" :label="$t('page.manage.menu.fixedIndexInTab')" path="fixedIndexInTab">
 | 
			
		||||
            <NInputNumber
 | 
			
		||||
              v-model:value="model.fixedIndexInTab"
 | 
			
		||||
              :placeholder="$t('page.manage.menu.form.fixedIndexInTab')"
 | 
			
		||||
            />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.iconTypeTitle')" path="iconType">
 | 
			
		||||
            <NRadioGroup v-model:value="model.iconType">
 | 
			
		||||
              <NRadio
 | 
			
		||||
                v-for="item in menuIconTypeOptions"
 | 
			
		||||
                :key="item.value"
 | 
			
		||||
                :value="item.value"
 | 
			
		||||
                :label="$t(item.label)"
 | 
			
		||||
              />
 | 
			
		||||
            </NRadioGroup>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.icon')" path="icon">
 | 
			
		||||
            <template v-if="model.iconType === '1'">
 | 
			
		||||
              <NInput v-model:value="model.icon" :placeholder="$t('page.manage.menu.form.icon')" class="flex-1">
 | 
			
		||||
                <template #suffix>
 | 
			
		||||
@@ -213,37 +324,65 @@ watch(visible, () => {
 | 
			
		||||
                :options="localIconOptions"
 | 
			
		||||
              />
 | 
			
		||||
            </template>
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.routeName')" path="routeName">
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.routeName')" path="routeName">
 | 
			
		||||
            <NInput v-model:value="model.routeName" :placeholder="$t('page.manage.menu.form.routeName')" />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.routePath')" path="routePath">
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.routePath')" path="routePath">
 | 
			
		||||
            <NInput v-model:value="model.routePath" :placeholder="$t('page.manage.menu.form.routePath')" />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem v-if="showLayout" :label="$t('page.manage.menu.layout')" path="layout">
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi v-if="showLayout" span="24" :label="$t('page.manage.menu.layout')" path="layout">
 | 
			
		||||
            <NSelect
 | 
			
		||||
              v-model:value="model.layout"
 | 
			
		||||
              :options="layoutOptions"
 | 
			
		||||
              :placeholder="$t('page.manage.menu.form.layout')"
 | 
			
		||||
            />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem v-if="showPage" :label="$t('page.manage.menu.page')" path="page">
 | 
			
		||||
          <NSelect v-model:value="model.page" :options="pageOptions" :placeholder="$t('page.manage.menu.form.page')" />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.menuStatus')" path="status">
 | 
			
		||||
          <NRadioGroup v-model:value="model.status">
 | 
			
		||||
            <NRadio v-for="item in enableStatusOptions" :key="item.value" :value="item.value" :label="$t(item.label)" />
 | 
			
		||||
          </NRadioGroup>
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.hideInMenu')" path="hideInMenu">
 | 
			
		||||
          <NRadioGroup v-model:value="model.hideInMenu">
 | 
			
		||||
            <NRadio value :label="$t('common.yesOrNo.yes')" />
 | 
			
		||||
            <NRadio :value="false" :label="$t('common.yesOrNo.no')" />
 | 
			
		||||
          </NRadioGroup>
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
        <NFormItem :label="$t('page.manage.menu.order')" path="order">
 | 
			
		||||
          <NInputNumber v-model:value="model.order" :placeholder="$t('page.manage.menu.form.order')" />
 | 
			
		||||
        </NFormItem>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi v-if="showPage" span="24" :label="$t('page.manage.menu.page')" path="page">
 | 
			
		||||
            <NSelect
 | 
			
		||||
              v-model:value="model.page"
 | 
			
		||||
              :options="pageOptions"
 | 
			
		||||
              :placeholder="$t('page.manage.menu.form.page')"
 | 
			
		||||
            />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi v-if="showPage" span="24" :label="$t('page.manage.menu.activeMenu')" path="activeMenu">
 | 
			
		||||
            <NSelect
 | 
			
		||||
              v-model:value="model.activeMenu"
 | 
			
		||||
              :options="pageOptions"
 | 
			
		||||
              :placeholder="$t('page.manage.menu.form.activeMenu')"
 | 
			
		||||
            />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.href')" path="href">
 | 
			
		||||
            <NInput v-model:value="model.href" :placeholder="$t('page.manage.menu.form.href')" />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.roles')" path="roles">
 | 
			
		||||
            <NSelect
 | 
			
		||||
              v-model:value="model.roles"
 | 
			
		||||
              multiple
 | 
			
		||||
              :options="roleOptions"
 | 
			
		||||
              :placeholder="$t('page.manage.menu.form.roles')"
 | 
			
		||||
            />
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
          <NFormItemGi span="24" :label="$t('page.manage.menu.query')">
 | 
			
		||||
            <NDynamicInput
 | 
			
		||||
              v-model:value="dynamicQueryKeyValuePairs"
 | 
			
		||||
              preset="pair"
 | 
			
		||||
              :key-placeholder="$t('page.manage.menu.form.queryKey')"
 | 
			
		||||
              :value-placeholder="$t('page.manage.menu.form.queryValue')"
 | 
			
		||||
            >
 | 
			
		||||
              <template #action="{ index, create, remove }">
 | 
			
		||||
                <NSpace class="ml-2">
 | 
			
		||||
                  <NButton size="medium" @click="() => create(index)">
 | 
			
		||||
                    <icon-ic:round-plus class="text-icon" />
 | 
			
		||||
                  </NButton>
 | 
			
		||||
                  <NButton size="medium" @click="() => remove(index)">
 | 
			
		||||
                    <icon-ic-round-remove class="text-icon" />
 | 
			
		||||
                  </NButton>
 | 
			
		||||
                </NSpace>
 | 
			
		||||
              </template>
 | 
			
		||||
            </NDynamicInput>
 | 
			
		||||
          </NFormItemGi>
 | 
			
		||||
        </NGrid>
 | 
			
		||||
      </NForm>
 | 
			
		||||
      <template #footer>
 | 
			
		||||
        <NSpace :size="16">
 | 
			
		||||
 
 | 
			
		||||
@@ -40,3 +40,21 @@ export function transformLayoutAndPageToComponent(layout: string, page: string)
 | 
			
		||||
 | 
			
		||||
  return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function transformToQueryObject(data: Record<string, string>[]) {
 | 
			
		||||
  const query: Record<string, string> = {};
 | 
			
		||||
  data.forEach(pair => {
 | 
			
		||||
    if (pair.key && pair.value) {
 | 
			
		||||
      query[pair.key] = pair.value;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return query;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function transformToKeyValuePairs(query?: Record<string, string>) {
 | 
			
		||||
  const safeQuery = query || {};
 | 
			
		||||
  return Object.entries(safeQuery).map(([key, value]) => ({
 | 
			
		||||
    key,
 | 
			
		||||
    value
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user