The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .env.development
├── .env.production
├── .eslintignore
├── .gitignore
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
    ├── favicon.ico
    └── index.html
├── src
    ├── App.vue
    ├── api
    │   ├── money.js
    │   ├── sales.js
    │   └── user.js
    ├── assets
    │   ├── datas
    │   │   ├── area.json
    │   │   └── logs.json
    │   ├── echarts
    │   │   ├── china.js
    │   │   └── roma.min.js
    │   ├── github
    │   │   ├── 1.png
    │   │   ├── 2.png
    │   │   ├── 3.png
    │   │   ├── 4.png
    │   │   └── 5.png
    │   └── img
    │   │   ├── 401.png
    │   │   ├── 404.png
    │   │   ├── 500.png
    │   │   ├── america.svg
    │   │   ├── avatar-2.jpg
    │   │   ├── avatar-3.png
    │   │   ├── avatar.png
    │   │   ├── backTop.png
    │   │   ├── bg9.jpg
    │   │   ├── china.svg
    │   │   ├── logo.png
    │   │   ├── orange.png
    │   │   ├── pro_map.gif
    │   │   ├── qcode.jpg
    │   │   ├── qq.png
    │   │   ├── toMPic02.png
    │   │   ├── wechat.jpg
    │   │   └── zan.png
    ├── components
    │   ├── backTop
    │   │   └── index.vue
    │   ├── echarts
    │   │   ├── barChart.vue
    │   │   ├── lineChart.vue
    │   │   ├── orderSource.vue
    │   │   ├── pieChart.vue
    │   │   ├── radarChart.vue
    │   │   └── theme
    │   │   │   └── westeros.json
    │   ├── iconSvg
    │   │   ├── IconSvg.vue
    │   │   └── index.js
    │   ├── pagination
    │   │   └── index.vue
    │   └── test.vue
    ├── directive
    │   └── permission
    │   │   ├── index.js
    │   │   └── permission.js
    ├── lang
    │   ├── en.js
    │   ├── index.js
    │   └── zh.js
    ├── layout
    │   ├── bread.vue
    │   ├── content.vue
    │   ├── footerNav.vue
    │   ├── headNav.vue
    │   ├── home.vue
    │   ├── index.js
    │   ├── leftMenu.vue
    │   └── topMenu.vue
    ├── main.js
    ├── mockjs
    │   ├── index.js
    │   ├── money.js
    │   ├── sales.js
    │   └── user.js
    ├── page
    │   ├── errorPage
    │   │   ├── 401.vue
    │   │   └── 404.vue
    │   ├── fundData
    │   │   ├── fundPosition.vue
    │   │   ├── incomePayPosition.vue
    │   │   └── typePosition.vue
    │   ├── fundList
    │   │   ├── chinaTabsList.vue
    │   │   ├── components
    │   │   │   ├── addFundDialog.vue
    │   │   │   ├── chinaTabsTable.vue
    │   │   │   └── searchItem.vue
    │   │   ├── data
    │   │   │   └── chinaTabs.json
    │   │   ├── fundList.vue
    │   │   └── moneyData
    │   │   │   └── index.vue
    │   ├── index
    │   │   ├── components
    │   │   │   ├── cardList.vue
    │   │   │   ├── commentList.vue
    │   │   │   ├── logList.vue
    │   │   │   └── salesTable.vue
    │   │   └── index.vue
    │   ├── infoManage
    │   │   ├── infoModify.vue
    │   │   └── infoShow.vue
    │   ├── login.vue
    │   ├── permission
    │   │   ├── components
    │   │   │   └── SwitchRoles.vue
    │   │   ├── directive.vue
    │   │   └── page.vue
    │   ├── share
    │   │   ├── components
    │   │   │   ├── hengShare.vue
    │   │   │   ├── index.js
    │   │   │   ├── infoShare.vue
    │   │   │   ├── inviteShare.vue
    │   │   │   ├── jianshuLeftShare.vue
    │   │   │   ├── jianshuShare.vue
    │   │   │   ├── juejinShare.vue
    │   │   │   ├── sinaShare.vue
    │   │   │   ├── wxCodeModal.vue
    │   │   │   └── yanShare.vue
    │   │   └── index.vue
    │   └── userList
    │   │   └── userList.vue
    ├── permission.js
    ├── router
    │   ├── index.js
    │   └── topRouter.js
    ├── store
    │   ├── index.js
    │   └── modules
    │   │   ├── menu.js
    │   │   ├── money.js
    │   │   ├── permission.js
    │   │   └── user.js
    ├── style
    │   ├── common.less
    │   ├── element-ui.less
    │   ├── scrollBar.less
    │   └── variables.less
    └── utils
    │   ├── auth.js
    │   ├── axios.js
    │   ├── env.js
    │   ├── mUtils.js
    │   └── share.js
└── vue.config.js


/.env.development:
--------------------------------------------------------------------------------
1 | NODE_ENV = development
2 | VUE_APP_URL = ""


--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | NODE_ENV = production
2 | VUE_APP_URL = ""


--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 | src/*
4 | 


--------------------------------------------------------------------------------
/.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 | <b>小爱Admin</b>
  2 | 
  3 | **A magical vue element touzi admin.**
  4 | 
  5 | **分支说明:**
  6 | 
  7 | **master分支**:前后端统一开发的版本;可以用于学习nodejs+mongodb+express相关知识;
  8 | 
  9 | **dev分支**:进行了前后端分离的版本;用户只关注于前端部分,可忽略服务端;下载下来,即可运行;
 10 | 
 11 | <font color="#00dd00">
 12 | dev-permission分支:
 13 | 增加了权限管理(包括页面权限和按钮权限)的功能和顶栏三级菜单显示,完全剥离nodejs后台,使用mockjs模拟数据,让用户只需关注前端,更容易上手学习。目前此分支为正常维护分支。如有需要,请大家clone本分支代码运行。</font>
 14 | 
 15 | 
 16 | ## 项目博客文档
 17 | 
 18 | 1.[vue-小爱ADMIN系列文章(一):用Vue-cli3+mockjs 实现后台管理权限和三级菜单功能](https://github.com/wdlhao/webBlog/blob/master/readmes/01.md)
 19 | 
 20 | 2.[vue-小爱ADMIN系列文章(二):微信微博等分享,国际化,前端性能优化,nginx服务器部署](https://github.com/wdlhao/webBlog/blob/master/readmes/02.md)
 21 | 
 22 | ## 提示
 23 | 
 24 | 如果你觉得该项目对你有帮忙,记得给我点个赞**star**吧;
 25 | 
 26 | ## About
 27 | 
 28 | 本文主要讲解dev-permission分支内容:
 29 | 
 30 | ```bash
 31 |   如果对您对此项目有兴趣,可以点 "Star" 支持一下 谢谢! ^_^
 32 |   
 33 |   或者您可以 "follow" 一下,我会不断开源更多的有趣的项目
 34 |   
 35 |   开发环境 windows 64 、nodejs 6.11.0
 36 |   
 37 |   如有问题请直接在 Issues 中提,或者您发现问题并有非常好的解决方案,欢迎 PR
 38 | ```
 39 | 
 40 | ## 技术栈
 41 | 
 42 | **前端技术栈:** vue2 + vuex + vue-router + webpack + ES6/7 + less + element-ui
 43 | 
 44 | **服务端技术栈:** mockjs
 45 | 
 46 | ## 参考文档
 47 | 
 48 | *   [vue](https://vuejs.bootcss.com/v2/guide/):Vue是一套用于构建用户界面的渐进式框架。
 49 | 
 50 | *   [vuex](https://vuex.vuejs.org/zh/):Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
 51 | 
 52 | *   [vue-router](https://router.vuejs.org/zh/):Vue Router 是 Vue.js 官方的路由管理器。
 53 | 
 54 | *   [webpack](https://webpack.js.org/concepts/):前端模块打包器。
 55 | 
 56 | *   [less](http://lesscss.cn/):Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。
 57 | 
 58 | *   [element-ui](https://element.eleme.io/):Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。
 59 | 
 60 | *   [mockjs](https://github.com/nuysoft/Mock/wiki/Getting-Started):生成随机数据,拦截 Ajax 请求。
 61 | 
 62 | ## 前序准备
 63 | 
 64 | **运行前准备:**
 65 | 
 66 | 由于此项目是基于nodejs的前后端结合项目,你需要进行nodejs的相关准备工作。项目运行之前,请确保系统已经安装以下应用:
 67 | 
 68 | (1)、node (6.0 及以上版本)。使用细节,请参考:[node的下载及安装。](https://nodejs.org/en/download/)
 69 | 
 70 | ## 开发:
 71 | 
 72 | 1.  git clone -b dev-permission <https://github.com/wdlhao/vue2-element-touzi-admin>  (注意:要从dev-permission分支拉取代码)
 73 | 
 74 | 2.  cd vue2-element-touzi-admin
 75 | 
 76 | 3.  npm install
 77 | 
 78 | **本地运行:**
 79 | 
 80 | **npm run serve** 运行之后,会默认打开本地访问路径:<http://localhost:8012>
 81 | 
 82 | **发布:**
 83 | 
 84 | **npm run bulid** (生成打包之后的项目文件,此文件主要用于项目部署)。
 85 | 
 86 | ## 演示
 87 | 
 88 | 测试账号:
 89 | 
 90 | 1.  username: admin / password: 123456
 91 | 2.  username: editor / password: 123456
 92 | 
 93 | 注意:
 94 | 
 95 | admin:拥有最高权限,可以查看所有的页面和按钮;
 96 | 
 97 | editor:只有被赋予权限的页面和按钮才可以看到;
 98 | 
 99 | **温馨提示**:
100 | 
101 | 1、项目图标来源:
102 | 
103 | 本项目图标线上地址为://at.alicdn.com/t/font\_1258069\_e40c6mwl0x8.js, 在public/index.html中引入;
104 | 大家如需更换图标,可直接替换为自己iconfont线上的图标库地址即可;
105 | 
106 | 2、执行npm run dev报错处理方案:
107 | 
108 | 可能是你的本机安装nodejs版本过高,请卸载nodejs高版本,重新安装较低版本(如v10.9.0);
109 | 或者通过安装nvm来切换到较低版本也可以实现正常启动;
110 | 
111 | ## 查看更多demo
112 | 
113 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/github/1.png)
114 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/github/2.png)
115 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/github/3.png)
116 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/github/4.png)
117 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/github/5.png)
118 | 
119 | ## 技术答疑,交流QQ群
120 | 
121 | 项目说明:小爱ADMIN 是完全开源免费的管理系统集成方案,可以直接应用于相关后台管理系统模板;很多重点地方都做了详细的注释和解释。如果你也一样喜欢前端开发,欢迎加入我们的讨论/学习群,群内可以提问答疑,分享学习资料;
122 | 
123 | 欢迎添加群主微信和qq群答疑:
124 | 
125 | ![image](https://github.com/wdlhao/vue2-element-touzi-admin/blob/dev-permission/src/assets/img/qcode.jpg)
126 | 


--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   presets: [
3 |     '@vue/app'
4 |   ],
5 |   sourceType: 'unambiguous'
6 | }
7 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "vue",
 3 |   "version": "0.1.0",
 4 |   "private": true,
 5 |   "scripts": {
 6 |     "serve": "vue-cli-service serve",
 7 |     "dev": "vue-cli-service serve",
 8 |     "build": "vue-cli-service build",
 9 |     "build:prod": "vue-cli-service build --mode production",
10 |     "build:dev": "vue-cli-service build --mode development",
11 |     "lint": "vue-cli-service lint"
12 |   },
13 |   "dependencies": {
14 |     "axios": "^0.21.2",
15 |     "core-js": "^2.6.9",
16 |     "echarts": "^5.4.0",
17 |     "element-ui": "^2.11.1",
18 |     "express": "^4.17.1",
19 |     "js-cookie": "^2.2.1",
20 |     "mockjs": "^1.0.1-beta3",
21 |     "nprogress": "^0.2.0",
22 |     "qrcodejs2": "0.0.2",
23 |     "vue": "^2.6.10",
24 |     "vue-i18n": "^8.14.0",
25 |     "vue-router": "^3.1.2",
26 |     "vuex": "^3.1.1"
27 |   },
28 |   "devDependencies": {
29 |     "@babel/core": "^7.5.5",
30 |     "@babel/plugin-transform-runtime": "^7.5.5",
31 |     "@babel/preset-env": "^7.5.5",
32 |     "@vue/cli-plugin-babel": "^3.11.0",
33 |     "@vue/cli-plugin-eslint": "^3.11.0",
34 |     "@vue/cli-service": "^3.11.0",
35 |     "babel-eslint": "^10.0.2",
36 |     "compression-webpack-plugin": "^10.0.0",
37 |     "image-webpack-loader": "^8.1.0",
38 |     "less": "^4.1.3",
39 |     "less-loader": "^7.3.0",
40 |     "terser-webpack-plugin": "^5.3.6",
41 |     "vue-hot-reload-api": "^2.3.3",
42 |     "vue-loader": "^15.7.1",
43 |     "vue-template-compiler": "^2.6.10",
44 |     "webpack-bundle-analyzer": "^4.7.0"
45 |   },
46 |   "postcss": {
47 |     "plugins": {
48 |       "autoprefixer": {}
49 |     }
50 |   },
51 |   "browserslist": [
52 |     "> 1%",
53 |     "last 2 versions"
54 |   ]
55 | }
56 | 


--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/public/favicon.ico


--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html>
 3 |     <head>
 4 |         <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 5 |         <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 6 |         <meta name="renderer" content="webkit">
 7 |         <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
 8 |         <!-- <meta HTTP-EQUIV="pragma" CONTENT="no-cache"> 
 9 |         <meta HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> 
10 |         <meta HTTP-EQUIV="expires" CONTENT="0"> -->
11 |         <link rel="shortcut icon" href="favicon.ico"  type="image/x-icon"> 
12 |         <title>小爱Admin</title>
13 |           <!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
14 |         <% for (var i in
15 |         htmlWebpackPlugin.options.cdn &&  htmlWebpackPlugin.options.cdn.css) { %>
16 |         <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
17 |         <% } %>
18 |     </head>
19 |     <body>
20 |         <div id="app"></div>
21 | 
22 |         <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
23 |         <script src="//at.alicdn.com/t/font_1258069_e40c6mwl0x8.js"></script>
24 |         <% for (var i in
25 |         htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
26 |         <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
27 |         <% } %>
28 |     </body>
29 | </html>
30 | 


--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div id="app">
 3 | 		   <router-view></router-view>
 4 |     </div>
 5 | </template>
 6 | 
 7 | <script>
 8 |     export default {
 9 |     	name:'App'
10 |     }
11 | </script>
12 | 
13 | <style lang="less">
14 |     @import './style/variables';
15 | 	@import './style/element-ui';
16 | 	@import './style/common';
17 | 	@import './style/scrollBar';
18 | </style>
19 | 


--------------------------------------------------------------------------------
/src/api/money.js:
--------------------------------------------------------------------------------
 1 | import request from '@/utils/axios'
 2 | 
 3 | export function getMoneyIncomePay(params) {
 4 |   return request({
 5 |     url: '/money/get',
 6 |     method: 'get',
 7 |     params: params
 8 |   })
 9 | }
10 | 
11 | export function addMoney(params) {
12 |   return request({
13 |     url: '/money/add',
14 |     method: 'get',
15 |     params: params
16 |   })
17 | }
18 | 
19 | export function removeMoney(params) {
20 |   return request({
21 |     url: '/money/remove',
22 |     method: 'get',
23 |     params: params
24 |   })
25 | }
26 | 
27 | 
28 | export function batchremoveMoney(params) {
29 |   return request({
30 |     url: '/money/batchremove',
31 |     method: 'get',
32 |     params: params
33 |   })
34 | }
35 | 
36 | export function updateMoney(params) {
37 |   return request({
38 |     url: '/money/edit',
39 |     method: 'get',
40 |     params: params
41 |   })
42 | }
43 | 
44 | // export const addUser = params => { return axios.get(`${base}/user/add`, { params: params }) }
45 | 
46 | 
47 | 


--------------------------------------------------------------------------------
/src/api/sales.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/axios'
2 | 
3 | export function getSalesTableList(params) {
4 |   return request({
5 |     url: '/sales/get',
6 |     method: 'get',
7 |     params: params
8 |   })
9 | }


--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
 1 | import request from '@/utils/axios'
 2 | 
 3 | 
 4 | export function login(params) {
 5 |   return request({
 6 |     url: '/user/login',
 7 |     method: 'get',
 8 |     data:params
 9 |   })
10 | }
11 | export function logout(params) {
12 |   return request({
13 |     url: '/user/logout',
14 |     method: 'get',
15 |     data:params
16 |   })
17 | }
18 | 
19 | 
20 | export function getUserInfo(params) {
21 |   return request({
22 |     url: '/user/info/get',
23 |     method: 'get',
24 |     data:params
25 |   })
26 | }
27 | 
28 | export function getUserList(reqData) {
29 |   return request({
30 |     url:'/user/list/get',
31 |     method: 'get',
32 |     data: reqData
33 |   })
34 | }
35 | 
36 | 
37 | 


--------------------------------------------------------------------------------
/src/assets/datas/logs.json:
--------------------------------------------------------------------------------
 1 | {
 2 |    "success":true,
 3 |    "data":
 4 |       [
 5 |         { "createTime":"2019-08-20","data":["从以下方面对前端性能展开优化:","使用插件TerserPlugin,去除所有的console信息","使用CompressionPlugin,开启gzip压缩","忽略生产环境打包文件,使用外链cdn","图片压缩","路由按需加载","使用optimization.minimize(true):压缩代码和optimization.splitChunks():分割代码"]},
 6 |         { "createTime":"2019-08-14","data":["新增中英文切换功能","公共组件footerNav布局优化","顶部用户信息优化","新增回到顶部功能"]},
 7 |         { "createTime":"2019-08-10","data":["项目布局更改为左右布局,右侧内容超出窗口可视范围,出现滚动条","项目401页面和404页面重新改版"]},
 8 |         { "createTime":"2019-08-05","data":["项目401页面和404页面样式更新","剔除项目easy-mock模拟数据功能","项目登录之后的业务逻辑修改"]},
 9 |         { "createTime":"2019-07-10","data":["首页数据导航区域重新改版及ui优化","首页新增项目更新日志列表展示","首页新增访问量和下载量柱状图","首页新增用户投资区域雷达图","首页新增用户价格列表","首页新增用户评论列表","首页新增本项目技术栈progress"]},
10 |         { "createTime":"2019-07-03","data":["新增分享功能组件","分享功能组件包含4组横向排列","完成放简书网站分享组件","完成仿掘金网站分享组件","完成仿新浪分享组件"]},
11 |         { "createTime":"2019-06-22","data":["项目logo的设计和使用","项目添加微信和加入qq群宣传图的设计"]}
12 |       ]
13 | }


--------------------------------------------------------------------------------
/src/assets/echarts/roma.min.js:
--------------------------------------------------------------------------------
1 | !function(e,o){"function"==typeof define&&define.amd?define(["exports","echarts"],o):"object"==typeof exports&&"string"!=typeof exports.nodeName?o(0,require("echarts/lib/echarts")):o(0,e.echarts)}(this,function(e,o){o?o.registerTheme("roma",{color:["#E01F54","#001852","#f5e8c8","#b8d2c7","#c6b38e","#a4d8c2","#f3d999","#d3758f","#dcc392","#2e4783","#82b6e9","#ff6347","#a092f1","#0a915d","#eaf889","#6699FF","#ff6666","#3cb371","#d5b158","#38b6b6"],visualMap:{color:["#e01f54","#e7dbc3"],textStyle:{color:"#333"}},candlestick:{itemStyle:{color:"#e01f54",color0:"#001852"},lineStyle:{width:1,color:"#f5e8c8",color0:"#b8d2c7"},areaStyle:{color:"#a4d8c2",color0:"#f3d999"}},graph:{itemStyle:{color:"#a4d8c2"},linkStyle:{color:"#f3d999"}},gauge:{axisLine:{lineStyle:{color:[[.2,"#E01F54"],[.8,"#b8d2c7"],[1,"#001852"]],width:8}}}}):"undefined"!=typeof console&&console&&console.error&&console.error("ECharts is not Loaded")});


--------------------------------------------------------------------------------
/src/assets/github/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/github/1.png


--------------------------------------------------------------------------------
/src/assets/github/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/github/2.png


--------------------------------------------------------------------------------
/src/assets/github/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/github/3.png


--------------------------------------------------------------------------------
/src/assets/github/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/github/4.png


--------------------------------------------------------------------------------
/src/assets/github/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/github/5.png


--------------------------------------------------------------------------------
/src/assets/img/401.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/401.png


--------------------------------------------------------------------------------
/src/assets/img/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/404.png


--------------------------------------------------------------------------------
/src/assets/img/500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/500.png


--------------------------------------------------------------------------------
/src/assets/img/america.svg:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="iso-8859-1"?>
 2 | <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 3 | <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 4 | 	 viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
 5 | <rect style="fill:#F0F0F0;" width="512" height="512"/>
 6 | <g>
 7 | 	<rect y="64" style="fill:#D80027;" width="512" height="64"/>
 8 | 	<rect y="192" style="fill:#D80027;" width="512" height="64"/>
 9 | 	<rect y="320" style="fill:#D80027;" width="512" height="64"/>
10 | 	<rect y="448" style="fill:#D80027;" width="512" height="64"/>
11 | </g>
12 | <rect style="fill:#2E52B2;" width="256" height="275.69"/>
13 | <g>
14 | 	<polygon style="fill:#F0F0F0;" points="51.518,115.318 45.924,132.529 27.826,132.529 42.469,143.163 36.875,160.375 
15 | 		51.518,149.741 66.155,160.375 60.56,143.163 75.203,132.529 57.106,132.529 	"/>
16 | 	<polygon style="fill:#F0F0F0;" points="57.106,194.645 51.518,177.434 45.924,194.645 27.826,194.645 42.469,205.279 
17 | 		36.875,222.49 51.518,211.857 66.155,222.49 60.56,205.279 75.203,194.645 	"/>
18 | 	<polygon style="fill:#F0F0F0;" points="51.518,53.202 45.924,70.414 27.826,70.414 42.469,81.047 36.875,98.259 51.518,87.625 
19 | 		66.155,98.259 60.56,81.047 75.203,70.414 57.106,70.414 	"/>
20 | 	<polygon style="fill:#F0F0F0;" points="128.003,115.318 122.409,132.529 104.311,132.529 118.954,143.163 113.36,160.375 
21 | 		128.003,149.741 142.64,160.375 137.045,143.163 151.689,132.529 133.591,132.529 	"/>
22 | 	<polygon style="fill:#F0F0F0;" points="133.591,194.645 128.003,177.434 122.409,194.645 104.311,194.645 118.954,205.279 
23 | 		113.36,222.49 128.003,211.857 142.64,222.49 137.045,205.279 151.689,194.645 	"/>
24 | 	<polygon style="fill:#F0F0F0;" points="210.076,194.645 204.489,177.434 198.894,194.645 180.797,194.645 195.44,205.279 
25 | 		189.845,222.49 204.489,211.857 219.125,222.49 213.531,205.279 228.174,194.645 	"/>
26 | 	<polygon style="fill:#F0F0F0;" points="204.489,115.318 198.894,132.529 180.797,132.529 195.44,143.163 189.845,160.375 
27 | 		204.489,149.741 219.125,160.375 213.531,143.163 228.174,132.529 210.076,132.529 	"/>
28 | 	<polygon style="fill:#F0F0F0;" points="128.003,53.202 122.409,70.414 104.311,70.414 118.954,81.047 113.36,98.259 
29 | 		128.003,87.625 142.64,98.259 137.045,81.047 151.689,70.414 133.591,70.414 	"/>
30 | 	<polygon style="fill:#F0F0F0;" points="204.489,53.202 198.894,70.414 180.797,70.414 195.44,81.047 189.845,98.259 
31 | 		204.489,87.625 219.125,98.259 213.531,81.047 228.174,70.414 210.076,70.414 	"/>
32 | </g>
33 | <g>
34 | </g>
35 | <g>
36 | </g>
37 | <g>
38 | </g>
39 | <g>
40 | </g>
41 | <g>
42 | </g>
43 | <g>
44 | </g>
45 | <g>
46 | </g>
47 | <g>
48 | </g>
49 | <g>
50 | </g>
51 | <g>
52 | </g>
53 | <g>
54 | </g>
55 | <g>
56 | </g>
57 | <g>
58 | </g>
59 | <g>
60 | </g>
61 | <g>
62 | </g>
63 | </svg>
64 | 


--------------------------------------------------------------------------------
/src/assets/img/avatar-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/avatar-2.jpg


--------------------------------------------------------------------------------
/src/assets/img/avatar-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/avatar-3.png


--------------------------------------------------------------------------------
/src/assets/img/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/avatar.png


--------------------------------------------------------------------------------
/src/assets/img/backTop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/backTop.png


--------------------------------------------------------------------------------
/src/assets/img/bg9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/bg9.jpg


--------------------------------------------------------------------------------
/src/assets/img/china.svg:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="utf-8"?>
 2 | <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 3 | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 4 | 	 viewBox="-49 141 512 512" style="enable-background:new -49 141 512 512;" xml:space="preserve">
 5 | <style type="text/css">
 6 | 	.st0{fill:#D80027;}
 7 | 	.st1{fill:#FFDA44;}
 8 | </style>
 9 | <rect x="-49" y="141" class="st0" width="512" height="512"/>
10 | 
11 | <g>
12 | 	<polygon class="st1" points="91.1,296.8 113.2,364.8 184.7,364.8 126.9,406.9 149,474.9 91.1,432.9 33.2,474.9 55.4,406.9 
13 | 		-2.5,364.8 69,364.8 	"/>
14 | 	<polygon class="st1" points="254.5,537.5 237.6,516.7 212.6,526.4 227.1,503.9 210.2,483 236.1,489.9 250.7,467.4 252.1,494.2 
15 | 		278.1,501.1 253,510.7 	"/>
16 | 	<polygon class="st1" points="288.1,476.5 296.1,450.9 274.2,435.4 301,435 308.9,409.4 317.6,434.8 344.4,434.5 322.9,450.5 
17 | 		331.5,475.9 309.6,460.4 	"/>
18 | 	<polygon class="st1" points="333.4,328.9 321.6,353 340.8,371.7 314.3,367.9 302.5,391.9 297.9,365.5 271.3,361.7 295.1,349.2 
19 | 		290.5,322.7 309.7,341.4 	"/>
20 | 	<polygon class="st1" points="255.2,255.9 253.2,282.6 278.1,292.7 252,299.1 250.1,325.9 236,303.1 209.9,309.5 227.2,289 
21 | 		213,266.3 237.9,276.4 	"/>
22 | </g>
23 | </svg>
24 | 


--------------------------------------------------------------------------------
/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/logo.png


--------------------------------------------------------------------------------
/src/assets/img/orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/orange.png


--------------------------------------------------------------------------------
/src/assets/img/pro_map.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/pro_map.gif


--------------------------------------------------------------------------------
/src/assets/img/qcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/qcode.jpg


--------------------------------------------------------------------------------
/src/assets/img/qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/qq.png


--------------------------------------------------------------------------------
/src/assets/img/toMPic02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/toMPic02.png


--------------------------------------------------------------------------------
/src/assets/img/wechat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/wechat.jpg


--------------------------------------------------------------------------------
/src/assets/img/zan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wdlhao/vue2-element-touzi-admin/7a73cd93e5e3b22590c650cd99a33a4bc430e3a7/src/assets/img/zan.png


--------------------------------------------------------------------------------
/src/components/backTop/index.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div 
 3 |         class="backTop" 
 4 |         :style="{'opacity':opacity}" 
 5 |         v-show="gotop"
 6 |         @mouseenter="enterBackTop" 
 7 |         @mouseout="outBackTop"
 8 |         @click="handleScrollTop"
 9 |          >
10 |         <div class="back"></div>
11 |     </div>
12 | </template>
13 | 
14 | 
15 | <script>
16 |   export default {
17 |       name:'backTop',
18 |       data(){
19 |           return {
20 |             opacity:'.3',
21 |             gotop:false,
22 |             scrollHeight:100
23 |           }
24 |       },
25 |       props:['ele'],
26 |       created(){
27 | 
28 |       },
29 |       mounted(){
30 |           // 此处true需要加上,不加滚动事件可能绑定不成功
31 |          window.addEventListener("scroll", this.handleScroll, true);
32 |       },
33 |       methods:{ 
34 |         enterBackTop(){
35 |             this.opacity = '1';
36 |         },
37 |         outBackTop(){
38 |             this.opacity = '.3';
39 |         },
40 |         handleScroll(e) {
41 |             let scrolltop = e.target.scrollTop;
42 |             scrolltop > this.scrollHeight ? (this.gotop = true) : (this.gotop = false);
43 |         },
44 |         handleScrollTop() {
45 |             this.$nextTick(()=>{    
46 |                 this.ele.scrollTop = 0;
47 |             })
48 |         }
49 |     }
50 |   }
51 | </script>
52 | 
53 | <style lang="less" scoped>
54 |      .backTop{
55 |          position: fixed;
56 |          right: 50px;
57 |          bottom: 50px;
58 |          z-index: 10;
59 |          cursor: pointer;
60 |          .back{
61 |             background: url('../../assets/img/backTop.png') no-repeat;
62 |             background-position: center;
63 |             background-size: 16px;
64 |             background-color: #1890ff;
65 |             width: 40px;
66 |             height: 40px;
67 |             overflow: hidden;
68 |             color: #fff;
69 |             text-align: center;
70 |             border-radius: 50%;
71 |             transition: all .3s;
72 |             box-shadow: 0 0 15px 1px rgba(69,65,78,.1)
73 |          }
74 |      }
75 | </style>
76 | 


--------------------------------------------------------------------------------
/src/components/echarts/barChart.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div :id="id" class="orderArea orderbarArea"></div>
 3 | </template>
 4 | 
 5 | <script>
 6 |     import echarts from 'echarts'
 7 |     import echartsTheme from "cps/echarts/theme/westeros.json";
 8 |     export default {
 9 |         name:'barChat',
10 |         data(){
11 |             return {
12 |                  id:'barChart',
13 |                  myChart:null,
14 |             }
15 |         },
16 |         mounted(){
17 |             this.loadChart();
18 |         },
19 |         beforeDestroy() {
20 | 			if (!this.myChart) {
21 | 				return
22 | 			}
23 | 			this.myChart.dispose();
24 | 			this.myChart = null;
25 |         },
26 |         methods: {
27 |             loadChart(){
28 |                 this.$nextTick(() => {
29 |                     echarts.registerTheme('westeros', echartsTheme)
30 |                     this.myChart = echarts.init(document.getElementById(this.id),'westeros');
31 |                     this.myChart.setOption(this.initOption());
32 |                 })
33 |             },
34 |          	initOption(){
35 |                 let option = {
36 |                     tooltip : {
37 |                         trigger: 'axis'
38 |                     },
39 |                     legend: {
40 |                         data:['访问量','下载量']
41 |                     },
42 |                     xAxis : [
43 |                         {
44 |                             type : 'category',
45 |                             data : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月']
46 |                         }
47 |                     ],
48 |                     yAxis : [
49 |                         {
50 |                             type : 'value'
51 |                         }
52 |                     ],
53 |                     series : [
54 |                         {
55 |                             name:'访问量',
56 |                             type:'bar',
57 |                             data:[2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3],
58 |                             markPoint : {
59 |                                 data : [
60 |                                     {type : 'max', name: '最大值'},
61 |                                     {type : 'min', name: '最小值'}
62 |                                 ]
63 |                             }
64 |                         },
65 |                         {
66 |                             name:'下载量',
67 |                             type:'bar',
68 |                             data:[2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],
69 |                             markPoint : {
70 |                                 data : [
71 |                                     {name : '年最高', value : 182.2, xAxis: 7, yAxis: 183},
72 |                                     {name : '年最低', value : 2.3, xAxis: 11, yAxis: 3}
73 |                                 ]
74 |                             }
75 |                         }
76 |                     ]
77 |                 };
78 | 				return option;
79 | 			},
80 |         },
81 |         watch: {
82 |         }
83 |     }
84 | </script>
85 | 
86 | <style lang="less">
87 | 
88 | </style>
89 | 


--------------------------------------------------------------------------------
/src/components/echarts/lineChart.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div :id="id" class="orderArea"></div>
  3 | </template>
  4 | 
  5 | <script>
  6 | 	import echarts from 'echarts'
  7 |     import echartsTheme from "cps/echarts/theme/westeros.json";
  8 |     
  9 |     export default {
 10 |         data(){
 11 |             return {
 12 | 				id:"lineChart",
 13 |                 myChart:null,
 14 |             }
 15 |         },
 16 |         mounted(){
 17 |             this.loadChart();
 18 |         },
 19 | 		beforeDestroy() {
 20 | 			if (!this.myChart) {
 21 | 				return
 22 | 			}
 23 | 			this.myChart.dispose();
 24 | 			this.myChart = null;
 25 |         },
 26 |         methods: {
 27 | 			loadChart(){
 28 |   				this.$nextTick(() => {
 29 |                     echarts.registerTheme('westeros', echartsTheme)
 30 |                     this.myChart = echarts.init(document.getElementById(this.id),'westeros');
 31 |                     this.myChart.setOption(this.initOption());
 32 |                 })
 33 | 			},
 34 | 			initOption(){
 35 | 				let data = {
 36 | 					title: {
 37 | 						text: ''
 38 | 					},
 39 | 					tooltip : {
 40 | 						trigger: 'axis',
 41 | 						axisPointer: {
 42 | 							type: 'cross',
 43 | 							label: {
 44 | 								backgroundColor: '#6a7985'
 45 | 							}
 46 | 						}
 47 | 					},
 48 | 					legend: {
 49 | 						data:['股票','基金','债券','储蓄','期货']
 50 | 					},
 51 | 					grid: {
 52 | 						left: '3%',
 53 | 						right: '4%',
 54 | 						bottom: '3%',
 55 | 						containLabel: true
 56 | 					},
 57 | 					xAxis : [
 58 | 						{
 59 | 							type : 'category',
 60 | 							boundaryGap : false,
 61 | 							data : ['周一','周二','周三','周四','周五','周六','周日']
 62 | 						}
 63 | 					],
 64 | 					yAxis : [
 65 | 						{
 66 | 							type : 'value'
 67 | 						}
 68 | 					],
 69 | 					series : [
 70 | 						{
 71 | 							name:'股票',
 72 | 							type:'line',
 73 | 							stack: '总量',
 74 | 							areaStyle: {normal: {}},
 75 | 							data:[120, 132, 101, 134, 90, 230, 210]
 76 | 						},
 77 | 						{
 78 | 							name:'基金',
 79 | 							type:'line',
 80 | 							stack: '总量',
 81 | 							areaStyle: {normal: {}},
 82 | 							data:[220, 182, 191, 234, 290, 330, 310]
 83 | 						},
 84 | 						{
 85 | 							name:'债券',
 86 | 							type:'line',
 87 | 							stack: '总量',
 88 | 							areaStyle: {normal: {}},
 89 | 							data:[150, 232, 201, 154, 190, 330, 410]
 90 | 						},
 91 | 						{
 92 | 							name:'期货',
 93 | 							type:'line',
 94 | 							stack: '总量',
 95 | 							areaStyle: {normal: {}},
 96 | 							data:[320, 332, 301, 334, 390, 330, 320]
 97 | 						},
 98 | 						{
 99 | 							name:'储蓄',
100 | 							type:'line',
101 | 							stack: '总量',
102 | 							label: {
103 | 								normal: {
104 | 									show: true,
105 | 									position: 'top'
106 | 								}
107 | 							},
108 | 							areaStyle: {normal: {}},
109 | 							data:[820, 932, 901, 934, 1290, 1330, 1320]
110 | 						}
111 | 					]
112 | 				}
113 | 				return data;
114 | 			},
115 | 		},
116 |         watch: {
117 |             // id:()=>{
118 |             //     this.initOption()
119 |             // }
120 |         }
121 |     }
122 | </script>
123 | 
124 | <style lang="less">
125 | 
126 | </style>
127 | 


--------------------------------------------------------------------------------
/src/components/echarts/orderSource.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div :id="id" style="width:100%;height:280px;"></div>
  3 | </template>
  4 | 
  5 | <script>
  6 |    	import echarts from 'echarts'
  7 |     
  8 |     export default {
  9 |         data(){
 10 |             return {
 11 |                  myChart:null,
 12 |             }
 13 |         },
 14 |         mounted(){
 15 |             this.myChart = echarts.init(document.getElementById(this.id));
 16 |             this.myChart.setOption(this.redyBin2Option(this.type));
 17 |         },
 18 |         props: ['id','type'],
 19 |         methods: {
 20 |          	redyBin2Option(type){
 21 |                 let text,legend_data,series_data,inner_data;
 22 | 				if(type == "ordersource"){
 23 | 					text = "用户投资途径";
 24 | 					legend_data = ['直达','营销广告','搜索引擎','邮件营销','联盟广告','谷歌']
 25 |                     inner_data = [
 26 |                         {value:335, name:'直达', selected:true},
 27 |                         {value:679, name:'营销广告'},
 28 |                         {value:1548, name:'搜索引擎'}
 29 |                     ]
 30 | 					series_data = [
 31 |                         {value:310, name:'邮件营销'},
 32 |                         {value:234, name:'联盟广告'},
 33 |                         {value:634, name:'谷歌'}
 34 | 					]
 35 | 				}else{
 36 | 					text = "用户投资职业";
 37 | 					legend_data = ['金融人员','IT人员','保险人员','理财师','律师'];
 38 |                     inner_data = [
 39 |                         {value:335, name:'金融人员', selected:true},
 40 |                         {value:679, name:'IT人员'},
 41 |                         {value:1548, name:'保险人员'}
 42 |                     ]
 43 | 					series_data = [
 44 | 						{value:310, name:'理财师'},
 45 | 						{value:528, name:'律师'},
 46 | 					]
 47 | 				}
 48 | 				let data  = {
 49 | 					title : {
 50 | 						text: text,
 51 | 						subtext: '纯属虚构',
 52 | 						x:'center'
 53 | 					},
 54 | 					 tooltip: {
 55 | 						trigger: 'item',
 56 | 						formatter: "{a} <br/>{b}: {c} ({d}%)"
 57 | 					},
 58 | 					legend: {
 59 | 						orient: 'vertical',
 60 | 						x: 'left',
 61 | 						data:legend_data
 62 | 					},
 63 | 					series: [
 64 | 						{
 65 | 							name:'访问来源',
 66 | 							type:'pie',
 67 | 							selectedMode: 'single',
 68 | 							radius: [0, '30%'],
 69 |                             center: ['50%', '60%'],
 70 | 							label: {
 71 | 								normal: {
 72 | 									position: 'inner'
 73 | 								}
 74 | 							},
 75 | 							labelLine: {
 76 | 								normal: {
 77 | 									show: false
 78 | 								}
 79 | 							},
 80 | 							data:inner_data
 81 | 						},
 82 | 						{
 83 | 							name:'访问来源',
 84 | 							type:'pie',
 85 | 							radius: ['40%', '55%'],
 86 |                             center: ['50%', '60%'],
 87 | 							label: {
 88 | 								normal: {
 89 | 									backgroundColor: '#eee',
 90 | 									borderColor: '#aaa',
 91 | 									borderWidth: 1,
 92 | 									borderRadius: 4,
 93 | 									rich: {
 94 | 										a: {
 95 | 											color: '#999',
 96 | 											lineHeight: 22,
 97 | 											align: 'center'
 98 | 										},
 99 | 										hr: {
100 | 											borderColor: '#aaa',
101 | 											width: '100%',
102 | 											borderWidth: 0.5,
103 | 											height: 0
104 | 										},
105 | 										b: {
106 | 											fontSize: 16,
107 | 											lineHeight: 33
108 | 										},
109 | 										per: {
110 | 											color: '#eee',
111 | 											backgroundColor: '#334455',
112 | 											padding: [2, 4],
113 | 											borderRadius: 2
114 | 										}
115 | 									}
116 | 								}
117 | 							},
118 | 							data:series_data
119 | 						}
120 | 					]
121 | 				}
122 | 				return data;
123 | 			}
124 |         },
125 |         watch: {
126 |             type:(v)=>{
127 |                 this.redyBin2Option(v)
128 |             }
129 |         }
130 |     }
131 | </script>
132 | 
133 | <style lang="less">
134 | 
135 | </style>
136 | 


--------------------------------------------------------------------------------
/src/components/echarts/pieChart.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div :id="id" class="orderArea"></div>
  3 | </template>
  4 | 
  5 | <script>
  6 | 	import echarts from 'echarts'
  7 |     import echartsTheme from "cps/echarts/theme/westeros.json";
  8 |     
  9 |     export default {
 10 |         data(){
 11 |             return {
 12 | 				 id:'ordertype',
 13 |                  myChart:null,
 14 |             }
 15 |         },
 16 | 		props: ['type'],
 17 |         mounted(){
 18 | 			this.loadChart();
 19 | 		},
 20 | 		beforeDestroy() {
 21 | 			if (!this.myChart) {
 22 | 				return
 23 | 			}
 24 | 			this.myChart.dispose();
 25 | 			this.myChart = null;
 26 |         },
 27 |         methods: {
 28 | 			 loadChart(){
 29 |                 this.$nextTick(() => {
 30 |                     echarts.registerTheme('westeros', echartsTheme)
 31 |                     this.myChart = echarts.init(document.getElementById(this.id),'westeros');
 32 |                     this.myChart.setOption(this.initOption(this.type));
 33 |                 })
 34 |             },
 35 |          	initOption(type){
 36 | 				let text,legend_data,series_data;
 37 | 				if(type == "ordertype"){
 38 | 					text = "用户投资类型";
 39 | 					legend_data = ['基金','股票','债券','储蓄','期货'];
 40 | 					series_data = [
 41 | 						{value:335, name:'基金'},
 42 | 						{value:310, name:'股票'},
 43 | 						{value:234, name:'债券'},
 44 | 						{value:135, name:'储蓄'},
 45 | 						{value:1548, name:'期货'}
 46 | 					]
 47 | 				}else{
 48 | 					text = "用户投资区域";
 49 | 					legend_data = ['华东区','华南区','华中区','华北区','西南区','东北区','台港澳'];
 50 | 					series_data = [
 51 | 						{value:335, name:'华东区'},
 52 | 						{value:310, name:'华南区'},
 53 | 						{value:234, name:'华中区'},
 54 | 						{value:835, name:'华北区'},
 55 | 						{value:1548, name:'西南区'},
 56 | 						{value:335, name:'东北区'},
 57 | 						{value:454, name:'台港澳'}
 58 | 					]
 59 | 				}
 60 | 				let data = {
 61 | 					  title : {
 62 | 						text: text,
 63 | 						x:'center'
 64 | 					},
 65 | 					tooltip : {
 66 | 						trigger: 'item',
 67 | 						formatter: "{a} <br/>{b} : {c} ({d}%)"
 68 | 					},
 69 | 					legend: {
 70 | 						orient: 'vertical',
 71 | 						left: 'left',
 72 | 						data: legend_data
 73 | 					},
 74 | 					series : [
 75 | 						{
 76 | 							name: '访问来源',
 77 | 							type: 'pie',
 78 | 							radius : '50%',
 79 |                             center: ['50%', '60%'],
 80 | 							data:series_data,
 81 | 							itemStyle: {
 82 | 								emphasis: {
 83 | 									shadowBlur: 10,
 84 | 									shadowOffsetX: 0,
 85 | 									shadowColor: 'rgba(0, 0, 0, 0.5)'
 86 | 								}
 87 | 							}
 88 | 						}
 89 | 					]
 90 | 				}
 91 | 				return data;
 92 | 			},
 93 |         },
 94 |         watch: {
 95 |             type:(v)=>{
 96 |                 this.initOption(v)
 97 |             }
 98 |         }
 99 |     }
100 | </script>
101 | 
102 | <style lang="less">
103 | 
104 | </style>
105 | 


--------------------------------------------------------------------------------
/src/components/echarts/radarChart.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div :id="id" class="orderArea"></div>
  3 | </template>
  4 | 
  5 | <script>
  6 |     import echarts from 'echarts'
  7 |     import echartsTheme from "cps/echarts/theme/westeros.json";
  8 |     
  9 |     export default {
 10 |         name:'radarChart',
 11 |         data(){
 12 |             return {
 13 |                  id:"radarChart",
 14 |                  myChart:null,
 15 |             }
 16 |         },
 17 |         mounted(){
 18 |             this.loadChart();
 19 |         },
 20 |         beforeDestroy() {
 21 | 			if (!this.myChart) {
 22 | 				return
 23 | 			}
 24 | 			this.myChart.dispose();
 25 | 			this.myChart = null;
 26 |         },
 27 |         methods: {
 28 |             loadChart(){
 29 |                 this.$nextTick(() => {
 30 |                     this.myChart = echarts.init(document.getElementById(this.id));
 31 |                     this.myChart.setOption(this.initOption());
 32 |                 })
 33 |             },
 34 |          	initOption(){
 35 |                let option = {
 36 |                     radar: [{
 37 |                     name: {
 38 |                         fontSize: 11 // 顶点字体大小 不能设置 radio 否则不能生效
 39 |                     },
 40 |                     splitLine: {
 41 |                         lineStyle: {
 42 |                           color: '#00aaff' // 每一圈的边界颜色
 43 |                         }
 44 |                     },
 45 |                     axisLabel: {
 46 |                         inside: true
 47 |                     },
 48 |                     axisLine: {
 49 |                         lineStyle: {
 50 |                           color: '#00aaff' // 半径线颜色
 51 |                         }
 52 |                     },
 53 |                     splitArea: {
 54 |                         areaStyle: {
 55 |                         // color: ['#00aaff', '#0099ff', '#00aaff', '#0099ff', '#00aaff'] // 每一圈的颜色
 56 |                         }
 57 |                     },
 58 |                     indicator: [
 59 |                         {text: '东北区域', max: 100, color: '#87DE75'}, // 选中颜色
 60 |                         {text: '华南区域', max: 100,color: '#FFA3A1'},
 61 |                         {text: '华中区域', max: 100,color: '#FF9900'},
 62 |                         {text: '华北区域', max: 100,color: '#5FB4FA'},
 63 |                         {text: '西北区域', max: 100,color: '#a9d86e'},
 64 |                         {text: '西南区域', max: 100,color: '#FF6C60'},
 65 |                         {text: '东北区域', max: 100,color: '#18a689'},
 66 |                         {text: '港澳台', max: 100,color: '#3b5999'}
 67 |                     ]
 68 |                     }],
 69 |                     series: [{
 70 |                     type: 'radar',
 71 |                     data: [{
 72 |                         value: [60, 73, 85, 40, 50, 100],
 73 |                         areaStyle: {
 74 |                         normal: {
 75 |                             opacity: 0.8, // 图表透明度
 76 |                             color: '#87DE75' // 图表颜色
 77 |                         }
 78 |                         },
 79 |                         lineStyle: {
 80 |                         color: '#6397ff' // 图表边框颜色
 81 |                         },
 82 |                         label: {
 83 |                         normal: {
 84 |                             show: true,
 85 |                             color: '#6397ff', // 顶点数字颜色
 86 |                             fontSize: 16,  // 顶点数字大小
 87 |                             formatter: function (params) {
 88 |                               return params.value
 89 |                             }
 90 |                         }
 91 |                         }
 92 |                     }]
 93 |                     }]
 94 |                 }
 95 | 				return option;
 96 | 			},
 97 |         },
 98 |         watch: {
 99 |         }
100 |     }
101 | </script>
102 | 
103 | <style lang="less">
104 | 
105 | </style>
106 | 


--------------------------------------------------------------------------------
/src/components/iconSvg/IconSvg.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <svg class="svg-icon" aria-hidden="true">
 3 |     <use :xlink:href="iconName"></use>
 4 |   </svg>
 5 | </template>
 6 | 
 7 | <script>
 8 | export default {
 9 |   name: 'icon-svg',
10 |   props: {
11 |     iconClass: {
12 |       type: String,
13 |       required: true
14 |     }
15 |   },
16 |   computed: {
17 |     iconName() {
18 |       return `#${this.iconClass}`
19 |     }
20 |   }
21 | }
22 | </script>
23 | 
24 | <style>
25 | .svg-icon {
26 |   width: 1em;
27 |   height: 1em;
28 |   font-size: 18px;
29 |   vertical-align: -0.15em;
30 |   fill: currentColor;
31 |   overflow: hidden;
32 | }
33 | </style>
34 | 


--------------------------------------------------------------------------------
/src/components/iconSvg/index.js:
--------------------------------------------------------------------------------
1 | // 存放一些全局的组件
2 | import Vue from 'vue'
3 | import IconSvg from './IconSvg'
4 | //全局注册icon-svg
5 | Vue.component('icon-svg', IconSvg)
6 | 


--------------------------------------------------------------------------------
/src/components/pagination/index.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="pagination">
 3 |         <el-pagination
 4 |             v-if='pageTotal > 0'
 5 |             background
 6 |             :page-sizes="paginations.pageSizes"
 7 |             :page-size="paginations.pageSize"
 8 |             :layout="paginations.layout"
 9 |             :total="pageTotal"
10 |             :current-page='paginations.pageIndex'
11 |             @current-change='handleCurrentChange'
12 |             @size-change='handleSizeChange'>
13 |         </el-pagination>
14 |     </div>
15 | </template>
16 | 
17 | 
18 | <script>
19 |   export default {
20 |       name:'pagination',
21 |       data(){
22 |           return {
23 |             paginations: {
24 |                 pageIndex: 1,  // 当前位于哪页
25 |                 pageSize: 20,   // 1页显示多少条
26 |                 pageSizes: [5, 10, 15, 20],  //每页显示多少条
27 |                 layout: "total, sizes, prev, pager, next, jumper"   // 翻页属性
28 |             },
29 |           }
30 |       },
31 |       props:{
32 |           pageTotal:Number
33 |       },
34 |       created(){
35 | 
36 |       },
37 |       mounted(){
38 | 
39 |       },
40 |       methods:{ 
41 |            // 上下分页 pageIndex
42 |           handleCurrentChange(page){
43 |               this.$emit('handleCurrentChange',page);
44 |           },
45 |            // 每页多少条切换 pageSize
46 |           handleSizeChange(page_size){
47 |               this.$emit('handleSizeChange',page_size);
48 |           }
49 |       }
50 |   }
51 | </script>
52 | 
53 | <style lang="less" scoped>
54 |     .pagination{
55 |         text-align: right;
56 |         padding: 10px 18px;
57 |     }
58 | </style>
59 | 


--------------------------------------------------------------------------------
/src/components/test.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |   <div>
  3 |     <el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Title">
  4 |       <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
  5 |         <el-form-item label="手机号" prop="mobile">
  6 |           <el-input v-model="formData.mobile" placeholder="请输入手机号" :maxlength="11" show-word-limit clearable
  7 |             prefix-icon='el-icon-mobile' :style="{width: '100%'}"></el-input>
  8 |         </el-form-item>
  9 |         <el-form-item label="多行文本" prop="field101">
 10 |           <el-input v-model="formData.field101" type="textarea" placeholder="请输入多行文本"
 11 |             :autosize="{minRows: 4, maxRows: 4}" :style="{width: '100%'}"></el-input>
 12 |         </el-form-item>
 13 |         <el-form-item label="计数器" prop="field102">
 14 |           <el-input-number v-model="formData.field102" placeholder="计数器"></el-input-number>
 15 |         </el-form-item>
 16 |         <el-form-item label="下拉选择" prop="field103">
 17 |           <el-select v-model="formData.field103" placeholder="请选择下拉选择" clearable :style="{width: '100%'}">
 18 |             <el-option v-for="(item, index) in field103Options" :key="index" :label="item.label"
 19 |               :value="item.value" :disabled="item.disabled"></el-option>
 20 |           </el-select>
 21 |         </el-form-item>
 22 |         <el-form-item label="日期范围" prop="field105">
 23 |           <el-date-picker type="daterange" v-model="formData.field105" format="yyyy-MM-dd"
 24 |             value-format="yyyy-MM-dd" :style="{width: '100%'}" start-placeholder="开始日期" end-placeholder="结束日期"
 25 |             range-separator="至" clearable></el-date-picker>
 26 |         </el-form-item>
 27 |         <el-form-item label="评分" prop="field106">
 28 |           <el-rate v-model="formData.field106" allow-half show-score :disabled='true'></el-rate>
 29 |         </el-form-item>
 30 |         <el-row gutter="15">
 31 |         </el-row>
 32 |         <el-form-item label="多选框组" prop="field104">
 33 |           <el-checkbox-group v-model="formData.field104" size="medium">
 34 |             <el-checkbox v-for="(item, index) in field104Options" :key="index" :label="item.value"
 35 |               :disabled="item.disabled">{{item.label}}</el-checkbox>
 36 |           </el-checkbox-group>
 37 |         </el-form-item>
 38 |         <el-form-item label="下拉选择" prop="field108">
 39 |           <el-select v-model="formData.field108" placeholder="请选择下拉选择" clearable :style="{width: '100%'}">
 40 |             <el-option v-for="(item, index) in field108Options" :key="index" :label="item.label"
 41 |               :value="item.value" :disabled="item.disabled"></el-option>
 42 |           </el-select>
 43 |         </el-form-item>
 44 |       </el-form>
 45 |       <div slot="footer">
 46 |         <el-button @click="close">取消</el-button>
 47 |         <el-button type="primary" @click="handleConfirm">确定</el-button>
 48 |       </div>
 49 |     </el-dialog>
 50 |   </div>
 51 | </template>
 52 | <script>
 53 | export default {
 54 |   inheritAttrs: false,
 55 |   components: {},
 56 |   props: [],
 57 |   data() {
 58 |     return {
 59 |       formData: {
 60 |         mobile: '',
 61 |         field101: undefined,
 62 |         field102: undefined,
 63 |         field103: undefined,
 64 |         field105: null,
 65 |         field106: 0,
 66 |         field104: [],
 67 |         field108: undefined,
 68 |       },
 69 |       rules: {
 70 |         mobile: [{
 71 |           required: true,
 72 |           message: '请输入手机号',
 73 |           trigger: 'blur'
 74 |         }, {
 75 |           pattern: /^1(3|4|5|7|8|9)\d{9}$/,
 76 |           message: '手机号格式错误',
 77 |           trigger: 'blur'
 78 |         }],
 79 |         field101: [{
 80 |           required: true,
 81 |           message: '请输入多行文本',
 82 |           trigger: 'blur'
 83 |         }],
 84 |         field102: [{
 85 |           required: true,
 86 |           message: '计数器',
 87 |           trigger: 'blur'
 88 |         }],
 89 |         field103: [{
 90 |           required: true,
 91 |           message: '请选择下拉选择',
 92 |           trigger: 'change'
 93 |         }],
 94 |         field105: [{
 95 |           required: true,
 96 |           message: '日期范围不能为空',
 97 |           trigger: 'change'
 98 |         }],
 99 |         field106: [{
100 |           required: true,
101 |           message: '评分不能为空',
102 |           trigger: 'change'
103 |         }],
104 |         field104: [{
105 |           required: true,
106 |           type: 'array',
107 |           message: '请至少选择一个field104',
108 |           trigger: 'change'
109 |         }],
110 |         field108: [{
111 |           required: true,
112 |           message: '请选择下拉选择',
113 |           trigger: 'change'
114 |         }],
115 |       },
116 |       field103Options: [{
117 |         "label": "选项一",
118 |         "value": 1
119 |       }, {
120 |         "label": "选项二",
121 |         "value": 2
122 |       }],
123 |       field104Options: [{
124 |         "label": "选项一",
125 |         "value": 1
126 |       }, {
127 |         "label": "选项二",
128 |         "value": 2
129 |       }],
130 |       field108Options: [{
131 |         "label": "选项一",
132 |         "value": 1
133 |       }, {
134 |         "label": "选项二",
135 |         "value": 2
136 |       }],
137 |     }
138 |   },
139 |   computed: {},
140 |   watch: {},
141 |   created() {},
142 |   mounted() {},
143 |   methods: {
144 |     onOpen() {},
145 |     onClose() {
146 |       this.$refs['elForm'].resetFields()
147 |     },
148 |     close() {
149 |       this.$emit('update:visible', false)
150 |     },
151 |     handleConfirm() {
152 |       this.$refs['elForm'].validate(valid => {
153 |         if (!valid) return
154 |         this.close()
155 |       })
156 |     },
157 |   }
158 | }
159 | 
160 | </script>
161 | <style>
162 | .el-rate {
163 |   display: inline-block;
164 |   vertical-align: text-top;
165 | }
166 | 
167 | </style>
168 | 


--------------------------------------------------------------------------------
/src/directive/permission/index.js:
--------------------------------------------------------------------------------
 1 | import permission from './permission'
 2 | 
 3 | const install = function(Vue) {
 4 |   Vue.directive('permission', permission)
 5 | }
 6 | 
 7 | if (window.Vue) {
 8 |   window['permission'] = permission 
 9 |   Vue.use(install); // eslint-disable-line
10 | }
11 | 
12 | permission.install = install 
13 | export default permission
14 | 
15 | 
16 | 


--------------------------------------------------------------------------------
/src/directive/permission/permission.js:
--------------------------------------------------------------------------------
 1 | 
 2 | import store from '@/store'
 3 | 
 4 | export default{
 5 |   // inserted函数:当被绑定的元素插入到 DOM 中时……
 6 |   inserted(el, binding, vnode) {
 7 |     const { value } = binding  // 获取指令绑定的值;
 8 |     const roles = store.getters && store.getters.roles //用户本身的roles信息,arr;
 9 | 
10 |     if (value && value instanceof Array && value.length > 0) {
11 |       const permissionRoles = value
12 | 
13 |       const hasPermission = roles.some(role => { // 只要有一个满足即返回true
14 |         return permissionRoles.includes(role)
15 |       })
16 |       // 没有该指令,直接删除掉该指令元素;即页面不显示没有指令权限的按钮;
17 |       if (!hasPermission) {
18 |         el.parentNode && el.parentNode.removeChild(el)
19 |         // 因项目需要,本指令remove其父元素;一般情况下,只隐藏其本身;
20 |       }
21 |     } else {
22 |       throw new Error(`need roles! Like v-permission="['admin','editor']"`)
23 |     }
24 |   }
25 | }
26 | 
27 | 


--------------------------------------------------------------------------------
/src/lang/en.js:
--------------------------------------------------------------------------------
 1 | const zh = {
 2 |     // layout
 3 |     commons: {
 4 |       xiaoai: 'Ai.',
 5 |       admin: 'Admin',
 6 |       editor: 'Editor',
 7 |       quit: 'Sign Out',
 8 |       hi: 'Hi',
 9 |       index: 'Dashboard',
10 |       userManage: 'Users',
11 |       share: 'Share',
12 |       infoManage: 'Infos',
13 |       infoShow: 'InfoShow',
14 |       infoShow1: 'InfoShow1',
15 |       infoShow2: 'InfoShow2',
16 |       infoShow3: 'InfoShow3',
17 |       infoShow4: 'InfoShow4',
18 |       infoShow5: 'InfoShow5',
19 |       infoModify: 'InfoModify',
20 |       infoModify1:'InfoModify1',
21 |       infoModify2:'InfoModify2',
22 |       infoModify3:'InfoModify3',
23 |       fundManage: 'Money',
24 |       fundList: 'MoneyList',
25 |       chinaTabsList: 'AreaList',
26 |       fundData: 'FundData',
27 |       fundPosition: 'FundPosition',
28 |       typePosition: 'TypePosition',
29 |       incomePayPosition: 'IncomePayPosition',
30 |       permission: 'Permission',
31 |       pagePer: 'PagePermission',
32 |       directivePer: 'DirectivePermission',
33 |       errorPage: 'ErrorPage',
34 |       page401:'401',
35 |       page404:'404',
36 |       wechatNumber: 'wechat'
37 |     },
38 |     index:{
39 |       yearLoss:'Year Loss',
40 |       yearProfit:'Year Profit',
41 |       potentialInvestor:'Potential Investor',
42 |       intentionInvestor:'Intention Investor',
43 |       waitExamineInvestor:'Wait Examine Investor',
44 |       examiningInvestor:'Examining Investor',
45 |       tenMillion:'Ten Million',
46 |       person:'P'
47 |     }
48 |   }
49 |   
50 | export default zh;


--------------------------------------------------------------------------------
/src/lang/index.js:
--------------------------------------------------------------------------------
 1 | 
 2 | // 引入i18n国际化插件
 3 | import { getToken} from '@/utils/auth'
 4 | import Vue from 'vue'
 5 | import VueI18n from 'vue-i18n'
 6 | process.env.NODE_ENV === "development" ? Vue.use(VueI18n) : null;
 7 | 
 8 | import enLocale from './en'
 9 | import zhLocale from './zh'
10 |  
11 | // 注册i18n实例并引入语言文件,文件格式等下解析
12 | const i18n = new VueI18n({
13 |   locale: getToken('lang') || 'en',
14 |   messages: {
15 |     zh: {
16 |       ...zhLocale
17 |     },
18 |     en: {
19 |       ...enLocale
20 |     },
21 |   }
22 | });
23 | 
24 | export default i18n;


--------------------------------------------------------------------------------
/src/lang/zh.js:
--------------------------------------------------------------------------------
 1 | const zh = {
 2 |     // layout
 3 |     commons: {
 4 |       xiaoai: '小爱',
 5 |       admin: '管理员',
 6 |       editor: '赵晓编',
 7 |       quit: '退出',
 8 |       hi: '您好',
 9 |       index: '首页',
10 |       userManage: '用户管理',
11 |       share: '分享功能',
12 |       infoManage: '信息管理',
13 |       infoShow: '个人信息',
14 |       infoShow1: '个人信息子菜单1',
15 |       infoShow2: '个人信息子菜单2',
16 |       infoShow3: '个人信息子菜单3',
17 |       infoShow4: '个人信息子菜单4',
18 |       infoShow5: '个人信息子菜单5',
19 |       infoModify: '修改信息',
20 |       infoModify1:'修改信息子菜单1',
21 |       infoModify2:'修改信息子菜单2',
22 |       infoModify3:'修改信息子菜单3',
23 |       fundManage: '资金管理',
24 |       fundList: '资金流水',
25 |       chinaTabsList: '区域投资',
26 |       fundData: '资金数据',
27 |       fundPosition: '投资分布',
28 |       typePosition: '项目分布',
29 |       incomePayPosition: '收支分布',
30 |       permission: '权限设置',
31 |       pagePer: '页面权限',
32 |       directivePer: '按钮权限',
33 |       errorPage: '错误页面',
34 |       page401:'401',
35 |       page404:'404',
36 |       wechatNumber: '微信号'
37 |     },
38 |     index:{
39 |       yearLoss:'年度总盈亏',
40 |       yearProfit:'年度收益率',
41 |       potentialInvestor:'潜在投资人',
42 |       intentionInvestor:'意向投资人',
43 |       waitExamineInvestor:'待审投资人',
44 |       examiningInvestor:'审核中投资人',
45 |       tenMillion:'千万元',
46 |       person:'人'
47 |     }
48 |   }
49 |   
50 | export default zh;


--------------------------------------------------------------------------------
/src/layout/bread.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 | 	 <div class='bread_container' id="bread_container">
 3 | 	    <span @click="handleLefeMenu" class="bars"> 
 4 | 			<icon-svg icon-class="iconmenu-fold" :class="{isactive:changeBarDirection}" />
 5 | 		</span>
 6 |         <el-breadcrumb class="breadcrumb" separator="/">
 7 |             <el-breadcrumb-item 
 8 |                 v-for='(name,index) in matchedArr'
 9 | 				:key='index'
10 | 				>
11 | 				{{ $t(`commons.${name}`)}}
12 | 			</el-breadcrumb-item>
13 |         </el-breadcrumb>
14 |     </div>
15 | </template>
16 | 
17 | 
18 | <script>
19 | 
20 | export default {
21 | 	data(){
22 | 		return {
23 | 			changeBarDirection:false,
24 | 		}
25 | 	},
26 | 	created() {
27 | 	},
28 | 	computed:{
29 | 		matchedArr(){
30 | 			let temp = [],temps = [];
31 | 			this.$route.matched.filter((item,index,self) => {
32 | 				// if(item.meta.title){
33 | 				// 	const title = item.meta.title;
34 | 				//     temp.push(title);
35 | 				// }
36 | 				if(item.name){
37 | 					const name = item.name;
38 | 				    temp.push(name);
39 | 				}
40 | 			});
41 | 			temp.filter((item,index,self) => {
42 | 				if(!temps.includes(item)){
43 | 					temps.push(item);
44 | 				}
45 | 			})
46 | 			return temps;
47 | 		}
48 | 	},
49 | 	mounted(){
50 | 	},
51 | 	methods:{
52 | 		handleLefeMenu(){
53 | 		    this.$store.dispatch('setLeftCollapse');  // 折叠菜单
54 | 			this.$store.dispatch('handleLeftMenu');  // 改变菜单宽度 180->35/35-180
55 | 			this.changeBarDirection = !this.changeBarDirection;
56 | 		}
57 | 	},
58 | 	watch: {
59 |      
60 |     }
61 | }
62 | 
63 | 
64 | 
65 | </script>
66 | 
67 | <style lang="less">
68 | 	.bread_container{
69 | 		background-color: #eaebec;
70 | 		width: 100%;
71 | 		.bars{
72 | 			float: left;
73 |             margin: 4px 10px;
74 | 			cursor: pointer;
75 | 			.isactive{
76 | 				-webkit-transform: rotate(90deg);
77 | 				transform: rotate(90deg);
78 | 				transition: .38s;
79 | 				-webkit-transform-origin: 50% 50%;
80 | 				transform-origin: 50% 50%;
81 | 			}
82 | 		}
83 | 		.breadcrumb{
84 | 			height: 30px;
85 | 			line-height: 30px;
86 | 			.breadbutton{
87 | 				float:left;
88 | 				margin:4px 5px 0 0;
89 | 				
90 | 			}
91 | 		}
92 | 	}
93 | </style>
94 | 
95 | 
96 | 


--------------------------------------------------------------------------------
/src/layout/content.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="">
 3 |         <transition name="fade">
 4 |             <router-view></router-view>
 5 |         </transition>
 6 |     </div>
 7 | </template>
 8 | <script>
 9 |     export default {
10 |         name: '',
11 |         data () {
12 |             return {
13 |                 
14 |             }
15 |         },
16 |         methods:{
17 |             
18 |         },
19 |         created(){
20 |             
21 |         },
22 |         mounted(){
23 | 
24 |         }
25 |     }
26 | </script>
27 | 
28 | <style scoped lang='less'>
29 |     .fade-enter-active,
30 |     .fade-leave-active {
31 |         transition: opacity .3s
32 |     }
33 |     
34 |     .fade-enter,
35 |     .fade-leave-active {
36 |         opacity: 0
37 |     }
38 | </style>
39 | 


--------------------------------------------------------------------------------
/src/layout/footerNav.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 | 	 <div class='footer'>
 3 |         <p class="intro rflex">
 4 |             <span>{{ $t('commons.xiaoai') }}Admin</span>
 5 |             <a :href='github' target="_blank">
 6 |                <icon-svg icon-class="iconGithub" />
 7 |             </a>
 8 |             <span>wdlhao2013({{ $t('commons.wechatNumber') }})</span>
 9 |         </p>
10 |         <p class="beian">鄂ICP备18001612号</p>
11 |     </div>
12 | </template>
13 | 
14 | 
15 | <script>
16 |     import { github } from "@/utils/env";
17 | 
18 | export default {
19 |     name: "footerNav",
20 | 	data(){
21 | 		return {
22 |             github:github
23 | 		}
24 | 	},
25 | 	methods:{
26 | 
27 | 	}
28 | }
29 | </script>
30 | 
31 | <style lang="less">
32 | 	.footer{
33 |         width: 100%;
34 |         padding: 10px 0;
35 |         font-size:12px;
36 |         text-align: center;
37 |         background: #fff;
38 |         p{
39 |             line-height: 30px;
40 |         }
41 |         .intro{
42 |             width: 240px;
43 |             margin: 0 auto;
44 |             justify-content: space-between;
45 |             align-items: center;
46 |         }
47 |     }
48 | </style>
49 | 
50 | 
51 | 


--------------------------------------------------------------------------------
/src/layout/home.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="home rflex">
 3 |         <left-menu></left-menu>
 4 |         <div class="menu_right wflex el-scrollbar" ref="menu_right" :style="{left:sidebar.width+'px'}">
 5 |             <head-nav></head-nav>
 6 |             <div class="menu_content" ref="menu_content">
 7 |                 <bread></bread>
 8 |                 <router-view></router-view><!--页面渲染入口-->
 9 |             </div>
10 |             <footerNav></footerNav>
11 |             <backTop :ele="$refs.menu_right"></backTop>
12 |         </div>
13 |     </div>
14 | </template>
15 | <script>
16 |     import { mapState, mapGetters } from 'vuex'
17 | 
18 |     import HeadNav from './headNav.vue';
19 | 	import LeftMenu from './leftMenu.vue';
20 | 	import Bread from './bread.vue';
21 | 	import FooterNav from './footerNav.vue';
22 | 	import backTop from 'cps/backTop';
23 | 
24 |     export default {
25 |         name: 'home',
26 |         data(){
27 |             return {
28 |             }
29 |         },
30 |         computed:{
31 |             ...mapGetters(['sidebar']),
32 |         },
33 |         components:{
34 |             HeadNav,
35 |             LeftMenu,
36 |             Bread,
37 |             FooterNav,
38 |             backTop
39 |         },
40 |         created() {
41 |         },
42 |         mounted (){
43 |         },
44 |         watch:{
45 |           
46 |         }
47 |     }
48 | </script>
49 | <style scoped lang='less'>
50 |     .home{
51 |         .menu_right{
52 |             overflow: auto;
53 |             position: absolute;
54 |             right:0;
55 |             top:0;
56 |             bottom:0;
57 |             background:#F6F7FC;
58 |             .menu_content{
59 |                 position: relative;
60 |                 margin-top:60px;
61 |                 width:100%;
62 |                 background:#f0f2f5;
63 |             }
64 | 
65 |         }
66 |     }
67 | </style>
68 | 


--------------------------------------------------------------------------------
/src/layout/index.js:
--------------------------------------------------------------------------------
 1 | import bread from './bread.vue'
 2 | import headNav from './headNav.vue'
 3 | import leftMenu from './leftMenu.vue'
 4 | import Layout from './home.vue'
 5 | import Content from './content.vue'
 6 | import footerNav from './footerNav.vue'
 7 | import topMenu from './topMenu.vue'
 8 | 
 9 | export {
10 |     Layout,
11 |     Content,
12 |     bread,
13 |     headNav,
14 |     leftMenu,
15 |     footerNav,
16 |     topMenu
17 | }
18 | 


--------------------------------------------------------------------------------
/src/layout/leftMenu.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |    <div class="menu_left cflex" :style="{width:sidebar.width+'px'}">
  3 |         <div class="menu_page_top rflex">
  4 |             <img :class='["logo",{"closeLogo":!sidebar.opened}]' :src="logo" alt="小爱admin" >
  5 |             <span class='title' v-show="sidebar.opened">{{$t('commons.xiaoai')}}<i>Admin</i></span>
  6 |         </div>
  7 |         <div class="menu_page_bottom is-scroll-left">
  8 |             <el-menu 
  9 |                 mode="vertical"
 10 |                 theme="dark" 
 11 |                 :show-timeout="200"
 12 |                 :default-active="$route.path" 
 13 |                 :collapse="isCollapse"
 14 |                 :background-color="menuObj.bgColor"
 15 |                 :text-color="menuObj.textColor"
 16 |                 :active-text-color="menuObj.activeTextColor"
 17 |                 :style="{width:sidebar.width+'px'}"
 18 |                 >
 19 |                     <template v-for="(item,index) in permission_routers">
 20 |                         <!--表示 有一级菜单-->
 21 |                         <router-link v-if="!item.hidden && item.noDropdown" :to="item.path+'/'+item.children[0].path" :key="index">
 22 |                             <el-menu-item class="dropItem" 
 23 |                                 :index="item.path+'/'+item.children[0].path"
 24 |                                 >
 25 |                                 <icon-svg v-if="item.meta.icon" :icon-class="item.meta.icon" />
 26 |                                 <span v-if="item.meta.title" slot="title">{{ $t(`commons.${item.name}`)}}</span> 
 27 |                             </el-menu-item>
 28 |                         </router-link>
 29 | 
 30 |                         <!--表示 有二级或者多级菜单 -->
 31 |                         <el-submenu v-if="item.children  && item.children.length >= 1 && !item.hidden && !item.noDropdown"  :index="item.path" :key="index">
 32 |                             <template slot="title">
 33 |                                 <icon-svg v-if="item.meta.icon" :icon-class="item.meta.icon" />
 34 |                                 <span v-if="item.meta.title" slot="title">{{ $t(`commons.${item.name}`)}}</span>
 35 |                             </template>
 36 |                             <!--直接定位到子路由上,子路由也可以实现导航功能-->
 37 |                             <router-link v-for="(citem,cindex) in item.children" :to="getIindex(citem,item,cindex)"  :key="cindex">
 38 |                                 <el-menu-item 
 39 |                                     v-if="citem.meta.routerType != 'topmenu' && citem.meta.title"
 40 |                                     :index="getIindex(citem,item,cindex)">
 41 |                                     <span slot="title"> {{ $t(`commons.${citem.name}`)}}</span>
 42 |                                 </el-menu-item> 
 43 |                             </router-link>
 44 |                         </el-submenu>
 45 |                     </template>
 46 |             </el-menu>
 47 |         </div>
 48 |     </div>
 49 | </template>
 50 | 
 51 | <script>
 52 | import { mapGetters } from 'vuex'
 53 | import * as mUtils from "@/utils/mUtils";
 54 | import logoImg from "@/assets/img/logo.png";
 55 | 
 56 | 
 57 | export default {
 58 |   name: "left-Menu",
 59 |   data() {
 60 |     return {
 61 |        menuObj:{
 62 |          bgColor:'#fff',
 63 |          textColor:'#666',
 64 |          activeTextColor:'#ff6428',
 65 |        },
 66 |        logo:logoImg
 67 |     };
 68 |   },
 69 |   computed:{
 70 |       ...mapGetters([
 71 |         'permission_routers',
 72 |         'isCollapse',
 73 |         'sidebar',
 74 |         'menuIndex'
 75 |       ]),
 76 |   },
 77 |   created(){
 78 |   },
 79 |   mounted(){
 80 |   },
 81 |   methods: {
 82 |     getIindex(citem,item,cindex){
 83 |       return (citem.meta.titleList)?item.path+'/'+citem.path+'/'+citem.meta.titleList[0].path:item.path+'/'+citem.path;
 84 |     }
 85 |   }
 86 | };
 87 | </script>
 88 | 
 89 | 
 90 | <style lang="less" scoped>
 91 |   @left-bgColor:#fff;  // 左侧菜单背景颜色;
 92 |   @icon-link:#FF6C60;
 93 |     .menu_left{
 94 |         position: absolute;
 95 |         top:0;
 96 |         left:0;
 97 |         bottom:0;
 98 |     }
 99 |     .menu_page_top{
100 |         width:100%;
101 |         height: 60px;
102 |         align-items: center;
103 |         justify-content: space-around;
104 |         text-transform: uppercase;
105 |         box-sizing: border-box;
106 |         box-shadow:0px 2px 5px 0px rgba(230,224,224,0.5);
107 |         .logo {
108 |             height: 36px;
109 |             width: 36px;
110 |             vertical-align: middle;
111 |             display: inline-block;
112 |         }
113 |         .closeLogo{
114 |             width:30px;
115 |             height:30px;
116 |         }
117 |         .title{
118 |             font-size: 22px;
119 |             i{
120 |                 color:#FF6C60;
121 |             }
122 |         }
123 |     }
124 |   .menu_page_bottom {
125 |       width:100%;
126 |       overflow-y: scroll;
127 |       overflow-x: hidden;
128 |       flex:1;
129 |       margin-top:3px;
130 |       z-index: 10;
131 |       box-shadow: 0 0 10px 0 rgba(230, 224, 224, 0.5)
132 |   }
133 | </style>
134 | 


--------------------------------------------------------------------------------
/src/layout/topMenu.vue:
--------------------------------------------------------------------------------
 1 | 
 2 | <template>
 3 |     <div class="menu_top wflex rflex" ref="menuTop">
 4 |         <el-menu 
 5 |             mode="horizontal" 
 6 |             class="el-menu-demo rflex el-scrollbar2 top-scrollbar2" 
 7 |             :background-color="menuObj.bgColor"
 8 |             :text-color="menuObj.textColor"
 9 |             :active-text-color="menuObj.activeTextColor"
10 |             :default-active="$route.path" 
11 |             >
12 |             <template v-for="(item,index) in topRouters">
13 |                 <router-link :to="$route.matched[1].path+'/'+item.path" :key="index">
14 |                     <el-menu-item :index="$route.matched[1].path+'/'+item.path">
15 |                       {{ $t(`commons.${item.path}`) }}
16 |                     </el-menu-item>
17 |                 </router-link>
18 |             </template>
19 |         </el-menu>
20 |     </div>
21 | </template>
22 | 
23 | <script>
24 |     import { mapGetters } from 'vuex'
25 |   
26 |     export default {
27 |         name:'top-menu',
28 |         data(){
29 |             return {
30 |                 menuObj:{
31 |                     bgColor:'',
32 |                     textColor:'#303133',
33 |                     activeTextColor:'#ff6428',
34 |                 },
35 |             }
36 |         },
37 |         computed:{
38 |             ...mapGetters(['topRouters'])
39 |         },
40 |         created(){
41 |            this.setLeftInnerMenu();  // 针对刷新页面时,也需要加载顶部菜单
42 |         },
43 |         mounted(){
44 |         },
45 |         methods:{
46 |             setLeftInnerMenu(){
47 |                 const titleList = this.$route.matched[1].meta.titleList;
48 |                 const currentTitle = titleList && this.$route.matched[2].meta.title;
49 |                 if( titleList && this.$route.matched[1].meta.routerType === 'leftmenu'){ // 点击的为 左侧的2级菜单
50 |                     this.$store.dispatch('ClickLeftInnerMenu',{'titleList':titleList});
51 |                     this.$store.dispatch('ClickTopMenu',{'title':currentTitle});
52 |                 }else{ // 点击左侧1级菜单
53 |                     this.$store.dispatch('ClickLeftInnerMenu',{'titleList':[]});
54 |                     this.$store.dispatch('ClickTopMenu',{'title':''});
55 |                 }
56 |             },
57 |             getPath(){
58 |                this.setLeftInnerMenu();
59 |             },
60 |         },
61 |         watch:{
62 |             "$route":"getPath" 
63 |         }
64 |     }
65 | 
66 | </script>
67 | 
68 | <style lang="less" scoped>
69 |     .menu_top{
70 |         // width:calc(100% - 350px);
71 |         .el-menu-demo{
72 |             overflow-y:hidden;
73 |             flex:1;
74 |         }
75 |         .el-menu-item:focus, .el-menu-item:hover {
76 |             outline: 0;
77 |             background-color: #ceeda8;
78 |         }
79 |         .router-link-active{
80 |           
81 |         }
82 |     }
83 | </style>


--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
 1 | import Vue from 'vue'
 2 | import App from './App'
 3 | import router from './router'
 4 | import store from './store/'
 5 | // 'development',use package;'production':use cdn;
 6 | import ElementUI from 'element-ui'
 7 | Vue.use(ElementUI, { size: 'mini'});
 8 | import('element-ui/lib/theme-chalk/index.css')
 9 | 
10 | import './components/iconSvg' 
11 | 
12 | import '@/permission' // permission control
13 | 
14 | import '@/mockjs'; //  mock数据
15 | 
16 | // i18n国际化
17 | import i18n from "@/lang";
18 | 
19 | // 分享功能集合
20 | import { shareConfig } from './utils/share';
21 | Vue.prototype.shareConfig = shareConfig;
22 | 
23 | 
24 | Vue.config.productionTip = false;
25 | 
26 | // 字体图标线上地址,在index中使用cdn载入;
27 | //at.alicdn.com/t/font_1258069_e40c6mwl0x8.js
28 | 
29 | new Vue({
30 |   router,
31 |   store,
32 |   i18n,  // 便于可以直接在组件中通过this.$i18n使用,也可以按需引用
33 |   render: h => h(App),
34 | }).$mount('#app')
35 | 


--------------------------------------------------------------------------------
/src/mockjs/index.js:
--------------------------------------------------------------------------------
 1 | // import Vue from 'vue'
 2 | import Mock from 'mockjs'
 3 | // process.env.NODE_ENV === "development" ? Vue.use(Mock) : null;
 4 | 
 5 | import tableAPI from './money'
 6 | import salesAPI from './sales'
 7 | import userAPI from './user'
 8 | 
 9 | // 设置全局延时 没有延时的话有时候会检测不到数据变化 建议保留
10 | Mock.setup({
11 |     timeout: '300-600'
12 | })
13 | 
14 | // 资金相关
15 | Mock.mock(/\/money\/get/, 'get', tableAPI.getMoneyList)
16 | Mock.mock(/\/money\/remove/, 'get', tableAPI.deleteMoney)
17 | Mock.mock(/\/money\/batchremove/, 'get', tableAPI.batchremoveMoney)
18 | Mock.mock(/\/money\/add/, 'get', tableAPI.createMoney)
19 | Mock.mock(/\/money\/edit/, 'get', tableAPI.updateMoney)
20 | // sales相关
21 | Mock.mock(/\/sales\/get/, 'get', salesAPI.getSalesList)
22 | // user相关
23 | Mock.mock(/\/user\/login/, 'get', userAPI.login)
24 | Mock.mock(/\/user\/logout/, 'get', userAPI.logout)
25 | Mock.mock(/\/user\/info\/get/, 'get', userAPI.getUserInfo)
26 | Mock.mock(/\/user\/list\/get/, 'get', userAPI.getUserList)
27 | 
28 | export default Mock;


--------------------------------------------------------------------------------
/src/mockjs/money.js:
--------------------------------------------------------------------------------
  1 | import Mock from 'mockjs'
  2 | 
  3 | import * as mUtils from '@/utils/mUtils'
  4 | 
  5 | 
  6 | let List = []
  7 | const count = 60
  8 | let typelist = [1, 2, 3, 4, 5, 6, 7, 8]
  9 | 
 10 | for (let i = 0; i < count; i++) {
 11 |   List.push(Mock.mock({
 12 |     id: Mock.Random.guid(),
 13 |     username: Mock.Random.cname(),
 14 |     address: Mock.mock('@county(true)'),
 15 |     createTime: Mock.Random.datetime(),
 16 |     income: Mock.Random.integer(0, 9999),
 17 |     pay: Mock.Random.integer(0, 9999), 
 18 |     accoutCash: Mock.Random.integer(0, 9999),
 19 |     'incomePayType|1': typelist
 20 |   }))
 21 | }
 22 | 
 23 | export default {
 24 |   /**
 25 |    * 获取列表
 26 |    * 要带参数 name, page, limt; name可以不填, page,limit有默认值。
 27 |    * @param name, page, limit
 28 |    * @return {{code: number, count: number, data: *[]}}
 29 |    */
 30 |   getMoneyList: config => {
 31 |     const { name, page = 1, limit = 20 } = mUtils.param2Obj(config.url)
 32 |     const mockList = List.filter(user => {
 33 |       if (name && user.username.indexOf(name) === -1) return false
 34 |       return true
 35 |     })
 36 |     const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
 37 |     return {
 38 |       code: 200,
 39 |       data: {
 40 |         total: mockList.length,
 41 |         moneyList: pageList
 42 |       }
 43 |     }
 44 |   },
 45 |   /**
 46 |    * 增加资金信息
 47 |    * @param username, address, createTime, income, pay , accoutCash, incomePayType
 48 |    * @return {{code: number, data: {message: string}}}
 49 |    */
 50 |   createMoney: config => {
 51 |     const { username, address, income, pay , accoutCash, incomePayType,tableAddress } = mUtils.param2Obj(config.url)
 52 |     List.unshift({
 53 |       id: Mock.Random.guid(),
 54 |       username: username,
 55 |       address: address,
 56 |       tableAddress:tableAddress,
 57 |       createTime: Mock.Random.now(),
 58 |       income: income,
 59 |       pay: pay,
 60 |       accoutCash: accoutCash,
 61 |       incomePayType: incomePayType
 62 |     })
 63 |     return {
 64 |       code: 200,
 65 |       data: {
 66 |         message: '添加成功'
 67 |       }
 68 |     }
 69 |   },
 70 |   /**
 71 |    * 删除用户
 72 |    * @param id
 73 |    * @return {*}
 74 |    */
 75 |   deleteMoney: config => {
 76 |     const { id } = mUtils.param2Obj(config.url)
 77 |     if (!id) {
 78 |       return {
 79 |         code: -999,
 80 |         message: '参数不正确'
 81 |       }
 82 |     } else {
 83 |       List = List.filter(u => u.id !== id)
 84 |       return {
 85 |         code: 200,
 86 |         data: {
 87 |           message: '删除成功'
 88 |         }
 89 |       }
 90 |     }
 91 |   },
 92 |   /**
 93 |    * 批量删除
 94 |    * @param config
 95 |    * @return {{code: number, data: {message: string}}}
 96 |    */
 97 |   batchremoveMoney: config => {
 98 |     console.log(config);
 99 |     // console.log(mUtils.param2Obj(config.url));
100 |     let { ids } = mUtils.param2Obj(config.url)
101 |     console.log(ids);
102 |     ids = ids.split(',')
103 |     List = List.filter(u => !ids.includes(u.id))
104 |     return {
105 |       code: 200,
106 |       data: {
107 |         message: '批量删除成功'
108 |       }
109 |     }
110 |   },
111 |   /**
112 |    * 修改用户
113 |    * @param id, name, addr, age, birth, sex
114 |    * @return {{code: number, data: {message: string}}}
115 |    */
116 |   updateMoney: config => {
117 |     const { id,username, address, income, pay , accoutCash, incomePayType } = mUtils.param2Obj(config.url)
118 |     List.some(u => {
119 |       if (u.id === id) {
120 |         u.username = username
121 |         u.address = address
122 |         u.income = income
123 |         u.pay = pay
124 |         u.accoutCash = accoutCash
125 |         u.incomePayType = incomePayType
126 |         return true
127 |       }
128 |     })
129 |     return {
130 |       code: 200,
131 |       data: {
132 |         message: '编辑成功'
133 |       }
134 |     }
135 |   }
136 | }


--------------------------------------------------------------------------------
/src/mockjs/sales.js:
--------------------------------------------------------------------------------
 1 | import Mock from 'mockjs'
 2 | 
 3 | import * as mUtils from '@/utils/mUtils'
 4 | 
 5 | 
 6 | let List = []
 7 | const count = 7
 8 | 
 9 | 
10 | for (let i = 0; i < count; i++) {
11 |   List.push(Mock.mock({
12 |     id: Mock.Random.guid(),
13 |     userImg:Mock.Random.image(),
14 |     username: Mock.Random.name(),
15 |     date: Mock.Random.datetime(),
16 |     price: Mock.Random.float(0, 9999, 2,2),
17 |     status:Mock.Random.natural( 1,4 ),
18 |     commentContent:Mock.Random.paragraph()
19 |   }))
20 | }
21 | 
22 | export default {
23 |   /**
24 |    * 获取sales列表数据
25 |    * 要带参数 name, page, limt; name可以不填, page,limit有默认值。
26 |    * @param name, page, limit
27 |    * @return {{code: number, count: number, data: *[]}}
28 |    */
29 |   getSalesList: config => {
30 |     const { name, page = 1, limit = 7 } = mUtils.param2Obj(config.url)
31 |     const mockList = List.filter(user => {
32 |       if (name && user.username.indexOf(name) === -1) return false
33 |       return true
34 |     })
35 |     const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
36 |     return {
37 |       code: 200,
38 |       data: {
39 |         total: mockList.length,
40 |         list: pageList
41 |       }
42 |     }
43 |   }
44 | }


--------------------------------------------------------------------------------
/src/mockjs/user.js:
--------------------------------------------------------------------------------
  1 | import Mock from 'mockjs'
  2 | import * as mUtils from '@/utils/mUtils'
  3 | 
  4 | 
  5 | let List = []
  6 | const count = 1000
  7 | let typelist = ['联通', '移动', '电信', '铁通']
  8 | 
  9 | for (let i = 0; i < count; i++) {
 10 |   List.push(Mock.mock({
 11 |     id: Mock.Random.guid(),
 12 |     sortnum: i + 1,
 13 |     username: Mock.Random.cname(),
 14 |     address: Mock.mock('@county(true)'),
 15 |     createTime: Mock.Random.datetime(),
 16 |     updateTime: Mock.Random.now(),
 17 |     ip:Mock.mock('@ip'),
 18 |     region:Mock.mock('@region'),
 19 |     areaId:/\d{7}/,
 20 |     email: Mock.Random.email(),
 21 |     'isp|1': typelist
 22 |   }))
 23 | }
 24 | 
 25 | export default {
 26 |   // 用户登录
 27 |   login: config => {
 28 |     let data = JSON.parse(config.body);
 29 |     let userList = {};
 30 |     if(data.username === 'admin'){
 31 |         userList = {
 32 |             token:'admin',
 33 |             name:'管理员',
 34 |         }
 35 |     }else if(data.username === 'editor'){
 36 |         userList = {
 37 |             token:'editor',
 38 |             name:'赵晓编',
 39 |         }
 40 |     }else{
 41 |        return {
 42 |         code: -1,
 43 |         data: {
 44 |           msg: "用户名错误",
 45 |           status:'fail'
 46 |         }
 47 |        }
 48 |     }
 49 |     return {
 50 |       code: 200,
 51 |       data: {
 52 |         userList: userList
 53 |       }
 54 |     }
 55 |   },
 56 |    // 用户登出
 57 |    logout: config => {
 58 |     return {
 59 |       code: 200,
 60 |       data: {
 61 |         userList: ""
 62 |       }
 63 |     }
 64 |   },
 65 |   // 获取登录用户信息
 66 |   getUserInfo:config => {
 67 |     let data = JSON.parse(config.body);
 68 |     let userList = {};
 69 |     if(data.token === 'admin'){
 70 |         userList = {
 71 |             roles: ['admin'],
 72 |             name:'admin',
 73 |             avatar:'https://wx.qlogo.cn/mmopen/vi_32/un2HbJJc6eiaviaibvMgiasFNlVDlNOb9E6WCpCrsO4wMMhHIbsvTkAbIehLwROVFlu8dLMcg00t3ZtOcgCCdcxlZA/132'
 74 |         }
 75 |     }else if(data.token === 'editor'){
 76 |         userList = {
 77 |             roles: ['editor'],
 78 |             name:'editor',
 79 |             avatar:'https://mirror-gold-cdn.xitu.io/168e088859e325b9d85?imageView2/1/w/100/h/100/q/85/format/webp/interlace/1'
 80 |         }
 81 |     }else{
 82 |         userList = {}
 83 |     }
 84 |     return {
 85 |       code: 200,
 86 |       data: {
 87 |         userList: userList
 88 |       }
 89 |     }
 90 |   },
 91 |    /**
 92 |    * 获取用户列表
 93 |    * 要带参数 name, page, limt; name可以不填, page,limit有默认值。
 94 |    * @param name, page, limit
 95 |    * @return {{code: number, count: number, data: *[]}}
 96 |    */
 97 |   getUserList:config => {
 98 |     const { limit , page } = JSON.parse(config.body);
 99 |     let mockList = List;
100 |     const userList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
101 |     return {
102 |       code: 200,
103 |       data: {
104 |         total: mockList.length,
105 |         userList: userList
106 |       }
107 |     }
108 |   }
109 | 
110 |   
111 | }


--------------------------------------------------------------------------------
/src/page/errorPage/401.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |   <div class="fillcontain" >
  3 |       <div class="contain" ref="contain">
  4 |         <div class="errPage-container cflex wflex">
  5 |             <img class="errorImg" :src="errorImg" />
  6 |             <p class="errorTitle">401</p>
  7 |             <p class="errorTro">抱歉,您没有权限访问该页面。</p>
  8 |             <router-link :to="{path: '/'}">
  9 |               <el-button size="mini" type="primary">返回首页</el-button>
 10 |             </router-link>
 11 |         </div>
 12 |       </div>
 13 |   </div>
 14 | </template>
 15 | 
 16 | <script>
 17 | import errorImg from "@/assets/img/401.png"
 18 | import * as mutils from '@/utils/mUtils'
 19 | 
 20 | export default {
 21 |     name: 'page401',
 22 |     data() {
 23 |       return {
 24 |         errorImg:errorImg
 25 |       }
 26 |     },
 27 |   	mounted(){
 28 |        mutils.setContentHeight(this,this.$refs.contain,250);
 29 | 
 30 | 		},
 31 |     methods: {
 32 |     }
 33 | }
 34 | </script>
 35 | 
 36 | <style lang="less" scoped>
 37 |     .contain{
 38 |       display: flex;
 39 |       justify-content: center;
 40 |       padding: 20px;
 41 |        .errPage-container {
 42 |           align-items: center;
 43 |           p{
 44 |             line-height: 30px;
 45 |           }
 46 |           .errorImg{
 47 |             width:286px;
 48 |             height: 325px;
 49 |             margin-bottom: 10px;
 50 |           }
 51 |           .errorTitle{
 52 |             color: rgba(0,0,0,.85);
 53 |             font-size: 24px;
 54 |           }
 55 |           .errorTro{
 56 |             color: rgba(0,0,0,.45);
 57 |            font-size: 14px;
 58 |           }
 59 |       a{
 60 |         text-decoration: underline;
 61 |       }
 62 |       .rows{
 63 |         height: 100%;
 64 |         .el-col{
 65 |           text-align: center;
 66 |           height: 100%;
 67 |         }
 68 |         .title{
 69 |           font-size: 170px;
 70 |           line-height: 1.2;
 71 |           color: #a9d86e;
 72 |         }
 73 |         .neirongItem{
 74 |           height: 100%;
 75 |           display: flex;
 76 |           justify-content: center;
 77 |           flex-direction: column;
 78 |           .tip{
 79 |             font-size: 30px;
 80 |             font-weight: bold;
 81 |             text-align: left;
 82 |           }
 83 |           .neirong{
 84 |             font-size: 20px;
 85 |             text-align: left;
 86 |           }
 87 |         }
 88 |       }
 89 |       .home{
 90 |         text-align: center;
 91 |       }
 92 |       .router-link-active:hover{
 93 |         color:#a9d86e;
 94 |         text-decoration: underline;
 95 |       }
 96 |     }
 97 |     }
 98 |    
 99 | </style>
100 | 


--------------------------------------------------------------------------------
/src/page/errorPage/404.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |   <div class="fillcontain">
  3 |       <div class="contain" ref="contain">
  4 |         <div class="errPage-container cflex wflex">
  5 |             <img class="errorImg" :src="errorImg" />
  6 |             <p class="errorTitle">404</p>
  7 |             <p class="errorTro">抱歉,你访问的页面不存在。</p>
  8 |             <router-link :to="{path: '/'}">
  9 |               <el-button size="mini" type="primary">返回首页</el-button>
 10 |             </router-link>
 11 |         </div>
 12 |       </div>
 13 |   </div>
 14 | </template>
 15 | 
 16 | <script>
 17 | import errorImg from "@/assets/img/404.png"
 18 | import * as mutils from '@/utils/mUtils'
 19 | 
 20 | 
 21 | export default {
 22 |     name: 'page401',
 23 |     data() {
 24 |       return {
 25 |         errorImg:errorImg
 26 |       }
 27 |     },
 28 |   	mounted(){
 29 |        mutils.setContentHeight(this,this.$refs.contain,250);
 30 | 		},
 31 |     methods: {
 32 |     }
 33 | }
 34 | </script>
 35 | 
 36 | <style lang="less" scoped>
 37 |     .contain{
 38 |       display: flex;
 39 |       justify-content: center;
 40 |       padding: 20px;
 41 |        .errPage-container {
 42 |           align-items: center;
 43 |           p{
 44 |             line-height: 30px;
 45 |           }
 46 |           .errorImg{
 47 |             width:286px;
 48 |             height: 325px;
 49 |             margin-bottom: 10px;
 50 |           }
 51 |           .errorTitle{
 52 |             color: rgba(0,0,0,.85);
 53 |             font-size: 24px;
 54 |           }
 55 |           .errorTro{
 56 |             color: rgba(0,0,0,.45);
 57 |            font-size: 14px;
 58 |           }
 59 |       a{
 60 |         text-decoration: underline;
 61 |       }
 62 |       .rows{
 63 |         height: 100%;
 64 |         .el-col{
 65 |           text-align: center;
 66 |           height: 100%;
 67 |         }
 68 |         .title{
 69 |           font-size: 170px;
 70 |           line-height: 1.2;
 71 |           color: #a9d86e;
 72 |         }
 73 |         .neirongItem{
 74 |           height: 100%;
 75 |           display: flex;
 76 |           justify-content: center;
 77 |           flex-direction: column;
 78 |           .tip{
 79 |             font-size: 30px;
 80 |             font-weight: bold;
 81 |             text-align: left;
 82 |           }
 83 |           .neirong{
 84 |             font-size: 20px;
 85 |             text-align: left;
 86 |           }
 87 |         }
 88 |       }
 89 |       .home{
 90 |         text-align: center;
 91 |       }
 92 |       .router-link-active:hover{
 93 |         color:#a9d86e;
 94 |         text-decoration: underline;
 95 |       }
 96 |     }
 97 |     }
 98 |    
 99 | </style>
100 | 


--------------------------------------------------------------------------------
/src/page/fundData/fundPosition.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="fillcontain">
  3 |         <div class="fillcontainer" ref="fillcontainer">
  4 |            <div id="fundPosition" class="echartsPosition"></div> 
  5 |         </div>
  6 |     </div>
  7 | </template>
  8 | 
  9 | <script>
 10 |     import echarts from 'echarts'
 11 |     import '@/assets/echarts/china.js';
 12 |     import '@/assets/echarts/roma.min.js';
 13 | 
 14 |     export default {
 15 |         data(){
 16 |           return {
 17 |             chart:null
 18 |           }
 19 |         },
 20 |        methods: {
 21 |           randomData() {
 22 |              return Math.round(Math.random()*1000000);
 23 |           },
 24 |           drawMap (id) {
 25 |             // echarts.init(),初始化数据      
 26 |             if ( this.chart &&  this.chart.dispose) { 
 27 |                this.chart.dispose(); 
 28 |             }   
 29 |             this.chart = echarts.init(document.getElementById(id),'roma');
 30 |             this.chart.setOption({
 31 |                 title: {
 32 |                     text: '2019年全国各省份投资情况',
 33 |                     subtext: '单位/万元',
 34 |                     left: 'center'
 35 |                 },
 36 |                 tooltip: {
 37 |                     trigger: 'item'
 38 |                 },
 39 |                 legend: {
 40 |                     orient: 'vertical',
 41 |                     left: 'left',
 42 |                     data:['总投资额']
 43 |                 },
 44 |                 visualMap: {
 45 |                     min: 0,
 46 |                     max: 1200000,
 47 |                     left: 'left',
 48 |                     top: 'bottom',
 49 |                     text: ['高','低'],           // 文本,默认为数值文本
 50 |                     calculable: true
 51 |                 },
 52 |                 toolbox: {
 53 |                     show: true,
 54 |                     orient: 'vertical',
 55 |                     left: 'right',
 56 |                     top: 'center',
 57 |                     feature: {
 58 |                         dataView: {readOnly: false},
 59 |                         restore: {},
 60 |                         saveAsImage: {}
 61 |                     }
 62 |                 },
 63 |                 series: [
 64 |                     {
 65 |                         name: '总投资额',
 66 |                         type: 'map',
 67 |                         mapType: 'china',
 68 |                         roam: false,
 69 |                         label: {
 70 |                             normal: {
 71 |                                 show: true
 72 |                             },
 73 |                             emphasis: {
 74 |                                 show: true
 75 |                             }
 76 |                         },
 77 |                         data:[
 78 |                             {name: '北京',value: this.randomData() },
 79 |                             {name: '天津',value: this.randomData() },
 80 |                             {name: '上海',value: this.randomData() },
 81 |                             {name: '重庆',value: this.randomData() },
 82 |                             {name: '河北',value: this.randomData() },
 83 |                             {name: '河南',value: this.randomData() },
 84 |                             {name: '云南',value: this.randomData() },
 85 |                             {name: '辽宁',value: this.randomData() },
 86 |                             {name: '黑龙江',value: this.randomData() },
 87 |                             {name: '湖南',value: this.randomData() },
 88 |                             {name: '安徽',value: this.randomData() },
 89 |                             {name: '山东',value: this.randomData() },
 90 |                             {name: '新疆',value: this.randomData() },
 91 |                             {name: '江苏',value: this.randomData() },
 92 |                             {name: '浙江',value: this.randomData() },
 93 |                             {name: '江西',value: this.randomData() },
 94 |                             {name: '湖北',value: this.randomData() },
 95 |                             {name: '广西',value: this.randomData() },
 96 |                             {name: '甘肃',value: this.randomData() },
 97 |                             {name: '山西',value: this.randomData() },
 98 |                             {name: '内蒙古',value: this.randomData() },
 99 |                             {name: '陕西',value: this.randomData() },
100 |                             {name: '吉林',value: this.randomData() },
101 |                             {name: '福建',value: this.randomData() },
102 |                             {name: '贵州',value: this.randomData() },
103 |                             {name: '广东',value: this.randomData() },
104 |                             {name: '青海',value: this.randomData() },
105 |                             {name: '西藏',value: this.randomData() },
106 |                             {name: '四川',value: this.randomData() },
107 |                             {name: '宁夏',value: this.randomData() },
108 |                             {name: '海南',value: this.randomData() },
109 |                             {name: '台湾',value: this.randomData() },
110 |                             {name: '香港',value: this.randomData() },
111 |                             {name: '澳门',value: this.randomData() }
112 |                         ]
113 |                     }
114 |                 ]
115 |             });
116 |           }
117 |         },
118 |         mounted(){
119 |               this.$nextTick(function() {
120 |                 this.$refs.fillcontainer.style.height = (document.body.clientHeight - 160)+'px'
121 |                 this.drawMap('fundPosition');
122 |                 var that = this;
123 |                 var resizeTimer = null;
124 |                 // 保证页面在放大或缩小时,也能够动态的显示数据
125 |                 window.onresize = function() {
126 |                   if (resizeTimer) clearTimeout(resizeTimer);
127 |                   resizeTimer = setTimeout(function() {
128 |                     that.$refs.fillcontainer.style.height = (document.body.clientHeight - 160)+'px'
129 |                     that.drawMap('fundPosition');
130 |                   }, 100);
131 |                 }
132 |             })
133 |         }
134 | 
135 |     }
136 | </script>
137 | 
138 | <style lang="less" scoped>
139 |    
140 | </style>
141 | 
142 | 
143 | 


--------------------------------------------------------------------------------
/src/page/fundData/typePosition.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="fillcontain">
  3 |         <div class="fillcontainer" ref="fillcontainer">
  4 |             <el-row :gutter="10"> 
  5 |                 <el-col :span="12" style="height:100%;">
  6 |                     <div id="typePosition"></div> 
  7 |                 </el-col>
  8 |                 <el-col :span="12" style="height:100%;">
  9 |                     <div id="typePosition2"></div> 
 10 |                 </el-col>
 11 |             </el-row>
 12 |        </div>
 13 |     </div>
 14 | </template>
 15 | 
 16 | <script>
 17 |     import echarts from 'echarts';
 18 |     import '../../../node_modules/echarts/theme/vintage.js';
 19 | 
 20 |     export default {
 21 |         data(){
 22 |           return {
 23 |             chart:null,
 24 |             chart_bar:null,
 25 |             data:{
 26 |                 legendData: ['储蓄','基金','股票','债券','期货'],
 27 |                 seriesData:[
 28 |                     {value:this.randomData(), name:'储蓄'},
 29 |                     {value:this.randomData(), name:'基金'},
 30 |                     {value:this.randomData(), name:'股票'},
 31 |                     {value:this.randomData(), name:'债券'},
 32 |                     {value:this.randomData(), name:'期货'}
 33 |                 ]
 34 |             }
 35 |           }
 36 |         },
 37 |         methods: {
 38 |           randomData() {
 39 |             return Math.round(Math.random()*1000000);
 40 |           },
 41 |           drawpie(id, radius, centery) {
 42 |             if ( this.chart &&  this.chart.dispose) { 
 43 |                  this.chart.dispose(); 
 44 |             } 
 45 |             this.chart = echarts.init(document.getElementById(id), 'vintage');
 46 |             this.chart.setOption({
 47 |                 angleAxis: {},
 48 |                 radiusAxis: {
 49 |                     type: 'category',
 50 |                     data: ['2011年', '2012年', '2013年', '2014年', '2015年', '2016年', '2017年'],
 51 |                     z: 10
 52 |                 },
 53 |                 polar: {},
 54 |                 series: [{
 55 |                         type: 'bar',
 56 |                         data: [80, 20, 30, 40, 70, 50, 10],
 57 |                         coordinateSystem: 'polar',
 58 |                         name: '储蓄',
 59 |                         stack: 'a'
 60 |                     }, {
 61 |                         type: 'bar',
 62 |                         data: [60, 40, 60, 10, 30, 20, 10],
 63 |                         coordinateSystem: 'polar',
 64 |                         name: '基金',
 65 |                         stack: 'a'
 66 |                     }, {
 67 |                         type: 'bar',
 68 |                         data: [10, 80, 30, 40, 50, 20, 50],
 69 |                         coordinateSystem: 'polar',
 70 |                         name: '股票',
 71 |                         stack: 'a'
 72 |                     },{
 73 |                         type: 'bar',
 74 |                         data: [60, 20, 10, 80, 30, 20, 50],
 75 |                         coordinateSystem: 'polar',
 76 |                         name: '债券',
 77 |                         stack: 'a'
 78 |                     }, {
 79 |                         type: 'bar',
 80 |                         data: [90, 20, 10, 40, 10, 20, 50],
 81 |                         coordinateSystem: 'polar',
 82 |                         name: '期货',
 83 |                         stack: 'a'
 84 |                     }],
 85 |                     legend: {
 86 |                         show: true,
 87 |                         data: ['储蓄', '基金', '股票','债券','期货']
 88 |                     }
 89 |             });
 90 |           },
 91 |           drawbar(id){
 92 |             if ( this.chart_bar &&  this.chart_bar.dispose) { 
 93 |                 this.chart_bar.dispose(); 
 94 |             } 
 95 |             this.chart_bar = echarts.init(document.getElementById(id),'vintage');
 96 |             this.chart_bar.setOption({
 97 |                  angleAxis: {
 98 |                         type: 'category',
 99 |                         data: ['2011年', '2012年', '2013年', '2014年', '2015年', '2016年', '2017年'],
100 |                         z: 10
101 |                     },
102 |                     radiusAxis: {},
103 |                     polar: {},
104 |                     series: [{
105 |                         type: 'bar',
106 |                         data: [80, 20, 30, 40, 70, 50, 10],
107 |                         coordinateSystem: 'polar',
108 |                         name: '储蓄',
109 |                         stack: 'a'
110 |                     }, {
111 |                         type: 'bar',
112 |                         data: [60, 40, 60, 10, 30, 20, 10],
113 |                         coordinateSystem: 'polar',
114 |                         name: '基金',
115 |                         stack: 'a'
116 |                     }, {
117 |                         type: 'bar',
118 |                         data: [10, 80, 30, 40, 50, 20, 50],
119 |                         coordinateSystem: 'polar',
120 |                         name: '股票',
121 |                         stack: 'a'
122 |                     },{
123 |                         type: 'bar',
124 |                         data: [60, 20, 10, 80, 30, 20, 50],
125 |                         coordinateSystem: 'polar',
126 |                         name: '债券',
127 |                         stack: 'a'
128 |                     }, {
129 |                         type: 'bar',
130 |                         data: [90, 20, 10, 40, 10, 20, 50],
131 |                         coordinateSystem: 'polar',
132 |                         name: '期货',
133 |                         stack: 'a'
134 |                     }],
135 |                     legend: {
136 |                         show: true,
137 |                         data: ['储蓄', '基金', '股票','债券','期货']
138 |                     }
139 |             })
140 |           }
141 |         },
142 |         mounted() {
143 |             this.$nextTick(function() {
144 |                 // this.$refs.fillcontainer.style.height = (document.body.clientHeight - 160)+'px'
145 |                 this.drawpie('typePosition');
146 |                 this.drawbar('typePosition2');
147 |                 // 保证页面在放大或缩小时,也能够动态的显示数据
148 |                 window.onresize = () => {
149 |                     // this.$refs.fillcontainer.style.height = (document.body.clientHeight - 160)+'px'
150 |                     this.drawpie('typePosition');
151 |                     this.drawbar('typePosition2');
152 |                 }
153 | 
154 |             })
155 |         }
156 |     }
157 | </script>
158 | 
159 | <style lang="less" scoped>
160 |       #typePosition,#typePosition2 {
161 |         position: relative;
162 |         width: 96%;
163 |         height: 530px;
164 |         padding: 10px;
165 |         border-radius: 10px;
166 |      }	
167 | </style>
168 | 
169 | 
170 | 


--------------------------------------------------------------------------------
/src/page/fundList/chinaTabsList.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="fillcontain">
 3 |         <div class="tabContainer" ref="tabContainer">
 4 |             <el-tabs type="border-card">
 5 |                 <el-tab-pane>
 6 |                     <span slot="label" @click="toggleTabs('eastChina')"><icon-svg icon-class="icondashboard" />华东区域</span>
 7 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
 8 |                 </el-tab-pane>
 9 |                 <el-tab-pane>
10 |                     <span slot="label" @click="toggleTabs('southChina')"><icon-svg icon-class="iconecharts" />华南区域</span>
11 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
12 |                 </el-tab-pane>
13 |                 <el-tab-pane>
14 |                     <span slot="label" @click="toggleTabs('centralChina')"><icon-svg icon-class="iconinfo" />华中区域</span>
15 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
16 |                 </el-tab-pane>
17 |                 <el-tab-pane>
18 |                     <span slot="label" @click="toggleTabs('northChina')"><icon-svg icon-class="iconpermission" />华北区域</span>
19 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
20 |                 </el-tab-pane>
21 |                 <el-tab-pane>
22 |                     <span slot="label" @click="toggleTabs('northwestChina')"><icon-svg icon-class="iconuser" />西北区域</span>
23 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
24 |                 </el-tab-pane>
25 |                 <el-tab-pane>
26 |                     <span slot="label" @click="toggleTabs('southwestChina')"><icon-svg icon-class="iconError" />西南地区</span>
27 |                     <china-tabs-table :toggleData="toggleData"></china-tabs-table>
28 |                 </el-tab-pane>
29 |                 <el-tab-pane>
30 |                      <span slot="label" @click="toggleTabs('northeastChina')"><icon-svg icon-class="iconfufei0" />东北地区</span>
31 |                      <china-tabs-table :toggleData="toggleData"></china-tabs-table>
32 |                 </el-tab-pane>
33 |                 <el-tab-pane>
34 |                      <span slot="label" @click="toggleTabs('specialareaChina')"><icon-svg icon-class="iconpay1" />台港澳地区</span>
35 |                      <china-tabs-table :toggleData="toggleData"></china-tabs-table>
36 |                 </el-tab-pane>
37 |             </el-tabs>
38 |             <pagination :pageTotal="pageTotal"></pagination>
39 |         </div>
40 |     </div>
41 | </template>
42 | 
43 | <script>
44 |     import chinaTabsTable from './components/chinaTabsTable'
45 |     import data from './data/chinaTabs.json';
46 |     import Pagination from "@/components/pagination";
47 | 
48 |     export default {
49 |         data(){
50 |             return {
51 |                 toggleData:'',
52 |                 pageTotal:60,
53 |             }
54 |         },
55 |         components: {
56 |              chinaTabsTable,
57 |              Pagination
58 |         },
59 |         mounted(){
60 |             //  this.setTabHeight();
61 |             //  window.onresize = () => {
62 |             //     this.setTabHeight();
63 |             //  }
64 |             this.toggleTabs('eastChina');
65 |         },
66 |         methods: {
67 |             setTabHeight(){
68 |                 this.$nextTick(() => {
69 |                     this.$refs.tabContainer.style.height =  (document.body.clientHeight - 160)+'px'
70 |                 })
71 |             },
72 |             toggleTabs(item){
73 |                 this.toggleData = item;
74 |             }
75 |         }
76 |     }
77 | </script>
78 | 
79 | <style lang="less" scoped>
80 |  
81 | </style>
82 | 
83 | 
84 | 


--------------------------------------------------------------------------------
/src/page/fundList/components/chinaTabsTable.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="chinaTabsTable">
  3 |        <el-table 
  4 |           :data="tableData" 
  5 |           style="width: 100%" align='center'>
  6 |             <el-table-column
  7 |                 prop="ID"
  8 |                 label="序号"
  9 |                 align='center'
 10 |                 width="80">
 11 |                 <template slot-scope="scope">
 12 |                     {{scope.$index+1}}
 13 |                 </template>
 14 |             </el-table-column>
 15 |             <el-table-column
 16 |                 prop="provinces"
 17 |                 label="省份"
 18 |                 align='center'
 19 |                 width="140">
 20 |             </el-table-column>
 21 |             <el-table-column
 22 |                 prop="orderMoney"
 23 |                 label="投资总额"
 24 |                 align='center'
 25 |                 width="120"
 26 |                 sortable>
 27 |                 <template slot-scope="scope">  
 28 |                     <span style="color:#CC0033">{{ scope.row.orderMoney }}</span>
 29 |                 </template>
 30 |             </el-table-column>
 31 |             <el-table-column
 32 |                 prop="incomeMoney"
 33 |                 label="收益金额"
 34 |                 align='center'
 35 |                 width="120"
 36 |                 sortable>
 37 |                 <template slot-scope="scope">  
 38 |                     <span style="color:#00d053;">+{{ scope.row.incomeMoney }}</span>
 39 |                 </template>
 40 |             </el-table-column>
 41 |             <el-table-column
 42 |                 prop="payType"
 43 |                 label="主要投资项目"
 44 |                 align='center'
 45 |                 width="120">
 46 |             <template slot-scope="scope">
 47 |                 <el-tag
 48 |                     type="info"
 49 |                         close-transition>
 50 |                     {{scope.row.payType}}
 51 |                 </el-tag>
 52 |             </template>
 53 |             </el-table-column>
 54 |             <el-table-column
 55 |                 prop="orderPeriod"
 56 |                 label="投资周期"
 57 |                 align='center'
 58 |                 width="120">
 59 |             </el-table-column>
 60 |             <el-table-column
 61 |                 prop="orderPersonConunt"
 62 |                 label="投资人数"
 63 |                 align='center'
 64 |                 width="120">
 65 |             </el-table-column>
 66 |             <el-table-column
 67 |                 prop="orderYearRate"
 68 |                 label="投资年变化率"
 69 |                 align='center'
 70 |                 width='120'
 71 |             >
 72 |             </el-table-column>
 73 |             <el-table-column
 74 |                 prop="remarks"
 75 |                 label="备注"
 76 |                 align='left'
 77 |                 >
 78 |                 <template slot-scope="scope">
 79 |                     <span style="color:#3366CC">{{scope.row.remarks}}</span>
 80 |             </template>
 81 |             </el-table-column>
 82 |         </el-table>
 83 |     </div>
 84 | </template>
 85 | 
 86 | <script>
 87 |     import data from '../data/chinaTabs.json';
 88 | 
 89 |     export default {
 90 |          data(){
 91 |              return {
 92 |                 tableData:[],
 93 |                 tableHeight:0,
 94 |              }
 95 |          },
 96 |          props:{
 97 |             toggleData:[String]
 98 |          },
 99 |          mounted(){
100 |              this.setTableHeight();
101 |              window.onresize = () => {
102 |                 this.setTableHeight();
103 |              }
104 |            
105 |          },
106 |          methods:{
107 |              setTableHeight(){
108 |                 this.$nextTick(() => {
109 |                    this.tableHeight =  document.body.clientHeight - 280
110 |                 })
111 |              },
112 |              showTableData(item){
113 |                 switch(item){
114 |                     case 'eastChina':
115 |                         this.tableData = data.china.eastChina;
116 |                         break;
117 |                     case 'southChina':
118 |                         this.tableData = data.china.southChina;
119 |                         break;
120 |                     case 'centralChina':
121 |                         this.tableData = data.china.centralChina;
122 |                         break;
123 |                     case 'northChina':
124 |                         this.tableData = data.china.northChina;
125 |                         break;
126 |                     case 'northwestChina':
127 |                         this.tableData = data.china.northwestChina;
128 |                         break;
129 |                     case 'southwestChina':
130 |                         this.tableData = data.china.southwestChina;
131 |                         break;
132 |                     case 'northeastChina':
133 |                         this.tableData = data.china.northeastChina;
134 |                         break;
135 |                     case 'specialareaChina':
136 |                         this.tableData = data.china.specialareaChina;
137 |                         break;
138 |                 }
139 |              }
140 |          },
141 |         watch: {
142 |             // 监听属性的变化,可以接收参数;
143 |              toggleData(v) {
144 |                 this.showTableData(v);
145 |             },
146 |         }
147 |     }
148 | </script>
149 | 
150 | <style lang="less">
151 | 
152 | </style>
153 | 


--------------------------------------------------------------------------------
/src/page/fundList/components/searchItem.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <div class="search_container searchArea">
 3 |         <el-form 
 4 |             :inline="true" 
 5 |             :model='search_data' 
 6 |             :rules="rules"
 7 |             ref="search_data" 
 8 |             class="demo-form-inline search-form">
 9 |             <el-form-item label="">
10 |                 <el-input v-model="search_data.name" placeholder="用户名"  @keyup.enter.native='onScreeoutMoney("search_data")'></el-input>
11 |             </el-form-item>
12 |             <el-form-item>
13 |                 <el-button type="primary" size ="mini" icon="search" @click='onScreeoutMoney("search_data")'>筛选</el-button>
14 |             </el-form-item>
15 | 
16 |             <el-form-item class="btnRight">
17 |                 <el-button type="primary" size ="mini" icon="view" @click='onBatchDelMoney()' :disabled="searchBtnDisabled">批量删除</el-button>
18 |                 <!-- <el-button type="success" size ="mini" icon="view">导出Elcel</el-button> -->
19 |                 <el-button type="primary" size ="mini" icon="view" @click='onAddMoney()'>添加</el-button>
20 |             </el-form-item>
21 |         </el-form>
22 |     </div>
23 | </template>
24 | 
25 | <script>
26 |    import { mapGetters } from 'vuex'
27 | 
28 |   export default {
29 |       name:'searchItem',
30 |       data(){
31 |           return {
32 |             search_data:{
33 |                 startTime:'',
34 |                 endTime:'',
35 |                 name:''
36 |             },
37 |             rules: {
38 |                 name: [
39 |                     { required: true, message: '请输入用户名', trigger: 'blur' },
40 |                 ]
41 |             }
42 |           }
43 |       },
44 |        computed:{
45 |         ...mapGetters(['searchBtnDisabled']),
46 | 
47 |       },
48 |       created(){
49 |       },
50 |       methods:{
51 |           onScreeoutMoney(searchForm){
52 |               this.$refs[searchForm].validate((valid) => {
53 | 					if (valid) {
54 |                         this.$store.commit('SET_SEARCH',this.search_data);
55 |                         this.$emit("searchList");
56 |                     }
57 |               })
58 |           },
59 |           onAddMoney(){
60 |               this.$emit("showDialog",'add');
61 |           },
62 |           onBatchDelMoney(){
63 |               this.$emit("onBatchDelMoney");
64 |           }
65 |       }
66 |   }
67 | </script>
68 | 
69 | <style lang="less" scoped>
70 |     .search_container{
71 |         margin-bottom: 20px;
72 |     }
73 |     .btnRight{
74 |         float: right;
75 |         margin-right: 0px !important;
76 |     }
77 |     .searchArea{
78 |         background:rgba(255,255,255,1);
79 |         border-radius:2px;
80 |         padding: 18px 18px 0;
81 |     }
82 | </style>
83 | 


--------------------------------------------------------------------------------
/src/page/fundList/moneyData/index.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="fillcontain">
 3 |        <p>{{topTitle}}</p>
 4 |    </div>
 5 | </template>
 6 | 
 7 | <script>    
 8 |     import { mapGetters } from "vuex";
 9 |     export default {
10 |         name:'moneyData',
11 |         data(){
12 |             return {
13 | 
14 |             }
15 |         },
16 |         computed:{
17 |             ...mapGetters(['topTitle'])
18 |         },
19 |         created(){
20 |             
21 |         },
22 |         methods:{
23 | 
24 |         }
25 |     }
26 | 
27 | </script>
28 | 
29 | <style lang="less" scoped>
30 | 
31 | </style>


--------------------------------------------------------------------------------
/src/page/index/components/cardList.vue:
--------------------------------------------------------------------------------
  1 | 
  2 | <template>
  3 |   <div class="cardContainer">
  4 |     <el-row>
  5 |       <el-col>
  6 |         <el-card :body-style="{ padding: '0px' }" class="cardBody">
  7 |           <a :href="github" target="_blank">
  8 |              <img :src="userImg" class="userImg" alt="">
  9 |           </a>
 10 |           <div class="progress wflex">
 11 |             <div class="rflex">
 12 |               <span class="title">vue:</span><el-progress :percentage="100" status="success"></el-progress>
 13 |             </div>
 14 |             <div class="rflex">
 15 |               <span class="title">js:</span><el-progress :percentage="80"></el-progress>
 16 |             </div>
 17 |             <div class="rflex">
 18 |               <span class="title">css:</span><el-progress :percentage="60"></el-progress>
 19 |             </div>
 20 |             <div class="rflex">
 21 |               <span class="title">html:</span><el-progress :percentage="90"></el-progress>
 22 |             </div>
 23 |             <div class="rflex">
 24 |               <span class="title">react:</span><el-progress :percentage="0"></el-progress>
 25 |             </div>
 26 |             <div class="rflex">
 27 |               <span class="title">angular:</span><el-progress :percentage="0"></el-progress>
 28 |             </div>
 29 |           </div>
 30 |         </el-card>
 31 |       </el-col>
 32 |     </el-row>
 33 |   </div>
 34 | </template>
 35 | 
 36 | <script>
 37 |   import userImg from "@/assets/img/avatar-3.png";
 38 |   import { github } from "@/utils/env";
 39 | 
 40 | export default {
 41 |   name:'cardList',
 42 |   data() {
 43 |     return {
 44 |         userImg:userImg,
 45 |         github:github
 46 |     };
 47 |   }
 48 | }
 49 | </script>
 50 | 
 51 | <style lang="less" scoped>
 52 | .cardContainer{
 53 |   padding: 10px;
 54 |   background: #fff;
 55 |   box-sizing: border-box;
 56 |   height:407px;
 57 |   max-height: 407px;
 58 |   overflow: hidden;
 59 |   border-radius: 6px;
 60 |   .userImg{
 61 |     width: 100%;
 62 |     height: 236px;
 63 |   }
 64 |   .progress {
 65 |    padding: 10px;
 66 |     .rflex{
 67 |        justify-content: space-between;
 68 |        align-items: center;
 69 |        .title{
 70 |          width:65px;
 71 |        }
 72 |       .el-progress {
 73 |         flex: 1;
 74 |       }
 75 |     }
 76 |   }
 77 | }
 78 |   .time {
 79 |     font-size: 13px;
 80 |     color: #999;
 81 |   }
 82 |   
 83 |   .bottom {
 84 |     margin-top: 13px;
 85 |     line-height: 12px;
 86 |   }
 87 | 
 88 |   .button {
 89 |     padding: 0;
 90 |     float: right;
 91 |   }
 92 | 
 93 |   .image {
 94 |     width: 100%;
 95 |     display: block;
 96 |   }
 97 | 
 98 |   .clearfix:before,
 99 |   .clearfix:after {
100 |       display: table;
101 |       content: "";
102 |   }
103 |   
104 |   .clearfix:after {
105 |       clear: both
106 |   }
107 | </style>
108 | 
109 | 


--------------------------------------------------------------------------------
/src/page/index/components/commentList.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <div class="commentContainer cflex">
 3 |         <div class="comment rflex" v-for="(item,index) in commentData" :key="index">
 4 |             <div class="left">
 5 |                <img class="userImg" :src="userImg" alt="img" />
 6 |             </div>
 7 |             <div class="right cflex wflex">
 8 |                <p class="username">{{item.username}}</p>
 9 |                <p class="content">{{item.commentContent.substring(0,100)}}</p>
10 |                <p class="dateTime"><icon-svg icon-class="icontime" />{{item.date}}</p>
11 |             </div>
12 |         </div>
13 |     </div>
14 | </template>
15 | 
16 | <script>
17 |   import { getSalesTableList } from "@/api/sales";
18 |   import userImg from "@/assets/img/avatar-3.png";
19 | 
20 |   export default {
21 |     name:'commentList',
22 |     data() {
23 |       return {
24 |         commentData: [],
25 |         userImg:userImg
26 |       }
27 |     },
28 |     mounted(){
29 |         this.getSalesList();
30 |     },
31 |     methods:{
32 |         // 获取列表数据
33 |         getSalesList(){
34 |             getSalesTableList({}).then(res => {
35 |                 let list = res.data.list
36 |                 this.commentData = list.slice(0,3);
37 |             })
38 |         },
39 |     }
40 |   }
41 | </script>
42 | 
43 | <style lang="less" scoped>
44 |  .commentContainer{
45 |    background: #fff;
46 |    padding: 10px;
47 |    box-sizing: border-box;
48 |    height: 407px;
49 |    max-height: 407px;
50 |    overflow: hidden;
51 |    border-radius: 6px;
52 |    justify-content: space-between;
53 |    .comment{
54 |        border-bottom: 1px solid #e8e8e8;
55 |        padding-bottom: 5px;
56 |       .left{
57 |          width:80px;
58 |          text-align: center;
59 |          .userImg{
60 |            width:50px;
61 |            border-radius: 50%;
62 |          }
63 |       }
64 |       .right{
65 |            justify-content: space-between;
66 |            height: 120px;
67 |           .username{
68 |             font-size: 14px;
69 |             font-weight: bold;
70 |           }
71 |           .content{
72 |             font-size: 13px;
73 |             line-height: 20px;
74 |           }
75 |           .dateTime{
76 |              text-align: right;
77 |              font-size: 13px;
78 |              color:#87DE75;
79 |              .svg-icon{
80 |                margin-right: 5px;
81 |              }
82 |           }
83 | 
84 |       }
85 |    }
86 |  }
87 | </style>


--------------------------------------------------------------------------------
/src/page/index/components/logList.vue:
--------------------------------------------------------------------------------
 1 | 
 2 | <template>
 3 |   <div class="logContainer">
 4 |     <el-card class="box-card">
 5 |       <div slot="header" class="clearfix">
 6 |         <a :href="github" target="_blank">
 7 |            <icon-svg icon-class="iconGithub" />
 8 |         </a>
 9 |         <span>项目更新日志:</span>
10 |       </div>
11 |       <div class="logArea el-scrollbar">
12 |             <div class="item" v-for="(item,index) in logsData" :key="index">
13 |               <p class="timeArea">
14 |                 <span class="title">日期:</span>
15 |                 <span class="title time">{{item.createTime}}</span>
16 |               </p>
17 |               <div class="logContent">
18 |                   <p class="title">更新内容:</p>
19 |                   <ul class="logUl">
20 |                     <li v-for="(citem,cindex) in item.data" :key="cindex">{{citem}}</li>
21 |                 </ul>
22 |               </div>
23 |           </div>
24 |       </div>
25 |    
26 |   </el-card>
27 |   </div>
28 | </template>
29 | 
30 | <script>
31 |   import logsData from "@/assets/datas/logs.json";
32 |   import { github } from "@/utils/env";
33 | 
34 |   export default {
35 |     name:'logList',
36 |     data() {
37 |       return {
38 |         logsData:logsData.data,
39 |         github:github
40 |       };
41 |     }
42 |   }
43 | </script>
44 | 
45 | <style lang="less" scoped>
46 | .logContainer{
47 |     padding: 10px;
48 |     background: #fff;
49 |     box-sizing: border-box;
50 |     height:370px;
51 |     max-height: 370px;
52 |     overflow: hidden;
53 |     border-radius: 6px;
54 |     .logArea{
55 |        overflow: auto;
56 |        height: 100%;
57 |     }
58 |     .item{
59 |        .title{
60 |          font-size: 13px;
61 |        }
62 |        .time{
63 |          color:#87DE75;
64 |        }
65 |         .logContent{
66 |           .logUl{
67 |             padding-left: 30px;
68 |             li{
69 |               font-size: 12px;
70 |               list-style: disc;
71 |               line-height: 20px;
72 |             }
73 |           }
74 |       }
75 |     }
76 | }
77 |   .clearfix:before,
78 |   .clearfix:after {
79 |       display: table;
80 |       content: "";
81 |   }
82 |   
83 |   .clearfix:after {
84 |       clear: both
85 |   }
86 | </style>
87 | 
88 | 


--------------------------------------------------------------------------------
/src/page/index/components/salesTable.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <div class="salesTable">
 3 |     <el-table
 4 |       :data="tableData"
 5 |       stripe
 6 |       height="424"
 7 |       style="width: 100%">
 8 |       <el-table-column
 9 |         class-name="salesUsername"
10 |         prop="username"
11 |         label="USERNAME"
12 |         width="150"
13 |       >
14 |         <template slot-scope="scope">
15 |             <img class="userImg" :src="userImg" alt="tuxiang"/>
16 |             {{(scope.row.username).substring(0,12)}}
17 |         </template>
18 |       </el-table-column>
19 |       <el-table-column
20 |         class-name="salesPrice"
21 |         prop="price"
22 |         label="PRICE"
23 |         width="80"
24 |       >
25 |         <template slot-scope="scope">
26 |           <span v-if="scope.row.status === 1" class="saleColor">$ {{scope.row.price}}</span>
27 |           <span v-if="scope.row.status === 2" class="taxColor">$ {{scope.row.price}}</span>
28 |           <span v-if="scope.row.status === 3" class="extenedColor">$ {{scope.row.price}}</span>
29 |           <span v-if="scope.row.status0 === 4" class="likeColor">$ {{scope.row.price}}</span>
30 |         </template>
31 |       </el-table-column>
32 |       <el-table-column
33 |         prop="date"
34 |         label="DATE"
35 |         width="160"
36 |       >
37 |         <template slot-scope="scope">
38 |            <icon-svg icon-class="icontime" />
39 |            {{scope.row.date}}
40 |         </template>
41 |       </el-table-column>
42 |       <el-table-column
43 |         class-name="salesStatus"
44 |         prop="status"
45 |         label="STATUS"
46 |         >
47 |         <template slot-scope="scope">
48 |           <span v-if="scope.row.status === 1" class="saleBgcolor">SALE</span>
49 |           <span v-if="scope.row.status === 2" class="taxBgcolor">TAX</span>
50 |           <span v-if="scope.row.status === 3" class="extenedBgcolor">EXTENDED</span>
51 |           <span v-if="scope.row.status === 4" class="likeBgcolor">LIKE</span>
52 |         </template>
53 |       </el-table-column>
54 |     
55 |     </el-table>
56 |   </div>
57 | </template>
58 | 
59 | <script>
60 |   import { getSalesTableList } from "@/api/sales";
61 |   import userImg from "@/assets/img/avatar-3.png";
62 | 
63 |   export default {
64 |     data() {
65 |       return {
66 |         tableData: [],
67 |         userImg:userImg
68 |       }
69 |     },
70 |     mounted(){
71 |         this.getSalesList();
72 |     },
73 |     methods:{
74 |         // 获取列表数据
75 |         getSalesList(){
76 |             getSalesTableList({}).then(res => {
77 |                 console.log(res);
78 |                 this.pageTotal = res.data.total
79 |                 this.tableData = res.data.list
80 |             })
81 |         },
82 |     }
83 |   }
84 | </script>
85 | 
86 | <style lang="less" scoped>
87 | 
88 | </style>


--------------------------------------------------------------------------------
/src/page/login.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |   	<div class="login_page">
  3 | 	  	<transition name="form-fade" mode="in-out">
  4 | 	  		<section class="form_contianer">
  5 | 			     <div class='titleArea rflex'>
  6 | 					<img class="logo" :src="logo" alt="小爱admin">
  7 | 					<span class='title'>小爱<i>Admin</i></span>
  8 | 				</div>
  9 | 		    	<el-form :model="loginForm" :rules="rules" ref="loginForm" class="loginForm">
 10 | 					<el-form-item prop="username" class="login-item">
 11 | 					    <span class="loginTips"><icon-svg icon-class="iconuser" /></span>
 12 | 						<el-input @keyup.enter.native ="submitForm('loginForm')"  class="area" type="text" placeholder="用户名" v-model="loginForm.username" ></el-input>
 13 | 					</el-form-item>
 14 | 					<el-form-item prop="password" class="login-item"> 
 15 | 					    <span class="loginTips"><icon-svg icon-class="iconLock" /></span>
 16 | 						<el-input @keyup.enter.native ="submitForm('loginForm')" class="area" type="password" placeholder="密码" v-model="loginForm.password"></el-input>
 17 | 					</el-form-item>
 18 | 					<el-form-item>
 19 | 				    	<el-button type="primary"  @click="submitForm('loginForm')" class="submit_btn">SIGN IN</el-button>
 20 | 				  	</el-form-item>
 21 | 					<div class="tiparea">
 22 | 						<p class="wxtip">温馨提示:</p>
 23 | 						<p class="tip">用户名为:admin/editor<span>(可用于切换权限)</span></p>
 24 | 						<p class="tip">密码为:123456</p>
 25 | 					</div>
 26 | 					<div class="sanFangArea">
 27 | 						<p class="title">第三方账号登录</p>
 28 | 						<ul class="rflex">
 29 | 							<li @click="loginByWechat">
 30 | 						       <icon-svg icon-class="iconwechat" />
 31 | 							</li>
 32 | 							<li>
 33 | 							    <icon-svg icon-class="iconweibo" />
 34 | 							</li>
 35 | 							<li>
 36 | 							    <icon-svg icon-class="iconGithub" />
 37 | 							</li>
 38 | 						</ul>
 39 | 				    </div>
 40 | 				</el-form>
 41 | 	  		</section>
 42 | 	  	</transition>
 43 |   	</div>
 44 | </template>
 45 | 
 46 | <script>
 47 | 	import logoImg from "@/assets/img/logo.png";
 48 | 	import { login } from "@/api/user";
 49 |     import { setToken } from '@/utils/auth'
 50 | 
 51 | 	export default {
 52 | 	    data(){
 53 | 			return {
 54 | 				logo:logoImg,
 55 | 				loginForm: {
 56 | 					username: 'admin',
 57 | 					password: '123456'
 58 | 				},
 59 | 				rules: {
 60 | 					username: [
 61 | 			            { required: true, message: '请输入用户名', trigger: 'blur' },
 62 | 						{ min: 2, max: 8, message: '长度在 2 到 8 个字符', trigger: 'blur' }
 63 | 			        ],
 64 | 					password: [
 65 | 						{ required: true, message: '请输入密码', trigger: 'blur' }
 66 | 					],
 67 | 				}
 68 | 			}
 69 | 		},
 70 | 		mounted(){
 71 | 		},
 72 | 		methods: {
 73 | 			loginByWechat(){
 74 | 			},
 75 | 			showMessage(type,message){
 76 |                 this.$message({
 77 |                     type: type,
 78 |                     message: message
 79 |                 });
 80 |             },
 81 | 		    submitForm(loginForm) {
 82 | 				this.$refs[loginForm].validate((valid) => {
 83 | 					if (valid) {
 84 | 						let userinfo = this.loginForm;
 85 | 						login(userinfo).then(res => {
 86 | 							let userList = res.data.userList;
 87 | 							setToken("Token",userList.token)
 88 | 							this.$router.push({ path: '/' })
 89 | 							this.$store.dispatch('initLeftMenu'); //设置左边菜单始终为展开状态
 90 | 						})
 91 | 					}
 92 | 				});
 93 | 			}
 94 | 		}
 95 | 	}
 96 | </script>
 97 | 
 98 | <style lang="less" scoped>
 99 | 	.login_page{
100 | 		position: absolute;
101 | 		width: 100%;
102 | 		height: 100%;
103 | 		background: url(../assets/img/bg9.jpg) no-repeat center center;
104 | 		background-size: 100% 100%;
105 | 	}
106 | 	.form_contianer{
107 | 		position: absolute;
108 | 		top: 50%;
109 |         left: 50%;
110 | 		transform: translate(-50%,-50%);
111 | 		background: #fff;
112 | 		width:370px;
113 | 		border-radius: 5px;
114 | 		padding: 25px;
115 | 		text-align: center;
116 | 		.titleArea{
117 | 			justify-content: center;
118 |    			align-items: center;
119 | 			text-transform: uppercase;
120 | 			font-size: 22px;
121 | 			width: 100%;
122 | 			padding-bottom: 20px;
123 | 			.logo{
124 | 				width: 40px;
125 |     			margin-right: 10px;
126 | 			}
127 | 			.title{
128 | 				i{
129 | 				   color: #FF6C60;
130 | 				}
131 | 			}
132 | 		}
133 | 	
134 | 		.loginForm{
135 | 			.submit_btn{
136 | 				width: 100%;
137 | 				padding:13px 0;
138 | 				font-size: 16px;
139 | 			}
140 | 			.loginTips{
141 | 				position: absolute;
142 | 				left: 10px;
143 | 				z-index: 20;
144 | 				// color: #FF7C1A;
145 | 				font-size: 18px;
146 | 				top: 50%;
147 | 				transform: translateY(-50%);
148 | 			}
149 | 	    }
150 | 	}
151 | 
152 | 	.manage_tip{
153 | 		margin-bottom:20px;
154 | 		.title{
155 | 			font-family: cursive;
156 | 			font-weight: bold;
157 | 			font-size: 26px;
158 | 			color:#fff;
159 | 		}
160 | 		.logo{
161 | 			width:60px;
162 | 			height:60px;
163 | 		}
164 | 	}
165 | 	
166 | 	.tiparea{
167 | 		text-align:left;
168 | 		font-size: 12px;
169 | 		color: #4cbb15;
170 | 		padding: 10px 0;
171 | 		.tip{
172 | 			margin-left: 54px;
173 | 		}
174 | 	}
175 | 	
176 | 	.form-fade-enter-active, .form-fade-leave-active {
177 | 	  	transition: all 1s;
178 | 	}
179 | 	.form-fade-enter, .form-fade-leave-active {
180 | 	  	transform: translate3d(0, -50px, 0);
181 | 	  	opacity: 0;
182 | 	}
183 | 	.loginForm{
184 | 		.el-button--primary{
185 | 			background-color:#FF7C1A;
186 | 			border:1px solid #FF7C1A;
187 | 		}
188 | 	}
189 | 	.sanFangArea{
190 | 		border-top: 1px solid #DCDFE6;
191 | 		padding: 10px 0;
192 | 		display: none;
193 | 		.title{
194 | 			font-size: 14px;
195 | 			color: #8b9196;
196 | 			margin-bottom: 10px;
197 | 		}
198 | 		ul{
199 | 			li{
200 | 				flex:1;
201 | 				display: flex;
202 | 				align-items: center;
203 | 				justify-content: center;
204 | 				cursor: pointer;
205 | 				.svg-icon{
206 | 					font-size: 24px;
207 | 				}
208 | 			}
209 | 		}
210 | 	}
211 | </style>
212 | 


--------------------------------------------------------------------------------
/src/page/permission/components/SwitchRoles.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <div>
 3 |     <!-- <div style="margin-bottom:15px;">{{$t('permission.roles')}}: {{roles}}</div>
 4 |     {{$t('permission.switchRoles')}}: -->
 5 |     <div class="ownPer">我的权限:{{roles}}</div>
 6 |     <div class="rflex">
 7 |         <p>切换权限:</p>
 8 |         <el-radio-group v-model="switchRoles">
 9 |           <el-radio-button label="editor"></el-radio-button>
10 |           <el-radio-button label="admin"></el-radio-button>
11 |         </el-radio-group>
12 |     </div>
13 |   </div>
14 | </template>
15 | 
16 | <script>
17 | export default {
18 |   computed: {
19 |     roles() {
20 |       return this.$store.getters.roles
21 |     },
22 |     switchRoles: {
23 |       get() {
24 |         return this.roles[0]
25 |       },
26 |       set(val) {
27 |         this.$store.dispatch('ChangeRoles', val).then(() => {
28 |           this.$emit('change')
29 |         })
30 |       }
31 |     }
32 |   }
33 | }
34 | 
35 | /**
36 |  * 如何获取到我的权限:
37 |  *  1、用户登录成功之后,根据token调取接口getInfo()获取到用户roles并存入vuex;
38 |  * 
39 |  * 
40 |  */
41 | 
42 | </script>
43 | 
44 | <style lang="less" scoped>
45 |   .ownPer{
46 |      margin-bottom: 20px;
47 |   }
48 |   .rflex{
49 |     align-items: center;
50 |     margin-bottom: 20px;
51 |   }
52 | </style>
53 | 
54 | 
55 | 


--------------------------------------------------------------------------------
/src/page/permission/directive.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <div class="fillcontain">
 3 |     <div class="contain" ref="contain">
 4 |       <switch-roles @change="handleRolesChange" />
 5 |       <div :key="key" class="cflex">
 6 |         <span v-permission="['admin']" class="permission-alert">
 7 |           Only admin can see this
 8 |           <el-button type="warning">admin</el-button>
 9 |           <el-button type="danger">危险按钮</el-button>
10 |         </span>
11 | 
12 |         <span  v-permission="['editor']"  class="permission-alert">
13 |           Only editor can see this
14 |           <el-button type="success">编辑</el-button>
15 |           <el-button type="info">信息按钮</el-button>
16 |         </span>
17 | 
18 |         <span v-permission="['admin','editor']" class="permission-alert">
19 |           Both adminand editor can see this
20 |           <el-button type="primary">主要按钮</el-button>
21 |           <el-button type="success">成功按钮</el-button>
22 |           <el-button type="info">信息按钮</el-button>
23 |           <el-button type="warning">警告按钮</el-button>
24 |           <el-button type="danger">危险按钮</el-button>
25 |         </span>
26 |       </div>
27 |     </div>
28 |   </div>
29 | </template>
30 | 
31 | <script>
32 | import permission from '@/directive/permission/index.js' // 权限判断指令
33 | import SwitchRoles from './components/SwitchRoles'
34 | import * as mutils from '@/utils/mUtils'
35 | 
36 | export default {
37 |   name: 'directivePermission',
38 |   components: { SwitchRoles },
39 |   directives: { permission },
40 |   data() {
41 |     return {
42 |       key: 1 // 为了能每次切换权限的时候重新初始化指令
43 |     }
44 |   },
45 |   mounted(){
46 |     mutils.setContentHeight(this,this.$refs.contain,210);
47 |   },
48 |   methods: {
49 |     handleRolesChange() {
50 |       this.key++
51 |     }
52 |   }
53 | }
54 | 
55 | /**
56 |  * 添加按钮权限的业务逻辑:
57 |  * 1、在需要添加权限的按钮上或按钮区域内,注册全局指令v-permission="['admin']",接收的值为数组形式;
58 |  * 2、指令内部的计算逻辑(参考directive/permission/permisson.js):
59 |  *   通过inserted函数,当被绑定的元素插入到DOM中时,如果指令的value为数组形式并传入roles信息时,
60 |  *   用户当前roles与指令roles,进行循环匹配,只要能匹配到就返回true;不能匹配,则隐藏该元素;
61 |  * 3、全局注册指令:Vue.directive('permission', permission)
62 |  * 4、Vue.use(install);
63 |  * 
64 |  */
65 | </script>
66 | 
67 | <style lang="less" scoped>
68 | .fillcontain {
69 |   .contain{
70 |     background: #fff;
71 |     padding: 20px;
72 |     box-sizing: border-box;
73 |   }
74 |   .cflex{
75 |     .permission-alert{
76 |        margin-bottom: 20px;
77 |     }
78 |   }
79 | }
80 | </style>
81 | 
82 | 


--------------------------------------------------------------------------------
/src/page/permission/page.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |   <div class="fillcontain" >
 3 |     <div  class="contain" ref="contain">
 4 |        <switch-roles @change="handleRolesChange" />
 5 |     </div>
 6 |   </div>
 7 | </template>
 8 | 
 9 | <script>
10 | import SwitchRoles from './components/SwitchRoles'
11 | import * as mutils from '@/utils/mUtils'
12 | 
13 | export default {
14 |   name: 'pagePermission',
15 |   components: { SwitchRoles },
16 |   mounted(){
17 |     mutils.setContentHeight(this,this.$refs.contain,210);
18 |   },
19 |   methods: {
20 |     handleRolesChange() {
21 |       // 没有这个可以匹配的路由"/permission/index",则会定位到404页面
22 |       this.$router.push({ path: '/permission/index?time=' + +new Date() })
23 |     },   
24 |   }
25 | }
26 | </script>
27 | 
28 | <style lang="less" scoped>
29 |  .contain{
30 |     background: #fff;
31 |     padding: 20px;
32 |     box-sizing: border-box;
33 |   }
34 | </style>
35 | 


--------------------------------------------------------------------------------
/src/page/share/components/hengShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件一:横向排列</p>
  4 | 		<div class="bottom rflex">
  5 | 			<span class="toTitle">分享到:</span>
  6 | 			<ul class="shareUl rflex wflex">
  7 | 				<li>
  8 | 					<div class="item" @mouseover="showqrcode()" @mouseout="hideqrcode()">
  9 | 						<icon-svg icon-class="iconwechat" />
 10 | 					</div>
 11 | 					<div class="qrcodeArea" v-show="qrcode.show">
 12 | 						<p class="saoTitle">扫一扫</p>
 13 | 						<div class="qrcode" id="qrCodeUrl"></div>
 14 | 					</div>
 15 | 				</li>
 16 | 				<li>
 17 | 					<div class="item" @click="shareToWeibo()">
 18 | 						<icon-svg icon-class="iconweibo" />
 19 | 					</div>
 20 | 				</li>
 21 | 				<li>
 22 | 					<div class="item" @click="shareToQQ()">
 23 | 						<icon-svg icon-class="iconqq" />
 24 | 					</div>
 25 | 				</li>
 26 | 				<li>
 27 | 					<div class="item" @click="shareToQQzone()">
 28 | 						<icon-svg icon-class="iconqq_zone" />
 29 | 					</div>
 30 | 				</li>
 31 | 			
 32 | 				<li>
 33 | 					<div class="item" @click="shareToDouban()">
 34 | 						<icon-svg icon-class="icondouban" />
 35 | 					</div>
 36 | 				</li>
 37 | 			</ul>
 38 | 		</div>
 39 |     </div>
 40 | </template>
 41 | 
 42 | <script>
 43 | 	import QRCode from 'qrcodejs2'
 44 | 	import { shareUrl } from "@/utils/env";
 45 | 
 46 | 	export default {
 47 | 	  name:'YanShare',
 48 | 	  data(){
 49 | 			return {
 50 | 				qrcode:{
 51 | 					show:false
 52 | 				},
 53 | 				qrcodeObj:{
 54 | 					text:shareUrl, // 要分享的网页路径
 55 | 					width:80,
 56 | 					height:80,
 57 | 					colorDark: '#000000',
 58 | 					colorLight: '#ffffff',
 59 | 					correctLevel: QRCode.CorrectLevel.H
 60 | 				}
 61 | 				
 62 | 			}
 63 | 		},
 64 | 		mounted(){
 65 | 			this.creatQrCode();
 66 | 		},
 67 | 		methods: {
 68 | 			showqrcode(){
 69 | 				this.qrcode.show  = true;
 70 | 			},
 71 | 			hideqrcode(){
 72 | 				this.qrcode.show  = false;
 73 | 			},
 74 | 			creatQrCode() {
 75 | 				this.$nextTick(() => {
 76 | 					new QRCode(document.getElementById('qrCodeUrl'), this.qrcodeObj)
 77 | 				});				
 78 | 			},
 79 | 			shareToQQ(){
 80 |                 this.$emit('shareToQQ');
 81 | 			},
 82 | 			shareToQQzone(){
 83 |                 this.$emit('shareToQQzone');
 84 | 			},
 85 | 			shareToWeibo(){
 86 |                 this.$emit('shareToWeibo');
 87 |             },
 88 | 			shareToDouban(){
 89 |                 this.$emit('shareToDouban');
 90 | 			}
 91 | 
 92 | 		}
 93 | 	}
 94 | </script>
 95 | 
 96 | <style lang="less" scoped>
 97 | 	.shareArea{
 98 | 		width: 340px;
 99 | 		align-items: center;
100 | 		background: #fff;
101 | 		border-radius: 4px;
102 |          .shareTitle{
103 |             border-bottom: 1px solid #e8e8e8;
104 |             padding: 10px;
105 |             box-sizing: border-box;
106 |             width: 100%;
107 |             font-size:14px;
108 |         }
109 | 		.bottom{
110 | 			align-items: center;
111 | 			padding: 20px 8px;
112 | 			width: 100%;
113 | 			height: 100%;
114 | 			box-sizing: border-box;
115 | 			.toTitle{
116 | 				font-size: 13px;
117 | 			}
118 | 			 .shareUl{
119 | 				justify-content: space-between;
120 | 				li{
121 | 					display: flex;
122 | 					flex-direction: column;
123 | 					align-items: center;
124 | 					position: relative;
125 | 					cursor: pointer;
126 | 					.title{
127 | 						margin-bottom: 10px;
128 | 						font-size: 13px;
129 | 						color:#a9d86e;
130 | 					}
131 | 					.item{
132 | 						background: #EFF2F7;
133 | 						width: 40px;
134 | 						height: 40px;
135 | 						border-radius: 50%;
136 | 						display: flex;
137 | 						justify-content: center;
138 | 						align-items: center;
139 | 						.svg-icon{
140 | 							font-size: 24px;
141 | 						}
142 | 						.active{
143 | 							color:#FF6C60;
144 | 						}
145 | 					}
146 | 					.qrcodeArea{
147 | 						position: absolute;
148 | 						top: 50px;
149 | 						left: -30px;
150 | 						text-align: center;
151 | 						border: 1px solid #a9d86e;
152 | 						border-radius: 4px;
153 | 						padding: 10px;
154 | 						z-index: 99;
155 | 						background: #fff;
156 | 						.saoTitle{
157 | 							font-size: 14px;
158 | 							color:#a9d86e;
159 | 							margin-bottom: 5px;
160 | 						}
161 | 					}
162 | 				}
163 | 			}
164 | 		}
165 |      
166 | 	}
167 | 	
168 | </style>
169 | 


--------------------------------------------------------------------------------
/src/page/share/components/index.js:
--------------------------------------------------------------------------------
 1 | import HengShare from "./hengShare";
 2 | import InviteShare from "./inviteShare";
 3 | import JianshuShare from "./jianshuShare";
 4 | import JianshuLeftShare from "./jianshuLeftShare";
 5 | import WxCodeModal from "./wxCodeModal";
 6 | import JuejinShare from "./juejinShare";
 7 | import InfoShare from "./infoShare";
 8 | import SinaShare from "./sinaShare";
 9 | import YanShare from "./yanShare";
10 | 
11 | export {
12 |     HengShare,
13 |     InviteShare,
14 |     JianshuShare,
15 |     JianshuLeftShare,
16 |     WxCodeModal,
17 |     JuejinShare,
18 |     InfoShare,
19 |     SinaShare,
20 |     YanShare
21 | }


--------------------------------------------------------------------------------
/src/page/share/components/infoShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件六:横向排列</p>
  4 | 		<div class="bottom cflex">
  5 | 			<div class="lineArea rflex">
  6 | 				<div class="line"></div>
  7 | 				<span class="lineTitle">分享到</span>
  8 | 				<div class="line"></div>
  9 | 			</div>
 10 | 			<ul class="shareUl rflex wflex">
 11 | 				<li>
 12 | 					<div class="item" @mouseover="showqrcode()" @mouseout="hideqrcode()">
 13 | 						<icon-svg icon-class="iconwechat" />
 14 | 					</div>
 15 | 					<div class="qrcodeArea" v-show="qrcode.show">
 16 | 						<p class="saoTitle">扫一扫</p>
 17 | 						<div class="qrcode" ref='qrCodeUrl1'></div>
 18 | 					</div>
 19 | 				</li>
 20 | 				<li>
 21 | 					<div class="item" @click="shareToWeibo()">
 22 | 						<icon-svg icon-class="iconweibo" />
 23 | 					</div>
 24 | 				</li>
 25 | 				<li>
 26 | 					<div class="item" @click="shareToQQ()">
 27 | 						<icon-svg icon-class="iconqq" />
 28 | 					</div>
 29 | 				</li>
 30 | 				<li>
 31 | 					<div class="item" @click="shareToQQzone()">
 32 | 						<icon-svg icon-class="iconqq_zone" />
 33 | 					</div>
 34 | 				</li>
 35 | 			
 36 | 				<li>
 37 | 					<div class="item" @click="shareToDouban()">
 38 | 						<icon-svg icon-class="icondouban" />
 39 | 					</div>
 40 | 				</li>
 41 | 			</ul>
 42 | 		</div>
 43 |     </div>
 44 | </template>
 45 | 
 46 | <script>
 47 | 	import QRCode from 'qrcodejs2'
 48 | 	import { shareUrl } from "@/utils/env";
 49 | 
 50 | 	export default {
 51 | 	  name:'infoShare',
 52 | 	  data(){
 53 | 			return {
 54 | 				qrcode:{
 55 | 					show:false
 56 | 				},
 57 | 				qrcodeObj:{
 58 | 					text:shareUrl, // 要分享的网页路径
 59 | 					width:80,
 60 | 					height:80,
 61 | 					colorDark: '#000000',
 62 | 					colorLight: '#ffffff',
 63 | 					correctLevel: QRCode.CorrectLevel.H
 64 | 				}
 65 | 				
 66 | 			}
 67 | 		},
 68 | 		mounted(){
 69 | 			this.creatQrCode();
 70 | 		},
 71 | 		methods: {
 72 | 			showqrcode(){
 73 | 				this.qrcode.show  = true;
 74 | 			},
 75 | 			hideqrcode(){
 76 | 				this.qrcode.show  = false;
 77 | 			},
 78 | 			creatQrCode() {
 79 | 				const qrcode = new QRCode(this.$refs.qrCodeUrl1, this.qrcodeObj)
 80 | 			},
 81 | 			shareToQQ(){
 82 |                 this.$emit('shareToQQ');
 83 | 			},
 84 | 			shareToQQzone(){
 85 |                 this.$emit('shareToQQzone');
 86 | 			},
 87 | 			shareToWeibo(){
 88 |                 this.$emit('shareToWeibo');
 89 |             },
 90 | 			shareToDouban(){
 91 |                 this.$emit('shareToDouban');
 92 | 			}
 93 | 
 94 | 		}
 95 | 	}
 96 | </script>
 97 | 
 98 | <style lang="less" scoped>
 99 | 	.shareArea{
100 | 		width: 340px;
101 | 		align-items: center;
102 | 		background: #fff;
103 | 		border-radius: 4px;
104 |          .shareTitle{
105 |             border-bottom: 1px solid #e8e8e8;
106 |             padding: 10px;
107 |             box-sizing: border-box;
108 |             width: 100%;
109 |             font-size:14px;
110 |         }
111 | 		.bottom{
112 | 			align-items: center;
113 | 			padding: 20px;
114 | 			width: 100%;
115 | 			height: 100%;
116 | 			box-sizing: border-box;
117 | 			.lineArea{
118 | 				padding: 10px;
119 | 				width:100%;
120 | 				text-align: center;
121 | 				align-items: center;
122 | 				justify-content: space-between;
123 | 				.lineTitle{
124 | 					font-size: 13px;
125 | 					margin: 0 5px;
126 | 				}
127 | 				.line{
128 | 					border-bottom:1px solid gold;
129 | 					flex:0.5;
130 | 				}
131 | 			}
132 | 			 .shareUl{
133 | 				    width: 100%;
134 | 					justify-content: space-between;
135 | 						li{
136 | 								display: flex;
137 | 								flex-direction: column;
138 | 								align-items: center;
139 | 								position: relative;
140 | 								cursor: pointer;
141 | 								.title{
142 | 									margin-bottom: 10px;
143 | 									font-size: 13px;
144 | 									color:#a9d86e;
145 | 								}
146 | 								.item{
147 | 									background: #EFF2F7;
148 | 									width: 40px;
149 | 									height: 40px;
150 | 									border-radius: 50%;
151 | 									display: flex;
152 | 									justify-content: center;
153 | 									align-items: center;
154 | 									.svg-icon{
155 | 										font-size: 24px;
156 | 									}
157 | 									.active{
158 | 										color:#FF6C60;
159 | 									}
160 | 								}
161 | 								.qrcodeArea{
162 | 									position: absolute;
163 | 									top: 50px;
164 | 									left: -30px;
165 | 									text-align: center;
166 | 									border: 1px solid #a9d86e;
167 | 									border-radius: 4px;
168 | 									padding: 10px;
169 | 									z-index: 99;
170 | 									background: #fff;
171 | 									.saoTitle{
172 | 										font-size: 14px;
173 | 										color:#a9d86e;
174 | 										margin-bottom: 5px;
175 | 									}
176 | 								}
177 | 						}
178 | 	    }
179 | 		}
180 |      
181 | 	}
182 | 	
183 | </style>
184 | 


--------------------------------------------------------------------------------
/src/page/share/components/inviteShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件二:横向排列</p>
  4 | 		<div class="bottom cflex">
  5 | 			<p class="title">邀请好友加入 >></p>
  6 | 			<ul class="shareUl rflex wflex">
  7 | 				<li>
  8 | 					<div class="item" @mouseover="showqrcode()" @mouseout="hideqrcode()">
  9 | 						<icon-svg icon-class="iconwechat" />
 10 | 					</div>
 11 | 					<div class="qrcodeArea" v-show="qrcode.show">
 12 | 						<p class="saoTitle">扫一扫</p>
 13 | 						<div class="qrcode" ref="qrCodeUrl2"></div>
 14 | 					</div>
 15 | 				</li>
 16 | 				<li>
 17 | 					<div class="item" @click="shareToWeibo()">
 18 | 						<icon-svg icon-class="iconweibo" />
 19 | 					</div>
 20 | 				</li>
 21 | 				<li>
 22 | 					<div class="item" @click="shareToQQ()">
 23 | 						<icon-svg icon-class="iconqq" />
 24 | 					</div>
 25 | 				</li>
 26 | 				<li>
 27 | 					<div class="item" @click="shareToQQzone()">
 28 | 						<icon-svg icon-class="iconqq_zone" />
 29 | 					</div>
 30 | 				</li>
 31 | 			
 32 | 				<li>
 33 | 					<div class="item" @click="shareToDouban()">
 34 | 						<icon-svg icon-class="icondouban" />
 35 | 					</div>
 36 | 				</li>
 37 | 			</ul>
 38 | 			<p class="shareIntro">朋友注册后,你会获得书币,书币可以用来去市集兑换物品。</p>
 39 | 		</div>
 40 |     </div>
 41 | </template>
 42 | 
 43 | <script>
 44 | 	import QRCode from 'qrcodejs2'
 45 | 	import { shareUrl } from "@/utils/env";
 46 | 
 47 | 	export default {
 48 | 	  name:'inviteShare',
 49 | 	  data(){
 50 | 			return {
 51 | 				qrcode:{
 52 | 					show:false
 53 | 				},
 54 | 				qrcodeObj:{
 55 | 					text:shareUrl, // 要分享的网页路径
 56 | 					width:80,
 57 | 					height:80,
 58 | 					colorDark: '#000000',
 59 | 					colorLight: '#ffffff',
 60 | 					correctLevel: QRCode.CorrectLevel.H
 61 | 				}
 62 | 				
 63 | 			}
 64 | 		},
 65 | 		mounted(){
 66 | 			this.creatQrCode();
 67 | 		},
 68 | 		methods: {
 69 | 			showqrcode(){
 70 | 				this.qrcode.show  = true;
 71 | 			},
 72 | 			hideqrcode(){
 73 | 				this.qrcode.show  = false;
 74 | 			},
 75 | 			creatQrCode() {
 76 | 				 const qrcode = new QRCode(this.$refs.qrCodeUrl2, this.qrcodeObj)
 77 | 			},
 78 | 			shareToQQ(){
 79 |                 this.$emit('shareToQQ');
 80 | 			},
 81 | 			shareToQQzone(){
 82 |                 this.$emit('shareToQQzone');
 83 | 			},
 84 | 			shareToWeibo(){
 85 |                 this.$emit('shareToWeibo');
 86 |             },
 87 | 			shareToDouban(){
 88 |                 this.$emit('shareToDouban');
 89 | 			}
 90 | 
 91 | 		}
 92 | 	}
 93 | </script>
 94 | 
 95 | <style lang="less" scoped>
 96 | 	.shareArea{
 97 | 		width: 300px;
 98 | 		align-items: center;
 99 | 		background: #fff;
100 | 		border-radius: 4px;
101 |          .shareTitle{
102 |             border-bottom: 1px solid #e8e8e8;
103 |             padding: 10px;
104 |             box-sizing: border-box;
105 |             width: 100%;
106 |             font-size:14px;
107 |         }
108 | 		.bottom{
109 | 			align-items: center;
110 | 			width: 100%;
111 | 			box-sizing: border-box;
112 | 			padding: 20px;
113 | 			.title{
114 | 				padding: 10px;
115 | 				box-sizing: border-box;
116 | 				width: 100%;
117 | 				font-size:14px;
118 | 			}
119 | 			.shareUl{
120 | 				justify-content: space-between;
121 | 				width: 100%;
122 | 				padding: 0 10px;
123 | 				box-sizing: border-box;
124 | 				li{
125 | 						display: flex;
126 | 						flex-direction: column;
127 | 						align-items: center;
128 | 						position: relative;
129 | 						cursor: pointer;
130 | 						.item{
131 | 							background: #EFF2F7;
132 | 							width: 40px;
133 | 							height: 40px;
134 | 							border-radius: 50%;
135 | 							display: flex;
136 | 							justify-content: center;
137 | 							align-items: center;
138 | 							.svg-icon{
139 | 								font-size: 24px;
140 | 							}
141 | 							.active{
142 | 								color:#FF6C60;
143 | 							}
144 | 						}
145 | 						.qrcodeArea{
146 | 							position: absolute;
147 | 							top: 50px;
148 | 							left: -30px;
149 | 							text-align: center;
150 | 							border: 1px solid #a9d86e;
151 | 							border-radius: 4px;
152 | 							padding: 10px;
153 | 							background: #fff;
154 | 							z-index: 99;
155 | 							.saoTitle{
156 | 								font-size: 14px;
157 | 								color:#a9d86e;
158 | 								margin-bottom: 5px;
159 | 							}
160 | 						}
161 | 				}
162 | 			}
163 | 			.shareIntro{
164 | 			    padding: 10px;
165 | 				box-sizing: border-box;
166 | 				width: 100%;
167 | 				font-size:12px;
168 | 			}
169 | 		}
170 |      
171 | 	}
172 | 	
173 | </style>
174 | 


--------------------------------------------------------------------------------
/src/page/share/components/jianshuLeftShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex sharelast">
  3 | 		<p class="shareTitle">分享组件三:仿简书侧栏分享</p>
  4 | 		<div class="bottom">
  5 | 			<el-tooltip class="item" effect="dark" content="分享文章" placement="left">
  6 | 				<el-button class="shareItem" v-popover:leftShareList>
  7 | 				  <icon-svg icon-class="iconshare" />
  8 | 				  <el-popover
  9 | 					ref="leftShareList"
 10 | 					popper-class="moreShareList leftShareList"
 11 | 					placement="left"
 12 | 					trigger="click">
 13 | 					<div>
 14 | 						<ul class="cflex">
 15 | 							<a href="#" @click="shareToWeixin()">
 16 | 								<li>
 17 | 									<div class="item">
 18 | 										<icon-svg icon-class="iconwechat" />
 19 | 										<span>分享到微信</span>
 20 | 									</div>
 21 | 								</li>
 22 | 							</a>
 23 | 							<a href="#" @click="shareToWeibo()">
 24 | 								<li>
 25 | 									<icon-svg icon-class="iconweibo" />
 26 | 									<span>分享到微博</span>
 27 | 								</li>
 28 | 							</a>
 29 | 							<a  href="#" @click="shareToQQ()">
 30 | 								<li>
 31 | 									<icon-svg icon-class="iconqq" />
 32 | 									<span>分享到QQ</span>
 33 | 								</li>
 34 | 							</a>
 35 | 							<a href="#" @click="shareToQQzone()">
 36 | 								<li>
 37 | 									<icon-svg icon-class="iconqq_zone" />
 38 | 									<span>分享到QQ空间</span>
 39 | 								</li>
 40 | 							</a>
 41 | 							<a href="#" @click="shareToDouban()">
 42 | 								<li>
 43 | 									<icon-svg icon-class="icondouban" />
 44 | 									<span>分享到豆瓣</span>
 45 | 								</li>
 46 | 							</a>
 47 | 							<a href="#">
 48 | 								<li>
 49 | 									<icon-svg icon-class="icontwitter" />
 50 | 									<span>分享到Witter</span>
 51 | 								</li>
 52 | 							</a>
 53 | 							<a href="#">
 54 | 								<li>
 55 | 									<icon-svg icon-class="iconfacebook" />
 56 | 									<span>分享到Facebook</span>
 57 | 								</li>
 58 | 							</a>
 59 | 							<a href="#">
 60 | 								<li>
 61 | 									<icon-svg icon-class="icongoogle" />
 62 | 									<span>分享到Google</span>
 63 | 								</li>
 64 | 							</a>
 65 | 						</ul>
 66 | 					</div>
 67 | 				</el-popover>
 68 | 				</el-button>
 69 | 			</el-tooltip>
 70 | 		</div>
 71 |     </div>
 72 | </template>
 73 | 
 74 | <script>
 75 | 	export default {
 76 | 	  name:'jianshuLeftShare',
 77 | 	  data(){
 78 | 			return {
 79 | 				
 80 | 			}
 81 | 		},
 82 | 		mounted(){
 83 | 			
 84 | 		},
 85 | 		methods: {
 86 | 			shareToWeixin(){
 87 |                 this.$emit('shareToWeixin');
 88 | 			},
 89 | 			shareToQQ(){
 90 |                 this.$emit('shareToQQ');
 91 | 			},
 92 | 			shareToQQzone(){
 93 |                 this.$emit('shareToQQzone');
 94 | 			},
 95 | 			shareToWeibo(){
 96 |                 this.$emit('shareToWeibo');
 97 |             },
 98 | 			shareToDouban(){
 99 |                 this.$emit('shareToDouban');
100 | 			}
101 | 
102 | 		}
103 | 	}
104 | </script>
105 | 
106 | <style lang="less" scoped>
107 | 	.shareArea{
108 | 		width: 340px;
109 | 		align-items: center;
110 | 		background: #fff;
111 |         border-radius: 4px;
112 |          .shareTitle{
113 |             border-bottom: 1px solid #e8e8e8;
114 |             padding: 10px;
115 |             box-sizing: border-box;
116 |             width: 100%;
117 |             font-size:14px;
118 |         }
119 | 		.bottom{
120 | 			align-items: center;
121 | 			width: 100%;
122 | 			height: 100%;
123 | 			box-sizing: border-box;
124 | 			display: flex;
125 | 			.shareItem{
126 | 				width: 50px;
127 | 				height: 50px;
128 | 				border: 1px solid #dcdcdc;
129 | 				display: flex;
130 | 				align-items: center;
131 | 				justify-content: center;
132 | 				margin: 0 auto;
133 | 			}
134 | 			.title{
135 | 				padding: 10px;
136 | 				box-sizing: border-box;
137 | 				width: 100%;
138 | 				font-size:14px;
139 | 			}
140 | 			.shareUl{
141 | 				justify-content: space-between;
142 | 				align-items: center;
143 | 				width: 100%;
144 | 				padding: 0 10px;
145 | 				box-sizing: border-box;
146 | 				li{
147 | 						display: flex;
148 | 						flex-direction: column;
149 | 						align-items: center;
150 | 						position: relative;
151 | 						cursor: pointer;
152 | 						.item{
153 | 							background: #EFF2F7;
154 | 							width: 40px;
155 | 							height: 40px;
156 | 							border-radius: 50%;
157 | 							display: flex;
158 | 							justify-content: center;
159 | 							align-items: center;
160 | 							.svg-icon{
161 | 								font-size: 24px;
162 | 							}
163 | 							.active{
164 | 								color:#FF6C60;
165 | 							}
166 | 						}
167 | 						.moreBtn{
168 | 							position: relative;
169 | 							font-size:14px;
170 | 							width:auto;
171 | 							height:40px;
172 | 							text-align: center;
173 | 							padding: 4px 18px;
174 | 							border-radius:50px;
175 | 							border: 1px solid #dcdcdc;
176 | 						}
177 | 				}
178 | 			}
179 | 		}
180 |      
181 | 	}
182 | 	
183 | </style>
184 | 


--------------------------------------------------------------------------------
/src/page/share/components/jianshuShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件三:仿简书底部分享</p>
  4 | 		<div class="bottom cflex">
  5 | 			<ul class="shareUl rflex wflex">
  6 | 				<li>
  7 | 					<div class="item" @click="shareToWeixin()">
  8 | 						<icon-svg icon-class="iconwechat" />
  9 | 					</div>
 10 | 				</li>
 11 | 				<li>
 12 | 					<div class="item" @click="shareToWeibo()">
 13 | 						<icon-svg icon-class="iconweibo" />
 14 | 					</div>
 15 | 				</li>
 16 | 				<li>
 17 | 					<div class="item" @click="shareToQQ()">
 18 | 						<icon-svg icon-class="iconqq" />
 19 | 					</div>
 20 | 				</li>
 21 | 				<li>
 22 | 					<el-button class="moreBtn" v-popover:moreShareList>
 23 | 						更多分享
 24 | 						<el-popover
 25 | 							ref="moreShareList"
 26 | 							popper-class="moreShareList"
 27 | 							placement="top"
 28 | 							trigger="click">
 29 | 								<div>
 30 | 									<ul class="cflex">
 31 | 										<a href="#" @click="shareToQQzone()">
 32 | 											<li>
 33 | 												<icon-svg icon-class="iconqq_zone" />
 34 | 												<span>分享到QQ空间</span>
 35 | 											</li>
 36 | 										</a>
 37 | 										<a href="#" @click="shareToDouban()">
 38 | 											<li>
 39 | 												<icon-svg icon-class="icondouban" />
 40 | 												<span>分享到豆瓣</span>
 41 | 											</li>
 42 | 										</a>
 43 | 										<a href="#">
 44 | 											<li>
 45 | 												<icon-svg icon-class="icontwitter" />
 46 | 												<span>分享到Witter</span>
 47 | 											</li>
 48 | 										</a>
 49 | 										<a href="#">
 50 | 											<li>
 51 | 												<icon-svg icon-class="iconfacebook" />
 52 | 												<span>分享到Facebook</span>
 53 | 											</li>
 54 | 										</a>
 55 | 										<a href="#">
 56 | 											<li>
 57 | 												<icon-svg icon-class="icongoogle" />
 58 | 												<span>分享到Google</span>
 59 | 											</li>
 60 | 										</a>
 61 | 									</ul>
 62 | 								</div>
 63 | 					   </el-popover>
 64 | 					</el-button>
 65 | 				</li>
 66 | 			</ul>
 67 | 		</div>
 68 |     </div>
 69 | </template>
 70 | 
 71 | <script>
 72 | 	export default {
 73 | 	  name:'jianshuShare',
 74 | 	  data(){
 75 | 			return {
 76 | 				
 77 | 			}
 78 | 		},
 79 | 		mounted(){
 80 | 		},
 81 | 		methods: {
 82 | 			shareToWeixin(){
 83 |                 this.$emit('shareToWeixin');
 84 | 			},
 85 | 			shareToQQ(){
 86 |                 this.$emit('shareToQQ');
 87 | 			},
 88 | 			shareToQQzone(){
 89 |                 this.$emit('shareToQQzone');
 90 | 			},
 91 | 			shareToWeibo(){
 92 |                 this.$emit('shareToWeibo');
 93 |             },
 94 | 			shareToDouban(){
 95 |                 this.$emit('shareToDouban');
 96 | 			}
 97 | 
 98 | 		}
 99 | 	}
100 | </script>
101 | 
102 | <style lang="less" scoped>
103 | 	.shareArea{
104 | 		width: 320px;
105 | 		align-items: center;
106 | 		background: #fff;
107 | 		border-radius: 4px;
108 |          .shareTitle{
109 |             border-bottom: 1px solid #e8e8e8;
110 |             padding: 10px;
111 |             box-sizing: border-box;
112 |             width: 100%;
113 |             font-size:14px;
114 |         }
115 | 		.bottom{
116 | 			align-items: center;
117 | 			width: 100%;
118 | 			height: 100%;
119 | 			box-sizing: border-box;
120 | 			padding: 20px 10px;
121 | 			.title{
122 | 				padding: 10px;
123 | 				box-sizing: border-box;
124 | 				width: 100%;
125 | 				font-size:14px;
126 | 			}
127 | 			.shareUl{
128 | 				justify-content: space-between;
129 | 				align-items: center;
130 | 				width: 100%;
131 | 				padding: 0 10px;
132 | 				box-sizing: border-box;
133 | 				li{
134 | 						display: flex;
135 | 						flex-direction: column;
136 | 						align-items: center;
137 | 						position: relative;
138 | 						cursor: pointer;
139 | 						.item{
140 | 							background: #EFF2F7;
141 | 							width: 40px;
142 | 							height: 40px;
143 | 							border-radius: 50%;
144 | 							display: flex;
145 | 							justify-content: center;
146 | 							align-items: center;
147 | 							.svg-icon{
148 | 								font-size: 24px;
149 | 							}
150 | 							.active{
151 | 								color:#FF6C60;
152 | 							}
153 | 						}
154 | 						.moreBtn{
155 | 							position: relative;
156 | 							font-size:14px;
157 | 							width:auto;
158 | 							height:40px;
159 | 							text-align: center;
160 | 							padding: 4px 18px;
161 | 							border-radius:50px;
162 | 							border: 1px solid #dcdcdc;
163 | 						}
164 | 						.qrcodeArea{
165 | 							position: absolute;
166 | 							top: 50px;
167 | 							left: -30px;
168 | 							text-align: center;
169 | 							border: 1px solid #a9d86e;
170 | 							border-radius: 4px;
171 | 							padding: 10px;
172 | 							background: #fff;
173 | 							.saoTitle{
174 | 								font-size: 14px;
175 | 								color:#a9d86e;
176 | 								margin-bottom: 5px;
177 | 							}
178 | 						}
179 | 				}
180 | 			}
181 | 			.shareIntro{
182 | 			    padding: 10px;
183 | 				box-sizing: border-box;
184 | 				width: 100%;
185 | 				font-size:12px;
186 | 			}
187 | 		}
188 |      
189 | 	}
190 | 	
191 | </style>
192 | 


--------------------------------------------------------------------------------
/src/page/share/components/juejinShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件五:仿掘金网站分享</p>
  4 | 		<div class="bottom">
  5 | 			<ul class="shareUl cflex wflex">
  6 | 				<li>
  7 | 					<span class="jueTitle">分享</span>
  8 | 				</li>
  9 | 				<li>
 10 | 					<div class="item" @mouseover="showqrcode()" @mouseout="hideqrcode()">
 11 | 						<icon-svg icon-class="iconwechat" />
 12 | 					</div>
 13 | 					<div class="qrcodeArea" v-show="qrcode.show">
 14 | 						<p class="saoTitle">扫一扫</p>
 15 | 						<div class="qrcode" ref="qrCodeUrl3" ></div>
 16 | 					</div>
 17 | 				</li>
 18 | 				<li>
 19 | 					<div class="item" @click="shareToWeibo()">
 20 | 						<icon-svg icon-class="iconweibo" />
 21 | 					</div>
 22 | 				</li>
 23 | 				<li>
 24 | 					<div class="item" @click="shareToQQ()">
 25 | 						<icon-svg icon-class="iconqq" />
 26 | 					</div>
 27 | 				</li>
 28 | 			
 29 | 			</ul>
 30 | 		</div>
 31 |     </div>
 32 | </template>
 33 | 
 34 | <script>
 35 | 	import QRCode from 'qrcodejs2'
 36 | 	import { shareUrl } from "@/utils/env";
 37 | 
 38 | 	export default {
 39 | 	  name:'juejinShare',
 40 | 	  data(){
 41 | 			return {
 42 | 				qrcode:{
 43 | 					show:false
 44 | 				},
 45 | 				qrcodeObj:{
 46 | 					text:shareUrl, // 要分享的网页路径
 47 | 					width:80,
 48 | 					height:80,
 49 | 					colorDark: '#000000',
 50 | 					colorLight: '#ffffff',
 51 | 					correctLevel: QRCode.CorrectLevel.H
 52 | 				}
 53 | 				
 54 | 			}
 55 | 		},
 56 | 		mounted(){
 57 | 			this.creatQrCode();
 58 | 		},
 59 | 		methods: {
 60 | 			showqrcode(){
 61 | 				this.qrcode.show  = true;
 62 | 			},
 63 | 			hideqrcode(){
 64 | 				this.qrcode.show  = false;
 65 | 			},
 66 | 			creatQrCode() {
 67 | 				const qrcode = new QRCode(this.$refs.qrCodeUrl3, this.qrcodeObj)
 68 | 			},
 69 | 			shareToQQ(){
 70 |                 this.$emit('shareToQQ');
 71 | 			},
 72 | 			shareToQQzone(){
 73 |                 this.$emit('shareToQQzone');
 74 | 			},
 75 | 			shareToWeibo(){
 76 |                 this.$emit('shareToWeibo');
 77 |             },
 78 | 			shareToDouban(){
 79 |                 this.$emit('shareToDouban');
 80 | 			}
 81 | 
 82 | 		}
 83 | 	}
 84 | </script>
 85 | 
 86 | <style lang="less" scoped>
 87 | 	.shareArea{
 88 | 		width: 300px;
 89 | 		align-items: center;
 90 | 		background: #fff;
 91 | 		border-radius: 4px;
 92 |          .shareTitle{
 93 |             border-bottom: 1px solid #e8e8e8;
 94 |             padding: 10px;
 95 |             box-sizing: border-box;
 96 |             width: 100%;
 97 |             font-size:14px;
 98 |         }
 99 | 		.bottom{
100 | 			align-items: center;
101 | 			padding: 0 20px;
102 | 			width: 100%;
103 | 			height: 100%;
104 |             box-sizing: border-box;
105 | 			 .shareUl{
106 | 				justify-content: space-between;
107 | 				li{
108 | 					display: flex;
109 | 					flex-direction: column;
110 | 					align-items: center;
111 | 					position: relative;
112 | 					cursor: pointer;
113 | 					margin-bottom: 10px;
114 | 					.jueTitle{
115 | 						color: #c6c6c6;
116 | 						font-size: 14px;
117 | 					}
118 | 					.title{
119 | 						margin-bottom: 10px;
120 | 						font-size: 13px;
121 | 						color:#a9d86e;
122 | 					}
123 | 					.item{
124 | 						background: #EFF2F7;
125 | 						width: 40px;
126 | 						height: 40px;
127 | 						border-radius: 50%;
128 | 						display: flex;
129 | 						justify-content: center;
130 | 						align-items: center;
131 | 						.svg-icon{
132 | 							font-size: 24px;
133 | 						}
134 | 						.active{
135 | 							color:#FF6C60;
136 | 						}
137 | 					}
138 | 					.qrcodeArea{
139 | 						position: absolute;
140 | 						z-index: 2;
141 | 						top: 50px;
142 | 						text-align: center;
143 | 						border: 1px solid #a9d86e;
144 | 						background: #fff;
145 | 						border-radius: 4px;
146 | 						padding: 10px;
147 | 						.saoTitle{
148 | 								font-size: 14px;
149 | 							color:#a9d86e;
150 | 							margin-bottom: 5px;
151 | 						}
152 | 					}
153 | 			    }
154 | 	        }
155 | 		}
156 |      
157 | 	}
158 | 	
159 | </style>
160 | 


--------------------------------------------------------------------------------
/src/page/share/components/sinaShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex">
  3 | 		<p class="shareTitle">分享组件六:仿新浪分享</p>
  4 | 		<div class="bottom rflex">
  5 | 			<ul class="shareUl rflex wflex">
  6 | 				<li>
  7 | 					<div class="item" @mouseover="showqrcode()" @mouseout="hideqrcode()">
  8 | 						<icon-svg icon-class="iconwechat" />
  9 | 					</div>
 10 | 					<div class="qrcodeArea" v-show="qrcode.show">
 11 | 						<p class="saoTitle">扫一扫</p>
 12 | 						<div class="qrcode" ref="qrCodeUrl4"></div>
 13 | 					</div>
 14 | 				</li>
 15 | 				<li>
 16 | 					<div class="item" @click="shareToWeibo()">
 17 | 						<icon-svg icon-class="iconweibo" />
 18 | 					</div>
 19 | 				</li>
 20 | 				<li>
 21 | 					<div class="item shareTu" v-popover:moreShareList>
 22 | 						<icon-svg icon-class="iconshare" />
 23 | 						<el-popover
 24 | 						ref="moreShareList"
 25 | 						popper-class="moreShareList"
 26 | 						placement="bottom"
 27 | 						trigger="hover">
 28 | 						<div class="shareOther">
 29 | 							<ul class="cflex wflex">
 30 | 								<a href="#">
 31 | 									<li>
 32 | 										<div class="item" @click="shareToQQ()">
 33 | 											<icon-svg icon-class="iconqq" />
 34 | 											<span>腾讯QQ</span>
 35 | 										</div>
 36 | 								</li>
 37 | 								</a>
 38 | 								<a href="#">
 39 | 									<li>
 40 | 										<div class="item" @click="shareToQQzone()">
 41 | 											<icon-svg icon-class="iconqq_zone" />
 42 | 											<span>QQ空间</span>
 43 | 										</div>
 44 | 									</li>
 45 | 								</a>
 46 | 							</ul>
 47 | 					    </div>
 48 | 					</el-popover>
 49 | 					</div>
 50 | 				</li>
 51 | 			</ul>
 52 | 		</div>
 53 |     </div>
 54 | </template>
 55 | 
 56 | <script>
 57 | 	import QRCode from 'qrcodejs2'
 58 | 	import { shareUrl } from "@/utils/env";
 59 | 
 60 | 	export default {
 61 | 	  name:'sinaShare',
 62 | 	  data(){
 63 | 			return {
 64 | 				qrcode:{
 65 | 					show:false
 66 | 				},
 67 | 				qrcodeObj:{
 68 | 					text:shareUrl, // 要分享的网页路径
 69 | 					width:80,
 70 | 					height:80,
 71 | 					colorDark: '#000000',
 72 | 					colorLight: '#ffffff',
 73 | 					correctLevel: QRCode.CorrectLevel.H
 74 | 				}
 75 | 				
 76 | 			}
 77 | 		},
 78 | 		mounted(){
 79 | 			this.creatQrCode();
 80 | 		},
 81 | 		methods: {
 82 | 			showqrcode(){
 83 | 				this.qrcode.show  = true;
 84 | 			},
 85 | 			hideqrcode(){
 86 | 				this.qrcode.show  = false;
 87 | 			},
 88 | 			creatQrCode() {
 89 | 				const qrcode = new QRCode(this.$refs.qrCodeUrl4, this.qrcodeObj)
 90 | 			},
 91 | 			shareToQQ(){
 92 |                 this.$emit('shareToQQ');
 93 | 			},
 94 | 			shareToQQzone(){
 95 |                 this.$emit('shareToQQzone');
 96 | 			},
 97 | 			shareToWeibo(){
 98 |                 this.$emit('shareToWeibo');
 99 |             },
100 | 			shareToDouban(){
101 |                 this.$emit('shareToDouban');
102 | 			}
103 | 
104 | 		}
105 | 	}
106 | </script>
107 | 
108 | <style lang="less" scoped>
109 | 	.shareArea{
110 | 		width: 320px;
111 | 		align-items: center;
112 | 		background: #fff;
113 | 		border-radius: 4px;
114 |          .shareTitle{
115 |             border-bottom: 1px solid #e8e8e8;
116 |             padding: 10px;
117 |             box-sizing: border-box;
118 |             width: 100%;
119 |             font-size:14px;
120 |         }
121 | 		.bottom{
122 | 			align-items: center;
123 | 			padding: 20px;
124 | 			width: 100%;
125 | 			height: 100%;
126 |             box-sizing: border-box;
127 | 			 .shareUl{
128 | 				 justify-content: center;
129 | 				li{
130 | 					display: flex;
131 | 					flex-direction: column;
132 | 					align-items: center;
133 | 					position: relative;
134 | 					cursor: pointer;
135 | 					margin-right: 10px;
136 | 					.title{
137 | 						margin-bottom: 10px;
138 | 						font-size: 13px;
139 | 						color:#a9d86e;
140 | 					}
141 | 					.item{
142 | 						width: 40px;
143 | 						height: 40px;
144 | 						display: flex;
145 | 						justify-content: center;
146 | 						align-items: center;
147 | 						.svg-icon{
148 | 							font-size: 24px;
149 | 						}
150 | 						.active{
151 | 							color:#FF6C60;
152 | 						}
153 | 					}
154 | 					.qrcodeArea{
155 | 						position: absolute;
156 | 						top: 50px;
157 | 						left: -30px;
158 | 						text-align: center;
159 | 						border: 1px solid #a9d86e;
160 | 						border-radius: 4px;
161 | 						padding: 10px;
162 | 						z-index: 99;
163 | 						background: #fff;
164 | 						.saoTitle{
165 | 							font-size: 14px;
166 | 							color:#a9d86e;
167 | 							margin-bottom: 5px;
168 | 						}
169 | 					}
170 | 				}
171 | 	        }
172 | 		}
173 |      
174 | 	}
175 | 	
176 | </style>
177 | 


--------------------------------------------------------------------------------
/src/page/share/components/wxCodeModal.vue:
--------------------------------------------------------------------------------
 1 | <template>
 2 |     <el-dialog
 3 |     :append-to-body="true"
 4 |     :width="wxModal.width"
 5 |     :height="wxModal.height"
 6 |     :visible.sync="wxModal.show"
 7 |     :before-close="handleClose"
 8 |     >
 9 |         <div class="wxContent">
10 |             <p class="qrtitle">打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮</p>
11 |             <div class="qrcode" ref="qrCodeUrl5"></div>
12 |         </div>
13 |     </el-dialog>
14 | </template>
15 | 
16 | <script>
17 | 	import QRCode from 'qrcodejs2'
18 | 	import { shareUrl } from "@/utils/env";
19 | 
20 | 	export default {
21 | 	  name:'wxCodeModal',
22 | 	  data(){
23 | 			return {
24 | 				qrcodeObj:{
25 | 					text:shareUrl, // 要分享的网页路径
26 | 					width:190,
27 | 					height:190,
28 | 					colorDark: '#000000',
29 | 					colorLight: '#ffffff',
30 | 					correctLevel: QRCode.CorrectLevel.H
31 | 				}
32 | 				
33 | 			}
34 |         },
35 |         props:{
36 |             wxModal:Object
37 |         },
38 | 		mounted(){
39 | 
40 | 		},
41 | 		methods: {
42 |             creatQrCode() {
43 |                 const qrcode = new QRCode(this.$refs.qrCodeUrl5, this.qrcodeObj)
44 | 			},
45 |             handleClose(){
46 |                 this.$emit('hideWxCodeModal')
47 |             }
48 | 
49 |         },
50 |         watch:{
51 |             'wxModal.show': {
52 |                 handler(newName, oldName) {
53 |                     console.log(newName)
54 |                     newName?this.creatQrCode():'';
55 |                 },
56 |                 deep: true,
57 |                 immediate: true
58 |             }
59 |         }
60 | 	}
61 | </script>
62 | 
63 | <style lang="less" scoped>
64 |   .wxContent{
65 |          text-align: center;
66 |          padding: 20px;
67 |         .qrtitle{
68 |             margin-bottom: 30px;
69 |         }
70 |         .qrcode{
71 |             display: flex;
72 |             align-items: center;
73 |             justify-content: center;
74 |             flex-direction: column;
75 |         }
76 |   }
77 | 	
78 | </style>
79 | 


--------------------------------------------------------------------------------
/src/page/share/components/yanShare.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="shareArea cflex sharelast">
  3 | 		<p class="shareTitle">分享组件八:横向排列</p>
  4 | 		<div class="bottom rflex">
  5 | 			<div class="yanItem" v-popover:yanShare>
  6 | 				<icon-svg icon-class="iconshare1" />
  7 | 				<el-popover
  8 | 				ref="yanShare"
  9 | 				popper-class="yanshare"
 10 | 				placement="bottom"
 11 | 				trigger="hover">
 12 | 				<ul class="shareUl rflex wflex">
 13 | 					<li>
 14 | 						<div class="item" v-popover:yanSharewx>
 15 | 							<icon-svg icon-class="iconwechat" />
 16 | 							<el-popover
 17 | 								ref="yanSharewx"
 18 | 								popper-class="yanSharewx"
 19 | 								placement="bottom"
 20 | 								trigger="hover">
 21 | 								<div class="qrcodeArea">
 22 | 									<p class="saoTitle">扫一扫</p>
 23 | 									<div class="qrcode" ref="qrCodeUrl6"></div>
 24 | 								</div>
 25 | 							</el-popover>
 26 | 						</div>
 27 | 					</li>
 28 | 					<li>
 29 | 						<div class="item" @click="shareToWeibo()">
 30 | 							<icon-svg icon-class="iconweibo" />
 31 | 						</div>
 32 | 					</li>
 33 | 					<li>
 34 | 						<div class="item" @click="shareToQQ()">
 35 | 							<icon-svg icon-class="iconqq" />
 36 | 						</div>
 37 | 					</li>
 38 | 					<li>
 39 | 						<div class="item" @click="shareToQQzone()">
 40 | 							<icon-svg icon-class="iconqq_zone" />
 41 | 						</div>
 42 | 					</li>
 43 | 				
 44 | 					<li>
 45 | 						<div class="item" @click="shareToDouban()">
 46 | 							<icon-svg icon-class="icondouban" />
 47 | 						</div>
 48 | 					</li>
 49 | 				</ul>
 50 | 			</el-popover>
 51 | 			</div>
 52 | 		</div>
 53 |     </div>
 54 | </template>
 55 | 
 56 | <script>
 57 | 	import QRCode from 'qrcodejs2'
 58 | 	import { shareUrl } from "@/utils/env";
 59 | 
 60 | 	export default {
 61 | 	  name:'hengShare',
 62 | 	  data(){
 63 | 			return {
 64 | 				qrcode:{
 65 | 					show:false
 66 | 				},
 67 | 				qrcodeObj:{
 68 | 					text:shareUrl, // 要分享的网页路径
 69 | 					width:80,
 70 | 					height:80,
 71 | 					colorDark: '#000000',
 72 | 					colorLight: '#ffffff',
 73 | 					correctLevel: QRCode.CorrectLevel.H
 74 | 				}
 75 | 				
 76 | 			}
 77 | 		},
 78 | 		mounted(){
 79 | 			this.creatQrCode();
 80 | 		},
 81 | 		methods: {
 82 | 			showqrcode(){
 83 | 				this.qrcode.show  = true;
 84 | 			},
 85 | 			hideqrcode(){
 86 | 				this.qrcode.show  = false;
 87 | 			},
 88 | 			creatQrCode() {
 89 | 				 const qrcode = new QRCode(this.$refs.qrCodeUrl6, this.qrcodeObj)
 90 | 			},
 91 | 			shareToQQ(){
 92 |                 this.$emit('shareToQQ');
 93 | 			},
 94 | 			shareToQQzone(){
 95 |                 this.$emit('shareToQQzone');
 96 | 			},
 97 | 			shareToWeibo(){
 98 |                 this.$emit('shareToWeibo');
 99 |             },
100 | 			shareToDouban(){
101 |                 this.$emit('shareToDouban');
102 | 			}
103 | 
104 | 		}
105 | 	}
106 | </script>
107 | 
108 | <style lang="less" scoped>
109 | 	.shareArea{
110 | 		width: 340px;
111 | 		align-items: center;
112 | 		background: #fff;
113 | 		border-radius: 4px;
114 |          .shareTitle{
115 |             border-bottom: 1px solid #e8e8e8;
116 |             padding: 10px;
117 |             box-sizing: border-box;
118 |             width: 100%;
119 |             font-size:14px;
120 |         }
121 | 		.bottom{
122 | 			align-items: center;
123 | 			padding: 20px;
124 | 			width: 100%;
125 | 			height: 100%;
126 | 			box-sizing: border-box;
127 | 			justify-content: center;
128 | 			.yanItem{
129 | 				justify-content: center;
130 | 				align-items: center;
131 | 				.svg-icon{
132 | 					font-size: 34px;
133 | 				}
134 | 			}
135 | 			.toTitle{
136 | 				font-size: 13px;
137 | 			}
138 | 			 .shareUl{
139 | 				justify-content: space-between;
140 | 				li{
141 | 					display: flex;
142 | 					flex-direction: column;
143 | 					align-items: center;
144 | 					position: relative;
145 | 					cursor: pointer;
146 | 					.title{
147 | 						margin-bottom: 10px;
148 | 						font-size: 13px;
149 | 						color:#a9d86e;
150 | 					}
151 | 					.item{
152 | 						background: #EFF2F7;
153 | 						width: 40px;
154 | 						height: 40px;
155 | 						border-radius: 50%;
156 | 						display: flex;
157 | 						justify-content: center;
158 | 						align-items: center;
159 | 						.svg-icon{
160 | 							font-size: 24px;
161 | 						}
162 | 						.active{
163 | 							color:#FF6C60;
164 | 						}
165 | 					}
166 | 					
167 | 				}
168 | 			}
169 | 		}
170 |      
171 | 	}
172 | 	
173 | </style>
174 | 


--------------------------------------------------------------------------------
/src/page/share/index.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |   	<div class="shareContainer" ref="shareContainer">
  3 | 		<el-row :gutter="20">
  4 | 			<el-col :span="6">
  5 | 			  <heng-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></heng-share>
  6 | 			</el-col>
  7 | 			<el-col :span="6">
  8 | 			   <invite-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></invite-share>
  9 | 			</el-col>
 10 | 			<el-col :span="6">
 11 | 			  <jianshu-share @shareToWeixin="shareToWeixin" @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></jianshu-share>
 12 | 			</el-col>
 13 | 			<el-col :span="6">
 14 | 			  <jianshu-left-share @shareToWeixin="shareToWeixin" @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></jianshu-left-share>
 15 | 			</el-col>
 16 | 		</el-row>
 17 | 		<el-row :gutter="20">
 18 | 			<el-col :span="6">
 19 | 		       <info-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></info-share>
 20 | 			</el-col>
 21 | 			<el-col :span="6">
 22 | 			  <juejin-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></juejin-share>
 23 | 			</el-col>
 24 | 			<el-col :span="6">
 25 | 		      <sina-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></sina-share>
 26 | 			</el-col>
 27 | 			<el-col :span="6">
 28 | 		      <yan-share @shareToQQ="shareToQQ" @shareToQQzone="shareToQQzone" @shareToWeibo="shareToWeibo" @shareToDouban="shareToDouban"></yan-share>
 29 | 			</el-col>
 30 | 		</el-row>
 31 | 		<wx-code-modal v-if="wxModal.show" :wxModal="wxModal" @hideWxCodeModal="hideWxCodeModal"></wx-code-modal>
 32 |   	</div>
 33 | </template>
 34 | 
 35 | <script>
 36 | 	import {
 37 | 		HengShare,
 38 | 		InviteShare,
 39 | 		JianshuShare,
 40 | 		JianshuLeftShare,
 41 | 		WxCodeModal,
 42 | 		JuejinShare,
 43 | 		InfoShare,
 44 | 		SinaShare,
 45 | 		YanShare
 46 | 	} from "./components";
 47 |     import * as mutils from '@/utils/mUtils'
 48 | 
 49 | 	export default {
 50 | 	  data(){
 51 | 			return {
 52 | 				wxModal:{
 53 | 					show:false,
 54 | 					width:"358px",
 55 | 					height:"358px",
 56 | 				}
 57 | 			}
 58 | 		},
 59 | 		components:{
 60 | 			HengShare,
 61 | 			InviteShare,
 62 | 			JianshuShare,
 63 | 			JianshuLeftShare,
 64 | 			WxCodeModal,
 65 | 			JuejinShare,
 66 | 			InfoShare,
 67 | 			SinaShare,
 68 | 			YanShare
 69 | 		},
 70 | 		mounted(){
 71 | 			mutils.setContentHeight(this,this.$refs.shareContainer,210);
 72 | 		},
 73 | 		methods: {
 74 | 			hideWxCodeModal(){
 75 | 				this.wxModal.show = false;
 76 | 			},
 77 | 			// 分享到微信,显示微信二维码弹框;
 78 | 			shareToWeixin(){
 79 | 				this.wxModal.show = true;
 80 | 			},
 81 | 			shareToQQ(){
 82 | 				this.shareConfig('qq')
 83 | 			},
 84 | 			shareToQQzone(){
 85 | 				this.shareConfig('qqZone')
 86 | 			},
 87 | 			shareToWeibo(){
 88 | 				this.shareConfig('weibo')
 89 | 			},
 90 | 			shareToDouban(){
 91 | 				this.shareConfig('douban')
 92 | 			}
 93 | 		}
 94 | 	}
 95 | </script>
 96 | 
 97 | <style lang="less" scoped>
 98 | 	.shareContainer{
 99 | 		display: flex;
100 | 		flex-direction: column;
101 | 		justify-content: space-around;
102 | 		padding: 20px;
103 | 		.el-row:first-child{
104 | 			margin-bottom: 20px;
105 | 		}
106 | 		.el-row{
107 | 			height:210px;
108 | 			.el-col{
109 | 				height:100%;
110 | 				.shareArea{
111 | 					height: 100%;
112 | 					width:100%;
113 | 				}
114 | 			}
115 | 		}
116 | 	}
117 | </style>
118 | 


--------------------------------------------------------------------------------
/src/page/userList/userList.vue:
--------------------------------------------------------------------------------
  1 | <template>
  2 |     <div class="fillcontain">
  3 |         <div class="contain">
  4 |           <div class="table_container">
  5 |             <el-table
  6 |                  v-loading="loading"
  7 |                  :data="tableData"
  8 |                  border
  9 |                  stripe
 10 |                  highlight-current-row
 11 |                  header-cell-class-name="table-header-class"
 12 |                  style="width:100%">
 13 |                 <el-table-column
 14 |                    label="序号"
 15 |                    width="60"
 16 |                    align='center'>
 17 |                    <template slot-scope="scope">
 18 |                        <span>{{scope.$index+(paginations.pageIndex - 1) * paginations.pageSize + 1}} </span>
 19 |                     </template>
 20 |                 </el-table-column>
 21 |                  <el-table-column
 22 |                    property="username"
 23 |                    label="用户姓名"
 24 |                    width="80"
 25 |                    align='center'>
 26 |                 </el-table-column>
 27 |                 <el-table-column
 28 |                    property="email"
 29 |                    label="邮箱地址"
 30 |                    width="180"
 31 |                    align='center'>
 32 |                 </el-table-column>
 33 |                 <el-table-column
 34 |                    property="address"
 35 |                    label="注册地址"
 36 |                    align='center'>
 37 |                 </el-table-column>
 38 |                  <el-table-column
 39 |                    property="region"
 40 |                    label="地区"
 41 |                    width="80"
 42 |                    align='center'>
 43 |                 </el-table-column> 
 44 |                   <el-table-column
 45 |                    property="isp"
 46 |                    label="网络"
 47 |                    width="80"
 48 |                    align='center'>
 49 |                 </el-table-column> 
 50 |                  <el-table-column
 51 |                    property="ip"
 52 |                    label="IP地址"
 53 |                    width="180"
 54 |                    align='center'>
 55 |                 </el-table-column>
 56 |                 <el-table-column
 57 |                    property="createTime"
 58 |                    label="注册时间"
 59 |                    width="180"
 60 |                    align='center'>
 61 |                 </el-table-column>
 62 |                 <el-table-column
 63 |                    property="updateTime"
 64 |                    label="登录时间"
 65 |                    width="180"
 66 |                    align='center'>
 67 |                 </el-table-column>
 68 |             </el-table>
 69 |            <el-row>
 70 |                 <el-col :span="24">
 71 |                     <div class="pagination">
 72 |                         <el-pagination
 73 |                             v-if='paginations.total > 0'
 74 |                             :page-sizes="paginations.pageSizes"
 75 |                             :page-size="paginations.pageSize"
 76 |                             :layout="paginations.layout"
 77 |                             :total="paginations.total"
 78 |                             :current-page='paginations.pageIndex'
 79 |                             @current-change='handleCurrentChange'
 80 |                             @size-change='handleSizeChange'>
 81 |                         </el-pagination>
 82 |                     </div>
 83 |                 </el-col>
 84 |             </el-row>
 85 |           </div>
 86 |         </div>
 87 |     </div>
 88 | </template>
 89 | 
 90 | <script>
 91 |     import { getUserList } from "@/api/user";
 92 |     export default {
 93 |         data(){
 94 |             return {
 95 |                 tableData: [],
 96 |                 loading:true,
 97 |               //需要给分页组件传的信息
 98 |                 paginations: {
 99 |                     total: 0,        // 总数
100 |                     pageIndex: 1,  // 当前位于哪页
101 |                     pageSize: 20,   // 1页显示多少条
102 |                     pageSizes: [5, 10, 15, 20],  //每页显示多少条
103 |                     layout: "total, sizes, prev, pager, next, jumper"   // 翻页属性
104 |                 },
105 |             }
106 |         },
107 |         created(){
108 |         },
109 |         mounted(){
110 |             this.getUserList();
111 |         },
112 |         methods: {
113 |             getUserList(){
114 |                 let para = {
115 |                     limit:this.paginations.pageSize,
116 |                     page:this.paginations.pageIndex
117 |                 }
118 |                 getUserList(para).then(res => {
119 |                     this.loading = false;
120 |                     this.paginations.total = res.data.total;
121 |                     this.tableData = res.data.userList;
122 |                 })
123 |             },
124 |             // 每页多少条切换
125 |             handleSizeChange(pageSize) {
126 |                this.paginations.pageSize = pageSize;
127 |                this.getUserList();
128 |             },
129 |             // 上下分页
130 |             handleCurrentChange(page) {
131 |                this.paginations.pageIndex = page;
132 |                this.getUserList();
133 |             }
134 |         },
135 |     }
136 | </script>
137 | 
138 | <style lang="less" scoped>
139 |     .fillcontain{
140 |         padding-bottom: 0;
141 |     }
142 |     .contain{
143 |         background: #fff;
144 |         padding: 10px;
145 |         margin-bottom: 20px;
146 |     }
147 |    .pagination{
148 |        padding: 10px 20px;
149 |        text-align: right;
150 |    }
151 | </style>
152 | 
153 | 
154 | 
155 | 


--------------------------------------------------------------------------------
/src/permission.js:
--------------------------------------------------------------------------------
  1 | import router from './router'
  2 | import store from './store'
  3 | import NProgress from 'nprogress' // Progress 进度条
  4 | process.env.NODE_ENV === "development" && import('nprogress/nprogress.css')
  5 | import { Message } from 'element-ui'
  6 | import { getToken } from '@/utils/auth' // 验权(从cookie中获取)
  7 | import { getUserInfo } from "@/api/user";
  8 | import {
  9 |   setTitle
 10 | } from '@/utils/mUtils' // 设置浏览器头部标题
 11 | 
 12 | function hasPermission(roles, permissionRoles) {
 13 |   if (roles.indexOf('admin') >= 0) return true 
 14 |   if (!permissionRoles) return true
 15 |   return roles.some(role => permissionRoles.indexOf(role) >= 0)
 16 | }
 17 | const whiteList = ['/login'] // 不重定向白名单
 18 | 
 19 | router.beforeEach((to, from, next) => {
 20 |   NProgress.start()
 21 |    // 设置浏览器头部标题
 22 |    const browserHeaderTitle = to.meta.title
 23 |    store.commit('SET_BROWSERHEADERTITLE', {
 24 |      browserHeaderTitle: browserHeaderTitle
 25 |    })
 26 |   // 点击登录时,拿到了token并存入了cookie,保证页面刷新时,始终可以拿到token
 27 |   if (getToken('Token')) {
 28 |     if(to.path === '/login') {
 29 |       next({ path: '/' })  
 30 |       NProgress.done() 
 31 |     } else {
 32 |       // 用户登录成功之后,每次点击路由都进行了角色的判断;
 33 |       if (store.getters.roles.length === 0) {
 34 |         let token = getToken('Token');
 35 |         getUserInfo({"token":token}).then().then(res => { // 根据token拉取用户信息
 36 |           let userList = res.data.userList;
 37 |           store.commit("SET_ROLES",userList.roles);
 38 |           store.commit("SET_NAME",userList.name);
 39 |           store.commit("SET_AVATAR",userList.avatar);
 40 |           store.dispatch('GenerateRoutes', { "roles":userList.roles }).then(() => { // 根据roles权限生成可访问的路由表
 41 |             router.addRoutes(store.getters.addRouters) // 动态添加可访问权限路由表
 42 |             next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
 43 |           })
 44 |         }).catch((err) => {
 45 |           store.dispatch('LogOut').then(() => {
 46 |             Message.error(err || 'Verification failed, please login again')
 47 |             next({ path: '/' })
 48 |           })
 49 |         })
 50 |       } else {
 51 |         // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
 52 |         if (hasPermission(store.getters.roles, to.meta.roles)) {
 53 |           next()//
 54 |         } else {
 55 |           next({ path: '/401', replace: true, query: { noGoBack: true }})
 56 |         }
 57 |       }
 58 |     }
 59 |   } else {
 60 |     if (whiteList.indexOf(to.path) !== -1) {
 61 |       // 点击退出时,会定位到这里
 62 |       next()
 63 |     } else {
 64 |       next('/login')
 65 |       NProgress.done()
 66 |     }
 67 |   }
 68 | })
 69 | 
 70 | router.afterEach(() => {
 71 |   NProgress.done() // 结束Progress
 72 |   setTimeout(() => {
 73 |     const browserHeaderTitle = store.getters.browserHeaderTitle
 74 |     setTitle(browserHeaderTitle)
 75 |   }, 0)
 76 | })
 77 | 
 78 | 
 79 | 
 80 | /**
 81 |   本系统权限逻辑分析:
 82 |   1、路由对象区分权限路由对象和非权限路由对象;初始化时,将非权限路由对象赋值给Router;同时设置权限路由中的meta对象,如:meta:{roles:['admin','editor']}
 83 |      表示该roles所拥有的路由权限;
 84 |   2、通过用户登录成功之后返回的roles值,进行路由的匹配并生成新的路由对象;
 85 |   3、用户成功登录并跳转到首页时,根据刚刚生成的路由对象,渲染左侧的菜单;即,不同的用户看到的菜单是不一样的;
 86 |   
 87 |   用户点击登录和退出之后的业务逻辑分析:
 88 |   1、用户点击登录按钮,通过路由导航钩子router.beforeEach()函数确定下一步的跳转逻辑,如下:
 89 |    1.1、用户已经登录成功过,并从cookie中拿到了token值;
 90 |      1.1.1、用户访问登录页面,直接定位到登录页面;
 91 |      1.1.1、用户访问非登录页面,需要根据用户是否有roles信息,进行不同的业务逻辑,如下:
 92 |         (1)、初始情况下,用户roles信息为空;
 93 |             1.通过getUserInfo()函数,根据token拉取用户信息;并通过store将该用户roles,name,avatar信息存储于vuex;
 94 |             2.通过store.dispatch('GenerateRoutes', { roles })去重新过滤和生成路由,通过router.addRoutes()合并路由表;   
 95 |             3.如果在获取用户信息接口时出现错误,则调取store.dispatch('LogOut')接口,返回到login页面;
 96 |           
 97 |         (2)、用户已经拥有roles信息;
 98 |             1.点击页面路由,通过roles权限判断 hasPermission()。如果用户有该路由权限,直接跳转对应的页面;如果没有权限,则跳转至401提示页面;
 99 |   
100 |   2.用户点击退出,token已被清空
101 |     1.如果设置了白名单用户,则直接跳转到相应的页面;
102 |     2.反之,则跳转至登录页面;
103 |  */


--------------------------------------------------------------------------------
/src/router/topRouter.js:
--------------------------------------------------------------------------------
 1 | 
 2 | export const topRouterMap = [
 3 |     {
 4 |         'parentName':'infoShow',
 5 |         'data':[
 6 |             {
 7 |                 path: 'infoShow1',
 8 |                 name: 'infoShow1',
 9 |                 meta: {
10 |                     title: '个人信息子菜单1',
11 |                     icon: 'fa-asterisk',
12 |                     routerType: 'topmenu'
13 |                 },
14 |                 component: () => import('@/page/infoManage/infoShow')
15 |             },
16 |             {
17 |                 path: 'infoShow2',
18 |                 name: 'infoShow2',
19 |                 meta: {
20 |                     title: '个人信息子菜单2',
21 |                     icon: 'fa-asterisk',
22 |                     routerType: 'topmenu'
23 |                 },
24 |                 component: () => import('@/page/fundList/moneyData')
25 |             },
26 |             {
27 |                 path: 'infoShow3',
28 |                 name: 'infoShow3',
29 |                 meta: {
30 |                     title: '个人信息子菜单3',
31 |                     icon: 'fa-asterisk',
32 |                     routerType: 'topmenu'
33 |                 },
34 |                 component: () => import('@/page/fundList/moneyData')
35 |             },
36 |             {
37 |                 path: 'infoShow4',
38 |                 name: 'infoShow4',
39 |                 meta: {
40 |                     title: '个人信息子菜单4',
41 |                     icon: 'fa-asterisk',
42 |                     routerType: 'topmenu'
43 |                 },
44 |                 component: () => import('@/page/fundList/moneyData')
45 |             },
46 |             {
47 |                 path: 'infoShow5',
48 |                 name: 'infoShow5',
49 |                 meta: {
50 |                     title: '个人信息子菜单5',
51 |                     icon: 'fa-asterisk',
52 |                     routerType: 'topmenu'
53 |                 },
54 |                 component: () => import('@/page/fundList/moneyData')
55 |             }
56 |         ]
57 |     },
58 |     {
59 |         'parentName':'infoModify',
60 |         'data':[
61 |             {
62 |                 path:'infoModify1',
63 |                 name:'infoModify1',
64 |                 meta:{
65 |                     title:'修改信息子菜单1',
66 |                     icon:'fa-asterisk',
67 |                     routerType:'topmenu'
68 |                 },
69 |                 component: () => import('@/page/infoManage/infoModify')
70 |             },
71 |             {
72 |                 path:'infoModify2',
73 |                 name:'infoModify2',
74 |                 meta:{
75 |                     title:'修改信息子菜单2',
76 |                     icon:'fa-asterisk',
77 |                     routerType:'topmenu'
78 |                 },
79 |                 component: () => import('@/page/fundList/moneyData')
80 |             },
81 |             {
82 |                 path:'infoModify3',
83 |                 name:'infoModify3',
84 |                 meta:{
85 |                     title:'修改信息子菜单3',
86 |                     icon:'fa-asterisk',
87 |                     routerType:'topmenu'
88 |                 },
89 |                 component: () => import('@/page/fundList/moneyData')
90 |             }
91 |         ]
92 |     }
93 | ]
94 | 
95 | 
96 | 


--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
 1 | import Vue from 'vue'
 2 | import Vuex from 'vuex'
 3 | if(process.env.NODE_ENV === "development"){
 4 |     Vue.use(Vuex) 
 5 | } 
 6 | 
 7 | import user from './modules/user'
 8 | import permission from './modules/permission'
 9 | import money from './modules/money'
10 | import menu from './modules/menu'
11 | 
12 | export default new Vuex.Store({
13 |     modules: {
14 |         user,
15 |         permission,
16 |         money,
17 |         menu
18 |     }
19 | });
20 | 
21 | 


--------------------------------------------------------------------------------
/src/store/modules/menu.js:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | const types = {
 4 |     HANDLE_LEFT_MENU:'HANDLE_LEFT_MENU',  // 收缩左侧菜单
 5 |     INIT_LEFT_MENU:'INIT_LEFT_MENU',     // 初始化左侧菜单
 6 |     SET_LEFT_COLLAPSE:"SET_LEFT_COLLAPSE", // 改变左边菜单的收缩宽度
 7 |     SET_FOOTER_SHOW:"SET_FOOTER_SHOW", // 显示隐藏底部layout
 8 | }
 9 | const menu = { 
10 |     state :{
11 |         minLeftMenuWidth:35,
12 |         maxLeftMenuWidth:180,
13 |         sidebar: {
14 |             opened: true,  
15 |             width: 180
16 |         },
17 |         isCollapse:false, // 菜单默认展开
18 |         isFooter:false
19 |     },
20 |     getters : {
21 |         sidebar:state => state.sidebar,
22 |         isCollapse:state => state.isCollapse,
23 |         isFooter:state => state.isFooter
24 |     },
25 |     mutations:{
26 |         [types.HANDLE_LEFT_MENU] (state) {  
27 |             if(state.sidebar.opened){//true
28 |                 state.sidebar.width = state.minLeftMenuWidth;
29 |             }else{
30 |                 state.sidebar.width = state.maxLeftMenuWidth;
31 |             }
32 |            state.sidebar.opened = !state.sidebar.opened
33 |         },
34 |         [types.INIT_LEFT_MENU] (state) {  
35 |             state.sidebar = state.sidebar
36 |         },
37 |         [types.SET_LEFT_COLLAPSE] (state) {  
38 |             state.isCollapse = !state.isCollapse 
39 |         },
40 |         [types.SET_FOOTER_SHOW] (state) {  
41 |             state.isFooter = true
42 |         }
43 |        
44 |     },
45 |     actions:{
46 |         handleLeftMenu:({ commit }) => {  
47 |            commit(types.HANDLE_LEFT_MENU)  
48 |         },
49 |         initLeftMenu:({ commit }) => {  
50 |            commit(types.INIT_LEFT_MENU)  
51 |         },
52 |         setLeftCollapse:({ commit}) => {  
53 |            commit(types.SET_LEFT_COLLAPSE)  
54 |         }
55 |     }
56 |     
57 | }
58 | 
59 | 
60 | export default menu;


--------------------------------------------------------------------------------
/src/store/modules/money.js:
--------------------------------------------------------------------------------
 1 | 
 2 | import { getMoneyIncomePay } from '@/api/money'  // 导入资金信息相关接口
 3 | 
 4 | const money = {
 5 |   state: {
 6 |      addFundDialog: {
 7 |         title:'新增资金信息',
 8 |         type:'add'
 9 |      },
10 |      search: {
11 |         startTime:'',
12 |         endTime:'',
13 |         name:''
14 |      },
15 |      searchBtnDisabled: true
16 |   },
17 |   getters:{
18 |     addFundDialog: state => state.addFundDialog,
19 |     search: state => state.search,
20 |     searchBtnDisabled: state => state.searchBtnDisabled,
21 |   },
22 |   mutations: {
23 |     SET_DIALOG_TITLE: (state, type) => {
24 |       if(type === 'add'){
25 |         state.addFundDialog.title = '新增资金信息'
26 |         state.addFundDialog.type = 'add'
27 |       }else{
28 |         state.addFundDialog.title = '编辑资金信息'
29 |         state.addFundDialog.type = 'edit'
30 |       }
31 |     },
32 |     SET_SEARCH : (state, payload) => {
33 |        state.search = payload;
34 |     },
35 |     SET_SEARCHBTN_DISABLED : (state, payload) => {
36 |       state.searchBtnDisabled = payload;
37 |    }
38 |   },
39 |   actions: {
40 |      // 获取资金列表
41 |      GetMoneyIncomePay({commit},reqData) {
42 |         return new Promise(resolve => {
43 |             getMoneyIncomePay(reqData).then(response => {
44 |                 const data = response.data
45 |                 resolve(data)
46 |             })
47 |         })
48 |     } 
49 | 
50 | 
51 |   }
52 | }
53 | 
54 | export default money
55 | 


--------------------------------------------------------------------------------
/src/store/modules/permission.js:
--------------------------------------------------------------------------------
  1 | import { asyncRouterMap, constantRouterMap } from '@/router'
  2 | import { topRouterMap } from "@/router/topRouter";
  3 | import * as mutils from '@/utils/mUtils'
  4 | 
  5 | 
  6 | 
  7 | // 循环追加顶栏菜单
  8 | function addTopRouter(){
  9 |   asyncRouterMap.forEach( (item) => {
 10 |     if(item.children && item.children.length >= 1){
 11 |       item.children.forEach((sitem) => {
 12 |        topRouterMap.forEach((citem) => {
 13 |           if(sitem.name === citem.parentName){
 14 |               let newChildren = item.children.concat(citem.topmenulist); // arr
 15 |               item.children = newChildren;
 16 |           }
 17 |        })
 18 |       })
 19 |     }
 20 |   })
 21 |   return asyncRouterMap;
 22 | }
 23 | 
 24 |  // 获取到当前路由对应顶部子菜单
 25 |  function filterTopRouters(data){
 26 |     let topRouters = topRouterMap.find((item)=>{
 27 |        return item.parentName === data.name
 28 |     })
 29 |     if(!mutils.isEmpty(topRouters)){
 30 |        return topRouters.topmenulist;
 31 |     }
 32 | }
 33 | 
 34 | /**
 35 |  * 通过meta.role判断是否与当前用户权限匹配
 36 |  * @param roles
 37 |  * @param route
 38 |  */
 39 | function hasPermission(roles, route) {
 40 |   // roles为权限身份数组
 41 |   if (route.meta && route.meta.roles) {
 42 |     return roles.some(role => route.meta.roles.indexOf(role) >= 0)
 43 |   } else {
 44 |     return true
 45 |   }
 46 | }
 47 | 
 48 | /**
 49 |  * 递归过滤异步路由表,返回符合用户角色权限的路由表
 50 |  * @param asyncRouterMap
 51 |  * @param roles
 52 |  */
 53 | function filterAsyncRouter(asyncRouterMap, roles) {
 54 |   // 返回满足条件的子路由对象
 55 |   const accessedRouters = asyncRouterMap.filter(route => {
 56 |     if (hasPermission(roles, route)) {
 57 |       if (route.children && route.children.length) {
 58 |         // route.children重新过滤赋值;
 59 |         route.children = filterAsyncRouter(route.children, roles)
 60 |       }
 61 |       return true // 返回该权限路由对象;
 62 |     }
 63 |     return false
 64 |   })
 65 |   return accessedRouters
 66 | }
 67 | 
 68 | 
 69 | const permission = {
 70 |   state: {
 71 |     routers: constantRouterMap,
 72 |     addRouters: [],
 73 |     topRouters:[],
 74 |     topTitle:'',
 75 |     menuIndex:0
 76 |   },
 77 |   getters:{
 78 |     permission_routers: state => state.routers, // 所有路由
 79 |     addRouters: state => state.addRouters,  // 权限过滤路由
 80 |     topRouters: state => state.topRouters,  // 顶部三级路由
 81 |     topTitle:state => state.topTitle, // 顶部的title
 82 |     menuIndex:state => state.menuIndex, // 顶部菜单的index
 83 |   },
 84 |   mutations: {
 85 |     SET_ROUTERS: (state, routers) => {
 86 |       state.addRouters = routers // 权限路由
 87 |       state.routers = constantRouterMap.concat(routers) // 总路由
 88 |     },
 89 |     CLICK_INNER_LEFT_MENU:(state,data) => { // titleList:arr
 90 |         state.topRouters = data.titleList;
 91 |     },
 92 |     CLICK_TOP_MENU:(state,data) => {
 93 |       state.topTitle = data.title
 94 |       state.menuIndex = data.menuIndex
 95 | 
 96 |     },
 97 |   },
 98 |   actions: {
 99 |     // 根据角色,重新设置权限路由;并保存到vuex中,SET_ROUTERS;
100 |     GenerateRoutes({ commit }, data) {
101 |       return new Promise(resolve => {
102 |         let roles = data.roles;
103 |         let accessedRouters = '';
104 |         if (roles.indexOf('admin') >= 0) {
105 |           // 如果是管理员,直接将权限路由赋值给新路由;
106 |           accessedRouters = asyncRouterMap
107 |         } else {
108 |           // 非管理员用户,如roles:['editor','developer'],则需要过滤权限路由数据
109 |           accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
110 |         }
111 |         commit('SET_ROUTERS', accessedRouters)
112 |         resolve()
113 |       })
114 |     },
115 |     ClickLeftInnerMenu({ commit },data) {
116 |       commit('CLICK_INNER_LEFT_MENU',data)
117 |     },
118 |     ClickTopMenu({ commit },data) {
119 |       commit('CLICK_TOP_MENU',data)
120 |     }
121 |   }
122 | }
123 | 
124 | export default permission
125 | 


--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
 1 | 
 2 | import * as mUtils from '@/utils/mUtils'
 3 | import { logout ,getUserInfo } from '@/api/user'  // 导入用户信息相关接口
 4 | import { getToken, setToken, removeToken } from '@/utils/auth'
 5 | 
 6 | 
 7 | const user  = {
 8 |   state : {
 9 |     name:'',
10 |     avatar:'',
11 |     token: getToken('Token'),
12 |     roles: [],
13 |     browserHeaderTitle: mUtils.getStore('browserHeaderTitle') || '小爱Admin'
14 |   },
15 |   getters : {
16 |     token: state => state.token,
17 |     roles: state => state.roles,
18 |     avatar: state => state.avatar,
19 |     name: state => state.name,
20 |     browserHeaderTitle: state => state.browserHeaderTitle,
21 |   },
22 |   mutations: {
23 |     SET_ROLES: (state, roles) => {
24 |         state.roles = roles
25 |     },
26 |     SET_BROWSERHEADERTITLE: (state, action) => {
27 |         state.browserHeaderTitle = action.browserHeaderTitle
28 |     },
29 |     SET_NAME: (state, name) => {
30 |       state.name = name
31 |     },
32 |     SET_AVATAR: (state, avatar) => {
33 |       state.avatar = avatar
34 |     }
35 |   },
36 |   actions:{
37 |       //登出
38 |       LogOut({ commit, reqData }) {
39 |         return new Promise((resolve, reject) => {
40 |           logout(reqData).then(response => {
41 |             commit('SET_ROLES', [])
42 |             removeToken('Token')
43 |             resolve()
44 |           })
45 |         })
46 |       },
47 |       // 动态修改权限;本实例中,role和token是相同的;
48 |       ChangeRoles({ commit }, role) {
49 |         return new Promise(resolve => {
50 |           const token = role;
51 |           setToken("Token",token)
52 |           getUserInfo({"token":token}).then(res => {
53 |             let data = res.data.userList;
54 |             commit('SET_ROLES', data.roles)
55 |             commit('SET_NAME', data.name)
56 |             commit('SET_AVATAR', data.avatar)
57 |             resolve()
58 |           })
59 |         })
60 |       },
61 |       
62 |   }
63 | }
64 | 
65 | export default user;
66 | 
67 | /**
68 |  * 1、用户退出,需要调取后台接口吗?后台具体的业务逻辑是什么?
69 |  * 
70 |  * 
71 |  */


--------------------------------------------------------------------------------
/src/style/common.less:
--------------------------------------------------------------------------------
  1 | body, div, span, header, footer, nav, section, aside, article, ul, dl, dt, dd, li, a, p, h1, h2, h3, h4,h5, h6, i, b, textarea, button, input, select, figure, figcaption {
  2 |     padding: 0;
  3 |     margin: 0;
  4 |     list-style: none;
  5 |     font-style: normal;
  6 |     text-decoration: none;
  7 |     border: none;
  8 |     font-family: "Microsoft Yahei",sans-serif;
  9 |     -webkit-tap-highlight-color:transparent;
 10 |     -webkit-font-smoothing: antialiased;
 11 |     &:focus {
 12 |         outline: none;
 13 |     }
 14 | }
 15 | 
 16 | 
 17 | 
 18 | input[type="button"], input[type="submit"], input[type="search"], input[type="reset"] {
 19 |     -webkit-appearance: none;
 20 | }
 21 | 
 22 | textarea { -webkit-appearance: none;}   
 23 | 
 24 | html,body{
 25 |     height: 100%;
 26 |     width: 100%;
 27 | }
 28 | 
 29 | .clear:after{
 30 |     content: '';
 31 |     display: block;
 32 |     clear: both;
 33 | }
 34 | 
 35 | .clear{
 36 |     zoom:1;
 37 | }
 38 | 
 39 | .back_img{
 40 |     background-repeat: no-repeat;
 41 |     background-size: 100% 100%;
 42 | }
 43 | 
 44 | .margin{
 45 |     margin: 0 auto;
 46 | }
 47 | 
 48 | .left{
 49 |     float: left;
 50 | }
 51 | 
 52 | .right{
 53 |     float: right;
 54 | }
 55 | 
 56 | .hide{
 57 |     display: none;
 58 | }
 59 | .show{
 60 |     display: block;
 61 | }
 62 | .text_center{
 63 |     text-align: center;
 64 | }
 65 | .text_left{
 66 |     text-align: left;
 67 | }
 68 | .text_right{
 69 |     text-align: right;
 70 | }
 71 | .ver_align{
 72 |    vertical-align: middle;
 73 | }
 74 | .rflex{
 75 |     display: flex;
 76 |     flex-direction: row;
 77 | }
 78 | .cflex{
 79 |     display: flex;
 80 |     flex-direction: column;
 81 | }
 82 | .wflex{
 83 |     flex:1;
 84 | }
 85 | 
 86 | 
 87 | // 公共的color
 88 | @left-bgColor:#2a3542;  // 左侧菜单背景颜色;
 89 | 
 90 | 
 91 | 
 92 | //页面的公共样式
 93 | .ellipsis{
 94 |     overflow: hidden;
 95 |     text-overflow: ellipsis;
 96 |     white-space: nowrap;
 97 | }
 98 | .el-table .odd-row {
 99 |    background:#eaefea;
100 | }
101 | .el-table .even-row {
102 |    background: #d9e6f1;
103 | }
104 | 
105 | .fillcontain{
106 |     padding: 20px;
107 |     box-sizing: border-box;
108 | }
109 | .fillcontainer{
110 |     width:100%;
111 |     text-align:center;
112 |     padding: 20px;
113 |     box-sizing: border-box;
114 |     background: #fff;
115 | }
116 | .tabContainer{
117 |     padding: 10px;
118 |     box-sizing: border-box;
119 |     background: #fff;
120 |     border-radius: 2px;
121 | }
122 | .echartsPosition{
123 |     position: relative;
124 |     width: 100%;
125 |     height: 100%;
126 |     box-shadow: 0 0 10px #FF6C60;
127 |     border-radius: 10px;
128 |     padding: 20px;
129 |     box-sizing: border-box;
130 | }
131 | .shareItem{
132 |     .shareArea{
133 |       margin-right: 30px;
134 |       flex:1;
135 |       .shareTitle{
136 |         border-left: 4px solid #a9d86e;
137 |       }
138 |     }
139 |     .sharelast{
140 |       margin-right: 0;
141 |     }
142 |   }
143 | 
144 |   .saleColor{
145 |     color:@saleColor;
146 |   }
147 |   .taxColor{
148 |     color:@taxColor;
149 |   }
150 |   .extenedColor{
151 |     color: @extenedColor;
152 |   }
153 |   .likeColor{
154 |     color: @likeColor;
155 |   }
156 |   .saleBgcolor{
157 |     background:@saleColor;
158 |   }
159 |   .taxBgcolor{
160 |     background: @taxColor;
161 |   }
162 |   .extenedBgcolor{
163 |     background: @extenedColor;
164 |   }
165 |   .likeBgcolor{
166 |     background: @likeColor;
167 |   }
168 |   .linkBgColor{
169 |     background: @linkColor;
170 |   }
171 |   .keleBgColor{
172 |     background: @keleColor;
173 |   }
174 | 
175 | 
176 | 


--------------------------------------------------------------------------------
/src/style/scrollBar.less:
--------------------------------------------------------------------------------
 1 | // 竖向滚动条
 2 | .is-scroll-right::-webkit-scrollbar,
 3 | .is-scroll-left::-webkit-scrollbar,
 4 | .el-scrollbar::-webkit-scrollbar {
 5 |     width: 4px;
 6 |     height: 8px !important;
 7 | }
 8 | /*滚动条的背景区域的内阴影*/
 9 | .el-scrollbar::-webkit-scrollbar-track {
10 |     //  box-shadow:0px 1px 3px rgba(0,0,0,0.3) inset;
11 |      /*滚动条的背景区域的圆角*/
12 |      border-radius: 10px;  
13 |      /*滚动条的背景颜色*/
14 |      background-color: #ddd;
15 | }
16 | /*滑块 滚动条的内阴影*/
17 | .is-scroll-right::-webkit-scrollbar-thumb,
18 | .is-scroll-left::-webkit-scrollbar-thumb,
19 | .el-scrollbar::-webkit-scrollbar-thumb {
20 |     box-shadow:0px 1px 3px rgba(0,0,0,0.3) inset;
21 |     background-color:#FF6C60;
22 |     border-radius: 4px;
23 | }
24 | 
25 | // 横向滚动条
26 | ::-webkit-scrollbar:horizontal {
27 |     width:0;
28 |     height:10px !important;
29 | }
30 | ::-webkit-scrollbar-track:horizontal {
31 |     background-color:#fff;
32 | }
33 | 
34 | 
35 | 
36 | 
37 | 
38 | 
39 | // .el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar {
40 | //     opacity: 1;
41 | //     -webkit-transition: opacity 340ms ease-out;
42 | //     transition: opacity 340ms ease-out
43 | // }
44 | 
45 | // .el-scrollbar__wrap {
46 | //     overflow: scroll;
47 | //     height: 100%;
48 | // }
49 | 
50 | // .el-scrollbar__wrap--hidden-default::-webkit-scrollbar {
51 | //     width: 0;
52 | //     height: 0
53 | // }
54 | 
55 | // .el-scrollbar__thumb {
56 | //     position: relative;
57 | //     display: block;
58 | //     width: 0;
59 | //     height: 0;
60 | //     cursor: pointer;
61 | //     border-radius: inherit;
62 | //     background-color: rgba(144,147,153,.3);
63 | //     -webkit-transition: .3s background-color;
64 | //     transition: .3s background-color
65 | // }
66 | 
67 | // .el-scrollbar__thumb:hover {
68 | //     background-color: rgba(144,147,153,.5)
69 | // }
70 | 
71 | // .el-scrollbar__bar {
72 | //     position: absolute;
73 | //     right: 2px;
74 | //     bottom: 2px;
75 | //     z-index: 1;
76 | //     border-radius: 4px;
77 | //     opacity: 0;
78 | //     -webkit-transition: opacity 120ms ease-out;
79 | //     transition: opacity 120ms ease-out
80 | // }
81 | 
82 | // .el-scrollbar__bar.is-vertical {
83 | //     width: 6px;
84 | //     top: 2px
85 | // }
86 | 
87 | // .el-scrollbar__bar.is-vertical>div {
88 | //     width: 100%
89 | // }
90 | 
91 | // .el-scrollbar__bar.is-horizontal {
92 | //     height: 6px;
93 | //     left: 2px
94 | // }
95 | 
96 | // .el-scrollbar__bar.is-horizontal>div {
97 | //     height: 100%
98 | // }
99 | 


--------------------------------------------------------------------------------
/src/style/variables.less:
--------------------------------------------------------------------------------
 1 | // 基础颜色
 2 | @saleColor:#FFA3A1;
 3 | @taxColor:#84d9d2;
 4 | @extenedColor:#87DE75;
 5 | @likeColor:#a5e7f0;
 6 | @linkColor:#93b7e3;
 7 | @keleColor:#edafda;
 8 | 
 9 | 
10 | // 背景颜色
11 | @white: #ffffff;
12 | @primary-color: #313653;
13 | @primary-color-light: fade(lighten(@primary-color, 5%), 15%);
14 | // 顶部背景色
15 | @layout-header-background:@primary-color;
16 | // 左边菜单light颜色
17 | @layout-sider-background-light: #f9f9f9;
18 | // 字体颜色
19 | @text-color: #000000;
20 | @table-selected-row-bg: #fbfbfb;
21 | @primary-2: @primary-color-light;
22 | // 基础圆角
23 | @border-radius-base: 2px;
24 | // 输入框后缀背景色
25 | @input-addon-bg: @primary-color;
26 | 
27 | 


--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
 1 | import Cookies from 'js-cookie'
 2 | 
 3 | // const TokenKey = 'Admin-Token'
 4 | 
 5 | export function getToken(TokenKey) {
 6 |   return Cookies.get(TokenKey)
 7 | }
 8 | 
 9 | export function setToken(TokenKey,token) {
10 |   return Cookies.set(TokenKey, token)
11 | }
12 | 
13 | export function removeToken(TokenKey) {
14 |   return Cookies.remove(TokenKey)
15 | }
16 | 


--------------------------------------------------------------------------------
/src/utils/axios.js:
--------------------------------------------------------------------------------
 1 | import axios from 'axios'
 2 | import { Message, MessageBox } from 'element-ui'
 3 | import store from '../store'
 4 | import { getToken } from '@/utils/auth'
 5 | 
 6 | // 创建axios实例
 7 | let service =  axios.create({
 8 |   baseURL: process.env.BASE_API, // api的base_url
 9 |   timeout: 5000 // 请求超时时间
10 | })
11 | // request拦截器
12 | service.interceptors.request.use(config => {
13 |   if (store.getters.token) {
14 |     config.headers = {
15 |       'Authorization' : "Token " + getToken('Token'), //携带权限参数
16 |      };
17 |   }
18 |   return config
19 | }, error => {
20 |   Promise.reject(error)
21 | })
22 | 
23 | // respone拦截器
24 | service.interceptors.response.use(
25 |   response => {
26 |    /**
27 |     * code:200,接口正常返回;
28 |     */
29 |     const res = response.data
30 |     if (res.code !== 200) {
31 |       Message({
32 |         message: res.message,
33 |         type: 'error',
34 |         duration: 5 * 1000
35 |       })
36 |       // 根据服务端约定的状态码:5001:非法的token; 5002:其他客户端登录了; 5004:Token 过期了;
37 |       if (res.code === 5001 || res.code === 5002 || res.code === 5004) {
38 |           MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
39 |             confirmButtonText: '重新登录',
40 |             cancelButtonText: '取消',
41 |             type: 'warning'
42 |           }).then(() => {
43 |             store.dispatch('LogOut').then(() => {
44 |               location.reload()// 为了重新实例化vue-router对象 避免bug
45 |             })
46 |           })
47 |       }
48 |       return Promise.reject('error')
49 |     } else { // res.code === 200,正常返回数据
50 |         return response.data
51 |     }
52 |   },
53 |   error => {
54 |     Message({
55 |       message: error.message,
56 |       type: 'error',
57 |       duration: 5 * 1000
58 |     })
59 |     return Promise.reject(error)
60 |   }
61 | )
62 | 
63 | export default service
64 | 


--------------------------------------------------------------------------------
/src/utils/env.js:
--------------------------------------------------------------------------------
 1 | const github = 'https://github.com/wdlhao/vue2-element-touzi-admin';
 2 | const appUrl = process.env.VUE_APP_URL  // development和production环境是不同的
 3 | const shareUrl = 'https://juejin.im/post/5d0b4d28f265da1baf7cf293'
 4 | const shareTitle = '用Vue-cli3+element+mockjs 实现后台管理权限系统及顶栏三级菜单显示';
 5 | const weibo = {
 6 |     'weiboUrl': 'http://service.weibo.com/share/share.php',
 7 |     'weiboAppkey' : '2003962826',
 8 |     'pic':'https://user-gold-cdn.xitu.io/2019/6/20/16b7425dfa01dbf3?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1'
 9 | }
10 | const qq = {
11 |     'baseUrl':'http://connect.qq.com/widget/shareqq/index.html',
12 |     'pic':'https://user-gold-cdn.xitu.io/2019/6/20/16b7425dfa01dbf3?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1',
13 |     'desc':'最近完成了我的后台管理系统权限功能的实现,同时觉得后台系统所有的菜单都左置,会限制菜单的扩展,因此我改进了三级菜单的显示。',
14 |     'summary':'文章梗概',
15 |     'source':'qzone'
16 | }
17 | const qqZone = {
18 |     'baseUrl':'https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey',
19 |     'pic':'https://user-gold-cdn.xitu.io/2019/6/20/16b7425dfa01dbf3?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1',
20 |     'desc':'最近完成了我的后台管理系统权限功能的实现,同时觉得后台系统所有的菜单都左置,会限制菜单的扩展,因此我改进了三级菜单的显示。',
21 |     'summary':'文章梗概',
22 |     'site':'qzone'
23 | }
24 | const douban = {
25 |     'baseUrl':'https://www.douban.com/share/service',
26 |     'pic':'https://user-gold-cdn.xitu.io/2019/6/20/16b7425dfa01dbf3?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1',
27 | }
28 | export {
29 |     appUrl,
30 |     shareUrl,
31 |     shareTitle,
32 |     weibo,
33 |     qq,
34 |     qqZone,
35 |     douban,
36 |     github
37 | }


--------------------------------------------------------------------------------
/src/utils/mUtils.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * 存储localStorage
  3 |  */
  4 | export const setStore = (name, content) => {
  5 | 	if (!name) return;
  6 | 	if (typeof content !== 'string') {
  7 | 		content = JSON.stringify(content);
  8 | 	}
  9 | 	window.localStorage.setItem(name, content);
 10 | }
 11 | 
 12 | /**
 13 |  * 获取localStorage
 14 |  */
 15 | export const getStore = name => {
 16 | 	if (!name) return;
 17 | 	var value = window.localStorage.getItem(name);
 18 |     if (value !== null) {
 19 |         try {
 20 |             value = JSON.parse(value);
 21 |         } catch (e) {
 22 |             value = value;
 23 |         }
 24 |     }
 25 |     return value;
 26 | }
 27 | 
 28 | /**
 29 |  * 删除localStorage
 30 |  */
 31 | export const removeStore = name => {
 32 | 	if (!name) return;
 33 | 	window.localStorage.removeItem(name);
 34 | }
 35 | 
 36 | /**
 37 |  * 让整数自动保留2位小数
 38 |  */
 39 | // export const returnFloat = value => { 
 40 | //     var value=Math.round(parseFloat(value)*100)/100; 
 41 | //     var xsd=value.toString().split("."); 
 42 | //     if(xsd.length==1){ 
 43 | //         value=value.toString()+".00"; 
 44 | //         return value;   
 45 | //     } 
 46 | //     if(xsd.length>1){ 
 47 | //         if(xsd[1].length<2){ 
 48 | //             value=value.toString()+"0"; 
 49 | //         } 
 50 | //         return value; 
 51 | //     } 
 52 | // } 
 53 | /**
 54 |  * @param {date} 标准时间格式:Fri Nov 17 2017 09:26:23 GMT+0800 (中国标准时间)
 55 |  * @param {type} 类型
 56 |  *   type == 1 ---> "yyyy-mm-dd hh:MM:ss.fff"
 57 |  *   type == 2 ---> "yyyymmddhhMMss"
 58 |  *   type == '' ---> "yyyy-mm-dd hh:MM:ss"
 59 |  */
 60 | export const formatDate = (date, type) =>{
 61 |     var year = date.getFullYear();//年
 62 |     var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;//月
 63 |     var day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();//日
 64 |     var hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();//时
 65 |     var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();//分
 66 |     var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();//秒
 67 |     var milliseconds = date.getMilliseconds() < 10 ? "0" + date.getMilliseconds() : date.getMilliseconds() //毫秒
 68 |     if (type == 1) {
 69 |         return year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + "." + milliseconds;
 70 |     } else if(type == 2){
 71 |         return year+""+month+""+day+""+hour+""+minutes+""+seconds;
 72 |     }else if(type == 3){
 73 |         return year + "-" + month + "-" + day;
 74 |     }else {
 75 |         return year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds;
 76 |     }
 77 | }
 78 | /**
 79 |  * 时间转换:20150101010101 --> '2015-01-01 01:01:01'
 80 |  */
 81 | export const parseToDate = (timeValue) => {
 82 |     var result = "";
 83 |     var year = timeValue.substr(0, 4);
 84 |     var month = timeValue.substr(4, 2);
 85 |     var date = timeValue.substr(6, 2);
 86 |     var hour = timeValue.substr(8, 2);
 87 |     var minute = timeValue.substr(10, 2);
 88 |     var second = timeValue.substr(12, 2);
 89 |     result = year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
 90 |     return result;
 91 | }
 92 | /**
 93 |  * 判断空值
 94 |  */
 95 | export const isEmpty = (keys) => {
 96 |     if (typeof keys === "string") {
 97 |         keys = keys.replace(/\"|&nbsp;|\\/g, '').replace(/(^\s*)|(\s*$)/g, "");
 98 |         if (keys == "" || keys == null || keys == "null" || keys === "undefined" ) {
 99 |             return true;
100 |         } else {
101 |             return false;
102 |         }
103 |     } else if (typeof keys === "undefined") {  // 未定义
104 |         return true;
105 |     } else if (typeof keys === "number") {
106 |         return false;
107 |     }else if(typeof keys === "boolean"){
108 |         return false;
109 |     }else if(typeof keys == "object"){
110 |         if(JSON.stringify(keys )=="{}"){
111 |             return true;
112 |         }else if(keys == null){ // null
113 |             return true;
114 |         }else{
115 |             return false;
116 |         }
117 |     }
118 | 
119 |     if(keys instanceof Array && keys.length == 0){// 数组
120 |         return true;
121 |     }
122 | 
123 | }
124 | 
125 | /**
126 |  * 返回两位的小数的字符串
127 |  */
128 | export const toFixedNum = (num) => {
129 |     const tonum = Number(num).toFixed(2);
130 |     return tonum;
131 | }
132 | 
133 | export const showMessage = () =>{
134 |     this.$message({
135 |         showClose: true,
136 |         message: '对不起,您暂无此操作权限~',
137 |         type: 'success'
138 |     });
139 | }
140 | 
141 | /**
142 |  * 读取base64
143 |  */
144 | export const  readFile = file => {
145 |     console.log(file)
146 |     //var file = this.files[0];
147 |     //判断是否是图片类型
148 |     if (!/image\/\w+/.test(file.raw.type)) {
149 |         alert("只能选择图片");
150 |         return false;
151 |     }
152 |     var reader = new FileReader();
153 |     reader.readAsDataURL(file);
154 |     reader.onload = function (e) { 
155 |         var filedata = {
156 |             filename: file.name,
157 |             filebase64: e.target.result
158 |         }
159 |         alert(e.target.result)
160 |     }
161 | }
162 | 
163 | /**
164 |  * 动态插入css
165 |  */
166 | export const loadStyle = url => {
167 |     const link = document.createElement('link')
168 |     link.type = 'text/css'
169 |     link.rel = 'stylesheet'
170 |     link.href = url
171 |     const head = document.getElementsByTagName('head')[0]
172 |     head.appendChild(link)
173 |   }
174 |   /**
175 |    * 设置浏览器头部标题
176 |    */
177 |   export const setTitle = (title) => {
178 |     title = title ? `${title}` : '小爱Admin'
179 |     window.document.title = title
180 |   }
181 | 
182 |   export const param2Obj = url => {
183 |     const search = url.split('?')[1]
184 |     if (!search) {
185 |       return {}
186 |     }
187 |     return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
188 |   }
189 | 
190 |   //是否为正整数
191 | export const isInteger = (s) => {
192 |     var re = /^[0-9]+$/ ;
193 |     return re.test(s)
194 | }   
195 | 
196 | export const setContentHeight = (that,ele,height) => {
197 |     that.$nextTick(() => {
198 |         ele.style.height =   (document.body.clientHeight - height)+'px'
199 |     })
200 |   }
201 | 


--------------------------------------------------------------------------------
/src/utils/share.js:
--------------------------------------------------------------------------------
 1 | import { weibo,qq,qqZone,douban,shareUrl,shareTitle } from "@/utils/env";
 2 | import * as mutils from "@/utils/mUtils";
 3 | 
 4 | function getParamsUrl(obj){
 5 |     let paramsUrl = '';
 6 |     for(let key in obj){
 7 |         paramsUrl += key+'='+obj[key]+'&'
 8 |     }
 9 |     return paramsUrl;
10 | }
11 | 
12 | export function shareConfig(type,obj){
13 |     let baseUrl = '';
14 |     if(mutils.isEmpty(obj)){
15 |         obj = {};
16 |     }
17 |     switch(type){
18 |         case 'weibo':
19 |             const weiboData = {
20 |                 'url':shareUrl, // 内容链接,默认当前页面location
21 |                 'title':shareTitle, // 可选参数, 默认当前页title
22 |                 'pic':obj.pic || weibo.pic, // 分享图片的路径(可选),多张图片通过"||"分开。
23 |                 'count':'y', /**是否显示分享数,y|n(可选)*/
24 |                 'searchPic':true // 是否要自动抓取页面上的图片。true|falsetrue:自动抓取,false:不自动抓取。
25 |             }
26 |             baseUrl = weibo.weiboUrl+'?appkey='+weibo.weiboAppkey+getParamsUrl(weiboData);
27 |             window.open(baseUrl,'_blank');
28 |             break;
29 |         case 'qq':
30 |             const qqData = {
31 |                 'url':shareUrl,
32 |                 'title':shareTitle,
33 |                 'pics':obj.pic || qq.pic,  //QZone接口暂不支持发送多张图片的能力,若传入多张图片,则会自动选入第一张图片作为预览图。
34 |                 'source':obj.source || qq.source, // 分享来源
35 |                 'desc':obj.desc || qq.desc, 
36 |                 'summary':obj.summary || qq.summary,
37 |             }
38 |             baseUrl = qq.baseUrl+'?'+getParamsUrl(qqData)
39 |             window.open(baseUrl,'_blank');
40 |             break;
41 |         case 'qqZone':
42 |             const qqZoneData = {
43 |                 'url':shareUrl,
44 |                 'title':shareTitle,
45 |                 'pics':obj.pic || (qqZone.pic).split(','), 
46 |                 'sharesource':obj.sharesource || qqZone.sharesource, // 分享来源
47 |                 'desc':obj.desc || qqZone.desc, 
48 |                 'summary':obj.summary || qqZone.summary,
49 |             }
50 |             baseUrl = qqZone.baseUrl+'?'+getParamsUrl(qqZoneData)
51 |             window.open(baseUrl,'_blank');
52 |             break;
53 |         case 'douban':
54 |             const doubanData = {
55 |                 'href':shareUrl,
56 |                 'name':shareTitle,
57 |                 'image':obj.pic || douban.pic,
58 |             }
59 |             baseUrl = douban.baseUrl+'?'+getParamsUrl(doubanData)
60 |             window.open(baseUrl,'_blank');
61 |             break;
62 |     }
63 | }


--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
  1 | 
  2 | const TerserPlugin = require('terser-webpack-plugin')  // 用于在生成环境剔除debuger和console
  3 | const CompressionPlugin = require("compression-webpack-plugin"); // gzip压缩,优化http请求,提高加载速度
  4 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin // 代码分析工具 
  5 | const path = require('path');
  6 | const resolve = dir => {
  7 |   return path.join(__dirname, dir);
  8 | };
  9 | 
 10 | const env = process.env.NODE_ENV
 11 | let target = process.env.VUE_APP_URL  // development和production环境是不同的
 12 | 
 13 | const cdn = {
 14 |   // 开发环境
 15 |   dev: {
 16 |       css: [],
 17 |       js: [
 18 |       ]
 19 |   },
 20 |   // 生产环境
 21 |   build: {
 22 |       css: [
 23 |         'https://cdn.bootcss.com/element-ui/2.11.1/theme-chalk/index.css',
 24 |         'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
 25 |       ],
 26 |       js: [
 27 |         'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
 28 |         'https://cdn.bootcss.com/vue-router/3.1.2/vue-router.min.js',
 29 |         'https://cdn.bootcss.com/vuex/2.3.1/vuex.min.js',
 30 |         'https://cdn.bootcss.com/axios/0.19.0/axios.min.js',
 31 |         'https://cdn.bootcss.com/vue-i18n/8.13.0/vue-i18n.min.js',
 32 |         'https://cdn.bootcss.com/element-ui/2.11.1/index.js',
 33 |         'https://cdn.bootcss.com/echarts/3.8.5/echarts.min.js',
 34 |         'https://cdn.bootcss.com/Mock.js/1.0.1-beta3/mock-min.js',
 35 |         'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js',
 36 |         'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js'
 37 |       ]
 38 |   }
 39 | }
 40 | 
 41 | 
 42 | module.exports = {
 43 |   publicPath: process.env.NODE_ENV === "production" ? "/permission/" : "/",
 44 |   outputDir: './dist',
 45 |   assetsDir:'static',
 46 |   filenameHashing:true, // false 来关闭文件名哈希
 47 |   lintOnSave: false, // 关闭eslint
 48 |   // 打包时不生成.map文件
 49 |   productionSourceMap: false,
 50 |   devServer: {
 51 |     open: true,
 52 |     host: '0.0.0.0',
 53 |     port: 8808
 54 |     // 由于本项目数据通过easy-mock和mockjs模拟,不存在跨域问题,无需配置代理;
 55 |     // proxy: { 
 56 |     //   '/v2': {
 57 |     //       target: target,
 58 |     //       changeOrigin: true
 59 |     //   }
 60 |     // }
 61 |   },
 62 |    // webpack相关配置
 63 |   chainWebpack: (config) => {
 64 |     config.entry.app = ['./src/main.js']
 65 |     config.resolve.alias
 66 |       .set('@', resolve('src'))
 67 |       .set('cps', resolve('src/components'))
 68 |     // 关闭npm run build之后,This can impact web performance 警告
 69 |     config.performance
 70 |       .set('hints', false)
 71 |     // 移除 prefetch 插件
 72 |     config.plugins.delete("prefetch");
 73 |     // 移除 preload 插件
 74 |     config.plugins.delete('preload');
 75 |     // 压缩代码
 76 |     config.optimization.minimize(true);
 77 |     // 分割代码
 78 |     config.optimization.splitChunks({
 79 |         chunks: 'all'
 80 |     })    
 81 |     // 对图片进行压缩处理
 82 |     config.module
 83 |     .rule('images')
 84 |     .use('image-webpack-loader')
 85 |     .loader('image-webpack-loader')
 86 |     .options({
 87 |         disable: true, // webpack@2.x and newer
 88 |         quality: '65-80',
 89 |         speed: 4
 90 |     })
 91 |     .end()
 92 |     // 项目文件大小分析
 93 |     config.plugin('webpack-bundle-analyzer')
 94 |     .use(new BundleAnalyzerPlugin({
 95 |       openAnalyzer: false,   // 是否打开默认浏览器
 96 |       analyzerPort:8777
 97 |     }))
 98 | 
 99 |     // 对vue-cli内部的 webpack 配置进行更细粒度的修改。
100 |     // 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
101 |     config
102 |     .plugin('html')
103 |     .tap(args => {
104 |       if (process.env.NODE_ENV === 'production') {
105 |           args[0].cdn = cdn.build
106 |       }
107 |       if (process.env.NODE_ENV === 'development') {
108 |           args[0].cdn = cdn.dev
109 |       }
110 |       return args
111 |     })
112 |   },
113 |   configureWebpack:config => {
114 |     // 为生产环境修改配置...
115 |     if (process.env.NODE_ENV === 'production') {
116 |       // 忽略生产环境打包的文件
117 |       config.externals = {
118 |         "vue": "Vue",
119 |         "vue-router": "VueRouter",
120 |         "vuex": "Vuex",
121 |         "vue-i18n": "VueI18n",
122 |         "axios": "axios",
123 |         'element-ui': 'ELEMENT',
124 |         'echarts':'echarts',
125 |         'mockjs':'Mock',
126 |         'nprogress':'NProgress',
127 |         'js-cookie':'Cookies'
128 |       }
129 |       // 去除console来减少文件大小,效果同'UglifyJsPlugin'
130 |       new TerserPlugin({
131 |         cache: true,
132 |         parallel: true,
133 |         sourceMap: true, // Must be set to true if using source-maps in production
134 |         terserOptions: {
135 |           compress: {
136 |             warnings: false,
137 |             drop_console: true,
138 |             drop_debugger: true,
139 |             pure_funcs: ['console.log']
140 |           }
141 |         }
142 |       })
143 |       // 开启gzip压缩
144 |       config.plugins.push(new CompressionPlugin({
145 |         algorithm: 'gzip',
146 |         test: new RegExp("\\.(" + ["js", "css"].join("|") + ")
quot;), // 匹配文件扩展名
147 |         // threshold: 10240, // 对超过10k的数据进行压缩
148 |         threshold: 5120, // 对超过5k的数据进行压缩
149 |         minRatio: 0.8,
150 |         cache: true, // 是否需要缓存
151 |         deleteOriginalAssets:false  // true删除源文件(不建议);false不删除源文件
152 |       }))
153 | 
154 |     } else {
155 |       // 为开发环境修改配置...
156 | 
157 |     }
158 |   },
159 |    // 第三方插件配置
160 |   pluginOptions: {
161 | 
162 |   }
163 | }
164 | 
165 | 


--------------------------------------------------------------------------------