index.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <div>
  3. <el-button
  4. round
  5. type="primary"
  6. size="mini"
  7. style="background: #66b1ff;border-color: #66b1ff"
  8. @click.stop="showConfigurator"
  9. >{{getActionDisplayName("theme-editor")}}</el-button>
  10. <transition name="fade">
  11. <div v-show="visible" class="configurator" ref="configurator">
  12. <div
  13. class="main-configurator"
  14. ref="mainConfigurator"
  15. >
  16. <div v-if="currentConfig">
  17. <main-panel
  18. :currentConfig="currentConfig"
  19. :defaultConfig="defaultConfig"
  20. :userConfig="userConfig"
  21. :globalValue="globalValue"
  22. @onChange="userConfigChange"
  23. ></main-panel>
  24. </div>
  25. <div v-if="init && !currentConfig" class="no-config">
  26. <img src="../../assets/images/theme-no-config.png" alt>
  27. <span v-if="pageCouldEdit">{{getActionDisplayName("no-config")}}</span>
  28. <span v-else>{{getActionDisplayName("no-need-config")}}</span>
  29. </div>
  30. <download-area></download-area>
  31. </div>
  32. </div>
  33. </transition>
  34. </div>
  35. </template>
  36. <style>
  37. .configurator {
  38. height: 100%;
  39. width: 24%;
  40. max-width: 400px;
  41. position: fixed;
  42. overflow-y: auto;
  43. top: 79px;
  44. right: 0;
  45. bottom: 0;
  46. background: #fff;
  47. color: #666;
  48. line-height: 24px;
  49. padding-right: 1%;
  50. }
  51. .configurator .main-configurator {
  52. height: 100%;
  53. overflow: auto;
  54. background: #F5F7FA;
  55. border: 1px solid #EBEEF5;
  56. box-shadow: 0 2px 10px 0 rgba(0,0,0,0.06);
  57. border-radius: 8px;
  58. width: 97%;
  59. box-sizing: border-box;
  60. margin: 0 .5% 0 5%;
  61. }
  62. .no-config {
  63. margin-top: 130px;
  64. text-align: center;
  65. img {
  66. width: 50%;
  67. display: block;
  68. margin: 0 auto;
  69. }
  70. span {
  71. margin-top: 10px;
  72. display: block;
  73. color: #909399;
  74. font-size: 14px;
  75. }
  76. }
  77. .fade-enter,.fade-leave-to {
  78. transform:translateX(100%);
  79. }
  80. .fade-enter-active ,.fade-leave-active {
  81. transition:all 0.3s ease;
  82. }
  83. @media (min-width: 1600px) {
  84. .configurator {
  85. padding-right: calc((100% - 1600px) / 2);
  86. }
  87. }
  88. </style>
  89. <script>
  90. import bus from '../../bus';
  91. import { getVars, updateVars } from './utils/api.js';
  92. import mainPanel from './main';
  93. import {
  94. filterConfigType,
  95. filterGlobalValue,
  96. updateDomHeadStyle,
  97. getActionDisplayName
  98. } from './utils/utils.js';
  99. import DocStyle from './docStyle';
  100. import Loading from './loading';
  101. import Shortcut from './shortcut';
  102. import DownloadArea from './download';
  103. const ELEMENT_THEME_USER_CONFIG = 'ELEMENT_THEME_USER_CONFIG';
  104. const DEFAULT_USER_CONFIG = {
  105. global: {},
  106. local: {}
  107. };
  108. export default {
  109. components: {
  110. mainPanel,
  111. DownloadArea
  112. },
  113. data() {
  114. return {
  115. init: false,
  116. visible: false,
  117. defaultConfig: null,
  118. currentConfig: null,
  119. userConfig: {
  120. global: {},
  121. local: {}
  122. },
  123. lastApply: 0,
  124. userConfigHistory: [],
  125. userConfigRedoHistory: [],
  126. hasLocalConfig: false
  127. };
  128. },
  129. mixins: [DocStyle, Loading, Shortcut],
  130. computed: {
  131. globalValue() {
  132. return filterGlobalValue(this.defaultConfig, this.userConfig);
  133. },
  134. pageCouldEdit() {
  135. const noNeedEdit = ['installation', 'quickstart', 'i18n', 'custom-theme', 'transition'];
  136. const lastPath = this.$route.path.split('/').slice(-1).pop();
  137. return noNeedEdit.indexOf(lastPath) < 0;
  138. }
  139. },
  140. mounted() {
  141. this.checkLocalThemeConfig();
  142. },
  143. methods: {
  144. getActionDisplayName(key) {
  145. return getActionDisplayName(key);
  146. },
  147. showConfigurator() {
  148. this.visible = !this.visible;
  149. this.visible ? this.enableShortcut() : this.disableShortcut();
  150. bus.$emit('user-theme-config-visible', this.visible);
  151. window.userThemeConfigVisible = Boolean(this.visible);
  152. if (this.init) return;
  153. this.$nextTick(() => {
  154. const loading = this.$loading({
  155. target: this.$refs.configurator
  156. });
  157. let defaultConfig;
  158. getVars()
  159. .then((res) => {
  160. defaultConfig = res;
  161. ga('send', 'event', 'ThemeConfigurator', 'Init');
  162. })
  163. .catch((err) => {
  164. this.onError(err);
  165. })
  166. .then(() => {
  167. setTimeout(() => {
  168. if (defaultConfig) {
  169. this.defaultConfig = defaultConfig;
  170. this.filterCurrentConfig();
  171. this.init = true;
  172. this.checkLocalThemeConfig();
  173. }
  174. loading.close();
  175. }, 300); // action after transition
  176. });
  177. });
  178. },
  179. checkLocalThemeConfig() {
  180. try {
  181. if (this.hasLocalConfig) {
  182. this.$message(getActionDisplayName('load-local-theme-config'));
  183. this.onAction();
  184. return;
  185. }
  186. const config = JSON.parse(localStorage.getItem(ELEMENT_THEME_USER_CONFIG));
  187. if (config && config.global) {
  188. this.userConfig = config;
  189. this.hasLocalConfig = true;
  190. this.showConfigurator();
  191. }
  192. } catch (e) {
  193. // bad local config
  194. }
  195. },
  196. filterCurrentConfig() {
  197. this.currentConfig = this.defaultConfig.find((config) => {
  198. return config.name === this.$route.path.split('/').pop().toLowerCase().replace('-', '');
  199. });
  200. },
  201. userConfigChange(e) {
  202. this.userConfigHistory.push(JSON.stringify(this.userConfig));
  203. this.userConfigRedoHistory = [];
  204. this.$set(this.userConfig[filterConfigType(this.currentConfig.name)], e.key, e.value);
  205. this.onAction();
  206. },
  207. applyStyle(res, time) {
  208. if (time < this.lastApply) return;
  209. this.updateDocs(() => {
  210. updateDomHeadStyle('chalk-style', res);
  211. });
  212. this.lastApply = time;
  213. },
  214. onDownload() {
  215. return updateVars(Object.assign({}, this.userConfig, {download: true}), (xhr) => {
  216. xhr.responseType = 'blob';
  217. });
  218. },
  219. onReset() {
  220. this.userConfig = {
  221. global: {},
  222. local: {}
  223. };
  224. this.onAction();
  225. },
  226. onAction() {
  227. this.triggerComponentLoading(true);
  228. const time = +new Date();
  229. const currentConfigString = JSON.stringify(this.userConfig);
  230. if (JSON.stringify(DEFAULT_USER_CONFIG) === currentConfigString) {
  231. localStorage.removeItem(ELEMENT_THEME_USER_CONFIG);
  232. } else {
  233. localStorage.setItem(ELEMENT_THEME_USER_CONFIG, currentConfigString);
  234. }
  235. updateVars(this.userConfig)
  236. .then((res) => {
  237. this.applyStyle(res, time);
  238. })
  239. .catch((err) => {
  240. this.onError(err);
  241. })
  242. .then(() => {
  243. this.triggerComponentLoading(false);
  244. });
  245. },
  246. onError(err) {
  247. let message;
  248. try {
  249. message = JSON.parse(err).message;
  250. } catch (e) {
  251. message = err;
  252. }
  253. this.$message.error(message);
  254. },
  255. triggerComponentLoading(val) {
  256. bus.$emit('user-theme-config-loading', val);
  257. },
  258. updateDocs(cb) {
  259. window.userThemeConfig = JSON.parse(JSON.stringify(this.userConfig));
  260. bus.$emit('user-theme-config-update', this.userConfig);
  261. this.updateDocStyle(this.userConfig, cb);
  262. },
  263. undo() {
  264. if (this.userConfigHistory.length > 0) {
  265. this.userConfigRedoHistory.push(JSON.stringify(this.userConfig));
  266. this.userConfig = JSON.parse(this.userConfigHistory.pop());
  267. this.onAction();
  268. }
  269. },
  270. redo() {
  271. if (this.userConfigRedoHistory.length > 0) {
  272. this.userConfigHistory.push(JSON.stringify(this.userConfig));
  273. this.userConfig = JSON.parse(this.userConfigRedoHistory.shift());
  274. this.onAction();
  275. }
  276. }
  277. },
  278. watch: {
  279. '$route.path'() {
  280. this.defaultConfig && this.filterCurrentConfig();
  281. if (!this.$refs.mainConfigurator) return;
  282. this.$nextTick(() => {
  283. this.$refs.mainConfigurator.scrollTop = 0;
  284. });
  285. }
  286. }
  287. };
  288. </script>