├── .env ├── .env.prod ├── .env.test ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── api │ ├── api.js │ └── request.js ├── assets │ ├── css │ │ ├── normalize.css │ │ └── public.css │ ├── icon │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── img │ │ └── tou.jpg │ ├── js │ │ └── common.js │ ├── logo.png │ └── scss │ │ └── global.scss ├── components │ ├── TreeTable │ │ ├── eval.js │ │ ├── index.vue │ │ └── readme.md │ └── showAside.vue ├── element-variables.scss ├── main.js ├── plugins │ └── element.js ├── router │ └── router.js ├── store │ ├── actions.js │ ├── getters.js │ ├── mutations.js │ ├── state.js │ └── store.js └── views │ ├── charts │ └── cricle.vue │ ├── component │ ├── countTo.vue │ ├── customEval.js │ ├── editor.vue │ ├── tree.vue │ ├── treeSelect.vue │ └── treeTable.vue │ ├── draggable │ ├── draglist.vue │ └── dragtable.vue │ ├── home │ └── index.vue │ ├── icons │ └── index.vue │ ├── layout │ ├── Layout.vue │ └── components │ │ ├── Aside.vue │ │ ├── Header.vue │ │ ├── Main.vue │ │ └── Tags.vue │ └── login │ └── index.vue └── vue.config.js /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV="development" 2 | BASE_URL="http://localhost:3000/" -------------------------------------------------------------------------------- /.env.prod: -------------------------------------------------------------------------------- 1 | NODE_ENV="production" 2 | BASE_URL="https://www.jubao56.com/" -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | NODE_ENV="test" 2 | BASE_URL="http://localhost:3000/" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | **由于最近公司要开发一个后台管理系统,查阅了很多vue框架,本人觉得element简洁,方便,于是选择它作为我们的首选框架,并分享给大家,如果您觉得有需要改进的地方可以提出来一起探讨,[掘金](https://juejin.im/post/5caab6f0f265da24c1118d90)。本文篇幅比较长,希望同学们可以耐心的读下去,如有不懂可以下方留言** 3 | 4 | # 一、目录 5 | | 目录 | 6 | | -------------------- | 7 | | 1.目录 | 8 | | 2.初始化项目 | 9 | | 3.文件目录介绍与整理 | 10 | | 4.开发环境与线上环境配置 | 11 | | 5.vue.config.js配置 | 12 | | 6.ElementUI引入 | 13 | | 7.vue-router路由介绍 | 14 | | 8.axios引入并封装 | 15 | | 9.vuex引入 | 16 | | 10.首页布局介绍 | 17 | | 11.结语 | 18 | 19 | # 二、初始化项目 20 | 首先全局安装vue脚手架,当前是第三版本vue-cli3.x,这里是用的npm包管理工具来安装的,如果你的网不是很好的话可以先安装淘宝镜像 `npm install -g cnpm -registry=https://registry.npm.taobao.org`,然后通过cnpm来安装 21 | ```vue 22 | cnpm install -g @vue/cli or npm install -g @vue/cli 23 | ``` 24 | 安装完成后,你还可以用这个命令来检查其版本是否正确 (3.x): 25 | ```vue 26 | vue --version 27 | ``` 28 | 安装脚手架后开始创建我们的项目 29 | ```vue 30 | vue create vue-admin-project 31 | ``` 32 | 随后会出现两个选项 33 | 34 | ![alt](https://github.com/zhuyihe/vue-admin-project/blob/uploadFile/src/assets/img/1.png) 35 | 36 | 37 | 选择第二项并继续,并选择自己需要配置的功能,完成后并继续,然后开始生成项目 38 | 39 | ![alt](https://github.com/zhuyihe/vue-admin-project/blob/uploadFile/src/assets/img/2.png) 40 | 41 | 项目初始化成功 42 | 43 | ![alt](https://github.com/zhuyihe/vue-admin-project/blob/uploadFile/src/assets/img/3.png) 44 | 接下来按照上面的提示运行`cd app`以及启动本地服务器`npm run serve`,当运行完成之后会提示你打来本地端口`http://localhost:8080`,会出现欢迎页面,此时代表你的vue项目初始化完成。 45 | 46 | ![alt](https://github.com/zhuyihe/vue-admin-project/blob/uploadFile/src/assets/img/5.png) 47 | 48 | # 三、文件目录介绍与整理 49 | 整理前的初始目录 50 | 51 | |-- vue-admin-project 52 | |-- .gitignore //git项目忽视文件 53 | |-- babel.config.js //babel 配置文件 54 | |-- package-lock.json //记录安装包的具体版本号 55 | |-- package.json //包的类型 56 | |-- README.md 57 | |-- public //项目打包后的目录 58 | | |-- favicon.ico 59 | | |-- index.html 60 | |-- src //项目开发目录 61 | |-- App.vue //主入口文件 62 | |-- main.js //主入口文件 63 | |-- router.js //vue-router文件 64 | |-- store.js //vuex 65 | |-- assets //静态文件 66 | |-- logo.png 67 | |-- components //组件存放目录 68 | |-- HelloWorld.vue 69 | |-- views //视图目录 70 | |-- About.vue 71 | |-- Home.vue 72 | 整理后的目录,主要更改``/src``文件夹下的目录 73 | 74 | |-- vue-admin-project 75 | |-- .gitignore 76 | |-- babel.config.js 77 | |-- package-lock.json 78 | |-- package.json 79 | |-- README.md 80 | |-- public 81 | |-- favicon.ico 82 | |-- index.html 83 | |-- src 84 | |-- App.vue 85 | |-- main.js 86 | |-- assets 87 | |-- logo.png 88 | |-- components 89 | |-- HelloWorld.vue 90 | |-- router //路由配置文件夹 91 | |-- router.js 92 | |-- store //状态管理文件夹 93 | |-- store.js 94 | |-- views 95 | |-- About.vue 96 | |-- Home.vue 97 | # 四、开发环境与线上环境配置 98 | vue-cli 3.0x与vue-cli 2.0x最主要的区别是项目结构目录精简化,这也带来了许多问题,很多配置需要自己配置,由于2.0x版本中直接在`cofig/`文件夹下面配置开发环境与线上环境,3.0x则需要自己配置。
99 | 首先配置开发环境,在项目根目录下新建一个文件`.env`文件。 100 | ```javascript 101 | NODE_ENV="development" //开发环境 102 | BASE_URL="http://localhost:3000/" //开发环境接口地址 103 | ``` 104 | 接下来我们配置线上环境,同样在项目根目录新建一个文件`.env.prod`这就表明是生产环境。 105 | ```javascript 106 | NODE_ENV="production" //生产环境 107 | BASE_URL="url" //生产环境的地址 108 | ``` 109 | 现在我们如何在项目中判断当前环境呢?
110 | 我们可以根据`process.env.BASE_URL`来获取它是线上环境还是开发环境,后面会有运用 111 | ```javascript 112 | if(process.env.NODE_ENV='development'){ 113 | console.log( process.env.BASE_URL) //http://localhost:3000/ 114 | }else{ 115 | console.log( process.env.BASE_URL) //url 116 | } 117 | ``` 118 | 至此,我们成功的配置好了开发环境与线上环境。 119 | 120 | # 五、vue.config.js配置 121 | 讲到`vue.config.js`项目配置文件,又不得不说下3.x和2.x的区别,2.x里面webpack相关的配置项直接在项目的`build/webpack.base.conf.js`里面配置,而3.x完全在`vue.config.js`中配置,这使得整个项目看起来更加简洁明了,项目运行速度更快。
122 | 由于项目初始化的时候没有`vue.config.js`配置文件,因此我们需要在项目根目录下新建一个`vue.config.js`配置项。
123 | 在这个配置项里面,本项目主要是配置三个东西,第一个就是目录别名`alias`,另一个是项目启动时自动打开浏览器,最后一个就是处理引入的全局scss文件。当然有`vue.config.js`的配置远远不止这几项,有兴趣的同学可以去看看[vue.config.js](https://cli.vuejs.org/zh/guide/webpack.html)具体配置,具体代码如下。 124 | ```javascript 125 | let path=require('path'); 126 | function resolve(dir){ 127 | return path.join(__dirname,dir) 128 | } 129 | module.exports = { 130 | chainWebpack: config => { 131 | //设置别名 132 | config.resolve.alias 133 | .set('@',resolve('src')) 134 | }, 135 | devServer: { 136 | open:true //打开浏览器窗口 137 | }, 138 | //定义scss全局变量 139 | css: { 140 | loaderOptions: { 141 | sass: { 142 | data: `@import "@/assets/scss/global.scss";` 143 | } 144 | } 145 | } 146 | } 147 | ``` 148 | # 六、ElementUI引入 149 | 开始安装ElementUI 150 | ```javascript 151 | vue add element 152 | ``` 153 | 接下来两个选项,第一个是全部引入,第二个是按需引入,我选择第一个`Fully import`,大家可以按照自己的项目而定。接下来会询问是否引入scss,这里选择是,语言选择zh-cn。
154 | 接下来会提示安装成功,并在项目首页有一个element样式的按钮。 155 | # 七、vue-router路由介绍入 156 | 路由管理也是本项目核心部分。
157 | 1.引入文件 158 | ```javascript 159 | import Vue from 'vue' 160 | import Router from 'vue-router' 161 | import store from '../store/store' //引入状态管理 162 | import NProgress from 'nprogress' //引入进度条组件 cnpm install nprogress --save 163 | import 'nprogress/nprogress.css' 164 | Vue.use(Router) 165 | ``` 166 | 2.路由懒加载 167 | ```javascript 168 | /** 169 | *@parma {String} name 文件夹名称 170 | *@parma {String} component 视图组件名称 171 | */ 172 | const getComponent = (name,component) => () => import(`@/views/${name}/${component}.vue`); 173 | ``` 174 | 3.路由配置 175 | ```javascript 176 | const myRouter=new Router({ 177 | routes: [ 178 | { 179 | path: '/', 180 | redirect: '/home', 181 | component: getComponent('login','index') 182 | }, 183 | { 184 | path: '/login', 185 | name: 'login', 186 | component: getComponent('login','index') 187 | }, 188 | { 189 | path: '/', 190 | component:getComponent('layout','Layout'), 191 | children:[{ 192 | path:'/home', 193 | name:'home', 194 | component: getComponent('home','index'), 195 | meta:{title:'首页'} 196 | }, 197 | { 198 | path:'/icon', 199 | component: getComponent('icons','index'), 200 | name:'icon', 201 | meta:{title:'自定义图标'} 202 | }, 203 | { 204 | path:'/editor', 205 | component: getComponent('component','editor'), 206 | name:'editor', 207 | meta:{title:'富文本编译器'} 208 | }, 209 | { 210 | path:'/countTo', 211 | component: getComponent('component','countTo'), 212 | name:'countTo', 213 | meta:{title:'数字滚动'} 214 | }, 215 | { 216 | path:'/tree', 217 | component: getComponent('component','tree'), 218 | name:'tree', 219 | meta:{title:'自定义树'} 220 | }, 221 | { 222 | path:'/treeTable', 223 | component: getComponent('component','treeTable'), 224 | name:'treeTable', 225 | meta:{title:'表格树'} 226 | }, 227 | { 228 | path:'/treeSelect', 229 | component: getComponent('component','treeSelect'), 230 | name:'treeSelect', 231 | meta:{title:'下拉树'} 232 | }, 233 | { 234 | path:'/draglist', 235 | component: getComponent('draggable','draglist'), 236 | name:'draglist', 237 | meta:{title:'拖拽列表'} 238 | }, 239 | { 240 | path:'/dragtable', 241 | component: getComponent('draggable','dragtable'), 242 | name:'dragtable', 243 | meta:{title:'拖拽表格'} 244 | }, 245 | { 246 | path:'/cricle', 247 | component: getComponent('charts','cricle'), 248 | name:'cricle', 249 | meta:{title:'饼图'} 250 | }, 251 | ] 252 | } 253 | ] 254 | }) 255 | ``` 256 | 4.本项目存在一个token,来验证权限问题,因此进入页面的时候需要判断是否存在token,如果不存在则跳转到登陆页面 257 | ```javascript 258 | //判断是否存在token 259 | myRouter.beforeEach((to,from,next)=>{ 260 | NProgress.start() 261 | if (to.path !== '/login' && !store.state.token) { 262 | next('/login') //跳转登录 263 | NProgress.done() // 结束Progress 264 | } 265 | next() 266 | }) 267 | myRouter.afterEach(() => { 268 | NProgress.done() // 结束Progress 269 | }) 270 | ``` 271 | 5.导出路由 272 | ```javascript 273 | export default myRouter 274 | ``` 275 | # 八、axios引入并封装 276 | 1.接口处理我选择的是axios,由于它遵循promise规范,能很好的避免回调地狱。现在我们开始安装 277 | ```javascript 278 | cnpm install axios -S 279 | ``` 280 | 2.在`src`目录下新建文件夹命名为`api`,里面新建两个文件,一个是`api.js`,用于接口的整合,另一个是`request.js`,根据相关业务封装axios请求。 281 | + request.js
282 | 1.引入依赖 283 | ```javascript 284 | import axios from "axios"; 285 | import router from "../router/router"; 286 | import { 287 | Loading 288 | } from "element-ui"; 289 | import {messages} from '../assets/js/common.js' //封装的提示文件 290 | import store from '../store/store' //引入vuex 291 | ``` 292 | 2.编写axios基本设置 293 | ```javascript 294 | axios.defaults.timeout = 60000; //设置接口超时时间 295 | axios.defaults.baseURL = process.env.BASE_URL; //根据环境设置基础路径 296 | axios.defaults.headers.post["Content-Type"] = 297 | "application/x-www-form-urlencoded;charset=UTF-8"; //设置编码 298 | let loading = null; //初始化loading 299 | ``` 300 | 3.编写请求拦截,也就是说在请求接口前要做的事情 301 | ```javascript 302 | /* 303 | *请求前拦截 304 | *用于处理需要请求前的操作 305 | */ 306 | axios.interceptors.request.use( 307 | config => { 308 | loading = Loading.service({ 309 | text: "正在加载中......", 310 | fullscreen: true 311 | }); 312 | if (store.state.token) { 313 | config.headers["Authorization"] = "Bearer " + store.state.token; 314 | } 315 | return config; 316 | }, 317 | error => { 318 | return Promise.reject(error); 319 | } 320 | ); 321 | ``` 322 | 4.编写请求响应拦截,用于处理数据返回操作 323 | ```javascript 324 | /* 325 | *请求响应拦截 326 | *用于处理数据返回后的操作 327 | */ 328 | axios.interceptors.response.use( 329 | response => { 330 | return new Promise((resolve, reject) => { 331 | //请求成功后关闭加载框 332 | if (loading) { 333 | loading.close(); 334 | } 335 | const res = response.data; 336 | if (res.err_code === 0) { 337 | resolve(res) 338 | } else{ 339 | reject(res) 340 | } 341 | }) 342 | }, 343 | error => { 344 | console.log(error) 345 | //请求成功后关闭加载框 346 | if (loading) { 347 | loading.close(); 348 | } 349 | //断网处理或者请求超时 350 | if (!error.response) { 351 | //请求超时 352 | if (error.message.includes("timeout")) { 353 | console.log("超时了"); 354 | messages("error", "请求超时,请检查互联网连接"); 355 | } else { 356 | //断网,可以展示断网组件 357 | console.log("断网了"); 358 | messages("error", "请检查网络是否已连接"); 359 | } 360 | return; 361 | } 362 | const status = error.response.status; 363 | switch (status) { 364 | case 500: 365 | messages("error", "服务器内部错误"); 366 | break; 367 | case 404: 368 | messages( 369 | "error", 370 | "未找到远程服务器" 371 | ); 372 | break; 373 | case 401: 374 | messages("warning", "用户登陆过期,请重新登陆"); 375 | localStorage.removeItem("token"); 376 | setTimeout(() => { 377 | router.replace({ 378 | path: "/login", 379 | query: { 380 | redirect: router.currentRoute.fullPath 381 | } 382 | }); 383 | }, 1000); 384 | break; 385 | case 400: 386 | messages("error", "数据异常,详情请咨询聚保服务热线"); 387 | break; 388 | default: 389 | messages("error", error.response.data.message); 390 | } 391 | return Promise.reject(error); 392 | } 393 | ); 394 | ``` 395 | 5.请求相关的事情已经完成,现在开始封装get,post请求 396 | ```javascript 397 | /* 398 | *get方法,对应get请求 399 | *@param {String} url [请求的url地址] 400 | *@param {Object} params [请求时候携带的参数] 401 | */ 402 | export function get(url, params) { 403 | return new Promise((resolve, reject) => { 404 | axios 405 | .get(url, { 406 | params 407 | }) 408 | .then(res => { 409 | resolve(res); 410 | }) 411 | .catch(err => { 412 | reject(err); 413 | }); 414 | }); 415 | } 416 | /* 417 | *post方法,对应post请求 418 | *@param {String} url [请求的url地址] 419 | *@param {Object} params [请求时候携带的参数] 420 | */ 421 | export function post(url, params) { 422 | return new Promise((resolve, reject) => { 423 | axios 424 | .post(url, params) 425 | .then(res => { 426 | resolve(res); 427 | }) 428 | .catch(err => { 429 | reject(err); 430 | }); 431 | }); 432 | } 433 | ``` 434 | + api.js
435 | 封装好axios的业务逻辑之后自然要开始,运用,首先引入`get`以及`post`方法 436 | ```javascript 437 | import {get,post} from './request'; 438 | ``` 439 | 接下来开始封装接口,并导出 440 | ```javascript 441 | //登陆 442 | export const login=(login)=>post('/api/post/user/login',login) 443 | //上传 444 | export const upload=(upload)=>get('/api/get/upload',upload) 445 | ``` 446 | 那我们如何调用接口呢?以登陆页面为例。 447 | ```javascript 448 | import { login } from "@/api/api.js"; //引入login 449 | ``` 450 | ```javascript 451 | /** 452 | * @oarma {Object} login 接口传递的参数 453 | */ 454 | login(login) 455 | .then(res => { 456 | //成功之后要做的事情 457 | }) 458 | .catch(err => { 459 | //出错时要做的事情 460 | }); 461 | ``` 462 | 接口相关的逻辑已经处理完。 463 | # 九、vuex引入 464 | 由于vue项目中组件之间传递数据比较复杂,因此官方引入了一个全局状态管理的东东,也就是现在要说的vuex,vuex能更好的管理数据,方便组件之间的通信。
465 | 现在在store文件夹下面新建四个文件`state.js`,`mutations.js`,`getter.js`,`action.js`。 466 | + state.js
467 | state就是Vuex中的公共的状态, 我是将state看作是所有组件的data, 用于保存所有组件的公共数据. 468 | ```javascript 469 | const state = { 470 | token: '',//权限验证 471 | tagsList: [], //打开的标签页个数, 472 | isCollapse: false, //侧边导航是否折叠 473 | } 474 | export default state //导出 475 | ``` 476 | + mutations.js
477 | 我将mutaions理解为store中的methods, mutations对象中保存着更改数据的回调函数,该函数名官方规定叫type, 第一个参数是state, 第二参数是payload, 也就是自定义的参数.改变state的值必须经过mutations 478 | ```javascript 479 | const mutations = { 480 | //保存token 481 | COMMIT_TOKEN(state, object) { 482 | state.token = object.token; 483 | }, 484 | //保存标签 485 | TAGES_LIST(state, arr) { 486 | state.tagsList = arr; 487 | }, 488 | IS_COLLAPSE(state, bool) { 489 | state.isCollapse = bool; 490 | } 491 | } 492 | export default mutations 493 | ``` 494 | + getter.js
495 | 我将getters属性理解为所有组件的computed属性,也就是计算属性。vuex的官方文档也是说到可以将getter理解为store的计算属性, getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 496 | ```javascript 497 | const getters={ 498 | //你要计算的属性 499 | } 500 | export default getters 501 | ``` 502 | + action.js
503 | actions 类似于 mutations,不同在于:
504 |     1.actions提交的是mutations而不是直接变更状态
505 |     2.actions中可以包含异步操作, mutations中绝对不允许出现异步
506 |     3.actions中的回调函数的第一个参数是context, 是一个与store实例具有相同属性和方法的对象
507 | ```javascript 508 | const actions={ 509 | 510 | } 511 | export default actions 512 | ``` 513 | + store.js
514 | store.js是vuex模块整合文件,由于刷新页面会造成vuex数据丢失,所以这里引入了一个vuex数据持久话插件,将state里面的数据保存到localstorage。
515 | 安装`vuex-persistedstate ` 516 | ```javascript 517 | npm install vuex-persistedstate --save 518 | ``` 519 | ```javascript 520 | import Vue from 'vue' 521 | import Vuex from 'vuex' 522 | import state from "./state"; 523 | import mutations from "./mutations"; 524 | import actions from "./actions"; 525 | import getters from "./getters"; 526 | //引入vuex 数据持久化插件 527 | import createPersistedState from "vuex-persistedstate" 528 | Vue.use(Vuex) 529 | 530 | export default new Vuex.Store({ 531 | state, 532 | mutations, 533 | actions, 534 | getters, 535 | plugins: [createPersistedState()] 536 | }) 537 | ``` 538 | 至此vuex引入完毕,如同学们还有不明白的可以去翻阅[vuex](https://vuex.vuejs.org/zh/)文档。 539 | # 十、首页布局介绍 540 | 现在我们开始进行页面的布局。首先我们来分析下首页的情况 541 | ![首页](https://github.com/zhuyihe/vue-admin-project/blob/uploadFile/src/assets/img/4.png) 542 | 1. 侧边栏 543 | 2. 顶部栏 544 | 3. 内容部分
545 | 首先我们在`view`文件夹下面新建一个`layout`文件夹,里面再添加一个`layout.vue`,以及`compentents`文件夹。 546 | + 侧边栏
547 | 在compentents文件夹下面新建一个`Aside.vue`文件,实现路由跳转相关的逻辑,运用了element导航菜单的路由模式,如有不明白的可以去[ElementUI导航菜单](http://element-cn.eleme.io/2.0/#/zh-CN/component/menu)去看看。 548 | ```html 549 | 590 | ``` 591 | ```javascript 592 | import { mapState } from "vuex"; 593 | export default { 594 | data() { 595 | return { 596 | //配置目录 597 | items: [ 598 | { 599 | icon: "el-icon-edit-outline", 600 | index: "home", 601 | title: "系统首页" 602 | }, 603 | { 604 | icon: "el-icon-edit-outline", 605 | index: "icon", 606 | title: "自定义图标" 607 | }, 608 | { 609 | icon: "el-icon-edit-outline", 610 | index: "component", 611 | title: "组件", 612 | subs: [ 613 | { 614 | index: "editor", 615 | title: "富文本编译器" 616 | }, 617 | { 618 | index: "countTo", 619 | title: "数字滚动" 620 | }, 621 | { 622 | index: "trees", 623 | title: "树形控件", 624 | subs: [ 625 | { 626 | index: "tree", 627 | title: "自定义树" 628 | }, 629 | { 630 | index: "treeSelect", 631 | title: "下拉树" 632 | } 633 | // ,{ 634 | // index:'treeTable', 635 | // title:'表格树', 636 | // } 637 | ] 638 | }, 639 | ] 640 | }, 641 | { 642 | icon: "el-icon-edit-outline", 643 | index: "draggable", 644 | title: "拖拽", 645 | subs: [ 646 | { 647 | index: "draglist", 648 | title: "拖拽列表" 649 | }, 650 | { 651 | index: "dragtable", 652 | title: "拖拽表格" 653 | } 654 | ] 655 | }, 656 | { 657 | icon: "el-icon-edit-outline", 658 | index: "charts", 659 | title: "图表", 660 | subs: [ 661 | { 662 | index: "cricle", 663 | title: "饼图" 664 | }, 665 | ] 666 | }, 667 | { 668 | icon: "el-icon-edit-outline", 669 | index: "7", 670 | title: "错误处理", 671 | subs: [ 672 | { 673 | index: "permission", 674 | title: "权限测试" 675 | }, 676 | { 677 | index: "404", 678 | title: "404页面" 679 | } 680 | ] 681 | }, 682 | ] 683 | }; 684 | }, 685 | computed: { 686 | onRoutes() { 687 | return this.$route.path.replace("/", ""); 688 | }, 689 | ...mapState(["isCollapse"]) //从vuex里面获取菜单是否折叠 690 | }, 691 | methods: { 692 | //下拉展开 693 | handleOpen(key, keyPath) { 694 | console.log(key, keyPath); 695 | }, 696 | //下来关闭 697 | handleClose(key, keyPath) { 698 | console.log(key, keyPath); 699 | } 700 | } 701 | }; 702 | ``` 703 | + 顶部栏
704 | 在`view/compentents`文件夹下面新建一个`Header.vue` 705 | ```html 706 | 751 | ``` 752 | ```javascript 753 | import showAside from "@/components/showAside.vue";//引入了一个侧边栏是否折叠的组件 754 | export default { 755 | // name:'header', 756 | components: { 757 | showAside 758 | }, 759 | data() { 760 | return { 761 | fullscreen: false, 762 | name: "linxin", 763 | message: 2, 764 | username: "zyh" 765 | }; 766 | }, 767 | computed: { 768 | isCollapse: { 769 | get: function() { 770 | return this.$store.state.isCollapse; 771 | }, 772 | set: function(newValue) { 773 | console.log(newValue); 774 | this.$store.commit("IS_COLLAPSE", newValue);//提交到vuex 775 | } 776 | } 777 | }, 778 | methods: { 779 | toggleClick() { 780 | this.isCollapse = !this.isCollapse; 781 | }, 782 | // 用户名下拉菜单选择事件 783 | logout(command) { 784 | this.$router.push("/login"); 785 | }, 786 | // 全屏事件 787 | handleFullScreen() { 788 | let element = document.documentElement; 789 | if (this.fullscreen) { 790 | if (document.exitFullscreen) { 791 | document.exitFullscreen(); 792 | } else if (document.webkitCancelFullScreen) { 793 | document.webkitCancelFullScreen(); 794 | } else if (document.mozCancelFullScreen) { 795 | document.mozCancelFullScreen(); 796 | } else if (document.msExitFullscreen) { 797 | document.msExitFullscreen(); 798 | } 799 | } else { 800 | if (element.requestFullscreen) { 801 | element.requestFullscreen(); 802 | } else if (element.webkitRequestFullScreen) { 803 | element.webkitRequestFullScreen(); 804 | } else if (element.mozRequestFullScreen) { 805 | element.mozRequestFullScreen(); 806 | } else if (element.msRequestFullscreen) { 807 | // IE11 808 | element.msRequestFullscreen(); 809 | } 810 | } 811 | this.fullscreen = !this.fullscreen; 812 | } 813 | } 814 | }; 815 | ``` 816 | 现在在`src/components`文件夹下面新建一个`showAside.vue`组件 817 | ```html 818 | 825 | ``` 826 | ```javascript 827 | export default { 828 | name: "showAside", 829 | props: { 830 | toggleClick: { 831 | type: Function, 832 | default: null 833 | } 834 | } 835 | }; 836 | ``` 837 | + 顶部导航栏标签组件
838 | 在`view/compentents`文件夹下面新建一个`Tags.vue` 839 | ```html 840 | 870 | ``` 871 | ```javascript 872 | import { messages } from "@/assets/js/common.js"; 873 | export default { 874 | created() { 875 | //判断标签里面是否有值 有的话直接加载 876 | if (this.tagsList.length == 0) { 877 | this.setTags(this.$route); 878 | } 879 | }, 880 | computed: { 881 | //computed 方法里面没有set方法因此不能使用mapState,需要重新定义set方法 882 | tagsList: { 883 | get: function() { 884 | return this.$store.state.tagsList; 885 | }, 886 | set: function(newValue) { 887 | this.$store.commit("TAGES_LIST", newValue); 888 | // this.$store.state.tagsList = newValue; 889 | } 890 | } 891 | }, 892 | watch: { 893 | //监听路由变化 894 | $route(newValue, oldValue) { 895 | this.setTags(newValue); 896 | } 897 | }, 898 | methods: { 899 | //选中的高亮 900 | isActive(path) { 901 | return path === this.$route.fullPath; 902 | }, 903 | handleCommand(command) { 904 | if (command == "closeOther") { 905 | // 关闭其他标签 906 | const curItem = this.tagsList.filter(item => { 907 | return item.path === this.$route.fullPath; 908 | }); 909 | this.tagsList = curItem; 910 | } 911 | }, 912 | //添加标签 913 | setTags(route) { 914 | let isIn = this.tagsList.some(item => { 915 | //判断标签是否存在 916 | return item.path === route.fullPath; 917 | }); 918 | //不存在 919 | if (!isIn) { 920 | // 判断当前的标签个数 921 | if (this.tagsList.length >= 10) { 922 | messages("warning", "当标签大于10个,请关闭后再打开"); 923 | } else { 924 | this.tagsList.push({ 925 | title: route.meta.title, 926 | path: route.fullPath, 927 | name: route.name 928 | }); 929 | //存到vuex 930 | this.$store.commit("TAGES_LIST", this.tagsList); 931 | } 932 | } 933 | }, 934 | closeTags(index) { 935 | console.log(this.tagsList.length); 936 | if (this.tagsList.length == 1) { 937 | messages("warning", "不可全都关闭"); 938 | } else { 939 | //删除当前 940 | let tags = this.tagsList.splice(index, 1); 941 | this.$store.commit("TAGES_LIST", this.tagsList); 942 | } 943 | } 944 | } 945 | }; 946 | ``` 947 | 接下来在`view/compentents`文件夹下面新建一个`Main.vue`,主要是将顶部导航标签栏以及内容部分结合起来。 948 | ```html 949 | 959 | ``` 960 | ```javascript 961 | import Tags from './Tags.vue' 962 | export default { 963 | components:{ 964 | Tags 965 | } 966 | } 967 | ``` 968 | 相关组件写好,在layout组件中汇总 969 | ```html 970 | 979 | ``` 980 | ```javascript 981 | import Aside from "./components/Aside.vue"; 982 | import Header from "./components/Header.vue"; 983 | import Main from "./components/Main.vue"; 984 | import { mapState } from "vuex"; 985 | export default { 986 | name: "Layout", 987 | components: { 988 | Aside, 989 | Header, 990 | Main 991 | }, 992 | computed: { 993 | ...mapState(["isCollapse"]) 994 | } 995 | }; 996 | ``` 997 | 至此首页布局已经规划完成。 998 | # 十一、结语 999 | 管理系统是多种多样的,每家公司都有不同的业务逻辑,本篇文章也只是抛砖引玉,还有许多需要修正改进的地方,如果同学们有更好的想法可以提出来希望大家一起完善本项目。 1000 | 1001 | |-- vue-admin-project 1002 | |-- .env 1003 | |-- .env.prod 1004 | |-- .env.test 1005 | |-- .gitignore 1006 | |-- babel.config.js 1007 | |-- package-lock.json 1008 | |-- package.json 1009 | |-- README.md 1010 | |-- vue.config.js 1011 | |-- public 1012 | | |-- favicon.ico 1013 | | |-- index.html 1014 | |-- src 1015 | |-- App.vue 1016 | |-- element-variables.scss 1017 | |-- main.js 1018 | |-- api 1019 | | |-- api.js 1020 | | |-- request.js 1021 | |-- assets 1022 | | |-- logo.png 1023 | | |-- css 1024 | | | |-- normalize.css 1025 | | | |-- public.css 1026 | | |-- icon 1027 | | | |-- demo.css 1028 | | | |-- demo_index.html 1029 | | | |-- iconfont.css 1030 | | | |-- iconfont.eot 1031 | | | |-- iconfont.js 1032 | | | |-- iconfont.svg 1033 | | | |-- iconfont.ttf 1034 | | | |-- iconfont.woff 1035 | | | |-- iconfont.woff2 1036 | | |-- img 1037 | | | |-- tou.jpg 1038 | | |-- js 1039 | | | |-- common.js 1040 | | |-- scss 1041 | | |-- global.scss 1042 | |-- components 1043 | | |-- showAside.vue 1044 | |-- plugins 1045 | | |-- element.js 1046 | |-- router 1047 | | |-- router.js 1048 | |-- store 1049 | | |-- actions.js 1050 | | |-- getters.js 1051 | | |-- mutations.js 1052 | | |-- state.js 1053 | | |-- store.js 1054 | |-- views 1055 | |-- layout 1056 | | |-- Layout.vue 1057 | | |-- components 1058 | | |-- Aside.vue 1059 | | |-- Header.vue 1060 | | |-- Main.vue 1061 | | |-- Tags.vue 1062 | 最后项目目录文件结构 -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my_project", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "@riophae/vue-treeselect": "^0.0.38", 11 | "axios": "^0.18.0", 12 | "echarts": "^4.2.1", 13 | "element-ui": "^2.4.5", 14 | "elementui_treegrid": "^0.0.3", 15 | "nprogress": "^0.2.0", 16 | "vue": "^2.6.6", 17 | "vue-count-to": "^1.0.13", 18 | "vue-json-viewer": "^2.1.4", 19 | "vue-router": "^3.0.1", 20 | "vue2-editor": "^2.6.6", 21 | "vuedraggable": "^2.20.0", 22 | "vuex": "^3.0.1", 23 | "vuex-persistedstate": "^2.5.4" 24 | }, 25 | "devDependencies": { 26 | "@vue/cli-plugin-babel": "^3.4.0", 27 | "@vue/cli-service": "^3.4.0", 28 | "compression-webpack-plugin": "^2.0.0", 29 | "node-sass": "^4.9.2", 30 | "sass-loader": "^7.1.0", 31 | "vue-cli-plugin-element": "^1.0.1", 32 | "vue-template-compiler": "^2.5.21" 33 | }, 34 | "postcss": { 35 | "plugins": { 36 | "autoprefixer": {} 37 | } 38 | }, 39 | "browserslist": [ 40 | "> 1%", 41 | "last 2 versions", 42 | "not ie <= 8" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | my_project 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /src/api/api.js: -------------------------------------------------------------------------------- 1 | import {get,post} from './request'; 2 | //登陆 3 | export const login=(login)=>post('/api/post/user/login',login) 4 | //上传 5 | export const upload=(upload)=>get('/api/get/upload',upload) -------------------------------------------------------------------------------- /src/api/request.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import router from "../router/router"; 3 | import { 4 | Loading 5 | } from "element-ui"; 6 | import {messages} from '../assets/js/common.js' 7 | import store from '../store/store' 8 | axios.defaults.timeout = 60000; 9 | axios.defaults.baseURL = process.env.BASE_URL; 10 | axios.defaults.headers.post["Content-Type"] = 11 | "application/x-www-form-urlencoded;charset=UTF-8"; 12 | let loading = null; 13 | /* 14 | *请求前拦截 15 | *用于处理需要请求前的操作 16 | */ 17 | axios.interceptors.request.use( 18 | config => { 19 | loading = Loading.service({ 20 | text: "正在加载中......", 21 | fullscreen: true 22 | }); 23 | if (store.state.token) { 24 | config.headers["Authorization"] = "Bearer " + store.state.token; 25 | } 26 | return config; 27 | }, 28 | error => { 29 | return Promise.reject(error); 30 | } 31 | ); 32 | /* 33 | *请求响应拦截 34 | *用于处理数据返回后的操作 35 | */ 36 | axios.interceptors.response.use( 37 | response => { 38 | return new Promise((resolve, reject) => { 39 | //请求成功后关闭加载框 40 | if (loading) { 41 | loading.close(); 42 | } 43 | const res = response.data; 44 | if (res.err_code === 0) { 45 | resolve(res) 46 | } else{ 47 | reject(res) 48 | } 49 | }) 50 | }, 51 | error => { 52 | console.log(error) 53 | //请求成功后关闭加载框 54 | if (loading) { 55 | loading.close(); 56 | } 57 | //断网处理或者请求超时 58 | if (!error.response) { 59 | //请求超时 60 | if (error.message.includes("timeout")) { 61 | console.log("超时了"); 62 | messages("error", "请求超时,请检查互联网连接"); 63 | } else { 64 | //断网,可以展示断网组件 65 | console.log("断网了"); 66 | messages("error", "请检查网络是否已连接"); 67 | } 68 | return; 69 | } 70 | const status = error.response.status; 71 | switch (status) { 72 | case 500: 73 | messages("error", "服务器内部错误"); 74 | break; 75 | case 404: 76 | messages( 77 | "error", 78 | "未找到远程服务器" 79 | ); 80 | break; 81 | case 401: 82 | messages("warning", "用户登陆过期,请重新登陆"); 83 | localStorage.removeItem("token"); 84 | setTimeout(() => { 85 | router.replace({ 86 | path: "/login", 87 | query: { 88 | redirect: router.currentRoute.fullPath 89 | } 90 | }); 91 | }, 1000); 92 | break; 93 | case 400: 94 | messages("error", "数据异常,详情请咨询聚保服务热线"); 95 | break; 96 | default: 97 | messages("error", error.response.data.message); 98 | } 99 | return Promise.reject(error); 100 | } 101 | ); 102 | /* 103 | *get方法,对应get请求 104 | *@param {String} url [请求的url地址] 105 | *@param {Object} params [请求时候携带的参数] 106 | */ 107 | export function get(url, params) { 108 | return new Promise((resolve, reject) => { 109 | axios 110 | .get(url, { 111 | params 112 | }) 113 | .then(res => { 114 | resolve(res); 115 | }) 116 | .catch(err => { 117 | reject(err); 118 | }); 119 | }); 120 | } 121 | /* 122 | *post方法,对应post请求 123 | *@param {String} url [请求的url地址] 124 | *@param {Object} params [请求时候携带的参数] 125 | */ 126 | export function post(url, params) { 127 | return new Promise((resolve, reject) => { 128 | axios 129 | .post(url, params) 130 | .then(res => { 131 | resolve(res); 132 | }) 133 | .catch(err => { 134 | reject(err); 135 | }); 136 | }); 137 | } -------------------------------------------------------------------------------- /src/assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | -------------------------------------------------------------------------------- /src/assets/css/public.css: -------------------------------------------------------------------------------- 1 | .el-form-item__label{ 2 | color: white ; 3 | } 4 | /* 5 | * ===================================================== 6 | * 页面公共样式、主体样式 7 | * ===================================================== 8 | */ 9 | 10 | /*初始化类*/ 11 | @charset "UTF-8"; 12 | html { 13 | font-family: "微软雅黑", Tahoma, Helvetica, Arial, sans-serif; 14 | /*去掉谷歌字体最小为12px的问题*/ 15 | -webkit-text-size-adjust: 100%; 16 | /*超链接的去除高亮*/ 17 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 18 | } 19 | *{ 20 | margin: 0; 21 | padding: 0; 22 | box-sizing: border-box; 23 | -webkit-box-sizing: border-box; 24 | } 25 | input[type=button], input[type=submit], input[type=file], button { 26 | cursor: pointer; 27 | /*去除表单元素点击时的外框*/ 28 | -webkit-appearance: none; 29 | outline: none; 30 | } 31 | body { 32 | margin: 0; 33 | padding: 0; 34 | line-height: normal; 35 | font-size: 0.24rem; 36 | /*下滑更流畅*/ 37 | -webkit-overflow-scrolling: touch; 38 | } 39 | 40 | img { 41 | border: 0; 42 | vertical-align: middle; 43 | } 44 | 45 | *:focus{ 46 | outline: medium; 47 | } 48 | 49 | article, 50 | aside, 51 | details, 52 | figcaption, 53 | figure, 54 | footer, 55 | header, 56 | hgroup, 57 | main, 58 | nav, 59 | section, 60 | summary { 61 | display: block; 62 | } 63 | a{ 64 | text-decoration: none; 65 | color: white; 66 | } 67 | audio, 68 | canvas, 69 | progress, 70 | video { 71 | display: block; 72 | } 73 | 74 | a { 75 | background: transparent; 76 | text-decoration: none; 77 | } 78 | 79 | ol, ul, li { 80 | list-style: outside none none; 81 | } 82 | 83 | table { 84 | border-collapse: collapse; 85 | border-spacing: 0; 86 | } 87 | 88 | a:active { 89 | opacity: 0.5; 90 | -webkit-transition: 0.3s; 91 | transition: 0.3s; 92 | } 93 | 94 | select, 95 | textarea, 96 | input, 97 | button { 98 | border:1px solid #c8c7cc; 99 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 100 | -webkit-appearance: none !important; 101 | } 102 | 103 | /*主样式*/ 104 | .pull-left { 105 | float: left !important; 106 | } 107 | 108 | .pull-right { 109 | float: right !important; 110 | } 111 | 112 | .clearfix:before, .clearfix:after { 113 | display: table; 114 | content: ' '; 115 | } 116 | .clearfix:after { 117 | clear: both; 118 | } 119 | 120 | .ellipsis { 121 | overflow: hidden; 122 | white-space: nowrap; 123 | text-overflow: ellipsis; 124 | } 125 | 126 | .ellipsis-2 { 127 | display: -webkit-box; 128 | overflow: hidden; 129 | white-space: normal !important; 130 | text-overflow: ellipsis; 131 | word-wrap: break-word; 132 | -webkit-line-clamp: 2; 133 | -webkit-box-orient: vertical; 134 | } 135 | 136 | .dcenter { 137 | margin-left: auto; 138 | margin-right: auto; 139 | } 140 | 141 | /*解决移动端1px边框变粗样式*/ 142 | .border-t, 143 | .border-b, 144 | .border-l, 145 | .border-r, 146 | .border-tb, 147 | .border { 148 | position: relative; 149 | } 150 | .border-t:after, 151 | .border-b:after, 152 | .border-l:after, 153 | .border-r:after, 154 | .border-tb:after, 155 | .border:after { 156 | display: block; 157 | content: ''; 158 | position: absolute; 159 | top: 0; 160 | right: 0; 161 | bottom: 0; 162 | left: 0; 163 | -webkit-transform-origin: 0 0; 164 | -webkit-transform: scale(1); 165 | pointer-events: none; 166 | } 167 | .border-l:after { 168 | border-left: 1px solid #c8c7cc; 169 | } 170 | .border-r:after { 171 | border-right: 1px solid #c8c7cc; 172 | } 173 | .border-t:after { 174 | border-top: 1px solid #c8c7cc; 175 | } 176 | .border-b:after { 177 | border-bottom: 1px solid #c8c7cc; 178 | } 179 | .border-tb:after { 180 | border-top: 1px solid #c8c7cc; 181 | border-bottom: 1px solid #c8c7cc; 182 | } 183 | .border:after { 184 | border:1px solid #c8c7cc; 185 | } 186 | .border.border-radius:after { 187 | border-radius: 6px; 188 | } 189 | @media screen and (-webkit-min-device-pixel-ratio:1.5) { 190 | .border-t:after, 191 | .border-b:after, 192 | .border-l:after, 193 | .border-r:after, 194 | .border-tb:after, 195 | .border:after { 196 | right: -100%; 197 | bottom: -100%; 198 | -webkit-transform: scale(0.5); 199 | } 200 | } 201 | 202 | /*仿IOS开关*/ 203 | .switch { 204 | -webkit-appearance: none; 205 | -moz-appearance: none; 206 | appearance: none; 207 | position: relative; 208 | width: 52px; 209 | height: 32px; 210 | border: 1px solid #DFDFDF; 211 | outline: 0; 212 | border-radius: 16px; 213 | box-sizing: border-box; 214 | background: #DFDFDF; 215 | } 216 | .switch:before { 217 | content: " "; 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | width: 50px; 222 | height: 30px; 223 | border-radius: 15px; 224 | background-color: #FDFDFD; 225 | -webkit-transition: -webkit-transform .3s; 226 | transition: -webkit-transform .3s; 227 | transition: transform .3s; 228 | transition: transform .3s, -webkit-transform .3s; 229 | } 230 | .switch:after { 231 | content: " "; 232 | position: absolute; 233 | top: 0; 234 | left: 0; 235 | width: 30px; 236 | height: 30px; 237 | border-radius: 15px; 238 | background-color: #FFFFFF; 239 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); 240 | -webkit-transition: -webkit-transform .3s; 241 | transition: -webkit-transform .3s; 242 | transition: transform .3s; 243 | transition: transform .3s, -webkit-transform .3s; 244 | } 245 | .switch:checked { 246 | border-color: #04BE02; 247 | background-color: #04BE02; 248 | } 249 | .switch:checked:before { 250 | -webkit-transform: scale(0); 251 | transform: scale(0); 252 | } 253 | .switch:checked:after { 254 | -webkit-transform: translateX(20px); 255 | transform: translateX(20px); 256 | } -------------------------------------------------------------------------------- /src/assets/icon/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/icon/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 |
    &#xe600;
    37 |
  • 38 | 39 |
  • 40 | 41 |
    眼睛
    42 |
    &#xe629;
    43 |
  • 44 | 45 |
  • 46 | 47 |
    消息
    48 |
    &#xe619;
    49 |
  • 50 | 51 |
  • 52 | 53 |
    用户
    54 |
    &#xe7bd;
    55 |
  • 56 | 57 |
58 |
59 |

Unicode 引用

60 |
61 | 62 |

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

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

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

70 |
71 |

Unicode 使用步骤如下:

72 |

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

73 |
@font-face {
 75 |   font-family: 'iconfont';
 76 |   src: url('iconfont.eot');
 77 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
 78 |       url('iconfont.woff2') format('woff2'),
 79 |       url('iconfont.woff') format('woff'),
 80 |       url('iconfont.ttf') format('truetype'),
 81 |       url('iconfont.svg#iconfont') format('svg');
 82 | }
 83 | 
84 |

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

85 |
.iconfont {
 87 |   font-family: "iconfont" !important;
 88 |   font-size: 16px;
 89 |   font-style: normal;
 90 |   -webkit-font-smoothing: antialiased;
 91 |   -moz-osx-font-smoothing: grayscale;
 92 | }
 93 | 
94 |

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

95 |
 96 | <span class="iconfont">&#x33;</span>
 98 | 
99 |
100 |

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

101 |
102 |
103 |
104 |
105 |
    106 | 107 |
  • 108 | 109 |
    110 | 流量 111 |
    112 |
    .icon-liuliang 113 |
    114 |
  • 115 | 116 |
  • 117 | 118 |
    119 | 眼睛 120 |
    121 |
    .icon-yanjing 122 |
    123 |
  • 124 | 125 |
  • 126 | 127 |
    128 | 消息 129 |
    130 |
    .icon-xiaoxi 131 |
    132 |
  • 133 | 134 |
  • 135 | 136 |
    137 | 用户 138 |
    139 |
    .icon-yonghu 140 |
    141 |
  • 142 | 143 |
144 |
145 |

font-class 引用

146 |
147 | 148 |

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

149 |

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

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

使用步骤如下:

157 |

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

158 |
<link rel="stylesheet" href="./iconfont.css">
159 | 
160 |

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

161 |
<span class="iconfont icon-xxx"></span>
162 | 
163 |
164 |

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

166 |
167 |
168 |
169 |
170 |
    171 | 172 |
  • 173 | 176 |
    流量
    177 |
    #icon-liuliang
    178 |
  • 179 | 180 |
  • 181 | 184 |
    眼睛
    185 |
    #icon-yanjing
    186 |
  • 187 | 188 |
  • 189 | 192 |
    消息
    193 |
    #icon-xiaoxi
    194 |
  • 195 | 196 |
  • 197 | 200 |
    用户
    201 |
    #icon-yonghu
    202 |
  • 203 | 204 |
205 |
206 |

Symbol 引用

207 |
208 | 209 |

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

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

使用步骤如下:

218 |

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

219 |
<script src="./iconfont.js"></script>
220 | 
221 |

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

222 |
<style>
223 | .icon {
224 |   width: 1em;
225 |   height: 1em;
226 |   vertical-align: -0.15em;
227 |   fill: currentColor;
228 |   overflow: hidden;
229 | }
230 | </style>
231 | 
232 |

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

233 |
<svg class="icon" aria-hidden="true">
234 |   <use xlink:href="#icon-xxx"></use>
235 | </svg>
236 | 
237 |
238 |
239 | 240 |
241 |
242 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /src/assets/icon/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1551687312467'); /* IE9 */ 3 | src: url('iconfont.eot?t=1551687312467#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAARUAAsAAAAACKQAAAQHAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDMgqEPIQDATYCJAMUCwwABCAFhG0HThuVB8gekiSBEBQESIhQAUaIAForqx7YgQ0B6bADIhVWwBLBEwrHQjHvBWXy73aoTpsJ1QY9zvNY0dtkImwmNb9HIjjoKTw+zXTD9/85lnqZYPmv7Vyyak17Axwl0ICiS2i6gWyivGV4q0t12FcTUOpiFTgxt7ga8CTITgFhnclAAV7CJanoDnJBpeDIRDgmlUf7oyc46v18+GVE4BFJY2TP6WtzdCC9BhpuM4frey12ENTDBcAfR4xBgISwpTC0DjMoDcJUqn/HMhZQykWimlr72vKGq/N1NN2GLCX9h0cQiySECtLZCIzF5YQaS0qg1l5KTK1/QOTa1EjzYK6UghJV3AL8j1B6uV8QS0Wjq6Iiz1tXYZuir3bNOtXc+uE1waXPWVi/Zo1qv7mCdWcg3HdWWDe3VjmnCQhxZ0UWNK1fr6w65gjzzmjNGovXqzn9rCYQ2O6v9/trA4E6X2Jtr7d2z5Xhq3PXR65ZKtmGamHghU1YkSh/6qt2G+GmO7NXNHK/LqgmDXOhbk59+frCs0Gw93bMjmGvQHTgORx5Ebj9XOg7WDf39KbP1/py2DvrHwnMxYW2bWDGRVXbSYWtMG3Fwva1ysUPcwqV55drqHPs0ON72qc5KJvOtsjPpZpJWRiaeLLpe5WL7mblKLvaF1L7lINP7eXU2CudVu+N7Mv/0OVn4LWUyp210av/DSezYWM9zIRZYM8GSBExs2Bmw4cXXUldydaZQNzzjQVx9XtYVAW7vGiyLVDBojR5xr7xt7x3IQZtm2brlYsK+ajmxaL5SngDsOxjRfnyM/2nl3/hvc5dUV/pVvmunn9koh/2JqSXh14ZYq+ZsCovVZflnV42IeOa6V92Ow+P45ZeD716iRAUBluRZ8V7o+sQP4imqWMeitVAfWYpU/5D/I472o7yNmny37yagJfn5o95yToWaK56q2aq+vWrG85KNC1xp4ky3VIpkdToG2eK1AUlXnqshU/DWFo37iTItY4gkukMsVwvZIIdBKmqUVCRGwOlgfKOr2qJxhSSAgNMAATNHIdIE3ch1sxTZIL9CKk2fkFFs8CE0twQdMaqPsEkyKIxRbAOdfQgk9Zh15teIvC7K7HBY6XouNZj1WLabcqiiCHD0tU8bMf0EPu4Ow2RhOiRnnbYUC5cD1utDuSkHWasJaFGQpyJYUPXFn1SqNZhAwIT0DAKgekgHXogJloOdnpnfkKQ+f5KmIGHFYWumFGVrYXR3EyrRyKECm+AzDPZG824lOe4dTKIRBB6iB7NwQbJhQBmpSUHxFm8mRmmRYQydig4JQrDc+mbmkKnt9ju8QookT3TEIU4JCENFfk6k8fMU3YDt8fuN5vsBk63iXJYw5weh91g9AAAAA==') format('woff2'), 5 | url('iconfont.woff?t=1551687312467') format('woff'), 6 | url('iconfont.ttf?t=1551687312467') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1551687312467#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-liuliang:before { 19 | content: "\e600"; 20 | } 21 | 22 | .icon-yanjing:before { 23 | content: "\e629"; 24 | } 25 | 26 | .icon-xiaoxi:before { 27 | content: "\e619"; 28 | } 29 | 30 | .icon-yonghu:before { 31 | content: "\e7bd"; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/assets/icon/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/icon/iconfont.eot -------------------------------------------------------------------------------- /src/assets/icon/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(s){var e,l='',t=(e=document.getElementsByTagName("script"))[e.length-1].getAttribute("data-injectcss");if(t&&!s.__iconfont__svg__cssinject__){s.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(e){console&&console.log(e)}}!function(e){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(e,0);else{var t=function(){document.removeEventListener("DOMContentLoaded",t,!1),e()};document.addEventListener("DOMContentLoaded",t,!1)}else document.attachEvent&&(n=e,c=s.document,o=!1,i=function(){o||(o=!0,n())},(l=function(){try{c.documentElement.doScroll("left")}catch(e){return void setTimeout(l,50)}i()})(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,i())});var n,c,o,i,l}(function(){var e,t,n,c,o,i;(e=document.createElement("div")).innerHTML=l,l=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",n=t,(c=document.body).firstChild?(o=n,(i=c.firstChild).parentNode.insertBefore(o,i)):c.appendChild(n))})}(window); -------------------------------------------------------------------------------- /src/assets/icon/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 | -------------------------------------------------------------------------------- /src/assets/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/icon/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/icon/iconfont.woff -------------------------------------------------------------------------------- /src/assets/icon/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/icon/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/img/tou.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/img/tou.jpg -------------------------------------------------------------------------------- /src/assets/js/common.js: -------------------------------------------------------------------------------- 1 | import {Message} from "element-ui"; 2 | export const messages = function (type, message) { 3 | Message({ 4 | type: type, 5 | message: message 6 | }); 7 | }; -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotian2017/vue-cli3-admin/789182f5a9375628e3dd5b11cdd0e5777bdac0d3/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/scss/global.scss: -------------------------------------------------------------------------------- 1 | $base-color:#bdb7ff; 2 | $base-white:white; 3 | $base-bule1:#4d79f6; 4 | $base-gray1:#f0f2f5; 5 | $base-ye:#F0B600; 6 | $base-green:#34A853; 7 | $base-pink:#E2918C; 8 | $base-f8:#f8f8f8; 9 | $base-666:#666; 10 | -------------------------------------------------------------------------------- /src/components/TreeTable/eval.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: jianglei 3 | * @Date: 2017-10-12 12:06:49 4 | */ 5 | 'use strict' 6 | import Vue from 'vue' 7 | export default function treeToArray(data, expandAll, parent = null, level = null) { 8 | let tmp = [] 9 | Array.from(data).forEach(function(record) { 10 | if (record._expanded === undefined) { 11 | Vue.set(record, '_expanded', expandAll) 12 | } 13 | let _level = 1 14 | if (level !== undefined && level !== null) { 15 | _level = level + 1 16 | } 17 | Vue.set(record, '_level', _level) 18 | // 如果有父元素 19 | if (parent) { 20 | Vue.set(record, 'parent', parent) 21 | } 22 | tmp.push(record) 23 | if (record.children && record.children.length > 0) { 24 | const children = treeToArray(record.children, expandAll, record, _level) 25 | tmp = tmp.concat(children) 26 | } 27 | }) 28 | return tmp 29 | } 30 | -------------------------------------------------------------------------------- /src/components/TreeTable/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 86 | 96 | 97 | 128 | -------------------------------------------------------------------------------- /src/components/TreeTable/readme.md: -------------------------------------------------------------------------------- 1 | ## 写在前面 2 | 此组件仅提供一个创建TreeTable的解决思路 3 | 4 | ## prop说明 5 | #### *data* 6 | **必填** 7 | 8 | 原始数据,要求是一个数组或者对象 9 | ```javascript 10 | [{ 11 | key1: value1, 12 | key2: value2, 13 | children: [{ 14 | key1: value1 15 | }, 16 | { 17 | key1: value1 18 | }] 19 | }, 20 | { 21 | key1: value1 22 | }] 23 | ``` 24 | 或者 25 | ```javascript 26 | { 27 | key1: value1, 28 | key2: value2, 29 | children: [{ 30 | key1: value1 31 | }, 32 | { 33 | key1: value1 34 | }] 35 | } 36 | ``` 37 | 38 | #### columns 39 | 列属性,要求是一个数组 40 | 41 | 1. text: 显示在表头的文字 42 | 2. value: 对应data的key。treeTable将显示相应的value 43 | 3. width: 每列的宽度,为一个数字(可选) 44 | 45 | 如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便 46 | 47 | 如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文 48 | ```javascript 49 | [{ 50 | value:string, 51 | text:string, 52 | width:number 53 | },{ 54 | value:string, 55 | text:string, 56 | width:number 57 | }] 58 | ``` 59 | 60 | #### expandAll 61 | 是否默认全部展开,boolean值,默认为false 62 | 63 | #### evalFunc 64 | 解析函数,function,非必须 65 | 66 | 如果不提供,将使用默认的[evalFunc](./eval.js) 67 | 68 | 如果提供了evalFunc,那么会用提供的evalFunc去解析data,并返回treeTable渲染所需要的值。如何编写一个evalFunc,请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js) 69 | 70 | #### evalArgs 71 | 解析函数的参数,是一个数组 72 | 73 | **请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开* 74 | 75 | 如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了 76 | 77 | 如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了 78 | 79 | 具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)的`evalArgs`属性值 80 | 81 | ## slot 82 | 这是一个自定义列的插槽。 83 | 84 | 默认情况下,treeTable只有一行行展示数据的功能。但是一般情况下,我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式,这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue),[实例效果](https://panjiachen.github.io/vue-element-admin/#/table/tree-table) 85 | 86 | `slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示 87 | 88 | ## 其他 89 | 如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue 90 | -------------------------------------------------------------------------------- /src/components/showAside.vue: -------------------------------------------------------------------------------- 1 | 8 | 19 | 29 | 30 | -------------------------------------------------------------------------------- /src/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Write your variables here. All available variables can be 3 | found in element-ui/packages/theme-chalk/src/common/var.scss. 4 | For example, to overwrite the theme color: 5 | */ 6 | $--color-primary: #409EFF; 7 | 8 | /* icon font path, required */ 9 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 10 | 11 | @import "~element-ui/packages/theme-chalk/src/index"; 12 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router/router' 4 | import store from './store/store' 5 | import './plugins/element.js' 6 | import './assets/css/normalize.css' 7 | import './assets/css/public.css' 8 | import '@/element-variables.scss' 9 | import {messages} from './assets/js/common' 10 | // import './assets/scss/global.scss' 11 | // 引入字体文件 12 | import '@/assets/icon/iconfont.css' 13 | Vue.config.productionTip = false 14 | //全局挂载提示框 15 | Vue.prototype.$message=messages 16 | new Vue({ 17 | router, 18 | store, 19 | render: h => h(App) 20 | }).$mount('#app') 21 | -------------------------------------------------------------------------------- /src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import '../element-variables.scss' 4 | 5 | Vue.use(Element) 6 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import store from '../store/store' 4 | import NProgress from 'nprogress' //进度条 5 | import 'nprogress/nprogress.css' 6 | Vue.use(Router) 7 | // 路由懒加载 8 | const getComponent = (name,component) => () => import(`@/views/${name}/${component}.vue`); 9 | const myRouter=new Router({ 10 | routes: [ 11 | { 12 | path: '/', 13 | redirect: '/home', 14 | component: getComponent('login','index') 15 | }, 16 | { 17 | path: '/login', 18 | name: 'login', 19 | component: getComponent('login','index') 20 | }, 21 | { 22 | path: '/', 23 | component:getComponent('layout','Layout'), 24 | children:[{ 25 | path:'/home', 26 | name:'home', 27 | component: getComponent('home','index'), 28 | meta:{title:'首页'} 29 | }, 30 | { 31 | path:'/icon', 32 | component: getComponent('icons','index'), 33 | name:'icon', 34 | meta:{title:'自定义图标'} 35 | }, 36 | { 37 | path:'/editor', 38 | component: getComponent('component','editor'), 39 | name:'editor', 40 | meta:{title:'富文本编译器'} 41 | }, 42 | { 43 | path:'/countTo', 44 | component: getComponent('component','countTo'), 45 | name:'countTo', 46 | meta:{title:'数字滚动'} 47 | }, 48 | { 49 | path:'/tree', 50 | component: getComponent('component','tree'), 51 | name:'tree', 52 | meta:{title:'自定义树'} 53 | }, 54 | { 55 | path:'/treeTable', 56 | component: getComponent('component','treeTable'), 57 | name:'treeTable', 58 | meta:{title:'表格树'} 59 | }, 60 | { 61 | path:'/treeSelect', 62 | component: getComponent('component','treeSelect'), 63 | name:'treeSelect', 64 | meta:{title:'下拉树'} 65 | }, 66 | { 67 | path:'/draglist', 68 | component: getComponent('draggable','draglist'), 69 | name:'draglist', 70 | meta:{title:'拖拽列表'} 71 | }, 72 | { 73 | path:'/dragtable', 74 | component: getComponent('draggable','dragtable'), 75 | name:'dragtable', 76 | meta:{title:'拖拽表格'} 77 | }, 78 | { 79 | path:'/cricle', 80 | component: getComponent('charts','cricle'), 81 | name:'cricle', 82 | meta:{title:'饼图'} 83 | }, 84 | ] 85 | } 86 | ] 87 | }) 88 | //判断是否存在token 89 | myRouter.beforeEach((to,from,next)=>{ 90 | NProgress.start() 91 | if (to.path !== '/login' && !store.state.token) { 92 | next('/login') 93 | NProgress.done() // 结束Progress 94 | } 95 | next() 96 | }) 97 | myRouter.afterEach(() => { 98 | NProgress.done() // 结束Progress 99 | }) 100 | export default myRouter -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | const actions={ 2 | 3 | } 4 | export default actions -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters={ 2 | 3 | } 4 | export default getters -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | const mutations = { 2 | //保存token 3 | COMMIT_TOKEN(state, object) { 4 | state.token = object.token; 5 | }, 6 | //保存标签 7 | TAGES_LIST(state, arr) { 8 | state.tagsList = arr; 9 | }, 10 | IS_COLLAPSE(state, bool) { 11 | state.isCollapse = bool; 12 | } 13 | } 14 | export default mutations -------------------------------------------------------------------------------- /src/store/state.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | token: '', 3 | role_id: '', 4 | tagsList: [], //打开的标签页个数, 5 | isCollapse: false, //侧边导航是否折叠 6 | } 7 | export default state -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import state from "./state"; 4 | import mutations from "./mutations"; 5 | import actions from "./actions"; 6 | import getters from "./getters"; 7 | //引入vuex 数据持久化插件 8 | import createPersistedState from "vuex-persistedstate" 9 | Vue.use(Vuex) 10 | 11 | export default new Vuex.Store({ 12 | state, 13 | mutations, 14 | actions, 15 | getters, 16 | plugins: [createPersistedState()] 17 | }) 18 | -------------------------------------------------------------------------------- /src/views/charts/cricle.vue: -------------------------------------------------------------------------------- 1 | 6 | 120 | 134 | 135 | -------------------------------------------------------------------------------- /src/views/component/countTo.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 176 | 370 | -------------------------------------------------------------------------------- /src/views/component/customEval.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: jianglei 3 | * @Date: 2017-10-12 12:06:49 4 | */ 5 | 'use strict' 6 | import Vue from 'vue' 7 | export default function treeToArray(data, expandAll, parent, level, item) { 8 | const marLTemp = [] 9 | let tmp = [] 10 | Array.from(data).forEach(function(record) { 11 | if (record._expanded === undefined) { 12 | Vue.set(record, '_expanded', expandAll) 13 | } 14 | let _level = 1 15 | if (level !== undefined && level !== null) { 16 | _level = level + 1 17 | } 18 | Vue.set(record, '_level', _level) 19 | // 如果有父元素 20 | if (parent) { 21 | Vue.set(record, 'parent', parent) 22 | // 如果父元素有偏移量,需要计算在this的偏移量中 23 | // 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和 24 | if (!marLTemp[_level]) { 25 | marLTemp[_level] = 0 26 | } 27 | Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft) 28 | Vue.set(record, '_width', record[item] / parent[item] * parent._width) 29 | // 在本次计算过偏移量后加上自己长度,以供下一个元素使用 30 | marLTemp[_level] += record._width 31 | } else { 32 | // 如果为根 33 | // 初始化偏移量存储map 34 | marLTemp[record.id] = [] 35 | // map中是一个数组,存储的是每级的长度和 36 | // 初始情况下为0 37 | marLTemp[record.id][_level] = 0 38 | Vue.set(record, '_marginLeft', 0) 39 | Vue.set(record, '_width', 1) 40 | } 41 | tmp.push(record) 42 | if (record.children && record.children.length > 0) { 43 | const children = treeToArray(record.children, expandAll, record, _level, item) 44 | tmp = tmp.concat(children) 45 | } 46 | }) 47 | return tmp 48 | } 49 | -------------------------------------------------------------------------------- /src/views/component/editor.vue: -------------------------------------------------------------------------------- 1 | 7 | 38 | 39 | 58 | 59 | -------------------------------------------------------------------------------- /src/views/component/tree.vue: -------------------------------------------------------------------------------- 1 | 23 | 120 | 128 | -------------------------------------------------------------------------------- /src/views/component/treeSelect.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 145 | 165 | -------------------------------------------------------------------------------- /src/views/component/treeTable.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 139 | -------------------------------------------------------------------------------- /src/views/draggable/draglist.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 56 | 83 | -------------------------------------------------------------------------------- /src/views/draggable/dragtable.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 49 | -------------------------------------------------------------------------------- /src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 146 | 269 | 413 | 414 | -------------------------------------------------------------------------------- /src/views/icons/index.vue: -------------------------------------------------------------------------------- 1 | 637 | 640 | 674 | 675 | -------------------------------------------------------------------------------- /src/views/layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 10 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/views/layout/components/Aside.vue: -------------------------------------------------------------------------------- 1 | 42 | 152 | 174 | -------------------------------------------------------------------------------- /src/views/layout/components/Header.vue: -------------------------------------------------------------------------------- 1 | 46 | 110 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /src/views/layout/components/Main.vue: -------------------------------------------------------------------------------- 1 | 11 | 19 | -------------------------------------------------------------------------------- /src/views/layout/components/Tags.vue: -------------------------------------------------------------------------------- 1 | 31 | 108 | 181 | 182 | -------------------------------------------------------------------------------- /src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 31 | 95 | 119 | 120 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | let path=require('path'); 2 | function resolve(dir){ 3 | return path.join(__dirname,dir) 4 | } 5 | module.exports = { 6 | chainWebpack: config => { 7 | //这里是对环境的配置,不同的环境对应不同的BASE_URL 8 | config.plugin('define').tap(args => { 9 | args[0]['process.env'].BASE_URL = JSON.stringify(process.env.BASE_URL) 10 | return args; 11 | }); 12 | //设置别名 13 | config.resolve.alias 14 | .set('@',resolve('src')) 15 | }, 16 | devServer: { 17 | open:true 18 | // 设置代理 19 | // lintOnSave: true, 20 | // devServer: { 21 | // proxy: { 22 | // // proxy all requests starting with /api to jsonplaceholder 23 | // '/api': { 24 | // target: 'http://test.jubao56.com/', //代理接口 25 | // changeOrigin: true, 26 | // pathRewrite: { 27 | // '^/api': '' //代理的路径 28 | // } 29 | // } 30 | // } 31 | // } 32 | // proxy: 'http://test.jubao56.com/' 33 | }, 34 | //定义scss全局变量 35 | css: { 36 | loaderOptions: { 37 | sass: { 38 | data: `@import "@/assets/scss/global.scss";` 39 | } 40 | } 41 | } 42 | } --------------------------------------------------------------------------------