Complate Dashboard index
108
package.json
|
@ -1,35 +1,55 @@
|
|||
{
|
||||
"name": "halo-admin",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"postinstall": "opencollective-postinstall"
|
||||
},
|
||||
"dependencies": {
|
||||
"ant-design-vue": "^1.3.7",
|
||||
"@antv/data-set": "^0.10.1",
|
||||
"ant-design-vue": "~1.3.7",
|
||||
"axios": "^0.18.0",
|
||||
"enquire.js": "^2.1.6",
|
||||
"js-cookie": "^2.2.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"md5": "^2.2.1",
|
||||
"moment": "^2.24.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"vue": "^2.6.6",
|
||||
"opencollective-postinstall": "^2.0.2",
|
||||
"viser-vue": "^2.3.3",
|
||||
"vue": "^2.5.22",
|
||||
"vue-clipboard2": "^0.2.1",
|
||||
"vue-cropper": "0.4.4",
|
||||
"vue-ls": "^3.2.0",
|
||||
"vue-router": "^3.0.1",
|
||||
"vuejs-logger": "^1.5.3",
|
||||
"vuex": "^3.0.1"
|
||||
"vue-svg-component-runtime": "^1.0.1",
|
||||
"vuex": "^3.1.0",
|
||||
"opencollective": "^1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.5.0",
|
||||
"@vue/cli-plugin-eslint": "^3.5.0",
|
||||
"@vue/cli-plugin-pwa": "^3.5.0",
|
||||
"@vue/cli-service": "^3.5.0",
|
||||
"@vue/eslint-config-prettier": "^4.0.1",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@vue/cli-plugin-babel": "^3.3.0",
|
||||
"@vue/cli-plugin-eslint": "^3.3.0",
|
||||
"@vue/cli-plugin-unit-jest": "^3.3.0",
|
||||
"@vue/cli-service": "^3.3.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.20",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-plugin-import": "^1.11.0",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-plugin-html": "^5.0.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"node-sass": "^4.9.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"vue-loader": "^15.7.0",
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
"less": "^3.8.1",
|
||||
"less-loader": "^4.1.0",
|
||||
"vue-svg-icon-loader": "^2.1.1",
|
||||
"vue-template-compiler": "^2.5.22"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
@ -37,11 +57,59 @@
|
|||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/recommended",
|
||||
"@vue/prettier"
|
||||
"plugin:vue/strongly-recommended",
|
||||
"@vue/standard"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {
|
||||
"generator-star-spacing": "off",
|
||||
"no-mixed-operators": 0,
|
||||
"vue/max-attributes-per-line": [
|
||||
2,
|
||||
{
|
||||
"singleline": 5,
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"vue/attribute-hyphenation": 0,
|
||||
"vue/html-self-closing": 0,
|
||||
"vue/component-name-in-template-casing": 0,
|
||||
"vue/html-closing-bracket-spacing": 0,
|
||||
"vue/singleline-html-element-content-newline": 0,
|
||||
"vue/no-unused-components": 0,
|
||||
"vue/multiline-html-element-content-newline": 0,
|
||||
"vue/no-use-v-if-with-v-for": 0,
|
||||
"vue/html-closing-bracket-newline": 0,
|
||||
"vue/no-parsing-error": 0,
|
||||
"no-console": 0,
|
||||
"no-tabs": 0,
|
||||
"quotes": [
|
||||
2,
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"semi": [
|
||||
2,
|
||||
"never",
|
||||
{
|
||||
"beforeStatementContinuationChars": "never"
|
||||
}
|
||||
],
|
||||
"no-delete-var": 2,
|
||||
"prefer-const": [
|
||||
2,
|
||||
{
|
||||
"ignoreReadBeforeAssign": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
|
@ -52,6 +120,6 @@
|
|||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
"not ie <= 10"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 799 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
|
@ -1,149 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,16.000000) scale(0.000320,-0.000320)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M18 46618 c45 -75 122 -207 122 -211 0 -2 25 -45 55 -95 30 -50 55
|
||||
-96 55 -102 0 -5 5 -10 10 -10 6 0 10 -4 10 -9 0 -5 73 -135 161 -288 89 -153
|
||||
173 -298 187 -323 14 -25 32 -57 41 -72 88 -149 187 -324 189 -335 2 -7 8 -13
|
||||
13 -13 5 0 9 -4 9 -10 0 -5 46 -89 103 -187 175 -302 490 -846 507 -876 8 -16
|
||||
20 -36 25 -45 28 -46 290 -498 339 -585 13 -23 74 -129 136 -236 61 -107 123
|
||||
-215 137 -240 14 -25 29 -50 33 -56 5 -5 23 -37 40 -70 18 -33 38 -67 44 -75
|
||||
11 -16 21 -33 63 -109 14 -25 29 -50 33 -56 4 -5 21 -35 38 -65 55 -100 261
|
||||
-455 269 -465 4 -5 14 -21 20 -35 15 -29 41 -75 103 -180 24 -41 52 -88 60
|
||||
-105 9 -16 57 -100 107 -185 112 -193 362 -626 380 -660 8 -14 23 -38 33 -55
|
||||
11 -16 23 -37 27 -45 4 -8 26 -46 48 -85 23 -38 53 -90 67 -115 46 -81 64
|
||||
-113 178 -310 62 -107 121 -210 132 -227 37 -67 56 -99 85 -148 16 -27 32 -57
|
||||
36 -65 4 -8 15 -27 25 -42 9 -15 53 -89 96 -165 44 -76 177 -307 296 -513 120
|
||||
-206 268 -463 330 -570 131 -227 117 -203 200 -348 36 -62 73 -125 82 -140 10
|
||||
-15 21 -34 25 -42 4 -8 20 -37 36 -65 17 -27 38 -65 48 -82 49 -85 64 -111 87
|
||||
-153 13 -25 28 -49 32 -55 4 -5 78 -134 165 -285 87 -151 166 -288 176 -305
|
||||
10 -16 26 -43 35 -59 9 -17 125 -217 257 -445 132 -229 253 -441 270 -471 17
|
||||
-30 45 -79 64 -108 18 -29 33 -54 33 -57 0 -2 20 -37 44 -77 24 -40 123 -212
|
||||
221 -383 97 -170 190 -330 205 -355 16 -25 39 -65 53 -90 13 -25 81 -144 152
|
||||
-265 70 -121 137 -238 150 -260 12 -22 37 -65 55 -95 18 -30 43 -73 55 -95 12
|
||||
-22 48 -85 80 -140 77 -132 163 -280 190 -330 13 -22 71 -123 130 -225 59
|
||||
-102 116 -199 126 -217 10 -17 29 -50 43 -72 15 -22 26 -43 26 -45 0 -2 27
|
||||
-50 60 -106 33 -56 60 -103 60 -105 0 -2 55 -98 90 -155 8 -14 182 -316 239
|
||||
-414 13 -22 45 -79 72 -124 27 -46 49 -86 49 -89 0 -2 14 -24 30 -48 16 -24
|
||||
30 -46 30 -49 0 -5 74 -135 100 -176 5 -8 24 -42 43 -75 50 -88 58 -101 262
|
||||
-455 104 -179 199 -345 213 -370 14 -25 28 -49 32 -55 4 -5 17 -26 28 -45 10
|
||||
-19 62 -109 114 -200 114 -197 133 -230 170 -295 16 -27 33 -57 38 -65 17 -28
|
||||
96 -165 103 -180 4 -8 16 -28 26 -45 10 -16 77 -131 148 -255 72 -124 181
|
||||
-313 243 -420 62 -107 121 -209 131 -227 35 -62 323 -560 392 -678 38 -66 83
|
||||
-145 100 -175 16 -30 33 -59 37 -65 4 -5 17 -27 29 -47 34 -61 56 -100 90
|
||||
-156 17 -29 31 -55 31 -57 0 -2 17 -32 39 -67 21 -35 134 -229 251 -433 117
|
||||
-203 235 -407 261 -451 27 -45 49 -85 49 -88 0 -4 8 -19 19 -34 15 -21 200
|
||||
-341 309 -533 10 -19 33 -58 51 -87 17 -29 31 -54 31 -56 0 -2 25 -44 55 -94
|
||||
30 -50 55 -95 55 -98 0 -4 6 -15 14 -23 7 -9 27 -41 43 -71 17 -30 170 -297
|
||||
342 -594 171 -296 311 -542 311 -547 0 -5 5 -9 10 -9 6 0 10 -4 10 -10 0 -5
|
||||
22 -47 49 -92 27 -46 58 -99 68 -118 24 -43 81 -140 93 -160 5 -8 66 -114 135
|
||||
-235 69 -121 130 -227 135 -235 12 -21 259 -447 283 -490 10 -19 28 -47 38
|
||||
-62 11 -14 19 -29 19 -32 0 -3 37 -69 83 -148 99 -170 305 -526 337 -583 13
|
||||
-22 31 -53 41 -70 11 -16 22 -37 26 -45 7 -14 82 -146 103 -180 14 -24 181
|
||||
-311 205 -355 13 -22 46 -80 75 -130 29 -49 64 -110 78 -135 14 -25 51 -88 82
|
||||
-140 31 -52 59 -102 63 -110 4 -8 18 -33 31 -55 205 -353 284 -489 309 -535
|
||||
17 -30 45 -78 62 -106 18 -28 36 -60 39 -72 4 -12 12 -22 17 -22 5 0 9 -4 9
|
||||
-10 0 -5 109 -197 241 -427 133 -230 250 -431 259 -448 51 -90 222 -385 280
|
||||
-485 37 -63 78 -135 92 -160 14 -25 67 -117 118 -205 51 -88 101 -175 111
|
||||
-193 34 -58 55 -95 149 -257 51 -88 101 -173 110 -190 9 -16 76 -131 147 -255
|
||||
72 -124 140 -241 151 -260 61 -108 281 -489 355 -615 38 -66 77 -133 87 -150
|
||||
35 -63 91 -161 100 -175 14 -23 99 -169 128 -220 54 -97 135 -235 142 -245 4
|
||||
-5 20 -32 35 -60 26 -48 238 -416 276 -480 10 -16 26 -46 37 -65 30 -53 382
|
||||
-661 403 -695 10 -16 22 -37 26 -45 4 -8 26 -48 50 -88 24 -41 43 -75 43 -77
|
||||
0 -2 22 -40 50 -85 27 -45 50 -84 50 -86 0 -3 38 -69 83 -147 84 -142 302
|
||||
-520 340 -587 10 -19 34 -60 52 -90 18 -30 44 -75 57 -100 14 -25 45 -79 70
|
||||
-120 25 -41 56 -96 70 -121 14 -25 77 -133 138 -240 62 -107 122 -210 132
|
||||
-229 25 -43 310 -535 337 -581 11 -19 26 -45 34 -59 17 -32 238 -414 266 -460
|
||||
11 -19 24 -41 28 -49 3 -7 75 -133 160 -278 84 -146 153 -269 153 -274 0 -5 5
|
||||
-9 10 -9 6 0 10 -4 10 -10 0 -5 82 -150 181 -322 182 -314 201 -346 240 -415
|
||||
12 -21 80 -139 152 -263 71 -124 141 -245 155 -270 14 -25 28 -49 32 -55 6 -8
|
||||
145 -248 220 -380 37 -66 209 -362 229 -395 11 -19 24 -42 28 -49 4 -8 67
|
||||
-118 140 -243 73 -125 133 -230 133 -233 0 -2 15 -28 33 -57 19 -29 47 -78 64
|
||||
-108 17 -30 53 -93 79 -139 53 -90 82 -141 157 -272 82 -142 115 -199 381
|
||||
-659 142 -245 268 -463 281 -485 12 -22 71 -125 132 -230 60 -104 172 -298
|
||||
248 -430 76 -132 146 -253 156 -270 11 -16 22 -36 26 -44 3 -8 30 -54 60 -103
|
||||
29 -49 53 -91 53 -93 0 -3 18 -34 40 -70 22 -36 40 -67 40 -69 0 -2 37 -66 81
|
||||
-142 45 -77 98 -168 119 -204 20 -36 47 -81 58 -100 12 -19 27 -47 33 -62 6
|
||||
-16 15 -28 20 -28 5 0 9 -4 9 -9 0 -6 63 -118 140 -251 77 -133 140 -243 140
|
||||
-245 0 -2 18 -33 41 -70 22 -37 49 -83 60 -101 10 -19 29 -51 40 -71 25 -45
|
||||
109 -189 126 -218 7 -11 17 -29 22 -40 6 -11 22 -38 35 -60 14 -22 37 -62 52
|
||||
-90 14 -27 35 -62 45 -77 11 -14 19 -29 19 -32 0 -3 18 -35 40 -71 22 -36 40
|
||||
-67 40 -69 0 -2 19 -35 42 -72 23 -38 55 -94 72 -124 26 -47 139 -244 171
|
||||
-298 6 -9 21 -36 34 -60 28 -48 37 -51 51 -19 6 12 19 36 29 52 10 17 27 46
|
||||
38 65 11 19 104 181 208 360 103 179 199 345 213 370 14 25 42 74 64 109 21
|
||||
34 38 65 38 67 0 2 18 33 40 69 22 36 40 67 40 69 0 3 177 310 199 346 16 26
|
||||
136 234 140 244 2 5 25 44 52 88 27 44 49 81 49 84 0 2 18 34 40 70 22 36 40
|
||||
67 40 69 0 2 20 36 43 77 35 58 169 289 297 513 9 17 50 86 90 155 40 69 86
|
||||
150 103 180 16 30 35 62 41 70 6 8 16 24 22 35 35 64 72 129 167 293 59 100
|
||||
116 199 127 220 11 20 30 53 41 72 43 72 1070 1850 1121 1940 14 25 65 113
|
||||
113 195 48 83 96 166 107 185 10 19 28 50 38 68 11 18 73 124 137 235 64 111
|
||||
175 303 246 427 71 124 173 299 225 390 52 91 116 202 143 248 27 45 49 85 49
|
||||
89 0 4 6 14 14 22 7 9 28 43 46 76 26 47 251 436 378 655 11 19 29 51 40 70
|
||||
11 19 101 176 201 348 99 172 181 317 181 323 0 5 5 9 10 9 6 0 10 5 10 11 0
|
||||
6 8 23 18 37 11 15 32 52 49 82 16 30 130 228 253 440 122 212 234 405 248
|
||||
430 13 25 39 70 57 100 39 65 69 117 130 225 25 44 50 87 55 95 12 19 78 134
|
||||
220 380 61 107 129 224 150 260 161 277 222 382 246 425 15 28 47 83 71 123
|
||||
24 41 43 78 43 83 0 5 4 9 8 9 4 0 13 12 19 28 7 15 23 45 36 67 66 110 277
|
||||
478 277 483 0 3 6 13 14 21 7 9 27 41 43 71 17 30 45 80 63 110 34 57 375 649
|
||||
394 685 6 11 16 27 22 35 6 8 26 42 44 75 18 33 41 74 51 90 10 17 24 41 32
|
||||
55 54 97 72 128 88 152 11 14 19 28 19 30 0 3 79 141 175 308 96 167 175 305
|
||||
175 308 0 3 6 13 14 21 7 9 26 39 41 66 33 60 276 483 338 587 24 40 46 80 50
|
||||
88 4 8 13 24 20 35 14 23 95 163 125 215 11 19 52 91 92 160 40 69 80 139 90
|
||||
155 9 17 103 179 207 360 105 182 200 346 211 365 103 181 463 802 489 845 7
|
||||
11 15 27 19 35 4 8 29 51 55 95 64 110 828 1433 848 1470 9 17 24 41 33 55 9
|
||||
14 29 48 45 77 15 28 52 93 82 145 30 51 62 107 71 123 17 30 231 398 400 690
|
||||
51 88 103 179 115 202 12 23 26 48 32 55 6 7 24 38 40 68 17 30 61 107 98 170
|
||||
37 63 84 144 103 180 19 36 41 72 48 81 8 8 14 18 14 21 0 4 27 51 59 106 32
|
||||
55 72 124 89 154 16 29 71 125 122 213 51 88 104 180 118 205 13 25 28 50 32
|
||||
55 4 6 17 26 28 45 11 19 45 80 77 135 31 55 66 116 77 135 11 19 88 152 171
|
||||
295 401 694 620 1072 650 1125 11 19 87 152 170 295 83 143 158 273 166 288 9
|
||||
16 21 36 26 45 6 9 31 52 55 96 25 43 54 94 66 115 11 20 95 164 186 321 91
|
||||
157 173 299 182 315 9 17 26 46 37 65 12 19 66 114 121 210 56 96 108 186 117
|
||||
200 8 14 24 40 34 59 24 45 383 664 412 713 5 9 17 29 26 45 15 28 120 210
|
||||
241 419 36 61 68 117 72 125 4 8 12 23 19 34 35 57 245 420 262 453 11 20 35
|
||||
61 53 90 17 29 32 54 32 56 0 3 28 51 62 108 33 57 70 119 80 138 10 19 23 42
|
||||
28 50 5 8 32 53 59 100 27 47 149 258 271 470 122 212 234 405 248 430 30 53
|
||||
62 108 80 135 6 11 15 27 19 35 4 8 85 150 181 315 96 165 187 323 202 350 31
|
||||
56 116 202 130 225 5 8 25 42 43 75 19 33 92 159 162 280 149 257 157 271 202
|
||||
350 19 33 38 67 43 75 9 14 228 392 275 475 12 22 55 96 95 165 40 69 80 139
|
||||
90 155 24 42 202 350 221 383 9 15 27 47 41 72 14 25 75 131 136 236 61 106
|
||||
121 210 134 232 99 172 271 470 279 482 5 8 23 40 40 70 18 30 81 141 142 245
|
||||
60 105 121 210 135 235 14 25 71 124 127 220 56 96 143 247 194 335 51 88 96
|
||||
167 102 175 14 24 180 311 204 355 23 43 340 590 356 615 5 8 50 87 101 175
|
||||
171 301 517 898 582 1008 25 43 46 81 46 83 0 2 12 23 27 47 14 23 40 67 56
|
||||
97 16 30 35 62 42 70 7 8 15 22 18 30 4 8 20 38 37 65 16 28 33 57 37 65 6 12
|
||||
111 196 143 250 5 8 55 95 112 193 57 98 113 195 126 215 12 20 27 46 32 57 6
|
||||
11 14 27 20 35 5 8 76 130 156 270 80 140 165 287 187 325 23 39 52 90 66 115
|
||||
13 25 30 52 37 61 8 8 14 18 14 21 0 4 41 77 92 165 50 87 175 302 276 478
|
||||
101 176 208 360 236 408 28 49 67 117 86 152 19 35 41 70 48 77 6 6 12 15 12
|
||||
19 0 7 124 224 167 291 12 21 23 40 23 42 0 2 21 40 46 83 26 43 55 92 64 109
|
||||
54 95 327 568 354 614 19 30 45 75 59 100 71 128 82 145 89 148 4 2 8 8 8 13
|
||||
0 5 42 82 94 172 311 538 496 858 518 897 14 25 40 70 58 100 18 30 42 71 53
|
||||
90 10 19 79 139 152 265 73 127 142 246 153 265 10 19 43 76 72 125 29 50 63
|
||||
108 75 130 65 116 80 140 87 143 4 2 8 8 8 12 0 8 114 212 140 250 6 8 14 24
|
||||
20 35 5 11 54 97 108 190 l100 170 -9611 3 c-5286 1 -9614 -1 -9618 -5 -5 -6
|
||||
-419 -719 -619 -1068 -89 -155 -267 -463 -323 -560 -38 -66 -81 -140 -95 -165
|
||||
-31 -56 -263 -457 -526 -910 -110 -190 -224 -388 -254 -440 -29 -52 -61 -109
|
||||
-71 -125 -23 -39 -243 -420 -268 -465 -11 -19 -204 -352 -428 -740 -224 -388
|
||||
-477 -826 -563 -975 -85 -148 -185 -322 -222 -385 -37 -63 -120 -207 -185
|
||||
-320 -65 -113 -177 -306 -248 -430 -72 -124 -172 -297 -222 -385 -51 -88 -142
|
||||
-245 -202 -350 -131 -226 -247 -427 -408 -705 -65 -113 -249 -432 -410 -710
|
||||
-160 -278 -388 -673 -506 -877 -118 -205 -216 -373 -219 -373 -3 0 -52 82
|
||||
-109 183 -58 100 -144 250 -192 332 -95 164 -402 696 -647 1120 -85 149 -228
|
||||
396 -317 550 -212 365 -982 1700 -1008 1745 -10 19 -43 76 -72 125 -29 50 -64
|
||||
110 -77 135 -14 25 -63 110 -110 190 -47 80 -96 165 -110 190 -14 25 -99 171
|
||||
-188 325 -89 154 -174 300 -188 325 -13 25 -64 113 -112 195 -48 83 -140 242
|
||||
-205 355 -65 113 -183 317 -263 454 -79 137 -152 264 -163 282 -50 89 -335
|
||||
583 -354 614 -12 19 -34 58 -50 85 -15 28 -129 226 -253 440 -124 215 -235
|
||||
408 -247 430 -12 22 -69 121 -127 220 -58 99 -226 389 -373 645 -148 256 -324
|
||||
561 -392 678 -67 117 -134 232 -147 255 -13 23 -33 59 -46 80 l-22 37 -9615 0
|
||||
-9615 0 20 -32z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 10 KiB |
|
@ -1,17 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>halo-admin</title>
|
||||
<link rel="icon" href="<%= BASE_URL %>logo.png">
|
||||
<title>Halo Dashboard</title>
|
||||
<style>#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but halo-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<div id="app">
|
||||
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>
|
||||
</div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}
|
|
@ -0,0 +1 @@
|
|||
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="preloading-animate">
|
||||
<div class="preloading-wrapper">
|
||||
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}
|
|
@ -0,0 +1 @@
|
|||
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 20 KiB |
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "halo-admin",
|
||||
"short_name": "halo-admin",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./img/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./img/icons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"background_color": "#000000",
|
||||
"theme_color": "#4DBA87"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
User-agent: *
|
||||
Disallow:
|
56
src/App.vue
|
@ -1,25 +1,45 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
<a-locale-provider :locale="locale">
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
</div>
|
||||
</a-locale-provider>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#app {
|
||||
font-family: "Avenir", Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
#nav {
|
||||
padding: 30px;
|
||||
a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
&.router-link-exact-active {
|
||||
color: #42b983;
|
||||
<script>
|
||||
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
|
||||
import { deviceEnquire, DEVICE_TYPE } from '@/utils/device'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
locale: zhCN
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const { $store } = this
|
||||
deviceEnquire(deviceType => {
|
||||
switch (deviceType) {
|
||||
case DEVICE_TYPE.DESKTOP:
|
||||
$store.commit('TOGGLE_DEVICE', 'desktop')
|
||||
$store.dispatch('setSidebar', true)
|
||||
break
|
||||
case DEVICE_TYPE.TABLET:
|
||||
$store.commit('TOGGLE_DEVICE', 'tablet')
|
||||
$store.dispatch('setSidebar', false)
|
||||
break
|
||||
case DEVICE_TYPE.MOBILE:
|
||||
default:
|
||||
$store.commit('TOGGLE_DEVICE', 'mobile')
|
||||
$store.dispatch('setSidebar', true)
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
const api = {
|
||||
Login: '/auth/login',
|
||||
Logout: '/auth/logout',
|
||||
ForgePassword: '/auth/forge-password',
|
||||
Register: '/auth/register',
|
||||
twoStepCode: '/auth/2step-code',
|
||||
SendSms: '/account/sms',
|
||||
SendSmsErr: '/account/sms_err',
|
||||
// get my info
|
||||
UserInfo: '/user/info'
|
||||
}
|
||||
export default api
|
|
@ -0,0 +1,61 @@
|
|||
import api from './index'
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
/**
|
||||
* login func
|
||||
* parameter: {
|
||||
* username: '',
|
||||
* password: '',
|
||||
* remember_me: true,
|
||||
* captcha: '12345'
|
||||
* }
|
||||
* @param parameter
|
||||
* @returns {*}
|
||||
*/
|
||||
export function login (parameter) {
|
||||
return axios({
|
||||
url: '/auth/login',
|
||||
method: 'post',
|
||||
data: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getSmsCaptcha (parameter) {
|
||||
return axios({
|
||||
url: api.SendSms,
|
||||
method: 'post',
|
||||
data: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo () {
|
||||
return axios({
|
||||
url: '/user/info',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function logout () {
|
||||
return axios({
|
||||
url: '/auth/logout',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* get user 2step code open?
|
||||
* @param parameter {*}
|
||||
*/
|
||||
export function get2step (parameter) {
|
||||
return axios({
|
||||
url: api.twoStepCode,
|
||||
method: 'post',
|
||||
data: parameter
|
||||
})
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { axios } from '@/utils/request'
|
||||
|
||||
const api = {
|
||||
user: '/user',
|
||||
role: '/role',
|
||||
service: '/service',
|
||||
permission: '/permission',
|
||||
permissionNoPager: '/permission/no-pager',
|
||||
orgTree: '/org/tree'
|
||||
}
|
||||
|
||||
export default api
|
||||
|
||||
export function getUserList (parameter) {
|
||||
return axios({
|
||||
url: api.user,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleList (parameter) {
|
||||
return axios({
|
||||
url: api.role,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getServiceList (parameter) {
|
||||
return axios({
|
||||
url: api.service,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getPermissions (parameter) {
|
||||
return axios({
|
||||
url: api.permissionNoPager,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function getOrgTree (parameter) {
|
||||
return axios({
|
||||
url: api.orgTree,
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
// id == 0 add post
|
||||
// id != 0 update put
|
||||
export function saveService (parameter) {
|
||||
return axios({
|
||||
url: api.service,
|
||||
method: parameter.id === 0 ? 'post' : 'put',
|
||||
data: parameter
|
||||
})
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 21</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
|
||||
<g id="Group-21" transform="translate(77.000000, 73.000000)">
|
||||
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
||||
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
|
||||
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
|
||||
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
|
||||
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
|
||||
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
|
||||
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
||||
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
|
||||
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
|
||||
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
|
||||
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
|
||||
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
|
||||
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
||||
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
||||
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
||||
</g>
|
||||
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
|
||||
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
|
||||
</g>
|
||||
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
||||
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
|
||||
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
||||
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
|
||||
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
|
||||
</g>
|
||||
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
|
||||
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
|
||||
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
||||
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
|
||||
</g>
|
||||
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
||||
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
|
||||
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
||||
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
||||
</g>
|
||||
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
||||
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
||||
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
|
||||
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
|
||||
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
||||
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
||||
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
||||
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
||||
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1551058675966" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7872" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M85.333333 512h85.333334a340.736 340.736 0 0 1 99.712-241.621333 337.493333 337.493333 0 0 1 108.458666-72.96 346.453333 346.453333 0 0 1 261.546667-1.749334A106.154667 106.154667 0 0 0 746.666667 298.666667C805.802667 298.666667 853.333333 251.136 853.333333 192S805.802667 85.333333 746.666667 85.333333c-29.397333 0-55.978667 11.776-75.221334 30.933334-103.722667-41.514667-222.848-40.874667-325.76 2.517333a423.594667 423.594667 0 0 0-135.68 91.264 423.253333 423.253333 0 0 0-91.306666 135.637333A426.88 426.88 0 0 0 85.333333 512z m741.248 133.205333c-17.109333 40.618667-41.685333 77.141333-72.96 108.416s-67.797333 55.850667-108.458666 72.96a346.453333 346.453333 0 0 1-261.546667 1.749334A106.154667 106.154667 0 0 0 277.333333 725.333333C218.197333 725.333333 170.666667 772.864 170.666667 832S218.197333 938.666667 277.333333 938.666667c29.397333 0 55.978667-11.776 75.221334-30.933334A425.173333 425.173333 0 0 0 512 938.666667a425.941333 425.941333 0 0 0 393.258667-260.352A426.325333 426.325333 0 0 0 938.666667 512h-85.333334a341.034667 341.034667 0 0 1-26.752 133.205333z" p-id="7873"></path><path d="M512 318.378667c-106.752 0-193.621333 86.869333-193.621333 193.621333S405.248 705.621333 512 705.621333s193.621333-86.869333 193.621333-193.621333S618.752 318.378667 512 318.378667z m0 301.909333c-59.690667 0-108.288-48.597333-108.288-108.288S452.309333 403.712 512 403.712s108.288 48.597333 108.288 108.288-48.597333 108.288-108.288 108.288z" p-id="7874"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.6 (67491) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Vue</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="69.644116%" y1="0%" x2="69.644116%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#29CDFF" offset="0%"></stop>
|
||||
<stop stop-color="#148EFF" offset="37.8600687%"></stop>
|
||||
<stop stop-color="#0A60FF" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-19.8191553%" y1="-36.7931464%" x2="138.57919%" y2="157.637507%" id="linearGradient-2">
|
||||
<stop stop-color="#29CDFF" offset="0%"></stop>
|
||||
<stop stop-color="#0F78FF" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-3">
|
||||
<stop stop-color="#FA8E7D" offset="0%"></stop>
|
||||
<stop stop-color="#F74A5C" offset="51.2635191%"></stop>
|
||||
<stop stop-color="#F51D2C" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Vue" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group" transform="translate(19.000000, 9.000000)">
|
||||
<path d="M89.96,90.48 C78.58,93.48 68.33,83.36 67.62,82.48 L46.6604487,62.2292258 C45.5023849,61.1103236 44.8426845,59.5728835 44.8296987,57.9626396 L44.5035564,17.5209948 C44.4948861,16.4458744 44.0537714,15.4195095 43.2796864,14.6733517 L29.6459999,1.53153737 C28.055475,-0.00160504005 25.5232423,0.0449126588 23.9900999,1.63543756 C23.2715121,2.38092066 22.87,3.37600834 22.87,4.41143746 L22.87,64.3864751 C22.87,67.0807891 23.9572233,69.6611067 25.885409,71.5429748 L63.6004615,108.352061 C65.9466323,110.641873 69.6963584,110.624605 72.0213403,108.313281" id="Path-Copy" fill="url(#linearGradient-1)" fill-rule="nonzero" transform="translate(56.415000, 54.831157) scale(-1, 1) translate(-56.415000, -54.831157) "></path>
|
||||
<path d="M68,90.1163122 C56.62,93.1163122 45.46,83.36 44.75,82.48 L23.7904487,62.2292258 C22.6323849,61.1103236 21.9726845,59.5728835 21.9596987,57.9626396 L21.6335564,17.5209948 C21.6248861,16.4458744 21.1837714,15.4195095 20.4096864,14.6733517 L6.7759999,1.53153737 C5.185475,-0.00160504005 2.65324232,0.0449126588 1.12009991,1.63543756 C0.401512125,2.38092066 3.90211878e-13,3.37600834 3.90798505e-13,4.41143746 L3.94351218e-13,64.3864751 C3.94681177e-13,67.0807891 1.08722326,69.6611067 3.01540903,71.5429748 L40.7807092,108.401101 C43.1069304,110.671444 46.8180151,110.676525 49.1504445,108.412561" id="Path" fill="url(#linearGradient-2)" fill-rule="nonzero"></path>
|
||||
<path d="M43.2983488,19.0991931 L27.5566079,3.88246244 C26.7624281,3.11476967 26.7409561,1.84862177 27.5086488,1.05444194 C27.8854826,0.664606611 28.4044438,0.444472651 28.9466386,0.444472651 L60.3925021,0.444472651 C61.4970716,0.444472651 62.3925021,1.33990315 62.3925021,2.44447265 C62.3925021,2.9858375 62.1730396,3.50407742 61.7842512,3.88079942 L46.0801285,19.0975301 C45.3051579,19.8484488 44.0742167,19.8491847 43.2983488,19.0991931 Z" id="Path" fill="url(#linearGradient-3)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<tooltip v-if="tips !== ''">
|
||||
<template slot="title">{{ tips }}</template>
|
||||
<avatar :size="avatarSize" :src="src" />
|
||||
</tooltip>
|
||||
<avatar v-else :size="avatarSize" :src="src" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from 'ant-design-vue/es/avatar'
|
||||
import Tooltip from 'ant-design-vue/es/tooltip'
|
||||
|
||||
export default {
|
||||
name: 'AvatarItem',
|
||||
components: {
|
||||
Avatar,
|
||||
Tooltip
|
||||
},
|
||||
props: {
|
||||
tips: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
size: this.$parent.size
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
avatarSize () {
|
||||
return this.size !== 'mini' && this.size || 20
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$parent.size' (val) {
|
||||
this.size = val
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,99 @@
|
|||
<!--
|
||||
<template>
|
||||
<div :class="[prefixCls]">
|
||||
<ul>
|
||||
<slot></slot>
|
||||
<template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template>
|
||||
|
||||
<template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength">
|
||||
<avatar-item :size="size">
|
||||
<avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar>
|
||||
</avatar-item>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
-->
|
||||
|
||||
<script>
|
||||
import Avatar from 'ant-design-vue/es/avatar'
|
||||
import AvatarItem from './Item'
|
||||
import { filterEmpty } from '@/components/_util/util'
|
||||
|
||||
export default {
|
||||
AvatarItem,
|
||||
name: 'AvatarList',
|
||||
components: {
|
||||
Avatar,
|
||||
AvatarItem
|
||||
},
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-avatar-list'
|
||||
},
|
||||
/**
|
||||
* 头像大小 类型: large、small 、mini, default
|
||||
* 默认值: default
|
||||
*/
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 'default'
|
||||
},
|
||||
/**
|
||||
* 要显示的最大项目
|
||||
*/
|
||||
maxLength: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 多余的项目风格
|
||||
*/
|
||||
excessItemsStyle: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
color: '#f56a00',
|
||||
backgroundColor: '#fde3cf'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
getItems (items) {
|
||||
const classString = {
|
||||
[`${this.prefixCls}-item`]: true,
|
||||
[`${this.size}`]: true
|
||||
}
|
||||
|
||||
if (this.maxLength > 0) {
|
||||
items = items.slice(0, this.maxLength)
|
||||
items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>))
|
||||
}
|
||||
const itemList = items.map((item) => (
|
||||
<li class={ classString }>{ item }</li>
|
||||
))
|
||||
return itemList
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { prefixCls, size } = this.$props
|
||||
const classString = {
|
||||
[`${prefixCls}`]: true,
|
||||
[`${size}`]: true
|
||||
}
|
||||
const items = filterEmpty(this.$slots.default)
|
||||
const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null
|
||||
|
||||
return (
|
||||
<div class={ classString }>
|
||||
{ itemsDom }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
import AvatarList from './List'
|
||||
import './index.less'
|
||||
|
||||
export default AvatarList
|
|
@ -0,0 +1,60 @@
|
|||
@import "../index";
|
||||
|
||||
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
|
||||
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
|
||||
|
||||
.@{avatar-list-prefix-cls} {
|
||||
display: inline-block;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0 0 0 8px;
|
||||
font-size: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.@{avatar-list-item-prefix-cls} {
|
||||
display: inline-block;
|
||||
font-size: @font-size-base;
|
||||
margin-left: -8px;
|
||||
width: @avatar-size-base;
|
||||
height: @avatar-size-base;
|
||||
|
||||
:global {
|
||||
.ant-avatar {
|
||||
border: 1px solid #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
width: @avatar-size-lg;
|
||||
height: @avatar-size-lg;
|
||||
}
|
||||
|
||||
&.small {
|
||||
width: @avatar-size-sm;
|
||||
height: @avatar-size-sm;
|
||||
}
|
||||
|
||||
&.mini {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
||||
:global {
|
||||
.ant-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
|
||||
.ant-avatar-string {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# AvatarList 用户头像列表
|
||||
|
||||
|
||||
一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import AvatarList from '@/components/AvatarList'
|
||||
const AvatarListItem = AvatarList.AvatarItem
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AvatarList,
|
||||
AvatarListItem
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<avatar-list size="mini">
|
||||
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
|
||||
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
</avatar-list>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<avatar-list :max-length="3">
|
||||
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
|
||||
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
|
||||
</avatar-list>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### AvatarList
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ---------------- | -------- | ---------------------------------- | --------- |
|
||||
| size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` |
|
||||
| maxLength | 要显示的最大项目 | number | - |
|
||||
| excessItemsStyle | 多余的项目风格 | CSSProperties | - |
|
||||
|
||||
### AvatarList.Item
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ---- | ------ | --------- | --- |
|
||||
| tips | 头像展示文案 | string | - |
|
||||
| src | 头像图片连接 | string | - |
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<span>
|
||||
{{ lastTime | format }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
function fixedZero (val) {
|
||||
return val * 1 < 10 ? `0${val}` : val
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'CountDown',
|
||||
props: {
|
||||
format: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
target: {
|
||||
type: [Date, Number],
|
||||
required: true
|
||||
},
|
||||
onEnd: {
|
||||
type: Function,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
dateTime: '0',
|
||||
originTargetTime: 0,
|
||||
lastTime: 0,
|
||||
timer: 0,
|
||||
interval: 1000
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
format (time) {
|
||||
const hours = 60 * 60 * 1000
|
||||
const minutes = 60 * 1000
|
||||
|
||||
const h = Math.floor(time / hours)
|
||||
const m = Math.floor((time - h * hours) / minutes)
|
||||
const s = Math.floor((time - h * hours - m * minutes) / 1000)
|
||||
return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.initTime()
|
||||
this.tick()
|
||||
},
|
||||
methods: {
|
||||
initTime () {
|
||||
let lastTime = 0
|
||||
let targetTime = 0
|
||||
this.originTargetTime = this.target
|
||||
try {
|
||||
if (Object.prototype.toString.call(this.target) === '[object Date]') {
|
||||
targetTime = this.target
|
||||
} else {
|
||||
targetTime = new Date(this.target).getTime()
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error('invalid target prop')
|
||||
}
|
||||
|
||||
lastTime = targetTime - new Date().getTime()
|
||||
|
||||
this.lastTime = lastTime < 0 ? 0 : lastTime
|
||||
},
|
||||
tick () {
|
||||
const { onEnd } = this
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
if (this.lastTime < this.interval) {
|
||||
clearTimeout(this.timer)
|
||||
this.lastTime = 0
|
||||
if (typeof onEnd === 'function') {
|
||||
onEnd()
|
||||
}
|
||||
} else {
|
||||
this.lastTime -= this.interval
|
||||
this.tick()
|
||||
}
|
||||
}, this.interval)
|
||||
}
|
||||
},
|
||||
beforeUpdate () {
|
||||
if (this.originTargetTime !== this.target) {
|
||||
this.initTime()
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
import CountDown from './CountDown'
|
||||
|
||||
export default CountDown
|
|
@ -0,0 +1,34 @@
|
|||
# CountDown 倒计时
|
||||
|
||||
倒计时组件。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import CountDown from '@/components/CountDown/CountDown'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CountDown
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<count-down :target="new Date().getTime() + 3000000" :on-end="onEndHandle" />
|
||||
```
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| target | 目标时间 | Date | - |
|
||||
| onEnd | 倒计时结束回调 | funtion | -|
|
|
@ -0,0 +1,64 @@
|
|||
<script>
|
||||
import Tooltip from 'ant-design-vue/es/tooltip'
|
||||
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/util'
|
||||
/*
|
||||
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
|
||||
|
||||
const TooltipOverlayStyle = {
|
||||
overflowWrap: 'break-word',
|
||||
wordWrap: 'break-word',
|
||||
};
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'Ellipsis',
|
||||
components: {
|
||||
Tooltip
|
||||
},
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-ellipsis'
|
||||
},
|
||||
tooltip: {
|
||||
type: Boolean
|
||||
},
|
||||
length: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
lines: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
fullWidthRecognition: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getStrDom (str, fullLength) {
|
||||
return (
|
||||
<span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span>
|
||||
)
|
||||
},
|
||||
getTooltip (fullStr, fullLength) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<template slot="title">{ fullStr }</template>
|
||||
{ this.getStrDom(fullStr, fullLength) }
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { tooltip, length } = this.$props
|
||||
const str = this.$slots.default.map(vNode => vNode.text).join('')
|
||||
const fullLength = getStrFullLength(str)
|
||||
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
|
||||
return (
|
||||
strDom
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,3 @@
|
|||
import Ellipsis from './Ellipsis'
|
||||
|
||||
export default Ellipsis
|
|
@ -0,0 +1,38 @@
|
|||
# Ellipsis 文本自动省略号
|
||||
|
||||
文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import Ellipsis from '@/components/Ellipsis'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Ellipsis
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<ellipsis :length="100" tooltip>
|
||||
There were injuries alleged in three cases in 2015, and a
|
||||
fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
|
||||
</ellipsis>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
tooltip | 移动到文本展示完整内容的提示 | boolean | -
|
||||
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
|
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<div :class="prefixCls">
|
||||
<div style="float: left">
|
||||
<slot name="extra">{{ extra }}</slot>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FooterToolBar',
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-footer-toolbar'
|
||||
},
|
||||
extra: {
|
||||
type: [String, Object],
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,4 @@
|
|||
import FooterToolBar from './FooterToolBar'
|
||||
import './index.less'
|
||||
|
||||
export default FooterToolBar
|
|
@ -0,0 +1,23 @@
|
|||
@import "../index";
|
||||
|
||||
@footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar";
|
||||
|
||||
.@{footer-toolbar-prefix-cls} {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
|
||||
background: #fff;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding: 0 24px;
|
||||
z-index: 9;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
# FooterToolbar 底部工具栏
|
||||
|
||||
固定在底部的工具栏。
|
||||
|
||||
|
||||
|
||||
## 何时使用
|
||||
|
||||
固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import FooterToolBar from '@/components/FooterToolbar'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FooterToolBar
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示
|
||||
|
||||
```html
|
||||
<footer-tool-bar>
|
||||
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
||||
</footer-tool-bar>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<footer-tool-bar extra="扩展信息提示">
|
||||
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
||||
</footer-tool-bar>
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
children (slot) | 工具栏内容,向右对齐 | - | -
|
||||
extra | 额外信息,向左对齐 | String, Object | -
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<a-button type="danger">Danger</a-button>
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br />
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-cli documentation</a
|
||||
>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>babel</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>pwa</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>eslint</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
|
||||
>Forum</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
|
||||
>Community Chat</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
|
||||
>Twitter</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-router</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-devtools#vue-devtools"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>vue-devtools</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-loader</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/awesome-vue"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>awesome-vue</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "HelloWorld",
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-tabs>
|
||||
<a-tab-pane v-for="(v, i) in icons" :tab="v.title" :key="i">
|
||||
<ul>
|
||||
<li v-for="(icon, key) in v.icons" :key="`${v.title}-${key}`" :class="{ 'active': selectedIcon==icon }">
|
||||
<a-icon :type="icon" :style="{ fontSize: '36px' }" @click="handleSelectedIcon(icon)" />
|
||||
</li>
|
||||
</ul>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from './icons'
|
||||
|
||||
export default {
|
||||
name: 'IconSelect',
|
||||
data () {
|
||||
return {
|
||||
selectedIcon: '',
|
||||
icons
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectedIcon (icon) {
|
||||
this.selectedIcon = icon
|
||||
this.$emit('change', icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
ul{
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
overflow-y: scroll;
|
||||
height: 250px;
|
||||
li{
|
||||
display: inline-block;
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.active{
|
||||
box-shadow: 0px 0px 5px 2px #888888;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,48 @@
|
|||
IconSelector
|
||||
====
|
||||
|
||||
> 图标选择组件,常用于为某一个数据设定一个图标时使用
|
||||
> eg: 设定菜单列表时,为每个菜单设定一个图标
|
||||
|
||||
该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装
|
||||
|
||||
|
||||
|
||||
### 使用方式
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<icon-selector @change="handleIconChange"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconSelector from '@/components/IconSelector'
|
||||
|
||||
export default {
|
||||
name: 'YourView',
|
||||
components: {
|
||||
IconSelector
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleIconChange (icon) {
|
||||
console.log('change Icon', icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 事件
|
||||
|
||||
|
||||
| 名称 | 说明 | 类型 | 默认值 |
|
||||
| ------ | -------------------------- | ------ | ------ |
|
||||
| change | 当改变了 `icon` 选中项触发 | String | - |
|
|
@ -0,0 +1,26 @@
|
|||
export default [
|
||||
{
|
||||
title: '方向性图标',
|
||||
icons: ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrows-alt', 'down', 'up', 'left', 'right', 'caret-up', 'caret-down', 'caret-left', 'caret-right', 'up-circle', 'down-circle', 'left-circle', 'right-circle', 'double-right', 'double-left', 'vertical-left', 'vertical-right', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap', 'swap-left', 'swap-right', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'play-circle', 'up-square', 'down-square', 'left-square', 'right-square', 'login', 'logout', 'menu-fold', 'menu-unfold', 'border-bottom', 'border-horizontal', 'border-inner', 'border-left', 'border-right', 'border-top', 'border-verticle', 'pic-center', 'pic-left', 'pic-right', 'radius-bottomleft', 'radius-bottomright', 'radius-upleft', 'fullscreen', 'fullscreen-exit']
|
||||
},
|
||||
{
|
||||
title: '提示建议性图标',
|
||||
icons: ['question', 'question-circle', 'plus', 'plus-circle', 'pause', 'pause-circle', 'minus', 'minus-circle', 'plus-square', 'minus-square', 'info', 'info-circle', 'exclamation', 'exclamation-circle', 'close', 'close-circle', 'close-square', 'check', 'check-circle', 'check-square', 'clock-circle', 'warning', 'issues-close', 'stop']
|
||||
},
|
||||
{
|
||||
title: '编辑类图标',
|
||||
icons: ['edit', 'form', 'copy', 'scissor', 'delete', 'snippets', 'diff', 'highlight', 'align-center', 'align-left', 'align-right', 'bg-colors', 'bold', 'italic', 'underline', 'strikethrough', 'redo', 'undo', 'zoom-in', 'zoom-out', 'font-colors', 'font-size', 'line-height', 'colum-height', 'dash', 'small-dash', 'sort-ascending', 'sort-descending', 'drag', 'ordered-list', 'radius-setting']
|
||||
},
|
||||
{
|
||||
title: '数据类图标',
|
||||
icons: ['area-chart', 'pie-chart', 'bar-chart', 'dot-chart', 'line-chart', 'radar-chart', 'heat-map', 'fall', 'rise', 'stock', 'box-plot', 'fund', 'sliders']
|
||||
},
|
||||
{
|
||||
title: '网站通用图标',
|
||||
icons: ['lock', 'unlock', 'bars', 'book', 'calendar', 'cloud', 'cloud-download', 'code', 'copy', 'credit-card', 'delete', 'desktop', 'download', 'ellipsis', 'file', 'file-text', 'file-unknown', 'file-pdf', 'file-word', 'file-excel', 'file-jpg', 'file-ppt', 'file-markdown', 'file-add', 'folder', 'folder-open', 'folder-add', 'hdd', 'frown', 'meh', 'smile', 'inbox', 'laptop', 'appstore', 'link', 'mail', 'mobile', 'notification', 'paper-clip', 'picture', 'poweroff', 'reload', 'search', 'setting', 'share-alt', 'shopping-cart', 'tablet', 'tag', 'tags', 'to-top', 'upload', 'user', 'video-camera', 'home', 'loading', 'loading-3-quarters', 'cloud-upload', 'star', 'heart', 'environment', 'eye', 'camera', 'save', 'team', 'solution', 'phone', 'filter', 'exception', 'export', 'customer-service', 'qrcode', 'scan', 'like', 'dislike', 'message', 'pay-circle', 'calculator', 'pushpin', 'bulb', 'select', 'switcher', 'rocket', 'bell', 'disconnect', 'database', 'compass', 'barcode', 'hourglass', 'key', 'flag', 'layout', 'printer', 'sound', 'usb', 'skin', 'tool', 'sync', 'wifi', 'car', 'schedule', 'user-add', 'user-delete', 'usergroup-add', 'usergroup-delete', 'man', 'woman', 'shop', 'gift', 'idcard', 'medicine-box', 'red-envelope', 'coffee', 'copyright', 'trademark', 'safety', 'wallet', 'bank', 'trophy', 'contacts', 'global', 'shake', 'api', 'fork', 'dashboard', 'table', 'profile', 'alert', 'audit', 'branches', 'build', 'border', 'crown', 'experiment', 'fire', 'money-collect', 'property-safety', 'read', 'reconciliation', 'rest', 'security-scan', 'insurance', 'interation', 'safety-certificate', 'project', 'thunderbolt', 'block', 'cluster', 'deployment-unit', 'dollar', 'euro', 'pound', 'file-done', 'file-exclamation', 'file-protect', 'file-search', 'file-sync', 'gateway', 'gold', 'robot', 'shopping']
|
||||
},
|
||||
{
|
||||
title: '品牌和标识',
|
||||
icons: ['android', 'apple', 'windows', 'ie', 'chrome', 'github', 'aliwangwang', 'dingding', 'weibo-square', 'weibo-circle', 'taobao-circle', 'html5', 'weibo', 'twitter', 'wechat', 'youtube', 'alipay-circle', 'taobao', 'skype', 'qq', 'medium-workmark', 'gitlab', 'medium', 'linkedin', 'google-plus', 'dropbox', 'facebook', 'codepen', 'code-sandbox', 'amazon', 'google', 'codepen-circle', 'alipay', 'ant-design', 'aliyun', 'zhihu', 'slack', 'slack-square', 'behance', 'behance-square', 'dribbble', 'dribbble-square', 'instagram', 'yuque', 'alibaba', 'yahoo']
|
||||
}
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
import IconSelector from './IconSelector'
|
||||
export default IconSelector
|
|
@ -0,0 +1,175 @@
|
|||
<!--
|
||||
<template>
|
||||
<div style="margin: -23px -24px 24px -24px">
|
||||
<!–<a-dropdown :trigger="['contextmenu']" overlayClassName="multi-tab-menu-wrapper">
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="1">1st menu item</a-menu-item>
|
||||
<a-menu-item key="2">2nd menu item</a-menu-item>
|
||||
<a-menu-item key="3">3rd menu item</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>–>
|
||||
<a-tabs
|
||||
hideAdd
|
||||
v-model="activeKey"
|
||||
type="editable-card"
|
||||
:tabBarStyle="{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }"
|
||||
@edit="onEdit"
|
||||
>
|
||||
<a-tab-pane v-for="page in pages" :style="{ height: 0 }" :tab="page.meta.title" :key="page.fullPath" :closable="pages.length > 1">
|
||||
</a-tab-pane>
|
||||
<template slot="renderTabBar" slot-scope="props, DefaultTabBar">
|
||||
<component :is="DefaultTabBar" {...props} />
|
||||
</template>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
-->
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MultiTab',
|
||||
data () {
|
||||
return {
|
||||
fullPathList: [],
|
||||
pages: [],
|
||||
activeKey: '',
|
||||
newTabIndex: 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.pages.push(this.$route)
|
||||
this.fullPathList.push(this.$route.fullPath)
|
||||
this.selectedLastPath()
|
||||
},
|
||||
methods: {
|
||||
onEdit (targetKey, action) {
|
||||
this[action](targetKey)
|
||||
},
|
||||
remove (targetKey) {
|
||||
this.pages = this.pages.filter(page => page.fullPath !== targetKey)
|
||||
this.fullPathList = this.fullPathList.filter(path => path !== targetKey)
|
||||
// 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页
|
||||
if (!this.fullPathList.includes(this.activeKey)) {
|
||||
this.selectedLastPath()
|
||||
}
|
||||
},
|
||||
selectedLastPath () {
|
||||
this.activeKey = this.fullPathList[this.fullPathList.length - 1]
|
||||
},
|
||||
|
||||
// content menu
|
||||
closeThat (e) {
|
||||
this.remove(e)
|
||||
},
|
||||
closeLeft (e) {
|
||||
const currentIndex = this.fullPathList.indexOf(e)
|
||||
if (currentIndex > 0) {
|
||||
this.fullPathList.forEach((item, index) => {
|
||||
if (index < currentIndex) {
|
||||
this.remove(item)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.info('左侧没有标签')
|
||||
}
|
||||
},
|
||||
closeRight (e) {
|
||||
const currentIndex = this.fullPathList.indexOf(e)
|
||||
if (currentIndex < (this.fullPathList.length - 1)) {
|
||||
this.fullPathList.forEach((item, index) => {
|
||||
if (index > currentIndex) {
|
||||
this.remove(item)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.info('右侧没有标签')
|
||||
}
|
||||
},
|
||||
closeAll (e) {
|
||||
const currentIndex = this.fullPathList.indexOf(e)
|
||||
this.fullPathList.forEach((item, index) => {
|
||||
if (index !== currentIndex) {
|
||||
this.remove(item)
|
||||
}
|
||||
})
|
||||
},
|
||||
closeMenuClick ({ key, item, domEvent }) {
|
||||
const vkey = domEvent.target.getAttribute('data-vkey')
|
||||
switch (key) {
|
||||
case 'close-right':
|
||||
this.closeRight(vkey)
|
||||
break
|
||||
case 'close-left':
|
||||
this.closeLeft(vkey)
|
||||
break
|
||||
case 'close-all':
|
||||
this.closeAll(vkey)
|
||||
break
|
||||
default:
|
||||
case 'close-that':
|
||||
this.closeThat(vkey)
|
||||
break
|
||||
}
|
||||
},
|
||||
renderTabPaneMenu (e) {
|
||||
return (
|
||||
<a-menu {...{ on: { click: this.closeMenuClick } }}>
|
||||
<a-menu-item key="close-that" data-vkey={e}>关闭当前标签</a-menu-item>
|
||||
<a-menu-item key="close-right" data-vkey={e}>关闭右侧</a-menu-item>
|
||||
<a-menu-item key="close-left" data-vkey={e}>关闭左侧</a-menu-item>
|
||||
<a-menu-item key="close-all" data-vkey={e}>关闭全部</a-menu-item>
|
||||
</a-menu>
|
||||
)
|
||||
},
|
||||
// render
|
||||
renderTabPane (title, keyPath) {
|
||||
const menu = this.renderTabPaneMenu(keyPath)
|
||||
|
||||
return (
|
||||
<a-dropdown overlay={menu} trigger={['contextmenu']}>
|
||||
<span style={{ userSelect: 'none' }}>{ title }</span>
|
||||
</a-dropdown>
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route': function (newVal) {
|
||||
this.activeKey = newVal.fullPath
|
||||
if (this.fullPathList.indexOf(newVal.fullPath) < 0) {
|
||||
this.fullPathList.push(newVal.fullPath)
|
||||
this.pages.push(newVal)
|
||||
}
|
||||
},
|
||||
activeKey: function (newPathKey) {
|
||||
this.$router.push({ path: newPathKey })
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { onEdit, $data: { pages } } = this
|
||||
const panes = pages.map(page => {
|
||||
return (
|
||||
<a-tab-pane
|
||||
style={{ height: 0 }}
|
||||
tab={this.renderTabPane(page.meta.title, page.fullPath)}
|
||||
key={page.fullPath} closable={pages.length > 1}
|
||||
>
|
||||
</a-tab-pane>)
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="ant-pro-multi-tab">
|
||||
<div class="ant-pro-multi-tab-wrapper">
|
||||
<a-tabs
|
||||
hideAdd
|
||||
type={'editable-card'}
|
||||
v-model={this.activeKey}
|
||||
tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }}
|
||||
{...{ on: { edit: onEdit } }}>
|
||||
{panes}
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
import MultiTab from './MultiTab'
|
||||
import './index.less'
|
||||
|
||||
export default MultiTab
|
|
@ -0,0 +1,20 @@
|
|||
@import '../index';
|
||||
|
||||
@multi-tab-prefix-cls: ~"@{ant-pro-prefix}-multi-tab";
|
||||
@multi-tab-wrapper-prefix-cls: ~"@{ant-pro-prefix}-multi-tab-wrapper";
|
||||
|
||||
/*
|
||||
.topmenu .@{multi-tab-prefix-cls} {
|
||||
max-width: 1200px;
|
||||
margin: -23px auto 24px auto;
|
||||
}
|
||||
*/
|
||||
.@{multi-tab-prefix-cls} {
|
||||
margin: -23px -24px 24px -24px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.topmenu .@{multi-tab-wrapper-prefix-cls} {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<div :class="[prefixCls]">
|
||||
<slot name="subtitle">
|
||||
<div :class="[`${prefixCls}-subtitle`]">{{ typeof subTitle === 'string' ? subTitle : subTitle() }}</div>
|
||||
</slot>
|
||||
<div class="number-info-value">
|
||||
<span>{{ total }}</span>
|
||||
<span class="sub-total">
|
||||
{{ subTotal }}
|
||||
<icon :type="`caret-${status}`" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Icon from 'ant-design-vue/es/icon'
|
||||
|
||||
export default {
|
||||
name: 'NumberInfo',
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-number-info'
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
subTotal: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
subTitle: {
|
||||
type: [String, Function],
|
||||
default: ''
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: 'up'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Icon
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "index";
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
import NumberInfo from './NumberInfo'
|
||||
|
||||
export default NumberInfo
|
|
@ -0,0 +1,55 @@
|
|||
@import "../index";
|
||||
|
||||
@numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info";
|
||||
|
||||
.@{numberInfo-prefix-cls} {
|
||||
|
||||
.ant-pro-number-info-subtitle {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.number-info-value {
|
||||
margin-top: 4px;
|
||||
font-size: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
|
||||
& > span {
|
||||
color: @heading-color;
|
||||
display: inline-block;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
font-size: 24px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
.sub-total {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-lg;
|
||||
vertical-align: top;
|
||||
margin-right: 0;
|
||||
i {
|
||||
font-size: 12px;
|
||||
transform: scale(0.82);
|
||||
margin-left: 4px;
|
||||
}
|
||||
:global {
|
||||
.anticon-caret-up {
|
||||
color: @red-6;
|
||||
}
|
||||
.anticon-caret-down {
|
||||
color: @green-6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
# NumberInfo 数据文本
|
||||
|
||||
常用在数据卡片中,用于突出展示某个业务数据。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import NumberInfo from '@/components/NumberInfo'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NumberInfo
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<number-info
|
||||
:sub-title="() => { return 'Visits this week' }"
|
||||
:total="12321"
|
||||
status="up"
|
||||
:sub-total="17.1"></number-info>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
title | 标题 | ReactNode\|string | -
|
||||
subTitle | 子标题 | ReactNode\|string | -
|
||||
total | 总量 | ReactNode\|string | -
|
||||
subTotal | 子总量 | ReactNode\|string | -
|
||||
status | 增加状态 | 'up \| down' | -
|
||||
theme | 状态样式 | string | 'light'
|
||||
gap | 设置数字和描述之间的间距(像素)| number | 8
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<div class="result">
|
||||
<div>
|
||||
<a-icon :class="{ 'icon': true, 'success': isSuccess, 'error': !isSuccess }" :type="isSuccess ? 'check-circle' : 'close-circle'"/>
|
||||
</div>
|
||||
<div class="title" v-if="title">{{ title }}</div>
|
||||
<div class="description" v-if="description">{{ description }}</div>
|
||||
<div class="content" v-if="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="action">
|
||||
<slot name="action"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Result',
|
||||
props: {
|
||||
isSuccess: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.result {
|
||||
text-align: center;
|
||||
width: 72%;
|
||||
margin: 0 auto;
|
||||
padding: 24px 0 8px;
|
||||
|
||||
.icon {
|
||||
font-size: 72px;
|
||||
line-height: 72px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.success {
|
||||
color: #52c41a;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
.title {
|
||||
font-size: 24px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-weight: 500;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.description {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.content {
|
||||
background: #fafafa;
|
||||
padding: 24px 40px;
|
||||
border-radius: 2px;
|
||||
text-align: left;
|
||||
}
|
||||
.action {
|
||||
margin-top: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.result {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: unset;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,2 @@
|
|||
import Result from './Result.vue'
|
||||
export default Result
|
|
@ -0,0 +1,124 @@
|
|||
import { Menu, Icon, Input } from 'ant-design-vue'
|
||||
|
||||
const { Item, ItemGroup, SubMenu } = Menu
|
||||
const { Search } = Input
|
||||
|
||||
export default {
|
||||
name: 'Tree',
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
openKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
search: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.localOpenKeys = this.openKeys.slice(0)
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
localOpenKeys: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePlus (item) {
|
||||
this.$emit('add', item)
|
||||
},
|
||||
handleTitleClick (...args) {
|
||||
this.$emit('titleClick', { args })
|
||||
},
|
||||
|
||||
renderSearch () {
|
||||
return (
|
||||
<Search
|
||||
placeholder="input search text"
|
||||
style="width: 100%; margin-bottom: 1rem"
|
||||
/>
|
||||
)
|
||||
},
|
||||
renderIcon (icon) {
|
||||
return icon && (<Icon type={icon} />) || null
|
||||
},
|
||||
renderMenuItem (item) {
|
||||
return (
|
||||
<Item key={item.key}>
|
||||
{ this.renderIcon(item.icon) }
|
||||
{ item.title }
|
||||
<a class="btn" style="width: 20px;z-index:1300" {...{ on: { click: () => this.handlePlus(item) } }}><a-icon type="plus"/></a>
|
||||
</Item>
|
||||
)
|
||||
},
|
||||
renderItem (item) {
|
||||
return item.children ? this.renderSubItem(item, item.key) : this.renderMenuItem(item, item.key)
|
||||
},
|
||||
renderItemGroup (item) {
|
||||
const childrenItems = item.children.map(o => {
|
||||
return this.renderItem(o, o.key)
|
||||
})
|
||||
|
||||
return (
|
||||
<ItemGroup key={item.key}>
|
||||
<template slot="title">
|
||||
<span>{ item.title }</span>
|
||||
<a-dropdown>
|
||||
<a class="btn"><a-icon type="ellipsis" /></a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="1">新增</a-menu-item>
|
||||
<a-menu-item key="2">合并</a-menu-item>
|
||||
<a-menu-item key="3">移除</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
{ childrenItems }
|
||||
</ItemGroup>
|
||||
)
|
||||
},
|
||||
renderSubItem (item, key) {
|
||||
const childrenItems = item.children && item.children.map(o => {
|
||||
return this.renderItem(o, o.key)
|
||||
})
|
||||
|
||||
const title = (
|
||||
<span slot="title">
|
||||
{ this.renderIcon(item.icon) }
|
||||
<span>{ item.title }</span>
|
||||
</span>
|
||||
)
|
||||
|
||||
if (item.group) {
|
||||
return this.renderItemGroup(item)
|
||||
}
|
||||
// titleClick={this.handleTitleClick(item)}
|
||||
return (
|
||||
<SubMenu key={key}>
|
||||
{ title }
|
||||
{ childrenItems }
|
||||
</SubMenu>
|
||||
)
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { dataSource, search } = this.$props
|
||||
|
||||
// this.localOpenKeys = openKeys.slice(0)
|
||||
const list = dataSource.map(item => {
|
||||
return this.renderItem(item)
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="tree-wrapper">
|
||||
{ search ? this.renderSearch() : null }
|
||||
<Menu mode="inline" class="custom-tree" {...{ on: { click: item => this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}>
|
||||
{ list }
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div :class="[prefixCls, reverseColor && 'reverse-color' ]">
|
||||
<span>
|
||||
<slot name="term"></slot>
|
||||
<span class="item-text">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</span>
|
||||
<span :class="[flag]"><a-icon :type="`caret-${flag}`"/></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Trend',
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'ant-pro-trend'
|
||||
},
|
||||
/**
|
||||
* 上升下降标识:up|down
|
||||
*/
|
||||
flag: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
/**
|
||||
* 颜色反转
|
||||
*/
|
||||
reverseColor: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "index";
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
import Trend from './Trend.vue'
|
||||
|
||||
export default Trend
|
|
@ -0,0 +1,42 @@
|
|||
@import "../index";
|
||||
|
||||
@trend-prefix-cls: ~"@{ant-pro-prefix}-trend";
|
||||
|
||||
.@{trend-prefix-cls} {
|
||||
display: inline-block;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
|
||||
.up,
|
||||
.down {
|
||||
margin-left: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
transform: scale(0.83);
|
||||
}
|
||||
}
|
||||
|
||||
.item-text {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
color: rgba(0,0,0,.85);
|
||||
}
|
||||
|
||||
.up {
|
||||
color: @red-6;
|
||||
}
|
||||
.down {
|
||||
color: @green-6;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
&.reverse-color .up {
|
||||
color: @green-6;
|
||||
}
|
||||
&.reverse-color .down {
|
||||
color: @red-6;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# Trend 趋势标记
|
||||
|
||||
趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。
|
||||
|
||||
|
||||
|
||||
引用方式:
|
||||
|
||||
```javascript
|
||||
import Trend from '@/components/Trend'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Trend
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代码演示 [demo](https://pro.loacg.com/test/home)
|
||||
|
||||
```html
|
||||
<trend flag="up">5%</trend>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<trend flag="up">
|
||||
<span slot="term">工资</span>
|
||||
5%
|
||||
</trend>
|
||||
```
|
||||
或
|
||||
```html
|
||||
<trend flag="up" term="工资">5%</trend>
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|------------------------------------------|-------------|-------|
|
||||
| flag | 上升下降标识:`up|down` | string | - |
|
||||
| reverseColor | 颜色反转 | Boolean | false |
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* components util
|
||||
*/
|
||||
|
||||
/**
|
||||
* 清理空值,对象
|
||||
* @param children
|
||||
* @returns {*[]}
|
||||
*/
|
||||
export function filterEmpty (children = []) {
|
||||
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串长度,英文字符 长度1,中文字符长度2
|
||||
* @param {*} str
|
||||
*/
|
||||
export const getStrFullLength = (str = '') =>
|
||||
str.split('').reduce((pre, cur) => {
|
||||
const charCode = cur.charCodeAt(0)
|
||||
if (charCode >= 0 && charCode <= 128) {
|
||||
return pre + 1
|
||||
}
|
||||
return pre + 2
|
||||
}, 0)
|
||||
|
||||
/**
|
||||
* 截取字符串,根据 maxLength 截取后返回
|
||||
* @param {*} str
|
||||
* @param {*} maxLength
|
||||
*/
|
||||
export const cutStrByFullLength = (str = '', maxLength) => {
|
||||
let showLength = 0
|
||||
return str.split('').reduce((pre, cur) => {
|
||||
const charCode = cur.charCodeAt(0)
|
||||
if (charCode >= 0 && charCode <= 128) {
|
||||
showLength += 1
|
||||
} else {
|
||||
showLength += 2
|
||||
}
|
||||
if (showLength <= maxLength) {
|
||||
return pre + cur
|
||||
}
|
||||
return pre
|
||||
}, '')
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
height="254"
|
||||
:data="data"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const data = []
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
data.push({
|
||||
x: `${i + 1}月`,
|
||||
y: Math.floor(Math.random() * 1000) + 200
|
||||
})
|
||||
}
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 22
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data,
|
||||
scale,
|
||||
tooltip
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
|
||||
<div class="chart-card-header">
|
||||
<div class="meta">
|
||||
<span class="chart-card-title">
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
</span>
|
||||
<span class="chart-card-action">
|
||||
<slot name="action"></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="total">
|
||||
<slot name="total">
|
||||
<span>{{ typeof total === 'function' && total() || total }}</span>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-card-content">
|
||||
<div class="content-fix">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-card-footer">
|
||||
<div class="field">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ChartCard',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
total: {
|
||||
type: [Function, Number, String],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-card-header {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
.meta {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-card-action {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.chart-card-footer {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding-top: 9px;
|
||||
margin-top: 8px;
|
||||
|
||||
> * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.field {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-card-content {
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
height: 46px;
|
||||
width: 100%;
|
||||
|
||||
.content-fix {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.total {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
color: #000;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
font-size: 30px;
|
||||
line-height: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-chart
|
||||
:forceFit="true"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:padding="0">
|
||||
<v-tooltip />
|
||||
<v-interval
|
||||
:shape="['liquid-fill-gauge']"
|
||||
position="transfer*value"
|
||||
color=""
|
||||
:v-style="{
|
||||
lineWidth: 10,
|
||||
opacity: 0.75
|
||||
}"
|
||||
:tooltip="[
|
||||
'transfer*value',
|
||||
(transfer, value) => {
|
||||
return {
|
||||
name: transfer,
|
||||
value,
|
||||
};
|
||||
},
|
||||
]"
|
||||
></v-interval>
|
||||
<v-guide
|
||||
v-for="(row, index) in data"
|
||||
:key="index"
|
||||
type="text"
|
||||
:top="true"
|
||||
:position="{
|
||||
gender: row.transfer,
|
||||
value: 45
|
||||
}"
|
||||
:content="row.value + '%'"
|
||||
:v-style="{
|
||||
fontSize: 100,
|
||||
textAlign: 'center',
|
||||
opacity: 0.75,
|
||||
}"
|
||||
/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Liquid',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,56 @@
|
|||
<template>
|
||||
<div class="antv-chart-mini">
|
||||
<div class="chart-wrapper" :style="{ height: 46 }">
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
|
||||
<v-tooltip />
|
||||
<v-smooth-area position="x*y" />
|
||||
</v-chart>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
const data = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
}
|
||||
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 22
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'MiniArea',
|
||||
data () {
|
||||
return {
|
||||
data,
|
||||
tooltip,
|
||||
scale,
|
||||
height: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div class="antv-chart-mini">
|
||||
<div class="chart-wrapper" :style="{ height: 46 }">
|
||||
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
|
||||
<v-tooltip />
|
||||
<v-bar position="x*y" />
|
||||
</v-chart>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
const data = []
|
||||
const beginDay = new Date().getTime()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
|
||||
y: Math.round(Math.random() * 10)
|
||||
})
|
||||
}
|
||||
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '时间',
|
||||
min: 1,
|
||||
max: 30
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'MiniBar',
|
||||
data () {
|
||||
return {
|
||||
data,
|
||||
tooltip,
|
||||
scale,
|
||||
height: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="chart-mini-progress">
|
||||
<div class="target" :style="{ left: target + '%'}">
|
||||
<span :style="{ backgroundColor: color }" />
|
||||
<span :style="{ backgroundColor: color }"/>
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MiniProgress',
|
||||
props: {
|
||||
target: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '10px'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#13C2C2'
|
||||
},
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-mini-progress {
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.target {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
span {
|
||||
border-radius: 100px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
width: 2px;
|
||||
|
||||
&:last-child {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progress-wrapper {
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
|
||||
.progress {
|
||||
transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
|
||||
border-radius: 1px 0 0 1px;
|
||||
background-color: #1890ff;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
|
||||
<v-tooltip></v-tooltip>
|
||||
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
|
||||
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
|
||||
<v-legend dataKey="user" marker="circle" :offset="30" />
|
||||
<v-coord type="polar" radius="0.8" />
|
||||
<v-line position="item*score" color="user" :size="2" />
|
||||
<v-point position="item*score" color="user" :size="4" shape="circle" />
|
||||
</v-chart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const axis1Opts = {
|
||||
dataKey: 'item',
|
||||
line: null,
|
||||
tickLine: null,
|
||||
grid: {
|
||||
lineStyle: {
|
||||
lineDash: null
|
||||
},
|
||||
hideFirstLine: false
|
||||
}
|
||||
}
|
||||
const axis2Opts = {
|
||||
dataKey: 'score',
|
||||
line: null,
|
||||
tickLine: null,
|
||||
grid: {
|
||||
type: 'polygon',
|
||||
lineStyle: {
|
||||
lineDash: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scale = [
|
||||
{
|
||||
dataKey: 'score',
|
||||
min: 0,
|
||||
max: 80
|
||||
}, {
|
||||
dataKey: 'user',
|
||||
alias: '类型'
|
||||
}
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'Radar',
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
axis1Opts,
|
||||
axis2Opts,
|
||||
scale
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="rank">
|
||||
<h4 class="title">{{ title }}</h4>
|
||||
<ul class="list">
|
||||
<li :key="index" v-for="(item, index) in list">
|
||||
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
|
||||
<span>{{ item.name }}</span>
|
||||
<span>{{ item.total }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RankList',
|
||||
// ['title', 'list']
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.rank {
|
||||
padding: 0 32px 32px 72px;
|
||||
|
||||
.list {
|
||||
margin: 25px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin-top: 16px;
|
||||
|
||||
span {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
&:first-child {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-right: 24px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
&.active {
|
||||
background-color: #314659;
|
||||
color: #fff;
|
||||
}
|
||||
&:last-child {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile .rank {
|
||||
padding: 0 32px 32px 32px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div :style="{ padding: '0 0 32px 32px' }">
|
||||
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
|
||||
<v-chart
|
||||
height="254"
|
||||
:data="data"
|
||||
:scale="scale"
|
||||
:forceFit="true"
|
||||
:padding="['auto', 'auto', '40', '50']">
|
||||
<v-tooltip />
|
||||
<v-axis />
|
||||
<v-bar position="x*y"/>
|
||||
</v-chart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const tooltip = [
|
||||
'x*y',
|
||||
(x, y) => ({
|
||||
name: x,
|
||||
value: y
|
||||
})
|
||||
]
|
||||
const scale = [{
|
||||
dataKey: 'x',
|
||||
title: '日期(天)',
|
||||
alias: '日期(天)',
|
||||
min: 2
|
||||
}, {
|
||||
dataKey: 'y',
|
||||
title: '流量(Gb)',
|
||||
alias: '流量(Gb)',
|
||||
min: 1
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'Bar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
data: [],
|
||||
scale,
|
||||
tooltip
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getMonthBar()
|
||||
},
|
||||
methods: {
|
||||
getMonthBar () {
|
||||
this.$http.get('/analysis/month-bar')
|
||||
.then(res => {
|
||||
this.data = res.result
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div class="chart-trend">
|
||||
{{ term }}
|
||||
<span>{{ rate }}%</span>
|
||||
<span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Trend',
|
||||
props: {
|
||||
term: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
type: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
target: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
fixed: {
|
||||
type: Number,
|
||||
default: 2
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
trend: this.type && 'up' || 'down',
|
||||
rate: this.percentage
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const type = this.type === null ? this.value >= this.target : this.type
|
||||
this.trend = type ? 'up' : 'down'
|
||||
this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-trend {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
||||
.trend-icon {
|
||||
font-size: 12px;
|
||||
|
||||
&.up, &.down {
|
||||
margin-left: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
transform: scale(.83);
|
||||
}
|
||||
}
|
||||
|
||||
&.up {
|
||||
color: #f5222d;
|
||||
}
|
||||
&.down {
|
||||
color: #52c41a;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
.antv-chart-mini {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.chart-wrapper {
|
||||
position: absolute;
|
||||
bottom: -28px;
|
||||
width: 100%;
|
||||
|
||||
/* margin: 0 -5px;
|
||||
overflow: hidden;*/
|
||||
}
|
||||
}
|
|
@ -0,0 +1,496 @@
|
|||
@import './index.less';
|
||||
|
||||
body {
|
||||
// 打开滚动条固定显示
|
||||
overflow-y: scroll;
|
||||
|
||||
&.colorWeak {
|
||||
filter: invert(80%);
|
||||
}
|
||||
}
|
||||
|
||||
.layout.ant-layout {
|
||||
height: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.mobile,&.tablet {
|
||||
|
||||
.ant-layout-content {
|
||||
|
||||
.content {
|
||||
margin: 24px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ant-table-wrapper
|
||||
* 覆盖的表格手机模式样式,如果想修改在手机上表格最低宽度,可以在这里改动
|
||||
*/
|
||||
.ant-table-wrapper {
|
||||
.ant-table-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ant-table-body {
|
||||
min-width: 800px;
|
||||
}
|
||||
}
|
||||
.topmenu {
|
||||
/* 必须为 topmenu 才能启用流式布局 */
|
||||
&.content-width-Fluid {
|
||||
.header-index-wide {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
.sidemenu {
|
||||
.ant-header-fixedHeader {
|
||||
|
||||
&.ant-header-side-opened, &.ant-header-side-closed {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-layout-has-sider {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
font-size: 20px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
cursor: pointer;
|
||||
transition: color .3s;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
}
|
||||
|
||||
.topmenu {
|
||||
.ant-header-fixedHeader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
transition: width .2s;
|
||||
|
||||
&.ant-header-side-opened {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.ant-header-side-closed {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
/* 必须为 topmenu 才能启用流式布局 */
|
||||
&.content-width-Fluid {
|
||||
.header-index-wide {
|
||||
max-width: unset;
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.page-header-index-wide {
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sidemenu {
|
||||
.ant-header-fixedHeader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
transition: width .2s;
|
||||
|
||||
&.ant-header-side-opened {
|
||||
width: calc(100% - 256px)
|
||||
}
|
||||
|
||||
&.ant-header-side-closed {
|
||||
width: calc(100% - 80px)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 64px;
|
||||
padding: 0 12px 0 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header, .top-nav-header-index {
|
||||
|
||||
.user-wrapper {
|
||||
float: right;
|
||||
height: 100%;
|
||||
|
||||
.action {
|
||||
cursor: pointer;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
transition: all .3s;
|
||||
height: 100%;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 20px 8px 20px 0;
|
||||
color: #1890ff;
|
||||
background: hsla(0, 0%, 100%, .85);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.user-wrapper {
|
||||
|
||||
.action {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
a {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mobile,&.tablet {
|
||||
.top-nav-header-index {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
|
||||
.trigger {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.logo.top-nav-header {
|
||||
flex: 0 0 56px;
|
||||
text-align: center;
|
||||
line-height: 58px;
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
.trigger {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tablet {
|
||||
// overflow: hidden; text-overflow:ellipsis; white-space: nowrap;
|
||||
.top-nav-header-index {
|
||||
|
||||
.header-index-wide {
|
||||
|
||||
.header-index-left {
|
||||
.logo > a {
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.ant-menu.ant-menu-horizontal {
|
||||
flex: 1 1;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.top-nav-header-index {
|
||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
||||
position: relative;
|
||||
transition: background .3s,width .2s;
|
||||
|
||||
.header-index-wide {
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
height: 64px;
|
||||
|
||||
.ant-menu.ant-menu-horizontal {
|
||||
border: none;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
}
|
||||
|
||||
.header-index-left {
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
|
||||
.logo.top-nav-header {
|
||||
width: 165px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
line-height: 64px;
|
||||
transition: all .3s;
|
||||
overflow: hidden;
|
||||
|
||||
img, svg {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
font-size: 16px;
|
||||
margin: 0 0 0 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-index-right {
|
||||
flex: 0 0 auto;
|
||||
height: 64px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
background-color: #fff;
|
||||
|
||||
.header-index-wide {
|
||||
.header-index-left {
|
||||
.logo {
|
||||
h1 {
|
||||
color: #002140;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 内容区
|
||||
.layout-content {
|
||||
margin: 24px 24px 0px;
|
||||
height: 100%;
|
||||
height: 64px;
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.topmenu {
|
||||
.page-header-index-wide {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// drawer-sider 自定义
|
||||
.ant-drawer.drawer-sider {
|
||||
.sider {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.ant-drawer-content {
|
||||
background-color: rgb(0, 21, 41);
|
||||
}
|
||||
}
|
||||
&.light {
|
||||
box-shadow: none;
|
||||
.ant-drawer-content {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-drawer-body {
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单样式
|
||||
.sider {
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
height: auto;
|
||||
|
||||
.ant-layout-sider-children {
|
||||
padding-top: 64px;
|
||||
overflow-y: hidden;
|
||||
&:hover{
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-fixed-sidemenu {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
padding-left: 24px;
|
||||
-webkit-transition: all .3s;
|
||||
transition: all .3s;
|
||||
background: #002140;
|
||||
overflow: hidden;
|
||||
z-index: 9;
|
||||
|
||||
img, svg, h1 {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
img, svg {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
margin: 0 0 0 12px;
|
||||
font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
|
||||
font-weight: 600;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
background-color: #fff;
|
||||
box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05);
|
||||
|
||||
.logo {
|
||||
background: #fff;
|
||||
box-shadow: 1px 1px 0px 0px #e8e8e8;
|
||||
|
||||
h1 {
|
||||
color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-menu-light {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 外置的样式控制
|
||||
.user-dropdown-menu {
|
||||
span {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
.user-dropdown-menu-wrapper.ant-dropdown-menu {
|
||||
padding: 4px 0;
|
||||
|
||||
.ant-dropdown-menu-item {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.ant-dropdown-menu-item > .anticon:first-child,
|
||||
.ant-dropdown-menu-item > a > .anticon:first-child,
|
||||
.ant-dropdown-menu-submenu-title > .anticon:first-child
|
||||
.ant-dropdown-menu-submenu-title > a > .anticon:first-child {
|
||||
min-width: 12px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 数据列表 样式
|
||||
.table-alert {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.table-page-search-wrapper {
|
||||
|
||||
.ant-form-inline {
|
||||
|
||||
.ant-form-item {
|
||||
display: flex;
|
||||
margin-bottom: 24px;
|
||||
margin-right: 0;
|
||||
|
||||
.ant-form-item-control-wrapper {
|
||||
flex: 1 1;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
>.ant-form-item-label {
|
||||
line-height: 32px;
|
||||
padding-right: 8px;
|
||||
width: auto;
|
||||
}
|
||||
.ant-form-item-control {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-page-search-submitButtons {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
.table-operator {
|
||||
margin-bottom: 18px;
|
||||
|
||||
button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<template>
|
||||
<div class="globalFooter">
|
||||
<div class="copyright">
|
||||
Proudly power by
|
||||
<a href="https://github.com/halo-dev/halo" target="_blank">Halo v1.0</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "GlobalFooter",
|
||||
data() {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.globalFooter {
|
||||
margin: 48px 0 24px 0;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
.copyright {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
|
||||
a {
|
||||
color: @text-color-secondary;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,48 @@
|
|||
// chart
|
||||
import Bar from '@/components/chart/Bar'
|
||||
import ChartCard from '@/components/chart/ChartCard'
|
||||
import Liquid from '@/components/chart/Liquid'
|
||||
import MiniArea from '@/components/chart/MiniArea'
|
||||
import MiniBar from '@/components/chart/MiniBar'
|
||||
import MiniProgress from '@/components/chart/MiniProgress'
|
||||
import Radar from '@/components/chart/Radar'
|
||||
import RankList from '@/components/chart/RankList'
|
||||
import TransferBar from '@/components/chart/TransferBar'
|
||||
|
||||
// pro components
|
||||
import AvatarList from '@/components/AvatarList'
|
||||
import CountDown from '@/components/CountDown'
|
||||
import Ellipsis from '@/components/Ellipsis'
|
||||
import FooterToolbar from '@/components/FooterToolbar'
|
||||
import NumberInfo from '@/components/NumberInfo'
|
||||
import DetailList from '@/components/tools/DetailList'
|
||||
import Tree from '@/components/Tree/Tree'
|
||||
import Trend from '@/components/Trend'
|
||||
import STable from '@/components/table'
|
||||
import MultiTab from '@/components/MultiTab'
|
||||
import Result from '@/components/Result'
|
||||
import IconSelector from '@/components/IconSelector'
|
||||
|
||||
export {
|
||||
AvatarList,
|
||||
Bar,
|
||||
ChartCard,
|
||||
Liquid,
|
||||
MiniArea,
|
||||
MiniBar,
|
||||
MiniProgress,
|
||||
Radar,
|
||||
RankList,
|
||||
TransferBar,
|
||||
Trend,
|
||||
CountDown,
|
||||
Ellipsis,
|
||||
FooterToolbar,
|
||||
NumberInfo,
|
||||
DetailList,
|
||||
Tree,
|
||||
STable,
|
||||
MultiTab,
|
||||
Result,
|
||||
IconSelector
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
@import "~ant-design-vue/lib/style/index";
|
||||
|
||||
// The prefix to use on all css classes from ant-pro.
|
||||
@ant-pro-prefix : ant-pro;
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<global-layout>
|
||||
<multi-tab v-if="$store.getters.multiTab"></multi-tab>
|
||||
<transition name="page-transition">
|
||||
<route-view />
|
||||
</transition>
|
||||
</global-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RouteView from '@/components/layouts/RouteView'
|
||||
import MultiTab from '@/components/MultiTab'
|
||||
import GlobalLayout from '@/components/page/GlobalLayout'
|
||||
|
||||
export default {
|
||||
name: 'BasicLayout',
|
||||
components: {
|
||||
RouteView,
|
||||
MultiTab,
|
||||
GlobalLayout
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import url('../global.less');
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="page-transition" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the page transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.page-transition-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-transition-enter .page-transition-container,
|
||||
.page-transition-leave-active .page-transition-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<div>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'BlankLayout'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<page-layout :desc="description" :title="getTitle" :link-list="linkList" :search="search" :tabs="tabs">
|
||||
<div slot="extra" class="extra-img">
|
||||
<img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
|
||||
</div>
|
||||
<!-- keep-alive -->
|
||||
<route-view ref="content"></route-view>
|
||||
</page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageLayout from '../page/PageLayout'
|
||||
import RouteView from './RouteView'
|
||||
|
||||
export default {
|
||||
name: 'PageContent',
|
||||
components: {
|
||||
RouteView,
|
||||
PageLayout
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: '',
|
||||
description: '',
|
||||
linkList: [],
|
||||
extraImage: '',
|
||||
search: false,
|
||||
tabs: {}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getPageHeaderInfo()
|
||||
},
|
||||
updated () {
|
||||
this.getPageHeaderInfo()
|
||||
},
|
||||
computed: {
|
||||
|
||||
getTitle () {
|
||||
return this.$route.meta.title
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
getPageHeaderInfo () {
|
||||
// eslint-disable-next-line
|
||||
this.title = this.$route.meta.title
|
||||
// 因为套用了一层 route-view 所以要取 ref 对象下的子节点的第一个对象
|
||||
const content = this.$refs.content && this.$refs.content.$children[0]
|
||||
|
||||
if (content) {
|
||||
this.description = content.description
|
||||
this.linkList = content.linkList
|
||||
this.extraImage = content.extraImage
|
||||
this.search = content.search === true
|
||||
this.tabs = content.tabs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.extra-img {
|
||||
margin-top: -60px;
|
||||
text-align: center;
|
||||
width: 195px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
.extra-img{
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
width: 96px;
|
||||
|
||||
img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,26 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'RouteView',
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
render () {
|
||||
const { $route: { meta }, $store: { getters } } = this
|
||||
const inKeep = (
|
||||
<keep-alive>
|
||||
<router-view />
|
||||
</keep-alive>
|
||||
)
|
||||
const notKeep = (
|
||||
<router-view />
|
||||
)
|
||||
// 这里增加了 multiTab 的判断,当开启了 multiTab 时
|
||||
// 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
|
||||
// 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
|
||||
if (meta.keepAlive === false) {
|
||||
return notKeep
|
||||
}
|
||||
return getters.multiTab || meta.keepAlive ? inKeep : notKeep
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<div id="userLayout" :class="['user-layout-wrapper', device]">
|
||||
<div class="container">
|
||||
<div class="top">
|
||||
<div class="header">
|
||||
<a href="/">
|
||||
<img src="~@/assets/logo.svg" class="logo" alt="logo">
|
||||
<span class="title">Ant Design</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="desc">
|
||||
Ant Design 是西湖区最具影响力的 Web 设计规范
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<route-view></route-view>
|
||||
|
||||
<div class="footer">
|
||||
<div class="links">
|
||||
<a href="_self">帮助</a>
|
||||
<a href="_self">隐私</a>
|
||||
<a href="_self">条款</a>
|
||||
</div>
|
||||
<div class="copyright">
|
||||
Copyright © 2018 白鹭学园技术组出品
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RouteView from '@/components/layouts/RouteView'
|
||||
import { mixinDevice } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'UserLayout',
|
||||
components: { RouteView },
|
||||
mixins: [mixinDevice],
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
mounted () {
|
||||
document.body.classList.add('userLayout')
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.body.classList.remove('userLayout')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
#userLayout.user-layout-wrapper {
|
||||
height: 100%;
|
||||
|
||||
&.mobile {
|
||||
.container {
|
||||
.main {
|
||||
max-width: 368px;
|
||||
width: 98%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
|
||||
background-size: 100%;
|
||||
padding: 110px 0 144px;
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top {
|
||||
text-align: center;
|
||||
|
||||
.header {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
margin-left: -12px;
|
||||
margin-top: -10px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 44px;
|
||||
vertical-align: top;
|
||||
margin-right: 16px;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 33px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-top: 12px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
min-width: 260px;
|
||||
width: 368px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
padding: 0 16px;
|
||||
margin: 48px 0 24px;
|
||||
text-align: center;
|
||||
|
||||
.links {
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
a {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
transition: all 0.3s;
|
||||
&:not(:last-child) {
|
||||
margin-right: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.copyright {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,7 @@
|
|||
import UserLayout from '@/components/layouts/UserLayout'
|
||||
import BlankLayout from '@/components/layouts/BlankLayout'
|
||||
import BasicLayout from '@/components/layouts/BasicLayout'
|
||||
import RouteView from '@/components/layouts/RouteView'
|
||||
import PageView from '@/components/layouts/PageView'
|
||||
|
||||
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<a-layout-sider
|
||||
:class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]"
|
||||
width="256px"
|
||||
:collapsible="collapsible"
|
||||
v-model="collapsed"
|
||||
:trigger="null">
|
||||
<logo />
|
||||
<s-menu
|
||||
:collapsed="collapsed"
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
:mode="mode"
|
||||
@select="onSelect"
|
||||
style="padding: 16px 0px;"></s-menu>
|
||||
</a-layout-sider>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ALayoutSider from 'ant-design-vue/es/layout/Sider'
|
||||
import Logo from '../tools/Logo'
|
||||
import SMenu from './index'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'SideMenu',
|
||||
components: { ALayoutSider, Logo, SMenu },
|
||||
mixins: [mixin, mixinDevice],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'inline'
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
collapsible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelect (obj) {
|
||||
this.$emit('menuSelect', obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,2 @@
|
|||
import SMenu from './menu'
|
||||
export default SMenu
|
|
@ -0,0 +1,171 @@
|
|||
import Menu from 'ant-design-vue/es/menu'
|
||||
import Icon from 'ant-design-vue/es/icon'
|
||||
|
||||
const { Item, SubMenu } = Menu
|
||||
|
||||
export default {
|
||||
name: 'SMenu',
|
||||
props: {
|
||||
menu: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'inline'
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
openKeys: [],
|
||||
selectedKeys: [],
|
||||
cachedOpenKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rootSubmenuKeys: vm => {
|
||||
const keys = []
|
||||
vm.menu.forEach(item => keys.push(item.path))
|
||||
return keys
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.updateMenu()
|
||||
},
|
||||
watch: {
|
||||
collapsed (val) {
|
||||
if (val) {
|
||||
this.cachedOpenKeys = this.openKeys.concat()
|
||||
this.openKeys = []
|
||||
} else {
|
||||
this.openKeys = this.cachedOpenKeys
|
||||
}
|
||||
},
|
||||
$route: function () {
|
||||
this.updateMenu()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// select menu item
|
||||
onOpenChange (openKeys) {
|
||||
// 在水平模式下时执行,并且不再执行后续
|
||||
if (this.mode === 'horizontal') {
|
||||
this.openKeys = openKeys
|
||||
return
|
||||
}
|
||||
// 非水平模式时
|
||||
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
|
||||
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
|
||||
this.openKeys = openKeys
|
||||
} else {
|
||||
this.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||
}
|
||||
},
|
||||
updateMenu () {
|
||||
const routes = this.$route.matched.concat()
|
||||
|
||||
if (routes.length >= 4 && this.$route.meta.hidden) {
|
||||
routes.pop()
|
||||
this.selectedKeys = [routes[2].path]
|
||||
} else {
|
||||
this.selectedKeys = [routes.pop().path]
|
||||
}
|
||||
|
||||
const openKeys = []
|
||||
if (this.mode === 'inline') {
|
||||
routes.forEach(item => {
|
||||
openKeys.push(item.path)
|
||||
})
|
||||
}
|
||||
|
||||
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||
},
|
||||
|
||||
// render
|
||||
renderItem (menu) {
|
||||
if (!menu.hidden) {
|
||||
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
|
||||
}
|
||||
return null
|
||||
},
|
||||
renderMenuItem (menu) {
|
||||
const target = menu.meta.target || null
|
||||
const props = {
|
||||
to: { name: menu.name },
|
||||
target: target
|
||||
}
|
||||
return (
|
||||
<Item {...{ key: menu.path }}>
|
||||
<router-link {...{ props }}>
|
||||
{this.renderIcon(menu.meta.icon)}
|
||||
<span>{menu.meta.title}</span>
|
||||
</router-link>
|
||||
</Item>
|
||||
)
|
||||
},
|
||||
renderSubMenu (menu) {
|
||||
const itemArr = []
|
||||
if (!menu.hideChildrenInMenu) {
|
||||
menu.children.forEach(item => itemArr.push(this.renderItem(item)))
|
||||
}
|
||||
return (
|
||||
<SubMenu {...{ key: menu.path }}>
|
||||
<span slot="title">
|
||||
{this.renderIcon(menu.meta.icon)}
|
||||
<span>{menu.meta.title}</span>
|
||||
</span>
|
||||
{itemArr}
|
||||
</SubMenu>
|
||||
)
|
||||
},
|
||||
renderIcon (icon) {
|
||||
if (icon === 'none' || icon === undefined) {
|
||||
return null
|
||||
}
|
||||
const props = {}
|
||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||
return (
|
||||
<Icon {... { props } }/>
|
||||
)
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { mode, theme, menu } = this
|
||||
const props = {
|
||||
mode: mode,
|
||||
theme: theme,
|
||||
openKeys: this.openKeys
|
||||
}
|
||||
const on = {
|
||||
select: obj => {
|
||||
this.selectedKeys = obj.selectedKeys
|
||||
this.$emit('select', obj)
|
||||
},
|
||||
openChange: this.onOpenChange
|
||||
}
|
||||
|
||||
const menuTree = menu.map(item => {
|
||||
if (item.hidden) {
|
||||
return null
|
||||
}
|
||||
return this.renderItem(item)
|
||||
})
|
||||
// {...{ props, on: on }}
|
||||
return (
|
||||
<Menu vModel={this.selectedKeys} {...{ props, on: on }}>
|
||||
{menuTree}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
import Menu from 'ant-design-vue/es/menu'
|
||||
import Icon from 'ant-design-vue/es/icon'
|
||||
|
||||
const { Item, SubMenu } = Menu
|
||||
|
||||
export default {
|
||||
name: 'SMenu',
|
||||
props: {
|
||||
menu: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'inline'
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
openKeys: [],
|
||||
selectedKeys: [],
|
||||
cachedOpenKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rootSubmenuKeys: vm => {
|
||||
const keys = []
|
||||
vm.menu.forEach(item => keys.push(item.path))
|
||||
return keys
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.updateMenu()
|
||||
},
|
||||
watch: {
|
||||
collapsed (val) {
|
||||
if (val) {
|
||||
this.cachedOpenKeys = this.openKeys.concat()
|
||||
this.openKeys = []
|
||||
} else {
|
||||
this.openKeys = this.cachedOpenKeys
|
||||
}
|
||||
},
|
||||
$route: function () {
|
||||
this.updateMenu()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
renderIcon: function (h, icon) {
|
||||
if (icon === 'none' || icon === undefined) {
|
||||
return null
|
||||
}
|
||||
const props = {}
|
||||
typeof (icon) === 'object' ? props.component = icon : props.type = icon
|
||||
return h(Icon, { props: { ...props } })
|
||||
},
|
||||
renderMenuItem: function (h, menu, pIndex, index) {
|
||||
const target = menu.meta.target || null
|
||||
return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index }, [
|
||||
h('router-link', { attrs: { to: { name: menu.name }, target: target } }, [
|
||||
this.renderIcon(h, menu.meta.icon),
|
||||
h('span', [menu.meta.title])
|
||||
])
|
||||
])
|
||||
},
|
||||
renderSubMenu: function (h, menu, pIndex, index) {
|
||||
const this2_ = this
|
||||
const subItem = [h('span', { slot: 'title' }, [this.renderIcon(h, menu.meta.icon), h('span', [menu.meta.title])])]
|
||||
const itemArr = []
|
||||
const pIndex_ = pIndex + '_' + index
|
||||
console.log('menu', menu)
|
||||
if (!menu.hideChildrenInMenu) {
|
||||
menu.children.forEach(function (item, i) {
|
||||
itemArr.push(this2_.renderItem(h, item, pIndex_, i))
|
||||
})
|
||||
}
|
||||
return h(SubMenu, { key: menu.path ? menu.path : 'submenu_' + pIndex + '_' + index }, subItem.concat(itemArr))
|
||||
},
|
||||
renderItem: function (h, menu, pIndex, index) {
|
||||
if (!menu.hidden) {
|
||||
return menu.children && !menu.hideChildrenInMenu
|
||||
? this.renderSubMenu(h, menu, pIndex, index)
|
||||
: this.renderMenuItem(h, menu, pIndex, index)
|
||||
}
|
||||
},
|
||||
renderMenu: function (h, menuTree) {
|
||||
const this2_ = this
|
||||
const menuArr = []
|
||||
menuTree.forEach(function (menu, i) {
|
||||
if (!menu.hidden) {
|
||||
menuArr.push(this2_.renderItem(h, menu, '0', i))
|
||||
}
|
||||
})
|
||||
return menuArr
|
||||
},
|
||||
onOpenChange (openKeys) {
|
||||
const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
|
||||
if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
|
||||
this.openKeys = openKeys
|
||||
} else {
|
||||
this.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||
}
|
||||
},
|
||||
updateMenu () {
|
||||
const routes = this.$route.matched.concat()
|
||||
|
||||
if (routes.length >= 4 && this.$route.meta.hidden) {
|
||||
routes.pop()
|
||||
this.selectedKeys = [routes[2].path]
|
||||
} else {
|
||||
this.selectedKeys = [routes.pop().path]
|
||||
}
|
||||
|
||||
const openKeys = []
|
||||
if (this.mode === 'inline') {
|
||||
routes.forEach(item => {
|
||||
openKeys.push(item.path)
|
||||
})
|
||||
}
|
||||
|
||||
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
return h(
|
||||
Menu,
|
||||
{
|
||||
props: {
|
||||
theme: this.$props.theme,
|
||||
mode: this.$props.mode,
|
||||
openKeys: this.openKeys,
|
||||
selectedKeys: this.selectedKeys
|
||||
},
|
||||
on: {
|
||||
openChange: this.onOpenChange,
|
||||
select: obj => {
|
||||
this.selectedKeys = obj.selectedKeys
|
||||
this.$emit('select', obj)
|
||||
}
|
||||
}
|
||||
},
|
||||
this.renderMenu(h, this.menu)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div class="footer">
|
||||
<div class="copyright">
|
||||
Proudly power by <a href="https://github.com/halo-dev/halo">Halo</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LayoutFooter',
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.footer {
|
||||
padding: 0 16px;
|
||||
margin: 48px 0 24px;
|
||||
text-align: center;
|
||||
.copyright {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,108 @@
|
|||
<template>
|
||||
<!-- , width: fixedHeader ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%' -->
|
||||
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" :style="{ padding: '0' }">
|
||||
<div v-if="mode === 'sidemenu'" class="header">
|
||||
<a-icon
|
||||
v-if="device==='mobile'"
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||
@click="toggle"></a-icon>
|
||||
<a-icon
|
||||
v-else
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
|
||||
@click="toggle"/>
|
||||
|
||||
<user-menu></user-menu>
|
||||
</div>
|
||||
<div v-else :class="['top-nav-header-index', theme]">
|
||||
<div class="header-index-wide">
|
||||
<div class="header-index-left">
|
||||
<logo class="top-nav-header" :show-title="device !== 'mobile'" />
|
||||
<s-menu
|
||||
v-if="device !== 'mobile'"
|
||||
mode="horizontal"
|
||||
:menu="menus"
|
||||
:theme="theme"
|
||||
></s-menu>
|
||||
<a-icon
|
||||
v-else
|
||||
class="trigger"
|
||||
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
|
||||
@click="toggle"></a-icon>
|
||||
</div>
|
||||
<user-menu class="header-index-right"></user-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-layout-header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UserMenu from '../tools/UserMenu'
|
||||
import SMenu from '../menu/'
|
||||
import Logo from '../tools/Logo'
|
||||
|
||||
import { mixin } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'GlobalHeader',
|
||||
components: {
|
||||
UserMenu,
|
||||
SMenu,
|
||||
Logo
|
||||
},
|
||||
mixins: [mixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
// sidemenu, topmenu
|
||||
default: 'sidemenu'
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'dark'
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
device: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'desktop'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
headerBarFixed: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
if (this.autoHideHeader) {
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
|
||||
if (scrollTop > 100) {
|
||||
this.headerBarFixed = true
|
||||
} else {
|
||||
this.headerBarFixed = false
|
||||
}
|
||||
} else {
|
||||
this.headerBarFixed = false
|
||||
}
|
||||
},
|
||||
toggle () {
|
||||
this.$emit('toggle')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<a-layout class="layout" :class="[device]">
|
||||
|
||||
<template v-if="isSideMenu()">
|
||||
<a-drawer
|
||||
v-if="isMobile()"
|
||||
:wrapClassName="'drawer-sider ' + navTheme"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
placement="left"
|
||||
@close="() => this.collapsed = false"
|
||||
>
|
||||
<side-menu
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="false"
|
||||
:collapsible="true"
|
||||
mode="inline"
|
||||
@menuSelect="menuSelect"></side-menu>
|
||||
</a-drawer>
|
||||
|
||||
<side-menu
|
||||
v-else
|
||||
mode="inline"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:collapsible="true"></side-menu>
|
||||
</template>
|
||||
<!-- 下次优化这些代码 -->
|
||||
<template v-else>
|
||||
<a-drawer
|
||||
v-if="isMobile()"
|
||||
:wrapClassName="'drawer-sider ' + navTheme"
|
||||
placement="left"
|
||||
@close="() => this.collapsed = false"
|
||||
:closable="false"
|
||||
:visible="collapsed"
|
||||
>
|
||||
<side-menu
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="false"
|
||||
:collapsible="true"
|
||||
mode="inline"
|
||||
@menuSelect="menuSelect"></side-menu>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
|
||||
<!-- layout header -->
|
||||
<global-header
|
||||
:mode="layoutMode"
|
||||
:menus="menus"
|
||||
:theme="navTheme"
|
||||
:collapsed="collapsed"
|
||||
:device="device"
|
||||
@toggle="toggle"
|
||||
/>
|
||||
|
||||
<!-- layout content -->
|
||||
<a-layout-content :style="{ margin: $store.getters.multiTab ? '24px 24px 0' : '24px 24px 0', height: '100%', paddingTop: fixedHeader ? '64px' : '0' }">
|
||||
<slot></slot>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- layout footer -->
|
||||
<a-layout-footer style="padding: 0">
|
||||
<global-footer />
|
||||
</a-layout-footer>
|
||||
<setting-drawer></setting-drawer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SideMenu from '@/components/menu/SideMenu'
|
||||
import GlobalHeader from '@/components/page/GlobalHeader'
|
||||
import GlobalFooter from '@/components/page/GlobalFooter'
|
||||
import SettingDrawer from '@/components/setting/SettingDrawer'
|
||||
import { asyncRouterMap } from '@/config/router.config.js'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
name: 'GlobalLayout',
|
||||
components: {
|
||||
SideMenu,
|
||||
GlobalHeader,
|
||||
GlobalFooter,
|
||||
SettingDrawer
|
||||
},
|
||||
mixins: [mixin, mixinDevice],
|
||||
data () {
|
||||
return {
|
||||
collapsed: false,
|
||||
menus: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
// 主路由
|
||||
mainMenu: state => state.permission.addRouters
|
||||
}),
|
||||
contentPaddingLeft () {
|
||||
if (!this.fixSidebar || this.isMobile()) {
|
||||
return '0'
|
||||
}
|
||||
if (this.sidebarOpened) {
|
||||
return '256px'
|
||||
}
|
||||
return '80px'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sidebarOpened (val) {
|
||||
this.collapsed = !val
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.menus = asyncRouterMap.find((item) => item.path === '/').children
|
||||
// this.menus = this.mainMenu.find((item) => item.path === '/').children
|
||||
this.collapsed = !this.sidebarOpened
|
||||
},
|
||||
mounted () {
|
||||
const userAgent = navigator.userAgent
|
||||
if (userAgent.indexOf('Edge') > -1) {
|
||||
this.$nextTick(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
setTimeout(() => {
|
||||
this.collapsed = !this.collapsed
|
||||
}, 16)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setSidebar']),
|
||||
toggle () {
|
||||
this.collapsed = !this.collapsed
|
||||
this.setSidebar(!this.collapsed)
|
||||
triggerWindowResizeEvent()
|
||||
},
|
||||
paddingCalc () {
|
||||
let left = ''
|
||||
if (this.sidebarOpened) {
|
||||
left = this.isDesktop() ? '256px' : '80px'
|
||||
} else {
|
||||
left = this.isMobile() && '0' || (this.fixSidebar && '80px' || '0')
|
||||
}
|
||||
return left
|
||||
},
|
||||
menuSelect () {
|
||||
if (!this.isDesktop()) {
|
||||
this.collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,241 @@
|
|||
<template>
|
||||
<div class="page-header">
|
||||
<div class="page-header-index-wide">
|
||||
<a-breadcrumb class="breadcrumb">
|
||||
<a-breadcrumb-item v-for="(item, index) in breadList" :key="index">
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
>{{ item.meta.title }}</router-link>
|
||||
<span v-else>{{ item.meta.title }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
|
||||
<div class="detail">
|
||||
<div class="main" v-if="!$route.meta.hiddenHeaderContent">
|
||||
<div class="row">
|
||||
<img v-if="logo" :src="logo" class="logo"/>
|
||||
<h1 v-if="title" class="title">{{ title }}</h1>
|
||||
<div class="action">
|
||||
<slot name="action"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div v-if="avatar" class="avatar">
|
||||
<a-avatar :src="avatar"/>
|
||||
</div>
|
||||
<div v-if="this.$slots.content" class="headerContent">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
<div v-if="this.$slots.extra" class="extra">
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="pageMenu"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Breadcrumb from '@/components/tools/Breadcrumb'
|
||||
|
||||
export default {
|
||||
name: 'PageHeader',
|
||||
components: {
|
||||
's-breadcrumb': Breadcrumb
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
breadcrumb: {
|
||||
type: Array,
|
||||
default: null,
|
||||
required: false
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
breadList: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb () {
|
||||
this.breadList = []
|
||||
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}})
|
||||
|
||||
this.name = this.$route.name
|
||||
this.$route.matched.forEach((item) => {
|
||||
// item.name !== 'index' && this.breadList.push(item)
|
||||
this.breadList.push(item)
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route () {
|
||||
this.getBreadcrumb()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.page-header {
|
||||
background: #fff;
|
||||
padding: 16px 32px 0;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: flex;
|
||||
/*margin-bottom: 16px;*/
|
||||
|
||||
.avatar {
|
||||
flex: 0 1 72px;
|
||||
margin: 0 24px 8px 0;
|
||||
|
||||
& > span {
|
||||
border-radius: 72px;
|
||||
display: block;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
flex: 0 1 auto;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.avatar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 500;
|
||||
color: rgba(0,0,0,.85);
|
||||
margin-bottom: 16px;
|
||||
flex: auto;
|
||||
|
||||
}
|
||||
.logo {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.content, .headerContent {
|
||||
flex: auto;
|
||||
color: rgba(0,0,0,.45);
|
||||
line-height: 22px;
|
||||
|
||||
.link {
|
||||
margin-top: 16px;
|
||||
line-height: 24px;
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.extra {
|
||||
flex: 0 1 auto;
|
||||
margin-left: 88px;
|
||||
min-width: 242px;
|
||||
text-align: right;
|
||||
}
|
||||
.action {
|
||||
margin-left: 56px;
|
||||
min-width: 266px;
|
||||
flex: 0 1 auto;
|
||||
text-align: right;
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile .page-header {
|
||||
|
||||
.main {
|
||||
|
||||
.row {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.avatar {
|
||||
flex: 0 1 25%;
|
||||
margin: 0 2% 8px 0;
|
||||
}
|
||||
|
||||
.content, .headerContent {
|
||||
flex: 0 1 70%;
|
||||
|
||||
.link {
|
||||
margin-top: 16px;
|
||||
line-height: 24px;
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.extra {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 0;
|
||||
min-width: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-left: unset;
|
||||
min-width: 266px;
|
||||
flex: 0 1 auto;
|
||||
text-align: left;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|