mirror of
https://gitee.com/nicefold/seewis-soybean-plus.git
synced 2026-10-14 03:52:28 +08:00
merge(projects): Merge remote-tracking branch 'Soybean/main' into dev
This commit is contained in:
+20
-20
@@ -67,7 +67,7 @@
|
||||
"@sa/utils": "workspace:*",
|
||||
"@types/streamsaver": "^2.0.5",
|
||||
"@umoteam/editor": "^8.1.0",
|
||||
"@vueuse/core": "14.0.0",
|
||||
"@vueuse/core": "14.1.0",
|
||||
"clipboard": "2.0.11",
|
||||
"dayjs": "1.11.19",
|
||||
"defu": "6.1.4",
|
||||
@@ -76,50 +76,50 @@
|
||||
"jsencrypt": "^3.5.4",
|
||||
"json5": "2.2.3",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"naive-ui": "2.43.1",
|
||||
"naive-ui": "2.43.2",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "3.0.4",
|
||||
"streamsaver": "^2.0.6",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"vue": "3.5.24",
|
||||
"vue": "3.5.25",
|
||||
"vue-advanced-cropper": "^2.8.9",
|
||||
"vue-draggable-plus": "0.6.0",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-i18n": "11.2.2",
|
||||
"vue-router": "4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@elegant-router/vue": "0.3.8",
|
||||
"@iconify/json": "2.2.407",
|
||||
"@iconify/json": "2.2.414",
|
||||
"@sa/scripts": "workspace:*",
|
||||
"@sa/uno-preset": "workspace:*",
|
||||
"@soybeanjs/eslint-config": "1.7.3",
|
||||
"@soybeanjs/eslint-config": "1.7.4",
|
||||
"@types/node": "24.10.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/eslint-config": "66.5.6",
|
||||
"@unocss/preset-icons": "66.5.6",
|
||||
"@unocss/preset-uno": "66.5.6",
|
||||
"@unocss/transformer-directives": "66.5.6",
|
||||
"@unocss/transformer-variant-group": "66.5.6",
|
||||
"@unocss/vite": "66.5.6",
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.1",
|
||||
"@unocss/eslint-config": "66.5.10",
|
||||
"@unocss/preset-icons": "66.5.10",
|
||||
"@unocss/preset-uno": "66.5.10",
|
||||
"@unocss/transformer-directives": "66.5.10",
|
||||
"@unocss/transformer-variant-group": "66.5.10",
|
||||
"@unocss/vite": "66.5.10",
|
||||
"@vitejs/plugin-vue": "6.0.2",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.2",
|
||||
"consola": "3.4.2",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-plugin-vue": "10.5.1",
|
||||
"eslint-plugin-vue": "10.6.2",
|
||||
"kolorist": "1.8.0",
|
||||
"sass": "1.94.0",
|
||||
"sass": "1.94.2",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsx": "4.20.6",
|
||||
"tsx": "4.21.0",
|
||||
"typescript": "5.9.3",
|
||||
"unplugin-icons": "22.5.0",
|
||||
"unplugin-vue-components": "30.0.0",
|
||||
"vite": "7.2.2",
|
||||
"vite": "7.2.6",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-vue-devtools": "8.0.3",
|
||||
"vite-plugin-vue-devtools": "8.0.5",
|
||||
"vue-eslint-parser": "10.2.0",
|
||||
"vue-tsc": "3.1.4"
|
||||
"vue-tsc": "3.1.5"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm sa git-commit-verify",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/alova",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./fetch": "./src/fetch.ts",
|
||||
@@ -15,6 +15,6 @@
|
||||
"dependencies": {
|
||||
"@alova/mock": "2.0.17",
|
||||
"@sa/utils": "workspace:*",
|
||||
"alova": "3.3.4"
|
||||
"alova": "3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/axios",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/color",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/hooks",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/materials",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/scripts",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"bin": {
|
||||
"sa": "./bin.ts"
|
||||
},
|
||||
@@ -14,15 +14,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@soybeanjs/changelog": "0.3.25",
|
||||
"bumpp": "10.3.1",
|
||||
"bumpp": "10.3.2",
|
||||
"c12": "3.3.2",
|
||||
"cac": "6.7.14",
|
||||
"consola": "3.4.2",
|
||||
"enquirer": "2.4.1",
|
||||
"execa": "9.6.0",
|
||||
"execa": "9.6.1",
|
||||
"kolorist": "1.8.0",
|
||||
"npm-check-updates": "19.1.2",
|
||||
"picomatch": "4.0.3",
|
||||
"rimraf": "6.1.0"
|
||||
"rimraf": "6.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/uno-preset",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sa/utils",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
Generated
+724
-460
File diff suppressed because it is too large
Load Diff
@@ -131,6 +131,7 @@ export function useNaivePaginatedTable<ResponseData, ApiData>(
|
||||
getColumns,
|
||||
onFetched: data => {
|
||||
pagination.itemCount = data.total;
|
||||
pagination.pageSize = data.pageSize;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRoute } from 'vue-router';
|
||||
import { useContext } from '@sa/hooks';
|
||||
import type { RouteKey } from '@elegant-router/types';
|
||||
import { useRouteStore } from '@/store/modules/route';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
|
||||
export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu', useMixMenu);
|
||||
@@ -10,6 +11,7 @@ export const [provideMixMenuContext, useMixMenuContext] = useContext('MixMenu',
|
||||
function useMixMenu() {
|
||||
const route = useRoute();
|
||||
const routeStore = useRouteStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { selectedKey } = useMenu();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
|
||||
@@ -131,10 +133,46 @@ function useMixMenu() {
|
||||
() => secondLevelMenus.value.find(menu => menu.key === activeSecondLevelMenuKey.value)?.children || []
|
||||
);
|
||||
|
||||
const hasChildLevelMenus = computed(() => childLevelMenus.value.length > 0);
|
||||
|
||||
function getDeepestLevelMenuKey(): RouteKey | null {
|
||||
if (!secondLevelMenus.value.length || !themeStore.sider.autoSelectFirstMenu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const secondLevelFirstMenu = secondLevelMenus.value[0];
|
||||
|
||||
if (!secondLevelFirstMenu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function findDeepest(menu: App.Global.Menu): RouteKey {
|
||||
if (!menu.children?.length) {
|
||||
return menu.routeKey;
|
||||
}
|
||||
|
||||
return findDeepest(menu.children[0]);
|
||||
}
|
||||
|
||||
return findDeepest(secondLevelFirstMenu);
|
||||
}
|
||||
|
||||
function activeDeepestLevelMenuKey() {
|
||||
const deepestLevelMenuKey = getDeepestLevelMenuKey();
|
||||
if (!deepestLevelMenuKey) return;
|
||||
|
||||
// select the deepest second level menu
|
||||
handleSelectSecondLevelMenu(deepestLevelMenuKey);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
() => {
|
||||
getActiveFirstLevelMenuKey();
|
||||
// if there are child level menus, get the active second level menu key
|
||||
if (hasChildLevelMenus.value) {
|
||||
getActiveSecondLevelMenuKey();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
@@ -152,7 +190,10 @@ function useMixMenu() {
|
||||
isActiveSecondLevelMenuHasChildren,
|
||||
handleSelectSecondLevelMenu,
|
||||
getActiveSecondLevelMenuKey,
|
||||
childLevelMenus
|
||||
childLevelMenus,
|
||||
hasChildLevelMenus,
|
||||
getDeepestLevelMenuKey,
|
||||
activeDeepestLevelMenuKey
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { SimpleScrollbar } from '@sa/materials';
|
||||
import type { RouteKey } from '@elegant-router/types';
|
||||
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
@@ -18,12 +19,28 @@ const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||
useMixMenuContext('TopHybridHeaderFirst');
|
||||
const {
|
||||
firstLevelMenus,
|
||||
secondLevelMenus,
|
||||
activeFirstLevelMenuKey,
|
||||
handleSelectFirstLevelMenu,
|
||||
activeDeepestLevelMenuKey
|
||||
} = useMixMenuContext('TopHybridHeaderFirst');
|
||||
const { selectedKey } = useMenu();
|
||||
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
|
||||
/**
|
||||
* Handle first level menu select
|
||||
* @param key RouteKey
|
||||
*/
|
||||
function handleSelectMenu(key: RouteKey) {
|
||||
handleSelectFirstLevelMenu(key);
|
||||
|
||||
// if there are second level menus, select the deepest one by default
|
||||
activeDeepestLevelMenuKey();
|
||||
}
|
||||
|
||||
function updateExpandedKeys() {
|
||||
if (appStore.siderCollapse || !selectedKey.value) {
|
||||
expandedKeys.value = [];
|
||||
@@ -49,7 +66,7 @@ watch(
|
||||
:options="firstLevelMenus"
|
||||
:indent="18"
|
||||
responsive
|
||||
@update:value="handleSelectFirstLevelMenu"
|
||||
@update:value="handleSelectMenu"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport :to="`#${GLOBAL_SIDER_MENU_ID}`">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { RouteKey } from '@elegant-router/types';
|
||||
import { GLOBAL_HEADER_MENU_ID, GLOBAL_SIDER_MENU_ID } from '@/constants/app';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
@@ -13,9 +14,25 @@ defineOptions({
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { routerPushByKeyWithMetaQuery } = useRouterPush();
|
||||
const { firstLevelMenus, secondLevelMenus, activeFirstLevelMenuKey, handleSelectFirstLevelMenu } =
|
||||
useMixMenuContext('TopHybridSidebarFirst');
|
||||
const {
|
||||
firstLevelMenus,
|
||||
secondLevelMenus,
|
||||
activeFirstLevelMenuKey,
|
||||
handleSelectFirstLevelMenu,
|
||||
activeDeepestLevelMenuKey
|
||||
} = useMixMenuContext('TopHybridSidebarFirst');
|
||||
const { selectedKey } = useMenu();
|
||||
|
||||
/**
|
||||
* Handle first level menu select
|
||||
* @param key RouteKey
|
||||
*/
|
||||
function handleSelectMenu(key: RouteKey) {
|
||||
handleSelectFirstLevelMenu(key);
|
||||
|
||||
// if there are second level menus, select the deepest one by default
|
||||
activeDeepestLevelMenuKey();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -37,7 +54,7 @@ const { selectedKey } = useMenu();
|
||||
:sider-collapse="appStore.siderCollapse"
|
||||
:dark-mode="themeStore.darkMode"
|
||||
:theme-color="themeStore.themeColor"
|
||||
@select="handleSelectFirstLevelMenu"
|
||||
@select="handleSelectMenu"
|
||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -34,15 +34,15 @@ const {
|
||||
isActiveSecondLevelMenuHasChildren,
|
||||
handleSelectSecondLevelMenu,
|
||||
getActiveSecondLevelMenuKey,
|
||||
childLevelMenus
|
||||
childLevelMenus,
|
||||
hasChildLevelMenus,
|
||||
activeDeepestLevelMenuKey
|
||||
} = useMixMenuContext('VerticalHybridHeaderFirst');
|
||||
const { selectedKey } = useMenu();
|
||||
|
||||
const inverted = computed(() => !themeStore.darkMode && themeStore.sider.inverted);
|
||||
|
||||
const hasChildMenus = computed(() => childLevelMenus.value.length > 0);
|
||||
|
||||
const showDrawer = computed(() => hasChildMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
||||
const showDrawer = computed(() => hasChildLevelMenus.value && (drawerVisible.value || appStore.mixSiderFixed));
|
||||
|
||||
function handleSelectMixMenu(key: RouteKey) {
|
||||
handleSelectSecondLevelMenu(key);
|
||||
@@ -52,12 +52,33 @@ function handleSelectMixMenu(key: RouteKey) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle second level menu selection based on autoSelectFirstMenu setting:
|
||||
* - When disabled: Activate first second-level menu for display only, expand third-level menu if exists
|
||||
* - When enabled: Navigate to the deepest menu automatically
|
||||
*/
|
||||
function handleSelectMenu(key: RouteKey) {
|
||||
handleSelectFirstLevelMenu(key);
|
||||
|
||||
if (secondLevelMenus.value.length > 0) {
|
||||
handleSelectMixMenu(secondLevelMenus.value[0].routeKey);
|
||||
if (secondLevelMenus.value.length === 0) return;
|
||||
|
||||
const secondFirstMenuKey = secondLevelMenus.value[0].routeKey;
|
||||
|
||||
// Case 1: autoSelectFirstMenu disabled - only activate menu for display
|
||||
if (!themeStore.sider.autoSelectFirstMenu) {
|
||||
// Check if there are third-level menus
|
||||
const hasChildren = secondLevelMenus.value.find(menu => menu.key === secondFirstMenuKey)?.children?.length;
|
||||
|
||||
// If there are third-level menus, expand them
|
||||
if (hasChildren) {
|
||||
handleSelectMixMenu(secondFirstMenuKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 2: autoSelectFirstMenu enabled - navigate to deepest menu
|
||||
activeDeepestLevelMenuKey();
|
||||
setDrawerVisible(false);
|
||||
}
|
||||
|
||||
function handleResetActiveMenu() {
|
||||
@@ -115,7 +136,9 @@ watch(
|
||||
</FirstLevelMenu>
|
||||
<div
|
||||
class="relative h-full transition-width-300"
|
||||
:style="{ width: appStore.mixSiderFixed && hasChildMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px' }"
|
||||
:style="{
|
||||
width: appStore.mixSiderFixed && hasChildLevelMenus ? themeStore.sider.mixChildMenuWidth + 'px' : '0px'
|
||||
}"
|
||||
>
|
||||
<DarkModeContainer
|
||||
class="absolute-lt h-full flex-col-stretch nowrap-hidden shadow-sm transition-all-300"
|
||||
|
||||
@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const visible = defineModel<boolean>('visible');
|
||||
|
||||
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();
|
||||
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain, homeTab } = useTabStore();
|
||||
const { SvgIconVNode } = useSvgIcon();
|
||||
|
||||
type DropdownOption = {
|
||||
@@ -64,6 +64,23 @@ const options = computed(() => {
|
||||
icon: SvgIconVNode({ icon: 'ant-design:line-outlined', fontSize: 18 })
|
||||
}
|
||||
];
|
||||
|
||||
if (props.tabId !== homeTab?.id) {
|
||||
if (isTabRetain(props.tabId)) {
|
||||
opts.push({
|
||||
key: 'unpin',
|
||||
label: $t('dropdown.unpin'),
|
||||
icon: SvgIconVNode({ icon: 'mdi:pin-off-outline', fontSize: 18 })
|
||||
});
|
||||
} else {
|
||||
opts.push({
|
||||
key: 'pin',
|
||||
label: $t('dropdown.pin'),
|
||||
icon: SvgIconVNode({ icon: 'mdi:pin-outline', fontSize: 18 })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { excludeKeys, disabledKeys } = props;
|
||||
|
||||
const result = opts.filter(opt => !excludeKeys.includes(opt.key));
|
||||
@@ -98,6 +115,12 @@ const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
|
||||
},
|
||||
closeAll() {
|
||||
clearTabs();
|
||||
},
|
||||
pin() {
|
||||
fixTab(props.tabId);
|
||||
},
|
||||
unpin() {
|
||||
unfixTab(props.tabId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const themeStore = useThemeStore();
|
||||
|
||||
const layoutMode = computed(() => themeStore.layout.mode);
|
||||
const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layoutMode.value.includes('hybrid'));
|
||||
const isHybridLayoutMode = computed(() => layoutMode.value.includes('hybrid'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -32,6 +33,12 @@ const isMixLayoutMode = computed(() => layoutMode.value.includes('mix') || layou
|
||||
<SettingItem v-if="layoutMode === 'vertical-mix'" key="5" :label="$t('theme.layout.sider.mixChildMenuWidth')">
|
||||
<NInputNumber v-model:value="themeStore.sider.mixChildMenuWidth" size="small" :step="1" class="w-120px" />
|
||||
</SettingItem>
|
||||
<SettingItem v-if="isHybridLayoutMode" key="6" :label="$t('theme.layout.sider.autoSelectFirstMenu')">
|
||||
<template #suffix>
|
||||
<IconTooltip :desc="$t('theme.layout.sider.autoSelectFirstMenuTip')" />
|
||||
</template>
|
||||
<NSwitch v-model:value="themeStore.sider.autoSelectFirstMenu" />
|
||||
</SettingItem>
|
||||
</TransitionGroup>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -188,7 +188,10 @@ const local: App.I18n.Schema = {
|
||||
collapsedWidth: 'Sider Collapsed Width',
|
||||
mixWidth: 'Mix Sider Width',
|
||||
mixCollapsedWidth: 'Mix Sider Collapse Width',
|
||||
mixChildMenuWidth: 'Mix Child Menu Width'
|
||||
mixChildMenuWidth: 'Mix Child Menu Width',
|
||||
autoSelectFirstMenu: 'Auto Select First Submenu',
|
||||
autoSelectFirstMenuTip:
|
||||
'When a first-level menu is clicked, the first submenu is automatically selected and navigated to the deepest level'
|
||||
},
|
||||
footer: {
|
||||
title: 'Footer Settings',
|
||||
@@ -1252,7 +1255,9 @@ const local: App.I18n.Schema = {
|
||||
closeOther: 'Close Other',
|
||||
closeLeft: 'Close Left',
|
||||
closeRight: 'Close Right',
|
||||
closeAll: 'Close All'
|
||||
closeAll: 'Close All',
|
||||
pin: 'Pin Tab',
|
||||
unpin: 'Unpin Tab'
|
||||
},
|
||||
icon: {
|
||||
themeConfig: 'Theme Configuration',
|
||||
|
||||
@@ -185,7 +185,9 @@ const local: App.I18n.Schema = {
|
||||
collapsedWidth: '侧边栏折叠宽度',
|
||||
mixWidth: '混合布局侧边栏宽度',
|
||||
mixCollapsedWidth: '混合布局侧边栏折叠宽度',
|
||||
mixChildMenuWidth: '混合布局子菜单宽度'
|
||||
mixChildMenuWidth: '混合布局子菜单宽度',
|
||||
autoSelectFirstMenu: '自动选择第一个子菜单',
|
||||
autoSelectFirstMenuTip: '点击一级菜单时,自动选择并导航到第一个子菜单的最深层级'
|
||||
},
|
||||
footer: {
|
||||
title: '底部设置',
|
||||
@@ -1245,7 +1247,9 @@ const local: App.I18n.Schema = {
|
||||
closeOther: '关闭其它',
|
||||
closeLeft: '关闭左侧',
|
||||
closeRight: '关闭右侧',
|
||||
closeAll: '关闭所有'
|
||||
closeAll: '关闭所有',
|
||||
pin: '固定标签',
|
||||
unpin: '取消固定'
|
||||
},
|
||||
icon: {
|
||||
themeConfig: '主题配置',
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
getTabByRoute,
|
||||
getTabIdByRoute,
|
||||
isTabInTabs,
|
||||
reorderFixedTabs,
|
||||
updateTabByI18nKey,
|
||||
updateTabsByI18nKey
|
||||
} from './shared';
|
||||
@@ -258,6 +259,48 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
||||
await clearTabs(excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix tab
|
||||
*
|
||||
* @param tabId
|
||||
*/
|
||||
function fixTab(tabId: string) {
|
||||
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
|
||||
if (tabIndex === -1) return;
|
||||
|
||||
const tab = tabs.value[tabIndex];
|
||||
const fixedCount = getFixedTabIds(tabs.value).length;
|
||||
tab.fixedIndex = fixedCount;
|
||||
|
||||
if (tabIndex !== fixedCount) {
|
||||
tabs.value.splice(tabIndex, 1);
|
||||
tabs.value.splice(fixedCount, 0, tab);
|
||||
}
|
||||
|
||||
reorderFixedTabs(tabs.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfix tab
|
||||
*
|
||||
* @param tabId
|
||||
*/
|
||||
function unfixTab(tabId: string) {
|
||||
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
|
||||
if (tabIndex === -1) return;
|
||||
|
||||
const tab = tabs.value[tabIndex];
|
||||
tab.fixedIndex = undefined;
|
||||
|
||||
const fixedCount = getFixedTabIds(tabs.value).length;
|
||||
if (tabIndex !== fixedCount) {
|
||||
tabs.value.splice(tabIndex, 1);
|
||||
tabs.value.splice(fixedCount, 0, tab);
|
||||
}
|
||||
|
||||
reorderFixedTabs(tabs.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new label of tab
|
||||
*
|
||||
@@ -328,6 +371,7 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
||||
/** All tabs */
|
||||
tabs: allTabs,
|
||||
activeTabId,
|
||||
homeTab,
|
||||
initHomeTab,
|
||||
initTabStore,
|
||||
addTab,
|
||||
@@ -338,6 +382,8 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
|
||||
clearTabs,
|
||||
clearLeftTabs,
|
||||
clearRightTabs,
|
||||
fixTab,
|
||||
unfixTab,
|
||||
switchRouteByTab,
|
||||
setTabLabel,
|
||||
resetTabLabel,
|
||||
|
||||
@@ -198,6 +198,18 @@ export function getFixedTabIds(tabs: App.Global.Tab[]) {
|
||||
return fixedTabs.map(tab => tab.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder fixed tabs fixedIndex
|
||||
*
|
||||
* @param tabs
|
||||
*/
|
||||
export function reorderFixedTabs(tabs: App.Global.Tab[]) {
|
||||
const fixedTabs = getFixedTabs(tabs);
|
||||
fixedTabs.forEach((t, i) => {
|
||||
t.fixedIndex = i;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tabs label
|
||||
*
|
||||
|
||||
@@ -48,7 +48,8 @@ export const themeSettings: App.Theme.ThemeSetting = {
|
||||
collapsedWidth: 64,
|
||||
mixWidth: 90,
|
||||
mixCollapsedWidth: 64,
|
||||
mixChildMenuWidth: 200
|
||||
mixChildMenuWidth: 200,
|
||||
autoSelectFirstMenu: false
|
||||
},
|
||||
footer: {
|
||||
visible: true,
|
||||
|
||||
Vendored
+5
-1
@@ -96,6 +96,8 @@ declare namespace App {
|
||||
mixCollapsedWidth: number;
|
||||
/** Child menu width when the layout is 'vertical-mix', 'top-hybrid-sidebar-first', or 'top-hybrid-header-first' */
|
||||
mixChildMenuWidth: number;
|
||||
/** Whether to auto select the first submenu */
|
||||
autoSelectFirstMenu: boolean;
|
||||
};
|
||||
/** Footer */
|
||||
footer: {
|
||||
@@ -296,7 +298,7 @@ declare namespace App {
|
||||
type FormRule = import('naive-ui').FormItemRule;
|
||||
|
||||
/** The global dropdown key */
|
||||
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
|
||||
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll' | 'pin' | 'unpin';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,6 +470,8 @@ declare namespace App {
|
||||
mixWidth: string;
|
||||
mixCollapsedWidth: string;
|
||||
mixChildMenuWidth: string;
|
||||
autoSelectFirstMenu: string;
|
||||
autoSelectFirstMenuTip: string;
|
||||
};
|
||||
footer: {
|
||||
title: string;
|
||||
|
||||
Vendored
-4
@@ -104,8 +104,6 @@ declare module 'vue' {
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NLayout: typeof import('naive-ui')['NLayout']
|
||||
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
||||
NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
|
||||
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||
NList: typeof import('naive-ui')['NList']
|
||||
NListItem: typeof import('naive-ui')['NListItem']
|
||||
@@ -255,8 +253,6 @@ declare global {
|
||||
const NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
const NLayout: typeof import('naive-ui')['NLayout']
|
||||
const NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
||||
const NLayoutFooter: typeof import('naive-ui')['NLayoutFooter']
|
||||
const NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
||||
const NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||
const NList: typeof import('naive-ui')['NList']
|
||||
const NListItem: typeof import('naive-ui')['NListItem']
|
||||
|
||||
Reference in New Issue
Block a user