commit
e3a9c77fd1
@ -11,6 +11,7 @@
|
|||||||
:sider-collapsed-width="siderCollapsedWidth"
|
:sider-collapsed-width="siderCollapsedWidth"
|
||||||
:sider-collapse="app.siderCollapse"
|
:sider-collapse="app.siderCollapse"
|
||||||
:fixed-footer="theme.footer.fixed"
|
:fixed-footer="theme.footer.fixed"
|
||||||
|
:footer-visible="theme.footer.visible"
|
||||||
@update:sider-collapse="app.setSiderCollapse"
|
@update:sider-collapse="app.setSiderCollapse"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
|
@ -23,6 +23,8 @@ interface Props {
|
|||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
/** 当前路由路径 */
|
/** 当前路由路径 */
|
||||||
currentPath?: string;
|
currentPath?: string;
|
||||||
|
/** 是否固定在tab卡不可关闭 */
|
||||||
|
affix?: boolean;
|
||||||
/** 鼠标x坐标 */
|
/** 鼠标x坐标 */
|
||||||
x: number;
|
x: number;
|
||||||
/** 鼠标y坐标 */
|
/** 鼠标y坐标 */
|
||||||
@ -72,7 +74,7 @@ const options = computed<Option[]>(() => [
|
|||||||
{
|
{
|
||||||
label: '关闭',
|
label: '关闭',
|
||||||
key: 'close-current',
|
key: 'close-current',
|
||||||
disabled: props.currentPath === tab.homeTab.fullPath,
|
disabled: props.currentPath === tab.homeTab.fullPath || Boolean(props.affix),
|
||||||
icon: iconRender({ icon: 'ant-design:close-outlined' })
|
icon: iconRender({ icon: 'ant-design:close-outlined' })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
:key="item.fullPath"
|
:key="item.fullPath"
|
||||||
:is-active="tab.activeTab === item.fullPath"
|
:is-active="tab.activeTab === item.fullPath"
|
||||||
:primary-color="theme.themeColor"
|
:primary-color="theme.themeColor"
|
||||||
:closable="item.name !== tab.homeTab.name"
|
:closable="!(item.name === tab.homeTab.name || item.meta.affix)"
|
||||||
:dark-mode="theme.darkMode"
|
:dark-mode="theme.darkMode"
|
||||||
:class="{ '!mr-0': isChromeMode && index === tab.tabs.length - 1, 'mr-10px': !isChromeMode }"
|
:class="{ '!mr-0': isChromeMode && index === tab.tabs.length - 1, 'mr-10px': !isChromeMode }"
|
||||||
@click="tab.handleClickTab(item.fullPath)"
|
@click="tab.handleClickTab(item.fullPath)"
|
||||||
@close="tab.removeTab(item.fullPath)"
|
@close="tab.removeTab(item.fullPath)"
|
||||||
@contextmenu="handleContextMenu($event, item.fullPath)"
|
@contextmenu="handleContextMenu($event, item.fullPath, item.meta.affix)"
|
||||||
>
|
>
|
||||||
<svg-icon
|
<svg-icon
|
||||||
:icon="item.meta.icon"
|
:icon="item.meta.icon"
|
||||||
@ -24,6 +24,7 @@
|
|||||||
<context-menu
|
<context-menu
|
||||||
:visible="dropdown.visible"
|
:visible="dropdown.visible"
|
||||||
:current-path="dropdown.currentPath"
|
:current-path="dropdown.currentPath"
|
||||||
|
:affix="dropdown.affix"
|
||||||
:x="dropdown.x"
|
:x="dropdown.x"
|
||||||
:y="dropdown.y"
|
:y="dropdown.y"
|
||||||
@update:visible="handleDropdownVisible"
|
@update:visible="handleDropdownVisible"
|
||||||
@ -66,6 +67,7 @@ async function getActiveTabClientX() {
|
|||||||
|
|
||||||
const dropdown = reactive({
|
const dropdown = reactive({
|
||||||
visible: false,
|
visible: false,
|
||||||
|
affix: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
currentPath: ''
|
currentPath: ''
|
||||||
@ -76,8 +78,8 @@ function showDropdown() {
|
|||||||
function hideDropdown() {
|
function hideDropdown() {
|
||||||
dropdown.visible = false;
|
dropdown.visible = false;
|
||||||
}
|
}
|
||||||
function setDropdown(x: number, y: number, currentPath: string) {
|
function setDropdown(x: number, y: number, currentPath: string, affix?: boolean) {
|
||||||
Object.assign(dropdown, { x, y, currentPath });
|
Object.assign(dropdown, { x, y, currentPath, affix });
|
||||||
}
|
}
|
||||||
|
|
||||||
let isClickContextMenu = false;
|
let isClickContextMenu = false;
|
||||||
@ -89,7 +91,7 @@ function handleDropdownVisible(visible: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 点击右键菜单 */
|
/** 点击右键菜单 */
|
||||||
async function handleContextMenu(e: MouseEvent, fullPath: string) {
|
async function handleContextMenu(e: MouseEvent, fullPath: string, affix?: boolean) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const { clientX, clientY } = e;
|
const { clientX, clientY } = e;
|
||||||
@ -101,7 +103,7 @@ async function handleContextMenu(e: MouseEvent, fullPath: string) {
|
|||||||
hideDropdown();
|
hideDropdown();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setDropdown(clientX, clientY, fullPath);
|
setDropdown(clientX, clientY, fullPath, affix);
|
||||||
showDropdown();
|
showDropdown();
|
||||||
isClickContextMenu = false;
|
isClickContextMenu = false;
|
||||||
}, DURATION);
|
}, DURATION);
|
||||||
|
@ -61,6 +61,9 @@
|
|||||||
<setting-menu label="固定底部">
|
<setting-menu label="固定底部">
|
||||||
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
|
<n-switch :value="theme.footer.fixed" @update:value="theme.setFooterIsFixed" />
|
||||||
</setting-menu>
|
</setting-menu>
|
||||||
|
<setting-menu label="显示底部">
|
||||||
|
<n-switch :value="theme.footer.visible" @update:value="theme.setFooterVisible" />
|
||||||
|
</setting-menu>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -110,7 +110,8 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"fixed": false,
|
"fixed": false,
|
||||||
"height": 48
|
"height": 48,
|
||||||
|
"visible": true
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
"animate": true,
|
"animate": true,
|
||||||
|
@ -88,7 +88,8 @@ const defaultThemeSetting: Theme.Setting = {
|
|||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
fixed: false,
|
fixed: false,
|
||||||
height: 48
|
height: 48,
|
||||||
|
visible: true
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
animate: true,
|
animate: true,
|
||||||
|
@ -149,6 +149,10 @@ export const useThemeStore = defineStore('theme-store', {
|
|||||||
setFooterHeight(height: number) {
|
setFooterHeight(height: number) {
|
||||||
this.footer.height = height;
|
this.footer.height = height;
|
||||||
},
|
},
|
||||||
|
/** 设置底部是否显示 */
|
||||||
|
setFooterVisible(isVisible: boolean) {
|
||||||
|
this.footer.visible = isVisible;
|
||||||
|
},
|
||||||
/** 设置切换页面时是否过渡动画 */
|
/** 设置切换页面时是否过渡动画 */
|
||||||
setPageIsAnimate(animate: boolean) {
|
setPageIsAnimate(animate: boolean) {
|
||||||
this.page.animate = animate;
|
this.page.animate = animate;
|
||||||
|
2
src/typings/route.d.ts
vendored
2
src/typings/route.d.ts
vendored
@ -60,6 +60,8 @@ declare namespace AuthRoute {
|
|||||||
activeMenu?: RouteKey;
|
activeMenu?: RouteKey;
|
||||||
/** 表示是否是多级路由的中间级路由(用于转换路由数据时筛选多级路由的标识,定义路由时不用填写) */
|
/** 表示是否是多级路由的中间级路由(用于转换路由数据时筛选多级路由的标识,定义路由时不用填写) */
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
|
/** 是否固定在tab卡不可关闭 */
|
||||||
|
affix?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Route<K extends AllRouteKey = AllRouteKey> = K extends AllRouteKey
|
type Route<K extends AllRouteKey = AllRouteKey> = K extends AllRouteKey
|
||||||
|
2
src/typings/system.d.ts
vendored
2
src/typings/system.d.ts
vendored
@ -236,6 +236,8 @@ declare namespace Theme {
|
|||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
/** 底部高度 */
|
/** 底部高度 */
|
||||||
height: number;
|
height: number;
|
||||||
|
/* 底部是否可见 */
|
||||||
|
visible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 页面样式 */
|
/** 页面样式 */
|
||||||
|
Loading…
Reference in New Issue
Block a user