Browse Source

前端新增可配置侧边栏风格,可以配置网页主题色

main
Devil 3 years ago
parent
commit
5147d05430
  1. 151
      web/src/components/themeChange/index.vue
  2. 4
      web/src/core/element_lazy.js
  3. 1
      web/src/core/gin-vue-admin.js
  4. 4
      web/src/store/index.js
  5. 33
      web/src/store/module/app.js
  6. 9
      web/src/style/basics.scss
  7. 15
      web/src/style/element_visiable.scss
  8. 124
      web/src/style/main.scss
  9. 71
      web/src/style/side.scss
  10. 25
      web/src/view/layout/aside/historyComponent/history.vue
  11. 15
      web/src/view/layout/aside/index.vue
  12. 6
      web/src/view/layout/index.vue
  13. 9
      web/src/view/layout/search/search.vue
  14. 108
      web/src/view/layout/setting/index.vue
  15. 9510
      web/yarn.lock

151
web/src/components/themeChange/index.vue

@ -0,0 +1,151 @@
<template>
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: ''
}
},
computed: {
defaultTheme() {
return this.$store.state.app.theme
}
},
watch: {
defaultTheme: {
handler: function(val, oldVal) {
this.theme = val
},
immediate: true
},
async theme(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 $message = this.$message({
message: '修改主题色中',
customClass: 'theme-message',
type: 'success',
duration: 0,
iconClass: 'el-icon-loading'
})
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 = `https://unpkg.com/element-ui@${version}/lib/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)
$message.close()
}
},
methods: {
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>

4
web/src/core/element_lazy.js

@ -58,7 +58,8 @@ import {
Upload,
Progress,
MessageBox,
Image
Image,
ColorPicker
} from 'element-ui'
Vue.use(Button)
@ -110,6 +111,7 @@ Vue.use(Progress)
Vue.use(Scrollbar)
Vue.use(Loading.directive)
Vue.use(Image)
Vue.use(ColorPicker)
Vue.prototype.$loading = Loading.service
Vue.prototype.$message = Message

1
web/src/core/gin-vue-admin.js

@ -11,6 +11,7 @@ import '../../node_modules/timeline-vuejs/dist/timeline-vuejs.css'
// 路由守卫
import Bus from '@/utils/bus'
// 加载网站配置文件夹
import '../style/element_visiable.scss' // 导入主题色配置
import config from './config'
Vue.prototype.$GIN_VUE_ADMIN = config
console.log(config)

4
web/src/store/index.js

@ -5,6 +5,7 @@ import VuexPersistence from 'vuex-persist'
import { user } from '@/store/module/user'
import { router } from '@/store/module/router'
import { dictionary } from '@/store/module/dictionary'
import { app } from '@/store/module/app'
Vue.use(Vuex)
const vuexLocal = new VuexPersistence({
@ -15,7 +16,8 @@ export const store = new Vuex.Store({
modules: {
user,
router,
dictionary
dictionary,
app
},
plugins: [vuexLocal.plugin]
})

33
web/src/store/module/app.js

@ -0,0 +1,33 @@
import variables from '@/style/element_visiable.scss'
export const app = {
namespaced: true,
state: {
theme: variables.colorPrimary,
sideMode: 'dark'
},
mutations: {
CHANGETHEME: (state, value) => {
state.theme = value
},
CHANGESIDEMODE: (state) => {
if (state.sideMode === 'dark') {
state.sideMode = 'light'
} else {
state.sideMode = 'dark'
}
}
},
actions: {
changeTheme({ commit }, data) {
commit('CHANGETHEME', data)
},
changeSideMode({ commit }) {
commit('CHANGESIDEMODE')
}
},
getters: {
getSIdeMode(state) {
return state.sideMode
}
}
}

9
web/src/style/basics.scss

@ -1,3 +1,4 @@
// basice
$font-size: 14px;
$icon-size:17px;
@ -14,10 +15,11 @@ $width-mobile-aside:210px;
$color-aside:rgba(255, 255, 255,.9);
$icon-arrow-size-aside:12px;
$width-submenu-aside:55px;
$bg-aside:#191a23;
$bg-aside:#fff;
$height-aside-tilte:64px;
$height-aside-img:30px;
$width-aside-img:30px;
$aside-color:#000;
// header
$height-header: 60px;
// nav-scroll
@ -35,3 +37,8 @@ $height-car:68px;
// mobile
$padding-xs: 5px;
$margin-xs: 5px;
:export{
bg_aside : $bg-aside ;
color_aside : $aside-color
}

15
web/src/style/element_visiable.scss

@ -0,0 +1,15 @@
/* 改变主题色变量 */
$--color-primary: #1890ff;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
:export {
colorPrimary: $--color-primary
}

124
web/src/style/main.scss

@ -8,7 +8,7 @@
*/
@import '@/style/basics.scss';
@import "side.scss";
html {
line-height: 1.15;
/* 1 */
@ -546,138 +546,18 @@ li {
top: 0;
}
.el-aside {
-webkit-transition: width .2s;
transition: width .2s;
width: $width-aside;
background-color: $bg-aside;
height: 100%;
position: fixed;
font-size: 0;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
.el-menu {
border-right: none;
}
.tilte {
min-height: $height-aside-tilte;
line-height: $height-aside-tilte;
background: $bg-aside;
text-align: center;
.logoimg {
width: $width-aside-img;
height: $height-aside-img;
vertical-align: middle;
background: #fff;
border-radius: 50%;
padding: 3px;
}
.tit-text {
display: inline-block;
color: #fff;
font-weight: 600;
font-size: 20px;
vertical-align: middle;
padding-left: 10px;
}
}
}
.aside {
.el-menu-vertical {
background-color: $bg-aside;
}
.el-submenu {
background-color: $bg-aside;
.el-menu {
.el-menu-item {
background-color: #000408;
height: 44px;
line-height: 44px;
}
.is-active {
background-color: #1890ff;
// 关闭三级菜单二级菜单样式
ul{
border:none;
}
}
// 关闭三级菜单二级菜单样式
.is-active.is-opened{
background-color: #191a23;
ul{
border:none;
}
}
}
}
.el-menu-item:focus, .el-menu-item:hover{
background-color: transparent;
}
.el-menu-item:hover i,
.el-menu-item:hover span {
color: #fff;
}
.el-submenu__title:hover {
background-color: $bg-aside;
}
.el-submenu__title:hover i,
.el-submenu__title:hover span {
color: #fff;
}
.el-menu--inline {
border-left: 5px solid #2c3b41;
}
}
.hideside {
.aside {
width: $width-hideside-aside;
}
}
.mobile.hideside {
.el-aside {
-webkit-transition-duration: .2s;
transition-duration: .2s;
-webkit-transform: translate3d(-210px, 0, 0);
transform: translate3d(-220px, 0, 0);
}
}
.mobile {
.el-aside {
-webkit-transition: -webkit-transform .28s;
transition: -webkit-transform .28s;
transition: transform .28s;
transition: transform .28s, -webkit-transform .28s;
width: $width-mobile-aside;
}
}
.main-cont.el-main {
min-height: 100%;
-webkit-transition: margin-left .28s;
transition: margin-left .28s;
margin-left: $width-aside;
position: relative;
}
.hideside {
.main-cont.el-main {
margin-left: 54px;
//margin-left: 54px;
}
}

71
web/src/style/side.scss

@ -0,0 +1,71 @@
@import '@/style/basics.scss';
.el-aside {
-webkit-transition: width .2s;
transition: width .2s;
width: $width-aside;
background-color: $bg-aside;
height: 100%;
position: fixed;
font-size: 0;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
color: $aside-color;
.el-menu {
border-right: none;
}
.tilte {
min-height: $height-aside-tilte;
line-height: $height-aside-tilte;
background: $bg-aside;
text-align: center;
.logoimg {
width: $width-aside-img;
height: $height-aside-img;
vertical-align: middle;
background: #fff;
border-radius: 50%;
padding: 3px;
}
.tit-text {
display: inline-block;
color: $aside-color;
font-weight: 600;
font-size: 20px;
vertical-align: middle;
padding-left: 10px;
}
}
}
.hideside {
.aside {
width: $width-hideside-aside;
}
}
.mobile.hideside {
.el-aside {
-webkit-transition-duration: .2s;
transition-duration: .2s;
-webkit-transform: translate3d(-210px, 0, 0);
transform: translate3d(-220px, 0, 0);
}
}
.mobile {
.el-aside {
-webkit-transition: -webkit-transform .28s;
transition: -webkit-transform .28s;
transition: transform .28s;
transition: transform .28s, -webkit-transform .28s;
margin-left: -54px;
}
}

25
web/src/view/layout/aside/historyComponent/history.vue

@ -9,11 +9,12 @@
@tab-remove="removeTab"
>
<el-tab-pane
v-for="item in historys"
v-for="(item , index) in historys"
:key="item.name + JSON.stringify(item.query)+JSON.stringify(item.params)"
:label="item.meta.title"
:name="item.name + JSON.stringify(item.query)+JSON.stringify(item.params)"
:tab="item"
:style="'z-index:'+index"
/>
</el-tabs>
@ -23,6 +24,7 @@
<li @click="closeLeft">关闭左侧</li>
<li @click="closeRight">关闭右侧</li>
<li @click="closeOther">关闭其他</li>
<li v-if="activeValue" @click="reload">重新加载</li>
</ul>
</div>
</template>
@ -257,6 +259,9 @@ export default {
}
}
this.historys.splice(index, 1)
},
reload() {
this.$bus.$emit('reload')
}
}
}
@ -266,8 +271,9 @@ export default {
.contextmenu {
width: 100px;
margin: 0;
border: 1px solid #ccc;
background: #fff;
border: 1px solid rgba(160, 156, 156, 0.2);
background: rgba(255,255,255,0.8);
backdrop-filter: blur(10px);
z-index: 3000;
position: absolute;
list-style-type: none;
@ -275,14 +281,23 @@ export default {
border-radius: 4px;
font-size: 14px;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.2);
box-shadow: 1px 1px 15px 0 rgba(0, 0, 0, 0.1);
}
.contextmenu li {
margin: 0;
padding: 7px 16px;
}
.contextmenu li:hover {
background: #f2f2f2;
color: #1890ff;
cursor: pointer;
}
.el-tabs__item{
-webkit-mask: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANoAAAAkBAMAAAAdqzmBAAAAMFBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlTPQ5AAAAD3RSTlMAr3DvEM8wgCBA379gj5//tJBPAAAAnUlEQVRIx2NgAAM27fj/tAO/xBsYkIHyf9qCT8iWMf6nNQhAsk2f5rYheY7Dnua2/U+A28ZEe8v+F9Ax2v7/F4DbxkUH2wzgtvHTwbYPo7aN2jZq26hto7aN2jZq25Cy7Qvctnw62PYNbls9HWz7S8/G6//PsI6H4396gAUQy1je08W2jxDbpv6nD4gB2uWp+J9eYPsEhv/0BPS1DQBvoBLVZ3BppgAAAABJRU5ErkJggg==);
mask: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANoAAAAkBAMAAAAdqzmBAAAAMFBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlTPQ5AAAAD3RSTlMAr3DvEM8wgCBA379gj5//tJBPAAAAnUlEQVRIx2NgAAM27fj/tAO/xBsYkIHyf9qCT8iWMf6nNQhAsk2f5rYheY7Dnua2/U+A28ZEe8v+F9Ax2v7/F4DbxkUH2wzgtvHTwbYPo7aN2jZq26hto7aN2jZq25Cy7Qvctnw62PYNbls9HWz7S8/G6//PsI6H4396gAUQy1je08W2jxDbpv6nD4gB2uWp+J9eYPsEhv/0BPS1DQBvoBLVZ3BppgAAAABJRU5ErkJggg==);
-webkit-mask-size: 100% 100%;
&.is-closable:hover{
background: #dee1e6;
}
}
</style>

15
web/src/view/layout/aside/index.vue

@ -6,9 +6,9 @@
:collapse="isCollapse"
:collapse-transition="true"
:default-active="active"
active-text-color="#fff"
class="el-menu-vertical"
text-color="rgb(191, 203, 217)"
:background-color="$store.getters['app/getSIdeMode'] === 'light' ? '#fff' : '#111'"
text-color="#777"
unique-opened
@select="selectMenuItem"
>
@ -95,4 +95,15 @@ export default {
vertical-align: middle;
}
}
.el-menu-item {
background-color: #fff;
height: 44px;
line-height: 44px;
color: #000;
&.is-active{
background-color: #e6f7ff ;
border-right: 4px solid ;
color: #1890ff ;
}
}
</style>

6
web/src/view/layout/index.vue

@ -74,9 +74,9 @@
<router-view v-if="!$route.meta.keepAlive && reloadFlag" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box" />
</transition>
<BottomInfo />
<setting />
</el-main>
</el-container>
</el-container>
</template>
@ -88,6 +88,7 @@ import Search from '@/view/layout/search/search'
import BottomInfo from '@/view/layout/bottomInfo/bottomInfo'
import { mapGetters, mapActions } from 'vuex'
import CustomPic from '@/components/customPic'
import Setting from './setting'
export default {
name: 'Layout',
components: {
@ -96,7 +97,8 @@ export default {
Screenfull,
Search,
BottomInfo,
CustomPic
CustomPic,
Setting
},
data() {
return {

9
web/src/view/layout/search/search.vue

@ -19,6 +19,8 @@
</el-select>
</div>
</transition>
<div
:style="{display:'inline-block',float:'right',width:'31px',textAlign:'left',fontSize:'16px',paddingTop:'2px'}"
class="user-box"
@ -36,11 +38,13 @@ import { mapGetters } from 'vuex'
export default {
name: 'SearchComponent',
data() {
return {
value: '',
show: false,
reload: false
reload: false,
color: '#fff'
}
},
computed: {
@ -66,7 +70,8 @@ export default {
setTimeout(() => {
this.reload = false
}, 500)
}
},
}
}
</script>

108
web/src/view/layout/setting/index.vue

@ -0,0 +1,108 @@
<template>
<div>
<el-button type="primary" class="drawer-container" icon="el-icon-setting" @click="showSettingDrawar" />
<el-drawer
title="系统配置"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose"
>
<div class="setting_body">
<div class="setting_card">
<div class="setting_title">侧边栏主题</div>
<div class="setting_content">
<div class="item" @click="chageMode('light')">
<i v-if="sideMode === 'light'" class="el-icon-check check" />
<img src="https://gw.alipayobjects.com/zos/antfincdn/NQ%24zoisaD2/jpRkZQMyYRryryPNtyIC.svg">
</div>
<div class="item" @click="chageMode('dark')">
<i v-if="sideMode === 'dark'" class="el-icon-check check" />
<img src="https://gw.alipayobjects.com/zos/antfincdn/XwFOFbLkSM/LCkqqYNmvBEbokSDscrm.svg">
</div>
</div>
</div>
<div class="setting_card">
<div class="setting_title">主题色</div>
<div class="">
<theme-change style="width: 30px;height: 30px;margin-top: 20px" @change="themeChange" />
</div>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
import themeChange from '@/components/themeChange'
export default {
data() {
return {
drawer: false,
direction: 'rtl',
sideMode: 'dark'
}
},
components: {
themeChange
},
methods: {
handleClose() {
this.drawer = false
},
showSettingDrawar() {
this.drawer = true
},
chageMode(e) {
this.sideMode = e
this.$store.dispatch('app/changeSideMode')
},
themeChange(val) {
this.$store.dispatch('app/changeTheme', val)
}
}
}
</script>
<style lang="scss" scoped>
.drawer-container {
position: absolute;
right: 0;
top: 20%;
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
color: #fff;
border-radius: 4px 0 0 4px;
cursor: pointer;
-webkit-box-shadow: inset 0 0 6px rgb(0 ,0 ,0, 10%);
}
.setting_body{
padding: 20px;
.setting_card{
margin-bottom: 20px;
}
.setting_content{
margin-top: 20px;
display: flex;
.item{
position: relative;
display: flex;
align-items: center;
justify-content: center;
.check{
position: absolute;
font-size: 20px;
color: #00afff;
}
img{
margin-right: 20px;
}
}
}
}
</style>

9510
web/yarn.lock
File diff suppressed because it is too large
View File

Loading…
Cancel
Save