async function updateVariable() { let curTheme = store.getUserThemeByNameFromStore(this.$store.state.editingTheme, true, true) try { let res = await api.updateVariable(curTheme.theme) this.replaceTheme(res.data) } catch (error) { console.log(error) } }
参数为当前主题修改的变量数据,后端编译完后返回css字符串,需要动态插入到head标签里:
function replaceTheme(data) { let id = 'HUI_PREVIEW_THEME' let el = document.querySelector('#' + id) if (el) { el.innerHTML = data } else { el = document.createElement('style') el.innerHTML = data el.id = id document.head.appendChild(el) } }
这样就达到了修改变量后实时预览的效果,下载主题也是类似,把当前编辑的主题的数据发送给后端编译完后生成压缩包进行下载。
下载:因为要发送主题变量进行编译下载,所以不能使用get方法,但使用post方法进行下载比较麻烦,所以为了简单起见,下载操作实际是在浏览器端做的。
function downloadTheme(data) { axios({ url: '/api/v1/download', method: 'post', responseType: 'blob', // important data }).then((response) => { const url = window.URL.createObjectURL(new Blob([response.data])) const link = document.createElement('a') link.href = url link.setAttribute('download', 'theme.zip') link.click() }) }
至此,主流程已经跑通,接下来是一些提升体验的功能。
1.重置功能:重置理应是重置到某个主题复制来源的那个主题的,但是其实必要性也不是特别大,所以就简单做,直接把当前主题的配置变量清空,即theme.common={},同时需要重新请求变量数据及请求编译。
2.前进回退功能:前进回退功能说白了就是把每一步操作的数据都克隆一份并存到一个数组里,然后设置一个指针,比如index,指向当前所在的位置,前进就是index++,后退就是index--,然后取出对应数组里的数据替换当前的数据。对于本项目,需要存两个东西,一个是主题数据,一个是变量数据。可以通过对象形式存到一个数组里,也可以向本项目一样搞两个数组。
具体实现:
1.先把初始的主题数据拷贝一份扔进历史数组themeHistoryList里,请求到变量数据后扔进variableHistoryList数组里
2.每次修改后把修改后的变量数据和主题数据都复制一份扔进去,同时指针historyIndex加1
3.根据前进还是回退来设置historyIndex的值,同时取出对应位置的主题和变量数据替换当前的数据,然后请求编译
需要注意的是在重置和返回主题列表页面时要复位themeHistoryList、variableHistoryList、historyIndex
3.颜色预览组件优化
因为颜色预览组件是需要显示当前颜色和颜色值的,那么就会有一个问题,字体颜色不能写死,否则如果字体写死白色,那么如果这个变量的颜色值又修改成白色,那么将一片白色,啥也看不见,所以需要动态判断是用黑色还是白色,有兴趣详细了解判断算法可阅读:
function const getContrastYIQ = (hexcolor) => { hexcolor = colorToHEX(hexcolor).substring(1) let r = parseInt(hexcolor.substr(0, 2), 16) let g = parseInt(hexcolor.substr(2, 2), 16) let b = parseInt(hexcolor.substr(4, 2), 16) let yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000 return (yiq >= 128) ? 'black' : 'white' }
colorToHEX是一个将各种类型的颜色值都转为十六进制颜色的函数。
4.一些小细节
logo、导航、返回按钮、返回顶部等小控件随当前编辑中的主题色进行变色。
到这里前端部分就结束了,让我们喝口水继续。
后端部分
后端用的是nodejs及eggjs框架,对eggjs不熟悉的话可先阅读一下文档: eggjs.org/zh-cn/ ,后端部分比较简单,先看路由:
module.exports = app => { const { router, controller } = app // 获取官方主题列表 router.get(`${BASE_URL}/getOfficialThemes`, controller.index.getOfficialThemes) // 返回变量数据 router.get(`${BASE_URL}/getVariable`, controller.index.getVariable) // 编译scss router.post(`${BASE_URL}/updateVariable`, controller.index.updateVariable) // 下载 router.post(`${BASE_URL}/download`, controller.index.download) }
目前官方主题列表和变量数据都是一个写死的json文件。所以核心只有两部分,编译scss和下载,先看编译。
编译scss