fix(frontend): полная зачистка UI legacy и унификация брендовой палитры (fix41)
This commit is contained in:
@@ -1,138 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
||||||
|
|
||||||
import { addClass, removeClass } from "@/utils/index";
|
|
||||||
|
|
||||||
const show = ref(false);
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
buttonTop: {
|
|
||||||
default: 250,
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(show, (value) => {
|
|
||||||
if (value) {
|
|
||||||
addEventClick();
|
|
||||||
}
|
|
||||||
if (value) {
|
|
||||||
addClass(document.body, "showRightPanel");
|
|
||||||
} else {
|
|
||||||
removeClass(document.body, "showRightPanel");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function addEventClick() {
|
|
||||||
window.addEventListener("click", closeSidebar, { passive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeSidebar(evt: any) {
|
|
||||||
// Клик по выбору темы не закрывает панель
|
|
||||||
let parent = evt.target.closest(".right-panel-container");
|
|
||||||
if (!parent) {
|
|
||||||
show.value = false;
|
|
||||||
window.removeEventListener("click", closeSidebar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rightPanel = ref();
|
|
||||||
|
|
||||||
function insertToBody() {
|
|
||||||
const body = document.querySelector("body") as any;
|
|
||||||
body.insertBefore(rightPanel.value, body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
insertToBody();
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
rightPanel.value.remove();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div :class="{ show: show }" ref="rightPanel">
|
|
||||||
<div class="right-panel-overlay" />
|
|
||||||
<div class="right-panel-container">
|
|
||||||
<div
|
|
||||||
class="right-panel-btn"
|
|
||||||
:style="{
|
|
||||||
top: buttonTop + 'px',
|
|
||||||
}"
|
|
||||||
@click="show = !show"
|
|
||||||
>
|
|
||||||
<i-ep-close v-show="show" />
|
|
||||||
<i-ep-setting v-show="!show" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.showRightPanel {
|
|
||||||
position: relative;
|
|
||||||
width: calc(100% - 15px);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 999;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 300px;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: var(--el-bg-color-overlay);
|
|
||||||
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-overlay {
|
|
||||||
z-index: 99;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel-container {
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel-btn {
|
|
||||||
position: absolute;
|
|
||||||
left: -36px;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
color: var(--el-color-white);
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: var(--el-color-primary);
|
|
||||||
border-radius: 6px 0 0 6px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
vertical-align: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,16 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
|
||||||
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="app-main">
|
<section class="app-main">
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<transition name="router-fade" mode="out-in">
|
<transition name="router-fade" mode="out-in">
|
||||||
<keep-alive :include="tagsViewStore.cachedViews">
|
<component :is="Component" :key="route.fullPath" />
|
||||||
<component :is="Component" :key="route.fullPath" />
|
|
||||||
</keep-alive>
|
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</section>
|
</section>
|
||||||
@@ -21,27 +13,10 @@ const tagsViewStore = useTagsViewStore();
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
/* 50= navbar 50 */
|
|
||||||
min-height: calc(100vh - 50px);
|
min-height: calc(100vh - 50px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: var(--el-bg-color-page);
|
background-color: var(--el-bg-color-page);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hasTagsView {
|
|
||||||
.app-main {
|
|
||||||
/* 84 = navbar + tags-view = 50 + 34 */
|
|
||||||
min-height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
|
||||||
min-height: 100vh;
|
|
||||||
padding-top: 84px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import { storeToRefs } from "pinia";
|
|||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useAppStore } from "@/store/modules/app";
|
import { useAppStore } from "@/store/modules/app";
|
||||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
|
||||||
import { useAdminStore } from "@/store/modules/admin";
|
import { useAdminStore } from "@/store/modules/admin";
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const tagsViewStore = useTagsViewStore();
|
|
||||||
const adminStore = useAdminStore();
|
const adminStore = useAdminStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -39,9 +37,6 @@ function logout() {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
adminStore
|
adminStore
|
||||||
.logout()
|
.logout()
|
||||||
.then(() => {
|
|
||||||
tagsViewStore.delAllViews();
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
router.push(`/login?redirect=${route.fullPath}`);
|
router.push(`/login?redirect=${route.fullPath}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useSettingsStore } from "@/store/modules/settings";
|
|
||||||
|
|
||||||
import IconEpSunny from "~icons/ep/sunny";
|
|
||||||
import IconEpMoon from "~icons/ep/moon";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тёмная тема.
|
|
||||||
*/
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const isDark = useDark();
|
|
||||||
const toggleDark = () => useToggle(isDark);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Переключение layout.
|
|
||||||
*/
|
|
||||||
function changeLayout(layout: string) {
|
|
||||||
settingsStore.changeSetting({ key: "layout", value: layout });
|
|
||||||
window.document.body.setAttribute("layout", settingsStore.layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Цвета темы
|
|
||||||
const themeColors = ref<string[]>([
|
|
||||||
"#409EFF",
|
|
||||||
"#304156",
|
|
||||||
"#11a983",
|
|
||||||
"#13c2c2",
|
|
||||||
"#6959CD",
|
|
||||||
"#f5222d",
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Переключение цвета темы.
|
|
||||||
*/
|
|
||||||
function changeThemeColor(color: string) {
|
|
||||||
settingsStore.changeSetting({ key: "themeColor", value: color });
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
"--el-color-primary",
|
|
||||||
settingsStore.themeColor
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.document.body.setAttribute("layout", settingsStore.layout);
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
"--el-color-primary",
|
|
||||||
settingsStore.themeColor
|
|
||||||
);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="settings-container">
|
|
||||||
<h3 class="text-base font-bold">Настройки проекта</h3>
|
|
||||||
<el-divider>Тема</el-divider>
|
|
||||||
|
|
||||||
<div class="flex justify-center" @click.stop>
|
|
||||||
<el-switch
|
|
||||||
v-model="isDark"
|
|
||||||
@change="toggleDark"
|
|
||||||
inline-prompt
|
|
||||||
:active-icon="IconEpMoon"
|
|
||||||
:inactive-icon="IconEpSunny"
|
|
||||||
active-color="var(--el-fill-color-dark)"
|
|
||||||
inactive-color="var(--el-color-primary)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-divider>Настройки интерфейса</el-divider>
|
|
||||||
<div class="py-[8px] flex justify-between">
|
|
||||||
<span class="text-xs">Включить вкладки</span>
|
|
||||||
<el-switch v-model="settingsStore.tagsView" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="py-[8px] flex justify-between">
|
|
||||||
<span class="text-xs">Зафиксировать header</span>
|
|
||||||
<el-switch v-model="settingsStore.fixedHeader" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="py-[8px] flex justify-between">
|
|
||||||
<span class="text-xs">Логотип в боковом меню</span>
|
|
||||||
<el-switch v-model="settingsStore.sidebarLogo" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-divider>Цвет темы</el-divider>
|
|
||||||
|
|
||||||
<ul class="w-full space-x-2 flex justify-center py-2">
|
|
||||||
<li
|
|
||||||
class="inline-block w-[30px] h-[30px] cursor-pointer"
|
|
||||||
v-for="(color, index) in themeColors"
|
|
||||||
:key="index"
|
|
||||||
:style="{ background: color }"
|
|
||||||
@click="changeThemeColor(color)"
|
|
||||||
></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.settings-container {
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
.layout {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
position: relative;
|
|
||||||
width: 18%;
|
|
||||||
height: 45px;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #f0f2f5;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item.is-active {
|
|
||||||
border: 2px solid var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-mix div:nth-child(1) {
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
background: #1b2a47;
|
|
||||||
box-shadow: 0 0 1px #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-mix div:nth-child(2) {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 30%;
|
|
||||||
height: 70%;
|
|
||||||
background: #1b2a47;
|
|
||||||
box-shadow: 0 0 1px #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-top div:nth-child(1) {
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
background: #1b2a47;
|
|
||||||
box-shadow: 0 0 1px #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-left div:nth-child(1) {
|
|
||||||
width: 30%;
|
|
||||||
height: 100%;
|
|
||||||
background: #1b2a47;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-left div:nth-child(2) {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 70%;
|
|
||||||
height: 30%;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 0 1px #888;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useSettingsStore } from "@/store/modules/settings";
|
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
collapse: {
|
collapse: {
|
||||||
@@ -14,33 +11,58 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-[50px] bg-gray-800 dark:bg-[var(--el-bg-color-overlay)]">
|
<div class="sidebar-logo">
|
||||||
<transition name="sidebarLogoFade">
|
<transition name="sidebarLogoFade">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="collapse"
|
v-if="collapse"
|
||||||
key="collapse"
|
key="collapse"
|
||||||
class="h-full w-full flex items-center justify-center"
|
class="sidebar-logo-link"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<img v-if="settingsStore.sidebarLogo" :src="logo" class="w-5 h-5" />
|
<img :src="logo" class="sidebar-logo-image" />
|
||||||
<span v-else class="ml-3 text-white text-sm font-bold">HY2XS</span>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
v-else
|
v-else
|
||||||
key="expand"
|
key="expand"
|
||||||
class="h-full w-full flex items-center justify-center"
|
class="sidebar-logo-link"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<img v-if="settingsStore.sidebarLogo" :src="logo" class="w-5 h-5" />
|
<img :src="logo" class="sidebar-logo-image" />
|
||||||
<span class="ml-3 text-white text-sm font-bold">HY2XS</span>
|
<span class="sidebar-logo-title">HY2XS</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// https://cn.vuejs.org/guide/built-ins/transition.html#the-transition-component
|
.sidebar-logo {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
background-color: var(--menuBg);
|
||||||
|
border-bottom: 1px solid rgb(255 255 255 / 6%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo-image {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo-title {
|
||||||
|
margin-left: 12px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebarLogoFade-enter-active {
|
.sidebarLogoFade-enter-active {
|
||||||
transition: opacity 2s;
|
transition: opacity 2s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,19 @@ import { useRoute } from "vue-router";
|
|||||||
import SidebarItem from "./SidebarItem.vue";
|
import SidebarItem from "./SidebarItem.vue";
|
||||||
import Logo from "./Logo.vue";
|
import Logo from "./Logo.vue";
|
||||||
|
|
||||||
import { useSettingsStore } from "@/store/modules/settings";
|
|
||||||
import { usePermissionStore } from "@/store/modules/permission";
|
import { usePermissionStore } from "@/store/modules/permission";
|
||||||
import { useAppStore } from "@/store/modules/app";
|
import { useAppStore } from "@/store/modules/app";
|
||||||
import { storeToRefs } from "pinia";
|
|
||||||
import variables from "@/styles/variables.module.scss";
|
import variables from "@/styles/variables.module.scss";
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { sidebarLogo } = storeToRefs(settingsStore);
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'has-logo': sidebarLogo }">
|
<div class="has-logo">
|
||||||
<logo v-if="sidebarLogo" :collapse="!appStore.sidebar.opened" />
|
<logo :collapse="!appStore.sidebar.opened" />
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="route.path"
|
:default-active="route.path"
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
|
||||||
|
|
||||||
const tagAndTagSpacing = ref(4);
|
|
||||||
const { proxy } = getCurrentInstance() as any;
|
|
||||||
|
|
||||||
const emits = defineEmits(["scroll"]);
|
|
||||||
const emitScroll = () => {
|
|
||||||
emits("scroll");
|
|
||||||
};
|
|
||||||
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
|
||||||
|
|
||||||
const scrollWrapper = computed(
|
|
||||||
() => proxy?.$refs.scrollContainer.$refs.wrapRef
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
scrollWrapper.value.addEventListener("scroll", emitScroll, true);
|
|
||||||
});
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
scrollWrapper.value.removeEventListener("scroll", emitScroll);
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleScroll(e: WheelEvent) {
|
|
||||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
|
|
||||||
scrollWrapper.value.scrollLeft =
|
|
||||||
scrollWrapper.value.scrollLeft + eventDelta / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveToTarget(currentTag: TagView) {
|
|
||||||
const $container = proxy.$refs.scrollContainer.$el;
|
|
||||||
const $containerWidth = $container.offsetWidth;
|
|
||||||
const $scrollWrapper = scrollWrapper.value;
|
|
||||||
|
|
||||||
let firstTag = null;
|
|
||||||
let lastTag = null;
|
|
||||||
|
|
||||||
// find first tag and last tag
|
|
||||||
if (tagsViewStore.visitedViews.length > 0) {
|
|
||||||
firstTag = tagsViewStore.visitedViews[0];
|
|
||||||
lastTag = tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstTag === currentTag) {
|
|
||||||
$scrollWrapper.scrollLeft = 0;
|
|
||||||
} else if (lastTag === currentTag) {
|
|
||||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
|
|
||||||
} else {
|
|
||||||
const tagListDom = document.getElementsByClassName("tags-item");
|
|
||||||
const currentIndex = tagsViewStore.visitedViews.findIndex(
|
|
||||||
(item) => item === currentTag
|
|
||||||
);
|
|
||||||
let prevTag = null;
|
|
||||||
let nextTag = null;
|
|
||||||
for (const k in tagListDom) {
|
|
||||||
if (k !== "length" && Object.hasOwnProperty.call(tagListDom, k)) {
|
|
||||||
if (
|
|
||||||
(tagListDom[k] as any).dataset.path ===
|
|
||||||
tagsViewStore.visitedViews[currentIndex - 1].path
|
|
||||||
) {
|
|
||||||
prevTag = tagListDom[k];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(tagListDom[k] as any).dataset.path ===
|
|
||||||
tagsViewStore.visitedViews[currentIndex + 1].path
|
|
||||||
) {
|
|
||||||
nextTag = tagListDom[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the tag's offsetLeft after of nextTag
|
|
||||||
const afterNextTagOffsetLeft =
|
|
||||||
(nextTag as any).offsetLeft +
|
|
||||||
(nextTag as any).offsetWidth +
|
|
||||||
tagAndTagSpacing.value;
|
|
||||||
|
|
||||||
// the tag's offsetLeft before of prevTag
|
|
||||||
const beforePrevTagOffsetLeft =
|
|
||||||
(prevTag as any).offsetLeft - tagAndTagSpacing.value;
|
|
||||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
|
||||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
|
|
||||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
|
||||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
moveToTarget,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<el-scrollbar
|
|
||||||
ref="scrollContainer"
|
|
||||||
class="scroll-container"
|
|
||||||
:vertical="false"
|
|
||||||
@wheel.prevent="handleScroll"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</el-scrollbar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.scroll-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
.el-scrollbar__bar {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-scrollbar__wrap {
|
|
||||||
height: 49px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,375 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import {
|
|
||||||
getCurrentInstance,
|
|
||||||
nextTick,
|
|
||||||
ref,
|
|
||||||
watch,
|
|
||||||
onMounted,
|
|
||||||
ComponentInternalInstance,
|
|
||||||
} from "vue";
|
|
||||||
import { storeToRefs } from "pinia";
|
|
||||||
|
|
||||||
import path from "path-browserify";
|
|
||||||
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
|
||||||
|
|
||||||
import { translateRouteTitleI18n } from "@/utils/i18n";
|
|
||||||
|
|
||||||
import { usePermissionStore } from "@/store/modules/permission";
|
|
||||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
|
||||||
import ScrollPane from "./ScrollPane.vue";
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
||||||
const router = useRouter();
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const permissionStore = usePermissionStore();
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
|
||||||
|
|
||||||
const { visitedViews } = storeToRefs(tagsViewStore);
|
|
||||||
|
|
||||||
const selectedTag = ref({});
|
|
||||||
const scrollPaneRef = ref();
|
|
||||||
const left = ref(0);
|
|
||||||
const top = ref(0);
|
|
||||||
const affixTags = ref<TagView[]>([]);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
route,
|
|
||||||
() => {
|
|
||||||
addTags();
|
|
||||||
moveToCurrentTag();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Выполнить сразу при инициализации
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const tagMenuVisible = ref(false); // Состояние отображения меню действий с вкладкой
|
|
||||||
watch(tagMenuVisible, (value) => {
|
|
||||||
if (value) {
|
|
||||||
document.body.addEventListener("click", closeTagMenu);
|
|
||||||
} else {
|
|
||||||
document.body.removeEventListener("click", closeTagMenu);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function filterAffixTags(routes: any[], basePath = "/") {
|
|
||||||
let tags: TagView[] = [];
|
|
||||||
|
|
||||||
routes.forEach((route) => {
|
|
||||||
if (route.meta && route.meta.affix) {
|
|
||||||
const tagPath = path.resolve(basePath, route.path);
|
|
||||||
tags.push({
|
|
||||||
fullPath: tagPath,
|
|
||||||
path: tagPath,
|
|
||||||
name: route.name,
|
|
||||||
meta: { ...route.meta },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.children) {
|
|
||||||
const childTags = filterAffixTags(route.children, route.path);
|
|
||||||
if (childTags.length >= 1) {
|
|
||||||
tags = tags.concat(childTags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTags() {
|
|
||||||
const tags: TagView[] = filterAffixTags(permissionStore.routes);
|
|
||||||
affixTags.value = tags;
|
|
||||||
for (const tag of tags) {
|
|
||||||
// Must have tag name
|
|
||||||
if (tag.name) {
|
|
||||||
tagsViewStore.addVisitedView(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTags() {
|
|
||||||
if (route.name) {
|
|
||||||
tagsViewStore.addView(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveToCurrentTag() {
|
|
||||||
nextTick(() => {
|
|
||||||
for (const r of tagsViewStore.visitedViews) {
|
|
||||||
if (r.path === route.path) {
|
|
||||||
scrollPaneRef.value.moveToTarget(r);
|
|
||||||
// when query is different then update
|
|
||||||
if (r.fullPath !== route.fullPath) {
|
|
||||||
tagsViewStore.updateVisitedView(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function isActive(tag: TagView) {
|
|
||||||
return tag.path === route.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAffix(tag: TagView) {
|
|
||||||
return tag.meta && tag.meta.affix;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFirstView() {
|
|
||||||
try {
|
|
||||||
return (
|
|
||||||
(selectedTag.value as TagView).fullPath ===
|
|
||||||
tagsViewStore.visitedViews[1].fullPath ||
|
|
||||||
(selectedTag.value as TagView).fullPath === "/index"
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLastView() {
|
|
||||||
try {
|
|
||||||
return (
|
|
||||||
(selectedTag.value as TagView).fullPath ===
|
|
||||||
tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1].fullPath
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshSelectedTag(view: TagView) {
|
|
||||||
tagsViewStore.delCachedView(view);
|
|
||||||
const { fullPath } = view;
|
|
||||||
nextTick(() => {
|
|
||||||
router.replace({ path: "/redirect" + fullPath }).catch((err) => {
|
|
||||||
console.warn(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toLastView(visitedViews: TagView[], view?: any) {
|
|
||||||
const latestView = visitedViews.slice(-1)[0];
|
|
||||||
if (latestView && latestView.fullPath) {
|
|
||||||
router.push(latestView.fullPath);
|
|
||||||
} else {
|
|
||||||
// now the default is to redirect to the home page if there is no tags-view,
|
|
||||||
// you can adjust it according to your needs.
|
|
||||||
if (view.name === "Dashboard") {
|
|
||||||
// to reload home page
|
|
||||||
router.replace({ path: "/redirect" + view.fullPath });
|
|
||||||
} else {
|
|
||||||
router.push("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeSelectedTag(view: TagView) {
|
|
||||||
tagsViewStore.delView(view).then((res: any) => {
|
|
||||||
if (isActive(view)) {
|
|
||||||
toLastView(res.visitedViews, view);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeLeftTags() {
|
|
||||||
tagsViewStore.delLeftViews(selectedTag.value).then((res: any) => {
|
|
||||||
if (
|
|
||||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
|
||||||
) {
|
|
||||||
toLastView(res.visitedViews);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function closeRightTags() {
|
|
||||||
tagsViewStore.delRightViews(selectedTag.value).then((res: any) => {
|
|
||||||
if (
|
|
||||||
!res.visitedViews.find((item: any) => item.fullPath === route.fullPath)
|
|
||||||
) {
|
|
||||||
toLastView(res.visitedViews);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeOtherTags() {
|
|
||||||
router.push(selectedTag.value);
|
|
||||||
tagsViewStore.delOtherViews(selectedTag.value).then(() => {
|
|
||||||
moveToCurrentTag();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeAllTags(view: TagView) {
|
|
||||||
tagsViewStore.delAllViews().then((res: any) => {
|
|
||||||
toLastView(res.visitedViews, view);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openTagMenu(tag: TagView, e: MouseEvent) {
|
|
||||||
const menuMinWidth = 105;
|
|
||||||
|
|
||||||
// console.log("test", proxy?.$el);
|
|
||||||
|
|
||||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
|
||||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
|
||||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
|
||||||
const l = e.clientX - offsetLeft + 15; // 15: margin right
|
|
||||||
|
|
||||||
if (l > maxLeft) {
|
|
||||||
left.value = maxLeft;
|
|
||||||
} else {
|
|
||||||
left.value = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
top.value = e.clientY;
|
|
||||||
tagMenuVisible.value = true;
|
|
||||||
selectedTag.value = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeTagMenu() {
|
|
||||||
tagMenuVisible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleScroll() {
|
|
||||||
closeTagMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initTags();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="tags-container">
|
|
||||||
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll">
|
|
||||||
<router-link
|
|
||||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
|
|
||||||
v-for="tag in visitedViews"
|
|
||||||
:key="tag.path"
|
|
||||||
:data-path="tag.path"
|
|
||||||
:to="{ path: tag.path, query: tag.query }"
|
|
||||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
|
||||||
@contextmenu.prevent="openTagMenu(tag, $event)"
|
|
||||||
>
|
|
||||||
{{ translateRouteTitleI18n(tag.meta?.title) }}
|
|
||||||
<span
|
|
||||||
v-if="!isAffix(tag)"
|
|
||||||
class="tags-item-close"
|
|
||||||
@click.prevent.stop="closeSelectedTag(tag)"
|
|
||||||
>
|
|
||||||
<i-ep-close class="text-[10px]" />
|
|
||||||
</span>
|
|
||||||
</router-link>
|
|
||||||
</scroll-pane>
|
|
||||||
|
|
||||||
<!-- Меню действий с вкладкой -->
|
|
||||||
<ul
|
|
||||||
v-show="tagMenuVisible"
|
|
||||||
class="tag-menu"
|
|
||||||
:style="{ left: left + 'px', top: top + 'px' }"
|
|
||||||
>
|
|
||||||
<li @click="refreshSelectedTag(selectedTag)">
|
|
||||||
<svg-icon icon-class="refresh" />
|
|
||||||
Обновить
|
|
||||||
</li>
|
|
||||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
|
||||||
<svg-icon icon-class="close" />
|
|
||||||
Закрыть
|
|
||||||
</li>
|
|
||||||
<li @click="closeOtherTags">
|
|
||||||
<svg-icon icon-class="close_other" />
|
|
||||||
Закрыть остальные
|
|
||||||
</li>
|
|
||||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
|
||||||
<svg-icon icon-class="close_left" />
|
|
||||||
Закрыть слева
|
|
||||||
</li>
|
|
||||||
<li v-if="!isLastView()" @click="closeRightTags">
|
|
||||||
<svg-icon icon-class="close_right" />
|
|
||||||
Закрыть справа
|
|
||||||
</li>
|
|
||||||
<li @click="closeAllTags(selectedTag)">
|
|
||||||
<svg-icon icon-class="close_all" />
|
|
||||||
Закрыть все
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.tags-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 34px;
|
|
||||||
background-color: var(--el-bg-color);
|
|
||||||
border: 1px solid var(--el-border-color-light);
|
|
||||||
box-shadow: 0 1px 1px var(--el-box-shadow-light);
|
|
||||||
|
|
||||||
.tags-item {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 8px;
|
|
||||||
margin: 4px 0 0 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid var(--el-border-color-light);
|
|
||||||
|
|
||||||
&:first-of-type {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: #fff;
|
|
||||||
background-color: var(--el-color-primary);
|
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: inline-block;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
margin-right: 5px;
|
|
||||||
content: "";
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-close {
|
|
||||||
border-radius: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
background: rgb(0 0 0 / 16%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-menu {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
font-size: 12px;
|
|
||||||
background: var(--el-bg-color-overlay);
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: var(--el-box-shadow-light);
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding: 8px 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--el-fill-color-light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
export { default as Navbar } from "./Navbar.vue";
|
export { default as Navbar } from "./Navbar.vue";
|
||||||
export { default as AppMain } from "./AppMain.vue";
|
export { default as AppMain } from "./AppMain.vue";
|
||||||
export { default as Settings } from "./Settings/index.vue";
|
|
||||||
export { default as TagsView } from "./TagsView/index.vue";
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, watchEffect } from "vue";
|
import { computed, watchEffect } from "vue";
|
||||||
import { useWindowSize } from "@vueuse/core";
|
import { useWindowSize } from "@vueuse/core";
|
||||||
import { AppMain, Navbar, Settings, TagsView } from "./components/index";
|
import { AppMain, Navbar } from "./components/index";
|
||||||
import Sidebar from "./components/Sidebar/index.vue";
|
import Sidebar from "./components/Sidebar/index.vue";
|
||||||
import RightPanel from "@/components/RightPanel/index.vue";
|
|
||||||
|
|
||||||
import { useAppStore } from "@/store/modules/app";
|
import { useAppStore } from "@/store/modules/app";
|
||||||
import { useSettingsStore } from "@/store/modules/settings";
|
|
||||||
|
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
|
|
||||||
@@ -20,11 +18,6 @@ const { width } = useWindowSize();
|
|||||||
const WIDTH = 992;
|
const WIDTH = 992;
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
|
|
||||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
|
||||||
const showTagsView = computed(() => settingsStore.tagsView);
|
|
||||||
const showSettings = computed(() => settingsStore.showSettings);
|
|
||||||
|
|
||||||
const classObj = computed(() => ({
|
const classObj = computed(() => ({
|
||||||
hideSidebar: !appStore.sidebar.opened,
|
hideSidebar: !appStore.sidebar.opened,
|
||||||
@@ -65,19 +58,13 @@ function handleOutsideClick() {
|
|||||||
|
|
||||||
<Sidebar class="sidebar-container" />
|
<Sidebar class="sidebar-container" />
|
||||||
|
|
||||||
<div :class="{ hasTagsView: showTagsView }" class="main-container">
|
<div class="main-container">
|
||||||
<div :class="{ 'fixed-header': fixedHeader }">
|
<div class="layout-header">
|
||||||
<navbar />
|
<navbar />
|
||||||
<tags-view v-if="showTagsView" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Основная страница-->
|
<!--Основная страница-->
|
||||||
<app-main />
|
<app-main />
|
||||||
|
|
||||||
<!-- Панель настроек -->
|
|
||||||
<RightPanel v-if="showSettings">
|
|
||||||
<settings />
|
|
||||||
</RightPanel>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -100,23 +87,6 @@ function handleOutsideClick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 9;
|
|
||||||
width: calc(100% - #{$sideBarWidth});
|
|
||||||
transition: width 0.28s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hideSidebar .fixed-header {
|
|
||||||
width: calc(100% - #{$sideBarCollapsedWidth});
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile .fixed-header {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-bg {
|
.drawer-bg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -128,4 +98,3 @@ function handleOutsideClick() {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import "virtual:svg-icons-register";
|
|||||||
import i18n from "@/lang/index";
|
import i18n from "@/lang/index";
|
||||||
|
|
||||||
// Стили
|
// Стили
|
||||||
import "element-plus/theme-chalk/dark/css-vars.css";
|
|
||||||
import "@/styles/index.scss";
|
import "@/styles/index.scss";
|
||||||
import "uno.css";
|
import "uno.css";
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,10 @@
|
|||||||
// Системные настройки
|
|
||||||
interface DefaultSettings {
|
interface DefaultSettings {
|
||||||
/**
|
|
||||||
* Заголовок системы
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Показывать настройки
|
|
||||||
*/
|
|
||||||
showSettings: boolean;
|
|
||||||
/**
|
|
||||||
* Показывать навигацию по вкладкам
|
|
||||||
*/
|
|
||||||
tagsView: boolean;
|
|
||||||
/**
|
|
||||||
*Фиксировать header
|
|
||||||
*/
|
|
||||||
fixedHeader: boolean;
|
|
||||||
/**
|
|
||||||
* Показывать логотип в боковом меню
|
|
||||||
*/
|
|
||||||
sidebarLogo: boolean;
|
|
||||||
/**
|
|
||||||
* Layout навигации
|
|
||||||
*/
|
|
||||||
layout: string;
|
|
||||||
/**
|
|
||||||
* Цвет темы
|
|
||||||
*/
|
|
||||||
themeColor: string;
|
|
||||||
/**
|
|
||||||
* Тема интерфейса
|
|
||||||
*/
|
|
||||||
theme: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Язык
|
|
||||||
*/
|
|
||||||
language: string;
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultSettings: DefaultSettings = {
|
const defaultSettings: DefaultSettings = {
|
||||||
title: "HY2XS admin",
|
|
||||||
showSettings: true,
|
|
||||||
tagsView: true,
|
|
||||||
fixedHeader: false,
|
|
||||||
sidebarLogo: true,
|
|
||||||
layout: "left",
|
|
||||||
themeColor: "#409EFF",
|
|
||||||
/**
|
|
||||||
* Тема интерфейса
|
|
||||||
*
|
|
||||||
* dark:Тёмная тема
|
|
||||||
* light: Светлая тема
|
|
||||||
*/
|
|
||||||
theme: "dark",
|
|
||||||
language: "ru", // ru | en
|
language: "ru", // ru | en
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defaultSettings;
|
export default defaultSettings;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import { defineStore } from "pinia";
|
|
||||||
import defaultSettings from "@/settings";
|
|
||||||
import { useStorage } from "@vueuse/core";
|
|
||||||
|
|
||||||
export const useSettingsStore = defineStore("setting", () => {
|
|
||||||
// state
|
|
||||||
const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView);
|
|
||||||
|
|
||||||
const showSettings = ref<boolean>(defaultSettings.showSettings);
|
|
||||||
const fixedHeader = ref<boolean>(defaultSettings.fixedHeader);
|
|
||||||
const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo);
|
|
||||||
|
|
||||||
const layout = useStorage<string>("layout", defaultSettings.layout);
|
|
||||||
|
|
||||||
const themeColor = useStorage<string>(
|
|
||||||
"themeColor",
|
|
||||||
defaultSettings.themeColor
|
|
||||||
);
|
|
||||||
|
|
||||||
// actions
|
|
||||||
function changeSetting(param: { key: string; value: any }) {
|
|
||||||
const { key, value } = param;
|
|
||||||
switch (key) {
|
|
||||||
case "showSettings":
|
|
||||||
showSettings.value = value;
|
|
||||||
break;
|
|
||||||
case "fixedHeader":
|
|
||||||
fixedHeader.value = value;
|
|
||||||
break;
|
|
||||||
case "tagsView":
|
|
||||||
tagsView.value = value;
|
|
||||||
break;
|
|
||||||
case "sidevarLogo":
|
|
||||||
sidebarLogo.value = value;
|
|
||||||
break;
|
|
||||||
case "layout":
|
|
||||||
layout.value = value;
|
|
||||||
break;
|
|
||||||
case "themeColor":
|
|
||||||
themeColor.value = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
showSettings,
|
|
||||||
tagsView,
|
|
||||||
fixedHeader,
|
|
||||||
sidebarLogo,
|
|
||||||
layout,
|
|
||||||
themeColor,
|
|
||||||
changeSetting,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
import { defineStore } from "pinia";
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { RouteLocationNormalized } from "vue-router";
|
|
||||||
|
|
||||||
export interface TagView extends Partial<RouteLocationNormalized> {
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup
|
|
||||||
export const useTagsViewStore = defineStore("tagsView", () => {
|
|
||||||
// state
|
|
||||||
const visitedViews = ref<TagView[]>([]);
|
|
||||||
const cachedViews = ref<string[]>([]);
|
|
||||||
|
|
||||||
// actions
|
|
||||||
function addVisitedView(view: TagView) {
|
|
||||||
if (visitedViews.value.some((v) => v.path === view.path)) return;
|
|
||||||
if (view.meta && view.meta.affix) {
|
|
||||||
visitedViews.value.unshift(
|
|
||||||
Object.assign({}, view, {
|
|
||||||
title: view.meta?.title || "no-name",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
visitedViews.value.push(
|
|
||||||
Object.assign({}, view, {
|
|
||||||
title: view.meta?.title || "no-name",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addCachedView(view: TagView) {
|
|
||||||
const viewName = view.name as string;
|
|
||||||
if (cachedViews.value.includes(viewName)) return;
|
|
||||||
if (view.meta?.keepAlive) {
|
|
||||||
cachedViews.value.push(viewName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function delVisitedView(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
for (const [i, v] of visitedViews.value.entries()) {
|
|
||||||
if (v.path === view.path) {
|
|
||||||
visitedViews.value.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve([...visitedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delCachedView(view: TagView) {
|
|
||||||
const viewName = view.name as string;
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const index = cachedViews.value.indexOf(viewName);
|
|
||||||
index > -1 && cachedViews.value.splice(index, 1);
|
|
||||||
resolve([...cachedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delOtherVisitedViews(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
visitedViews.value = visitedViews.value.filter((v) => {
|
|
||||||
return v.meta?.affix || v.path === view.path;
|
|
||||||
});
|
|
||||||
resolve([...visitedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delOtherCachedViews(view: TagView) {
|
|
||||||
const viewName = view.name as string;
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const index = cachedViews.value.indexOf(viewName);
|
|
||||||
if (index > -1) {
|
|
||||||
cachedViews.value = cachedViews.value.slice(index, index + 1);
|
|
||||||
} else {
|
|
||||||
// if index = -1, there is no cached tags
|
|
||||||
cachedViews.value = [];
|
|
||||||
}
|
|
||||||
resolve([...cachedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateVisitedView(view: TagView) {
|
|
||||||
for (let v of visitedViews.value) {
|
|
||||||
if (v.path === view.path) {
|
|
||||||
v = Object.assign(v, view);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addView(view: TagView) {
|
|
||||||
addVisitedView(view);
|
|
||||||
addCachedView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
function delView(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
delVisitedView(view);
|
|
||||||
delCachedView(view);
|
|
||||||
resolve({
|
|
||||||
visitedViews: [...visitedViews.value],
|
|
||||||
cachedViews: [...cachedViews.value],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delOtherViews(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
delOtherVisitedViews(view);
|
|
||||||
delOtherCachedViews(view);
|
|
||||||
resolve({
|
|
||||||
visitedViews: [...visitedViews.value],
|
|
||||||
cachedViews: [...cachedViews.value],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delLeftViews(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const currIndex = visitedViews.value.findIndex(
|
|
||||||
(v) => v.path === view.path
|
|
||||||
);
|
|
||||||
if (currIndex === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
visitedViews.value = visitedViews.value.filter((item, index) => {
|
|
||||||
// affix:true Фиксированная вкладка, например главная
|
|
||||||
if (index >= currIndex || (item.meta && item.meta.affix)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheIndex = cachedViews.value.indexOf(item.name as string);
|
|
||||||
if (cacheIndex > -1) {
|
|
||||||
cachedViews.value.splice(cacheIndex, 1);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
resolve({
|
|
||||||
visitedViews: [...visitedViews.value],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function delRightViews(view: TagView) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const currIndex = visitedViews.value.findIndex(
|
|
||||||
(v) => v.path === view.path
|
|
||||||
);
|
|
||||||
if (currIndex === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
visitedViews.value = visitedViews.value.filter((item, index) => {
|
|
||||||
// affix:true Фиксированная вкладка, например главная
|
|
||||||
if (index <= currIndex || (item.meta && item.meta.affix)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheIndex = cachedViews.value.indexOf(item.name as string);
|
|
||||||
if (cacheIndex > -1) {
|
|
||||||
cachedViews.value.splice(cacheIndex, 1);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
resolve({
|
|
||||||
visitedViews: [...visitedViews.value],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delAllViews() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix);
|
|
||||||
visitedViews.value = affixTags;
|
|
||||||
cachedViews.value = [];
|
|
||||||
resolve({
|
|
||||||
visitedViews: [...visitedViews.value],
|
|
||||||
cachedViews: [...cachedViews.value],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delAllVisitedViews() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix);
|
|
||||||
visitedViews.value = affixTags;
|
|
||||||
resolve([...visitedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delAllCachedViews() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
cachedViews.value = [];
|
|
||||||
resolve([...cachedViews.value]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
visitedViews,
|
|
||||||
cachedViews,
|
|
||||||
addVisitedView,
|
|
||||||
addCachedView,
|
|
||||||
delVisitedView,
|
|
||||||
delCachedView,
|
|
||||||
delOtherVisitedViews,
|
|
||||||
delOtherCachedViews,
|
|
||||||
updateVisitedView,
|
|
||||||
addView,
|
|
||||||
delView,
|
|
||||||
delOtherViews,
|
|
||||||
delLeftViews,
|
|
||||||
delRightViews,
|
|
||||||
delAllViews,
|
|
||||||
delAllVisitedViews,
|
|
||||||
delAllCachedViews,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
html.dark {
|
|
||||||
--menuBg: var(--el-bg-color-overlay);
|
|
||||||
--menuText: #fff;
|
|
||||||
--menuActiveText: var(--el-menu-active-color);
|
|
||||||
--menuHover: rgb(0 0 0 / 20%);
|
|
||||||
--subMenuBg: var(--el-menu-bg-color);
|
|
||||||
--subMenuActiveText: var(--el-menu-active-color);
|
|
||||||
--subMenuHover: rgb(0 0 0 / 20%);
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
color: var(--el-text-color-regular);
|
|
||||||
background-color: var(--el-bg-color);
|
|
||||||
|
|
||||||
.setting-container .setting-item:hover {
|
|
||||||
background: var(--el-fill-color-light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel-btn {
|
|
||||||
background-color: var(--el-color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-icon,
|
|
||||||
svg {
|
|
||||||
fill: var(--el-text-color-regular);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-container {
|
|
||||||
.el-menu-item.is-active .svg-icon {
|
|
||||||
fill: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,3 @@
|
|||||||
:root {
|
|
||||||
// Здесь можно задать пользовательские переменные цвета
|
|
||||||
// Цвет active-состояния основной кнопки Element Plus, меняется вместе с темой
|
|
||||||
--el-color-primary-dark: #0d84ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Переопределение стилей Element Plus
|
// Переопределение стилей Element Plus
|
||||||
.el-breadcrumb__inner,
|
.el-breadcrumb__inner,
|
||||||
.el-breadcrumb__inner a {
|
.el-breadcrumb__inner a {
|
||||||
|
|||||||
@@ -1,19 +1,38 @@
|
|||||||
@import "./sidebar";
|
@import "./sidebar";
|
||||||
@import "./reset";
|
@import "./reset";
|
||||||
@import "./dark";
|
|
||||||
@import "./element-plus";
|
@import "./element-plus";
|
||||||
|
|
||||||
.app-container {
|
.app-container {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
|
||||||
.search {
|
.search,
|
||||||
padding: 18px 0 0 10px;
|
.page-toolbar {
|
||||||
margin-bottom: 10px;
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
background-color: var(--el-bg-color-overlay);
|
background-color: var(--el-bg-color-overlay);
|
||||||
border: 1px solid var(--el-border-color-light);
|
border: 1px solid var(--el-border-color-light);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: var(--el-box-shadow-light);
|
box-shadow: var(--el-box-shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-toolbar {
|
||||||
|
.el-form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px 16px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
padding: 18px 0 0 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,28 @@
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
padding: 0 16px !important;
|
padding: 0 16px !important;
|
||||||
|
color: $menuText !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item:hover,
|
||||||
|
.el-sub-menu__title:hover {
|
||||||
|
background-color: $menuHover !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item.is-active {
|
||||||
|
position: relative;
|
||||||
|
color: $menuActiveText !important;
|
||||||
|
background-color: $menuActiveBg !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item.is-active::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 2px;
|
||||||
|
content: "";
|
||||||
|
background-color: $menuActiveBorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
@@ -61,6 +83,8 @@
|
|||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
color: currentColor;
|
||||||
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
@@ -236,4 +260,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
// Глобальные SCSS-переменные
|
// Глобальные SCSS-переменные
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--menuBg: #304156;
|
--el-color-primary: #ff4e2f;
|
||||||
--menuText: #bfcbd9;
|
--el-color-primary-dark-2: #cc3e26;
|
||||||
--menuActiveText: #409eff;
|
--el-color-primary-light-3: #ff826d;
|
||||||
--menuHover: #263445;
|
--el-color-primary-light-5: #ffa797;
|
||||||
--subMenuBg: #1f2d3d;
|
--el-color-primary-light-7: #ffc8bd;
|
||||||
--subMenuActiveText: #f4f4f5;
|
--el-color-primary-light-8: #ffdcd6;
|
||||||
--subMenuHover: #001528;
|
--el-color-primary-light-9: #ffedea;
|
||||||
|
|
||||||
|
--menuBg: #181818;
|
||||||
|
--menuText: #fff;
|
||||||
|
--menuActiveText: #fff;
|
||||||
|
--menuHover: #2a2d2e;
|
||||||
|
--subMenuBg: #1f1f1f;
|
||||||
|
--subMenuActiveText: #fff;
|
||||||
|
--subMenuHover: #2a2d2e;
|
||||||
|
--menuActiveBg: #37373d;
|
||||||
|
--menuActiveBorder: #ff4e2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
$menuBg: var(--menuBg);
|
$menuBg: var(--menuBg);
|
||||||
@@ -18,8 +28,9 @@ $menuHover: var(--menuHover);
|
|||||||
$subMenuBg: var(--subMenuBg);
|
$subMenuBg: var(--subMenuBg);
|
||||||
$subMenuActiveText: var(--subMenuActiveText);
|
$subMenuActiveText: var(--subMenuActiveText);
|
||||||
$subMenuHover: var(--subMenuHover);
|
$subMenuHover: var(--subMenuHover);
|
||||||
|
$menuActiveBg: var(--menuActiveBg);
|
||||||
|
$menuActiveBorder: var(--menuActiveBorder);
|
||||||
|
|
||||||
$sideBarWidth: 210px;
|
$sideBarWidth: 210px;
|
||||||
$sideBarCollapsedWidth: 54px;
|
$sideBarCollapsedWidth: 54px;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
-4
@@ -14,7 +14,6 @@ declare module '@vue/runtime-core' {
|
|||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||||
@@ -45,19 +44,16 @@ declare module '@vue/runtime-core' {
|
|||||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
|
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
|
||||||
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
|
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
|
||||||
IEpClose: typeof import('~icons/ep/close')['default']
|
|
||||||
IEpDownload: typeof import('~icons/ep/download')['default']
|
IEpDownload: typeof import('~icons/ep/download')['default']
|
||||||
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
||||||
IEpRefresh: typeof import('~icons/ep/refresh')['default']
|
IEpRefresh: typeof import('~icons/ep/refresh')['default']
|
||||||
IEpRefreshRight: typeof import('~icons/ep/refresh-right')['default']
|
IEpRefreshRight: typeof import('~icons/ep/refresh-right')['default']
|
||||||
IEpSetting: typeof import('~icons/ep/setting')['default']
|
|
||||||
IEpUpload: typeof import('~icons/ep/upload')['default']
|
IEpUpload: typeof import('~icons/ep/upload')['default']
|
||||||
IEpUserFilled: typeof import('~icons/ep/user-filled')['default']
|
IEpUserFilled: typeof import('~icons/ep/user-filled')['default']
|
||||||
ImputMultiple: typeof import('./../components/ImputMultiple/index.vue')['default']
|
ImputMultiple: typeof import('./../components/ImputMultiple/index.vue')['default']
|
||||||
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
|
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
|
||||||
MapAdd: typeof import('./../components/MapAdd/index.vue')['default']
|
MapAdd: typeof import('./../components/MapAdd/index.vue')['default']
|
||||||
Pagination: typeof import('./../components/Pagination/index.vue')['default']
|
Pagination: typeof import('./../components/Pagination/index.vue')['default']
|
||||||
RightPanel: typeof import('./../components/RightPanel/index.vue')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
|
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="search">
|
<div class="page-toolbar">
|
||||||
<el-form inline>
|
<el-form inline>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="submitForm" :icon="Select">
|
<el-button type="primary" @click="submitForm" :icon="Select">
|
||||||
@@ -169,7 +169,7 @@ import {
|
|||||||
UploadFile,
|
UploadFile,
|
||||||
UploadRawFile,
|
UploadRawFile,
|
||||||
UploadRequestOptions,
|
UploadRequestOptions,
|
||||||
} from "element-plus/lib/components";
|
} from "element-plus";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="search">
|
<div class="page-toolbar">
|
||||||
<el-form :inline="true">
|
<el-form :inline="true">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="handleExport">
|
<el-button @click="handleExport">
|
||||||
@@ -997,7 +997,7 @@ import { assignWith, deepCopy } from "@/utils/copy";
|
|||||||
import {
|
import {
|
||||||
UploadFile,
|
UploadFile,
|
||||||
UploadRequestOptions,
|
UploadRequestOptions,
|
||||||
} from "element-plus/lib/components";
|
} from "element-plus";
|
||||||
import { dashboardSummaryApi } from "@/api/dashboard";
|
import { dashboardSummaryApi } from "@/api/dashboard";
|
||||||
import { UploadUserFile } from "element-plus";
|
import { UploadUserFile } from "element-plus";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="search">
|
<div class="page-toolbar">
|
||||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||||
<el-form-item :label="$t('log.numLine')" prop="numLine">
|
<el-form-item :label="$t('log.numLine')" prop="numLine">
|
||||||
<el-select
|
<el-select
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<div class="search">
|
<div class="page-toolbar">
|
||||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||||
<el-form-item :label="$t('log.numLine')" prop="numLine">
|
<el-form-item :label="$t('log.numLine')" prop="numLine">
|
||||||
<el-select
|
<el-select
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ const handleLogin = () => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #2d3a4b;
|
background-color: var(--menuBg);
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
width: 520px;
|
width: 520px;
|
||||||
@@ -206,8 +206,8 @@ const handleLogin = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item {
|
.el-form-item {
|
||||||
background: rgb(0 0 0 / 10%);
|
background: var(--subMenuBg);
|
||||||
border: 1px solid rgb(255 255 255 / 10%);
|
border: 1px solid rgb(255 255 255 / 12%);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ import {
|
|||||||
exportPeerApi,
|
exportPeerApi,
|
||||||
} from "@/api/peer";
|
} from "@/api/peer";
|
||||||
import { PeerPageDto, PeerSaveDto, PeerUpdateDto, PeerVo } from "@/api/peer/types";
|
import { PeerPageDto, PeerSaveDto, PeerUpdateDto, PeerVo } from "@/api/peer/types";
|
||||||
import { UploadFile, UploadRawFile, UploadRequestOptions } from "element-plus/lib/components";
|
import { UploadFile, UploadRawFile, UploadRequestOptions } from "element-plus";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|||||||
@@ -105,6 +105,43 @@ export default defineConfig((): UserConfig => {
|
|||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
|
manualChunks(id: string) {
|
||||||
|
if (!id.includes("node_modules")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
id.includes("echarts") ||
|
||||||
|
id.includes("zrender") ||
|
||||||
|
id.includes("vue-echarts")
|
||||||
|
) {
|
||||||
|
return "charts";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
id.includes("element-plus") ||
|
||||||
|
id.includes("@element-plus") ||
|
||||||
|
id.includes("@popperjs")
|
||||||
|
) {
|
||||||
|
return "element-plus";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
id.includes("/vue/") ||
|
||||||
|
id.includes("vue-router") ||
|
||||||
|
id.includes("pinia") ||
|
||||||
|
id.includes("vue-i18n") ||
|
||||||
|
id.includes("@vueuse")
|
||||||
|
) {
|
||||||
|
return "vue-core";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.includes("qrcode.vue") || id.includes("vuedraggable")) {
|
||||||
|
return "ui-extra";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "vendor";
|
||||||
|
},
|
||||||
sanitizeFileName(name: string): string {
|
sanitizeFileName(name: string): string {
|
||||||
// https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
|
// https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
|
||||||
const match = DRIVE_LETTER_REGEX.exec(name);
|
const match = DRIVE_LETTER_REGEX.exec(name);
|
||||||
|
|||||||
Reference in New Issue
Block a user