Main #1
+2
-11
@@ -1,20 +1,11 @@
|
||||
<template>
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
<theme-picker />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThemePicker from "@/components/ThemePicker"
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: { ThemePicker }
|
||||
name: "App"
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#app .theme-picker {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -92,9 +92,6 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
},
|
||||
routes() {
|
||||
return this.$store.getters.defaultRoutes
|
||||
}
|
||||
@@ -213,7 +210,7 @@ export default {
|
||||
activeStyle(index) {
|
||||
if (index !== this.activeIndex) return {}
|
||||
return {
|
||||
'background-color': this.theme,
|
||||
'background-color': '#00875A',
|
||||
'color': '#fff'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="['#00875A', '#006B47', '#007F4F', '#11a983', '#13c2c2', '#6959CD', '#f5222d', '#212121', ]"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const ORIGINAL_THEME = '#00875A' // default color
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
chalk: '', // content of theme-chalk css
|
||||
theme: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultTheme() {
|
||||
return this.$store.state.settings.theme
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultTheme: {
|
||||
handler: function(val, oldVal) {
|
||||
this.theme = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
async theme(val) {
|
||||
await this.setTheme(val)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.defaultTheme !== ORIGINAL_THEME) {
|
||||
this.setTheme(this.defaultTheme)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async setTheme(val) {
|
||||
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
|
||||
if (typeof val !== 'string') return
|
||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
||||
|
||||
const getHandler = (variable, id) => {
|
||||
return () => {
|
||||
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
|
||||
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
|
||||
|
||||
let styleTag = document.getElementById(id)
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.setAttribute('id', id)
|
||||
document.head.appendChild(styleTag)
|
||||
}
|
||||
styleTag.innerText = newStyle
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.chalk) {
|
||||
const url = `/styles/theme-chalk/index.css`
|
||||
await this.getCSSString(url, 'chalk')
|
||||
}
|
||||
|
||||
const chalkHandler = getHandler('chalk', 'chalk-style')
|
||||
chalkHandler()
|
||||
|
||||
const styles = [].slice.call(document.querySelectorAll('style'))
|
||||
.filter(style => {
|
||||
const text = style.innerText
|
||||
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
|
||||
})
|
||||
styles.forEach(style => {
|
||||
const { innerText } = style
|
||||
if (typeof innerText !== 'string') return
|
||||
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
|
||||
})
|
||||
|
||||
this.$emit('change', val)
|
||||
},
|
||||
|
||||
updateStyle(style, oldCluster, newCluster) {
|
||||
let newStyle = style
|
||||
oldCluster.forEach((color, index) => {
|
||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
||||
})
|
||||
return newStyle
|
||||
},
|
||||
|
||||
getCSSString(url, variable) {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
xhr.open('GET', url)
|
||||
xhr.send()
|
||||
})
|
||||
},
|
||||
|
||||
getThemeCluster(theme) {
|
||||
const tintColor = (color, tint) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) { // when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
}
|
||||
|
||||
const shadeColor = (color, shade) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
red = Math.round((1 - shade) * red)
|
||||
green = Math.round((1 - shade) * green)
|
||||
blue = Math.round((1 - shade) * blue)
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
|
||||
const clusters = [theme]
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
|
||||
}
|
||||
clusters.push(shadeColor(theme, 0.1))
|
||||
return clusters
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-message,
|
||||
.theme-picker-dropdown {
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,35 +0,0 @@
|
||||
<template>
|
||||
<footer v-if="visible" class="copyright">
|
||||
<span>{{ content }}</span>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
visible() {
|
||||
return this.$store.state.settings.footerVisible
|
||||
},
|
||||
content() {
|
||||
return this.$store.state.settings.footerContent
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.copyright {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 36px;
|
||||
padding: 10px 20px;
|
||||
text-align: right;
|
||||
background-color: #f8f8f8;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
border-top: 1px solid #e7e7e7;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div class="navbar" :class="['nav' + navType, { 'header-mode': headerMode }]">
|
||||
<div class="navbar" :class="[{ 'header-mode': headerMode }]">
|
||||
<hamburger v-if="!headerMode" id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb v-if="!headerMode && navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="!headerMode && navType == 2" id="topmenu-container" class="topmenu-container" />
|
||||
<breadcrumb v-if="!headerMode" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<div class="right-menu">
|
||||
<template v-if="device!=='mobile'">
|
||||
<el-tooltip content="菜单搜索" effect="dark" placement="bottom">
|
||||
@@ -34,9 +33,6 @@
|
||||
<router-link to="/user/profile">
|
||||
<el-dropdown-item icon="el-icon-user">个人中心</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item v-if="setting" icon="el-icon-s-tools" @click.native="setLayout">
|
||||
<span>布局设置</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item icon="el-icon-lock" @click.native="lockScreen">
|
||||
<span>锁定屏幕</span>
|
||||
</el-dropdown-item>
|
||||
@@ -52,7 +48,6 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import TopNav from './TopNav'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import Search from '@/components/HeaderSearch'
|
||||
@@ -61,7 +56,6 @@ import HeaderNotice from './HeaderNotice'
|
||||
export default {
|
||||
components: {
|
||||
Breadcrumb,
|
||||
TopNav,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
Search,
|
||||
@@ -85,16 +79,6 @@ export default {
|
||||
'nickName',
|
||||
'roles'
|
||||
]),
|
||||
setting: {
|
||||
get() {
|
||||
return this.$store.state.settings.showSettings
|
||||
}
|
||||
},
|
||||
navType: {
|
||||
get() {
|
||||
return this.$store.state.settings.navType
|
||||
}
|
||||
},
|
||||
// 用户角色标签:根据 roles 数组智能映射为友好文本
|
||||
userRoleLabel() {
|
||||
const roleMap = {
|
||||
@@ -112,9 +96,6 @@ export default {
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
},
|
||||
setLayout(event) {
|
||||
this.$emit('setLayout')
|
||||
},
|
||||
handleCommand(command) {
|
||||
// 预留:下拉菜单 command 事件入口
|
||||
},
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="280px"
|
||||
:title="'布局设置'"
|
||||
class="drawer-setting"
|
||||
:with-header="false"
|
||||
:modal-append-to-body="false"
|
||||
>
|
||||
<div class="setting-drawer-index">
|
||||
<div class="setting-drawer-index__title">主题色</div>
|
||||
<div class="setting-drawer-index__main-color theme-color">
|
||||
<ul class="ul-color">
|
||||
<li
|
||||
v-for="item in colorList"
|
||||
:key="item.color"
|
||||
class="li-theme"
|
||||
:class="{ active: item.color === theme }"
|
||||
:style="{ background: item.color }"
|
||||
@click="changeTheme(item.color)"
|
||||
>
|
||||
<i v-show="item.color === theme" class="icon-check el-icon-check" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="setting-drawer-index__title">界面功能</div>
|
||||
<div class="setting-item">
|
||||
<span class="setting-item__label">侧边栏颜色</span>
|
||||
<el-dropdown trigger="click" @command="handleSideTheme">
|
||||
<span class="setting-item__value">
|
||||
{{ sideThemeLabel }}
|
||||
<i class="el-icon-arrow-down" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="theme-dark">深色主题</el-dropdown-item>
|
||||
<el-dropdown-item command="theme-light">浅色主题</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<span class="setting-item__label">导航栏模式</span>
|
||||
<el-dropdown trigger="click" @command="handleNavType">
|
||||
<span class="setting-item__value">
|
||||
{{ navTypeLabel }}
|
||||
<i class="el-icon-arrow-down" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="1">侧边栏模式</el-dropdown-item>
|
||||
<el-dropdown-item command="2">顶部导航模式</el-dropdown-item>
|
||||
<el-dropdown-item command="3">侧栏+顶栏</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<span class="setting-item__label"> TagsView 标签栏</span>
|
||||
<el-switch
|
||||
:value="tagsView"
|
||||
@change="changeSetting({ key: 'tagsView', value: !tagsView })"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<span class="setting-item__label"> 侧边栏 Logo</span>
|
||||
<el-switch
|
||||
:value="sidebarLogo"
|
||||
@change="changeSetting({ key: 'sidebarLogo', value: !sidebarLogo })"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<span class="setting-item__label"> 固定 Header</span>
|
||||
<el-switch
|
||||
:value="fixedHeader"
|
||||
@change="changeSetting({ key: 'fixedHeader', value: !fixedHeader })"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
/**
|
||||
* 主题色列表
|
||||
*/
|
||||
colorList: [
|
||||
{ color: '#00875A', title: '教育绿' },
|
||||
{ color: '#304156', title: '深蓝' },
|
||||
{ color: '#409EFF', title: '亮蓝' },
|
||||
{ color: '#626AEF', title: '亮紫' },
|
||||
{ color: '#EB2F96', title: '粉红' },
|
||||
{ color: '#F8721F', title: '橙色' },
|
||||
{ color: '#CD201F', title: '红色' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
theme: state => state.settings.theme,
|
||||
sideTheme: state => state.settings.sideTheme,
|
||||
navType: state => state.settings.navType,
|
||||
tagsView: state => state.settings.tagsView,
|
||||
sidebarLogo: state => state.settings.sidebarLogo,
|
||||
fixedHeader: state => state.settings.fixedHeader
|
||||
}),
|
||||
sideThemeLabel() {
|
||||
return this.sideTheme === 'theme-dark' ? '深色主题' : '浅色主题'
|
||||
},
|
||||
navTypeLabel() {
|
||||
const map = { 1: '侧边栏模式', 2: '顶部导航模式', 3: '侧栏+顶栏' }
|
||||
return map[this.navType] || '侧边栏模式'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSetting() {
|
||||
this.visible = true
|
||||
},
|
||||
closeSetting() {
|
||||
this.visible = false
|
||||
},
|
||||
// 修改主题色
|
||||
changeTheme(color) {
|
||||
this.$store.dispatch('settings/changeSetting', { key: 'theme', value: color })
|
||||
},
|
||||
handleSideTheme(command) {
|
||||
this.changeSetting({ key: 'sideTheme', value: command })
|
||||
},
|
||||
handleNavType(command) {
|
||||
this.changeSetting({ key: 'navType', value: Number(command) })
|
||||
},
|
||||
// 修改布局设置,并持久化到本地存储
|
||||
changeSetting(data) {
|
||||
this.$store.dispatch('settings/changeSetting', data)
|
||||
const setting = JSON.parse(localStorage.getItem('layout-setting') || '{}')
|
||||
setting[data.key] = data.value
|
||||
localStorage.setItem('layout-setting', JSON.stringify(setting))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-drawer-index {
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__title {
|
||||
margin-top: 26px;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
&__main-color {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.theme-color {
|
||||
.ul-color {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.li-theme {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-right: 6px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
&.active {
|
||||
box-shadow: 0 0 0 2px #fff, 0 0 0 4px currentColor;
|
||||
}
|
||||
|
||||
.icon-check {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
|
||||
&__value {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 12px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dcdfe6;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,430 +0,0 @@
|
||||
<template>
|
||||
<el-drawer size="280px" :visible="showSettings" :with-header="false" :append-to-body="true" :before-close="closeSetting" :lock-scroll="false">
|
||||
<div class="drawer-container">
|
||||
<div>
|
||||
<div class="setting-drawer-content">
|
||||
<div class="setting-drawer-title">
|
||||
<h3 class="drawer-title">菜单导航设置</h3>
|
||||
</div>
|
||||
<div class="nav-wrap">
|
||||
<el-tooltip content="左侧菜单" placement="bottom">
|
||||
<div class="item left" @click="handleNavType(1)" :style="{'--theme': theme}" :class="{ activeItem: navType == 1 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="混合菜单" placement="bottom">
|
||||
<div class="item mix" @click="handleNavType(2)" :style="{'--theme': theme}" :class="{ activeItem: navType == 2 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="顶部菜单" placement="bottom">
|
||||
<div class="item top" @click="handleNavType(3)" :style="{'--theme': theme}" :class="{ activeItem: navType == 3 }">
|
||||
<b></b><b></b>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="setting-drawer-title">
|
||||
<h3 class="drawer-title">主题风格设置</h3>
|
||||
</div>
|
||||
<div class="setting-drawer-block-checbox">
|
||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
|
||||
<img src="@/assets/images/dark.svg" alt="dark">
|
||||
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||
<i aria-label="图标: check" class="anticon anticon-check">
|
||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/>
|
||||
</svg>
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
|
||||
<img src="@/assets/images/light.svg" alt="light">
|
||||
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||
<i aria-label="图标: check" class="anticon anticon-check">
|
||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/>
|
||||
</svg>
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>主题颜色</span>
|
||||
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider/>
|
||||
|
||||
<h3 class="drawer-title">系统布局配置</h3>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>开启页签</span>
|
||||
<el-switch v-model="tagsView" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>持久化标签页</span>
|
||||
<el-switch v-model="tagsViewPersist" :disabled="!tagsView" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>显示页签图标</span>
|
||||
<el-switch v-model="tagsIcon" :disabled="!tagsView" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>标签页样式</span>
|
||||
<el-radio-group v-model="tagsViewStyle" :disabled="!tagsView" size="mini" class="drawer-switch">
|
||||
<el-radio-button label="card">卡片</el-radio-button>
|
||||
<el-radio-button label="chrome">谷歌</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>固定 Header</span>
|
||||
<el-switch v-model="fixedHeader" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>显示 Logo</span>
|
||||
<el-switch v-model="sidebarLogo" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>动态标题</span>
|
||||
<el-switch v-model="dynamicTitle" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<div class="drawer-item">
|
||||
<span>底部版权</span>
|
||||
<el-switch v-model="footerVisible" class="drawer-switch" />
|
||||
</div>
|
||||
|
||||
<el-divider/>
|
||||
|
||||
<el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">保存配置</el-button>
|
||||
<el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">重置配置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThemePicker from '@/components/ThemePicker'
|
||||
|
||||
export default {
|
||||
components: { ThemePicker },
|
||||
expose: ['openSetting'],
|
||||
data() {
|
||||
return {
|
||||
theme: this.$store.state.settings.theme,
|
||||
sideTheme: this.$store.state.settings.sideTheme,
|
||||
navType: this.$store.state.settings.navType,
|
||||
showSettings: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fixedHeader: {
|
||||
get() {
|
||||
return this.$store.state.settings.fixedHeader
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'fixedHeader',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
tagsViewPersist: {
|
||||
get() {
|
||||
return this.$store.state.settings.tagsViewPersist
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'tagsViewPersist',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
tagsView: {
|
||||
get() {
|
||||
return this.$store.state.settings.tagsView
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'tagsView',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
tagsIcon: {
|
||||
get() {
|
||||
return this.$store.state.settings.tagsIcon
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'tagsIcon',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
tagsViewStyle: {
|
||||
get() {
|
||||
return this.$store.state.settings.tagsViewStyle
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'tagsViewStyle',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
sidebarLogo: {
|
||||
get() {
|
||||
return this.$store.state.settings.sidebarLogo
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'sidebarLogo',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
dynamicTitle: {
|
||||
get() {
|
||||
return this.$store.state.settings.dynamicTitle
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'dynamicTitle',
|
||||
value: val
|
||||
})
|
||||
this.$store.dispatch('settings/setTitle', this.$store.state.settings.title)
|
||||
}
|
||||
},
|
||||
footerVisible: {
|
||||
get() {
|
||||
return this.$store.state.settings.footerVisible
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'footerVisible',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
navType: {
|
||||
handler(val) {
|
||||
if (val == 1) {
|
||||
this.$store.dispatch("app/toggleSideBarHide", false)
|
||||
}
|
||||
if (val == 2) {
|
||||
}
|
||||
if (val == 3) {
|
||||
this.$store.dispatch("app/toggleSideBarHide", true)
|
||||
}
|
||||
if ([1, 3].includes(val)) {
|
||||
this.$store.commit("SET_SIDEBAR_ROUTERS",this.$store.state.permission.defaultRoutes)
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
themeChange(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'theme',
|
||||
value: val
|
||||
})
|
||||
this.theme = val
|
||||
},
|
||||
handleTheme(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'sideTheme',
|
||||
value: val
|
||||
})
|
||||
this.sideTheme = val
|
||||
},
|
||||
handleNavType(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'navType',
|
||||
value: val
|
||||
})
|
||||
this.navType = val
|
||||
},
|
||||
openSetting() {
|
||||
this.showSettings = true
|
||||
},
|
||||
closeSetting(){
|
||||
this.showSettings = false
|
||||
},
|
||||
saveSetting() {
|
||||
this.$modal.loading("正在保存到本地,请稍候...")
|
||||
if (!this.tagsViewPersist) {
|
||||
this.$cache.local.remove('tags-view-visited')
|
||||
}
|
||||
this.$cache.local.set(
|
||||
"layout-setting",
|
||||
`{
|
||||
"navType":${this.navType},
|
||||
"tagsView":${this.tagsView},
|
||||
"tagsIcon":${this.tagsIcon},
|
||||
"tagsViewStyle":"${this.tagsViewStyle}",
|
||||
"tagsViewPersist":${this.tagsViewPersist},
|
||||
"fixedHeader":${this.fixedHeader},
|
||||
"sidebarLogo":${this.sidebarLogo},
|
||||
"dynamicTitle":${this.dynamicTitle},
|
||||
"footerVisible":${this.footerVisible},
|
||||
"sideTheme":"${this.sideTheme}",
|
||||
"theme":"${this.theme}"
|
||||
}`
|
||||
)
|
||||
setTimeout(this.$modal.closeLoading(), 1000)
|
||||
},
|
||||
resetSetting() {
|
||||
this.$modal.loading("正在清除设置缓存并刷新,请稍候...")
|
||||
this.$cache.local.remove('tags-view-visited')
|
||||
this.$cache.local.remove("layout-setting")
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-drawer-content {
|
||||
.setting-drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.setting-drawer-block-checbox-item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.setting-drawer-block-checbox-selectIcon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 15px;
|
||||
padding-left: 24px;
|
||||
color: #00875A;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
|
||||
.drawer-title {
|
||||
margin-bottom: 12px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.drawer-item {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.drawer-switch {
|
||||
float: right
|
||||
}
|
||||
}
|
||||
|
||||
// 导航模式
|
||||
.nav-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.activeItem {
|
||||
border: 2px solid #{'var(--theme)'} !important;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
margin-right: 16px;
|
||||
cursor: pointer;
|
||||
width: 56px;
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
background: #f0f2f5;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.left {
|
||||
b:first-child {
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #fff;
|
||||
}
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
}
|
||||
.mix {
|
||||
b:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
}
|
||||
b:last-child {
|
||||
width: 30%;
|
||||
background: #1b2a47;
|
||||
position: absolute;
|
||||
height: 70%;
|
||||
border-radius: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
.top {
|
||||
b:first-child {
|
||||
display: block;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div :class="['sidebar-theme-wrapper', settings.sideTheme]" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
|
||||
<template>
|
||||
<div :class="['sidebar-theme-wrapper', 'theme-dark']" :style="{ backgroundColor: variables.menuBackground }">
|
||||
<el-scrollbar class="theme-dark" wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="isCollapse"
|
||||
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
||||
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
||||
:background-color="variables.menuBackground"
|
||||
:text-color="variables.menuColor"
|
||||
:unique-opened="true"
|
||||
:active-text-color="settings.sideTheme === 'theme-dark' ? '#ffffff' : settings.theme"
|
||||
:active-text-color="'#ffffff'"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div id="tags-view-container" class="tags-view-container" :class="{ 'tags-view-container--chrome': tagsViewStyle === 'chrome' }" :style="chromeVars">
|
||||
<template>
|
||||
<div id="tags-view-container" class="tags-view-container">
|
||||
<!-- 左切换箭头 -->
|
||||
<span class="tags-nav-btn tags-nav-btn--left" :class="{ disabled: !canScrollLeft }" @click="scrollLeft">
|
||||
<i class="el-icon-arrow-left" />
|
||||
@@ -11,7 +11,7 @@
|
||||
v-for="tag in visitedViews"
|
||||
ref="tag"
|
||||
:key="tag.path"
|
||||
:class="{ 'active': isActive(tag), 'has-icon': tagsIcon }"
|
||||
:class="{ 'active': isActive(tag) }"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
@@ -19,7 +19,6 @@
|
||||
@click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent.native="openMenu(tag, $event)"
|
||||
>
|
||||
<svg-icon v-if="tagsIcon && tag.meta && tag.meta.icon && tag.meta.icon !== '#'" :icon-class="tag.meta.icon" style="margin-right: 3px;" />
|
||||
{{ tag.title }}
|
||||
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
||||
</router-link>
|
||||
@@ -91,26 +90,8 @@ export default {
|
||||
routes() {
|
||||
return this.$store.state.permission.routes
|
||||
},
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
},
|
||||
tagsIcon() {
|
||||
return this.$store.state.settings.tagsIcon
|
||||
},
|
||||
tagsViewStyle() {
|
||||
return this.$store.state.settings.tagsViewStyle
|
||||
},
|
||||
selectedDropdownTag() {
|
||||
return this.visitedViews.find(v => this.isActive(v)) || {}
|
||||
},
|
||||
chromeVars() {
|
||||
if (this.tagsViewStyle !== 'chrome') return {}
|
||||
const primary = this.theme || '#00875A'
|
||||
return {
|
||||
'--chrome-tab-active-bg': this.mixHexWithWhite(primary, 0.15),
|
||||
'--chrome-tab-text-active': primary,
|
||||
'--chrome-wing-r': '10px'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -148,24 +129,14 @@ export default {
|
||||
this.toggleFullscreen()
|
||||
}
|
||||
},
|
||||
mixHexWithWhite(hex, ratio) {
|
||||
const clean = hex.replace('#', '')
|
||||
const r = parseInt(clean.substring(0, 2), 16)
|
||||
const g = parseInt(clean.substring(2, 4), 16)
|
||||
const b = parseInt(clean.substring(4, 6), 16)
|
||||
const mr = Math.round(r * ratio + 255 * (1 - ratio))
|
||||
const mg = Math.round(g * ratio + 255 * (1 - ratio))
|
||||
const mb = Math.round(b * ratio + 255 * (1 - ratio))
|
||||
return `rgb(${mr}, ${mg}, ${mb})`
|
||||
},
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
},
|
||||
tagActiveStyle(tag) {
|
||||
if (!this.isActive(tag) || this.tagsViewStyle !== 'card') return {}
|
||||
if (!this.isActive(tag)) return {}
|
||||
return {
|
||||
"background-color": this.theme,
|
||||
"border-color": this.theme
|
||||
"background-color": "#00875A",
|
||||
"border-color": "#00875A"
|
||||
}
|
||||
},
|
||||
isAffix(tag) {
|
||||
@@ -209,9 +180,6 @@ export default {
|
||||
return tags
|
||||
},
|
||||
initTags() {
|
||||
if (this.$store.state.settings.tagsViewPersist) {
|
||||
this.$store.dispatch('tagsView/loadPersistedViews')
|
||||
}
|
||||
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
|
||||
for (const tag of affixTags) {
|
||||
if (tag.name) {
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
<template>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
mode="horizontal"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
||||
:icon-class="item.meta.icon"/>
|
||||
{{ item.meta.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-submenu :style="{'--theme': theme}" index="more" :key="visibleNumber" v-if="topMenus.length > visibleNumber">
|
||||
<template slot="title">更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item
|
||||
:index="item.path"
|
||||
:key="index"
|
||||
v-if="index >= visibleNumber">
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
||||
:icon-class="item.meta.icon"/>
|
||||
{{ item.meta.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { constantRoutes } from "@/router"
|
||||
import { isHttp } from "@/utils/validate"
|
||||
|
||||
// 隐藏侧边栏路由
|
||||
const hideList = ['/index', '/user/profile']
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 顶部栏初始数
|
||||
visibleNumber: 5,
|
||||
// 当前激活菜单的 index
|
||||
currentIndex: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
},
|
||||
// 顶部显示菜单
|
||||
topMenus() {
|
||||
let topMenus = []
|
||||
this.routers.map((menu) => {
|
||||
if (menu.hidden !== true) {
|
||||
// 兼容顶部栏一级菜单内部跳转
|
||||
if (menu.path === '/' && menu.children) {
|
||||
topMenus.push(menu.children[0])
|
||||
} else {
|
||||
topMenus.push(menu)
|
||||
}
|
||||
}
|
||||
})
|
||||
return topMenus
|
||||
},
|
||||
// 所有的路由信息
|
||||
routers() {
|
||||
return this.$store.state.permission.topbarRouters
|
||||
},
|
||||
// 设置子路由
|
||||
childrenMenus() {
|
||||
var childrenMenus = []
|
||||
this.routers.map((router) => {
|
||||
for (var item in router.children) {
|
||||
if (router.children[item].parentPath === undefined) {
|
||||
if (router.path === "/") {
|
||||
router.children[item].path = "/" + router.children[item].path
|
||||
} else {
|
||||
if (!isHttp(router.children[item].path)) {
|
||||
router.children[item].path = router.path + "/" + router.children[item].path
|
||||
}
|
||||
}
|
||||
router.children[item].parentPath = router.path
|
||||
}
|
||||
childrenMenus.push(router.children[item])
|
||||
}
|
||||
})
|
||||
return constantRoutes.concat(childrenMenus)
|
||||
},
|
||||
// 默认激活的菜单
|
||||
activeMenu() {
|
||||
const path = this.$route.path
|
||||
let activePath = path
|
||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
||||
const tmpPath = path.substring(1, path.length)
|
||||
if (!this.$route.meta.link) {
|
||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"))
|
||||
this.$store.dispatch('app/toggleSideBarHide', false)
|
||||
}
|
||||
} else if (!this.$route.children) {
|
||||
activePath = path
|
||||
this.$store.dispatch('app/toggleSideBarHide', true)
|
||||
}
|
||||
this.activeRoutes(activePath)
|
||||
return activePath
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
window.addEventListener('resize', this.setVisibleNumber)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.setVisibleNumber)
|
||||
},
|
||||
mounted() {
|
||||
this.setVisibleNumber()
|
||||
},
|
||||
methods: {
|
||||
// 根据宽度计算设置显示栏数
|
||||
setVisibleNumber() {
|
||||
const width = document.body.getBoundingClientRect().width / 3
|
||||
this.visibleNumber = parseInt(width / 85)
|
||||
},
|
||||
// 菜单选择事件
|
||||
handleSelect(key, keyPath) {
|
||||
this.currentIndex = key
|
||||
const route = this.routers.find(item => item.path === key)
|
||||
if (isHttp(key)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(key, "_blank")
|
||||
} else if (!route || !route.children) {
|
||||
// 没有子路由路径内部打开
|
||||
const routeMenu = this.childrenMenus.find(item => item.path === key)
|
||||
if (routeMenu && routeMenu.query) {
|
||||
let query = JSON.parse(routeMenu.query)
|
||||
this.$router.push({ path: key, query: query })
|
||||
} else {
|
||||
this.$router.push({ path: key })
|
||||
}
|
||||
this.$store.dispatch('app/toggleSideBarHide', true)
|
||||
} else {
|
||||
// 显示左侧联动菜单
|
||||
this.activeRoutes(key)
|
||||
this.$store.dispatch('app/toggleSideBarHide', false)
|
||||
}
|
||||
},
|
||||
// 当前激活的路由
|
||||
activeRoutes(key) {
|
||||
var routes = []
|
||||
if (this.childrenMenus && this.childrenMenus.length > 0) {
|
||||
this.childrenMenus.map((item) => {
|
||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
||||
routes.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (routes.length > 0) {
|
||||
this.$store.commit("SET_SIDEBAR_ROUTERS", routes)
|
||||
} else {
|
||||
this.$store.dispatch('app/toggleSideBarHide', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
|
||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* submenu item */
|
||||
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #303133 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,4 @@
|
||||
export { default as AppMain } from './AppMain'
|
||||
export { default as AppMain } from './AppMain'
|
||||
export { default as Navbar } from './Navbar'
|
||||
export { default as Settings } from './Settings'
|
||||
export { default as Sidebar } from './Sidebar/index.vue'
|
||||
export { default as TagsView } from './TagsView/index.vue'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme, '--current-color-light': theme + '1a', '--current-color-dark-bg': theme + '33'}">
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper" :style="{'--current-color': '#00875A', '--current-color-light': '#00875A1a', '--current-color-dark-bg': '#00875A33'}">
|
||||
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
||||
|
||||
<!-- 顶部全宽 Header -->
|
||||
@@ -15,23 +15,22 @@
|
||||
<span class="sys-title">教学管理信息系统</span>
|
||||
</div>
|
||||
<semester-bar class="top-header__center" />
|
||||
<navbar class="top-header__right" @setLayout="setLayout" :header-mode="true" />
|
||||
<navbar class="top-header__right" :header-mode="true" />
|
||||
</div>
|
||||
|
||||
<!-- 下方:侧边栏 + 主内容 -->
|
||||
<div class="lower-container" :class="{sidebarHide:sidebar.hide}">
|
||||
<sidebar v-if="!sidebar.hide" class="sidebar-container"/>
|
||||
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
|
||||
<tags-view v-if="needTagsView"/>
|
||||
<div :class="{hasTagsView:true,sidebarHide:sidebar.hide}" class="main-container">
|
||||
<tags-view/>
|
||||
<app-main/>
|
||||
<settings ref="settingRef"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
||||
import { AppMain, Navbar, Sidebar, TagsView } from './components'
|
||||
import SemesterBar from './components/SemesterBar'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import ResizeMixin from './mixin/ResizeHandler'
|
||||
@@ -43,7 +42,6 @@ export default {
|
||||
components: {
|
||||
AppMain,
|
||||
Navbar,
|
||||
Settings,
|
||||
Sidebar,
|
||||
TagsView,
|
||||
SemesterBar,
|
||||
@@ -52,11 +50,8 @@ export default {
|
||||
mixins: [ResizeMixin],
|
||||
computed: {
|
||||
...mapState({
|
||||
theme: state => state.settings.theme,
|
||||
sideTheme: state => state.settings.sideTheme,
|
||||
sidebar: state => state.app.sidebar,
|
||||
device: state => state.app.device,
|
||||
needTagsView: state => state.settings.tagsView
|
||||
device: state => state.app.device
|
||||
}),
|
||||
classObj() {
|
||||
return {
|
||||
@@ -74,9 +69,6 @@ export default {
|
||||
},
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
},
|
||||
setLayout() {
|
||||
this.$refs.settingRef.openSetting()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import router from './router'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import { Message } from 'element-ui'
|
||||
import NProgress from 'nprogress'
|
||||
@@ -18,7 +18,6 @@ const isWhiteList = (path) => {
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
if (getToken()) {
|
||||
to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
|
||||
const isLock = store.getters.isLock
|
||||
/* has token*/
|
||||
if (to.path === '/login') {
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* 网页标题
|
||||
*/
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
|
||||
/**
|
||||
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
||||
*/
|
||||
sideTheme: 'theme-dark',
|
||||
|
||||
/**
|
||||
* 系统布局配置
|
||||
*/
|
||||
showSettings: true,
|
||||
|
||||
/**
|
||||
* 菜单导航模式 1、纯左侧 2、混合(左侧+顶部) 3、纯顶部
|
||||
*/
|
||||
navType: 1,
|
||||
|
||||
/**
|
||||
* 是否显示 tagsView
|
||||
*/
|
||||
tagsView: true,
|
||||
|
||||
/**
|
||||
* 持久化标签页
|
||||
*/
|
||||
tagsViewPersist: false,
|
||||
|
||||
/**
|
||||
* 显示页签图标
|
||||
*/
|
||||
tagsIcon: false,
|
||||
|
||||
/**
|
||||
* 标签页样式:card 卡片(默认)、chrome 谷歌浏览器风格
|
||||
*/
|
||||
tagsViewStyle: 'card',
|
||||
|
||||
/**
|
||||
* 是否固定头部
|
||||
*/
|
||||
fixedHeader: true,
|
||||
|
||||
/**
|
||||
* 是否显示logo
|
||||
*/
|
||||
sidebarLogo: true,
|
||||
|
||||
/**
|
||||
* 是否显示动态标题
|
||||
*/
|
||||
dynamicTitle: false,
|
||||
|
||||
/**
|
||||
* 是否显示底部版权
|
||||
*/
|
||||
footerVisible: false,
|
||||
|
||||
/**
|
||||
* 底部版权文本内容
|
||||
*/
|
||||
footerContent: 'Copyright © 2018-2026 roomroot. All Rights Reserved.'
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import app from './modules/app'
|
||||
import lock from './modules/lock'
|
||||
@@ -6,7 +6,6 @@ import dict from './modules/dict'
|
||||
import user from './modules/user'
|
||||
import tagsView from './modules/tagsView'
|
||||
import permission from './modules/permission'
|
||||
import settings from './modules/settings'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
@@ -18,8 +17,7 @@ const store = new Vuex.Store({
|
||||
dict,
|
||||
user,
|
||||
tagsView,
|
||||
permission,
|
||||
settings
|
||||
permission
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import defaultSettings from '@/settings'
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
|
||||
const { sideTheme, showSettings, navType, tagsView, tagsViewPersist, tagsIcon, tagsViewStyle, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
|
||||
|
||||
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
|
||||
const state = {
|
||||
title: '',
|
||||
theme: storageSetting.theme || '#00875A',
|
||||
sideTheme: storageSetting.sideTheme || sideTheme,
|
||||
showSettings: showSettings,
|
||||
navType: storageSetting.navType === undefined ? navType : storageSetting.navType,
|
||||
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
|
||||
tagsViewPersist: storageSetting.tagsViewPersist === undefined ? tagsViewPersist : storageSetting.tagsViewPersist,
|
||||
tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
|
||||
tagsViewStyle: storageSetting.tagsViewStyle === undefined ? tagsViewStyle : storageSetting.tagsViewStyle,
|
||||
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
|
||||
sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
|
||||
dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle,
|
||||
footerVisible: storageSetting.footerVisible === undefined ? footerVisible : storageSetting.footerVisible,
|
||||
footerContent: footerContent
|
||||
}
|
||||
const mutations = {
|
||||
CHANGE_SETTING: (state, { key, value }) => {
|
||||
if (state.hasOwnProperty(key)) {
|
||||
state[key] = value
|
||||
}
|
||||
},
|
||||
SET_TITLE: (state, title) => {
|
||||
state.title = title
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
// 修改布局设置
|
||||
changeSetting({ commit }, data) {
|
||||
commit('CHANGE_SETTING', data)
|
||||
},
|
||||
// 设置网页标题
|
||||
setTitle({ commit }, title) {
|
||||
commit('SET_TITLE', title)
|
||||
useDynamicTitle()
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import store from '@/store'
|
||||
import cache from '@/plugins/cache'
|
||||
|
||||
const PERSIST_KEY = 'tags-view-visited'
|
||||
|
||||
// 布局已固定,不启用标签页持久化
|
||||
function isPersistEnabled() {
|
||||
return store.state.settings.tagsViewPersist
|
||||
return false
|
||||
}
|
||||
|
||||
function saveVisitedViews(views) {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import store from '@/store'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
/**
|
||||
* 动态修改标题
|
||||
*/
|
||||
export function useDynamicTitle() {
|
||||
if (store.state.settings.dynamicTitle) {
|
||||
document.title = store.state.settings.title + ' - ' + defaultSettings.title
|
||||
} else {
|
||||
document.title = defaultSettings.title
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="login">
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<h3 class="title">{{title}}</h3>
|
||||
@@ -50,14 +50,13 @@
|
||||
<script>
|
||||
import Cookies from "js-cookie"
|
||||
import { encrypt, decrypt } from '@/utils/jsencrypt'
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
footerContent: defaultSettings.footerContent,
|
||||
footerContent: "Copyright © 2018-2026 roomroot. All Rights Reserved.",
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="register">
|
||||
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form">
|
||||
<h3 class="title">{{title}}</h3>
|
||||
@@ -55,14 +55,13 @@
|
||||
<script>
|
||||
import { register } from "@/api/login"
|
||||
import passwordRule from "@/utils/passwordRule"
|
||||
import defaultSettings from '@/settings'
|
||||
|
||||
export default {
|
||||
mixins: [passwordRule],
|
||||
data() {
|
||||
return {
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
footerContent: defaultSettings.footerContent,
|
||||
footerContent: "Copyright © 2018-2026 roomroot. All Rights Reserved.",
|
||||
registerForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
|
||||
Reference in New Issue
Block a user