refactor: ♻️ 侧边栏和页签样式重构
This commit is contained in:
parent
cd6dd5b6f1
commit
5a650c6830
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from "vue";
|
||||
import { scrollTo } from "@/utils/scroll-to";
|
||||
|
||||
const props = defineProps({
|
||||
total: {
|
||||
|
|
@ -63,17 +62,11 @@ const pageSize = useVModel(props, "limit", emit);
|
|||
|
||||
function handleSizeChange(val: number) {
|
||||
emit("pagination", { page: currentPage, limit: val });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
currentPage.value = val;
|
||||
emit("pagination", { page: val, limit: props.limit });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:default-active="currentRoute.path"
|
||||
:default-active="activeMenu"
|
||||
:collapse-transition="false"
|
||||
:collapse="!appStore.sidebarOpen"
|
||||
:unique-opened="false"
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
import { useAppStore } from "@/store";
|
||||
import router from "@/router";
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
menus: {
|
||||
required: true,
|
||||
default: () => {
|
||||
|
|
@ -28,7 +28,9 @@ defineProps({
|
|||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const currentRoute = useRoute(); // 当前路由
|
||||
const activeMenu = computed(() => {
|
||||
return router.currentRoute.value.path;
|
||||
});
|
||||
|
||||
/**
|
||||
* 菜单激活回调
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<!-- 当前路由没有子菜单项 -->
|
||||
<el-menu-item v-if="showMenuItem" :index="item.path">
|
||||
<!-- 没有子菜单 -->
|
||||
<el-menu-item v-if="showMenuItem" :index="item.path" :key="item.path">
|
||||
<menu-title
|
||||
:icon="onlyOneChild.meta?.icon"
|
||||
:title="onlyOneChild.meta?.title"
|
||||
/>
|
||||
</el-menu-item>
|
||||
|
||||
<!-- 当前路由有子菜单项 -->
|
||||
<el-sub-menu :index="item.path" v-else-if="!item.meta?.hidden">
|
||||
<!-- 有子菜单 -->
|
||||
<el-sub-menu v-else-if="!item.meta?.hidden" :index="item.path">
|
||||
<template #title>
|
||||
<menu-title :icon="item.meta?.icon" :title="item.meta?.title" />
|
||||
</template>
|
||||
|
|
@ -35,7 +35,7 @@ const showMenuItem = computed(() => {
|
|||
);
|
||||
});
|
||||
|
||||
// 用于存储当前路由的唯一子菜单 ( 如果 item 的 chidren 只有一个子菜单,onlyOneChild 存储的就是这个子菜单)
|
||||
// 临时变量,存储唯一子菜单,如果有多个子菜单,只会存储最后一个子菜单
|
||||
const onlyOneChild = ref<any>(null);
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +45,7 @@ function hasSingleShowingChild(
|
|||
children: RouteRecordRaw[] = [],
|
||||
parent: RouteRecordRaw
|
||||
) {
|
||||
// 过滤出显示的子菜单
|
||||
//子菜单
|
||||
const showingChildren = children.filter((route: RouteRecordRaw) => {
|
||||
if (route.meta?.hidden) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<svg-icon v-if="icon" :icon-name="icon" />
|
||||
<span v-if="title" class="ml-1">{{ translateRouteTitle(title) }}</span>
|
||||
<span class="title" v-if="title">{{ translateRouteTitle(title) }}</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -17,3 +17,17 @@ defineProps({
|
|||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-menu {
|
||||
.title {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&--collapse {
|
||||
.title {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!--侧边栏 -->
|
||||
<template>
|
||||
<el-aside class="flex flex-col" :width="sideBarWidth">
|
||||
<el-aside class="sidebar-container" :width="sideBarWidth">
|
||||
<Logo
|
||||
v-if="settingsStore.showSidebarLogo"
|
||||
:collapse="!appStore.sidebarOpen"
|
||||
|
|
@ -20,7 +20,79 @@ const sideBarWidth = computed(() => {
|
|||
if (appStore.isMobile) {
|
||||
return appStore.sidebarOpen ? "210px" : "0";
|
||||
} else {
|
||||
return appStore.sidebarOpen ? "210px" : "54px";
|
||||
return appStore.sidebarOpen ? "210px" : "64px";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-menu {
|
||||
--el-menu-hover-bg-color: transparent;
|
||||
--el-menu-bg-color: transparent;
|
||||
|
||||
&--collapse {
|
||||
.el-sub-menu,
|
||||
.el-menu-item {
|
||||
justify-content: center;
|
||||
|
||||
&__title {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu {
|
||||
&.is-active > .el-sub-menu__title {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-sub-menu__title {
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
position: absolute;
|
||||
inset: 2px 5px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-fill-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.is-active > * {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
position: absolute;
|
||||
inset: 2px 5px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-fill-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&.is-active::before {
|
||||
position: absolute;
|
||||
inset: 2px 5px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-color-primary-light-8);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@
|
|||
:name="item.fullPath"
|
||||
>
|
||||
<template #label>
|
||||
<div @contextmenu.prevent="openContextmenu($event, item)">
|
||||
<span>{{ item.title }}</span>
|
||||
|
||||
<div
|
||||
@contextmenu.prevent="openContextmenu($event, item)"
|
||||
class="flex flex-center"
|
||||
>
|
||||
<span>{{ translateRouteTitle(item.title) }}</span>
|
||||
<el-icon
|
||||
v-if="!item.affix"
|
||||
:size="12"
|
||||
class="mt-[5px] ml-[5px] hover:bg-primary rounded-full hover:color-white"
|
||||
class="ml-1 hover:bg-primary rounded-full hover:color-white"
|
||||
@click.stop="closeTag(item)"
|
||||
>
|
||||
<i-ep-close />
|
||||
|
|
@ -23,13 +25,13 @@
|
|||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div
|
||||
<!-- <div
|
||||
class="flex-center border border-gray-300 cursor-pointer w-[40px]"
|
||||
@click="refreshSelectedTag(selectedTag)"
|
||||
>
|
||||
<svg-icon icon-name="refresh" />
|
||||
</div>
|
||||
|
||||
-->
|
||||
<!-- tag标签操作菜单 -->
|
||||
<ul
|
||||
v-show="contextmenuVisible"
|
||||
|
|
@ -68,6 +70,7 @@
|
|||
import { useTagsViewStore, usePermissionStore } from "@/store";
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { TabPaneName } from "element-plus";
|
||||
import { translateRouteTitle } from "@/utils/i18n";
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
|
@ -94,7 +97,7 @@ watch(
|
|||
activeName.value = newFullPath;
|
||||
},
|
||||
{
|
||||
immediate: true, //初始化立即执行
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -184,10 +187,10 @@ function closeTag(view: TagView) {
|
|||
/**
|
||||
* 切换页签
|
||||
*
|
||||
* @param targetFullPath
|
||||
* @param targetPath
|
||||
*/
|
||||
function changeTag(targetFullPath: TabPaneName) {
|
||||
router.push(targetFullPath as string);
|
||||
function changeTag(targetPath: TabPaneName) {
|
||||
router.push(targetPath as string);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -379,14 +382,15 @@ onMounted(() => {
|
|||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs) {
|
||||
width: calc(100% - 40px);
|
||||
width: 100%;
|
||||
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
|
||||
.el-tabs__item {
|
||||
height: 30px;
|
||||
padding: 0 2px;
|
||||
margin-top: 4px;
|
||||
margin-left: 8px;
|
||||
padding: 0 8px !important;
|
||||
margin: 4px 0 0 5px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 2px;
|
||||
|
||||
|
|
@ -395,10 +399,6 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
|
||||
.is-icon-close:hover {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.is-active {
|
||||
background: var(--el-color-primary-light-8);
|
||||
border-color: var(--el-color-primary-light-3);
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||
{
|
||||
path: "/dashboard",
|
||||
component: () => import("@/views/dashboard/index.vue"),
|
||||
name: "Dashboard", // 用于 keep-alive, 必须与SFC自动推导或者显示声明的组件name一致
|
||||
// https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
|
||||
name: "Dashboard", // 用于 keep-alive, 必须与SFC自动推导或者显示声明的组件name一致 https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
|
||||
meta: {
|
||||
title: "dashboard",
|
||||
icon: "homepage",
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
.el-menu {
|
||||
--el-menu-hover-bg-color: transparent;
|
||||
--el-menu-bg-color: transparent;
|
||||
|
||||
.el-sub-menu {
|
||||
&.is-active > .el-sub-menu__title {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-sub-menu__title {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
position: absolute;
|
||||
inset: 2px 12px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-fill-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.is-active > * {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
position: absolute;
|
||||
inset: 2px 12px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-fill-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&.is-active::before {
|
||||
position: absolute;
|
||||
inset: 2px 12px;
|
||||
clear: both;
|
||||
content: "";
|
||||
background: var(--el-color-primary-light-8);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
}
|
||||
t--;
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b;
|
||||
};
|
||||
|
||||
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||
const requestAnimFrame = (function () {
|
||||
return (
|
||||
window.requestAnimationFrame ||
|
||||
(window as any).webkitRequestAnimationFrame ||
|
||||
(window as any).mozRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
}
|
||||
);
|
||||
})();
|
||||
|
||||
/**
|
||||
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||
* @param {number} amount
|
||||
*/
|
||||
const move = (amount: number) => {
|
||||
document.documentElement.scrollTop = amount;
|
||||
(document.body.parentNode as HTMLElement).scrollTop = amount;
|
||||
document.body.scrollTop = amount;
|
||||
};
|
||||
|
||||
const position = () => {
|
||||
return (
|
||||
document.documentElement.scrollTop ||
|
||||
(document.body.parentNode as HTMLElement).scrollTop ||
|
||||
document.body.scrollTop
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} to
|
||||
* @param {number} duration
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export const scrollTo = (to: number, duration: number, callback?: any) => {
|
||||
const start = position();
|
||||
const change = to - start;
|
||||
const increment = 20;
|
||||
let currentTime = 0;
|
||||
duration = typeof duration === "undefined" ? 500 : duration;
|
||||
const animateScroll = function () {
|
||||
// increment the time
|
||||
currentTime += increment;
|
||||
// find the value with the quadratic in-out easing function
|
||||
const val = easeInOutQuad(currentTime, start, change, duration);
|
||||
// move the document.body
|
||||
move(val);
|
||||
// do the animation unless its over
|
||||
if (currentTime < duration) {
|
||||
requestAnimFrame(animateScroll);
|
||||
} else {
|
||||
if (callback && typeof callback === "function") {
|
||||
// the animation is done so lets callback
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
animateScroll();
|
||||
};
|
||||
Loading…
Reference in New Issue