├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── babel.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── admin.json ├── favicon.ico ├── index.html └── test.json ├── src ├── App.vue ├── api │ ├── apk │ │ └── index.js │ ├── code │ │ └── index.js │ ├── login │ │ └── index.js │ ├── menu │ │ └── index.js │ ├── optionLog │ │ └── index.js │ ├── role │ │ └── index.js │ └── user │ │ └── index.js ├── i18n │ ├── index.js │ ├── lang │ │ ├── en.js │ │ ├── zh-cn.js │ │ └── zh-tw.js │ └── pages │ │ ├── home │ │ ├── en.js │ │ ├── zh-cn.js │ │ └── zh-tw.js │ │ └── login │ │ ├── en.js │ │ ├── zh-cn.js │ │ └── zh-tw.js ├── layout │ ├── component │ │ ├── aside.vue │ │ ├── columnsAside.vue │ │ ├── header.vue │ │ └── main.vue │ ├── footer │ │ └── index.vue │ ├── index.vue │ ├── logo │ │ └── index.vue │ ├── main │ │ ├── classic.vue │ │ ├── columns.vue │ │ ├── defaults.vue │ │ └── transverse.vue │ ├── navBars │ │ ├── breadcrumb │ │ │ ├── breadcrumb.vue │ │ │ ├── index.vue │ │ │ ├── search.vue │ │ │ ├── setings.vue │ │ │ ├── user.vue │ │ │ └── userNews.vue │ │ ├── index.vue │ │ └── tagsView │ │ │ ├── contextmenu.vue │ │ │ └── tagsView.vue │ ├── navMenu │ │ ├── horizontal.vue │ │ ├── subItem.vue │ │ └── vertical.vue │ ├── routerView │ │ ├── iframes.vue │ │ ├── link.vue │ │ └── parent.vue │ └── upgrade │ │ └── index.vue ├── main.js ├── router │ └── index.js ├── store │ ├── index.js │ └── modules │ │ ├── keepAliveNames.js │ │ ├── routesList.js │ │ ├── tagsViewRoutes.js │ │ ├── themeConfig.js │ │ └── userInfos.js ├── theme │ ├── app.scss │ ├── base.scss │ ├── common │ │ └── transition.scss │ ├── dark.scss │ ├── element.scss │ ├── index.scss │ ├── loading.scss │ ├── media │ │ ├── dialog.scss │ │ ├── error.scss │ │ ├── form.scss │ │ ├── home.scss │ │ ├── index.scss │ │ ├── layout.scss │ │ ├── login.scss │ │ ├── media.scss │ │ └── scrollbar.scss │ ├── other.scss │ └── variables.scss ├── utils │ ├── componentSize.js │ ├── formatTime.js │ ├── getStyleSheets.js │ ├── loading.js │ ├── request.js │ ├── setIconfont.js │ ├── storage.js │ ├── theme.js │ └── toolsValidate.js └── views │ ├── apk │ └── index.vue │ ├── code │ └── index.vue │ ├── error │ ├── 401.vue │ └── 404.vue │ ├── fun │ ├── signCanvas │ │ └── index.vue │ └── tagsView │ │ └── index.vue │ ├── home │ ├── index.scss │ ├── index.vue │ └── mock.js │ ├── login │ ├── index.vue │ └── mock.js │ ├── loginLog │ └── index.vue │ ├── menu │ └── index.vue │ ├── optionLog │ └── index.vue │ ├── pages │ └── formAdapt │ │ └── index.vue │ ├── personal │ └── index.vue │ ├── role │ └── index.vue │ ├── tools │ └── index.vue │ └── user │ └── index.vue └── vue.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | node_modules 3 | lib 4 | *.md 5 | *.scss 6 | *.woff 7 | *.ttf 8 | *.json 9 | .vscode 10 | .idea 11 | dist 12 | mock 13 | public 14 | bin 15 | build 16 | config 17 | index.html 18 | src/assets -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | parserOptions: { 7 | parser: '@babel/eslint-parser', 8 | }, 9 | plugins: ['vue'], 10 | extends: ['plugin:vue/essential', 'eslint:recommended'], 11 | rules: { 12 | // http://eslint.cn/docs/rules/ 13 | 'vue/no-parsing-error': 'off', 14 | 'no-unused-vars': 'error', 15 | 'no-dupe-args': 'error', 16 | 'no-empty': 'off', 17 | 'no-extra-semi': 'off', 18 | 'no-constant-condition': 'off', 19 | 'no-console': 'error', 20 | 'vue/multi-word-component-names': 'off', 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 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 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 一行最多多少个字符 3 | printWidth: 150, 4 | // 指定每个缩进级别的空格数 5 | tabWidth: 2, 6 | // 使用制表符而不是空格缩进行 7 | useTabs: true, 8 | // 在语句末尾打印分号 9 | semi: true, 10 | // 使用单引号而不是双引号 11 | singleQuote: true, 12 | // 更改引用对象属性的时间 可选值"" 13 | quoteProps: 'as-needed', 14 | // 在JSX中使用单引号而不是双引号 15 | jsxSingleQuote: false, 16 | // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none 17 | trailingComma: 'es5', 18 | // 在对象文字中的括号之间打印空格 19 | bracketSpacing: true, 20 | // jsx 标签的反尖括号需要换行 21 | jsxBracketSameLine: false, 22 | // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x 23 | arrowParens: 'always', 24 | // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 25 | rangeStart: 0, 26 | rangeEnd: Infinity, 27 | // 指定要使用的解析器,不需要写文件开头的 @prettier 28 | requirePragma: false, 29 | // 不需要自动在文件开头插入 @prettier 30 | insertPragma: false, 31 | // 使用默认的折行标准 always\never\preserve 32 | proseWrap: 'preserve', 33 | // 指定HTML文件的全局空格敏感度 css\strict\ignore 34 | htmlWhitespaceSensitivity: 'css', 35 | // Vue文件脚本和样式标签缩进 36 | vueIndentScriptAndStyle: false, 37 | // 换行符使用 lf 结尾是 可选值"" 38 | endOfLine: 'lf', 39 | }; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lyt-Top 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

x-springboot-ui

2 | 3 | #### 📚 后台地址 https://github.com/yzcheng90/X-SpringBoot 4 | 5 | #### 🚧 安装 cnpm、yarn 6 | 7 | - 复制代码(桌面 cmd 运行) `npm install -g cnpm --registry=https://registry.npm.taobao.org` 8 | - 复制代码(桌面 cmd 运行) `npm install -g yarn` 9 | 10 | #### ⚡ 使用说明 11 | 12 | 建议使用 cnpm,因为 yarn 有时会报错。`npm install` 安装报错的话,请使用 `cnpm install`。 13 | 14 | > 注意:`node` 需大于 `12.xxx` 小于等于 `v16.14.0`,否则安装依赖将报错。 15 | 16 | ```bash 17 | # 克隆项目 18 | git clone https://github.com/yzcheng90/x-springboot-ui.git 19 | 20 | # 进入项目 21 | cd x-springboot-ui 22 | 23 | # 安装依赖 24 | cnpm install 25 | cnpm install eslint-webpack-plugin --save-dev 26 | cnpm install core-js --save-dev 27 | npm install element-plus --save-dev 28 | npm install --save @vue/composition-api 29 | npm install --save @vue/shared 30 | npm install --save @vue/reactivity 31 | 32 | 33 | 34 | # 运行项目 35 | cnpm run dev 36 | 37 | # 打包发布 38 | cnpm run build 39 | ``` 40 | 41 | #### 部署说明 42 | 43 | nginx 配置 44 | ```yaml 45 | server { 46 | listen 80; 47 | server_name localhost; 48 | client_max_body_size 100m; 49 | 50 | location / { 51 | # UI目录 52 | root i:/ui; 53 | #动态页面 54 | proxy_set_header X-forwarded-for $proxy_add_x_forwarded_for; 55 | proxy_set_header X-Real-IP $remote_addr; 56 | if ( !-e $request_filename ){ 57 | proxy_pass http://127.0.0.1:8080; 58 | } 59 | } 60 | 61 | location ^~// { 62 | proxy_set_header X-forwarded-for $proxy_add_x_forwarded_for; 63 | proxy_set_header X-Real-IP $remote_addr; 64 | proxy_pass http://127.0.0.1:8080; 65 | } 66 | } 67 | ``` 68 | 访问:http://localhost 69 | 70 | 71 | #### 📚 开发文档 72 | - 前台UI框架地址:https://gitee.com/lyt-top/vue-next-admin.git 73 | - 查看开发文档:vue-next-admin-doc 74 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/cli-plugin-babel/preset'], 3 | plugins: ['@babel/plugin-proposal-optional-chaining'], 4 | }; 5 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 5 | "jsx": "preserve", 6 | "baseUrl": ".", 7 | "paths": { 8 | "/@/*": ["src/*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "x-springboot-ui", 3 | "version": "1.2.1", 4 | "private": true, 5 | "description": "vue2 webpack admin template", 6 | "author": "czx", 7 | "license": "MIT", 8 | "scripts": { 9 | "dev": "vue-cli-service serve", 10 | "build": "vue-cli-service build", 11 | "lint": "vue-cli-service lint", 12 | "webpack": "webpack --version" 13 | }, 14 | "dependencies": { 15 | "axios": "0.24.0", 16 | "clipboard": "2.0.8", 17 | "composition-api": "0.0.0", 18 | "countup.js": "2.0.8", 19 | "echarts": "5.2.2", 20 | "element-ui": "2.15.6", 21 | "nprogress": "0.2.0", 22 | "screenfull": "5.2.0", 23 | "sign-canvas": "1.1.4", 24 | "vue": "2.6.14", 25 | "vue-i18n": "8.26.7", 26 | "vue-particles": "1.0.9", 27 | "vue-router": "3.5.3", 28 | "vue-seamless-scroll": "1.1.23", 29 | "vuex": "3.6.2" 30 | }, 31 | "devDependencies": { 32 | "@babel/eslint-parser": "7.16.5", 33 | "@babel/plugin-proposal-optional-chaining": "7.17.12", 34 | "@vue/cli-plugin-babel": "4.5.15", 35 | "@vue/cli-plugin-eslint": "4.5.15", 36 | "@vue/cli-plugin-router": "4.5.15", 37 | "@vue/cli-plugin-vuex": "4.5.15", 38 | "@vue/cli-service": "4.5.15", 39 | "core-js": "^3.27.1", 40 | "eslint": "7.0.0", 41 | "eslint-plugin-vue": "8.2.0", 42 | "eslint-webpack-plugin": "^3.2.0", 43 | "sass": "1.45.0", 44 | "sass-loader": "10.1.1", 45 | "vue-template-compiler": "2.6.14" 46 | }, 47 | "browserslist": [ 48 | "> 1%", 49 | "last 2 versions", 50 | "not dead" 51 | ], 52 | "engines": { 53 | "node": ">=12.0.0", 54 | "npm": ">= 6.0.0" 55 | }, 56 | "keywords": [ 57 | "vue", 58 | "vue3", 59 | "vuejs/vue-next", 60 | "element-ui", 61 | "element-plus", 62 | "vue-next-admin", 63 | "next-admin" 64 | ], 65 | "repository": { 66 | "type": "git", 67 | "url": "https://github.com/yzcheng90/x-springboot-ui.git" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /public/admin.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "data": [ 4 | { 5 | "path": "/home", 6 | "name": "home", 7 | "component": "home", 8 | "meta": { 9 | "title": "message.router.home", 10 | "isLink": "", 11 | "isHide": false, 12 | "isKeepAlive": true, 13 | "isAffix": true, 14 | "isIframe": false, 15 | "roles": ["admin", "common"], 16 | "icon": "iconfont icon-shouye" 17 | } 18 | }, 19 | { 20 | "path": "/tools", 21 | "name": "tools", 22 | "component": "tools", 23 | "meta": { 24 | "title": "message.router.tools", 25 | "isLink": "", 26 | "isHide": false, 27 | "isKeepAlive": true, 28 | "isAffix": false, 29 | "isIframe": false, 30 | "roles": ["admin", "common"], 31 | "icon": "iconfont icon-gongju" 32 | } 33 | }, 34 | { 35 | "path": "/menu", 36 | "name": "menu", 37 | "component": "layout/routerView/parent", 38 | "redirect": "/menu/menu1", 39 | "meta": { 40 | "title": "message.router.menu", 41 | "isLink": "", 42 | "isHide": false, 43 | "isKeepAlive": true, 44 | "isAffix": false, 45 | "isIframe": false, 46 | "roles": ["admin", "common"], 47 | "icon": "iconfont icon-caidan" 48 | }, 49 | "children": [ 50 | { 51 | "path": "/menu/menu1", 52 | "name": "menu1", 53 | "component": "layout/routerView/parent", 54 | "redirect": "/menu/menu1/menu11", 55 | "meta": { 56 | "title": "message.router.menu1", 57 | "isLink": "", 58 | "isHide": false, 59 | "isKeepAlive": true, 60 | "isAffix": false, 61 | "isIframe": false, 62 | "roles": ["admin", "common"], 63 | "icon": "iconfont icon-caidan" 64 | }, 65 | "children": [ 66 | { 67 | "path": "/menu/menu1/menu11", 68 | "name": "menu11", 69 | "component": "menu/menu1/menu11/index", 70 | "meta": { 71 | "title": "message.router.menu11", 72 | "isLink": "", 73 | "isHide": false, 74 | "isKeepAlive": true, 75 | "isAffix": false, 76 | "isIframe": false, 77 | "roles": ["admin", "common"], 78 | "icon": "iconfont icon-caidan" 79 | } 80 | }, 81 | { 82 | "path": "/menu/menu1/menu12", 83 | "name": "menu12", 84 | "component": "layout/routerView/parent", 85 | "redirect": "/menu/menu1/menu12/menu121", 86 | "meta": { 87 | "title": "message.router.menu12", 88 | "isLink": "", 89 | "isHide": false, 90 | "isKeepAlive": true, 91 | "isAffix": false, 92 | "isIframe": false, 93 | "roles": ["admin", "common"], 94 | "icon": "iconfont icon-caidan" 95 | }, 96 | "children": [ 97 | { 98 | "path": "/menu/menu1/menu12/menu121", 99 | "name": "menu121", 100 | "component": "menu/menu1/menu12/menu121/index", 101 | "meta": { 102 | "title": "message.router.menu121", 103 | "isLink": "", 104 | "isHide": false, 105 | "isKeepAlive": true, 106 | "isAffix": false, 107 | "isIframe": false, 108 | "roles": ["admin", "common"], 109 | "icon": "iconfont icon-caidan" 110 | } 111 | }, 112 | { 113 | "path": "/menu/menu1/menu12/menu122", 114 | "name": "menu122", 115 | "component": "menu/menu1/menu12/menu122/index", 116 | "meta": { 117 | "title": "message.router.menu122", 118 | "isLink": "", 119 | "isHide": false, 120 | "isKeepAlive": true, 121 | "isAffix": false, 122 | "isIframe": false, 123 | "roles": ["admin", "common"], 124 | "icon": "iconfont icon-caidan" 125 | } 126 | } 127 | ] 128 | }, 129 | { 130 | "path": "/menu/menu1/menu13", 131 | "name": "menu13", 132 | "component": "menu/menu1/menu13/index", 133 | "meta": { 134 | "title": "message.router.menu13", 135 | "isLink": "", 136 | "isHide": false, 137 | "isKeepAlive": true, 138 | "isAffix": false, 139 | "isIframe": false, 140 | "roles": ["admin", "common"], 141 | "icon": "iconfont icon-caidan" 142 | } 143 | } 144 | ] 145 | }, 146 | { 147 | "path": "/menu/menu2", 148 | "name": "menu2", 149 | "component": "menu/menu2/index", 150 | "meta": { 151 | "title": "message.router.menu2", 152 | "isLink": "", 153 | "isHide": false, 154 | "isKeepAlive": true, 155 | "isAffix": false, 156 | "isIframe": false, 157 | "roles": ["admin", "common"], 158 | "icon": "iconfont icon-caidan" 159 | } 160 | } 161 | ] 162 | }, 163 | { 164 | "path": "/fun", 165 | "name": "funIndex", 166 | "component": "layout/routerView/parent", 167 | "redirect": "/fun/tagsView", 168 | "meta": { 169 | "title": "message.router.funIndex", 170 | "isLink": "", 171 | "isHide": false, 172 | "isKeepAlive": true, 173 | "isAffix": false, 174 | "isIframe": false, 175 | "roles": ["admin", "common"], 176 | "icon": "iconfont icon-crew_feature" 177 | }, 178 | "children": [ 179 | { 180 | "path": "/fun/tagsView", 181 | "name": "funTagsView", 182 | "component": "fun/tagsView/index", 183 | "meta": { 184 | "title": "message.router.funTagsView", 185 | "isLink": "", 186 | "isHide": false, 187 | "isKeepAlive": true, 188 | "isAffix": false, 189 | "isIframe": false, 190 | "roles": ["admin", "common"], 191 | "icon": "el-icon-thumb" 192 | } 193 | }, 194 | { 195 | "path": "/fun/signCanvas", 196 | "name": "funSignCanvas", 197 | "component": "fun/signCanvas/index", 198 | "meta": { 199 | "title": "message.router.funSignCanvas", 200 | "isLink": "", 201 | "isHide": false, 202 | "isKeepAlive": true, 203 | "isAffix": false, 204 | "isIframe": false, 205 | "roles": ["admin", "common"], 206 | "icon": "el-icon-edit" 207 | } 208 | } 209 | ] 210 | }, 211 | { 212 | "path": "/pages", 213 | "name": "pagesIndex", 214 | "component": "layout/routerView/parent", 215 | "redirect": "/pages/formAdapt", 216 | "meta": { 217 | "title": "message.router.pagesIndex", 218 | "isLink": "", 219 | "isHide": false, 220 | "isKeepAlive": true, 221 | "isAffix": false, 222 | "isIframe": false, 223 | "roles": ["admin", "common"], 224 | "icon": "iconfont icon-fuzhiyemian" 225 | }, 226 | "children": [ 227 | { 228 | "path": "/pages/formAdapt", 229 | "name": "pagesFormAdapt", 230 | "component": "pages/formAdapt/index", 231 | "meta": { 232 | "title": "message.router.pagesFormAdapt", 233 | "isLink": "", 234 | "isHide": false, 235 | "isKeepAlive": true, 236 | "isAffix": false, 237 | "isIframe": false, 238 | "roles": ["admin", "common"], 239 | "icon": "iconfont icon-biaodan" 240 | } 241 | } 242 | ] 243 | }, 244 | { 245 | "path": "/personal", 246 | "name": "personal", 247 | "component": "personal/index", 248 | "meta": { 249 | "title": "message.router.personal", 250 | "isLink": "", 251 | "isHide": false, 252 | "isKeepAlive": true, 253 | "isAffix": false, 254 | "isIframe": false, 255 | "roles": ["admin", "common"], 256 | "icon": "iconfont icon-gerenzhongxin" 257 | } 258 | }, 259 | { 260 | "path": "/link", 261 | "name": "layoutLinkView", 262 | "component": "layout/routerView/parent", 263 | "meta": { 264 | "title": "message.router.layoutLinkView", 265 | "isLink": "https://element-plus.gitee.io/#/zh-CN/component/installation", 266 | "isHide": false, 267 | "isKeepAlive": false, 268 | "isAffix": false, 269 | "isIframe": false, 270 | "roles": ["admin"], 271 | "icon": "iconfont icon-caozuo-wailian" 272 | } 273 | }, 274 | { 275 | "path": "/iframes", 276 | "name": "layoutIfameView", 277 | "component": "layout/routerView/parent", 278 | "meta": { 279 | "title": "message.router.layoutIfameView", 280 | "isLink": "https://element-plus.gitee.io/zh-CN/#/zh-CN/component/installation", 281 | "isHide": false, 282 | "isKeepAlive": false, 283 | "isAffix": true, 284 | "isIframe": true, 285 | "roles": ["admin"], 286 | "icon": "iconfont icon-neiqianshujuchucun" 287 | } 288 | } 289 | ] 290 | } 291 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yzcheng90/x-springboot-ui/9e00e98b85f838c7001b049abe135a5a089d6de3/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 16 | vue-prev-admin 17 | 18 | 19 |
20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "data": [ 4 | { 5 | "path": "/home", 6 | "name": "home", 7 | "component": "home", 8 | "meta": { 9 | "title": "message.router.home", 10 | "isLink": "", 11 | "isHide": false, 12 | "isKeepAlive": true, 13 | "isAffix": true, 14 | "isIframe": false, 15 | "roles": ["admin", "common"], 16 | "icon": "iconfont icon-shouye" 17 | } 18 | }, 19 | { 20 | "path": "/tools", 21 | "name": "tools", 22 | "component": "tools", 23 | "meta": { 24 | "title": "message.router.tools", 25 | "isLink": "", 26 | "isHide": false, 27 | "isKeepAlive": true, 28 | "isAffix": false, 29 | "isIframe": false, 30 | "roles": ["admin"], 31 | "icon": "iconfont icon-gongju" 32 | } 33 | }, 34 | { 35 | "path": "/menu", 36 | "name": "menu", 37 | "component": "layout/routerView/parent", 38 | "redirect": "/menu/menu1", 39 | "meta": { 40 | "title": "message.router.menu", 41 | "isLink": "", 42 | "isHide": false, 43 | "isKeepAlive": true, 44 | "isAffix": false, 45 | "isIframe": false, 46 | "roles": ["admin"], 47 | "icon": "iconfont icon-caidan" 48 | }, 49 | "children": [ 50 | { 51 | "path": "/menu/menu1", 52 | "name": "menu1", 53 | "component": "layout/routerView/parent", 54 | "redirect": "/menu/menu1/menu11", 55 | "meta": { 56 | "title": "message.router.menu1", 57 | "isLink": "", 58 | "isHide": false, 59 | "isKeepAlive": true, 60 | "isAffix": false, 61 | "isIframe": false, 62 | "roles": ["admin", "common"], 63 | "icon": "iconfont icon-caidan" 64 | }, 65 | "children": [ 66 | { 67 | "path": "/menu/menu1/menu11", 68 | "name": "menu11", 69 | "component": "menu/menu1/menu11/index", 70 | "meta": { 71 | "title": "message.router.menu11", 72 | "isLink": "", 73 | "isHide": false, 74 | "isKeepAlive": true, 75 | "isAffix": false, 76 | "isIframe": false, 77 | "roles": ["admin", "common"], 78 | "icon": "iconfont icon-caidan" 79 | } 80 | }, 81 | { 82 | "path": "/menu/menu1/menu12", 83 | "name": "menu12", 84 | "component": "layout/routerView/parent", 85 | "redirect": "/menu/menu1/menu12/menu121", 86 | "meta": { 87 | "title": "message.router.menu12", 88 | "isLink": "", 89 | "isHide": false, 90 | "isKeepAlive": true, 91 | "isAffix": false, 92 | "isIframe": false, 93 | "roles": ["admin", "common"], 94 | "icon": "iconfont icon-caidan" 95 | }, 96 | "children": [ 97 | { 98 | "path": "/menu/menu1/menu12/menu121", 99 | "name": "menu121", 100 | "component": "menu/menu1/menu12/menu121/index", 101 | "meta": { 102 | "title": "message.router.menu121", 103 | "isLink": "", 104 | "isHide": false, 105 | "isKeepAlive": true, 106 | "isAffix": false, 107 | "isIframe": false, 108 | "roles": ["admin", "common"], 109 | "icon": "iconfont icon-caidan" 110 | } 111 | }, 112 | { 113 | "path": "/menu/menu1/menu12/menu122", 114 | "name": "menu122", 115 | "component": "menu/menu1/menu12/menu122/index", 116 | "meta": { 117 | "title": "message.router.menu122", 118 | "isLink": "", 119 | "isHide": false, 120 | "isKeepAlive": true, 121 | "isAffix": false, 122 | "isIframe": false, 123 | "roles": ["admin", "common"], 124 | "icon": "iconfont icon-caidan" 125 | } 126 | } 127 | ] 128 | }, 129 | { 130 | "path": "/menu/menu1/menu13", 131 | "name": "menu13", 132 | "component": "menu/menu1/menu13/index", 133 | "meta": { 134 | "title": "message.router.menu13", 135 | "isLink": "", 136 | "isHide": false, 137 | "isKeepAlive": true, 138 | "isAffix": false, 139 | "isIframe": false, 140 | "roles": ["admin", "common"], 141 | "icon": "iconfont icon-caidan" 142 | } 143 | } 144 | ] 145 | }, 146 | { 147 | "path": "/menu/menu2", 148 | "name": "menu2", 149 | "component": "menu/menu2/index", 150 | "meta": { 151 | "title": "message.router.menu2", 152 | "isLink": "", 153 | "isHide": false, 154 | "isKeepAlive": true, 155 | "isAffix": false, 156 | "isIframe": false, 157 | "roles": ["admin", "common"], 158 | "icon": "iconfont icon-caidan" 159 | } 160 | } 161 | ] 162 | }, 163 | { 164 | "path": "/fun", 165 | "name": "funIndex", 166 | "component": "layout/routerView/parent", 167 | "redirect": "/fun/tagsView", 168 | "meta": { 169 | "title": "message.router.funIndex", 170 | "isLink": "", 171 | "isHide": false, 172 | "isKeepAlive": true, 173 | "isAffix": false, 174 | "isIframe": false, 175 | "roles": ["admin", "common"], 176 | "icon": "iconfont icon-crew_feature" 177 | }, 178 | "children": [ 179 | { 180 | "path": "/fun/tagsView", 181 | "name": "funTagsView", 182 | "component": "fun/tagsView/index", 183 | "meta": { 184 | "title": "message.router.funTagsView", 185 | "isLink": "", 186 | "isHide": false, 187 | "isKeepAlive": true, 188 | "isAffix": false, 189 | "isIframe": false, 190 | "roles": ["admin", "common"], 191 | "icon": "el-icon-thumb" 192 | } 193 | } 194 | ] 195 | }, 196 | { 197 | "path": "/link", 198 | "name": "layoutLinkView", 199 | "component": "layout/routerView/parent", 200 | "meta": { 201 | "title": "message.router.layoutLinkView", 202 | "isLink": "https://element-plus.gitee.io/#/zh-CN/component/installation", 203 | "isHide": false, 204 | "isKeepAlive": false, 205 | "isAffix": false, 206 | "isIframe": false, 207 | "roles": ["admin"], 208 | "icon": "iconfont icon-caozuo-wailian" 209 | } 210 | }, 211 | { 212 | "path": "/iframes", 213 | "name": "layoutIfameView", 214 | "component": "layout/routerView/parent", 215 | "meta": { 216 | "title": "message.router.layoutIfameView", 217 | "isLink": "https://gitee.com/lyt-top/vue-next-admin", 218 | "isHide": false, 219 | "isKeepAlive": false, 220 | "isAffix": true, 221 | "isIframe": true, 222 | "roles": ["admin"], 223 | "icon": "iconfont icon-neiqianshujuchucun" 224 | } 225 | } 226 | ] 227 | } 228 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 77 | -------------------------------------------------------------------------------- /src/api/apk/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | /** 4 | * APK版本管理 5 | * 6 | * @author czx 7 | * @email object_czx@163.com 8 | * @date 2023-01-26 20:32:33 9 | */ 10 | export function useApkApi() { 11 | return { 12 | list: (params) => { 13 | return request({ 14 | url: '/apk/version/list', 15 | method: 'get', 16 | params 17 | }); 18 | }, 19 | add: (params) => { 20 | return request({ 21 | url: '/apk/version/save', 22 | method: 'post', 23 | data: params 24 | }); 25 | }, 26 | update: (params) => { 27 | return request({ 28 | url: '/apk/version/update', 29 | method: 'post', 30 | data: params 31 | }); 32 | }, 33 | delete: (params) => { 34 | return request({ 35 | url: '/apk/version/delete', 36 | method: 'post', 37 | data: params 38 | }); 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/api/code/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | 4 | export function useCodeGenApi() { 5 | return { 6 | list: (params) => { 7 | return request({ 8 | url: '/sys/gen/list', 9 | method: 'get', 10 | params: params 11 | }); 12 | }, 13 | create: (params) => { 14 | return request({ 15 | url: '/sys/gen/create', 16 | method: 'post', 17 | data: params 18 | }); 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/login/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | /** 4 | * 登录api接口集合 5 | * @method signIn 用户登录 6 | * @method signOut 用户退出登录 7 | */ 8 | export function useLoginApi() { 9 | return { 10 | signIn: (params) => { 11 | return request({ 12 | url: '/token/login', 13 | method: 'post', 14 | params: params 15 | }); 16 | }, 17 | signOut: () => { 18 | return request({ 19 | url: '/token/logout', 20 | method: 'post' 21 | }); 22 | }, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/api/menu/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | /** 4 | * webpack 的代理只是本地开发生效,打包后需要在部署环境 搭建 nginx 代理 5 | * 所以: 6 | * 开发环境,请求跨越的接口。为了配置跨越示例 7 | * 线上环境,请求目录下的 `json` 数据 8 | * 一般后端接口都会处理跨越问题,可根据具体情况进行修改 9 | * json 格式地址:https://gitee.com/lyt-top/vue-next-admin-images/tree/master/vue2 10 | * 本地菜单地址:public/xxx.json 11 | */ 12 | 13 | /** 14 | * 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu 15 | * 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由 16 | * @method getMenuAdmin 获取后端动态路由菜单(admin) 17 | */ 18 | export function useMenuApi() { 19 | return { 20 | getRouterMenu: () => { 21 | return request({ 22 | url: '/sys/user/sysInfo', 23 | method: 'get' 24 | }); 25 | 26 | // 本地数据,路径:`/public/xxx.json` 27 | // return request({ 28 | // url: './admin.json', 29 | // method: 'get', 30 | // params, 31 | // }); 32 | // 模拟跨域 33 | // return request({ 34 | // url: '/gitee/lyt-top/vue-next-admin-images/raw/master/vue2/admin.json', 35 | // method: 'get', 36 | // }); 37 | }, 38 | list: (params) => { 39 | return request({ 40 | url: '/sys/menu/list', 41 | method: 'get', 42 | params 43 | }); 44 | }, 45 | save: (params) => { 46 | return request({ 47 | url: '/sys/menu/save', 48 | method: 'post', 49 | data: params 50 | }); 51 | }, 52 | update: (params) => { 53 | return request({ 54 | url: '/sys/menu/update', 55 | method: 'post', 56 | data: params 57 | }); 58 | }, 59 | delete: (params) => { 60 | return request({ 61 | url: '/sys/menu/delete', 62 | method: 'post', 63 | data: params 64 | }); 65 | }, 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/api/optionLog/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | 4 | export function useOptionLogApi() { 5 | return { 6 | list: (params) => { 7 | return request({ 8 | url: '/sys/log/list', 9 | method: 'get', 10 | params: params 11 | }); 12 | }, 13 | loginList: (params) => { 14 | return request({ 15 | url: '/sys/log/loginList', 16 | method: 'get', 17 | params: params 18 | }); 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/role/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | 4 | export function roleListApi() { 5 | return { 6 | list: (params) => { 7 | return request({ 8 | url: '/sys/role/list', 9 | method: 'get', 10 | params 11 | }); 12 | }, 13 | add: (params) => { 14 | return request({ 15 | url: '/sys/role/save', 16 | method: 'post', 17 | data: params 18 | }); 19 | }, 20 | update: (params) => { 21 | return request({ 22 | url: '/sys/role/update', 23 | method: 'post', 24 | data: params 25 | }); 26 | }, 27 | delete: (params) => { 28 | return request({ 29 | url: '/sys/role/delete', 30 | method: 'post', 31 | data: params 32 | }); 33 | }, 34 | select: () => { 35 | return request({ 36 | url: '/sys/role/select', 37 | method: 'get' 38 | }); 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/api/user/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | 4 | export function useListApi() { 5 | return { 6 | list: (params) => { 7 | return request({ 8 | url: '/sys/user/list', 9 | method: 'get', 10 | params 11 | }); 12 | }, 13 | add: (params) => { 14 | return request({ 15 | url: '/sys/user/save', 16 | method: 'post', 17 | data: params 18 | }); 19 | }, 20 | update: (params) => { 21 | return request({ 22 | url: '/sys/user/update', 23 | method: 'post', 24 | data: params 25 | }); 26 | }, 27 | delete: (params) => { 28 | return request({ 29 | url: '/sys/user/delete', 30 | method: 'post', 31 | data: params 32 | }); 33 | } 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueI18n from 'vue-i18n'; 3 | import zhcnLocale from 'element-ui/lib/locale/lang/zh-CN'; 4 | import enLocale from 'element-ui/lib/locale/lang/en'; 5 | import zhtwLocale from 'element-ui/lib/locale/lang/zh-TW'; 6 | import store from '@/store/index.js'; 7 | 8 | import nextZhcn from '@/i18n/lang/zh-cn.js'; 9 | import nextEn from '@/i18n/lang/en.js'; 10 | import nextZhtw from '@/i18n/lang/zh-tw.js'; 11 | 12 | import pagesHomeZhcn from '@/i18n/pages/home/zh-cn.js'; 13 | import pagesHomeEn from '@/i18n/pages/home/en.js'; 14 | import pagesHomeZhtw from '@/i18n/pages/home/zh-tw.js'; 15 | import pagesLoginZhcn from '@/i18n/pages/login/zh-cn.js'; 16 | import pagesLoginEn from '@/i18n/pages/login/en.js'; 17 | import pagesLoginZhtw from '@/i18n/pages/login/zh-tw.js'; 18 | 19 | // 使用插件 20 | Vue.use(VueI18n); 21 | 22 | // 定义语言国际化内容 23 | /** 24 | * 说明: 25 | * /src/i18n/lang 下的 js 为框架的国际化内容 26 | * /src/i18n/pages 下的 js 为各界面的国际化内容 27 | */ 28 | const messages = { 29 | 'zh-cn': { 30 | ...zhcnLocale, 31 | message: { 32 | ...nextZhcn, 33 | ...pagesHomeZhcn, 34 | ...pagesLoginZhcn, 35 | }, 36 | }, 37 | en: { 38 | ...enLocale, 39 | message: { 40 | ...nextEn, 41 | ...pagesHomeEn, 42 | ...pagesLoginEn, 43 | }, 44 | }, 45 | 'zh-tw': { 46 | ...zhtwLocale, 47 | message: { 48 | ...nextZhtw, 49 | ...pagesHomeZhtw, 50 | ...pagesLoginZhtw, 51 | }, 52 | }, 53 | }; 54 | 55 | // 导出语言国际化 56 | export const i18n = new VueI18n({ 57 | locale: store.state.themeConfig.themeConfig.globalI18n, 58 | fallbackLocale: 'zh-cn', 59 | messages, 60 | }); 61 | -------------------------------------------------------------------------------- /src/i18n/lang/en.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | router: { 4 | home: 'home', 5 | system: 'system', 6 | systemRole: 'systemRole', 7 | systemMenu: 'systemMenu', 8 | systemUser: 'systemUser', 9 | systemLogs: 'systemLogs', 10 | optionLog: 'optionLog', 11 | loginLog: 'loginLog', 12 | codeGen: 'codeGen', 13 | swagger: 'swagger', 14 | limits: 'limits', 15 | limitsFrontEnd: 'FrontEnd', 16 | limitsFrontEndPage: 'FrontEndPage', 17 | limitsFrontEndBtn: 'FrontEndBtn', 18 | limitsBackEnd: 'BackEnd', 19 | limitsBackEndEndPage: 'BackEndEndPage', 20 | menu: 'menu', 21 | menu1: 'menu1', 22 | menu11: 'menu11', 23 | menu12: 'menu12', 24 | menu121: 'menu121', 25 | menu122: 'menu122', 26 | menu13: 'menu13', 27 | menu2: 'menu2', 28 | funIndex: 'function', 29 | funTagsView: 'funTagsView', 30 | funSignCanvas: 'Online signature', 31 | funCountup: 'countup', 32 | funEchartsTree: 'echartsTree', 33 | funSelector: 'funSelector', 34 | funWangEditor: 'wangEditor', 35 | funCropper: 'cropper', 36 | funMindMap: 'G6 MindMap', 37 | funQrcode: 'qrcode', 38 | funEchartsMap: 'EchartsMap', 39 | funPrintJs: 'PrintJs', 40 | funClipboard: 'Copy cut', 41 | funScreenShort: 'screenCapture', 42 | pagesIndex: 'pages', 43 | pagesFiltering: 'Filtering', 44 | pagesFilteringDetails: 'FilteringDetails', 45 | pagesFilteringDetails1: 'FilteringDetails1', 46 | pagesIocnfont: 'iconfont icon', 47 | pagesElement: 'element icon', 48 | pagesAwesome: 'awesome icon', 49 | pagesCityLinkage: 'CityLinkage', 50 | pagesFormAdapt: 'FormAdapt', 51 | pagesListAdapt: 'ListAdapt', 52 | pagesWaterfall: 'Waterfall', 53 | pagesSteps: 'Steps', 54 | chartIndex: 'chartIndex', 55 | personal: 'personal', 56 | tools: 'tools', 57 | layoutLinkView: 'LinkView', 58 | layoutIfameView: 'IfameView', 59 | }, 60 | staticRoutes: { 61 | signIn: 'signIn', 62 | notFound: 'notFound', 63 | noPower: 'noPower', 64 | }, 65 | user: { 66 | title0: 'Component size', 67 | title1: 'Language switching', 68 | title2: 'Menu search', 69 | title3: 'Layout configuration', 70 | title4: 'news', 71 | title5: 'Full screen on', 72 | title6: 'Full screen off', 73 | dropdownDefault: 'default', 74 | dropdownMedium: 'medium', 75 | dropdownSmall: 'small', 76 | dropdownMini: 'mini', 77 | dropdown1: 'home page', 78 | dropdown2: 'Personal Center', 79 | dropdown3: '404', 80 | dropdown4: '401', 81 | dropdown5: 'Log out', 82 | dropdown6: 'Code warehouse', 83 | searchPlaceholder: 'Menu search: support Chinese, routing path', 84 | newTitle: 'notice', 85 | newBtn: 'All read', 86 | newGo: 'Go to the notification center', 87 | newDesc: 'No notice', 88 | logOutTitle: 'Tips', 89 | logOutMessage: 'This operation will log out. Do you want to continue?', 90 | logOutConfirm: 'determine', 91 | logOutCancel: 'cancel', 92 | logOutExit: 'Exiting', 93 | logOutSuccess: 'Exit successfully!', 94 | }, 95 | tagsView: { 96 | refresh: 'refresh', 97 | close: 'close', 98 | closeOther: 'closeOther', 99 | closeAll: 'closeAll', 100 | fullscreen: 'fullscreen', 101 | }, 102 | notFound: { 103 | foundTitle: 'Wrong address input, please re-enter the address~', 104 | foundMsg: 'You can check the web address first, and then re-enter or give us feedback.', 105 | foundBtn: 'Back to home page', 106 | }, 107 | noAccess: { 108 | accessTitle: 'You are not authorized to operate~', 109 | accessMsg: 'Contact information: add QQ group discussion 665452019', 110 | accessBtn: 'Reauthorization', 111 | }, 112 | layout: { 113 | configTitle: 'Layout configuration', 114 | oneTitle: 'Global Themes', 115 | twoTitle: 'Menu / top bar', 116 | twoTopBar: 'Top bar background', 117 | twoMenuBar: 'Menu background', 118 | twoColumnsMenuBar: 'Column menu background', 119 | twoTopBarColor: 'Top bar default font color', 120 | twoMenuBarColor: 'Menu default font color', 121 | twoColumnsMenuBarColor: 'Default font color bar menu', 122 | twoIsTopBarColorGradual: 'Top bar gradient', 123 | twoIsMenuBarColorGradual: 'Menu gradient', 124 | twoIsMenuBarColorHighlight: 'Menu font highlight', 125 | threeTitle: 'Interface settings', 126 | threeIsCollapse: 'Menu horizontal collapse', 127 | threeIsUniqueOpened: 'Menu accordion', 128 | threeIsFixedHeader: 'Fixed header', 129 | threeIsClassicSplitMenu: 'Classic layout split menu', 130 | threeIsLockScreen: 'Open the lock screen', 131 | threeLockScreenTime: 'screen locking(s/s)', 132 | fourTitle: 'Interface display', 133 | fourIsShowLogo: 'Sidebar logo', 134 | fourIsBreadcrumb: 'Open breadcrumb', 135 | fourIsBreadcrumbIcon: 'Open breadcrumb icon', 136 | fourIsTagsview: 'Open tagsview', 137 | fourIsTagsviewIcon: 'Open tagsview Icon', 138 | fourIsCacheTagsView: 'Enable tagsview cache', 139 | fourIsSortableTagsView: 'Enable tagsview drag', 140 | fourIsFooter: 'Open footer', 141 | fourIsGrayscale: 'Grey model', 142 | fourIsInvert: 'Color weak mode', 143 | fourIsDark: 'Dark Mode', 144 | fourIsWartermark: 'Turn on watermark', 145 | fourWartermarkText: 'Watermark copy', 146 | fiveTitle: 'Other settings', 147 | fiveTagsStyle: 'Tagsview style', 148 | fiveAnimation: 'page animation', 149 | fiveColumnsAsideStyle: 'Column style', 150 | fiveColumnsAsideLayout: 'Column layout', 151 | sixTitle: 'Layout switch', 152 | sixDefaults: 'One', 153 | sixClassic: 'Two', 154 | sixTransverse: 'Three', 155 | sixColumns: 'Four', 156 | tipText: 'Click the button below to copy the layout configuration to `/src/store/modules/themeConfig.js` It has been modified in.', 157 | copyText: 'replication configuration', 158 | resetText: 'restore default', 159 | copyTextSuccess: 'Copy succeeded!', 160 | copyTextError: 'Copy failed!', 161 | }, 162 | upgrade: { 163 | title: 'New version', 164 | msg: 'The new version is available, please update it now! Dont worry, the update is fast!', 165 | desc: 'Prompt: Update will restore the default configuration', 166 | btnOne: 'Cruel refusal', 167 | btnTwo: 'Update now', 168 | btnTwoLoading: 'Updating', 169 | }, 170 | }; 171 | -------------------------------------------------------------------------------- /src/i18n/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | router: { 4 | home: '首页', 5 | system: '系统设置', 6 | systemRole: '角色管理', 7 | systemMenu: '菜单管理', 8 | systemUser: '用户管理', 9 | systemLogs: '日志管理', 10 | optionLog: '操作日志', 11 | loginLog: '登录日志', 12 | codeGen: '代码生成', 13 | swagger: '接口文档', 14 | limits: '权限管理', 15 | limitsFrontEnd: '前端控制', 16 | limitsFrontEndPage: '页面权限', 17 | limitsFrontEndBtn: '按钮权限', 18 | limitsBackEnd: '后端控制', 19 | limitsBackEndEndPage: '页面权限', 20 | menu: '菜单嵌套', 21 | menu1: '菜单1', 22 | menu11: '菜单11', 23 | menu12: '菜单12', 24 | menu121: '菜单121', 25 | menu122: '菜单122', 26 | menu13: '菜单13', 27 | menu2: '菜单2', 28 | funIndex: '功能', 29 | funTagsView: 'tagsView 操作', 30 | funSignCanvas: '在线签名', 31 | funCountup: 'countup 数字滚动', 32 | funEchartsTree: 'echartsTree 树图', 33 | funSelector: '图标选择器', 34 | funWangEditor: 'wangEditor 编辑器', 35 | funCropper: 'cropper 图片裁剪', 36 | funMindMap: 'G6 思维导图', 37 | funQrcode: 'qrcode 二维码生成', 38 | funEchartsMap: '地理坐标/地图', 39 | funPrintJs: '页面打印', 40 | funClipboard: '复制剪切', 41 | funScreenShort: 'web端自定义截屏', 42 | pagesIndex: '页面', 43 | pagesFiltering: '过滤筛选组件', 44 | pagesFilteringDetails: '过滤筛选组件详情', 45 | pagesFilteringDetails1: '过滤筛选组件详情111', 46 | pagesIocnfont: 'iconfont 字体图标', 47 | pagesElement: 'element 字体图标', 48 | pagesAwesome: 'awesome 字体图标', 49 | pagesCityLinkage: '城市多级联动', 50 | pagesFormAdapt: '表单自适应', 51 | pagesListAdapt: '列表自适应', 52 | pagesWaterfall: '瀑布屏', 53 | pagesSteps: '步骤条', 54 | chartIndex: '大数据图表', 55 | personal: '个人中心', 56 | tools: '工具类集合', 57 | layoutLinkView: '外链', 58 | layoutIfameView: '内嵌 iframe', 59 | }, 60 | staticRoutes: { 61 | signIn: '登录', 62 | notFound: '找不到此页面', 63 | noPower: '没有权限', 64 | }, 65 | user: { 66 | title0: '组件大小', 67 | title1: '语言切换', 68 | title2: '菜单搜索', 69 | title3: '布局配置', 70 | title4: '消息', 71 | title5: '开全屏', 72 | title6: '关全屏', 73 | dropdownDefault: '默认', 74 | dropdownMedium: '中等', 75 | dropdownSmall: '小型', 76 | dropdownMini: '超小', 77 | dropdown1: '首页', 78 | dropdown2: '个人中心', 79 | dropdown3: '404', 80 | dropdown4: '401', 81 | dropdown5: '退出登录', 82 | dropdown6: '代码仓库', 83 | searchPlaceholder: '菜单搜索:支持中文、路由路径', 84 | newTitle: '通知', 85 | newBtn: '全部已读', 86 | newGo: '前往通知中心', 87 | newDesc: '暂无通知', 88 | logOutTitle: '提示', 89 | logOutMessage: '此操作将退出登录, 是否继续?', 90 | logOutConfirm: '确定', 91 | logOutCancel: '取消', 92 | logOutExit: '退出中', 93 | logOutSuccess: '安全退出成功!', 94 | }, 95 | tagsView: { 96 | refresh: '刷新', 97 | close: '关闭', 98 | closeOther: '关闭其它', 99 | closeAll: '全部关闭', 100 | fullscreen: '当前页全屏', 101 | }, 102 | notFound: { 103 | foundTitle: '地址输入错误,请重新输入地址~', 104 | foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。', 105 | foundBtn: '返回首页', 106 | }, 107 | noAccess: { 108 | accessTitle: '您未被授权,没有操作权限~', 109 | accessMsg: '联系方式:加QQ群探讨 665452019', 110 | accessBtn: '重新授权', 111 | }, 112 | layout: { 113 | configTitle: '布局配置', 114 | oneTitle: '全局主题', 115 | twoTitle: '菜单 / 顶栏', 116 | twoTopBar: '顶栏背景', 117 | twoMenuBar: '菜单背景', 118 | twoColumnsMenuBar: '分栏菜单背景', 119 | twoTopBarColor: '顶栏默认字体颜色', 120 | twoMenuBarColor: '菜单默认字体颜色', 121 | twoColumnsMenuBarColor: '分栏菜单默认字体颜色', 122 | twoIsTopBarColorGradual: '顶栏背景渐变', 123 | twoIsMenuBarColorGradual: '菜单背景渐变', 124 | twoIsMenuBarColorHighlight: '菜单字体背景高亮', 125 | threeTitle: '界面设置', 126 | threeIsCollapse: '菜单水平折叠', 127 | threeIsUniqueOpened: '菜单手风琴', 128 | threeIsFixedHeader: '固定 Header', 129 | threeIsClassicSplitMenu: '经典布局分割菜单', 130 | threeIsLockScreen: '开启锁屏', 131 | threeLockScreenTime: '自动锁屏(s/秒)', 132 | fourTitle: '界面显示', 133 | fourIsShowLogo: '侧边栏 Logo', 134 | fourIsBreadcrumb: '开启 Breadcrumb', 135 | fourIsBreadcrumbIcon: '开启 Breadcrumb 图标', 136 | fourIsTagsview: '开启 Tagsview', 137 | fourIsTagsviewIcon: '开启 Tagsview 图标', 138 | fourIsCacheTagsView: '开启 TagsView 缓存', 139 | fourIsSortableTagsView: '开启 TagsView 拖拽', 140 | fourIsFooter: '开启 Footer', 141 | fourIsGrayscale: '灰色模式', 142 | fourIsInvert: '色弱模式', 143 | fourIsDark: '深色模式', 144 | fourIsWartermark: '开启水印', 145 | fourWartermarkText: '水印文案', 146 | fiveTitle: '其它设置', 147 | fiveTagsStyle: 'Tagsview 风格', 148 | fiveAnimation: '主页面切换动画', 149 | fiveColumnsAsideStyle: '分栏高亮风格', 150 | fiveColumnsAsideLayout: '分栏布局风格', 151 | sixTitle: '布局切换', 152 | sixDefaults: '默认', 153 | sixClassic: '经典', 154 | sixTransverse: '横向', 155 | sixColumns: '分栏', 156 | tipText: '点击下方按钮,复制布局配置去 `src/store/modules/themeConfig.js` 中修改。', 157 | copyText: '一键复制配置', 158 | resetText: '一键恢复默认', 159 | copyTextSuccess: '复制成功!', 160 | copyTextError: '复制失败!', 161 | }, 162 | upgrade: { 163 | title: '新版本升级', 164 | msg: '新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!', 165 | desc: '提示:更新会还原默认配置', 166 | btnOne: '残忍拒绝', 167 | btnTwo: '马上更新', 168 | btnTwoLoading: '更新中', 169 | }, 170 | }; 171 | -------------------------------------------------------------------------------- /src/i18n/lang/zh-tw.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | router: { 4 | home: '首頁', 5 | system: '系統設置', 6 | systemRole: '角色列表', 7 | systemMenu: '選單管理', 8 | systemUser: '用戶管理', 9 | systemLogs: '日志管理', 10 | optionLog: '操作日志', 11 | loginLog: '登录日志', 12 | codeGen: '代码生成', 13 | swagger: '接口文档', 14 | limits: '許可權管理', 15 | limitsFrontEnd: '前端控制', 16 | limitsFrontEndPage: '頁面許可權', 17 | limitsFrontEndBtn: '按鈕許可權', 18 | limitsBackEnd: '後端控制', 19 | limitsBackEndEndPage: '頁面許可權', 20 | menu: '選單嵌套', 21 | menu1: '選單1', 22 | menu11: '選單11', 23 | menu12: '選單12', 24 | menu121: '選單121', 25 | menu122: '選單122', 26 | menu13: '選單13', 27 | menu2: '選單2', 28 | funIndex: '功能', 29 | funTagsView: 'tagsView 操作', 30 | funSignCanvas: '線上簽名', 31 | funCountup: 'countup 數位滾動', 32 | funEchartsTree: 'echartsTree 樹圖', 33 | funSelector: '圖標選擇器', 34 | funWangEditor: 'wangEditor 編輯器', 35 | funCropper: 'cropper 圖片裁剪', 36 | funMindMap: 'G6 心智圖', 37 | funQrcode: 'qrcode 二維碼生成', 38 | funEchartsMap: '地理座標/地圖', 39 | funPrintJs: '頁面列印', 40 | funClipboard: '複製剪切', 41 | funScreenShort: '自定義截圖', 42 | pagesIndex: '頁面', 43 | pagesFiltering: '過濾篩選組件', 44 | pagesFilteringDetails: '過濾篩選組件詳情', 45 | pagesFilteringDetails1: '過濾篩選組件詳情111', 46 | pagesIocnfont: 'iconfont 字體圖標', 47 | pagesElement: 'element 字體圖標', 48 | pagesAwesome: 'awesome 字體圖標', 49 | pagesCityLinkage: '都市多級聯動', 50 | pagesFormAdapt: '表單自我調整', 51 | pagesListAdapt: '清單自我調整', 52 | pagesWaterfall: '瀑布屏', 53 | pagesSteps: '步驟條', 54 | chartIndex: '大資料圖表', 55 | personal: '個人中心', 56 | tools: '工具類集合', 57 | layoutLinkView: '外鏈', 58 | layoutIfameView: '内嵌 iframe', 59 | }, 60 | staticRoutes: { 61 | signIn: '登入', 62 | notFound: '找不到此頁面', 63 | noPower: '沒有許可權', 64 | }, 65 | user: { 66 | title0: '組件大小', 67 | title1: '語言切換', 68 | title2: '選單蒐索', 69 | title3: '佈局配寘', 70 | title4: '消息', 71 | title5: '開全屏', 72 | title6: '關全屏', 73 | dropdownDefault: '默認', 74 | dropdownMedium: '中等', 75 | dropdownSmall: '小型', 76 | dropdownMini: '超小', 77 | dropdown1: '首頁', 78 | dropdown2: '個人中心', 79 | dropdown3: '404', 80 | dropdown4: '401', 81 | dropdown5: '登出', 82 | dropdown6: '程式碼倉庫', 83 | searchPlaceholder: '選單蒐索:支援中文、路由路徑', 84 | newTitle: '通知', 85 | newBtn: '全部已讀', 86 | newGo: '前往通知中心', 87 | newDesc: '暫無通知', 88 | logOutTitle: '提示', 89 | logOutMessage: '此操作將登出,是否繼續?', 90 | logOutConfirm: '確定', 91 | logOutCancel: '取消', 92 | logOutExit: '退出中', 93 | logOutSuccess: '安全退出成功!', 94 | }, 95 | tagsView: { 96 | refresh: '重繪', 97 | close: '關閉', 98 | closeOther: '關閉其它', 99 | closeAll: '全部關閉', 100 | fullscreen: '當前頁全屏', 101 | }, 102 | notFound: { 103 | foundTitle: '地址輸入錯誤,請重新輸入地址~', 104 | foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。', 105 | foundBtn: '返回首頁', 106 | }, 107 | noAccess: { 108 | accessTitle: '您未被授權,沒有操作許可權~', 109 | accessMsg: '聯繫方式:加QQ群探討665452019', 110 | accessBtn: '重新授權', 111 | }, 112 | layout: { 113 | configTitle: '佈局配寘', 114 | oneTitle: '全域主題', 115 | twoTitle: '選單 / 頂欄', 116 | twoTopBar: '頂欄背景', 117 | twoMenuBar: '選單背景', 118 | twoColumnsMenuBar: '分欄選單背景', 119 | twoTopBarColor: '頂欄默認字體顏色', 120 | twoMenuBarColor: '選單默認字體顏色', 121 | twoColumnsMenuBarColor: '分欄選單默認字體顏色', 122 | twoIsTopBarColorGradual: '頂欄背景漸變', 123 | twoIsMenuBarColorGradual: '選單背景漸變', 124 | twoIsMenuBarColorHighlight: '選單字體背景高亮', 125 | threeTitle: '介面設定', 126 | threeIsCollapse: '選單水准折疊', 127 | threeIsUniqueOpened: '選單手風琴', 128 | threeIsFixedHeader: '固定 Header', 129 | threeIsClassicSplitMenu: '經典佈局分割選單', 130 | threeIsLockScreen: '開啟鎖屏', 131 | threeLockScreenTime: '自動鎖屏(s/秒)', 132 | fourTitle: '介面顯示', 133 | fourIsShowLogo: '側邊欄 Logo', 134 | fourIsBreadcrumb: '開啟 Breadcrumb', 135 | fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標', 136 | fourIsTagsview: '開啟 Tagsview', 137 | fourIsTagsviewIcon: '開啟 Tagsview 圖標', 138 | fourIsCacheTagsView: '開啟 TagsView 緩存', 139 | fourIsSortableTagsView: '開啟 TagsView 拖拽', 140 | fourIsFooter: '開啟 Footer', 141 | fourIsGrayscale: '灰色模式', 142 | fourIsInvert: '色弱模式', 143 | fourIsDark: '深色模式', 144 | fourIsWartermark: '開啟浮水印', 145 | fourWartermarkText: '浮水印文案', 146 | fiveTitle: '其它設定', 147 | fiveTagsStyle: 'Tagsview 風格', 148 | fiveAnimation: '主頁面切換動畫', 149 | fiveColumnsAsideStyle: '分欄高亮風格', 150 | fiveColumnsAsideLayout: '分欄佈局風格', 151 | sixTitle: '佈局切換', 152 | sixDefaults: '默認', 153 | sixClassic: '經典', 154 | sixTransverse: '橫向', 155 | sixColumns: '分欄', 156 | tipText: '點擊下方按鈕,複製佈局配寘去`src/store/modules/themeConfig.js`中修改。', 157 | copyText: '一鍵複製配寘', 158 | resetText: '一鍵恢復默認', 159 | copyTextSuccess: '複製成功!', 160 | copyTextError: '複製失敗!', 161 | }, 162 | upgrade: { 163 | title: '新版本陞級', 164 | msg: '新版本來啦,馬上更新嘗鮮吧! 不用擔心,更新很快的哦!', 165 | desc: '提示:更新會還原默認配寘', 166 | btnOne: '殘忍拒絕', 167 | btnTwo: '馬上更新', 168 | btnTwoLoading: '更新中', 169 | }, 170 | }; 171 | -------------------------------------------------------------------------------- /src/i18n/pages/home/en.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | card: { 4 | title1: 'My desk', 5 | title2: 'Message notification', 6 | title3: 'more', 7 | title4: 'Marketing recommendation', 8 | title5: 'more', 9 | title6: 'Inventory operations', 10 | title7: 'Performance', 11 | title8: 'Out of stock monitoring', 12 | title9: 'Performance overtime warning', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/i18n/pages/home/zh-cn.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | card: { 4 | title1: '我的工作台', 5 | title2: '消息通知', 6 | title3: '更多', 7 | title4: '营销推荐', 8 | title5: '更多', 9 | title6: '库存作业', 10 | title7: '履约情况', 11 | title8: '缺货监控', 12 | title9: '履约超时预警', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/i18n/pages/home/zh-tw.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | card: { 4 | title1: '我的工作臺', 5 | title2: '消息通知', 6 | title3: '更多', 7 | title4: '行銷推薦', 8 | title5: '更多', 9 | title6: '庫存工作', 10 | title7: '履約情况', 11 | title8: '缺貨監控', 12 | title9: '履約超時預警', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/i18n/pages/login/en.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | login: { 4 | placeholder1: 'The user name admin or not is test', 5 | placeholder2: 'Password: 123456', 6 | placeholder3: 'Please enter the verification code', 7 | btnText: 'Sign in', 8 | link: { 9 | one1: 'Third party login', 10 | one2: 'Links', 11 | }, 12 | signInText: 'welcome back!', 13 | copyright: { 14 | one5: 'Copyright: XXX Software Technology Co., Ltd', 15 | two6: 'Copyright: XXX software technology ICP preparation no.xxxx', 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/i18n/pages/login/zh-cn.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | login: { 4 | placeholder1: '用户名 admin', 5 | placeholder2: '密码:123456', 6 | placeholder3: '请输入验证码', 7 | btnText: '登 录', 8 | link: { 9 | one1: '第三方登录', 10 | one2: '友情链接', 11 | }, 12 | signInText: '欢迎回来!', 13 | copyright: { 14 | one5: '版权所有:xxx软件科技有限公司', 15 | two6: 'Copyright: XXX Software Technology 粤ICP备xxx号', 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/i18n/pages/login/zh-tw.js: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | login: { 4 | placeholder1: '用戶名admin或不輸均為test', 5 | placeholder2: '密碼:123456', 6 | placeholder3: '請輸入驗證碼', 7 | btnText: '登 录', 8 | link: { 9 | one1: '協力廠商登入', 10 | one2: '友情連結', 11 | }, 12 | signInText: '歡迎回來!', 13 | copyright: { 14 | one5: '版權所有:xxx軟件科技有限公司', 15 | two6: 'Copyright: XXX Software Technology 粵ICP備xxxx號', 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/layout/component/aside.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 107 | -------------------------------------------------------------------------------- /src/layout/component/columnsAside.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 153 | 154 | 224 | -------------------------------------------------------------------------------- /src/layout/component/header.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | -------------------------------------------------------------------------------- /src/layout/component/main.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 94 | -------------------------------------------------------------------------------- /src/layout/footer/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 30 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 52 | -------------------------------------------------------------------------------- /src/layout/logo/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 34 | 35 | 71 | -------------------------------------------------------------------------------- /src/layout/main/classic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /src/layout/main/columns.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 34 | -------------------------------------------------------------------------------- /src/layout/main/defaults.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 42 | -------------------------------------------------------------------------------- /src/layout/main/transverse.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /src/layout/navBars/breadcrumb/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 112 | 113 | 140 | -------------------------------------------------------------------------------- /src/layout/navBars/breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 66 | 67 | 78 | -------------------------------------------------------------------------------- /src/layout/navBars/breadcrumb/search.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 84 | 85 | 101 | -------------------------------------------------------------------------------- /src/layout/navBars/breadcrumb/user.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 205 | 206 | 252 | -------------------------------------------------------------------------------- /src/layout/navBars/breadcrumb/userNews.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 59 | 60 | 127 | -------------------------------------------------------------------------------- /src/layout/navBars/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/layout/navBars/tagsView/contextmenu.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 96 | 97 | 111 | -------------------------------------------------------------------------------- /src/layout/navMenu/horizontal.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 116 | 117 | 139 | -------------------------------------------------------------------------------- /src/layout/navMenu/subItem.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 42 | -------------------------------------------------------------------------------- /src/layout/navMenu/vertical.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 74 | -------------------------------------------------------------------------------- /src/layout/routerView/iframes.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 47 | -------------------------------------------------------------------------------- /src/layout/routerView/link.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 36 | 37 | 84 | -------------------------------------------------------------------------------- /src/layout/routerView/parent.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 48 | -------------------------------------------------------------------------------- /src/layout/upgrade/index.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 82 | 83 | 150 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | import router from './router'; 4 | import store from './store'; 5 | 6 | import Particles from 'vue-particles'; 7 | import Element from 'element-ui'; 8 | import 'element-ui/lib/theme-chalk/index.css'; 9 | import '@/theme/index.scss'; 10 | import { i18n } from '@/i18n/index.js'; 11 | import { globalComponentSize } from '@/utils/componentSize.js'; 12 | 13 | Vue.use(Particles); 14 | Vue.use(Element, { i18n: (key, value) => i18n.t(key, value), size: globalComponentSize }); 15 | 16 | Vue.config.productionTip = false; 17 | Vue.prototype.bus = new Vue(); 18 | 19 | new Vue({ 20 | router, 21 | store, 22 | i18n, 23 | render: (h) => h(App), 24 | }).$mount('#app'); 25 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import store from '../store'; 3 | import VueRouter from 'vue-router'; 4 | import NProgress from 'nprogress'; 5 | import 'nprogress/nprogress.css'; 6 | import { Session } from '@/utils/storage'; 7 | import { PrevLoading } from '@/utils/loading.js'; 8 | import { useMenuApi } from '@/api/menu'; 9 | 10 | const menuApi = useMenuApi(); 11 | 12 | // 解决 `element ui` 导航栏重复点菜单报错问题 13 | const originalPush = VueRouter.prototype.push; 14 | VueRouter.prototype.push = function push(location) { 15 | return originalPush.call(this, location).catch((err) => err); 16 | }; 17 | 18 | // 安装 VueRouter 插件 19 | Vue.use(VueRouter); 20 | 21 | // 定义动态路由 22 | const dynamicRoutes = [ 23 | { 24 | path: '/', 25 | name: '/', 26 | component: 'layout/index', 27 | redirect: '/home', 28 | meta: { 29 | isKeepAlive: true, 30 | }, 31 | children: [], 32 | }, 33 | ]; 34 | 35 | // 定义静态路由 36 | const staticRoutes = [ 37 | { 38 | path: '/login', 39 | name: 'login', 40 | component: () => import('@/views/login'), 41 | meta: { 42 | title: '登录', 43 | }, 44 | }, 45 | { 46 | path: '/404', 47 | name: 'notFound', 48 | component: () => import('@/views/error/404.vue'), 49 | meta: { 50 | title: 'message.staticRoutes.notFound', 51 | }, 52 | }, 53 | { 54 | path: '/401', 55 | name: 'noPower', 56 | component: () => import('@/views/error/401.vue'), 57 | meta: { 58 | title: 'message.staticRoutes.noPower', 59 | }, 60 | }, 61 | ]; 62 | 63 | // 加载静态路由 64 | const createRouter = () => 65 | new VueRouter({ 66 | routes: staticRoutes, 67 | }); 68 | 69 | // 创建路由 70 | const router = createRouter(); 71 | 72 | // 加载 loading 73 | PrevLoading.start(); 74 | 75 | // 多级嵌套数组处理成一维数组 76 | export function formatFlatteningRoutes(arr) { 77 | if (arr.length <= 0) return false; 78 | for (let i = 0; i < arr.length; i++) { 79 | if (arr[i].children) { 80 | arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1)); 81 | } 82 | } 83 | return arr; 84 | } 85 | 86 | // 处理 tagsViewList 数据,默认路由全部缓存 87 | // isKeepAlive 处理 `name` 值,进行路由缓存 88 | export function formatTwoStageRoutes(arr) { 89 | if (arr.length <= 0) return false; 90 | const newArr = []; 91 | const cacheList = []; 92 | arr.forEach((v) => { 93 | newArr.push({ ...v }); 94 | cacheList.push(v.name); 95 | store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList); 96 | }); 97 | return newArr; 98 | } 99 | 100 | // 判断路由 meta.roles 中是否包含当前登录用户权限字段 101 | export function hasAuth(roles, route) { 102 | if (route.meta && route.meta.roles) return roles.some((role) => route.meta.roles.includes(role)); 103 | else return true; 104 | } 105 | 106 | // 递归过滤有权限的路由 107 | export function setFilterMenuFun(routes, role) { 108 | const menu = []; 109 | routes.forEach((route) => { 110 | const item = { ...route }; 111 | if (hasAuth(role, item)) { 112 | if (item.children) item.children = setFilterMenuFun(item.children, role); 113 | menu.push(item); 114 | } 115 | }); 116 | return menu; 117 | } 118 | 119 | // 缓存多级嵌套数组处理后的一维数组(tagsView、菜单搜索中使用:未过滤隐藏的(isHide)) 120 | export function setCacheTagsViewRoutes(arr) { 121 | // 先处理有权限的路由,否则 tagsView、菜单搜索中无权限的路由也将显示 122 | let rolesRoutes = setFilterMenuFun(arr, store.state.userInfos.userInfos.roles); 123 | // 添加到 vuex setTagsViewRoutes 中 124 | store.dispatch('tagsViewRoutes/setTagsViewRoutes', formatTwoStageRoutes(formatFlatteningRoutes(rolesRoutes))); 125 | } 126 | 127 | // 递归处理多余的 layout : ,让需要访问的组件保持在第一层 layout 层。 128 | // 因为 `keep-alive` 只能缓存二级路由 129 | // 默认初始化时就执行 130 | export function keepAliveSplice(to) { 131 | if (to.matched && to.matched.length > 2) { 132 | to.matched.map((v, k) => { 133 | if (v.components.default instanceof Function) { 134 | v.components.default().then((components) => { 135 | if (components.default.name === 'parent') { 136 | to.matched.splice(k, 1); 137 | router.push({ path: to.path, query: to.query }); 138 | keepAliveSplice(to); 139 | } 140 | }); 141 | } else { 142 | if (v.components.default.name === 'parent') { 143 | to.matched.splice(k, 1); 144 | keepAliveSplice(to); 145 | } 146 | } 147 | }); 148 | } 149 | } 150 | 151 | // 处理后端返回的 `component` 路径,拼装实现懒加载 152 | export function loadView(path) { 153 | /** 154 | * 打包成一个 js、一个 css 155 | */ 156 | // if (path.indexOf('layout') > -1) return () => Promise.resolve(require(`@/${path}`)); 157 | // else return () => Promise.resolve(require(`@/views/${path}`)); 158 | 159 | /** 160 | * 打包成多个 js、多个 css 161 | */ 162 | if (path.indexOf('layout') > -1) return () => import(`@/${path}`); 163 | else return () => import(`@/views/${path}`); 164 | } 165 | 166 | // 递归处理每一项 `component` 中的路径 167 | export function dynamicRouter(routes) { 168 | return routes.map((view) => { 169 | if (view.component) view.component = loadView(view.component); 170 | if (view.children) dynamicRouter(view.children); 171 | return view; 172 | }); 173 | } 174 | 175 | // 添加路由,模拟数据与方法,可自行进行修改 admin 176 | // 添加动态路由,`{ path: '*', redirect: '/404' }` 防止页面刷新,静态路由丢失问题 177 | // next({ ...to, replace: true }) 动态路由 addRoute 完毕后才放行,防止刷新时 NProgress 进度条加载2次 178 | // 文档地址:https://router.vuejs.org/zh/api/#router-addroutes 179 | export function adminUser(router, to, next) { 180 | resetRouter(); 181 | menuApi 182 | .getRouterMenu() 183 | .then(async (res) => { 184 | // eslint-disable-next-line no-console 185 | console.log("后台请求路由进行加载") 186 | 187 | // 存储用户信息到浏览器缓存 188 | Session.set('userInfo', res.data.userInfo); 189 | // 存储用户信息到vuex 190 | store.dispatch('userInfos/setUserInfos', res.data.userInfo); 191 | // 保存路由信息 192 | store.dispatch('routesList/setRoutesList', res.data.menus); 193 | 194 | dynamicRoutes[0].children = res.data.menus; 195 | const awaitRoute = await dynamicRouter(dynamicRoutes); 196 | [...awaitRoute, { path: '*', redirect: '/404' }].forEach((route) => { 197 | router.addRoute({ ...route }); 198 | }); 199 | setCacheTagsViewRoutes(JSON.parse(JSON.stringify(res.data.menus))); 200 | next({ ...to, replace: true }); 201 | }) 202 | .catch(() => {}); 203 | } 204 | 205 | // 重置路由 206 | export function resetRouter() { 207 | router.matcher = createRouter().matcher; 208 | } 209 | 210 | // 延迟关闭进度条 211 | export function delayNProgressDone(time = 300) { 212 | setTimeout(() => { 213 | NProgress.done(); 214 | }, time); 215 | } 216 | 217 | // 动态加载后端返回路由路由(模拟数据) 218 | export function getRouterList(router, to, next) { 219 | // eslint-disable-next-line no-console 220 | console.log("开始加载路由:getRouterList") 221 | adminUser(router, to, next); 222 | } 223 | 224 | // 路由加载前 225 | router.beforeEach((to, from, next) => { 226 | keepAliveSplice(to); 227 | NProgress.configure({ showSpinner: false }); 228 | if (to.meta.title && to.path !== '/login') NProgress.start(); 229 | let token = Session.get('token'); 230 | if (to.path === '/login' && !token) { 231 | NProgress.start(); 232 | next(); 233 | delayNProgressDone(); 234 | } else { 235 | if (!token) { 236 | NProgress.start(); 237 | next('/login'); 238 | Session.clear(); 239 | delayNProgressDone(); 240 | } else if (token && to.path === '/login') { 241 | next('/home'); 242 | delayNProgressDone(); 243 | } else { 244 | if (Object.keys(store.state.routesList.routesList).length <= 0) { 245 | getRouterList(router, to, next); 246 | } else { 247 | next(); 248 | delayNProgressDone(0); 249 | } 250 | } 251 | } 252 | }); 253 | 254 | // 路由加载后 255 | router.afterEach(() => { 256 | PrevLoading.done(); 257 | delayNProgressDone(); 258 | }); 259 | 260 | // 导出路由 261 | export default router; 262 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | Vue.use(Vuex); 5 | 6 | let moduleFn = require.context('./modules', false, /\.js$/); 7 | let modules = moduleFn.keys().reduce((p, c) => { 8 | let mod = moduleFn(c).default; 9 | mod = { ...mod, namespaced: true }; 10 | let modName = c.match(/\.\/(\w+)\.js$/)[1]; 11 | p[modName] = mod; 12 | return p; 13 | }, {}); 14 | 15 | export default new Vuex.Store({ modules }); 16 | -------------------------------------------------------------------------------- /src/store/modules/keepAliveNames.js: -------------------------------------------------------------------------------- 1 | const keepAliveNamesModule = { 2 | namespaced: true, 3 | state: { 4 | keepAliveNames: [], 5 | }, 6 | mutations: { 7 | // 设置路由缓存(name字段) 8 | getCacheKeepAlive(state, data) { 9 | state.keepAliveNames = data; 10 | }, 11 | }, 12 | actions: { 13 | // 设置路由缓存(name字段) 14 | async setCacheKeepAlive({ commit }, data) { 15 | commit('getCacheKeepAlive', data); 16 | }, 17 | }, 18 | }; 19 | 20 | export default keepAliveNamesModule; 21 | -------------------------------------------------------------------------------- /src/store/modules/routesList.js: -------------------------------------------------------------------------------- 1 | const routesListModule = { 2 | namespaced: true, 3 | state: { 4 | routesList: [], 5 | }, 6 | mutations: { 7 | // 设置路由,菜单中使用到 8 | getRoutesList(state, data) { 9 | state.routesList = data; 10 | }, 11 | }, 12 | actions: { 13 | // 设置路由,菜单中使用到 14 | async setRoutesList({ commit }, data) { 15 | // eslint-disable-next-line no-console 16 | console.log("设置路由,菜单中使用到") 17 | commit('getRoutesList', data); 18 | }, 19 | }, 20 | }; 21 | 22 | export default routesListModule; 23 | -------------------------------------------------------------------------------- /src/store/modules/tagsViewRoutes.js: -------------------------------------------------------------------------------- 1 | const tagsViewRoutesModule = { 2 | namespaced: true, 3 | state: { 4 | tagsViewRoutes: [], 5 | }, 6 | mutations: { 7 | // 设置 TagsView 路由 8 | getTagsViewRoutes(state, data) { 9 | state.tagsViewRoutes = data; 10 | }, 11 | }, 12 | actions: { 13 | // 设置 TagsView 路由 14 | async setTagsViewRoutes({ commit }, data) { 15 | commit('getTagsViewRoutes', data); 16 | }, 17 | }, 18 | }; 19 | 20 | export default tagsViewRoutesModule; 21 | -------------------------------------------------------------------------------- /src/store/modules/themeConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 2020.05.28 by lyt 优化 3 | * 修改一下配置时,需要每次都清理 `window.localStorage` 浏览器永久缓存,配置才会生效 4 | */ 5 | const themeConfigModule = { 6 | namespaced: true, 7 | state: { 8 | themeConfig: { 9 | // 是否开启布局配置抽屉 10 | isDrawer: false, 11 | 12 | /** 13 | * 全局主题 14 | */ 15 | // 默认 primary 主题颜色 16 | primary: '#409eff', 17 | // 是否开启深色模式 18 | isIsDark: false, 19 | 20 | /** 21 | * 菜单 / 顶栏 22 | * 请注意: 23 | * 需要同时修改 `/@/theme/common/var.scss` 对应的值, 24 | * 不提供像 vue-next-admin 一样的实现 25 | */ 26 | // 默认顶栏导航背景颜色 27 | topBar: '#ffffff', 28 | // 默认顶栏导航字体颜色 29 | topBarColor: '#606266', 30 | // 默认菜单导航背景颜色 31 | menuBar: '#545c64', 32 | // 默认菜单导航字体颜色 33 | menuBarColor: '#eaeaea', 34 | // 默认分栏菜单背景颜色 35 | columnsMenuBar: '#545c64', 36 | // 默认分栏菜单字体颜色 37 | columnsMenuBarColor: '#e6e6e6', 38 | 39 | /** 40 | * 界面设置 41 | */ 42 | // 是否开启菜单水平折叠效果 43 | isCollapse: false, 44 | // 是否开启菜单手风琴效果 45 | isUniqueOpened: false, 46 | // 是否开启固定 Header 47 | isFixedHeader: false, 48 | 49 | /** 50 | * 界面显示 51 | */ 52 | // 是否开启侧边栏 Logo 53 | isShowLogo: false, 54 | // 是否开启 Breadcrumb 55 | isBreadcrumb: true, 56 | // 是否开启 Breadcrumb 图标 57 | isBreadcrumbIcon: false, 58 | // 是否开启 Tagsview 59 | isTagsview: true, 60 | // 是否开启 Tagsview 图标 61 | isTagsviewIcon: false, 62 | // 是否开启 TagsView 缓存 63 | isCacheTagsView: false, 64 | // 是否开启 Footer 底部版权信息 65 | isFooter: false, 66 | // 是否开启灰色模式 67 | isGrayscale: false, 68 | // 是否开启色弱模式 69 | isInvert: false, 70 | 71 | /** 72 | * 其它设置 73 | */ 74 | // 默认 Tagsview 风格,可选 1、 tags-style-one,自行扩展: 75 | // 1、需修改 @/layout/navBars/breadcrumb/setings.vue `getThemeConfig.tagsStyle` el-option 76 | // 2、需修改 @/layout/navBars/tagsView/tagsView.vue 代码最底部注释部分 css 样式 77 | tagsStyle: 'tags-style-one', 78 | // 主页面切换动画:可选值"",默认 slide-right 79 | animation: 'slide-right', 80 | // 分栏高亮风格:可选值"",默认 columns-round 81 | columnsAsideStyle: 'columns-round', 82 | // 分栏布局风格:可选值"",默认 columns-horizontal 83 | columnsAsideLayout: 'columns-vertical', 84 | 85 | /** 86 | * 布局切换 87 | * 注意:为了演示,切换布局时,颜色会被还原成默认,代码位置:/@/layout/navBars/breadcrumb/setings.vue 88 | * 中的 `initSetLayoutChange(设置布局切换,重置主题样式)` 方法 89 | */ 90 | // 布局切换:可选值"",默认 defaults 91 | layout: 'defaults', 92 | 93 | /** 94 | * 全局网站标题 / 副标题 95 | */ 96 | // 网站主标题(菜单导航、浏览器当前网页标题) 97 | globalTitle: '后台管理系统', 98 | // 网站副标题(登录页顶部文字) 99 | globalViceTitle: 'x-springboot', 100 | // 网站描述(登录页顶部文字) 101 | globalViceDes: 'vue2.x后台管理系统', 102 | // 默认初始语言,可选值"",默认 zh-cn 103 | globalI18n: 'zh-cn', 104 | // 默认全局组件大小,可选值"<|medium|small|mini>",默认 '' 105 | globalComponentSize: '', 106 | }, 107 | }, 108 | mutations: { 109 | // 设置布局配置 110 | getThemeConfig(state, data) { 111 | state.themeConfig = data; 112 | }, 113 | }, 114 | actions: { 115 | // 设置布局配置 116 | setThemeConfig({ commit }, data) { 117 | commit('getThemeConfig', data); 118 | }, 119 | }, 120 | }; 121 | 122 | export default themeConfigModule; 123 | -------------------------------------------------------------------------------- /src/store/modules/userInfos.js: -------------------------------------------------------------------------------- 1 | import { Session } from '@/utils/storage.js'; 2 | 3 | const userInfosModule = { 4 | namespaced: true, 5 | state: { 6 | userInfos: {}, 7 | }, 8 | mutations: { 9 | // 获取用户信息 10 | getUserInfos(state, data) { 11 | // eslint-disable-next-line no-console 12 | console.log("获取用户信息") 13 | state.userInfos = data; 14 | }, 15 | }, 16 | actions: { 17 | // 设置用户信息 18 | async setUserInfos({ commit }, data) { 19 | // eslint-disable-next-line no-console 20 | console.log("设置用户信息") 21 | if (data) { 22 | commit('getUserInfos', data); 23 | } else { 24 | if (Session.get('userInfo')) commit('getUserInfos', Session.get('userInfo')); 25 | } 26 | }, 27 | }, 28 | }; 29 | 30 | export default userInfosModule; 31 | -------------------------------------------------------------------------------- /src/theme/app.scss: -------------------------------------------------------------------------------- 1 | /* 初始化样式 2 | ------------------------------- */ 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | outline: none; 8 | } 9 | 10 | :root { 11 | --prev-bg-menuBar: #545c64; 12 | --prev-bg-menuBarColor: #eaeaea; 13 | --prev-bg-topBar: #ffffff; 14 | --prev-bg-topBarColor: #606266; 15 | --prev-bg-columnsMenuBar: #545c64; 16 | --prev-bg-columnsMenuBarColor: #e6e6e6; 17 | --prev-bg-main-color: #f8f8f8; 18 | --prev-bg-color: #f5f7fa; 19 | --prev-bg-white: #ffffff; 20 | --prev-color-primary: #409eff; 21 | --prev-color-text-white: #ffffff; 22 | --prev-color-text-black: #000000; 23 | --prev-color-text-primary: #303133; 24 | --prev-color-text-regular: #606266; 25 | --prev-color-text-secondary: #909399; 26 | --prev-color-text-placeholder: #c0c4cc; 27 | --prev-color-hover: rgba(0, 0, 0, 0.04); 28 | --prev-color-seting-main: #e9eef3; 29 | --prev-color-seting-aside: #d3dce6; 30 | --prev-color-seting-header: #b3c0d1; 31 | --prev-border-color-hover: #c0c4cc; 32 | --prev-border-color-base: #dcdfe6; 33 | --prev-border-color-light: #e4e7ed; 34 | --prev-border-color-lighter: #ebeef5; 35 | --prev-border-color-extra-light: #f2f6fc; 36 | } 37 | 38 | html, 39 | body, 40 | #app { 41 | width: 100%; 42 | height: 100%; 43 | background-color: var(--prev-bg-main-color); 44 | font-size: 14px; 45 | } 46 | 47 | /* 主布局样式 48 | ------------------------------- */ 49 | .layout-container { 50 | width: 100%; 51 | height: 100%; 52 | .layout-aside { 53 | background: var(--prev-bg-menuBar); 54 | box-shadow: 2px 0 6px rgb(0 21 41 / 1%); 55 | height: inherit; 56 | position: relative; 57 | z-index: 1; 58 | display: flex; 59 | flex-direction: column; 60 | overflow-x: hidden !important; 61 | .el-scrollbar__view { 62 | overflow: hidden; 63 | } 64 | } 65 | .layout-header { 66 | padding: 0 !important; 67 | } 68 | .layout-main { 69 | padding: 0 !important; 70 | overflow: hidden; 71 | width: 100%; 72 | background-color: var(--prev-bg-main-color); 73 | } 74 | .el-scrollbar { 75 | width: 100%; 76 | } 77 | .layout-view-bg-white { 78 | background: var(--prev-bg-white); 79 | width: 100%; 80 | height: 100%; 81 | border-radius: 4px; 82 | border: 1px solid var(--prev-border-color-lighter); 83 | } 84 | .layout-el-aside-br-color { 85 | border-right: 1px solid rgb(238, 238, 238); 86 | } 87 | .layout-aside-width-default { 88 | width: 220px !important; 89 | transition: width 0.3s ease; 90 | } 91 | .layout-aside-width64 { 92 | width: 64px !important; 93 | transition: width 0.3s ease; 94 | } 95 | .layout-aside-width1 { 96 | width: 1px !important; 97 | transition: width 0.3s ease; 98 | } 99 | .layout-scrollbar { 100 | @extend .el-scrollbar; 101 | padding: 15px; 102 | } 103 | .layout-mian-height-50 { 104 | height: calc(100vh - 50px); 105 | } 106 | .layout-columns-warp { 107 | flex: 1; 108 | display: flex; 109 | overflow: hidden; 110 | } 111 | .layout-hide { 112 | display: none; 113 | } 114 | } 115 | 116 | /* 进度条颜色 117 | ------------------------------- */ 118 | #nprogress .bar { 119 | background: var(--prev-color-primary) !important; 120 | } 121 | 122 | /* flex 弹性布局 123 | ------------------------------- */ 124 | .flex { 125 | display: flex; 126 | } 127 | .flex-auto { 128 | flex: 1; 129 | } 130 | .flex-center { 131 | @extend .flex; 132 | flex-direction: column; 133 | width: 100%; 134 | overflow: hidden; 135 | } 136 | .flex-margin { 137 | margin: auto; 138 | } 139 | .flex-warp { 140 | display: flex; 141 | flex-wrap: wrap; 142 | align-content: flex-start; 143 | margin: 0 -5px; 144 | .flex-warp-item { 145 | padding: 5px; 146 | .flex-warp-item-box { 147 | width: 100%; 148 | height: 100%; 149 | } 150 | } 151 | } 152 | /* 宽高 100% 153 | ------------------------------- */ 154 | .w100 { 155 | width: 100% !important; 156 | } 157 | .h100 { 158 | height: 100% !important; 159 | } 160 | .vh100 { 161 | height: 100vh !important; 162 | } 163 | .max100vh { 164 | max-height: 100vh !important; 165 | } 166 | .min100vh { 167 | min-height: 100vh !important; 168 | } 169 | 170 | /* 颜色值 171 | ------------------------------- */ 172 | .color-primary { 173 | color: var(--prev-color-primary); 174 | } 175 | .color-success { 176 | color: var(--prev-color-success); 177 | } 178 | .color-warning { 179 | color: var(--prev-color-warning); 180 | } 181 | .color-danger { 182 | color: var(--prev-color-danger); 183 | } 184 | .color-info { 185 | color: var(--prev-color-info); 186 | } 187 | 188 | /* 溢出省略号 189 | ------------------------------- */ 190 | .one-text-overflow { 191 | overflow: hidden; 192 | text-overflow: ellipsis; 193 | white-space: nowrap; 194 | } 195 | .two-text-overflow { 196 | display: -webkit-box; 197 | -webkit-box-orient: vertical; 198 | -webkit-line-clamp: 2; 199 | overflow: hidden; 200 | } 201 | .overflow { 202 | overflow: hidden !important; 203 | } 204 | 205 | /* 字体大小全局样式 206 | ------------------------------- */ 207 | @for $i from 10 through 32 { 208 | .font#{$i} { 209 | font-size: #{$i}px !important; 210 | } 211 | } 212 | 213 | /* 外边距、内边距全局样式 214 | ------------------------------- */ 215 | @for $i from 5 through 35 { 216 | .mt#{$i} { 217 | margin-top: #{$i}px !important; 218 | } 219 | .mr#{$i} { 220 | margin-right: #{$i}px !important; 221 | } 222 | .mb#{$i} { 223 | margin-bottom: #{$i}px !important; 224 | } 225 | .ml#{$i} { 226 | margin-left: #{$i}px !important; 227 | } 228 | .pt#{$i} { 229 | padding-top: #{$i}px !important; 230 | } 231 | .pr#{$i} { 232 | padding-right: #{$i}px !important; 233 | } 234 | .pb#{$i} { 235 | padding-bottom: #{$i}px !important; 236 | } 237 | .pl#{$i} { 238 | padding-left: #{$i}px !important; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/theme/base.scss: -------------------------------------------------------------------------------- 1 | @import 'common/transition.scss'; 2 | -------------------------------------------------------------------------------- /src/theme/common/transition.scss: -------------------------------------------------------------------------------- 1 | /* 页面切换动画 2 | ------------------------------- */ 3 | .slide-right-enter-active, 4 | .slide-right-leave-active, 5 | .slide-left-enter-active, 6 | .slide-left-leave-active { 7 | will-change: transform; 8 | transition: all 0.3s ease; 9 | } 10 | // slide-right 11 | .slide-right-enter { 12 | opacity: 0; 13 | transform: translateX(-20px); 14 | } 15 | .slide-right-leave-active { 16 | opacity: 0; 17 | transform: translateX(20px); 18 | } 19 | // slide-left 20 | .slide-left-enter-from { 21 | @extend .slide-right-leave-active; 22 | } 23 | .slide-left-leave-to { 24 | @extend .slide-right-enter; 25 | } 26 | // opacitys 27 | .opacitys-enter-active, 28 | .opacitys-leave-active { 29 | will-change: transform; 30 | transition: all 0.3s ease; 31 | } 32 | .opacitys-enter, 33 | .opacitys-leave-active { 34 | opacity: 0; 35 | } 36 | 37 | /* Breadcrumb 面包屑过渡动画 38 | ------------------------------- */ 39 | .breadcrumb-enter-active, 40 | .breadcrumb-leave-active { 41 | transition: all 0.3s; 42 | } 43 | .breadcrumb-enter, 44 | .breadcrumb-leave-active { 45 | opacity: 0; 46 | transform: translateX(20px); 47 | } 48 | .breadcrumb-move { 49 | transition: all 0.3s; 50 | } 51 | .breadcrumb-leave-active { 52 | position: absolute; 53 | } 54 | 55 | /* logo 过渡动画 56 | ------------------------------- */ 57 | @keyframes logoAnimation { 58 | 0% { 59 | transform: scale(0); 60 | } 61 | 80% { 62 | transform: scale(1.2); 63 | } 64 | 100% { 65 | transform: scale(1); 66 | } 67 | } 68 | 69 | /* 404、401 过渡动画 70 | ------------------------------- */ 71 | @keyframes error-num { 72 | 0% { 73 | transform: translateY(60px); 74 | opacity: 0; 75 | } 76 | 100% { 77 | transform: translateY(0); 78 | opacity: 1; 79 | } 80 | } 81 | @keyframes error-img { 82 | 0% { 83 | opacity: 0; 84 | } 85 | 100% { 86 | opacity: 1; 87 | } 88 | } 89 | 90 | /* 左右左 link.vue 91 | ------------------------------- */ 92 | @keyframes toRight { 93 | 0% { 94 | left: -5px; 95 | } 96 | 50% { 97 | left: 100%; 98 | } 99 | 100% { 100 | left: -5px; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/theme/dark.scss: -------------------------------------------------------------------------------- 1 | /* 深色模式样式 2 | ------------------------------- */ 3 | [data-theme='dark'] { 4 | --prev-bg-menuBar: #191919 !important; 5 | --prev-bg-menuBarColor: #dadada !important; 6 | --prev-bg-topBar: #191919 !important; 7 | --prev-bg-topBarColor: #dadada !important; 8 | --prev-bg-columnsMenuBar: #191919 !important; 9 | --prev-bg-columnsMenuBarColor: #dadada !important; 10 | --prev-bg-main-color: #1f1f1f !important; 11 | --prev-bg-color: rgba(0, 0, 0, 0.3) !important; 12 | --prev-bg-white: #191919 !important; 13 | --prev-color-text-black: #ffffff !important; 14 | --prev-color-text-primary: #dadada !important; 15 | --prev-color-text-regular: #dadada !important; 16 | --prev-color-text-secondary: #a3a3a3 !important; 17 | --prev-color-hover: rgba(0, 0, 0, 0.3) !important; 18 | --prev-color-seting-main: #505050 !important; 19 | --prev-color-seting-aside: #3c3c3c !important; 20 | --prev-color-seting-header: #303030 !important; 21 | --prev-border-color-hover: #616161 !important; 22 | --prev-border-color-base: #333333 !important; 23 | --prev-border-color-light: #333333 !important; 24 | --prev-border-color-lighter: #333333 !important; 25 | --prev-border-color-extra-light: #333333 !important; 26 | 27 | // menu 28 | .layout-aside { 29 | border-right: 1px solid var(--prev-border-color-lighter) !important; 30 | } 31 | 32 | // drawer 33 | .el-drawer { 34 | border-left: 1px solid var(--prev-border-color-lighter) !important; 35 | } 36 | 37 | // button 38 | .el-button--default { 39 | background: var(--prev-bg-white); 40 | color: var(--prev-color-text-primary); 41 | border-color: var(--prev-border-color-lighter); 42 | &:hover, 43 | &:focus { 44 | color: var(--prev-color-primary) !important; 45 | background: var(--prev-color-primary-light-8) !important; 46 | border-color: var(--prev-color-primary-light-6) !important; 47 | } 48 | &:focus { 49 | border-color: var(--prev-color-primary-light-1) !important; 50 | } 51 | &:active { 52 | border-color: var(--prev-color-primary-light-6) !important; 53 | } 54 | } 55 | 56 | // tag 57 | .el-tag.el-tag--info { 58 | background-color: var(--prev-bg-white) !important; 59 | border-color: var(--prev-border-color-light) !important; 60 | color: var(--prev-color-text-regular) !important; 61 | } 62 | 63 | // switch 64 | .el-switch:not(.is-checked) { 65 | .el-switch__core { 66 | border-color: var(--prev-border-color-base) !important; 67 | background-color: var(--prev-border-color-base) !important; 68 | } 69 | } 70 | 71 | // TimePicker 72 | .el-time-spinner__item.active:not(.disabled) { 73 | color: var(--prev-color-primary) !important; 74 | } 75 | 76 | // date 77 | .el-date-table td.in-range div, 78 | .el-date-table td.in-range div:hover, 79 | .el-date-table.is-week-mode .el-date-table__row.current div, 80 | .el-date-table.is-week-mode .el-date-table__row:hover div, 81 | .el-date-table td.selected div, 82 | .el-month-table td.in-range div, 83 | .el-month-table td.in-range div:hover { 84 | background-color: var(--prev-bg-color) !important; 85 | } 86 | 87 | // transfer 88 | .el-transfer-panel, 89 | .el-transfer-panel .el-transfer-panel__header { 90 | background-color: var(--prev-bg-color) !important; 91 | } 92 | 93 | // loading 94 | .el-loading-mask { 95 | background-color: var(--prev-bg-color) !important; 96 | } 97 | 98 | // dropdown 99 | .el-dropdown-menu__item:focus, 100 | .el-dropdown-menu__item:not(.is-disabled):hover { 101 | background-color: var(--prev-color-hover) !important; 102 | } 103 | 104 | // dialog 105 | .el-dialog, 106 | .el-calendar { 107 | border: 1px solid var(--prev-border-color-lighter); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/theme/element.scss: -------------------------------------------------------------------------------- 1 | /* 防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度) 2 | ------------------------------- */ 3 | .el-scrollbar { 4 | overflow: hidden; 5 | position: relative; 6 | height: 100%; 7 | } 8 | .el-scrollbar__wrap { 9 | overflow: auto !important; 10 | overflow-x: hidden !important; 11 | max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/ 12 | } 13 | .el-select-dropdown .el-scrollbar__wrap { 14 | overflow-x: scroll !important; 15 | } 16 | .el-select-dropdown__wrap { 17 | max-height: 274px !important; /*修复Select 选择器高度问题*/ 18 | } 19 | .el-autocomplete-suggestion__wrap { 20 | max-height: 280px !important; 21 | } 22 | 23 | /* Button 按钮 24 | ------------------------------- */ 25 | // 第三方字体图标大小 26 | .el-button i.iconfont, 27 | .el-button i.fa { 28 | font-size: 14px !important; 29 | margin-right: 5px; 30 | } 31 | .el-button--medium i.iconfont, 32 | .el-button--medium i.fa { 33 | font-size: 14px !important; 34 | margin-right: 5px; 35 | } 36 | .el-button--small i.iconfont, 37 | .el-button--small i.fa { 38 | font-size: 12px !important; 39 | margin-right: 5px; 40 | } 41 | .el-button--mini i.iconfont, 42 | .el-button--mini i.fa { 43 | font-size: 12px !important; 44 | margin-right: 5px; 45 | } 46 | 47 | /* Dialog 对话框 48 | ------------------------------- */ 49 | .el-overlay, 50 | .el-dialog__wrapper { 51 | display: flex; 52 | align-items: center; 53 | justify-content: center; 54 | .el-dialog { 55 | margin: 0 auto !important; 56 | .el-dialog__body { 57 | padding: 20px !important; 58 | } 59 | } 60 | } 61 | .el-dialog__body { 62 | max-height: calc(90vh - 111px) !important; 63 | overflow-y: auto; 64 | overflow-x: hidden; 65 | } 66 | 67 | /* Alert 警告 68 | ------------------------------- */ 69 | .el-alert--warning.is-light { 70 | border: 1px solid rgba(230, 162, 60, 0.3) !important; 71 | } 72 | .el-alert--success.is-light { 73 | border: 1px solid rgba(103, 194, 58, 0.3) !important; 74 | } 75 | .el-alert--info.is-light { 76 | border: 1px solid rgba(144, 147, 153, 0.3) !important; 77 | } 78 | .el-alert--error.is-light { 79 | border: 1px solid rgba(245, 108, 108, 0.3) !important; 80 | } 81 | 82 | /* Table 表格 83 | ------------------------------- */ 84 | .el-table-column--selection { 85 | .el-checkbox { 86 | margin-right: unset !important; 87 | } 88 | } 89 | .el-table::before, 90 | .el-table--group::after, 91 | .el-table--border::after { 92 | z-index: 99 !important; 93 | } 94 | 95 | /* 下拉选择器/时间选择器滚动条 96 | ------------------------------- */ 97 | .el-select-dropdown .el-scrollbar__wrap, 98 | .el-picker-panel .el-scrollbar__wrap { 99 | overflow-x: scroll !important; 100 | } 101 | 102 | /* NavMenu 导航菜单 103 | ------------------------------- */ 104 | // 默认样式修改 105 | .el-menu { 106 | border-right: none !important; 107 | } 108 | .el-menu-item, 109 | .el-submenu__title { 110 | height: 50px !important; 111 | line-height: 50px !important; 112 | color: var(--prev-bg-menuBarColor) !important; 113 | transition: none !important; 114 | } 115 | // horizontal 水平方向时 116 | .el-menu--horizontal > .el-menu-item.is-active, 117 | .el-menu--horizontal > .el-submenu.is-active .el-submenu__title { 118 | border-bottom: 3px solid !important; 119 | border-bottom-color: var(--prev-color-primary) !important; 120 | color: var(--prev-color-primary) !important; 121 | } 122 | .el-menu--horizontal .el-menu-item:not(.is-disabled):focus, 123 | .el-menu--horizontal .el-menu-item:not(.is-disabled):hover, 124 | .el-menu--horizontal > .el-submenu:focus .el-submenu__title, 125 | .el-menu--horizontal > .el-submenu:hover .el-submenu__title, 126 | .el-menu--horizontal .el-menu .el-menu-item.is-active, 127 | .el-menu--horizontal .el-menu .el-submenu.is-active > .el-submenu__title { 128 | color: var(--prev-color-primary) !important; 129 | } 130 | .el-menu.el-menu--horizontal { 131 | border-bottom: none !important; 132 | } 133 | .el-menu--horizontal > .el-menu-item, 134 | .el-menu--horizontal > .el-submenu .el-submenu__title { 135 | color: var(--bg-topBarColor) !important; 136 | } 137 | // 外部链接时 138 | .el-menu-item a, 139 | .el-menu-item a:hover, 140 | .el-menu-item i, 141 | .el-submenu__title i { 142 | color: inherit; 143 | text-decoration: none; 144 | } 145 | .el-menu-item a { 146 | width: 86%; 147 | display: inline-block; 148 | } 149 | // 默认 hover 时 150 | .el-menu-item:hover, 151 | .el-submenu__title:hover { 152 | color: var(--prev-color-primary) !important; 153 | background-color: transparent !important; 154 | i { 155 | color: var(--prev-color-primary) !important; 156 | } 157 | } 158 | // 高亮时 159 | .el-menu-item.is-active { 160 | color: var(--prev-color-primary) !important; 161 | } 162 | .el-active-extend { 163 | color: #ffffff !important; 164 | background-color: var(--prev-color-primary) !important; 165 | i { 166 | color: #ffffff !important; 167 | } 168 | } 169 | #add-is-active { 170 | @extend .el-active-extend; 171 | &:hover { 172 | @extend .el-active-extend; 173 | } 174 | } 175 | // 菜单收起时且是a链接 176 | .is-dark a { 177 | color: #ffffff !important; 178 | text-decoration: none; 179 | } 180 | // 菜单收起时鼠标经过背景颜色/字体颜色 181 | .el-menu--vertical { 182 | background: var(--prev-bg-menuBar) !important; 183 | } 184 | .el-menu--horizontal { 185 | .el-menu { 186 | background: var(--bg-topBar) !important; 187 | } 188 | .el-menu-item, 189 | .el-submenu__title { 190 | color: var(--bg-topBarColor); 191 | } 192 | } 193 | // 第三方图标字体间距/大小设置 194 | .el-menu-item .iconfont, 195 | .el-submenu .iconfont, 196 | .el-menu-item .fa, 197 | .el-submenu__title .fa { 198 | font-size: 14px !important; 199 | display: inline-block; 200 | vertical-align: middle; 201 | margin-right: 5px; 202 | width: 24px; 203 | text-align: center; 204 | } 205 | // element plus 本身字体图标 206 | .el-submenu [class^='el-icon-'], 207 | .el-menu-item [class^='el-icon-'] { 208 | font-size: 14px !important; 209 | } 210 | // 去掉离开浏览器时,菜单的默认高亮 211 | .el-menu-item:focus { 212 | background-color: transparent !important; 213 | } 214 | 215 | /* Alert 警告 216 | ------------------------------- */ 217 | .el-alert__title { 218 | word-break: break-all; 219 | } 220 | -------------------------------------------------------------------------------- /src/theme/index.scss: -------------------------------------------------------------------------------- 1 | @import './base.scss'; 2 | @import './app.scss'; 3 | @import './other.scss'; 4 | @import './element.scss'; 5 | @import './media/media.scss'; 6 | @import './variables.scss'; 7 | @import './dark.scss'; 8 | -------------------------------------------------------------------------------- /src/theme/loading.scss: -------------------------------------------------------------------------------- 1 | .loading-prev { 2 | width: 100%; 3 | height: 100%; 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | z-index: 2; 8 | background-color: var(--prev-bg-white); 9 | } 10 | .loading-prev .loading-prev-box { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -50%); 15 | } 16 | .loading-prev .loading-prev-box-warp { 17 | width: 80px; 18 | height: 80px; 19 | } 20 | .loading-prev .loading-prev-box-warp .loading-prev-box-item { 21 | width: 33.333333%; 22 | height: 33.333333%; 23 | background: var(--prev-color-primary); 24 | float: left; 25 | animation: loading-prev-animation 1.2s infinite ease; 26 | border-radius: 1px; 27 | } 28 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(7) { 29 | animation-delay: 0s; 30 | } 31 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(4), 32 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(8) { 33 | animation-delay: 0.1s; 34 | } 35 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(1), 36 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(5), 37 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(9) { 38 | animation-delay: 0.2s; 39 | } 40 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(2), 41 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(6) { 42 | animation-delay: 0.3s; 43 | } 44 | .loading-prev .loading-prev-box-warp .loading-prev-box-item:nth-child(3) { 45 | animation-delay: 0.4s; 46 | } 47 | @keyframes loading-prev-animation { 48 | 0%, 49 | 70%, 50 | 100% { 51 | transform: scale3D(1, 1, 1); 52 | } 53 | 35% { 54 | transform: scale3D(0, 0, 1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/theme/media/dialog.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于800px 4 | ------------------------------- */ 5 | @media screen and (max-width: 800px) { 6 | .el-dialog { 7 | width: 90%; 8 | } 9 | .el-dialog.is-fullscreen { 10 | width: 100% !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/theme/media/error.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .error { 7 | .error-flex { 8 | flex-direction: column-reverse !important; 9 | height: auto !important; 10 | width: 100% !important; 11 | } 12 | .right, 13 | .left { 14 | flex: unset !important; 15 | display: flex !important; 16 | } 17 | .left-item { 18 | margin: auto !important; 19 | } 20 | .right img { 21 | max-width: 450px !important; 22 | @extend .left-item; 23 | } 24 | } 25 | } 26 | 27 | /* 页面宽度大于768px小于992px 28 | ------------------------------- */ 29 | @media screen and (min-width: $sm) and (max-width: $md) { 30 | .error { 31 | .error-flex { 32 | padding-left: 30px !important; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/theme/media/form.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-form-item__label { 7 | width: 100% !important; 8 | text-align: left !important; 9 | } 10 | .el-form-item__content { 11 | margin-left: 0 !important; 12 | } 13 | .el-form-item { 14 | display: unset !important; 15 | } 16 | .login-form .el-form-item { 17 | display: block !important; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/theme/media/home.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于1200px 4 | ------------------------------- */ 5 | @media screen and (max-width: $lg) { 6 | .home-recommend-row { 7 | .home-recommend { 8 | margin-bottom: 15px; 9 | } 10 | & .el-col:last-of-type, 11 | & .el-col:nth-last-child(2) { 12 | .home-recommend { 13 | margin-bottom: 0; 14 | } 15 | } 16 | } 17 | .home-lg { 18 | margin-bottom: 15px; 19 | } 20 | } 21 | 22 | /* 页面宽度小于992px 23 | ------------------------------- */ 24 | @media screen and (max-width: $md) { 25 | .home-recommend-row { 26 | & .el-col:nth-last-child(2) { 27 | margin-bottom: 15px; 28 | } 29 | & .el-col:last-of-type { 30 | .home-recommend { 31 | margin-bottom: 0; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/theme/media/index.scss: -------------------------------------------------------------------------------- 1 | /* 栅格布局(媒体查询变量) 2 | * $xs <768px 响应式栅格 3 | * $sm ≥768px 响应式栅格 4 | * $md ≥992px 响应式栅格 5 | * $lg ≥1200px 响应式栅格 6 | * $xl ≥1920px 响应式栅格 7 | ------------------------------- */ 8 | $xs: 576px; 9 | $sm: 768px; 10 | $md: 992px; 11 | $lg: 1200px; 12 | $xl: 1920px; 13 | 14 | /* 页面宽度小于576px 15 | ------------------------------- */ 16 | @media screen and (max-width: $xs) { 17 | } 18 | 19 | /* 页面宽度小于768px 20 | ------------------------------- */ 21 | @media screen and (max-width: $sm) { 22 | } 23 | 24 | /* 页面宽度大于768px小于992px 25 | ------------------------------- */ 26 | @media screen and (min-width: $sm) and (max-width: $md) { 27 | } 28 | 29 | /* 页面宽度大于992px小于1200px 30 | ------------------------------- */ 31 | @media screen and (min-width: $md) and (max-width: $lg) { 32 | } 33 | 34 | /* 页面宽度大于1920px 35 | ------------------------------- */ 36 | @media screen and (min-width: $xl) { 37 | } 38 | -------------------------------------------------------------------------------- /src/theme/media/layout.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | // MessageBox 弹框 7 | .el-message-box { 8 | width: 80% !important; 9 | } 10 | } 11 | 12 | /* 页面宽度小于768px 13 | ------------------------------- */ 14 | @media screen and (max-width: $sm) { 15 | // Breadcrumb 面包屑 16 | .layout-navbars-breadcrumb-hide { 17 | display: none; 18 | } 19 | // 外链视图 20 | .layout-view-link { 21 | a { 22 | max-width: 80%; 23 | text-align: center; 24 | } 25 | } 26 | // 菜单搜索 27 | .layout-search-dialog { 28 | .el-autocomplete { 29 | width: 80% !important; 30 | } 31 | } 32 | } 33 | 34 | /* 页面宽度小于1000px 35 | ------------------------------- */ 36 | @media screen and (max-width: 1000px) { 37 | // 布局配置 38 | .layout-drawer-content-flex { 39 | position: relative; 40 | &::after { 41 | content: '手机版不支持切换布局'; 42 | position: absolute; 43 | top: 0; 44 | right: 0; 45 | bottom: 0; 46 | left: 0; 47 | z-index: 1; 48 | text-align: center; 49 | height: 140px; 50 | line-height: 140px; 51 | background: rgba(255, 255, 255, 0.9); 52 | color: #666666; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/theme/media/login.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .login-weaper { 7 | height: 420px !important; 8 | .login-left { 9 | display: none !important; 10 | } 11 | .login-right { 12 | width: 100% !important; 13 | border-top-left-radius: 4px; 14 | border-bottom-left-radius: 4px; 15 | .login-main { 16 | width: 94% !important; 17 | } 18 | } 19 | } 20 | } 21 | 22 | /* 页面宽度大于576px小于992px 23 | ------------------------------- */ 24 | @media screen and (min-width: $xs) and (max-width: $md) { 25 | .login-left { 26 | display: none !important; 27 | } 28 | .login-right { 29 | border-top-left-radius: 4px; 30 | border-bottom-left-radius: 4px; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/theme/media/media.scss: -------------------------------------------------------------------------------- 1 | @import './login.scss'; 2 | @import './error.scss'; 3 | @import './home.scss'; 4 | @import './layout.scss'; 5 | @import './scrollbar.scss'; 6 | @import './dialog.scss'; 7 | @import './form.scss'; 8 | -------------------------------------------------------------------------------- /src/theme/media/scrollbar.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | // element plus scrollbar 7 | .el-scrollbar__bar.is-vertical { 8 | width: 2px !important; 9 | } 10 | .el-scrollbar__bar.is-horizontal { 11 | height: 2px !important; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/theme/other.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yzcheng90/x-springboot-ui/9e00e98b85f838c7001b049abe135a5a089d6de3/src/theme/other.scss -------------------------------------------------------------------------------- /src/utils/componentSize.js: -------------------------------------------------------------------------------- 1 | import { Local } from '@/utils/storage.js'; 2 | 3 | // 全局组件大小 4 | export const globalComponentSize = Local.get('themeConfigPrev') ? Local.get('themeConfigPrev').globalComponentSize : ''; 5 | -------------------------------------------------------------------------------- /src/utils/formatTime.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 年(Y) 可用1-4个占位符 3 | * 月(m)、日(d)、小时(H)、分(M)、秒(S) 可用1-2个占位符 4 | * 星期(W) 可用1-3个占位符 5 | * 季度(q为阿拉伯数字,Q为中文数字)可用1或4个占位符 6 | * 7 | * let date = new Date() 8 | * formatDate(date, "YYYY-mm-dd HH:MM:SS") // 2020-02-09 14:04:23 9 | * formatDate(date, "YYYY-mm-dd HH:MM:SS Q") // 2020-02-09 14:09:03 一 10 | * formatDate(date, "YYYY-mm-dd HH:MM:SS WWW") // 2020-02-09 14:45:12 星期日 11 | * formatDate(date, "YYYY-mm-dd HH:MM:SS QQQQ") // 2020-02-09 14:09:36 第一季度 12 | * formatDate(date, "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-02-09 14:46:12 星期日 第一季度 13 | */ 14 | export function formatDate(date, format) { 15 | let we = date.getDay(); // 星期 16 | let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度 17 | const opt = { 18 | 'Y+': date.getFullYear().toString(), // 年 19 | 'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1) 20 | 'd+': date.getDate().toString(), // 日 21 | 'H+': date.getHours().toString(), // 时 22 | 'M+': date.getMinutes().toString(), // 分 23 | 'S+': date.getSeconds().toString(), // 秒 24 | 'q+': qut, // 季度 25 | }; 26 | const week = { 27 | // 中文数字 (星期) 28 | '0': '日', 29 | '1': '一', 30 | '2': '二', 31 | '3': '三', 32 | '4': '四', 33 | '5': '五', 34 | '6': '六', 35 | }; 36 | const quarter = { 37 | // 中文数字(季度) 38 | '1': '一', 39 | '2': '二', 40 | '3': '三', 41 | '4': '四', 42 | }; 43 | if (/(W+)/.test(format)) { 44 | format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]); 45 | } 46 | if (/(Q+)/.test(format)) { 47 | // 输入一个Q,只输出一个中文数字,输入4个Q,则拼接上字符串 48 | format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]); 49 | } 50 | for (let k in opt) { 51 | let r = new RegExp('(' + k + ')').exec(format); 52 | if (r) { 53 | // 若输入的长度不为1,则前面补零 54 | format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')); 55 | } 56 | } 57 | return format; 58 | } 59 | 60 | /** 61 | * 10秒: 10 * 1000 62 | * 1分: 60 * 1000 63 | * 1小时: 60 * 60 * 1000 64 | * 24小时:60 * 60 * 24 * 1000 65 | * 3天: 60 * 60* 24 * 1000 * 3 66 | * 67 | * let data = new Date() 68 | * formatPast(data) // 刚刚 69 | * formatPast(data - 11 * 1000) // 11秒前 70 | * formatPast(data - 2 * 60 * 1000) // 2分钟前 71 | * formatPast(data - 60 * 60 * 2 * 1000) // 2小时前 72 | * formatPast(data - 60 * 60 * 2 * 1000) // 2小时前 73 | * formatPast(data - 60 * 60 * 71 * 1000) // 2天前 74 | * formatPast("2020-06-01") // 2020-06-01 75 | * formatPast("2020-06-01", "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-06-01 08:00:00 星期一 第二季度 76 | */ 77 | export function formatPast(param, format = 'YYYY-mm-dd') { 78 | // 传入格式处理、存储转换值 79 | let t, s; 80 | // 获取js 时间戳 81 | let time = new Date().getTime(); 82 | // 是否是对象 83 | typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param); 84 | // 当前时间戳 - 传入时间戳 85 | time = Number.parseInt(time - t); 86 | if (time < 10000) { 87 | // 10秒内 88 | return '刚刚'; 89 | } else if (time < 60000 && time >= 10000) { 90 | // 超过10秒少于1分钟内 91 | s = Math.floor(time / 1000); 92 | return `${s}秒前`; 93 | } else if (time < 3600000 && time >= 60000) { 94 | // 超过1分钟少于1小时 95 | s = Math.floor(time / 60000); 96 | return `${s}分钟前`; 97 | } else if (time < 86400000 && time >= 3600000) { 98 | // 超过1小时少于24小时 99 | s = Math.floor(time / 3600000); 100 | return `${s}小时前`; 101 | } else if (time < 259200000 && time >= 86400000) { 102 | // 超过1天少于3天内 103 | s = Math.floor(time / 86400000); 104 | return `${s}天前`; 105 | } else { 106 | // 超过3天 107 | let date = typeof param === 'string' || 'object' ? new Date(param) : param; 108 | return formatDate(date, format); 109 | } 110 | } 111 | 112 | /** 113 | * formatAxis(new Date()) // 上午好 114 | */ 115 | export function formatAxis(param) { 116 | let hour = new Date(param).getHours(); 117 | if (hour < 6) { 118 | return '凌晨好'; 119 | } else if (hour < 9) { 120 | return '早上好'; 121 | } else if (hour < 12) { 122 | return '上午好'; 123 | } else if (hour < 14) { 124 | return '中午好'; 125 | } else if (hour < 17) { 126 | return '下午好'; 127 | } else if (hour < 19) { 128 | return '傍晚好'; 129 | } else if (hour < 22) { 130 | return '晚上好'; 131 | } else { 132 | return '夜里好'; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/utils/getStyleSheets.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | // 获取阿里字体图标 4 | const getAlicdnIconfont = () => { 5 | return new Promise((resolve, reject) => { 6 | Vue.nextTick(() => { 7 | const styles = document.styleSheets; 8 | let sheetsList = []; 9 | let sheetsIconList = []; 10 | for (let i = 0; i < styles.length; i++) { 11 | if (styles[i].href && styles[i].href.indexOf('at.alicdn.com') > -1) { 12 | sheetsList.push(styles[i]); 13 | } 14 | } 15 | for (let i = 0; i < sheetsList.length; i++) { 16 | for (let j = 0; j < sheetsList[i].cssRules.length; j++) { 17 | if (sheetsList[i].cssRules[j].selectorText && sheetsList[i].cssRules[j].selectorText.indexOf('.icon-') > -1) { 18 | sheetsIconList.push( 19 | `${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\\:\\:before/gi, '')}` 20 | ); 21 | } 22 | } 23 | } 24 | if (sheetsIconList.length > 0) resolve(sheetsIconList); 25 | else reject('未获取到值,请刷新重试'); 26 | }); 27 | }); 28 | }; 29 | 30 | // 初始化获取 css 样式,获取 element plus 自带图标 31 | const getElementPlusIconfont = () => { 32 | return new Promise((resolve, reject) => { 33 | Vue.nextTick(() => { 34 | const styles = document.styleSheets; 35 | let sheetsIconList = []; 36 | for (let i = 0; i < styles.length; i++) { 37 | for (let j = 0; j < styles[i].cssRules.length; j++) { 38 | if (styles[i].cssRules[j].selectorText && styles[i].cssRules[j].selectorText.indexOf('.el-icon-') === 0) { 39 | sheetsIconList.push( 40 | `${styles[i].cssRules[j].selectorText.substring(1, styles[i].cssRules[j].selectorText.length).replace(/\\:\\:before/gi, '')}` 41 | ); 42 | } 43 | } 44 | } 45 | if (sheetsIconList.length > 0) resolve(sheetsIconList); 46 | else reject('未获取到值,请刷新重试'); 47 | }); 48 | }); 49 | }; 50 | 51 | // 初始化获取 css 样式,这里使用 fontawesome 的图标 52 | const getAwesomeIconfont = () => { 53 | return new Promise((resolve, reject) => { 54 | Vue.nextTick(() => { 55 | const styles = document.styleSheets; 56 | let sheetsList = []; 57 | let sheetsIconList = []; 58 | for (let i = 0; i < styles.length; i++) { 59 | if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) { 60 | sheetsList.push(styles[i]); 61 | } 62 | } 63 | for (let i = 0; i < sheetsList.length; i++) { 64 | for (let j = 0; j < sheetsList[i].cssRules.length; j++) { 65 | if ( 66 | sheetsList[i].cssRules[j].selectorText && 67 | sheetsList[i].cssRules[j].selectorText.indexOf('.fa-') === 0 && 68 | sheetsList[i].cssRules[j].selectorText.indexOf(',') === -1 69 | ) { 70 | sheetsIconList.push( 71 | `${sheetsList[i].cssRules[j].selectorText.substring(1, sheetsList[i].cssRules[j].selectorText.length).replace(/\\:\\:before/gi, '')}` 72 | ); 73 | } 74 | } 75 | } 76 | if (sheetsIconList.length > 0) resolve(sheetsIconList); 77 | else reject('未获取到值,请刷新重试'); 78 | }); 79 | }); 80 | }; 81 | 82 | // 定义导出方法集合 83 | const initIconfont = { 84 | // iconfont 85 | ali: () => { 86 | return getAlicdnIconfont(); 87 | }, 88 | // element plus 89 | ele: () => { 90 | return getElementPlusIconfont(); 91 | }, 92 | // fontawesome 93 | awe: () => { 94 | return getAwesomeIconfont(); 95 | }, 96 | }; 97 | 98 | // 导出方法 99 | export default initIconfont; 100 | -------------------------------------------------------------------------------- /src/utils/loading.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import loadingCss from '@/theme/loading.scss'; 3 | 4 | // 定义方法 5 | export const PrevLoading = { 6 | // 载入 css 7 | setCss: () => { 8 | let link = document.createElement('link'); 9 | link.rel = 'stylesheet'; 10 | link.href = loadingCss; 11 | link.crossOrigin = 'anonymous'; 12 | document.getElementsByTagName('head')[0].appendChild(link); 13 | }, 14 | // 创建 loading 15 | start: () => { 16 | const bodys = document.body; 17 | const div = document.createElement('div'); 18 | div.setAttribute('class', 'loading-prev'); 19 | const htmls = ` 20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | `; 34 | div.innerHTML = htmls; 35 | bodys.insertBefore(div, bodys.childNodes[0]); 36 | }, 37 | // 移除 loading 38 | done: () => { 39 | Vue.nextTick(() => { 40 | setTimeout(() => { 41 | const el = document.querySelector('.loading-prev'); 42 | el && el.parentNode?.removeChild(el); 43 | }, 1000); 44 | }); 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | import router, { resetRouter } from '@/router/index'; 3 | import axios from 'axios'; 4 | import { Message, MessageBox } from 'element-ui'; 5 | import { Session } from '@/utils/storage'; 6 | 7 | // 创建 axios 实例 8 | const service = axios.create({ 9 | baseURL: process.env.VUE_APP_BASE_API, 10 | timeout: 50000, 11 | headers: { 'Content-Type': 'application/json' }, 12 | }); 13 | 14 | // 添加请求拦截器 15 | service.interceptors.request.use( 16 | (config) => { 17 | // 在发送请求之前做些什么 token 18 | if (Session.get('token')) { 19 | config.headers['token'] = `${Session.get('token')}`; 20 | } 21 | return config; 22 | }, 23 | (error) => { 24 | // 对请求错误做些什么 25 | return Promise.reject(error); 26 | } 27 | ); 28 | 29 | // 添加响应拦截器 30 | service.interceptors.response.use( 31 | (response) => { 32 | // 对响应数据做点什么 33 | const res = response.data; 34 | if (res.code && res.code !== 0) { 35 | // `token` 过期或者账号已在别处登录 36 | if (res.code === 401) { 37 | // 清除浏览器全部临时缓存 38 | Session.clear(); 39 | router.push('/login'); 40 | store.commit('setMenuData', {}); 41 | resetRouter(); // 重置路由 42 | MessageBox.alert('你已被登出,请重新登录', '提示', {}) 43 | .then(() => {}) 44 | .catch(() => {}); 45 | }else if(res.code === 500){ 46 | Message.error(res.msg); 47 | }else { 48 | Message.error("请求code 错误"); 49 | } 50 | return Promise.reject(service.interceptors.response.error); 51 | } else { 52 | return response.data; 53 | } 54 | }, 55 | (error) => { 56 | // 对响应错误做点什么 57 | if (error.message.indexOf('timeout') != -1) { 58 | Message.error('网络超时'); 59 | } else if (error.message == 'Network Error') { 60 | Message.error('网络连接错误'); 61 | } else { 62 | Message.error(error.message); 63 | } 64 | return Promise.reject(error); 65 | } 66 | ); 67 | 68 | // 导出 axios 实例 69 | export default service; 70 | -------------------------------------------------------------------------------- /src/utils/setIconfont.js: -------------------------------------------------------------------------------- 1 | // 字体图标 url 2 | const cssCdnUrlList = ['//at.alicdn.com/t/font_2348547_zjuiclx86c.css', '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css']; 3 | // 第三方 js url 4 | const jsCdnUrlList = []; 5 | 6 | // 动态设置字体图标 7 | export function setCssCdn() { 8 | if (cssCdnUrlList.length <= 0) return false; 9 | cssCdnUrlList.map((v) => { 10 | let link = document.createElement('link'); 11 | link.rel = 'stylesheet'; 12 | link.href = v; 13 | link.crossOrigin = 'anonymous'; 14 | document.getElementsByTagName('head')[0].appendChild(link); 15 | }); 16 | } 17 | 18 | // 批量设置第三方js 19 | export function setJsCdn() { 20 | if (jsCdnUrlList.length <= 0) return false; 21 | jsCdnUrlList.map((v) => { 22 | let link = document.createElement('script'); 23 | link.src = v; 24 | document.body.appendChild(link); 25 | }); 26 | } 27 | 28 | // 设置执行函数 29 | const setIntroduction = { 30 | cssCdn: () => { 31 | setCssCdn(); 32 | }, 33 | jsCdn: () => { 34 | setJsCdn(); 35 | }, 36 | }; 37 | 38 | // 导出函数方法 39 | export default setIntroduction; 40 | -------------------------------------------------------------------------------- /src/utils/storage.js: -------------------------------------------------------------------------------- 1 | // 1、window.localStorage 浏览器永久缓存 2 | export const Local = { 3 | // 设置永久缓存 4 | set(key, val) { 5 | window.localStorage.setItem(key, JSON.stringify(val)); 6 | }, 7 | // 获取永久缓存 8 | get(key) { 9 | let json = window.localStorage.getItem(key); 10 | return JSON.parse(json); 11 | }, 12 | // 移除永久缓存 13 | remove(key) { 14 | window.localStorage.removeItem(key); 15 | }, 16 | // 移除全部永久缓存 17 | clear() { 18 | window.localStorage.clear(); 19 | }, 20 | }; 21 | 22 | // 2、window.sessionStorage 浏览器临时缓存 23 | export const Session = { 24 | // 设置临时缓存 25 | set(key, val) { 26 | window.sessionStorage.setItem(key, JSON.stringify(val)); 27 | }, 28 | // 获取临时缓存 29 | get(key) { 30 | let json = window.sessionStorage.getItem(key); 31 | return JSON.parse(json); 32 | }, 33 | // 移除临时缓存 34 | remove(key) { 35 | window.sessionStorage.removeItem(key); 36 | }, 37 | // 移除全部临时缓存 38 | clear() { 39 | window.sessionStorage.clear(); 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /src/utils/theme.js: -------------------------------------------------------------------------------- 1 | import { Message } from 'element-ui'; 2 | 3 | /** 4 | * 颜色转换函数 5 | * @method hexToRgb hex 颜色转 rgb 颜色 6 | * @method rgbToHex rgb 颜色转 Hex 颜色 7 | * @method getDarkColor 加深颜色值 8 | * @method getLightColor 变浅颜色值 9 | */ 10 | export function useChangeColor() { 11 | // str 颜色值字符串 12 | const hexToRgb = (str) => { 13 | let hexs = ''; 14 | let reg = /^#?[0-9A-Fa-f]{6}$/; 15 | if (!reg.test(str)) { 16 | Message.warning('输入错误的hex'); 17 | return ''; 18 | } 19 | str = str.replace('#', ''); 20 | hexs = str.match(/../g); 21 | for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16); 22 | return hexs; 23 | }; 24 | // r 代表红色 | g 代表绿色 | b 代表蓝色 25 | const rgbToHex = (r, g, b) => { 26 | let reg = /^\d{1,3}$/; 27 | if (!reg.test(r) || !reg.test(g) || !reg.test(b)) { 28 | Message.warning('输入错误的rgb颜色值'); 29 | return ''; 30 | } 31 | let hexs = [r.toString(16), g.toString(16), b.toString(16)]; 32 | for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`; 33 | return `#${hexs.join('')}`; 34 | }; 35 | // color 颜色值字符串 | level 变浅的程度,限0-1之间 36 | const getDarkColor = (color, level) => { 37 | let reg = /^#?[0-9A-Fa-f]{6}$/; 38 | if (!reg.test(color)) { 39 | Message.warning('输入错误的hex颜色值'); 40 | return ''; 41 | } 42 | let rgb = useChangeColor().hexToRgb(color); 43 | for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level)); 44 | return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]); 45 | }; 46 | // color 颜色值字符串 | level 加深的程度,限0-1之间 47 | const getLightColor = (color, level) => { 48 | let reg = /^#?[0-9A-Fa-f]{6}$/; 49 | if (!reg.test(color)) { 50 | Message.warning('输入错误的hex颜色值'); 51 | return ''; 52 | } 53 | let rgb = useChangeColor().hexToRgb(color); 54 | for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]); 55 | return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]); 56 | }; 57 | return { 58 | hexToRgb, 59 | rgbToHex, 60 | getDarkColor, 61 | getLightColor, 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/toolsValidate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 2020.11.29 lyt 整理 3 | * 工具类集合,适用于平时开发 4 | */ 5 | 6 | /** 7 | * 验证百分比(不可以小数) 8 | * @param val 当前值字符串 9 | * @returns 返回处理后的字符串 10 | */ 11 | export function verifyNumberPercentage(val) { 12 | // 匹配空格 13 | let v = val.replace(/(^\s*)|(\s*$)/g, ''); 14 | // 只能是数字和小数点,不能是其他输入 15 | v = v.replace(/[^\d]/g, ''); 16 | // 不能以0开始 17 | v = v.replace(/^0/g, ''); 18 | // 数字超过100,赋值成最大值100 19 | v = v.replace(/^[1-9]\d\d{1,3}$/, '100'); 20 | // 返回结果 21 | return v; 22 | } 23 | 24 | /** 25 | * 验证百分比(可以小数) 26 | * @param val 当前值字符串 27 | * @returns 返回处理后的字符串 28 | */ 29 | export function verifyNumberPercentageFloat(val) { 30 | let v = verifyNumberIntegerAndFloat(val); 31 | // 数字超过100,赋值成最大值100 32 | v = v.replace(/^[1-9]\d\d{1,3}$/, '100'); 33 | // 超过100之后不给再输入值 34 | v = v.replace(/^100\.$/, '100'); 35 | // 返回结果 36 | return v; 37 | } 38 | 39 | // 小数或整数(不可以负数) 40 | export function verifyNumberIntegerAndFloat(val) { 41 | // 匹配空格 42 | let v = val.replace(/(^\s*)|(\s*$)/g, ''); 43 | // 只能是数字和小数点,不能是其他输入 44 | v = v.replace(/[^\d.]/g, ''); 45 | // 以0开始只能输入一个 46 | v = v.replace(/^0{2}$/g, '0'); 47 | // 保证第一位只能是数字,不能是点 48 | v = v.replace(/^\./g, ''); 49 | // 小数只能出现1位 50 | v = v.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.'); 51 | // 小数点后面保留2位 52 | v = v.replace(/^(\\-)*(\d+)\.(\d\d).*$/, '$1$2.$3'); 53 | // 返回结果 54 | return v; 55 | } 56 | 57 | // 正整数验证 58 | export function verifiyNumberInteger(val) { 59 | // 匹配空格 60 | let v = val.replace(/(^\s*)|(\s*$)/g, ''); 61 | // 去掉 '.' , 防止贴贴的时候出现问题 如 0.1.12.12 62 | v = v.replace(/[\\.]*/g, ''); 63 | // 去掉以 0 开始后面的数, 防止贴贴的时候出现问题 如 00121323 64 | v = v.replace(/(^0[\d]*)$/g, '0'); 65 | // 首位是0,只能出现一次 66 | v = v.replace(/^0\d$/g, '0'); 67 | // 只匹配数字 68 | v = v.replace(/[^\d]/g, ''); 69 | // 返回结果 70 | return v; 71 | } 72 | 73 | // 去掉中文及空格 74 | export function verifyCnAndSpace(val) { 75 | // 匹配中文与空格 76 | let v = val.replace(/[\u4e00-\u9fa5\s]+/g, ''); 77 | // 匹配空格 78 | v = v.replace(/(^\s*)|(\s*$)/g, ''); 79 | // 返回结果 80 | return v; 81 | } 82 | 83 | // 去掉英文及空格 84 | export function verifyEnAndSpace(val) { 85 | // 匹配英文与空格 86 | let v = val.replace(/[a-zA-Z]+/g, ''); 87 | // 匹配空格 88 | v = v.replace(/(^\s*)|(\s*$)/g, ''); 89 | // 返回结果 90 | return v; 91 | } 92 | 93 | // 禁止输入空格 94 | export function verifyAndSpace(val) { 95 | // 匹配空格 96 | let v = val.replace(/(^\s*)|(\s*$)/g, ''); 97 | // 返回结果 98 | return v; 99 | } 100 | 101 | // 金额用 `,` 区分开 102 | export function verifyNumberComma(val) { 103 | // 调用小数或整数(不可以负数)方法 104 | let v = verifyNumberIntegerAndFloat(val); 105 | // 字符串转成数组 106 | v = v.toString().split('.'); 107 | // \B 匹配非单词边界,两边都是单词字符或者两边都是非单词字符 108 | v[0] = v[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); 109 | // 数组转字符串 110 | v = v.join('.'); 111 | // 返回结果 112 | return v; 113 | } 114 | 115 | // 匹配文字变色(搜索时) 116 | export function verifyTextColor(val, text = '', color = 'red') { 117 | // 返回内容,添加颜色 118 | let v = text.replace(new RegExp(val, 'gi'), `${val}`); 119 | // 返回结果 120 | return v; 121 | } 122 | 123 | // 数字转中文大写 124 | export function verifyNumberCnUppercase(val, unit = '仟佰拾亿仟佰拾万仟佰拾元角分', v = '') { 125 | // 当前内容字符串添加 2个0,为什么?? 126 | val += '00'; 127 | // 返回某个指定的字符串值在字符串中首次出现的位置,没有出现,则该方法返回 -1 128 | let lookup = val.indexOf('.'); 129 | // substring:不包含结束下标内容,substr:包含结束下标内容 130 | if (lookup >= 0) val = val.substring(0, lookup) + val.substr(lookup + 1, 2); 131 | // 根据内容 val 的长度,截取返回对应大写 132 | unit = unit.substr(unit.length - val.length); 133 | // 循环截取拼接大写 134 | for (let i = 0; i < val.length; i++) { 135 | v += '零壹贰叁肆伍陆柒捌玖'.substr(val.substr(i, 1), 1) + unit.substr(i, 1); 136 | } 137 | // 正则处理 138 | v = v 139 | .replace(/零角零分$/, '整') 140 | .replace(/零[仟佰拾]/g, '零') 141 | .replace(/零{2,}/g, '零') 142 | .replace(/零([亿|万])/g, '$1') 143 | .replace(/零+元/, '元') 144 | .replace(/亿零{0,3}万/, '亿') 145 | .replace(/^元/, '零元'); 146 | // 返回结果 147 | return v; 148 | } 149 | 150 | // 手机号码 151 | export function verifyPhone(val) { 152 | // false: 手机号码不正确 153 | if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\d{8}$/.test(val)) return false; 154 | // true: 手机号码正确 155 | else return true; 156 | } 157 | 158 | // 国内电话号码 159 | export function verifyTelPhone(val) { 160 | // false: 国内电话号码不正确 161 | if (!/\d{3}-\d{8}|\d{4}-\d{7}/.test(val)) return false; 162 | // true: 国内电话号码正确 163 | else return true; 164 | } 165 | 166 | // 登录账号 (字母开头,允许5-16字节,允许字母数字下划线) 167 | export function verifyAccount(val) { 168 | // false: 登录账号不正确 169 | if (!/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/.test(val)) return false; 170 | // true: 登录账号正确 171 | else return true; 172 | } 173 | 174 | // 密码 (以字母开头,长度在6~16之间,只能包含字母、数字和下划线) 175 | export function verifyPassword(val) { 176 | // false: 密码不正确 177 | if (!/^[a-zA-Z]\w{5,15}$/.test(val)) return false; 178 | // true: 密码正确 179 | else return true; 180 | } 181 | 182 | // 强密码 (字母+数字+特殊字符,长度在6-16之间) 183 | export function verifyPasswordPowerful(val) { 184 | // false: 强密码不正确 185 | if ( 186 | !/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\\.*]+$)(?![\d!@#$%^&\\.*]+$)[a-zA-Z\d!@#$%^&\\.*]{6,16}$/.test(val) 187 | ) 188 | return false; 189 | // true: 强密码正确 190 | else return true; 191 | } 192 | 193 | // 密码强度 194 | export function verifyPasswordStrength(val) { 195 | let v = ''; 196 | // 弱:纯数字,纯字母,纯特殊字符 197 | if (/^(?:\d+|[a-zA-Z]+|[!@#$%^&\\.*]+){6,16}$/.test(val)) v = '弱'; 198 | // 中:字母+数字,字母+特殊字符,数字+特殊字符 199 | if (/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\\.*]+$)[a-zA-Z\d!@#$%^&\\.*]{6,16}$/.test(val)) v = '中'; 200 | // 强:字母+数字+特殊字符 201 | if ( 202 | /^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&\\.*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&\\.*]+$)(?![\d!@#$%^&\\.*]+$)[a-zA-Z\d!@#$%^&\\.*]{6,16}$/.test(val) 203 | ) 204 | v = '强'; 205 | // 返回结果 206 | return v; 207 | } 208 | 209 | // IP地址 210 | export function verifyIPAddress(val) { 211 | // false: IP地址不正确 212 | if ( 213 | !/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test( 214 | val 215 | ) 216 | ) 217 | return false; 218 | // true: IP地址正确 219 | else return true; 220 | } 221 | 222 | // 邮箱 223 | export function verifyEmail(val) { 224 | // false: 邮箱不正确 225 | if ( 226 | !/^(([^<>()\\[\]\\.,;:\s@"]+(\.[^<>()\\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( 227 | val 228 | ) 229 | ) 230 | return false; 231 | // true: 邮箱正确 232 | else return true; 233 | } 234 | 235 | // 身份证 236 | export function verifyIdCard(val) { 237 | // false: 身份证不正确 238 | if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val)) return false; 239 | // true: 身份证正确 240 | else return true; 241 | } 242 | 243 | // 姓名 244 | export function verifyFullName(val) { 245 | // false: 姓名不正确 246 | if (!/^[\u4e00-\u9fa5]{1,6}(·[\u4e00-\u9fa5]{1,6}){0,2}$/.test(val)) return false; 247 | // true: 姓名正确 248 | else return true; 249 | } 250 | 251 | // 邮政编码 252 | export function verifyPostalCode(val) { 253 | // false: 邮政编码不正确 254 | if (!/^[1-9][0-9]{5}$/.test(val)) return false; 255 | // true: 邮政编码正确 256 | else return true; 257 | } 258 | 259 | // url 260 | export function verifyUrl(val) { 261 | // false: url不正确 262 | if ( 263 | !/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( 264 | val 265 | ) 266 | ) 267 | return false; 268 | // true: url正确 269 | else return true; 270 | } 271 | 272 | // 车牌号 273 | export function verifyCarNum(val) { 274 | // false: 车牌号不正确 275 | if ( 276 | !/^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test( 277 | val 278 | ) 279 | ) 280 | return false; 281 | // true:车牌号正确 282 | else return true; 283 | } 284 | -------------------------------------------------------------------------------- /src/views/error/401.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 38 | 39 | 96 | -------------------------------------------------------------------------------- /src/views/error/404.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 34 | 35 | 92 | -------------------------------------------------------------------------------- /src/views/fun/signCanvas/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 90 | -------------------------------------------------------------------------------- /src/views/fun/tagsView/index.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 122 | -------------------------------------------------------------------------------- /src/views/home/index.scss: -------------------------------------------------------------------------------- 1 | .home { 2 | width: 100%; 3 | overflow: hidden; 4 | .home-card-more { 5 | float: right; 6 | padding: 3px 0; 7 | font-size: 13px; 8 | } 9 | .home-card-time { 10 | float: right; 11 | font-size: 13px; 12 | width: 130px; 13 | margin-top: -4px; 14 | } 15 | .user-item { 16 | height: 198px; 17 | display: flex; 18 | align-items: center; 19 | .user-item-left { 20 | width: 100px; 21 | height: 130px; 22 | border-radius: 4px; 23 | overflow: hidden; 24 | img { 25 | width: 100%; 26 | height: 100%; 27 | } 28 | } 29 | .user-item-right { 30 | flex: 1; 31 | padding: 15px; 32 | .right-title { 33 | font-size: 20px; 34 | } 35 | .right-l-v { 36 | font-size: 13px; 37 | display: flex; 38 | .right-label { 39 | color: var(--prev-color-text-regular); 40 | width: 40px; 41 | } 42 | .right-value { 43 | flex: 1; 44 | } 45 | } 46 | } 47 | } 48 | .info { 49 | height: 198px; 50 | .info-scroll { 51 | height: 100%; 52 | overflow: hidden; 53 | .info-ul { 54 | list-style: none; 55 | .info-item { 56 | display: flex; 57 | font-size: 13px; 58 | color: var(--prev-color-text-regular); 59 | height: 28px; 60 | line-height: 28px; 61 | &:hover { 62 | color: var(--prev-color-primary); 63 | cursor: pointer; 64 | } 65 | .info-item-left { 66 | flex: 1; 67 | flex-shrink: 0; 68 | text-overflow: ellipsis; 69 | white-space: nowrap; 70 | overflow: hidden; 71 | } 72 | .info-item-right { 73 | width: 60px; 74 | text-align: right; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | .home-recommend-row { 81 | .home-recommend { 82 | position: relative; 83 | height: 100px; 84 | color: var(--prev-color-text-white); 85 | border-radius: 4px; 86 | overflow: hidden; 87 | cursor: pointer; 88 | &:hover { 89 | i { 90 | right: 0px !important; 91 | bottom: 0px !important; 92 | transition: all ease 0.3s; 93 | } 94 | } 95 | i { 96 | position: absolute; 97 | right: -10px; 98 | bottom: -10px; 99 | font-size: 70px; 100 | transform: rotate(-30deg); 101 | transition: all ease 0.3s; 102 | } 103 | .home-recommend-auto { 104 | padding: 15px; 105 | position: absolute; 106 | left: 0; 107 | top: 5%; 108 | .home-recommend-msg { 109 | font-size: 12px; 110 | margin-top: 10px; 111 | } 112 | } 113 | } 114 | } 115 | .charts { 116 | width: 100%; 117 | height: 282.6px; 118 | display: flex; 119 | padding: 12px 15px; 120 | .charts-left { 121 | flex: 1; 122 | height: 100%; 123 | } 124 | .charts-right { 125 | flex: 1; 126 | height: 100%; 127 | } 128 | } 129 | .home-charts { 130 | height: 282.6px; 131 | .home-charts-item { 132 | background-color: var(--prev-bg-main-color); 133 | padding: 19px 15px; 134 | border-radius: 2px; 135 | display: flex; 136 | align-items: center; 137 | margin-bottom: 12px; 138 | cursor: pointer; 139 | &:last-of-type { 140 | margin-bottom: 0; 141 | } 142 | &:hover { 143 | .home-charts-item-right { 144 | i { 145 | transform: rotate(45deg); 146 | transition: all ease 0.3s; 147 | } 148 | } 149 | } 150 | .home-charts-item-left { 151 | flex: 1; 152 | .home-charts-item-title { 153 | font-size: 13px; 154 | } 155 | .home-charts-item-num { 156 | font-size: 20px; 157 | margin-top: 5px; 158 | } 159 | } 160 | .home-charts-item-right { 161 | i { 162 | font-size: 20px; 163 | padding: 8px; 164 | border-radius: 100%; 165 | transition: all ease 0.3s; 166 | } 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/views/home/mock.js: -------------------------------------------------------------------------------- 1 | // 首页模拟数据 2 | export const recommendList = [ 3 | { 4 | title: '优惠券', 5 | msg: '现金券、折扣券、营销必备', 6 | icon: 'el-icon-food', 7 | bg: '#48D18D', 8 | iconColor: '#64d89d', 9 | }, 10 | { 11 | title: '多人拼团', 12 | msg: '社交电商、开辟流量', 13 | icon: 'el-icon-shopping-bag-1', 14 | bg: '#F95959', 15 | iconColor: '#F86C6B', 16 | }, 17 | { 18 | title: '分销中心', 19 | msg: '轻松招募分销员,成功推广奖励', 20 | icon: 'el-icon-school', 21 | bg: '#8595F4', 22 | iconColor: '#92A1F4', 23 | }, 24 | { 25 | title: '秒杀', 26 | msg: '超低价抢购引导更多销量', 27 | icon: 'el-icon-alarm-clock', 28 | bg: '#FEBB50', 29 | iconColor: '#FDC566', 30 | }, 31 | ]; 32 | 33 | export const chartsRightList = [ 34 | { 35 | title: '距离60分钟订单数量', 36 | num: 188, 37 | icon: 'el-icon-document', 38 | iconBg: '#C3E9FE', 39 | color: '#1890FF', 40 | }, 41 | { 42 | title: '距离30分钟订单数量', 43 | num: 15, 44 | icon: 'el-icon-tickets', 45 | iconBg: '#F1EBC5', 46 | color: '#FDB850', 47 | }, 48 | { 49 | title: '已超时数量', 50 | num: 6, 51 | icon: 'el-icon-document-delete', 52 | iconBg: '#F2D7C4', 53 | color: '#F8958C', 54 | }, 55 | ]; 56 | 57 | export const newsInfoList = [ 58 | { 59 | title: '[发布] 2023年01月22日发布基于1.0版本', 60 | date: '01/22', 61 | link: 'https://github.com/yzcheng90/x-springboot-ui', 62 | }, 63 | ]; 64 | 65 | export const dailyMessage = [ 66 | '祝你开心每一天!', 67 | '忙碌了一周,停一停脚步!', 68 | '世间美好,与你环环相扣!', 69 | '永远相信美好的事情即将发生!', 70 | '每一天,遇见更好的自己!', 71 | '保持热爱,奔赴山海!', 72 | '生活明朗,万物可爱!', 73 | '愿每一天醒来都是美好的开始!', 74 | '没有希望的地方,就没有奋斗!', 75 | '我最珍贵的时光都行走在路上!', 76 | '成功,往往住在失败的隔壁!', 77 | '人只要不失去方向,就不会失去自己!', 78 | '每条堵住的路,都有一个出口!', 79 | '没有谁能击垮你,除非你自甘堕落!', 80 | '微笑着的人并非没有痛苦!', 81 | '生活变的再糟糕,也不妨碍我变得更好!', 82 | '你要悄悄努力,然后惊艳众人!', 83 | ]; 84 | -------------------------------------------------------------------------------- /src/views/login/mock.js: -------------------------------------------------------------------------------- 1 | export const quotationsList = [ 2 | { 3 | name: '颜渊', 4 | comeFrom: '论语', 5 | content: 6 | '自己不喜欢的,就不要强加给别人。饥寒是自己不喜欢的,不要把它强加给别人;耻辱是自己不喜欢的,也不要把它强加给别人。将心比心,推己及人,从自己的利与害想到对别人的利与害,多替别人着想,这是终生应该奉行的原则。', 7 | }, 8 | { 9 | name: '荀子', 10 | comeFrom: '劝学', 11 | content: 12 | '木料经过木工用墨线(木工用具)划直线加工以后,就变直了;金属物品在磨刀石上磨砺后,就能锋利。人经过学习磨练,自我反省,就会变得聪慧明智,不犯错误,也越来越坚强。', 13 | }, 14 | { 15 | name: '里仁', 16 | comeFrom: '论语', 17 | content: 18 | '见到贤人,就应该想着向他学习;看见不贤的人,便应该自己反省,对不如自己的人喜欢讥笑、轻视,因而沾沾自喜;对比自己强的人喜欢贬低,甚至嫉妒、畏惧退缩,害怕与他们交往:这都是不正确的态度。', 19 | }, 20 | { 21 | name: '述而', 22 | comeFrom: '论语', 23 | content: 24 | '君子心地平坦宽广,小人却经常局促忧愁。君子襟怀坦白,安贫乐业,与人为善,知足常乐,所以能坦荡荡。小人欲念太多,患得患失,忧心忡忡,怨天尤人,局促不安,所以常心怀戚戚。', 25 | }, 26 | { 27 | name: '老子', 28 | comeFrom: '第六十四章', 29 | content: 30 | '千里遥远的路程是从脚下第一步开始的。任何事情的成功都是从头开始,从小到大逐渐积累的。万事开头难,没有个开头就不会有结果。任何事情都要从一点一滴的小事开始做起。', 31 | }, 32 | { 33 | name: '朱熹', 34 | comeFrom: '训学斋规', 35 | content: 36 | '读书有三到,谓心到,眼到,口到。心不在此,则眼看不仔细,心眼既不专一,却只漫浪诵读,决不能记,久也不能久也。三到之中,心到最急,心既到矣,眼口岂不到乎?', 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /src/views/loginLog/index.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 96 | 97 | -------------------------------------------------------------------------------- /src/views/optionLog/index.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 169 | 170 | -------------------------------------------------------------------------------- /src/views/pages/formAdapt/index.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 94 | -------------------------------------------------------------------------------- /src/views/personal/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 26 | 30 | -------------------------------------------------------------------------------- /src/views/role/index.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 243 | 244 | 247 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const url = 'http://localhost:8080' 2 | module.exports = { 3 | publicPath: './', 4 | outputDir: 'ui', 5 | assetsDir: 'static', 6 | productionSourceMap: false, 7 | lintOnSave: false, 8 | devServer: { 9 | open: false, // 自动打开浏览器 10 | port: 9999, // 前台代理端口号 11 | https: false, // https: {type: Booleam} 12 | hotOnly: false, // 热更新 13 | proxy: { 14 | // 设置代理 15 | '/': { 16 | target: url, 17 | ws: false, 18 | secure: false, 19 | changeOrigin: true, 20 | pathRewrite: { 21 | '^/': '', 22 | }, 23 | }, 24 | }, 25 | }, 26 | chainWebpack(config) { 27 | // 移除打包后 index.html 所有打包好的文件都预加载行为 28 | config.plugins.delete('preload'); 29 | config.plugins.delete('prefetch'); 30 | }, 31 | css: { 32 | extract: { ignoreOrder: true }, 33 | }, 34 | }; 35 | --------------------------------------------------------------------------------