Skip to content

Commit 6931843

Browse files
authored
Merge pull request #12 from pdsuwwz/feature/theme
Feature/theme
2 parents c032041 + d5f7419 commit 6931843

File tree

6 files changed

+288
-10
lines changed

6 files changed

+288
-10
lines changed

src/components/Layout/LayoutBasic.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<div class="inner-content">
99
<router-view />
1010
</div>
11+
<Settings />
1112
</main>
1213
</section>
1314
</div>
@@ -17,12 +18,14 @@
1718
import NavigationMenuBar from '@/components/Navigation/MenuBar/index'
1819
import NavigationNavBar from '@/components/Navigation/NavBar'
1920
import NavigationPageHeader from '@/components/Navigation/PageHeader'
21+
import Settings from '@/components/Layout/components/Settings'
2022
export default {
2123
name: 'LayoutBasic',
2224
components: {
2325
NavigationMenuBar,
2426
NavigationNavBar,
25-
NavigationPageHeader
27+
NavigationPageHeader,
28+
Settings
2629
}
2730
}
2831
</script>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<template>
2+
<div class="right-panel">
3+
<transition name="setting-icon">
4+
<el-button
5+
v-if="!isSetting"
6+
type="primary"
7+
class="wrap-settings"
8+
@click="handleDrawer()"
9+
>
10+
<i class="el-icon-setting"></i>
11+
</el-button>
12+
</transition>
13+
<el-drawer
14+
title="主题配置"
15+
size="22%"
16+
:visible="isSetting"
17+
@close="handleDrawer()"
18+
>
19+
<div class="wrap-slot">
20+
<div
21+
v-for="(slotItem, slotIndex) in slotList"
22+
:key="slotIndex"
23+
class="slot-item"
24+
>
25+
<div class="slot-title">
26+
{{ slotItem.title }}
27+
</div>
28+
<component
29+
:is="slotItem.component"
30+
/>
31+
</div>
32+
</div>
33+
</el-drawer>
34+
</div>
35+
</template>
36+
37+
<script>
38+
import ThemePocker from '@/components/ThemePicker'
39+
export default {
40+
name: 'RightPanel',
41+
components: {
42+
ThemePocker
43+
},
44+
data () {
45+
return {
46+
isSetting: false,
47+
isDrawer: true,
48+
slotList: [
49+
{
50+
title: '主题色',
51+
component: 'ThemePocker'
52+
}
53+
]
54+
}
55+
},
56+
methods: {
57+
handleDrawer () {
58+
this.isSetting = !this.isSetting
59+
}
60+
}
61+
}
62+
</script>
63+
64+
<style scoped lang="scss">
65+
.right-panel {
66+
.setting-icon-enter-active {
67+
transition: opacity .5s .1s;
68+
}
69+
.setting-icon-enter, .setting-icon-leave-to /* .fade-leave-active below version 2.1.8 */ {
70+
opacity: 0;
71+
}
72+
.wrap-settings {
73+
width: 45px;
74+
height: 45px;
75+
position: fixed;
76+
right: 0px;
77+
top: 50%;
78+
border-radius: 6px 0px 0px 6px;
79+
cursor: pointer;
80+
z-index: 99999;
81+
transform: translateY(-50%);
82+
padding: 0px;
83+
.el-icon-setting {
84+
font-size: 30px;
85+
}
86+
}
87+
/deep/ .wrap-slot {
88+
width: 100%;
89+
height: 100%;
90+
padding: 20px;
91+
.slot-item {
92+
display: flex;
93+
align-items: center;
94+
.slot-title {
95+
flex: 1;
96+
}
97+
}
98+
}
99+
}
100+
</style>

src/components/Navigation/MenuBar/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
:default-active="getCurrentRoute"
1818
class="menubar-menu-list"
1919
:collapse="isCollapse"
20-
:collapse-transition="false"
21-
:active-text-color="getActiveTextColor"
2220
@select="handleMenuSelect"
2321
>
22+
<!-- :collapse-transition="false" -->
23+
<!-- :active-text-color="getActiveTextColor" -->
2424
<NavigationMenuBarItem
2525
v-for="(route, index) in getRoutes"
2626
:key="index"
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<template>
2+
<el-color-picker
3+
v-model="theme"
4+
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
5+
class="theme-picker"
6+
popper-class="theme-picker-dropdown"
7+
/>
8+
</template>
9+
10+
<script>
11+
const version = require('element-ui/package.json').version // element-ui version from node_modules
12+
const ORIGINAL_THEME = '#409EFF' // default color
13+
14+
export default {
15+
data () {
16+
return {
17+
chalk: '', // content of theme-chalk css
18+
theme: ''
19+
}
20+
},
21+
computed: {
22+
defaultTheme () {
23+
return ORIGINAL_THEME
24+
}
25+
},
26+
watch: {
27+
defaultTheme: {
28+
handler: function (val, oldVal) {
29+
this.theme = val
30+
},
31+
immediate: true
32+
},
33+
async theme (val) {
34+
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
35+
if (typeof val !== 'string') return
36+
const themeCluster = this.getThemeCluster(val.replace('#', ''))
37+
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
38+
console.log(themeCluster, originalCluster)
39+
40+
const $message = this.$message({
41+
message: ' Compiling the theme',
42+
customClass: 'theme-message',
43+
type: 'success',
44+
duration: 0,
45+
iconClass: 'el-icon-loading'
46+
})
47+
48+
const getHandler = (variable, id) => {
49+
return () => {
50+
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
51+
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
52+
53+
let styleTag = document.getElementById(id)
54+
if (!styleTag) {
55+
styleTag = document.createElement('style')
56+
styleTag.setAttribute('id', id)
57+
document.head.appendChild(styleTag)
58+
}
59+
styleTag.innerText = newStyle
60+
}
61+
}
62+
63+
// if (!this.chalk) {
64+
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
65+
await this.getCSSString(url, 'chalk')
66+
// }
67+
68+
const chalkHandler = getHandler('chalk', 'chalk-style')
69+
70+
chalkHandler()
71+
72+
const styles = [].slice.call(document.querySelectorAll('style'))
73+
.filter(style => {
74+
const text = style.innerText
75+
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
76+
})
77+
styles.forEach(style => {
78+
const { innerText } = style
79+
if (typeof innerText !== 'string') return
80+
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
81+
})
82+
83+
this.$emit('change', val)
84+
85+
$message.close()
86+
}
87+
},
88+
89+
methods: {
90+
updateStyle (style, oldCluster, newCluster) {
91+
let newStyle = style
92+
oldCluster.forEach((color, index) => {
93+
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
94+
})
95+
return newStyle
96+
},
97+
98+
getCSSString (url, variable) {
99+
return new Promise(resolve => {
100+
const xhr = new XMLHttpRequest()
101+
xhr.onreadystatechange = () => {
102+
if (xhr.readyState === 4 && xhr.status === 200) {
103+
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
104+
resolve()
105+
}
106+
}
107+
xhr.open('GET', url)
108+
xhr.send()
109+
})
110+
},
111+
112+
getThemeCluster (theme) {
113+
const tintColor = (color, tint) => {
114+
let red = parseInt(color.slice(0, 2), 16)
115+
let green = parseInt(color.slice(2, 4), 16)
116+
let blue = parseInt(color.slice(4, 6), 16)
117+
118+
if (tint === 0) { // when primary color is in its rgb space
119+
return [red, green, blue].join(',')
120+
} else {
121+
red += Math.round(tint * (255 - red))
122+
green += Math.round(tint * (255 - green))
123+
blue += Math.round(tint * (255 - blue))
124+
125+
red = red.toString(16)
126+
green = green.toString(16)
127+
blue = blue.toString(16)
128+
129+
return `#${red}${green}${blue}`
130+
}
131+
}
132+
133+
const shadeColor = (color, shade) => {
134+
let red = parseInt(color.slice(0, 2), 16)
135+
let green = parseInt(color.slice(2, 4), 16)
136+
let blue = parseInt(color.slice(4, 6), 16)
137+
138+
red = Math.round((1 - shade) * red)
139+
green = Math.round((1 - shade) * green)
140+
blue = Math.round((1 - shade) * blue)
141+
142+
red = red.toString(16)
143+
green = green.toString(16)
144+
blue = blue.toString(16)
145+
146+
return `#${red}${green}${blue}`
147+
}
148+
149+
const clusters = [theme]
150+
for (let i = 0; i <= 9; i++) {
151+
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
152+
}
153+
clusters.push(shadeColor(theme, 0.1))
154+
return clusters
155+
}
156+
}
157+
}
158+
</script>
159+
160+
<style>
161+
.theme-message,
162+
.theme-picker-dropdown {
163+
z-index: 99999 !important;
164+
}
165+
166+
.theme-picker .el-color-picker__trigger {
167+
height: 26px !important;
168+
width: 26px !important;
169+
padding: 2px;
170+
}
171+
172+
.theme-picker-dropdown .el-color-dropdown__link-btn {
173+
display: none;
174+
}
175+
</style>

src/styles/element-variables.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ $--border-color-light: #dfe4ed;
1717
$--border-color-lighter: #e6ebf5;
1818
$--table-border: 1px solid #dfe6ec;
1919

20-
:export {
21-
colorPrimary: $--color-primary;
22-
}
20+
// :export {
21+
// colorPrimary: $--color-primary;
22+
// }

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,10 +2116,10 @@ aws4@^1.8.0:
21162116
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
21172117
integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
21182118

2119-
axios@^0.20.0:
2120-
version "0.20.0"
2121-
resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd"
2122-
integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==
2119+
axios@^0.21.1:
2120+
version "0.21.1"
2121+
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
2122+
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
21232123
dependencies:
21242124
follow-redirects "^1.10.0"
21252125

0 commit comments

Comments
 (0)