diff --git a/src/mixins/theme.js b/src/mixins/theme.js
new file mode 100644
index 00000000..615ea27f
--- /dev/null
+++ b/src/mixins/theme.js
@@ -0,0 +1,174 @@
+import {
+ mapGetters
+} from "vuex";
+const version = require("element-ui/package.json").version; // element-ui version from node_modules
+const ORIGINAL_THEME = "#409EFF"; // default color
+export default function () {
+ return {
+ data() {
+ return {
+ themeVal: ORIGINAL_THEME
+ }
+ },
+ created() {
+ this.themeVal = this.theme;
+ },
+ watch: {
+ themeVal(val, oldVal) {
+ this.$store.commit("SET_THEME", val);
+ this.updateTheme(val, oldVal);
+ }
+ },
+ computed: {
+ ...mapGetters(["theme"])
+ },
+ methods: {
+ updateTheme(val, oldVal) {
+ if (typeof val !== "string") return;
+ const head = document.getElementsByTagName("head")[0];
+ 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);
+ head.appendChild(styleTag);
+ }
+ styleTag.innerText = newStyle;
+ };
+ };
+
+ const chalkHandler = getHandler("chalk", "chalk-style");
+
+ if (!this.chalk) {
+ const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
+ this.getCSSString(url, chalkHandler, "chalk");
+ } else {
+ chalkHandler();
+ }
+
+ const link = [].slice.call(
+ document.getElementsByTagName("head")[0].getElementsByTagName("link")
+ );
+ for (let i = link.length - 3; i < link.length; i++) {
+ const style = link[i];
+ this.getCSSString(style.href, innerText => {
+ const originalCluster = this.getThemeCluster(
+ ORIGINAL_THEME.replace("#", "")
+ );
+ const newStyle = this.updateStyle(
+ innerText,
+ originalCluster,
+ themeCluster
+ );
+ let styleTag = document.getElementById(i);
+ if (!styleTag) {
+ styleTag = document.createElement("style");
+ styleTag.id = i;
+ styleTag.innerText = newStyle;
+ head.appendChild(styleTag);
+ }
+ });
+ }
+
+ 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
+ );
+ });
+ },
+ updateStyle(style, oldCluster, newCluster) {
+ let newStyle = style;
+ oldCluster.forEach((color, index) => {
+ newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
+ });
+ return newStyle;
+ },
+
+ getCSSString(url, callback, variable) {
+ const xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = () => {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ if (variable) {
+ this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
+ }
+ callback(xhr.responseText);
+ }
+ };
+ 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;
+ }
+ }
+ }
+}
diff --git a/src/page/index/top/top-theme.vue b/src/page/index/top/top-theme.vue
index 285839ae..e9d9e2a0 100644
--- a/src/page/index/top/top-theme.vue
+++ b/src/page/index/top/top-theme.vue
@@ -1,170 +1,17 @@
-