chore: 同步完整版代码

This commit is contained in:
xiaoxian521 2023-02-28 22:42:12 +08:00
parent 2c887ee1d9
commit 1e30b31be2
34 changed files with 1552 additions and 1436 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 啝裳 Copyright (c) 2023 pure-admin
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -18,6 +18,10 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
- [Click me to view the domestic documentation site](https://yiming_chang.gitee.io/pure-admin-doc) - [Click me to view the domestic documentation site](https://yiming_chang.gitee.io/pure-admin-doc)
- [Click me to view foreign document site](https://pure-admin.github.io/pure-admin-doc) - [Click me to view foreign document site](https://pure-admin.github.io/pure-admin-doc)
## Preview
- [Click me to view the preview station](https://pure-admin-thin.netlify.app/#/login)
## Usage ## Usage
### Installation dependencies ### Installation dependencies

View File

@ -22,6 +22,10 @@
- [点我查看国内文档站](https://yiming_chang.gitee.io/pure-admin-doc) - [点我查看国内文档站](https://yiming_chang.gitee.io/pure-admin-doc)
- [点我查看国外文档站](https://pure-admin.github.io/pure-admin-doc) - [点我查看国外文档站](https://pure-admin.github.io/pure-admin-doc)
## 预览
- [点我查看预览站](https://pure-admin-thin.netlify.app/#/login)
## 维护者 ## 维护者
[xiaoxian521](https://github.com/xiaoxian521) [xiaoxian521](https://github.com/xiaoxian521)

View File

@ -30,34 +30,34 @@
], ],
"dependencies": { "dependencies": {
"@pureadmin/descriptions": "^1.1.0", "@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.9.0", "@pureadmin/table": "^2.0.0",
"@pureadmin/utils": "^1.8.5", "@pureadmin/utils": "^1.8.5",
"@vueuse/core": "^9.6.0", "@vueuse/core": "^9.13.0",
"@vueuse/motion": "2.0.0-beta.12", "@vueuse/motion": "2.0.0-beta.12",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.2.0", "axios": "1.2.2",
"dayjs": "^1.11.6", "dayjs": "^1.11.7",
"echarts": "^5.4.0", "echarts": "^5.4.1",
"element-plus": "^2.2.27", "element-plus": "^2.2.32",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.0.28", "pinia": "^2.0.32",
"qs": "^6.11.0", "qs": "^6.11.0",
"responsive-storage": "^2.1.0", "responsive-storage": "^2.2.0",
"vue": "^3.2.45", "vue": "^3.2.47",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue-types": "^4.2.1" "vue-types": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@iconify-icons/ep": "^1.2.7", "@iconify-icons/ep": "^1.2.10",
"@iconify-icons/ri": "^1.2.3", "@iconify-icons/ri": "^1.2.4",
"@iconify/vue": "^4.0.0", "@iconify/vue": "^4.1.0",
"@pureadmin/theme": "^3.0.0", "@pureadmin/theme": "^3.0.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
@ -65,30 +65,30 @@
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.42.1", "@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0", "@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2", "@vue/eslint-config-typescript": "^11.0.2",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"cloc": "^2.10.0", "cloc": "^2.11.0",
"cssnano": "^5.1.14", "cssnano": "^5.1.14",
"eslint": "^8.8.0", "eslint": "^8.8.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^9.7.0", "eslint-plugin-vue": "^9.9.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "11.1.2", "lint-staged": "11.1.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"postcss": "^8.4.18", "postcss": "^8.4.21",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-import": "^15.0.0", "postcss-import": "^15.1.0",
"postcss-scss": "^4.0.5", "postcss-scss": "^4.0.6",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"pretty-quick": "3.1.1", "pretty-quick": "3.1.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"rollup-plugin-visualizer": "^5.8.3", "rollup-plugin-visualizer": "^5.9.0",
"sass": "^1.56.1", "sass": "^1.57.1",
"sass-loader": "^13.2.0", "sass-loader": "^13.2.0",
"stylelint": "^14.3.0", "stylelint": "^14.3.0",
"stylelint-config-html": "^1.0.0", "stylelint-config-html": "^1.0.0",
@ -97,18 +97,18 @@
"stylelint-config-standard": "^29.0.0", "stylelint-config-standard": "^29.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"svgo": "^3.0.2", "svgo": "^3.0.2",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.7",
"terser": "^5.16.1", "terser": "^5.16.1",
"typescript": "^4.9.3", "typescript": "^4.9.5",
"unplugin-vue-define-options": "^1.1.1", "unplugin-vue-define-options": "^1.0.0",
"vite": "^4.0.3", "vite": "^4.1.4",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-remove-console": "^1.3.0", "vite-plugin-remove-console": "^2.1.0",
"vite-svg-loader": "^3.6.0", "vite-svg-loader": "^4.0.0",
"vue-eslint-parser": "^9.1.0", "vue-eslint-parser": "^9.1.0",
"vue-tsc": "^1.0.9" "vue-tsc": "^1.2.0"
}, },
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {

1737
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

1
public/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path fill="#386BF3" d="M410.558.109c0 210.974-300.876 361.752-300.876 633.548 0 174.943 134.704 316.787 300.876 316.787s300.877-141.817 300.877-316.787C711.408 361.752 410.558 210.974 410.558.109z"/><path fill="#C3D2FB" d="M613.469 73.665c0 211.055-300.877 361.914-300.877 633.547C312.592 882.156 447.296 1024 613.47 1024s300.876-141.817 300.876-316.788C914.29 435.58 613.469 284.72 613.469 73.665z"/><path fill="#303F5B" d="M312.592 707.212c0-183.713 137.636-312.171 226.723-441.39 81.702 106.112 172.12 218.74 172.12 367.726A309.755 309.755 0 0 1 420.36 950.064a323.114 323.114 0 0 1-107.769-242.852z"/></svg>

After

Width:  |  Height:  |  Size: 712 B

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 2208059 */ font-family: "iconfont"; /* Project id 2208059 */
src: url("iconfont.woff2?t=1638023560828") format("woff2"), src: url("iconfont.woff2?t=1671895108120") format("woff2"),
url("iconfont.woff?t=1638023560828") format("woff"), url("iconfont.woff?t=1671895108120") format("woff"),
url("iconfont.ttf?t=1638023560828") format("truetype"); url("iconfont.ttf?t=1671895108120") format("truetype");
} }
.iconfont { .iconfont {
@ -13,26 +13,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.team-icontabs::before { .pure-iconfont-tabs:before {
content: "\e63e"; content: "\e63e";
} }
.team-iconlogo::before { .pure-iconfont-logo:before {
content: "\e620"; content: "\e620";
} }
.team-iconxinpin::before { .pure-iconfont-new:before {
content: "\e614";
}
.team-iconxinpinrenqiwang::before {
content: "\e615"; content: "\e615";
} }
.team-iconexit-fullscreen::before {
content: "\e62a";
}
.team-iconfullscreen::before {
content: "\e62b";
}

File diff suppressed because one or more lines are too long

View File

@ -2,50 +2,29 @@
"id": "2208059", "id": "2208059",
"name": "pure-admin", "name": "pure-admin",
"font_family": "iconfont", "font_family": "iconfont",
"css_prefix_text": "team-icon", "css_prefix_text": "pure-iconfont-",
"description": "pure-admin", "description": "pure-admin-iconfont",
"glyphs": [ "glyphs": [
{ {
"icon_id": "20594647", "icon_id": "20594647",
"name": "标签页", "name": "Tabs",
"font_class": "tabs", "font_class": "tabs",
"unicode": "e63e", "unicode": "e63e",
"unicode_decimal": 58942 "unicode_decimal": 58942
}, },
{ {
"icon_id": "22129506", "icon_id": "22129506",
"name": "水能", "name": "PureLogo",
"font_class": "logo", "font_class": "logo",
"unicode": "e620", "unicode": "e620",
"unicode_decimal": 58912 "unicode_decimal": 58912
}, },
{
"icon_id": "7795613",
"name": "新品",
"font_class": "xinpin",
"unicode": "e614",
"unicode_decimal": 58900
},
{ {
"icon_id": "7795615", "icon_id": "7795615",
"name": "新品人气王", "name": "New",
"font_class": "xinpinrenqiwang", "font_class": "new",
"unicode": "e615", "unicode": "e615",
"unicode_decimal": 58901 "unicode_decimal": 58901
},
{
"icon_id": "5698509",
"name": "全屏缩小",
"font_class": "exit-fullscreen",
"unicode": "e62a",
"unicode_decimal": 58922
},
{
"icon_id": "5698510",
"name": "全屏显示",
"font_class": "fullscreen",
"unicode": "e62b",
"unicode_decimal": 58923
} }
] ]
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,7 @@
import { ListItem } from "./data"; import { ListItem } from "./data";
import { ref, PropType, nextTick } from "vue"; import { ref, PropType, nextTick } from "vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { deviceDetection } from "@pureadmin/utils";
const props = defineProps({ const props = defineProps({
noticeItem: { noticeItem: {
@ -15,6 +16,7 @@ const titleTooltip = ref(false);
const descriptionRef = ref(null); const descriptionRef = ref(null);
const descriptionTooltip = ref(false); const descriptionTooltip = ref(false);
const { tooltipEffect } = useNav(); const { tooltipEffect } = useNav();
const isMobile = deviceDetection();
function hoverTitle() { function hoverTitle() {
nextTick(() => { nextTick(() => {
@ -63,6 +65,7 @@ function hoverDescription(event, description) {
:disabled="!titleTooltip" :disabled="!titleTooltip"
:content="props.noticeItem.title" :content="props.noticeItem.title"
placement="top-start" placement="top-start"
:enterable="!isMobile"
> >
<div <div
ref="titleRef" ref="titleRef"

View File

@ -1,17 +0,0 @@
<script setup lang="ts">
import { useFullscreen } from "@vueuse/core";
const { isFullscreen, toggle } = useFullscreen();
</script>
<template>
<div
class="screen-full w-[36px] h-[48px] flex-ac cursor-pointer navbar-bg-hover"
@click="toggle"
>
<FontIcon
:title="isFullscreen ? '退出全屏' : '全屏'"
:icon="isFullscreen ? 'team-iconexit-fullscreen' : 'team-iconfullscreen'"
/>
</div>
</template>

View File

@ -3,10 +3,9 @@ import { useRouter } from "vue-router";
import { cloneDeep } from "@pureadmin/utils"; import { cloneDeep } from "@pureadmin/utils";
import SearchResult from "./SearchResult.vue"; import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue"; import SearchFooter from "./SearchFooter.vue";
import { deleteChildren } from "@/utils/tree";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { ref, computed, shallowRef } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { ref, watch, computed, nextTick, shallowRef } from "vue";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import Search from "@iconify-icons/ep/search"; import Search from "@iconify-icons/ep/search";
@ -32,7 +31,7 @@ const handleSearch = useDebounceFn(search, 300);
/** 菜单树形结构 */ /** 菜单树形结构 */
const menusData = computed(() => { const menusData = computed(() => {
return deleteChildren(cloneDeep(usePermissionStoreHook().wholeMenus)); return cloneDeep(usePermissionStoreHook().wholeMenus);
}); });
const show = computed({ const show = computed({
@ -44,14 +43,6 @@ const show = computed({
} }
}); });
watch(show, async val => {
if (val) {
/** 自动聚焦 */
await nextTick();
inputRef.value?.focus();
}
});
/** 将菜单树形结构扁平化为一维数组,用于菜单查询 */ /** 将菜单树形结构扁平化为一维数组,用于菜单查询 */
function flatTree(arr) { function flatTree(arr) {
const res = []; const res = [];
@ -135,9 +126,11 @@ onKeyStroke("ArrowDown", handleDown);
<template> <template>
<el-dialog <el-dialog
top="5vh" top="5vh"
:width="device === 'mobile' ? '80vw' : '50vw'"
v-model="show" v-model="show"
:width="device === 'mobile' ? '80vw' : '50vw'"
:before-close="handleClose" :before-close="handleClose"
@opened="inputRef.focus()"
@closed="inputRef.blur()"
> >
<el-input <el-input
ref="inputRef" ref="inputRef"

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import { toRaw } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
const props = defineProps({
extraIcon: {
type: String,
default: ""
}
});
</script>
<template>
<div v-if="props.extraIcon" class="flex justify-center items-center">
<component
:is="useRenderIcon(toRaw(props.extraIcon))"
class="w-[30px] h-[30px]"
/>
</div>
</template>

View File

@ -40,8 +40,8 @@ watch(
class="horizontal-header" class="horizontal-header"
> >
<div class="horizontal-header-left" @click="backHome"> <div class="horizontal-header-left" @click="backHome">
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" /> <img src="/logo.svg" alt="logo" />
<h4>{{ title }}</h4> <span>{{ title }}</span>
</div> </div>
<el-menu <el-menu
router router

View File

@ -24,7 +24,7 @@ const iconClass = computed(() => {
"align-middle", "align-middle",
"text-primary", "text-primary",
"cursor-pointer", "cursor-pointer",
"duration-[360ms]", "duration-[100ms]",
"hover:text-primary", "hover:text-primary",
"dark:hover:!text-white" "dark:hover:!text-white"
]; ];

View File

@ -18,7 +18,7 @@ const { title } = useNav();
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" to="/"
> >
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" /> <img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
</router-link> </router-link>
<router-link <router-link
@ -28,7 +28,7 @@ const { title } = useNav();
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" to="/"
> >
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" /> <img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
</router-link> </router-link>
</transition> </transition>
@ -37,37 +37,33 @@ const { title } = useNav();
<style lang="scss" scoped> <style lang="scss" scoped>
.sidebar-logo-container { .sidebar-logo-container {
position: relative;
width: 100%; width: 100%;
height: 48px; height: 48px;
text-align: center;
overflow: hidden; overflow: hidden;
position: relative;
.sidebar-logo-link { .sidebar-logo-link {
height: 100%; height: 100%;
overflow: hidden; display: flex;
white-space: nowrap; flex-wrap: nowrap;
text-overflow: ellipsis; align-items: center;
margin-top: 5px;
img {
height: 32px;
display: inline-block;
}
.sidebar-title { .sidebar-title {
display: block; height: 32px;
width: 160px; line-height: 32px;
margin: 2px 0 0 12px;
color: $subMenuActiveText;
display: inline-block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
text-align: left; text-overflow: ellipsis;
color: #1890ff; font-size: 18px;
font-weight: 600; font-weight: 600;
font-size: 20px;
margin-top: 10px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
}
}
.collapses {
.sidebar-logo {
margin-right: 0;
} }
} }
} }

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import extraIcon from "./extraIcon.vue";
import Search from "../search/index.vue"; import Search from "../search/index.vue";
import Notice from "../notice/index.vue"; import Notice from "../notice/index.vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
@ -21,6 +22,7 @@ const {
menuSelect, menuSelect,
resolvePath, resolvePath,
username, username,
getDivStyle,
avatarsStyle avatarsStyle
} = useNav(); } = useNav();
@ -78,15 +80,12 @@ watch(
:is="useRenderIcon(route.meta && toRaw(route.meta.icon))" :is="useRenderIcon(route.meta && toRaw(route.meta.icon))"
/> />
</div> </div>
<span class="select-none">{{ route.meta.title }}</span> <div :style="getDivStyle">
<FontIcon <span class="select-none">
v-if="route.meta.extraIcon" {{ route.meta.title }}
width="30px" </span>
height="30px" <extraIcon :extraIcon="route.meta.extraIcon" />
style="position: absolute; right: 10px" </div>
:icon="route.meta.extraIcon.name"
:svg="route.meta.extraIcon.svg ? true : false"
/>
</template> </template>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>

View File

@ -1,17 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import path from "path"; import path from "path";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import extraIcon from "./extraIcon.vue";
import { childrenType } from "../../types"; import { childrenType } from "../../types";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue"; import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
import ArrowUp from "@iconify-icons/ep/arrow-up"; import ArrowUp from "@iconify-icons/ep/arrow-up-bold";
import EpArrowDown from "@iconify-icons/ep/arrow-down"; import EpArrowDown from "@iconify-icons/ep/arrow-down-bold";
import ArrowLeft from "@iconify-icons/ep/arrow-left"; import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
import ArrowRight from "@iconify-icons/ep/arrow-right"; import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
const { layout, isCollapse, tooltipEffect } = useNav(); const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
const props = defineProps({ const props = defineProps({
item: { item: {
@ -27,17 +28,11 @@ const props = defineProps({
} }
}); });
const getExtraIconStyle = computed((): CSSProperties => { const getSpanStyle = computed((): CSSProperties => {
if (!isCollapse.value) { return {
return { width: "100%",
position: "absolute", textAlign: "center"
right: "10px" };
};
} else {
return {
position: "static"
};
}
}); });
const getNoDropdownStyle = computed((): CSSProperties => { const getNoDropdownStyle = computed((): CSSProperties => {
@ -47,16 +42,6 @@ const getNoDropdownStyle = computed((): CSSProperties => {
}; };
}); });
const getDivStyle = computed((): CSSProperties => {
return {
width: !isCollapse.value ? "" : "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getMenuTextStyle = computed(() => { const getMenuTextStyle = computed(() => {
return { return {
overflow: "hidden", overflow: "hidden",
@ -65,19 +50,54 @@ const getMenuTextStyle = computed(() => {
}; };
}); });
const getSubTextStyle = computed((): CSSProperties => { const getsubMenuIconStyle = computed((): CSSProperties => {
return { return {
width: !isCollapse.value ? "210px" : "", display: "flex",
display: "inline-block", justifyContent: "center",
overflow: "hidden", alignItems: "center",
textOverflow: "ellipsis" margin:
layout.value === "horizontal"
? "0 5px 0 0"
: isCollapse.value
? "0 auto"
: "0 5px 0 0"
}; };
}); });
const getSpanStyle = computed(() => { const getSubTextStyle = computed((): CSSProperties => {
return { if (!isCollapse.value) {
overflow: "hidden", return {
textOverflow: "ellipsis" width: "210px",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
} else {
return {
width: ""
};
}
});
const getSubMenuDivStyle = computed((): any => {
return item => {
return !isCollapse.value
? {
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
}
: {
width: "100%",
textAlign:
item?.parentId === null
? "center"
: layout.value === "mix" && item?.pathList?.length === 2
? "center"
: ""
};
}; };
}); });
@ -114,6 +134,20 @@ function hoverMenu(key) {
}); });
} }
//
function overflowSlice(text, item?: any) {
const newText =
(text?.length > 1 ? text.toString().slice(0, 1) : text) + "...";
if (item && !(isCollapse.value && item?.parentId === null)) {
return layout.value === "mix" &&
item?.pathList?.length === 2 &&
isCollapse.value
? newText
: text;
}
return newText;
}
function hasOneShowingChild( function hasOneShowingChild(
children: childrenType[] = [], children: childrenType[] = [],
parent: childrenType parent: childrenType
@ -150,84 +184,78 @@ function resolvePath(routePath) {
</script> </script>
<template> <template>
<template <el-menu-item
v-if=" v-if="
hasOneShowingChild(props.item.children, props.item) && hasOneShowingChild(props.item.children, props.item) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren) (!onlyOneChild.children || onlyOneChild.noShowingChildren)
" "
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
:style="getNoDropdownStyle"
> >
<el-menu-item <div
:index="resolvePath(onlyOneChild.path)" v-if="toRaw(props.item.meta.icon)"
:class="{ 'submenu-title-noDropdown': !isNest }" class="sub-menu-icon"
:style="getNoDropdownStyle" :style="getsubMenuIconStyle"
> >
<div class="sub-menu-icon" v-if="toRaw(props.item.meta.icon)"> <component
<component :is="
:is=" useRenderIcon(
useRenderIcon( toRaw(onlyOneChild.meta.icon) ||
toRaw(onlyOneChild.meta.icon) || (props.item.meta && toRaw(props.item.meta.icon))
(props.item.meta && toRaw(props.item.meta.icon)) )
)
"
/>
</div>
<div
v-if="
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1
" "
:style="getDivStyle" />
> </div>
<span :style="getMenuTextStyle"> <span
v-if="
!props.item?.meta.icon &&
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1
"
:style="getSpanStyle"
>
{{ overflowSlice(onlyOneChild.meta.title) }}
</span>
<span
v-if="
!onlyOneChild.meta.icon &&
isCollapse &&
layout === 'mix' &&
props.item?.pathList?.length === 2
"
:style="getSpanStyle"
>
{{ overflowSlice(onlyOneChild.meta.title) }}
</span>
<template #title>
<div :style="getDivStyle">
<span v-if="layout === 'horizontal'">
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}
</span> </span>
</div> <el-tooltip
<div v-else
v-if=" placement="top"
isCollapse && layout === 'mix' && props.item?.pathList?.length === 2 :effect="tooltipEffect"
" :offset="-10"
:style="getDivStyle" :disabled="!onlyOneChild.showTooltip"
> >
<span :style="getMenuTextStyle"> <template #content>
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}
</span> </template>
</div> <span
<template #title> ref="menuTextRef"
<div :style="getDivStyle"> :style="getMenuTextStyle"
<span v-if="layout === 'horizontal'"> @mouseover="hoverMenu(onlyOneChild)"
>
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}
</span> </span>
<el-tooltip </el-tooltip>
v-else <extraIcon :extraIcon="onlyOneChild.meta.extraIcon" />
placement="top" </div>
:effect="tooltipEffect" </template>
:offset="-10" </el-menu-item>
:disabled="!onlyOneChild.showTooltip"
>
<template #content>
{{ onlyOneChild.meta.title }}
</template>
<span
ref="menuTextRef"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{ onlyOneChild.meta.title }}
</span>
</el-tooltip>
<FontIcon
v-if="onlyOneChild.meta.extraIcon"
width="30px"
height="30px"
:style="getExtraIconStyle"
:icon="onlyOneChild.meta.extraIcon.name"
:svg="onlyOneChild.meta.extraIcon.svg ? true : false"
/>
</div>
</template>
</el-menu-item>
</template>
<el-sub-menu <el-sub-menu
v-else v-else
@ -236,7 +264,11 @@ function resolvePath(routePath) {
:index="resolvePath(props.item.path)" :index="resolvePath(props.item.path)"
> >
<template #title> <template #title>
<div v-if="toRaw(props.item.meta.icon)" class="sub-menu-icon"> <div
v-if="toRaw(props.item.meta.icon)"
:style="getsubMenuIconStyle"
class="sub-menu-icon"
>
<component <component
:is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))" :is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
/> />
@ -244,34 +276,36 @@ function resolvePath(routePath) {
<span v-if="layout === 'horizontal'"> <span v-if="layout === 'horizontal'">
{{ props.item.meta.title }} {{ props.item.meta.title }}
</span> </span>
<el-tooltip <div
v-else :style="getSubMenuDivStyle(props.item)"
placement="top" v-if="
:effect="tooltipEffect" !(
:offset="-10" isCollapse &&
:disabled="!props.item.showTooltip" toRaw(props.item.meta.icon) &&
props.item.parentId === null
)
"
> >
<template #content> <el-tooltip
{{ props.item.meta.title }} v-if="layout !== 'horizontal'"
</template> placement="top"
<div :effect="tooltipEffect"
ref="menuTextRef" :offset="-10"
:style="getSubTextStyle" :disabled="!props.item.showTooltip"
@mouseover="hoverMenu(props.item)"
> >
<span :style="getSpanStyle"> <template #content>
{{ props.item.meta.title }} {{ props.item.meta.title }}
</template>
<span
ref="menuTextRef"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
{{ overflowSlice(props.item.meta.title, props.item) }}
</span> </span>
</div> </el-tooltip>
</el-tooltip> <extraIcon v-if="!isCollapse" :extraIcon="props.item.meta.extraIcon" />
<FontIcon </div>
v-if="props.item.meta.extraIcon"
width="30px"
height="30px"
style="position: absolute; right: 10px"
:icon="props.item.meta.extraIcon.name"
:svg="props.item.meta.extraIcon.svg ? true : false"
/>
</template> </template>
<sidebar-item <sidebar-item
v-for="child in props.item.children" v-for="child in props.item.children"

View File

@ -281,7 +281,7 @@
left: 0; left: 0;
bottom: 0; bottom: 0;
background: var(--el-color-primary); background: var(--el-color-primary);
animation: scheduleInWidth 400ms ease-in; animation: scheduleInWidth 200ms ease-in;
} }
/* 灵动模式下鼠标移出隐藏蓝色进度条 */ /* 灵动模式下鼠标移出隐藏蓝色进度条 */
@ -292,5 +292,5 @@
left: 0; left: 0;
bottom: 0; bottom: 0;
background: var(--el-color-primary); background: var(--el-color-primary);
animation: scheduleOutWidth 400ms ease-in; animation: scheduleOutWidth 200ms ease-in;
} }

View File

@ -63,7 +63,7 @@ const dynamicTagView = () => {
moveToView(index); moveToView(index);
}; };
const moveToView = (index: number): void => { const moveToView = async (index: number): Promise<void> => {
const tabNavPadding = 10; const tabNavPadding = 10;
if (!instance.refs["dynamic" + index]) return; if (!instance.refs["dynamic" + index]) return;
const tabItemEl = instance.refs["dynamic" + index][0]; const tabItemEl = instance.refs["dynamic" + index][0];
@ -73,8 +73,13 @@ const moveToView = (index: number): void => {
const scrollbarDomWidth = scrollbarDom.value const scrollbarDomWidth = scrollbarDom.value
? scrollbarDom.value?.offsetWidth ? scrollbarDom.value?.offsetWidth
: 0; : 0;
// dom
await nextTick();
// //
const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0; const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
scrollbarDomWidth <= tabDomWidth scrollbarDomWidth <= tabDomWidth
? (isShowArrow.value = true) ? (isShowArrow.value = true)
: (isShowArrow.value = false); : (isShowArrow.value = false);
@ -192,6 +197,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
length length
}) as any; }) as any;
} }
dynamicTagView();
}; };
if (tag === "other") { if (tag === "other") {
@ -488,6 +494,11 @@ watch([route], () => {
dynamicTagView(); dynamicTagView();
}); });
watch(isFullscreen, () => {
tagsViews[6].icon = Fullscreen;
tagsViews[6].text = "全屏";
});
onMounted(() => { onMounted(() => {
useResizeObserver( useResizeObserver(
scrollbarDom, scrollbarDom,

View File

@ -38,7 +38,7 @@ export function useDataThemeChange() {
const body = document.documentElement as HTMLElement; const body = document.documentElement as HTMLElement;
/** 设置导航主题色 */ /** 设置导航主题色 */
function setLayoutThemeColor(theme = "default") { function setLayoutThemeColor(theme = getConfig().Theme ?? "default") {
layoutTheme.value.theme = theme; layoutTheme.value.theme = theme;
toggleTheme({ toggleTheme({
scopeName: `layout-theme-${theme}` scopeName: `layout-theme-${theme}`

View File

@ -1,9 +1,9 @@
import { computed } from "vue";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { emitter } from "@/utils/mitt"; import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types"; import { routeMetaType } from "../types";
import { useGlobal } from "@pureadmin/utils"; import { useGlobal } from "@pureadmin/utils";
import { computed, CSSProperties } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { router, remainingPaths } from "@/router"; import { router, remainingPaths } from "@/router";
import { useAppStoreHook } from "@/store/modules/app"; import { useAppStoreHook } from "@/store/modules/app";
@ -20,6 +20,16 @@ export function useNav() {
/** 平台`layout`中所有`el-tooltip`的`effect`配置,默认`light` */ /** 平台`layout`中所有`el-tooltip`的`effect`配置,默认`light` */
const tooltipEffect = getConfig()?.TooltipEffect ?? "light"; const tooltipEffect = getConfig()?.TooltipEffect ?? "light";
const getDivStyle = computed((): CSSProperties => {
return {
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
/** 用户名 */ /** 用户名 */
const username = computed(() => { const username = computed(() => {
return useUserStoreHook()?.username; return useUserStoreHook()?.username;
@ -130,6 +140,7 @@ export function useNav() {
$storage, $storage,
backHome, backHome,
onPanel, onPanel,
getDivStyle,
changeTitle, changeTitle,
toggleSideBar, toggleSideBar,
menuSelect, menuSelect,

View File

@ -11,10 +11,15 @@ import {
import { tagsViewsType } from "../types"; import { tagsViewsType } from "../types";
import { useEventListener } from "@vueuse/core"; import { useEventListener } from "@vueuse/core";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { isEqual, isBoolean } from "@pureadmin/utils";
import { useSettingStoreHook } from "@/store/modules/settings"; import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { storageLocal, toggleClass, hasClass } from "@pureadmin/utils"; import {
isEqual,
isBoolean,
storageLocal,
toggleClass,
hasClass
} from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
import CloseAllTags from "@iconify-icons/ri/subtract-line"; import CloseAllTags from "@iconify-icons/ri/subtract-line";

View File

@ -67,10 +67,7 @@ export type childrenType = {
icon?: string; icon?: string;
title?: string; title?: string;
showParent?: boolean; showParent?: boolean;
extraIcon?: { extraIcon?: string;
svg?: boolean;
name?: string;
};
}; };
showTooltip?: boolean; showTooltip?: boolean;
parentId?: number; parentId?: number;

View File

@ -60,7 +60,7 @@ export const remainingPaths = Object.keys(remainingRouter).map(v => {
/** 创建路由实例 */ /** 创建路由实例 */
export const router: Router = createRouter({ export const router: Router = createRouter({
history: getHistoryMode(), history: getHistoryMode(import.meta.env.VITE_ROUTER_HISTORY),
routes: constantRoutes.concat(...(remainingRouter as any)), routes: constantRoutes.concat(...(remainingRouter as any)),
strict: true, strict: true,
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {

View File

@ -322,8 +322,7 @@ function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
} }
/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */ /** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */
function getHistoryMode(): RouterHistory { function getHistoryMode(routerHistory): RouterHistory {
const routerHistory = import.meta.env.VITE_ROUTER_HISTORY;
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
const historyMode = routerHistory.split(","); const historyMode = routerHistory.split(",");
const leftMode = historyMode[0]; const leftMode = historyMode[0];

View File

@ -1,9 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { store } from "@/store"; import { store } from "@/store";
import { isEqual } from "@pureadmin/utils";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { multiType, positionType } from "./types"; import { multiType, positionType } from "./types";
import { isUrl, storageLocal } from "@pureadmin/utils"; import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
export const useMultiTagsStore = defineStore({ export const useMultiTagsStore = defineStore({
id: "pure-multiTags", id: "pure-multiTags",
@ -54,6 +53,9 @@ export const useMultiTagsStore = defineStore({
if (isUrl(tagVal?.name)) return; if (isUrl(tagVal?.name)) return;
// 如果title为空拒绝添加空信息到标签页 // 如果title为空拒绝添加空信息到标签页
if (tagVal?.meta?.title.length === 0) return; if (tagVal?.meta?.title.length === 0) return;
// showLink:false 不添加到标签页
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
return;
const tagPath = tagVal.path; const tagPath = tagVal.path;
// 判断tag是否已存在 // 判断tag是否已存在
const tagHasExits = this.multiTags.some(tag => { const tagHasExits = this.multiTags.some(tag => {

View File

@ -6,7 +6,8 @@
/* 自定义全局 CssVar */ /* 自定义全局 CssVar */
:root { :root {
--pure-transition-duration: 0.016s; /* 左侧菜单展开、收起动画时长 */
--pure-transition-duration: 0.3s;
} }
/* 灰色模式 */ /* 灰色模式 */

View File

@ -14,12 +14,13 @@
} }
.sub-menu-icon { .sub-menu-icon {
vertical-align: middle;
font-size: 18px; font-size: 18px;
display: inline-flex;
justify-content: center;
align-items: center;
margin-right: 5px; margin-right: 5px;
svg {
width: 18px;
height: 18px;
}
} }
.set-icon { .set-icon {
@ -161,6 +162,24 @@
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
} }
.el-menu-item.is-active.nest-menu > * {
z-index: 1;
color: #fff;
}
.el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute;
left: 8px;
right: 8px;
margin: 4px 0;
top: 0;
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important;
}
.el-menu .el-menu--inline .el-sub-menu__title, .el-menu .el-menu--inline .el-sub-menu__title,
& .el-sub-menu .el-menu-item { & .el-sub-menu .el-menu-item {
font-size: 12px; font-size: 12px;
@ -168,14 +187,209 @@
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
} }
/* 无子集的激活菜单背景 */ /* 有子集的激活菜单左侧小竖条 */
.is-active.submenu-title-noDropdown.outer-most { .el-menu--collapse
background: $subMenuActiveBg !important; .is-active.outer-most.el-sub-menu
> .el-sub-menu__title::before {
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
background-color: $menuActiveBefore;
content: "";
clear: both;
transition: all var(--pure-transition-duration) ease-in-out;
transform: translateY(0);
} }
/* 有子集的激活菜单背景 */ .el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before {
.is-active.nest-menu { content: "";
background: $subMenuActiveBg !important; display: block;
position: absolute;
height: 0;
width: 3px;
transform: translateY(-50%);
top: 50%;
}
/* 无子集的激活菜单背景 */
.is-active.submenu-title-noDropdown.outer-most > * {
z-index: 1;
color: #fff;
}
.is-active.submenu-title-noDropdown.outer-most::before {
content: "";
clear: both;
position: absolute;
left: 8px;
right: 8px;
margin: 4px 0;
top: 0;
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important;
}
}
/* vertical 菜单折叠 */
.el-menu--vertical {
.el-menu--popup {
background-color: $subMenuBg !important;
.el-menu-item {
span {
font-size: 12px;
}
}
}
& > .el-menu {
i {
margin-right: 20px;
}
}
.is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important;
i {
color: $subMenuActiveText !important;
}
}
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
}
.el-menu-item,
.el-sub-menu__title {
height: 50px;
line-height: 50px;
color: $menuText;
background-color: $subMenuBg;
&:hover {
color: $menuTitleHover !important;
}
}
.is-active {
transition: color 0.3s;
color: $subMenuActiveText !important;
}
.el-menu-item.is-active.nest-menu > * {
z-index: 1;
color: #fff;
}
.el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute;
left: 8px;
right: 8px;
top: 0;
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important;
}
.el-menu-item,
.el-sub-menu {
.iconfont {
font-size: 18px;
}
.el-menu-tooltip__trigger {
width: 54px;
padding: 0;
}
}
}
/* horizontal 菜单 */
.el-menu--horizontal {
& > .el-sub-menu .el-sub-menu__icon-arrow {
position: static !important;
margin-top: 0;
}
.el-menu--popup {
background-color: $subMenuBg !important;
.el-menu-item {
color: $menuText;
background-color: $subMenuBg;
span {
font-size: 12px;
}
}
.el-sub-menu__title {
color: $menuText;
}
}
/* 无子菜单时激活 border-bottom */
.router-link-exact-active > .submenu-title-noDropdown {
height: 60px;
border-bottom: 2px solid var(--el-menu-active-color);
}
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
&:hover {
color: $menuTitleHover !important;
}
}
.is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important;
i {
color: $subMenuActiveText !important;
}
}
.nest-menu .el-sub-menu > .el-sub-menu__title,
.el-menu-item {
&:hover {
color: $menuTitleHover !important;
}
}
.el-menu-item.is-active {
transition: color 0.3s;
color: $subMenuActiveText !important;
}
.el-menu-item.is-active.nest-menu > * {
z-index: 1;
color: #fff;
}
.el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute;
left: 5px;
right: 5px;
top: 0;
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important;
} }
} }
@ -195,19 +409,24 @@
align-items: center; align-items: center;
padding-left: 10px; padding-left: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.125s ease; transition: all var(--pure-transition-duration) ease;
i { img {
font-size: 30px; height: 32px;
color: #1890ff; display: inline-block;
margin-right: 4px;
} }
h4 { span {
font-size: 16px; height: 32px;
font-weight: 700; line-height: 32px;
margin: 2px 0 0 12px;
color: $subMenuActiveText; color: $subMenuActiveText;
transition: all 0.5s; display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 18px;
font-weight: 600;
} }
} }
@ -273,6 +492,7 @@
.el-menu-item, .el-menu-item,
.el-sub-menu__title { .el-sub-menu__title {
padding-right: var(--el-menu-base-level-padding);
color: $menuText; color: $menuText;
&:hover { &:hover {
@ -306,181 +526,10 @@
} }
} }
/* vertical 菜单折叠 */
.el-menu--vertical {
.el-menu--popup {
background-color: $subMenuBg !important;
.el-menu-item {
span {
font-size: 12px;
}
}
}
& > .el-menu {
i {
margin-right: 20px;
}
}
.is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important;
i {
color: $subMenuActiveText !important;
}
}
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
}
.el-menu-item,
.el-sub-menu__title {
height: 50px;
line-height: 50px;
color: $menuText;
background-color: $subMenuBg;
&:hover {
color: $menuTitleHover !important;
}
}
.is-active {
transition: color 0.3s;
color: $subMenuActiveText !important;
}
.el-menu-item.is-active.nest-menu {
background: $subMenuActiveBg !important;
}
.el-menu-item,
.el-sub-menu {
// i {
// width: 20px;
// text-align: center;
// font-size: 16px;
// }
// i.fa {
// margin-right: 5px;
// font-size: 16px;
// }
.el-menu-tooltip__trigger {
width: 54px;
padding: 18px !important;
}
}
}
/* horizontal 菜单 */
.el-menu--horizontal {
& > .el-sub-menu .el-sub-menu__icon-arrow {
position: static !important;
margin-top: 0;
}
.el-menu--popup {
background-color: $subMenuBg !important;
.el-menu-item {
color: $menuText;
background-color: $subMenuBg;
span {
font-size: 12px;
}
}
.el-sub-menu__title {
color: $menuText;
}
}
/* 无子菜单时激活 border-bottom */
.router-link-exact-active > .submenu-title-noDropdown {
height: 60px;
border-bottom: 2px solid var(--el-menu-active-color);
}
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
&:hover {
color: $menuTitleHover !important;
}
}
.is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important;
i {
color: $subMenuActiveText !important;
}
}
.nest-menu .el-sub-menu > .el-sub-menu__title,
.el-menu-item {
&:hover {
color: $menuTitleHover !important;
}
}
/* 有子集的激活菜单背景 */
.is-active.nest-menu {
background: $subMenuActiveBg !important;
}
.el-menu-item.is-active {
transition: color 0.3s;
color: $subMenuActiveText !important;
}
}
.el-menu--collapse .el-menu .el-sub-menu { .el-menu--collapse .el-menu .el-sub-menu {
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
} }
/* 有子菜单 */
.el-menu--collapse
.is-active.outer-most.el-sub-menu
> .el-sub-menu__title::before,
/* 无子菜单 */
.el-menu--collapse .is-active.submenu-title-noDropdown.outer-most::before {
position: absolute;
top: 0;
left: 2px;
width: 2px;
height: 100%;
background-color: $menuActiveBefore;
content: "";
clear: both;
transition: all 0.125s ease-in-out;
transform: translateY(0);
}
.el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before,
.el-menu--collapse .submenu-title-noDropdown.outer-most::before {
content: "";
display: block;
position: absolute;
height: 0;
width: 3px;
transform: translateY(-50%);
top: 50%;
}
/* 手机端 */ /* 手机端 */
.mobile { .mobile {
.fixed-header { .fixed-header {
@ -526,7 +575,7 @@ body[layout="vertical"] {
} }
.sidebar-container { .sidebar-container {
transition: width 0.125s; transition: width var(--pure-transition-duration);
width: 54px !important; width: 54px !important;
.is-active.submenu-title-noDropdown.outer-most { .is-active.submenu-title-noDropdown.outer-most {
@ -543,11 +592,10 @@ body[layout="vertical"] {
.el-sub-menu { .el-sub-menu {
& > .el-sub-menu__title { & > .el-sub-menu__title {
& > span { & > span {
height: 0; height: 100%;
width: 0; width: 100%;
overflow: hidden; text-align: center;
visibility: hidden; visibility: visible;
display: inline-block;
} }
} }
} }
@ -557,7 +605,7 @@ body[layout="vertical"] {
} }
.el-sub-menu__title { .el-sub-menu__title {
padding: 0 18px !important; padding: 0;
} }
} }
@ -584,9 +632,13 @@ body[layout="horizontal"] {
$sideBarWidth: 0; $sideBarWidth: 0;
@include merge-style($sideBarWidth); @include merge-style($sideBarWidth);
.fixed-header,
.main-container {
transition: none !important;
}
.fixed-header { .fixed-header {
width: 100%; width: 100%;
transition: none !important;
} }
} }
@ -609,7 +661,7 @@ body[layout="mix"] {
} }
.sidebar-container { .sidebar-container {
transition: width 0.125s; transition: width var(--pure-transition-duration);
width: 54px !important; width: 54px !important;
.is-active.submenu-title-noDropdown.outer-most { .is-active.submenu-title-noDropdown.outer-most {
@ -625,12 +677,12 @@ body[layout="mix"] {
.el-menu--collapse { .el-menu--collapse {
.el-sub-menu { .el-sub-menu {
& > .el-sub-menu__title { & > .el-sub-menu__title {
padding: 0;
& > span { & > span {
height: 0; height: 100%;
width: 0; width: 100%;
overflow: hidden; text-align: center;
visibility: hidden; visibility: visible;
display: inline-block;
} }
} }
} }

View File

@ -13,8 +13,6 @@ const Print = function (dom, options?: object): PrintFunction {
styleStr: "", styleStr: "",
// Elements that need to dynamically get and set the height // Elements that need to dynamically get and set the height
setDomHeightArr: [], setDomHeightArr: [],
// Echart dom List
echartDomArr: [],
// Callback before printing // Callback before printing
printBeforeFn: null, printBeforeFn: null,
// Callback after printing // Callback after printing
@ -73,6 +71,8 @@ Print.prototype = {
const inputs = document.querySelectorAll("input"); const inputs = document.querySelectorAll("input");
const selects = document.querySelectorAll("select"); const selects = document.querySelectorAll("select");
const textareas = document.querySelectorAll("textarea"); const textareas = document.querySelectorAll("textarea");
const canvass = document.querySelectorAll("canvas");
for (let k = 0; k < inputs.length; k++) { for (let k = 0; k < inputs.length; k++) {
if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
if (inputs[k].checked == true) { if (inputs[k].checked == true) {
@ -108,6 +108,15 @@ Print.prototype = {
} }
} }
for (let k4 = 0; k4 < canvass.length; k4++) {
const imageURL = canvass[k4].toDataURL("image/png");
const img = document.createElement("img");
img.src = imageURL;
img.setAttribute("style", "max-width: 100%;");
img.className = "isNeedRemove";
canvass[k4].parentNode.insertBefore(img, canvass[k4].nextElementSibling);
}
return this.dom.outerHTML; return this.dom.outerHTML;
}, },
/** /**
@ -130,6 +139,12 @@ Print.prototype = {
doc.open(); doc.open();
doc.write(content); doc.write(content);
doc.close(); doc.close();
const removes = document.querySelectorAll(".isNeedRemove");
for (let k = 0; k < removes.length; k++) {
removes[k].parentNode.removeChild(removes[k]);
}
// eslint-disable-next-line @typescript-eslint/no-this-alias // eslint-disable-next-line @typescript-eslint/no-this-alias
const _this = this; const _this = this;
iframe.onload = function (): void { iframe.onload = function (): void {
@ -137,42 +152,16 @@ Print.prototype = {
if (_this.conf.printBeforeFn) { if (_this.conf.printBeforeFn) {
_this.conf.printBeforeFn({ doc }); _this.conf.printBeforeFn({ doc });
} }
_this.toPrint(w);
_this.drawEchartImg(doc).then(() => { setTimeout(function () {
_this.toPrint(w); document.body.removeChild(iframe);
setTimeout(function () { // After popup, callback
document.body.removeChild(iframe); if (_this.conf.printDoneCallBack) {
// After popup, callback _this.conf.printDoneCallBack();
if (_this.conf.printDoneCallBack) { }
_this.conf.printDoneCallBack(); }, 100);
}
}, 100);
});
}; };
}, },
/**
* echarts printing
* @param {Object} doc iframe window
*/
drawEchartImg(doc): Promise<void> {
return new Promise<void>(resolve => {
if (this.conf.echartDomArr && this.conf.echartDomArr.length > 0) {
this.conf.echartDomArr.forEach(e => {
const dom = doc.querySelector("#" + e.$el.id);
const img = new Image();
const w = dom.offsetWidth + "px";
const H = dom.offsetHeight + "px";
img.style.width = w;
img.style.height = H;
img.src = e.imgSrc;
dom.innerHTML = "";
dom.appendChild(img);
});
}
resolve();
});
},
/** /**
Print Print
*/ */

7
types/global.d.ts vendored
View File

@ -176,11 +176,8 @@ declare global {
title: string; title: string;
/** 菜单图标 `可选` */ /** 菜单图标 `可选` */
icon?: string | FunctionalComponent | IconifyIcon; icon?: string | FunctionalComponent | IconifyIcon;
/** 菜单名称右侧的额外图标,支持`fontawesome`、`iconfont`、`element-plus-icon` `可选` */ /** 菜单名称右侧的额外图标 */
extraIcon?: { extraIcon?: string | FunctionalComponent | IconifyIcon;
svg?: boolean;
name?: string;
};
/** 是否在菜单中显示(默认`true``可选` */ /** 是否在菜单中显示(默认`true``可选` */
showLink?: boolean; showLink?: boolean;
/** 是否显示父级菜单 `可选` */ /** 是否显示父级菜单 `可选` */