├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── core ├── .babelrc ├── .gitignore ├── README.md ├── dist │ ├── config │ │ └── settings.js │ ├── index.js │ └── utils │ │ ├── array.js │ │ ├── event.js │ │ ├── jwt.js │ │ ├── number.js │ │ ├── storage.js │ │ ├── time.js │ │ ├── type.js │ │ ├── utils.js │ │ └── validate.js ├── package.json ├── src │ ├── config │ │ └── settings.js │ ├── index.js │ └── utils │ │ ├── array.js │ │ ├── event.js │ │ ├── jwt.js │ │ ├── number.js │ │ ├── storage.js │ │ ├── time.js │ │ ├── type.js │ │ ├── utils.js │ │ └── validate.js └── yarn.lock ├── http ├── .babelrc ├── README.md ├── dist │ ├── config │ │ └── settings.js │ ├── index.js │ └── utils │ │ ├── fetch.js │ │ ├── http.js │ │ └── mock.js ├── package.json ├── src │ ├── config │ │ └── settings.js │ ├── index.js │ └── utils │ │ ├── fetch.js │ │ ├── http.js │ │ └── mock.js └── yarn.lock ├── ui ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ ├── css │ │ │ ├── clear.css │ │ │ ├── variables.css │ │ │ ├── variables.min.css │ │ │ └── variables.scss │ │ └── logo.png │ ├── main.js │ ├── packages │ │ ├── add │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── container │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── context-menu │ │ │ ├── components │ │ │ │ └── contentmenuList │ │ │ │ │ └── index.vue │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── fade-in │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── index.js │ │ ├── input │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── scroll │ │ │ ├── index.js │ │ │ └── index.vue │ │ ├── table-dynamic │ │ │ ├── index.js │ │ │ └── index.vue │ │ └── table │ │ │ ├── index.js │ │ │ └── index.vue │ └── plugins │ │ ├── element.js │ │ └── wlui.js ├── vue.config.js └── yarn.lock └── vue ├── .babelrc ├── README.md ├── dist ├── config │ ├── settings.js │ └── settins.js ├── index.js └── utils │ ├── auth │ ├── async-routes.js │ ├── index.js │ ├── map-router.js │ ├── router-guard.js │ └── va-auth.js │ ├── directives │ ├── index.js │ └── v-auth.js │ ├── filters │ ├── index.js │ └── time.js │ └── init │ └── render.js ├── package.json ├── src ├── config │ └── settings.js ├── index.js └── utils │ ├── auth │ ├── async-routes.js │ ├── index.js │ ├── map-router.js │ ├── router-guard.js │ └── va-auth.js │ ├── directives │ ├── index.js │ └── v-auth.js │ ├── filters │ ├── index.js │ └── time.js │ └── init │ └── render.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin-debug/ 3 | bin-release/ 4 | [Oo]bj/ 5 | [Bb]in/ 6 | 7 | # Other files and folders 8 | .settings/ 9 | node_modules 10 | node_modules/ 11 | core/node_modules/ 12 | core/node_modules 13 | http/node_modules/ 14 | http/node_modules 15 | ui/node_modules/ 16 | ui/node_modules 17 | vue/node_modules/ 18 | vue/node_modules 19 | 20 | # Executables 21 | *.swf 22 | *.air 23 | *.ipa 24 | *.apk 25 | 26 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 27 | # should NOT be excluded as they contain compiler settings and other important 28 | # information for Eclipse / Flash Builder. 29 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "svn.ignoreMissingSvnWarning": true, 3 | "git.ignoreLimitWarning": true 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wl 2 | 项目架构方案之路 3 | 4 | ## List 5 | 1. wl-core 框架无关核心层级工具类 6 | 7 | 2. wl-http 基于axios封装的请求类 8 | 9 | 3. wl-baseui 基础ui组件 10 | 11 | 4. wl-vue vue通用基础封装 12 | 13 | > 下列说明中 export 内的方法名,均可 import { xx } from 'wl-core' 导入使用 14 | 15 | ## wl-core 16 | 17 | 无框架依赖的核心层及工具层封装 18 | 19 | ### array 20 | 提供有关数组的操作方法 Function 21 | ```js 22 | export { 23 | valInDeep, // 从树形数据中递归筛选目标值 24 | flattenDeep, // 将树形数据向下递归为一维数组 25 | flattenDeepParents, // 将树形数据向上将此支线递归为一维数组 26 | regDeepParents, // 根据条件递归祖先元素 27 | arrayToTree, // 将数组转化成树结构 28 | patchTreeChain, // 如果数据里缺少树枝节点,则根据parents和自增长id补全整条树链,输出数据调用上部arrToTree函数组装成完整的树 29 | locationAfterDelete, // 数组删除后重新定位 30 | splicParentsUntil, // 从坐标值拼接指定字段到祖先元素 31 | intersectionBy, // 根据数组2内的元素,通过match字段匹配数组1内的完整内容组成的数据 32 | deepClone, // 深拷贝 33 | getMax, // 筛选出数组中最大值 34 | getMin, // 筛选出数组中最小值 35 | autoPositionAfterDelete, // 重写的一维数组删除数据后自动定位 36 | }; 37 | ``` 38 | 39 | ### event 40 | 提供对浏览器事件的封装 Function 41 | ```js 42 | export { 43 | throttle, // 节流函数 44 | debounce // 防抖函数 45 | } 46 | ``` 47 | 48 | ### Storage 49 | 提供对Storage操作的类,包括local和session两种 Class 50 | ```js 51 | export { 52 | Storage 53 | } 54 | ``` 55 | 均为静态方法,可直接调用。它包括有: 56 | ```js 57 | Storage.set('key','value','type[local|session]') // 将键值存入本地存储 58 | Storage.get('key','type[local|session]') // 根据key取本地存储数据 59 | Storage.remove('key','type[local|session]') // 根据key删除本地存储数据 60 | Storage.had('key','type[local|session]') // 根据key查询本地存储中是否存在key的实例 61 | Storage.clear('type[local|session]') // 清空本地存储 62 | Storage.count('type[local|session]') // 获取存储库里存储实例个数 63 | ``` 64 | 65 | ### Time 66 | 提供对时间操作的类 Class 67 | ```js 68 | export { 69 | Time 70 | } 71 | ``` 72 | 主要用于对时间进行两次及以上操作的情况!对于时间格式化,在wl-vue中提供了time过滤器 73 | 1. 使用需要先实例化,全部方法如下: 74 | ```js 75 | const timer = new Time('2020-02-14', 'YYYY/MM/DD'); 76 | 77 | timer.dayjs(date) // 将时间格式转化为dayjs格式(实例化时已经自动调用了此方法) 78 | timer.format(format) // 格式化时间 79 | timer.add(num, unit) // 时间加上数量,要加的时间数量,数量的时间单位 80 | timer.subtract(num, unit) // 时间减去数量,要加的时间数量,数量的时间单位 81 | timer.isBefore(date, unit) // 时间是否在date之前,要比较的时间,时间的单位 82 | timer.diff(date, unit) // 计算时差,要减的日期,时间计算时间的单位 83 | ``` 84 | 2. 静态方法可直接调用 85 | ```js 86 | Time.quickFormat(new Date(), 'YYYY/MM/DD') 87 | Time.init(new Date()) 88 | ``` 89 | 90 | ### Type 91 | 提供判断数据类型的类 Class 92 | ```js 93 | export { 94 | DataType 95 | } 96 | ``` 97 | 均为静态方法,可直接调用。它包括有: 98 | ```js 99 | DataType.isObject(data) // 是否对象 100 | DataType.isEmptyObject(data) // 是否空对象 101 | DataType.isArray(data) // 是否数组 102 | DataType.isEmptyArray(data) // 是否空数组 103 | ``` 104 | 105 | ### validate 106 | 数据验证 107 | ```js 108 | export { 109 | vaPhone, // el手机格式校验 110 | regPhone, // 正则手机格式校验 111 | isNum, // 验证数字 112 | isInteger, // 验证整数 113 | validate, // 整体表单验证 114 | } 115 | 116 | ``` 117 | ### WlNumber 118 | 提供精确数字计算的类 Class 119 | ```js 120 | export { 121 | WlNumber 122 | } 123 | ``` 124 | 125 | ```js 126 | const beginNum = new WlNumber(1); 127 | // 以下返回big数据,可以使用toString()或to.Fixed()转化 128 | beginNum.plus(2) // 加 129 | beginNum.minus(2) // 减 130 | beginNum.times(2) // 乘 131 | beginNum.div(2) // 除以 132 | beginNum.mod(2) // 取余 133 | beginNum.abs() // 取绝对值 134 | // 以下返回Boolean值 135 | beginNum.gt(2) // 大于 136 | beginNum.gte(2) // 大于等于 137 | beginNum.lt(2) // 小于 138 | beginNum.lte(2) // 小于等于 139 | ``` 140 | 静态方法 141 | ```js 142 | WlNumber.toNumber(val) // 返回转化后的bumber型值,不可转化的返回0 143 | ``` 144 | 145 | ## wl-http 146 | 147 | 通信层封装 148 | 149 | ### 1. 实例化http:__http__.js 150 | 151 | ```js 152 | import Http from "wl-http" 153 | 154 | const options = { 155 | axios: null, // 是否使用外部axios实例,无特殊情况禁止使用 156 | options: { 157 | retry: 2, //Number 请求失败自动重连次数 默认2 158 | retryDelay: 1000, // 请求失败自动重连时间间隔 默认1000ms 159 | withCredentials: true, // Boolean 开启请求跨域 默认true 160 | headers: { 161 | "Content-Type": "application/json;charset=UTF-8" 162 | }, // Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 163 | timeout: 5000, // Number 请求超时时间 默认5000 164 | baseURL: '' // String 请求地址前缀 默认'' 165 | expand: {} // 其他需要扩展的配置项 other axios 支持的config字段 166 | }, // 以上字段均有默认值,正常情况下 此options 无需提供 167 | requestInterceptorSuccessCb:()=>{}, // 非必填 请求拦截器成功回调,必须返回一个config对象 168 | responseInterceptorSuccessCb:()=>{}, // 非必填 响应拦截器成功回调,必须返回一个response对象 169 | responseInterceptorErrorCb:()=>{}, // 非必填 响应拦截器失败回调,必须返回一个response对象 170 | }, // 实例化http可选配置项 均为非必填项 171 | 172 | const http = new Http(options); 173 | 174 | export default http; 175 | ``` 176 | 177 | ### 2. 在具体的api文件中使用:user.js 178 | ```js 179 | import http from "__http__.js" 180 | 181 | const getMenuApi = (params) => http.get({ 182 | url:'', 183 | params 184 | }) 185 | 186 | const addUserApi = (data) => http.post({ 187 | url:'', 188 | data 189 | }) 190 | 191 | export { 192 | getMenuApi, 193 | addUserApi 194 | } 195 | ``` 196 | 197 | ### 3. 在vue文件中最终调用 198 | ```js 199 | import {addUserApi} from "@/api/user.js" 200 | 201 | methods: { 202 | addUser(){ 203 | const _data = { 204 | name: 'weilan', 205 | des: '前端架构师' 206 | } 207 | addUserApi(_data).then( ({data})=>{ 208 | ... 209 | }) 210 | } 211 | } 212 | ``` 213 | 214 | ### 4. Http类提供 get,post,all,del,put,patch 共6中方法 215 | 216 | ## wl-baseui 217 | 218 | 基础ui库,不包含复杂组件,完整组件库见wlui 219 | 220 | ```js 221 | import WlBaseUi from "wl-base-ui" 222 | 223 | Vue.use(WlBaseUi) 224 | 225 | // 下为详细列表 226 | { 227 | WlAdd, // 防抖按钮 228 | WlContainer, // 页面视图盒子 229 | WlContextMenu, // 右键扩展组件 230 | WlFadeIn, // 右侧滑入组件 231 | WlInput, // 带校验的输入框组件 232 | WlScroll, // 滚动条美化组件 233 | WlTable, // 表格组件 234 | WlTableDynamic, // 动态表格组件 235 | } 236 | ``` 237 | 238 | ## wl-vue 239 | 240 | vue基础层封装 241 | 242 | ### 1. 使用render函数创建vue实例:render.js 243 | 244 | ```js 245 | import { render } from "wl-vue" 246 | import App from "./App.vue"; 247 | import router from "./router"; 248 | import store from "./store"; 249 | 250 | const options = { 251 | root: App, // 必须 当前应用的根组件 一般是app.vue 252 | router: router, // 必须 router实例 253 | store: store, // 必须 store实例 254 | options: { // options 实例化vue配置项 下为详细注解 255 | fastclick: false, // 默认false 是否启用移动端快速点击插件 256 | cookie: false, // 默认false 是否启用vue-cookie操作插件 257 | lazyOptions: {}, // 默认null 启用图片VueLazyLoad懒加载插件时的配置项, 不传表示不启用 258 | filters: [], // 默认[] 过滤器数组 格式为 {name:"", rule: ()=>{}} 内置有date,dateTime时间格式化过滤器 259 | directives: [], // 默认[] 指令数组 格式为 {name:"", rule: ()=>{}} 内置有v-auth鉴权指令 260 | plugins: [], // 默认[] 插件数组 [wlui, el-input] 可以直接Vue.use()的插件数组 261 | fncBeforeVue: ()=>{}, // 实例化vue前可执行的回调函数 fncBeforeVue(vue){... 你的逻辑} 262 | auth: true, // 默认true 是否需要鉴权系统,如果不需要,后续参数无需再传 263 | }, 264 | routeOptions:{ // 路由守卫配置项 用于前端鉴权及异步路由 下为详细注解 265 | tokenKey: 'token', // 存储在local中的token的key 266 | dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 默认'app/setToken' 267 | dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 默认'menu/setMenu' 268 | dispatchSetMenuList: 'menu/setMenuList', // store设置一维菜单的actions命名空间 269 | dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 默认'menu/setPermissions' 270 | pathLogin: '/login', // 登录页的 router path 默认'/login' 271 | pathLogged: '/index', // 已登录后 再进登录页时自动重定向的 router path 默认'/index' 272 | apiFn: ()=>{}, // **必须** 获取菜单数据的api函数,返回值为一个promise 273 | vaJwtExpiredFn: ()=>{}, // 自定义校验jwt是否过期的函数 默认为比较jwt携带过期时间与当前时间比较,单位秒,传入表示自定义过期规则 274 | }, 275 | menuOptions:{ // 菜单数据解析为路由数据配置项 下为详细注解 276 | url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 277 | name: 'routerName', // 命名路由 278 | meta: 'meta', // 路由元数据 279 | children: 'children', // 子菜单字段 280 | permissions: 'permissions', // 按钮权限字段 281 | path404: 'error/404' // 404路径, 282 | mapPathFn: (item) => {} // 路由映射文件路径方法 必填 283 | }, 284 | nextRoutes:[] // 需要登录后插入的、非后台返回的路由列表 默认[] 285 | } 286 | const mount = "#app"; // 非必选 vue挂载dom 默认为#app 287 | 288 | // 实例化vue 289 | const vueRender = () => render(options, mount); 290 | 291 | export default vueRender; 292 | ``` 293 | 294 | ### 2. 在main.js内实例化vue 295 | 下面是 最少 & 必须 的配置项: 296 | ```js 297 | import { render } from "wl-vue" 298 | import App from "./App.vue"; 299 | import store from "./store"; 300 | import router from "./router"; 301 | import nextRoutes from "./router/next-router" 302 | import routeMap from "./router/map-router" 303 | import { getMenuApi } from "./api/menu" 304 | 305 | // 声明鉴权需要的参数 306 | const routeOptions = { 307 | apiFn: getMenuApi 308 | } 309 | 310 | // 声明菜单解析为路由所需参数 311 | const menuOptions = { 312 | mapPathFn: (item) => routeMap(item.url) 313 | } 314 | 315 | // 导出手动实例化vue函数 316 | const vueRender = () => render({ root: App, router, store, routeOptions, nextRoutes, menuOptions }); 317 | 318 | export default vueRender; 319 | ``` 320 | 321 | ### 3. 注意事项 322 | 323 | 1. 注意:菜单数据映射为路由数据默认你有一个layout路由且默认重定向至`/index`如下, 其他菜单路由均挂载在layout的children内 324 | ```js 325 | let userRouter = { 326 | path: "/layout", 327 | name: "layout", 328 | component: () => import('@/views/layout/index.vue'), 329 | redirect: '/index', 330 | children: [] 331 | }; 332 | ``` 333 | 2. 注意:路由的映射规则如下: 334 | ```js 335 | component: () => import(`@/views${url}/index.vue`) 336 | ``` 337 | 3. 注意:因为在封装里路由映射文件会找不到,因此需要在每个项目里传入一个映射方法 338 | src/router/map-router.js 339 | ```js 340 | module.exports = path => () => import(`@/views${path}/index.vue`); 341 | ``` 342 | 343 | 4. 注意:路由守卫检查store中是否已经存在用户菜单指定为: 344 | ```js 345 | store.getters.menu 346 | ``` 347 | 348 | ### 4. 其他方法 349 | ```js 350 | import { VaJwt } from "wl-vue" 351 | 352 | // VaJwt是一个验证和解析未加密jwt的类,提供了许多静态方法 353 | // console.log(VaJwt) 打印查看 354 | // 静态方法无需实例化可直接使用,例: 355 | const payload = VaJwt.payloadAtob(jwt); 356 | // 解析jwt中有效载荷内的数据 357 | ``` 358 | -------------------------------------------------------------------------------- /core/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ], 5 | "plugins": [ 6 | "@babel/plugin-proposal-class-properties" 7 | ] 8 | } -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin-debug/ 3 | bin-release/ 4 | [Oo]bj/ 5 | [Bb]in/ 6 | 7 | src 8 | .babelrc 9 | yarn.lock 10 | 11 | # Other files and folders 12 | .settings/ 13 | node_modules 14 | node_modules/ 15 | core/node_modules/ 16 | core/node_modules 17 | http/node_modules/ 18 | http/node_modules 19 | ui/node_modules/ 20 | ui/node_modules 21 | vue/node_modules/ 22 | vue/node_modules 23 | 24 | # Executables 25 | *.swf 26 | *.air 27 | *.ipa 28 | *.apk 29 | 30 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 31 | # should NOT be excluded as they contain compiler settings and other important 32 | # information for Eclipse / Flash Builder. 33 | -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | # wl-core 2 | 3 | 无框架依赖的核心层及工具层封装 4 | 下列说明中 export 内的方法名,均可 import { xx } from 'wl-core' 导入使用 5 | 6 | ### array 7 | 提供有关数组的操作方法 Function 8 | ```js 9 | export { 10 | valInDeep, // 从树形数据中递归筛选目标值 function (arr = [], val, id = "Id", childs = "Children") 11 | flattenDeep, // 将树形数据向下递归为一维数组 function (arr = [], childs = "Children") 12 | flattenDeepParents, // 将树形数据向上将此支线递归为一维数组 function (arr, parent) 13 | regDeepParents, // 根据条件递归祖先元素 function (row, parent, reg) 14 | arrayToTree, // function ( array = [], options = { id: "id", pid: "pid", children: "children" , rootPidVal:null}) rootPidVal应该是个数组格式 15 | patchTreeChain, // 如果数据里缺少树枝节点,则根据parents和自增长id补全整条树链,输出数据调用上部arrToTree函数组装成完整的树 function patchTreeChain( data, sourceData, options = { Id: "Id", ParentId: "ParentId", Parents: "Parents", IdentityId: "IdentityId", root: "00000000-0000-0000-0000-000000000000" } ) 16 | locationAfterDelete, // 数组删除后重新定位 function (data, delId, actId, useTree = false) 17 | splicParentsUntil, // 从坐标值拼接指定字段到祖先元素 18 | intersectionBy, // 根据数组2内的元素,通过match字段匹配数组1内的完整内容组成的数据 19 | deepClone, // 深拷贝 20 | getMax, // 筛选出数组中最大值 21 | getMin, // 筛选出数组中最小值 22 | autoPositionAfterDelete, // 重写的一维数组删除数据后自动定位 23 | }; 24 | ``` 25 | 26 | ### event 27 | 提供对浏览器事件的封装 Function 28 | ```js 29 | export { 30 | throttle, // 节流函数 31 | debounce // 防抖函数 32 | } 33 | ``` 34 | 35 | ### Storage 36 | 提供对Storage操作的类,包括local和session两种 Class 37 | ```js 38 | export { 39 | Storage 40 | } 41 | ``` 42 | 均为静态方法,可直接调用。它包括有: 43 | ```js 44 | Storage.set('key','value','type[local|session]') // 将键值存入本地存储 45 | Storage.get('key','type[local|session]') // 根据key取本地存储数据 46 | Storage.remove('key','type[local|session]') // 根据key删除本地存储数据 47 | Storage.had('key','type[local|session]') // 根据key查询本地存储中是否存在key的实例 48 | Storage.clear('type[local|session]') // 清空本地存储 49 | Storage.count('type[local|session]') // 获取存储库里存储实例个数 50 | ``` 51 | 52 | ### Time 53 | 提供对时间操作的类 Class 54 | ```js 55 | export { 56 | Time 57 | } 58 | ``` 59 | 主要用于对时间进行两次及以上操作的情况!对于时间格式化,在wl-vue中提供了time过滤器 60 | 1. 使用需要先实例化,全部方法如下: 61 | ```js 62 | const timer = new Time('2020-02-14', 'YYYY/MM/DD'); 63 | 64 | timer.dayjs(date) // 将时间格式转化为dayjs格式(实例化时已经自动调用了此方法) 65 | timer.format(format) // 格式化时间 66 | timer.add(num, unit) // 时间加上数量,要加的时间数量,数量的时间单位 67 | timer.subtract(num, unit) // 时间减去数量,要加的时间数量,数量的时间单位 68 | timer.isBefore(date, unit) // 时间是否在date之前,要比较的时间,时间的单位 69 | timer.diff(date, unit) // 计算时差,要减的日期,时间计算时间的单位 70 | ``` 71 | 2. 静态方法可直接调用 72 | ```js 73 | Time.quickFormat(new Date(), 'YYYY/MM/DD') 74 | Time.init(new Date()) 75 | ``` 76 | 77 | ### Type 78 | 提供判断数据类型的类 Class 79 | ```js 80 | export { 81 | DataType 82 | } 83 | ``` 84 | 均为静态方法,可直接调用。它包括有: 85 | ```js 86 | DataType.isObject(data) // 是否对象 87 | DataType.isEmptyObject(data) // 是否空对象 88 | DataType.isArray(data) // 是否数组 89 | DataType.isEmptyArray(data) // 是否空数组 90 | ``` 91 | 92 | ### validate 93 | 数据验证 94 | ```js 95 | export { 96 | vaPhone, // el手机格式校验 97 | regPhone, // 正则手机格式校验 98 | isNum, // 验证数字 99 | isInteger, // 验证整数 100 | validate, // 整体表单验证 101 | } 102 | ``` 103 | ### WlNumber 104 | 提供精确数字计算的类 Class 105 | ```js 106 | export { 107 | WlNumber 108 | } 109 | ``` 110 | 111 | ```js 112 | const beginNum = new WlNumber(1); 113 | // 以下返回big数据,可以使用toString()或to.Fixed()转化 114 | beginNum.plus(2) // 加 115 | beginNum.minus(2) // 减 116 | beginNum.times(2) // 乘 117 | beginNum.div(2) // 除以 118 | beginNum.mod(2) // 取余 119 | beginNum.abs() // 取绝对值 120 | // 以下返回Boolean值 121 | beginNum.gt(2) // 大于 122 | beginNum.gte(2) // 大于等于 123 | beginNum.lt(2) // 小于 124 | beginNum.lte(2) // 小于等于 125 | ``` 126 | 静态方法 127 | ```js 128 | WlNumber.toNumber(val) // 返回转化后的bumber型值,不可转化的返回0 129 | ``` 130 | 131 | ### 常用JWT校验及解析方法 132 | ```js 133 | import { VaJwt } from "wl-core" 134 | 135 | // VaJwt是一个验证和解析未加密jwt的类,提供了许多静态方法 136 | // console.log(VaJwt) 打印查看 137 | // 静态方法无需实例化可直接使用,例: 138 | const payload = VaJwt.payloadAtob(jwt); 139 | // 解析jwt中有效载荷内的数据 140 | ``` 141 | 142 | ## 版本更新说明 143 | > v1.1.9 修复数组方法arrayToTree的一个默认参数错误 -------------------------------------------------------------------------------- /core/dist/config/settings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports._timeUnit = exports._storageType = void 0; 7 | // 基础配置项 8 | // 存储类型 9 | var _storageType = { 10 | Local: 'local', 11 | Session: 'session' 12 | }; // 时间单位 大小写不敏感 支持负数和缩写 13 | 14 | exports._storageType = _storageType; 15 | var _timeUnit = { 16 | Year: 'year', 17 | // Y 年 18 | Quarter: 'quarter', 19 | // Q 季度 20 | Month: 'month', 21 | // M 月 22 | Week: 'week', 23 | // W 周 24 | Day: 'day', 25 | // d 天 26 | Hour: 'hour', 27 | // h 时 28 | Minute: 'minute', 29 | // m 分 30 | Second: 'second' // s 秒 31 | 32 | }; 33 | exports._timeUnit = _timeUnit; -------------------------------------------------------------------------------- /core/dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var _exportNames = { 7 | Storage: true, 8 | DataType: true, 9 | Time: true, 10 | WlNumber: true, 11 | VaJwt: true 12 | }; 13 | Object.defineProperty(exports, "Storage", { 14 | enumerable: true, 15 | get: function get() { 16 | return _storage["default"]; 17 | } 18 | }); 19 | Object.defineProperty(exports, "DataType", { 20 | enumerable: true, 21 | get: function get() { 22 | return _type["default"]; 23 | } 24 | }); 25 | Object.defineProperty(exports, "Time", { 26 | enumerable: true, 27 | get: function get() { 28 | return _time["default"]; 29 | } 30 | }); 31 | Object.defineProperty(exports, "WlNumber", { 32 | enumerable: true, 33 | get: function get() { 34 | return _number["default"]; 35 | } 36 | }); 37 | Object.defineProperty(exports, "VaJwt", { 38 | enumerable: true, 39 | get: function get() { 40 | return _jwt["default"]; 41 | } 42 | }); 43 | 44 | var _storage = _interopRequireDefault(require("./utils/storage")); 45 | 46 | var _type = _interopRequireDefault(require("./utils/type")); 47 | 48 | var _time = _interopRequireDefault(require("./utils/time")); 49 | 50 | var _number = _interopRequireDefault(require("./utils/number")); 51 | 52 | var _jwt = _interopRequireDefault(require("./utils/jwt")); 53 | 54 | var _array = require("./utils/array"); 55 | 56 | Object.keys(_array).forEach(function (key) { 57 | if (key === "default" || key === "__esModule") return; 58 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 59 | if (key in exports && exports[key] === _array[key]) return; 60 | Object.defineProperty(exports, key, { 61 | enumerable: true, 62 | get: function get() { 63 | return _array[key]; 64 | } 65 | }); 66 | }); 67 | 68 | var _validate = require("./utils/validate"); 69 | 70 | Object.keys(_validate).forEach(function (key) { 71 | if (key === "default" || key === "__esModule") return; 72 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 73 | if (key in exports && exports[key] === _validate[key]) return; 74 | Object.defineProperty(exports, key, { 75 | enumerable: true, 76 | get: function get() { 77 | return _validate[key]; 78 | } 79 | }); 80 | }); 81 | 82 | var _event = require("./utils/event"); 83 | 84 | Object.keys(_event).forEach(function (key) { 85 | if (key === "default" || key === "__esModule") return; 86 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 87 | if (key in exports && exports[key] === _event[key]) return; 88 | Object.defineProperty(exports, key, { 89 | enumerable: true, 90 | get: function get() { 91 | return _event[key]; 92 | } 93 | }); 94 | }); 95 | 96 | var _utils = require("./utils/utils"); 97 | 98 | Object.keys(_utils).forEach(function (key) { 99 | if (key === "default" || key === "__esModule") return; 100 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 101 | if (key in exports && exports[key] === _utils[key]) return; 102 | Object.defineProperty(exports, key, { 103 | enumerable: true, 104 | get: function get() { 105 | return _utils[key]; 106 | } 107 | }); 108 | }); 109 | 110 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } -------------------------------------------------------------------------------- /core/dist/utils/event.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.debounce = exports.throttle = void 0; 7 | 8 | /** 9 | * @author weilan 10 | * @description event事件库 11 | * @time 2020.04.21 12 | */ 13 | 14 | /** 15 | * @method 节流函数(500ms内只能点击一次,点击后立即触发,重复点击无效,必须等3s之后才能点击第二次) 16 | * @param {Function} {handler} 事件处理函数 17 | * @param {Number} {delay} 恢复点击的毫秒数 18 | */ 19 | var throttle = function throttle(handler, delay) { 20 | var last, deferTimer; 21 | return function () { 22 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 23 | args[_key] = arguments[_key]; 24 | } 25 | 26 | var that = this; 27 | var now = +new Date(); 28 | 29 | if (last && now < last + delay) { 30 | deferTimer && clearTimeout(deferTimer); 31 | deferTimer = setTimeout(function () { 32 | last = now; 33 | handler.apply(that, args); 34 | }, delay); 35 | } else { 36 | last = now; 37 | handler.apply(that, args); 38 | } 39 | }; 40 | }; 41 | /** 42 | * @method 防抖函数(500ms之后出结果,重复点击无效,如果重复点击了,重新计算3s时间,从点击的时刻算起,必须等待3s时间触发事件) 43 | * @param {Function} {handler} 事件处理函数 44 | * @param {Number} {delay} 恢复点击的毫秒数 45 | */ 46 | 47 | 48 | exports.throttle = throttle; 49 | 50 | var debounce = function debounce(handler) { 51 | var delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 500; 52 | var timeout; 53 | return function () { 54 | for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 55 | args[_key2] = arguments[_key2]; 56 | } 57 | 58 | // 获取函数的作用域和变量 59 | var that = this; // 每次事件被触发,都会清除当前的timer,然后重写设置超时调用 60 | 61 | timeout && clearTimeout(timeout); 62 | timeout = setTimeout(function () { 63 | handler.apply(that, args); 64 | }, delay); 65 | }; 66 | }; 67 | 68 | exports.debounce = debounce; -------------------------------------------------------------------------------- /core/dist/utils/jwt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _time2 = _interopRequireDefault(require("./time")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 15 | 16 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 17 | 18 | var VaJwt = /*#__PURE__*/function () { 19 | function VaJwt() { 20 | _classCallCheck(this, VaJwt); 21 | } 22 | 23 | _createClass(VaJwt, null, [{ 24 | key: "extractJwtPayload", 25 | 26 | /** 27 | * 截取jwt中有效载荷部分 28 | * @param {*} jwt 29 | */ 30 | value: function extractJwtPayload(jwt) { 31 | if (!jwt) throw Error('缺少jwt!'); 32 | var jwt_split = jwt.split('.'); 33 | if (jwt_split.length !== 3) throw Error('jwt格式不正确!'); 34 | return jwt_split[1]; 35 | } 36 | /** 37 | * 简单解析未特殊加密的payload部分 38 | * @param {*} jwt 39 | */ 40 | 41 | }, { 42 | key: "payloadAtob", 43 | value: function payloadAtob(jwt) { 44 | var jwt_payload = this.extractJwtPayload(jwt); 45 | var decodedData = window.atob(jwt_payload); 46 | return JSON.parse(decodedData); 47 | } 48 | /** 49 | * 检验jwt是否过期 50 | * @param {String} jwt 51 | * @param {Function} vaCb 自定义验证函数,返回Boolean true表示过期 52 | */ 53 | 54 | }, { 55 | key: "vaJwtExpired", 56 | value: function vaJwtExpired(jwt, vaCb) { 57 | var exp = this.payloadAtob(jwt).exp * 1000; 58 | 59 | if (vaCb) { 60 | return vaCb(exp); 61 | } 62 | 63 | var _time = new _time2["default"](exp); 64 | 65 | return _time.isBefore(new Date()); 66 | } 67 | /** 68 | * 监测浏览器tab页切换立即校验账号 69 | * @param {Function} cb 检测到切换后的回调函数 70 | */ 71 | 72 | }, { 73 | key: "vaVisibilityChange", 74 | value: function vaVisibilityChange(cb) { 75 | window.addEventListener("visibilitychange", cb); 76 | } 77 | }]); 78 | 79 | return VaJwt; 80 | }(); 81 | 82 | exports["default"] = VaJwt; -------------------------------------------------------------------------------- /core/dist/utils/number.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _big = _interopRequireDefault(require("big.js")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 15 | 16 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 17 | 18 | var WlNumber = /*#__PURE__*/function () { 19 | /** 20 | * @name 实例化bigjs 21 | * @param {Number|String} val 22 | */ 23 | function WlNumber(val) { 24 | _classCallCheck(this, WlNumber); 25 | 26 | this._val = new _big["default"](val); 27 | } 28 | /** 29 | * @name 加 30 | * @param {Number|String} val 要加的值 31 | */ 32 | 33 | 34 | _createClass(WlNumber, [{ 35 | key: "plus", 36 | value: function plus(val) { 37 | this._val = this._val.plus(val); 38 | return this._val; 39 | } 40 | /** 41 | * @name 减 42 | * @param {Number|String} val 要减的值 43 | */ 44 | 45 | }, { 46 | key: "minus", 47 | value: function minus(val) { 48 | this._val = this._val.minus(val); 49 | return this._val; 50 | } 51 | /** 52 | * @name 乘 53 | * @param {Number|String} val 要乘的值 54 | */ 55 | 56 | }, { 57 | key: "times", 58 | value: function times(val) { 59 | this._val = this._val.times(val); 60 | return this._val; 61 | } 62 | /** 63 | * @name 除以 64 | * @param {Number|String} val 要除以的值 65 | */ 66 | 67 | }, { 68 | key: "div", 69 | value: function div(val) { 70 | this._val = this._val.div(val); 71 | return this._val; 72 | } 73 | /** 74 | * @name 取余 75 | * @param {Number|String} val 要除以的值 76 | */ 77 | 78 | }, { 79 | key: "mod", 80 | value: function mod(val) { 81 | this._val = this._val.mod(val); 82 | return this._val; 83 | } 84 | /** 85 | * @name 取绝对值 86 | * @param {Number|String} val 取绝对值的值 87 | */ 88 | 89 | }, { 90 | key: "abs", 91 | value: function abs() { 92 | this._val = this._val.abs(); 93 | return this._val; 94 | } 95 | /** 96 | * @name 大于 97 | * @param {Number|String} val 取比较的值 98 | * @returns Boolean 99 | */ 100 | 101 | }, { 102 | key: "gt", 103 | value: function gt(val) { 104 | return this._val.gt(val); 105 | } 106 | /** 107 | * @name 大于等于 108 | * @param {Number|String} val 取比较的值 109 | * @returns Boolean 110 | */ 111 | 112 | }, { 113 | key: "gte", 114 | value: function gte(val) { 115 | return this._val.gte(val); 116 | } 117 | /** 118 | * @name 小于 119 | * @param {Number|String} val 取比较的值 120 | * @returns Boolean 121 | */ 122 | 123 | }, { 124 | key: "lt", 125 | value: function lt(val) { 126 | return this._val.lt(val); 127 | } 128 | /** 129 | * @name 小于等于 130 | * @param {Number|String} val 取比较的值 131 | * @returns Boolean 132 | */ 133 | 134 | }, { 135 | key: "lte", 136 | value: function lte(val) { 137 | return this._val.lte(val); 138 | } 139 | /** 140 | * @name 将数据转化为数字类型,如果不可转化则返回0 141 | * @param {*} val 142 | */ 143 | 144 | }], [{ 145 | key: "toNumber", 146 | value: function toNumber(val) { 147 | return Number(val) || 0; 148 | } 149 | }]); 150 | 151 | return WlNumber; 152 | }(); 153 | 154 | var _default = WlNumber; 155 | exports["default"] = _default; -------------------------------------------------------------------------------- /core/dist/utils/storage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _settings = require("../config/settings"); 9 | 10 | var _type = _interopRequireDefault(require("./type")); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 17 | 18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 19 | 20 | /** 21 | * @method 基础判断 22 | * @param storageType 存储类型 23 | * @param encryptType 加密类型 24 | */ 25 | var _core = function _core() { 26 | var storageType = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _settings._storageType.Local; 27 | var encryptType = arguments.length > 1 ? arguments[1] : undefined; 28 | 29 | if (!encryptType) { 30 | return { 31 | storage: storageType === _settings._storageType.Local ? localStorage : sessionStorage 32 | }; 33 | } 34 | }; 35 | 36 | var Storage = /*#__PURE__*/function () { 37 | function Storage() { 38 | _classCallCheck(this, Storage); 39 | } 40 | 41 | _createClass(Storage, null, [{ 42 | key: "set", 43 | 44 | /** 45 | * @method 将键值存入本地存储 46 | * @param {*} key 键 47 | * @param {*} value 值 48 | * @param {*} type 类型 默认storageType.Local 49 | * @param {*} encrypt 加密配置项 50 | */ 51 | value: function set(key, value, type, encrypt) { 52 | var _core2 = _core(type), 53 | storage = _core2.storage; 54 | 55 | var _processed_value = _type["default"].isObject(value) || _type["default"].isArray(value) ? JSON.stringify(value) : value; 56 | 57 | storage.setItem(key, _processed_value); 58 | } 59 | /** 60 | * @method 根据key取本地存储数据 61 | * @param {*} key 键 62 | * @param {*} type 类型 默认storageType.Local 63 | * @param {*} encrypt 加密配置项 64 | */ 65 | 66 | }, { 67 | key: "get", 68 | value: function get(key, type, encrypt) { 69 | var _core3 = _core(type), 70 | storage = _core3.storage; 71 | 72 | var _stoarge_value = storage.getItem(key); 73 | 74 | try { 75 | return JSON.parse(_stoarge_value); 76 | } catch (err) { 77 | return _stoarge_value; 78 | } 79 | } 80 | /** 81 | * @method 根据key删除本地存储数据 82 | * @param {*} key 键 83 | * @param {*} type 类型 默认storageType.Local 84 | */ 85 | 86 | }, { 87 | key: "remove", 88 | value: function remove(key, type) { 89 | var _core4 = _core(type), 90 | storage = _core4.storage; 91 | 92 | storage.removeItem(key); 93 | } 94 | /** 95 | * @method 清空本地存储 96 | * @param {*} type 类型 默认storageType.Local 97 | */ 98 | 99 | }, { 100 | key: "clear", 101 | value: function clear(type) { 102 | var _core5 = _core(type), 103 | storage = _core5.storage; 104 | 105 | storage.clear(); 106 | } 107 | /** 108 | * @method 根据key查询本地存储中是否存在key的实例 109 | * @param {*} key 键 110 | * @param {*} type 类型 默认storageType.Local 111 | */ 112 | 113 | }, { 114 | key: "had", 115 | value: function had(key, type) { 116 | var _core6 = _core(type), 117 | storage = _core6.storage; 118 | 119 | return key in storage; 120 | } 121 | /** 122 | * @method 获取存储库里存储实例个数 123 | * @param {*} type 124 | */ 125 | 126 | }, { 127 | key: "count", 128 | value: function count(type) { 129 | var _core7 = _core(type), 130 | storage = _core7.storage; 131 | 132 | return storage.length; 133 | } 134 | }]); 135 | 136 | return Storage; 137 | }(); 138 | 139 | exports["default"] = Storage; -------------------------------------------------------------------------------- /core/dist/utils/time.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _dayjs = _interopRequireDefault(require("dayjs")); 9 | 10 | var _settings = require("../config/settings"); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 17 | 18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 19 | 20 | var Time = /*#__PURE__*/function () { 21 | /** 22 | * 时间类实例化 23 | * @param {*} date 时间 24 | * @param {*} format 格式 25 | */ 26 | function Time() { 27 | var date = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; 28 | var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; 29 | 30 | _classCallCheck(this, Time); 31 | 32 | this.__date__ = this.dayjs(date); 33 | this.__format__ = format; 34 | } 35 | /** 36 | * 将时间格式转化为dayjs格式 37 | * @param {*} date 时间 38 | */ 39 | 40 | 41 | _createClass(Time, [{ 42 | key: "dayjs", 43 | value: function dayjs(date) { 44 | this.__date__ = (0, _dayjs["default"])(date); 45 | return this.__date__; 46 | } 47 | /** 48 | * 格式化时间 49 | * @param {String} format 格式 50 | */ 51 | 52 | }, { 53 | key: "format", 54 | value: function format(_format) { 55 | var _this$__date__, _this$__date__$format; 56 | 57 | return (_this$__date__ = this.__date__) === null || _this$__date__ === void 0 ? void 0 : (_this$__date__$format = _this$__date__.format) === null || _this$__date__$format === void 0 ? void 0 : _this$__date__$format.call(_this$__date__, _format); 58 | } 59 | /** 60 | * 时间相加 61 | * @param {*} num 要加的时间 62 | * @param {*} unit 要加的时间的单位 63 | */ 64 | 65 | }, { 66 | key: "add", 67 | value: function add(num) { 68 | var _this$__date__2, _this$__date__2$add; 69 | 70 | var unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _settings._timeUnit.Second; 71 | return (_this$__date__2 = this.__date__) === null || _this$__date__2 === void 0 ? void 0 : (_this$__date__2$add = _this$__date__2.add) === null || _this$__date__2$add === void 0 ? void 0 : _this$__date__2$add.call(_this$__date__2, num, unit); 72 | } 73 | /** 74 | * 时间相减 75 | * @param {*} num 要加的时间 76 | * @param {*} unit 要加的时间的单位 77 | */ 78 | 79 | }, { 80 | key: "subtract", 81 | value: function subtract(num) { 82 | var _this$__date__3, _this$__date__3$subtr; 83 | 84 | var unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _settings._timeUnit.Second; 85 | return (_this$__date__3 = this.__date__) === null || _this$__date__3 === void 0 ? void 0 : (_this$__date__3$subtr = _this$__date__3.subtract) === null || _this$__date__3$subtr === void 0 ? void 0 : _this$__date__3$subtr.call(_this$__date__3, num, unit); 86 | } 87 | /** 88 | * 时间比较,是否之前 89 | * @param {*} endDate 结束时间 90 | * @param {*} unit 时间单位默认秒 91 | */ 92 | 93 | }, { 94 | key: "isBefore", 95 | value: function isBefore(endDate) { 96 | var unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _settings._timeUnit.Second; 97 | return this.__date__.isBefore(endDate, unit); 98 | } 99 | /** 100 | * 计算时差 101 | * @param {*} endDate 结束时间 102 | * @param {*} unit 时间单位默认秒 103 | */ 104 | 105 | }, { 106 | key: "diff", 107 | value: function diff(endDate) { 108 | var unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _settings._timeUnit.Second; 109 | return this.__date__.diff(endDate, unit); 110 | } 111 | /** 112 | * @name 静态时间格式化 113 | * @param {Date} date 时间 114 | * @param {String} format 格式,默认YYYY-MM-DD 115 | */ 116 | 117 | }], [{ 118 | key: "quickFormat", 119 | value: function quickFormat(date) { 120 | var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "YYYY-MM-DD"; 121 | return (0, _dayjs["default"])(date).format(format); 122 | } 123 | /** 124 | * @name 初始化时间为dayjs格式 125 | * @param {Date} date 时间 126 | */ 127 | 128 | }, { 129 | key: "init", 130 | value: function init(date) { 131 | return (0, _dayjs["default"])(date); 132 | } 133 | }]); 134 | 135 | return Time; 136 | }(); 137 | 138 | exports["default"] = Time; -------------------------------------------------------------------------------- /core/dist/utils/type.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9 | 10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 11 | 12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 13 | 14 | /** 15 | * @author weilan 16 | * @description 类型检查基础类 17 | * @time 2020.04.20 18 | */ 19 | var DataType = /*#__PURE__*/function () { 20 | function DataType() { 21 | _classCallCheck(this, DataType); 22 | } 23 | 24 | _createClass(DataType, null, [{ 25 | key: "isObject", 26 | 27 | /** 28 | * @method 检测当前目标是否为对象 29 | * @param {*} item 30 | */ 31 | value: function isObject(item) { 32 | return Object.prototype.toString.call(item) === "[object Object]"; 33 | } 34 | /** 35 | * @method 检测当前目标是否为空对象 36 | * @param {*} item 37 | */ 38 | 39 | }, { 40 | key: "isEmptyObject", 41 | value: function isEmptyObject(item) { 42 | return this.isObject(item) && Object.keys(item).length === 0; 43 | } 44 | /** 45 | * @method 检测当前目标是否为数组 46 | * @param {*} item 47 | */ 48 | 49 | }, { 50 | key: "isArray", 51 | value: function isArray(item) { 52 | return Array.isArray(item); 53 | } 54 | /** 55 | * @method 检测当前目标是否为空数组 56 | * @param {*} item 57 | */ 58 | 59 | }, { 60 | key: "isEmptyArray", 61 | value: function isEmptyArray(item) { 62 | return this.isArray(item) && item.length === 0; 63 | } 64 | }]); 65 | 66 | return DataType; 67 | }(); 68 | 69 | exports["default"] = DataType; -------------------------------------------------------------------------------- /core/dist/utils/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.download = download; 7 | 8 | /** 9 | * @author weilan 10 | * @time 2020.07.01 11 | * @description 通用工具函数 12 | */ 13 | 14 | /** 15 | * @name处理下载接口返回的文件流数据 16 | * @param {*} res http请求返回数据 17 | */ 18 | function download(res) { 19 | // 错误处理 20 | if (res.data.type == "application/json") { 21 | var reader = new FileReader(); 22 | reader.readAsText(res.data, 'utf-8'); 23 | 24 | reader.onload = function () { 25 | var json_data = JSON.parse(reader.result); 26 | throw Error(json_data.Message); 27 | }; 28 | 29 | return; 30 | } // 下载处理 31 | 32 | 33 | var filename = "content-disposition" in res.headers ? decodeURIComponent(res.headers["content-disposition"].split(";")[1].split("=")[1].replace(/"/g, "")) : "下载文件"; 34 | 35 | try { 36 | if (window.navigator.msSaveOrOpenBlob) { 37 | navigator.msSaveBlob(res.data, filename); 38 | } else { 39 | var blob = new Blob([res.data], { 40 | type: "application/vnd.ms-excel" 41 | }); 42 | var url = URL.createObjectURL(blob); 43 | var link = document.createElement("a"); 44 | link.setAttribute("href", url); 45 | link.setAttribute("download", filename); 46 | link.style.display = "none"; 47 | document.body.appendChild(link); 48 | link.click(); 49 | URL.revokeObjectURL(url); // 释放URL 对象 50 | 51 | document.body.removeChild(link); 52 | } 53 | } catch (err) { 54 | throw Error(err); 55 | } 56 | } -------------------------------------------------------------------------------- /core/dist/utils/validate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.vaPhone = vaPhone; 7 | exports.regPhone = regPhone; 8 | exports.isNum = isNum; 9 | exports.isInteger = isInteger; 10 | exports.validate = validate; 11 | 12 | function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } 13 | 14 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 15 | 16 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 17 | 18 | /** 19 | * auth: weilan 20 | * time: 2020-03-11 21 | * des: el自定义表单验证及正则验证 22 | * rule:el校验以va开头 vaPhone;正则验证以reg开头 23 | */ 24 | // el手机格式校验 25 | function vaPhone(rule, value, callback) { 26 | if (!value || regPhone(value)) { 27 | callback(); 28 | } else { 29 | callback(new Error('请输入正确的手机号!')); 30 | } 31 | } // 正则手机格式校验 32 | 33 | 34 | function regPhone(value) { 35 | return /^1[3-9][0-9]{9}/.test(value); 36 | } 37 | /** 38 | * 验证是数字类型或可转换为数字类型 39 | * @param {*} value 要验证的值 40 | */ 41 | 42 | 43 | function isNum(value) { 44 | return !Number.isNaN(Math.sign(value)); 45 | } 46 | /** 47 | * @name 验证整数 48 | * @param {*} val 要验证的内容 49 | */ 50 | 51 | 52 | function isInteger(val) { 53 | return /^[0-9]*$/.test(value); 54 | } 55 | /** 56 | * 需要校验的表格验证 57 | * @param {*} columns 表头 58 | * @param {*} length 长度 59 | */ 60 | 61 | 62 | function validate(columns, length, _vm) { 63 | var _va_columns = columns.filter(function (i) { 64 | return i.validate; 65 | }); 66 | 67 | var _iterator = _createForOfIteratorHelper(_va_columns), 68 | _step; 69 | 70 | try { 71 | for (_iterator.s(); !(_step = _iterator.n()).done;) { 72 | var i = _step.value; 73 | 74 | for (var t = 0; t < length; t++) { 75 | var _va_result = _vm.$refs[i.prop + t].validate(); 76 | 77 | if (!_va_result) return false; 78 | } 79 | } 80 | } catch (err) { 81 | _iterator.e(err); 82 | } finally { 83 | _iterator.f(); 84 | } 85 | 86 | return true; 87 | } -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wl-core", 3 | "version": "1.1.9", 4 | "description": "项目基础核心库", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/babel src --out-dir dist", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/wl-ui/wl.git" 13 | }, 14 | "keywords": [ 15 | "wl-core", 16 | "核心库", 17 | "utils", 18 | "工具库", 19 | "array", 20 | "数组", 21 | "number", 22 | "数字型操作", 23 | "time", 24 | "时间类型操作", 25 | "type", 26 | "类型判断", 27 | "storage", 28 | "本地存储", 29 | "validate", 30 | "校验方法" 31 | ], 32 | "author": "weilan <17796646068@163.com>", 33 | "license": "ISC", 34 | "bugs": { 35 | "url": "https://github.com/wl-ui/wl/issues" 36 | }, 37 | "homepage": "https://github.com/wl-ui/wl#readme", 38 | "devDependencies": { 39 | "@babel/cli": "^7.4.4", 40 | "@babel/core": "^7.4.4", 41 | "@babel/plugin-proposal-class-properties": "^7.8.3", 42 | "@babel/preset-env": "^7.4.4" 43 | }, 44 | "dependencies": { 45 | "big.js": "^5.2.2", 46 | "dayjs": "^1.8.25" 47 | } 48 | } -------------------------------------------------------------------------------- /core/src/config/settings.js: -------------------------------------------------------------------------------- 1 | // 基础配置项 2 | 3 | // 存储类型 4 | export const _storageType = { 5 | Local: 'local', 6 | Session: 'session' 7 | } 8 | 9 | // 时间单位 大小写不敏感 支持负数和缩写 10 | export const _timeUnit = { 11 | Year: 'year', // Y 年 12 | Quarter: 'quarter', // Q 季度 13 | Month: 'month', // M 月 14 | Week: 'week', // W 周 15 | Day: 'day', // d 天 16 | Hour: 'hour', // h 时 17 | Minute: 'minute', // m 分 18 | Second: 'second', // s 秒 19 | } -------------------------------------------------------------------------------- /core/src/index.js: -------------------------------------------------------------------------------- 1 | import Storage from "./utils/storage"; 2 | import DataType from "./utils/type"; 3 | import Time from "./utils/time"; 4 | import WlNumber from "./utils/number"; 5 | import VaJwt from "./utils/jwt" 6 | 7 | export * from "./utils/array" 8 | export * from "./utils/validate" 9 | export * from "./utils/event" 10 | export * from "./utils/utils" 11 | export { 12 | Storage, 13 | DataType, 14 | Time, 15 | VaJwt, 16 | WlNumber 17 | } -------------------------------------------------------------------------------- /core/src/utils/array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @auther weilan 3 | * @time 2020.03.09 4 | * @description: 一个数组操作函数库 5 | */ 6 | 7 | import Time from "./time" 8 | 9 | /** 10 | * 从树形数据中递归筛选目标值 11 | * arr 数据源 12 | * val 目标值 13 | * id 需要判断相等的字段 14 | * childs 子集 15 | */ 16 | function valInDeep(arr = [], val, id = "Id", childs = "Children") { 17 | return arr.reduce((flat, item) => { 18 | return flat.concat( 19 | item[id] == val ? item : valInDeep(item[childs] || [], val, id, childs) 20 | ); 21 | }, []); 22 | } 23 | 24 | /** 25 | * 将树形数据向下递归为一维数组 26 | * @param {*} arr 数据源 27 | * @param {*} childs 子集key 28 | */ 29 | function flattenDeep(arr = [], childs = "Children") { 30 | return arr.reduce((flat, item) => { 31 | return flat.concat( 32 | item, 33 | item[childs] ? flattenDeep(item[childs], childs) : [] 34 | ); 35 | }, []); 36 | } 37 | 38 | /** 39 | * 将树形数据向上将此支线递归为一维数组 40 | * @param {*} arr 数据源 41 | * @param {*} parent 父级 42 | */ 43 | function flattenDeepParents(arr, parent) { 44 | return arr.reduce((flat, item) => { 45 | return flat.concat( 46 | item[parent] || [], 47 | item[parent] ? flattenDeepParents([item[parent]], parent) : [] 48 | ); 49 | }, []); 50 | } 51 | 52 | /** 53 | * 根据条件递归祖先元素 54 | * @param {*} row 数据源 55 | * @param {*} parent 父级数据 56 | * @param {*} reg 回调 57 | */ 58 | function regDeepParents(row, parent, reg) { 59 | if (row[parent]) { 60 | reg && reg(row[parent]); 61 | regDeepParents(row[parent], parent, reg); 62 | } 63 | } 64 | 65 | /** 66 | * 将数组转化成树结构 array to tree 67 | * @param {*} array 数据源 68 | * @param {*} options 字段名配置项 69 | */ 70 | function arrayToTree( 71 | array = [], 72 | options = { id: "id", pid: "pid", children: "children", rootPidVal: null }, 73 | ) { 74 | let array_ = []; // 创建储存剔除叶子节点后的骨架节点数组 75 | let unique = {}; // 创建盒子辅助本轮children合并去重 76 | let root_pid = options.rootPidVal || [ 77 | 0, 78 | "0", 79 | undefined, 80 | "undefined", 81 | null, 82 | "null", 83 | "00000000-0000-0000-0000-000000000000", 84 | "" 85 | ]; // 可能存在的根节点pid形式 86 | array.forEach(item => { 87 | // 筛选可以插入当前节点的所有子节点 88 | let children_array = array.filter( 89 | it => it[options.pid] === item[options.id] 90 | ); 91 | if (Array.isArray(item[options.children]) && item[options.children].length) { 92 | // 去重合并数组 93 | item[options.children].map(i => (unique[i[options.id]] = 1)); 94 | item[options.children].push( 95 | ...children_array.filter(i => unique[i[options.id]] !== 1) 96 | ); 97 | } else { 98 | item[options.children] = children_array; 99 | } 100 | // 当children_array有数据时插入下一轮array_,当无数据时将最后留下来的根节点树形插入数组 101 | let has_children = children_array.length > 0; 102 | if ( 103 | has_children || 104 | (!has_children && root_pid.includes(item[options.pid])) 105 | ) { 106 | array_.push(item); 107 | } 108 | }); 109 | // 当数组内仅有根节点时退出,否组继续处理 最终递归深度次 110 | if (!array_.every(item => root_pid.includes(item[options.pid]))) { 111 | return arrayToTree(array_, options); 112 | } else { 113 | return array_; 114 | } 115 | } 116 | 117 | /** 118 | * 如果数据里缺少树枝节点,则根据parents和自增长id补全整条树链,输出数据调用上部arrToTree函数组装成完整的树 119 | * @param {Array} data 当前选中的某些数据 [一维数组] 120 | * @param {Array} sourceData 拥有完整数据的源数据 [一维数组] 121 | * @param {Object} options 配置参数,包括id,pid,树链parents,组成树链的自增长IdentityId, 根节点的默认pid为空的guid 122 | */ 123 | function patchTreeChain( 124 | data, 125 | sourceData, 126 | options = { 127 | Id: "Id", 128 | ParentId: "ParentId", 129 | Parents: "Parents", 130 | IdentityId: "IdentityId", 131 | root: "00000000-0000-0000-0000-000000000000" 132 | } 133 | ) { 134 | let _out_put_data = [], // 声明一个导出数据盒子 135 | _all_lack_data = []; // 声明一个全部需要补全的节点盒子 136 | data.forEach(i => { 137 | // 当一个节点在整个已选节点里找不到父节点时,并且此节点不是根节点时,从源数据中补全 138 | if (!data.find(t => t[options.Id] === i[options.ParentId]) && 139 | i[options.ParentId] !== options.root 140 | ) { 141 | // 首先将记录在节点身上的父级树链拆分 142 | let _parents = i[options.Parents] 143 | .substring(1, i[options.Parents].length - 1) 144 | .split(",") 145 | .filter(item => !!item); 146 | // 然后查找父级树链中某一链条是否已在数据中存在,已存在的不需要补全,从树链中剔除 147 | let _lack_parents = _parents.filter( 148 | e => data.findIndex(m => m[options.IdentityId] == e) === -1 149 | ); 150 | // 合并全部需要补全的数据 151 | _all_lack_data = _all_lack_data.concat(_lack_parents); 152 | } 153 | }); 154 | // 去重后根据IdentityId在源数据中找到完整的节点数据并组装 155 | [...new Set(_all_lack_data)].forEach(item => { 156 | _out_put_data.push(sourceData.find(it => it[options.IdentityId] == item)); 157 | }); 158 | // 最后返回当前数据和需要补全父级树链的数据 159 | return _out_put_data.concat(data); 160 | } 161 | 162 | /** 163 | * 从坐标值拼接指定字段到祖先元素 164 | * @param {*} data 一维数据源 165 | * @param {*} coordinate 坐标值数据 166 | * @param {*} options 配置项 167 | */ 168 | function splicParentsUntil(data, coordinate, options = { 169 | pathName: 'name', // 所要拼接字段 170 | pathConnector: '\\', // 连接符 171 | pathId: "id", // 数据源匹配字段 172 | pathParents: "parents", 173 | pathIdentityId: "identityId", 174 | }) { 175 | let coordinate_item = data.find(i => i[options.pathId] === coordinate[options.pathId]); 176 | if (!coordinate_item) return ''; 177 | if (!coordinate_item[options.pathParents]) return coordinate_item[options.pathName]; 178 | let _parents = coordinate_item[options.pathParents] 179 | .substring(1, coordinate_item[options.pathParents].length - 1) 180 | .split(",") 181 | .filter(i => !!i); 182 | let splic_parents = ''; 183 | _parents.forEach(i => { 184 | let _parent = data.find(t => t[options.pathIdentityId] == i); 185 | splic_parents += `${_parent[options.pathName]}${options.pathConnector}` 186 | }) 187 | return splic_parents + coordinate_item[options.pathName]; 188 | } 189 | 190 | /** 191 | * 根据数组2内的元素,通过match字段匹配数组1内的完整内容组成的数据 192 | * @param {*} array1 带有完整item对象的源数组列表 193 | * @param {*} array2 只带有match字段组成的简单数组 194 | * @param {*} match 用于匹配数组1和数组2的字段 195 | */ 196 | function intersectionBy(array1 = [], array2 = [], match = "Id") { 197 | if ([null, "null", undefined, "undefined"].includes(array2)) return; 198 | let data = []; 199 | array2.forEach(item => { 200 | let match_success = array1.find(it => it[match] === item); 201 | match_success && data.push(match_success); 202 | }); 203 | return data; 204 | } 205 | 206 | /** 207 | * 深拷贝 208 | * @param {*} source 要拷贝的数据 209 | */ 210 | function deepClone(source) { 211 | if (!source && typeof source !== "object") { 212 | throw new Error("error arguments", "shallowClone"); 213 | } 214 | const targetObj = source.constructor === Array ? [] : {}; 215 | Object.keys(source).forEach(keys => { 216 | if (source[keys] && typeof source[keys] === "object") { 217 | targetObj[keys] = source[keys].constructor === Array ? [] : {}; 218 | targetObj[keys] = deepClone(source[keys]); 219 | } else { 220 | targetObj[keys] = source[keys]; 221 | } 222 | }); 223 | return targetObj; 224 | } 225 | 226 | /** 227 | * 筛选出数组中最大值 228 | * @param {*} arr 数据 229 | * @param {*} key 如果是复杂型数组,请指定字段key 230 | * @param {*} stamp 如果是时间格式,请设置以转化时间戳 231 | */ 232 | function getMax(arr = [], key = null, stamp = false) { 233 | let _o = !key ? arr : arr.map(i => i[key]); 234 | let _t = !stamp ? _o : _o.map(i => Time.init(i).valueOf()); 235 | return Math.max(..._t); 236 | } 237 | 238 | /** 239 | * 筛选出数组中最小值 240 | * @param {*} arr 数据 241 | * @param {*} key 如果是复杂型数组,请指定字段key 242 | * @param {*} stamp 如果是时间格式,请设置以转化时间戳 243 | */ 244 | function getMin(arr = [], key = null, stamp = false) { 245 | let _o = !key ? arr : arr.map(i => i[key]); 246 | let _t = !stamp ? _o : _o.map(i => Time.init(i).valueOf()); 247 | return Math.min(..._t); 248 | } 249 | 250 | /** 251 | * @name 数组去重 252 | * @param {Array} arr 原数组 253 | * @param {String} key 要比对的key,不传则认为是简单数组 254 | */ 255 | const unique = (arr, key) => { 256 | let hashList = []; 257 | key ? arr.forEach(i => { 258 | if (hashList.find(t => t[key] == i[key])) return; 259 | hashList.push(i) 260 | }) : arr.forEach(i => { 261 | if (hashList.includes(i)) return; 262 | hashList.push(i) 263 | }) 264 | return hashList 265 | } 266 | 267 | /** 268 | * @name 数组查重 269 | * @param {Array} arr 原数组 270 | * @param {String} key 要比对的key,不传则认为是简单数组 271 | */ 272 | const depData = (arr, key) => { 273 | let hashList = []; 274 | const depData = arr.filter((i) => { 275 | const _item = key ? i[key] : i 276 | if (hashList.includes(_item)) return i; 277 | hashList.push(_item); 278 | }); 279 | return depData 280 | } 281 | 282 | /** 283 | * @name 删除数据后自动定位 284 | * @param {Array} data 未删除前数据 285 | * @param {String} key 作为判断依据的数据key 286 | * @param {String|Number} delId 要删除数据的id 287 | * @param {String|Number} actId 当前选中的数据id 288 | * @param {Boolean} isTree 289 | * @param {String} keyParent 290 | */ 291 | const autoPositionAfterDelete = (data, key, delId, actId, isTree, keyParent) => { 292 | // 源数据校验 293 | if (!Array.isArray(data)) throw Error('data必须是一个数组'); 294 | // 非树形结构 295 | // if (!isTree) { 296 | // 找到当前选中数据索引 297 | const activeIndex = data.findIndex(i => i[key] === actId); 298 | // 删后数据 299 | const nextData = data.filter(i => i[key] !== delId); 300 | // 删除的是非当前选中数据,或删后数组为空,无需重新定位 301 | if (delId !== actId || !nextData.length) return { nextItem: null, nextData } 302 | // 删除的是当前选中数据,自动定位前一个数据,第0时自动定位后一个数据 303 | const nextIndex = activeIndex !== 0 ? activeIndex - 1 : 0 304 | return { nextItem: nextData[nextIndex], nextData } 305 | // } 306 | // 树形结构 307 | } 308 | 309 | export { 310 | valInDeep, // 从树形数据中递归筛选目标值 311 | flattenDeep, // 将树形数据向下递归为一维数组 312 | flattenDeepParents, // 将树形数据向上将此支线递归为一维数组 313 | regDeepParents, // 根据条件递归祖先元素 314 | arrayToTree, // 将数组转化成树结构 315 | patchTreeChain, // 如果数据里缺少树枝节点,则根据parents和自增长id补全整条树链,输出数据调用上部arrToTree函数组装成完整的树 316 | splicParentsUntil, // 从坐标值拼接指定字段到祖先元素 317 | intersectionBy, // 根据数组2内的元素,通过match字段匹配数组1内的完整内容组成的数据 318 | deepClone, // 深拷贝 319 | getMax, // 筛选出数组中最大值 320 | getMin, // 筛选出数组中最小值 321 | unique, // 数组去重 322 | depData, // 获取数组重复数据 323 | autoPositionAfterDelete, // 数组删除数据后自动定位 324 | }; -------------------------------------------------------------------------------- /core/src/utils/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @description event事件库 4 | * @time 2020.04.21 5 | */ 6 | 7 | /** 8 | * @method 节流函数(500ms内只能点击一次,点击后立即触发,重复点击无效,必须等3s之后才能点击第二次) 9 | * @param {Function} {handler} 事件处理函数 10 | * @param {Number} {delay} 恢复点击的毫秒数 11 | */ 12 | const throttle = (handler, delay) => { 13 | let last, deferTimer 14 | return function (...args) { 15 | let that = this 16 | let now = +new Date() 17 | if (last && now < last + delay) { 18 | deferTimer && clearTimeout(deferTimer) 19 | deferTimer = setTimeout(() => { 20 | last = now 21 | handler.apply(that, args) 22 | }, delay) 23 | } else { 24 | last = now 25 | handler.apply(that, args) 26 | } 27 | } 28 | } 29 | 30 | /** 31 | * @method 防抖函数(500ms之后出结果,重复点击无效,如果重复点击了,重新计算3s时间,从点击的时刻算起,必须等待3s时间触发事件) 32 | * @param {Function} {handler} 事件处理函数 33 | * @param {Number} {delay} 恢复点击的毫秒数 34 | */ 35 | const debounce = (handler, delay = 500) => { 36 | let timeout 37 | return function (...args) { 38 | // 获取函数的作用域和变量 39 | let that = this 40 | // 每次事件被触发,都会清除当前的timer,然后重写设置超时调用 41 | timeout && clearTimeout(timeout) 42 | timeout = setTimeout(() => { 43 | handler.apply(that, args) 44 | }, delay) 45 | } 46 | } 47 | 48 | export { 49 | throttle, // 节流函数 50 | debounce // 防抖函数 51 | } -------------------------------------------------------------------------------- /core/src/utils/jwt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.04.23 4 | * @description 用户身份校验类 5 | */ 6 | 7 | import Time from "./time" 8 | 9 | export default class VaJwt { 10 | /** 11 | * 截取jwt中有效载荷部分 12 | * @param {*} jwt 13 | */ 14 | static extractJwtPayload(jwt) { 15 | if (!jwt) throw Error('缺少jwt!'); 16 | let jwt_split = jwt.split('.'); 17 | if (jwt_split.length !== 3) throw Error('jwt格式不正确!'); 18 | return jwt_split[1]; 19 | } 20 | 21 | /** 22 | * 简单解析未特殊加密的payload部分 23 | * @param {*} jwt 24 | */ 25 | static payloadAtob(jwt) { 26 | let jwt_payload = this.extractJwtPayload(jwt); 27 | let decodedData = window.atob(jwt_payload); 28 | return JSON.parse(decodedData); 29 | } 30 | 31 | /** 32 | * 检验jwt是否过期 33 | * @param {String} jwt 34 | * @param {Function} vaCb 自定义验证函数,返回Boolean true表示过期 35 | */ 36 | static vaJwtExpired(jwt, vaCb) { 37 | let exp = this.payloadAtob(jwt).exp * 1000; 38 | if (vaCb) { 39 | return vaCb(exp) 40 | } 41 | let _time = new Time(exp); 42 | return _time.isBefore(new Date()); 43 | } 44 | 45 | /** 46 | * 监测浏览器tab页切换立即校验账号 47 | * @param {Function} cb 检测到切换后的回调函数 48 | */ 49 | static vaVisibilityChange(cb) { 50 | window.addEventListener("visibilitychange", cb) 51 | } 52 | } -------------------------------------------------------------------------------- /core/src/utils/number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.05.07 4 | * @description 依赖big.js处理js数字运算的精度问题 5 | */ 6 | import Big from "big.js" 7 | 8 | class WlNumber { 9 | /** 10 | * @name 实例化bigjs 11 | * @param {Number|String} val 12 | */ 13 | constructor(val) { 14 | this._val = new Big(val); 15 | } 16 | 17 | /** 18 | * @name 加 19 | * @param {Number|String} val 要加的值 20 | */ 21 | plus(val) { 22 | this._val = this._val.plus(val); 23 | return this._val; 24 | } 25 | 26 | /** 27 | * @name 减 28 | * @param {Number|String} val 要减的值 29 | */ 30 | minus(val) { 31 | this._val = this._val.minus(val); 32 | return this._val; 33 | } 34 | 35 | /** 36 | * @name 乘 37 | * @param {Number|String} val 要乘的值 38 | */ 39 | times(val) { 40 | this._val = this._val.times(val); 41 | return this._val; 42 | } 43 | 44 | /** 45 | * @name 除以 46 | * @param {Number|String} val 要除以的值 47 | */ 48 | div(val) { 49 | this._val = this._val.div(val); 50 | return this._val; 51 | } 52 | 53 | /** 54 | * @name 取余 55 | * @param {Number|String} val 要除以的值 56 | */ 57 | mod(val) { 58 | this._val = this._val.mod(val); 59 | return this._val; 60 | } 61 | 62 | /** 63 | * @name 取绝对值 64 | * @param {Number|String} val 取绝对值的值 65 | */ 66 | abs() { 67 | this._val = this._val.abs(); 68 | return this._val; 69 | } 70 | 71 | /** 72 | * @name 大于 73 | * @param {Number|String} val 取比较的值 74 | * @returns Boolean 75 | */ 76 | gt(val) { 77 | return this._val.gt(val); 78 | } 79 | 80 | /** 81 | * @name 大于等于 82 | * @param {Number|String} val 取比较的值 83 | * @returns Boolean 84 | */ 85 | gte(val) { 86 | return this._val.gte(val); 87 | } 88 | 89 | /** 90 | * @name 小于 91 | * @param {Number|String} val 取比较的值 92 | * @returns Boolean 93 | */ 94 | lt(val) { 95 | return this._val.lt(val); 96 | } 97 | 98 | /** 99 | * @name 小于等于 100 | * @param {Number|String} val 取比较的值 101 | * @returns Boolean 102 | */ 103 | lte(val) { 104 | return this._val.lte(val); 105 | } 106 | 107 | /** 108 | * @name 将数据转化为数字类型,如果不可转化则返回0 109 | * @param {*} val 110 | */ 111 | static toNumber(val) { 112 | return Number(val) || 0 113 | } 114 | } 115 | 116 | export default WlNumber; -------------------------------------------------------------------------------- /core/src/utils/storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @description 浏览器存储函数 4 | * @time 2020.04.20 5 | */ 6 | 7 | import { _storageType } from '../config/settings'; 8 | import DataType from './type'; 9 | 10 | /** 11 | * @method 基础判断 12 | * @param storageType 存储类型 13 | * @param encryptType 加密类型 14 | */ 15 | const _core = (storageType = _storageType.Local, encryptType) => { 16 | if (!encryptType) { 17 | return { storage: storageType === _storageType.Local ? localStorage : sessionStorage } 18 | } 19 | } 20 | 21 | export default class Storage { 22 | /** 23 | * @method 将键值存入本地存储 24 | * @param {*} key 键 25 | * @param {*} value 值 26 | * @param {*} type 类型 默认storageType.Local 27 | * @param {*} encrypt 加密配置项 28 | */ 29 | static set(key, value, type, encrypt) { 30 | const { storage } = _core(type); 31 | let _processed_value = DataType.isObject(value) || DataType.isArray(value) ? JSON.stringify(value) : value; 32 | storage.setItem(key, _processed_value); 33 | } 34 | 35 | /** 36 | * @method 根据key取本地存储数据 37 | * @param {*} key 键 38 | * @param {*} type 类型 默认storageType.Local 39 | * @param {*} encrypt 加密配置项 40 | */ 41 | static get(key, type, encrypt) { 42 | const { storage } = _core(type); 43 | let _stoarge_value = storage.getItem(key); 44 | try { 45 | return JSON.parse(_stoarge_value); 46 | } catch (err) { 47 | return _stoarge_value; 48 | } 49 | } 50 | 51 | /** 52 | * @method 根据key删除本地存储数据 53 | * @param {*} key 键 54 | * @param {*} type 类型 默认storageType.Local 55 | */ 56 | static remove(key, type) { 57 | const { storage } = _core(type); 58 | storage.removeItem(key) 59 | } 60 | 61 | /** 62 | * @method 清空本地存储 63 | * @param {*} type 类型 默认storageType.Local 64 | */ 65 | static clear(type) { 66 | const { storage } = _core(type); 67 | storage.clear(); 68 | } 69 | 70 | /** 71 | * @method 根据key查询本地存储中是否存在key的实例 72 | * @param {*} key 键 73 | * @param {*} type 类型 默认storageType.Local 74 | */ 75 | static had(key, type) { 76 | const { storage } = _core(type); 77 | return key in storage; 78 | } 79 | 80 | /** 81 | * @method 获取存储库里存储实例个数 82 | * @param {*} type 83 | */ 84 | static count(type) { 85 | const { storage } = _core(type); 86 | return storage.length 87 | } 88 | } -------------------------------------------------------------------------------- /core/src/utils/time.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.04.23 4 | * @description 时间操作方法 5 | */ 6 | import dayJs from "dayjs" 7 | import { _timeUnit } from '../config/settings'; 8 | 9 | export default class Time { 10 | /** 11 | * 时间类实例化 12 | * @param {*} date 时间 13 | * @param {*} format 格式 14 | */ 15 | constructor(date = null, format = null) { 16 | this.__date__ = this.dayjs(date); 17 | this.__format__ = format; 18 | } 19 | 20 | /** 21 | * 将时间格式转化为dayjs格式 22 | * @param {*} date 时间 23 | */ 24 | dayjs(date) { 25 | this.__date__ = dayJs(date); 26 | return this.__date__; 27 | } 28 | 29 | /** 30 | * 格式化时间 31 | * @param {String} format 格式 32 | */ 33 | format(format) { 34 | return this.__date__?.format?.(format) 35 | } 36 | 37 | /** 38 | * 时间相加 39 | * @param {*} num 要加的时间 40 | * @param {*} unit 要加的时间的单位 41 | */ 42 | add(num, unit = _timeUnit.Second) { 43 | return this.__date__?.add?.(num, unit) 44 | } 45 | 46 | /** 47 | * 时间相减 48 | * @param {*} num 要加的时间 49 | * @param {*} unit 要加的时间的单位 50 | */ 51 | subtract(num, unit = _timeUnit.Second) { 52 | return this.__date__?.subtract?.(num, unit) 53 | } 54 | 55 | /** 56 | * 时间比较,是否之前 57 | * @param {*} endDate 结束时间 58 | * @param {*} unit 时间单位默认秒 59 | */ 60 | isBefore(endDate, unit = _timeUnit.Second) { 61 | return this.__date__.isBefore(endDate, unit); 62 | } 63 | 64 | /** 65 | * 计算时差 66 | * @param {*} endDate 结束时间 67 | * @param {*} unit 时间单位默认秒 68 | */ 69 | diff(endDate, unit = _timeUnit.Second) { 70 | return this.__date__.diff(endDate, unit); 71 | } 72 | 73 | /** 74 | * @name 静态时间格式化 75 | * @param {Date} date 时间 76 | * @param {String} format 格式,默认YYYY-MM-DD 77 | */ 78 | static quickFormat(date, format = "YYYY-MM-DD") { 79 | return dayJs(date).format(format) 80 | } 81 | 82 | /** 83 | * @name 初始化时间为dayjs格式 84 | * @param {Date} date 时间 85 | */ 86 | static init(date) { 87 | return dayJs(date) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/utils/type.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @description 类型检查基础类 4 | * @time 2020.04.20 5 | */ 6 | export default class DataType { 7 | /** 8 | * @method 检测当前目标是否为对象 9 | * @param {*} item 10 | */ 11 | static isObject(item) { 12 | return Object.prototype.toString.call(item) === "[object Object]"; 13 | } 14 | 15 | /** 16 | * @method 检测当前目标是否为空对象 17 | * @param {*} item 18 | */ 19 | static isEmptyObject(item) { 20 | return this.isObject(item) && Object.keys(item).length === 0; 21 | } 22 | 23 | /** 24 | * @method 检测当前目标是否为数组 25 | * @param {*} item 26 | */ 27 | static isArray(item) { 28 | return Array.isArray(item); 29 | } 30 | 31 | /** 32 | * @method 检测当前目标是否为空数组 33 | * @param {*} item 34 | */ 35 | static isEmptyArray(item) { 36 | return this.isArray(item) && item.length === 0; 37 | } 38 | } -------------------------------------------------------------------------------- /core/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.07.01 4 | * @description 通用工具函数 5 | */ 6 | 7 | /** 8 | * @name处理下载接口返回的文件流数据 9 | * @param {*} res http请求返回数据 10 | */ 11 | function download(res) { 12 | // 错误处理 13 | if (res.data.type == "application/json") { 14 | let reader = new FileReader(); 15 | reader.readAsText(res.data, 'utf-8'); 16 | reader.onload = function () { 17 | let json_data = JSON.parse(reader.result); 18 | throw Error(json_data.Message); 19 | } 20 | return; 21 | } 22 | // 下载处理 23 | let filename = "content-disposition" in res.headers ? 24 | decodeURIComponent( 25 | res.headers["content-disposition"] 26 | .split(";")[1] 27 | .split("=")[1] 28 | .replace(/"/g, "") 29 | ) : 30 | "下载文件"; 31 | try { 32 | if (window.navigator.msSaveOrOpenBlob) { 33 | navigator.msSaveBlob(res.data, filename); 34 | } else { 35 | let blob = new Blob([res.data], { 36 | type: "application/vnd.ms-excel" 37 | }); 38 | let url = URL.createObjectURL(blob); 39 | let link = document.createElement("a"); 40 | link.setAttribute("href", url); 41 | link.setAttribute("download", filename); 42 | link.style.display = "none"; 43 | document.body.appendChild(link); 44 | link.click(); 45 | URL.revokeObjectURL(url); // 释放URL 对象 46 | document.body.removeChild(link); 47 | } 48 | } catch (err) { 49 | throw Error(err); 50 | } 51 | } 52 | 53 | export { 54 | download 55 | } -------------------------------------------------------------------------------- /core/src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * auth: weilan 3 | * time: 2020-03-11 4 | * des: el自定义表单验证及正则验证 5 | * rule:el校验以va开头 vaPhone;正则验证以reg开头 6 | */ 7 | 8 | // el手机格式校验 9 | function vaPhone(rule, value, callback) { 10 | if (!value || regPhone(value)) { callback(); } else { callback(new Error('请输入正确的手机号!')); } 11 | } 12 | 13 | // 正则手机格式校验 14 | function regPhone(value) { 15 | return /^1[3-9][0-9]{9}/.test(value) 16 | } 17 | 18 | /** 19 | * 验证是数字类型或可转换为数字类型 20 | * @param {*} value 要验证的值 21 | */ 22 | function isNum(value) { 23 | return !Number.isNaN(Math.sign(value)); 24 | } 25 | 26 | /** 27 | * @name 验证整数 28 | * @param {*} val 要验证的内容 29 | */ 30 | function isInteger(val) { 31 | return /^[0-9]*$/.test(value); 32 | } 33 | 34 | /** 35 | * 需要校验的表格验证 36 | * @param {*} columns 表头 37 | * @param {*} length 长度 38 | */ 39 | function validate(columns, length, _vm) { 40 | let _va_columns = columns.filter(i => i.validate); 41 | for (let i of _va_columns) { 42 | for (let t = 0; t < length; t++) { 43 | let _va_result = _vm.$refs[i.prop + t].validate() 44 | if (!_va_result) return false; 45 | } 46 | } 47 | return true; 48 | } 49 | 50 | export { 51 | vaPhone, // el手机格式校验 52 | regPhone, // 正则手机格式校验 53 | isNum, // 验证数字 54 | isInteger, // 验证整数 55 | validate, // 整体表单验证 56 | } -------------------------------------------------------------------------------- /http/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ], 5 | "plugins": [ 6 | "@babel/plugin-proposal-class-properties" 7 | ] 8 | } -------------------------------------------------------------------------------- /http/README.md: -------------------------------------------------------------------------------- 1 | # wl-http 2 | 3 | 通信层封装 4 | 5 | ### 1. 实例化http:__http__.js 6 | 7 | ```js 8 | import Http from "wl-http" 9 | 10 | const options = { 11 | axios: null, // 是否使用外部axios实例,无特殊情况禁止使用 12 | axiosOptions: { 13 | retry: 2, //Number 请求失败自动重连次数 默认2 14 | retryDelay: 1000, // 请求失败自动重连时间间隔 默认1000ms 15 | withCredentials: true, // Boolean 开启请求跨域 默认true 16 | headers: { 17 | "Content-Type": "application/json;charset=UTF-8" 18 | }, // Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 19 | timeout: 5000, // Number 请求超时时间 默认5000 20 | baseURL: '' // String 请求地址前缀 默认'' 21 | expand: {} // 其他需要扩展的配置项 other axios 支持的config字段 22 | }, // 以上字段均有默认值,正常情况下 此options 无需提供 23 | requestInterceptorSuccessCb:()=>{}, // 非必填 请求拦截器成功回调,必须返回一个config对象 24 | responseInterceptorSuccessCb:()=>{}, // 非必填 响应拦截器成功回调,必须返回一个response对象 25 | responseInterceptorErrorCb:()=>{}, // 非必填 响应拦截器失败回调,必须返回一个response对象 26 | }, // 实例化http可选配置项 均为非必填项 27 | 28 | const http = new Http(options); 29 | 30 | export default http; 31 | ``` 32 | 33 | ### 2. 在具体的api文件中使用:user.js 34 | ```js 35 | import http from "__http__.js" 36 | 37 | const getMenuApi = (params) => http.get({ 38 | url:'', 39 | params 40 | }) 41 | 42 | const addUserApi = (data) => http.post({ 43 | url:'', 44 | data 45 | }) 46 | 47 | export { 48 | getMenuApi, 49 | addUserApi 50 | } 51 | ``` 52 | 53 | ### 3. 在vue文件中最终调用 54 | ```js 55 | import {addUserApi} from "@/api/user.js" 56 | 57 | methods: { 58 | addUser(){ 59 | const _data = { 60 | name: 'weilan', 61 | des: '前端架构师' 62 | } 63 | addUserApi(_data).then( ({data})=>{ 64 | ... 65 | }) 66 | } 67 | } 68 | ``` 69 | 70 | ### 4. Http类提供 get,post,all,del,put,patch 共6中方法 -------------------------------------------------------------------------------- /http/dist/config/settings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports._httpCode = exports._httpType = exports._httpOptions = void 0; 7 | // 基础配置项 8 | var _httpOptions = { 9 | baseURL: '', 10 | // api的base_url 11 | retry: 2, 12 | retryDelay: 1000, 13 | withCredentials: true, 14 | headers: { 15 | "Content-Type": "application/json;charset=UTF-8" 16 | }, 17 | timeout: 5000, 18 | // request timeout 19 | method: 'post' // 默认请求方法 20 | 21 | }; // http类型 22 | 23 | exports._httpOptions = _httpOptions; 24 | var _httpType = { 25 | GET: 'get', 26 | POST: 'post', 27 | PUT: 'put', 28 | PATCH: 'patch', 29 | DELETE: 'delete' 30 | }; // 后台状态码 31 | 32 | exports._httpType = _httpType; 33 | var _httpCode = { 34 | ok: 200, 35 | err: 300, 36 | noAuth: 401 37 | }; 38 | exports._httpCode = _httpCode; -------------------------------------------------------------------------------- /http/dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "mockXHR", { 7 | enumerable: true, 8 | get: function get() { 9 | return _mock.mockXHR; 10 | } 11 | }); 12 | exports["default"] = void 0; 13 | 14 | var _http = _interopRequireDefault(require("./utils/http")); 15 | 16 | var _mock = require("./utils/mock"); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 19 | 20 | var _default = _http["default"]; 21 | exports["default"] = _default; -------------------------------------------------------------------------------- /http/dist/utils/fetch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _axios = _interopRequireDefault(require("axios")); 9 | 10 | var _wlCore = require("wl-core"); 11 | 12 | var _settings = require("../config/settings"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 15 | 16 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 17 | 18 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 19 | 20 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 21 | 22 | function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } 23 | 24 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } 25 | 26 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 27 | 28 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 29 | 30 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 31 | 32 | // 导入配置项 33 | 34 | /** 35 | * @method 配置请求拦截器 36 | * @param {Object} instance axios实例 37 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 38 | */ 39 | var _configRequestInterceptor = function _configRequestInterceptor(instance, requestInterceptorSuccessCb) { 40 | instance.interceptors.request.use(function (config) { 41 | if (requestInterceptorSuccessCb) { 42 | var _config = requestInterceptorSuccessCb(config); 43 | 44 | if (!_wlCore.DataType.isObject(_config)) { 45 | throw Error('requestInterceptorSuccessCb必须返回一个config对象'); 46 | } 47 | 48 | return _config; 49 | } 50 | 51 | return config; 52 | }, function (error) { 53 | return Promise.reject(error); 54 | }); 55 | }; 56 | /** 57 | * @method 配置响应拦截器 58 | * @param {Object} instance axios实例 59 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 60 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 61 | * @param {Number} retry 非必填 请求失败自动重试次数 默认2 62 | * @param {Number} retryDelay 非必填 请求失败自动重试时间间隔 默认1000ms 63 | */ 64 | 65 | 66 | var _configResponseInterceptor = function _configResponseInterceptor(instance, responseInterceptorSuccessCb, responseInterceptorErrorCb, retry, retryDelay) { 67 | // 自动重试机制 68 | instance.defaults.retry = retry; 69 | instance.defaults.retryDelay = retryDelay; // 响应拦截器 70 | 71 | instance.interceptors.response.use(function (res) { 72 | if (responseInterceptorSuccessCb) { 73 | var _res = responseInterceptorSuccessCb(res); 74 | 75 | if (!_wlCore.DataType.isObject(_res)) { 76 | throw Error('responseInterceptorSuccessCb必须返回一个response对象'); 77 | } 78 | 79 | return _res; 80 | } 81 | 82 | return res; 83 | }, function (err) { 84 | var _errres$status, _errres$config$url, _errres$config; 85 | 86 | var config = err.config; 87 | var errres = err.response; 88 | var err_type = (_errres$status = errres === null || errres === void 0 ? void 0 : errres.status) !== null && _errres$status !== void 0 ? _errres$status : 0; // 收集错误信息 89 | 90 | switch (err_type) { 91 | case 400: 92 | err.message = "请求无效"; 93 | break; 94 | 95 | case 401: 96 | err.message = "由于长时间未操作,登录已超时,请重新登录"; 97 | break; 98 | 99 | case 403: 100 | err.message = "拒绝访问"; 101 | break; 102 | 103 | case 404: 104 | err.message = "\u8BF7\u6C42\u5730\u5740\u51FA\u9519: ".concat((_errres$config$url = errres === null || errres === void 0 ? void 0 : (_errres$config = errres.config) === null || _errres$config === void 0 ? void 0 : _errres$config.url) !== null && _errres$config$url !== void 0 ? _errres$config$url : '/'); 105 | break; 106 | 107 | case 405: 108 | err.message = "\u672A\u6388\u6743"; 109 | break; 110 | 111 | case 408: 112 | err.message = "请求超时"; 113 | break; 114 | 115 | case 500: 116 | err.message = "服务器内部错误"; 117 | break; 118 | 119 | case 501: 120 | err.message = "服务未实现"; 121 | break; 122 | 123 | case 502: 124 | err.message = "网关错误"; 125 | break; 126 | 127 | case 503: 128 | err.message = "服务不可用"; 129 | break; 130 | 131 | case 504: 132 | err.message = "网关超时"; 133 | break; 134 | 135 | case 505: 136 | err.message = "HTTP版本不受支持"; 137 | break; 138 | 139 | default: 140 | err.message = "网络波动,请重试"; 141 | } // If config does not exist or the retry option is not set, reject 142 | 143 | 144 | if (!config || !config.retry) return Promise.reject(err); // Set the variable for keeping track of the retry count 145 | 146 | config.__retryCount = config.__retryCount || 0; // Check if we've maxed out the total number of retries 147 | 148 | if (config.__retryCount >= config.retry) { 149 | // 自定义重复请求后失败的回调 150 | if (responseInterceptorErrorCb) { 151 | var _res = responseInterceptorErrorCb(err); 152 | 153 | if (!_wlCore.DataType.isObject(err === null || err === void 0 ? void 0 : err.config)) { 154 | throw Error('responseInterceptorErrorCb必须返回一个err包含{config,response}'); 155 | } 156 | 157 | return Promise.reject(_res); 158 | } // Reject with the error 159 | 160 | 161 | return Promise.reject(err); 162 | } // Increase the retry count 163 | 164 | 165 | config.__retryCount += 1; // Create new promise to handle exponential backoff 166 | 167 | var backoff = new Promise(function (resolve) { 168 | setTimeout(function () { 169 | resolve(); 170 | }, config.retryDelay || 1); 171 | }); // Return the promise in which recalls axios to retry the request 172 | 173 | return backoff.then(function () { 174 | if (config.baseURL) { 175 | config.url = config.url.replace(config.baseURL, ""); 176 | } 177 | 178 | return instance(config); 179 | }); 180 | }); 181 | }; 182 | 183 | var Fetch = /*#__PURE__*/function () { 184 | function Fetch() { 185 | _classCallCheck(this, Fetch); 186 | 187 | // this.__successCode__ = _httpCode.ok; 188 | this.__http__ = null; 189 | } 190 | /** 191 | * @method 创建axios实例 192 | * @param {Object} param0 配置项 193 | * @description retry:Number 请求失败自动重连次数 默认2 194 | * @description retryDelay:Number 请求失败自动重连时间间隔 默认1000ms 195 | * @description withCredentials:Boolean 开启请求跨域 默认true 196 | * @description headers:Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 197 | * @description timeout:Number 请求超时时间 默认5000 198 | * @description baseURL:String 请求地址前缀 默认'' 199 | * @description successCode:Number //废弃 后台请求成功状态码,默认200 将会把所有非200的请求回调归入reject 200 | * @description expand:Object 其他需要扩展的配置项 other 201 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 202 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 203 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 204 | * @returns 返回创建后的axios实例 205 | */ 206 | 207 | 208 | _createClass(Fetch, null, [{ 209 | key: "create", 210 | value: function create() { 211 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 212 | _ref$retry = _ref.retry, 213 | retry = _ref$retry === void 0 ? _settings._httpOptions.retry : _ref$retry, 214 | _ref$retryDelay = _ref.retryDelay, 215 | retryDelay = _ref$retryDelay === void 0 ? _settings._httpOptions.retryDelay : _ref$retryDelay, 216 | _ref$withCredentials = _ref.withCredentials, 217 | withCredentials = _ref$withCredentials === void 0 ? _settings._httpOptions.withCredentials : _ref$withCredentials, 218 | _ref$headers = _ref.headers, 219 | headers = _ref$headers === void 0 ? _settings._httpOptions.headers : _ref$headers, 220 | _ref$timeout = _ref.timeout, 221 | timeout = _ref$timeout === void 0 ? _settings._httpOptions.timeout : _ref$timeout, 222 | _ref$baseURL = _ref.baseURL, 223 | baseURL = _ref$baseURL === void 0 ? _settings._httpOptions.baseURL : _ref$baseURL, 224 | expand = _objectWithoutProperties(_ref, ["retry", "retryDelay", "withCredentials", "headers", "timeout", "baseURL"]); 225 | 226 | var requestInterceptorSuccessCb = arguments.length > 1 ? arguments[1] : undefined; 227 | var responseInterceptorSuccessCb = arguments.length > 2 ? arguments[2] : undefined; 228 | var responseInterceptorErrorCb = arguments.length > 3 ? arguments[3] : undefined; 229 | 230 | // 处理类内部successCode 231 | // this._successCode = successCode ?? _httpCode.ok; 232 | // 整理配置项 233 | var _options = _objectSpread({ 234 | baseURL: baseURL, 235 | withCredentials: withCredentials, 236 | headers: headers, 237 | timeout: timeout 238 | }, expand); // 创建axios实例 239 | 240 | 241 | var _http = _axios["default"].create(_options); // 注册请求拦截器 242 | 243 | 244 | _configRequestInterceptor(_http, requestInterceptorSuccessCb); // 注册响应拦截器 245 | 246 | 247 | _configResponseInterceptor(_http, responseInterceptorSuccessCb, responseInterceptorErrorCb, retry, retryDelay); 248 | 249 | this.__http__ = _http; 250 | return _http; 251 | } 252 | /** 253 | * 通过向 axios 传递相关配置来创建单个请求 254 | * @param {Object} param0 255 | * @description url:String 请求地址 256 | * @description method:String 请求方法类型 默认post 257 | * @description params:Object 即将与请求一起发送的 URL 参数 258 | * @description data:Object 作为请求主体被发送的数据 259 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 260 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 261 | */ 262 | 263 | }, { 264 | key: "axios", 265 | value: function axios() { 266 | var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 267 | url = _ref2.url, 268 | _ref2$method = _ref2.method, 269 | method = _ref2$method === void 0 ? _settings._httpOptions.method : _ref2$method, 270 | params = _ref2.params, 271 | data = _ref2.data, 272 | instance = _ref2.instance, 273 | expand = _objectWithoutProperties(_ref2, ["url", "method", "params", "data", "instance"]); 274 | 275 | // 废弃 返回一个新的promise,注意:此promise将把http错误和与create axios时 276 | // 整理请求参数 277 | var _options = _objectSpread({ 278 | url: url, 279 | method: method, 280 | params: params, 281 | data: data 282 | }, expand); // 处理请求并直接返回_http() 283 | 284 | 285 | var _http = instance ? instance() : this.__http__; 286 | 287 | return _http(_options); 288 | } 289 | /** 290 | * 执行多个并发请求 291 | * @param {Array} list axios Promise 对象 292 | */ 293 | 294 | }, { 295 | key: "all", 296 | value: function all(list) { 297 | if (!_wlCore.DataType.isArray(list)) { 298 | throw Error('必须传入一个数组!'); 299 | } 300 | 301 | return this.__http__.all(list); 302 | } 303 | }]); 304 | 305 | return Fetch; 306 | }(); 307 | 308 | exports["default"] = Fetch; -------------------------------------------------------------------------------- /http/dist/utils/http.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | 8 | var _fetch = _interopRequireDefault(require("./fetch")); 9 | 10 | var _settings = require("../config/settings"); 11 | 12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 13 | 14 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 15 | 16 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 17 | 18 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 19 | 20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21 | 22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 25 | 26 | // 导入配置项 27 | var Http = /*#__PURE__*/function () { 28 | /** 29 | * 30 | * @param {Object} axios 外部axios实例 无特殊情况不要使用此参数; 如果传入则表示使用自定义axios实例,后续参数将不会产生作用 31 | * @param {Object} axiosOptions Fetch.create 32 | * @description retry:Number 请求失败自动重连次数 默认2 33 | * @description retryDelay:Number 请求失败自动重连时间间隔 默认1000ms 34 | * @description withCredentials:Boolean 开启请求跨域 默认true 35 | * @description headers:Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 36 | * @description timeout:Number 请求超时时间 默认5000 37 | * @description baseURL:String 请求地址前缀 默认'' 38 | * @description successCode:Number //废弃 后台请求成功状态码,默认200 将会把所有非200的请求回调归入reject 39 | * @description expand:Object 其他需要扩展的配置项 other 40 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 41 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 42 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 43 | */ 44 | function Http() { 45 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 46 | axios = _ref.axios, 47 | axiosOptions = _ref.axiosOptions, 48 | requestInterceptorSuccessCb = _ref.requestInterceptorSuccessCb, 49 | responseInterceptorSuccessCb = _ref.responseInterceptorSuccessCb, 50 | responseInterceptorErrorCb = _ref.responseInterceptorErrorCb; 51 | 52 | _classCallCheck(this, Http); 53 | 54 | this.__http__ = axios || _fetch["default"].create(axiosOptions, requestInterceptorSuccessCb, responseInterceptorSuccessCb, responseInterceptorErrorCb); 55 | } 56 | /** 57 | * get方法请求 58 | * @param {Object} options 59 | * @description url:String 请求地址 60 | * @description params:Object 即将与请求一起发送的 URL 参数 61 | * @description data:Object 作为请求主体被发送的数据 62 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 63 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 64 | */ 65 | 66 | 67 | _createClass(Http, [{ 68 | key: "get", 69 | value: function get(options) { 70 | return _fetch["default"].axios(_objectSpread(_objectSpread({}, options), {}, { 71 | method: _settings._httpType.GET 72 | })); 73 | } 74 | /** 75 | * post方法请求 76 | * @param {Object} options 77 | * @description url:String 请求地址 78 | * @description params:Object 即将与请求一起发送的 URL 参数 79 | * @description data:Object 作为请求主体被发送的数据 80 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 81 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 82 | */ 83 | 84 | }, { 85 | key: "post", 86 | value: function post(options) { 87 | return _fetch["default"].axios(_objectSpread(_objectSpread({}, options), {}, { 88 | method: _settings._httpType.POST 89 | })); 90 | } 91 | /** 92 | * 执行多个并发请求 93 | * @param {Array} list axios Promise 对象 94 | */ 95 | 96 | }, { 97 | key: "all", 98 | value: function all(list) { 99 | return _fetch["default"].all(list); 100 | } 101 | /** 102 | * delete方法请求 103 | * @param {Object} options 104 | * @description url:String 请求地址 105 | * @description params:Object 即将与请求一起发送的 URL 参数 106 | * @description data:Object 作为请求主体被发送的数据 107 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 108 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 109 | */ 110 | 111 | }, { 112 | key: "del", 113 | value: function del(options) { 114 | return _fetch["default"].axios(_objectSpread(_objectSpread({}, options), {}, { 115 | method: _settings._httpType.DELETE 116 | })); 117 | } 118 | /** 119 | * put方法请求 120 | * @param {Object} options 121 | * @description url:String 请求地址 122 | * @description params:Object 即将与请求一起发送的 URL 参数 123 | * @description data:Object 作为请求主体被发送的数据 124 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 125 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 126 | */ 127 | 128 | }, { 129 | key: "put", 130 | value: function put(options) { 131 | return _fetch["default"].axios(_objectSpread(_objectSpread({}, options), {}, { 132 | method: _settings._httpType.PUT 133 | })); 134 | } 135 | /** 136 | * put方法请求 137 | * @param {Object} options 138 | * @description url:String 请求地址 139 | * @description params:Object 即将与请求一起发送的 URL 参数 140 | * @description data:Object 作为请求主体被发送的数据 141 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 142 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 143 | */ 144 | 145 | }, { 146 | key: "patch", 147 | value: function patch(options) { 148 | return _fetch["default"].axios(_objectSpread(_objectSpread({}, options), {}, { 149 | method: _settings._httpType.PATCH 150 | })); 151 | } 152 | }]); 153 | 154 | return Http; 155 | }(); 156 | 157 | exports["default"] = Http; -------------------------------------------------------------------------------- /http/dist/utils/mock.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.param2Obj = param2Obj; 7 | exports.mockXHR = mockXHR; 8 | 9 | var _mockjs = _interopRequireDefault(require("mockjs")); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 12 | 13 | function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } 14 | 15 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 16 | 17 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 18 | 19 | function param2Obj(url) { 20 | var search = url.split('?')[1]; 21 | 22 | if (!search) { 23 | return {}; 24 | } 25 | 26 | return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"').replace(/\+/g, ' ') + '"}'); 27 | } 28 | 29 | function mockXHR() { 30 | var mocks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 31 | // mock patch 32 | // https://github.com/nuysoft/Mock/issues/300 33 | _mockjs["default"].XHR.prototype.proxy_send = _mockjs["default"].XHR.prototype.send; 34 | 35 | _mockjs["default"].XHR.prototype.send = function () { 36 | if (this.custom.xhr) { 37 | this.custom.xhr.withCredentials = this.withCredentials || false; 38 | 39 | if (this.responseType) { 40 | this.custom.xhr.responseType = this.responseType; 41 | } 42 | } 43 | 44 | this.proxy_send.apply(this, arguments); 45 | }; 46 | 47 | function XHR2ExpressReqWrap(respond) { 48 | return function (options) { 49 | var result = null; 50 | 51 | if (respond instanceof Function) { 52 | var body = options.body, 53 | type = options.type, 54 | url = options.url; // https://expressjs.com/en/4x/api.html#req 55 | 56 | result = respond({ 57 | method: type, 58 | body: JSON.parse(body), 59 | query: param2Obj(url) 60 | }); 61 | } else { 62 | result = respond; 63 | } 64 | 65 | return _mockjs["default"].mock(result); 66 | }; 67 | } 68 | 69 | var _iterator = _createForOfIteratorHelper(mocks), 70 | _step; 71 | 72 | try { 73 | for (_iterator.s(); !(_step = _iterator.n()).done;) { 74 | var i = _step.value; 75 | 76 | if (i.intercept) { 77 | var _iterator2 = _createForOfIteratorHelper(i.fetchs), 78 | _step2; 79 | 80 | try { 81 | for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { 82 | var fetch = _step2.value; 83 | 84 | _mockjs["default"].mock(new RegExp(fetch.url), fetch.type || 'get', XHR2ExpressReqWrap(fetch.response)); 85 | } 86 | } catch (err) { 87 | _iterator2.e(err); 88 | } finally { 89 | _iterator2.f(); 90 | } 91 | } 92 | } 93 | } catch (err) { 94 | _iterator.e(err); 95 | } finally { 96 | _iterator.f(); 97 | } 98 | } // const responseFake = (url, type, respond) => { 99 | // return { 100 | // url: new RegExp(`/mock${url}`), 101 | // type: type || 'get', 102 | // response(req, res) { 103 | // res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) 104 | // } 105 | // } 106 | // } 107 | // export default mocks.map(route => { 108 | // return responseFake(route.url, route.type, route.response) 109 | // }) -------------------------------------------------------------------------------- /http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wl-http", 3 | "version": "1.1.3", 4 | "description": "基础通信库封装", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/babel src --out-dir dist", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/wl-ui/wl.git" 13 | }, 14 | "keywords": [ 15 | "wl-http", 16 | "http", 17 | "axios", 18 | "mock", 19 | "mockjs", 20 | "vue" 21 | ], 22 | "author": "weilan <17796646068@163.com>", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/wl-ui/wl/issues" 26 | }, 27 | "homepage": "https://github.com/wl-ui/wl#readme", 28 | "devDependencies": { 29 | "@babel/cli": "^7.4.4", 30 | "@babel/core": "^7.4.4", 31 | "@babel/plugin-proposal-class-properties": "^7.8.3", 32 | "@babel/preset-env": "^7.4.4" 33 | }, 34 | "dependencies": { 35 | "axios": "^0.18.0", 36 | "mockjs": "^1.1.0", 37 | "wl-core": "^1.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /http/src/config/settings.js: -------------------------------------------------------------------------------- 1 | // 基础配置项 2 | 3 | export const _httpOptions = { 4 | baseURL: '', // api的base_url 5 | retry: 2, 6 | retryDelay: 1000, 7 | withCredentials: true, 8 | headers: { 9 | "Content-Type": "application/json;charset=UTF-8" 10 | }, 11 | timeout: 5000, // request timeout 12 | method: 'post' // 默认请求方法 13 | } 14 | 15 | // http类型 16 | export const _httpType = { 17 | GET: 'get', 18 | POST: 'post', 19 | PUT: 'put', 20 | PATCH: 'patch', 21 | DELETE: 'delete' 22 | } 23 | 24 | // 后台状态码 25 | export const _httpCode = { 26 | ok: 200, 27 | err: 300, 28 | noAuth: 401 29 | } -------------------------------------------------------------------------------- /http/src/index.js: -------------------------------------------------------------------------------- 1 | import Http from "./utils/http" 2 | 3 | export default Http; 4 | 5 | export { mockXHR } from "./utils/mock" -------------------------------------------------------------------------------- /http/src/utils/fetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @description axios通信类封装 4 | * @time 2020.04.20 5 | */ 6 | 7 | import axios from "axios"; // 导入axios库 8 | import { DataType } from "wl-core" // 导入wl基础核心库 9 | import { _httpOptions } from "../config/settings"; // 导入配置项 10 | 11 | /** 12 | * @method 配置请求拦截器 13 | * @param {Object} instance axios实例 14 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 15 | */ 16 | const _configRequestInterceptor = (instance, requestInterceptorSuccessCb) => { 17 | instance.interceptors.request.use(config => { 18 | if (requestInterceptorSuccessCb) { 19 | const _config = requestInterceptorSuccessCb(config); 20 | if (!DataType.isObject(_config)) { 21 | throw Error('requestInterceptorSuccessCb必须返回一个config对象') 22 | } 23 | return _config; 24 | } 25 | return config; 26 | }, error => { 27 | return Promise.reject(error); 28 | }) 29 | } 30 | 31 | /** 32 | * @method 配置响应拦截器 33 | * @param {Object} instance axios实例 34 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 35 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 36 | * @param {Number} retry 非必填 请求失败自动重试次数 默认2 37 | * @param {Number} retryDelay 非必填 请求失败自动重试时间间隔 默认1000ms 38 | */ 39 | const _configResponseInterceptor = (instance, responseInterceptorSuccessCb, responseInterceptorErrorCb, retry, retryDelay) => { 40 | // 自动重试机制 41 | instance.defaults.retry = retry; 42 | instance.defaults.retryDelay = retryDelay; 43 | // 响应拦截器 44 | instance.interceptors.response.use( 45 | res => { 46 | if (responseInterceptorSuccessCb) { 47 | const _res = responseInterceptorSuccessCb(res); 48 | if (!DataType.isObject(_res)) { 49 | throw Error('responseInterceptorSuccessCb必须返回一个response对象') 50 | } 51 | return _res; 52 | } 53 | return res; 54 | }, 55 | err => { 56 | let config = err.config; 57 | let errres = err.response; 58 | let err_type = errres?.status ?? 0; 59 | // 收集错误信息 60 | switch (err_type) { 61 | case 400: 62 | err.message = "请求无效"; 63 | break; 64 | 65 | case 401: 66 | err.message = "由于长时间未操作,登录已超时,请重新登录"; 67 | break; 68 | 69 | case 403: 70 | err.message = "拒绝访问"; 71 | break; 72 | 73 | case 404: 74 | err.message = `请求地址出错: ${errres?.config?.url ?? '/'}`; 75 | break; 76 | 77 | case 405: 78 | err.message = `未授权`; 79 | break; 80 | 81 | case 408: 82 | err.message = "请求超时"; 83 | break; 84 | 85 | case 500: 86 | err.message = "服务器内部错误"; 87 | break; 88 | 89 | case 501: 90 | err.message = "服务未实现"; 91 | break; 92 | 93 | case 502: 94 | err.message = "网关错误"; 95 | break; 96 | 97 | case 503: 98 | err.message = "服务不可用"; 99 | break; 100 | 101 | case 504: 102 | err.message = "网关超时"; 103 | break; 104 | 105 | case 505: 106 | err.message = "HTTP版本不受支持"; 107 | break; 108 | 109 | default: 110 | err.message = "网络波动,请重试"; 111 | } 112 | 113 | // If config does not exist or the retry option is not set, reject 114 | if (!config || !config.retry) return Promise.reject(err); 115 | 116 | // Set the variable for keeping track of the retry count 117 | config.__retryCount = config.__retryCount || 0; 118 | // Check if we've maxed out the total number of retries 119 | if (config.__retryCount >= config.retry) { 120 | // 自定义重复请求后失败的回调 121 | if (responseInterceptorErrorCb) { 122 | const _res = responseInterceptorErrorCb(err); 123 | if (!DataType.isObject(err?.config)) { 124 | throw Error('responseInterceptorErrorCb必须返回一个err包含{config,response}') 125 | } 126 | return Promise.reject(_res); 127 | } 128 | // Reject with the error 129 | return Promise.reject(err); 130 | } 131 | 132 | // Increase the retry count 133 | config.__retryCount += 1; 134 | 135 | // Create new promise to handle exponential backoff 136 | let backoff = new Promise((resolve) => { 137 | setTimeout(() => { 138 | resolve(); 139 | }, config.retryDelay || 1); 140 | }); 141 | 142 | // Return the promise in which recalls axios to retry the request 143 | return backoff.then(() => { 144 | if (config.baseURL) { 145 | config.url = config.url.replace(config.baseURL, ""); 146 | } 147 | return instance(config); 148 | }); 149 | } 150 | ); 151 | } 152 | 153 | export default class Fetch { 154 | constructor() { 155 | // this.__successCode__ = _httpCode.ok; 156 | this.__http__ = null; 157 | } 158 | 159 | /** 160 | * @method 创建axios实例 161 | * @param {Object} param0 配置项 162 | * @description retry:Number 请求失败自动重连次数 默认2 163 | * @description retryDelay:Number 请求失败自动重连时间间隔 默认1000ms 164 | * @description withCredentials:Boolean 开启请求跨域 默认true 165 | * @description headers:Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 166 | * @description timeout:Number 请求超时时间 默认5000 167 | * @description baseURL:String 请求地址前缀 默认'' 168 | * @description successCode:Number //废弃 后台请求成功状态码,默认200 将会把所有非200的请求回调归入reject 169 | * @description expand:Object 其他需要扩展的配置项 other 170 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 171 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 172 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 173 | * @returns 返回创建后的axios实例 174 | */ 175 | static create({ 176 | retry = _httpOptions.retry, 177 | retryDelay = _httpOptions.retryDelay, 178 | withCredentials = _httpOptions.withCredentials, 179 | headers = _httpOptions.headers, 180 | timeout = _httpOptions.timeout, 181 | baseURL = _httpOptions.baseURL, 182 | // successCode, 183 | ...expand 184 | } = {}, requestInterceptorSuccessCb, responseInterceptorSuccessCb, responseInterceptorErrorCb) { 185 | // 处理类内部successCode 186 | // this._successCode = successCode ?? _httpCode.ok; 187 | // 整理配置项 188 | const _options = { 189 | baseURL, 190 | withCredentials, 191 | headers, 192 | timeout, 193 | ...expand 194 | } 195 | // 创建axios实例 196 | const _http = axios.create(_options); 197 | // 注册请求拦截器 198 | _configRequestInterceptor(_http, requestInterceptorSuccessCb); 199 | // 注册响应拦截器 200 | _configResponseInterceptor(_http, responseInterceptorSuccessCb, responseInterceptorErrorCb, retry, retryDelay); 201 | this.__http__ = _http; 202 | return _http; 203 | } 204 | 205 | /** 206 | * 通过向 axios 传递相关配置来创建单个请求 207 | * @param {Object} param0 208 | * @description url:String 请求地址 209 | * @description method:String 请求方法类型 默认post 210 | * @description params:Object 即将与请求一起发送的 URL 参数 211 | * @description data:Object 作为请求主体被发送的数据 212 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 213 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 214 | */ 215 | static axios({ 216 | url, 217 | method = _httpOptions.method, 218 | params, 219 | data, 220 | instance, 221 | ...expand 222 | } = {}) { 223 | // 废弃 返回一个新的promise,注意:此promise将把http错误和与create axios时 224 | // 整理请求参数 225 | const _options = { 226 | url, 227 | method, 228 | params, 229 | data, 230 | ...expand 231 | } 232 | // 处理请求并直接返回_http() 233 | const _http = instance ? instance() : this.__http__; 234 | return _http(_options); 235 | } 236 | 237 | /** 238 | * 执行多个并发请求 239 | * @param {Array} list axios Promise 对象 240 | */ 241 | static all(list) { 242 | if (!DataType.isArray(list)) { 243 | throw Error('必须传入一个数组!'); 244 | } 245 | return this.__http__.all(list) 246 | } 247 | } -------------------------------------------------------------------------------- /http/src/utils/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @description 基于./fetch的请求封装 4 | * @time 2020.04.21 5 | */ 6 | 7 | import Fetch from './fetch' // 导入fetch类 8 | import { _httpType } from "../config/settings" // 导入配置项 9 | 10 | export default class Http { 11 | /** 12 | * 13 | * @param {Object} axios 外部axios实例 无特殊情况不要使用此参数; 如果传入则表示使用自定义axios实例,后续参数将不会产生作用 14 | * @param {Object} axiosOptions Fetch.create 15 | * @description retry:Number 请求失败自动重连次数 默认2 16 | * @description retryDelay:Number 请求失败自动重连时间间隔 默认1000ms 17 | * @description withCredentials:Boolean 开启请求跨域 默认true 18 | * @description headers:Object 请求头配置 默认"Content-Type": "application/json;charset=UTF-8" 19 | * @description timeout:Number 请求超时时间 默认5000 20 | * @description baseURL:String 请求地址前缀 默认'' 21 | * @description successCode:Number //废弃 后台请求成功状态码,默认200 将会把所有非200的请求回调归入reject 22 | * @description expand:Object 其他需要扩展的配置项 other 23 | * @param {Function} requestInterceptorSuccessCb 非必填 请求拦截器成功回调,必须返回一个config对象 24 | * @param {Function} responseInterceptorSuccessCb 非必填 响应拦截器成功回调,必须返回一个response对象 25 | * @param {Function} responseInterceptorErrorCb 非必填 响应拦截器失败回调,必须返回一个response对象 26 | */ 27 | constructor({ axios, axiosOptions, requestInterceptorSuccessCb, responseInterceptorSuccessCb, responseInterceptorErrorCb } = {}) { 28 | this.__http__ = axios || Fetch.create(axiosOptions, requestInterceptorSuccessCb, responseInterceptorSuccessCb, responseInterceptorErrorCb); 29 | } 30 | 31 | /** 32 | * get方法请求 33 | * @param {Object} options 34 | * @description url:String 请求地址 35 | * @description params:Object 即将与请求一起发送的 URL 参数 36 | * @description data:Object 作为请求主体被发送的数据 37 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 38 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 39 | */ 40 | get(options) { 41 | return Fetch.axios({ ...options, method: _httpType.GET }) 42 | } 43 | 44 | /** 45 | * post方法请求 46 | * @param {Object} options 47 | * @description url:String 请求地址 48 | * @description params:Object 即将与请求一起发送的 URL 参数 49 | * @description data:Object 作为请求主体被发送的数据 50 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 51 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 52 | */ 53 | post(options) { 54 | return Fetch.axios({ ...options, method: _httpType.POST }) 55 | } 56 | 57 | /** 58 | * 执行多个并发请求 59 | * @param {Array} list axios Promise 对象 60 | */ 61 | all(list) { 62 | return Fetch.all(list) 63 | } 64 | 65 | /** 66 | * delete方法请求 67 | * @param {Object} options 68 | * @description url:String 请求地址 69 | * @description params:Object 即将与请求一起发送的 URL 参数 70 | * @description data:Object 作为请求主体被发送的数据 71 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 72 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 73 | */ 74 | del(options) { 75 | return Fetch.axios({ ...options, method: _httpType.DELETE }) 76 | } 77 | 78 | /** 79 | * put方法请求 80 | * @param {Object} options 81 | * @description url:String 请求地址 82 | * @description params:Object 即将与请求一起发送的 URL 参数 83 | * @description data:Object 作为请求主体被发送的数据 84 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 85 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 86 | */ 87 | put(options) { 88 | return Fetch.axios({ ...options, method: _httpType.PUT }) 89 | } 90 | 91 | /** 92 | * put方法请求 93 | * @param {Object} options 94 | * @description url:String 请求地址 95 | * @description params:Object 即将与请求一起发送的 URL 参数 96 | * @description data:Object 作为请求主体被发送的数据 97 | * @description instance:Object 外部传入的axios实例,默认使用内部创建,无特殊需求不得在外部创建多余实例 98 | * @description expand:Object 扩展对象,其他不常用的axios(options)配置项放在expand字段传入,key值和axios文档一致 99 | */ 100 | patch(options) { 101 | return Fetch.axios({ ...options, method: _httpType.PATCH }) 102 | } 103 | } -------------------------------------------------------------------------------- /http/src/utils/mock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * auth: weilan 3 | * time: 2020.04.17 4 | * des: 搭建mock服务 5 | */ 6 | 7 | import Mock from 'mockjs' 8 | 9 | export function param2Obj(url) { 10 | const search = url.split('?')[1] 11 | if (!search) { 12 | return {} 13 | } 14 | return JSON.parse( 15 | '{"' + 16 | decodeURIComponent(search) 17 | .replace(/"/g, '\\"') 18 | .replace(/&/g, '","') 19 | .replace(/=/g, '":"') 20 | .replace(/\+/g, ' ') + 21 | '"}' 22 | ) 23 | } 24 | 25 | export function mockXHR(mocks = []) { 26 | // mock patch 27 | // https://github.com/nuysoft/Mock/issues/300 28 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 29 | Mock.XHR.prototype.send = function () { 30 | if (this.custom.xhr) { 31 | this.custom.xhr.withCredentials = this.withCredentials || false 32 | 33 | if (this.responseType) { 34 | this.custom.xhr.responseType = this.responseType 35 | } 36 | } 37 | this.proxy_send(...arguments) 38 | } 39 | 40 | function XHR2ExpressReqWrap(respond) { 41 | return function (options) { 42 | let result = null 43 | if (respond instanceof Function) { 44 | const { body, type, url } = options 45 | // https://expressjs.com/en/4x/api.html#req 46 | result = respond({ 47 | method: type, 48 | body: JSON.parse(body), 49 | query: param2Obj(url) 50 | }) 51 | } else { 52 | result = respond 53 | } 54 | return Mock.mock(result) 55 | } 56 | } 57 | 58 | for (const i of mocks) { 59 | if (i.intercept) { 60 | for (const fetch of i.fetchs) { 61 | Mock.mock(new RegExp(fetch.url), fetch.type || 'get', XHR2ExpressReqWrap(fetch.response)) 62 | } 63 | } 64 | } 65 | } 66 | 67 | // const responseFake = (url, type, respond) => { 68 | 69 | // return { 70 | // url: new RegExp(`/mock${url}`), 71 | // type: type || 'get', 72 | // response(req, res) { 73 | // res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) 74 | // } 75 | // } 76 | // } 77 | 78 | // export default mocks.map(route => { 79 | // return responseFake(route.url, route.type, route.response) 80 | // }) 81 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | # wl-baseui 2 | 3 | 基础ui库,不包含复杂组件,完整组件库见wlui 4 | 5 | ```js 6 | import WlBaseUi from "wl-base-ui" 7 | 8 | Vue.use(WlBaseUi) 9 | 10 | // 下为详细列表 11 | { 12 | WlAdd, // 防抖按钮 13 | WlContainer, // 页面视图盒子 14 | WlContextMenu, // 右键扩展组件 15 | WlFadeIn, // 右侧滑入组件 16 | WlInput, // 带校验的输入框组件 17 | WlScroll, // 滚动条美化组件 18 | WlTable, // 表格组件 19 | WlTableDynamic, // 动态表格组件 20 | } 21 | ``` 22 | ## Project setup 23 | ``` 24 | yarn install 25 | ``` 26 | 27 | ### Compiles and hot-reloads for development 28 | ``` 29 | yarn serve 30 | ``` 31 | 32 | ### Compiles and minifies for production 33 | ``` 34 | yarn build 35 | ``` 36 | 37 | ### Lints and fixes files 38 | ``` 39 | yarn lint 40 | ``` 41 | 42 | ### Customize configuration 43 | See [Configuration Reference](https://cli.vuejs.org/config/). 44 | -------------------------------------------------------------------------------- /ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "presets": [ 3 | "@vue/cli-plugin-babel/preset" 4 | ], 5 | "plugins": [ 6 | [ 7 | "component", 8 | { 9 | "libraryName": "element-ui", 10 | "styleLibraryName": "theme-chalk" 11 | } 12 | ] 13 | ] 14 | } -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wl-baseui", 3 | "version": "1.0.0", 4 | "private": false, 5 | "repository": { 6 | "type": "git", 7 | "url": "git+ssh://git@github.com/wl-ui/wl.git" 8 | }, 9 | "keywords": [ 10 | "wl-ui", 11 | "wlui", 12 | "wl-base-ui", 13 | "element-ui", 14 | "base-ui", 15 | "vue", 16 | "ui" 17 | ], 18 | "author": "weilan <17796646068@163.com>", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/wl-ui/wl/issues" 22 | }, 23 | "homepage": "https://github.com/wl-ui/wl#readme", 24 | "main": "dist/wl-baseui.umd.min.js", 25 | "scripts": { 26 | "serve": "vue-cli-service serve", 27 | "build": "vue-cli-service build --target lib --name wl-baseui --dest dist ./src/packages/index.js", 28 | "lint": "vue-cli-service lint" 29 | }, 30 | "dependencies": { 31 | "core-js": "^3.6.4", 32 | "element-ui": "^2.4.5", 33 | "vue": "^2.6.11", 34 | "wl-core": "^1.0.2" 35 | }, 36 | "devDependencies": { 37 | "@vue/cli-plugin-babel": "^4.3.0", 38 | "@vue/cli-plugin-eslint": "^4.3.0", 39 | "@vue/cli-service": "^4.3.0", 40 | "babel-eslint": "^10.1.0", 41 | "babel-plugin-component": "^1.1.1", 42 | "eslint": "^6.7.2", 43 | "eslint-plugin-vue": "^6.2.2", 44 | "node-sass": "^4.13.1", 45 | "sass-loader": "^8.0.2", 46 | "vue-cli-plugin-element": "^1.0.1", 47 | "vue-template-compiler": "^2.6.11" 48 | }, 49 | "eslintConfig": { 50 | "root": true, 51 | "env": { 52 | "node": true 53 | }, 54 | "extends": [ 55 | "plugin:vue/essential", 56 | "eslint:recommended" 57 | ], 58 | "parserOptions": { 59 | "parser": "babel-eslint" 60 | }, 61 | "rules": {} 62 | }, 63 | "browserslist": [ 64 | "> 1%", 65 | "last 2 versions", 66 | "not dead" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wl-ui/wl/d7c83593203b8a6ef428b142e50da39b27fce1a2/ui/public/favicon.ico -------------------------------------------------------------------------------- /ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 44 | 45 | 59 | -------------------------------------------------------------------------------- /ui/src/assets/css/clear.css: -------------------------------------------------------------------------------- 1 | body{line-height:1.666;color:#666;font-size:14px}body,input{font-family:"verdana"}body,h1,h2,h3,h4,h5,h6,ul,ol,li,p,dl,dt,dd,table,th,td{margin:0;padding:0}table,th,td,img{border:0}em,i,th{font-style:normal;text-decoration:none}h1,h2,h3,h4,h5,h6,th{font-size:100%;font-weight:normal}input,select,button,textarea,table{margin:0;font-family:inherit;font-size:100%}input,button{outline:none}ul,ol{list-style:none}table{border-collapse:collapse;border-spacing:0}th,caption{text-align:left}a{color:#666;text-decoration:none;outline:none;-webkit-tap-highlight-color:transparent}select{background-color:#fff}iframe{width:100%;height:100%;border:none} 2 | -------------------------------------------------------------------------------- /ui/src/assets/css/variables.css: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /ui/src/assets/css/variables.min.css: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /ui/src/assets/css/variables.scss: -------------------------------------------------------------------------------- 1 | // 头部样式变量 2 | $header-height: 60px; 3 | $header-background: #417bb7; //'#ededed', 4 | $header-color: #fff; //'#2a3f54', 5 | $header-padding: 15px; 6 | 7 | // 主视图区样式变量 8 | $main-base-color: #f2f3f3; 9 | $main-body-padding: 8px; 10 | 11 | // 间距大小 12 | $space-n: 8px; // 迷你间距 13 | $space-s: 10px; // 小间距 14 | $space-m: 12px; // 中间距 15 | $space-l: 15px; // 大间距 16 | $space-xl: 20px; // 大大间距 17 | 18 | // 色值 19 | $border-color-ddd: #dcdfe6; 20 | $border-color-eee: #e4e7ed; 21 | $border-color-fff: #f2f6fc; 22 | -------------------------------------------------------------------------------- /ui/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wl-ui/wl/d7c83593203b8a6ef428b142e50da39b27fce1a2/ui/src/assets/logo.png -------------------------------------------------------------------------------- /ui/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './plugins/element.js' 4 | import './plugins/wlui.js' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | render: h => h(App), 10 | }).$mount('#app') 11 | -------------------------------------------------------------------------------- /ui/src/packages/add/index.js: -------------------------------------------------------------------------------- 1 | import Add from "./index.vue"; 2 | 3 | Add.install = function (Vue) { 4 | Vue.component(Add.name, Add); 5 | }; 6 | 7 | export default Add; -------------------------------------------------------------------------------- /ui/src/packages/add/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 36 | 37 | -------------------------------------------------------------------------------- /ui/src/packages/container/index.js: -------------------------------------------------------------------------------- 1 | import Container from "./index.vue"; 2 | 3 | Container.install = function (Vue) { 4 | Vue.component(Container.name, Container); 5 | }; 6 | 7 | export default Container; -------------------------------------------------------------------------------- /ui/src/packages/container/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 36 | 37 | -------------------------------------------------------------------------------- /ui/src/packages/context-menu/components/contentmenuList/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | 50 | -------------------------------------------------------------------------------- /ui/src/packages/context-menu/index.js: -------------------------------------------------------------------------------- 1 | import ContextMenu from "./index.vue"; 2 | 3 | ContextMenu.install = function (Vue) { 4 | Vue.component(ContextMenu.name, ContextMenu); 5 | }; 6 | 7 | export default ContextMenu; -------------------------------------------------------------------------------- /ui/src/packages/context-menu/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 78 | 79 | 112 | -------------------------------------------------------------------------------- /ui/src/packages/fade-in/index.js: -------------------------------------------------------------------------------- 1 | import FadeIn from "./index.vue"; 2 | 3 | FadeIn.install = function (Vue) { 4 | Vue.component(FadeIn.name, FadeIn); 5 | }; 6 | 7 | export default FadeIn; -------------------------------------------------------------------------------- /ui/src/packages/fade-in/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 41 | 42 | -------------------------------------------------------------------------------- /ui/src/packages/index.js: -------------------------------------------------------------------------------- 1 | 2 | import Container from "./container"; 3 | import ContextMenu from "./context-menu"; 4 | import Table from "./table"; 5 | import Scroll from "./scroll"; 6 | import Add from "./add"; 7 | import FadeIn from "./fade-in"; 8 | import TableDynamic from "./table-dynamic"; 9 | import Input from "./input"; 10 | 11 | const components = [Container, ContextMenu, Table, Scroll, Add, FadeIn, TableDynamic, Input]; 12 | 13 | 14 | const install = function (Vue) { 15 | components.forEach(component => { 16 | Vue.component(component.name, component); 17 | }); 18 | }; 19 | 20 | if (typeof window !== "undefined" && window.Vue) { 21 | install(window.Vue); 22 | } 23 | 24 | export default { 25 | install, 26 | Container, 27 | ContextMenu, 28 | Table, 29 | Scroll, 30 | Add, 31 | FadeIn, 32 | TableDynamic, 33 | Input 34 | }; -------------------------------------------------------------------------------- /ui/src/packages/input/index.js: -------------------------------------------------------------------------------- 1 | import Input from "./index.vue"; 2 | 3 | Input.install = function (Vue) { 4 | Vue.component(Input.name, Input); 5 | }; 6 | 7 | export default Input; -------------------------------------------------------------------------------- /ui/src/packages/input/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 122 | 123 | -------------------------------------------------------------------------------- /ui/src/packages/scroll/index.js: -------------------------------------------------------------------------------- 1 | import Scroll from "./index.vue"; 2 | 3 | Scroll.install = function (Vue) { 4 | Vue.component(Scroll.name, Scroll); 5 | }; 6 | 7 | export default Scroll; -------------------------------------------------------------------------------- /ui/src/packages/scroll/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ui/src/packages/table-dynamic/index.js: -------------------------------------------------------------------------------- 1 | import TableDynamic from "./index.vue"; 2 | 3 | TableDynamic.install = function (Vue) { 4 | Vue.component(TableDynamic.name, TableDynamic); 5 | }; 6 | 7 | export default TableDynamic; -------------------------------------------------------------------------------- /ui/src/packages/table-dynamic/index.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 132 | 133 | -------------------------------------------------------------------------------- /ui/src/packages/table/index.js: -------------------------------------------------------------------------------- 1 | import Table from "./index.vue"; 2 | 3 | Table.install = function (Vue) { 4 | Vue.component(Table.name, Table); 5 | }; 6 | 7 | export default Table; -------------------------------------------------------------------------------- /ui/src/packages/table/index.vue: -------------------------------------------------------------------------------- 1 | 116 | 378 | -------------------------------------------------------------------------------- /ui/src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { Button, Row, Col, Loading, Input, Scrollbar } from 'element-ui' 3 | 4 | Vue.use(Button) 5 | Vue.use(Row) 6 | Vue.use(Col) 7 | Vue.use(Input) 8 | Vue.use(Scrollbar) 9 | Vue.use(Loading.directive); 10 | Vue.prototype.$loading = Loading.service; 11 | Vue.prototype.$ELEMENT = { size: 'small' }; 12 | -------------------------------------------------------------------------------- /ui/src/plugins/wlui.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import wlui from "../packages/index"; 3 | 4 | Vue.use(wlui); -------------------------------------------------------------------------------- /ui/vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | devServer: { 4 | hot: true, 5 | open: true, 6 | disableHostCheck: false, 7 | overlay: { 8 | warnings: false, 9 | errors: true 10 | }, 11 | }, 12 | configureWebpack: { 13 | externals: { 14 | vue: "Vue", 15 | 'element-ui': 'Element', 16 | } 17 | }, 18 | css: { 19 | extract: false, 20 | loaderOptions: { 21 | sass: { 22 | prependData: `@import "./src/assets/css/variables.scss";` 23 | } 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /vue/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": ["@babel/plugin-transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /vue/README.md: -------------------------------------------------------------------------------- 1 | # wl-vue 2 | 3 | vue基础层封装 4 | 5 | ### 1. 使用render函数创建vue实例:render.js 6 | 7 | ```js 8 | import { render } from "wl-vue" 9 | import App from "./App.vue"; 10 | import router from "./router"; 11 | import store from "./store"; 12 | 13 | const options = { 14 | root: App, // 必须 当前应用的根组件 一般是app.vue 15 | router: router, // 必须 router实例 16 | store: store, // 必须 store实例 17 | options: { // options 实例化vue配置项 下为详细注解 18 | fastclick: false, // 默认false 是否启用移动端快速点击插件 19 | cookie: false, // 默认false 是否启用vue-cookie操作插件 20 | lazyOptions: {}, // 默认null 启用图片VueLazyLoad懒加载插件时的配置项, 不传表示不启用 21 | filters: [], // 默认[] 过滤器数组 格式为 {name:"", rule: ()=>{}} 内置有date,dateTime时间格式化过滤器 22 | directives: [], // 默认[] 指令数组 格式为 {name:"", rule: ()=>{}} 内置有v-auth鉴权指令 23 | plugins: [], // 默认[] 插件数组 [wlui, el-input] 可以直接Vue.use()的插件数组 24 | fncBeforeVue: ()=>{}, // 实例化vue前可执行的回调函数 fncBeforeVue(vue){... 你的逻辑} 25 | auth: true, // 默认true 是否需要鉴权系统,如果不需要,后续参数无需再传 26 | }, 27 | routeOptions:{ // 路由守卫配置项 用于前端鉴权及异步路由 下为详细注解 28 | tokenKey: 'token', // 存储在local中的token的key 29 | dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 默认'app/setToken' 30 | dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 默认'menu/setMenu' 31 | dispatchSetMenuList: 'menu/setMenuList', // store设置一维菜单的actions命名空间 32 | dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 默认'menu/setPermissions' 33 | pathLogin: '/login', // 登录页的 router path 默认'/login' 34 | pathLogged: '/index', // 已登录后 再进登录页时自动重定向的 router path 默认'/index' 35 | apiFn: ()=>{}, // **必须** 获取菜单数据的api函数,返回值为一个promise 36 | vaJwtExpiredFn: ()=>{}, // 自定义校验jwt是否过期的函数 默认为比较jwt携带过期时间与当前时间比较,单位秒,传入表示自定义过期规则 37 | }, 38 | menuOptions:{ // 菜单数据解析为路由数据配置项 下为详细注解 39 | url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 40 | name: 'routerName', // 命名路由 41 | meta: 'meta', // 路由元数据 42 | children: 'children', // 子菜单字段 43 | permissions: 'permissions', // 按钮权限字段 44 | path404: 'error/404' // 404路径, 45 | mapPathFn: (item) => {} // 路由映射文件路径方法 必填 46 | }, 47 | nextRoutes:[] // 需要登录后插入的、非后台返回的路由列表 默认[] 48 | } 49 | const mount = "#app"; // 非必选 vue挂载dom 默认为#app 50 | 51 | // 实例化vue 52 | const vueRender = () => render(options, mount); 53 | 54 | export default vueRender; 55 | ``` 56 | 57 | ### 2. 在main.js内实例化vue 58 | 下面是 最少 & 必须 的配置项: 59 | ```js 60 | import { render } from "wl-vue" 61 | import App from "./App.vue"; 62 | import store from "./store"; 63 | import router from "./router"; 64 | import nextRoutes from "./router/next-router" 65 | import routeMap from "./router/map-router" 66 | import { getMenuApi } from "./api/menu" 67 | 68 | // 声明鉴权需要的参数 69 | const routeOptions = { 70 | apiFn: getMenuApi 71 | } 72 | 73 | // 声明菜单解析为路由所需参数 74 | const menuOptions = { 75 | mapPathFn: (item) => routeMap(item.url) 76 | } 77 | 78 | // 导出手动实例化vue函数 79 | const vueRender = () => render({ root: App, router, store, routeOptions, nextRoutes, menuOptions }); 80 | 81 | export default vueRender; 82 | ``` 83 | 84 | ### 3. 注意事项 85 | 86 | 1. 注意:菜单数据映射为路由数据默认你有一个layout路由且默认重定向至`/index`如下, 其他菜单路由均挂载在layout的children内 87 | ```js 88 | let userRouter = { 89 | path: "/layout", 90 | name: "layout", 91 | component: () => import('@/views/layout/index.vue'), 92 | redirect: '/index', 93 | children: [] 94 | }; 95 | ``` 96 | 2. 注意:路由的映射规则如下: 97 | ```js 98 | component: () => import(`@/views${url}/index.vue`) 99 | ``` 100 | 3. 注意:因为在封装里路由映射文件会找不到,因此需要在每个项目里传入一个映射方法 101 | src/router/map-router.js 102 | ```js 103 | module.exports = path => () => import(`@/views${path}/index.vue`); 104 | ``` 105 | 106 | 4. 注意:路由守卫检查store中是否已经存在用户菜单指定为: 107 | ```js 108 | store.getters.menu 109 | ``` 110 | 111 | ### 4. 其他方法 112 | ```js 113 | import { VaJwt } from "wl-vue" 114 | 115 | // VaJwt是一个验证和解析未加密jwt的类,提供了许多静态方法 116 | // console.log(VaJwt) 打印查看 117 | // 静态方法无需实例化可直接使用,例: 118 | const payload = VaJwt.payloadAtob(jwt); 119 | // 解析jwt中有效载荷内的数据 120 | ``` -------------------------------------------------------------------------------- /vue/dist/config/settings.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports._vueOptions = exports._routeGuardOptions = exports._menuDataOptions = void 0; 7 | // 菜单数据字段配置项 8 | var _menuDataOptions = { 9 | url: 'url', 10 | // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 11 | name: 'routerName', 12 | // 命名路由 13 | meta: 'meta', 14 | // 路由元数据 15 | children: 'children', 16 | // 子菜单字段 17 | permissions: 'permissions', 18 | // 按钮权限字段 19 | path404: 'error/404' 20 | }; 21 | exports._menuDataOptions = _menuDataOptions; 22 | var _routeGuardOptions = { 23 | tokenKey: 'token', 24 | // 存储在local中的token的key 25 | dispatchSetToken: 'app/setToken', 26 | // store设置token的actions命名空间 27 | dispatchSetMenu: 'menu/setMenu', 28 | // store设置菜单的actions命名空间 29 | dispatchSetMenuList: 'menu/setMenuList', 30 | // store设置一维菜单的actions命名空间 31 | dispatchSetPermissions: 'menu/setPermissions', 32 | // store设置按钮权限码的actions命名空间 33 | pathLogin: '/login', 34 | // 登录页的 router path 35 | pathLogged: '/index', 36 | // 已登录后 再进登录页要重定向的 router path 37 | apiFn: null, 38 | // 获取菜单数据的api函数 39 | vaJwtExpiredFn: null // 自定义校验jwt是否过期的函数 40 | 41 | }; // 实例化vue配置项 42 | 43 | exports._routeGuardOptions = _routeGuardOptions; 44 | var _vueOptions = { 45 | mount: '#app' 46 | }; 47 | exports._vueOptions = _vueOptions; -------------------------------------------------------------------------------- /vue/dist/config/settins.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports._vueOptions = exports._routeGuardOptions = exports._menuDataOptions = void 0; 7 | // 菜单数据字段配置项 8 | var _menuDataOptions = { 9 | url: 'url', 10 | // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 11 | name: 'routerName', 12 | // 命名路由 13 | meta: 'meta', 14 | // 路由元数据 15 | children: 'children', 16 | // 子菜单字段 17 | permissions: 'permissions', 18 | // 按钮权限字段 19 | path404: 'error/404' 20 | }; 21 | exports._menuDataOptions = _menuDataOptions; 22 | var _routeGuardOptions = { 23 | tokenKey: 'token', 24 | // 存储在local中的token的key 25 | dispatchSetToken: 'app/setToken', 26 | // store设置token的actions命名空间 27 | dispatchSetMenu: 'menu/setMenu', 28 | // store设置菜单的actions命名空间 29 | dispatchSetPermissions: 'menu/setPermissions', 30 | // store设置按钮权限码的actions命名空间 31 | pathLogin: '/login', 32 | // 登录页的 router path 33 | pathLogged: '/index', 34 | // 已登录后 再进登录页要重定向的 router path 35 | apiFn: 'getPermsApi', 36 | // 获取菜单数据的api函数 37 | vaJwtExpiredFn: 'vaJwtExpired' // 自定义校验jwt是否过期的函数 38 | 39 | }; // 实例化vue配置项 40 | 41 | exports._routeGuardOptions = _routeGuardOptions; 42 | var _vueOptions = { 43 | mount: '#app' 44 | }; 45 | exports._vueOptions = _vueOptions; -------------------------------------------------------------------------------- /vue/dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | Object.defineProperty(exports, "render", { 9 | enumerable: true, 10 | get: function get() { 11 | return _render["default"]; 12 | } 13 | }); 14 | Object.defineProperty(exports, "VaJwt", { 15 | enumerable: true, 16 | get: function get() { 17 | return _vaAuth["default"]; 18 | } 19 | }); 20 | 21 | var _render = _interopRequireDefault(require("./utils/init/render")); 22 | 23 | var _vaAuth = _interopRequireDefault(require("./utils/auth/va-auth")); -------------------------------------------------------------------------------- /vue/dist/utils/auth/async-routes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 11 | 12 | var _interopRequireWildcard2 = _interopRequireDefault(require("@babel/runtime/helpers/interopRequireWildcard")); 13 | 14 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 15 | 16 | var _wlCore = require("wl-core"); 17 | 18 | var _settings = require("../../config/settings"); 19 | 20 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 21 | 22 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 23 | 24 | /** 25 | * 异步推入鉴权路由 要求必须存在@/views/layout/index.vue主体视图盒子和/index首页路径 26 | * @param {Array} data 菜单数据 27 | * @param {Array} nextRoutes 需要登录后插入的 非后台返回的 路由列表 28 | * @param {Object} options 菜单数据解析为路由数据配置项 下面是字段及默认值说明 29 | * @description url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 30 | * @description name: 'routerName', // 命名路由 31 | * @description meta: 'meta', // 路由元数据 32 | * @description children: 'children', // 子菜单字段 33 | * @description permissions: 'permissions', // 按钮权限字段 34 | * @description path404: 'error/404' // 404路径 35 | * @description mapPathFn: ()=>{} // 路由映射文件路径函数 36 | * @returns {Object} {routes: 整理好的异步路由router.addRoutes()即可, permissions: 权限code码} 37 | */ 38 | var asyncRoutes = function asyncRoutes(data, nextRoutes, options) { 39 | if (!_wlCore.DataType.isObject(options)) throw Error('options 必须是一个对象!'); 40 | 41 | var _options = _objectSpread(_objectSpread({}, _settings._menuDataOptions), options); 42 | 43 | if (!_options.mapPathFn) throw Error('options 内必须有路由映射真实路径方法 mapPathFn!'); // 主视图路由 44 | 45 | var userRouter = { 46 | path: "/layout", 47 | name: "layout", 48 | component: function component() { 49 | return Promise.resolve().then(function () { 50 | return (0, _interopRequireWildcard2["default"])(require('@/views/layout/index.vue')); 51 | }); 52 | }, 53 | redirect: '/index', 54 | children: [] 55 | }; // 创建路由盒子 56 | 57 | var routerBox = []; // 创建权限码数组 58 | 59 | var permissions = []; // 将菜单数据处理为一维函数 60 | 61 | var menu = (0, _wlCore.flattenDeep)(data, _options.children); // 处理路由映射真实路径,放在封装里babel之后就失效了,暂时不提供这个公共方法,在每个项目里写一遍吧 62 | // let routeMapFile = _options.mapPathFn ? _options.mapPathFn : routeMap; 63 | // 遍历处理路由 64 | 65 | menu.forEach(function (item) { 66 | var _url = item[_options.url]; 67 | if (!_url) return; 68 | 69 | try { 70 | var routerItem = { 71 | path: _url, 72 | // 路由路径名 73 | name: item[_options.name], 74 | // 命名路由 用于配合菜单简洁跳转 75 | meta: item[_options.meta], 76 | // 路由元信息 定义路由时即可携带的参数,可用来管理每个路由的按钮操作权限 77 | component: _options.mapPathFn(item) // 路由映射真实视图路径 78 | 79 | }; // 将所有权限码收集存入store 80 | 81 | var _permissions = item[_options.permissions]; 82 | if (_wlCore.DataType.isArray(_permissions)) permissions.push.apply(permissions, (0, _toConsumableArray2["default"])(_permissions)); 83 | routerBox.push(routerItem); 84 | } catch (err) { 85 | throw Error('路由映射规则为:@/views${url}/index.vue', err); 86 | } 87 | }); // 推入需要异步加载的,非服务端获取的功能性页面 88 | 89 | routerBox.push.apply(routerBox, (0, _toConsumableArray2["default"])(nextRoutes)); 90 | userRouter.children = routerBox; 91 | var errorBox = { 92 | path: "*", 93 | redirect: _options.path404 94 | }; 95 | return { 96 | routes: [userRouter, errorBox], 97 | menuList: menu, 98 | permissions: permissions 99 | }; 100 | }; 101 | 102 | var _default = asyncRoutes; 103 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/auth/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _routerGuard = _interopRequireDefault(require("./router-guard")); 11 | 12 | var _default = _routerGuard["default"]; 13 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/auth/map-router.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | var _interopRequireWildcard2 = _interopRequireDefault(require("@babel/runtime/helpers/interopRequireWildcard")); 6 | 7 | /** 8 | * @author weilan 9 | * @time 2020.04.26 10 | * @description 将路由映射到真实路径,babel之后会失效,将这句代码在每个项目中复制,也可修改映射规则 11 | */ 12 | module.exports = function (path) { 13 | return function () { 14 | return Promise.resolve("@/views".concat(path, "/index.vue")).then(function (s) { 15 | return (0, _interopRequireWildcard2["default"])(require(s)); 16 | }); 17 | }; 18 | }; -------------------------------------------------------------------------------- /vue/dist/utils/auth/router-guard.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); 11 | 12 | var _wlCore = require("wl-core"); 13 | 14 | var _vaAuth = _interopRequireDefault(require("./va-auth")); 15 | 16 | var _asyncRoutes2 = _interopRequireDefault(require("./async-routes")); 17 | 18 | var _settings = require("../../config/settings"); 19 | 20 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } 21 | 22 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 23 | 24 | // 路由守卫配置项 25 | 26 | /** 27 | * 注册路由守卫 28 | * @param {*} router router实例 29 | * @param {*} store vuex实例 30 | * @param {*} routeOptions 注册路由配置项 下为详细注解 31 | * @description tokenKey: 'token', // 存储在local中的token的key 32 | * @description dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 33 | * @description dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 34 | * @description dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 35 | * @description pathLogin: '/login', // 登录页的 router path 36 | * @description pathLogged: '/index', // 已登录后 再进登录页要重定向的 router path 37 | * @description apiFn: ()={}, // 获取菜单数据的api函数 38 | * @description vaJwtExpiredFn: ()={}, // 自定义校验jwt是否过期的函数 39 | * @param {*} menuOptions 菜单数据解析为路由数据配置项 40 | * @param {*} nextRoutes 需要登录后插入的 非后台返回的 路由列表 41 | */ 42 | var registerRouteGuard = function registerRouteGuard(router, store, routeOptions, menuOptions, nextRoutes) { 43 | if (!_wlCore.DataType.isObject(routeOptions)) throw Error('routeOptions 必须是一个对象!'); 44 | 45 | var _option = _objectSpread(_objectSpread({}, _settings._routeGuardOptions), routeOptions); 46 | 47 | if (!(_option === null || _option === void 0 ? void 0 : _option.apiFn)) throw Error('apiFn lost!缺少获取菜单数据的api函数!'); 48 | router.beforeEach(function (to, from, next) { 49 | // 检查是否存在登录状态 50 | var _jwt = _wlCore.Storage.get(_option.tokenKey); // 存在登陆状态 51 | 52 | 53 | if (_jwt && _jwt != 'undefined') { 54 | // 第一次打开页面token过期进入登陆页 55 | if (_vaAuth["default"].vaJwtExpired(_jwt, _option.vaJwtExpiredFn)) { 56 | store.dispatch(_option.dispatchSetToken, ''); 57 | 58 | _wlCore.Storage.remove(_option.tokenKey); 59 | 60 | next({ 61 | path: _option.pathLogin 62 | }); 63 | return; 64 | } // 没过期自动登录 65 | 66 | 67 | store.dispatch(_option.dispatchSetToken, _jwt); // 判断当前用户是否已拉取权限菜单 68 | 69 | if (store.getters.menu.length === 0) { 70 | _option.apiFn().then(function (_ref) { 71 | var data = _ref.data; 72 | 73 | var _menu = data.data || []; 74 | /* */ 75 | 76 | 77 | var _asyncRoutes = (0, _asyncRoutes2["default"])(_menu, nextRoutes, menuOptions), 78 | routes = _asyncRoutes.routes, 79 | permissions = _asyncRoutes.permissions, 80 | menuList = _asyncRoutes.menuList; 81 | 82 | router.addRoutes(routes); // 推入异步路由 83 | 84 | store.dispatch(_option.dispatchSetMenu, _menu); // 将菜单数据存入store 85 | 86 | store.dispatch(_option.dispatchSetMenuList, menuList); // 将菜单一维化数据存入store 87 | 88 | store.dispatch(_option.dispatchSetPermissions, permissions); // 将权限码数据存入store 89 | 90 | next(_objectSpread(_objectSpread({}, to), {}, { 91 | replace: true 92 | })); 93 | })["catch"](); 94 | 95 | return; 96 | } // 已登录状态 去往登录页时自动重定向至配置页 其他跳转正常进行 97 | 98 | 99 | to.path === _option.pathLogin ? next(_option.pathLogged) : next(); 100 | return; 101 | } // 无登录状态时 可进入白名单页面 去其他页面则重定向至登陆 102 | 103 | 104 | to.path === _option.pathLogin || to.meta.withoutAuth ? next() : next({ 105 | path: _option.pathLogin 106 | }); 107 | }); 108 | }; 109 | 110 | var _default = registerRouteGuard; 111 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/auth/va-auth.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); 11 | 12 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); 13 | 14 | var _wlCore = require("wl-core"); 15 | 16 | /** 17 | * @author weilan 18 | * @time 2020.04.23 19 | * @description 用户身份校验类 20 | */ 21 | var VaJwt = /*#__PURE__*/function () { 22 | function VaJwt() { 23 | (0, _classCallCheck2["default"])(this, VaJwt); 24 | } 25 | 26 | (0, _createClass2["default"])(VaJwt, null, [{ 27 | key: "extractJwtPayload", 28 | 29 | /** 30 | * 截取jwt中有效载荷部分 31 | * @param {*} jwt 32 | */ 33 | value: function extractJwtPayload(jwt) { 34 | if (!jwt) throw Error('缺少jwt!'); 35 | var jwt_split = jwt.split('.'); 36 | if (jwt_split.length !== 3) throw Error('jwt格式不正确!'); 37 | return jwt_split[1]; 38 | } 39 | /** 40 | * 简单解析未特殊加密的payload部分 41 | * @param {*} jwt 42 | */ 43 | 44 | }, { 45 | key: "payloadAtob", 46 | value: function payloadAtob(jwt) { 47 | var jwt_payload = this.extractJwtPayload(jwt); 48 | var decodedData = window.atob(jwt_payload); 49 | return JSON.parse(decodedData); 50 | } 51 | /** 52 | * 检验jwt是否过期 53 | * @param {String} jwt 54 | * @param {Function} vaCb 自定义验证函数,返回Boolean true表示过期 55 | */ 56 | 57 | }, { 58 | key: "vaJwtExpired", 59 | value: function vaJwtExpired(jwt, vaCb) { 60 | var exp = this.payloadAtob(jwt).exp * 1000; 61 | 62 | if (vaCb) { 63 | return vaCb(exp); 64 | } 65 | 66 | var _time = new _wlCore.Time(exp); 67 | 68 | return _time.isBefore(new Date()); 69 | } 70 | /** 71 | * 监测浏览器tab页切换立即校验账号 72 | * @param {Function} cb 检测到切换后的回调函数 73 | */ 74 | 75 | }, { 76 | key: "vaVisibilityChange", 77 | value: function vaVisibilityChange(cb) { 78 | window.addEventListener("visibilitychange", cb); 79 | } 80 | }]); 81 | return VaJwt; 82 | }(); 83 | 84 | exports["default"] = VaJwt; -------------------------------------------------------------------------------- /vue/dist/utils/directives/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _vAuth = _interopRequireDefault(require("./v-auth")); 11 | 12 | var directives = [_vAuth["default"]]; 13 | var _default = directives; 14 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/directives/v-auth.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var _default = { 8 | name: 'auth', 9 | rule: function rule(store) { 10 | return { 11 | // 指令已经添加到元素上,el-指令相关dom元素;binding-对象 12 | inserted: function inserted(el, binding) { 13 | var _store$getters; 14 | 15 | // 将权限码字段从binding中提取 16 | var _data = binding.value; // 将所有按钮权限码提取 17 | 18 | var _permissions = (store === null || store === void 0 ? void 0 : (_store$getters = store.getters) === null || _store$getters === void 0 ? void 0 : _store$getters.permissions) || []; 19 | 20 | if (!_data) throw new Error("The auth code is required\uFF0Ceg: v-auth=\"add\" || v-auth=\"['add', 'edit']\""); // 当前权限是一个集合时 21 | 22 | var _hasPermissions = Array.isArray(_data) ? _data.some(function (i) { 23 | return _permissions.includes(i); 24 | }) : _permissions.includes(_data); 25 | 26 | if (!_hasPermissions) { 27 | var _el$parentNode, _el$parentNode$remove; 28 | 29 | el === null || el === void 0 ? void 0 : (_el$parentNode = el.parentNode) === null || _el$parentNode === void 0 ? void 0 : (_el$parentNode$remove = _el$parentNode.removeChild) === null || _el$parentNode$remove === void 0 ? void 0 : _el$parentNode$remove.call(_el$parentNode, el); 30 | } 31 | } 32 | }; 33 | } 34 | }; 35 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/filters/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 11 | 12 | var _time = _interopRequireDefault(require("./time")); 13 | 14 | var _default = (0, _toConsumableArray2["default"])(_time["default"]); 15 | 16 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/filters/time.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _dayjs = _interopRequireDefault(require("dayjs")); 11 | 12 | /** 13 | * @author weilan 14 | * @time 2020.04.22 15 | * @description 时间操作类filter 16 | */ 17 | 18 | /** 19 | * 日期格式化 20 | * @param {*} date 21 | */ 22 | var dateFormat = { 23 | name: 'date', 24 | rule: function rule(date) { 25 | return date ? (0, _dayjs["default"])(date).format('YYYY-MM-DD') : null; 26 | } 27 | }; 28 | /** 29 | * 日期时间格式化 30 | * @param {*} dateTime 31 | */ 32 | 33 | var dateTimeFormat = { 34 | name: 'dateTime', 35 | rule: function rule(date) { 36 | return date ? (0, _dayjs["default"])(date).format('YYYY-MM-DD HH:mm:ss') : null; 37 | } 38 | }; 39 | var _default = [dateFormat, dateTimeFormat]; 40 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/dist/utils/init/render.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports["default"] = void 0; 9 | 10 | var _vue = _interopRequireDefault(require("vue")); 11 | 12 | var _fastclick = _interopRequireDefault(require("fastclick")); 13 | 14 | var _vueCookie = _interopRequireDefault(require("vue-cookie")); 15 | 16 | var _vueLazyload = _interopRequireDefault(require("vue-lazyload")); 17 | 18 | var _filters2 = _interopRequireDefault(require("../filters")); 19 | 20 | var _directives2 = _interopRequireDefault(require("../directives")); 21 | 22 | var _auth = _interopRequireDefault(require("../auth")); 23 | 24 | var _settings = require("../../config/settings"); 25 | 26 | var _wlCore = require("wl-core"); 27 | 28 | // 移动端click事件解决方案 29 | // cookie操作库 30 | // 图片懒加载库 31 | // 导入默认过滤器 32 | // 导入默认指令 33 | // 导入鉴权系统 34 | 35 | /** 36 | * vue 实例化函数 37 | * @param {Object} param0 配置项 下为详细注解 38 | * @desc {Object} 必须 root 当前应用的根组件 一般是app.vue 39 | * @desc {Object} 必须 router router实例 40 | * @desc {Object} 必须 store store实例 41 | * @desc {Object} options 实例化vue配置项 下为详细注解 42 | * @description fastclick 默认false 是否启用移动端快速点击插件 43 | * @description cookie 默认false 是否启用cookie操作插件 44 | * @description lazyOptions 默认null 启用图片懒加载插件时的配置项 45 | * @description filters 默认[] 过滤器数组 格式为 {name:"", rule: ()=>{}} 46 | * @description directives 默认[] 指令数组 格式为 {name:"", rule: ()=>{}} 47 | * @description plugins 默认[] 插件数组 [wlui, el-input] 可以直接Vue.use()的插件数组 48 | * @description fncBeforeVue 实例化vue前可执行的回调函数 fncBeforeVue(vue){... 你的逻辑} 49 | * @description auth 是否需要鉴权系统,如果不需要,后续参数无需再传 50 | * @desc {Object} routeOptions 路由守卫配置项 下为详细注解 51 | * @description tokenKey: 'token', // 存储在local中的token的key 52 | * @description dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 53 | * @description dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 54 | * @description dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 55 | * @description pathLogin: '/login', // 登录页的 router path 56 | * @description pathLogged: '/index', // 已登录后 再进登录页要重定向的 router path 57 | * @description apiFn: ()=>{}, // 获取菜单数据的api函数 58 | * @description vaJwtExpiredFn: ()=>{}, // 自定义校验jwt是否过期的函数 59 | * @desc {Object}: menuOptions 菜单数据解析为路由数据配置项 下为详细注解 60 | * @description url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 61 | * @description name: 'routerName', // 命名路由 62 | * @description meta: 'meta', // 路由元数据 63 | * @description children: 'children', // 子菜单字段 64 | * @description permissions: 'permissions', // 按钮权限字段 65 | * @description path404: 'error/404' // 404路径 66 | * @desc nextRoutes 需要登录后插入的 非后台返回的 路由列表 67 | * @param {String} mount 默认#app 要挂载的dom节点id 68 | * @returns vm 初始化后的vue实例 69 | */ 70 | var render = function render() { 71 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 72 | root = _ref.root, 73 | router = _ref.router, 74 | store = _ref.store, 75 | _ref$options = _ref.options, 76 | options = _ref$options === void 0 ? {} : _ref$options, 77 | _ref$routeOptions = _ref.routeOptions, 78 | routeOptions = _ref$routeOptions === void 0 ? {} : _ref$routeOptions, 79 | _ref$menuOptions = _ref.menuOptions, 80 | menuOptions = _ref$menuOptions === void 0 ? {} : _ref$menuOptions, 81 | _ref$nextRoutes = _ref.nextRoutes, 82 | nextRoutes = _ref$nextRoutes === void 0 ? [] : _ref$nextRoutes; 83 | 84 | var mount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _settings._vueOptions.mount; 85 | 86 | // 检查必要条件 87 | if (!root || !router || !store) { 88 | throw Error('创建vue实例至少需要有{root, router, store}字段'); 89 | } // 检查 render 配置项是否是对象格式 90 | 91 | 92 | if (!_wlCore.DataType.isObject(options)) { 93 | throw Error('options必须是对象格式'); 94 | } // 提取render配置参数 95 | 96 | 97 | var _options$fastclick = options.fastclick, 98 | fastclick = _options$fastclick === void 0 ? false : _options$fastclick, 99 | _options$cookie = options.cookie, 100 | cookie = _options$cookie === void 0 ? false : _options$cookie, 101 | _options$auth = options.auth, 102 | auth = _options$auth === void 0 ? true : _options$auth, 103 | lazyOptions = options.lazyOptions, 104 | _options$plugins = options.plugins, 105 | plugins = _options$plugins === void 0 ? [] : _options$plugins, 106 | _options$filters = options.filters, 107 | filters = _options$filters === void 0 ? [] : _options$filters, 108 | _options$directives = options.directives, 109 | directives = _options$directives === void 0 ? [] : _options$directives, 110 | fncBeforeVue = options.fncBeforeVue; // 检查指令和过滤器格式 111 | 112 | if (!_wlCore.DataType.isArray(filters) || !_wlCore.DataType.isArray(directives) || !_wlCore.DataType.isArray(plugins)) { 113 | console.error('filters、directives、plugins需要是数组格式!'); 114 | } // 为Vue注册全局过滤器 115 | 116 | 117 | var _filters = _filters2["default"].concat(filters); 118 | 119 | _filters.map(function (item) { 120 | return _vue["default"].filter(item.name, item.rule); 121 | }); // 为Vue注册全局指令 122 | 123 | 124 | var _directives = _directives2["default"].concat(directives); 125 | 126 | _directives.map(function (item) { 127 | return _vue["default"].directive(item.name, item.rule(store)); 128 | }); // 为Vue注册全局组件 129 | 130 | 131 | plugins.map(function (item) { 132 | return _vue["default"].use(item); 133 | }); // 解决移动端的300ms延迟问题(默认不启用) 134 | 135 | fastclick && _fastclick["default"].attach(document.body); // 是否使用VueCookie(默认不启用) 136 | 137 | cookie && _vue["default"].use(_vueCookie["default"]); // 启动图片懒加载(默认不启用) 138 | 139 | _wlCore.DataType.isObject(lazyOptions) && _vue["default"].use(_vueLazyload["default"], lazyOptions); // 在实例化vue前 可传入回调函数自定义逻辑 140 | 141 | fncBeforeVue && fncBeforeVue(_vue["default"]); // 执行鉴权系统 142 | 143 | auth && (0, _auth["default"])(router, store, routeOptions, menuOptions, nextRoutes); // 阻止启动生产消息 144 | 145 | _vue["default"].config.productionTip = false; // 实例化vue 146 | 147 | var vm = new _vue["default"]({ 148 | router: router, 149 | store: store, 150 | render: function render(h) { 151 | return h(root); 152 | } 153 | }).$mount(mount); 154 | return vm; 155 | }; 156 | 157 | var _default = render; 158 | exports["default"] = _default; -------------------------------------------------------------------------------- /vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wl-vue", 3 | "version": "1.2.4", 4 | "description": "vue基础库", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/babel src --out-dir dist", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/wl-ui/wl.git" 13 | }, 14 | "keywords": [ 15 | "vue", 16 | "wl-vue", 17 | "auth", 18 | "jwt", 19 | "token", 20 | "filters", 21 | "directives", 22 | "render", 23 | "permissions", 24 | "前端鉴权", 25 | "异步路由", 26 | "基础库" 27 | ], 28 | "author": "weilan <17796646068@163.com>", 29 | "license": "ISC", 30 | "bugs": { 31 | "url": "https://github.com/wl-ui/wl/issues" 32 | }, 33 | "homepage": "https://github.com/wl-ui/wl#readme", 34 | "dependencies": { 35 | "@babel/runtime": "^7.9.2", 36 | "dayjs": "^1.8.25", 37 | "fastclick": "^1.0.6", 38 | "vue-cookie": "^1.1.4", 39 | "vue-i18n": "^8.15.4", 40 | "vue-lazyload": "^1.3.3", 41 | "wl-core": "^1.0.3" 42 | }, 43 | "peerDependencies": { 44 | "vue": "^2.6.11", 45 | "vue-router": "^3.1.3", 46 | "vuex": "^3.1.2" 47 | }, 48 | "devDependencies": { 49 | "@babel/cli": "^7.4.4", 50 | "@babel/core": "^7.4.4", 51 | "@babel/plugin-transform-runtime": "^7.9.0", 52 | "@babel/preset-env": "^7.4.4" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vue/src/config/settings.js: -------------------------------------------------------------------------------- 1 | // 菜单数据字段配置项 2 | 3 | const _menuDataOptions = { 4 | url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 5 | name: 'routerName', // 命名路由 6 | meta: 'meta', // 路由元数据 7 | children: 'children', // 子菜单字段 8 | permissions: 'permissions', // 按钮权限字段 9 | path404: 'error/404' 10 | } 11 | 12 | const _routeGuardOptions = { 13 | tokenKey: 'token', // 存储在local中的token的key 14 | dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 15 | dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 16 | dispatchSetMenuList: 'menu/setMenuList', // store设置一维菜单的actions命名空间 17 | dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 18 | pathLogin: '/login', // 登录页的 router path 19 | pathLogged: '/index', // 已登录后 再进登录页要重定向的 router path 20 | apiFn: null, // 获取菜单数据的api函数 21 | vaJwtExpiredFn: null, // 自定义校验jwt是否过期的函数 22 | } 23 | 24 | // 实例化vue配置项 25 | const _vueOptions = { 26 | mount: '#app' 27 | } 28 | 29 | export { 30 | _menuDataOptions, 31 | _routeGuardOptions, 32 | _vueOptions 33 | } -------------------------------------------------------------------------------- /vue/src/index.js: -------------------------------------------------------------------------------- 1 | import render from './utils/init/render' 2 | import VaJwt from './utils/auth/va-auth' 3 | 4 | export { 5 | render, 6 | VaJwt 7 | } -------------------------------------------------------------------------------- /vue/src/utils/auth/async-routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.03.09 4 | * @description 整理需要登录后异步推入的路由 5 | */ 6 | import { flattenDeep, DataType } from "wl-core" 7 | // import routeMap from "./map-router" 8 | import { _menuDataOptions } from "../../config/settings" 9 | 10 | /** 11 | * 异步推入鉴权路由 要求必须存在@/views/layout/index.vue主体视图盒子和/index首页路径 12 | * @param {Array} data 菜单数据 13 | * @param {Array} nextRoutes 需要登录后插入的 非后台返回的 路由列表 14 | * @param {Object} options 菜单数据解析为路由数据配置项 下面是字段及默认值说明 15 | * @description url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 16 | * @description name: 'routerName', // 命名路由 17 | * @description meta: 'meta', // 路由元数据 18 | * @description children: 'children', // 子菜单字段 19 | * @description permissions: 'permissions', // 按钮权限字段 20 | * @description path404: 'error/404' // 404路径 21 | * @description mapPathFn: ()=>{} // 路由映射文件路径函数 22 | * @returns {Object} {routes: 整理好的异步路由router.addRoutes()即可, permissions: 权限code码} 23 | */ 24 | const asyncRoutes = (data, nextRoutes, options) => { 25 | if (!DataType.isObject(options)) throw Error('options 必须是一个对象!'); 26 | let _options = { ..._menuDataOptions, ...options } 27 | if (!_options.mapPathFn) throw Error('options 内必须有路由映射真实路径方法 mapPathFn!'); 28 | // 主视图路由 29 | let userRouter = { 30 | path: "/layout", 31 | name: "layout", 32 | component: () => import('@/views/layout/index.vue'), 33 | redirect: '/index', 34 | children: [] 35 | }; 36 | // 创建路由盒子 37 | let routerBox = []; 38 | // 创建权限码数组 39 | let permissions = []; 40 | // 将菜单数据处理为一维函数 41 | let menu = flattenDeep(data, _options.children); 42 | // 处理路由映射真实路径,放在封装里babel之后就失效了,暂时不提供这个公共方法,在每个项目里写一遍吧 43 | // let routeMapFile = _options.mapPathFn ? _options.mapPathFn : routeMap; 44 | // 遍历处理路由 45 | menu.forEach(item => { 46 | let _url = item[_options.url]; 47 | if (!_url) return; 48 | try { 49 | let routerItem = { 50 | path: _url, // 路由路径名 51 | name: item[_options.name], // 命名路由 用于配合菜单简洁跳转 52 | meta: item[_options.meta], // 路由元信息 定义路由时即可携带的参数,可用来管理每个路由的按钮操作权限 53 | component: _options.mapPathFn(item) // 路由映射真实视图路径 54 | }; 55 | // 将所有权限码收集存入store 56 | let _permissions = item[_options.permissions]; 57 | if (DataType.isArray(_permissions)) permissions.push(..._permissions); 58 | routerBox.push(routerItem); 59 | } catch (err) { 60 | throw Error('路由映射规则为:@/views${url}/index.vue', err); 61 | } 62 | }); 63 | // 推入需要异步加载的,非服务端获取的功能性页面 64 | routerBox.push(...nextRoutes); 65 | userRouter.children = routerBox; 66 | let errorBox = { 67 | path: "*", 68 | redirect: _options.path404 69 | }; 70 | 71 | return { 72 | routes: [userRouter, errorBox], 73 | menuList: menu, 74 | permissions 75 | } 76 | } 77 | 78 | export default asyncRoutes -------------------------------------------------------------------------------- /vue/src/utils/auth/index.js: -------------------------------------------------------------------------------- 1 | import registerRouteGuard from "./router-guard" 2 | 3 | export default registerRouteGuard; -------------------------------------------------------------------------------- /vue/src/utils/auth/map-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.04.26 4 | * @description 将路由映射到真实路径,babel之后会失效,将这句代码在每个项目中复制,也可修改映射规则 5 | */ 6 | 7 | module.exports = path => () => import(`@/views${path}/index.vue`); -------------------------------------------------------------------------------- /vue/src/utils/auth/router-guard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * auth:weilan 3 | * time: 2020.03.09 4 | * 鉴权路由守卫 5 | */ 6 | import { Storage, DataType } from "wl-core" 7 | import VaUserAuth from "./va-auth" 8 | import asyncRoutes from './async-routes'; // 导入异步插入路由函数 9 | import { _routeGuardOptions } from "../../config/settings"; // 路由守卫配置项 10 | 11 | /** 12 | * 注册路由守卫 13 | * @param {*} router router实例 14 | * @param {*} store vuex实例 15 | * @param {*} routeOptions 注册路由配置项 下为详细注解 16 | * @description tokenKey: 'token', // 存储在local中的token的key 17 | * @description dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 18 | * @description dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 19 | * @description dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 20 | * @description pathLogin: '/login', // 登录页的 router path 21 | * @description pathLogged: '/index', // 已登录后 再进登录页要重定向的 router path 22 | * @description apiFn: ()={}, // 获取菜单数据的api函数 23 | * @description vaJwtExpiredFn: ()={}, // 自定义校验jwt是否过期的函数 24 | * @param {*} menuOptions 菜单数据解析为路由数据配置项 25 | * @param {*} nextRoutes 需要登录后插入的 非后台返回的 路由列表 26 | */ 27 | const registerRouteGuard = (router, store, routeOptions, menuOptions, nextRoutes) => { 28 | if (!DataType.isObject(routeOptions)) throw Error('routeOptions 必须是一个对象!'); 29 | let _option = { ..._routeGuardOptions, ...routeOptions } 30 | if (!_option?.apiFn) throw Error('apiFn lost!缺少获取菜单数据的api函数!'); 31 | 32 | router.beforeEach((to, from, next) => { 33 | // 检查是否存在登录状态 34 | let _jwt = Storage.get(_option.tokenKey); 35 | // 存在登陆状态 36 | if (_jwt && _jwt != 'undefined') { 37 | // 第一次打开页面token过期进入登陆页 38 | if (VaUserAuth.vaJwtExpired(_jwt, _option.vaJwtExpiredFn)) { 39 | store.dispatch(_option.dispatchSetToken, '') 40 | Storage.remove(_option.tokenKey) 41 | next({ 42 | path: _option.pathLogin 43 | }); 44 | return; 45 | } 46 | // 没过期自动登录 47 | store.dispatch(_option.dispatchSetToken, _jwt) 48 | // 判断当前用户是否已拉取权限菜单 49 | if (store.getters.menu.length === 0) { 50 | _option.apiFn() 51 | .then(({ data }) => { 52 | let _menu = data.data || [];/* */ 53 | let { routes, permissions, menuList } = asyncRoutes(_menu, nextRoutes, menuOptions) 54 | router.addRoutes(routes); // 推入异步路由 55 | store.dispatch(_option.dispatchSetMenu, _menu); // 将菜单数据存入store 56 | store.dispatch(_option.dispatchSetMenuList, menuList); // 将菜单一维化数据存入store 57 | store.dispatch(_option.dispatchSetPermissions, permissions); // 将权限码数据存入store 58 | next({ ...to, replace: true }); 59 | }) 60 | .catch(); 61 | return; 62 | } 63 | // 已登录状态 去往登录页时自动重定向至配置页 其他跳转正常进行 64 | to.path === _option.pathLogin ? next(_option.pathLogged) : next(); 65 | return; 66 | } 67 | // 无登录状态时 可进入白名单页面 去其他页面则重定向至登陆 68 | to.path === _option.pathLogin || to.meta.withoutAuth ? next() : next({ 69 | path: _option.pathLogin 70 | }); 71 | 72 | }); 73 | } 74 | 75 | export default registerRouteGuard; -------------------------------------------------------------------------------- /vue/src/utils/auth/va-auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.04.23 4 | * @description 用户身份校验类 5 | */ 6 | import { Time } from "wl-core" 7 | 8 | export default class VaJwt { 9 | /** 10 | * 截取jwt中有效载荷部分 11 | * @param {*} jwt 12 | */ 13 | static extractJwtPayload(jwt) { 14 | if (!jwt) throw Error('缺少jwt!'); 15 | let jwt_split = jwt.split('.'); 16 | if (jwt_split.length !== 3) throw Error('jwt格式不正确!'); 17 | return jwt_split[1]; 18 | } 19 | 20 | /** 21 | * 简单解析未特殊加密的payload部分 22 | * @param {*} jwt 23 | */ 24 | static payloadAtob(jwt) { 25 | let jwt_payload = this.extractJwtPayload(jwt); 26 | let decodedData = window.atob(jwt_payload); 27 | return JSON.parse(decodedData); 28 | } 29 | 30 | /** 31 | * 检验jwt是否过期 32 | * @param {String} jwt 33 | * @param {Function} vaCb 自定义验证函数,返回Boolean true表示过期 34 | */ 35 | static vaJwtExpired(jwt, vaCb) { 36 | let exp = this.payloadAtob(jwt).exp * 1000; 37 | if (vaCb) { 38 | return vaCb(exp) 39 | } 40 | let _time = new Time(exp); 41 | return _time.isBefore(new Date()); 42 | } 43 | 44 | /** 45 | * 监测浏览器tab页切换立即校验账号 46 | * @param {Function} cb 检测到切换后的回调函数 47 | */ 48 | static vaVisibilityChange(cb) { 49 | window.addEventListener("visibilitychange", cb) 50 | } 51 | } -------------------------------------------------------------------------------- /vue/src/utils/directives/index.js: -------------------------------------------------------------------------------- 1 | import auth from './v-auth' 2 | 3 | const directives = [auth] 4 | 5 | export default directives 6 | -------------------------------------------------------------------------------- /vue/src/utils/directives/v-auth.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | name: 'auth', 4 | rule: (store) => ({ 5 | // 指令已经添加到元素上,el-指令相关dom元素;binding-对象 6 | inserted(el, binding) { 7 | // 将权限码字段从binding中提取 8 | const { value: _data } = binding; 9 | // 将所有按钮权限码提取 10 | const _permissions = store?.getters?.permissions || []; 11 | if (!_data) throw new Error(`The auth code is required,eg: v-auth="add" || v-auth="['add', 'edit']"`) 12 | // 当前权限是一个集合时 13 | let _hasPermissions = Array.isArray(_data) ? _data.some(i => _permissions.includes(i)) : _permissions.includes(_data); 14 | if (!_hasPermissions) { 15 | el?.parentNode?.removeChild?.(el) 16 | } 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /vue/src/utils/filters/index.js: -------------------------------------------------------------------------------- 1 | import timeFormat from "./time" 2 | 3 | export default [...timeFormat]; -------------------------------------------------------------------------------- /vue/src/utils/filters/time.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author weilan 3 | * @time 2020.04.22 4 | * @description 时间操作类filter 5 | */ 6 | 7 | import dayjs from "dayjs" 8 | 9 | /** 10 | * 日期格式化 11 | * @param {*} date 12 | */ 13 | const dateFormat = { 14 | name: 'date', 15 | rule: (date) => { 16 | return date ? dayjs(date).format('YYYY-MM-DD') : null 17 | } 18 | } 19 | 20 | /** 21 | * 日期时间格式化 22 | * @param {*} dateTime 23 | */ 24 | const dateTimeFormat = { 25 | name: 'dateTime', 26 | rule: (date) => { 27 | return date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : null 28 | } 29 | } 30 | 31 | export default [dateFormat, dateTimeFormat] -------------------------------------------------------------------------------- /vue/src/utils/init/render.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import FastClick from 'fastclick' // 移动端click事件解决方案 3 | import VueCookie from 'vue-cookie' // cookie操作库 4 | import VueLazyLoad from 'vue-lazyload' // 图片懒加载库 5 | import selfFilters from '../filters' // 导入默认过滤器 6 | import selfDirectives from '../directives' // 导入默认指令 7 | import registerRouteGuard from "../auth" // 导入鉴权系统 8 | import { _vueOptions } from "../../config/settings" 9 | import { DataType } from "wl-core" 10 | 11 | /** 12 | * vue 实例化函数 13 | * @param {Object} param0 配置项 下为详细注解 14 | * @desc {Object} 必须 root 当前应用的根组件 一般是app.vue 15 | * @desc {Object} 必须 router router实例 16 | * @desc {Object} 必须 store store实例 17 | * @desc {Object} options 实例化vue配置项 下为详细注解 18 | * @description fastclick 默认false 是否启用移动端快速点击插件 19 | * @description cookie 默认false 是否启用cookie操作插件 20 | * @description lazyOptions 默认null 启用图片懒加载插件时的配置项 21 | * @description filters 默认[] 过滤器数组 格式为 {name:"", rule: ()=>{}} 22 | * @description directives 默认[] 指令数组 格式为 {name:"", rule: ()=>{}} 23 | * @description plugins 默认[] 插件数组 [wlui, el-input] 可以直接Vue.use()的插件数组 24 | * @description fncBeforeVue 实例化vue前可执行的回调函数 fncBeforeVue(vue){... 你的逻辑} 25 | * @description auth 是否需要鉴权系统,如果不需要,后续参数无需再传 26 | * @desc {Object} routeOptions 路由守卫配置项 下为详细注解 27 | * @description tokenKey: 'token', // 存储在local中的token的key 28 | * @description dispatchSetToken: 'app/setToken', // store设置token的actions命名空间 29 | * @description dispatchSetMenu: 'menu/setMenu', // store设置菜单的actions命名空间 30 | * @description dispatchSetPermissions: 'menu/setPermissions', // store设置按钮权限码的actions命名空间 31 | * @description pathLogin: '/login', // 登录页的 router path 32 | * @description pathLogged: '/index', // 已登录后 再进登录页要重定向的 router path 33 | * @description apiFn: ()=>{}, // 获取菜单数据的api函数 34 | * @description vaJwtExpiredFn: ()=>{}, // 自定义校验jwt是否过期的函数 35 | * @desc {Object}: menuOptions 菜单数据解析为路由数据配置项 下为详细注解 36 | * @description url: 'url', // 前端地址栏路由 将映射真实文件路径 映射规则:import(`@/views${url}/index.vue`) 37 | * @description name: 'routerName', // 命名路由 38 | * @description meta: 'meta', // 路由元数据 39 | * @description children: 'children', // 子菜单字段 40 | * @description permissions: 'permissions', // 按钮权限字段 41 | * @description path404: 'error/404' // 404路径 42 | * @desc nextRoutes 需要登录后插入的 非后台返回的 路由列表 43 | * @param {String} mount 默认#app 要挂载的dom节点id 44 | * @returns vm 初始化后的vue实例 45 | */ 46 | const render = ({ root, router, store, options = {}, routeOptions = {}, menuOptions = {}, nextRoutes = [] } = {}, mount = _vueOptions.mount) => { 47 | // 检查必要条件 48 | if (!root || !router || !store) { 49 | throw Error('创建vue实例至少需要有{root, router, store}字段'); 50 | } 51 | // 检查 render 配置项是否是对象格式 52 | if (!DataType.isObject(options)) { 53 | throw Error('options必须是对象格式'); 54 | } 55 | // 提取render配置参数 56 | const { fastclick = false, cookie = false, auth = true, lazyOptions, plugins = [], filters = [], directives = [], fncBeforeVue } = options; 57 | // 检查指令和过滤器格式 58 | if (!DataType.isArray(filters) || !DataType.isArray(directives) || !DataType.isArray(plugins)) { 59 | console.error('filters、directives、plugins需要是数组格式!') 60 | } 61 | // 为Vue注册全局过滤器 62 | let _filters = selfFilters.concat(filters); 63 | _filters.map(item => Vue.filter(item.name, item.rule)); 64 | // 为Vue注册全局指令 65 | let _directives = selfDirectives.concat(directives); 66 | _directives.map(item => Vue.directive(item.name, item.rule(store))); 67 | // 为Vue注册全局组件 68 | plugins.map(item => Vue.use(item)); 69 | // 解决移动端的300ms延迟问题(默认不启用) 70 | fastclick && FastClick.attach(document.body); 71 | // 是否使用VueCookie(默认不启用) 72 | cookie && Vue.use(VueCookie); 73 | // 启动图片懒加载(默认不启用) 74 | DataType.isObject(lazyOptions) && Vue.use(VueLazyLoad, lazyOptions); 75 | // 在实例化vue前 可传入回调函数自定义逻辑 76 | fncBeforeVue && fncBeforeVue(Vue); 77 | // 执行鉴权系统 78 | auth && registerRouteGuard(router, store, routeOptions, menuOptions, nextRoutes); 79 | // 阻止启动生产消息 80 | Vue.config.productionTip = false; 81 | // 实例化vue 82 | const vm = new Vue({ 83 | router, 84 | store, 85 | render: h => h(root) 86 | }).$mount(mount); 87 | return vm; 88 | } 89 | 90 | export default render; --------------------------------------------------------------------------------