refactor: remove project settings panel

This commit is contained in:
gin
2026-05-26 21:08:58 +08:00
parent 2a702fa6a9
commit ebccb7283f
27 changed files with 79 additions and 989 deletions
@@ -3,18 +3,13 @@ package com.agileboot.common.enums.common;
import com.agileboot.common.enums.BasicEnum;
/**
* 系统配置
* 系统配置,对应 sys_config 表的 config_key 字段。
*
* @author valarchie
* 对应 sys_config表的config_key字段
*/
public enum ConfigKeyEnum implements BasicEnum<String> {
/**
* 菜单类型
*/
SKIN_THEME("sys.index.skinName", "系统皮肤主题"),
INIT_PASSWORD("sys.user.initPassword", "初始密码"),
SIDE_BAR_THEME("sys.index.sideTheme", "侧边栏开关"),
CAPTCHA("sys.account.captchaOnOff", "验证码开关"),
REGISTER("sys.account.registerUser", "注册开放功能");
@@ -36,5 +31,4 @@ public enum ConfigKeyEnum implements BasicEnum<String> {
return description;
}
}
@@ -19,9 +19,7 @@ Date: 2022-10-07 16:05:40
-- ----------------------------
-- Records of sys_config
-- ----------------------------
INSERT INTO `sys_config` VALUES ('1', '主框架页-默认皮肤样式名称', 'sys.index.skinName', '["skin-blue","skin-green","skin-purple","skin-red","skin-yellow"]', 'skin-blue', '1', null, null, '2022-08-28 22:12:19', '2022-05-21 08:30:55', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow', '0');
INSERT INTO `sys_config` VALUES ('2', '用户管理-账号初始密码', 'sys.user.initPassword', '', '1234567', '1', null, null, '2022-08-28 21:54:19', '2022-05-21 08:30:55', '初始化密码 123456', '0');
INSERT INTO `sys_config` VALUES ('3', '主框架页-侧边栏主题', 'sys.index.sideTheme', '["theme-dark","theme-light"]', 'theme-dark', '1', null, null, '2022-08-28 22:12:15', '2022-08-20 08:30:55', '深色主题theme-dark,浅色主题theme-light', '0');
INSERT INTO `sys_config` VALUES ('4', '账号自助-验证码开关', 'sys.account.captchaOnOff', '["true","false"]', 'false', '0', null, null, '2022-08-28 22:03:37', '2022-05-21 08:30:55', '是否开启验证码功能(true开启,false关闭)', '0');
INSERT INTO `sys_config` VALUES ('5', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', '["true","false"]', 'true', '0', null, '1', '2022-10-05 22:18:57', '2022-05-21 08:30:55', '是否开启注册用户功能(true开启,false关闭)', '0');
@@ -1,8 +1,6 @@
INSERT INTO app.sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', '["skin-blue","skin-green","skin-purple","skin-red","skin-yellow"]', 'skin-blue', true, null, null, '2022-08-28 22:12:19', '2022-05-21 08:30:55', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow', 0);
INSERT INTO app.sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '', '123456', true, null, 1, '2023-07-20 14:42:08', '2022-05-21 08:30:55', '初始化密码 123456', 0);
INSERT INTO app.sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', '["theme-dark","theme-light"]', 'theme-dark', true, null, null, '2022-08-28 22:12:15', '2022-08-20 08:30:55', '深色主题theme-dark,浅色主题theme-light', 0);
INSERT INTO app.sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (4, '账号自助-验证码开关', 'sys.account.captchaOnOff', '["true","false"]', 'false', false, null, 1, '2023-07-20 14:39:36', '2022-05-21 08:30:55', '是否开启验证码功能(true开启,false关闭)', 0);
INSERT INTO app.sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', '["true","false"]', 'true', false, null, 1, '2022-10-05 22:18:57', '2022-05-21 08:30:55', '是否开启注册用户功能(true开启,false关闭)', 0);
-2
View File
@@ -23,9 +23,7 @@ create table sys_config
)
comment '参数配置表';
INSERT INTO sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', '["skin-blue","skin-green","skin-purple","skin-red","skin-yellow"]', 'skin-blue', 1, null, null, '2022-08-28 22:12:19', '2022-05-21 08:30:55', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow', 0);
INSERT INTO sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '', '123456', 1, null, 1, '2023-07-20 14:42:08', '2022-05-21 08:30:55', '初始化密码 123456', 0);
INSERT INTO sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', '["theme-dark","theme-light"]', 'theme-dark', 1, null, null, '2022-08-28 22:12:15', '2022-08-20 08:30:55', '深色主题theme-dark,浅色主题theme-light', 0);
INSERT INTO sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (4, '账号自助-验证码开关', 'sys.account.captchaOnOff', '["true","false"]', 'false', 0, null, 1, '2023-07-20 14:39:36', '2022-05-21 08:30:55', '是否开启验证码功能(true开启,false关闭)', 0);
INSERT INTO sys_config (config_id, config_name, config_key, config_options, config_value, is_allow_change, creator_id, updater_id, update_time, create_time, remark, deleted) VALUES (5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', '["true","false"]', 'true', 0, null, 1, '2022-10-05 22:18:57', '2022-05-21 08:30:55', '是否开启注册用户功能(true开启,false关闭)', 0);
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
"dev": "vite",
"serve": "pnpm dev",
"build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 vite build",
"build:staging": "rimraf dist && vite build --mode staging",
-5
View File
@@ -8,13 +8,8 @@
"Layout": "vertical",
"Theme": "default",
"DarkMode": false,
"Grey": false,
"Weak": false,
"HideTabs": false,
"SidebarStatus": true,
"EpThemeColor": "#409EFF",
"ShowLogo": true,
"ShowModel": "smart",
"MenuArrowIconNoTransition": true,
"CachingAsyncRoutes": false,
"TooltipEffect": "light",
+2 -16
View File
@@ -8,7 +8,7 @@ const props = defineProps({
fixedHeader: Boolean
});
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
const { $config } = useGlobal<GlobalPropertiesApi>();
const keepAlive = computed(() => {
return $config?.KeepAlive;
@@ -20,22 +20,8 @@ const transitions = computed(() => {
};
});
const hideTabs = computed(() => {
return $storage?.configure.hideTabs;
});
const layout = computed(() => {
return $storage?.layout.layout === "vertical";
});
const getSectionStyle = computed(() => {
return [
hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 85px;" : "",
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
props.fixedHeader ? "" : "padding-top: 0;"
];
return ["padding-top: 85px;", props.fixedHeader ? "" : "padding-top: 0;"];
});
const transitionMain = defineComponent({
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
const { dataTheme, dataThemeChange } = useDataThemeChange();
</script>
<template>
<span class="header-dark-switch navbar-bg-hover" title="切换暗黑模式">
<el-switch
v-model="dataTheme"
inline-prompt
:active-icon="dayIcon"
:inactive-icon="darkIcon"
@change="dataThemeChange"
/>
</span>
</template>
<style lang="scss" scoped>
.header-dark-switch {
display: flex;
align-items: center;
height: 48px;
padding: 0 10px;
}
</style>
+2 -12
View File
@@ -2,18 +2,17 @@
import Search from "./search/index.vue";
import Notice from "./notice/index.vue";
import mixNav from "./sidebar/mixNav.vue";
import HeaderDarkSwitch from "./headerDarkSwitch.vue";
import { useNav } from "@/layout/hooks/useNav";
import Breadcrumb from "./sidebar/breadCrumb.vue";
import topCollapse from "./sidebar/topCollapse.vue";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
const {
layout,
device,
logout,
userProfile,
onPanel,
pureApp,
username,
userAvatar,
@@ -41,11 +40,9 @@ const {
<mixNav v-if="layout === 'mix'" />
<div v-if="layout === 'vertical'" class="vertical-header-right">
<!-- 菜单搜索 -->
<Search />
<!-- 通知 -->
<Notice id="header-notice" />
<!-- 退出登录 -->
<HeaderDarkSwitch />
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
<img :src="userAvatar" :style="avatarsStyle" />
@@ -72,13 +69,6 @@ const {
</el-dropdown-menu>
</template>
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
title="打开项目配置"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
</span>
</div>
</div>
</template>
@@ -1,158 +0,0 @@
<script setup lang="ts">
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import Close from "@iconify-icons/ep/close";
const target = ref(null);
const show = ref<Boolean>(false);
const iconClass = computed(() => {
return [
"mr-[20px]",
"outline-none",
"width-[20px]",
"height-[20px]",
"rounded-[4px]",
"cursor-pointer",
"transition-colors",
"hover:bg-[#0000000f]",
"dark:hover:bg-[#ffffff1f]",
"dark:hover:text-[#ffffffd9]"
];
});
onClickOutside(target, (event: any) => {
if (event.clientX > target.value.offsetLeft) return;
show.value = false;
});
onMounted(() => {
emitter.on("openPanel", () => {
show.value = true;
});
});
onBeforeUnmount(() => {
// `openPanel`
emitter.off("openPanel");
});
</script>
<template>
<div :class="{ show: show }" class="right-panel-container">
<div class="right-panel-background" />
<div ref="target" class="right-panel bg-bg_color">
<div class="right-panel-items">
<div class="project-configuration">
<h4 class="dark:text-white">项目配置</h4>
<span title="关闭配置" :class="iconClass">
<IconifyIconOffline
class="dark:text-white"
width="20px"
height="20px"
:icon="Close"
@click="show = !show"
/>
</span>
</div>
<div
class="border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]"
/>
<slot />
</div>
</div>
</div>
</template>
<style>
.showright-panel {
position: relative;
width: calc(100% - 15px);
overflow: hidden;
}
</style>
<style lang="scss" scoped>
.right-panel-background {
position: fixed;
top: 0;
left: 0;
z-index: -1;
background: rgb(0 0 0 / 20%);
opacity: 0;
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
}
.right-panel {
position: fixed;
top: 0;
right: 0;
z-index: 40000;
width: 100%;
max-width: 315px;
height: 100vh;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%);
}
.show {
transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
.right-panel-background {
z-index: 20000;
width: 100%;
height: 100%;
opacity: 1;
}
.right-panel {
transform: translate(0);
}
}
.handle-button {
position: absolute;
top: 45%;
left: -48px;
z-index: 0;
width: 48px;
height: 48px;
font-size: 24px;
line-height: 48px;
color: #fff;
text-align: center;
pointer-events: auto;
cursor: pointer;
background: rgb(24 144 255);
border-radius: 6px 0 0 6px !important;
i {
font-size: 24px;
line-height: 48px;
}
}
.right-panel-items {
height: calc(100vh - 60px);
margin-top: 60px;
overflow-y: auto;
}
.project-configuration {
position: fixed;
top: 15px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 30px;
margin-left: 10px;
}
:deep(.el-divider--horizontal) {
width: 90%;
margin: 20px auto 0;
}
</style>
@@ -1,529 +0,0 @@
<script setup lang="ts">
import {
ref,
unref,
watch,
reactive,
computed,
nextTick,
onBeforeMount
} from "vue";
import {
useDark,
debounce,
useGlobal,
storageLocal,
storageSession
} from "@pureadmin/utils";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "@/utils/mitt";
import { resetRouter } from "@/router";
import { removeToken } from "@/utils/auth";
import { routerArrays } from "@/layout/types";
import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import Check from "@iconify-icons/ep/check";
import Logout from "@iconify-icons/ri/logout-circle-r-line";
const router = useRouter();
const { isDark } = useDark();
const { device, tooltipEffect } = useNav();
const { $storage } = useGlobal<GlobalPropertiesApi>();
const mixRef = ref();
const verticalRef = ref();
const horizontalRef = ref();
const {
dataTheme,
layoutTheme,
themeColors,
dataThemeChange,
setEpThemeColor,
setLayoutThemeColor
} = useDataThemeChange();
/* body添加layout属性,作用于src/style/sidebar.scss */
if (unref(layoutTheme)) {
const layout = unref(layoutTheme).layout;
const theme = unref(layoutTheme).theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
});
setLayoutModel(layout);
}
/** 默认灵动模式 */
const markValue = ref($storage.configure?.showModel ?? "smart");
const logoVal = ref($storage.configure?.showLogo ?? true);
const settings = reactive({
greyVal: $storage.configure.grey,
weakVal: $storage.configure.weak,
tabsVal: $storage.configure.hideTabs,
showLogo: $storage.configure.showLogo,
showModel: $storage.configure.showModel,
multiTagsCache: $storage.configure.multiTagsCache
});
const getThemeColorStyle = computed(() => {
return color => {
return { background: color };
};
});
/** 当网页为暗黑模式时不显示亮白色切换选项 */
const showThemeColors = computed(() => {
return themeColor => {
return themeColor === "light" && isDark.value ? false : true;
};
});
function storageConfigureChange<T>(key: string, val: T): void {
const storageConfigure = $storage.configure;
storageConfigure[key] = val;
$storage.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
className = className.replace(clsName, "").trim();
targetEl.className = flag ? `${className} ${clsName} ` : className;
}
/** 灰色模式设置 */
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
storageConfigureChange("grey", value);
};
/** 色弱模式设置 */
const weekChange = (value): void => {
toggleClass(
settings.weakVal,
"html-weakness",
document.querySelector("html")
);
storageConfigureChange("weak", value);
};
const tagsChange = () => {
const showVal = settings.tabsVal;
storageConfigureChange("hideTabs", showVal);
emitter.emit("tagViewsChange", showVal as unknown as string);
};
const multiTagsCacheChange = () => {
const multiTagsCache = settings.multiTagsCache;
storageConfigureChange("multiTagsCache", multiTagsCache);
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal().clear();
storageSession().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html"));
router.push("/login");
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
resetRouter();
}
function onChange(label) {
storageConfigureChange("showModel", label);
emitter.emit("tagViewsShowModel", label);
}
/** 侧边栏Logo */
function logoChange() {
unref(logoVal)
? storageConfigureChange("showLogo", true)
: storageConfigureChange("showLogo", false);
emitter.emit("logoChange", unref(logoVal));
}
function setFalse(Doms): any {
Doms.forEach(v => {
toggleClass(false, "is-select", unref(v));
});
}
/** 主题色 激活选择项 */
const getThemeColor = computed(() => {
return current => {
if (
current === layoutTheme.value.theme &&
layoutTheme.value.theme !== "light"
) {
return "#fff";
} else if (
current === layoutTheme.value.theme &&
layoutTheme.value.theme === "light"
) {
return "#1d2b45";
} else {
return "transparent";
}
};
});
/** 设置导航模式 */
function setLayoutModel(layout: string) {
layoutTheme.value.layout = layout;
window.document.body.setAttribute("layout", layout);
$storage.layout = {
layout,
theme: layoutTheme.value.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
};
useAppStoreHook().setLayout(layout);
}
watch($storage, ({ layout }) => {
switch (layout["layout"]) {
case "vertical":
toggleClass(true, "is-select", unref(verticalRef));
debounce(setFalse([horizontalRef]), 50);
debounce(setFalse([mixRef]), 50);
break;
case "horizontal":
toggleClass(true, "is-select", unref(horizontalRef));
debounce(setFalse([verticalRef]), 50);
debounce(setFalse([mixRef]), 50);
break;
case "mix":
toggleClass(true, "is-select", unref(mixRef));
debounce(setFalse([verticalRef]), 50);
debounce(setFalse([horizontalRef]), 50);
break;
}
});
onBeforeMount(() => {
/* 初始化项目配置 */
nextTick(() => {
settings.greyVal &&
document.querySelector("html")?.setAttribute("class", "html-grey");
settings.weakVal &&
document.querySelector("html")?.setAttribute("class", "html-weakness");
settings.tabsVal && tagsChange();
});
});
</script>
<template>
<panel>
<el-divider>主题</el-divider>
<el-switch
v-model="dataTheme"
inline-prompt
class="pure-datatheme"
:active-icon="dayIcon"
:inactive-icon="darkIcon"
@change="dataThemeChange"
/>
<el-divider>导航栏模式</el-divider>
<ul class="pure-theme">
<el-tooltip
:effect="tooltipEffect"
class="item"
content="左侧模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
ref="verticalRef"
@click="setLayoutModel('vertical')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="顶部模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
ref="horizontalRef"
@click="setLayoutModel('horizontal')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="混合模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
ref="mixRef"
@click="setLayoutModel('mix')"
>
<div />
<div />
</li>
</el-tooltip>
</ul>
<el-divider>主题色</el-divider>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
:key="index"
v-show="showThemeColors(item.themeColor)"
:style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
style="margin: 0.1em 0.1em 0 0"
:size="17"
:color="getThemeColor(item.themeColor)"
>
<IconifyIconOffline :icon="Check" />
</el-icon>
</li>
</ul>
<el-divider>界面显示</el-divider>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">侧边栏Logo</span>
<el-switch
v-model="logoVal"
inline-prompt
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">标签页持久化</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="multiTagsCacheChange"
/>
</li>
<li>
<span class="dark:text-white">标签风格</span>
<el-radio-group v-model="markValue" size="small" @change="onChange">
<el-radio label="card">卡片</el-radio>
<el-radio label="smart">灵动</el-radio>
</el-radio-group>
</li>
</ul>
<el-divider />
<el-button
type="danger"
style="width: 90%; margin: 24px 15px"
@click="onReset"
>
<IconifyIconOffline
:icon="Logout"
width="15"
height="15"
style="margin-right: 4px"
/>
清空缓存并返回登录页
</el-button>
</panel>
</template>
<style lang="scss" scoped>
:deep(.el-divider__text) {
font-size: 16px;
font-weight: 700;
}
.is-select {
border: 2px solid var(--el-color-primary);
}
.setting {
width: 100%;
li {
display: flex;
align-items: center;
justify-content: space-between;
margin: 25px;
}
}
.pure-datatheme {
display: block;
width: 100%;
height: 50px;
padding-top: 25px;
text-align: center;
}
.pure-theme {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
height: 50px;
margin-top: 25px;
li {
position: relative;
width: 18%;
height: 45px;
overflow: hidden;
cursor: pointer;
background: #f0f2f5;
border-radius: 4px;
box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%);
&:nth-child(1) {
div {
&:nth-child(1) {
width: 30%;
height: 100%;
background: #1b2a47;
}
&:nth-child(2) {
position: absolute;
top: 0;
right: 0;
width: 70%;
height: 30%;
background: #fff;
box-shadow: 0 0 1px #888;
}
}
}
&:nth-child(2) {
div {
&:nth-child(1) {
width: 100%;
height: 30%;
background: #1b2a47;
box-shadow: 0 0 1px #888;
}
}
}
&:nth-child(3) {
div {
&:nth-child(1) {
width: 100%;
height: 30%;
background: #1b2a47;
box-shadow: 0 0 1px #888;
}
&:nth-child(2) {
position: absolute;
bottom: 0;
left: 0;
width: 30%;
height: 70%;
background: #fff;
box-shadow: 0 0 1px #888;
}
}
}
}
}
.theme-color {
display: flex;
justify-content: center;
width: 100%;
height: 40px;
margin-top: 20px;
li {
float: left;
width: 20px;
height: 20px;
margin-top: 8px;
margin-right: 8px;
font-weight: 700;
text-align: center;
cursor: pointer;
border-radius: 2px;
&:nth-child(2) {
border: 1px solid #ddd;
}
}
}
</style>
@@ -2,12 +2,12 @@
import Search from "../search/index.vue";
import Notice from "../notice/index.vue";
import SidebarItem from "./sidebarItem.vue";
import HeaderDarkSwitch from "../headerDarkSwitch.vue";
import { isAllEmpty } from "@pureadmin/utils";
import { ref, nextTick, computed } from "vue";
import { useNav } from "@/layout/hooks/useNav";
import { usePermissionStoreHook } from "@/store/modules/permission";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
const menuRef = ref();
@@ -16,7 +16,6 @@ const {
title,
logout,
backTopMenu,
onPanel,
username,
userAvatar,
avatarsStyle
@@ -55,11 +54,9 @@ nextTick(() => {
/>
</el-menu>
<div class="horizontal-header-right">
<!-- 菜单搜索 -->
<Search />
<!-- 通知 -->
<Notice id="header-notice" />
<!-- 退出登录 -->
<HeaderDarkSwitch />
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover">
<img :src="userAvatar" :style="avatarsStyle" />
@@ -77,13 +74,6 @@ nextTick(() => {
</el-dropdown-menu>
</template>
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
title="打开项目配置"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
</span>
</div>
</div>
</template>
@@ -2,6 +2,7 @@
import extraIcon from "./extraIcon.vue";
import Search from "../search/index.vue";
import Notice from "../notice/index.vue";
import HeaderDarkSwitch from "../headerDarkSwitch.vue";
import { isAllEmpty } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
@@ -9,7 +10,6 @@ import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { getParentPaths, findRouteByPath } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line";
const menuRef = ref();
const defaultActive = ref(null);
@@ -18,7 +18,6 @@ const {
route,
device,
logout,
onPanel,
resolvePath,
username,
userAvatar,
@@ -28,7 +27,6 @@ const {
function getDefaultActive(routePath) {
const wholeMenus = usePermissionStoreHook().wholeMenus;
/** 当前路由的父级路径 */
const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
defaultActive.value = !isAllEmpty(route.meta?.activePath)
? route.meta.activePath
@@ -54,8 +52,8 @@ watch(
<template>
<div
v-if="device !== 'mobile'"
class="horizontal-header"
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header"
>
<el-menu
router
@@ -88,11 +86,9 @@ watch(
</el-menu-item>
</el-menu>
<div class="horizontal-header-right">
<!-- 菜单搜索 -->
<Search />
<!-- 通知 -->
<Notice id="header-notice" />
<!-- 退出登录 -->
<HeaderDarkSwitch />
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
<img :src="userAvatar" :style="avatarsStyle" />
@@ -110,13 +106,6 @@ watch(
</el-dropdown-menu>
</template>
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
title="打开项目配置"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />
</span>
</div>
</div>
</template>
@@ -1,23 +1,15 @@
<script setup lang="ts">
import Logo from "./logo.vue";
import { useRoute } from "vue-router";
import { emitter } from "@/utils/mitt";
import SidebarItem from "./sidebarItem.vue";
import leftCollapse from "./leftCollapse.vue";
import { useNav } from "@/layout/hooks/useNav";
import { responsiveStorageNameSpace } from "@/config";
import { storageLocal, isAllEmpty } from "@pureadmin/utils";
import { isAllEmpty } from "@pureadmin/utils";
import { findRouteByPath, getParentPaths } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
import { ref, computed, watch, onMounted } from "vue";
const route = useRoute();
const showLogo = ref(
storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure`
)?.showLogo ?? true
);
const { device, pureApp, isCollapse, menuSelect, toggleSideBar } = useNav();
const subMenuData = ref([]);
@@ -37,21 +29,18 @@ const defaultActive = computed(() =>
);
function getSubMenuData() {
let path = "";
path = defaultActive.value;
const path = defaultActive.value;
subMenuData.value = [];
// path
const parentPathArr = getParentPaths(
path,
usePermissionStoreHook().wholeMenus
);
//
const parenetRoute = findRouteByPath(
const parentRoute = findRouteByPath(
parentPathArr[0] || path,
usePermissionStoreHook().wholeMenus
);
if (!parenetRoute?.children) return;
subMenuData.value = parenetRoute?.children;
if (!parentRoute?.children) return;
subMenuData.value = parentRoute.children;
}
watch(
@@ -65,24 +54,12 @@ watch(
onMounted(() => {
getSubMenuData();
emitter.on("logoChange", key => {
showLogo.value = key;
});
});
onBeforeUnmount(() => {
// `logoChange`
emitter.off("logoChange");
});
</script>
<template>
<div
v-loading="loading"
:class="['sidebar-container', showLogo ? 'has-logo' : '']"
>
<Logo v-if="showLogo" :collapse="isCollapse" />
<div v-loading="loading" :class="['sidebar-container', 'has-logo']">
<Logo :collapse="isCollapse" />
<el-scrollbar
wrap-class="scrollbar-wrapper"
:class="[device === 'mobile' ? 'mobile' : 'pc']"
@@ -243,25 +243,6 @@
}
}
/* 卡片模式下鼠标移入显示蓝色边框 */
.card-in {
color: var(--el-color-primary);
a {
color: var(--el-color-primary);
}
}
/* 卡片模式下鼠标移出隐藏蓝色边框 */
.card-out {
color: #666;
border: none;
a {
color: #666;
}
}
/* 灵动模式 */
.schedule-active {
position: absolute;
@@ -21,13 +21,11 @@ const {
route,
router,
visible,
showTags,
instance,
multiTags,
tagsViews,
buttonTop,
buttonLeft,
showModel,
translateX,
pureSetting,
activeIndex,
@@ -477,17 +475,6 @@ onMounted(() => {
//
showMenuModel(route.fullPath);
//
emitter.on("tagViewsChange", (key: any) => {
if (unref(showTags as any) === key) return;
(showTags as any).value = key;
});
//
emitter.on("tagViewsShowModel", key => {
showModel.value = key;
});
//
emitter.on("changLayoutRoute", indexPath => {
dynamicRouteTag(indexPath);
@@ -503,15 +490,13 @@ onMounted(() => {
});
onBeforeUnmount(() => {
// `tagViewsChange``tagViewsShowModel``changLayoutRoute`
emitter.off("tagViewsChange");
emitter.off("tagViewsShowModel");
//
emitter.off("changLayoutRoute");
});
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<div ref="containerDom" class="tags-view">
<span v-show="isShowArrow" class="arrow-left">
<IconifyIconOffline :icon="ArrowLeftSLine" @click="handleScroll(200)" />
</span>
@@ -521,13 +506,7 @@ onBeforeUnmount(() => {
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
:key="index"
:class="[
'scroll-item is-closable',
linkIsActive(item),
route.path === item.path && showModel === 'card'
? 'card-active'
: ''
]"
:class="['scroll-item is-closable', linkIsActive(item)]"
@contextmenu.prevent="openMenu(item, $event)"
@mouseenter.prevent="onMouseenter(index)"
@mouseleave.prevent="onMouseleave(index)"
@@ -549,11 +528,7 @@ onBeforeUnmount(() => {
>
<IconifyIconOffline :icon="CloseBold" />
</span>
<div
:ref="'schedule' + index"
v-if="showModel !== 'card'"
:class="[scheduleIsActive(item)]"
/>
<div :ref="'schedule' + index" :class="[scheduleIsActive(item)]" />
</div>
</div>
</div>
+1 -6
View File
@@ -24,14 +24,9 @@ export function useLayout() {
epThemeColor: $config?.EpThemeColor ?? "#409EFF"
};
}
/** 灰色模式、色弱模式、隐藏标签页 */
/** 标签页缓存配置 */
if (!$storage.configure) {
$storage.configure = {
grey: $config?.Grey ?? false,
weak: $config?.Weak ?? false,
hideTabs: $config?.HideTabs ?? false,
showLogo: $config?.ShowLogo ?? true,
showModel: $config?.ShowModel ?? "smart",
multiTagsCache: $config?.MultiTagsCache ?? false
};
}
-5
View File
@@ -87,10 +87,6 @@ export function useNav() {
router.push(getTopMenu()?.path);
}
function onPanel() {
emitter.emit("openPanel");
}
function toggleSideBar() {
pureApp.toggleSideBar();
}
@@ -130,7 +126,6 @@ export function useNav() {
routers,
$storage,
backTopMenu,
onPanel,
getDivStyle,
changeTitle,
toggleSideBar,
+22 -71
View File
@@ -1,6 +1,5 @@
import {
ref,
unref,
watch,
computed,
reactive,
@@ -11,16 +10,9 @@ import {
import { tagsViewsType } from "../types";
import { useEventListener } from "@vueuse/core";
import { useRoute, useRouter } from "vue-router";
import { responsiveStorageNameSpace } from "@/config";
import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import {
isEqual,
isBoolean,
storageLocal,
toggleClass,
hasClass
} from "@pureadmin/utils";
import { isEqual, isBoolean, toggleClass, hasClass } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
import CloseAllTags from "@iconify-icons/ri/subtract-line";
@@ -41,22 +33,7 @@ export function useTags() {
const translateX = ref(0);
const visible = ref(false);
const activeIndex = ref(-1);
// 当前右键选中的路由信息
const currentSelect = ref({});
/** 显示模式,默认灵动模式 */
const showModel = ref(
storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure`
)?.showModel || "smart"
);
/** 是否隐藏标签页,默认显示 */
const showTags =
ref(
storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure`
).hideTabs
) ?? ref("false");
const multiTags: any = computed(() => {
return useMultiTagsStoreHook().multiTags;
});
@@ -73,35 +50,35 @@ export function useTags() {
icon: Close,
text: "关闭当前标签页",
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
disabled: multiTags.value.length <= 1,
show: true
},
{
icon: CloseLeftTags,
text: "关闭左侧标签页",
divided: true,
disabled: multiTags.value.length > 1 ? false : true,
disabled: multiTags.value.length <= 1,
show: true
},
{
icon: CloseRightTags,
text: "关闭右侧标签页",
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
disabled: multiTags.value.length <= 1,
show: true
},
{
icon: CloseOtherTags,
text: "关闭其他标签页",
divided: true,
disabled: multiTags.value.length > 2 ? false : true,
disabled: multiTags.value.length <= 2,
show: true
},
{
icon: CloseAllTags,
text: "关闭全部标签页",
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
disabled: multiTags.value.length <= 1,
show: true
},
{
@@ -124,13 +101,11 @@ export function useTags() {
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
if (Object.keys(route.query).length > 0) {
return isEqual(route.query, item.query) ? previous : next;
} else {
}
return isEqual(route.params, item.params) ? previous : next;
}
} else {
return route.path === item.path ? previous : next;
}
}
const iconIsActive = computed(() => {
return (item, index) => {
@@ -165,34 +140,25 @@ export function useTags() {
visible.value = false;
};
/** 鼠标移入添加激活样式 */
function onMouseenter(index) {
if (index) activeIndex.value = index;
if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
return;
toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
} else {
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
}
function getScheduleElement(index) {
const refs = instance?.refs[`schedule${index}`];
return Array.isArray(refs) ? refs[0] : refs;
}
function onMouseenter(index) {
if (index) activeIndex.value = index;
const scheduleEl = getScheduleElement(index);
if (!scheduleEl || hasClass(scheduleEl, "schedule-active")) return;
toggleClass(true, "schedule-in", scheduleEl);
toggleClass(false, "schedule-out", scheduleEl);
}
/** 鼠标移出恢复默认样式 */
function onMouseleave(index) {
activeIndex.value = -1;
if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
return;
toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
} else {
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
}
const scheduleEl = getScheduleElement(index);
if (!scheduleEl || hasClass(scheduleEl, "schedule-active")) return;
toggleClass(false, "schedule-in", scheduleEl);
toggleClass(true, "schedule-out", scheduleEl);
}
function onContentFullScreen() {
@@ -201,19 +167,6 @@ export function useTags() {
: pureSetting.changeSetting({ key: "hiddenSideBar", value: true });
}
onMounted(() => {
if (!showModel.value) {
const configure = storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure`
);
configure.showModel = "card";
storageLocal().setItem(
`${responsiveStorageNameSpace()}configure`,
configure
);
}
});
watch(
() => visible.value,
() => {
@@ -225,10 +178,8 @@ export function useTags() {
route,
router,
visible,
showTags,
instance,
multiTags,
showModel,
tagsViews,
buttonTop,
buttonLeft,
+2 -17
View File
@@ -7,7 +7,7 @@ import { useLayout } from "./hooks/useLayout";
import { useResizeObserver } from "@vueuse/core";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
import { deviceDetection, useGlobal } from "@pureadmin/utils";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import {
h,
@@ -22,13 +22,11 @@ import {
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
import appMain from "./components/appMain.vue";
import setting from "./components/setting/index.vue";
import Vertical from "./components/sidebar/vertical.vue";
import Horizontal from "./components/sidebar/horizontal.vue";
import backTop from "@/assets/svg/back_top.svg?component";
const appWrapperRef = ref();
const { isDark } = useDark();
const { layout } = useLayout();
const isMobile = deviceDetection();
const pureSetting = useSettingStoreHook();
@@ -54,10 +52,6 @@ const set: setType = reactive({
withoutAnimation: set.sidebar.withoutAnimation,
mobile: set.device === "mobile"
};
}),
hideTabs: computed(() => {
return $storage?.configure.hideTabs;
})
});
@@ -122,14 +116,7 @@ const layoutHeader = defineComponent({
return h(
"div",
{
class: { "fixed-header": set.fixedHeader },
style: [
set.hideTabs && layout.value.includes("horizontal")
? isDark.value
? "box-shadow: 0 1px 4px #0d0d0d"
: "box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08)"
: ""
]
class: { "fixed-header": set.fixedHeader }
},
{
default: () => [
@@ -188,8 +175,6 @@ const layoutHeader = defineComponent({
<app-main :fixed-header="set.fixedHeader" />
</el-scrollbar>
</div>
<!-- 系统设置 -->
<setting />
</div>
</template>
-1
View File
@@ -45,7 +45,6 @@ export interface setType {
withoutAnimation: boolean;
mobile: boolean;
};
hideTabs: boolean;
}
export type menuType = {
-11
View File
@@ -39,17 +39,6 @@ html.dark {
}
}
/* 项目配置面板 */
.right-panel-items {
.el-divider__text {
--el-bg-color: var(--el-bg-color);
}
.el-divider--horizontal {
border-top: none;
}
}
/* element-plus */
.el-table__cell {
background: var(--el-bg-color);
-1
View File
@@ -48,7 +48,6 @@
/* 自定义 tooltip 的类名 */
.pure-tooltip {
// 右侧操作面板right-panel类名的z-index为40000tooltip需要大于它才能显示
z-index: 41000 !important;
}
-12
View File
@@ -3,18 +3,6 @@
@import "./sidebar";
@import "./dark";
/* 自定义全局 CssVar */
:root {
/* 左侧菜单展开、收起动画时长 */
--pure-transition-duration: 0.3s;
}
/* 灰色模式 */
.html-grey {
filter: grayscale(100%);
}
/* 色弱模式 */
.html-weakness {
filter: invert(80%);
}
-4
View File
@@ -3,10 +3,6 @@ import mitt from "mitt";
/** 全局公共事件需要在此处添加类型 */
type Events = {
openPanel: string;
tagViewsChange: string;
tagViewsShowModel: string;
logoChange: boolean;
changLayoutRoute: string;
};
-5
View File
@@ -17,11 +17,6 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
epThemeColor: config.EpThemeColor ?? "#409EFF"
},
configure: Storage.getData("configure", nameSpace) ?? {
grey: config.Grey ?? false,
weak: config.Weak ?? false,
hideTabs: config.HideTabs ?? false,
showLogo: config.ShowLogo ?? true,
showModel: config.ShowModel ?? "smart",
multiTagsCache: config.MultiTagsCache ?? false
}
},
-15
View File
@@ -86,13 +86,8 @@ declare global {
Layout?: string;
Theme?: string;
DarkMode?: boolean;
Grey?: boolean;
Weak?: boolean;
HideTabs?: boolean;
SidebarStatus?: boolean;
EpThemeColor?: string;
ShowLogo?: boolean;
ShowModel?: string;
MenuArrowIconNoTransition?: boolean;
CachingAsyncRoutes?: boolean;
TooltipEffect?: Effect;
@@ -114,13 +109,8 @@ declare global {
layout?: string;
theme?: string;
darkMode?: boolean;
grey?: boolean;
weak?: boolean;
hideTabs?: boolean;
sidebarStatus?: boolean;
epThemeColor?: string;
showLogo?: boolean;
showModel?: string;
username?: string;
}
@@ -139,11 +129,6 @@ declare global {
epThemeColor?: string;
};
configure: {
grey?: boolean;
weak?: boolean;
hideTabs?: boolean;
showLogo?: boolean;
showModel?: string;
multiTagsCache?: boolean;
};
tags?: Array<any>;