소스 검색

Chore: support theme configuration (#15190)

iamkun 6 년 전
부모
커밋
ac3aa99503
76개의 변경된 파일2689개의 추가작업 그리고 570개의 파일을 삭제
  1. BIN
      examples/assets/images/icon-check.png
  2. BIN
      examples/assets/images/icon-copy.png
  3. BIN
      examples/assets/images/icon-download.png
  4. BIN
      examples/assets/images/icon-edit.png
  5. 19 0
      examples/assets/images/icon-redo.svg
  6. 19 0
      examples/assets/images/icon-undo.svg
  7. 19 0
      examples/assets/images/icon-upload.svg
  8. BIN
      examples/assets/images/theme-no-config.png
  9. 16 6
      examples/components/header.vue
  10. 153 0
      examples/components/theme-configurator/action.vue
  11. 0 78
      examples/components/theme-configurator/download.vue
  12. 4 1
      examples/components/theme-configurator/editor/borderRadius.vue
  13. 3 1
      examples/components/theme-configurator/editor/boxShadow.vue
  14. 1 0
      examples/components/theme-configurator/editor/color-picker/src/components/picker-dropdown.vue
  15. 5 1
      examples/components/theme-configurator/editor/color.vue
  16. 4 1
      examples/components/theme-configurator/editor/fontLineHeight.vue
  17. 5 1
      examples/components/theme-configurator/editor/fontSize.vue
  18. 4 1
      examples/components/theme-configurator/editor/fontWeight.vue
  19. 7 1
      examples/components/theme-configurator/editor/mixin.vue
  20. 4 1
      examples/components/theme-configurator/editor/simpleText.vue
  21. 87 189
      examples/components/theme-configurator/index.vue
  22. 15 9
      examples/components/theme-configurator/main.vue
  23. 1 15
      examples/components/theme-configurator/utils/utils.js
  24. 280 0
      examples/components/theme/basic-tokens-preview.vue
  25. 518 0
      examples/components/theme/components-preview.vue
  26. 16 0
      examples/components/theme/constant.js
  27. 0 0
      examples/components/theme/loader/ajax.js
  28. 0 0
      examples/components/theme/loader/api.js
  29. 3 2
      examples/components/theme/loader/docStyle.vue
  30. 106 0
      examples/components/theme/loader/index.vue
  31. 1 1
      examples/components/theme/loader/loading/index.vue
  32. 0 0
      examples/components/theme/loader/loading/progress.js
  33. 0 0
      examples/components/theme/loader/loading/progress.vue
  34. 38 0
      examples/components/theme/localstorage.js
  35. 401 0
      examples/components/theme/theme-card.vue
  36. 13 0
      examples/components/theme/theme-list.js
  37. 24 0
      examples/components/theme/utils.js
  38. 2 1
      examples/docs/en-US/border.md
  39. 2 1
      examples/docs/en-US/color.md
  40. 2 1
      examples/docs/en-US/typography.md
  41. 2 1
      examples/docs/es/border.md
  42. 2 1
      examples/docs/es/color.md
  43. 2 1
      examples/docs/es/typography.md
  44. 2 1
      examples/docs/fr-FR/border.md
  45. 2 1
      examples/docs/fr-FR/color.md
  46. 2 1
      examples/docs/fr-FR/typography.md
  47. 2 1
      examples/docs/zh-CN/border.md
  48. 2 1
      examples/docs/zh-CN/color.md
  49. 2 1
      examples/docs/zh-CN/typography.md
  50. 4 0
      examples/i18n/component.json
  51. 36 0
      examples/i18n/page.json
  52. 184 26
      examples/i18n/theme-editor.json
  53. 3 42
      examples/pages/template/component.tpl
  54. 3 0
      examples/pages/template/theme-nav.tpl
  55. 195 0
      examples/pages/template/theme-preview.tpl
  56. 225 0
      examples/pages/template/theme.tpl
  57. 19 1
      examples/route.config.js
  58. 1 1
      packages/theme-chalk/src/badge.scss
  59. 5 5
      packages/theme-chalk/src/checkbox.scss
  60. 6 6
      packages/theme-chalk/src/collapse.scss
  61. 159 110
      packages/theme-chalk/src/common/var.scss
  62. 1 1
      packages/theme-chalk/src/date-picker/date-picker.scss
  63. 7 7
      packages/theme-chalk/src/date-picker/date-table.scss
  64. 3 3
      packages/theme-chalk/src/date-picker/month-table.scss
  65. 2 2
      packages/theme-chalk/src/date-picker/picker-panel.scss
  66. 1 1
      packages/theme-chalk/src/date-picker/year-table.scss
  67. 1 1
      packages/theme-chalk/src/dialog.scss
  68. 2 2
      packages/theme-chalk/src/menu.scss
  69. 10 10
      packages/theme-chalk/src/message-box.scss
  70. 9 9
      packages/theme-chalk/src/message.scss
  71. 6 6
      packages/theme-chalk/src/notification.scss
  72. 9 9
      packages/theme-chalk/src/pagination.scss
  73. 2 2
      packages/theme-chalk/src/popover.scss
  74. 4 4
      packages/theme-chalk/src/popper.scss
  75. 1 1
      packages/theme-chalk/src/table.scss
  76. 1 1
      packages/theme-chalk/src/tag.scss

BIN
examples/assets/images/icon-check.png


BIN
examples/assets/images/icon-copy.png


BIN
examples/assets/images/icon-download.png


BIN
examples/assets/images/icon-edit.png


+ 19 - 0
examples/assets/images/icon-redo.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="13px" height="12px" viewBox="0 0 13 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
+    <title>icon-redo</title>
+    <desc>Created with Sketch.</desc>
+    <g id="theme" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="element-theme-editor" transform="translate(-1118.000000, -167.000000)" fill="#606266" fill-rule="nonzero">
+            <g id="custom" transform="translate(1060.000000, 146.000000)">
+                <g id="Group-2" transform="translate(30.000000, 15.000000)">
+                    <g id="Group" transform="translate(0.000000, 4.000000)">
+                        <g id="icon-clockwise" transform="translate(26.000000, 0.000000)">
+                            <path d="M12.2585,3.59833136 L12.2585,2.3085 L13.2585,2.3085 L13.2585,5.6365 L9.9295,5.6365 L9.9295,4.6365 L11.8833737,4.6365 C10.9510824,3.62012881 9.6272717,3.0179 8.2,3.0179 C5.43814237,3.0179 3.2,5.25604237 3.2,8.0179 C3.2,10.7797576 5.43814237,13.0179 8.2,13.0179 C10.9618576,13.0179 13.2,10.7797576 13.2,8.0179 L14.2,8.0179 C14.2,11.3320424 11.5141424,14.0179 8.2,14.0179 C4.88585763,14.0179 2.2,11.3320424 2.2,8.0179 C2.2,4.70375763 4.88585763,2.0179 8.2,2.0179 C9.73531163,2.0179 11.1716793,2.59926473 12.2585,3.59833136 Z" id="icon-redo"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 19 - 0
examples/assets/images/icon-undo.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="13px" height="12px" viewBox="0 0 13 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
+    <title>icon-undo</title>
+    <desc>Created with Sketch.</desc>
+    <g id="theme" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="element-theme-editor" transform="translate(-1092.000000, -167.000000)" fill="#606266" fill-rule="nonzero">
+            <g id="custom" transform="translate(1060.000000, 146.000000)">
+                <g id="Group-2" transform="translate(30.000000, 15.000000)">
+                    <g id="Group" transform="translate(0.000000, 4.000000)">
+                        <g id="icon-clockwise">
+                            <path d="M12.2585,3.59833136 L12.2585,2.3085 L13.2585,2.3085 L13.2585,5.6365 L9.9295,5.6365 L9.9295,4.6365 L11.8833737,4.6365 C10.9510824,3.62012881 9.6272717,3.0179 8.2,3.0179 C5.43814237,3.0179 3.2,5.25604237 3.2,8.0179 C3.2,10.7797576 5.43814237,13.0179 8.2,13.0179 C10.9618576,13.0179 13.2,10.7797576 13.2,8.0179 L14.2,8.0179 C14.2,11.3320424 11.5141424,14.0179 8.2,14.0179 C4.88585763,14.0179 2.2,11.3320424 2.2,8.0179 C2.2,4.70375763 4.88585763,2.0179 8.2,2.0179 C9.73531163,2.0179 11.1716793,2.59926473 12.2585,3.59833136 Z" id="icon-undo" transform="translate(8.200000, 8.017900) scale(-1, 1) translate(-8.200000, -8.017900) "></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 19 - 0
examples/assets/images/icon-upload.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
+    <title>icon_upload</title>
+    <desc>Created with Sketch.</desc>
+    <g id="theme" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="element-theme" transform="translate(-207.000000, -662.000000)" fill="#909399">
+            <g id="section" transform="translate(70.000000, 511.000000)">
+                <g id="card" transform="translate(0.000000, 60.000000)">
+                    <g id="btn" transform="translate(108.000000, 91.000000)">
+                        <g id="icon-upload" transform="translate(29.000000, 0.000000)">
+                            <path d="M13,-0.000600000001 L3.015,9.9854 L4.429,11.3984 L12,3.8284 L12,20.4854 L14.001,20.4854 L14.001,3.8284 L21.572,11.3984 L22.986,9.9854 L14.415,1.4144 L13,-0.000600000001 Z M13,2.8284 L13.158,2.9844 L12.843,2.9844 L13,2.8284 Z M24.001,19.9854 L24.001,23.9854 L2,23.9854 L2,19.9854 L0,19.9854 L0,25.9854 L26.001,25.9854 L26.001,19.9854 L24.001,19.9854 Z" id="icon_upload"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

BIN
examples/assets/images/theme-no-config.png


+ 16 - 6
examples/components/header.vue

@@ -301,6 +301,15 @@
               :to="`/${ lang }/component`">{{ langConfig.components }}
             </router-link>
           </li>
+          <li 
+            class="nav-item"
+            v-if="showThemeConfigurator"
+          >
+            <router-link
+              active-class="active"
+              :to="`/${ lang }/theme`">{{ langConfig.theme }}
+            </router-link>
+          </li>
           <li class="nav-item">
             <router-link
               active-class="active"
@@ -364,8 +373,7 @@
           
           <!--theme picker-->
           <li class="nav-item nav-theme-switch" v-show="isComponentPage">
-            <theme-configurator :key="lang" v-if="showThemeConfigurator"></theme-configurator>
-            <theme-picker v-else></theme-picker>
+            <theme-picker v-if="!showThemeConfigurator"></theme-picker>
           </li>
         </ul>
       </div>
@@ -374,12 +382,13 @@
 </template>
 <script>
   import ThemePicker from './theme-picker.vue';
-  import ThemeConfigurator from './theme-configurator';
   import AlgoliaSearch from './search.vue';
   import compoLang from '../i18n/component.json';
   import Element from 'main/index.js';
-  import { getVars } from './theme-configurator/utils/api.js';
+  import themeLoader from './theme/loader';
+  import { getVars } from './theme/loader/api.js';
   import bus from '../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from './theme/constant.js';
 
   const { version } = Element;
 
@@ -401,9 +410,10 @@
       };
     },
 
+    mixins: [themeLoader],
+
     components: {
       ThemePicker,
-      ThemeConfigurator,
       AlgoliaSearch
     },
 
@@ -470,7 +480,7 @@
       xhr.open('GET', '/versions.json');
       xhr.send();
       let primaryLast = '#409EFF';
-      bus.$on('user-theme-config-update', (val) => {
+      bus.$on(ACTION_USER_CONFIG_UPDATE, (val) => {
         let primaryColor = val.global['$--color-primary'];
         if (!primaryColor) primaryColor = '#409EFF';
         const base64svg = 'data:image/svg+xml;base64,';

+ 153 - 0
examples/components/theme-configurator/action.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="configurator-action">
+      <div class="action-group">
+        <el-tooltip :content="getActionDisplayName('undo')">
+          <img 
+            src="../../assets/images/icon-undo.svg"
+            @click="onUndo"
+            :class="{ 'active': userConfigHistory.length > 0 }"
+          />
+        </el-tooltip>
+        <el-tooltip :content="getActionDisplayName('redo')">
+          <img 
+            src="../../assets/images/icon-redo.svg"
+            @click="onRedo"
+            :class="{ 'active': userConfigRedoHistory.length > 0 }"
+          />
+        </el-tooltip>
+        <div class="button-group">
+          <el-button 
+            class="reset"
+            type="primary" 
+            round 
+            size="mini"
+            :disabled="isOfficial"
+            @click="onReset"
+          >
+            {{getActionDisplayName('reset-theme')}}
+          </el-button>
+          <el-button 
+            class="download"
+            type="primary" 
+            round 
+            size="mini"
+            :disabled="downloadDisabled"
+            @click="onDownload"
+          >
+            {{getActionDisplayName('download-theme')}}
+          </el-button>
+        </div>
+      </div>
+      <el-select v-model="selectedComponent" class="selector">
+        <el-option
+          v-for="item in selectOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value">
+        </el-option>
+      </el-select>
+      <div class="line"></div>
+    </div>
+</template>
+
+<style lang="scss">
+.configurator-action {
+  padding: 15px 18px 0;
+  .action-group {
+    padding: 5px 0;
+    img {
+      cursor: not-allowed;
+      width: 16px;
+      height: 16px;
+      padding: 7px 0;
+      margin-left: 5px;
+      opacity: .5;
+      &.active {
+        opacity: 1;
+        cursor: pointer;
+      }
+      &:last-of-type {
+        margin-left: 10px;
+      }
+    }
+    .button-group {
+      float: right;
+      .el-button {
+        padding: 6px 15px;
+        &.is-disabled {
+          color: #C0C4CC;
+          background-color: #fff;
+          border-color: #EBEEF5;
+        }
+      }
+      .reset {
+        background: #E6F1FC;
+        color: #1989FA;
+        border-color: #A2CFFC;
+      }
+      .download {
+        background: #1989FA;
+        color: #FFF;
+        border-color: #1989FA
+      }
+    }
+  }
+  .selector {
+    width: 100%;
+    input {
+      background: #f5f7fa;
+      border: none;
+      font-size: 18px;
+      padding-left: 0;
+      color: #606266;
+    }
+  }
+  .line {
+    width: 100%;
+    height: 0;
+    border-bottom: 1px solid #DCDFE6;
+  }
+}
+</style>
+
+<script>
+import { getActionDisplayName } from './utils/utils';
+export default {
+  props: {
+    selectOptions: Array,
+    userConfigHistory: Array,
+    userConfigRedoHistory: Array,
+    isOfficial: Boolean,
+    onUndo: Function,
+    onRedo: Function
+  },
+  data() {
+    return {
+      selectedComponent: 'color',
+      downloadDisabled: false
+    };
+  },
+  watch: {
+    selectedComponent: {
+      handler(val) {
+        this.$emit('select', val);
+      }
+    }
+  },
+  methods: {
+    getActionDisplayName(key) {
+      return getActionDisplayName(key);
+    },
+    onReset() {
+      this.$parent.onReset();
+    },
+    onDownload() {
+      this.downloadDisabled = true;
+      this.$parent.onDownload();
+      setTimeout(() => {
+        this.downloadDisabled = false;
+      }, 2500);
+    }
+  }
+};
+</script>

+ 0 - 78
examples/components/theme-configurator/download.vue

@@ -1,78 +0,0 @@
-<template>
-  <div class="action-area">
-    <div class="action-area-main">
-    <div class="action-button">
-      <el-button type="warning" @click.stop="onReset">{{getActionDisplayName("reset-theme")}}</el-button>
-    </div>
-    <div class="action-button">
-      <el-button 
-        type="primary" 
-        :loading=downloading
-        style="background: #66b1ff;border-color: #66b1ff"
-        @click.stop="onDownload">
-        {{getActionDisplayName("download-theme")}}
-      </el-button>
-      </div>
-    </div>
-  </div>
-</template>
-<style>
-.action-area { 
-  width: 24%;
-  max-width: 400px;
-  position: fixed;
-  right: 0;
-  bottom: 0;
-  padding-right: 1%;
-}
-@media (min-width: 1600px) {
-  .action-area {
-    padding-right: calc((100% - 1600px) / 2);
-  }
-}
-.action-area-main {
-  opacity: .95;
-  background: #F5F7FA;
-  height: 70px;
-  bottom: 0;
-  width: 97%;
-  box-sizing: border-box;
-  margin: 0 .5% 0 5%;
-}
-.action-button {
-  width: 50%;
-  text-align: center;
-  display: inline-block;
-  padding-top: 15px;
-}
-</style>
-<script>
-import { getActionDisplayName } from './utils/utils.js';
-export default {
-  data() {
-    return {
-      downloading: false
-    };
-  },
-  methods: {
-    onReset() {
-      this.$parent.onReset();
-    },
-    getActionDisplayName(key) {
-      return getActionDisplayName(key);
-    },
-    onDownload() {
-      this.downloading = true;
-      this.$parent.onDownload()
-        .then()
-        .catch((err) => {
-          this.$parent.onError(err);
-        })
-        .then(() => {
-          this.downloading = false;
-        });
-      ga('send', 'event', 'ThemeConfigurator', 'Download');
-    }
-  }
-};
-</script>

+ 4 - 1
examples/components/theme-configurator/editor/borderRadius.vue

@@ -1,7 +1,9 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <theme-input 
@@ -10,6 +12,7 @@
         @change="onChange"
       ></theme-input>
       <el-select 
+        size="medium"
         v-if="!isGlobal"
         v-model="value" 
         class="select"

+ 3 - 1
examples/components/theme-configurator/editor/boxShadow.vue

@@ -1,7 +1,9 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}} 
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
       <el-button 
         class="plus-button" 
         size="mini" 

+ 1 - 0
examples/components/theme-configurator/editor/color-picker/src/components/picker-dropdown.vue

@@ -29,6 +29,7 @@
         <el-button
           plain
           size="mini"
+          type="primary"
           class="el-color-dropdown__btn"
           @click="confirmValue">
           {{ t('el.colorpicker.confirm') }}

+ 5 - 1
examples/components/theme-configurator/editor/color.vue

@@ -1,11 +1,14 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <div class="content-80">
         <el-input
+          size="medium"
           :value=displayValue
           readonly
           slot="reference"
@@ -14,6 +17,7 @@
       </div>
       <div class="content-20">
         <color-picker 
+          size="medium"
           ref="colorPicker"
           class="colorPicker"
           v-model="pickerColor" 

+ 4 - 1
examples/components/theme-configurator/editor/fontLineHeight.vue

@@ -1,12 +1,15 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <el-select 
         v-model="value" 
         class="select"
+        size="medium"
         @change="onSelectChange"
       >
         <el-option

+ 5 - 1
examples/components/theme-configurator/editor/fontSize.vue

@@ -1,12 +1,15 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <el-select 
         v-model="value" 
         class="select"
+        size="medium"
         @change="onSelectChange"
       >
         <el-option
@@ -34,6 +37,7 @@ const defaultFontSize = [
   '16px',
   '18px',
   '20px',
+  '22px',
   '28px',
   '36px',
   '48px'

+ 4 - 1
examples/components/theme-configurator/editor/fontWeight.vue

@@ -1,12 +1,15 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <el-select 
         v-model="value" 
         class="select"
+        size="medium"
         @change="onSelectChange"
       >
         <el-option

+ 7 - 1
examples/components/theme-configurator/editor/mixin.vue

@@ -5,7 +5,7 @@
     .config-label {
       color: #606266;;
       font-size: 14px;
-      padding-bottom: 5px;
+      padding-bottom: 8px;
       position: relative;
     }
     .config-content {
@@ -59,6 +59,12 @@ export default {
     displayName() {
       return getStyleDisplayName(this.config, this.componentName);
     },
+    displayKeyName() {
+      if (this.config.name) {
+        return this.config.key.replace('$--', '');
+      }
+      return this.config.key.replace(`$--${this.componentName}-`, '');
+    },
     isGlobal() {
       return !this.config.value.startsWith('$');
     }

+ 4 - 1
examples/components/theme-configurator/editor/simpleText.vue

@@ -1,11 +1,14 @@
 <template>
   <section class="config" :key="displayName">
     <div class="config-label">
-      {{displayName}}
+      <el-tooltip :content="displayName">
+        <span>{{displayKeyName}}</span>
+      </el-tooltip>
     </div>
     <div class="config-content">
       <theme-input 
         :val="value"
+        size="medium"
         @change="onChange"
       ></theme-input>
     </div>

+ 87 - 189
examples/components/theme-configurator/index.vue

@@ -1,161 +1,95 @@
 <template>
-  <div>
-    <el-button
-      round
-      type="primary"
-      size="mini"
-      style="background: #66b1ff;border-color: #66b1ff"
-      @click.stop="showConfigurator"
-    >{{getActionDisplayName("theme-editor")}}</el-button>
-    <transition name="fade">
-      <div v-show="visible" class="configurator" ref="configurator">
-        <div 
-          class="main-configurator"
-          ref="mainConfigurator"
-        >
-          <div v-if="currentConfig">
-            <main-panel
-              :currentConfig="currentConfig"
-              :defaultConfig="defaultConfig"
-              :userConfig="userConfig"
-              :globalValue="globalValue"
-              @onChange="userConfigChange"
-            ></main-panel>
-          </div>
-          <div v-if="init && !currentConfig" class="no-config">
-            <img src="../../assets/images/theme-no-config.png" alt>
-            <span v-if="pageCouldEdit">{{getActionDisplayName("no-config")}}</span>
-            <span v-else>{{getActionDisplayName("no-need-config")}}</span>
-          </div>
-          <download-area></download-area>
-        </div>
-      </div>
-    </transition>
+  <div class="main-configurator" ref='configurator'>
+    <action-panel
+      :selectOptions="selectOptions"
+      :userConfigHistory="userConfigHistory"
+      :userConfigRedoHistory="userConfigRedoHistory"
+      :onUndo="undo"
+      :onRedo="redo"
+      :isOfficial="isOfficial"
+      @select="onSelectChange"
+    ></action-panel>
+    <main-panel
+      v-if="defaultConfig"
+      :currentConfig="currentConfig"
+      :defaultConfig="defaultConfig"
+      :userConfig="userConfig"
+      :globalValue="globalValue"
+      @onChange="userConfigChange"
+    ></main-panel>
   </div>
 </template>
 
-<style>
-.configurator {
+<style lang="scss">
+.main-configurator {
   height: 100%;
-  width: 24%;
-  max-width: 400px;
-  position: fixed;
-  overflow-y: auto;
-  top: 79px;
-  right: 0;
-  bottom: 0;
-  background: #fff;
-  color: #666;
-  line-height: 24px;
-  padding-right: 1%;
+  display: flex;
+  flex-direction: column;
 }
-.configurator .main-configurator {
-  height: 100%;
-  overflow: auto;
-  background: #F5F7FA;
-  border: 1px solid #EBEEF5;
-  box-shadow: 0 2px 10px 0 rgba(0,0,0,0.06);
-  border-radius: 8px;
-  width: 97%;
-  box-sizing: border-box;
-  margin: 0 .5% 0 5%;
-}
-.no-config {
-  margin-top: 130px;
-  text-align: center;
-  img {
-    width: 50%;
-    display: block;
-    margin: 0 auto;
-  }
-  span {
-    margin-top: 10px;
-    display: block;
-    color: #909399;
-    font-size: 14px;
-  }
-}
-.fade-enter,.fade-leave-to {
-    transform:translateX(100%);
-}
-.fade-enter-active ,.fade-leave-active {
-    transition:all 0.3s ease;
-}
-
-@media (min-width: 1600px) {
-  .configurator {
-    padding-right: calc((100% - 1600px) / 2);
-  }
-}
-
 </style>
 
 <script>
-import bus from '../../bus';
-import { getVars, updateVars } from './utils/api.js';
+import bus from '../../bus.js';
+import { getVars } from '../theme/loader/api.js';
 import mainPanel from './main';
+import actionPanel from './action';
 import {
   filterConfigType,
   filterGlobalValue,
-  updateDomHeadStyle,
   getActionDisplayName
 } from './utils/utils.js';
-import DocStyle from './docStyle';
-import Loading from './loading';
 import Shortcut from './shortcut';
-import DownloadArea from './download';
-
-const ELEMENT_THEME_USER_CONFIG = 'ELEMENT_THEME_USER_CONFIG';
-
-const DEFAULT_USER_CONFIG = {
-  global: {},
-  local: {}
-};
+import {
+  ACTION_APPLY_THEME,
+  ACTION_DOWNLOAD_THEME,
+  ACTION_COMPONECT_SELECT
+} from '../theme/constant.js';
 
 export default {
+  props: {
+    themeConfig: Object,
+    isOfficial: Boolean,
+    onUserConfigUpdate: Function
+  },
   components: {
     mainPanel,
-    DownloadArea
+    actionPanel
   },
   data() {
     return {
       init: false,
-      visible: false,
       defaultConfig: null,
       currentConfig: null,
       userConfig: {
         global: {},
         local: {}
       },
-      lastApply: 0,
       userConfigHistory: [],
       userConfigRedoHistory: [],
-      hasLocalConfig: false
+      hasLocalConfig: false,
+      selectOptions: [],
+      selectedComponent: 'color'
     };
   },
-  mixins: [DocStyle, Loading, Shortcut],
+  mixins: [Shortcut],
   computed: {
     globalValue() {
       return filterGlobalValue(this.defaultConfig, this.userConfig);
-    },
-    pageCouldEdit() {
-      const noNeedEdit = ['installation', 'quickstart', 'i18n', 'custom-theme', 'transition'];
-      const lastPath = this.$route.path.split('/').slice(-1).pop();
-      return noNeedEdit.indexOf(lastPath) < 0;
     }
   },
   mounted() {
-    this.checkLocalThemeConfig();
+    ga('send', 'event', 'ThemeConfigurator', 'Init');
+    this.showConfigurator();
+    this.enableShortcut();
+  },
+  beforeDestroy() {
+    this.disableShortcut();
   },
   methods: {
     getActionDisplayName(key) {
       return getActionDisplayName(key);
     },
     showConfigurator() {
-      this.visible = !this.visible;
-      this.visible ? this.enableShortcut() : this.disableShortcut();
-      bus.$emit('user-theme-config-visible', this.visible);
-      window.userThemeConfigVisible = Boolean(this.visible);
       if (this.init) return;
       this.$nextTick(() => {
         const loading = this.$loading({
@@ -163,109 +97,69 @@ export default {
         });
         let defaultConfig;
         getVars()
-          .then((res) => {
+          .then(res => {
             defaultConfig = res;
-            ga('send', 'event', 'ThemeConfigurator', 'Init');
           })
-          .catch((err) => {
+          .catch(err => {
             this.onError(err);
           })
           .then(() => {
             setTimeout(() => {
               if (defaultConfig) {
                 this.defaultConfig = defaultConfig;
+                this.setSelectOption();
                 this.filterCurrentConfig();
                 this.init = true;
-                this.checkLocalThemeConfig();
               }
               loading.close();
             }, 300); // action after transition
           });
       });
     },
-    checkLocalThemeConfig() {
-      try {
-        if (this.hasLocalConfig) {
-          this.$message(getActionDisplayName('load-local-theme-config'));
-          this.onAction();
-          return;
-        }
-        const config = JSON.parse(localStorage.getItem(ELEMENT_THEME_USER_CONFIG));
-        if (config && config.global) {
-          this.userConfig = config;
-          this.hasLocalConfig = true;
-          this.showConfigurator();
-        }
-      } catch (e) {
-        // bad local config
-      }
+    setSelectOption() {
+      this.selectOptions = this.defaultConfig.map((config) => ({
+        label: config.name.charAt(0).toUpperCase() + config.name.slice(1),
+        value: config.name
+      })).sort((a, b) => {
+        const A = a.label;
+        const B = b.label;
+        if (A < B) return -1;
+        if (A > B) return 1;
+        return 0;
+      });
     },
     filterCurrentConfig() {
-      this.currentConfig = this.defaultConfig.find((config) => {
-        return config.name === this.$route.path.split('/').pop().toLowerCase().replace('-', '');
+      this.currentConfig = this.defaultConfig.find(config => {
+        return (
+          config.name === this.selectedComponent
+        );
       });
     },
     userConfigChange(e) {
       this.userConfigHistory.push(JSON.stringify(this.userConfig));
       this.userConfigRedoHistory = [];
-      this.$set(this.userConfig[filterConfigType(this.currentConfig.name)], e.key, e.value);
+      this.$set(
+        this.userConfig[filterConfigType(this.currentConfig.name)],
+        e.key,
+        e.value
+      );
       this.onAction();
     },
-    applyStyle(res, time) {
-      if (time < this.lastApply) return;
-      this.updateDocs(() => {
-        updateDomHeadStyle('chalk-style', res);
-      });
-      this.lastApply = time;
-    },
-    onDownload() {
-      return updateVars(Object.assign({}, this.userConfig, {download: true}), (xhr) => {
-        xhr.responseType = 'blob';
-      });
-    },
     onReset() {
+      this.userConfigRedoHistory = [];
+      this.userConfigHistory = [];
       this.userConfig = {
         global: {},
         local: {}
       };
       this.onAction();
     },
-    onAction() {
-      this.triggerComponentLoading(true);
-      const time = +new Date();
-      const currentConfigString = JSON.stringify(this.userConfig);
-      if (JSON.stringify(DEFAULT_USER_CONFIG) === currentConfigString) {
-        localStorage.removeItem(ELEMENT_THEME_USER_CONFIG);
-      } else {
-        localStorage.setItem(ELEMENT_THEME_USER_CONFIG, currentConfigString);
-      }
-      updateVars(this.userConfig)
-        .then((res) => {
-          this.applyStyle(res, time);
-        })
-        .catch((err) => {
-          this.onError(err);
-        })
-        .then(() => {
-          this.triggerComponentLoading(false);
-        });
-    },
-    onError(err) {
-      let message;
-      try {
-        message = JSON.parse(err).message;
-      } catch (e) {
-        message = err;
-      }
-      this.$message.error(message);
-    },
-    triggerComponentLoading(val) {
-      bus.$emit('user-theme-config-loading', val);
+    onDownload() {
+      bus.$emit(ACTION_DOWNLOAD_THEME, this.userConfig);
     },
-    updateDocs(cb) {
-      window.userThemeConfig = JSON.parse(JSON.stringify(this.userConfig));
-      bus.$emit('user-theme-config-update', this.userConfig);
-      this.updateDocStyle(this.userConfig, cb);
+    onAction() {
+      this.onUserConfigUpdate(this.userConfig);
+      bus.$emit(ACTION_APPLY_THEME, this.userConfig);
     },
     undo() {
       if (this.userConfigHistory.length > 0) {
@@ -280,16 +174,20 @@ export default {
         this.userConfig = JSON.parse(this.userConfigRedoHistory.shift());
         this.onAction();
       }
+    },
+    onSelectChange(val) {
+      bus.$emit(ACTION_COMPONECT_SELECT, val);
+      this.selectedComponent = val;
+      this.filterCurrentConfig();
     }
   },
   watch: {
-    '$route.path'() {
-      this.defaultConfig && this.filterCurrentConfig();
-      if (!this.$refs.mainConfigurator) return;
-      this.$nextTick(() => {
-        this.$refs.mainConfigurator.scrollTop = 0;
-      });
-
+    themeConfig: {
+      handler(val, oldVal) {
+        if (!oldVal.globnal) {
+          this.userConfig = val;
+        }
+      }
     }
   }
 };

+ 15 - 9
examples/components/theme-configurator/main.vue

@@ -1,12 +1,12 @@
 <template>
-  <div class="main">
+  <div class="main" ref="mainPanel">
     <!-- <span>{{configName}}</span> -->
     <div v-for="(config, key) in configByOrder" :key="key">
       <span 
         v-if="showCategory(config.category, key + 1)"
         class="category-name"
       >
-        {{getCategoryDisplayName(config.category)}}
+        {{config.category}}
       </span>
       <component 
         :is="editorComponent(config.type)"
@@ -23,14 +23,14 @@
 
 <style>
 .main {
-  margin-bottom: 140px;
-  padding: 10px;
+  padding: 0 18px 15px;
+  overflow-y: auto;
 }
 .category-name {
   color: #C0C4CC;
   font-size: 18px;
   display: block;
-  margin: 8px 0 4px 0;
+  margin: 13px 0 3px 0;
 }
 </style>
 
@@ -42,7 +42,7 @@ import fontLineHeightEditor from './editor/fontLineHeight';
 import borderRadiusEditor from './editor/borderRadius';
 import boxShadowEditor from './editor/boxShadow';
 import simpleTextEditor from './editor/simpleText';
-import { filterConfigType, getCategoryDisplayName } from './utils/utils.js';
+import { filterConfigType } from './utils/utils.js';
 
 export default {
   components: {
@@ -80,9 +80,6 @@ export default {
     }
   },
   methods: {
-    getCategoryDisplayName(key) {
-      return getCategoryDisplayName(key);
-    },
     editorComponent(type) {
       switch (type) {
         case 'color':
@@ -119,6 +116,15 @@ export default {
     return {
       categoryDisplay: {}
     };
+  },
+  watch: {
+    currentConfig: {
+      handler() {
+        this.$nextTick(() => {
+          this.$refs.mainPanel.scrollTo(0, 0);
+        });
+      }
+    }
   }
 };
 </script>

+ 1 - 15
examples/components/theme-configurator/utils/utils.js

@@ -55,7 +55,7 @@ export const getStyleDisplayName = (config, componentName) => {
   if (config.name) {
     return getVariableDisplayName(config.key.replace('$--', ''));
   }
-  let displayName = config.key.replace(`$--${componentName}-`, '').replace();
+  let displayName = config.key.replace(`$--${componentName}-`, '');
   Object.keys(displayNameMap).forEach((name) => {
     displayName = displayName.replace(name, displayNameMap[name]);
   });
@@ -66,17 +66,3 @@ export const getStyleDisplayName = (config, componentName) => {
 export const getActionDisplayName = (key) => {
   return getNameFromI18N('action')[key] || key;
 };
-
-export const getCategoryDisplayName = (key) => {
-  return getNameFromI18N('category')[key] || key;
-};
-
-export const updateDomHeadStyle = (id, styleContent) => {
-  let styleTag = document.getElementById(id);
-  if (!styleTag) {
-    styleTag = document.createElement('style');
-    styleTag.setAttribute('id', id);
-    document.head.appendChild(styleTag);
-  }
-  styleTag.innerText = styleContent.replace(/@font-face{[^}]+}/, '');
-};

+ 280 - 0
examples/components/theme/basic-tokens-preview.vue

@@ -0,0 +1,280 @@
+<style lang="scss">
+.component-preview {
+  .heading>div{
+    margin-bottom: 15px;
+  }
+  .title {
+    font-size: 18px;
+    font-weight:400;
+    padding: 0 20px
+  }
+  .paragraph {
+    padding: 0 20px
+  }
+  .demo-color-box {
+    margin: 0;
+  }
+  .color {
+    margin-right: -12%;
+  }
+}
+</style>
+<template>
+  <div class="component-preview">
+    <h4>Color</h4>
+    <div class="color">
+      <el-row :gutter="12">
+        <el-col :span="4" v-for="(color, key) in colorLine" :key="key">
+          <div class="demo-color-box" :style="{ background: dataProxy(color) }">
+            {{color}}
+            <div class="value">{{dataProxy(color)}}</div>
+            <div class="bg-color-sub">
+              <div
+                class="bg-success-sub-item"
+                v-for="(item, key) in Array(2)"
+                :key="key"
+                :style="{ background: tintColor(dataProxy(color), (key + 8) / 10) }"
+              ></div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+      <el-row :gutter="12">
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other" :style="{ background: color_text_primary }">
+            Primary Text
+            <div class="value">{{color_text_primary}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other" :style="{ background: color_text_regular }">
+            Regular Text
+            <div class="value">{{color_text_regular}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other" :style="{ background: color_text_secondary }">
+            Secondary Text
+            <div class="value">{{color_text_secondary}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other" :style="{ background: color_text_placeholder }">
+            Placeholder
+            <div class="value">{{color_text_placeholder}}</div>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="12">
+        <el-col :span="4">
+          <div
+            class="demo-color-box demo-color-box-other demo-color-box-lite"
+            :style="{ background: border_color_base }"
+          >
+            Border Base
+            <div class="value">{{border_color_base}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div
+            class="demo-color-box demo-color-box-other demo-color-box-lite"
+            :style="{ background: border_color_light }"
+          >
+            Border Light
+            <div class="value">{{border_color_light}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div
+            class="demo-color-box demo-color-box-other demo-color-box-lite"
+            :style="{ background: border_color_lighter }"
+          >
+            Border Lighter
+            <div class="value">{{border_color_lighter}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div
+            class="demo-color-box demo-color-box-other demo-color-box-lite"
+            :style="{ background: border_color_extra_light }"
+          >
+            Border Extralight
+            <div class="value">{{border_color_extra_light}}</div>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="12">
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other" :style="{ background: color_black }">
+            Background B
+            <div class="value">{{color_black}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div
+            class="demo-color-box demo-color-box-other"
+            :style="{ background: color_white, color: '#303133', border: '1px solid #eee' }"
+          >
+            Background W
+            <div class="value">{{color_white}}</div>
+          </div>
+        </el-col>
+        <el-col :span="4">
+          <div class="demo-color-box demo-color-box-other bg-transparent">
+            Background
+            <div class="value">Transparent</div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+    <h4>Typography</h4>
+    <el-row :gutter="12">
+      <el-col :span="6" class="heading">
+        <div :style="{ fontSize: font_size_extra_large }">Heading1</div>
+        <div :style="{ fontSize: font_size_large }">Heading2</div>
+        <div :style="{ fontSize: font_size_medium }">Heading3</div>
+        <div :style="{ fontSize: font_size_base }">Heading4</div>
+        <div :style="{ fontSize: font_size_small }">Heading5</div>
+        <div :style="{ fontSize: font_size_extra_small }">Heading6</div>
+      </el-col>
+      <el-col :span="9">
+        <div class="title">Example body text</div>
+        <p 
+          class="paragraph"
+          :style="{ 
+            fontSize: font_size_base,
+            fontWeight: font_weight_primary, 
+            lineHeight: font_line_height_primary
+          }" >
+          With MySpace becoming more popular every day, there is the constant need to be different. There are millions of users. If MySpace layouts are chosen well, then you can enhance your profile a great deal.</p>
+      </el-col>
+      <el-col :span="9">
+        <div class="title">Example small text</div>
+        <p 
+          class="paragraph"
+          :style="{ 
+            fontSize: font_size_small,
+            fontWeight: font_weight_secondary, 
+            lineHeight: font_line_height_secondary
+          }" >
+          Computers have become ubiquitous in almost every facet of our lives. At work, desk jockeys spend hours in front of their desktops, while delivery people scan bar codes with handhelds and workers in the field stay in touch with the central office via their notebooks. Computer hardware weaves itself through the fabric of our lives.</p>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import bus from '../../bus';
+import { tintColor } from '../../color.js';
+import {
+  ACTION_COMPONECT_SELECT,
+  ACTION_USER_CONFIG_UPDATE
+} from './constant.js';
+
+const original = {
+  'color_primary': '#409EFF',
+  'color_success': '#67C23A',
+  'color_warning': '#E6A23C',
+  'color_danger': '#F56C6C',
+  'color_info': '#909399',
+  'color_white': '#FFFFFF',
+  'color_black': '#000000',
+  'color_text_primary': '#303133',
+  'color_text_regular': '#606266',
+  'color_text_secondary': '#909399',
+  'color_text_placeholder': '#C0C4CC',
+  'border_color_base': '#DCDFE6',
+  'border_color_light': '#E4E7ED',
+  'border_color_lighter': '#EBEEF5',
+  'border_color_extra_light': '#F2F6FC',
+  'font_size_extra_large': '20px',
+  'font_size_large': '18px',
+  'font_size_medium': '16px',
+  'font_size_base': '14px',
+  'font_size_small': '13px',
+  'font_size_extra_small': '12px',
+  'font_weight_primary': 500,
+  'font_weight_secondary': 100,
+  'font_line_height_primary': '24px',
+  'font_line_height_secondary': '16px'
+};
+
+export default {
+  created() {
+    bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
+    bus.$on(ACTION_COMPONECT_SELECT, (val) => {
+      this.$nextTick(() => {
+        const getSelectElement = Array.from(document.querySelectorAll('h4')).filter((el) => (el.innerText.toLowerCase() === val));
+        if (getSelectElement[0]) {
+          const elementTop = getSelectElement[0].getBoundingClientRect().top;
+          window.scrollTo(0, window.pageYOffset + elementTop - 20); // 20 for padding
+        }
+      });
+    });
+  },
+  mounted() {
+    this.setGlobal();
+  },
+  methods: {
+    tintColor(a, b) {
+      return tintColor(a, b);
+    },
+    dataProxy(value) {
+      return this[`color_${value.toLowerCase()}`];
+    },
+    setGlobal() {
+      if (window.userThemeConfig) {
+        this.global = window.userThemeConfig.global;
+      }
+    }
+  },
+  watch: {
+    global: {
+      immediate: true,
+      handler(value) {
+        Object.keys(original).forEach((v) => {
+          const key = `$--${v.replace(/_/g, '-')}`;
+          if (value[key]) {
+            this[v] = value[key];
+          } else {
+            this[v] = original[v];
+          }
+        });
+      }
+    }
+  },
+  data() {
+    return {
+      global: {},
+      colorLine: ['Primary', 'Success', 'Warning', 'Danger', 'Info'],
+      'color_primary': '',
+      'color_success': '',
+      'color_warning': '',
+      'color_danger': '',
+      'color_info': '',
+      'color_white': '',
+      'color_black': '',
+      'color_text_primary': '',
+      'color_text_regular': '',
+      'color_text_secondary': '',
+      'color_text_placeholder': '',
+      'border_color_base': '',
+      'border_color_light': '',
+      'border_color_lighter': '',
+      'border_color_extra_light': '',
+      'font_size_extra_large': '',
+      'font_size_large': '',
+      'font_size_medium': '',
+      'font_size_base': '',
+      'font_size_small': '',
+      'font_size_extra_small': '',
+      'font_weight_primary': 0,
+      'font_weight_secondary': 0,
+      'font_line_height_primary': '',
+      'font_line_height_secondary': ''
+    };
+  }
+};
+</script>

+ 518 - 0
examples/components/theme/components-preview.vue

@@ -0,0 +1,518 @@
+<style lang="scss">
+.component-preview {
+  padding-right: 10px;
+  &:last-of-type {
+    padding-bottom: 20px;
+  }
+  h4 {
+    font-size: 20px;
+    margin: 40px 0 20px;
+    color: #909399
+  }
+  .demo-item {
+    margin-top: 10px;
+    margin-right: 40px;
+  }
+
+  .demo-line {
+    margin: 15px 0;
+  }
+
+  .el-carousel__item:nth-child(2n) {
+    background-color: #99a9bf;
+  }
+
+  .el-carousel__item:nth-child(2n + 1) {
+    background-color: #d3dce6;
+  }
+}
+</style>
+<template>
+  <div class="component-preview">
+    <h4>Button</h4>
+    <el-row class="demo-line">
+      <el-button>Default</el-button>
+      <el-button type="primary">Primary</el-button>
+      <el-button type="success">Success</el-button>
+      <el-button type="info">Info</el-button>
+      <el-button type="warning">Warning</el-button>
+      <el-button type="danger">Danger</el-button>
+    </el-row>
+    <el-row class="demo-line">
+      <el-button plain>Plain</el-button>
+      <el-button type="primary" plain>Primary</el-button>
+      <el-button type="success" plain>Success</el-button>
+      <el-button type="info" plain>Info</el-button>
+      <el-button type="warning" plain>Warning</el-button>
+      <el-button type="danger" plain>Danger</el-button>
+    </el-row>
+    <el-row class="demo-line">
+      <el-button round>Round</el-button>
+      <el-button type="primary" round>Primary</el-button>
+      <el-button type="success" round>Success</el-button>
+      <el-button type="info" round>Info</el-button>
+      <el-button type="warning" round>Warning</el-button>
+      <el-button type="danger" round>Danger</el-button>
+    </el-row>
+    <el-row class="demo-line">
+      <el-button icon="el-icon-search" circle></el-button>
+      <el-button type="primary" icon="el-icon-edit" circle></el-button>
+      <el-button type="success" icon="el-icon-check" circle></el-button>
+      <el-button type="info" icon="el-icon-message" circle></el-button>
+      <el-button type="warning" icon="el-icon-star-off" circle></el-button>
+      <el-button type="danger" icon="el-icon-delete" circle></el-button>
+    </el-row>
+    <el-row class="demo-line">
+      <el-button>Default</el-button>
+      <el-button size="medium">Medium</el-button>
+      <el-button size="small">Small</el-button>
+      <el-button size="mini">Mini</el-button>
+    </el-row>
+    <h4>Radio</h4>
+    <el-row class="demo-line">
+      <el-radio v-model="radio" label="1">Option A</el-radio>
+      <el-radio v-model="radio" label="2">Option B</el-radio>
+    </el-row>
+    <el-row class="demo-line">
+      <el-radio-group v-model="radio1">
+        <el-radio-button label="New York"></el-radio-button>
+        <el-radio-button label="Washington"></el-radio-button>
+        <el-radio-button label="Los Angeles"></el-radio-button>
+        <el-radio-button label="Chicago"></el-radio-button>
+      </el-radio-group>
+    </el-row>
+    <el-row class="demo-line">
+      <el-radio v-model="radio2" label="1" border>Option A</el-radio>
+      <el-radio v-model="radio2" label="2" border>Option B</el-radio>
+    </el-row>
+    <h4>Checkbox</h4>
+    <el-row class="demo-line">
+      <el-checkbox v-model="checked">Option</el-checkbox>
+    </el-row>
+    <el-row class="demo-line">
+      <el-checkbox-group v-model="checked1">
+        <el-checkbox-button v-for="city in ['Shanghai', 'Beijing', 'Guangzhou', 'Shenzhen']" :label="city" :key="city">{{city}}</el-checkbox-button>
+      </el-checkbox-group>
+    </el-row>
+    <el-row class="demo-line">
+      <el-checkbox v-model="checked2" label="Option1" border></el-checkbox>
+    </el-row>
+    <h4>Input</h4>
+    <el-row style="width: 180px">
+      <el-input placeholder="Please input" v-model="input"></el-input>
+    </el-row>
+    <h4>InputNumber</h4>
+    <el-row>
+      <el-input-number v-model="inputNumber" :min="1" :max="10"></el-input-number>
+    </el-row>
+    <h4>Select</h4>
+    <el-row>
+      <el-select v-model="selectValue" placeholder="Select">
+        <el-option
+          v-for="item in selectOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        ></el-option>
+      </el-select>
+    </el-row>
+    <h4>Cascader</h4>
+    <el-row>
+      <el-cascader :options="cascadeOptions" v-model="cascaderValue"></el-cascader>
+    </el-row>
+    <h4>Switch</h4>
+    <el-row>
+      <el-switch v-model="switchValue"></el-switch>
+      <el-switch
+        style="margin-left: 40px"
+        v-model="switchValue"
+        active-text="Pay by month"
+        inactive-text="Pay by year">
+      </el-switch>
+    </el-row>
+    <h4>Slider</h4>
+    <el-row style="width: 380px">
+      <el-slider v-model="slider"></el-slider>
+    </el-row>
+    <h4>DatePicker</h4>
+    <el-row>
+      <el-date-picker v-model="datePicker" type="date"></el-date-picker>
+    </el-row>
+    <h4>Rate</h4>
+    <el-row>
+      <el-rate class="demo-line" v-model="rate"></el-rate> 
+      <el-rate
+        class="demo-line"
+        v-model="rate"
+        show-score
+        text-color="#ff9900"
+        score-template="{value} points">
+      </el-rate>
+    </el-row>
+    <h4>Transfer</h4>
+    <el-row>
+      <el-transfer v-model="transfer" filterable :data="transferData">
+        <el-button class="transfer-footer" slot="left-footer" size="small">Operation</el-button>
+        <el-button class="transfer-footer" slot="right-footer" size="small">Operation</el-button>
+      </el-transfer>
+    </el-row>
+    <h4>Table</h4>
+    <el-row>
+      <el-table :data="tableData" style="width: 70%">
+        <el-table-column prop="date" label="Date" width="180"></el-table-column>
+        <el-table-column prop="name" label="Name" width="180"></el-table-column>
+        <el-table-column prop="address" label="Address"></el-table-column>
+      </el-table>
+    </el-row>
+    <h4>Tag</h4>
+    <el-row>
+      <el-tag class="demo-item" closable>Tag One</el-tag>
+      <el-tag class="demo-item" closable type="success">Tag Two</el-tag>
+      <el-tag class="demo-item" closable type="info">Tag Three</el-tag>
+      <el-tag class="demo-item" closable type="warning">Tag Four</el-tag>
+      <el-tag class="demo-item" closable type="danger">Tag Five</el-tag>
+    </el-row>
+    <h4>Progress</h4>
+    <el-row style="width: 380px">
+      <el-progress :percentage="20"></el-progress>
+      <el-progress :percentage="60" status="exception"></el-progress>
+      <el-progress :percentage="100" status="success"></el-progress>
+    </el-row>
+    <h4>Tree</h4>
+    <el-row style="width: 380px">
+      <el-tree :data="treeData" :props="defaultTreeProps" ></el-tree>
+    </el-row>
+    <h4>Pagination</h4>
+    <el-row>
+      <el-pagination layout="prev, pager, next" :total="1000"></el-pagination>
+    </el-row>
+    <h4>Badge</h4>
+    <el-row>
+      <el-badge :value="12" class="demo-item">
+        <el-button size="small">comments</el-button>
+      </el-badge>
+      <el-badge :value="3" class="demo-item">
+        <el-button size="small">replies</el-button>
+      </el-badge>
+      <el-badge :value="1" class="demo-item" type="primary">
+        <el-button size="small">comments</el-button>
+      </el-badge>
+      <el-badge :value="2" class="demo-item" type="warning">
+        <el-button size="small">replies</el-button>
+      </el-badge>
+    </el-row>
+    <h4>Alert</h4>
+    <el-row style="width: 380px;">
+      <el-alert class="demo-item" title="success alert" type="success" show-icon></el-alert>
+      <el-alert class="demo-item" title="info alert" type="info" close-text="Gotcha" show-icon></el-alert>
+      <el-alert class="demo-item" title="warning alert" type="warning" show-icon></el-alert>
+      <el-alert
+        class="demo-item"
+        title="error alert"
+        type="error"
+        description="more text description"
+        show-icon>
+      </el-alert>
+    </el-row>
+    <h4>Loading</h4>
+    <el-row>
+      <el-table :data="tableData" style="width: 90%" v-loading="true">
+        <el-table-column prop="date" label="Date" width="180"></el-table-column>
+        <el-table-column prop="name" label="Name" width="180"></el-table-column>
+        <el-table-column prop="address" label="Address"></el-table-column>
+      </el-table>
+    </el-row>
+    <h4>Message</h4>
+    <el-row>
+      <div role="alert" class="demo-item el-message el-message--success el-message-fade-leave-active el-message-fade-leave-to" style="top: 0;left: 0;width: 100px; opacity: 1; position: relative;transform: none;"><i class="el-message__icon el-icon-success"></i><p class="el-message__content">Congrats, this is a success message.</p><!----></div>
+      <div role="alert" class="demo-item el-message el-message--warning el-message-fade-leave-active el-message-fade-leave-to" style="top: 0;left: 0;width: 100px; opacity: 1; position: relative;transform: none;"><i class="el-message__icon el-icon-warning"></i><p class="el-message__content">Warning, this is a warning message.</p><!----></div>
+      <div role="alert" class="demo-item el-message el-message--info el-message-fade-leave-active el-message-fade-leave-to" style="top: 0;left: 0;width: 100px; opacity: 1; position: relative;transform: none;"><i class="el-message__icon el-icon-info"></i><p class="el-message__content">This is a message.</p><!----></div>
+      <div role="alert" class="demo-item el-message el-message--error el-message-fade-leave-active el-message-fade-leave-to" style="top: 0;left: 0;width: 100px; opacity: 1; position: relative;transform: none;"><i class="el-message__icon el-icon-error"></i><p class="el-message__content">Oops, this is a error message.</p><!----></div>
+    </el-row>
+    <h4>MessageBox</h4>
+    <el-row>
+      <div class="el-message-box"><div class="el-message-box__header"><div class="el-message-box__title"><!----><span>Warning</span></div><button type="button" aria-label="Close" class="el-message-box__headerbtn"><i class="el-message-box__close el-icon-close"></i></button></div><div class="el-message-box__content"><div class="el-message-box__status el-icon-warning"></div><div class="el-message-box__message"><p>This will permanently delete the file. Continue?</p></div><div class="el-message-box__input" style="display: none;"><div class="el-input"><!----><input type="text" autocomplete="off" placeholder="" class="el-input__inner"><!----><!----><!----></div><div class="el-message-box__errormsg" style="visibility: hidden;"></div></div></div><div class="el-message-box__btns"><button type="button" class="el-button el-button--default el-button--small"><!----><!----><span>
+          Cancel
+        </span></button><button type="button" class="el-button el-button--default el-button--small el-button--primary "><!----><!----><span>
+          OK
+        </span></button></div></div>
+    </el-row>
+    <h4>Notification</h4>
+    <el-row>
+      <div role="alert" class="el-notification right" style="position: relative; left: 0;"><!----><div class="el-notification__group"><span class="el-notification__title">Notification</span><div class="el-notification__content"><div>This is a message </div></div><div class="el-notification__closeBtn el-icon-close"></div></div></div>
+    </el-row>
+    <h4>Menu</h4>
+    <el-row>
+      <el-menu :default-active="menu" class="el-menu-demo" mode="horizontal">
+        <el-menu-item index="1">Processing Center</el-menu-item>
+        <el-submenu index="2">
+          <template slot="title">Workspace</template>
+          <el-menu-item index="2-1">item one</el-menu-item>
+          <el-menu-item index="2-2">item two</el-menu-item>
+          <el-menu-item index="2-3">item three</el-menu-item>
+          <el-submenu index="2-4">
+            <template slot="title">item four</template>
+            <el-menu-item index="2-4-1">item one</el-menu-item>
+            <el-menu-item index="2-4-2">item two</el-menu-item>
+            <el-menu-item index="2-4-3">item three</el-menu-item>
+          </el-submenu>
+        </el-submenu>
+        <el-menu-item index="3" disabled>Info</el-menu-item>
+        <el-menu-item index="4">
+          <a href="https://www.ele.me" target="_blank">Orders</a>
+        </el-menu-item>
+      </el-menu>
+      <el-menu
+        default-active="2"
+        class="demo-line"
+      >
+        <el-submenu index="1">
+          <template slot="title">
+            <i class="el-icon-location"></i>
+            <span>Navigator One</span>
+          </template>
+          <el-menu-item-group title="Group One">
+            <el-menu-item index="1-1">item one</el-menu-item>
+            <el-menu-item index="1-2">item one</el-menu-item>
+          </el-menu-item-group>
+          <el-menu-item-group title="Group Two">
+            <el-menu-item index="1-3">item three</el-menu-item>
+          </el-menu-item-group>
+          <el-submenu index="1-4">
+            <template slot="title">item four</template>
+            <el-menu-item index="1-4-1">item one</el-menu-item>
+          </el-submenu>
+        </el-submenu>
+        <el-menu-item index="2">
+          <i class="el-icon-menu"></i>
+          <span>Navigator Two</span>
+        </el-menu-item>
+        <el-menu-item index="3" disabled>
+          <i class="el-icon-document"></i>
+          <span>Navigator Three</span>
+        </el-menu-item>
+        <el-menu-item index="4">
+          <i class="el-icon-setting"></i>
+          <span>Navigator Four</span>
+        </el-menu-item>
+      </el-menu>
+    </el-row>
+    <h4>Tabs</h4>
+    <el-row>
+      <el-tabs v-model="tab" class="demo-item">
+        <el-tab-pane label="User" name="first">User</el-tab-pane>
+        <el-tab-pane label="Config" name="second">Config</el-tab-pane>
+        <el-tab-pane label="Role" name="third">Role</el-tab-pane>
+        <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
+      </el-tabs>
+      <el-tabs type="card" class="demo-item">
+        <el-tab-pane label="User">User</el-tab-pane>
+        <el-tab-pane label="Config">Config</el-tab-pane>
+        <el-tab-pane label="Role">Role</el-tab-pane>
+        <el-tab-pane label="Task">Task</el-tab-pane>
+      </el-tabs>
+    </el-row>
+    <h4>Dialog</h4>
+    <el-row>
+      <div role="dialog" aria-modal="true" aria-label="Tips" class="el-dialog" style="margin: 0"><div class="el-dialog__header"><span class="el-dialog__title">Tips</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><div class="el-dialog__body"><span>This is a message</span> </div><div class="el-dialog__footer"><span class="dialog-footer"><button type="button" class="el-button el-button--default"><!----><!----><span>Cancel</span></button> <button type="button" class="el-button el-button--primary"><!----><!----><span>Confirm</span></button></span></div></div>
+    </el-row>
+    <h4>Tooltip</h4>
+    <el-row>
+      <div role="tooltip" x-placement="top" class="el-tooltip__popper is-dark" style="position: relative; width: 40px;text-align: center;">Dark<div x-arrow="" class="popper__arrow"></div>
+      </div>
+      <div role="tooltip" x-placement="top" class="el-tooltip__popper is-light" style="margin-top: 10px;position: relative; width: 40px;text-align: center;">Light<div x-arrow="" class="popper__arrow"></div>
+      </div>
+    </el-row>
+    <h4>Popover</h4>
+    <el-row>
+      <div role="tooltip" x-placement="top" id="el-popover-2936" aria-hidden="true" class="el-popover el-popper el-popover--plain" tabindex="0" style="width: 200px; position: relative; "><div class="el-popover__title">Title</div>this is content, this is content, this is content<div x-arrow="" class="popper__arrow"></div></div>
+    </el-row>
+    <h4>Card</h4>
+    <el-row>
+      <el-card class="box-card">
+        <div slot="header" class="clearfix">
+          <span>Card name</span>
+        </div>
+      </el-card>
+    </el-row>
+    <h4>Carousel</h4>
+    <el-row>
+      <el-carousel height="150px">
+        <el-carousel-item v-for="item in 4" :key="item">
+          <h3>{{ item }}</h3>
+        </el-carousel-item>
+      </el-carousel>
+    </el-row>
+    <h4>Collapse</h4>
+    <el-row>
+      <el-collapse v-model="collapse">
+        <el-collapse-item title="Consistent" name="1">
+          <div>Consistent with real life: in line with the process and logic of real life, and comply with languages and habits that the users are used to;</div>
+        </el-collapse-item>
+        <el-collapse-item title="Feedback" name="2">
+          <div>Operation feedback: enable the users to clearly perceive their operations by style updates and interactive effects;</div>
+        </el-collapse-item>
+      </el-collapse>
+    </el-row>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      radio: '1',
+      radio1: 'Washington',
+      radio2: '1',
+      checked: true,
+      checked1: ['Shanghai'],
+      checked2: true,
+      input: 'Element',
+      inputNumber: 1,
+      selectOptions: [
+        {
+          value: 'Option1',
+          label: 'Option1'
+        },
+        {
+          value: 'Option2',
+          label: 'Option2'
+        },
+        {
+          value: 'Option3',
+          label: 'Option3'
+        },
+        {
+          value: 'Option4',
+          label: 'Option4'
+        },
+        {
+          value: 'Option5',
+          label: 'Option5'
+        }
+      ],
+      selectValue: '',
+      cascadeOptions: [
+        {
+          value: 'guide',
+          label: 'Guide',
+          children: [
+            {
+              value: 'disciplines',
+              label: 'Disciplines',
+              children: [
+                {
+                  value: 'consistency',
+                  label: 'Consistency'
+                },
+                {
+                  value: 'feedback',
+                  label: 'Feedback'
+                }
+              ]
+            }
+          ]
+        },
+        {
+          value: 'resource',
+          label: 'Resource',
+          children: [
+            {
+              value: 'axure',
+              label: 'Axure Components'
+            },
+            {
+              value: 'sketch',
+              label: 'Sketch Templates'
+            },
+            {
+              value: 'docs',
+              label: 'Design Documentation'
+            }
+          ]
+        }
+      ],
+      cascaderValue: [],
+      switchValue: true,
+      slider: 28,
+      datePicker: '',
+      rate: null,
+      transferData: (() => {
+        const data = [];
+        for (let i = 1; i <= 15; i++) {
+          data.push({
+            key: i,
+            label: `Option ${i}`,
+            disabled: i % 4 === 0
+          });
+        }
+        return data;
+      })(),
+      transfer: [1, 4],
+      tableData: [
+        {
+          date: '2016-05-03',
+          name: 'Tom',
+          address: 'No. 189, Grove St, Los Angeles'
+        },
+        {
+          date: '2016-05-02',
+          name: 'Tom',
+          address: 'No. 189, Grove St, Los Angeles'
+        },
+        {
+          date: '2016-05-04',
+          name: 'Tom',
+          address: 'No. 189, Grove St, Los Angeles'
+        },
+        {
+          date: '2016-05-01',
+          name: 'Tom',
+          address: 'No. 189, Grove St, Los Angeles'
+        }
+      ],
+      menu: '1',
+      tab: 'first',
+      collapse: ['1'],
+      treeData: [{
+        label: 'Level one 1',
+        children: [{
+          label: 'Level two 1-1',
+          children: [{
+            label: 'Level three 1-1-1'
+          }]
+        }]
+      }, {
+        label: 'Level one 2',
+        children: [{
+          label: 'Level two 2-1',
+          children: [{
+            label: 'Level three 2-1-1'
+          }]
+        }, {
+          label: 'Level two 2-2',
+          children: [{
+            label: 'Level three 2-2-1'
+          }]
+        }]
+      }, {
+        label: 'Level one 3',
+        children: [{
+          label: 'Level two 3-1',
+          children: [{
+            label: 'Level three 3-1-1'
+          }]
+        }, {
+          label: 'Level two 3-2',
+          children: [{
+            label: 'Level three 3-2-1'
+          }]
+        }]
+      }],
+      defaultTreeProps: {
+        children: 'children',
+        label: 'label'
+      }
+    };
+  }
+};
+</script>

+ 16 - 0
examples/components/theme/constant.js

@@ -0,0 +1,16 @@
+export const DEFAULT_THEME_CONFIG = {
+  global: {},
+  local: {}
+};
+
+export const ELEMENT_THEME_USER_CONFIG = 'ELEMENT_THEME_USER_CONFIG';
+
+export const ELEMENT_THEME_PREVIEW_CONFIG = 'ELEMENT_THEME_PREVIEW_CONFIG';
+
+export const ACTION_DOWNLOAD_THEME = 'ELEMENT_THEME_ACTION_DOWNLOAD';
+
+export const ACTION_APPLY_THEME = 'ELEMENT_THEME_ACTION_ALLPY_CSS';
+
+export const ACTION_COMPONECT_SELECT = 'ELEMENT_THEME_ACTION_COMPONECT_SELECT';
+
+export const ACTION_USER_CONFIG_UPDATE = 'ELEMENT_THEME_USER_CONFIG_UPDATE';

+ 0 - 0
examples/components/theme-configurator/utils/ajax.js → examples/components/theme/loader/ajax.js


+ 0 - 0
examples/components/theme-configurator/utils/api.js → examples/components/theme/loader/api.js


+ 3 - 2
examples/components/theme-configurator/docStyle.vue → examples/components/theme/loader/docStyle.vue

@@ -1,7 +1,8 @@
 <script>
 const ORIGINAL_THEME = '#409EFF';
-import { get as ajaxGet } from './utils/ajax';
-import { updateDomHeadStyle } from './utils/utils.js';
+import { get as ajaxGet } from './ajax.js';
+import { updateDomHeadStyle } from '../utils.js';
+
 export default {
   data() {
     return {

+ 106 - 0
examples/components/theme/loader/index.vue

@@ -0,0 +1,106 @@
+<script>
+import bus from '../../../bus.js';
+import Loading from './loading';
+import DocStyle from './docStyle';
+import { updateVars } from './api.js';
+import { updateDomHeadStyle } from '../utils.js';
+import {
+  ACTION_APPLY_THEME,
+  ACTION_DOWNLOAD_THEME,
+  ACTION_USER_CONFIG_UPDATE
+} from '../constant.js';
+import {
+  loadUserThemeFromLocal,
+  loadPreviewFromLocal
+} from '../localstorage.js';
+import { getActionDisplayName } from '../../theme-configurator/utils/utils';
+
+export default {
+  mixins: [Loading, DocStyle],
+  mounted() {
+    this.checkLocalThemeConfig();
+    bus.$on(ACTION_APPLY_THEME, val => {
+      this.userConfig = val;
+      this.onAction();
+    });
+    bus.$on(ACTION_DOWNLOAD_THEME, val => {
+      this.onDownload(val);
+    });
+  },
+  data() {
+    return {
+      userConfig: {},
+      lastApply: 0
+    };
+  },
+  methods: {
+    applyStyle(res, time) {
+      if (time < this.lastApply) return;
+      this.updateDocs(() => {
+        updateDomHeadStyle('chalk-style', res);
+      });
+      this.lastApply = time;
+    },
+    onDownload(themeConfig) {
+      this.triggertProgressBar(true);
+      updateVars(
+        Object.assign({}, themeConfig, { download: true }),
+        xhr => {
+          xhr.responseType = 'blob';
+        }
+      ).then()
+        .catch((err) => {
+          this.onError(err);
+        })
+        .then(() => {
+          this.triggertProgressBar(false);
+        });
+      ga('send', 'event', 'ThemeConfigurator', 'Download');
+    },
+    onAction() {
+      this.triggertProgressBar(true);
+      const time = +new Date();
+      updateVars(this.userConfig)
+        .then(res => {
+          this.applyStyle(res, time);
+        })
+        .catch(err => {
+          this.onError(err);
+        })
+        .then(() => {
+          this.triggertProgressBar(false);
+        });
+    },
+    onError(err) {
+      let message;
+      try {
+        message = JSON.parse(err).message;
+      } catch (e) {
+        message = err;
+      }
+      this.$message.error(message);
+    },
+    triggertProgressBar(isLoading) {
+      bus.$emit('user-theme-config-loading', isLoading);
+    },
+    updateDocs(cb) {
+      window.userThemeConfig = JSON.parse(JSON.stringify(this.userConfig));
+      bus.$emit(ACTION_USER_CONFIG_UPDATE, this.userConfig);
+      this.updateDocStyle(this.userConfig, cb);
+    },
+    checkLocalThemeConfig() {
+      // load user local theme
+      const previewConfig = loadPreviewFromLocal();
+      if (previewConfig.type === 'user') {
+        const userConfig = loadUserThemeFromLocal();
+        this.$message(getActionDisplayName('load-local-theme-config'));
+        const config = userConfig.filter(theme => (theme.name === previewConfig.name));
+        if (config && config[0]) {
+          this.userConfig = JSON.parse(config[0].theme);
+          this.onAction();
+        }
+      }
+    }
+  }
+};
+</script>

+ 1 - 1
examples/components/theme-configurator/loading.vue → examples/components/theme/loader/loading/index.vue

@@ -9,7 +9,7 @@
 </style>
 <script>
 
-import bus from '../../bus.js';
+import bus from '../../../../bus.js';
 import './progress.js';
 
 export default {

+ 0 - 0
examples/components/theme-configurator/progress.js → examples/components/theme/loader/loading/progress.js


+ 0 - 0
examples/components/theme-configurator/progress.vue → examples/components/theme/loader/loading/progress.vue


+ 38 - 0
examples/components/theme/localstorage.js

@@ -0,0 +1,38 @@
+import {
+  ELEMENT_THEME_PREVIEW_CONFIG,
+  ELEMENT_THEME_USER_CONFIG
+} from './constant';
+
+export const saveToLocal = (key, value) => {
+  localStorage.setItem(key, JSON.stringify(value));
+};
+
+export const loadFromLocal = (key) => {
+  try {
+    return JSON.parse(localStorage.getItem(key));
+  } catch (e) {
+    console.error(e);
+    return null;
+  }
+};
+
+export const savePreviewToLocal = (value) => {
+  saveToLocal(ELEMENT_THEME_PREVIEW_CONFIG, value);
+};
+
+export const loadPreviewFromLocal = () => {
+  return loadFromLocal(ELEMENT_THEME_PREVIEW_CONFIG) || {};
+};
+
+export const removePreviewFromLocal = () => {
+  return localStorage.removeItem(ELEMENT_THEME_PREVIEW_CONFIG);
+};
+
+export const saveUserThemeToLocal = (value) => {
+  saveToLocal(ELEMENT_THEME_USER_CONFIG, value);
+};
+
+export const loadUserThemeFromLocal = () => {
+  return loadFromLocal(ELEMENT_THEME_USER_CONFIG);
+};
+

+ 401 - 0
examples/components/theme/theme-card.vue

@@ -0,0 +1,401 @@
+<style lang="scss">
+.theme-card-item {
+  user-select: none;
+  border-radius: 4px;
+  overflow: hidden;
+  background: #fff;
+  height: 90%;
+  margin: 25px 0;
+  box-shadow: 0 0 1px 0 #666;
+  &.is-hidden {
+    opacity: 0;
+    height: 0;
+  }
+  .upload {
+    cursor: pointer;
+    background: #f5f7fa;
+    height: 100%;
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    align-items:center;
+    .upload-action {
+      width: 40%;
+      margin: 0 auto;
+      text-align: center;
+      color: #606266;
+      img {
+        display: block;
+        margin: 0 auto;
+      }
+      span {
+        display: block;
+        font-size: 14px;
+        margin-top: 8px;
+      }
+    }
+  }
+  .preview {
+    position: relative;
+    height: 70%;
+    width: 100%;
+    .line {
+      height: 50%;
+    }
+    .line-2 {
+      width: 50%;
+      height: 100%;
+      display: inline-block;
+    }
+    .line-4 {
+      width: 25%;
+      height: 100%;
+      display: inline-block;
+    }
+    .action {
+      transition: all .3s;
+      position: absolute;
+      opacity: 0;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      .action-mask {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: #000;
+        opacity: 0.4;
+      }
+      .action-block {
+        position: absolute;
+        width: 50%;
+        height: 50%;
+        left: 25%;
+        top: 25%;
+      }
+      .action-item {
+        cursor: pointer;
+        display: inline-block;
+        height: 100%;
+        width: 30%;
+        color: #eee;
+        &:hover {
+          color: #fff;
+          .circle {
+            border-color: #fff;
+          }
+        }
+        .icon {
+          height: 50%;
+          font-size: 22px;
+          text-align: center;
+          display: flex;
+          justify-content:center;
+          align-items:center;
+          img {
+            width: 130%;
+          }
+        }
+        .name {
+          font-size: 12px;
+          height: 50%;
+          text-align: center;
+          display: flex;
+          justify-content:center;
+          align-items:center;
+          margin-top: 4px;
+        }
+      }
+      .action-item-right {
+        margin-left: 40%;
+      }
+    }
+  }
+  .info {
+    height: 30%;
+    line-height: 16px;
+    display: flex;
+    align-items: center;
+    .info-center {
+      width: 100%;
+    }
+    .title {
+      font-weight: bold;
+      font-size: 16px;
+      color: #303133;
+      padding: 0 12px;
+      justify-content: space-between;
+    }
+    .right {
+      float: right;
+      font-weight: normal;
+      font-size: 14px;
+      color: #909399;
+    }
+    .more {
+      font-size: 16px;
+      cursor: pointer;
+    }
+    .description {
+      padding: 0 12px;
+      font-size: 14px;
+      color: #606266;
+      margin-top: 10px;
+    }
+  }
+  &.is-upload {
+    box-shadow: none;
+    border: 1px dashed #DCDFE6;
+  }
+  &.is-upload:hover {
+    box-shadow: none;
+  } 
+
+  &:hover {
+    box-shadow: 0 0 10px 0 #999;
+    .action {
+      opacity: 1;
+    }
+  }
+}
+</style>
+
+<template>
+  <section class="theme-card-item" :class="{'is-hidden': !config || !config.name, 'is-upload': isUpload}">
+    <template v-if="isUpload">
+      <div class="upload" @click="uploadClick">
+        <div class="upload-action">
+          <img src="../../assets/images/icon-upload.svg"/>
+          <span>{{getActionDisplayName('upload-theme')}}</span>
+        </div>
+      </div>
+      <input 
+        class="el-upload__input" 
+        type="file" 
+        ref="input"  
+        @change="uploadAction"
+        accept="application/json"
+      />
+    </template>
+    <template v-else>
+      <div class="preview">
+        <div class="line">
+          <span class="line-2" :style="{background: mainColor}"></span>
+          <span class="line-2" :style="{background: textPrimaryColor}"></span>
+        </div>
+        <div class="line">
+          <span class="line-4" :style="{background: mainColor50}"></span>
+          <span class="line-4" :style="{background: mainColor80}"></span>
+          <span class="line-4" :style="{background: borderBaseColor}"></span>
+          <span class="line-4" :style="{background: textSecondaryColor}"></span>
+        </div>
+        <div class="action">
+          <div class="action-mask"></div>
+          <div class="action-block">
+            <div
+              class="action-item"
+              :class="index && 'action-item-right'"
+              v-for="(item, index) in actionArray"
+              :key="index"
+              @click="iconClick(item.action)"
+            >
+              <div class="icon">
+                <img :src="item.icon"/>
+                <span class="circle"></span>
+              </div>
+              <div class="name">
+                <span>{{item.name}}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="info">
+        <div class="info-center">
+          <div class="title">
+            <span>{{config.name}}</span>
+            <span class="right" v-if="isOfficial">by {{config.author}}</span>
+            <span class="right more" v-else>
+              <el-dropdown @command="actionClick">
+                <i class="el-icon-more"></i>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="rename">{{getActionDisplayName('rename-theme')}}</el-dropdown-item>
+                  <el-dropdown-item command="copy">{{getActionDisplayName('copy-theme')}}</el-dropdown-item>
+                  <el-dropdown-item 
+                      command="delete" 
+                      style="color: #F56C6C;"
+                    >
+                      {{getActionDisplayName('delete-theme')}}
+                    </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </span>
+          </div>
+          <div class="description" v-if="isOfficial">{{getActionDisplayName(getDescriptionKey(config.name))}} </div>
+          <div class="description" v-else>{{getActionDisplayName('last-modified')}} {{formatDate(config.update)}}</div>
+        </div>
+      </div>
+    </template>
+  </section>
+</template>
+
+<script>
+import bus from '../../bus';
+import {
+  DEFAULT_THEME_CONFIG,
+  ACTION_DOWNLOAD_THEME
+} from './constant.js';
+import { savePreviewToLocal } from './localstorage';
+import { tintColor } from '../../color.js';
+import dateUtil from 'element-ui/src/utils/date';
+import { getActionDisplayName } from '../theme-configurator/utils/utils';
+
+export default {
+  props: {
+    config: Object,
+    type: String,
+    base: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      deleteVisible: false
+    };
+  },
+  methods: {
+    getActionDisplayName(key) {
+      return getActionDisplayName(key);
+    },
+    getDescriptionKey(name) {
+      return name ? `description-${name.toLowerCase()}` : '';
+    },
+    formatDate(timestamp) {
+      if (!timestamp) return '';
+      return dateUtil.format(new Date(timestamp), 'yyyy-MM-dd HH:mm');
+    },
+    uploadClick() {
+      this.$refs.input.value = null;
+      this.$refs.input.click();
+    },
+    uploadAction(ev) {
+      const files = ev.target.files;
+      if (!files) return;
+      var reader = new FileReader();
+      reader.onload = (e) => {
+        try {
+          const jsonString = e.target.result;
+          const jsonObject = JSON.parse(jsonString);
+          if (!jsonObject.global || !jsonObject.local) {
+            return this.$message.error('JSON format error');
+          }
+          this.$emit('action', 'upload', jsonString);
+        } catch (e) {
+          this.$message.error('Upload error');
+          console.error(e);
+        }
+      };
+      reader.readAsText(files[0]);
+    },
+    actionClick(e) {
+      this.$emit('action', e, this.config);
+    },
+    iconClick(e) {
+      switch (e) {
+        case 'preview':
+        case 'edit':
+          const { name, theme } = this.config;
+          savePreviewToLocal({
+            type: this.type,
+            name,
+            theme
+          });
+          this.$router.push({
+            name: `theme-preview-${this.$route.meta.lang}`,
+            params: {
+              refer: 'theme'
+            }
+          });
+          this.$nextTick(() => {
+            window.scrollTo(0, 0);
+          });
+          break;
+        case 'download':
+          bus.$emit(ACTION_DOWNLOAD_THEME, this.theme);
+          break;
+        default:
+          this.$emit('action', e, this.config);
+          return;
+      }
+    },
+    deleteUserTheme() {
+      this.deleteVisible = false;
+      this.$emit('action', 'delete', this.config);
+    }
+  },
+  computed: {
+    isUpload() {
+      return this.type === 'upload';
+    },
+    theme() {
+      if (this.config.theme) {
+        return JSON.parse(this.config.theme);
+      }
+      return DEFAULT_THEME_CONFIG;
+    },
+    mainColor() {
+      return this.theme.global['$--color-primary'] || '#1989FA';
+    },
+    mainColor50() {
+      return tintColor(this.mainColor, 0.5);
+    },
+    mainColor80() {
+      return tintColor(this.mainColor, 0.8);
+    },
+    textPrimaryColor() {
+      return this.theme.global['$--color-text-primary'] || '#303133';
+    },
+    borderBaseColor() {
+      return this.theme.global['$--border-color-base'] || '#DCDFE6';
+    },
+    textSecondaryColor() {
+      return this.theme.global['$--color-text-secondary'] || '#909399';
+    },
+    isOfficial() {
+      return this.type === 'official';
+    },
+    actionArray() {
+      if (this.isOfficial) {
+        return [
+          {
+            icon: require('../../assets/images/icon-check.png'),
+            name: getActionDisplayName('theme-check'),
+            action: 'preview'
+          },
+          {
+            icon: require('../../assets/images/icon-copy.png'),
+            name: getActionDisplayName('theme-copy'),
+            action: 'copy'
+          }
+        ];
+      }
+      return [
+        {
+          icon: require('../../assets/images/icon-edit.png'),
+          name: getActionDisplayName('theme-edit'),
+          action: 'edit'
+        },
+        {
+          icon: require('../../assets/images/icon-download.png'),
+          name: getActionDisplayName('download-theme'),
+          action: 'download'
+        }
+      ];
+    }
+  }
+};
+</script>

+ 13 - 0
examples/components/theme/theme-list.js

@@ -0,0 +1,13 @@
+const themeList = [
+  {
+    name: 'Element',
+    author: 'Element',
+    theme: '{"global":{"$--color-primary":"#409EFF"},"local":{}}'
+  },
+  {
+    name: 'Napos',
+    author: 'Element',
+    theme: '{"global":{"$--color-primary":"#1989FA"},"local":{}}'
+  }
+];
+export default themeList;

+ 24 - 0
examples/components/theme/utils.js

@@ -0,0 +1,24 @@
+export const isEmptyObject = (obj) => (JSON.stringify(obj) === '{}');
+
+export const getThemeConfigObject = (config) => {
+  try {
+    const conf = JSON.parse(config);
+    const { global, local } = conf;
+    if (!isEmptyObject(global) || !isEmptyObject(local)) {
+      return conf;
+    }
+    return false;
+  } catch (e) {
+    return false;
+  }
+};
+
+export const updateDomHeadStyle = (id, styleContent) => {
+  let styleTag = document.getElementById(id);
+  if (!styleTag) {
+    styleTag = document.createElement('style');
+    styleTag.setAttribute('id', id);
+    document.head.appendChild(styleTag);
+  }
+  styleTag.innerText = styleContent.replace(/@font-face{[^}]+}/, '');
+};

+ 2 - 1
examples/docs/en-US/border.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     '$--box-shadow-light': 'boxShadowLight',
     '$--box-shadow-base': 'boxShadowBase',
@@ -14,7 +15,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/en-US/color.md

@@ -1,6 +1,7 @@
 <script>
   import bus from '../../bus';
   import { tintColor } from '../../color.js';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     'primary': '$--color-primary',
     'success': '$--color-success',
@@ -37,7 +38,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/en-US/typography.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = [
     '$--font-size-extra-large',
     '$--font-size-large',
@@ -18,7 +19,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/es/border.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     '$--box-shadow-light': 'boxShadowLight',
     '$--box-shadow-base': 'boxShadowBase',
@@ -14,7 +15,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/es/color.md

@@ -1,6 +1,7 @@
 <script>
   import bus from '../../bus';
   import { tintColor } from '../../color.js';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     'primary': '$--color-primary',
     'success': '$--color-success',
@@ -37,7 +38,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/es/typography.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = [
     '$--font-size-extra-large',
     '$--font-size-large',
@@ -18,7 +19,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/fr-FR/border.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     '$--box-shadow-light': 'boxShadowLight',
     '$--box-shadow-base': 'boxShadowBase',
@@ -14,7 +15,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/fr-FR/color.md

@@ -1,6 +1,7 @@
 <script>
   import bus from '../../bus';
   import { tintColor } from '../../color.js';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     'primary': '$--color-primary',
     'success': '$--color-success',
@@ -37,7 +38,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/fr-FR/typography.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = [
     '$--font-size-extra-large',
     '$--font-size-large',
@@ -18,7 +19,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/zh-CN/border.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     '$--box-shadow-light': 'boxShadowLight',
     '$--box-shadow-base': 'boxShadowBase',
@@ -14,7 +15,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/zh-CN/color.md

@@ -1,6 +1,7 @@
 <script>
   import bus from '../../bus';
   import { tintColor } from '../../color.js';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = {
     'primary': '$--color-primary',
     'success': '$--color-success',
@@ -37,7 +38,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 2 - 1
examples/docs/zh-CN/typography.md

@@ -1,5 +1,6 @@
 <script>
   import bus from '../../bus';
+  import { ACTION_USER_CONFIG_UPDATE } from '../../components/theme/constant.js';
   const varMap = [
     '$--font-size-extra-large',
     '$--font-size-large',
@@ -18,7 +19,7 @@
   }
   export default {
     created() {
-      bus.$on('user-theme-config-update', this.setGlobal);
+      bus.$on(ACTION_USER_CONFIG_UPDATE, this.setGlobal);
     },
     mounted() {
       this.setGlobal();

+ 4 - 0
examples/i18n/component.json

@@ -24,6 +24,7 @@
     "header": {
       "guide": "指南",
       "components": "组件",
+      "theme": "主题",
       "resource": "资源"
     },
     "nav": {
@@ -55,6 +56,7 @@
     "header": {
       "guide": "Guide",
       "components": "Component",
+      "theme": "Theme",
       "resource": "Resource"
     },
     "nav": {
@@ -86,6 +88,7 @@
     "header": {
       "guide": "Guía",
       "components": "Componentes",
+      "theme": "Theme",
       "resource": "Recursos"
     },
     "nav": {
@@ -117,6 +120,7 @@
     "header": {
       "guide": "Guide",
       "components": "Composants",
+      "theme": "Theme",
       "resource": "Ressources"
     },
     "nav": {

+ 36 - 0
examples/i18n/page.json

@@ -17,6 +17,15 @@
         "paraSize": "18"
       },
       "component": {},
+      "theme": {
+        "1": "官方主题",
+        "2": "我的主题",
+        "3": "主题名称"
+      },
+      "theme-preview": {
+        "1": "返回"
+      },
+      "theme-nav": {},
       "changelog": {
         "1": "更新日志",
         "2": "zh-CN"
@@ -109,6 +118,15 @@
         "paraSize": "18"
       },
       "component": {},
+      "theme": {
+        "1": "Official Theme",
+        "2": "My Theme",
+        "3": "Theme name"
+      },
+      "theme-preview": {
+        "1": "Back"
+      },
+      "theme-nav": {},
       "changelog": {
         "1": "Changelog",
         "2": "en-US"
@@ -201,6 +219,15 @@
         "paraSize": "18"
       },
       "component": {},
+      "theme": {
+        "1": "Official Theme",
+        "2": "My Theme",
+        "3": "Theme name"
+      },
+      "theme-preview": {
+        "1": "Back"
+      },
+      "theme-nav": {},
       "changelog": {
         "1": "Lista de cambios",
         "2": "es"
@@ -293,6 +320,15 @@
         "paraSize": "18"
       },
       "component": {},
+      "theme": {
+        "1": "Official Theme",
+        "2": "My Theme",
+        "3": "Theme name"
+      },
+      "theme-preview": {
+        "1": "Back"
+      },
+      "theme-nav": {},
       "changelog": {
         "1": "Changelog",
         "2": "fr-FR"

+ 184 - 26
examples/i18n/theme-editor.json

@@ -16,28 +16,63 @@
       "icon": "图标",
       "placeholder": "占位符",
       "dropdown": "下拉菜单",
+      "checked": "选中状态",
+      "active": "激活状态",
+      "hover": "鼠标悬停状态",
       "max": "最大",
+      "medium": "中号",
+      "small": "小号",
+      "mini": "最小号",
       "min": "最小",
       "focus": "聚焦",
       "selected": "选中",
       "height": "高度",
       "size": "大小",
+      "header": "头部",
       "group": "分组",
       "radius": "圆角",
       "width": "宽度",
-      "color": "颜色"
+      "color": "颜色",
+      "title": "标题",
+      "content": "内容",
+      "success": "成功状态",
+      "danger": "危险状态",
+      "warning": "警告状态",
+      "info": "提示状态",
+      "customed": "客制化的",
+      "disabled": "禁用状态",
+      "default": "默认",
+      "primary": "主要",
+      "inrange": "日期范围"
     },
     "action": {
-      "theme-editor": "主题编辑器",
-      "no-config": "暂不可编辑,敬请期待",
-      "no-need-config": "本页无需编辑,看看其他页面吧",
-      "load-local-theme-config": "正在恢复您上次自定义的主题",
+      "require-them-name": "主题名称是必填项",
+      "duplicate-them-name": "主题名称重复",
+      "confirm-delete-theme": "确定要删除这个主题?",
+      "max-user-theme": "已达自定义主题上限",
+      "no-preview-config": "获取主题预览配置错误",
+      "undo": "撤销",
+      "redo": "重做",
+      "notice": "提示",
+      "confirm": "确定",
+      "cancel": "取消",
+      "load-local-theme-config": "正在恢复您上次编辑的自定义主题",
+      "upload-theme": "点击上传主题",
+      "rename-theme": "修改命名",
+      "copy-theme": "复制主题",
+      "last-modified": "最近修改",
       "reset-theme": "重置",
-      "download-theme": "下载"
+      "delete-theme": "删除主题",
+      "download-theme": "下载",
+      "theme-check": "查看",
+      "theme-copy": "复制",
+      "theme-edit": "编辑",
+      "description-element": "默认主题",
+      "description-napos": "深色主题"
     },
     "category": {
       "BrandColor": "品牌颜色",
-      "SecondaryColor": "辅助颜色",
+      "FunctionalColor": "辅助颜色",
       "FontColor": "文字颜色",
       "BorderColor": "边框颜色",
       "BackgroundColor": "背景颜色",
@@ -84,7 +119,11 @@
       "font-weight-primary": "主要文字粗细",
       "font-weight-secondary": "次要文字粗细",
       "font-line-height-primary": "主要文字行高",
-      "font-line-height-secondary": "次要文字行高"
+      "font-line-height-secondary": "次要文字行高",
+      "tooltip-fill": "Dark 主题背景色",
+      "tooltip-color": "Light 主题背景色",
+      "slider-height": "滑块轨道高度",
+      "datepicker-off-font-color": "不是当前月份的日期文字颜色"
     }
   },
   {
@@ -133,16 +172,33 @@
       "border-radius": "border radius"
     },
     "action": {
-      "theme-editor": "Theme Editor",
-      "no-config": "Please stay tuned",
-      "load-local-theme-config": "Loading your last edit theme config",
-      "no-need-config": "No config in this page",
+      "require-them-name": "Theme name is required",
+      "duplicate-them-name": "Duplicate them name",
+      "confirm-delete-theme": "Are you sure you want to delete this theme?",
+      "no-preview-config": "No preview config found",
+      "max-user-theme": "Maxium user theme limit",
+      "undo": "Undo",
+      "redo": "Redo",
+      "notice": "Notice",
+      "confirm": "Confirm",
+      "cancel": "Cancel",
+      "load-local-theme-config": "Loading your last saved theme config",
+      "last-modified": "Last modified",
+      "upload-theme": "Click to upload theme",
       "reset-theme": "Reset",
-      "download-theme": "Download"
+      "rename-theme": "Rename",
+      "copy-theme": "Copy",
+      "delete-theme": "Delete",
+      "download-theme": "Download",
+      "theme-check": "Preview",
+      "theme-copy": "Copy",
+      "theme-edit": "Edit",
+      "description-element": "Default theme",
+      "description-napos": "Dark theme"
     },
     "category": {
       "BrandColor": "Brand Color",
-      "SecondaryColor": "Secondary Color",
+      "FunctionalColor": "Functional Color",
       "FontColor": "Font Color",
       "BorderColor": "Border Color",
       "BackgroundColor": "Background Color",
@@ -153,6 +209,40 @@
   },
   {
     "lang": "es",
+    "variable-name" : {
+      "color-primary": "primary color",
+      "color-white": "basic white",
+      "color-black": "basic black",
+      "color-success": "success color",
+      "color-warning": "warning color",
+      "color-danger": "danger color",
+      "color-info": "info color",
+      "color-text-primary": "primary text color",
+      "color-text-regular": "regular text color",
+      "color-text-secondary": "secondary text color",
+      "color-text-placeholder": "placeholder text color",
+      "border-color-base": "border color base",
+      "border-color-light": "border color light",
+      "border-color-lighter": "border color lighter",
+      "border-color-extra-light": "border color extra light",
+      "background-color-base": "base background color",
+      "border-radius-base": "border radius base",
+      "border-radius-small": "border radius small",
+      "border-radius-circle": "border radius circle",
+      "box-shadow-base": "box shadow base",
+      "box-shadow-dark": "box shadow dark",
+      "box-shadow-light": "box shadow light",
+      "font-size-extra-large": "extra large font size",
+      "font-size-large": "large font size",
+      "font-size-medium": "medium font size",
+      "font-size-base": "base font size",
+      "font-size-small": "small font size",
+      "font-size-extra-small": "extra small font size",
+      "font-weight-primary": "primary font weight",
+      "font-weight-secondary": "secondary font weight",
+      "font-line-height-primary": "primary font line height",
+      "font-line-height-secondary": "secondary font line height"
+    },
     "display-name": {
       "border-color": "border color",
       "font-color": "font color",
@@ -163,16 +253,33 @@
       "border-radius": "border radius"
     },
     "action": {
-      "theme-editor": "Theme Editor",
-      "no-config": "Please stay tuned",
-      "load-local-theme-config": "Loading your last edit theme config",
-      "no-need-config": "No config in this page",
+      "require-them-name": "Theme name is required",
+      "duplicate-them-name": "Duplicate them name",
+      "confirm-delete-theme": "Are you sure you want to delete this theme?",
+      "no-preview-config": "No preview config found",
+      "max-user-theme": "Maxium user theme limit",
+      "undo": "Undo",
+      "redo": "Redo",
+      "notice": "Notice",
+      "confirm": "Confirm",
+      "cancel": "Cancel",
+      "load-local-theme-config": "Loading your last saved theme config",
+      "last-modified": "Last modified",
+      "upload-theme": "Click to upload theme",
       "reset-theme": "Reset",
-      "download-theme": "Download"
+      "rename-theme": "Rename",
+      "copy-theme": "Copy",
+      "delete-theme": "Delete",
+      "download-theme": "Download",
+      "theme-check": "Preview",
+      "theme-copy": "Copy",
+      "theme-edit": "Edit",
+      "description-element": "Default theme",
+      "description-napos": "Dark theme"
     },
     "category": {
       "BrandColor": "Brand Color",
-      "SecondaryColor": "Secondary Color",
+      "FunctionalColor": "Functional Color",
       "FontColor": "Font Color",
       "BorderColor": "Border Color",
       "BackgroundColor": "Background Color",
@@ -183,6 +290,40 @@
   },
   {
     "lang": "fr-FR",
+    "variable-name" : {
+      "color-primary": "primary color",
+      "color-white": "basic white",
+      "color-black": "basic black",
+      "color-success": "success color",
+      "color-warning": "warning color",
+      "color-danger": "danger color",
+      "color-info": "info color",
+      "color-text-primary": "primary text color",
+      "color-text-regular": "regular text color",
+      "color-text-secondary": "secondary text color",
+      "color-text-placeholder": "placeholder text color",
+      "border-color-base": "border color base",
+      "border-color-light": "border color light",
+      "border-color-lighter": "border color lighter",
+      "border-color-extra-light": "border color extra light",
+      "background-color-base": "base background color",
+      "border-radius-base": "border radius base",
+      "border-radius-small": "border radius small",
+      "border-radius-circle": "border radius circle",
+      "box-shadow-base": "box shadow base",
+      "box-shadow-dark": "box shadow dark",
+      "box-shadow-light": "box shadow light",
+      "font-size-extra-large": "extra large font size",
+      "font-size-large": "large font size",
+      "font-size-medium": "medium font size",
+      "font-size-base": "base font size",
+      "font-size-small": "small font size",
+      "font-size-extra-small": "extra small font size",
+      "font-weight-primary": "primary font weight",
+      "font-weight-secondary": "secondary font weight",
+      "font-line-height-primary": "primary font line height",
+      "font-line-height-secondary": "secondary font line height"
+    },
     "display-name": {
       "border-color": "border color",
       "font-color": "font color",
@@ -193,16 +334,33 @@
       "border-radius": "border radius"
     },
     "action": {
-      "theme-editor": "Theme Editor",
-      "no-config": "Please stay tuned",
-      "load-local-theme-config": "Loading your last edit theme config",
-      "no-need-config": "No config in this page",
+      "require-them-name": "Theme name is required",
+      "duplicate-them-name": "Duplicate them name",
+      "confirm-delete-theme": "Are you sure you want to delete this theme?",
+      "no-preview-config": "No preview config found",
+      "max-user-theme": "Maxium user theme limit",
+      "undo": "Undo",
+      "redo": "Redo",
+      "notice": "Notice",
+      "confirm": "Confirm",
+      "cancel": "Cancel",
+      "load-local-theme-config": "Loading your last saved theme config",
+      "last-modified": "Last modified",
+      "upload-theme": "Click to upload theme",
       "reset-theme": "Reset",
-      "download-theme": "Download"
+      "rename-theme": "Rename",
+      "copy-theme": "Copy",
+      "delete-theme": "Delete",
+      "download-theme": "Download",
+      "theme-check": "Preview",
+      "theme-copy": "Copy",
+      "theme-edit": "Edit",
+      "description-element": "Default theme",
+      "description-napos": "Dark theme"
     },
     "category": {
       "BrandColor": "Brand Color",
-      "SecondaryColor": "Secondary Color",
+      "FunctionalColor": "Functional Color",
       "FontColor": "Font Color",
       "BorderColor": "Border Color",
       "BackgroundColor": "Background Color",

+ 3 - 42
examples/pages/template/component.tpl

@@ -135,38 +135,6 @@
     }
   }
 
-  @media (min-width: 1140px) {
-    .page-component__content {
-      transition:padding-right 0.3s ease;
-      &.theme-config {
-        padding-right: 26%;  
-      }
-    }
-    .page-container.page-component {
-      transition:all 0.3s ease;
-      &.theme-config {
-        width: 98%;
-        .page-component__nav {
-          animation-delay: 1s;
-          padding-left: 2%;
-        }
-      }
-    }
-  }
-
-  @media (min-width: 1600px) {
-    .page-component__content {
-      &.theme-config {
-        padding-right: 25%;
-      }
-    }
-    .page-container.page-component {
-      &.theme-config {
-        width: 1600px;
-      }
-    }
-  }
-
   @media (max-width: 768px) {
     .page-component {
       .page-component__nav {
@@ -197,11 +165,11 @@
 </style>
 <template>
   <el-scrollbar class="page-component__scroll" ref="componentScrollBar">
-  <div class="page-container page-component" :class="{'theme-config': isThemeConfigVisible}">
+  <div class="page-container page-component">
     <el-scrollbar class="page-component__nav">
       <side-nav :data="navsData[lang]" :base="`/${ lang }/component`"></side-nav>
     </el-scrollbar>
-    <div class="page-component__content" :class="{'theme-config': isThemeConfigVisible}">
+    <div class="page-component__content">
       <router-view class="content"></router-view>
       <footer-nav></footer-nav>
     </div>
@@ -234,8 +202,7 @@
         scrollTop: 0,
         showHeader: true,
         componentScrollBar: null,
-        componentScrollBoxElement: null,
-        isThemeConfigVisible: false
+        componentScrollBoxElement: null
       };
     },
     watch: {
@@ -296,14 +263,8 @@
       bus.$on('navFade', val => {
         this.navFaded = val;
       });
-      bus.$on('user-theme-config-visible', val => {
-        this.isThemeConfigVisible = val;
-      });
     },
     mounted() {
-      if (window.userThemeConfigVisible) {
-        this.isThemeConfigVisible = window.userThemeConfigVisible;
-      }
       this.componentScrollBar = this.$refs.componentScrollBar;
       this.componentScrollBox = this.componentScrollBar.$el.querySelector('.el-scrollbar__wrap');
       this.throttledScrollHandler = throttle(300, this.handleScroll);

+ 3 - 0
examples/pages/template/theme-nav.tpl

@@ -0,0 +1,3 @@
+<template>
+  <router-view></router-view>
+</template>

+ 195 - 0
examples/pages/template/theme-preview.tpl

@@ -0,0 +1,195 @@
+<style lang="scss">
+.page-container.page-theme-preview {
+  padding-top: 30px;
+  .display {
+    width: 75%;
+    display: inline-block;
+    vertical-align: top;
+    h3 {
+      font-size: 28px;
+      margin: 30px 0 0 0;
+    }
+  }
+  .side {
+    display: inline-block;
+    width: 25%;
+    .editor {
+      overflow: hidden;
+      background: #f5f7fa;
+      border: 1px solid #ebeef5;
+      border-radius: 5px;
+      margin-bottom: 20px;
+      &.fixed {
+        position: fixed;
+        width: 285px;
+        box-sizing: border-box;
+      }
+    }
+  }
+}
+</style>
+<template>
+  <div class="page-container page-theme-preview" ref="themePreview">
+    <section class="display">
+      <el-button type="text" icon="el-icon-back" @click="navBack">
+        <%= 1 >
+      </el-button>
+      <h3>{{previewConfig.name}}</h3>
+      <basic-tokens-preview>
+      </basic-tokens-preview>
+      <components-preview>
+      </components-preview>
+    </section>
+    <aside class="side">
+      <section class="editor" :style="{top: `${editorTop}px`, height: `${editorHeight}px`}" :class="{'fixed': isFixed}">
+        <theme-configurator
+          :isOfficial="isOfficial"
+          :themeConfig="themeConfig"
+          :onUserConfigUpdate="onUserConfigUpdate"
+        >
+        </theme-configurator>
+      </section>
+    </aside>
+  </div>
+</template>
+<script>
+import bus from '../../bus.js';
+import ThemeConfigurator from '../../components/theme-configurator';
+import ComponentsPreview from '../../components/theme/components-preview';
+import BasicTokensPreview from '../../components/theme/basic-tokens-preview';
+import {
+  loadPreviewFromLocal,
+  loadUserThemeFromLocal,
+  saveUserThemeToLocal
+} from '../../components/theme/localstorage';
+import {
+  getThemeConfigObject
+} from '../../components/theme/utils';
+import {
+  ACTION_APPLY_THEME
+} from '../../components/theme/constant.js';
+import throttle from 'throttle-debounce/throttle';
+import { getActionDisplayName } from '../../components/theme-configurator/utils/utils';
+
+const maxUserTheme = 8;
+
+export default {
+  components: {
+    ThemeConfigurator,
+    BasicTokensPreview,
+    ComponentsPreview
+  },
+  data() {
+    return {
+      previewConfig: {},
+      themeConfig: {},
+      userTheme: [],
+      editorTop: 0,
+      editorHeight: 1000,
+      isFixed: false
+    };
+  },
+  computed: {
+    isOfficial() {
+      return this.previewConfig.type === 'official';
+    }
+  },
+  created() {
+    this.throttledHandleScroll = throttle(10, true, index => {
+      this.handleScroll(index);
+    });
+  },
+  methods: {
+    navBack() {
+      this.$router.go(-1);
+      this.$nextTick(() => {
+        window.scrollTo(0, 0);
+      });
+    },
+    getNewUserThemeName(originName) {
+      let n = 1;
+      let name;
+      while (true) {
+        name = `${originName}-${n}`;
+        if (this.userTheme.filter(theme => (theme.name === name)).length === 0) {
+          break;
+        }
+        n += 1;
+      }
+      return name;
+    },
+    onUserConfigUpdate(userConfig) {
+      const themeConfig = JSON.stringify(userConfig);
+      const { type, name } = this.previewConfig;
+      if (this.isOfficial) {
+        if (this.userTheme.length >= maxUserTheme) {
+          this.$message.error(getActionDisplayName('max-user-theme'));
+          return;
+        }
+        const autoUserName = this.getNewUserThemeName(name);
+        this.previewConfig.name = autoUserName;
+        this.previewConfig.type = 'user';
+        this.userTheme.push({
+          update: Date.now(),
+          name: autoUserName,
+          theme: themeConfig
+        });
+        saveUserThemeToLocal(this.userTheme);
+        return;
+      }
+      if (type === 'user') {
+        this.userTheme.forEach((config) => {
+          if (config.name === name) {
+            config.update = Date.now();
+            config.theme = themeConfig;
+          }
+        });
+        saveUserThemeToLocal(this.userTheme);
+      }
+    },
+    handleScroll() {
+      const rect = this.$refs.themePreview.getBoundingClientRect();
+      let offsetTop = rect.top;
+      let offsetBottom = rect.bottom;
+      const calHeight = this.editorHeight + 30 + 20;
+      if (offsetTop < 0) {
+        this.isFixed = true;
+        if (offsetBottom < calHeight) {
+          this.editorTop = 30 - calHeight + offsetBottom;
+        } else {
+          this.editorTop = 30;
+        }
+      } else {
+        this.isFixed = false;
+        this.editorTop = 0;
+      }
+    }
+  },
+  beforeDestroy() {
+    window.removeEventListener('scroll', this.throttledHandleScroll);
+  },
+  mounted() {
+    this.editorHeight = window.innerHeight - 40 - 5;
+    window.addEventListener('scroll', this.throttledHandleScroll);
+    this.userTheme = loadUserThemeFromLocal();
+    const previewConfig = loadPreviewFromLocal();
+    const pageRefer = this.$route.params.refer;
+    if (!previewConfig || !pageRefer) {
+      this.$alert(getActionDisplayName('no-preview-config'), getActionDisplayName('notice'), {
+        confirmButtonText: getActionDisplayName('confirm'),
+        callback: action => {
+          const newPath = this.$route.path.replace('/preview', '');
+          this.$router.replace(newPath);
+        }
+      });
+      return;
+    }
+    this.previewConfig = previewConfig;
+    const themeConfig = getThemeConfigObject(previewConfig.theme);
+    if (themeConfig) {
+      this.themeConfig = themeConfig;
+      bus.$emit(ACTION_APPLY_THEME, themeConfig);
+    }
+  }
+};
+</script>

+ 225 - 0
examples/pages/template/theme.tpl

@@ -0,0 +1,225 @@
+<style lang="scss">
+.page-theme {
+  &:last-child {
+    margin-bottom: 55px;
+  }
+  h2 {
+    font-size: 28px;
+    line-height: 28px;
+    margin: 0;
+  }
+  ul {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+  }
+  .theme-card {
+    display: inline-block;
+    height: 150px;
+    height: 16vw;
+    max-height: 230px;
+    flex: 0 0 24%;
+    cursor: default;
+    vertical-align: bottom;
+  }
+  .theme-section {
+    margin-bottom: 20px;
+  }
+  .second-section {
+    margin-top: 60px;
+  }
+}
+</style>
+<template>
+  <div class="page-container page-theme">
+    <section class="theme-section">
+      <h2><%= 1 ></h2>
+      <ul>
+        <li class="theme-card" v-for="item in officialTheme" :key="item.name">
+          <theme-card 
+            type="official" 
+            :config="item"
+            @action="onAction"
+          ></theme-card>
+        </li>
+      </ul>
+    </section>
+    <section class="theme-section second-section">
+      <h2><%= 2 > ({{userThemeCount}}/{{maxUserTheme}})</h2>
+      <ul>
+        <li class="theme-card" v-if="showUserUpload">
+          <theme-card 
+            type="upload" 
+            :config="{name: 'upload'}"
+            @action="onAction"
+          ></theme-card>
+        </li>
+        <li class="theme-card" v-for="item in displayUserTheme" :key="item.name">
+          <theme-card 
+            type="user"
+            :config="item"
+            @action="onAction"
+          ></theme-card>
+        </li>
+      </ul>
+    </section>
+    <el-dialog :visible.sync="copyDialogVisible">
+      <el-form :model="copyForm" ref="copyForm" :rules="copyFormRule">
+        <el-form-item label="<%= 3 >" prop="name">
+          <el-input v-model="copyForm.name"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="closeCopyForm">{{getActionDisplayName('cancel')}}</el-button>
+        <el-button type="primary" @click="copyToUser">{{getActionDisplayName('confirm')}}</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import ThemeCard from '../../components/theme/theme-card.vue';
+import ThemeList from '../../components/theme/theme-list.js';
+import { saveUserThemeToLocal, loadUserThemeFromLocal } from '../../components/theme/localstorage';
+import { getActionDisplayName } from '../../components/theme-configurator/utils/utils';
+
+const maxUserTheme = 8;
+
+export default {
+  components: {
+    ThemeCard
+  },
+  mounted() {
+    this.userTheme = loadUserThemeFromLocal();
+    if (!Array.isArray(this.userTheme)) {
+      this.userTheme = [];
+      saveUserThemeToLocal(this.userTheme);
+    }
+  },
+  data() {
+    return {
+      officialTheme: this.padEmpeyTheme(ThemeList),
+      userTheme: [],
+      maxUserTheme,
+      copyDialogVisible: false,
+      copyForm: {},
+      copyFormRule: {
+        name: [{
+          validator: this.validateCopyName,
+          trigger: 'blur'
+        }]
+      }
+    };
+  },
+  computed: {
+    userThemeCount() {
+      return this.userTheme.length;
+    },
+    showUserUpload() {
+      return this.userThemeCount < maxUserTheme;
+    },
+    displayUserTheme() {
+      return this.padEmpeyTheme(this.userTheme, this.showUserUpload ? 1 : 0);
+    }
+  },
+  methods: {
+    getActionDisplayName(key) {
+      return getActionDisplayName(key);
+    },
+    validateCopyName(rule, value, callback) {
+      if (!value) {
+        callback(new Error(this.getActionDisplayName('require-them-name')));
+      } else if (this.filterUserThemeByName(value).length > 0) {
+        callback(new Error(this.getActionDisplayName('duplicate-them-name')));
+      } else {
+        callback();
+      }
+    },
+    filterUserThemeByName(name, include = true) {
+      return this.userTheme.filter((theme) => (include ? theme.name === name : theme.name !== name));
+    },
+    padEmpeyTheme(theme, add = 0) {
+      if (!theme.length) return [];
+      const pad = 4 - ((theme.length + add) % 4);
+      if (pad < 4) return theme.concat(Array(pad).fill({}));
+      return theme;
+    },
+    onAction(name, item) {
+      switch (name) {
+        case 'copy':
+          this.openCopyForm(item.theme);
+          break;
+        case 'upload':
+          this.openCopyForm(item);
+          break;
+        case 'rename':
+          this.openRenameForm(item.name);
+          break;
+        case 'delete':
+          this.$confirm(this.getActionDisplayName('confirm-delete-theme'), this.getActionDisplayName('notice'), {
+            confirmButtonText: this.getActionDisplayName('confirm'),
+            cancelButtonText: this.getActionDisplayName('cancel'),
+            type: 'warning'
+          }).then(() => {
+            this.deleteUserThemeByName(item.name);
+          }).catch(() => {});
+          break;
+        default:
+          return;
+      }
+    },
+    deleteUserThemeByName(name) {
+      this.userTheme = this.filterUserThemeByName(name, false);
+      this.saveToLocal();
+    },
+    openRenameForm(name) {
+      this.copyForm.oldname = name;
+      this.copyDialogVisible = true;
+    },
+    openCopyForm(theme) {
+      if (this.userTheme.length >= 8) {
+        this.$message.error(this.getActionDisplayName('max-user-theme'));
+        return;
+      }
+      this.copyForm.theme = theme;
+      this.copyDialogVisible = true;
+    },
+    closeCopyForm() {
+      this.copyDialogVisible = false;
+      this.$nextTick(() => {
+        this.copyForm = {};
+      });
+    },
+    copyToUser() {
+      this.$refs.copyForm.validate((valid) => {
+        if (valid) {
+          const { theme, name, oldname } = this.copyForm;
+          if (theme) {
+            // copy
+            this.userTheme.push({
+              update: Date.now(),
+              name,
+              theme
+            });
+          } else {
+            // rename
+            this.userTheme.forEach((config) => {
+              if (config.name === oldname) {
+                config.update = Date.now();
+                config.name = name;
+              }
+            });
+          }
+          this.saveToLocal();
+          this.closeCopyForm();
+        }
+      });
+    },
+    saveToLocal() {
+      saveUserThemeToLocal(this.userTheme);
+    }
+  }
+};
+</script>

+ 19 - 1
examples/route.config.js

@@ -123,6 +123,24 @@ const generateMiscRoutes = function(lang) {
     }]
   };
 
+  let themeRoute = {
+    path: `/${ lang }/theme`,
+    component: load(lang, 'theme-nav'),
+    children: [
+      {
+        path: '/', // 主题管理
+        name: 'theme' + lang,
+        meta: { lang },
+        component: load(lang, 'theme')
+      },
+      {
+        path: 'preview', // 主题预览编辑
+        name: 'theme-preview-' + lang,
+        meta: { lang },
+        component: load(lang, 'theme-preview')
+      }]
+  };
+
   let resourceRoute = {
     path: `/${ lang }/resource`, // 资源
     meta: { lang },
@@ -137,7 +155,7 @@ const generateMiscRoutes = function(lang) {
     component: load(lang, 'index')
   };
 
-  return [guideRoute, resourceRoute, indexRoute];
+  return [guideRoute, resourceRoute, themeRoute, indexRoute];
 };
 
 langs.forEach(lang => {

+ 1 - 1
packages/theme-chalk/src/badge.scss

@@ -7,7 +7,7 @@
   display: inline-block;
 
   @include e(content) {
-    background-color: $--badge-fill;
+    background-color: $--badge-background-color;
     border-radius: $--badge-radius;
     color: $--color-white;
     display: inline-block;

+ 5 - 5
packages/theme-chalk/src/checkbox.scss

@@ -105,7 +105,7 @@
     @include when(disabled) {
       .el-checkbox__inner {
         background-color: $--checkbox-disabled-input-fill;
-        border-color: $--checkbox-disabled-input-border-color;
+        border-color: $--checkbox-disabled-border-color;
         cursor: not-allowed;
 
         &::after {
@@ -149,7 +149,7 @@
 
     @include when(checked) {
       .el-checkbox__inner {
-        background-color: $--checkbox-checked-input-background-color;
+        background-color: $--checkbox-checked-background-color;
         border-color: $--checkbox-checked-input-border-color;
 
         &::after {
@@ -168,7 +168,7 @@
     }
     @include when(indeterminate) {
       .el-checkbox__inner {
-        background-color: $--checkbox-checked-input-background-color;
+        background-color: $--checkbox-checked-background-color;
         border-color: $--checkbox-checked-input-border-color;
 
         &::before {
@@ -193,11 +193,11 @@
     display: inline-block;
     position: relative;
     border: $--checkbox-input-border;
-    border-radius: $--checkbox-input-border-radius;
+    border-radius: $--checkbox-border-radius;
     box-sizing: border-box;
     width: $--checkbox-input-width;
     height: $--checkbox-input-height;
-    background-color: $--checkbox-input-background-color;
+    background-color: $--checkbox-background-color;
     z-index: $--index-normal;
     transition: border-color .25s cubic-bezier(.71,-.46,.29,1.46),
     background-color .25s cubic-bezier(.71,-.46,.29,1.46);

+ 6 - 6
packages/theme-chalk/src/collapse.scss

@@ -18,11 +18,11 @@
     align-items: center;
     height: $--collapse-header-height;
     line-height: $--collapse-header-height;
-    background-color: $--collapse-header-fill;
-    color: $--collapse-header-color;
+    background-color: $--collapse-header-background-color;
+    color: $--collapse-header-font-color;
     cursor: pointer;
     border-bottom: 1px solid $--collapse-border-color;
-    font-size: $--collapse-header-size;
+    font-size: $--collapse-header-font-size;
     font-weight: 500;
     transition: border-bottom-color .3s;
     outline: none;
@@ -44,7 +44,7 @@
 
   @include e(wrap) {
     will-change: height;
-    background-color: $--collapse-content-fill;
+    background-color: $--collapse-content-background-color;
     overflow: hidden;
     box-sizing: border-box;
     border-bottom: 1px solid $--collapse-border-color;
@@ -52,8 +52,8 @@
 
   @include e(content) {
     padding-bottom: 25px;
-    font-size: $--collapse-content-size;
-    color: $--collapse-content-color;
+    font-size: $--collapse-content-font-size;
+    color: $--collapse-content-font-color;
     line-height: 1.769230769230769;
   }
 

+ 159 - 110
packages/theme-chalk/src/common/var.scss

@@ -15,11 +15,11 @@ $--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
 
 /* Color
 -------------------------- */
-/// color|1|BrandColor|0
+/// color|1|Brand Color|0
 $--color-primary: #409EFF !default;
-/// color|1|BackgroundColor|4
+/// color|1|Background Color|4
 $--color-white: #FFFFFF !default;
-/// color|1|BackgroundColor|4
+/// color|1|Background Color|4
 $--color-black: #000000 !default;
 $--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
 $--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
@@ -30,13 +30,13 @@ $--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /
 $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
 $--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
 $--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
-/// color|1|SecondaryColor|1
+/// color|1|Functional Color|1
 $--color-success: #67C23A !default;
-/// color|1|SecondaryColor|1
+/// color|1|Functional Color|1
 $--color-warning: #E6A23C !default;
-/// color|1|SecondaryColor|1
+/// color|1|Functional Color|1
 $--color-danger: #F56C6C !default;
-/// color|1|SecondaryColor|1
+/// color|1|Functional Color|1
 $--color-info: #909399 !default;
 
 $--color-success-light: mix($--color-white, $--color-success, 80%) !default;
@@ -48,26 +48,26 @@ $--color-success-lighter: mix($--color-white, $--color-success, 90%) !default;
 $--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default;
 $--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default;
 $--color-info-lighter: mix($--color-white, $--color-info, 90%) !default;
-/// color|1|FontColor|2
+/// color|1|Font Color|2
 $--color-text-primary: #303133 !default;
-/// color|1|FontColor|2
+/// color|1|Font Color|2
 $--color-text-regular: #606266 !default;
-/// color|1|FontColor|2
+/// color|1|Font Color|2
 $--color-text-secondary: #909399 !default;
-/// color|1|FontColor|2
+/// color|1|Font Color|2
 $--color-text-placeholder: #C0C4CC !default;
-/// color|1|BorderColor|3
+/// color|1|Border Color|3
 $--border-color-base: #DCDFE6 !default;
-/// color|1|BorderColor|3
+/// color|1|Border Color|3
 $--border-color-light: #E4E7ED !default;
-/// color|1|BorderColor|3
+/// color|1|Border Color|3
 $--border-color-lighter: #EBEEF5 !default;
-/// color|1|BorderColor|3
+/// color|1|Border Color|3
 $--border-color-extra-light: #F2F6FC !default;
 
 // Background
-/// color|1|BackgroundColor|4
-$--background-color-base: #f5f7fa !default;
+/// color|1|Background Color|4
+$--background-color-base: #F5F7FA !default;
 
 /* Link
 -------------------------- */
@@ -102,25 +102,25 @@ $--fill-base: $--color-white !default;
 /* Typography
 -------------------------- */
 $--font-path: 'fonts' !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-extra-large: 20px !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-large: 18px !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-medium: 16px !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-base: 14px !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-small: 13px !default;
-/// fontSize|1|FontSize|0
+/// fontSize|1|Font Size|0
 $--font-size-extra-small: 12px !default;
-/// fontWeight|1|FontWeight|1
+/// fontWeight|1|Font Weight|1
 $--font-weight-primary: 500 !default;
-/// fontWeight|1|FontWeight|1
+/// fontWeight|1|Font Weight|1
 $--font-weight-secondary: 100 !default;
-/// fontLineHeight|1|LineHeight|2
+/// fontLineHeight|1|Line Height|2
 $--font-line-height-primary: 24px !default;
-/// fontLineHeight|1|LineHeight|2
+/// fontLineHeight|1|Line Height|2
 $--font-line-height-secondary: 16px !default;
 $--font-color-disabled-base: #bbb !default;
 /* Size
@@ -155,17 +155,13 @@ $--checkbox-font-color: $--color-text-regular !default;
 $--checkbox-input-height: 14px !default;
 $--checkbox-input-width: 14px !default;
 /// borderRadius||Border|2
-$--checkbox-input-border-radius: $--border-radius-small !default;
+$--checkbox-border-radius: $--border-radius-small !default;
 /// color||Color|0
-$--checkbox-input-background-color: $--color-white !default;
+$--checkbox-background-color: $--color-white !default;
 $--checkbox-input-border: $--border-base !default;
-/// color||Color|0
-$--checkbox-input-border-color: $--border-color-base !default;
-/// color||Color|0
-$--checkbox-icon-color: $--color-white !default;
 
 /// color||Color|0
-$--checkbox-disabled-input-border-color: $--border-color-base !default;
+$--checkbox-disabled-border-color: $--border-color-base !default;
 $--checkbox-disabled-input-fill: #edf2fc !default;
 $--checkbox-disabled-icon-color: $--color-text-placeholder !default;
 
@@ -177,7 +173,7 @@ $--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default;
 $--checkbox-checked-font-color: $--color-primary !default;
 $--checkbox-checked-input-border-color: $--color-primary !default;
 /// color||Color|0
-$--checkbox-checked-input-background-color: $--color-primary !default;
+$--checkbox-checked-background-color: $--color-primary !default;
 $--checkbox-checked-icon-color: $--fill-base !default;
 
 $--checkbox-input-border-color-hover: $--color-primary !default;
@@ -204,8 +200,6 @@ $--checkbox-bordered-mini-input-width: 12px !default;
 /// height||Other|4
 $--checkbox-bordered-mini-height: 28px !default;
 
-/// fontSize||Font|1
-$--checkbox-button-font-size: $--font-size-base !default;
 /// color||Color|0
 $--checkbox-button-checked-background-color: $--color-primary !default;
 /// color||Color|0
@@ -305,7 +299,6 @@ $--select-option-selected-font-color: $--color-primary !default;
 $--select-option-selected-hover: $--background-color-base !default;
 
 $--select-group-color: $--color-info !default;
-/// height||Other|4
 $--select-group-height: 30px !default;
 $--select-group-font-size: 12px !default;
 
@@ -314,7 +307,6 @@ $--select-dropdown-shadow: $--box-shadow-light !default;
 $--select-dropdown-empty-color: #999 !default;
 /// height||Other|4
 $--select-dropdown-max-height: 274px !default;
-/// padding||Spacing|3
 $--select-dropdown-padding: 6px 0 !default;
 $--select-dropdown-empty-padding: 10px 0 !default;
 $--select-dropdown-border: solid 1px $--border-color-light !default;
@@ -322,10 +314,15 @@ $--select-dropdown-border: solid 1px $--border-color-light !default;
 /* Alert
 -------------------------- */
 $--alert-padding: 8px 16px !default;
+/// borderRadius||Border|2
 $--alert-border-radius: $--border-radius-base !default;
+/// fontSize||Font|1
 $--alert-title-font-size: 13px !default;
+/// fontSize||Font|1
 $--alert-description-font-size: 12px !default;
+/// fontSize||Font|1
 $--alert-close-font-size: 12px !default;
+/// fontSize||Font|1
 $--alert-close-customed-font-size: 13px !default;
 
 $--alert-success-color: $--color-success-lighter !default;
@@ -333,23 +330,34 @@ $--alert-info-color: $--color-info-lighter !default;
 $--alert-warning-color: $--color-warning-lighter !default;
 $--alert-danger-color: $--color-danger-lighter !default;
 
+/// height||Other|4
 $--alert-icon-size: 16px !default;
+/// height||Other|4
 $--alert-icon-large-size: 28px !default;
 
-/* Message Box
+/* MessageBox
 -------------------------- */
+/// color||Color|0
+$--messagebox-title-color: $--color-text-primary !default;
 $--msgbox-width: 420px !default;
 $--msgbox-border-radius: 4px !default;
-$--msgbox-font-size: $--font-size-large !default;
-$--msgbox-content-font-size: $--font-size-base !default;
-$--msgbox-content-color: $--color-text-regular !default;
-$--msgbox-error-font-size: 12px !default;
+/// fontSize||Font|1
+$--messagebox-font-size: $--font-size-large !default;
+/// fontSize||Font|1
+$--messagebox-content-font-size: $--font-size-base !default;
+/// color||Color|0
+$--messagebox-content-color: $--color-text-regular !default;
+/// fontSize||Font|1
+$--messagebox-error-font-size: 12px !default;
 $--msgbox-padding-primary: 15px !default;
-
-$--msgbox-success-color: $--color-success !default;
-$--msgbox-info-color: $--color-info !default;
-$--msgbox-warning-color: $--color-warning !default;
-$--msgbox-danger-color: $--color-danger !default;
+/// color||Color|0
+$--messagebox-success-color: $--color-success !default;
+/// color||Color|0
+$--messagebox-info-color: $--color-info !default;
+/// color||Color|0
+$--messagebox-warning-color: $--color-warning !default;
+/// color||Color|0
+$--messagebox-danger-color: $--color-danger !default;
 
 /* Message
 -------------------------- */
@@ -357,43 +365,60 @@ $--message-shadow: $--box-shadow-base !default;
 $--message-min-width: 380px !default;
 $--message-background-color: #edf2fc !default;
 $--message-padding: 15px 15px 15px 20px !default;
-$--message-content-color: $--color-text-regular !default;
-$--message-close-color: $--color-text-placeholder !default;
+/// color||Color|0
+$--message-close-icon-color: $--color-text-placeholder !default;
+/// height||Other|4
 $--message-close-size: 16px !default;
+/// color||Color|0
 $--message-close-hover-color: $--color-text-secondary !default;
 
-$--message-success-color: $--color-success !default;
-$--message-info-color: $--color-info !default;
-$--message-warning-color: $--color-warning !default;
-$--message-danger-color: $--color-danger !default;
+/// color||Color|0
+$--message-success-font-color: $--color-success !default;
+/// color||Color|0
+$--message-info-font-color: $--color-info !default;
+/// color||Color|0
+$--message-warning-font-color: $--color-warning !default;
+/// color||Color|0
+$--message-danger-font-color: $--color-danger !default;
 
 /* Notification
 -------------------------- */
 $--notification-width: 330px !default;
+/// padding||Spacing|3
 $--notification-padding: 14px 26px 14px 13px !default;
 $--notification-radius: 8px !default;
 $--notification-shadow: $--box-shadow-light !default;
+/// color||Color|0
 $--notification-border-color: $--border-color-lighter !default;
 $--notification-icon-size: 24px !default;
 $--notification-close-font-size: $--message-close-size !default;
 $--notification-group-margin-left: 13px !default;
 $--notification-group-margin-right: 8px !default;
-$--notification-font-size: $--font-size-base !default;
-$--notification-color: $--color-text-regular !default;
+/// fontSize||Font|1
+$--notification-content-font-size: $--font-size-base !default;
+/// color||Color|0
+$--notification-content-color: $--color-text-regular !default;
+/// fontSize||Font|1
 $--notification-title-font-size: 16px !default;
+/// color||Color|0
 $--notification-title-color: $--color-text-primary !default;
 
+/// color||Color|0
 $--notification-close-color: $--color-text-secondary !default;
+/// color||Color|0
 $--notification-close-hover-color: $--color-text-regular !default;
 
-$--notification-success-color: $--color-success !default;
-$--notification-info-color: $--color-info !default;
-$--notification-warning-color: $--color-warning !default;
-$--notification-danger-color: $--color-danger !default;
+/// color||Color|0
+$--notification-success-icon-color: $--color-success !default;
+/// color||Color|0
+$--notification-info-icon-color: $--color-info !default;
+/// color||Color|0
+$--notification-warning-icon-color: $--color-warning !default;
+/// color||Color|0
+$--notification-danger-icon-color: $--color-danger !default;
 
 /* Input
 -------------------------- */
-/// fontSize||Font|1
 $--input-font-size: $--font-size-base !default;
 /// color||Color|0
 $--input-font-color: $--color-text-regular !default;
@@ -447,13 +472,7 @@ $--cascader-menu-font-color: $--color-text-regular !default;
 /// color||Color|0
 $--cascader-menu-selected-font-color: $--color-primary !default;
 $--cascader-menu-fill: $--fill-base !default;
-/// fontSize||Font|1
-$--cascader-menu-font-size: $--font-size-base !default;
-/// borderRadius||Border|2
-$--cascader-menu-radius: $--border-radius-base !default;
 $--cascader-menu-border: $--border-base !default;
-/// color||Color|0
-$--cascader-menu-border-color: $--border-color-base !default;
 $--cascader-menu-border-width: $--border-width-base !default;
 $--cascader-menu-color: $--color-text-regular !default;
 $--cascader-menu-option-color-active: $--color-text-secondary !default;
@@ -463,8 +482,6 @@ $--cascader-menu-option-fill-hover: rgba($--color-text-primary, 0.06) !default;
 $--cascader-menu-option-color-disabled: #999 !default;
 $--cascader-menu-option-fill-disabled: rgba($--color-black, 0.06) !default;
 $--cascader-menu-option-empty-color: #666 !default;
-/// color||Color|0
-$--cascader-menu-group-color: #999 !default;
 $--cascader-menu-shadow: 0 1px 2px rgba($--color-black, 0.14), 0 0 3px rgba($--color-black, 0.14) !default;
 $--cascader-menu-option-pinyin-color: #999 !default;
 $--cascader-menu-submenu-shadow: 1px 1px 2px rgba($--color-black, 0.14), 1px 0 2px rgba($--color-black, 0.14) !default;
@@ -587,28 +604,27 @@ $--cascader-height: 200px !default;
 $--switch-on-color: $--color-primary !default;
 /// color||Color|0
 $--switch-off-color: $--border-color-base !default;
-/// color||Color|0
-$--switch-disabled-color: $--border-color-lighter !default;
 /// fontSize||Font|1
 $--switch-font-size: $--font-size-base !default;
-/// borderRadius||Border|2
 $--switch-core-border-radius: 10px !default;
-/// height||Other|4
+// height||Other|4 TODO: width 代码写死的40px 所以下面这三个属性都没意义
 $--switch-width: 40px !default;
-/// height||Other|4
+// height||Other|4
 $--switch-height: 20px !default;
-/// height||Other|4
+// height||Other|4
 $--switch-button-size: 16px !default;
 
 /* Dialog
 -------------------------- */
 $--dialog-background-color: $--color-white !default;
 $--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default;
-$--dialog-close-hover-color: $--color-primary !default;
+/// fontSize||Font|1
 $--dialog-title-font-size: $--font-size-large !default;
-$--dialog-font-size: 14px !default;
-/// fontLineHeight|
+/// fontSize||Font|1
+$--dialog-content-font-size: 14px !default;
+/// fontLineHeight||LineHeight|2
 $--dialog-font-line-height: $--font-line-height-primary !default;
+/// padding||Spacing|3
 $--dialog-padding-primary: 20px !default;
 
 /* Table
@@ -619,7 +635,7 @@ $--table-border: 1px solid $--table-border-color !default;
 /// color||Color|0
 $--table-font-color: $--color-text-regular !default;
 /// color||Color|0
-$--table-header-color: $--color-text-secondary !default;
+$--table-header-font-color: $--color-text-secondary !default;
 /// color||Color|0
 $--table-row-hover-background-color: $--background-color-base !default;
 $--table-current-row-background-color: $--color-primary-light-9 !default;
@@ -629,36 +645,55 @@ $--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default;
 
 /* Pagination
 -------------------------- */
+/// fontSize||Font|1
 $--pagination-font-size: 13px !default;
-$--pagination-fill: $--color-white !default;
-$--pagination-color: $--color-text-primary !default;
+/// color||Color|0
+$--pagination-background-color: $--color-white !default;
+/// color||Color|0
+$--pagination-font-color: $--color-text-primary !default;
 $--pagination-border-radius: 3px !default;
+/// color||Color|0
 $--pagination-button-color: $--color-text-primary !default;
+/// height||Other|4
 $--pagination-button-width: 35.5px !default;
+/// height||Other|4
 $--pagination-button-height: 28px !default;
+/// color||Color|0
 $--pagination-button-disabled-color: $--color-text-placeholder !default;
-$--pagination-button-disabled-fill: $--color-white !default;
-$--pagination-hover-fill: $--color-primary !default;
-$--pagination-hover-color: $--color-white !default;
+/// color||Color|0
+$--pagination-button-disabled-background-color: $--color-white !default;
+/// color||Color|0
+$--pagination-hover-color: $--color-primary !default;
 
 /* Popover
 -------------------------- */
-$--popover-fill: $--color-white !default;
+/// color||Color|0
+$--popover-background-color: $--color-white !default;
+/// fontSize||Font|1
 $--popover-font-size: $--font-size-base !default;
+/// color||Color|0
 $--popover-border-color: $--border-color-lighter !default;
 $--popover-arrow-size: 6px !default;
+/// padding||Spacing|3
 $--popover-padding: 12px !default;
 $--popover-padding-large: 18px 20px !default;
+/// fontSize||Font|1
 $--popover-title-font-size: 16px !default;
-$--popover-title-color: $--color-text-primary !default;
+/// color||Color|0
+$--popover-title-font-color: $--color-text-primary !default;
 
 /* Tooltip
 -------------------------- */
+/// color|1|Color|0
 $--tooltip-fill: $--color-text-primary !default;
+/// color|1|Color|0
 $--tooltip-color: $--color-white !default;
+/// fontSize||Font|1
 $--tooltip-font-size: 12px !default;
+/// color||Color|0
 $--tooltip-border-color: $--color-text-primary !default;
 $--tooltip-arrow-size: 6px !default;
+/// padding||Spacing|3
 $--tooltip-padding: 10px !default;
 
 /* Tag
@@ -668,7 +703,7 @@ $--tag-fill: rgba($--color-primary, 0.10) !default;
 /// color||Color|0
 $--tag-font-color: $--color-primary !default;
 /// color||Color|0
-$--tag-background-color: $--color-primary !default;
+$--tag-default-hover-background-color: $--color-primary !default;
 $--tag-border: rgba($--color-primary, 0.20) !default;
 /// fontSize||Font|1
 $--tag-font-size: 12px !default;
@@ -711,16 +746,22 @@ $--dropdown-menuItem-hover-color: $--link-color !default;
 
 /* Badge
 -------------------------- */
-$--badge-fill: $--color-danger !default;
+/// color||Color|0
+$--badge-background-color: $--color-danger !default;
 $--badge-radius: 10px !default;
+/// fontSize||Font|1
 $--badge-font-size: 12px !default;
+/// padding||Spacing|3
 $--badge-padding: 6px !default;
+/// height||Other|4
 $--badge-size: 18px !default;
 
 /* Card
 --------------------------*/
+/// color||Color|0
 $--card-border-color: $--border-color-lighter !default;
 $--card-border-radius: 4px !default;
+/// padding||Spacing|3
 $--card-padding: 20px !default;
 
 /* Slider
@@ -730,15 +771,11 @@ $--slider-main-background-color: $--color-primary !default;
 /// color||Color|0
 $--slider-runway-background-color: $--border-color-light !default;
 $--slider-button-hover-color: mix($--color-primary, black, 97%) !default;
-/// color||Color|0
 $--slider-stop-background-color: $--color-white !default;
-/// color||Color|0
 $--slider-disable-color: $--color-text-placeholder !default;
-
-/// margin||Spacing|3
 $--slider-margin: 16px 0 !default;
 $--slider-border-radius: 3px !default;
-/// height||Other|4
+/// height|1|Other|4
 $--slider-height: 6px !default;
 /// height||Other|4
 $--slider-button-size: 16px !default;
@@ -753,14 +790,16 @@ $--steps-padding: 20px !default;
 
 /* Menu
 --------------------------*/
+/// fontSize||Font|1
 $--menu-item-font-size: $--font-size-base !default;
-$--menu-item-color: $--color-text-primary !default;
-$--menu-item-fill: $--color-white !default;
+/// color||Color|0
+$--menu-item-font-color: $--color-text-primary !default;
+/// color||Color|0
+$--menu-background-color: $--color-white !default;
 $--menu-item-hover-fill: $--color-primary-light-9 !default;
 
 /* Rate
 --------------------------*/
-/// height||Other|3
 $--rate-height: 20px !default;
 /// fontSize||Font|1
 $--rate-font-size: $--font-size-base !default;
@@ -768,14 +807,12 @@ $--rate-font-size: $--font-size-base !default;
 $--rate-icon-size: 18px !default;
 /// margin||Spacing|2
 $--rate-icon-margin: 6px !default;
-/// color||Color|0
 $--rate-icon-color: $--color-text-placeholder !default;
 
 /* DatePicker
 --------------------------*/
-/// color||Color|0
 $--datepicker-font-color: $--color-text-regular !default;
-/// color||Color|0
+/// color|1|Color|0
 $--datepicker-off-font-color: $--color-text-placeholder !default;
 /// color||Color|0
 $--datepicker-header-font-color: $--color-text-regular !default;
@@ -783,18 +820,20 @@ $--datepicker-icon-color: $--color-text-primary !default;
 $--datepicker-border-color: $--disabled-border-base !default;
 $--datepicker-inner-border-color: #e4e4e4 !default;
 /// color||Color|0
-$--datepicker-inrange-color: $--border-color-extra-light !default;
+$--datepicker-inrange-background-color: $--border-color-extra-light !default;
 /// color||Color|0
-$--datepicker-inrange-hover-color: $--border-color-extra-light !default;
+$--datepicker-inrange-hover-background-color: $--border-color-extra-light !default;
 /// color||Color|0
 $--datepicker-active-color: $--color-primary !default;
 /// color||Color|0
-$--datepicker-text-hover-color: $--color-primary !default;
+$--datepicker-hover-font-color: $--color-primary !default;
 $--datepicker-cell-hover-color: #fff !default;
 
 /* Loading
 --------------------------*/
+/// height||Other|4
 $--loading-spinner-size: 42px !default;
+/// height||Other|4
 $--loading-fullscreen-spinner-size: 50px !default;
 
 /* Scrollbar
@@ -804,11 +843,14 @@ $--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default;
 
 /* Carousel
 --------------------------*/
+/// fontSize||Font|1
 $--carousel-arrow-font-size: 12px !default;
 $--carousel-arrow-size: 36px !default;
 $--carousel-arrow-background: rgba(31, 45, 61, 0.11) !default;
 $--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23) !default;
+/// width||Other|4
 $--carousel-indicator-width: 30px !default;
+/// height||Other|4
 $--carousel-indicator-height: 2px !default;
 $--carousel-indicator-padding-horizontal: 4px !default;
 $--carousel-indicator-padding-vertical: 12px !default;
@@ -816,15 +858,22 @@ $--carousel-indicator-out-color: $--border-color-hover !default;
 
 /* Collapse
 --------------------------*/
+/// color||Color|0
 $--collapse-border-color: $--border-color-lighter !default;
+/// height||Other|4
 $--collapse-header-height: 48px !default;
-$--collapse-header-padding: 20px !default;
-$--collapse-header-fill: $--color-white !default;
-$--collapse-header-color: $--color-text-primary !default;
-$--collapse-header-size: 13px !default;
-$--collapse-content-fill: $--color-white !default;
-$--collapse-content-size: 13px !default;
-$--collapse-content-color: $--color-text-primary !default;
+/// color||Color|0
+$--collapse-header-background-color: $--color-white !default;
+/// color||Color|0
+$--collapse-header-font-color: $--color-text-primary !default;
+/// fontSize||Font|1
+$--collapse-header-font-size: 13px !default;
+/// color||Color|0
+$--collapse-content-background-color: $--color-white !default;
+/// fontSize||Font|1
+$--collapse-content-font-size: 13px !default;
+/// color||Color|0
+$--collapse-content-font-color: $--color-text-primary !default;
 
 /* Transfer
 --------------------------*/

+ 1 - 1
packages/theme-chalk/src/date-picker/date-picker.scss

@@ -67,7 +67,7 @@
     color: $--color-text-regular;
 
     &:hover {
-      color: $--datepicker-text-hover-color;
+      color: $--datepicker-hover-font-color;
     }
 
     &.active {

+ 7 - 7
packages/theme-chalk/src/date-picker/date-table.scss

@@ -9,7 +9,7 @@
     .el-date-table__row {
       &:hover {
         div {
-          background-color: $--datepicker-inrange-color;
+          background-color: $--datepicker-inrange-background-color;
         }
         td.available:hover {
           color: $--datepicker-font-color;
@@ -27,7 +27,7 @@
       }
 
       &.current div {
-        background-color: $--datepicker-inrange-color;
+        background-color: $--datepicker-inrange-background-color;
       }
     }
   }
@@ -77,13 +77,13 @@
     }
 
     &.available:hover {
-      color: $--datepicker-text-hover-color;
+      color: $--datepicker-hover-font-color;
     }
 
     &.in-range div {
-      background-color: $--datepicker-inrange-color;
+      background-color: $--datepicker-inrange-background-color;
       &:hover {
-        background-color: $--datepicker-inrange-hover-color;
+        background-color: $--datepicker-inrange-hover-background-color;
       }
     }
 
@@ -123,10 +123,10 @@
     &.selected div {
       margin-left: 5px;
       margin-right: 5px;
-      background-color: $--datepicker-inrange-color;
+      background-color: $--datepicker-inrange-background-color;
       border-radius: 15px;
       &:hover {
-        background-color: $--datepicker-inrange-hover-color;
+        background-color: $--datepicker-inrange-hover-background-color;
       }
     }
 

+ 3 - 3
packages/theme-chalk/src/date-picker/month-table.scss

@@ -44,14 +44,14 @@
       margin: 0 auto;
       border-radius: 18px;
       &:hover {
-        color: $--datepicker-text-hover-color;
+        color: $--datepicker-hover-font-color;
       }
     }
 
     &.in-range div {
-      background-color: $--datepicker-inrange-color;
+      background-color: $--datepicker-inrange-background-color;
       &:hover {
-        background-color: $--datepicker-inrange-hover-color;
+        background-color: $--datepicker-inrange-hover-background-color;
       }
     }
     &.start-date div,

+ 2 - 2
packages/theme-chalk/src/date-picker/picker-panel.scss

@@ -45,7 +45,7 @@
     cursor: pointer;
 
     &:hover {
-      color: $--datepicker-text-hover-color;
+      color: $--datepicker-hover-font-color;
     }
 
     &.active {
@@ -81,7 +81,7 @@
     margin-top: 8px;
 
     &:hover {
-      color: $--datepicker-text-hover-color;
+      color: $--datepicker-hover-font-color;
     }
 
     @include when(disabled) {

+ 1 - 1
packages/theme-chalk/src/date-picker/year-table.scss

@@ -40,7 +40,7 @@
       margin: 0 auto;
 
       &:hover {
-        color: $--datepicker-text-hover-color;
+        color: $--datepicker-hover-font-color;
       }
     }
 

+ 1 - 1
packages/theme-chalk/src/dialog.scss

@@ -66,7 +66,7 @@
   @include e(body) {
     padding: 30px 20px;
     color: $--color-text-regular;
-    font-size: $--dialog-font-size;
+    font-size: $--dialog-content-font-size;
     word-break: break-all;
   }
 

+ 2 - 2
packages/theme-chalk/src/menu.scss

@@ -7,7 +7,7 @@
   height: 56px;
   line-height: 56px;
   font-size: $--menu-item-font-size;
-  color: $--menu-item-color;
+  color: $--menu-item-font-color;
   padding: 0 20px;
   list-style: none;
   cursor: pointer;
@@ -43,7 +43,7 @@
   position: relative;
   margin: 0;
   padding-left: 0;
-  background-color: $--menu-item-fill;
+  background-color: $--menu-background-color;
   @include utils-clearfix;
   &.el-menu--horizontal {
     border-bottom: solid 1px #e6e6e6;

+ 10 - 10
packages/theme-chalk/src/message-box.scss

@@ -12,7 +12,7 @@
   background-color: $--color-white;
   border-radius: $--msgbox-border-radius;
   border: 1px solid $--border-color-lighter;
-  font-size: $--msgbox-font-size;
+  font-size: $--messagebox-font-size;
   box-shadow: $--box-shadow-light;
   text-align: left;
   overflow: hidden;
@@ -44,9 +44,9 @@
   @include e(title) {
     padding-left: 0;
     margin-bottom: 0;
-    font-size: $--msgbox-font-size;
+    font-size: $--messagebox-font-size;
     line-height: 1;
-    color: $--color-text-primary;
+    color: $--messagebox-title-color;
   }
 
   @include e(headerbtn) {
@@ -75,8 +75,8 @@
   @include e(content) {
     position: relative;
     padding: 10px $--msgbox-padding-primary;
-    color: $--msgbox-content-color;
-    font-size: $--msgbox-content-font-size;
+    color: $--messagebox-content-color;
+    font-size: $--messagebox-content-font-size;
   }
 
   @include e(input) {
@@ -107,19 +107,19 @@
     }
 
     &.el-icon-success {
-      color: $--msgbox-success-color;
+      color: $--messagebox-success-color;
     }
 
     &.el-icon-info {
-      color: $--msgbox-info-color;
+      color: $--messagebox-info-color;
     }
 
     &.el-icon-warning {
-      color: $--msgbox-warning-color;
+      color: $--messagebox-warning-color;
     }
 
     &.el-icon-error {
-      color: $--msgbox-danger-color;
+      color: $--messagebox-danger-color;
     }
   }
 
@@ -134,7 +134,7 @@
 
   @include e(errormsg) {
     color: $--color-danger;
-    font-size: $--msgbox-error-font-size;
+    font-size: $--messagebox-error-font-size;
     min-height: 18px;
     margin-top: 2px;
   }

+ 9 - 9
packages/theme-chalk/src/message.scss

@@ -35,7 +35,7 @@
 
   @include m(info) {
     .el-message__content {
-      color: $--message-info-color;
+      color: $--message-info-font-color;
     }
   }
 
@@ -44,7 +44,7 @@
     border-color: $--color-success-light;
 
     .el-message__content {
-      color: $--message-success-color;
+      color: $--message-success-font-color;
     }
   }
 
@@ -53,7 +53,7 @@
     border-color: $--color-warning-light;
 
     .el-message__content {
-      color: $--message-warning-color;
+      color: $--message-warning-font-color;
     }
   }
 
@@ -62,7 +62,7 @@
     border-color: $--color-danger-light;
 
     .el-message__content {
-      color: $--message-danger-color;
+      color: $--message-danger-font-color;
     }
   }
 
@@ -85,7 +85,7 @@
     right: 15px;
     transform: translateY(-50%);
     cursor: pointer;
-    color: $--message-close-color;
+    color: $--message-close-icon-color;
     font-size: $--message-close-size;
 
     &:focus {
@@ -97,19 +97,19 @@
   }
 
   & .el-icon-success {
-    color: $--message-success-color;
+    color: $--message-success-font-color;
   }
 
   & .el-icon-error {
-    color: $--message-danger-color;
+    color: $--message-danger-font-color;
   }
 
   & .el-icon-info {
-    color: $--message-info-color;
+    color: $--message-info-font-color;
   }
 
   & .el-icon-warning {
-    color: $--message-warning-color;
+    color: $--message-warning-font-color;
   }
 }
 

+ 6 - 6
packages/theme-chalk/src/notification.scss

@@ -35,10 +35,10 @@
   }
 
   @include e(content) {
-    font-size: $--notification-font-size;
+    font-size: $--notification-content-font-size;
     line-height: 21px;
     margin: 6px 0 0 0;
-    color: $--notification-color;
+    color: $--notification-content-color;
     text-align: justify;
 
     p {
@@ -66,19 +66,19 @@
   }
 
   .el-icon-success {
-    color: $--notification-success-color;
+    color: $--notification-success-icon-color;
   }
 
   .el-icon-error {
-    color: $--notification-danger-color;
+    color: $--notification-danger-icon-color;
   }
 
   .el-icon-info {
-    color: $--notification-info-color;
+    color: $--notification-info-icon-color;
   }
 
   .el-icon-warning {
-    color: $--notification-warning-color;
+    color: $--notification-warning-icon-color;
   }
 }
 

+ 9 - 9
packages/theme-chalk/src/pagination.scss

@@ -6,7 +6,7 @@
 @include b(pagination) {
   white-space: nowrap;
   padding: 2px 5px;
-  color: $--pagination-color;
+  color: $--pagination-font-color;
   font-weight: bold;
   @include utils-clearfix;
 
@@ -53,12 +53,12 @@
     }
 
     &:hover {
-      color: $--pagination-hover-fill;
+      color: $--pagination-hover-color;
     }
 
     &:disabled {
       color: $--pagination-button-disabled-color;
-      background-color: $--pagination-button-disabled-fill;
+      background-color: $--pagination-button-disabled-background-color;
       cursor: not-allowed;
     }
   }
@@ -67,7 +67,7 @@
   .btn-next {
     background: center center no-repeat;
     background-size: 16px;
-    background-color: $--pagination-fill;
+    background-color: $--pagination-background-color;
     cursor: pointer;
     margin: 0;
     color: $--pagination-button-color;
@@ -139,7 +139,7 @@
       padding-left: 8px;
 
       &:hover {
-        border-color: $--pagination-hover-fill;
+        border-color: $--pagination-hover-color;
       }
     }
   }
@@ -214,7 +214,7 @@
 
     .el-pager li:not(.disabled) {
       &:hover {
-        color: $--pagination-hover-fill;
+        color: $--pagination-hover-color;
       }
 
       &.active {
@@ -249,7 +249,7 @@
 
   li {
     padding: 0 4px;
-    background: $--pagination-fill;
+    background: $--pagination-background-color;
     vertical-align: top;
     display: inline-block;
     font-size: $--pagination-font-size;
@@ -284,11 +284,11 @@
     }
 
     &:hover {
-      color: $--pagination-hover-fill;
+      color: $--pagination-hover-color;
     }
 
     &.active {
-      color: $--pagination-hover-fill;
+      color: $--pagination-hover-color;
       cursor: default;
     }
   }

+ 2 - 2
packages/theme-chalk/src/popover.scss

@@ -4,7 +4,7 @@
 
 @include b(popover) {
   position: absolute;
-  background: $--popover-fill;
+  background: $--popover-background-color;
   min-width: 150px;
   border-radius: 4px;
   border: 1px solid $--popover-border-color;
@@ -22,7 +22,7 @@
   }
 
   @include e(title) {
-    color: $--popover-title-color;
+    color: $--popover-title-font-color;
     font-size: $--popover-title-font-size;
     line-height: 1;
     margin-bottom: 12px;

+ 4 - 4
packages/theme-chalk/src/popper.scss

@@ -36,7 +36,7 @@
     &::after {
       bottom: 1px;
       margin-left: -$--popover-arrow-size;
-      border-top-color: $--popover-fill;
+      border-top-color: $--popover-background-color;
       border-bottom-width: 0;
     }
   }
@@ -56,7 +56,7 @@
       top: 1px;
       margin-left: -$--popover-arrow-size;
       border-top-width: 0;
-      border-bottom-color: $--popover-fill;
+      border-bottom-color: $--popover-background-color;
     }
   }
 
@@ -74,7 +74,7 @@
     &::after {
       bottom: -$--popover-arrow-size;
       left: 1px;
-      border-right-color: $--popover-fill;
+      border-right-color: $--popover-background-color;
       border-left-width: 0;
     }
   }
@@ -95,7 +95,7 @@
       bottom: -$--popover-arrow-size;
       margin-left: -$--popover-arrow-size;
       border-right-width: 0;
-      border-left-color: $--popover-fill;
+      border-left-color: $--popover-background-color;
     }
   }
 }

+ 1 - 1
packages/theme-chalk/src/table.scss

@@ -103,7 +103,7 @@
   }
 
   thead {
-    color: $--table-header-color;
+    color: $--table-header-font-color;
     font-weight: 500;
 
     &.is-group {

+ 1 - 1
packages/theme-chalk/src/tag.scss

@@ -33,7 +33,7 @@
     }
 
     &:hover {
-      background-color: $--tag-background-color;
+      background-color: $--tag-default-hover-background-color;
       color: $--color-white;
     }
   }