├── .env.local
├── .env.production
├── .gitignore
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── bg.jpg
├── favicon.ico
└── index.html
├── src
├── App.vue
├── api
│ ├── admin.js
│ ├── login.js
│ └── table.js
├── assets
│ ├── 404_images
│ │ ├── 404.png
│ │ └── 404_cloud.png
│ ├── iconfont
│ │ ├── demo.css
│ │ ├── iconfont.css
│ │ ├── iconfont.eot
│ │ ├── iconfont.js
│ │ ├── iconfont.svg
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ └── logo.png
├── components
│ ├── Breadcrumb
│ │ └── index.vue
│ ├── Hamburger
│ │ └── index.vue
│ └── SvgIcon
│ │ └── index.vue
├── main.js
├── permisssion.js
├── router.js
├── store
│ ├── getters.js
│ ├── index.js
│ ├── modules
│ │ ├── admin.js
│ │ ├── app.js
│ │ └── user.js
│ └── routePermission.js
├── styles
│ ├── element-ui.scss
│ ├── index.scss
│ ├── normalize.scss
│ ├── sidebar.scss
│ ├── transition.scss
│ └── variables.scss
├── utils
│ ├── auth.js
│ ├── index.js
│ ├── request.js
│ └── validate.js
└── views
│ ├── 404.vue
│ ├── admin
│ ├── manage-money.vue
│ └── manage-users.vue
│ ├── center
│ └── index.vue
│ ├── dashboard
│ └── index.vue
│ ├── display
│ ├── fiction.vue
│ ├── index.vue
│ └── technology.vue
│ ├── edit
│ ├── index.vue
│ └── test.js
│ ├── layout
│ ├── Layout.vue
│ ├── components
│ │ ├── AppMain.vue
│ │ ├── Navbar.vue
│ │ ├── Sidebar
│ │ │ ├── SidebarItem.vue
│ │ │ └── index.vue
│ │ └── index.js
│ └── mixin
│ │ └── ResizeHandler.js
│ ├── login
│ └── index.vue
│ ├── nested
│ ├── menu1
│ │ ├── index.vue
│ │ ├── menu1-1
│ │ │ └── index.vue
│ │ ├── menu1-2
│ │ │ ├── index.vue
│ │ │ ├── menu1-2-1
│ │ │ │ └── index.vue
│ │ │ └── menu1-2-2
│ │ │ │ └── index.vue
│ │ └── menu1-3
│ │ │ └── index.vue
│ └── menu2
│ │ └── index.vue
│ ├── register
│ └── index.vue
│ ├── table
│ └── index.vue
│ └── tree
│ └── index.vue
└── vue.config.js
/.env.local:
--------------------------------------------------------------------------------
1 | VUE_APP_BASE_API=/nodejsapi
2 | VUE_APP_PUBLIC_URL=/
3 | #VUE_LIFECIRCLE_EVENT=DEV
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | VUE_APP_BASE_API=/nodejsapi
2 | VUE_APP_PUBLIC_URL=http://vue.wtodd.wang
3 | VUE_LIFECIRCLE_EVENT=ANALYZE
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # Log files
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Editor directories and files
11 | .idea
12 | .vscode
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 | *.sw*
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-cli3.0-vueadmin
2 | 技术栈主要使用vue-cli3.0+vue+elementUI+vuex+axios。
3 | 这是一个基于手摸手系列,vueadmin-template 进行改造的版本----感谢作者风骚花裤衩。
4 |
5 | 由于是基于vue-cli3.0为基础进行的开发,所以同比vue-cli2会有区别:
6 | 1、项目的目录结构发生了变化,vue-cli3.0隐藏了webpack的配置文件,目录看起来非常的清爽简洁,在目标上追求0配置进行开发,将大部分时间用在开发上,避免在配置上浪费过多时间。但是个人风格配置无法避免,这里提供了一个vue.config.js 进行项目的配置;
7 |
8 | 2、线上预览地址 登陆:admin 123456
9 |
10 | ##注意事项:
11 | 1、由于个人风格原因,该项目去掉了eslint限制,需要的同学可以自己增加;
12 | 2、为了跑通整个项目,这里我使用nodejs写了几个接口进行验证,包括token、userinfo、list,并且使用cors开放了跨域,需要的同学可以直接使用,无需代理;
13 | 3、后端使用的是express + mongodb,地址为: https://github.com/adouwt/nodejsAPI ,用的是es6的语法写的后端服务,这个项目都在一直更新,如有需要,可以关注,私聊讨论。
14 | 4、可以直接注册用户,默认为dev角色,注册 ,admin登录进去,可以新建用户
15 | 如果这个项目对觉得OK,可以点击右上角的star啊
16 |
17 | ## 简单的描述下,从注册页面到注册成功跳转页面的数据流程
18 | ### views/register/index.vue 触发actions Register
19 | ### --> store/user.js的Register, 这个js调用了 api/login.js register方法 ,register方法返回一个request的promise对象
20 | ### --> utils/request.js promise resolve(或者reject)数据 给 store/user.js
21 | ### --> store/user.js register promise then(或者catch)方法接收到返回数据,然后 resolve(reject)response给 view/resgiter/index.vue
22 | ### --> view/resgiter/index.vue 接受到注册成功信息
23 | ### 其中,错误信息会在 request.js 进行拦截处理,在每次页面路由跳转时候,都会经过permission.js
24 |
25 | ### todo
26 | #### ~~新增一个接口 配置用户角色能访问的路由, 每次当用户登录成功,permission.js 拉取用户信息时,限制用户的访问路由,没有权限时强制跳转到一个没有权限的提示页面~~
27 | #### ~~用户管理那里新增用户,修改密码,修改权限等功能~~
28 |
29 | #### 接口访问权限
30 | ##### 接口访问时候需要接口校验,简单的传递,在参数里面校验是否是 role
31 | ### Project setup
32 | ```
33 | npm install
34 | ```
35 |
36 | ### Compiles and hot-reloads for development
37 | ```
38 | npm run serve
39 | ```
40 |
41 | ### Compiles and minifies for production
42 | ```
43 | npm run build
44 | ```
45 |
46 | ### Compiles and minifies for production to analyze the component percent
47 | ```
48 | npm run analyze
49 | ```
50 |
51 |
52 |
53 | #### 2019-5-31
54 | 默认关闭 开发环境的包文件分析,如果需要的话,可以修改.env.local VUE_LIFECIRCLE_EVENT 为DEV
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-admin-template",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "Wtodd <1259709654@qq.com>",
6 | "scripts": {
7 | "serve": "vue-cli-service serve",
8 | "build": "cross-env NODE_ENV=production vue-cli-service build --mode production",
9 | "analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
10 | },
11 | "dependencies": {
12 | "@tinymce/tinymce-vue": "^2.0.0",
13 | "axios": "0.17.1",
14 | "cross-env": "^5.2.0",
15 | "element-ui": "2.3.4",
16 | "js-cookie": "2.2.0",
17 | "normalize.css": "7.0.0",
18 | "nprogress": "0.2.0",
19 | "vue": "^2.5.17",
20 | "vue-ajax-upload": "^0.1.7",
21 | "vue-router": "^3.0.1",
22 | "vue2-editor": "^2.6.6",
23 | "vuex": "^3.0.1"
24 | },
25 | "devDependencies": {
26 | "@vue/cli-plugin-babel": "^3.0.0",
27 | "@vue/cli-service": "^3.5.3",
28 | "node-sass": "^4.9.3",
29 | "qiniu-webpack-plugin": "^0.4.2",
30 | "sass-loader": "^7.1.0",
31 | "svg-sprite-loader": "3.5.2",
32 | "vue-template-compiler": "^2.5.17",
33 | "webpack-bundle-analyzer": "^2.13.1"
34 | },
35 | "postcss": {
36 | "plugins": {
37 | "autoprefixer": {}
38 | }
39 | },
40 | "browserslist": [
41 | "> 1%",
42 | "last 2 versions",
43 | "not ie <= 8"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/public/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/public/bg.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-cli3 admin
9 |
10 |
11 |
12 | We're sorry but my doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/api/admin.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getAllUser() {
4 | console.log(456798132)
5 | return request({
6 | url: '/get/alluser',
7 | method: 'get',
8 | data: {
9 | page: page
10 | }
11 | })
12 | }
13 | export function deleteOneUser(id) {
14 | return request({
15 | url: '/post/deleteuser',
16 | method: 'post',
17 | data: {
18 | id: id
19 | }
20 | })
21 | }
22 |
23 | export function GetAllUserFromPage(page, skip) {
24 | return request({
25 | url: '/post/getUsersFromPage',
26 | method: 'post',
27 | data: {
28 | page: page,
29 | skip: skip
30 | }
31 | })
32 | }
33 |
34 | export function updateSomeOneRole(id, roles) {
35 | return request({
36 | url: '/post/updatesomerole',
37 | method: 'post',
38 | data: {
39 | id: id,
40 | roles: roles
41 | }
42 | })
43 | }
44 |
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function login(username, password) {
4 | return request({
5 | url: '/post/login',
6 | method: 'post',
7 | data: {
8 | username,
9 | password
10 | }
11 | })
12 | }
13 | // 像这样的结构不好,前端页面添加一个参数,这里也相应的添加
14 | export function register(username, password, type, roles, email, registerCode) {
15 | // console.log('register in login.js')
16 | return request({
17 | url: '/post/register',
18 | method: 'post',
19 | data: {
20 | username,
21 | password,
22 | type: type,
23 | roles: roles,
24 | email: email,
25 | registerCode: registerCode
26 | }
27 | })
28 | }
29 |
30 | // 管理员添加用户
31 | export function adminRegister(username, password, roles, age) {
32 | // console.log('register in login.js')
33 | return request({
34 | url: '/post/adminRegister',
35 | method: 'post',
36 | data: {
37 | username,
38 | password,
39 | roles: roles,
40 | age: age
41 | }
42 | })
43 | }
44 |
45 |
46 |
47 | export function getInfo(token) {
48 | return request({
49 | url: '/get/user/info',
50 | method: 'get',
51 | params: { token }
52 | })
53 | }
54 |
55 | export function logout(token) {
56 | return request({
57 | url: '/post/logout',
58 | method: 'post',
59 | params: { token }
60 | })
61 | }
62 |
63 | export function sendEmail(email) {
64 | return request({
65 | url: '/post/sendEmailCode',
66 | method: 'post',
67 | data: {
68 | email
69 | }
70 | })
71 | }
72 |
73 | export function uploadImg(_id, file) {
74 | return request({
75 | url: '/post/uploadImg',
76 | method: 'post',
77 | data: {
78 | _id: _id,
79 | avatar_file: file
80 | }
81 | })
82 | }
83 |
--------------------------------------------------------------------------------
/src/api/table.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getList(params) {
4 | return request({
5 | url: '/table/list',
6 | method: 'get',
7 | params
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/src/assets/iconfont/demo.css:
--------------------------------------------------------------------------------
1 | /* Logo 字体 */
2 | @font-face {
3 | font-family: "iconfont logo";
4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
9 | }
10 |
11 | .logo {
12 | font-family: "iconfont logo";
13 | font-size: 160px;
14 | font-style: normal;
15 | -webkit-font-smoothing: antialiased;
16 | -moz-osx-font-smoothing: grayscale;
17 | }
18 |
19 | /* tabs */
20 | .nav-tabs {
21 | position: relative;
22 | }
23 |
24 | .nav-tabs .nav-more {
25 | position: absolute;
26 | right: 0;
27 | bottom: 0;
28 | height: 42px;
29 | line-height: 42px;
30 | color: #666;
31 | }
32 |
33 | #tabs {
34 | border-bottom: 1px solid #eee;
35 | }
36 |
37 | #tabs li {
38 | cursor: pointer;
39 | width: 100px;
40 | height: 40px;
41 | line-height: 40px;
42 | text-align: center;
43 | font-size: 16px;
44 | border-bottom: 2px solid transparent;
45 | position: relative;
46 | z-index: 1;
47 | margin-bottom: -1px;
48 | color: #666;
49 | }
50 |
51 |
52 | #tabs .active {
53 | border-bottom-color: #f00;
54 | color: #222;
55 | }
56 |
57 | .tab-container .content {
58 | display: none;
59 | }
60 |
61 | /* 页面布局 */
62 | .main {
63 | padding: 30px 100px;
64 | width: 960px;
65 | margin: 0 auto;
66 | }
67 |
68 | .main .logo {
69 | color: #333;
70 | text-align: left;
71 | margin-bottom: 30px;
72 | line-height: 1;
73 | height: 110px;
74 | margin-top: -50px;
75 | overflow: hidden;
76 | *zoom: 1;
77 | }
78 |
79 | .main .logo a {
80 | font-size: 160px;
81 | color: #333;
82 | }
83 |
84 | .helps {
85 | margin-top: 40px;
86 | }
87 |
88 | .helps pre {
89 | padding: 20px;
90 | margin: 10px 0;
91 | border: solid 1px #e7e1cd;
92 | background-color: #fffdef;
93 | overflow: auto;
94 | }
95 |
96 | .icon_lists {
97 | width: 100% !important;
98 | overflow: hidden;
99 | *zoom: 1;
100 | }
101 |
102 | .icon_lists li {
103 | width: 100px;
104 | margin-bottom: 10px;
105 | margin-right: 20px;
106 | text-align: center;
107 | list-style: none !important;
108 | cursor: default;
109 | }
110 |
111 | .icon_lists li .code-name {
112 | line-height: 1.2;
113 | }
114 |
115 | .icon_lists .icon {
116 | display: block;
117 | height: 100px;
118 | line-height: 100px;
119 | font-size: 42px;
120 | margin: 10px auto;
121 | color: #333;
122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear;
123 | -moz-transition: font-size 0.25s linear, width 0.25s linear;
124 | transition: font-size 0.25s linear, width 0.25s linear;
125 | }
126 |
127 | .icon_lists .icon:hover {
128 | font-size: 100px;
129 | }
130 |
131 | .icon_lists .svg-icon {
132 | /* 通过设置 font-size 来改变图标大小 */
133 | width: 1em;
134 | /* 图标和文字相邻时,垂直对齐 */
135 | vertical-align: -0.15em;
136 | /* 通过设置 color 来改变 SVG 的颜色/fill */
137 | fill: currentColor;
138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
139 | normalize.css 中也包含这行 */
140 | overflow: hidden;
141 | }
142 |
143 | .icon_lists li .name,
144 | .icon_lists li .code-name {
145 | color: #666;
146 | }
147 |
148 | /* markdown 样式 */
149 | .markdown {
150 | color: #666;
151 | font-size: 14px;
152 | line-height: 1.8;
153 | }
154 |
155 | .highlight {
156 | line-height: 1.5;
157 | }
158 |
159 | .markdown img {
160 | vertical-align: middle;
161 | max-width: 100%;
162 | }
163 |
164 | .markdown h1 {
165 | color: #404040;
166 | font-weight: 500;
167 | line-height: 40px;
168 | margin-bottom: 24px;
169 | }
170 |
171 | .markdown h2,
172 | .markdown h3,
173 | .markdown h4,
174 | .markdown h5,
175 | .markdown h6 {
176 | color: #404040;
177 | margin: 1.6em 0 0.6em 0;
178 | font-weight: 500;
179 | clear: both;
180 | }
181 |
182 | .markdown h1 {
183 | font-size: 28px;
184 | }
185 |
186 | .markdown h2 {
187 | font-size: 22px;
188 | }
189 |
190 | .markdown h3 {
191 | font-size: 16px;
192 | }
193 |
194 | .markdown h4 {
195 | font-size: 14px;
196 | }
197 |
198 | .markdown h5 {
199 | font-size: 12px;
200 | }
201 |
202 | .markdown h6 {
203 | font-size: 12px;
204 | }
205 |
206 | .markdown hr {
207 | height: 1px;
208 | border: 0;
209 | background: #e9e9e9;
210 | margin: 16px 0;
211 | clear: both;
212 | }
213 |
214 | .markdown p {
215 | margin: 1em 0;
216 | }
217 |
218 | .markdown>p,
219 | .markdown>blockquote,
220 | .markdown>.highlight,
221 | .markdown>ol,
222 | .markdown>ul {
223 | width: 80%;
224 | }
225 |
226 | .markdown ul>li {
227 | list-style: circle;
228 | }
229 |
230 | .markdown>ul li,
231 | .markdown blockquote ul>li {
232 | margin-left: 20px;
233 | padding-left: 4px;
234 | }
235 |
236 | .markdown>ul li p,
237 | .markdown>ol li p {
238 | margin: 0.6em 0;
239 | }
240 |
241 | .markdown ol>li {
242 | list-style: decimal;
243 | }
244 |
245 | .markdown>ol li,
246 | .markdown blockquote ol>li {
247 | margin-left: 20px;
248 | padding-left: 4px;
249 | }
250 |
251 | .markdown code {
252 | margin: 0 3px;
253 | padding: 0 5px;
254 | background: #eee;
255 | border-radius: 3px;
256 | }
257 |
258 | .markdown strong,
259 | .markdown b {
260 | font-weight: 600;
261 | }
262 |
263 | .markdown>table {
264 | border-collapse: collapse;
265 | border-spacing: 0px;
266 | empty-cells: show;
267 | border: 1px solid #e9e9e9;
268 | width: 95%;
269 | margin-bottom: 24px;
270 | }
271 |
272 | .markdown>table th {
273 | white-space: nowrap;
274 | color: #333;
275 | font-weight: 600;
276 | }
277 |
278 | .markdown>table th,
279 | .markdown>table td {
280 | border: 1px solid #e9e9e9;
281 | padding: 8px 16px;
282 | text-align: left;
283 | }
284 |
285 | .markdown>table th {
286 | background: #F7F7F7;
287 | }
288 |
289 | .markdown blockquote {
290 | font-size: 90%;
291 | color: #999;
292 | border-left: 4px solid #e9e9e9;
293 | padding-left: 0.8em;
294 | margin: 1em 0;
295 | }
296 |
297 | .markdown blockquote p {
298 | margin: 0;
299 | }
300 |
301 | .markdown .anchor {
302 | opacity: 0;
303 | transition: opacity 0.3s ease;
304 | margin-left: 8px;
305 | }
306 |
307 | .markdown .waiting {
308 | color: #ccc;
309 | }
310 |
311 | .markdown h1:hover .anchor,
312 | .markdown h2:hover .anchor,
313 | .markdown h3:hover .anchor,
314 | .markdown h4:hover .anchor,
315 | .markdown h5:hover .anchor,
316 | .markdown h6:hover .anchor {
317 | opacity: 1;
318 | display: inline-block;
319 | }
320 |
321 | .markdown>br,
322 | .markdown>p>br {
323 | clear: both;
324 | }
325 |
326 |
327 | .hljs {
328 | display: block;
329 | background: white;
330 | padding: 0.5em;
331 | color: #333333;
332 | overflow-x: auto;
333 | }
334 |
335 | .hljs-comment,
336 | .hljs-meta {
337 | color: #969896;
338 | }
339 |
340 | .hljs-string,
341 | .hljs-variable,
342 | .hljs-template-variable,
343 | .hljs-strong,
344 | .hljs-emphasis,
345 | .hljs-quote {
346 | color: #df5000;
347 | }
348 |
349 | .hljs-keyword,
350 | .hljs-selector-tag,
351 | .hljs-type {
352 | color: #a71d5d;
353 | }
354 |
355 | .hljs-literal,
356 | .hljs-symbol,
357 | .hljs-bullet,
358 | .hljs-attribute {
359 | color: #0086b3;
360 | }
361 |
362 | .hljs-section,
363 | .hljs-name {
364 | color: #63a35c;
365 | }
366 |
367 | .hljs-tag {
368 | color: #333333;
369 | }
370 |
371 | .hljs-title,
372 | .hljs-attr,
373 | .hljs-selector-id,
374 | .hljs-selector-class,
375 | .hljs-selector-attr,
376 | .hljs-selector-pseudo {
377 | color: #795da3;
378 | }
379 |
380 | .hljs-addition {
381 | color: #55a532;
382 | background-color: #eaffea;
383 | }
384 |
385 | .hljs-deletion {
386 | color: #bd2c00;
387 | background-color: #ffecec;
388 | }
389 |
390 | .hljs-link {
391 | text-decoration: underline;
392 | }
393 |
394 | /* 代码高亮 */
395 | /* PrismJS 1.15.0
396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
397 | /**
398 | * prism.js default theme for JavaScript, CSS and HTML
399 | * Based on dabblet (http://dabblet.com)
400 | * @author Lea Verou
401 | */
402 | code[class*="language-"],
403 | pre[class*="language-"] {
404 | color: black;
405 | background: none;
406 | text-shadow: 0 1px white;
407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
408 | text-align: left;
409 | white-space: pre;
410 | word-spacing: normal;
411 | word-break: normal;
412 | word-wrap: normal;
413 | line-height: 1.5;
414 |
415 | -moz-tab-size: 4;
416 | -o-tab-size: 4;
417 | tab-size: 4;
418 |
419 | -webkit-hyphens: none;
420 | -moz-hyphens: none;
421 | -ms-hyphens: none;
422 | hyphens: none;
423 | }
424 |
425 | pre[class*="language-"]::-moz-selection,
426 | pre[class*="language-"] ::-moz-selection,
427 | code[class*="language-"]::-moz-selection,
428 | code[class*="language-"] ::-moz-selection {
429 | text-shadow: none;
430 | background: #b3d4fc;
431 | }
432 |
433 | pre[class*="language-"]::selection,
434 | pre[class*="language-"] ::selection,
435 | code[class*="language-"]::selection,
436 | code[class*="language-"] ::selection {
437 | text-shadow: none;
438 | background: #b3d4fc;
439 | }
440 |
441 | @media print {
442 |
443 | code[class*="language-"],
444 | pre[class*="language-"] {
445 | text-shadow: none;
446 | }
447 | }
448 |
449 | /* Code blocks */
450 | pre[class*="language-"] {
451 | padding: 1em;
452 | margin: .5em 0;
453 | overflow: auto;
454 | }
455 |
456 | :not(pre)>code[class*="language-"],
457 | pre[class*="language-"] {
458 | background: #f5f2f0;
459 | }
460 |
461 | /* Inline code */
462 | :not(pre)>code[class*="language-"] {
463 | padding: .1em;
464 | border-radius: .3em;
465 | white-space: normal;
466 | }
467 |
468 | .token.comment,
469 | .token.prolog,
470 | .token.doctype,
471 | .token.cdata {
472 | color: slategray;
473 | }
474 |
475 | .token.punctuation {
476 | color: #999;
477 | }
478 |
479 | .namespace {
480 | opacity: .7;
481 | }
482 |
483 | .token.property,
484 | .token.tag,
485 | .token.boolean,
486 | .token.number,
487 | .token.constant,
488 | .token.symbol,
489 | .token.deleted {
490 | color: #905;
491 | }
492 |
493 | .token.selector,
494 | .token.attr-name,
495 | .token.string,
496 | .token.char,
497 | .token.builtin,
498 | .token.inserted {
499 | color: #690;
500 | }
501 |
502 | .token.operator,
503 | .token.entity,
504 | .token.url,
505 | .language-css .token.string,
506 | .style .token.string {
507 | color: #9a6e3a;
508 | background: hsla(0, 0%, 100%, .5);
509 | }
510 |
511 | .token.atrule,
512 | .token.attr-value,
513 | .token.keyword {
514 | color: #07a;
515 | }
516 |
517 | .token.function,
518 | .token.class-name {
519 | color: #DD4A68;
520 | }
521 |
522 | .token.regex,
523 | .token.important,
524 | .token.variable {
525 | color: #e90;
526 | }
527 |
528 | .token.important,
529 | .token.bold {
530 | font-weight: bold;
531 | }
532 |
533 | .token.italic {
534 | font-style: italic;
535 | }
536 |
537 | .token.entity {
538 | cursor: help;
539 | }
540 |
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {font-family: "iconfont";
2 | src: url('iconfont.eot?t=1558080209229'); /* IE9 */
3 | src: url('iconfont.eot?t=1558080209229#iefix') format('embedded-opentype'), /* IE6-IE8 */
4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
5 | url('iconfont.woff?t=1558080209229') format('woff'),
6 | url('iconfont.ttf?t=1558080209229') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
7 | url('iconfont.svg?t=1558080209229#iconfont') format('svg'); /* iOS 4.1- */
8 | }
9 |
10 | .iconfont {
11 | font-family: "iconfont" !important;
12 | font-size: 16px;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .icon-kaquanguanli:before {
19 | content: "\e616";
20 | }
21 |
22 | .icon-huiyuan:before {
23 | content: "\e617";
24 | }
25 |
26 | .icon-shangchengguanli:before {
27 | content: "\e618";
28 | }
29 |
30 | .icon-zuzhiyushouquan:before {
31 | content: "\e619";
32 | }
33 |
34 | .icon-jiaoyiguanli:before {
35 | content: "\e61a";
36 | }
37 |
38 | .icon-huodongguanli:before {
39 | content: "\e61b";
40 | }
41 |
42 | .icon-zhifuguanli:before {
43 | content: "\e61c";
44 | }
45 |
46 | .icon-gongzhonghaopeizhi:before {
47 | content: "\e61d";
48 | }
49 |
50 | .icon-neirongguanli:before {
51 | content: "\e61e";
52 | }
53 |
54 | .icon-jichuxinxi:before {
55 | content: "\e61f";
56 | }
57 |
58 | .icon-qudaoguanli:before {
59 | content: "\e620";
60 | }
61 |
62 | .icon-duanxinpingtai:before {
63 | content: "\e621";
64 | }
65 |
66 | .icon-tongdaoguanli:before {
67 | content: "\e622";
68 | }
69 |
70 | .icon-yingxiaoduanxin:before {
71 | content: "\e623";
72 | }
73 |
74 | .icon-duanxinyingxiao:before {
75 | content: "\e624";
76 | }
77 |
78 | .icon-jichuziliao:before {
79 | content: "\e625";
80 | }
81 |
82 | .icon-pingtaiguanli:before {
83 | content: "\e626";
84 | }
85 |
86 | .icon-xiaochengxv:before {
87 | content: "\e627";
88 | }
89 |
90 | .icon-zhuanxiangfenxi:before {
91 | content: "\e628";
92 | }
93 |
94 | .icon-jiesuanxitong:before {
95 | content: "\e629";
96 | }
97 |
98 | .icon-yingxiaotuiguang:before {
99 | content: "\e62a";
100 | }
101 |
102 | .icon-huiyuanjifen:before {
103 | content: "\e62b";
104 | }
105 |
106 | .icon-huiyuanbiaoqian:before {
107 | content: "\e62c";
108 | }
109 |
110 | .icon-liebianguanli:before {
111 | content: "\e62d";
112 | }
113 |
114 | .icon-yingxiaozhixing:before {
115 | content: "\e62e";
116 | }
117 |
118 | .icon-jingzhunyingxiao:before {
119 | content: "\e62f";
120 | }
121 |
122 | .icon-huiyuanquanyi:before {
123 | content: "\e630";
124 | }
125 |
126 | .icon-wandayaochiri:before {
127 | content: "\e631";
128 | }
129 |
130 | .icon-huiyuandengji:before {
131 | content: "\e632";
132 | }
133 |
134 | .icon-huiyuanguanli:before {
135 | content: "\e633";
136 | }
137 |
138 | .icon-yingxiaocehua:before {
139 | content: "\e634";
140 | }
141 |
142 | .icon-gonggaoguanli:before {
143 | content: "\e635";
144 | }
145 |
146 | .icon-shangpinguanli:before {
147 | content: "\e636";
148 | }
149 |
150 | .icon-shanghuguanli:before {
151 | content: "\e637";
152 | }
153 |
154 | .icon-huiyuandingyi:before {
155 | content: "\e638";
156 | }
157 |
158 | .icon-huiyuanfenxi:before {
159 | content: "\e639";
160 | }
161 |
162 | .icon-huiyuankashezhi:before {
163 | content: "\e63a";
164 | }
165 |
166 | .icon-zengzhibiaoqian:before {
167 | content: "\e63b";
168 | }
169 |
170 | .icon-jingyinggaikuang:before {
171 | content: "\e63c";
172 | }
173 |
174 | .icon-waibuyonghuguanli:before {
175 | content: "\e63d";
176 | }
177 |
178 | .icon-shezhi:before {
179 | content: "\e63e";
180 | }
181 |
182 | .icon-dakaguanli:before {
183 | content: "\e63f";
184 | }
185 |
186 | .icon-huiyuantuozhan:before {
187 | content: "\e640";
188 | }
189 |
190 | .icon-libao:before {
191 | content: "\e641";
192 | }
193 |
194 | .icon-shijianguanli:before {
195 | content: "\e642";
196 | }
197 |
198 | .icon-qiandaohuodong:before {
199 | content: "\e643";
200 | }
201 |
202 | .icon-shouye:before {
203 | content: "\e644";
204 | }
205 |
206 | .icon-hetongguanli:before {
207 | content: "\e645";
208 | }
209 |
210 | .icon-mendianruzhu:before {
211 | content: "\e646";
212 | }
213 |
214 | .icon-choujiang:before {
215 | content: "\e647";
216 | }
217 |
218 | .icon-wanpuxiaoer:before {
219 | content: "\e648";
220 | }
221 |
222 | .icon-tingchechangguanli:before {
223 | content: "\e649";
224 | }
225 |
226 | .icon-jiesuanguanli:before {
227 | content: "\e64a";
228 | }
229 |
230 | .icon-mendianguanli:before {
231 | content: "\e64b";
232 | }
233 |
234 |
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/iconfont/iconfont.eot
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | Created by iconfont
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/iconfont/iconfont.ttf
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/iconfont/iconfont.woff
--------------------------------------------------------------------------------
/src/assets/iconfont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/iconfont/iconfont.woff2
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{item.meta.title}}
6 | {{item.meta.title}}
7 |
8 |
9 |
10 |
11 |
12 |
40 |
41 |
53 |
--------------------------------------------------------------------------------
/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
8 |
10 |
12 |
13 |
14 |
15 |
16 |
31 |
32 |
46 |
--------------------------------------------------------------------------------
/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
33 |
34 |
43 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 | import store from "./store";
5 | // reset CSS
6 | import "normalize.css/normalize.css";
7 |
8 | import ElementUI from "element-ui";
9 | import "element-ui/lib/theme-chalk/index.css";
10 | import "./assets/iconfont/iconfont.css" // 简化处理时候 只用element-UI 的图标库,项目大的时候 改成vue-awesome
11 | import '@/styles/index.scss' // global css
12 | import './permisssion' // permission control
13 |
14 | Vue.use(ElementUI);
15 | Vue.config.productionTip = false;
16 |
17 | new Vue({
18 | router,
19 | store,
20 | render: h => h(App)
21 | }).$mount("#app");
22 |
--------------------------------------------------------------------------------
/src/permisssion.js:
--------------------------------------------------------------------------------
1 | import router from './router'
2 | import store from './store'
3 | import { Message } from 'element-ui'
4 | import NProgress from 'nprogress' // progress bar
5 | import 'nprogress/nprogress.css'// progress bar style
6 | import { getToken } from '@/utils/auth' // getToken from cookie
7 |
8 | NProgress.configure({ showSpinner: false })// NProgress Configuration
9 |
10 | // permission judge function
11 | function hasPermission(roles, permissionRoles) {
12 | if (roles.indexOf('admin') >= 0) {
13 | return true // 管理员直接通过
14 | }
15 | if (!permissionRoles) {
16 | return true // 如果没有权限限制 直接通过
17 | }
18 | return roles.some(role => permissionRoles.indexOf(role) >= 0)
19 | }
20 |
21 | const whiteList = ['/login', '/auth-redirect', '/register']// 没有限制登陆要求的页面
22 |
23 | router.beforeEach((to, from, next) => {
24 | NProgress.start() // start progress bar
25 | if (getToken()) { // 存在登陆信息
26 | if (to.path === '/login') {
27 | next({ path: '/' }) // 登陆 && 登陆界面
28 | NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
29 | } else {
30 | // console.log(store.getters.roles.length)
31 | if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
32 | store.dispatch('GetUserInfo').then(res => { // 拉取user_info
33 | const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
34 | store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
35 | router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
36 | next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
37 | })
38 | }).catch((err) => {
39 | console.log(err, 'reqwrqweerr2')
40 | store.dispatch('FedLogOut').then(() => {
41 | Message.error(err || 'Verification failed, please login again')
42 | next({ path: '/' })
43 | })
44 | })
45 | } else {
46 | // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
47 | if (hasPermission(store.getters.roles, to.meta.roles)) {
48 | next()
49 | } else {
50 | next({ path: '/401', replace: true, query: { noGoBack: true }})
51 | }
52 | // 可删 ↑
53 | }
54 | }
55 | } else {
56 | /* has no token*/
57 | if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
58 | next()
59 | } else {
60 | next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
61 | NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
62 | }
63 | }
64 | })
65 |
66 | router.afterEach(() => {
67 | NProgress.done() // finish progress bar
68 | })
69 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | /* Layout */
4 | import Layout from './views/layout/Layout'
5 | // 异步加载 按需加载
6 | const TableIndex = () => import(/* webpackChunkName: "TableIndex" */ '@/views/table/index')
7 | const TreeIndex = () => import(/* webpackChunkName: "TreeIndex" */ '@/views/tree/index')
8 |
9 | // 同步加载 按需加载
10 | // const TechnologyIndex = require(/* webpackChunkName: "TechnologyIndex" */ '@/views/display/technology')
11 | const TechnologyIndex = () => import(/* webpackChunkName: "TechnologyIndex" */ '@/views/display/technology')
12 | // const FictionIndex = require(/* webpackChunkName: "FictionIndex" */ '@/views/display/fiction')
13 | const FictionIndex = () => import(/* webpackChunkName: "FictionIndex" */ '@/views/display/fiction')
14 |
15 | const MyIndex = () => import(/* webpackChunkName: "MyIndex" */ '@/views/center/index')
16 |
17 | Vue.use(Router)
18 | // hidden: false, 控制是否在左侧导航显示,用作过滤
19 |
20 | export const asyncRouterMap = [
21 | {
22 | path: '/admin/manage-users',
23 | component: Layout,
24 | meta: {roles: ['admin', 'boss']},
25 | hidden: false,
26 | children: [{
27 | path: '/admin/manage-users',
28 | name: 'ManageUsers',
29 | component: () => import('@/views/admin/manage-users'),
30 | meta: { title: 'manage-users', icon: 'icon-huiyuan' }
31 | }]
32 | },
33 | {
34 | path: '/admin/manage-money',
35 | component: Layout,
36 | meta: {roles: ['admin', 'boss']},
37 | hidden: false,
38 | children: [
39 | {
40 | path: '/admin/manage-money',
41 | name: 'ManageMoney',
42 | component: () => import('@/views/admin/manage-money'),
43 | meta: { title: 'Manage-money', icon: 'icon-huiyuanjifen' }
44 | }
45 | ]
46 | },
47 | { path: '*', redirect: '/404', hidden: true }
48 | ];
49 |
50 | export const constantRouterMap = [
51 | { path: '/login', component: () => import('@/views/login/index'), hidden: true, roles: 'all' },
52 | { path: '/register', component: () => import('@/views/register/index'), hidden: true, roles: 'all' },
53 | {
54 | path: '/',
55 | component: Layout,
56 | redirect: '/dashboard',
57 | name: 'Dashboard',
58 | hidden: true,
59 | roles: 'all',
60 | children: [{
61 | path: 'dashboard',
62 | component: () => import('@/views/dashboard/index'),
63 | }]
64 | },
65 | {
66 | path: '/example',
67 | component: Layout,
68 | redirect: '/example/table',
69 | name: 'Example',
70 | // meta: { title: 'Example', icon: 'example' },
71 | meta: { title: 'Example', icon: 'icon-qudaoguanli' },
72 | roles: 'dev',
73 | children: [
74 | {
75 | path: 'table',
76 | name: 'Table',
77 | component: TableIndex,
78 | meta: { title: 'Table', icon: 'icon-shoujitianchong' }
79 | },
80 | {
81 | path: 'tree',
82 | name: 'Tree',
83 | component: TreeIndex,
84 | // meta: { title: 'Tree', icon: 'tree' }
85 | meta: { title: 'Tree', icon: 'icon-gengduotianchong' }
86 | }
87 | /**
88 | * 多个子路由,某些子路由用户有权限,某些没有权限,因此role 要放在最内层的 的children里
89 | * 这里简化处理,暂不这样做
90 | * */
91 | ]
92 | },
93 |
94 | {
95 | path: '/form',
96 | component: Layout,
97 | children: [
98 | {
99 | path: 'index',
100 | name: 'Edit',
101 | component: () => import('@/views/edit/index'),
102 | meta: { title: 'edit', icon: 'icon-huiyuandingyi' }
103 | }
104 | ]
105 | },
106 |
107 | {
108 | path: '/nested',
109 | component: Layout,
110 | redirect: '/nested/menu1',
111 | name: 'nested',
112 | meta: {
113 | title: 'nested',
114 | icon: 'icon-shijianguanli'
115 | },
116 | children: [
117 | {
118 | path: 'menu1',
119 | component: () => import('@/views/nested/menu1/index'), // Parent router-view
120 | name: 'menu1',
121 | meta: { title: 'menu1' },
122 | children: [
123 | {
124 | path: 'menu1-1',
125 | component: () => import('@/views/nested/menu1/menu1-1'),
126 | name: 'menu1-1',
127 | meta: { title: 'menu1-1' }
128 | },
129 | {
130 | path: 'menu1-2',
131 | component: () => import('@/views/nested/menu1/menu1-2'),
132 | name: 'menu1-2',
133 | meta: { title: 'menu1-2' },
134 | children: [
135 | {
136 | path: 'menu1-2-1',
137 | component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
138 | name: 'menu1-2-1',
139 | meta: { title: 'menu1-2-1' }
140 | },
141 | {
142 | path: 'menu1-2-2',
143 | component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
144 | name: 'menu1-2-2',
145 | meta: { title: 'menu1-2-2' }
146 | }
147 | ]
148 | },
149 | {
150 | path: 'menu1-3',
151 | component: () => import('@/views/nested/menu1/menu1-3'),
152 | name: 'menu1-3',
153 | meta: { title: 'menu1-3' }
154 | }
155 | ]
156 | },
157 | {
158 | path: 'menu2',
159 | component: () => import('@/views/nested/menu2/index'),
160 | meta: { title: 'menu2' }
161 | }
162 | ]
163 | },
164 | {
165 | path: '/display',
166 | component: Layout,
167 | redirect: '/display/tech',
168 | name: 'Display',
169 | // meta: { title: 'Example', icon: 'example' },
170 | meta: { title: 'display', icon: 'icon-yingxiaocehua' },
171 | role: 'dev',
172 | children: [
173 | {
174 | path: 'technology',
175 | name: 'Technology',
176 | component: TechnologyIndex,
177 | meta: { title: 'technology', icon: 'icon-shoujitianchong' }
178 | },
179 | {
180 | path: 'fiction',
181 | name: 'Fiction',
182 | component: FictionIndex,
183 | meta: { title: 'Fiction', icon: 'icon-gengduotianchong' }
184 | }
185 | ]
186 | },
187 | {
188 | path: '/my',
189 | component: Layout,
190 | name: 'personalPage',
191 | hidden: true, // 左侧路由导航会遍历这个名称,如果没有,就不会显示在左侧导航表中
192 | children: [
193 | {
194 | path: '',
195 | // name: '个人中心',
196 | component: MyIndex,
197 | // meta: { title: '个人中心', icon: 'icon-huiyuandingyi' }
198 | }
199 | ]
200 | },
201 | { path: '/404', component: () => import('@/views/404'), hidden: true, role: 'all' },
202 | ]
203 |
204 | export default new Router({
205 | mode: "history",
206 | scrollBehavior: () => ({ y: 0 }),
207 | routes: constantRouterMap
208 | });
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | sidebar: state => state.app.sidebar,
3 | device: state => state.app.device,
4 | token: state => state.user.token,
5 | avatar: state => state.user.avatar,
6 | name: state => state.user.name,
7 | roles: state => state.user.roles,
8 | _id: state => state.user._id,
9 | permission_routers: state => state.permission.routers,
10 | addRouters: state => state.permission.addRouters,
11 | }
12 | export default getters
13 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import app from './modules/app'
4 | import user from './modules/user'
5 | import admin from './modules/admin'
6 | import getters from './getters'
7 | import permission from './routePermission'
8 |
9 | Vue.use(Vuex)
10 | const store = new Vuex.Store({
11 | modules: {
12 | permission,
13 | app,
14 | user,
15 | admin
16 | },
17 | getters
18 | })
19 |
20 | export default store
21 |
--------------------------------------------------------------------------------
/src/store/modules/admin.js:
--------------------------------------------------------------------------------
1 | import { getAllUser, GetAllUserFromPage, deleteOneUser, updateSomeOneRole } from '@/api/admin'
2 | /** eslint disabled */
3 | const admin = {
4 | state: {
5 | },
6 |
7 | mutations: {
8 | },
9 |
10 | actions: {
11 | // 获取所有用户
12 | GetAllUser({ commit }, {page}) {
13 | return new Promise((resolve, reject) => {
14 | getAllUser(page).then(response => {
15 | resolve(response)
16 | }).catch(error => {
17 | reject(error)
18 | })
19 | })
20 | },
21 | // 获取所有用户(分页获取)
22 | GetAllUserFromPage({ commit }, {page, skip}) {
23 | return new Promise((resolve, reject) => {
24 | console.log(page, skip)
25 | GetAllUserFromPage(page, skip).then(response => {
26 | resolve(response)
27 | }).catch(error => {
28 | reject(error)
29 | })
30 | })
31 | },
32 | // 删除用户
33 | DeleteOneUser ({ commit }, {id}) {
34 | return new Promise((resolve, reject) => {
35 | deleteOneUser(id).then(response => {
36 | resolve(response)
37 | }).catch(error => {
38 | reject(error)
39 | })
40 | })
41 | },
42 | // 修改用户
43 | UpdateSomeOneRole ({ commit }, {id, roles}) {
44 | console.log(roles)
45 | return new Promise((resolve, reject) => {
46 | updateSomeOneRole(id, roles).then(response => {
47 | resolve(response)
48 | }).catch(error => {
49 | reject(error)
50 | })
51 | })
52 | },
53 | }
54 | }
55 |
56 | export default admin
57 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const app = {
4 | state: {
5 | sidebar: {
6 | opened: !+Cookies.get('sidebarStatus'),
7 | withoutAnimation: false
8 | },
9 | device: 'desktop'
10 | },
11 | mutations: {
12 | TOGGLE_SIDEBAR: state => {
13 | if (state.sidebar.opened) {
14 | Cookies.set('sidebarStatus', 1)
15 | } else {
16 | Cookies.set('sidebarStatus', 0)
17 | }
18 | state.sidebar.opened = !state.sidebar.opened
19 | state.sidebar.withoutAnimation = false
20 | },
21 | CLOSE_SIDEBAR: (state, withoutAnimation) => {
22 | Cookies.set('sidebarStatus', 1)
23 | state.sidebar.opened = false
24 | state.sidebar.withoutAnimation = withoutAnimation
25 | },
26 | TOGGLE_DEVICE: (state, device) => {
27 | state.device = device
28 | }
29 | },
30 | actions: {
31 | ToggleSideBar: ({ commit }) => {
32 | commit('TOGGLE_SIDEBAR')
33 | },
34 | CloseSideBar({ commit }, { withoutAnimation }) {
35 | commit('CLOSE_SIDEBAR', withoutAnimation)
36 | },
37 | ToggleDevice({ commit }, device) {
38 | commit('TOGGLE_DEVICE', device)
39 | }
40 | }
41 | }
42 |
43 | export default app
44 |
--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { adminRegister, register, login, logout, getInfo ,sendEmail, uploadImg} from '@/api/login'
2 | import { getToken, setToken, removeToken } from '@/utils/auth'
3 | /** eslint disabled */
4 | const user = {
5 | state: {
6 | token: getToken(),
7 | name: '',
8 | avatar: '',
9 | roles: [],
10 | roleRouters: []
11 | },
12 |
13 | mutations: {
14 | SET_TOKEN: (state, token) => {
15 | state.token = token
16 | },
17 | SET_NAME: (state, name) => {
18 | state.name = name
19 | },
20 | SET_AVATAR: (state, avatar) => {
21 | state.avatar = avatar
22 | },
23 | SET_ROLES: (state, roles) => {
24 | state.roles = roles
25 | },
26 | SET_USERID: (state, _id) => {
27 | state._id = _id
28 | },
29 | SET_ROLE_ROUTERS: (state, roleRouters) => {
30 | state.roleRouters = roleRouters
31 | }
32 | },
33 |
34 | actions: {
35 | // 发送验证码
36 | SendEmail({ commit }, email) {
37 | console.log(email,'email')
38 | return new Promise((resolve, reject) => {
39 | sendEmail(email).then(response => {
40 | resolve(response)
41 | }).catch(error => {
42 | console.log(error)
43 | reject(error)
44 | })
45 | })
46 | },
47 | // 注册
48 | Register({ commit }, userInfo) {
49 | const username = userInfo.username.trim()
50 | const roles = userInfo.roles
51 | const type = userInfo.type.trim()
52 | const email = userInfo.email
53 | const registerCode = userInfo.registerCode
54 | return new Promise((resolve, reject) => {
55 | register(username, userInfo.password, type, roles, email, registerCode).then(response => {
56 | // then 这里接收到的只要成功的提示,失败的情况已经在拦截器里面处理
57 | // console.log('actions', response)
58 | setToken(response.token) // 注册成功直接登陆 使用
59 | commit('SET_TOKEN', response.token) // 注册成功直接登陆 使用
60 | resolve(response)
61 | }).catch(error => {
62 | console.log(error)
63 | reject(error)
64 | })
65 | })
66 | },
67 | // 管理员添加用户
68 | adminRegister({ commit }, userInfo) {
69 | const username = userInfo.username.trim()
70 | const roles = userInfo.roles
71 | const age = userInfo.age.trim()
72 | return new Promise((resolve, reject) => {
73 | // 修改成传递一个对象过来,在源头处修改,省得在很多地方逐个添加参数
74 | adminRegister(username, userInfo.password, roles, age).then(response => {
75 | // then 这里接收到的只要成功的提示,失败的情况已经在拦截器里面处理
76 | // console.log('actions', response)
77 | // setToken(response.token) // 注册成功直接登陆 使用
78 | // commit('SET_TOKEN', response.token) // 注册成功直接登陆 使用
79 | resolve(response)
80 | }).catch(error => {
81 | console.log(error)
82 | reject(error)
83 | })
84 | })
85 | },
86 | // 登录
87 | Login({ commit }, userInfo) {
88 | const username = userInfo.username.trim()
89 | return new Promise((resolve, reject) => {
90 | login(username, userInfo.password).then(response => {
91 | // then 这里接收到的只要成功的提示,失败的情况已经在拦截器里面处理
92 | setToken(response.token)
93 | commit('SET_TOKEN', response.token)
94 | resolve(response)
95 | }).catch(error => {
96 | reject(error)
97 | })
98 | })
99 | },
100 |
101 | // 获取用户信息
102 | GetUserInfo({ commit, state }) {
103 | return new Promise((resolve, reject) => {
104 | getInfo(state.token).then(response => {
105 | // console.log(response)
106 | if (response.data.expired) {
107 | reject('登录已经过期,请重新登录!')
108 | }
109 | const data = response.data
110 | if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
111 | commit('SET_ROLES', data.roles)
112 | } else {
113 | reject('getInfo: roles must be a non-null array !')
114 | }
115 | commit('SET_NAME', data.name)
116 | commit('SET_AVATAR', data.avatar_url)
117 | commit('SET_USERID', data._id)
118 | resolve(response)
119 | }).catch(error => {
120 | // console.log(error, '115')
121 | reject(error)
122 | })
123 | })
124 | },
125 |
126 | UploadImage({commit}, {_id, file}) {
127 | // todo 过滤没有登录
128 | console.log(file)
129 | return new Promise((resolve, reject) => {
130 | uploadImg(_id, file)
131 | .then(response => {
132 | commit('SET_USERID', _id)
133 | resolve(response)
134 | })
135 | .catch(err => {
136 | reject(err)
137 | })
138 | })
139 | },
140 | // 登出
141 | LogOut({ commit, state }) {
142 | return new Promise((resolve, reject) => {
143 | logout(state.token).then(() => {
144 | console.log('out')
145 | commit('SET_TOKEN', '')
146 | commit('SET_ROLE', '')
147 | removeToken()
148 | resolve()
149 | }).catch(error => {
150 | reject(error)
151 | })
152 | })
153 | },
154 |
155 | // 前端 登出
156 | FedLogOut({ commit }) {
157 | return new Promise(resolve => {
158 | commit('SET_TOKEN', '')
159 | removeToken()
160 | resolve()
161 | })
162 | }
163 | }
164 | }
165 |
166 | export default user
167 |
--------------------------------------------------------------------------------
/src/store/routePermission.js:
--------------------------------------------------------------------------------
1 | import { asyncRouterMap, constantRouterMap } from '@/router'
2 |
3 | /**
4 | * 通过meta.role判断是否与当前用户权限匹配
5 | * @param roles
6 | * @param route
7 | */
8 | function hasPermission(roles, route) {
9 | if (route.meta && route.meta.roles) {
10 | return roles.some(role => route.meta.roles.includes(role))
11 | } else {
12 | return true
13 | }
14 | }
15 |
16 | /**
17 | * 递归过滤异步路由表,返回符合用户角色权限的路由表
18 | * @param routes asyncRouterMap
19 | * @param roles
20 | */
21 | function filterAsyncRouter(routes, roles) {
22 | const res = []
23 |
24 | routes.forEach(route => {
25 | const tmp = { ...route }
26 | if (hasPermission(roles, tmp)) {
27 | if (tmp.children) {
28 | tmp.children = filterAsyncRouter(tmp.children, roles)
29 | }
30 | res.push(tmp)
31 | }
32 | })
33 |
34 | return res
35 | }
36 |
37 | const permission = {
38 | state: {
39 | routers: constantRouterMap,
40 | addRouters: []
41 | },
42 | mutations: {
43 | SET_ROUTERS: (state, routers) => {
44 | state.addRouters = routers
45 | state.routers = constantRouterMap.concat(routers)
46 | }
47 | },
48 | actions: {
49 | GenerateRoutes({ commit }, data) {
50 | return new Promise(resolve => {
51 | const { roles } = data
52 | let accessedRouters
53 | if (roles.includes('admin')) {
54 | accessedRouters = asyncRouterMap
55 | } else {
56 | accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
57 | }
58 | commit('SET_ROUTERS', accessedRouters)
59 | resolve()
60 | })
61 | }
62 | }
63 | }
64 |
65 | export default permission
66 |
--------------------------------------------------------------------------------
/src/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | //to reset element-ui default css
2 | .el-upload {
3 | input[type="file"] {
4 | display: none !important;
5 | }
6 | }
7 |
8 | .el-upload__input {
9 | display: none;
10 | }
11 |
12 | //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
13 | .el-dialog {
14 | transform: none;
15 | left: 0;
16 | position: relative;
17 | margin: 0 auto;
18 | }
19 |
20 | //element ui upload
21 | .upload-container {
22 | .el-upload {
23 | width: 100%;
24 | .el-upload-dragger {
25 | width: 100%;
26 | height: 200px;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 | @import './transition.scss';
3 | @import './element-ui.scss';
4 | @import './sidebar.scss';
5 | @import './normalize.scss';
6 |
7 | body {
8 | height: 100%;
9 | -moz-osx-font-smoothing: grayscale;
10 | -webkit-font-smoothing: antialiased;
11 | text-rendering: optimizeLegibility;
12 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
13 | }
14 |
15 | label {
16 | font-weight: 700;
17 | }
18 |
19 | html {
20 | height: 100%;
21 | box-sizing: border-box;
22 | }
23 |
24 | #app{
25 | height: 100%;
26 | }
27 |
28 | *,
29 | *:before,
30 | *:after {
31 | box-sizing: inherit;
32 | }
33 |
34 | a,
35 | a:focus,
36 | a:hover {
37 | cursor: pointer;
38 | color: inherit;
39 | outline: none;
40 | text-decoration: none;
41 | }
42 |
43 | div:focus{
44 | outline: none;
45 | }
46 |
47 | a:focus,
48 | a:active {
49 | outline: none;
50 | }
51 |
52 | a,
53 | a:focus,
54 | a:hover {
55 | cursor: pointer;
56 | color: inherit;
57 | text-decoration: none;
58 | }
59 |
60 | .clearfix {
61 | &:after {
62 | visibility: hidden;
63 | display: block;
64 | font-size: 0;
65 | content: " ";
66 | clear: both;
67 | height: 0;
68 | }
69 | }
70 |
71 | //main-container全局样式
72 | .app-main{
73 | min-height: 100%
74 | }
75 |
76 | .app-container {
77 | padding: 20px;
78 | }
79 |
--------------------------------------------------------------------------------
/src/styles/normalize.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in iOS.
9 | */
10 |
11 | html {
12 | line-height: 1.15; /* 1 */
13 | -webkit-text-size-adjust: 100%; /* 2 */
14 | }
15 |
16 | /* Sections
17 | ========================================================================== */
18 |
19 | /**
20 | * Remove the margin in all browsers.
21 | */
22 |
23 | body {
24 | margin: 0;
25 | }
26 |
27 | /**
28 | * Render the `main` element consistently in IE.
29 | */
30 |
31 | main {
32 | display: block;
33 | }
34 |
35 | /**
36 | * Correct the font size and margin on `h1` elements within `section` and
37 | * `article` contexts in Chrome, Firefox, and Safari.
38 | */
39 |
40 | h1 {
41 | font-size: 2em;
42 | margin: 0.67em 0;
43 | }
44 |
45 | /* Grouping content
46 | ========================================================================== */
47 |
48 | /**
49 | * 1. Add the correct box sizing in Firefox.
50 | * 2. Show the overflow in Edge and IE.
51 | */
52 |
53 | hr {
54 | box-sizing: content-box; /* 1 */
55 | height: 0; /* 1 */
56 | overflow: visible; /* 2 */
57 | }
58 |
59 | /**
60 | * 1. Correct the inheritance and scaling of font size in all browsers.
61 | * 2. Correct the odd `em` font sizing in all browsers.
62 | */
63 |
64 | pre {
65 | font-family: monospace, monospace; /* 1 */
66 | font-size: 1em; /* 2 */
67 | }
68 |
69 | /* Text-level semantics
70 | ========================================================================== */
71 |
72 | /**
73 | * Remove the gray background on active links in IE 10.
74 | */
75 |
76 | a {
77 | background-color: transparent;
78 | }
79 |
80 | /**
81 | * 1. Remove the bottom border in Chrome 57-
82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
83 | */
84 |
85 | abbr[title] {
86 | border-bottom: none; /* 1 */
87 | text-decoration: underline; /* 2 */
88 | text-decoration: underline dotted; /* 2 */
89 | }
90 |
91 | /**
92 | * Add the correct font weight in Chrome, Edge, and Safari.
93 | */
94 |
95 | b,
96 | strong {
97 | font-weight: bolder;
98 | }
99 |
100 | /**
101 | * 1. Correct the inheritance and scaling of font size in all browsers.
102 | * 2. Correct the odd `em` font sizing in all browsers.
103 | */
104 |
105 | code,
106 | kbd,
107 | samp {
108 | font-family: monospace, monospace; /* 1 */
109 | font-size: 1em; /* 2 */
110 | }
111 |
112 | /**
113 | * Add the correct font size in all browsers.
114 | */
115 |
116 | small {
117 | font-size: 80%;
118 | }
119 |
120 | /**
121 | * Prevent `sub` and `sup` elements from affecting the line height in
122 | * all browsers.
123 | */
124 |
125 | sub,
126 | sup {
127 | font-size: 75%;
128 | line-height: 0;
129 | position: relative;
130 | vertical-align: baseline;
131 | }
132 |
133 | sub {
134 | bottom: -0.25em;
135 | }
136 |
137 | sup {
138 | top: -0.5em;
139 | }
140 |
141 | /* Embedded content
142 | ========================================================================== */
143 |
144 | /**
145 | * Remove the border on images inside links in IE 10.
146 | */
147 |
148 | img {
149 | border-style: none;
150 | }
151 |
152 | /* Forms
153 | ========================================================================== */
154 |
155 | /**
156 | * 1. Change the font styles in all browsers.
157 | * 2. Remove the margin in Firefox and Safari.
158 | */
159 |
160 | button,
161 | input,
162 | optgroup,
163 | select,
164 | textarea {
165 | font-family: inherit; /* 1 */
166 | font-size: 100%; /* 1 */
167 | line-height: 1.15; /* 1 */
168 | margin: 0; /* 2 */
169 | }
170 |
171 | /**
172 | * Show the overflow in IE.
173 | * 1. Show the overflow in Edge.
174 | */
175 |
176 | button,
177 | input { /* 1 */
178 | overflow: visible;
179 | }
180 |
181 | /**
182 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
183 | * 1. Remove the inheritance of text transform in Firefox.
184 | */
185 |
186 | button,
187 | select { /* 1 */
188 | text-transform: none;
189 | }
190 |
191 | /**
192 | * Correct the inability to style clickable types in iOS and Safari.
193 | */
194 |
195 | button,
196 | [type="button"],
197 | [type="reset"],
198 | [type="submit"] {
199 | -webkit-appearance: button;
200 | }
201 |
202 | /**
203 | * Remove the inner border and padding in Firefox.
204 | */
205 |
206 | button::-moz-focus-inner,
207 | [type="button"]::-moz-focus-inner,
208 | [type="reset"]::-moz-focus-inner,
209 | [type="submit"]::-moz-focus-inner {
210 | border-style: none;
211 | padding: 0;
212 | }
213 |
214 | /**
215 | * Restore the focus styles unset by the previous rule.
216 | */
217 |
218 | button:-moz-focusring,
219 | [type="button"]:-moz-focusring,
220 | [type="reset"]:-moz-focusring,
221 | [type="submit"]:-moz-focusring {
222 | outline: 1px dotted ButtonText;
223 | }
224 |
225 | /**
226 | * Correct the padding in Firefox.
227 | */
228 |
229 | fieldset {
230 | padding: 0.35em 0.75em 0.625em;
231 | }
232 |
233 | /**
234 | * 1. Correct the text wrapping in Edge and IE.
235 | * 2. Correct the color inheritance from `fieldset` elements in IE.
236 | * 3. Remove the padding so developers are not caught out when they zero out
237 | * `fieldset` elements in all browsers.
238 | */
239 |
240 | legend {
241 | box-sizing: border-box; /* 1 */
242 | color: inherit; /* 2 */
243 | display: table; /* 1 */
244 | max-width: 100%; /* 1 */
245 | padding: 0; /* 3 */
246 | white-space: normal; /* 1 */
247 | }
248 |
249 | /**
250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera.
251 | */
252 |
253 | progress {
254 | vertical-align: baseline;
255 | }
256 |
257 | /**
258 | * Remove the default vertical scrollbar in IE 10+.
259 | */
260 |
261 | textarea {
262 | overflow: auto;
263 | }
264 |
265 | /**
266 | * 1. Add the correct box sizing in IE 10.
267 | * 2. Remove the padding in IE 10.
268 | */
269 |
270 | [type="checkbox"],
271 | [type="radio"] {
272 | box-sizing: border-box; /* 1 */
273 | padding: 0; /* 2 */
274 | }
275 |
276 | /**
277 | * Correct the cursor style of increment and decrement buttons in Chrome.
278 | */
279 |
280 | [type="number"]::-webkit-inner-spin-button,
281 | [type="number"]::-webkit-outer-spin-button {
282 | height: auto;
283 | }
284 |
285 | /**
286 | * 1. Correct the odd appearance in Chrome and Safari.
287 | * 2. Correct the outline style in Safari.
288 | */
289 |
290 | [type="search"] {
291 | -webkit-appearance: textfield; /* 1 */
292 | outline-offset: -2px; /* 2 */
293 | }
294 |
295 | /**
296 | * Remove the inner padding in Chrome and Safari on macOS.
297 | */
298 |
299 | [type="search"]::-webkit-search-decoration {
300 | -webkit-appearance: none;
301 | }
302 |
303 | /**
304 | * 1. Correct the inability to style clickable types in iOS and Safari.
305 | * 2. Change font properties to `inherit` in Safari.
306 | */
307 |
308 | ::-webkit-file-upload-button {
309 | -webkit-appearance: button; /* 1 */
310 | font: inherit; /* 2 */
311 | }
312 |
313 | /* Interactive
314 | ========================================================================== */
315 |
316 | /*
317 | * Add the correct display in Edge, IE 10+, and Firefox.
318 | */
319 |
320 | details {
321 | display: block;
322 | }
323 |
324 | /*
325 | * Add the correct display in all browsers.
326 | */
327 |
328 | summary {
329 | display: list-item;
330 | }
331 |
332 | /* Misc
333 | ========================================================================== */
334 |
335 | /**
336 | * Add the correct display in IE 10+.
337 | */
338 |
339 | template {
340 | display: none;
341 | }
342 |
343 | /**
344 | * Add the correct display in IE 10.
345 | */
346 |
347 | [hidden] {
348 | display: none;
349 | }
350 |
--------------------------------------------------------------------------------
/src/styles/sidebar.scss:
--------------------------------------------------------------------------------
1 | #app {
2 | // 主体区域
3 | .main-container {
4 | min-height: 100%;
5 | transition: margin-left .28s;
6 | margin-left: 180px;
7 | position: relative;
8 | }
9 | // 侧边栏
10 | .sidebar-container {
11 | transition: width 0.28s;
12 | width: 180px !important;
13 | height: 100%;
14 | position: fixed;
15 | font-size: 0px;
16 | top: 0;
17 | bottom: 0;
18 | left: 0;
19 | z-index: 1001;
20 | overflow: hidden;
21 | //reset element-ui css
22 | .horizontal-collapse-transition {
23 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
24 | }
25 | .scrollbar-wrapper {
26 | height: calc(100% + 15px);
27 | .el-scrollbar__view {
28 | height: 100%;
29 | }
30 | }
31 | .is-horizontal {
32 | display: none;
33 | }
34 | a {
35 | display: inline-block;
36 | width: 100%;
37 | overflow: hidden;
38 | }
39 | .svg-icon {
40 | margin-right: 16px;
41 | }
42 | .el-menu {
43 | border: none;
44 | height: 100%;
45 | width: 100% !important;
46 | }
47 | }
48 | .hideSidebar {
49 | .sidebar-container {
50 | width: 36px !important;
51 | }
52 | .main-container {
53 | margin-left: 36px;
54 | }
55 | .submenu-title-noDropdown {
56 | padding-left: 10px !important;
57 | position: relative;
58 | .el-tooltip {
59 | padding: 0 10px !important;
60 | }
61 | }
62 | .el-submenu {
63 | overflow: hidden;
64 | &>.el-submenu__title {
65 | padding-left: 10px !important;
66 | .el-submenu__icon-arrow {
67 | display: none;
68 | }
69 | }
70 | }
71 | .el-menu--collapse {
72 | .el-submenu {
73 | &>.el-submenu__title {
74 | &>span {
75 | height: 0;
76 | width: 0;
77 | overflow: hidden;
78 | visibility: hidden;
79 | display: inline-block;
80 | }
81 | }
82 | }
83 | }
84 | }
85 | .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
86 | .sidebar-container .el-submenu .el-menu-item {
87 | min-width: 180px !important;
88 | background-color: $subMenuBg !important;
89 | &:hover {
90 | background-color: $menuHover !important;
91 | }
92 | }
93 | .el-menu--collapse .el-menu .el-submenu {
94 | min-width: 180px !important;
95 | }
96 |
97 | //适配移动端
98 | .mobile {
99 | .main-container {
100 | margin-left: 0px;
101 | }
102 | .sidebar-container {
103 | transition: transform .28s;
104 | width: 180px !important;
105 | }
106 | &.hideSidebar {
107 | .sidebar-container {
108 | transition-duration: 0.3s;
109 | transform: translate3d(-180px, 0, 0);
110 | }
111 | }
112 | }
113 | .withoutAnimation {
114 | .main-container,
115 | .sidebar-container {
116 | transition: none;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | //globl transition css
2 |
3 | /*fade*/
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /*fade*/
15 | .breadcrumb-enter-active,
16 | .breadcrumb-leave-active {
17 | transition: all .5s;
18 | }
19 |
20 | .breadcrumb-enter,
21 | .breadcrumb-leave-active {
22 | opacity: 0;
23 | transform: translateX(20px);
24 | }
25 |
26 | .breadcrumb-move {
27 | transition: all .5s;
28 | }
29 |
30 | .breadcrumb-leave-active {
31 | position: absolute;
32 | }
33 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | //sidebar
2 | $menuBg:#304156;
3 | $subMenuBg:#1f2d3d;
4 | $menuHover:#001528;
5 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'erlinger'
4 | const routeKey = 'route'
5 | /**
6 | * 将用户token存储
7 | */
8 | export function getToken() {
9 | return Cookies.get(TokenKey)
10 | }
11 |
12 | export function setToken(token) {
13 | return Cookies.set(TokenKey, token)
14 | }
15 |
16 | export function removeToken() {
17 | return Cookies.remove(TokenKey)
18 | }
19 | /**
20 | * 将路由存储
21 | */
22 | export function setRouteToken(route) {
23 | return Cookies.set(routeKey, route)
24 | }
25 |
26 | export function getRouteToken() {
27 | return Cookies.get(routeKey)
28 | }
29 |
30 | export function removeRouteToken() {
31 | return Cookies.remove(routeKey)
32 | }
33 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @param {*} time
4 | * @param {*} cFormat
5 | */
6 | export function parseTime(time, cFormat) {
7 | if (arguments.length === 0) {
8 | return null
9 | }
10 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
11 | let date
12 | if (typeof time === 'object') {
13 | date = time
14 | } else {
15 | if (('' + time).length === 10) time = parseInt(time) * 1000
16 | date = new Date(time)
17 | }
18 | const formatObj = {
19 | y: date.getFullYear(),
20 | m: date.getMonth() + 1,
21 | d: date.getDate(),
22 | h: date.getHours(),
23 | i: date.getMinutes(),
24 | s: date.getSeconds(),
25 | a: date.getDay()
26 | }
27 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
28 | let value = formatObj[key]
29 | if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
30 | if (result.length > 0 && value < 10) {
31 | value = '0' + value
32 | }
33 | return value || 0
34 | })
35 | return time_str
36 | }
37 |
38 | export function formatTime(time, option) {
39 | time = +time * 1000
40 | const d = new Date(time)
41 | const now = Date.now()
42 |
43 | const diff = (now - d) / 1000
44 |
45 | if (diff < 30) {
46 | return '刚刚'
47 | } else if (diff < 3600) { // less 1 hour
48 | return Math.ceil(diff / 60) + '分钟前'
49 | } else if (diff < 3600 * 24) {
50 | return Math.ceil(diff / 3600) + '小时前'
51 | } else if (diff < 3600 * 24 * 2) {
52 | return '1天前'
53 | }
54 | if (option) {
55 | return parseTime(time, option)
56 | } else {
57 | return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/utils/request.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 | let baseURL = process.env.NODE_ENV === 'production' ? 'http://vue.wtodd.wang:4000' : '/'
7 | /** eslint disabled */
8 | // 创建axios实例
9 | const service = axios.create({
10 | baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
11 | timeout: 20000 // 请求超时时间
12 | })
13 | // request拦截器
14 | service.interceptors.request.use(
15 | config => {
16 | if (store.getters.token) {
17 | config.headers['w-token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
18 | }
19 | // console.log(config, 'config') // config 请求数据
20 | return config
21 | },
22 | error => {
23 | // Do something with request error
24 | console.log(error) // for debug
25 | Promise.reject(error)
26 | }
27 | )
28 |
29 | // response 拦截器
30 | service.interceptors.response.use(
31 | response => {
32 | /**
33 | * code为非20000是抛错 可结合自己业务进行修改
34 | */
35 | // console.log(response, '拦截器 response') // // response 响应数据
36 | const res = response.data
37 | console.log(res) // 在这里已经对返回的参数 做了判断,如果是失败的信息,就弹框提示失败的信息
38 | // if (res.code !== 20000) {
39 | if (!res.success) {
40 | // 这里可以统一提示错误信息
41 | Message({
42 | message: res.message,
43 | type: 'error'
44 | })
45 | // 也可以针对具体的返回参数 提示不同的信息
46 | // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
47 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
48 | MessageBox.confirm(
49 | '你已被登出,可以取消继续留在该页面,或者重新登录',
50 | '确定登出',
51 | {
52 | confirmButtonText: '重新登录',
53 | cancelButtonText: '取消',
54 | type: 'warning'
55 | }
56 | ).then(() => {
57 | store.dispatch('FedLogOut').then(() => {
58 | location.reload() // 为了重新实例化vue-router对象 避免bug
59 | })
60 | })
61 | }
62 | return Promise.reject('error')
63 | } else {
64 | // console.log(response.data)
65 | return response.data
66 | }
67 | },
68 | error => {
69 | console.log('err info' + error) // for debug
70 | Message({
71 | message: error.message,
72 | type: 'error',
73 | duration: 20 * 1000
74 | })
75 | return Promise.reject(error)
76 | }
77 | )
78 |
79 | export default service
80 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @param {*} str
4 | */
5 | export function isvalidUsername(str) {
6 | // const valid_map = ['admin', 'editor']
7 | const valid_map = str
8 | return valid_map.indexOf(str.trim()) >= 0
9 | }
10 |
11 | /* 合法uri*/
12 | export function validateURL(textval) {
13 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
14 | return urlregex.test(textval)
15 | }
16 |
17 | /* 小写字母*/
18 | export function validateLowerCase(str) {
19 | const reg = /^[a-z]+$/
20 | return reg.test(str)
21 | }
22 |
23 | /* 大写字母*/
24 | export function validateUpperCase(str) {
25 | const reg = /^[A-Z]+$/
26 | return reg.test(str)
27 | }
28 |
29 | /* 大小写字母*/
30 | export function validatAlphabets(str) {
31 | const reg = /^[A-Za-z]+$/
32 | return reg.test(str)
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
OOPS!
12 |
15 |
{{ message }}
16 |
请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告
17 |
返回首页
18 |
19 |
20 |
21 |
22 |
23 |
42 |
43 |
237 |
--------------------------------------------------------------------------------
/src/views/admin/manage-money.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
金钱管理
4 |
name:{{name}}
5 |
role:{{roles}}
6 |
7 |
8 |
9 |
22 |
23 |
34 |
--------------------------------------------------------------------------------
/src/views/admin/manage-users.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
用户管理:
4 |
name:{{name}}
5 |
role:{{roles}}
6 |
7 |
所有用户
8 |
9 |
15 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
34 |
35 | {{item}}
36 |
37 |
38 |
43 |
44 |
48 |
49 |
50 | Edit
53 | Delete
58 |
59 |
60 |
61 |
62 |
63 |
64 |
71 |
72 |
73 |
74 |
75 |
添加用户
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 立即创建
100 | 重置
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
114 |
118 |
119 |
120 |
121 |
122 |
125 |
126 |
姓名:{{currentUser}}
127 |
128 |
当前角色:{{currentRole}}
129 |
修改:
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
145 |
146 |
147 |
148 |
149 |
150 |
325 |
326 |
349 |
--------------------------------------------------------------------------------
/src/views/center/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 个人中心
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 姓名: 而恶
25 | 角色: 管理员
26 |
27 |
注册已经有 12 个月
28 |
29 | ff
30 | ff
31 | ff
32 | ff
33 |
34 |
35 |
36 |
37 |
38 |
39 |
83 |
84 |
123 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
name:{{name}}
4 |
role:{{roles}}
5 |
按钮权限限制
6 |
7 | admin
8 | admin
9 |
10 |
11 |
12 | dev
13 | dev
14 |
15 |
16 | boss
17 | boss
18 |
19 |
20 |
21 |
22 |
38 |
39 |
50 |
--------------------------------------------------------------------------------
/src/views/display/fiction.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
fiction
4 |
5 | Lorem ipsum dolor, sit amet
6 | consectetur adipisicing elit. Necessitatibus adipisci,
7 | hic eaque mollitia ipsam odio cumque ab provident facere omnis sequi,
8 | explicabo maiores? Architecto ipsam aspernatur illum impedit nam nihil?
9 |
10 |
11 |
12 |
13 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/views/display/index.vue:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adouwt/vue-cli3-admin-system/6cd999169d5fc0b8322487f5f2093f2ad6a0fb87/src/views/display/index.vue
--------------------------------------------------------------------------------
/src/views/display/technology.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
technology
4 |
Lorem ipsum, dolor sit amet consectetur adipisicing elit.
5 | Quidem eos sapiente dolorem tenetur repellat odio fugiat iusto
6 | aperiam! Debitis velit excepturi voluptates dolore?
7 | Quasi esse molestias itaque, soluta consequuntur molestiae!
8 |
9 |
10 |
11 |
12 |
22 |
23 |
28 |
--------------------------------------------------------------------------------
/src/views/edit/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 发布
21 | 保存草稿
22 | 保存草稿ceshi
23 |
24 |
25 |
26 |
下面是tinymce
27 |
28 |
29 |
30 | tinymce保存草稿
31 |
32 |
33 |
34 |
35 |
36 |
37 |
78 |
79 |
90 |
91 |
--------------------------------------------------------------------------------
/src/views/edit/test.js:
--------------------------------------------------------------------------------
1 | // /*
2 | // * My97 DatePicker 4.8
3 | // * License: http://www.my97.net/license.asp
4 | // */
5 | // var $dp, WdatePicker1, WdatePicker;
6 |
7 | // WdatePicker1 = (function () {
8 | // var l = {
9 | // $langList: [{ name: "en", charset: "UTF-8" },
10 | // { name: "zh-cn", charset: "gb2312" },
11 | // { name: "zh-tw", charset: "GBK" }],
12 | // $skinList: [{ name: "default", charset: "gb2312" },
13 | // { name: "whyGreen", charset: "gb2312" },
14 | // { name: "blue", charset: "gb2312" },
15 | // { name: "green", charset: "gb2312" },
16 | // { name: "simple", charset: "gb2312" },
17 | // { name: "ext", charset: "gb2312" },
18 | // { name: "blueFresh", charset: "gb2312" },
19 | // { name: "twoer", charset: "gb2312" },
20 | // { name: "YcloudRed", charset: "gb2312" }],
21 | // $wdate: true,
22 | // $crossFrame: true,
23 | // $preLoad: false,
24 | // $dpPath: "",
25 | // doubleCalendar: false,
26 | // enableKeyboard: true,
27 | // enableInputMask: true,
28 | // autoUpdateOnChanged: null,
29 | // weekMethod: "MSExcel",
30 | // position: {},
31 | // lang: "auto",
32 | // skin: "default",
33 | // dateFmt: "yyyy-MM-dd",
34 | // realDateFmt: "yyyy-MM-dd",
35 | // realTimeFmt: "HH:mm:ss",
36 | // realFullFmt: "%Date %Time",
37 | // minDate: "0001-01-01 00:00:00",
38 | // maxDate: "9999-12-31 23:59:59",
39 | // minTime: "00:00:00",
40 | // maxTime: "23:59:59",
41 | // startDate: "",
42 | // alwaysUseStartDate: false,
43 | // yearOffset: 1911,
44 | // firstDayOfWeek: 0,
45 | // isShowWeek: false,
46 | // highLineWeekDay: true,
47 | // isShowClear: true,
48 | // isShowToday: true,
49 | // isShowOK: true,
50 | // isShowOthers: true,
51 | // readOnly: false,
52 | // errDealMode: 0,
53 | // autoPickDate: null,
54 | // qsEnabled: true,
55 | // autoShowQS: false,
56 | // hmsMenuCfg: { H: [1, 6], m: [5, 6], s: [15, 4] },
57 | // opposite: false,
58 |
59 | // specialDates: null, specialDays: null, disabledDates: null,
60 |
61 | // disabledDays: null, onpicking: null, onpicked: null, onclearing: null, oncleared: null, ychanging: null, ychanged: null, Mchanging: null, Mchanged: null, dchanging: null, dchanged: null, Hchanging: null, Hchanged: null, mchanging: null, mchanged: null, schanging: null, schanged: null, eCont: null, vel: null, elProp: "", errMsg: "", quickSel: [], has: {}, getRealLang: function () { var d = l.$langList; for (var e = 0; e < d.length; e++) { if (d[e].name == this.lang) { return d[e] } } return d[0] }
62 | // };
63 | // WdatePicker = g;
64 | // var n = window, i = { innerHTML: "" }, z = "document", B = "documentElement", H = "getElementsByTagName", E, u, h, f, D;
65 | // var v = navigator.appName; if (v == "Microsoft Internet Explorer") { h = true } else { if (v == "Opera") { D = true } else { f = true } } u = l.$dpPath || q(); if (l.$wdate) { m(u + "skin/WdatePicker.css") } E = n;
66 | // if (l.$crossFrame) { try { while (E.parent != E && E.parent[z][H]("frameset").length == 0) { E = E.parent } } catch (y) { } } if (!E.$dp) { E.$dp = { ff: f, ie: h, opera: D, status: 0, defMinDate: l.minDate, defMaxDate: l.maxDate } } b();
67 | // if (l.$preLoad && $dp.status == 0) { k(n, "onload", function () { g(null, true) }) } if (!n[z].docMD) { k(n[z], "onmousedown", s, true); n[z].docMD = true } if (!E[z].docMD) { k(E[z], "onmousedown", s, true); E[z].docMD = true } k(n, "onunload", function () { if ($dp.dd) { r($dp.dd, "none") } });
68 | // function b() { try { E[z], E.$dp = E.$dp || {} } catch (I) { E = n; $dp = $dp || {} } var w = { win: n, $: function (e) { return (typeof e == "string") ? n[z].getElementById(e) : e }, $D: function (J, e) { return this.$DV(this.$(J).value, e) }, $DV: function (J, e) { if (J != "") { this.dt = $dp.cal.splitDate(J, $dp.cal.dateFmt);
69 | // if (e) { for (var L in e) { if (this.dt[L] === undefined) { this.errMsg = "invalid property:" + L } else { this.dt[L] += e[L]; if (L == "M") { var M = e.M > 0 ? 1 : 0; var K = new Date(this.dt.y, this.dt.M, 0).getDate(); this.dt.d = Math.min(K + M, this.dt.d) } } } } if (this.dt.refresh()) { return this.dt } } return "" }, show: function () { var K = E[z].getElementsByTagName("div"), J = 100000; for (var e = 0; e < K.length; e++) { var L = parseInt(K[e].style.zIndex);
70 | // if (L > J) { J = L } } this.dd.style.zIndex = J + 2; r(this.dd, "block"); r(this.dd.firstChild, "") }, unbind: function (e) { e = this.$(e); if (e.initcfg) { t(e, "onclick", function () { g(e.initcfg) });
71 | // t(e, "onfocus", function () { g(e.initcfg) }) } }, hide: function () { r(this.dd, "none") }, attachEvent: k }; for (var d in w) { E.$dp[d] = w[d] } $dp = E.$dp } function k(I, J, w, d) { if (I.addEventListener) { var e = J.replace(/on/, ""); w._ieEmuEventHandler = function (K) { return w(K) }; I.addEventListener(e, w._ieEmuEventHandler, d) } else { I.attachEvent(J, w) } } function t(w, I, e) { if (w.removeEventListener) { var d = I.replace(/on/, ""); e._ieEmuEventHandler = function (J) { return e(J) }; w.removeEventListener(d, e._ieEmuEventHandler, false) } else { w.detachEvent(I, e) } } function C(w, e, d) { if (typeof w != typeof e) { return false } if (typeof w == "object") { if (!d) { for (var I in w) { if (typeof e[I] == "undefined") { return false } if (!C(w[I], e[I], true)) { return false } } } return true } else { if (typeof w == "function" && typeof e == "function") { return w.toString() == e.toString() } else { return w == e } } } function q() { var I, w, d = n[z][H]("script"); for (var e = 0; e < d.length; e++) { I = d[e].getAttribute("src") || ""; I = I.substr(0, I.toLowerCase().indexOf("wdatepicker.js")); var w = I.lastIndexOf("/"); if (w > 0) { I = I.substring(0, w + 1) } if (I) { break } } return I } function m(w, I, J) { var d = n[z][H]("HEAD").item(0), e = n[z].createElement("link"); if (d) { e.href = w; e.rel = "stylesheet"; e.type = "text/css"; if (I) { e.title = I } if (J) { e.charset = J } d.appendChild(e) } } function p(I) { I = I || E; var L = 0, d = 0; while (I != E) { var N = I.parent[z][H]("iframe"); for (var J = 0; J < N.length; J++) { try { if (N[J].contentWindow == I) { var K = o(N[J]); L += K.left; d += K.top; break } } catch (M) { } } I = I.parent } return { leftM: L, topM: d } } function o(I, w) { if (I.getBoundingClientRect) { return I.getBoundingClientRect() } else { var J = { ROOT_TAG: /^body|html$/i, OP_SCROLL: /^(?:inline|table-row)$/i }; var e = false, M = null, P = I.offsetTop, K = I.offsetLeft, d = I.offsetWidth, O = I.offsetHeight; var L = I.offsetParent; if (L != I) { while (L) { K += L.offsetLeft; P += L.offsetTop; if (c(L, "position").toLowerCase() == "fixed") { e = true } else { if (L.tagName.toLowerCase() == "body") { M = L.ownerDocument.defaultView } } L = L.offsetParent } } L = I.parentNode; while (L.tagName && !J.ROOT_TAG.test(L.tagName)) { if (L.scrollTop || L.scrollLeft) { if (!J.OP_SCROLL.test(r(L))) { if (!D || L.style.overflow !== "visible") { K -= L.scrollLeft; P -= L.scrollTop } } } L = L.parentNode } if (!e) { var N = F(M); K -= N.left; P -= N.top } d += K; O += P; return { left: K, top: P, right: d, bottom: O } } } function x(e) { e = e || E; var J = e[z], I = (e.innerWidth) ? e.innerWidth : (J[B] && J[B].clientWidth) ? J[B].clientWidth : J.body.offsetWidth, d = (e.innerHeight) ? e.innerHeight : (J[B] && J[B].clientHeight) ? J[B].clientHeight : J.body.offsetHeight; return { width: I, height: d } } function F(e) { e = e || E; var J = e[z], d = J[B], I = J.body; J = (d && d.scrollTop != null && (d.scrollTop > I.scrollTop || d.scrollLeft > I.scrollLeft)) ? d : I; return { top: J.scrollTop, left: J.scrollLeft } } function s(d) { try { var w = d ? (d.srcElement || d.target) : null; if ($dp.cal && !$dp.eCont && $dp.dd && w != $dp.el && $dp.dd.style.display == "block") { $dp.cal.close() } } catch (d) { } } function A() { $dp.status = 2 } var G, j; function g(M, d) { if (!$dp) { return } b(); var J = {}; for (var L in M) { J[L] = M[L] } for (var L in l) { if (L.substring(0, 1) != "$" && J[L] === undefined) { J[L] = l[L] } } if (d) { if (!w()) { j = j || setInterval(function () { if (E[z].readyState == "complete") { clearInterval(j) } g(null, true) }, 50); return } if ($dp.status == 0) { $dp.status = 1; J.el = i; a(J, true) } else { return } } else { if (J.eCont) { J.eCont = $dp.$(J.eCont); J.el = i; J.autoPickDate = true; J.qsEnabled = false; a(J) } else { if (l.$preLoad && $dp.status != 2) { return } var I = N(); if (n.event === I || I) { J.srcEl = I.srcElement || I.target; I.cancelBubble = true } J.el = J.el = $dp.$(J.el || J.srcEl); if (J.el == null) { alert("WdatePicker:el is null") } try { if (!J.el || J.el.My97Mark === true || J.el.disabled || ($dp.dd && r($dp.dd) != "none" && $dp.dd.style.left != "-970px")) { if (J.el.My97Mark) { J.el.My97Mark = false } return } } catch (K) { } if (I && J.el.nodeType == 1 && !C(J.el.initcfg, M)) { $dp.unbind(J.el); J.el.initcfg = M } a(J) } } function w() { if (h && E != n && E[z].readyState != "complete") { return false } return true } function N() { if (f) { try { func = N.caller; while (func != null) { var O = func.arguments[0]; if (O && (O + "").indexOf("Event") >= 0) { return O } func = func.caller } } catch (P) { } return null } return event } } function c(e, d) { return e.currentStyle ? e.currentStyle[d] : document.defaultView.getComputedStyle(e, false)[d] } function r(e, d) { if (e) { if (d != null) { e.style.display = d } else { return c(e, "display") } } } function a(e, d) { var K = e.el ? e.el.nodeName : "INPUT"; if (d || e.eCont || new RegExp(/input|textarea|div|span|p|a/ig).test(K)) { e.elProp = K == "INPUT" ? "value" : "innerHTML" } else { return } if (e.lang == "auto") { e.lang = h ? navigator.browserLanguage.toLowerCase() : navigator.language.toLowerCase() } if (!e.eCont) { for (var J in e) { $dp[J] = e[J] } } if (!$dp.dd || e.eCont || ($dp.dd && (e.getRealLang().name != $dp.dd.lang || e.skin != $dp.dd.skin))) { if (e.eCont) { w(e.eCont, e) } else { $dp.dd = E[z].createElement("DIV"); $dp.dd.style.cssText = "position:absolute"; E[z].body.appendChild($dp.dd); w($dp.dd, e); if (d) { $dp.dd.style.left = $dp.dd.style.top = "-970px" } else { $dp.show(); I($dp) } } } else { if ($dp.cal) { $dp.show(); $dp.cal.init(); if (!$dp.eCont) { I($dp) } } } function w(V, P) { var O = E[z].domain, S = false, M = ''; V.innerHTML = M; var L = l.$langList, U = l.$skinList, T; try { T = V.lastChild.contentWindow[z] } catch (Q) { S = true; V.removeChild(V.lastChild); var N = E[z].createElement("iframe"); N.hideFocus = true; N.frameBorder = 0; N.scrolling = "no"; N.src = "javascript:(function(){var d=document;d.open();d.domain='" + O + "';})()"; V.appendChild(N); setTimeout(function () { T = V.lastChild.contentWindow[z]; R() }, 97); return } R(); function R() { var Y = P.getRealLang(); V.lang = Y.name; V.skin = P.skin; var X = ["
47 |
48 |
51 |
--------------------------------------------------------------------------------
/src/views/layout/components/AppMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
29 |
--------------------------------------------------------------------------------
/src/views/layout/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
角色: {{roles}}
8 |
{{name}}
9 |
10 |
11 |
12 |
13 |
14 |
15 | Home
16 |
17 |
18 |
19 |
20 | MY Center
21 |
22 |
23 |
24 | LogOut
25 |
26 |
27 |
28 |
29 |
30 |
31 |
61 |
62 |
109 |
110 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/SidebarItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
90 |
91 |
96 |
97 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
37 |
--------------------------------------------------------------------------------
/src/views/layout/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navbar } from './Navbar'
2 | export { default as Sidebar } from './Sidebar'
3 | export { default as AppMain } from './AppMain'
4 |
--------------------------------------------------------------------------------
/src/views/layout/mixin/ResizeHandler.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 |
3 | const { body } = document
4 | const WIDTH = 1024
5 | const RATIO = 3
6 |
7 | export default {
8 | watch: {
9 | $route(route) {
10 | if (this.device === 'mobile' && this.sidebar.opened) {
11 | store.dispatch('CloseSideBar', { withoutAnimation: false })
12 | }
13 | }
14 | },
15 | beforeMount() {
16 | window.addEventListener('resize', this.resizeHandler)
17 | },
18 | mounted() {
19 | const isMobile = this.isMobile()
20 | if (isMobile) {
21 | store.dispatch('ToggleDevice', 'mobile')
22 | store.dispatch('CloseSideBar', { withoutAnimation: true })
23 | }
24 | },
25 | methods: {
26 | isMobile() {
27 | const rect = body.getBoundingClientRect()
28 | return rect.width - RATIO < WIDTH
29 | },
30 | resizeHandler() {
31 | if (!document.hidden) {
32 | const isMobile = this.isMobile()
33 | store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop')
34 |
35 | if (isMobile) {
36 | store.dispatch('CloseSideBar', { withoutAnimation: true })
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ** 管理系统 **
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
23 |
24 |
25 |
26 |
27 |
28 | Sign in
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
110 |
111 |
145 |
146 |
208 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/menu1-1/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/menu1-2/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/nested/menu1/menu1-3/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/nested/menu2/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/register/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | vue-cli3 admin--注册
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
34 |
40 |
41 | 发送验证码
42 |
43 |
44 |
45 |
46 |
47 |
49 |
50 |
54 |
55 |
56 | Sign up
57 |
58 |
59 |
60 |
61 |
62 |
63 |
188 |
189 |
221 |
222 |
311 |
--------------------------------------------------------------------------------
/src/views/table/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{scope.$index}}
7 |
8 |
9 |
10 |
11 | {{scope.row.title}}
12 |
13 |
14 |
15 |
16 | {{scope.row.author}}
17 |
18 |
19 |
20 |
21 | {{scope.row.pageviews}}
22 |
23 |
24 |
25 |
26 | {{scope.row.status}}
27 |
28 |
29 |
30 |
31 |
32 | {{scope.row.display_time}}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
73 |
--------------------------------------------------------------------------------
/src/views/tree/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
71 |
72 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
2 | const path = require('path');
3 |
4 | function resolve(dir) {
5 | return path.join(__dirname, dir)
6 | }
7 | module.exports = {
8 | // 项目部署的基础路径
9 | // 我们默认假设你的应用将会部署在域名的根部,
10 | // 比如 https://www.my-app.com/
11 | // 如果你的应用时部署在一个子路径下,那么你需要在这里
12 | // 指定子路径。比如,如果你的应用部署在
13 | // https://www.foobar.com/my-app/
14 | // 那么将这个值改为 `/my-app/`
15 | publicPath: process.env.VUE_APP_PUBLIC_URL,
16 |
17 | // 将构建好的文件输出到哪里
18 | outputDir: 'dist',
19 |
20 | // 放置静态资源的地方 (js/css/img/font/...)
21 | // assetsDir: '',
22 |
23 | // 是否在保存的时候使用 `eslint-loader` 进行检查。
24 | // 有效的值:`ture` | `false` | `"error"`
25 | // 当设置为 `"error"` 时,检查出的错误会触发编译失败。
26 | lintOnSave: true,
27 |
28 | // 使用带有浏览器内编译器的完整构建版本
29 | // 查阅 https://cn.vuejs.org/v2/guide/installation.html#运行时-编译器-vs-只包含运行时
30 | // compiler: false,
31 |
32 | // babel-loader 默认会跳过 node_modules 依赖。
33 | // 通过这个选项可以显式转译一个依赖。
34 | transpileDependencies: [/* string or regex */],
35 |
36 | // 是否为生产环境构建生成 source map?
37 | productionSourceMap: false,
38 |
39 | // 调整内部的 webpack 配置。
40 | // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/webpack.md
41 | chainWebpack: () => { },
42 | configureWebpack: () => { },
43 |
44 | // CSS 相关选项
45 | css: {
46 | // 将组件内的 CSS 提取到一个单独的 CSS 文件 (只用在生产环境中)
47 | // 也可以是一个传递给 `extract-text-webpack-plugin` 的选项对象
48 | extract: true,
49 |
50 | // 是否开启 CSS source map?
51 | sourceMap: false,
52 |
53 | // 为预处理器的 loader 传递自定义选项。比如传递给
54 | // sass-loader 时,使用 `{ sass: { ... } }`。
55 | loaderOptions: {},
56 |
57 | // 为所有的 CSS 及其预处理文件开启 CSS Modules。
58 | // 这个选项不会影响 `*.vue` 文件。
59 | modules: false
60 | },
61 |
62 | // 在生产环境下为 Babel 和 TypeScript 使用 `thread-loader`
63 | // 在多核机器下会默认开启。
64 | parallel: require('os').cpus().length > 1,
65 |
66 | // PWA 插件的选项。
67 | // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli-plugin-pwa/README.md
68 | pwa: {},
69 |
70 | // 配置 webpack-dev-server 行为。
71 | devServer: {
72 | open: process.platform === 'darwin',
73 | host: '0.0.0.0',
74 | port: 8080,
75 | https: false,
76 | hotOnly: false,
77 | open:true,
78 | // 查阅 https://github.com/vuejs/vue-docs-zh-cn/blob/master/vue-cli/cli-service.md#配置代理
79 | proxy: {
80 | '/nodejsapi': {
81 | // target: 'http://vue.wtodd.wang:4000',
82 | target: 'http://localhost:4000',
83 | changeOrigin: true,
84 | ws: true,
85 | pathRewrite: {
86 | '^/nodejsapi': ''
87 | }
88 | }
89 | },
90 | before: app => { }
91 | },
92 |
93 | configureWebpack: config => {
94 | if (process.env.NODE_ENV === 'production') {
95 | // 为生产环境修改配置...
96 | if(process.env.VUE_LIFECIRCLE_EVENT === 'ANALYZE'){
97 | config.plugins.push(
98 | new BundleAnalyzerPlugin(
99 | {
100 | analyzerMode: 'server',
101 | analyzerHost: '127.0.0.1',
102 | analyzerPort: 8889,
103 | reportFilename: 'report.html',
104 | defaultSizes: 'parsed',
105 | openAnalyzer: true,
106 | generateStatsFile: false,
107 | statsFilename: 'stats.json',
108 | statsOptions: null,
109 | logLevel: 'info'
110 | }
111 | )
112 | );
113 | }
114 |
115 | } else {
116 | // 为开发环境修改配置...
117 | if(process.env.VUE_LIFECIRCLE_EVENT === 'DEV'){
118 | config.plugins.push(
119 | new BundleAnalyzerPlugin(
120 | {
121 | analyzerMode: 'server',
122 | analyzerHost: '127.0.0.1',
123 | analyzerPort: 8888,
124 | reportFilename: 'report.html',
125 | defaultSizes: 'parsed',
126 | openAnalyzer: true,
127 | generateStatsFile: false,
128 | statsFilename: 'stats.json',
129 | statsOptions: null,
130 | logLevel: 'info'
131 | }
132 | )
133 | );
134 | }
135 | }
136 |
137 | },
138 |
139 | // 第三方插件的选项
140 | pluginOptions: {
141 |
142 | }
143 | }
--------------------------------------------------------------------------------