├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── package.json ├── public ├── favicon.ico └── index.html └── src ├── App.vue ├── assets ├── base │ └── index.css ├── iconFont │ ├── demo.css │ ├── demo_index.html │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.json │ ├── iconfont.svg │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 └── logo.png ├── components ├── charts │ ├── bar.vue │ └── line.vue └── layout │ ├── Aside.vue │ ├── Head.vue │ └── Main.vue ├── docs ├── 前端解析.xlsx ├── 前端解析的副本.xlsx ├── 前端解析的副本2.xlsx └── 前端解析的副本3.xlsx ├── main.js ├── mock ├── charts │ ├── bar.js │ ├── index.js │ └── line.js └── icon │ ├── icon.js │ └── index.js ├── router ├── config.js ├── index.js └── layout.js ├── store └── index.js ├── utils ├── color.js ├── config.js ├── getAsyncData.js ├── goRepeat.js ├── isArrToObj.js └── verification.js └── views ├── Charts ├── Bar │ └── index.vue └── Line │ └── index.vue ├── Docs ├── Drag │ └── index.vue └── Xlsx │ ├── components │ ├── handleHeads.js │ ├── handleTbody.js │ └── handleXlsx.js │ └── index.vue ├── Found └── index.vue ├── Game └── GreedySnake │ ├── components │ ├── food.js │ ├── index.js │ └── snake.js │ └── index.vue ├── Home └── index.vue ├── Icon └── index.vue ├── Layout └── index.vue ├── Login └── index.vue └── Personal └── index.vue /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: false, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | yarn.lock* 8 | package-lock.json* 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tyf2345 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3.x-admin 2 | 3 | ### 前言 4 | 这是一个完全使用 vue3.x 语法开发的后台管理系统,页面主要包括 CSS3特效, 前端解析xlsx,前端文件下载,可拖拽的div,图表,益智小游戏,vuex4.x 存储用户信息等功能,该项目基本覆盖了vue3.x全家桶的方方面面。 功能丰富,注释多多, 近期会更新D3.js图表,并会进行一些代码优化,欢迎大家提意见。 5 | 使用vue3.x开发项目 `真香!!!` 6 | 7 | ### demo: [传送阵](http://114.215.147.221:8086/login) 8 | ### 技术栈 9 | ```js 10 | Vue3.x + vue-router4.x + vuex4.x + Less + Echarts 11 | ``` 12 | 13 | ### 项目运行 14 | ```js 15 | git clone https://github.com/Tyf2345/vue3.x-admin.git 16 | cd vue-admin-ele 17 | yarn install 18 | yarn serve 19 | ``` 20 | 21 | ### vue2.x版本传送地址 (https://github.com/Tyf2345/vue-admin-ele) 22 | 23 | 24 | ### 说明 25 | 26 | > 如果对您有帮助,您可以点右上角 "Star" 支持一下 谢谢! ^_^ 27 | 28 | > 或者您可以 "follow" 一下,我会不断开源更多的有趣的项目 29 | 30 | > 如有问题请直接在 Issues 中提 31 | 32 | ### 目标功能 33 | - [x] 登录/登出 -- 完成 34 | - [x] 首页 -- 完成 35 | - [x] icon -- 完成 36 | - [x] table -- 完成 37 | - [x] 柱图 -- 完成 38 | - [x] 折线图 -- 完成 39 | - [x] 拖拽div -- 完成 40 | - [x] 前端解析xlsx -- 完成 41 | - [x] 个贪吃蛇小游戏 -- 完成 42 | - [x] 人中心 -- 完成 43 | 44 | ### 结构目录 45 | 46 | ├── build // webpack配置文件
47 | ├── config // 项目打包路径
48 | ├── src // 源码目录
49 | │   ├── assets // 公共资源, 公共css
50 | │   ├── components // 公共组件
51 | │   ├── docs // 前端上传 解析xlsx文件
52 | │   ├── mock // 异步模拟ajax调用接口
53 | │   ├── router // 路由相关
54 | │   ├── vuex // 状态管理相关
55 | │   ├── utils // 工具类,方法
56 | │   ├── views // 页面相关
57 | │   ├── App.vue // 页面入口文件
58 | │   ├── main.js // 程序入口文件,加载各种公共组件
59 | ├── index.html // 入口html文件
60 | 61 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend-vue3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "dayjs": "^1.8.28", 13 | "echarts": "^4.8.0", 14 | "mock": "^0.1.1", 15 | "vue": "^3.0.0-beta.15", 16 | "vue-router": "^4.0.0-alpha.12", 17 | "vuex": "^4.0.0-beta.2", 18 | "xlsx": "^0.16.2" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-plugin-babel": "~4.4.0", 22 | "@vue/cli-plugin-eslint": "~4.4.0", 23 | "@vue/cli-plugin-router": "~4.4.0", 24 | "@vue/cli-service": "~4.4.0", 25 | "@vue/compiler-sfc": "^3.0.0-beta.1", 26 | "babel-eslint": "^10.1.0", 27 | "eslint": "^6.7.2", 28 | "eslint-plugin-vue": "^7.0.0-alpha.0", 29 | "less": "^3.11.3", 30 | "less-loader": "^6.1.0", 31 | "vue-cli-plugin-vue-next": "~0.1.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /src/assets/base/index.css: -------------------------------------------------------------------------------- 1 | html,body,div,p,ul,li,span,a,button,input{ 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | html,body,#app{ 7 | height: 100%; 8 | } 9 | li{ 10 | list-style: none; 11 | } 12 | .ontline{ 13 | outline:0; 14 | } 15 | .cursorP{ 16 | cursor: pointer; 17 | } 18 | .ovflowHide{ 19 | overflow: hidden; 20 | } 21 | .positionR{ 22 | position: relative; 23 | } 24 | .positionA{ 25 | position: absolute; 26 | } 27 | .TRBL{ 28 | top: 0; 29 | right: 0; 30 | bottom: 0; 31 | left: 0; 32 | } 33 | .opacity0{ 34 | opacity: 0; 35 | } 36 | .w100{ 37 | width: 100%; 38 | } 39 | .h100{ 40 | height: 100vh; 41 | } 42 | .w80px{ 43 | width: 80px; 44 | } 45 | .w120px{ 46 | width: 120px; 47 | } 48 | .flx{ 49 | display: flex; 50 | } 51 | .flx1{ 52 | flex:1; 53 | } 54 | .border4px{ 55 | border-radius: 4px; 56 | } 57 | 58 | .pd0-20{ 59 | padding: 0 20px; 60 | } 61 | .pd0-40{ 62 | padding: 0 40px; 63 | } 64 | .mr15{ 65 | margin-right: 15px; 66 | } 67 | .mb15{ 68 | margin-bottom: 15px; 69 | } 70 | 71 | 72 | 73 | .colorfff { 74 | color: #fff; 75 | } 76 | 77 | .charts{ 78 | height: 400px; 79 | border: 1px solid #b3c0d1; 80 | border-radius: 6px; 81 | } -------------------------------------------------------------------------------- /src/assets/iconFont/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/assets/iconFont/demo_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IconFont Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 29 |
30 |
31 |
    32 | 33 |
  • 34 | 35 |
    文档
    36 |
    &#xe608;
    37 |
  • 38 | 39 |
  • 40 | 41 |
    游戏
    42 |
    &#xe620;
    43 |
  • 44 | 45 |
  • 46 | 47 |
    图表
    48 |
    &#xe6d8;
    49 |
  • 50 | 51 |
  • 52 | 53 |
    图标
    54 |
    &#xe606;
    55 |
  • 56 | 57 |
  • 58 | 59 |
    shouye
    60 |
    &#xe607;
    61 |
  • 62 | 63 |
  • 64 | 65 |
    collapse-right
    66 |
    &#xe94e;
    67 |
  • 68 | 69 |
  • 70 | 71 |
    collapse-left
    72 |
    &#xe60e;
    73 |
  • 74 | 75 |
  • 76 | 77 |
    xiala
    78 |
    &#xe659;
    79 |
  • 80 | 81 |
82 |
83 |

Unicode 引用

84 |
85 | 86 |

Unicode 是字体在网页端最原始的应用方式,特点是:

87 |
    88 |
  • 兼容性最好,支持 IE6+,及所有现代浏览器。
  • 89 |
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 90 |
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • 91 |
92 |
93 |

注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式

94 |
95 |

Unicode 使用步骤如下:

96 |

第一步:拷贝项目下面生成的 @font-face

97 |
@font-face {
 99 |   font-family: 'iconfont';
100 |   src: url('iconfont.eot');
101 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
102 |       url('iconfont.woff2') format('woff2'),
103 |       url('iconfont.woff') format('woff'),
104 |       url('iconfont.ttf') format('truetype'),
105 |       url('iconfont.svg#iconfont') format('svg');
106 | }
107 | 
108 |

第二步:定义使用 iconfont 的样式

109 |
.iconfont {
111 |   font-family: "iconfont" !important;
112 |   font-size: 16px;
113 |   font-style: normal;
114 |   -webkit-font-smoothing: antialiased;
115 |   -moz-osx-font-smoothing: grayscale;
116 | }
117 | 
118 |

第三步:挑选相应图标并获取字体编码,应用于页面

119 |
120 | <span class="iconfont">&#x33;</span>
122 | 
123 |
124 |

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

125 |
126 |
127 |
128 |
129 |
    130 | 131 |
  • 132 | 133 |
    134 | 文档 135 |
    136 |
    .icon-ziyuan 137 |
    138 |
  • 139 | 140 |
  • 141 | 142 |
    143 | 游戏 144 |
    145 |
    .icon-youxi-yuanshijituantubiao 146 |
    147 |
  • 148 | 149 |
  • 150 | 151 |
    152 | 图表 153 |
    154 |
    .icon-tubiao 155 |
    156 |
  • 157 | 158 |
  • 159 | 160 |
    161 | 图标 162 |
    163 |
    .icon-tubiao1 164 |
    165 |
  • 166 | 167 |
  • 168 | 169 |
    170 | shouye 171 |
    172 |
    .icon-shouye 173 |
    174 |
  • 175 | 176 |
  • 177 | 178 |
    179 | collapse-right 180 |
    181 |
    .icon-collapseright 182 |
    183 |
  • 184 | 185 |
  • 186 | 187 |
    188 | collapse-left 189 |
    190 |
    .icon-collapse-left 191 |
    192 |
  • 193 | 194 |
  • 195 | 196 |
    197 | xiala 198 |
    199 |
    .icon-xiala 200 |
    201 |
  • 202 | 203 |
204 |
205 |

font-class 引用

206 |
207 | 208 |

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

209 |

与 Unicode 使用方式相比,具有如下特点:

210 |
    211 |
  • 兼容性良好,支持 IE8+,及所有现代浏览器。
  • 212 |
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • 213 |
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • 214 |
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • 215 |
216 |

使用步骤如下:

217 |

第一步:引入项目下面生成的 fontclass 代码:

218 |
<link rel="stylesheet" href="./iconfont.css">
219 | 
220 |

第二步:挑选相应图标并获取类名,应用于页面:

221 |
<span class="iconfont icon-xxx"></span>
222 | 
223 |
224 |

" 225 | iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

226 |
227 |
228 |
229 |
230 |
    231 | 232 |
  • 233 | 236 |
    文档
    237 |
    #icon-ziyuan
    238 |
  • 239 | 240 |
  • 241 | 244 |
    游戏
    245 |
    #icon-youxi-yuanshijituantubiao
    246 |
  • 247 | 248 |
  • 249 | 252 |
    图表
    253 |
    #icon-tubiao
    254 |
  • 255 | 256 |
  • 257 | 260 |
    图标
    261 |
    #icon-tubiao1
    262 |
  • 263 | 264 |
  • 265 | 268 |
    shouye
    269 |
    #icon-shouye
    270 |
  • 271 | 272 |
  • 273 | 276 |
    collapse-right
    277 |
    #icon-collapseright
    278 |
  • 279 | 280 |
  • 281 | 284 |
    collapse-left
    285 |
    #icon-collapse-left
    286 |
  • 287 | 288 |
  • 289 | 292 |
    xiala
    293 |
    #icon-xiala
    294 |
  • 295 | 296 |
297 |
298 |

Symbol 引用

299 |
300 | 301 |

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 302 | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

303 |
    304 |
  • 支持多色图标了,不再受单色限制。
  • 305 |
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • 306 |
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • 307 |
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • 308 |
309 |

使用步骤如下:

310 |

第一步:引入项目下面生成的 symbol 代码:

311 |
<script src="./iconfont.js"></script>
312 | 
313 |

第二步:加入通用 CSS 代码(引入一次就行):

314 |
<style>
315 | .icon {
316 |   width: 1em;
317 |   height: 1em;
318 |   vertical-align: -0.15em;
319 |   fill: currentColor;
320 |   overflow: hidden;
321 | }
322 | </style>
323 | 
324 |

第三步:挑选相应图标并获取类名,应用于页面:

325 |
<svg class="icon" aria-hidden="true">
326 |   <use xlink:href="#icon-xxx"></use>
327 | </svg>
328 | 
329 |
330 |
331 | 332 |
333 |
334 | 353 | 354 | 355 | -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1592300743808'); /* IE9 */ 3 | src: url('iconfont.eot?t=1592300743808#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1592300743808') format('woff'), 6 | url('iconfont.ttf?t=1592300743808') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1592300743808#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-ziyuan:before { 19 | content: "\e608"; 20 | } 21 | 22 | .icon-youxi-yuanshijituantubiao:before { 23 | content: "\e620"; 24 | } 25 | 26 | .icon-tubiao:before { 27 | content: "\e6d8"; 28 | } 29 | 30 | .icon-tubiao1:before { 31 | content: "\e606"; 32 | } 33 | 34 | .icon-shouye:before { 35 | content: "\e607"; 36 | } 37 | 38 | .icon-collapseright:before { 39 | content: "\e94e"; 40 | } 41 | 42 | .icon-collapse-left:before { 43 | content: "\e60e"; 44 | } 45 | 46 | .icon-xiala:before { 47 | content: "\e659"; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/assets/iconFont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(c){var t,e,l,o,i,n,a,s='',h=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss");if(h&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function d(){n||(n=!0,o())}e=function(){var c,t,e,l,o,i=document.createElement("div");i.innerHTML=s,s=null,(c=i.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",t=c,(e=document.body).firstChild?(l=t,(o=e.firstChild).parentNode.insertBefore(l,o)):e.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),e()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(o=e,i=c.document,n=!1,(a=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(a,50)}d()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,d())})}(window); -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1879252", 3 | "name": "myBacnend", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "11177590", 10 | "name": "文档", 11 | "font_class": "ziyuan", 12 | "unicode": "e608", 13 | "unicode_decimal": 58888 14 | }, 15 | { 16 | "icon_id": "3696033", 17 | "name": "游戏", 18 | "font_class": "youxi-yuanshijituantubiao", 19 | "unicode": "e620", 20 | "unicode_decimal": 58912 21 | }, 22 | { 23 | "icon_id": "6851550", 24 | "name": "图表", 25 | "font_class": "tubiao", 26 | "unicode": "e6d8", 27 | "unicode_decimal": 59096 28 | }, 29 | { 30 | "icon_id": "7533297", 31 | "name": "图标", 32 | "font_class": "tubiao1", 33 | "unicode": "e606", 34 | "unicode_decimal": 58886 35 | }, 36 | { 37 | "icon_id": "8221830", 38 | "name": "shouye", 39 | "font_class": "shouye", 40 | "unicode": "e607", 41 | "unicode_decimal": 58887 42 | }, 43 | { 44 | "icon_id": "142287", 45 | "name": "collapse-right", 46 | "font_class": "collapseright", 47 | "unicode": "e94e", 48 | "unicode_decimal": 59726 49 | }, 50 | { 51 | "icon_id": "14364270", 52 | "name": "collapse-left", 53 | "font_class": "collapse-left", 54 | "unicode": "e60e", 55 | "unicode_decimal": 58894 56 | }, 57 | { 58 | "icon_id": "630037", 59 | "name": "xiala", 60 | "font_class": "xiala", 61 | "unicode": "e659", 62 | "unicode_decimal": 58969 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/assets/iconFont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/assets/iconFont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconFont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/assets/iconFont/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/charts/bar.vue: -------------------------------------------------------------------------------- 1 | 4 | 49 | -------------------------------------------------------------------------------- /src/components/charts/line.vue: -------------------------------------------------------------------------------- 1 | 4 | 49 | -------------------------------------------------------------------------------- /src/components/layout/Aside.vue: -------------------------------------------------------------------------------- 1 | 48 | 103 | -------------------------------------------------------------------------------- /src/components/layout/Head.vue: -------------------------------------------------------------------------------- 1 | 23 | 49 | -------------------------------------------------------------------------------- /src/components/layout/Main.vue: -------------------------------------------------------------------------------- 1 | 20 | 112 | -------------------------------------------------------------------------------- /src/docs/前端解析.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/docs/前端解析.xlsx -------------------------------------------------------------------------------- /src/docs/前端解析的副本.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/docs/前端解析的副本.xlsx -------------------------------------------------------------------------------- /src/docs/前端解析的副本2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/docs/前端解析的副本2.xlsx -------------------------------------------------------------------------------- /src/docs/前端解析的副本3.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tyf2345/vue3.x-admin/a555f5e8e119a06eefaa39348d4d99555efcc452/src/docs/前端解析的副本3.xlsx -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from './store' 5 | //公共css 6 | import "./assets/base/index.css"; 7 | // iconfont 8 | import './assets/iconFont/iconfont.css' 9 | const app = createApp(App) 10 | app.use(router) 11 | app.use(store) 12 | app.mount("#app"); 13 | -------------------------------------------------------------------------------- /src/mock/charts/bar.js: -------------------------------------------------------------------------------- 1 | const barData = [ 2 | { 3 | title: "服装销量", 4 | x: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"], 5 | y: [5, 20, 36, 10, 10, 20] 6 | }, 7 | { 8 | title:'周出勤', 9 | name:'出勤次数', 10 | x: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 11 | y: [120, 200, 150, 80, 70, 110, 130] 12 | }, 13 | { 14 | title:'周出勤', 15 | name:'出勤次数', 16 | x: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 17 | y: [120, 200, 150, 80, 70, 110, 130] 18 | },{ 19 | title: "服装销量", 20 | x: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"], 21 | y: [5, 20, 36, 10, 10, 20] 22 | },{ 23 | title: "服装销量", 24 | x: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"], 25 | y: [5, 20, 36, 10, 10, 20] 26 | } 27 | ] 28 | export default barData -------------------------------------------------------------------------------- /src/mock/charts/index.js: -------------------------------------------------------------------------------- 1 | // 异步模拟ajax请求 2 | import barData from './bar' 3 | import lineData from './line' 4 | import getAsyncData from '../../utils/getAsyncData' 5 | 6 | //柱状图数据 7 | const getBarData = getAsyncData(barData) 8 | 9 | //折线图数据 10 | const getLineData = getAsyncData(lineData) 11 | export { 12 | getBarData, 13 | getLineData 14 | } -------------------------------------------------------------------------------- /src/mock/charts/line.js: -------------------------------------------------------------------------------- 1 | let lineData = [ 2 | { 3 | title: '周堆叠图', 4 | x: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], 5 | y: [ 6 | { 7 | name: '邮件营销', 8 | type: 'line', 9 | stack: '总量', 10 | data: [120, 132, 101, 134, 90, 230, 210] 11 | }, 12 | { 13 | name: '联盟广告', 14 | type: 'line', 15 | stack: '总量', 16 | data: [220, 182, 191, 234, 290, 330, 310] 17 | }, 18 | { 19 | name: '视频广告', 20 | type: 'line', 21 | stack: '总量', 22 | data: [150, 232, 201, 154, 190, 330, 410] 23 | }, 24 | { 25 | name: '直接访问', 26 | type: 'line', 27 | stack: '总量', 28 | data: [320, 332, 301, 334, 390, 330, 320] 29 | }, 30 | { 31 | name: '搜索引擎', 32 | type: 'line', 33 | stack: '总量', 34 | data: [820, 932, 901, 934, 1290, 1330, 1320] 35 | } 36 | ] 37 | }, 38 | { 39 | title: '单条折线图', 40 | x: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], 41 | y: [{ 42 | type: 'line', 43 | data: [820, 932, 901, 934, 1290, 1330, 1320] 44 | }], 45 | } 46 | ] 47 | for (let i = 0; i < 2; i++) { 48 | lineData.push(...lineData) 49 | 50 | } 51 | export default lineData -------------------------------------------------------------------------------- /src/mock/icon/icon.js: -------------------------------------------------------------------------------- 1 | 2 | const iconData = [ 3 | { 4 | id: 0, 5 | name:'文档', 6 | icon: "icon-ziyuan" 7 | }, 8 | { 9 | id: 1, 10 | name:'游戏', 11 | icon: "icon-youxi-yuanshijituantubiao" 12 | }, 13 | { 14 | id: 2, 15 | name:'图表', 16 | icon: " ion-tubiao" 17 | }, 18 | { 19 | id: 3, 20 | name:"图标", 21 | icon: "icon-tubiao1" 22 | }, 23 | { 24 | id: 4, 25 | name:"shouye", 26 | icon: "icon-shouye" 27 | }, 28 | { 29 | id: 5, 30 | name:'collapse-right', 31 | icon: "icon-collapseright" 32 | }, 33 | { 34 | id: 6, 35 | name:'collapse-left', 36 | icon: "icon-collapse-left" 37 | }, 38 | { 39 | id: 7, 40 | name:'xiala', 41 | icon: "icon-xiala" 42 | } 43 | ] 44 | 45 | 46 | export default iconData -------------------------------------------------------------------------------- /src/mock/icon/index.js: -------------------------------------------------------------------------------- 1 | import iconData from './icon' 2 | import getAsyncData from '../../utils/getAsyncData' 3 | 4 | const getIconData = getAsyncData(iconData) 5 | 6 | export { 7 | getIconData 8 | } -------------------------------------------------------------------------------- /src/router/config.js: -------------------------------------------------------------------------------- 1 | import layout from './layout' 2 | 3 | let LayoutData = [] 4 | // 多维数据变一纬数组 ==》递归 5 | layout.forEach(_forEach) 6 | function _forEach(data) { 7 | if (data.children) { 8 | data.children.forEach(_forEach) 9 | } else { 10 | delete data.flag; 11 | LayoutData.push(data) 12 | } 13 | } 14 | 15 | const routes = [ 16 | { 17 | path: "/login", 18 | component: () => import("../views/Login"), 19 | }, 20 | { 21 | path: '/layout', 22 | component: () => import('../views/Layout'), 23 | children: [ 24 | { 25 | path: '', 26 | redirect: LayoutData[0].path, 27 | }, 28 | ...LayoutData 29 | ] 30 | }, 31 | { 32 | path: '/:catchAll(.*)', 33 | component: () => import('../views/Found') 34 | } 35 | ] 36 | 37 | export default routes -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from "vue-router"; 2 | import routes from './config' 3 | 4 | 5 | const router = createRouter({ 6 | history: createWebHistory(process.env.BASE_URL), 7 | routes, 8 | }); 9 | router.beforeEach((to, from, next) => { 10 | if (to.path === '/login') { 11 | next() 12 | return 13 | } 14 | let user = JSON.parse(localStorage.getItem('user')) 15 | if (user && user.userName && user.pwd) { 16 | next() 17 | return 18 | } 19 | next('/login') 20 | }) 21 | export default router; 22 | -------------------------------------------------------------------------------- /src/router/layout.js: -------------------------------------------------------------------------------- 1 | const layout = [ 2 | { 3 | path: '/layout/home', 4 | name: '首页', 5 | icon:'shouye icon-shouye', 6 | component: () => import('../views/Home') 7 | }, 8 | { 9 | path: '/layout/icon', 10 | name: '图标', 11 | icon:'图标 icon-tubiao1', 12 | component: () => import('../views/Icon') 13 | }, 14 | { 15 | name: '图表', 16 | icon:'图表 icon-tubiao', 17 | children:[ 18 | { 19 | path:'/layout/charts/bar', 20 | name:'柱状图', 21 | component: () => import('../views/Charts/Bar') 22 | }, { 23 | path:'/layout/charts/line', 24 | name:'折线图', 25 | component: () => import('../views/Charts/Line') 26 | } 27 | ] 28 | }, 29 | { 30 | name: '文档', 31 | icon:'文档 icon-ziyuan', 32 | children:[ 33 | { 34 | path:'/layout/docs/drag', 35 | name:'拖拽', 36 | component:() =>import('../views/Docs/Drag') 37 | }, 38 | { 39 | path:'/layout/docs/xlsx', 40 | name:'解析xlsx', 41 | component: () =>import('../views/Docs/Xlsx') 42 | } 43 | ] 44 | }, 45 | { 46 | name: '游戏', 47 | icon:'游戏 icon-youxi-yuanshijituantubiao', 48 | children:[ 49 | { 50 | path:'/layout/game/greedysnake', 51 | name:"贪吃 🐍", 52 | component: ()=>import('../views/Game/GreedySnake') 53 | } 54 | ] 55 | }, 56 | { 57 | path:'/layout/personal', 58 | name:'个人中心', 59 | icon:'shouye icon-shouye', 60 | component: () => import ('../views/Personal') 61 | } 62 | ] 63 | 64 | export default layout -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex' 2 | 3 | // 用户信息 4 | const store = createStore({ 5 | state(){ 6 | return { 7 | user:{} 8 | } 9 | }, 10 | mutations:{ 11 | userFun({user},{userName, pwd, time}) { 12 | user.userName = userName 13 | user.pwd = pwd 14 | user.time = time 15 | } 16 | } 17 | }) 18 | 19 | export default store -------------------------------------------------------------------------------- /src/utils/color.js: -------------------------------------------------------------------------------- 1 | // 随机获取十六进制颜色 2 | function getColor() { 3 | var str = "#"; 4 | var arr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]; 5 | for (var i = 0; i < 6; i++) { 6 | //产生的每个随机数都是一个索引,根据索引找到数组中对应的值,拼接到一起 7 | str += arr[parseInt(Math.random() * 16)]; 8 | 9 | } 10 | return str; 11 | } 12 | export default getColor -------------------------------------------------------------------------------- /src/utils/config.js: -------------------------------------------------------------------------------- 1 | export const time = 300 -------------------------------------------------------------------------------- /src/utils/getAsyncData.js: -------------------------------------------------------------------------------- 1 | import { time } from './config' 2 | //模拟ajax异步请求 3 | const getAsyncData = data => { 4 | return new Promise(res => setTimeout(() => res(data), time)) 5 | } 6 | export default getAsyncData -------------------------------------------------------------------------------- /src/utils/goRepeat.js: -------------------------------------------------------------------------------- 1 | // 去重 2 | export function goRepeat(data, name) { 3 | 4 | let newData = [],newObj = {} 5 | if (data.length) { 6 | data.forEach(item => { 7 | if (!newObj[item[name]]) { 8 | newData.push(item) 9 | newObj[item[name]] = true 10 | } 11 | }) 12 | } 13 | console.log('newData',newData) 14 | return newData 15 | } -------------------------------------------------------------------------------- /src/utils/isArrToObj.js: -------------------------------------------------------------------------------- 1 | // 数组转为对象 ==> [xx,qq,aa]:{xx:'',qq:'',aa:''} 2 | function isArrToObj(arr) { 3 | let obj = {} 4 | 5 | for (let i = 0; i < arr.length; i++) { 6 | 7 | let arr_i = arr[i] 8 | obj[arr_i] = '' 9 | } 10 | return obj 11 | 12 | } 13 | 14 | export default isArrToObj -------------------------------------------------------------------------------- /src/utils/verification.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 非空判断 3 | * @param {isTrimAction} str 4 | */ 5 | export function isTrimAction(str) { 6 | return (str+'').trim().length ? true: false 7 | } -------------------------------------------------------------------------------- /src/views/Charts/Bar/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 29 | -------------------------------------------------------------------------------- /src/views/Charts/Line/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 29 | -------------------------------------------------------------------------------- /src/views/Docs/Drag/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 85 | -------------------------------------------------------------------------------- /src/views/Docs/Xlsx/components/handleHeads.js: -------------------------------------------------------------------------------- 1 | 2 | function Heads(data) { 3 | let heads = [] 4 | data.forEach(item=>{ 5 | for (const k in item) { 6 | if (heads.indexOf(k)=== -1) { 7 | heads.push(k) 8 | } 9 | } 10 | }) 11 | return heads 12 | } 13 | export default Heads 14 | 15 | -------------------------------------------------------------------------------- /src/views/Docs/Xlsx/components/handleTbody.js: -------------------------------------------------------------------------------- 1 | import isArrToObj from '../../../../utils/isArrToObj' 2 | 3 | function handleTbody(data, headArr) { 4 | 5 | let newData = [] 6 | let headObj = isArrToObj(headArr) 7 | data.forEach(item => { 8 | 9 | let newObj = {...headObj} 10 | for (const k in item) { 11 | 12 | if (headArr.indexOf(k) !== -1) { 13 | newObj[k] = item[k] 14 | } 15 | } 16 | newData.push(newObj) 17 | 18 | }) 19 | return newData 20 | } 21 | 22 | export default handleTbody -------------------------------------------------------------------------------- /src/views/Docs/Xlsx/components/handleXlsx.js: -------------------------------------------------------------------------------- 1 | import XLSX from 'xlsx' 2 | 3 | function handleXlsx(file) { 4 | let workbook = XLSX.read(file.target.result, { type: 'array' }) 5 | var sheetNames = workbook.SheetNames; 6 | var worksheet = workbook.Sheets[sheetNames[0]]; 7 | let json = XLSX.utils.sheet_to_json(worksheet); 8 | let csv = XLSX.utils.sheet_to_csv(worksheet) 9 | return {json,csv} 10 | } 11 | export default handleXlsx -------------------------------------------------------------------------------- /src/views/Docs/Xlsx/index.vue: -------------------------------------------------------------------------------- 1 | 40 | 90 | -------------------------------------------------------------------------------- /src/views/Found/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | -------------------------------------------------------------------------------- /src/views/Game/GreedySnake/components/food.js: -------------------------------------------------------------------------------- 1 | import getColor from '../../../../utils/color' 2 | let foods = [] 3 | class Food { 4 | constructor({x,y,width,height}) { 5 | this.x = x || 0 6 | this.y = y || 0 7 | this.width = width || 20 8 | this.height = height || 20 9 | } 10 | //初始化 11 | init(el) { 12 | this.bgc = getColor() 13 | this.delNode() 14 | this.foodNode(el) 15 | } 16 | // 创建食物节点 17 | foodNode(el) { 18 | // 创建食物div 19 | let Div = document.createElement('div') 20 | // 设置div样式 21 | Div.style.position = 'absolute' 22 | Div.style.width = this.width + 'px' 23 | Div.style.height = this.height + 'px' 24 | Div.style.backgroundColor = this.bgc 25 | // 计算 x, y轴坐标 26 | this.x = parseInt(Math.random()*el.offsetWidth/this.width)*this.width 27 | this.y = parseInt(Math.random()*el.offsetHeight/this.height)*this.height 28 | 29 | Div.style.left = this.x + 'px' 30 | Div.style.top = this.y + 'px' 31 | 32 | //将div 添加到地图中 33 | el.appendChild(Div) 34 | //将div添加到数组中 35 | foods.push(Div) 36 | } 37 | //删除食物🍜 和数组中的节点 38 | delNode() { 39 | foods.forEach((ele,i)=>{ 40 | //找到父节点,通过父节点删除当前节点 41 | ele.parentNode.removeChild(ele) 42 | // 删除数组中的数据, 防止多次实力化时创建多个div(食物🍜) 43 | foods.splice(i,1) 44 | }) 45 | } 46 | } 47 | 48 | export default Food -------------------------------------------------------------------------------- /src/views/Game/GreedySnake/components/index.js: -------------------------------------------------------------------------------- 1 | 2 | //食物 3 | import Food from './food' 4 | import Snake from './snake' 5 | 6 | let flag = true 7 | // 贪吃🐍 入口 8 | class Game { 9 | constructor(el) { 10 | this.el = el 11 | this.food = new Food({}) 12 | this.snake = new Snake({}) 13 | this.timer = null 14 | } 15 | init() { 16 | if(!flag) return 17 | //食物 18 | this.food.init(this.el) 19 | // 🐍 20 | this.snake.init(this.el) 21 | //🐍移动 22 | this.runSnake(this.food, this.el) 23 | //键盘按下 24 | this.bindKey() 25 | } 26 | //移动 27 | runSnake(food, el) { 28 | this.timer = setInterval(() => { 29 | flag = false 30 | this.snake.move(food, el) 31 | this.snake.init(el) 32 | this.boundary() 33 | }, 130) 34 | 35 | } 36 | //移动边界 37 | boundary() { 38 | const { el, snake, timer } = this 39 | //地图 ,max X,Y 40 | const maxX = el.offsetWidth / snake.width 41 | const maxY = el.offsetHeight / snake.height 42 | 43 | //🐍头 X,Y 44 | const headX = snake.body[0].x 45 | const headY = snake.body[0].y 46 | // 移动x轴边界 47 | if (headX < 0 || headX >= maxX) { 48 | clearInterval(timer) 49 | flag = true 50 | alert('X轴撞墙了,最终得分:'+snake.fraction) 51 | } 52 | if (headY < 0 || headY >= maxY) { 53 | clearInterval(timer) 54 | flag = true 55 | alert('Y轴撞墙了,最终得分:'+snake.fraction) 56 | } 57 | } 58 | //按键,改变蛇头方向 59 | bindKey() { 60 | document.addEventListener('keydown', (e) => { 61 | switch (e.keyCode) { 62 | case 37: 63 | this.snake.direction = 'left' 64 | break; 65 | case 38: 66 | this.snake.direction = 'top' 67 | break; 68 | case 39: 69 | this.snake.direction = 'right' 70 | break; 71 | case 40: 72 | this.snake.direction = 'bottom' 73 | break; 74 | } 75 | }) 76 | } 77 | //移除键盘事件 78 | removeKeyCode() { 79 | document.removeEventListener('keydown') 80 | } 81 | } 82 | 83 | export default Game -------------------------------------------------------------------------------- /src/views/Game/GreedySnake/components/snake.js: -------------------------------------------------------------------------------- 1 | import getColor from '../../../../utils/color' 2 | let snakes = [] 3 | 4 | class Snake { 5 | 6 | constructor({ width, height, direction }) { 7 | this.width = width || 20 8 | this.height = height || 20 9 | this.direction = direction || 'right' 10 | this.fraction = 0 // 得分 11 | this.body = [ 12 | { 13 | x: 2, y: 2, bgc: 'red' 14 | }, { 15 | x: 1, y: 2, bgc: getColor() 16 | }, { 17 | x: 0, y: 2, bgc: getColor() 18 | } 19 | ] 20 | } 21 | //初始化 22 | init(el) { 23 | this.delSnake() 24 | this.snakeNodes(el) 25 | } 26 | // 创建🐍节点 27 | snakeNodes(el) { 28 | for (let i = 0; i < this.body.length; i++) { 29 | const ele = this.body[i] 30 | // 创建div, 设置属性 31 | let Div = document.createElement('div') 32 | Div.style.position = 'absolute' 33 | Div.style.width = this.width + 'px' 34 | Div.style.height = this.height + 'px' 35 | Div.style.left = ele.x * this.width + 'px' 36 | Div.style.top = ele.y * this.height + 'px' 37 | Div.style.backgroundColor = ele.bgc 38 | if(i === 0){ 39 | Div.style.textAlign="center" 40 | Div.style.lineHeight = this.height + 'px' 41 | Div.style.zIndex = '1' 42 | Div.innerText = '🐍' 43 | } 44 | // 添加到数组中 45 | snakes.push(Div) 46 | //添加到地图节点中 47 | el.appendChild(Div) 48 | } 49 | } 50 | //删除节点,防止被多次实例化调用 51 | delSnake() { 52 | 53 | for (let i = snakes.length - 1; i >= 0; i--) { 54 | const ele = snakes[i] 55 | //删除当前元素 56 | ele.parentNode.removeChild(ele) 57 | //删除数组中当前元素 58 | snakes.splice(i, 1) 59 | } 60 | } 61 | //移动🐍 62 | move(food, el) { 63 | const { x, y } = food 64 | 65 | //移动身体 66 | for (let i = this.body.length - 1; i > 0; i--) { 67 | this.body[i].x = this.body[i - 1].x 68 | this.body[i].y = this.body[i - 1].y 69 | } 70 | //判断蛇头方向 71 | switch (this.direction) { 72 | case 'right': 73 | this.body[0].x += 1 74 | break; 75 | case 'left': 76 | this.body[0].x -= 1 77 | break; 78 | case 'top': 79 | this.body[0].y -= 1 80 | break; 81 | case 'bottom': 82 | this.body[0].y += 1 83 | break; 84 | } 85 | // 判断🐍吃食物 86 | //🐍头位置 87 | const headX = this.body[0].x * this.width 88 | const headY = this.body[0].y * this.height 89 | if (x === headX && y === headY) { 90 | // 🐍尾巴添加 div 91 | let { x, y } = this.body[this.body.length - 1] 92 | this.body.push({ 93 | x, y, bgc:food.bgc 94 | }) 95 | //得分 96 | // this.fraction++ 97 | this.fraction += 2 98 | //初始化食物 99 | food.init(el) 100 | } 101 | } 102 | } 103 | 104 | export default Snake -------------------------------------------------------------------------------- /src/views/Game/GreedySnake/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 38 | -------------------------------------------------------------------------------- /src/views/Home/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 38 | -------------------------------------------------------------------------------- /src/views/Icon/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 25 | -------------------------------------------------------------------------------- /src/views/Layout/index.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/views/Login/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 59 | -------------------------------------------------------------------------------- /src/views/Personal/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 44 | --------------------------------------------------------------------------------