Commit 2e9681bc 2e9681bc0ccfff0c962b1ee59b5df4dbb0567f5e by 官美宏

初始化项目

0 parents
Showing 314 changed files with 21990 additions and 0 deletions
1 # https://editorconfig.org
2 root = true
3
4 [*]
5 charset = utf-8
6 indent_style = space
7 indent_size = 2
8 end_of_line = lf
9 insert_final_newline = true
10 trim_trailing_whitespace = true
11
12 [*.md]
13 insert_final_newline = false
14 trim_trailing_whitespace = false
1 ENV = 'development'
2
3 # 接口地址
4 # VUE_APP_BASE_API = 'http://10.100.6.140:18006'
5 # VUE_APP_BASE_API = 'http://139.196.4.234:18006'
6 VUE_APP_BASE_API = 'http://139.196.192.242:18000'
7 # VUE_APP_BASE_API = 'http://10.100.6.90:8000'
8 # VUE_APP_WS_API = 'ws://localhost:8000'
9
10 # 是否启用 babel-plugin-dynamic-import-node插件
11 VUE_CLI_BABEL_TRANSPILE_MODULES = false
1 ENV = 'production'
2
3 # 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置
4 # 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
5 VUE_APP_BASE_API = '/prod-api'
6 # 如果接口是 http 形式, wss 需要改为 ws
7 # VUE_APP_WS_API = 'ws://139.196.4.234:18006'
1 build/*.js
2 src/assets
3 public
4 dist
1 module.exports = {
2 root: true,
3 parserOptions: {
4 parser: 'babel-eslint',
5 sourceType: 'module'
6 },
7 env: {
8 browser: true,
9 node: true,
10 es6: true,
11 },
12 extends: ['plugin:vue/recommended', 'eslint:recommended'],
13
14 // add your custom rules here
15 //it is base on https://github.com/vuejs/eslint-config-vue
16 rules: {
17 "vue/max-attributes-per-line": [2, {
18 "singleline": 10,
19 "multiline": {
20 "max": 1,
21 "allowFirstLine": false
22 }
23 }],
24 "vue/singleline-html-element-content-newline": "off",
25 "vue/multiline-html-element-content-newline":"off",
26 "vue/name-property-casing": ["error", "PascalCase"],
27 "vue/no-v-html": "off",
28 'accessor-pairs': 2,
29 'arrow-spacing': [2, {
30 'before': true,
31 'after': true
32 }],
33 'block-spacing': [2, 'always'],
34 'brace-style': [2, '1tbs', {
35 'allowSingleLine': true
36 }],
37 'camelcase': [0, {
38 'properties': 'always'
39 }],
40 'comma-dangle': [2, 'never'],
41 'comma-spacing': [2, {
42 'before': false,
43 'after': true
44 }],
45 'comma-style': [2, 'last'],
46 'constructor-super': 2,
47 'curly': [2, 'multi-line'],
48 'dot-location': [2, 'property'],
49 'eol-last': 2,
50 'eqeqeq': ["error", "always", {"null": "ignore"}],
51 'generator-star-spacing': [2, {
52 'before': true,
53 'after': true
54 }],
55 'handle-callback-err': [2, '^(err|error)$'],
56 'indent': [2, 2, {
57 'SwitchCase': 1
58 }],
59 'jsx-quotes': [2, 'prefer-single'],
60 'key-spacing': [2, {
61 'beforeColon': false,
62 'afterColon': true
63 }],
64 'keyword-spacing': [2, {
65 'before': true,
66 'after': true
67 }],
68 'new-cap': [2, {
69 'newIsCap': true,
70 'capIsNew': false
71 }],
72 'new-parens': 2,
73 'no-array-constructor': 2,
74 'no-caller': 2,
75 'no-console': 'off',
76 'no-class-assign': 2,
77 'no-cond-assign': 2,
78 'no-const-assign': 2,
79 'no-control-regex': 0,
80 'no-delete-var': 2,
81 'no-dupe-args': 2,
82 'no-dupe-class-members': 2,
83 'no-dupe-keys': 2,
84 'no-duplicate-case': 2,
85 'no-empty-character-class': 2,
86 'no-empty-pattern': 2,
87 'no-eval': 2,
88 'no-ex-assign': 2,
89 'no-extend-native': 2,
90 'no-extra-bind': 2,
91 'no-extra-boolean-cast': 2,
92 'no-extra-parens': [2, 'functions'],
93 'no-fallthrough': 2,
94 'no-floating-decimal': 2,
95 'no-func-assign': 2,
96 'no-implied-eval': 2,
97 'no-inner-declarations': [2, 'functions'],
98 'no-invalid-regexp': 2,
99 'no-irregular-whitespace': 2,
100 'no-iterator': 2,
101 'no-label-var': 2,
102 'no-labels': [2, {
103 'allowLoop': false,
104 'allowSwitch': false
105 }],
106 'no-lone-blocks': 2,
107 'no-mixed-spaces-and-tabs': 2,
108 'no-multi-spaces': 2,
109 'no-multi-str': 2,
110 'no-multiple-empty-lines': [2, {
111 'max': 1
112 }],
113 'no-native-reassign': 2,
114 'no-negated-in-lhs': 2,
115 'no-new-object': 2,
116 'no-new-require': 2,
117 'no-new-symbol': 2,
118 'no-new-wrappers': 2,
119 'no-obj-calls': 2,
120 'no-octal': 2,
121 'no-octal-escape': 2,
122 'no-path-concat': 2,
123 'no-proto': 2,
124 'no-redeclare': 2,
125 'no-regex-spaces': 2,
126 'no-return-assign': [2, 'except-parens'],
127 'no-self-assign': 2,
128 'no-self-compare': 2,
129 'no-sequences': 2,
130 'no-shadow-restricted-names': 2,
131 'no-spaced-func': 2,
132 'no-sparse-arrays': 2,
133 'no-this-before-super': 2,
134 'no-throw-literal': 2,
135 'no-trailing-spaces': 2,
136 'no-undef': 2,
137 'no-undef-init': 2,
138 'no-unexpected-multiline': 2,
139 'no-unmodified-loop-condition': 2,
140 'no-unneeded-ternary': [2, {
141 'defaultAssignment': false
142 }],
143 'no-unreachable': 2,
144 'no-unsafe-finally': 2,
145 'no-unused-vars': [2, {
146 'vars': 'all',
147 'args': 'none'
148 }],
149 'no-useless-call': 2,
150 'no-useless-computed-key': 2,
151 'no-useless-constructor': 2,
152 'no-useless-escape': 0,
153 'no-whitespace-before-property': 2,
154 'no-with': 2,
155 'one-var': [2, {
156 'initialized': 'never'
157 }],
158 'operator-linebreak': [2, 'after', {
159 'overrides': {
160 '?': 'before',
161 ':': 'before'
162 }
163 }],
164 'padded-blocks': [2, 'never'],
165 'quotes': [2, 'single', {
166 'avoidEscape': true,
167 'allowTemplateLiterals': true
168 }],
169 'semi': [2, 'never'],
170 'semi-spacing': [2, {
171 'before': false,
172 'after': true
173 }],
174 'space-before-blocks': [2, 'always'],
175 'space-before-function-paren': [2, 'never'],
176 'space-in-parens': [2, 'never'],
177 'space-infix-ops': 2,
178 'space-unary-ops': [2, {
179 'words': true,
180 'nonwords': false
181 }],
182 'spaced-comment': [2, 'always', {
183 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
184 }],
185 'template-curly-spacing': [2, 'never'],
186 'use-isnan': 2,
187 'valid-typeof': 2,
188 'wrap-iife': [2, 'any'],
189 'yield-star-spacing': [2, 'both'],
190 'yoda': [2, 'never'],
191 'prefer-const': 2,
192 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
193 'object-curly-spacing': [2, 'always', {
194 objectsInObjects: false
195 }],
196 'array-bracket-spacing': [2, 'never']
197 }
198 }
1 .DS_Store
2 node_modules/
3 dist/
4 npm-debug.log*
5 yarn-debug.log*
6 yarn-error.log*
7 **/*.log
8
9 tests/**/coverage/
10 tests/e2e/reports
11 selenium-debug.log
12
13 # Editor directories and files
14 .idea
15 .vscode
16 *.suo
17 *.ntvs*
18 *.njsproj
19 *.sln
20 *.local
21
22 package-lock.json
23 yarn.lock
1 language: node_js
2 node_js: 10
3 script: npm run test
4 notifications:
5 email: false
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction, and
10 distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all other entities
16 that control, are controlled by, or are under common control with that entity.
17 For the purposes of this definition, "control" means (i) the power, direct or
18 indirect, to cause the direction or management of such entity, whether by
19 contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 outstanding shares, or (iii) beneficial ownership of such entity.
21
22 "You" (or "Your") shall mean an individual or Legal Entity exercising
23 permissions granted by this License.
24
25 "Source" form shall mean the preferred form for making modifications, including
26 but not limited to software source code, documentation source, and configuration
27 files.
28
29 "Object" form shall mean any form resulting from mechanical transformation or
30 translation of a Source form, including but not limited to compiled object code,
31 generated documentation, and conversions to other media types.
32
33 "Work" shall mean the work of authorship, whether in Source or Object form, made
34 available under the License, as indicated by a copyright notice that is included
35 in or attached to the work (an example is provided in the Appendix below).
36
37 "Derivative Works" shall mean any work, whether in Source or Object form, that
38 is based on (or derived from) the Work and for which the editorial revisions,
39 annotations, elaborations, or other modifications represent, as a whole, an
40 original work of authorship. For the purposes of this License, Derivative Works
41 shall not include works that remain separable from, or merely link (or bind by
42 name) to the interfaces of, the Work and Derivative Works thereof.
43
44 "Contribution" shall mean any work of authorship, including the original version
45 of the Work and any modifications or additions to that Work or Derivative Works
46 thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 by the copyright owner or by an individual or Legal Entity authorized to submit
48 on behalf of the copyright owner. For the purposes of this definition,
49 "submitted" means any form of electronic, verbal, or written communication sent
50 to the Licensor or its representatives, including but not limited to
51 communication on electronic mailing lists, source code control systems, and
52 issue tracking systems that are managed by, or on behalf of, the Licensor for
53 the purpose of discussing and improving the Work, but excluding communication
54 that is conspicuously marked or otherwise designated in writing by the copyright
55 owner as "Not a Contribution."
56
57 "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 of whom a Contribution has been received by Licensor and subsequently
59 incorporated within the Work.
60
61 2. Grant of Copyright License.
62
63 Subject to the terms and conditions of this License, each Contributor hereby
64 grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 irrevocable copyright license to reproduce, prepare Derivative Works of,
66 publicly display, publicly perform, sublicense, and distribute the Work and such
67 Derivative Works in Source or Object form.
68
69 3. Grant of Patent License.
70
71 Subject to the terms and conditions of this License, each Contributor hereby
72 grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 irrevocable (except as stated in this section) patent license to make, have
74 made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 such license applies only to those patent claims licensable by such Contributor
76 that are necessarily infringed by their Contribution(s) alone or by combination
77 of their Contribution(s) with the Work to which such Contribution(s) was
78 submitted. If You institute patent litigation against any entity (including a
79 cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 Contribution incorporated within the Work constitutes direct or contributory
81 patent infringement, then any patent licenses granted to You under this License
82 for that Work shall terminate as of the date such litigation is filed.
83
84 4. Redistribution.
85
86 You may reproduce and distribute copies of the Work or Derivative Works thereof
87 in any medium, with or without modifications, and in Source or Object form,
88 provided that You meet the following conditions:
89
90 You must give any other recipients of the Work or Derivative Works a copy of
91 this License; and
92 You must cause any modified files to carry prominent notices stating that You
93 changed the files; and
94 You must retain, in the Source form of any Derivative Works that You distribute,
95 all copyright, patent, trademark, and attribution notices from the Source form
96 of the Work, excluding those notices that do not pertain to any part of the
97 Derivative Works; and
98 If the Work includes a "NOTICE" text file as part of its distribution, then any
99 Derivative Works that You distribute must include a readable copy of the
100 attribution notices contained within such NOTICE file, excluding those notices
101 that do not pertain to any part of the Derivative Works, in at least one of the
102 following places: within a NOTICE text file distributed as part of the
103 Derivative Works; within the Source form or documentation, if provided along
104 with the Derivative Works; or, within a display generated by the Derivative
105 Works, if and wherever such third-party notices normally appear. The contents of
106 the NOTICE file are for informational purposes only and do not modify the
107 License. You may add Your own attribution notices within Derivative Works that
108 You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 provided that such additional attribution notices cannot be construed as
110 modifying the License.
111 You may add Your own copyright statement to Your modifications and may provide
112 additional or different license terms and conditions for use, reproduction, or
113 distribution of Your modifications, or for any such Derivative Works as a whole,
114 provided Your use, reproduction, and distribution of the Work otherwise complies
115 with the conditions stated in this License.
116
117 5. Submission of Contributions.
118
119 Unless You explicitly state otherwise, any Contribution intentionally submitted
120 for inclusion in the Work by You to the Licensor shall be under the terms and
121 conditions of this License, without any additional terms or conditions.
122 Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 any separate license agreement you may have executed with Licensor regarding
124 such Contributions.
125
126 6. Trademarks.
127
128 This License does not grant permission to use the trade names, trademarks,
129 service marks, or product names of the Licensor, except as required for
130 reasonable and customary use in describing the origin of the Work and
131 reproducing the content of the NOTICE file.
132
133 7. Disclaimer of Warranty.
134
135 Unless required by applicable law or agreed to in writing, Licensor provides the
136 Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 including, without limitation, any warranties or conditions of TITLE,
139 NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 solely responsible for determining the appropriateness of using or
141 redistributing the Work and assume any risks associated with Your exercise of
142 permissions under this License.
143
144 8. Limitation of Liability.
145
146 In no event and under no legal theory, whether in tort (including negligence),
147 contract, or otherwise, unless required by applicable law (such as deliberate
148 and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 liable to You for damages, including any direct, indirect, special, incidental,
150 or consequential damages of any character arising as a result of this License or
151 out of the use or inability to use the Work (including but not limited to
152 damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 any and all other commercial damages or losses), even if such Contributor has
154 been advised of the possibility of such damages.
155
156 9. Accepting Warranty or Additional Liability.
157
158 While redistributing the Work or Derivative Works thereof, You may choose to
159 offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 other liability obligations and/or rights consistent with this License. However,
161 in accepting such obligations, You may act only on Your own behalf and on Your
162 sole responsibility, not on behalf of any other Contributor, and only if You
163 agree to indemnify, defend, and hold each Contributor harmless for any liability
164 incurred by, or claims asserted against, such Contributor by reason of your
165 accepting any such warranty or additional liability.
166
167 END OF TERMS AND CONDITIONS
168
169 APPENDIX: How to apply the Apache License to your work
170
171 To apply the Apache License to your work, attach the following boilerplate
172 notice, with the fields enclosed by brackets "{}" replaced with your own
173 identifying information. (Don't include the brackets!) The text should be
174 enclosed in the appropriate comment syntax for the file format. We also
175 recommend that a file or class name and description of purpose be included on
176 the same "printed page" as the copyright notice for easier identification within
177 third-party archives.
178
179 Copyright 2019 Zheng Jie
180
181 Licensed under the Apache License, Version 2.0 (the "License");
182 you may not use this file except in compliance with the License.
183 You may obtain a copy of the License at
184
185 http://www.apache.org/licenses/LICENSE-2.0
186
187 Unless required by applicable law or agreed to in writing, software
188 distributed under the License is distributed on an "AS IS" BASIS,
189 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 See the License for the specific language governing permissions and
191 limitations under the License.
1 # WEB
2
3 前端框架
4
5 ​ 随着项目的增多,每次开启新项目都需要获取el-admin源码进行二次修改后才可以使用,故将el-admin进行二次封装,以便后面有新的项目可以直接git clone该项目直接进行开发,提高效率
6
7
8
9 #### Build Setup
10
11 ``` bash
12 # 切换分支
13 git checkout dev
14
15 # 安装依赖
16 npm install
17
18 # 更改服务端地址
19 .env.development
20
21 # 启动服务 localhost:8013
22 npm run dev
23
24 # 构建生产环境
25 npm run build:prod
26 ```
1 const plugins = ['@vue/babel-plugin-transform-vue-jsx']
2 // 生产环境移除console
3 if (process.env.NODE_ENV === 'production') {
4 plugins.push('transform-remove-console')
5 }
6 module.exports = {
7 plugins: plugins,
8 presets: [
9 '@vue/app'
10 ]
11 }
1 module.exports = {
2 moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
3 transform: {
4 '^.+\\.vue$': 'vue-jest',
5 '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
6 'jest-transform-stub',
7 '^.+\\.jsx?$': 'babel-jest'
8 },
9 moduleNameMapper: {
10 '^@/(.*)$': '<rootDir>/src/$1'
11 },
12 snapshotSerializers: ['jest-serializer-vue'],
13 testMatch: [
14 '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
15 ],
16 collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
17 coverageDirectory: '<rootDir>/tests/unit/coverage',
18 // 'collectCoverage': true,
19 'coverageReporters': [
20 'lcov',
21 'text-summary'
22 ],
23 testURL: 'http://localhost/'
24 }
1 {
2 "name": "eladmin-web",
3 "version": "2.6.0",
4 "description": "EL-ADMIN 前端源码",
5 "author": "Zheng Jie",
6 "license": "Apache-2.0",
7 "scripts": {
8 "dev": "vue-cli-service serve",
9 "build:prod": "vue-cli-service build",
10 "build:stage": "vue-cli-service build --mode staging",
11 "preview": "node build/index.js --preview",
12 "lint": "eslint --ext .js,.vue src",
13 "test:unit": "jest --clearCache && vue-cli-service test:unit",
14 "svgo": "svgo -f src/assets/icons/svg --config=src/assets/icons/svgo.yml",
15 "new": "plop"
16 },
17 "husky": {
18 "hooks": {
19 "pre-commit": "lint-staged"
20 }
21 },
22 "lint-staged": {
23 "src/**/*.{js,vue}": [
24 "eslint --fix",
25 "git add"
26 ]
27 },
28 "repository": {
29 "type": "git",
30 "url": "https://github.com/elunez/eladmin-web.git"
31 },
32 "bugs": {
33 "url": "https://github.com/elunez/eladmin/issues"
34 },
35 "dependencies": {
36 "@riophae/vue-treeselect": "0.4.0",
37 "axios": "^0.21.1",
38 "clipboard": "2.0.4",
39 "codemirror": "^5.49.2",
40 "connect": "3.6.6",
41 "core-js": "^2.6.12",
42 "echarts": "^4.9.0",
43 "echarts-gl": "^1.1.1",
44 "echarts-wordcloud": "^1.1.3",
45 "el-select-tree": "^1.1.3",
46 "element-ui": "^2.13.2",
47 "file-saver": "1.3.8",
48 "fuse.js": "3.4.4",
49 "js-beautify": "^1.10.2",
50 "js-cookie": "2.2.0",
51 "jsencrypt": "^3.0.0-rc.1",
52 "jszip": "3.1.5",
53 "mavon-editor": "^2.9.0",
54 "normalize.css": "7.0.0",
55 "nprogress": "0.2.0",
56 "path-to-regexp": "2.4.0",
57 "qs": "^6.9.1",
58 "screenfull": "4.2.0",
59 "sortablejs": "1.8.4",
60 "vue": "2.6.10",
61 "vue-count-to": "1.0.13",
62 "vue-cropper": "0.4.9",
63 "vue-echarts": "^5.0.0-beta.0",
64 "vue-highlightjs": "^1.3.3",
65 "vue-image-crop-upload": "^2.5.0",
66 "vue-router": "3.0.2",
67 "vue-splitpane": "1.0.4",
68 "vuedraggable": "2.20.0",
69 "vuex": "3.1.0",
70 "wangeditor": "^3.1.1",
71 "xlsx": "^0.14.1"
72 },
73 "devDependencies": {
74 "@babel/core": "7.0.0",
75 "@babel/parser": "^7.7.4",
76 "@babel/register": "7.0.0",
77 "@vue/babel-plugin-transform-vue-jsx": "^1.2.1",
78 "@vue/cli-plugin-babel": "3.5.3",
79 "@vue/cli-plugin-eslint": "^3.9.1",
80 "@vue/cli-plugin-unit-jest": "3.5.3",
81 "@vue/cli-service": "3.5.3",
82 "@vue/test-utils": "1.0.0-beta.29",
83 "autoprefixer": "^9.5.1",
84 "babel-core": "7.0.0-bridge.0",
85 "babel-eslint": "10.0.1",
86 "babel-jest": "23.6.0",
87 "babel-plugin-dynamic-import-node": "2.3.0",
88 "babel-plugin-transform-remove-console": "^6.9.4",
89 "chalk": "2.4.2",
90 "chokidar": "2.1.5",
91 "connect": "3.6.6",
92 "eslint": "5.15.3",
93 "eslint-plugin-vue": "5.2.2",
94 "html-webpack-plugin": "3.2.0",
95 "http-proxy-middleware": "^0.19.1",
96 "husky": "1.3.1",
97 "lint-staged": "8.1.5",
98 "plop": "2.3.0",
99 "runjs": "^4.3.2",
100 "sass": "^1.26.10",
101 "sass-loader": "^7.1.0",
102 "script-ext-html-webpack-plugin": "2.1.3",
103 "script-loader": "0.7.2",
104 "serve-static": "^1.13.2",
105 "svg-sprite-loader": "4.1.3",
106 "svgo": "1.2.0",
107 "vue-template-compiler": "2.6.10"
108 },
109 "engines": {
110 "node": ">=8.9",
111 "npm": ">= 3.0.0"
112 },
113 "browserslist": [
114 "> 1%",
115 "last 2 versions"
116 ]
117 }
1 const viewGenerator = require('./plop-templates/view/prompt')
2 const componentGenerator = require('./plop-templates/component/prompt')
3
4 module.exports = function(plop) {
5 plop.setGenerator('view', viewGenerator)
6 plop.setGenerator('component', componentGenerator)
7 }
1 module.exports = {
2 plugins: {
3 autoprefixer: {}
4 }
5 }
No preview for this file type
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
6 <meta name="renderer" content="webkit">
7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
8 <link rel="icon" href="<%= BASE_URL %>favicon.ico">
9 <title><%= webpackConfig.name %></title>
10 </head>
11 <body>
12 <div id="app"></div>
13 <!-- built files will be auto injected -->
14 </body>
15 </html>
1 <template>
2 <div id="app">
3 <router-view />
4 </div>
5 </template>
6
7 <script>
8 export default {
9 name: 'App'
10 }
11 </script>
1 import request from '@/utils/request'
2 import qs from 'qs'
3
4 export function initData(url, params) {
5 return request({
6 url: url + '?' + qs.stringify(params, { indices: false }),
7 method: 'get'
8 })
9 }
10
11 export function download(url, params) {
12 return request({
13 url: url + '?' + qs.stringify(params, { indices: false }),
14 method: 'get',
15 responseType: 'blob'
16 })
17 }
1 import request from '@/utils/request'
2
3 export function get(dictName) {
4 const params = {
5 dictName,
6 page: 0,
7 size: 9999
8 }
9 return request({
10 url: 'api/dictDetail',
11 method: 'get',
12 params
13 })
14 }
15
16 export function getDictMap(dictName) {
17 const params = {
18 dictName,
19 page: 0,
20 size: 9999
21 }
22 return request({
23 url: 'api/dictDetail/map',
24 method: 'get',
25 params
26 })
27 }
28
29 export function add(data) {
30 return request({
31 url: 'api/dictDetail',
32 method: 'post',
33 data
34 })
35 }
36
37 export function del(id) {
38 return request({
39 url: 'api/dictDetail/' + id,
40 method: 'delete'
41 })
42 }
43
44 export function edit(data) {
45 return request({
46 url: 'api/dictDetail',
47 method: 'put',
48 data
49 })
50 }
51
52 export function fetchDetail(params) {
53 return request({
54 url: 'api/dictDetail/fetchDetail',
55 method: 'post',
56 params
57 })
58 }
59
60 export default { add, del, edit }
1 import request from '@/utils/request'
2
3 export function get(tableName) {
4 return request({
5 url: 'api/genConfig/' + tableName,
6 method: 'get'
7 })
8 }
9
10 export function update(data) {
11 return request({
12 url: 'api/genConfig',
13 data,
14 method: 'put'
15 })
16 }
1 import request from '@/utils/request'
2
3 export function getAllTable() {
4 return request({
5 url: 'api/generator/tables/all',
6 method: 'get'
7 })
8 }
9
10 export function generator(tableName, type) {
11 return request({
12 url: 'api/generator/' + tableName + '/' + type,
13 method: 'post',
14 responseType: type === 2 ? 'blob' : ''
15 })
16 }
17
18 export function save(data) {
19 return request({
20 url: 'api/generator',
21 data,
22 method: 'put'
23 })
24 }
25
26 export function sync(tables) {
27 return request({
28 url: 'api/generator/sync',
29 method: 'post',
30 data: tables
31 })
32 }
33
1 import request from '@/utils/request'
2
3 function getSexStatistics() {
4 return request({
5 url: 'api/statistics/sexStatistics',
6 method: 'get'
7 })
8 }
9 function getAgeStatistics() {
10 return request({
11 url: 'api/statistics/ageStatistics',
12 method: 'get'
13 })
14 }
15 function getVodStatistics() {
16 return request({
17 url: 'api/statistics/vodStatistics',
18 method: 'get'
19 })
20 }
21 function getLoginStatistics() {
22 return request({
23 url: 'api/statistics/loginStatistics',
24 method: 'get'
25 })
26 }
27 function getConsumeStatistics() {
28 return request({
29 url: 'api/statistics/consumeStatistics',
30 method: 'get'
31 })
32 }
33 function getStyleStatistics() {
34 return request({
35 url: 'api/statistics/styleStatistics',
36 method: 'get'
37 })
38 }
39 function getActivityStatistics() {
40 return request({
41 url: 'api/statistics/activityStatistics',
42 method: 'get'
43 })
44 }
45
46 export default {
47 getSexStatistics,
48 getAgeStatistics,
49 getVodStatistics,
50 getLoginStatistics,
51 getConsumeStatistics,
52 getStyleStatistics,
53 getActivityStatistics
54 }
1 import request from '@/utils/request'
2
3 export function login(username, password, code, uuid) {
4 return request({
5 url: 'auth/login',
6 method: 'post',
7 data: {
8 username,
9 password,
10 code,
11 uuid
12 }
13 })
14 }
15
16 export function getInfo() {
17 return request({
18 url: 'auth/info',
19 method: 'get'
20 })
21 }
22
23 export function getCodeImg() {
24 return request({
25 url: 'auth/code',
26 method: 'get'
27 })
28 }
29
30 export function logout() {
31 return request({
32 url: 'auth/logout',
33 method: 'delete'
34 })
35 }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/app',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/app',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/app',
22 method: 'put',
23 data
24 })
25 }
26
27 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function testDbConnect(data) {
4 return request({
5 url: 'api/database/testConnect',
6 method: 'post',
7 data
8 })
9 }
10
11 export function testServerConnect(data) {
12 return request({
13 url: 'api/serverDeploy/testConnect',
14 method: 'post',
15 data
16 })
17 }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/database',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/database',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/database',
22 method: 'put',
23 data
24 })
25 }
26
27 export function testDbConnection(data) {
28 return request({
29 url: 'api/database/testConnect',
30 method: 'post',
31 data
32 })
33 }
34
35 export default { add, edit, del, testDbConnection }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/deploy',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/deploy',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/deploy',
22 method: 'put',
23 data
24 })
25 }
26
27 export function getApps() {
28 return request({
29 url: 'api/app',
30 method: 'get'
31 })
32 }
33
34 export function getServers() {
35 return request({
36 url: 'api/serverDeploy',
37 method: 'get'
38 })
39 }
40
41 /**
42 * 启动服务
43 * @param data 选中行
44 */
45 export function startServer(data) {
46 return request({
47 url: 'api/deploy/startServer',
48 method: 'post',
49 data
50 })
51 }
52
53 /**
54 * 停止服务
55 * @param data 选中行
56 */
57 export function stopServer(data) {
58 return request({
59 url: 'api/deploy/stopServer',
60 method: 'post',
61 data
62 })
63 }
64
65 /**
66 * 停止服务
67 * @param data 选中行
68 */
69 export function serverStatus(data) {
70 return request({
71 url: 'api/deploy/serverStatus',
72 method: 'post',
73 data
74 })
75 }
76
77 export default { add, edit, del, stopServer, serverStatus, startServer, getServers, getApps }
1 import request from '@/utils/request'
2
3 export function del(ids) {
4 return request({
5 url: 'api/deployHistory',
6 method: 'delete',
7 data: ids
8 })
9 }
10
11 /**
12 * 版本回退
13 * @param data 选中行
14 */
15 export function reducte(data) {
16 return request({
17 url: 'api/deploy/serverReduction',
18 method: 'post',
19 data
20 })
21 }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/serverDeploy',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/serverDeploy',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/serverDeploy',
22 method: 'put',
23 data
24 })
25 }
26
27 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function getErrDetail(id) {
4 return request({
5 url: 'api/logs/error/' + id,
6 method: 'get'
7 })
8 }
9
10 export function delAllError() {
11 return request({
12 url: 'api/logs/del/error',
13 method: 'delete'
14 })
15 }
16
17 export function delAllInfo() {
18 return request({
19 url: 'api/logs/del/info',
20 method: 'delete'
21 })
22 }
1 import request from '@/utils/request'
2
3 export function del(keys) {
4 return request({
5 url: 'auth/online',
6 method: 'delete',
7 data: keys
8 })
9 }
1 import request from '@/utils/request'
2
3 export function resetEmail(data) {
4 return request({
5 url: 'api/code/resetEmail?email=' + data,
6 method: 'post'
7 })
8 }
9
10 export function updatePass(pass) {
11 return request({
12 url: 'api/users/updatePass/' + pass,
13 method: 'get'
14 })
15 }
1 import request from '@/utils/request'
2
3 export function getDepts(params) {
4 return request({
5 url: 'api/dept',
6 method: 'get',
7 params
8 })
9 }
10
11 export function getDeptSuperior(ids) {
12 const data = ids.length || ids.length === 0 ? ids : Array.of(ids)
13 return request({
14 url: 'api/dept/superior',
15 method: 'post',
16 data
17 })
18 }
19
20 export function add(data) {
21 return request({
22 url: 'api/dept',
23 method: 'post',
24 data
25 })
26 }
27
28 export function del(ids) {
29 return request({
30 url: 'api/dept/' + ids,
31 method: 'delete'
32 })
33 }
34
35 export function edit(data) {
36 return request({
37 url: 'api/dept',
38 method: 'put',
39 data
40 })
41 }
42
43 export default { add, edit, del, getDepts, getDeptSuperior }
1 import request from '@/utils/request'
2
3 export function getDicts() {
4 return request({
5 url: 'api/dict/all',
6 method: 'get'
7 })
8 }
9
10 export function add(data) {
11 return request({
12 url: 'api/dict',
13 method: 'post',
14 data
15 })
16 }
17
18 export function del(ids) {
19 return request({
20 url: 'api/dict/' + ids,
21 method: 'delete'
22 })
23 }
24
25 export function edit(data) {
26 return request({
27 url: 'api/dict',
28 method: 'put',
29 data
30 })
31 }
32
33 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function get(dictName) {
4 const params = {
5 dictName,
6 page: 0,
7 size: 9999
8 }
9 return request({
10 url: 'api/dictDetail',
11 method: 'get',
12 params
13 })
14 }
15
16 export function getDictMap(dictName) {
17 const params = {
18 dictName,
19 page: 0,
20 size: 9999
21 }
22 return request({
23 url: 'api/dictDetail/map',
24 method: 'get',
25 params
26 })
27 }
28
29 export function add(data) {
30 return request({
31 url: 'api/dictDetail',
32 method: 'post',
33 data
34 })
35 }
36
37 export function del(id) {
38 return request({
39 url: 'api/dictDetail/' + id,
40 method: 'delete'
41 })
42 }
43
44 export function edit(data) {
45 return request({
46 url: 'api/dictDetail',
47 method: 'put',
48 data
49 })
50 }
51
52 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function getAllJob() {
4 const params = {
5 page: 0,
6 size: 9999,
7 enabled: true
8 }
9 return request({
10 url: 'api/job',
11 method: 'get',
12 params
13 })
14 }
15
16 export function add(data) {
17 return request({
18 url: 'api/job',
19 method: 'post',
20 data
21 })
22 }
23
24 export function del(ids) {
25 return request({
26 url: 'api/job/' + ids,
27 method: 'delete'
28 })
29 }
30
31 export function edit(data) {
32 return request({
33 url: 'api/job',
34 method: 'put',
35 data
36 })
37 }
38
39 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function getMenusTree(pid) {
4 return request({
5 url: 'api/menus/tree',
6 method: 'get'
7 })
8 }
9
10 export function getMenus(params) {
11 return request({
12 url: 'api/menus',
13 method: 'get',
14 params
15 })
16 }
17
18 export function getMenuSuperior(ids) {
19 const data = Array.isArray(ids) || ids.length === 0 ? ids : Array.of(ids)
20 return request({
21 url: 'api/menus/superior',
22 method: 'post',
23 data
24 })
25 }
26
27 export function getChild(id) {
28 return request({
29 url: 'api/menus/child?id=' + id,
30 method: 'get'
31 })
32 }
33
34 export function buildMenus() {
35 return request({
36 url: 'api/menus/build',
37 method: 'get'
38 })
39 }
40
41 export function add(data) {
42 return request({
43 url: 'api/menus',
44 method: 'post',
45 data
46 })
47 }
48
49 export function del(ids) {
50 return request({
51 url: 'api/menus/' + ids,
52 method: 'delete'
53 })
54 }
55
56 export function edit(data) {
57 return request({
58 url: 'api/menus',
59 method: 'put',
60 data
61 })
62 }
63
64 export default { add, edit, del, getMenusTree, getMenuSuperior, getMenus, getChild }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/permissionTemplate',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(id) {
12 return request({
13 url: 'api/permissionTemplate/' + id,
14 method: 'delete'
15 })
16 }
17
18 export function edit(data) {
19 return request({
20 url: 'api/permissionTemplate',
21 method: 'put',
22 data
23 })
24 }
25
26 export function downloadPermissionTemplate(params) {
27 return request({
28 url: 'api/permissionTemplate/download',
29 method: 'get',
30 params,
31 responseType: 'blob'
32 })
33 }
34
35 export function getByCode(code) {
36 return request({
37 url: 'api/permissionTemplate/getByCode/' + code,
38 method: 'get'
39 })
40 }
41
42 export function getPNames() {
43 return request({
44 url: 'api/permission/getPNames',
45 method: 'get'
46 })
47 }
48
49 export function getPremission() {
50 return request({
51 url: 'api/permission',
52 method: 'get'
53 })
54 }
55
56 export function getAuthRefresh() {
57 return request({
58 url: 'api/permission/authRefresh/',
59 method: 'get'
60 })
61 }
62
63 export function getpremissionById(id) {
64 return request({
65 url: 'api/permissionPtRel/getPermissionsByPtId/' + id,
66 method: 'get'
67 })
68 }
69
70 export function putPremission(data) {
71 return request({
72 url: 'api/permissionPtRel/updatePermissionPtRel',
73 method: 'put',
74 data
75 })
76 }
77
78 export function getPremissionList() {
79 return request({
80 url: 'api/permissionTemplate',
81 method: 'get'
82 })
83 }
84
85 export function getUserPremissionList(id) {
86 return request({
87 url: 'api/usersPermissions/getPermissionsByRid/' + id,
88 method: 'get'
89 })
90 }
91
92 export function putUserPremission(data) {
93 return request({
94 url: 'api/usersPermissions/updateUserPermissions',
95 method: 'put',
96 data
97 })
98 }
99
100 export function updatePemission(id) {
101 return request({
102 url: 'api/permission/authRefresh/',
103 method: 'post'
104 })
105 }
106
1 import request from '@/utils/request'
2
3 // 获取所有的Role
4 export function getAll() {
5 return request({
6 url: 'api/roles/all',
7 method: 'get'
8 })
9 }
10
11 export function add(data) {
12 return request({
13 url: 'api/roles',
14 method: 'post',
15 data
16 })
17 }
18
19 export function get(id) {
20 return request({
21 url: 'api/roles/' + id,
22 method: 'get'
23 })
24 }
25
26 export function getLevel() {
27 return request({
28 url: 'api/roles/level',
29 method: 'get'
30 })
31 }
32
33 export function del(id) {
34 return request({
35 url: 'api/roles/' + id,
36 method: 'delete'
37 })
38 }
39
40 export function edit(data) {
41 return request({
42 url: 'api/roles',
43 method: 'put',
44 data
45 })
46 }
47
48 export function editPermission(data) {
49 return request({
50 url: 'api/roles/permission',
51 method: 'put',
52 data
53 })
54 }
55
56 export function editMenu(data) {
57 return request({
58 url: 'api/roles/menu',
59 method: 'put',
60 data
61 })
62 }
63
64 export function downloadRole(params) {
65 return request({
66 url: 'api/roles/download',
67 method: 'get',
68 params,
69 responseType: 'blob'
70 })
71 }
72
73 export default { add, edit, del, get, editMenu, getLevel }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/jobs',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/jobs',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/jobs',
22 method: 'put',
23 data
24 })
25 }
26
27 export function updateIsPause(id) {
28 return request({
29 url: 'api/jobs/' + id,
30 method: 'put'
31 })
32 }
33
34 export function execution(id) {
35 return request({
36 url: 'api/jobs/exec/' + id,
37 method: 'put'
38 })
39 }
40
41 export default { del, updateIsPause, execution, add, edit }
1 import request from '@/utils/request'
2 // import { encrypt } from '@/utils/rsaEncrypt'
3
4 export function add(data) {
5 return request({
6 url: 'api/users',
7 method: 'post',
8 data
9 })
10 }
11
12 export function del(ids) {
13 return request({
14 url: 'api/users/' + ids,
15 method: 'delete'
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/users',
22 method: 'put',
23 data
24 })
25 }
26
27 export function editUser(data) {
28 return request({
29 url: 'api/users/center',
30 method: 'put',
31 data
32 })
33 }
34
35 export function updatePass(user) {
36 const data = {
37 oldPass: user.oldPass,
38 newPass: user.newPass
39 }
40 return request({
41 url: 'api/users/updatePass/',
42 method: 'post',
43 data
44 })
45 }
46
47 export function resetPassword(data) {
48 return request({
49 url: '/api/users/resetPassword',
50 method: 'post',
51 data
52 })
53 }
54
55 export function updateEmail(form) {
56 const data = {
57 password: form.pass,
58 email: form.email
59 }
60 return request({
61 url: 'api/users/updateEmail/' + form.code,
62 method: 'post',
63 data
64 })
65 }
66
67 export default { add, edit, del }
68
1 import request from '@/utils/request'
2
3 export function get() {
4 return request({
5 url: 'api/aliPay',
6 method: 'get'
7 })
8 }
9
10 export function update(data) {
11 return request({
12 url: 'api/aliPay',
13 data,
14 method: 'put'
15 })
16 }
17
18 // 支付
19 export function toAliPay(url, data) {
20 return request({
21 url: 'api/' + url,
22 data,
23 method: 'post'
24 })
25 }
1 import request from '@/utils/request'
2
3 export function get() {
4 return request({
5 url: 'api/email',
6 method: 'get'
7 })
8 }
9
10 export function update(data) {
11 return request({
12 url: 'api/email',
13 data,
14 method: 'put'
15 })
16 }
17
18 export function send(data) {
19 return request({
20 url: 'api/email',
21 data,
22 method: 'post'
23 })
24 }
1 import request from '@/utils/request'
2
3 export function add(data) {
4 return request({
5 url: 'api/localStorage',
6 method: 'post',
7 data
8 })
9 }
10
11 export function del(ids) {
12 return request({
13 url: 'api/localStorage/',
14 method: 'delete',
15 data: ids
16 })
17 }
18
19 export function edit(data) {
20 return request({
21 url: 'api/localStorage',
22 method: 'put',
23 data
24 })
25 }
26
27 export default { add, edit, del }
1 import request from '@/utils/request'
2
3 export function get() {
4 return request({
5 url: 'api/qiNiuContent/config',
6 method: 'get'
7 })
8 }
9
10 export function update(data) {
11 return request({
12 url: 'api/qiNiuContent/config',
13 data,
14 method: 'put'
15 })
16 }
17
18 export function download(id) {
19 return request({
20 url: 'api/qiNiuContent/download/' + id,
21 method: 'get'
22 })
23 }
24
25 export function sync() {
26 return request({
27 url: 'api/qiNiuContent/synchronize',
28 method: 'post'
29 })
30 }
31
32 export function del(ids) {
33 return request({
34 url: 'api/qiNiuContent',
35 method: 'delete',
36 data: ids
37 })
38 }
39
40 export default { del, download, sync }
1 import Vue from 'vue'
2 import SvgIcon from '@/components/SvgIcon'// svg component
3
4 // register globally
5 Vue.component('svg-icon', SvgIcon)
6
7 const req = require.context('./svg', false, /\.svg$/)
8 const requireAll = requireContext => requireContext.keys().map(requireContext)
9 requireAll(req)
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.586" height="200" class="icon" p-id="2886" t="1553488917000" version="1.1" viewBox="0 0 1027 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M512 750.933333c-112.64 0-211.626667-119.466667-245.76-242.346666-34.133333 0-61.44-44.373333-61.44-102.4 0-17.066667 3.413333-34.133333 6.826667-47.786667-3.413333-20.48-6.826667-47.786667-6.826667-68.266667C204.8 129.706667 341.333333 0 508.586667 0S819.2 133.12 819.2 290.133333c0 20.48-3.413333 47.786667-6.826667 68.266667 3.413333 13.653333 6.826667 30.72 6.826667 47.786667 0 58.026667-27.306667 102.4-61.44 102.4-34.133333 122.88-133.12 242.346667-245.76 242.346666z m-235.52-279.893333h6.826667c3.413333 3.413333 10.24 6.826667 10.24 10.24 30.72 129.706667 129.706667 235.52 218.453333 235.52s187.733333-105.813333 218.453333-235.52c0-6.826667 6.826667-10.24 10.24-10.24 3.413333-3.413333 10.24 0 17.066667 0h3.413333c10.24 0 27.306667-27.306667 27.306667-68.266667 0-13.653333-3.413333-30.72-6.826667-40.96v-10.24c0-13.653333 3.413333-40.96 3.413334-61.44C785.066667 150.186667 658.773333 34.133333 508.586667 34.133333 358.4 34.133333 238.933333 150.186667 238.933333 290.133333c0 20.48 3.413333 47.786667 6.826667 64.853334v10.24c-3.413333 10.24-6.826667 23.893333-6.826667 40.96 0 40.96 17.066667 68.266667 27.306667 68.266666h3.413333c0-3.413333 3.413333-3.413333 6.826667-3.413333z" p-id="2887"/><path fill="#bfbfbf" d="M1006.933333 1024H17.066667c-10.24 0-17.066667-6.826667-17.066667-17.066667v-40.96c0-116.053333 75.093333-218.453333 184.32-259.413333l112.64-30.72c6.826667 0 13.653333 0 17.066667 3.413333 51.2 58.026667 122.88 136.533333 197.973333 136.533334s143.36-78.506667 197.973333-136.533334c3.413333-3.413333 10.24-6.826667 17.066667-3.413333l109.226667 30.72c112.64 37.546667 191.146667 150.186667 191.146666 266.24v30.72c-3.413333 13.653333-10.24 20.48-20.48 20.48zM34.133333 989.866667h955.733334v-13.653334c0-98.986667-71.68-201.386667-167.253334-235.52l-98.986666-27.306666c-54.613333 61.44-129.706667 139.946667-215.04 139.946666s-160.426667-78.506667-215.04-139.946666l-98.986667 27.306666c-95.573333 34.133333-160.426667 122.88-160.426667 225.28v23.893334zM785.066667 375.466667h-51.2c-6.826667 0-13.653333-6.826667-17.066667-13.653334 0-3.413333-23.893333-109.226667-30.72-184.32-3.413333-30.72-30.72-58.026667-61.44-58.026666-23.893333 0-40.96 13.653333-58.026667 30.72-17.066667 13.653333-34.133333 20.48-54.613333 20.48-27.306667 0-47.786667 0-64.853333-20.48s-34.133333-30.72-47.786667-30.72c-40.96 0-71.68 30.72-75.093333 68.266666l-17.066667 170.666667c0 3.413333-3.413333 10.24-6.826667 10.24s-6.826667 6.826667-13.653333 6.826667c0 0-23.893333-3.413333-51.2-3.413334-10.24 0-17.066667-6.826667-17.066667-17.066666s10.24-17.066667 20.48-17.066667c13.653333 0 27.306667 0 37.546667 3.413333l17.066667-153.6c3.413333-58.026667 54.613333-102.4 109.226666-102.4 27.306667 0 54.613333 23.893333 71.68 44.373334 6.826667 6.826667 17.066667 6.826667 37.546667 6.826666 10.24 0 20.48-3.413333 30.72-13.653333 20.48-17.066667 47.786667-37.546667 81.92-37.546667 51.2 0 92.16 37.546667 95.573333 88.746667 3.413333 58.026667 20.48 136.533333 27.306667 167.253333H785.066667c10.24 0 17.066667 6.826667 17.066666 17.066667s-6.826667 17.066667-17.066666 17.066667z" p-id="2888"/><path fill="#bfbfbf" d="M324.266667 426.666667c-3.413333 0-10.24 0-13.653334-3.413334l-51.2-51.2c-6.826667-6.826667-6.826667-17.066667 0-23.893333s17.066667-6.826667 23.893334 0l51.2 51.2c6.826667 6.826667 6.826667 17.066667 0 23.893333 0 3.413333-6.826667 3.413333-10.24 3.413334zM699.733333 426.666667c-3.413333 0-6.826667 0-10.24-3.413334-6.826667-6.826667-10.24-17.066667-3.413333-23.893333l34.133333-51.2c6.826667-6.826667 17.066667-10.24 23.893334-3.413333s10.24 17.066667 3.413333 23.893333l-34.133333 51.2c-3.413333 3.413333-6.826667 6.826667-13.653334 6.826667zM563.2 426.666667c-3.413333 0-6.826667 0-10.24-3.413334 0 0-20.48-13.653333-40.96-13.653333s-40.96 13.653333-40.96 13.653333c-6.826667 6.826667-17.066667 3.413333-23.893333-3.413333-6.826667-6.826667-3.413333-17.066667 3.413333-23.893333 0 0 27.306667-20.48 61.44-20.48s61.44 20.48 61.44 20.48c6.826667 6.826667 10.24 17.066667 3.413333 23.893333-3.413333 3.413333-6.826667 6.826667-13.653333 6.826667zM631.466667 614.4c-27.306667 0-51.2-20.48-71.68-37.546667-17.066667-17.066667-34.133333-30.72-54.613334-30.72-17.066667 0-30.72 13.653333-47.786666 30.72-17.066667 17.066667-37.546667 37.546667-68.266667 37.546667-44.373333 0-58.026667-13.653333-81.92-37.546667l-17.066667-17.066666c-6.826667-6.826667-6.826667-17.066667 0-23.893334s17.066667-6.826667 23.893334 0l17.066666 17.066667c23.893333 23.893333 27.306667 27.306667 54.613334 27.306667 13.653333 0 27.306667-13.653333 44.373333-27.306667 20.48-20.48 40.96-40.96 71.68-40.96 30.72 0 54.613333 20.48 75.093333 40.96 17.066667 13.653333 34.133333 27.306667 47.786667 27.306667 30.72 0 34.133333-3.413333 54.613333-27.306667 3.413333-6.826667 10.24-10.24 17.066667-17.066667s17.066667-6.826667 23.893333 0 6.826667 17.066667 0 23.893334c-6.826667 6.826667-10.24 13.653333-17.066666 17.066666-13.653333 23.893333-27.306667 37.546667-71.68 37.546667zM529.066667 648.533333h-34.133334c-10.24 0-17.066667-6.826667-17.066666-17.066666s6.826667-17.066667 17.066666-17.066667h34.133334c10.24 0 17.066667 6.826667 17.066666 17.066667s-6.826667 17.066667-17.066666 17.066666z" p-id="2889"/><path fill="#bfbfbf" d="M631.466667 512c-47.786667 0-85.333333-37.546667-85.333334-85.333333s37.546667-85.333333 85.333334-85.333334 85.333333 37.546667 85.333333 85.333334-37.546667 85.333333-85.333333 85.333333z m0-136.533333c-27.306667 0-51.2 23.893333-51.2 51.2s23.893333 51.2 51.2 51.2 51.2-23.893333 51.2-51.2-23.893333-51.2-51.2-51.2zM392.533333 512C344.746667 512 307.2 474.453333 307.2 426.666667s37.546667-85.333333 85.333333-85.333334 85.333333 37.546667 85.333334 85.333334-37.546667 85.333333-85.333334 85.333333z m0-136.533333c-27.306667 0-51.2 23.893333-51.2 51.2s23.893333 51.2 51.2 51.2 51.2-23.893333 51.2-51.2-23.893333-51.2-51.2-51.2z" p-id="2890"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1468" t="1546239206365" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M907 378.05l-12.4-14.33c-61-70.41-166.93-177.81-236.2-239.4l-14.12-12.58C609.07 80.37 562.07 63.09 512 63.09s-97.08 17.28-132.32 48.65l-14.12 12.57c-69.28 61.61-175.24 169-236.2 239.41l-12.41 14.33c-63.92 73.87-63.92 194 0 267.87l12.41 14.33C190.24 730.6 296.2 838 365.54 899.69l14.14 12.57c35.24 31.37 82.23 48.65 132.32 48.65s97.06-17.28 132.32-48.65l14.13-12.57 8.15-7.25c21.93-19.46 31.93-28.35 62.69-58.93l7.27-7.36-1.16-1.22a28.35 28.35 0 0 0-45.87-32.14c-2.92 2.78-43.63 41.53-68.73 63.91l-14.1 12.56c-24.89 22.1-58.53 34.28-94.7 34.28s-69.82-12.19-94.69-34.31l-14.14-12.58c-67.85-60.34-171.47-165.38-231-234.14l-12.4-14.32c-21.73-25.12-33.7-59.29-33.7-96.2s12-71.07 33.7-96.17l12.41-14.33c59.5-68.75 163.12-173.79 231-234.15l14.1-12.57c24.86-22.12 58.49-34.31 94.68-34.31s69.83 12.19 94.7 34.3l14.12 12.58c67.86 60.37 171.49 165.41 231 234.14l12.39 14.34c45.22 52.21 45.34 143.76 0.26 192.07l-7.15 7.69c-20.35 21.94-32.64 35.19-45.62 39.1-12.3 3.71-27.89-0.23-57.53-14.54-49.55-23.94-119.64-64-144-78 9.87-19.61 32.46-67.6 43.11-115.62l2.86-12.87H534.5v-15.06h154.78v-57.37H534.5v-72.41h-56.89v72.41H322.83v57.37h154.78v15.05H358.54V491H573c-4.63 14.52-13.16 32.57-19.19 44.37-22.13-8.73-80.75-29.33-141-29.33-37.94 0-69.92 10.28-92.49 29.71-22.37 19.27-34.19 46-34.19 77.42s11.32 58.29 32.75 77.74c21.9 19.89 53 30.41 90 30.41 42.76 0 87.09-19 128.18-54.78a326.76 326.76 0 0 0 43.61-46.35c22.9 12.75 90 50 152.61 83.47 40.83 21.85 69.5 26.18 95.87 14.47 25.09-11.14 47.07-36.53 77.49-71.68l0.47-0.54C971 572.07 971 451.9 907 378.05zM407.83 662c-60.15 0-64.83-37.38-64.83-48.82a48.21 48.21 0 0 1 12.15-31.36c11.06-12.2 28.45-18.39 51.69-18.39 50 0 95 17.21 115.39 26.35C503.71 611.69 456 662 407.83 662z" p-id="1469"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="6244" t="1553935360914" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M957.217391 86.372174C957.217391 86.372174 957.217391 608.211478 957.217391 608.211478 957.217391 639.510261 949.782261 670.630957 934.956522 701.573565 920.086261 732.605217 900.674783 762.568348 876.633043 791.685565 852.591304 820.758261 825.121391 848.317217 794.267826 874.273391 763.369739 900.274087 732.070957 923.425391 700.326957 943.727304 668.538435 964.073739 637.68487 980.992 607.721739 994.437565 577.758609 1007.88313 551.490783 1017.09913 528.918261 1022.130087 528.918261 1022.130087 518.233043 1024 518.233043 1024 518.233043 1024 508.438261 1022.130087 508.438261 1022.130087 485.286957 1017.09913 458.440348 1007.88313 427.853913 994.437565 397.267478 980.992 365.523478 964.073739 332.577391 943.727304 299.631304 923.425391 267.308522 900.274087 235.52 874.273391 203.776 848.317217 175.415652 820.758261 150.483478 791.685565 125.551304 762.568348 105.382957 732.605217 89.978435 701.573565 74.48487 670.630957 66.782609 639.510261 66.782609 608.211478 66.782609 608.211478 66.782609 86.372174 66.782609 86.372174 66.782609 86.372174 103.290435 80.717913 103.290435 80.717913 103.290435 80.717913 512.890435 0 512.890435 0 512.890435 0 930.504348 80.717913 930.504348 80.717913 930.504348 80.717913 957.217391 86.372174 957.217391 86.372174 957.217391 86.372174 957.217391 86.372174 957.217391 86.372174ZM513.024 75.553391C513.024 75.553391 508.082087 74.529391 508.082087 74.529391 508.082087 74.529391 156.538435 137.527652 156.538435 137.527652 156.538435 137.527652 156.538435 466.765913 156.538435 466.765913 156.538435 466.765913 513.024 466.765913 513.024 466.765913 513.024 466.765913 513.024 75.553391 513.024 75.553391 513.024 75.553391 513.024 75.553391 513.024 75.553391ZM867.461565 466.765913C867.461565 466.765913 513.024 466.765913 513.024 466.765913 513.024 466.765913 513.024 935.401739 513.024 935.401739 535.81913 929.881043 560.617739 921.466435 587.419826 910.113391 614.177391 898.760348 640.623304 885.359304 666.713043 869.865739 692.847304 854.372174 717.957565 837.186783 742.13287 818.265043 766.308174 799.343304 787.634087 778.99687 806.288696 757.314783 824.898783 735.677217 839.724522 713.149217 850.810435 689.730783 861.94087 666.35687 867.461565 642.582261 867.461565 618.496 867.461565 618.496 867.461565 466.765913 867.461565 466.765913 867.461565 466.765913 867.461565 466.765913 867.461565 466.765913Z" p-id="6245"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574649142168" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1910" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M468.693333 16.725333a85.333333 85.333333 0 0 1 82.56 0l381.952 211.072a85.333333 85.333333 0 0 1 44.074667 74.666667v419.029333a85.333333 85.333333 0 0 1-44.074667 74.666667l-381.952 211.114667a85.333333 85.333333 0 0 1-82.56 0l-381.952-211.072A85.333333 85.333333 0 0 1 42.666667 721.493333V302.506667a85.333333 85.333333 0 0 1 44.074666-74.666667L468.693333 16.682667z m423.253334 285.781334l-381.994667-211.072L128 302.506667v418.986666l381.952 211.072 381.994667-211.072V302.506667z m-684.714667 42.197333a42.666667 42.666667 0 0 1 57.984-16.725333l244.736 135.253333 244.778667-135.253333a42.666667 42.666667 0 0 1 41.258666 74.666666l-243.370666 134.528v268.16a42.666667 42.666667 0 0 1-85.333334 0V537.173333L223.914667 402.688a42.666667 42.666667 0 0 1-16.682667-58.026667z" fill="#bfbfbf" p-id="1911"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574649191790" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2774" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M646 1024H100A100 100 0 0 1 0 924V258a100 100 0 0 1 100-100h546a100 100 0 0 1 100 100v31a40 40 0 1 1-80 0v-31a20 20 0 0 0-20-20H100a20 20 0 0 0-20 20v666a20 20 0 0 0 20 20h546a20 20 0 0 0 20-20V713a40 40 0 0 1 80 0v211a100 100 0 0 1-100 100z" fill="#cdcdcd" p-id="2775"></path><path d="M924 866H806a40 40 0 0 1 0-80h118a20 20 0 0 0 20-20V100a20 20 0 0 0-20-20H378a20 20 0 0 0-20 20v8a40 40 0 0 1-80 0v-8A100 100 0 0 1 378 0h546a100 100 0 0 1 100 100v666a100 100 0 0 1-100 100z" fill="#cdcdcd" p-id="2776"></path><path d="M469 887a40 40 0 0 1-27-10L152 618a40 40 0 0 1 1-60l290-248a40 40 0 0 1 66 30v128a367 367 0 0 0 241-128l94-111a40 40 0 0 1 70 35l-26 109a430 430 0 0 1-379 332v142a40 40 0 0 1-40 40zM240 589l189 169v-91a40 40 0 0 1 40-40c144 0 269-85 323-214a447 447 0 0 1-323 137 40 40 0 0 1-40-40v-83z" fill="#cdcdcd" p-id="2777"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="654" t="1545959978831" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M877.297288 553.796942L553.79643 877.298823c-54.370305 54.369282-126.656655 84.311221-203.547883 84.312245-76.890204-0.001023-149.177578-29.942963-203.546859-84.312245S62.389444 750.641145 62.389444 673.750941c0-76.890204 29.942963-149.177578 84.312244-203.547883l135.442762-135.441738c75.829036-75.829036 199.213157-75.829036 275.043217 0s75.830059 199.214181 0 275.043217L399.320173 767.674077c-17.620309 17.620309-46.188972 17.620309-63.809281 0-17.620309-17.621333-17.620309-46.188972 0-63.809281l157.867493-157.867494c40.645722-40.645722 40.645722-106.779955 0-147.424654-40.644699-40.645722-106.778932-40.645722-147.424654 0L210.51097 534.01234c-77.051887 77.05291-77.051887 202.423269 0 279.476179 77.051887 77.051887 202.423269 77.051887 279.475155 0l323.501882-323.501882c77.051887-77.050864 77.051887-202.423269 0-279.475156-77.05291-77.051887-202.424292-77.050864-279.476179 0-17.619286 17.620309-46.188972 17.620309-63.809281 0s-17.619286-46.189995 0-63.809281c54.369282-54.369282 126.657678-84.313268 203.546859-84.312244 76.892251 0 149.178601 29.942963 203.548906 84.312244 54.369282 54.369282 84.311221 126.656655 84.311221 203.547882 0 76.889181-29.942963 149.176554-84.312245 203.54686z" p-id="655"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="6717" t="1547360688278" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M890 120H134a70 70 0 0 0-70 70v500a70 70 0 0 0 70 70h756a70 70 0 0 0 70-70V190a70 70 0 0 0-70-70z m-10 520a40 40 0 0 1-40 40H712V448a40 40 0 0 0-80 0v232h-80V368a40 40 0 0 0-80 0v312h-80V512a40 40 0 0 0-80 0v168H184a40 40 0 0 1-40-40V240a40 40 0 0 1 40-40h656a40 40 0 0 1 40 40zM696 824H328a40 40 0 0 0 0 80h368a40 40 0 0 0 0-80z" p-id="6718"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574649229600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3752" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M942 191.9C939.8 128.9 748.1 78 512 78S84.2 128.9 82 191.9V831c0 63.5 192.5 115 430 115s430-51.5 430-115V191.9z m-56.7 393.8c-4.6 3.3-11.6 7.4-21.9 12.2-21.3 9.8-50.5 19.1-84.4 26.8-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7v-123c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v122.9h-7l0.3 0.3z m0-177c-4.6 3.3-11.6 7.4-21.9 12.2-21.3 9.8-50.5 19.1-84.4 26.8-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7V246.9c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v161.6h-7c0.1 0 0.2 0.1 0.3 0.2zM160.7 180.8C182 171 211.2 161.7 245 154c74-16.8 168.8-26 267-26s193 9.2 267 26c33.9 7.7 63.1 16.9 84.4 26.8 10.3 4.8 17.3 8.9 21.9 12.2-4.6 3.3-11.6 7.4-21.9 12.2C842 215 812.8 224.3 779 232c-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 4.7-3.3 11.7-7.4 22-12.2zM885.3 831c-4.6 3.3-11.6 7.4-21.9 12.2C842 853 812.8 862.3 779 870c-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7V639.5c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v191.3h-7c0.1 0.1 0.2 0.1 0.3 0.2z" fill="#cdcdcd" p-id="3753"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="5330" t="1553935012815" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M453.22752 781.67168 170.31936 781.67168 170.31936 409.18016l650.45632 0c11.6864 0 21.15968-9.47328 21.15968-21.15968L841.93536 218.68032l0-12.3904c0-11.6864-9.47328-21.15968-21.15968-21.15968l-11.8784 0L660.94464 185.13024l0-35.97184c0-11.6864-9.47328-21.15968-21.15968-21.15968-11.68768 0-21.15968 9.47328-21.15968 21.15968l0 35.97184L356.24704 185.13024l0-35.97184c0-11.6864-9.47328-21.15968-21.15968-21.15968s-21.15968 9.47328-21.15968 21.15968l0 35.97184L161.04064 185.13024l-11.88096 0c-11.6864 0-21.15968 9.47328-21.15968 21.15968l0 12.3904 0 169.34144 0 402.4192c0 18.49984 14.8224 33.55008 33.04064 33.55008l292.18816 0c11.6864 0 21.15968-9.472 21.15968-21.15968C474.38848 791.14496 464.91392 781.67168 453.22752 781.67168zM170.31936 227.4496l143.60832 0 0 35.97184c0 11.6864 9.47328 21.15968 21.15968 21.15968s21.15968-9.472 21.15968-21.15968l0-35.97184 262.37696 0 0 35.97184c0 11.6864 9.472 21.15968 21.15968 21.15968 11.6864 0 21.15968-9.472 21.15968-21.15968l0-35.97184L799.616 227.4496l0 139.41248L170.31936 366.86208 170.31936 227.4496zM690.49984 483.10016c-113.83808 0-206.44992 92.61312-206.44992 206.45248 0 113.83552 92.61184 206.44736 206.44992 206.44736s206.44992-92.61312 206.44992-206.44736C896.94976 575.71328 804.33792 483.10016 690.49984 483.10016zM690.49984 853.68064c-90.50112 0-164.13056-73.62816-164.13056-164.13184s73.62816-164.13184 164.13056-164.13184c90.5024 0 164.13184 73.62816 164.13184 164.13184S781.00224 853.68064 690.49984 853.68064zM390.10304 640.81536l-143.8848 0c-11.68768 0-21.15968 9.472-21.15968 21.15968 0 11.68512 9.472 21.1584 21.15968 21.1584l143.8848 0c11.6864 0 21.15968-9.47328 21.15968-21.1584C411.26144 650.28736 401.78816 640.81536 390.10304 640.81536zM390.10304 521.32608l-143.8848 0c-11.68768 0-21.15968 9.47328-21.15968 21.1584 0 11.68768 9.472 21.15968 21.15968 21.15968l143.8848 0c11.6864 0 21.15968-9.472 21.15968-21.15968C411.26144 530.80064 401.78816 521.32608 390.10304 521.32608zM803.1744 668.39296l-91.51488 0 0-50.78272c0-11.68768-9.472-21.15968-21.1584-21.15968s-21.15968 9.472-21.15968 21.15968l0 71.9424c0 11.68512 9.47328 21.1584 21.15968 21.1584l112.67328 0c11.6864 0 21.15968-9.47328 21.15968-21.1584C824.33536 677.86496 814.8608 668.39296 803.1744 668.39296z" p-id="5331"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574649300337" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4312" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M877.952 447.616v-0.256a272 272 0 0 0-479.68-175.68 166.144 166.144 0 0 0-226.016 155.296c0 4.768 0.32 9.6 0.704 14.144A196.896 196.896 0 0 0 206.592 832H448v-256H304l208-208 208 208H576v256h241.408a196.96 196.96 0 0 0 60.544-384.384z" fill="#cdcdcd" p-id="4313"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1799" t="1553478255619" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M329.285097 317.714062l-8.422833 4.428869c-8.78099 4.584412-13.528108 14.84715-11.923564 24.415063 1.644453 4.909823 3.491521 9.864672 5.492084 14.747889 2.030239 4.854565 4.230348 9.652847 6.53688 14.293541 5.621021 7.891737 16.246009 11.824303 25.699312 8.858762l9.041934-2.868327c14.741749-3.860934 31.115672-0.056282 42.62582 11.512195 11.549034 11.526521 15.374152 27.863604 11.549034 42.570561l-2.882654 9.126868c-2.958378 9.438976 0.938372 20.042475 8.830109 25.706475 4.634554 2.328022 9.403161 4.52813 14.323217 6.529717 4.876054 2.043542 9.80839 3.846608 14.739702 5.478781 9.538237 1.603521 19.87363-3.123131 24.414039-11.910261l4.402263-8.388041c7.67889-13.144368 21.915126-22.002107 38.267559-22.002107 16.338107 0 30.547737 8.829086 38.255279 21.931498l4.41352 8.459672c4.584412 8.78713 14.84715 13.513782 24.414039 11.910261 4.91187-1.632173 9.851369-3.462868 14.734586-5.478781 4.882194-2.030239 9.66615-4.201695 14.322194-6.529717 7.891737-5.622044 11.809977-16.253172 8.843412-25.706475l-2.852978-9.041934c-3.859911-14.733563-0.069585-31.085996 11.484565-42.655496 11.55415-11.525498 27.878954-15.372106 42.599214-11.512195l9.097192 2.88163c9.426697 2.952238 20.044522-0.937348 25.693172-8.829086 2.313695-4.656043 4.527107-9.411347 6.54302-14.322194 2.029216-4.883217 3.847631-9.80839 5.495154-14.748912 1.616824-9.581216-3.108804-19.843954-11.911284-24.429389l-8.402367-4.400217c-13.132088-7.665587-21.98778-21.901823-21.98778-38.255279 0-16.32378 8.830109-30.589692 21.974477-38.268582l8.416693-4.443196c8.80248-4.571109 13.528108-14.832823 11.924587-24.400736-1.6465-4.910846-3.479241-9.850345-5.493108-14.733563-2.031263-4.868891-4.202719-9.680477-6.529717-14.308891-5.622044-7.890714-16.253172-11.82328-25.708522-8.842389l-9.05626 2.852978c-14.747889 3.861958-31.071669 0.057305-42.654472-11.512195-11.55415-11.55415-15.344476-27.877931-11.484565-42.612517l2.852978-9.05626c2.966565-9.44-0.951675-20.043499-8.856715-25.692149-4.641717-2.328022-9.397021-4.542456-14.307867-6.544043-4.883217-2.029216-9.82374-3.846608-14.734586-5.465478-9.567913-1.632173-19.872606 3.123131-24.414039 11.895935l-4.400217 8.389064c-7.67889 13.174044-21.931498 22.002107-38.268582 22.002107-16.309454 0-30.576389-8.828063-38.267559-22.002107l-4.387937-8.389064c-4.554736-8.771781-14.8318-13.528108-24.405853-11.895935-4.954849 1.604544-9.873882 3.435239-14.763239 5.4225-4.883217 2.044566-9.688663 4.217045-14.323217 6.545066-7.891737 5.649674-11.808954 16.266475-8.830109 25.735128l2.826372 9.05626c3.882424 14.762215 0.057305 31.085996-11.491729 42.612517-11.510148 11.5695-27.849278 15.373129-42.611493 11.526521l-9.070586-2.867304c-9.44-2.980891-20.063965 0.951675-25.686009 8.842389-2.342348 4.628414-4.52813 9.44-6.53688 14.308891-2.036379 4.882194-3.847631 9.822716-5.492084 14.733563-1.603521 9.581216 3.142573 19.85828 11.923564 24.443715l8.402367 4.400217c13.156648 7.67889 21.986757 21.944801 21.986757 38.268582C351.251388 295.79689 342.421278 310.019823 329.285097 317.714062zM511.977999 171.706687c59.532885 0 107.795075 48.275493 107.795075 107.779725 0 59.490929-48.26219 107.752096-107.795075 107.752096-59.533908 0-107.752096-48.26219-107.752096-107.752096C404.226926 219.98218 452.445114 171.706687 511.977999 171.706687z" p-id="1800"/><path fill="#bfbfbf" d="M924.647713 689.174212 798.570249 689.174212 798.570249 581.650313c0-26.387997-21.476127-47.850821-47.864124-47.850821L276.2543 533.799492c-26.386974 0-47.851844 21.462824-47.851844 47.850821l0 107.523899L99.345124 689.174212c-20.419052 0-36.95568 16.550954-36.95568 36.948517l0 184.771237c0 20.399609 16.536628 36.962843 36.95568 36.962843l273.965675 0c20.397562 0 36.947494-16.564257 36.947494-36.962843L410.258293 726.122729c0-20.398586-16.550954-36.948517-36.947494-36.948517l-123.103736 0L250.207064 581.650313c0-14.366196 11.68104-26.047236 26.047236-26.047236l474.451826 0c14.364149 0 26.062586 11.68104 26.062586 26.047236l0 107.523899L650.689201 689.174212c-20.412912 0-36.962843 16.550954-36.962843 36.948517l0 184.771237c0 20.399609 16.549931 36.962843 36.962843 36.962843l273.958512 0c20.397562 0 36.96182-16.564257 36.96182-36.962843L961.609533 726.122729C961.609533 705.725166 945.044252 689.174212 924.647713 689.174212z" p-id="1801"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" fill="#bfbfbf" p-id="2423"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2807" t="1547195013953" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#cdcdcd" d="M529.0496 527.616l-30.7712-30.7456 85.0688-85.0944 30.7712 30.7712z" p-id="2808"/><path fill="#cdcdcd" d="M0 340.48l427.52 256 248.32 427.52L1024 0l-1024 340.48zM665.6 921.6l-207.36-355.84-355.84-212.48L911.36 81.92l-243.2 243.2 30.72 30.72 243.2-243.2L665.6 921.6z" p-id="2809"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1497" t="1554868028575" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M558.409143 658.285714h-92.818286l-28.379428 62.427429a18.285714 18.285714 0 1 1-33.28-15.140572l91.428571-201.142857a18.285714 18.285714 0 0 1 33.28 0l91.428571 201.142857a18.285714 18.285714 0 1 1-33.28 15.140572L558.409143 658.285714z m-16.64-36.571428L512 556.178286 482.230857 621.714286h59.538286zM329.142857 128h475.428572a18.285714 18.285714 0 1 1 0 36.571429H329.142857a91.428571 91.428571 0 0 0 0 182.857142h475.428572a18.285714 18.285714 0 0 1 18.285714 18.285715v512a18.285714 18.285714 0 0 1-18.285714 18.285714H329.142857A128 128 0 0 1 201.142857 768V256A128 128 0 0 1 329.142857 128zM237.714286 345.6V768A91.428571 91.428571 0 0 0 329.142857 859.428571h457.142857v-475.428571H329.142857a127.634286 127.634286 0 0 1-91.428571-38.4zM329.142857 274.285714a18.285714 18.285714 0 0 1 0-36.571428h438.857143a18.285714 18.285714 0 1 1 0 36.571428H329.142857z" p-id="1498"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1583752001956" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9290" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M891.8 320H608V39.2L891.8 320zM704 384v260c0 133.6-73 200.2-226.6 200.2H288V169.6h189.4c24 0 46.2 1.6 66.2 5v-168C521.8 2.2 498.8 0 474.4 0H96v1024h378.4C755.4 1024 896 894.8 896 636.2V384h-192z" fill="#707070" p-id="9291"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1583752303941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16654" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1024 896v128H0v-320h128v192h768v-192h128v192zM576 554.688L810.688 320 896 405.312l-384 384-384-384L213.312 320 448 554.688V0h128v554.688z" fill="#707070" p-id="16655"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2851" t="1554009929581" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M511.471952 957.559056c-51.013955 0-93.475781-37.318473-101.502932-86.07185l-199.795618 0c-32.961053 0-59.794291-26.834261-59.794291-59.827038 0-12.417319 3.735224-24.277935 10.811684-34.336434l83.646513-111.561431 533.235518 0 83.515524 111.364948c7.306713 10.484212 11.008167 22.246587 11.008167 34.532917 0 32.9938-26.833238 59.827038-59.794291 59.827038l-199.861112 0C604.914986 920.241606 562.485907 957.559056 511.471952 957.559056zM261.61307 699.312805l-73.293289 97.734961c-2.751786 3.964455-4.390168 9.174325-4.390168 14.612403 0 14.481414 11.762375 26.276536 26.243789 26.276536l231.969715 0 0 16.774739c0 38.202647 31.093441 69.296088 69.328835 69.296088 38.202647 0 69.296088-31.093441 69.296088-69.296088l0-16.774739 232.03521 0c14.481414 0 26.243789-11.795122 26.243789-26.276536 0-5.373606-1.605635-10.516959-4.652145-14.875403l-73.096806-97.472983L261.61307 699.311782zM786.461219 613.240955l-550.011281 0 0-188.951187c0-112.348386 68.673891-213.392858 172.142677-255.101499l0-3.113028c0-56.715033 46.164304-102.879337 102.879337-102.879337 56.715033 0 102.84659 46.164304 102.84659 102.879337l0 3.113028c103.468786 41.708641 172.142677 142.753113 172.142677 255.101499L786.461219 613.240955zM270.00044 579.690453l482.910277 0 0-155.400685c0-102.158899-64.67669-193.668827-160.969751-227.677789l-11.172926-3.964455 0-26.571261c0-38.235394-31.093441-69.328835-69.296088-69.328835-38.235394 0-69.328835 31.093441-69.328835 69.328835l0 26.571261-11.172926 3.964455c-96.294085 34.008962-160.969751 125.51889-160.969751 227.677789L270.00044 579.690453z" p-id="2852"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="5914" t="1547360570987" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M234.27218 32h58.36780969v179.99999344h-58.36874719V32zM583.99997844 32h58.27218562v179.99999344H583.99997844V32zM175.99999437 331.99998875h524.59216688v59.99999719H175.99999437v-59.99999719z m0 179.9999925h291.40780125v59.99999812H175.99999437v-59.99999812z m352.55998594 383.999985H32V79.99999812h767.99997v381.6477975C911.16871531 492.99216969 991.9999625 597.1043525 991.9999625 721.99997281c0 149.99999437-116.59218281 269.99998969-262.27217719 269.99998969a258.38436469 258.38436469 0 0 1-201.1199925-95.99999625z m212.35217906-443.75998312V138.31999625H91.08781062v699.35997188H492.36873219A277.72780125 277.72780125 0 0 1 467.40779562 721.99997281c0-149.99999437 116.59218281-269.99998969 262.31998969-269.99998875 3.744375 0 7.4878125 0.095625 11.18437407 0.23999906zM175.99999437 691.99997469h233.13561563v59.99999719H175.99999437v-59.99999719z m553.72779094-179.99999344c-110.73562031 0-203.9999925 95.99999625-203.9999925 209.99999156s93.26437125 209.99999156 203.9999925 209.9999925 203.9999925-95.99999625 203.99999156-209.9999925-93.26343375-209.99999156-203.99999156-209.99999156zM703.99997375 559.99997938h59.75999812v203.99999249H703.99997375V559.99997938z m59.75999812 239.99999062v59.75999812H703.99997375V799.99997h59.75999812z" p-id="5915"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2201" t="1545883026424" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M925.597853 836.903811c0.367367-2.783393 0.847298-5.528923 0.847298-8.40953L926.44515 180.091658c19.034519-11.079336 31.976272-31.470759 31.976272-55.082526 0-35.32146-28.633131-63.956637-63.953567-63.956637-23.611767 0-44.007283 12.941753-55.082526 31.980365L182.108594 93.03286c-11.076266-19.038612-31.470759-31.980365-55.082526-31.980365-35.31839 0-63.953567 28.635177-63.953567 63.956637 0 23.611767 12.9438 44.00319 31.976272 55.082526l0 648.402623c0 2.880607 0.479931 5.627161 0.851391 8.40953-19.4991 10.954493-32.827663 31.586392-32.827663 55.543014 0 35.317367 28.635177 63.953567 63.953567 63.953567 35.32146 0 63.953567-28.635177 63.953567-63.953567l639.536698 0c0 35.317367 28.631084 63.953567 63.953567 63.953567 35.319413 0 63.953567-28.635177 63.953567-63.953567C958.421422 868.490204 945.093882 847.859327 925.597853 836.903811zM862.491583 828.494281 159.00234 828.494281 159.00234 180.091658c9.596566-5.586229 17.524119-13.513782 23.110347-23.110347l657.273664 0c5.582135 9.596566 13.509688 17.524119 23.106254 23.110347L862.492606 828.494281z" p-id="2202"/><path d="M670.62781 252.915243 350.86202 252.915243 318.885747 252.915243 286.908452 252.915243 286.908452 380.818285 350.86202 380.818285 350.86202 316.864718 478.768131 316.864718 478.768131 668.610874 414.815587 668.610874 414.815587 732.564441 606.675266 732.564441 606.675266 668.610874 542.721699 668.610874 542.721699 316.864718 670.62781 316.864718 670.62781 380.818285 734.585471 380.818285 734.585471 252.915243 702.609199 252.915243Z" p-id="2203"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1583751668311" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2803" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M263.456 759.36c0 27.04 22.88 40.576 68.672 40.576 40.064 0 60.096-14.048 60.096-42.144 0-26.528-21.856-39.808-65.536-39.808C284.544 718.016 263.456 731.808 263.456 759.36zM853.344 0 170.688 0C76.8 0 0 76.8 0 170.688l0 682.624C0 947.264 76.8 1024 170.688 1024l682.656 0C947.232 1024 1024 947.264 1024 853.312L1024 170.688C1024 76.8 947.232 0 853.344 0zM475.744 408.992c-7.296 2.592-17.696 5.472-31.2 8.576 4.16 11.968 6.24 23.168 6.24 33.568 0 33.28-10.016 62.304-30.048 87.008-20.032 24.704-45.92 39.392-77.632 44.096-20.8 3.136-31.2 14.304-31.2 33.568 0 6.752 3.392 13.536 10.144 20.288 8.832 9.888 21.856 16.128 39.008 18.72 74.4 11.456 111.584 42.4 111.584 92.864 0 80.64-48.128 120.96-144.352 120.96-39.552 0-72.064-7.04-97.536-21.056-32.256-17.696-48.384-45.536-48.384-83.488 0-43.712 24.192-73.6 72.576-89.728l0-1.568c-17.696-10.912-26.528-27.584-26.528-49.952 0-29.12 8.32-47.36 24.96-54.624l0-1.568c-16.64-5.728-31.488-18.72-44.48-39.04-14.56-21.856-21.856-45.248-21.856-70.24 0-37.472 13.28-68.672 39.808-93.632 25.504-23.424 55.936-35.104 91.296-35.104 25.504 0 49.152 6.24 71.008 18.72 24.96 0 53.856-6.24 86.624-18.72L475.744 408.992 475.744 408.992zM602.176 679.008l-88.192 0c1.056-10.4 1.568-28.096 1.568-53.056L515.552 383.232c0-24.448-0.512-41.376-1.568-50.72l88.192 0c-1.056 9.888-1.568 26.272-1.568 49.152l0 239.552C600.608 647.776 601.152 667.04 602.176 679.008zM596.32 254.496c-10.656 11.456-23.296 17.152-37.856 17.152-15.072 0-27.968-5.728-38.624-17.152-10.656-11.456-16-24.96-16-40.576 0-16.128 5.344-29.92 16-41.376 10.656-11.456 23.552-17.152 38.624-17.152 14.56 0 27.2 5.728 37.856 17.152 10.656 11.456 16 25.216 16 41.376C612.32 229.504 606.976 243.04 596.32 254.496zM841.696 668.832c-19.264 10.4-42.4 15.616-69.472 15.616-37.984 0-64.256-13.504-78.816-40.576-10.944-20.288-16.384-52.288-16.384-95.968l0-139.68 0.768 0 0-1.568-11.712-0.768c-6.752 0-15.616 0.768-26.528 2.336L639.584 332.512l38.24 0 0-30.432c0-14.56-0.768-26.272-2.336-35.104l90.528 0c-1.536 9.888-2.336 21.056-2.336 33.536l0 32 67.872 0 0 75.68c-2.592 0-7.424-0.256-14.432-0.768-7.04-0.512-13.664-0.8-19.904-0.8l-33.568 0 0 145.152c0 34.848 11.456 52.288 34.336 52.288 16.128 0 30.688-4.416 43.712-13.248L841.696 668.832 841.696 668.832zM321.216 400.416c-32.768 0-49.152 19.264-49.152 57.76 0 35.904 16.384 53.856 49.152 53.856 31.744 0 47.616-18.208 47.616-54.624 0-15.104-3.648-28.096-10.912-39.008C349.056 406.4 336.832 400.416 321.216 400.416z" p-id="2804"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1258" t="1554279845314" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M921.9 468.6H749.6c-9.4 0-18.4 3.8-25 10.5-6.6 6.7-10.3 15.7-10.3 25.1v11.1c0 19.6 15.9 35.5 35.4 35.5h172.2c19.5 0 35.3-15.9 35.3-35.5v-11.1c0-9.4-3.7-18.4-10.3-25.1-6.6-6.7-15.6-10.5-25-10.5zM522.4 163.9c-53.6 42.6-165.7 102.3-246.3 159.8h-0.1c-0.9 0.6-1.8 3.8-2.8 4.3-9.5 5.4-13.8 20.1-65.6 20.1h-101c-26 0-42 12.2-42 39.6V631c0 27.4 14.7 40.9 42 40.9H208c51.5 0.1 55.7 14.8 65.2 20.1 0.9 0.5 1.8 3.7 2.7 4.3h0.1c78.2 57.5 191 121.8 246.4 162.7 16.7 12.3 72.1 33.9 72.1-42.1v-614c0-76.1-55.9-51.8-72.1-39z m159 167.8c9.2 16.1 27.3 20.2 40.5 9l141.5-119.3c13.3-11.1 16.5-33.2 7.4-49.4l-5.2-9.1c-9.1-16.1-27.3-20.1-40.5-9L683.6 273.2c-13.2 11.2-16.5 33.2-7.4 49.4l5.2 9.1z m40.4 347.4c-13.2-11.1-31.3-7-40.4 9l-5.2 9.1c-9.1 16.1-5.8 38.2 7.4 49.4L825.1 866c13.2 11.1 31.3 7.1 40.4-9l5.2-9.1c9.1-16.1 5.8-38.2-7.4-49.4L721.8 679.1z m0 0" p-id="1259"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="3572" t="1545136555590" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M403.2 780.8V619.2h-160c-89.6 0-161.6 72-161.6 161.6s72 161.6 161.6 161.6 160-72 160-161.6z m81.6 0c0 134.4-108.8 243.2-243.2 243.2S0 915.2 0 780.8s108.8-243.2 243.2-243.2h243.2c-1.6 0-1.6 243.2-1.6 243.2z m134.4 0V619.2h161.6c89.6 0 161.6 72 161.6 161.6s-72 161.6-161.6 161.6-161.6-72-161.6-161.6z m-81.6 0c0 134.4 108.8 243.2 243.2 243.2S1024 915.2 1024 780.8s-108.8-243.2-243.2-243.2H537.6v243.2z m-134.4-537.6v161.6h-160c-89.6 0-161.6-72-161.6-161.6S153.6 81.6 243.2 81.6s160 72 160 161.6z m81.6 0C484.8 108.8 376 0 243.2 0 108.8 1.6 0 108.8 0 243.2s108.8 243.2 243.2 243.2h243.2c-1.6 0-1.6-243.2-1.6-243.2z m134.4 0v161.6h161.6c89.6 0 161.6-72 161.6-161.6S870.4 81.6 780.8 81.6 619.2 153.6 619.2 243.2z m-81.6 0C537.6 108.8 646.4 0 780.8 0c134.4 1.6 241.6 108.8 241.6 243.2s-108.8 243.2-243.2 243.2H537.6v-81.6-161.6z" p-id="3573"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1303" t="1545960873089" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#4A576A" d="M784 112H240C152 112 80 184 80 272v480c0 88 72 160 160 160h544c88 0 160-72 160-160V272c0-88-72-160-160-160z m96 640c0 52.8-43.2 96-96 96H240c-52.8 0-96-43.2-96-96V272c0-52.8 43.2-96 96-96h544c52.8 0 96 43.2 96 96v480z" p-id="1304"/><path fill="#4A576A" d="M352 480c52.8 0 96-43.2 96-96s-43.2-96-96-96-96 43.2-96 96 43.2 96 96 96z m0-128c17.6 0 32 14.4 32 32s-14.4 32-32 32-32-14.4-32-32 14.4-32 32-32zM814.4 731.2l-3.2-3.2-177.6-177.6c-25.6-25.6-65.6-25.6-91.2 0l-80 80-36.8-36.8c-25.6-25.6-65.6-25.6-91.2 0l-134.4 134.4c-4.8 6.4-8 14.4-8 24 0 17.6 14.4 32 32 32 9.6 0 16-3.2 22.4-9.6l134.4-134.4 134.4 134.4c6.4 6.4 14.4 9.6 24 9.6 17.6 0 32-14.4 32-32 0-9.6-4.8-17.6-9.6-24l-52.8-52.8 80-80 180.8 180.8c6.4 4.8 12.8 8 20.8 8 17.6 0 32-14.4 32-32 0-8-3.2-16-8-20.8z" p-id="1305"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1572170050760" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5149" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M219.428571 658.285714q0-30.285714-21.428571-51.714285T146.285714 585.142857t-51.714285 21.428572T73.142857 658.285714t21.428572 51.714286T146.285714 731.428571t51.714286-21.428571T219.428571 658.285714z m109.714286-256q0-30.285714-21.428571-51.714285T256 329.142857t-51.714286 21.428572T182.857143 402.285714t21.428571 51.714286T256 475.428571t51.714286-21.428571T329.142857 402.285714z m244.571429 274.857143l57.714285-218.285714q3.428571-14.857143-4.285714-27.714286T605.142857 414.285714t-27.428571 3.714286-17.142857 22.571429l-57.714286 218.285714q-34.285714 2.857143-61.142857 24.857143t-36 56.285714q-11.428571 44 11.428571 83.428571t66.857143 50.857143 83.428571-11.428571 50.857143-66.857143q9.142857-34.285714-3.428571-66.857143t-41.142857-52z m377.142857-18.857143q0-30.285714-21.428572-51.714285T877.714286 585.142857t-51.714286 21.428572-21.428571 51.714285 21.428571 51.714286 51.714286 21.428571 51.714285-21.428571 21.428572-51.714286z m-365.714286-365.714285q0-30.285714-21.428571-51.714286T512 219.428571t-51.714286 21.428572T438.857143 292.571429t21.428571 51.714285T512 365.714286t51.714286-21.428572T585.142857 292.571429z m256 109.714285q0-30.285714-21.428571-51.714285T768 329.142857t-51.714286 21.428572T694.857143 402.285714t21.428571 51.714286T768 475.428571t51.714286-21.428571T841.142857 402.285714z m182.857143 256q0 149.142857-80.571429 276-10.857143 16.571429-30.857142 16.571429H111.428571q-20 0-30.857142-16.571429Q0 808 0 658.285714q0-104 40.571429-198.857143t109.142857-163.428571 163.428571-109.142857 198.857143-40.571429 198.857143 40.571429 163.428571 109.142857 109.142857 163.428571 40.571429 198.857143z" p-id="5150" fill="#bfbfbf"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="5670" t="1544682877339" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M449.98144 243.90144c-57.7536-10.624-121.00096-7.98208-167.65952 6.8352C195.92192 278.19008 146.95936 329.984 119.76192 406.528c-32.22528 90.69568-2.93376 190.62784 15.81056 248.63744 18.74432 58.0096 76.0576 144.97792 135.94112 155.8528 63.7696 11.58144 121.30816-9.89696 155.37664-50.7392 16.05632-19.24608 30.27456-46.75072 31.70816-74.46016 0.93184-18.0224-1.22368-31.64672-6.1696-45.14816-5.64224-15.40096-15.06816-29.6448-32.95232-46.57664-19.18976-18.17088-62.53056-34.09408-104.28416-28.29312-30.90432 4.29056-56.55552 15.67744-78.4384 32.47616-33.5872 25.78944-65.41312 82.65728-65.41312 82.65728s-1.152-33.25952 13.27104-66.69824c7.22944-16.75264 24.3968-44.8512 41.26208-58.99776 17.12128-14.36672 37.85728-31.5392 72.1664-40.32 38.8864-9.95328 78.27456-6.97856 93.62944-3.5584 46.39744 10.32192 82.24256 34.64704 111.616 65.32096 59.52512 62.16704 82.46784 161.536 51.75296 246.03648-10.28096 28.28288-38.85056 61.06112-69.30944 85.10976-18.32448 14.464-57.00096 35.4048-103.18848 45.14304-30.592 6.4512-64.48128 6.2976-97.93536 2.2272-83.95776-10.22464-159.50336-86.53312-195.11296-147.77856-35.61472-61.24544-89.0368-186.05568-75.80672-320.60928 6.30272-64.12288 28.08832-132.46976 62.88384-182.4768C114.77504 249.3952 167.1168 215.15264 194.80576 199.13216c52.89984-30.60736 132.29056-43.6224 185.19552-40.832 33.62816 1.77664 86.85568 12.66688 137.27232 38.08768 39.66464 20.00384 75.78112 55.2448 92.17536 95.37536 9.81504 24.02304 6.71232 47.49312 6.67136 47.49312-0.04608 0.11776 1.13664-2.54464-31.44192-34.05312C557.16352 278.59456 507.72992 254.53056 449.98144 243.90144L449.98144 243.90144z" p-id="5671"/><path d="M559.07328 155.18208c0 0 37.8624-68.5056 72.36096-88.50944 34.49856-20.00384 61.65504-7.67488 79.4624 5.92896 17.81248 13.60384 26.82368 30.91456 22.912 55.552-3.91168 24.63744-43.14624 71.76192-66.3296 106.22976C631.34208 207.98464 595.21024 181.58592 559.07328 155.18208L559.07328 155.18208z" p-id="5672"/><path d="M408.26368 117.03296c0 0 6.8096-62.65344 26.31168-88.04864 19.50208-25.3952 43.65824-24.41216 61.23008-19.64544 17.57184 4.76672 29.73184 14.97088 34.53952 34.56 4.80256 19.59424-9.73312 66.688-16.24576 99.46112C478.81728 134.58944 443.54048 125.80864 408.26368 117.03296L408.26368 117.03296z" p-id="5673"/><path d="M294.76352 125.06112c0 0-11.65824-45.60896-4.6592-68.34176 7.00416-22.72768 24.17664-28.16 37.7344-29.27104 13.55776-1.11104 24.7552 2.94912 33.2544 15.45728 8.4992 12.50304 10.66496 49.17248 14.69952 73.78432C348.78464 119.48032 321.77152 122.27072 294.76352 125.06112L294.76352 125.06112z" p-id="5674"/><path d="M185.1648 163.1232c0 0-20.89984-34.67264-20.79232-55.01952 0.10752-20.352 12.86656-28.93312 23.7312-33.1008 10.85952-4.16768 21.06368-3.51744 31.1552 4.72064 10.09152 8.23296 20.97152 37.8368 30.39744 57.08288C228.15744 145.57696 206.66368 154.35264 185.1648 163.1232L185.1648 163.1232z" p-id="5675"/><path d="M97.408 227.1488c0 0-26.7776-25.18528-31.72352-42.9312-4.9408-17.74592 4.0448-28.26752 12.47232-34.49344 8.4224-6.22592 17.46944-8.09472 28.29312-3.34336 10.81856 4.75648 27.6224 27.9296 40.59136 42.42944C130.49856 201.58976 113.95584 214.36928 97.408 227.1488L97.408 227.1488z" p-id="5676"/><path d="M762.08128 537.68704c38.15424 4.096 77.2864 17.52064 103.7568 35.49696 49.01376 33.28 70.04672 74.84416 72.832 127.85664 3.3024 62.81216-33.65376 119.96672-56.192 152.81664-22.53824 32.84992-74.63424 76.66176-114.17088 72.33024-42.10176-4.61824-74.15296-28.78464-87.88992-60.71296-6.4768-15.04768-10.2656-34.92864-6.00576-52.55168 2.76992-11.46368 6.656-19.59936 12.27264-27.136 6.40512-8.59648 14.96064-15.76448 29.312-23.04512 15.40608-7.81312 45.52192-9.71776 70.59456 1.6896 18.56 8.44288 32.512 20.352 43.08992 34.94912 16.24064 22.41024 25.59488 63.95904 25.59488 63.95904s6.91712-20.61824 4.10112-44.25216c-1.408-11.83744-6.93248-32.64-14.86336-44.64128-8.05376-12.1856-17.8432-26.80832-37.70368-38.69696-22.50752-13.47072-47.73376-18.944-57.98912-19.6608-30.98624-2.17088-57.97376 6.39488-82.07872 20.14208-48.86016 27.86304-81.73056 85.84192-78.22336 144.49664 1.17248 19.63008 12.96896 45.48608 27.57632 66.21696 8.78592 12.47232 29.11744 32.78848 56.23808 47.49312 17.96608 9.73824 39.22432 15.9488 60.93824 19.62496 54.49728 9.22624 116.02944-24.51456 149.74464-56.25344 33.71008-31.73888 90.41408-99.98336 107.17696-186.73664 7.9872-41.33888 7.06048-88.2176-5.42208-126.02368-13.71136-41.5232-40.12544-72.71424-54.49216-87.90528-27.4432-29.02016-74.752-51.95776-108.416-60.0576-21.39136-5.1456-56.77056-8.23296-93.08672-1.69472-28.57472 5.14048-57.75872 20.50048-75.49952 42.58816-10.61888 13.22496-13.04576 28.50304-13.04576 28.50304s-0.21504-1.79712 26.06592-15.47264C688.47616 539.46368 723.92192 533.59104 762.08128 537.68704L762.08128 537.68704z" p-id="5677"/><path d="M710.25664 461.7984c0 0-10.96704-49.96608-28.85632-68.92032-17.88416-18.94912-37.19168-16.2816-50.88256-11.07968-13.69088 5.20704-22.55872 14.37184-24.69376 30.53568-2.13504 16.16384 13.66528 52.98688 21.77536 78.8992C655.15008 481.42336 682.70592 471.60832 710.25664 461.7984L710.25664 461.7984z" p-id="5678"/><path d="M811.83232 465.97632c0 0 7.3984-40.51456-0.09216-60.0576-7.49056-19.54304-22.80448-23.424-34.70336-23.71072-11.89888-0.28672-21.41696 3.84512-28.07296 15.22176-6.656 11.37664-6.31808 43.58656-8.34048 65.3312C764.3648 463.83616 788.10112 464.90624 811.83232 465.97632L811.83232 465.97632z" p-id="5679"/><path d="M881.43872 492.1344c0 0 15.7952-26.40384 15.6416-41.94304-0.1536-15.54432-9.9072-22.144-18.19136-25.36448-8.28928-3.22048-16.05632-2.75968-23.71072 3.49184-7.64928 6.25152-15.83616 28.82048-22.94272 43.48416C848.64 478.58176 865.03936 485.35552 881.43872 492.1344L881.43872 492.1344z" p-id="5680"/><path d="M943.01184 536.38144c0 0 19.54816-17.82784 23.2704-30.59712 3.72224-12.76928-2.67264-20.52096-8.704-25.15456-6.03136-4.6336-12.53888-6.12352-20.39296-2.8416-7.85408 3.2768-20.18304 19.79904-29.6704 30.10048C919.3472 517.38624 931.17952 526.88384 943.01184 536.38144L943.01184 536.38144z" p-id="5681"/><path d="M986.07104 592.82432c0 0 21.46304-10.79296 27.86304-20.992 6.4-10.19392 2.72896-18.46272-1.39264-23.93088-4.11648-5.46816-9.43616-8.32512-17.1008-7.36256-7.66464 0.96256-22.50752 12.35456-33.3312 19.0208C970.09664 570.65472 978.08384 581.73952 986.07104 592.82432L986.07104 592.82432z" p-id="5682"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="749" t="1547192680027" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M377.596784 791.989528s-39.199482 22.799699 27.799633 30.399598c81.198926 9.199878 122.598379 7.999894 211.997197-8.999881 0 0 23.599688 14.799804 56.399254 27.599635C473.395518 926.787745 220.198866 835.988946 377.596784 791.989528m-24.399677-112.198517s-43.799421 32.399572 23.199693 39.399479c86.598855 8.999881 155.197948 9.599873 273.596382-13.199825 0 0 16.399783 16.599781 42.199442 25.599661-242.596792 70.999061-512.593222 5.799923-338.995517-51.799315m206.397271-190.197485c49.399347 56.799249-12.999828 107.998572-12.999828 107.998572s125.398342-64.799143 67.799103-145.798072c-53.799289-75.599-94.998744-113.198503 128.198305-242.596792 0.199997 0-350.395367 87.598842-182.99758 280.396292m265.196493 385.194907s28.999617 23.799685-31.799579 42.399439c-115.798469 34.999537-481.593632 45.599397-583.192289 1.399982-36.599516-15.799791 31.999577-37.999498 53.599291-42.599437 22.399704-4.799937 35.399532-3.999947 35.399532-3.999947-40.599463-28.599622-262.596528 56.199257-112.798508 80.398937 408.3946 66.399122 744.790152-29.799606 638.791553-77.598974M396.396536 563.592548s-186.197538 44.199416-65.999128 60.199204c50.799328 6.79991 151.99799 5.199931 246.196745-2.599966 76.998982-6.399915 154.397958-20.39973 154.397958-20.39973s-27.19964 11.599847-46.799381 24.999669c-188.997501 49.799342-553.992675 26.599648-448.994063-24.19968 88.998823-42.799434 161.197869-37.999498 161.197869-37.999497m333.995583 186.597532c192.197459-99.79868 103.198635-195.797411 41.199456-182.797582-15.199799 3.199958-21.999709 5.999921-21.999709 5.99992s5.599926-8.799884 16.399783-12.599833c122.598379-43.199429 216.997131 127.198318-39.599477 194.597427 0-0.199997 2.99996-2.799963 3.999947-5.199932M614.393653 0s106.398593 106.398593-100.998664 269.99643c-166.197802 131.198265-37.999498 206.197274 0 291.596144-96.998717-87.598842-168.197776-164.597824-120.398408-236.396874C463.195652 220.197088 657.393085 168.997765 614.393653 0m-198.997369 1020.786502c184.397562 11.799844 467.593817-6.599913 474.19373-93.798759 0 0-12.799831 32.999564-152.397985 59.399214-157.397919 29.599609-351.595351 26.199654-466.59383 7.199905 0-0.199997 23.599688 19.399743 144.798085 27.19964" p-id="750"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.585 12.087c0 6.616 3.974 11.98 8.877 11.98 4.902 0 8.877-5.364 8.877-11.98 0-6.616-3.975-11.98-8.877-11.98-4.903 0-8.877 5.364-8.877 11.98zM125.86.107H35.613c-1.268 0-2.114 1.426-2.114 2.852v18.255c0 1.712 1.057 2.853 2.114 2.853h90.247c1.268 0 2.114-1.426 2.114-2.853V2.96c0-1.711-1.057-2.852-2.114-2.852zM.106 62.86c0 6.615 3.974 11.979 8.876 11.979 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zM124.17 50.88H33.921c-1.268 0-2.114 1.425-2.114 2.851v18.256c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852V53.73c0-1.426-.846-2.852-2.114-2.852zM.106 115.913c0 6.616 3.974 11.98 8.876 11.98 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zm124.064-11.98H33.921c-1.268 0-2.114 1.426-2.114 2.853v18.255c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852v-18.255c0-1.427-.846-2.853-2.114-2.853z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="3654" t="1545700954682" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M868.935 1008.63l-713.868 0c-40.657 0-72.291-31.631-72.291-67.773l0-496.994c0-36.144 31.631-67.773 72.291-67.773l713.868 0c40.657 0 72.291 31.631 72.291 67.773l0 496.994c0 36.144-31.631 67.773-72.291 67.773l0 0 0 0 0 0zM512 543.259c-58.732 0-108.432 45.187-108.432 99.402 0 36.144 22.586 67.773 54.218 85.849l0 94.887c0 27.108 22.586 49.696 54.218 49.696s54.218-22.586 54.218-49.696l0-94.887c31.631-18.071 54.218-49.696 54.218-85.849 0-54.218-49.696-99.402-108.432-99.402l0 0 0 0zM512 114.031c-117.471 0-216.867 90.356-216.867 198.797l-108.432 0c0-162.655 144.582-298.202 320.79-298.202s320.79 135.546 320.79 298.202l-108.432 0c9.041-112.951-90.356-198.797-207.836-198.797l0 0 0 0zM512 114.031z" p-id="3655"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="4026" t="1547360510388" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M787.692308 275.692308V78.769231c0-43.323077-35.446154-78.769231-78.769231-78.769231H78.769231C35.446154 0 0 35.446154 0 78.769231v866.461538c0 43.323077 35.446154 78.769231 78.769231 78.769231h354.461538v-39.384615H78.769231c-19.692308 0-39.384615-15.753846-39.384616-39.384616V78.769231c0-23.630769 15.753846-39.384615 39.384616-39.384616h630.153846c19.692308 0 39.384615 15.753846 39.384615 39.384616v196.923077h39.384616z" p-id="4027"/><path fill="#bfbfbf" d="M137.846154 472.615385h196.923077c11.815385 0 19.692308 7.876923 19.692307 19.692307s-7.876923 19.692308-19.692307 19.692308h-196.923077c-11.815385 0-19.692308-7.876923-19.692308-19.692308s7.876923-19.692308 19.692308-19.692307zM137.846154 669.538462h118.153846c11.815385 0 19.692308 7.876923 19.692308 19.692307s-7.876923 19.692308-19.692308 19.692308h-118.153846c-11.815385 0-19.692308-7.876923-19.692308-19.692308s7.876923-19.692308 19.692308-19.692307zM137.846154 275.692308h354.461538c11.815385 0 19.692308 7.876923 19.692308 19.692307s-7.876923 19.692308-19.692308 19.692308h-354.461538c-11.815385 0-19.692308-7.876923-19.692308-19.692308s7.876923-19.692308 19.692308-19.692307zM716.8 433.230769c7.876923 0 19.692308 0 31.507692 3.938462 0 3.938462 3.938462 11.815385 3.938462 19.692307 3.938462 23.630769 11.815385 63.015385 55.138461 78.769231 7.876923 3.938462 15.753846 3.938462 19.692308 3.938462 31.507692 0 51.2-19.692308 66.953846-35.446154l11.815385-11.815385c43.323077 19.692308 66.953846 51.2 70.892308 98.461539-3.938462 3.938462-11.815385 7.876923-19.692308 7.876923-15.753846 7.876923-51.2 27.569231-51.2 70.892308s39.384615 63.015385 63.015384 74.830769c3.938462 3.938462 11.815385 7.876923 19.692308 7.876923-3.938462 47.261538-27.569231 74.830769-74.830769 98.461538l-11.815385-11.815384c-15.753846-15.753846-35.446154-31.507692-66.953846-31.507693-7.876923 0-15.753846 0-23.630769 3.938462-43.323077 11.815385-51.2 51.2-55.138462 74.830769 0 3.938462-3.938462 11.815385-3.938461 15.753846-11.815385 0-19.692308 3.938462-31.507692 3.938462-35.446154 0-63.015385-11.815385-86.646154-39.384616 0-3.938462 3.938462-11.815385 7.876923-15.753846 11.815385-23.630769 31.507692-59.076923 3.938461-94.523077-7.876923-11.815385-27.569231-27.569231-63.015384-27.56923-11.815385 0-19.692308 0-31.507693 3.938461-7.876923 0-15.753846 3.938462-19.692307 3.938462-23.630769-43.323077-23.630769-78.769231 0-122.092308 3.938462 0 11.815385 0 19.692307 3.938462 7.876923 0 19.692308 3.938462 31.507693 3.938461 31.507692 0 51.2-15.753846 63.015384-27.569231 27.569231-35.446154 7.876923-74.830769-3.938461-94.523077-3.938462-3.938462-7.876923-11.815385-7.876923-15.753846 23.630769-39.384615 51.2-51.2 82.707692-51.2m0-39.384615c-47.261538 0-82.707692 19.692308-118.153846 55.138461-27.569231 31.507692 35.446154 78.769231 11.815384 110.276923-7.876923 11.815385-19.692308 11.815385-31.507692 11.815385-15.753846 0-35.446154-3.938462-51.2-3.938461-11.815385 0-23.630769 3.938462-31.507692 15.753846-31.507692 55.138462-31.507692 110.276923 0 169.353846 7.876923 11.815385 19.692308 15.753846 31.507692 15.753846 15.753846 0 35.446154-3.938462 51.2-3.938462 11.815385 0 23.630769 3.938462 31.507692 11.815385 23.630769 35.446154-43.323077 78.769231-11.815384 110.276923 35.446154 39.384615 74.830769 55.138462 122.092308 55.138462 11.815385 0 27.569231 0 43.323076-3.938462 43.323077-7.876923 19.692308-78.769231 59.076924-94.523077h7.876923c31.507692 0 51.2 47.261538 78.76923 47.261539 3.938462 0 7.876923 0 11.815385-3.938462 63.015385-27.569231 94.523077-70.892308 102.4-133.907692 3.938462-43.323077-78.769231-43.323077-78.769231-82.707692 0-43.323077 82.707692-39.384615 78.769231-82.707693-7.876923-63.015385-39.384615-110.276923-102.4-133.907692-3.938462 0-7.876923-3.938462-11.815385-3.938462-31.507692 0-51.2 51.2-82.707692 51.2h-7.876923c-39.384615-11.815385-15.753846-86.646154-59.076923-98.461538-15.753846-7.876923-27.569231-7.876923-43.323077-7.876923z" p-id="4028"/><path fill="#bfbfbf" d="M748.307692 590.769231c43.323077 0 78.769231 35.446154 78.769231 78.769231s-35.446154 78.769231-78.769231 78.76923-78.769231-35.446154-78.76923-78.76923 35.446154-78.769231 78.76923-78.769231m0-39.384616c-66.953846 0-118.153846 51.2-118.153846 118.153847s51.2 118.153846 118.153846 118.153846 118.153846-51.2 118.153846-118.153846-51.2-118.153846-118.153846-118.153847z" p-id="4029"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1575115830633" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2251" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M409.002667 469.333333L300.8 361.130667 361.130667 300.8l211.2 211.2-211.2 211.2-60.330667-60.330667L409.002667 554.666667H128v-85.333334h281.002667zM469.333333 128h341.333334c46.933333 0 85.333333 38.4 85.333333 85.333333v597.333334c0 46.933333-38.4 85.333333-85.333333 85.333333h-341.333334v-85.333333h341.333334V213.333333h-341.333334V128z" fill="#cdcdcd" p-id="2252"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="983" t="1552025141027" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M895.318 192H128.682C93.008 192 64 220.968 64 256.616v510.698C64 802.986 93.008 832 128.682 832h766.636C930.992 832 960 802.986 960 767.312V256.616C960 220.968 930.992 192 895.318 192zM568.046 704h-112.096v-192l-84.08 107.756L287.826 512v192H175.738V320h112.088l84.044 135.96 84.08-135.96h112.096v384z m167.314 0l-139.27-192h84v-192h112.086v192h84.054l-140.906 192h0.036z" p-id="984"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2559" t="1545037285158" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M96 128h832v192H96zM96 416h832v192H96zM96 704h832v192H96z" p-id="2560"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574572606408" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1870" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M870.4 793.6H153.6C68.9024 793.6 0 724.6976 0 640V153.6C0 68.9024 68.9024 0 153.6 0h716.8c84.6976 0 153.6 68.9024 153.6 153.6v486.4c0 84.6976-68.9024 153.6-153.6 153.6zM153.6 51.2c-56.4608 0-102.4 45.9392-102.4 102.4v486.4c0 56.4608 45.9392 102.4 102.4 102.4h716.8c56.4608 0 102.4-45.9392 102.4-102.4V153.6c0-56.4608-45.9392-102.4-102.4-102.4H153.6zM793.6 1024H230.4a25.6 25.6 0 0 1 0-51.2h563.2a25.6 25.6 0 0 1 0 51.2zM633.6 908.8h-243.2a25.6 25.6 0 0 1 0-51.2h243.2a25.6 25.6 0 0 1 0 51.2z" fill="#bfbfbf" p-id="1871"></path><path d="M349.0688 404.3008A25.664 25.664 0 0 1 330.9568 396.8l-54.2976-54.3104a25.6 25.6 0 0 1 36.2112-36.2112l44.2624 44.2752 88.2688-20.3648 20.3648-88.256-44.2752-44.2752a25.6 25.6 0 1 1 36.1984-36.1984L512 215.7824c6.2464 6.2336 8.8192 15.2576 6.848 23.8592l-27.1616 117.6704a25.6 25.6 0 0 1-19.1872 19.1872l-117.6832 27.1488a25.7792 25.7792 0 0 1-5.7472 0.6528zM584.4096 639.6288a25.4976 25.4976 0 0 1-18.0992-7.5008L512 577.8304a25.5616 25.5616 0 0 1-6.848-23.8464l27.1488-117.6832a25.6256 25.6256 0 0 1 19.2-19.1872l117.6704-27.1488a25.536 25.536 0 0 1 23.8464 6.848l54.2976 54.3104a25.6 25.6 0 0 1-36.1984 36.2112l-44.2752-44.2752-88.256 20.3648-20.3648 88.256 44.2752 44.2752a25.6 25.6 0 0 1-18.0864 43.6736z" fill="#bfbfbf" p-id="1872"></path><path d="M557.248 467.648a25.4976 25.4976 0 0 1-18.0992-7.5008l-90.496-90.496a25.6 25.6 0 1 1 36.1984-36.1984l90.496 90.496a25.6 25.6 0 0 1-18.0992 43.6992z" fill="#bfbfbf" p-id="1873"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="4695" t="1543827393750" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"/><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"/><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"/></svg>
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="3833" t="1561614515233" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M807.049 391.258c0.946-9.62 1.45-19.37 1.45-29.239 0-163.7-132.706-296.406-296.406-296.406S215.687 198.318 215.687 362.02c0 9.802 0.498 19.486 1.432 29.043-43.925 18.95-74.675 62.638-74.675 113.516v330.363c0 68.25 55.328 123.58 123.58 123.58h491.672c68.25 0 123.578-55.328 123.578-123.58V504.578c0-50.704-30.54-94.267-74.225-113.32zM510.917 165.905c109.134 0 197.604 88.47 197.604 197.603 0 5.895-0.275 11.726-0.782 17.49H314.094a200.097 200.097 0 0 1-0.782-17.49c0.002-109.132 88.472-197.603 197.605-197.603z" p-id="3834"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M104.185 95.254c8.161 7.574 13.145 17.441 13.145 28.28 0 1.508-.098 2.998-.285 4.466h-10.784c.238-1.465.403-2.948.403-4.465 0-8.983-4.36-17.115-11.419-23.216C86 104.66 75.355 107.162 64 107.162c-11.344 0-21.98-2.495-31.22-6.83-7.064 6.099-11.444 14.218-11.444 23.203 0 1.517.165 3 .403 4.465H10.955a35.444 35.444 0 0 1-.285-4.465c0-10.838 4.974-20.713 13.127-28.291C9.294 85.42.003 70.417.003 53.58.003 23.99 28.656.001 64 .001s63.997 23.988 63.997 53.58c0 16.842-9.299 31.85-23.812 41.673zM64 36.867c-29.454 0-53.33-10.077-53.33 15.342 0 25.418 23.876 46.023 53.33 46.023 29.454 0 53.33-20.605 53.33-46.023 0-25.419-23.876-15.342-53.33-15.342zm24.888 25.644c-3.927 0-7.111-2.665-7.111-5.953 0-3.288 3.184-5.954 7.11-5.954 3.928 0 7.111 2.666 7.111 5.954s-3.183 5.953-7.11 5.953zm-3.556 16.372c0 4.11-9.55 7.442-21.332 7.442-11.781 0-21.332-3.332-21.332-7.442 0-1.06.656-2.064 1.8-2.976 3.295 2.626 10.79 4.465 19.532 4.465 8.743 0 16.237-1.84 19.531-4.465 1.145.912 1.801 1.916 1.801 2.976zm-46.22-16.372c-3.927 0-7.11-2.665-7.11-5.953 0-3.288 3.183-5.954 7.11-5.954 3.927 0 7.111 2.666 7.111 5.954s-3.184 5.953-7.11 5.953z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></g></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="3778" t="1543477660371" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path d="M418.496 705.6 383.296 705.6l0-53.248 0 0C384 484.48 257.28 503.808 255.872 503.808L255.488 503.744c-133.504 0-125.632 146.112-125.632 148.608l0 53.248L91.84 705.6C76.032 705.6 64 716.864 64 731.52l0 201.152c0 14.72 12.032 25.856 27.84 25.856l326.656 0c15.808 0 28.096-11.136 28.096-25.856L446.592 731.52C446.592 716.864 434.24 705.6 418.496 705.6zM175.936 652.352c0-0.448-4.928-100.8 77.376-100.8 7.296 0.256 78.144-4.032 78.144 100.8l0 53.248-155.52 0L175.936 652.352zM960 889.024c0 61.12-77.824 69.504-77.824 69.504L635.776 958.528l-80.64 0c0 0-41.088 0.96-41.088-39.168l-1.344-196.928C514.304 533.376 464 464.768 348.8 427.264c-33.344-24.96-47.168-31.296-47.168-78.528 0-47.232 38.848-47.232 38.848-47.232s0 1.344 13.824-76.416c13.44-75.456 87.232-155.968 208.32-160.64L562.624 64c1.792 0 3.456 0.192 5.248 0.192C569.856 64.192 571.776 64 573.824 64l0 0.512c105.28 5.312 181.056 85.44 194.432 160.576 13.888 77.76 13.888 76.416 13.888 76.416s38.848 0 38.848 47.232c0 47.232-13.824 63.936-47.168 88.896C740.48 462.656 750.4 516.48 698.88 563.968c-27.52 25.344-47.232 44.48-47.232 80.64 0 36.032 19.456 44.352 38.848 55.488s150.016 47.232 211.136 88.96S960 852.928 960 889.024z" p-id="3779"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1989" t="1554009861477" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M756.081 1012.218 291.154 1012.218c-68.473 0-123.982-55.975-123.982-125.054L167.172 136.836c0-69.078 55.512-125.054 123.982-125.054l464.928 0c68.474 0 123.984 55.975 123.984 125.054l0 750.328C880.066 956.242 824.555 1012.218 756.081 1012.218L756.081 1012.218zM818.062 136.836c0-34.516-27.751-62.526-61.98-62.526L291.154 74.31c-34.228 0-61.991 28.01-61.991 62.526l0 31.268 588.899 0L818.062 136.836 818.062 136.836zM818.062 230.63 229.163 230.63l0 499.242 588.899 0L818.062 230.63 818.062 230.63zM818.062 792.398 229.163 792.398l0 94.766c0 34.54 27.765 62.526 61.991 62.526l464.928 0c34.229 0 61.98-27.986 61.98-62.526L818.062 792.398 818.062 792.398zM523.623 918.429c-25.668 0-46.482-20.993-46.482-46.896 0-25.903 20.816-46.895 46.482-46.895 25.664 0 46.477 20.993 46.477 46.895S549.287 918.429 523.623 918.429L523.623 918.429z" p-id="1990"/><path fill="#8a8a8a" d="M756.081 1017.218 291.154 1017.218c-71.121 0-128.982-58.342-128.982-130.054L162.172 136.836c0-71.712 57.861-130.054 128.982-130.054l464.928 0c71.122 0 128.984 58.342 128.984 130.054l0 750.328C885.066 958.876 827.204 1017.218 756.081 1017.218zM291.154 16.783c-65.607 0-118.982 53.856-118.982 120.054l0 750.328c0 66.198 53.375 120.054 118.982 120.054l464.927 0c65.608 0 118.985-53.855 118.985-120.054L875.066 136.836c0-66.198-53.376-120.054-118.984-120.054L291.154 16.782zM756.082 954.69 291.154 954.69c-36.939 0-66.991-30.292-66.991-67.526l0-99.766 598.899 0 0.001 99.766C823.063 924.398 793.016 954.69 756.082 954.69zM234.163 797.398l0 89.766c0 31.72 25.566 57.526 56.991 57.526l464.928 0c31.419 0 56.98-25.807 56.98-57.526l0-89.766L234.163 797.398zM523.623 923.429c-28.387 0-51.482-23.28-51.482-51.896s23.096-51.895 51.482-51.895c28.385 0 51.477 23.279 51.477 51.895S552.008 923.429 523.623 923.429zM523.623 829.639c-22.873 0-41.482 18.794-41.482 41.895 0 23.102 18.609 41.896 41.482 41.896 22.871 0 41.477-18.794 41.477-41.896C565.1 848.433 546.494 829.639 523.623 829.639zM823.062 734.872 224.163 734.872 224.163 225.63l598.899 0L823.062 734.872zM234.163 724.872l578.899 0L813.062 235.63 234.163 235.63 234.163 724.872zM823.062 173.104 224.163 173.104l0-36.268c0-37.234 30.052-67.526 66.991-67.526l464.927 0c36.934 0 66.98 30.292 66.98 67.526L823.061 173.104zM234.163 163.104l578.899 0 0-26.268c0-31.72-25.562-57.526-56.98-57.526L291.154 79.31c-31.425 0-56.991 25.806-56.991 57.526L234.163 163.104z" p-id="1991"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1276" t="1546225865881" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M892.928 499.712c-45.056-45.056-116.736-45.056-161.792 0-36.864 36.864-43.008 92.16-20.48 135.168L602.112 743.424c-51.2 51.2-137.216 45.056-194.56-10.24s-61.44-143.36-10.24-194.56L522.24 413.696l-45.056-45.056-124.928 124.928c-75.776 75.776-71.68 204.8 10.24 286.72 43.008 43.008 98.304 65.536 153.6 65.536 49.152 0 96.256-18.432 133.12-53.248L757.76 684.032c16.384 8.192 34.816 14.336 55.296 14.336 28.672 0 59.392-10.24 79.872-32.768 45.056-49.152 45.056-122.88 0-165.888zM847.872 614.4c-18.432 18.432-51.2 18.432-69.632 0-18.432-18.432-18.432-51.2 0-69.632 10.24-10.24 22.528-14.336 34.816-14.336s24.576 4.096 34.816 14.336c18.432 18.432 18.432 51.2 0 69.632z" p-id="1277"/><path fill="#bfbfbf" d="M296.96 372.736l108.544-108.544c51.2-51.2 137.216-45.056 194.56 10.24 26.624 26.624 43.008 63.488 45.056 100.352 2.048 36.864-10.24 69.632-34.816 94.208L485.376 593.92l45.056 45.056L655.36 514.048c36.864-36.864 55.296-88.064 53.248-141.312-2.048-53.248-24.576-104.448-63.488-143.36-81.92-81.92-208.896-86.016-286.72-10.24L249.856 327.68c-43.008-22.528-98.304-16.384-135.168 20.48-45.056 45.056-45.056 116.736 0 161.792 22.528 22.528 51.2 32.768 79.872 32.768s59.392-10.24 79.872-32.768c38.912-38.912 45.056-94.208 22.528-137.216z m-65.536 90.112c-18.432 18.432-51.2 18.432-69.632 0-18.432-18.432-18.432-51.2 0-69.632 10.24-10.24 22.528-14.336 34.816-14.336 12.288 0 24.576 4.096 34.816 14.336 18.432 18.432 18.432 49.152 0 69.632z" p-id="1278"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1547360607827" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6287" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M900.512 388.751V287.87L512.708 63.967 124.904 287.87v240.093c-23.42 6.798-40.924 27.562-40.924 53.144s17.504 46.346 40.924 53.144v101.424l387.804 223.862 387.804-223.862V494.761c23.13-6.979 40.345-27.642 40.345-52.984 0-25.376-17.215-46.047-40.345-53.026zM114.87 581.107c0-13.999 11.356-25.355 25.355-25.355 13.958 0 25.314 11.356 25.314 25.355s-11.356 25.355-25.314 25.355c-13.999 0-25.355-11.356-25.355-25.355z m754.753 136.729L512.708 923.858 155.793 717.836v-83.638c23.265-6.883 40.635-27.603 40.635-53.09 0-25.487-17.37-46.207-40.635-53.09V305.709L512.708 99.646l356.915 206.063v82.856c-23.585 6.692-41.254 27.513-41.254 53.212 0 25.665 17.669 46.48 41.254 53.17v222.889z m14.99-250.745c-13.999 0-25.355-11.356-25.355-25.314 0-13.999 11.356-25.355 25.355-25.355s25.355 11.356 25.355 25.355c0 13.958-11.356 25.314-25.355 25.314z m-184.631-117.32c-5.947-2.271-12.678-0.661-17.014 4.047L511.841 542.29 338.029 357.576c-4.295-4.666-10.985-6.153-16.931-3.758a15.428 15.428 0 0 0-9.746 14.329v315.867h30.889V407.089l158.574 168.526a15.404 15.404 0 0 0 11.232 4.873c3.469-0.289 8.507-1.9 11.439-5.079l155.477-171.251v279.817h30.889v-319.79a15.492 15.492 0 0 0-9.87-14.414z" fill="#bfbfbf" p-id="6288"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2866" t="1543477186503" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path d="M609.093716 908.277839c0-106.125689 85.803749-191.929438 191.929438-191.929438 38.950386 0 75.07828 11.854465 105.561191 31.611907l0-3.38699c0-60.965821-71.69129-136.044101-128.705623-145.076075l76.771775-127.012128-178.945976 0c32.176406-46.288864 53.627343-107.254686 53.627343-174.994487 0-5.080485 0-10.725469 0-15.805954 69.433297-12.418964 114.593164-32.176406 114.593164-51.933848 0-21.450937-50.240353-41.208379-127.012128-53.627343C712.961411 158.059537 661.592062 11.289967 600.62624 11.289967c-27.095921 0-48.546858 14.112459-62.659316 35.563396 0 10.725469-10.725469 21.450937-24.837927 21.450937-12.418964 0-23.144432-9.031974-24.837927-19.757442C475.307607 27.660419 452.163175 11.289967 425.631753 11.289967 366.359427 11.289967 311.038589 145.640573 298.619625 179.510474 232.573319 191.929438 189.671444 209.993385 189.671444 231.444322c0 21.450937 44.595369 39.514884 114.593164 51.933848 0 5.080485 0 10.725469 0 15.805954 0 67.739802 19.757442 128.705623 53.627343 174.994487L178.945976 474.178611l76.771775 125.318633c-57.014333 0-132.092613 78.46527-132.092613 146.76957l1.693495 205.477398c0 33.869901 26.531422 60.965821 60.965821 60.965821l453.85667 0C620.383682 982.227122 609.093716 946.663727 609.093716 908.277839zM468.533627 962.46968l-28.789416 0-53.627343-395.148842 112.335171 98.222712L468.533627 962.46968zM498.45204 351.117971c-7.338479 62.659316-39.514884 76.771775-57.014333 76.771775-42.901874 0-55.320838-57.014333-57.014333-76.771775l-14.112459 0L370.310915 338.699008l0-12.418964 107.254686 0L477.565601 338.699008l71.69129 0 0-12.418964 107.254686 0L656.511577 338.699008l3.38699 0 0 12.418964-14.112459 0c0 0-9.031974 76.771775-57.014333 76.771775C539.660419 427.889746 530.628445 351.117971 530.628445 351.117971L498.45204 351.117971zM562.804851 962.46968l-30.482911-296.92613 112.335171-99.916207-53.627343 396.842337L562.804851 962.46968z" p-id="2867"/><path d="M918.438809 907.148842c0 1.693495-1.128997 2.822492-2.822492 2.822492l-97.658214 0 0 97.658214c0 1.693495-1.128997 2.822492-2.822492 2.822492l-43.466373 0c-1.693495 0-2.822492-1.128997-2.822492-2.822492l0-97.658214-97.658214 0c-1.693495 0-2.822492-1.128997-2.822492-2.822492L668.366042 863.68247c0-1.693495 1.128997-2.822492 2.822492-2.822492l97.658214 0 0-97.658214c0-1.693495 1.128997-2.822492 2.822492-2.822492l43.466373 0c1.693495 0 2.822492 1.128997 2.822492 2.822492l0 97.658214 97.658214 0c1.693495 0 2.822492 1.128997 2.822492 2.822492L918.438809 907.148842z" p-id="2868"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574576420335" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9530" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M915.94 827.89h-91.42a12.5 12.5 0 0 1 0-25h78.92v-535h-78.92a12.5 12.5 0 1 1 0-25h91.42a12.5 12.5 0 0 1 12.5 12.5v560a12.5 12.5 0 0 1-12.5 12.5z" fill="#cdcdcd" p-id="9531"></path><path d="M915.94 547.91h-91.42a12.5 12.5 0 0 1 0-25h91.42a12.5 12.5 0 0 1 0 25z" fill="#cdcdcd" p-id="9532"></path><path d="M774.6 231.51a90.63 90.63 0 0 1-90.37 90.37H215.94a90.62 90.62 0 0 1-81.18-50.81 88.92 88.92 0 0 0 15.4 1.33h468.29a90.26 90.26 0 0 0 81.18-129.92c42.44 7.35 74.97 44.57 74.97 89.03z" fill="#cdcdcd" p-id="9533"></path><path d="M684.23 334.38H215.94a102.23 102.23 0 0 1-55.12-16.11 103.66 103.66 0 0 1-37.28-41.69 12.5 12.5 0 0 1 13.38-17.82 76.71 76.71 0 0 0 13.24 1.14h468.29a77.76 77.76 0 0 0 70-111.91 12.5 12.5 0 0 1 13.35-17.83 103.18 103.18 0 0 1 85.3 101.35 103.13 103.13 0 0 1-102.87 102.87zM159.46 284.9a78.31 78.31 0 0 0 56.48 24.48h468.29a77.75 77.75 0 0 0 59.28-128.2 78.73 78.73 0 0 0-24.14-19.08 103.06 103.06 0 0 1-100.92 122.8z" fill="#cdcdcd" p-id="9534"></path><path d="M618.45 284.91H150.16a102.86 102.86 0 1 1 0-205.73h468.29a102.86 102.86 0 0 1 0 205.73zM150.16 104.18a77.86 77.86 0 1 0 0 155.73h468.29a77.86 77.86 0 0 0 0-155.73z" fill="#cdcdcd" p-id="9535"></path><path d="M176.2 235.43A53.39 53.39 0 1 1 229.59 182a53.45 53.45 0 0 1-53.39 53.43z m0-81.78A28.39 28.39 0 1 0 204.59 182a28.43 28.43 0 0 0-28.39-28.35zM551.12 194.54H423a12.5 12.5 0 0 1 0-25h128.12a12.5 12.5 0 0 1 0 25z" fill="#cdcdcd" p-id="9536"></path><path d="M774.6 534.74a90.63 90.63 0 0 1-90.37 90.37H215.94a90.62 90.62 0 0 1-81.18-50.81 88.92 88.92 0 0 0 15.4 1.33h468.29a90.26 90.26 0 0 0 81.18-129.92c42.44 7.35 74.97 44.57 74.97 89.03z" fill="#cdcdcd" p-id="9537"></path><path d="M684.23 637.61H215.94a102.23 102.23 0 0 1-55.12-16.11 103.66 103.66 0 0 1-37.28-41.69A12.5 12.5 0 0 1 136.92 562a76.71 76.71 0 0 0 13.24 1.14h468.29a77.76 77.76 0 0 0 70-111.91 12.5 12.5 0 0 1 13.35-17.83 103.18 103.18 0 0 1 85.3 101.34 103.13 103.13 0 0 1-102.87 102.87z m-524.77-49.48a78.31 78.31 0 0 0 56.48 24.48h468.29a77.75 77.75 0 0 0 59.28-128.2 78.73 78.73 0 0 0-24.14-19.08 103.06 103.06 0 0 1-100.92 122.8z" fill="#cdcdcd" p-id="9538"></path><path d="M618.45 588.13H150.16a102.86 102.86 0 1 1 0-205.73h468.29a102.86 102.86 0 0 1 0 205.73zM150.16 407.4a77.86 77.86 0 1 0 0 155.73h468.29a77.86 77.86 0 0 0 0-155.73z" fill="#cdcdcd" p-id="9539"></path><path d="M176.2 538.66a53.39 53.39 0 1 1 53.39-53.39 53.45 53.45 0 0 1-53.39 53.39z m0-81.78a28.39 28.39 0 1 0 28.39 28.39 28.43 28.43 0 0 0-28.39-28.39zM551.12 497.77H423a12.5 12.5 0 0 1 0-25h128.12a12.5 12.5 0 0 1 0 25z" fill="#cdcdcd" p-id="9540"></path><path d="M774.6 869.33a90.63 90.63 0 0 1-90.37 90.37H215.94a90.62 90.62 0 0 1-81.18-50.81 88.92 88.92 0 0 0 15.4 1.33h468.29a90.26 90.26 0 0 0 81.18-129.92c42.44 7.35 74.97 44.57 74.97 89.03z" fill="#cdcdcd" p-id="9541"></path><path d="M684.23 972.2H215.94a102.23 102.23 0 0 1-55.12-16.11 103.66 103.66 0 0 1-37.28-41.69 12.5 12.5 0 0 1 13.38-17.82 76.71 76.71 0 0 0 13.24 1.14h468.29a77.76 77.76 0 0 0 70-111.91A12.5 12.5 0 0 1 701.76 768a102.73 102.73 0 0 1 55.14 174 102.16 102.16 0 0 1-72.67 30.2z m-524.77-49.48a78.31 78.31 0 0 0 56.48 24.48h468.29A77.75 77.75 0 0 0 743.51 819a78.71 78.71 0 0 0-24.14-19.08 103.06 103.06 0 0 1-100.92 122.8z" fill="#cdcdcd" p-id="9542"></path><path d="M618.45 922.72H150.16a102.86 102.86 0 1 1 0-205.73h468.29a102.86 102.86 0 0 1 0 205.73zM150.16 742a77.86 77.86 0 1 0 0 155.73h468.29a77.86 77.86 0 1 0 0-155.73z" fill="#cdcdcd" p-id="9543"></path><path d="M176.2 873.25a53.39 53.39 0 1 1 53.39-53.39 53.45 53.45 0 0 1-53.39 53.39z m0-81.78a28.39 28.39 0 1 0 28.39 28.39 28.42 28.42 0 0 0-28.39-28.4zM551.12 832.35H423a12.5 12.5 0 0 1 0-25h128.12a12.5 12.5 0 0 1 0 25z" fill="#cdcdcd" p-id="9544"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 0 1 3.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 0 1-3.889 2.843 10.582 10.582 0 0 1-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 0 1-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 0 1-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 0 1 3.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 0 1 3.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 0 1-3.89 2.843 11 11 0 0 1-4.732 1.066 10.58 10.58 0 0 1-4.667-1.066 12.478 12.478 0 0 1-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 0 1-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 0 1 3.824-2.772 11.212 11.212 0 0 1 4.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 0 1 .778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 0 1-2.788 8.743 1236.373 1236.373 0 0 0-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 0 1-1.88-4.478 44.128 44.128 0 0 1-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 0 1-2.14-2.558 10.416 10.416 0 0 1-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 0 1 1.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M31.652 93.206h33.401c1.44 2.418 3.077 4.663 4.93 6.692h-38.33v-6.692zm0-10.586h28.914a44.8 44.8 0 0 1-1.264-6.688h-27.65v6.688zm0-17.27H59.39c.288-2.286.714-4.532 1.34-6.687H31.65v6.687h.003zm53.913 44.84v5.85c0 2.798-2.095 5.075-4.667 5.075h-70.07c-2.576 0-4.663-2.277-4.663-5.075V31.26l23.22-20.96v22.25H17.16v6.688h18.39V6.688h45.348c2.576 0 4.667 2.277 4.667 5.066v20.009c1.987-.675 4.053-1.128 6.17-1.445v-18.56C91.738 5.28 86.874 0 80.902 0H31.15L0 28.118v87.917c0 6.48 4.859 11.759 10.832 11.759h70.07c5.974 0 10.837-5.27 10.837-11.759v-4.41c-2.117-.312-4.183-.765-6.17-1.435h-.004zM23.279 58.667h-7.96v6.688h7.96v-6.688zm-7.956 41.23h7.96v-6.691h-7.96v6.692zm7.956-23.96h-7.96v6.687h7.96v-6.688zm89.718-15.042l-4.896-4.07-12.447 17.613-11.19-9.305-3.762 5.311 16.091 13.38 16.204-22.929zM128 70.978c0-18.632-13.97-33.782-31.147-33.782-17.168 0-31.135 15.155-31.135 33.782 0 18.628 13.97 33.783 31.135 33.783 17.172 0 31.143-15.15 31.143-33.783H128zm-6.17 0c0 14.933-11.203 27.1-24.981 27.1-13.77 0-24.987-12.158-24.987-27.1 0-14.941 11.195-27.099 24.987-27.099 13.778 0 24.982 12.158 24.982 27.1z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574679314986" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2155" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M546.26 538.81v257.63c0 18.38-14.9 33.29-33.29 33.29-18.01 0.37-32.91-13.93-33.29-31.94-0.01-0.45-0.01-0.9 0-1.35V538.81l-233-134.47c-15.73-9.25-21.07-29.45-11.98-45.27 8.82-15.71 28.7-21.3 44.41-12.48 0.29 0.16 0.57 0.33 0.86 0.5l233 133.14 231-133.14c15.43-9.29 35.48-4.31 44.77 11.13 0.17 0.28 0.34 0.57 0.5 0.86 9.09 15.82 3.74 36.01-11.98 45.27l-231 133.14v1.32z" fill="#515151" p-id="2156"></path><path d="M512 1006.74c-18.08 0-36.14-4.65-52.27-13.96L121.77 797.65c-32.24-18.61-52.27-53.3-52.27-90.53V316.87c0-37.23 20.03-71.92 52.27-90.53L459.73 31.22c32.24-18.61 72.29-18.61 104.53 0l337.97 195.13c32.24 18.61 52.27 53.3 52.27 90.53v390.25c0 37.23-20.03 71.92-52.27 90.53L564.27 992.78c-16.12 9.3-34.2 13.96-52.27 13.96z m0-940.77c-9.65 0-19.29 2.48-27.9 7.45L146.13 268.55c-17.21 9.94-27.9 28.45-27.9 48.32v390.25c0 19.87 10.69 38.39 27.9 48.32L484.1 950.57c17.21 9.94 38.59 9.93 55.8 0l337.97-195.13c17.21-9.94 27.9-28.45 27.9-48.32V316.87c0-19.87-10.69-38.39-27.9-48.32L539.9 73.43a55.757 55.757 0 0 0-27.9-7.46z" fill="#515151" p-id="2157"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="6696" t="1544607518503" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M896 716.8c-44.8 0-76.8 25.6-96 57.6L460.8 614.4l147.2-243.2c25.6 12.8 51.2 25.6 83.2 25.6 83.2 0 153.6-70.4 153.6-153.6s-70.4-153.6-153.6-153.6-153.6 64-153.6 147.2c0 19.2 6.4 38.4 12.8 57.6L300.8 403.2c-25.6-44.8-76.8-76.8-134.4-76.8C83.2 326.4 12.8 396.8 12.8 480c0 83.2 70.4 153.6 153.6 153.6 12.8 0 19.2 0 32-6.4l57.6 128c-12.8 12.8-19.2 25.6-19.2 44.8 0 38.4 32 64 64 64 38.4 0 64-32 64-64 0-12.8-6.4-19.2-6.4-32l70.4-121.6 352 166.4v6.4c0 64 51.2 115.2 115.2 115.2s115.2-51.2 115.2-115.2-51.2-102.4-115.2-102.4z m-204.8-582.4c57.6 0 108.8 44.8 108.8 108.8s-44.8 108.8-108.8 108.8-108.8-57.6-108.8-115.2 51.2-102.4 108.8-102.4zM569.6 332.8l-153.6 256-108.8-44.8c6.4-19.2 12.8-44.8 12.8-64V448l249.6-115.2zM64 480c0-57.6 44.8-108.8 108.8-108.8s108.8 44.8 108.8 108.8c0 57.6-44.8 108.8-108.8 108.8S64 537.6 64 480z m262.4 262.4c-6.4 0-12.8-6.4-19.2-6.4h-6.4l-57.6-128c12.8-6.4 25.6-12.8 38.4-25.6l108.8 51.2-64 108.8z m569.6 147.2c-38.4 0-64-32-64-64 0-38.4 32-64 64-64s64 32 64 64c0 38.4-25.6 64-64 64z" p-id="6697"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="3388" t="1553861152984" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M306.689473 349.006765l408.370805-0.3776c22.463618 0 40.678468-18.213827 40.678468-40.678468 0-22.463618-18.21485-40.677445-40.678468-40.677445l-408.370805 0.3776c-22.465664 0-40.679491 18.203594-40.679491 40.678468C266.009981 330.792938 284.223808 349.006765 306.689473 349.006765z" p-id="3389"/><path fill="#bfbfbf" d="M306.689473 511.719614l408.370805-0.3776c22.463618 0 40.678468-18.213827 40.678468-40.677445 0-22.463618-18.21485-40.677445-40.678468-40.677445l-408.370805 0.3776c-22.465664 0-40.679491 18.202571-40.679491 40.677445C266.009981 493.505787 284.223808 511.719614 306.689473 511.719614z" p-id="3390"/><path fill="#bfbfbf" d="M877.157095 624.190175l-0.337691-482.536968c-1.905396-43.141566-37.141922-77.602426-80.721463-77.751828l-573.366649 0.327458c-43.220361 1.917676-77.763085 37.302581-77.763085 80.999802l0 43.220361-0.335644 0 0.277316 645.76761 0.058328 0 0 43.220361c0 44.928259 36.428677 81.354889 81.357959 81.354889l40.677445 0 0 0.229221 120.088052-0.217964c22.465664 0 40.679491-18.213827 40.679491-40.677445 0-22.465664-18.213827-40.679491-40.679491-40.679491l-104.773252 0.189312c-39.148625-3.783163-53.509704-17.011442-56.051597-58.137095l-0.278339-623.272269 0.23843 32.224935c-0.972141-69.328995 14.937201-81.882915 80.204693-82.804914l-9.414417-0.099261 419.393859-0.23843c60.562331 1.429559 78.277808 12.652158 79.114872 70.174246l0 3.744277c0 2.99931 0 5.998621-0.081864 9.245572l0.081864-4.080945 0.25685 367.961466-160.131047 0.217964 0 0.11768-43.220361 0c-44.928259 0-81.354889 36.418444-81.354889 81.357959l0 40.677445-0.278339 0 0.257873 195.506742c-2.62171 12.93152 1.033539 26.874067 11.062963 36.903491 4.031826 4.030803 8.721639 7.021927 13.68672 9.006118 4.90573 2.087545 10.287297 3.25002 15.927761 3.25002 14.20349 0 26.656103-7.290033 33.924647-18.304901l271.679609-272.363178c3.01773-1.826602 5.838985-3.932566 8.303106-6.463202 7.704472-7.398504 12.532431-17.749246 12.532431-29.278837C878.171192 629.950366 877.793592 627.031896 877.157095 624.190175zM592.290139 819.2395l-0.078795-67.76129c0.556679-61.910025 14.896268-77.462233 82.549088-77.462233l-82.071204 0.545422-0.497327 73.223698-0.099261-73.79982 145.274688-0.197498L592.290139 819.2395z" p-id="3391"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574576079728" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6611" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M981.917808 277.742466H813.589041V122.038356c0-19.638356-15.430137-35.068493-35.068493-35.068493H252.493151c-19.638356 0-35.068493 15.430137-35.068493 35.068493v155.70411H42.082192c-19.638356 0-35.068493 15.430137-35.068493 35.068493V911.780822c0 19.638356 15.430137 35.068493 35.068493 35.068493h939.835616c19.638356 0 35.068493-15.430137 35.068493-35.068493V312.810959c0-19.638356-15.430137-35.068493-35.068493-35.068493z m-694.356164-120.635617H743.452055v120.635617H287.561644v-120.635617zM252.493151 347.879452h694.356164v133.260274h-74.345205v-50.49863c0-19.638356-15.430137-35.068493-35.068494-35.068493h-117.830137c-19.638356 0-35.068493 15.430137-35.068493 35.068493v50.49863h-336.657534v-50.49863c0-19.638356-15.430137-35.068493-35.068493-35.068493h-117.830137c-19.638356 0-35.068493 15.430137-35.068493 35.068493v50.49863H77.150685v-133.260274H252.493151z m549.873972 119.232877v140.273972h-47.69315v-140.273972h47.69315z m-526.027397 0v140.273972h-47.693151v-140.273972h47.693151zM77.150685 876.712329V551.276712h81.358904v89.775343c0 19.638356 15.430137 35.068493 35.068493 35.068493h117.830137c19.638356 0 35.068493-15.430137 35.068493-35.068493v-89.775343h336.657535v89.775343c0 19.638356 15.430137 35.068493 35.068493 35.068493h117.830137c19.638356 0 35.068493-15.430137 35.068493-35.068493v-89.775343h74.345205v324.032877H77.150685z" p-id="6612" fill="#bfbfbf"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.719" height="200" class="icon" p-id="10233" t="1543827724451" version="1.1" viewBox="0 0 1084 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path fill="#bfbfbf" d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234"/></svg>
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="1588584086939" class="icon" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1156" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css"></style></defs><path d="M1080.096 434.5c-4.216-23.731-26.924-47.945-50.596-53.185l-17.648-4.096a175.94 175.94 0 0 1-101.613-80.832 177.807 177.807 0 0 1-18.732-129.802l5.541-16.685c7.108-23.13-2.108-54.992-20.6-70.833 0 0-16.624-14.095-63.244-41.2-46.8-26.984-66.858-34.513-66.858-34.513-22.768-8.373-54.632-0.362-71.256 17.407l-12.287 13.251a173.47 173.47 0 0 1-120.466 48.066 174.133 174.133 0 0 1-121.008-48.487l-11.745-12.83C393.14 2.992 361.096-4.899 338.268 3.354c0 0-20.359 7.529-67.1 34.513-46.8 27.346-63.244 41.44-63.244 41.44-18.431 15.661-27.647 47.223-20.54 70.593l5.06 16.866a178.048 178.048 0 0 1-18.672 129.62A174.916 174.916 0 0 1 71.496 377.46l-17.045 3.855c-23.31 5.421-46.26 29.334-50.596 53.186 0 0-3.855 21.382-3.855 75.712s3.855 75.713 3.855 75.713C8.07 609.9 30.779 633.872 54.45 639.112l16.624 3.855A174.254 174.254 0 0 1 173.47 724.28c23.31 40.838 28.911 87.338 18.732 129.802l-4.818 16.444c-7.108 23.129 2.108 54.992 20.6 70.833 0 0 16.623 14.095 63.244 41.2 46.8 27.105 66.918 34.513 66.918 34.513 22.708 8.373 54.632 0.362 71.256-17.407l11.625-12.589a175.097 175.097 0 0 1 242.257 0.12l11.624 12.65c16.384 17.708 48.428 25.599 71.256 17.347 0 0 20.359-7.53 67.16-34.514 46.74-27.105 63.124-41.2 63.124-41.2 18.491-15.6 27.707-47.463 20.6-70.833l-5.06-17.106A176.723 176.723 0 0 1 910.66 724.4a176.06 176.06 0 0 1 102.396-81.314l16.684-3.855c23.31-5.42 46.26-29.333 50.596-53.185 0 0 3.855-21.383 3.855-75.713-0.241-54.33-4.096-75.833-4.096-75.833z m-537.82 293.335c-119.26 0-216.175-97.336-216.175-217.622a216.658 216.658 0 0 1 216.236-217.32c119.2 0 216.115 97.276 216.115 217.561-0.24 120.045-96.974 217.32-216.175 217.32z" p-id="1157"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.921.052H49.08c-1.865 0-3.198 1.599-3.198 3.464v6.661c0 1.865 1.6 3.464 3.198 3.464h29.84c1.865 0 3.198-1.599 3.198-3.464V3.516C82.385 1.65 80.786.052 78.92.052zm45.563 0H94.642c-1.865 0-3.464 1.599-3.464 3.464v6.661c0 1.865 1.599 3.464 3.464 3.464h29.842c1.865-.266 3.464-1.599 3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464zm0 22.382H40.02c-1.866 0-3.464-1.599-3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464H3.516C1.65.052.052 1.651.052 3.516V124.75c0 1.598 1.599 3.197 3.464 3.197h120.968c1.865 0 3.464-1.599 3.464-3.464V25.898c0-1.865-1.599-3.464-3.464-3.464z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M125.5 36.984L95.336 2.83C93.735 1.018 91.565 0 89.3 0c-2.263 0-4.433 1.018-6.033 2.83l-3.786 4.286c-1.6 1.812-3.77 2.83-6.032 2.831H54.553c-2.263 0-4.434-1.018-6.033-2.83L44.734 2.83C43.134 1.018 40.964 0 38.701 0c-2.263 0-4.434 1.018-6.034 2.83L2.5 36.984C.9 38.796 0 41.254 0 43.815c0 2.562.899 5.02 2.5 6.831L14.565 64.31c2.178 2.468 5.367 3.403 8.33 2.444 1.35-.435 2.709.592 2.709 2.18v49.407c0 5.313 3.84 9.66 8.532 9.66h59.726c4.693 0 8.532-4.347 8.532-9.66V68.934c0-1.59 1.36-2.616 2.71-2.181 2.962.96 6.15.024 8.329-2.444L125.5 50.646c1.6-1.811 2.499-4.269 2.499-6.83 0-2.563-.899-5.02-2.5-6.832z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2986" t="1546864403462" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M814.502 270.913l30.18-30.18 30.58 30.58 37.825-37.825-86.377-86.378-37.825 37.825 30.58 30.58-30.18 30.18c-66.856-61.301-154.087-100.642-250.374-106.858V66.548h-53.499v72.289c-214.046 13.792-383.407 191.652-383.407 409.199 0 226.533 183.624 410.157 410.157 410.157s410.157-183.624 410.157-410.157c-0.001-106.858-40.892-204.139-107.817-277.123zM529.994 598.26v74.606h-35.666V598.26c-20.724-7.385-35.666-26.958-35.666-50.225 0-23.267 14.942-42.842 35.666-50.225v-235.1h35.666v235.102c20.724 7.383 35.666 26.958 35.666 50.225 0 23.265-14.942 42.839-35.666 50.223z" p-id="2987"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.781" height="200" class="icon" p-id="1270" t="1545876582522" version="1.1" viewBox="0 0 1028 1024"><defs><style type="text/css"/></defs><path d="M20.746854 180.67456c-27.09504 27.136-21.31968 65.35168 0.26624 86.87616 0.86016 0.83968 87.49056 83.57888 209.3056 205.4144 3.13344 3.13344 7.22944 4.66944 11.32544 4.66944s8.192-1.57696 11.32544-4.66944c6.2464-6.2464 6.2464-16.384 0-22.6304-18.37056-18.37056-35.84-35.7376-52.40832-52.18304l50.85184-50.85184c6.2464-6.2464 6.2464-16.384 0-22.6304s-16.384-6.2464-22.6304 0l-50.97472 50.97472c-28.50816-28.16-53.63712-52.736-74.07616-72.66304l115.67104-115.67104c6.2464-6.2464 6.2464-16.384 0-22.6304s-16.384-6.2464-22.6304 0l-116.03968 116.03968c-23.552-22.81472-37.13024-35.77856-37.33504-36.00384-6.7584-6.7584-15.62624-25.76384 0-41.3696l160.01024-160.01024c6.18496-6.26688 22.6304-18.71872 40.67328-0.77824 0.79872 0.90112 81.59232 92.672 204.92288 216.00256 6.2464 6.2464 16.384 6.2464 22.6304 0s6.2464-16.384 0-22.6304c-122.55232-122.53184-202.752-213.6064-204.22656-215.26528-29.81888-29.75744-65.80224-20.82816-86.6304 0l-160.03072 160.01024zM699.392614 772.66944c-6.2464-6.2464-16.384-6.2464-22.6304 0l-50.91328 50.87232c-15.52384-15.70816-31.80544-32.1536-48.96768-49.31584-6.2464-6.2464-16.384-6.2464-22.6304 0s-6.2464 16.384 0 22.6304c118.53824 118.49728 201.3184 207.01184 202.50624 208.2816 10.97728 11.01824 26.0096 17.05984 42.33216 17.05984l0 0c16.7936 0 33.792-6.5536 44.31872-17.12128l159.92832-161.83296c20.13184-20.13184 21.27872-54.272 2.49856-73.1136l-214.58944-214.56896c-6.2464-6.2464-16.384-6.2464-22.6304 0s-6.2464 16.384 0 22.6304l214.58944 214.56896c6.41024 6.41024 5.18144 20.23424-2.51904 27.93472l-159.96928 161.83296c-3.6864 3.70688-12.04224 7.68-21.62688 7.68 0 0 0 0 0 0-5.50912 0-13.37344-1.35168-19.33312-7.33184-0.32768-0.34816-13.7216-14.66368-36.98688-39.0144l116.61312-116.61312c6.2464-6.2464 6.2464-16.384 0-22.6304s-16.384-6.2464-22.6304 0l-116.1216 116.1216c-19.968-20.76672-44.4416-46.01856-72.31488-74.42432l51.07712-51.07712c6.2464-6.20544 6.2464-16.32256 0-22.58944zM4.608614 1018.53184c3.072 3.13344 7.20896 4.79232 11.44832 4.79232 1.536 0 3.072-0.22528 4.56704-0.67584l320-95.3344c2.60096-0.77824 4.89472-2.1504 6.77888-4.05504l546.87744-550.56384c0.77824-0.77824 1.14688-1.76128 1.72032-2.62144l116.98176-117.00224c9.40032-9.44128 14.62272-21.95456 14.62272-35.28704 0-13.312-5.2224-25.82528-14.62272-35.20512l-167.5264-167.48544c-18.82112-18.82112-51.67104-18.82112-70.49216 0l-119.64416 119.64416c-0.77824 0.75776-1.18784 1.76128-1.76128 2.6624l-547.71712 547.7376c-1.80224 1.78176-3.13344 3.97312-3.91168 6.36928l-101.13024 310.8864c-1.86368 5.67296-0.4096 11.83744 3.74784 16.13824zM667.587174 168.67328l33.23904 33.23904-496.7424 498.0736-55.25504-12.53376 518.7584-518.77888zM224.072294 725.31968l499.42528-500.736 77.94688 77.94688-503.93088 497.47968-73.44128 0 0-74.69056zM860.365414 361.472l-519.80288 523.32544-18.47296-64.06144 501.98528-495.57504 36.29056 36.31104zM127.836774 715.48928l64.22528 14.58176 0 85.9136c0 8.82688 7.168 15.99488 15.99488 15.99488l83.968 0 20.29568 70.41024-271.60576 80.93696 87.1424-267.83744zM797.676134 37.72416c6.7584-6.7584 18.49344-6.7584 25.25184 0l167.5264 167.5264c3.35872 3.35872 5.2224 7.84384 5.2224 12.61568 0 4.73088-1.86368 9.25696-5.24288 12.63616l-107.86816 107.90912-192.73728-192.73728 107.86816-107.9296z" p-id="1271"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M44.8 0h79.543C126.78 0 128 1.422 128 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H44.8c-2.438 0-3.657-1.422-3.657-4.267V4.267C41.143 1.422 42.362 0 44.8 0zm22.857 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 80 64 78.578 64 75.733V52.267C64 49.422 65.219 48 67.657 48zm0 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 128 64 126.578 64 123.733v-23.466C64 97.422 65.219 96 67.657 96zM50.286 68.267c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V32h6.4c2.02 0 3.658-1.91 3.658-4.267V4.267C27.429 1.91 25.79 0 23.77 0H3.657C1.637 0 0 1.91 0 4.267v23.466C0 30.09 1.637 32 3.657 32h6.4v80c0 2.356 1.638 4.267 3.657 4.267h36.572c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V68.267h32.915z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1381" t="1545700997954" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M880 512H320V288c0-106.032 85.968-192 192-192s192 85.968 192 192a48 48 0 1 0 96 0c0-159.056-128.944-288-288-288S224 128.944 224 288v224H144a48 48 0 0 0-48 48v416a48 48 0 0 0 48 48h736a48 48 0 0 0 48-48V560a48 48 0 0 0-48-48zM560 786.688V880a48 48 0 1 1-96 0v-93.312c-28.576-16.624-48-47.248-48-82.688a96 96 0 1 1 192 0c0 35.44-19.424 66.064-48 82.688z" p-id="1382"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2702" t="1561614445035" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M914.285769 802.889143q0 68.534857-41.691429 108.251429t-110.884571 39.716571l-499.419429 0q-69.12 0-110.884571-39.716571t-41.691429-108.251429q0-30.281143 1.974857-59.172571t7.972571-62.317714 15.140571-62.025143 24.576-55.734857 35.401143-46.299429 48.859429-30.573714 63.707429-11.410286q5.12 0 23.990857 12.288t42.569143 27.428571 61.732571 27.428571 76.288 12.288 76.288-12.288 61.732571-27.428571 42.569143-27.428571 23.990857-12.288q34.889143 0 63.707429 11.410286t48.859429 30.573714 35.401143 46.299429 24.576 55.734857 15.140571 62.025143 7.972571 62.317714 1.974857 59.172571zM731.428626 292.571429q0 90.843429-64.292571 155.136t-155.136 64.292571-155.136-64.292571-64.292571-155.136 64.292571-155.136 155.136-64.292571 155.136 64.292571 64.292571 155.136z" p-id="2703"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="4263" t="1553934943780" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M512 256s-97.2-4.8-144-96c16.8 113.6 108 192 144 192s127.2-78.8 144-192c-46.8 91.2-144 96-144 96z" p-id="4264"/><path fill="#8a8a8a" d="M928 448C928 218.4 741.6 32 512 32S96 218.4 96 448c0 186.8 122.8 344.4 292.4 397.2-42 35.2-68.4 88-68.4 146.8h64c0-70.8 57.2-128 128-128s128 57.2 128 128h64c0-58.8-26.4-111.6-68.4-146.8 169.6-52.8 292.4-210.4 292.4-397.2z m-416 352c-194.4 0-352-157.6-352-352s157.6-352 352-352 352 157.6 352 352-157.6 352-352 352z" p-id="4265"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1777" t="1561614261684" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#8a8a8a" d="M963.801169 218.170758a72.850542 72.850542 0 0 0-57.513586-63.903984 1357.959666 1357.959666 0 0 1-185.960594-42.815669A690.80207 690.80207 0 0 1 554.17663 14.317049a76.045741 76.045741 0 0 0-88.826539 0 435.825173 435.825173 0 0 1-167.428438 96.495016 624.341926 624.341926 0 0 1-180.848276 44.732789 67.738223 67.738223 0 0 0-56.874546 63.903984S58.281712 379.847839 58.281712 521.075644c0 255.615937 301.626806 502.924356 452.440208 502.924356s406.42934-174.457877 447.32789-499.090117c10.224637-191.711953 3.195199-306.100085 3.195199-306.100085zM799.567929 415.63407l-315.046642 297.153527a42.815669 42.815669 0 0 1-52.401267 5.112318l-8.307518-7.029438L249.993665 530.022202a42.815669 42.815669 0 0 1 63.903984-58.791666L457.042574 621.404899l283.73369-268.396734a42.815669 42.815669 0 1 1 58.791665 63.903984" p-id="1778"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="4347" t="1544682770180" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M320 128c0-70.656-57.344-128-128-128S64 57.344 64 128s57.344 128 128 128S320 198.656 320 128zM544 192C596.992 192 640 148.992 640 96S596.992 0 544 0 448 43.008 448 96 491.008 192 544 192zM864 64C811.008 64 768 107.008 768 160S811.008 256 864 256 960 212.992 960 160 916.992 64 864 64zM537.088 257.216C460.032 256 372.352 267.904 302.144 330.112 231.872 392.32 240.64 547.712 339.968 589.504c99.392 41.728 121.28 113.92 94.656 203.776C408 883.136 453.44 960 567.04 960 762.88 960 832 776.512 832 570.944 832 347.392 744.128 260.352 537.088 257.216z" p-id="4348"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1572860137475" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1681" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M698.026667 597.333333C701.44 569.173333 704 541.013333 704 512 704 482.986667 701.44 454.826667 698.026667 426.666667L842.24 426.666667C849.066667 453.973333 853.333333 482.56 853.333333 512 853.333333 541.44 849.066667 570.026667 842.24 597.333333M622.506667 834.56C648.106667 787.2 667.733333 736 681.386667 682.666667L807.253333 682.666667C766.293333 753.066667 701.013333 807.68 622.506667 834.56M611.84 597.333333 412.16 597.333333C407.893333 569.173333 405.333333 541.013333 405.333333 512 405.333333 482.986667 407.893333 454.4 412.16 426.666667L611.84 426.666667C615.68 454.4 618.666667 482.986667 618.666667 512 618.666667 541.013333 615.68 569.173333 611.84 597.333333M512 851.626667C476.586667 800.426667 448 743.68 430.506667 682.666667L593.493333 682.666667C576 743.68 547.413333 800.426667 512 851.626667M341.333333 341.333333 216.746667 341.333333C257.28 270.506667 322.986667 215.893333 401.066667 189.44 375.466667 236.8 356.266667 288 341.333333 341.333333M216.746667 682.666667 341.333333 682.666667C356.266667 736 375.466667 787.2 401.066667 834.56 322.986667 807.68 257.28 753.066667 216.746667 682.666667M181.76 597.333333C174.933333 570.026667 170.666667 541.44 170.666667 512 170.666667 482.56 174.933333 453.973333 181.76 426.666667L325.973333 426.666667C322.56 454.826667 320 482.986667 320 512 320 541.013333 322.56 569.173333 325.973333 597.333333M512 171.946667C547.413333 223.146667 576 280.32 593.493333 341.333333L430.506667 341.333333C448 280.32 476.586667 223.146667 512 171.946667M807.253333 341.333333 681.386667 341.333333C667.733333 288 648.106667 236.8 622.506667 189.44 701.013333 216.32 766.293333 270.506667 807.253333 341.333333M512 85.333333C276.053333 85.333333 85.333333 277.333333 85.333333 512 85.333333 747.52 276.48 938.666667 512 938.666667 747.52 938.666667 938.666667 747.52 938.666667 512 938.666667 276.48 747.52 85.333333 512 85.333333Z" p-id="1682" fill="#515151"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2023" t="1564885435527" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M1024 659.2c0-147.2-147.2-262.4-307.2-262.4-172.8 0-307.2 115.2-307.2 262.4 0 147.2 134.4 262.4 307.2 262.4 38.4 0 70.4-6.4 108.8-19.2l102.4 51.2L896 870.4C972.8 812.8 1024 742.4 1024 659.2zM614.4 614.4c-19.2 0-38.4-19.2-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4 25.6 0 44.8 19.2 44.8 38.4C659.2 595.2 646.4 614.4 614.4 614.4zM812.8 614.4c-19.2 0-38.4-19.2-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4 25.6 0 44.8 19.2 44.8 38.4C864 595.2 844.8 614.4 812.8 614.4z" p-id="2024"/><path fill="#bfbfbf" d="M364.8 128C166.4 128 0 262.4 0 435.2c0 102.4 51.2 179.2 147.2 243.2l-38.4 108.8 128-64c44.8 6.4 83.2 19.2 128 19.2 12.8 0 25.6 0 32 0C390.4 716.8 384 691.2 384 665.6c0-160 134.4-288 307.2-288 12.8 0 25.6 0 32 0C697.6 236.8 537.6 128 364.8 128zM243.2 371.2C217.6 371.2 192 352 192 326.4c0-25.6 25.6-44.8 57.6-44.8s44.8 19.2 44.8 44.8C288 352 268.8 371.2 243.2 371.2zM499.2 371.2c-25.6 0-57.6-19.2-57.6-44.8 0-25.6 25.6-44.8 57.6-44.8 25.6 0 44.8 19.2 44.8 44.8C544 352 524.8 371.2 499.2 371.2z" p-id="2025"/></svg>
...\ No newline at end of file ...\ No newline at end of file
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="1574576020792" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5498" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M64 64h384v384H64V64z m0 512h384v384H64V576z m512 0h384v384H576V576z m192-128c106.039 0 192-85.961 192-192S874.039 64 768 64s-192 85.961-192 192 85.961 192 192 192z" p-id="5499" fill="#bfbfbf"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
1 # replace default config
2
3 # multipass: true
4 # full: true
5
6 plugins:
7
8 # - name
9 #
10 # or:
11 # - name: false
12 # - name: true
13 #
14 # or:
15 # - name:
16 # param1: 1
17 # param2: 2
18
19 - removeAttrs:
20 attrs:
21 - 'fill'
22 - 'fill-rule'
1 @import 'variables';
2
3 @mixin colorBtn($color) {
4 background: $color;
5
6 &:hover {
7 color: $color;
8
9 &:before,
10 &:after {
11 background: $color;
12 }
13 }
14 }
15
16 .blue-btn {
17 @include colorBtn($blue)
18 }
19
20 .light-blue-btn {
21 @include colorBtn($light-blue)
22 }
23
24 .red-btn {
25 @include colorBtn($red)
26 }
27
28 .pink-btn {
29 @include colorBtn($pink)
30 }
31
32 .green-btn {
33 @include colorBtn($green)
34 }
35
36 .tiffany-btn {
37 @include colorBtn($tiffany)
38 }
39
40 .yellow-btn {
41 @include colorBtn($yellow)
42 }
43
44 .pan-btn {
45 font-size: 14px;
46 color: #fff;
47 padding: 14px 36px;
48 border-radius: 8px;
49 border: none;
50 outline: none;
51 transition: 600ms ease all;
52 position: relative;
53 display: inline-block;
54
55 &:hover {
56 background: #fff;
57
58 &:before,
59 &:after {
60 width: 100%;
61 transition: 600ms ease all;
62 }
63 }
64
65 &:before,
66 &:after {
67 content: '';
68 position: absolute;
69 top: 0;
70 right: 0;
71 height: 2px;
72 width: 0;
73 transition: 400ms ease all;
74 }
75
76 &::after {
77 right: inherit;
78 top: inherit;
79 left: 0;
80 bottom: 0;
81 }
82 }
83
84 .custom-button {
85 display: inline-block;
86 line-height: 1;
87 white-space: nowrap;
88 cursor: pointer;
89 background: #fff;
90 color: #fff;
91 -webkit-appearance: none;
92 text-align: center;
93 box-sizing: border-box;
94 outline: 0;
95 margin: 0;
96 padding: 10px 15px;
97 font-size: 14px;
98 border-radius: 4px;
99 }
1 .head-container {
2 padding-bottom: 10px;
3 .filter-item {
4 display: inline-block;
5 vertical-align: middle;
6 margin: 0 3px 10px 0;
7 input {
8 height: 30.5px;
9 line-height: 30.5px;
10 }
11 }
12 .el-form-item-label {
13 margin: 0 3px 9px 0;
14 display: inline-block;
15 text-align: right;
16 vertical-align: middle;
17 font-size: 14px;
18 color: #606266;
19 line-height: 30.5px;
20 padding: 0 7px 0 7px;
21 }
22 .el-button+.el-button {
23 margin-left: 0 !important;
24 }
25 .el-select__caret.el-input__icon.el-icon-arrow-up{
26 line-height: 30.5px;
27 }
28 .date-item {
29 display: inline-block;
30 vertical-align: middle;
31 margin-bottom: 10px;
32 height: 30.5px !important;
33 width: 230px !important;
34 }
35 }
36 .el-avatar {
37 display: inline-block;
38 text-align: center;
39 background: #ccc;
40 color: #fff;
41 white-space: nowrap;
42 position: relative;
43 overflow: hidden;
44 vertical-align: middle;
45 width: 32px;
46 height: 32px;
47 line-height: 32px;
48 border-radius: 16px;
49 }
50
51 .logo-con{
52 height: 60px;
53 padding: 13px 0 0;
54 img{
55 height: 32px;
56 width: 135px;
57 display: block;
58 //margin: 0 auto;
59 }
60 }
61
62 #el-login-footer {
63 height: 40px;
64 line-height: 40px;
65 position: fixed;
66 bottom: 0;
67 width: 100%;
68 text-align: center;
69 color: #fff;
70 font-family: Arial, serif;
71 font-size: 12px;
72 letter-spacing: 1px;
73 }
74
75 #el-main-footer {
76 background: none repeat scroll 0 0 white;
77 border-top: 1px solid #e7eaec;
78 overflow: hidden;
79 padding: 10px 6px 0 6px;
80 height: 33px;
81 font-size: 0.7rem !important;
82 color: #7a8b9a;
83 letter-spacing: 0.8px;
84 font-family: Arial, sans-serif !important;
85 position: fixed;
86 bottom: 0;
87 z-index: 99;
88 width: 100%;
89 }
90 .eladmin-upload {
91 border: 1px dashed #c0ccda;
92 border-radius: 5px;
93 height: 45px;
94 line-height: 45px;
95 width: 368px;
96 }
97 .my-blockquote{
98 margin: 0 0 10px;
99 padding: 15px;
100 line-height: 22px;
101 border-left: 5px solid #00437B;
102 border-radius: 0 2px 2px 0;
103 background-color: #f2f2f2;
104 }
105 .my-code{
106 position: relative;
107 padding: 15px;
108 line-height: 20px;
109 border-left: 5px solid #ddd;
110 color: #333;
111 font-family: Courier New, serif;
112 font-size: 12px
113 }
114
115 .el-tabs{
116 margin-bottom: 25px;
117 }
1 // cover some element-ui styles
2
3 .el-breadcrumb__inner,
4 .el-breadcrumb__inner a {
5 font-weight: 400 !important;
6 }
7
8 .el-upload {
9 input[type="file"] {
10 display: none !important;
11 }
12 }
13
14 .el-upload__input {
15 display: none;
16 }
17
18 .cell {
19 .el-tag {
20 margin-right: 0;
21 }
22 }
23
24 .small-padding {
25 .cell {
26 padding-left: 5px;
27 padding-right: 5px;
28 }
29 }
30
31 .fixed-width {
32 .el-button--mini {
33 padding: 7px 10px;
34 width: 60px;
35 }
36 }
37
38 .status-col {
39 .cell {
40 padding: 0 10px;
41 text-align: center;
42
43 .el-tag {
44 margin-right: 0;
45 }
46 }
47 }
48
49 // to fixed https://github.com/ElemeFE/element/issues/2461
50 .el-dialog {
51 transform: none;
52 left: 0;
53 position: relative;
54 margin: 0 auto;
55 }
56
57 // refine element ui upload
58 .upload-container {
59 .el-upload {
60 width: 100%;
61
62 .el-upload-dragger {
63 width: 100%;
64 height: 200px;
65 }
66 }
67 }
68
69 // dropdown
70 .el-dropdown-menu {
71 a {
72 display: block
73 }
74 }
75
76 // fix date-picker ui bug in filter-item
77 .el-range-editor.el-input__inner {
78 display: inline-flex !important;
79 }
1 /**
2 * I think element-ui's default theme color is too light for long-term use.
3 * So I modified the default color and you can modify it to your liking.
4 **/
5
6 /* theme color */
7 $--color-primary: #1890ff;
8 $--color-success: #13ce66;
9 $--color-warning: #FFBA00;
10 $--color-danger: #ff4949;
11 // $--color-info: #1E1E1E;
12
13 $--button-font-weight: 400;
14
15 // $--color-text-regular: #1f2d3d;
16
17 $--border-color-light: #dfe4ed;
18 $--border-color-lighter: #e6ebf5;
19
20 $--table-border:1px solid#dfe6ec;
21
22 /* icon font path, required */
23 $--font-path: '~element-ui/lib/theme-chalk/fonts';
24
25 @import "../../../node_modules/element-ui/packages/theme-chalk/src/index";
26
27 // the :export directive is the magic sauce for webpack
28 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
29 :export {
30 theme: $--color-primary;
31 }
1 @import 'variables';
2 @import 'mixin';
3 @import 'transition';
4 @import 'element-ui';
5 @import 'sidebar';
6 @import 'btn';
7 @import 'eladmin';
8
9 body {
10 height: 100%;
11 -moz-osx-font-smoothing: grayscale;
12 -webkit-font-smoothing: antialiased;
13 text-rendering: optimizeLegibility;
14 font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
15 }
16
17 label {
18 font-weight: 700;
19 }
20
21 html {
22 height: 100%;
23 box-sizing: border-box;
24 }
25
26 #app {
27 height: 100%;
28 }
29
30 *,
31 *:before,
32 *:after {
33 box-sizing: inherit;
34 }
35
36 .no-padding {
37 padding: 0 !important;
38 }
39
40 .padding-content {
41 padding: 4px 0;
42 }
43
44 a:focus,
45 a:active {
46 outline: none;
47 }
48
49 a,
50 a:focus,
51 a:hover {
52 cursor: pointer;
53 color: inherit;
54 text-decoration: none;
55 }
56
57 div:focus {
58 outline: none;
59 }
60
61 .fr {
62 float: right;
63 }
64
65 .fl {
66 float: left;
67 }
68
69 .pr-5 {
70 padding-right: 5px;
71 }
72
73 .pl-5 {
74 padding-left: 5px;
75 }
76
77 .block {
78 display: block;
79 }
80
81 .pointer {
82 cursor: pointer;
83 }
84
85 .inlineBlock {
86 display: block;
87 }
88
89 .clearfix {
90 &:after {
91 visibility: hidden;
92 display: block;
93 font-size: 0;
94 content: " ";
95 clear: both;
96 height: 0;
97 }
98 }
99
100 aside {
101 background: #eef1f6;
102 padding: 8px 24px;
103 margin-bottom: 20px;
104 border-radius: 2px;
105 display: block;
106 line-height: 32px;
107 font-size: 16px;
108 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
109 color: #2c3e50;
110 -webkit-font-smoothing: antialiased;
111 -moz-osx-font-smoothing: grayscale;
112
113 a {
114 color: #337ab7;
115 cursor: pointer;
116
117 &:hover {
118 color: rgb(32, 160, 255);
119 }
120 }
121 }
122
123 //main-container全局样式
124 .app-container {
125 padding: 20px 20px 45px 20px;
126 }
127
128 .components-container {
129 margin: 30px 50px;
130 position: relative;
131 }
132
133 .pagination-container {
134 margin-top: 30px;
135 }
136
137 .text-center {
138 text-align: center
139 }
140
141 .sub-navbar {
142 height: 50px;
143 line-height: 50px;
144 position: relative;
145 width: 100%;
146 text-align: right;
147 padding-right: 20px;
148 transition: 600ms ease position;
149 background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
150
151 .subtitle {
152 font-size: 20px;
153 color: #fff;
154 }
155
156 &.draft {
157 background: #d0d0d0;
158 }
159
160 &.deleted {
161 background: #d0d0d0;
162 }
163 }
164
165 .link-type,
166 .link-type:focus {
167 color: #337ab7;
168 cursor: pointer;
169
170 &:hover {
171 color: rgb(32, 160, 255);
172 }
173 }
174
175 //refine vue-multiselect plugin
176 .multiselect {
177 line-height: 16px;
178 }
179
180 .multiselect--active {
181 z-index: 1000 !important;
182 }
1 @mixin clearfix {
2 &:after {
3 content: "";
4 display: table;
5 clear: both;
6 }
7 }
8
9 @mixin scrollBar {
10 &::-webkit-scrollbar-track-piece {
11 background: #d3dce6;
12 }
13
14 &::-webkit-scrollbar {
15 width: 6px;
16 }
17
18 &::-webkit-scrollbar-thumb {
19 background: #99a9bf;
20 border-radius: 20px;
21 }
22 }
23
24 @mixin relative {
25 position: relative;
26 width: 100%;
27 height: 100%;
28 }
29
30 @mixin pct($pct) {
31 width: #{$pct};
32 position: relative;
33 margin: 0 auto;
34 }
35
36 @mixin triangle($width, $height, $color, $direction) {
37 $width: $width/2;
38 $color-border-style: $height solid $color;
39 $transparent-border-style: $width solid transparent;
40 height: 0;
41 width: 0;
42
43 @if $direction==up {
44 border-bottom: $color-border-style;
45 border-left: $transparent-border-style;
46 border-right: $transparent-border-style;
47 }
48
49 @else if $direction==right {
50 border-left: $color-border-style;
51 border-top: $transparent-border-style;
52 border-bottom: $transparent-border-style;
53 }
54
55 @else if $direction==down {
56 border-top: $color-border-style;
57 border-left: $transparent-border-style;
58 border-right: $transparent-border-style;
59 }
60
61 @else if $direction==left {
62 border-right: $color-border-style;
63 border-top: $transparent-border-style;
64 border-bottom: $transparent-border-style;
65 }
66 }
1 #app {
2
3 .main-container {
4 min-height: 100%;
5 transition: margin-left .28s;
6 margin-left: $sideBarWidth;
7 position: relative;
8 }
9
10 .sidebar-container {
11 transition: width 0.28s;
12 width: $sideBarWidth !important;
13 background-color: $menuBg;
14 height: 100%;
15 position: fixed;
16 font-size: 0;
17 top: 0;
18 bottom: 0;
19 left: 0;
20 z-index: 1001;
21 overflow: hidden;
22
23 // reset element-ui css
24 .horizontal-collapse-transition {
25 transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
26 }
27
28 .scrollbar-wrapper {
29 overflow-x: hidden !important;
30 }
31
32 .el-scrollbar__bar.is-vertical {
33 right: 0;
34 }
35
36 .el-scrollbar {
37 height: 100%;
38 }
39
40 &.has-logo {
41 .el-scrollbar {
42 height: calc(100% - 50px);
43 }
44 }
45
46 .is-horizontal {
47 display: none;
48 }
49
50 a {
51 display: inline-block;
52 width: 100%;
53 overflow: hidden;
54 }
55
56 .svg-icon {
57 margin-right: 16px;
58 }
59
60 .el-menu {
61 border: none;
62 height: 100%;
63 width: 100% !important;
64 }
65
66 // menu hover
67 .submenu-title-noDropdown,
68 .el-submenu__title {
69 &:hover {
70 background-color: $menuHover !important;
71 }
72 }
73
74 .is-active>.el-submenu__title {
75 color: $subMenuActiveText !important;
76 }
77
78 & .nest-menu .el-submenu>.el-submenu__title,
79 & .el-submenu .el-menu-item {
80 min-width: $sideBarWidth !important;
81 background-color: $subMenuBg !important;
82
83 &:hover {
84 background-color: $subMenuHover !important;
85 }
86 }
87 }
88
89 .hideSidebar {
90 .sidebar-container {
91 width: 54px !important;
92 }
93
94 .main-container {
95 margin-left: 54px;
96 }
97
98 .submenu-title-noDropdown {
99 padding: 0 !important;
100 position: relative;
101
102 .el-tooltip {
103 padding: 0 !important;
104
105 .svg-icon {
106 margin-left: 20px;
107 }
108 }
109 }
110
111 .el-submenu {
112 overflow: hidden;
113
114 &>.el-submenu__title {
115 padding: 0 !important;
116
117 .svg-icon {
118 margin-left: 20px;
119 }
120
121 .el-submenu__icon-arrow {
122 display: none;
123 }
124 }
125 }
126
127 .el-menu--collapse {
128 .el-submenu {
129 &>.el-submenu__title {
130 &>span {
131 height: 0;
132 width: 0;
133 overflow: hidden;
134 visibility: hidden;
135 display: inline-block;
136 }
137 }
138 }
139 }
140 }
141
142 .el-menu--collapse .el-menu .el-submenu {
143 min-width: $sideBarWidth !important;
144 }
145
146 // mobile responsive
147 .mobile {
148 .main-container {
149 margin-left: 0;
150 }
151
152 .sidebar-container {
153 transition: transform .28s;
154 width: $sideBarWidth !important;
155 }
156
157 &.hideSidebar {
158 .sidebar-container {
159 pointer-events: none;
160 transition-duration: 0.3s;
161 transform: translate3d(-$sideBarWidth, 0, 0);
162 }
163 }
164 }
165
166 .withoutAnimation {
167
168 .main-container,
169 .sidebar-container {
170 transition: none;
171 }
172 }
173 }
174
175 // when menu collapsed
176 .el-menu--vertical {
177 &>.el-menu {
178 .svg-icon {
179 margin-right: 16px;
180 }
181 }
182
183 .nest-menu .el-submenu>.el-submenu__title,
184 .el-menu-item {
185 &:hover {
186 // you can use $subMenuHover
187 background-color: $menuHover !important;
188 }
189 }
190
191 // the scroll bar appears when the subMenu is too long
192 >.el-menu--popup {
193 max-height: 100vh;
194 overflow-y: auto;
195
196 &::-webkit-scrollbar-track-piece {
197 background: #d3dce6;
198 }
199
200 &::-webkit-scrollbar {
201 width: 6px;
202 }
203
204 &::-webkit-scrollbar-thumb {
205 background: #99a9bf;
206 border-radius: 20px;
207 }
208 }
209 }
1 // global transition css
2
3 /* fade */
4 .fade-enter-active,
5 .fade-leave-active {
6 transition: opacity 0.28s;
7 }
8
9 .fade-enter,
10 .fade-leave-active {
11 opacity: 0;
12 }
13
14 /* fade-transform */
15 .fade-transform-leave-active,
16 .fade-transform-enter-active {
17 transition: all .5s;
18 }
19
20 .fade-transform-enter {
21 opacity: 0;
22 transform: translateX(-30px);
23 }
24
25 .fade-transform-leave-to {
26 opacity: 0;
27 transform: translateX(30px);
28 }
29
30 /* breadcrumb transition */
31 .breadcrumb-enter-active,
32 .breadcrumb-leave-active {
33 transition: all .5s;
34 }
35
36 .breadcrumb-enter,
37 .breadcrumb-leave-active {
38 opacity: 0;
39 transform: translateX(20px);
40 }
41
42 .breadcrumb-move {
43 transition: all .5s;
44 }
45
46 .breadcrumb-leave-active {
47 position: absolute;
48 }
1 // base color
2 $blue:#324157;
3 $light-blue:#3A71A8;
4 $red:#C03639;
5 $pink: #E65D6E;
6 $green: #30B08F;
7 $tiffany: #4AB7BD;
8 $yellow:#FEC171;
9 $panGreen: #30B08F;
10
11 // sidebar
12 $menuText:#bfcbd9;
13 $menuActiveText:#409EFF;
14 $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
15
16 $menuBg:#304156;
17 $menuHover:#263445;
18
19 $subMenuBg:#1f2d3d;
20 $subMenuHover:#001528;
21
22 $sideBarWidth: 205px;
23
24 // the :export directive is the magic sauce for webpack
25 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
26 :export {
27 menuText: $menuText;
28 menuActiveText: $menuActiveText;
29 subMenuActiveText: $subMenuActiveText;
30 menuBg: $menuBg;
31 menuHover: $menuHover;
32 subMenuBg: $subMenuBg;
33 subMenuHover: $subMenuHover;
34 sideBarWidth: $sideBarWidth;
35 }
1 <template>
2 <el-breadcrumb class="app-breadcrumb" separator="/">
3 <transition-group name="breadcrumb">
4 <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
5 <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
6 <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
7 </el-breadcrumb-item>
8 </transition-group>
9 </el-breadcrumb>
10 </template>
11
12 <script>
13 import pathToRegexp from 'path-to-regexp'
14
15 export default {
16 data() {
17 return {
18 levelList: null
19 }
20 },
21 watch: {
22 $route(route) {
23 // if you go to the redirect page, do not update the breadcrumbs
24 if (route.path.startsWith('/redirect/')) {
25 return
26 }
27 this.getBreadcrumb()
28 }
29 },
30 created() {
31 this.getBreadcrumb()
32 },
33 methods: {
34 getBreadcrumb() {
35 // only show routes with meta.title
36 let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
37 const first = matched[0]
38
39 if (!this.isDashboard(first)) {
40 matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched)
41 }
42
43 this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
44 },
45 isDashboard(route) {
46 const name = route && route.name
47 if (!name) {
48 return false
49 }
50 return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
51 },
52 pathCompile(path) {
53 // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
54 const { params } = this.$route
55 var toPath = pathToRegexp.compile(path)
56 return toPath(params)
57 },
58 handleLink(item) {
59 const { redirect, path } = item
60 if (redirect) {
61 this.$router.push(redirect)
62 return
63 }
64 this.$router.push(this.pathCompile(path))
65 }
66 }
67 }
68 </script>
69
70 <style lang="scss" scoped>
71 .app-breadcrumb.el-breadcrumb {
72 display: inline-block;
73 font-size: 14px;
74 line-height: 50px;
75 margin-left: 8px;
76 .no-redirect {
77 color: #97a8be;
78 cursor: text;
79 }
80 }
81 </style>
1 <template>
2 <div class="crud-opts">
3 <span class="crud-opts-left">
4 <!--左侧插槽-->
5 <slot name="left" />
6 <el-button
7 v-if="crud.optShow.add"
8 v-permission="permission.add"
9 :disabled="operation.add"
10 class="filter-item"
11 size="mini"
12 type="primary"
13 icon="el-icon-plus"
14 @click="crud.toAdd"
15 >
16 新增
17 </el-button>
18 <el-button
19 v-if="crud.optShow.edit"
20 v-permission="permission.edit"
21 class="filter-item"
22 size="mini"
23 type="success"
24 icon="el-icon-edit"
25 :disabled="crud.selections.length !== 1"
26 @click="crud.toEdit(crud.selections[0])"
27 >
28 修改
29 </el-button>
30 <el-button
31 v-if="crud.optShow.del"
32 slot="reference"
33 v-permission="permission.del"
34 class="filter-item"
35 type="danger"
36 icon="el-icon-delete"
37 size="mini"
38 :loading="crud.delAllLoading"
39 :disabled="crud.selections.length === 0"
40 @click="toDelete(crud.selections)"
41 >
42 删除
43 </el-button>
44 <el-button
45 v-if="crud.optShow.download"
46 :loading="crud.downloadLoading"
47 :disabled="!crud.data.length"
48 class="filter-item"
49 size="mini"
50 type="warning"
51 icon="el-icon-download"
52 @click="crud.doExport"
53 >导出</el-button>
54 <!--右侧-->
55 <slot name="right" />
56 </span>
57 <el-button-group v-if="crud.optShow.rightIcon" class="crud-opts-right">
58 <el-button
59 size="mini"
60 plain
61 type="info"
62 icon="el-icon-search"
63 @click="toggleSearch()"
64 />
65 <el-button
66 size="mini"
67 icon="el-icon-refresh"
68 @click="crud.refresh()"
69 />
70 <el-popover
71 placement="bottom-end"
72 width="150"
73 trigger="click"
74 >
75 <el-button
76 slot="reference"
77 size="mini"
78 icon="el-icon-s-grid"
79 >
80 <i
81 class="fa fa-caret-down"
82 aria-hidden="true"
83 />
84 </el-button>
85 <el-checkbox
86 v-model="allColumnsSelected"
87 :indeterminate="allColumnsSelectedIndeterminate"
88 @change="handleCheckAllChange"
89 >
90 全选
91 </el-checkbox>
92 <el-checkbox
93 v-for="item in tableColumns"
94 :key="item.property"
95 v-model="item.visible"
96 @change="handleCheckedTableColumnsChange(item)"
97 >
98 {{ item.label }}
99 </el-checkbox>
100 </el-popover>
101 </el-button-group>
102 </div>
103 </template>
104 <script>
105 import CRUD, { crud } from '@crud/crud'
106
107 function sortWithRef(src, ref) {
108 const result = Object.assign([], ref)
109 let cursor = -1
110 src.forEach(e => {
111 const idx = result.indexOf(e)
112 if (idx === -1) {
113 cursor += 1
114 result.splice(cursor, 0, e)
115 } else {
116 cursor = idx
117 }
118 })
119 return result
120 }
121
122 export default {
123 mixins: [crud()],
124 props: {
125 permission: {
126 type: Object,
127 default: () => { return {} }
128 },
129 hiddenColumns: {
130 type: Array,
131 default: () => { return [] }
132 },
133 ignoreColumns: {
134 type: Array,
135 default: () => { return [] }
136 },
137 operation: {
138 type: Object,
139 default() {
140 return {
141 add: false
142 }
143 }
144 }
145 },
146 data() {
147 return {
148 tableColumns: [],
149 allColumnsSelected: true,
150 allColumnsSelectedIndeterminate: false,
151 tableUnwatcher: null,
152 // 忽略下次表格列变动
153 ignoreNextTableColumnsChange: false
154 }
155 },
156 watch: {
157 'crud.props.table'() {
158 this.updateTableColumns()
159 this.tableColumns.forEach(column => {
160 if (this.hiddenColumns.indexOf(column.property) !== -1) {
161 column.visible = false
162 this.updateColumnVisible(column)
163 }
164 })
165 },
166 'crud.props.table.store.states.columns'() {
167 this.updateTableColumns()
168 }
169 },
170 created() {
171 this.crud.updateProp('searchToggle', true)
172 },
173 methods: {
174 updateTableColumns() {
175 const table = this.crud.getTable()
176 if (!table) {
177 this.tableColumns = []
178 return
179 }
180 let cols = null
181 const columnFilter = e => e && e.type === 'default' && e.property && this.ignoreColumns.indexOf(e.property) === -1
182 const refCols = table.columns.filter(columnFilter)
183 if (this.ignoreNextTableColumnsChange) {
184 this.ignoreNextTableColumnsChange = false
185 return
186 }
187 this.ignoreNextTableColumnsChange = false
188 const columns = []
189 const fullTableColumns = table.$children.map(e => e.columnConfig).filter(columnFilter)
190 cols = sortWithRef(fullTableColumns, refCols)
191 cols.forEach(config => {
192 const column = {
193 property: config.property,
194 label: config.label,
195 visible: refCols.indexOf(config) !== -1
196 }
197 columns.push(column)
198 })
199 this.tableColumns = columns
200 },
201 toDelete(datas) {
202 this.$confirm(`确认删除选中的${datas.length}条数据?`, '提示', {
203 confirmButtonText: '确定',
204 cancelButtonText: '取消',
205 type: 'warning'
206 }).then(() => {
207 this.crud.delAllLoading = true
208 this.crud.doDelete(datas)
209 }).catch(() => {
210 })
211 },
212 handleCheckAllChange(val) {
213 if (val === false) {
214 this.allColumnsSelected = true
215 return
216 }
217 this.tableColumns.forEach(column => {
218 if (!column.visible) {
219 column.visible = true
220 this.updateColumnVisible(column)
221 }
222 })
223 this.allColumnsSelected = val
224 this.allColumnsSelectedIndeterminate = false
225 },
226 handleCheckedTableColumnsChange(item) {
227 let totalCount = 0
228 let selectedCount = 0
229 this.tableColumns.forEach(column => {
230 ++totalCount
231 selectedCount += column.visible ? 1 : 0
232 })
233 if (selectedCount === 0) {
234 this.crud.notify('请至少选择一列', CRUD.NOTIFICATION_TYPE.WARNING)
235 this.$nextTick(function() {
236 item.visible = true
237 })
238 return
239 }
240 this.allColumnsSelected = selectedCount === totalCount
241 this.allColumnsSelectedIndeterminate = selectedCount !== totalCount && selectedCount !== 0
242 this.updateColumnVisible(item)
243 },
244 updateColumnVisible(item) {
245 const table = this.crud.props.table
246 const vm = table.$children.find(e => e.prop === item.property)
247 const columnConfig = vm.columnConfig
248 if (item.visible) {
249 // 找出合适的插入点
250 const columnIndex = this.tableColumns.indexOf(item)
251 vm.owner.store.commit('insertColumn', columnConfig, columnIndex + 1, null)
252 } else {
253 vm.owner.store.commit('removeColumn', columnConfig, null)
254 }
255 this.ignoreNextTableColumnsChange = true
256 },
257 toggleSearch() {
258 this.crud.props.searchToggle = !this.crud.props.searchToggle
259 }
260 }
261 }
262 </script>
263
264 <style>
265 .crud-opts {
266 padding: 4px 0;
267 display: -webkit-flex;
268 display: flex;
269 align-items: center;
270 }
271 .crud-opts .crud-opts-right {
272 margin-left: auto;
273 }
274 </style>
1 <!--分页-->
2 <template>
3 <el-pagination
4 :page-size.sync="page.size"
5 :total="page.total"
6 :current-page.sync="page.page"
7 style="margin-top: 8px;"
8 layout="total, prev, pager, next, sizes"
9 @size-change="crud.sizeChangeHandler($event)"
10 @current-change="crud.pageChangeHandler"
11 />
12 </template>
13 <script>
14 import { pagination } from '@crud/crud'
15 export default {
16 mixins: [pagination()]
17 }
18 </script>
1 <!--搜索与重置-->
2 <template>
3 <span>
4 <el-button class="filter-item" :disabled="operation.add" size="mini" type="success" icon="el-icon-search" @click="crud.toQuery">搜索</el-button>
5 <el-button v-if="crud.optShow.reset" class="filter-item" size="mini" type="warning" icon="el-icon-refresh-left" @click="crud.resetQuery()">重置</el-button>
6 <slot name="sright" />
7 </span>
8 </template>
9 <script>
10 import { crud } from '@crud/crud'
11 export default {
12 mixins: [crud()],
13 props: {
14 itemClass: {
15 type: String,
16 required: false,
17 default: ''
18 },
19 operation: {
20 type: Object,
21 default() {
22 return {
23 add: false
24 }
25 }
26 }
27 }
28 }
29 </script>
1 <template>
2 <div>
3 <slot name="header" />
4 <el-tooltip v-if="operation.edit" slot="header" class="item" effect="dark" content="编辑" placement="top">
5 <el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click="crud.toEdit(data)" />
6 </el-tooltip>
7 <el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
8 <p>{{ msg }}</p>
9 <div style="text-align: right; margin: 0">
10 <el-button size="mini" type="text" @click="doCancel">取消</el-button>
11 <el-button :loading="crud.dataStatus[crud.getDataId(data)].delete === 2" type="primary" size="mini" @click="crud.doDelete(data)">确定</el-button>
12 </div>
13 <el-button v-if="operation.del" slot="reference" :disabled="disabledDle" type="danger" icon="el-icon-delete" size="mini" @click="toDelete" />
14 </el-popover>
15 <slot name="footer" />
16 </div>
17 </template>
18 <script>
19 import CRUD, { crud } from '@crud/crud'
20 export default {
21 mixins: [crud()],
22 props: {
23 data: {
24 type: Object,
25 required: true
26 },
27 permission: {
28 type: Object,
29 required: true
30 },
31 disabledEdit: {
32 type: Boolean,
33 default: false
34 },
35 disabledDle: {
36 type: Boolean,
37 default: false
38 },
39 msg: {
40 type: String,
41 default: '确定删除本条数据吗?'
42 },
43 operation: {
44 type: Object,
45 default() {
46 return {
47 edit: true,
48 del: true
49 }
50 }
51 }
52 },
53 data() {
54 return {
55 pop: false
56 }
57 },
58 methods: {
59 doCancel() {
60 this.pop = false
61 this.crud.cancelDelete(this.data)
62 },
63 toDelete() {
64 this.pop = true
65 },
66 [CRUD.HOOK.afterDelete](crud, data) {
67 if (data === this.data) {
68 this.pop = false
69 }
70 },
71 onPopoverShow() {
72 setTimeout(() => {
73 document.addEventListener('click', this.handleDocumentClick)
74 }, 0)
75 },
76 onPopoverHide() {
77 document.removeEventListener('click', this.handleDocumentClick)
78 },
79 handleDocumentClick(event) {
80 this.pop = false
81 }
82 }
83 }
84 </script>
1 import { initData, download } from '@/api/data'
2 import { parseTime, downloadFile } from '@/utils/index'
3 import Vue from 'vue'
4
5 /**
6 * CRUD配置
7 * @author moxun
8 * @param {*} options <br>
9 * @return crud instance.
10 * @example
11 * 要使用多crud时,请在关联crud的组件处使用crud-tag进行标记,如:<jobForm :job-status="dict.job_status" crud-tag="job" />
12 */
13 function CRUD(options) {
14 const defaultOptions = {
15 tag: 'default',
16 // id字段名
17 idField: 'id',
18 // 标题
19 title: '',
20 // 请求数据的url
21 url: '',
22 // 表格数据
23 data: [],
24 // 选择项
25 selections: [],
26 // 待查询的对象
27 query: {},
28 // 查询数据的参数
29 params: {},
30 // Form 表单
31 form: {},
32 // 重置表单
33 defaultForm: () => {},
34 // 排序规则,默认 id 降序, 支持多字段排序 ['id,desc', 'createTime,asc']
35 sort: ['id,desc'],
36 // 等待时间
37 time: 50,
38 // CRUD Method
39 crudMethod: {
40 add: (form) => {},
41 del: (id) => {},
42 edit: (form) => {},
43 get: (id) => {}
44 },
45 // 主页操作栏显示哪些按钮
46 optShow: {
47 add: true,
48 edit: true,
49 del: true,
50 download: true,
51 reset: true
52 },
53 // 自定义一些扩展属性
54 props: {},
55 // 在主页准备
56 queryOnPresenterCreated: true,
57 // 调试开关
58 debug: false
59 }
60 options = mergeOptions(defaultOptions, options)
61 const data = {
62 ...options,
63 // 记录数据状态
64 dataStatus: {},
65 status: {
66 add: CRUD.STATUS.NORMAL,
67 edit: CRUD.STATUS.NORMAL,
68 // 添加或编辑状态
69 get cu() {
70 if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) {
71 return CRUD.STATUS.NORMAL
72 } else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) {
73 return CRUD.STATUS.PREPARED
74 } else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) {
75 return CRUD.STATUS.PROCESSING
76 }
77 throw new Error('wrong crud\'s cu status')
78 },
79 // 标题
80 get title() {
81 return this.add > CRUD.STATUS.NORMAL ? `新增${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${crud.title}` : crud.title
82 }
83 },
84 msg: {
85 submit: '提交成功',
86 add: '新增成功',
87 edit: '编辑成功',
88 del: '删除成功'
89 },
90 page: {
91 // 页码
92 page: 0,
93 // 每页数据条数
94 size: 10,
95 // 总数据条数
96 total: 0
97 },
98 // 整体loading
99 loading: false,
100 // 导出的 Loading
101 downloadLoading: false,
102 // 删除的 Loading
103 delAllLoading: false
104 }
105 const methods = {
106 /**
107 * 通用的提示
108 */
109 submitSuccessNotify() {
110 crud.notify(crud.msg.submit, CRUD.NOTIFICATION_TYPE.SUCCESS)
111 },
112 addSuccessNotify() {
113 crud.notify(crud.msg.add, CRUD.NOTIFICATION_TYPE.SUCCESS)
114 },
115 editSuccessNotify() {
116 crud.notify(crud.msg.edit, CRUD.NOTIFICATION_TYPE.SUCCESS)
117 },
118 delSuccessNotify() {
119 crud.notify(crud.msg.del, CRUD.NOTIFICATION_TYPE.SUCCESS)
120 },
121 // 搜索
122 toQuery() {
123 crud.page.page = 1
124 crud.refresh()
125 },
126 // 刷新
127 refresh() {
128 if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) {
129 return
130 }
131 return new Promise((resolve, reject) => {
132 crud.loading = true
133 // 请求数据
134 initData(crud.url, crud.getQueryParams()).then(data => {
135 const table = crud.getTable()
136 if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据
137 table.store.states.treeData = {}
138 table.store.states.lazyTreeNodeMap = {}
139 }
140 crud.page.total = data.totalElements
141 crud.data = data.content
142 crud.resetDataStatus()
143 // time 毫秒后显示表格
144 setTimeout(() => {
145 crud.loading = false
146 callVmHook(crud, CRUD.HOOK.afterRefresh)
147 }, crud.time)
148 resolve(data)
149 }).catch(err => {
150 crud.loading = false
151 reject(err)
152 })
153 })
154 },
155 /**
156 * 启动添加
157 */
158 toAdd() {
159 crud.resetForm()
160 if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
161 return
162 }
163 crud.status.add = CRUD.STATUS.PREPARED
164 callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
165 callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
166 },
167 /**
168 * 启动编辑
169 * @param {*} data 数据项
170 */
171 toEdit(data) {
172 crud.resetForm(JSON.parse(JSON.stringify(data)))
173 if (!(callVmHook(crud, CRUD.HOOK.beforeToEdit, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
174 return
175 }
176 crud.status.edit = CRUD.STATUS.PREPARED
177 crud.getDataStatus(crud.getDataId(data)).edit = CRUD.STATUS.PREPARED
178 callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form)
179 callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
180 },
181 /**
182 * 启动删除
183 * @param {*} data 数据项
184 */
185 toDelete(data) {
186 crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.PREPARED
187 },
188 /**
189 * 取消删除
190 * @param {*} data 数据项
191 */
192 cancelDelete(data) {
193 if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) {
194 return
195 }
196 crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.NORMAL
197 callVmHook(crud, CRUD.HOOK.afterDeleteCancel, data)
198 },
199 /**
200 * 取消新增/编辑
201 */
202 cancelCU() {
203 const addStatus = crud.status.add
204 const editStatus = crud.status.edit
205 if (addStatus === CRUD.STATUS.PREPARED) {
206 if (!callVmHook(crud, CRUD.HOOK.beforeAddCancel, crud.form)) {
207 return
208 }
209 crud.status.add = CRUD.STATUS.NORMAL
210 }
211 if (editStatus === CRUD.STATUS.PREPARED) {
212 if (!callVmHook(crud, CRUD.HOOK.beforeEditCancel, crud.form)) {
213 return
214 }
215 crud.status.edit = CRUD.STATUS.NORMAL
216 crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
217 }
218 crud.resetForm()
219 if (addStatus === CRUD.STATUS.PREPARED) {
220 callVmHook(crud, CRUD.HOOK.afterAddCancel, crud.form)
221 }
222 if (editStatus === CRUD.STATUS.PREPARED) {
223 callVmHook(crud, CRUD.HOOK.afterEditCancel, crud.form)
224 }
225 // 清除表单验证
226 if (crud.findVM('form').$refs['form']) {
227 crud.findVM('form').$refs['form'].clearValidate()
228 }
229 },
230 /**
231 * 提交新增/编辑
232 */
233 submitCU() {
234 if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
235 return
236 }
237 crud.findVM('form').$refs['form'].validate(valid => {
238 if (!valid) {
239 return
240 }
241 if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
242 return
243 }
244 if (crud.status.add === CRUD.STATUS.PREPARED) {
245 crud.doAdd()
246 } else if (crud.status.edit === CRUD.STATUS.PREPARED) {
247 crud.doEdit()
248 }
249 })
250 },
251 /**
252 * 执行添加
253 */
254 doAdd() {
255 if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
256 return
257 }
258 crud.status.add = CRUD.STATUS.PROCESSING
259 crud.crudMethod.add(crud.form).then(() => {
260 crud.status.add = CRUD.STATUS.NORMAL
261 crud.resetForm()
262 crud.addSuccessNotify()
263 callVmHook(crud, CRUD.HOOK.afterSubmit)
264 crud.toQuery()
265 }).catch(() => {
266 crud.status.add = CRUD.STATUS.PREPARED
267 callVmHook(crud, CRUD.HOOK.afterAddError)
268 })
269 },
270 /**
271 * 执行编辑
272 */
273 doEdit() {
274 if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
275 return
276 }
277 crud.status.edit = CRUD.STATUS.PROCESSING
278 crud.crudMethod.edit(crud.form).then(() => {
279 crud.status.edit = CRUD.STATUS.NORMAL
280 crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
281 crud.editSuccessNotify()
282 crud.resetForm()
283 callVmHook(crud, CRUD.HOOK.afterSubmit)
284 crud.refresh()
285 }).catch(() => {
286 crud.status.edit = CRUD.STATUS.PREPARED
287 callVmHook(crud, CRUD.HOOK.afterEditError)
288 })
289 },
290 /**
291 * 执行删除
292 * @param {*} data 数据项
293 */
294 doDelete(data) {
295 let delAll = false
296 let dataStatus
297 const ids = []
298 if (data instanceof Array) {
299 delAll = true
300 data.forEach(val => {
301 ids.push(this.getDataId(val))
302 })
303 } else {
304 ids.push(this.getDataId(data))
305 dataStatus = crud.getDataStatus(this.getDataId(data))
306 }
307 if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) {
308 return
309 }
310 if (!delAll) {
311 dataStatus.delete = CRUD.STATUS.PROCESSING
312 }
313 return crud.crudMethod.del(ids).then(() => {
314 if (delAll) {
315 crud.delAllLoading = false
316 } else dataStatus.delete = CRUD.STATUS.PREPARED
317 crud.dleChangePage(1)
318 crud.delSuccessNotify()
319 callVmHook(crud, CRUD.HOOK.afterDelete, data)
320 crud.refresh()
321 }).catch(() => {
322 if (delAll) {
323 crud.delAllLoading = false
324 } else dataStatus.delete = CRUD.STATUS.PREPARED
325 })
326 },
327 /**
328 * 通用导出
329 */
330 doExport() {
331 crud.downloadLoading = true
332 download(crud.url + '/download', crud.getQueryParams()).then(result => {
333 downloadFile(result, crud.title + '数据', 'xlsx')
334 crud.downloadLoading = false
335 }).catch(() => {
336 crud.downloadLoading = false
337 })
338 },
339 /**
340 * 获取查询参数
341 */
342 getQueryParams: function() {
343 // 清除参数无值的情况
344 Object.keys(crud.query).length !== 0 && Object.keys(crud.query).forEach(item => {
345 if (crud.query[item] === null || crud.query[item] === '') crud.query[item] = undefined
346 })
347 Object.keys(crud.params).length !== 0 && Object.keys(crud.params).forEach(item => {
348 if (crud.params[item] === null || crud.params[item] === '') crud.params[item] = undefined
349 })
350 return {
351 page: crud.page.page - 1,
352 size: crud.page.size,
353 sort: crud.sort,
354 ...crud.query,
355 ...crud.params
356 }
357 },
358 // 当前页改变
359 pageChangeHandler(e) {
360 crud.page.page = e
361 crud.refresh()
362 },
363 // 每页条数改变
364 sizeChangeHandler(e) {
365 crud.page.size = e
366 crud.page.page = 1
367 crud.refresh()
368 },
369 // 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
370 dleChangePage(size) {
371 if (crud.data.length === size && crud.page.page !== 1) {
372 crud.page.page -= 1
373 }
374 },
375 // 选择改变
376 selectionChangeHandler(val) {
377 crud.selections = val
378 },
379 /**
380 * 重置查询参数
381 * @param {Boolean} toQuery 重置后进行查询操作
382 */
383 resetQuery(toQuery = true) {
384 const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery))
385 const query = crud.query
386 Object.keys(query).forEach(key => {
387 query[key] = defaultQuery[key]
388 })
389 // 重置参数
390 this.params = {}
391 if (toQuery) {
392 crud.toQuery()
393 }
394 },
395 /**
396 * 重置表单
397 * @param {Array} data 数据
398 */
399 resetForm(data) {
400 const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form')))
401 const crudFrom = crud.form
402 for (const key in form) {
403 if (crudFrom.hasOwnProperty(key)) {
404 crudFrom[key] = form[key]
405 } else {
406 Vue.set(crudFrom, key, form[key])
407 }
408 }
409 // add by ghl 2020-10-04 页面重复添加信息时,下拉框的校验会存在,需要找工取消
410 if (crud.findVM('form').$refs['form']) {
411 crud.findVM('form').$refs['form'].clearValidate()
412 }
413 },
414 /**
415 * 重置数据状态
416 */
417 resetDataStatus() {
418 const dataStatus = {}
419 function resetStatus(datas) {
420 datas.forEach(e => {
421 dataStatus[crud.getDataId(e)] = {
422 delete: 0,
423 edit: 0
424 }
425 if (e.children) {
426 resetStatus(e.children)
427 }
428 })
429 }
430 resetStatus(crud.data)
431 crud.dataStatus = dataStatus
432 },
433 /**
434 * 获取数据状态
435 * @param {Number | String} id 数据项id
436 */
437 getDataStatus(id) {
438 return crud.dataStatus[id]
439 },
440 /**
441 * 用于树形表格多选, 选中所有
442 * @param selection
443 */
444 selectAllChange(selection) {
445 // 如果选中的数目与请求到的数目相同就选中子节点,否则就清空选中
446 if (selection && selection.length === crud.data.length) {
447 selection.forEach(val => {
448 crud.selectChange(selection, val)
449 })
450 } else {
451 crud.getTable().clearSelection()
452 }
453 },
454 /**
455 * 用于树形表格多选,单选的封装
456 * @param selection
457 * @param row
458 */
459 selectChange(selection, row) {
460 // 如果selection中存在row代表是选中,否则是取消选中
461 if (selection.find(val => { return crud.getDataId(val) === crud.getDataId(row) })) {
462 if (row.children) {
463 row.children.forEach(val => {
464 crud.getTable().toggleRowSelection(val, true)
465 selection.push(val)
466 if (val.children) {
467 crud.selectChange(selection, val)
468 }
469 })
470 }
471 } else {
472 crud.toggleRowSelection(selection, row)
473 }
474 },
475 /**
476 * 切换选中状态
477 * @param selection
478 * @param data
479 */
480 toggleRowSelection(selection, data) {
481 if (data.children) {
482 data.children.forEach(val => {
483 crud.getTable().toggleRowSelection(val, false)
484 if (val.children) {
485 crud.toggleRowSelection(selection, val)
486 }
487 })
488 }
489 },
490 findVM(type) {
491 return crud.vms.find(vm => vm && vm.type === type).vm
492 },
493 notify(title, type = CRUD.NOTIFICATION_TYPE.INFO) {
494 crud.vms[0].vm.$notify({
495 title,
496 type,
497 duration: 2500
498 })
499 },
500 updateProp(name, value) {
501 Vue.set(crud.props, name, value)
502 },
503 getDataId(data) {
504 return data[this.idField]
505 },
506 getTable() {
507 return this.findVM('presenter').$refs.table
508 },
509 attchTable() {
510 const table = this.getTable()
511 this.updateProp('table', table)
512 const that = this
513 table.$on('expand-change', (row, expanded) => {
514 if (!expanded) {
515 return
516 }
517 const lazyTreeNodeMap = table.store.states.lazyTreeNodeMap
518 row.children = lazyTreeNodeMap[crud.getDataId(row)]
519 if (row.children) {
520 row.children.forEach(ele => {
521 const id = crud.getDataId(ele)
522 if (that.dataStatus[id] === undefined) {
523 that.dataStatus[id] = {
524 delete: 0,
525 edit: 0
526 }
527 }
528 })
529 }
530 })
531 }
532 }
533 const crud = Object.assign({}, data)
534 // 可观测化
535 Vue.observable(crud)
536 // 附加方法
537 Object.assign(crud, methods)
538 // 记录初始默认的查询参数,后续重置查询时使用
539 Object.assign(crud, {
540 defaultQuery: JSON.parse(JSON.stringify(data.query)),
541 // 预留4位存储:组件 主页、头部、分页、表单,调试查看也方便找
542 vms: Array(4),
543 /**
544 * 注册组件实例
545 * @param {String} type 类型
546 * @param {*} vm 组件实例
547 * @param {Number} index 该参数内部使用
548 */
549 registerVM(type, vm, index = -1) {
550 const vmObj = {
551 type,
552 vm: vm
553 }
554 if (index < 0) {
555 this.vms.push(vmObj)
556 return
557 }
558 if (index < 4) { // 内置预留vm数
559 this.vms[index] = vmObj
560 return
561 }
562 this.vms.length = Math.max(this.vms.length, index)
563 this.vms.splice(index, 1, vmObj)
564 },
565 /**
566 * 取消注册组件实例
567 * @param {*} vm 组件实例
568 */
569 unregisterVM(type, vm) {
570 for (let i = this.vms.length - 1; i >= 0; i--) {
571 if (this.vms[i] === undefined) {
572 continue
573 }
574 if (this.vms[i].type === type && this.vms[i].vm === vm) {
575 if (i < 4) { // 内置预留vm数
576 this.vms[i] = undefined
577 } else {
578 this.vms.splice(i, 1)
579 }
580 break
581 }
582 }
583 }
584 })
585 // 冻结处理,需要扩展数据的话,使用crud.updateProp(name, value),以crud.props.name形式访问,这个是响应式的,可以做数据绑定
586 Object.freeze(crud)
587 return crud
588 }
589
590 // hook VM
591 function callVmHook(crud, hook) {
592 if (crud.debug) {
593 console.log('callVmHook: ' + hook)
594 }
595 const tagHook = crud.tag ? hook + '$' + crud.tag : null
596 let ret = true
597 const nargs = [crud]
598 for (let i = 2; i < arguments.length; ++i) {
599 nargs.push(arguments[i])
600 }
601 // 有些组件扮演了多个角色,调用钩子时,需要去重
602 const vmSet = new Set()
603 crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
604 vmSet.forEach(vm => {
605 if (vm[hook]) {
606 ret = vm[hook].apply(vm, nargs) !== false && ret
607 }
608 if (tagHook && vm[tagHook]) {
609 ret = vm[tagHook].apply(vm, nargs) !== false && ret
610 }
611 })
612 return ret
613 }
614
615 function mergeOptions(src, opts) {
616 const optsRet = {
617 ...src
618 }
619 for (const key in src) {
620 if (opts.hasOwnProperty(key)) {
621 optsRet[key] = opts[key]
622 }
623 }
624 return optsRet
625 }
626
627 /**
628 * 查找crud
629 * @param {*} vm
630 * @param {string} tag
631 */
632 function lookupCrud(vm, tag) {
633 tag = tag || vm.$attrs['crud-tag'] || 'default'
634 // function lookupCrud(vm, tag) {
635 if (vm.$crud) {
636 const ret = vm.$crud[tag]
637 if (ret) {
638 return ret
639 }
640 }
641 return vm.$parent ? lookupCrud(vm.$parent, tag) : undefined
642 }
643
644 /**
645 * crud主页
646 */
647 function presenter(crud) {
648 if (crud) {
649 console.warn('[CRUD warn]: ' + 'please use $options.cruds() { return CRUD(...) or [CRUD(...), ...] }')
650 }
651 return {
652 data() {
653 // 在data中返回crud,是为了将crud与当前实例关联,组件观测crud相关属性变化
654 return {
655 crud: this.crud
656 }
657 },
658 beforeCreate() {
659 this.$crud = this.$crud || {}
660 let cruds = this.$options.cruds instanceof Function ? this.$options.cruds() : crud
661 if (!(cruds instanceof Array)) {
662 cruds = [cruds]
663 }
664 cruds.forEach(ele => {
665 if (this.$crud[ele.tag]) {
666 console.error('[CRUD error]: ' + 'crud with tag [' + ele.tag + ' is already exist')
667 }
668 this.$crud[ele.tag] = ele
669 ele.registerVM('presenter', this, 0)
670 })
671 this.crud = this.$crud['defalut'] || cruds[0]
672 },
673 methods: {
674 parseTime
675 },
676 created() {
677 for (const k in this.$crud) {
678 if (this.$crud[k].queryOnPresenterCreated) {
679 this.$crud[k].toQuery()
680 }
681 }
682 },
683 destroyed() {
684 for (const k in this.$crud) {
685 this.$crud[k].unregisterVM('presenter', this)
686 }
687 },
688 mounted() {
689 // 如果table未实例化(例如使用了v-if),请稍后在适当时机crud.attchTable刷新table信息
690 if (this.$refs.table !== undefined) {
691 this.crud.attchTable()
692 }
693 }
694 }
695 }
696
697 /**
698 * 头部
699 */
700 function header() {
701 return {
702 data() {
703 return {
704 crud: this.crud,
705 query: this.crud.query
706 }
707 },
708 beforeCreate() {
709 this.crud = lookupCrud(this)
710 this.crud.registerVM('header', this, 1)
711 },
712 destroyed() {
713 this.crud.unregisterVM('header', this)
714 }
715 }
716 }
717
718 /**
719 * 分页
720 */
721 function pagination() {
722 return {
723 data() {
724 return {
725 crud: this.crud,
726 page: this.crud.page
727 }
728 },
729 beforeCreate() {
730 this.crud = lookupCrud(this)
731 this.crud.registerVM('pagination', this, 2)
732 },
733 destroyed() {
734 this.crud.unregisterVM('pagination', this)
735 }
736 }
737 }
738
739 /**
740 * 表单
741 */
742 function form(defaultForm) {
743 return {
744 data() {
745 return {
746 crud: this.crud,
747 form: this.crud.form
748 }
749 },
750 beforeCreate() {
751 this.crud = lookupCrud(this)
752 this.crud.registerVM('form', this, 3)
753 },
754 created() {
755 this.crud.defaultForm = defaultForm
756 this.crud.resetForm()
757 },
758 destroyed() {
759 this.crud.unregisterVM('form', this)
760 }
761 }
762 }
763
764 /**
765 * crud
766 */
767 function crud(options = {}) {
768 const defaultOptions = {
769 type: undefined
770 }
771 options = mergeOptions(defaultOptions, options)
772 return {
773 data() {
774 return {
775 crud: this.crud
776 }
777 },
778 beforeCreate() {
779 this.crud = lookupCrud(this)
780 this.crud.registerVM(options.type, this)
781 },
782 destroyed() {
783 this.crud.unregisterVM(options.type, this)
784 }
785 }
786 }
787
788 /**
789 * CRUD钩子
790 */
791 CRUD.HOOK = {
792 /** 刷新 - 之前 */
793 beforeRefresh: 'beforeCrudRefresh',
794 /** 刷新 - 之后 */
795 afterRefresh: 'afterCrudRefresh',
796 /** 删除 - 之前 */
797 beforeDelete: 'beforeCrudDelete',
798 /** 删除 - 之后 */
799 afterDelete: 'afterCrudDelete',
800 /** 删除取消 - 之前 */
801 beforeDeleteCancel: 'beforeCrudDeleteCancel',
802 /** 删除取消 - 之后 */
803 afterDeleteCancel: 'afterCrudDeleteCancel',
804 /** 新建 - 之前 */
805 beforeToAdd: 'beforeCrudToAdd',
806 /** 新建 - 之后 */
807 afterToAdd: 'afterCrudToAdd',
808 /** 编辑 - 之前 */
809 beforeToEdit: 'beforeCrudToEdit',
810 /** 编辑 - 之后 */
811 afterToEdit: 'afterCrudToEdit',
812 /** 开始 "新建/编辑" - 之前 */
813 beforeToCU: 'beforeCrudToCU',
814 /** 开始 "新建/编辑" - 之后 */
815 afterToCU: 'afterCrudToCU',
816 /** "新建/编辑" 验证 - 之前 */
817 beforeValidateCU: 'beforeCrudValidateCU',
818 /** "新建/编辑" 验证 - 之后 */
819 afterValidateCU: 'afterCrudValidateCU',
820 /** 添加取消 - 之前 */
821 beforeAddCancel: 'beforeCrudAddCancel',
822 /** 添加取消 - 之后 */
823 afterAddCancel: 'afterCrudAddCancel',
824 /** 编辑取消 - 之前 */
825 beforeEditCancel: 'beforeCrudEditCancel',
826 /** 编辑取消 - 之后 */
827 afterEditCancel: 'afterCrudEditCancel',
828 /** 提交 - 之前 */
829 beforeSubmit: 'beforeCrudSubmitCU',
830 /** 提交 - 之后 */
831 afterSubmit: 'afterCrudSubmitCU',
832 afterAddError: 'afterCrudAddError',
833 afterEditError: 'afterCrudEditError'
834 }
835
836 /**
837 * CRUD状态
838 */
839 CRUD.STATUS = {
840 NORMAL: 0,
841 PREPARED: 1,
842 PROCESSING: 2
843 }
844
845 /**
846 * CRUD通知类型
847 */
848 CRUD.NOTIFICATION_TYPE = {
849 SUCCESS: 'success',
850 WARNING: 'warning',
851 INFO: 'info',
852 ERROR: 'error'
853 }
854
855 export default CRUD
856
857 export {
858 presenter,
859 header,
860 form,
861 pagination,
862 crud
863 }
1 <script>
2 import { DatePicker, DatePickerOptions } from 'element-ui'
3 import { calendarShortcuts } from '@/utils/shortcuts'
4
5 export default {
6 name: 'DateRangePicker',
7 mixins: [DatePicker],
8 props: {
9 type: {
10 type: String,
11 default: 'daterange'
12 },
13 valueFormat: {
14 type: String,
15 default: 'yyyy-MM-dd HH:mm:ss'
16 },
17 defaultTime: {
18 type: Array,
19 default: _ => ['00:00:00', '23:59:59']
20 },
21 pickerOptions: {
22 type: DatePickerOptions,
23 default: _ => {
24 return { shortcuts: calendarShortcuts }
25 }
26 },
27 size: {
28 type: String,
29 default: 'small'
30 },
31 rangeSeparator: {
32 type: String,
33 default: ':'
34 },
35 startPlaceholder: {
36 type: String,
37 default: '开始日期'
38 },
39 endPlaceholder: {
40 type: String,
41 default: '结束日期'
42 }
43 }
44 }
45 </script>
1 import Vue from 'vue'
2 import { get as getDictDetail } from '@/api/system/dictDetail'
3
4 export default class Dict {
5 constructor(dict) {
6 this.dict = dict
7 }
8
9 async init(names, completeCallback) {
10 if (names === undefined || name === null) {
11 throw new Error('need Dict names')
12 }
13 const ps = []
14 names.forEach(n => {
15 Vue.set(this.dict.dict, n, {})
16 Vue.set(this.dict.label, n, {})
17 Vue.set(this.dict, n, [])
18 ps.push(getDictDetail(n).then(data => {
19 this.dict[n].splice(0, 0, ...data.content)
20 data.content.forEach(d => {
21 Vue.set(this.dict.dict[n], d.value, d)
22 Vue.set(this.dict.label[n], d.value, d.label)
23 })
24 }))
25 })
26 await Promise.all(ps)
27 completeCallback()
28 }
29 }
1 import Dict from './Dict'
2
3 const install = function(Vue) {
4 Vue.mixin({
5 data() {
6 if (this.$options.dicts instanceof Array) {
7 const dict = {
8 dict: {},
9 label: {}
10 }
11 return {
12 dict
13 }
14 }
15 return {}
16 },
17 created() {
18 if (this.$options.dicts instanceof Array) {
19 new Dict(this.dict).init(this.$options.dicts, () => {
20 this.$nextTick(() => {
21 this.$emit('dictReady')
22 })
23 })
24 }
25 }
26 })
27 }
28
29 export default { install }
1 <template>
2 <div>
3 <svg-icon icon-class="doc" @click="click" />
4 </div>
5 </template>
6
7 <script>
8 export default {
9 name: 'Doc',
10 methods: {
11 click() {
12 window.open('https://el-admin.vip/guide/', '_blank')
13 }
14 }
15 }
16 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 require('echarts/theme/macarons') // echarts theme
8 import { debounce } from '@/utils'
9
10 export default {
11 props: {
12 className: {
13 type: String,
14 default: 'chart'
15 },
16 width: {
17 type: String,
18 default: '100%'
19 },
20 height: {
21 type: String,
22 default: '300px'
23 },
24 option: {
25 type: Object,
26 default: () => {}
27 },
28 optionData: {
29 type: Array,
30 default: () => []
31 }
32 },
33 data() {
34 return {
35 chart: null,
36 colorList: [
37 '#ff7e50', '#97d3f9', '#dd70d9', '#34cf34',
38 '#6497ef', '#85802b', '#D7504B', '#C6E579',
39 '#F4E001', '#F0805A', '#26C0C0'
40 ]
41 }
42 },
43 watch: {
44 option: {
45 handler(newVal) {
46 console.log('获取配置', newVal)
47 if (this.chart) {
48 this.setOption()
49 }
50 },
51 immediate: true,
52 deep: true
53 },
54 optionData: {
55 handler(newVal) {
56 console.log('获取柱状数据', newVal, this.chart)
57 if (this.chart) {
58 this.initData()
59 }
60 },
61 immediate: true,
62 deep: true
63 }
64 },
65 mounted() {
66 this.initChart()
67 this.__resizeHandler = debounce(() => {
68 if (this.chart) {
69 this.chart.resize()
70 }
71 }, 100)
72 window.addEventListener('resize', this.__resizeHandler)
73 },
74 beforeDestroy() {
75 if (!this.chart) {
76 return
77 }
78 window.removeEventListener('resize', this.__resizeHandler)
79 this.chart.dispose()
80 this.chart = null
81 },
82 methods: {
83 initChart() {
84 this.chart = echarts.init(this.$el, 'macarons')
85
86 const option = {
87 tooltip: {
88 trigger: 'axis'
89 // formatter: function(a, b, c, d) {
90 // console.log(a, b, c, d)
91 // }
92 },
93 grid: {
94 top: '10%',
95 left: '3%',
96 right: '4%',
97 bottom: '3%',
98 containLabel: true
99 },
100 xAxis: {
101 type: 'value',
102 boundaryGap: [0, 0.01],
103 // axisLabel: {
104 // formatter: function(index, value) {
105 // console.log(index, value)
106 // }
107 // },
108 axisLine: {
109 show: false
110 },
111 axisTick: {
112 show: false
113 },
114 splitLine: {
115 show: false
116 },
117 splitNumber: 5,
118 max: 'auto'
119 },
120 yAxis: {
121 type: 'category',
122 data: [],
123 axisLine: {
124 show: false
125 },
126 axisTick: {
127 show: false,
128 alignWithLabel: true
129 },
130 axisPointer: {
131 type: 'shadow'
132 }
133 },
134 series: [
135 {
136 name: '标签人数分布',
137 type: 'bar',
138 data: [],
139 itemStyle: {
140 color: (params) => {
141 return this.colorList[params.dataIndex]
142 }
143 }
144 }
145 ]
146 }
147 this.chart.setOption(option)
148 if (Object.keys(this.option).length > 0) {
149 this.setOption()
150 }
151 if (this.optionData.length > 0) {
152 this.initData()
153 }
154 },
155 initData() {
156 console.log(123)
157 const yAxis = this.optionData.map(item => item.name).reverse()
158 const xAxis = this.optionData.map(item => item.value).reverse()
159 // const describeList = tagNowData.list.map(item => item.describe).reverse()
160 const option = {
161 // xAxis: {
162 // max: xAxis[xAxis.length - 1]
163 // },
164 yAxis: {
165 data: yAxis
166 },
167 series: [
168 {
169 data: xAxis
170 }
171 ]
172 }
173 console.log(option)
174 this.chart.setOption(option)
175 },
176 setOption() {
177 this.chart.setOption(this.option)
178 }
179 }
180 }
181 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '500px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51 const dataMap = {}
52
53 function dataFormatter(obj) {
54 const pList = ['北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江', '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州', '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆']
55 let temp
56 for (let year = 2002; year <= 2011; year++) {
57 let max = 0
58 let sum = 0
59 temp = obj[year]
60 for (let i = 0, l = temp.length; i < l; i++) {
61 max = Math.max(max, temp[i])
62 sum += temp[i]
63 obj[year][i] = {
64 name: pList[i],
65 value: temp[i]
66 }
67 }
68 obj[year + 'max'] = Math.floor(max / 100) * 100
69 obj[year + 'sum'] = sum
70 }
71 return obj
72 }
73
74 dataMap.dataGDP = dataFormatter({
75 2011: [16251.93, 11307.28, 24515.76, 11237.55, 14359.88, 22226.7, 10568.83, 12582, 19195.69, 49110.27, 32318.85, 15300.65, 17560.18, 11702.82, 45361.85, 26931.03, 19632.26, 19669.56, 53210.28, 11720.87, 2522.66, 10011.37, 21026.68, 5701.84, 8893.12, 605.83, 12512.3, 5020.37, 1670.44, 2102.21, 6610.05],
76 2010: [14113.58, 9224.46, 20394.26, 9200.86, 11672, 18457.27, 8667.58, 10368.6, 17165.98, 41425.48, 27722.31, 12359.33, 14737.12, 9451.26, 39169.92, 23092.36, 15967.61, 16037.96, 46013.06, 9569.85, 2064.5, 7925.58, 17185.48, 4602.16, 7224.18, 507.46, 10123.48, 4120.75, 1350.43, 1689.65, 5437.47],
77 2009: [12153.03, 7521.85, 17235.48, 7358.31, 9740.25, 15212.49, 7278.75, 8587, 15046.45, 34457.3, 22990.35, 10062.82, 12236.53, 7655.18, 33896.65, 19480.46, 12961.1, 13059.69, 39482.56, 7759.16, 1654.21, 6530.01, 14151.28, 3912.68, 6169.75, 441.36, 8169.8, 3387.56, 1081.27, 1353.31, 4277.05],
78 2008: [11115, 6719.01, 16011.97, 7315.4, 8496.2, 13668.58, 6426.1, 8314.37, 14069.87, 30981.98, 21462.69, 8851.66, 10823.01, 6971.05, 30933.28, 18018.53, 11328.92, 11555, 36796.71, 7021, 1503.06, 5793.66, 12601.23, 3561.56, 5692.12, 394.85, 7314.58, 3166.82, 1018.62, 1203.92, 4183.21],
79 2007: [9846.81, 5252.76, 13607.32, 6024.45, 6423.18, 11164.3, 5284.69, 7104, 12494.01, 26018.48, 18753.73, 7360.92, 9248.53, 5800.25, 25776.91, 15012.46, 9333.4, 9439.6, 31777.01, 5823.41, 1254.17, 4676.13, 10562.39, 2884.11, 4772.52, 341.43, 5757.29, 2703.98, 797.35, 919.11, 3523.16],
80 2006: [8117.78, 4462.74, 11467.6, 4878.61, 4944.25, 9304.52, 4275.12, 6211.8, 10572.24, 21742.05, 15718.47, 6112.5, 7583.85, 4820.53, 21900.19, 12362.79, 7617.47, 7688.67, 26587.76, 4746.16, 1065.67, 3907.23, 8690.24, 2338.98, 3988.14, 290.76, 4743.61, 2277.35, 648.5, 725.9, 3045.26],
81 2005: [6969.52, 3905.64, 10012.11, 4230.53, 3905.03, 8047.26, 3620.27, 5513.7, 9247.66, 18598.69, 13417.68, 5350.17, 6554.69, 4056.76, 18366.87, 10587.42, 6590.19, 6596.1, 22557.37, 3984.1, 918.75, 3467.72, 7385.1, 2005.42, 3462.73, 248.8, 3933.72, 1933.98, 543.32, 612.61, 2604.19],
82 2004: [6033.21, 3110.97, 8477.63, 3571.37, 3041.07, 6672, 3122.01, 4750.6, 8072.83, 15003.6, 11648.7, 4759.3, 5763.35, 3456.7, 15021.84, 8553.79, 5633.24, 5641.94, 18864.62, 3433.5, 819.66, 3034.58, 6379.63, 1677.8, 3081.91, 220.34, 3175.58, 1688.49, 466.1, 537.11, 2209.09],
83 2003: [5007.21, 2578.03, 6921.29, 2855.23, 2388.38, 6002.54, 2662.08, 4057.4, 6694.23, 12442.87, 9705.02, 3923.11, 4983.67, 2807.41, 12078.15, 6867.7, 4757.45, 4659.99, 15844.64, 2821.11, 713.96, 2555.72, 5333.09, 1426.34, 2556.02, 185.09, 2587.72, 1399.83, 390.2, 445.36, 1886.35],
84 2002: [4315, 2150.76, 6018.28, 2324.8, 1940.94, 5458.22, 2348.54, 3637.2, 5741.03, 10606.85, 8003.67, 3519.72, 4467.55, 2450.48, 10275.5, 6035.48, 4212.82, 4151.54, 13502.42, 2523.73, 642.73, 2232.86, 4725.01, 1243.43, 2312.82, 162.04, 2253.39, 1232.03, 340.65, 377.16, 1612.6]
85 })
86
87 dataMap.dataPI = dataFormatter({
88 2011: [136.27, 159.72, 2905.73, 641.42, 1306.3, 1915.57, 1277.44, 1701.5, 124.94, 3064.78, 1583.04, 2015.31, 1612.24, 1391.07, 3973.85, 3512.24, 2569.3, 2768.03, 2665.2, 2047.23, 659.23, 844.52, 2983.51, 726.22, 1411.01, 74.47, 1220.9, 678.75, 155.08, 184.14, 1139.03],
89 2010: [124.36, 145.58, 2562.81, 554.48, 1095.28, 1631.08, 1050.15, 1302.9, 114.15, 2540.1, 1360.56, 1729.02, 1363.67, 1206.98, 3588.28, 3258.09, 2147, 2325.5, 2286.98, 1675.06, 539.83, 685.38, 2482.89, 625.03, 1108.38, 68.72, 988.45, 599.28, 134.92, 159.29, 1078.63],
90 2009: [118.29, 128.85, 2207.34, 477.59, 929.6, 1414.9, 980.57, 1154.33, 113.82, 2261.86, 1163.08, 1495.45, 1182.74, 1098.66, 3226.64, 2769.05, 1795.9, 1969.69, 2010.27, 1458.49, 462.19, 606.8, 2240.61, 550.27, 1067.6, 63.88, 789.64, 497.05, 107.4, 127.25, 759.74],
91 2008: [112.83, 122.58, 2034.59, 313.58, 907.95, 1302.02, 916.72, 1088.94, 111.8, 2100.11, 1095.96, 1418.09, 1158.17, 1060.38, 3002.65, 2658.78, 1780, 1892.4, 1973.05, 1453.75, 436.04, 575.4, 2216.15, 539.19, 1020.56, 60.62, 753.72, 462.27, 105.57, 118.94, 691.07],
92 2007: [101.26, 110.19, 1804.72, 311.97, 762.1, 1133.42, 783.8, 915.38, 101.84, 1816.31, 986.02, 1200.18, 1002.11, 905.77, 2509.14, 2217.66, 1378, 1626.48, 1695.57, 1241.35, 361.07, 482.39, 2032, 446.38, 837.35, 54.89, 592.63, 387.55, 83.41, 97.89, 628.72],
93 2006: [88.8, 103.35, 1461.81, 276.77, 634.94, 939.43, 672.76, 750.14, 93.81, 1545.05, 925.1, 1011.03, 865.98, 786.14, 2138.9, 1916.74, 1140.41, 1272.2, 1532.17, 1032.47, 323.48, 386.38, 1595.48, 382.06, 724.4, 50.9, 484.81, 334, 67.55, 79.54, 527.8],
94 2005: [88.68, 112.38, 1400, 262.42, 589.56, 882.41, 625.61, 684.6, 90.26, 1461.51, 892.83, 966.5, 827.36, 727.37, 1963.51, 1892.01, 1082.13, 1100.65, 1428.27, 912.5, 300.75, 463.4, 1481.14, 368.94, 661.69, 48.04, 435.77, 308.06, 65.34, 72.07, 509.99],
95 2004: [87.36, 105.28, 1370.43, 276.3, 522.8, 798.43, 568.69, 605.79, 83.45, 1367.58, 814.1, 950.5, 786.84, 664.5, 1778.45, 1649.29, 1020.09, 1022.45, 1248.59, 817.88, 278.76, 428.05, 1379.93, 334.5, 607.75, 44.3, 387.88, 286.78, 60.7, 65.33, 461.26],
96 2003: [84.11, 89.91, 1064.05, 215.19, 420.1, 615.8, 488.23, 504.8, 81.02, 1162.45, 717.85, 749.4, 692.94, 560, 1480.67, 1198.7, 798.35, 886.47, 1072.91, 658.78, 244.29, 339.06, 1128.61, 298.69, 494.6, 40.7, 302.66, 237.91, 48.47, 55.63, 412.9],
97 2002: [82.44, 84.21, 956.84, 197.8, 374.69, 590.2, 446.17, 474.2, 79.68, 1110.44, 685.2, 783.66, 664.78, 535.98, 1390, 1288.36, 707, 847.25, 1015.08, 601.99, 222.89, 317.87, 1047.95, 281.1, 463.44, 39.75, 282.21, 215.51, 47.31, 52.95, 305]
98 })
99
100 dataMap.dataSI = dataFormatter({
101 2011: [3752.48, 5928.32, 13126.86, 6635.26, 8037.69, 12152.15, 5611.48, 5962.41, 7927.89, 25203.28, 16555.58, 8309.38, 9069.2, 6390.55, 24017.11, 15427.08, 9815.94, 9361.99, 26447.38, 5675.32, 714.5, 5543.04, 11029.13, 2194.33, 3780.32, 208.79, 6935.59, 2377.83, 975.18, 1056.15, 3225.9],
102 2010: [3388.38, 4840.23, 10707.68, 5234, 6367.69, 9976.82, 4506.31, 5025.15, 7218.32, 21753.93, 14297.93, 6436.62, 7522.83, 5122.88, 21238.49, 13226.38, 7767.24, 7343.19, 23014.53, 4511.68, 571, 4359.12, 8672.18, 1800.06, 3223.49, 163.92, 5446.1, 1984.97, 744.63, 827.91, 2592.15],
103 2009: [2855.55, 3987.84, 8959.83, 3993.8, 5114, 7906.34, 3541.92, 4060.72, 6001.78, 18566.37, 11908.49, 4905.22, 6005.3, 3919.45, 18901.83, 11010.5, 6038.08, 5687.19, 19419.7, 3381.54, 443.43, 3448.77, 6711.87, 1476.62, 2582.53, 136.63, 4236.42, 1527.24, 575.33, 662.32, 1929.59],
104 2008: [2626.41, 3709.78, 8701.34, 4242.36, 4376.19, 7158.84, 3097.12, 4319.75, 6085.84, 16993.34, 11567.42, 4198.93, 5318.44, 3554.81, 17571.98, 10259.99, 5082.07, 5028.93, 18502.2, 3037.74, 423.55, 3057.78, 5823.39, 1370.03, 2452.75, 115.56, 3861.12, 1470.34, 557.12, 609.98, 2070.76],
105 2007: [2509.4, 2892.53, 7201.88, 3454.49, 3193.67, 5544.14, 2475.45, 3695.58, 5571.06, 14471.26, 10154.25, 3370.96, 4476.42, 2975.53, 14647.53, 8282.83, 4143.06, 3977.72, 16004.61, 2425.29, 364.26, 2368.53, 4648.79, 1124.79, 2038.39, 98.48, 2986.46, 1279.32, 419.03, 455.04, 1647.55],
106 2006: [2191.43, 2457.08, 6110.43, 2755.66, 2374.96, 4566.83, 1915.29, 3365.31, 4969.95, 12282.89, 8511.51, 2711.18, 3695.04, 2419.74, 12574.03, 6724.61, 3365.08, 3187.05, 13469.77, 1878.56, 308.62, 1871.65, 3775.14, 967.54, 1705.83, 80.1, 2452.44, 1043.19, 331.91, 351.58, 1459.3],
107 2005: [2026.51, 2135.07, 5271.57, 2357.04, 1773.21, 3869.4, 1580.83, 2971.68, 4381.2, 10524.96, 7164.75, 2245.9, 3175.92, 1917.47, 10478.62, 5514.14, 2852.12, 2612.57, 11356.6, 1510.68, 240.83, 1564, 3067.23, 821.16, 1426.42, 63.52, 1951.36, 838.56, 264.61, 281.05, 1164.79],
108 2004: [1853.58, 1685.93, 4301.73, 1919.4, 1248.27, 3061.62, 1329.68, 2487.04, 3892.12, 8437.99, 6250.38, 1844.9, 2770.49, 1566.4, 8478.69, 4182.1, 2320.6, 2190.54, 9280.73, 1253.7, 205.6, 1376.91, 2489.4, 681.5, 1281.63, 52.74, 1553.1, 713.3, 211.7, 244.05, 914.47],
109 2003: [1487.15, 1337.31, 3417.56, 1463.38, 967.49, 2898.89, 1098.37, 2084.7, 3209.02, 6787.11, 5096.38, 1535.29, 2340.82, 1204.33, 6485.05, 3310.14, 1956.02, 1777.74, 7592.78, 984.08, 175.82, 1135.31, 2014.8, 569.37, 1047.66, 47.64, 1221.17, 572.02, 171.92, 194.27, 719.54],
110 2002: [1249.99, 1069.08, 2911.69, 1134.31, 754.78, 2609.85, 943.49, 1843.6, 2622.45, 5604.49, 4090.48, 1337.04, 2036.97, 941.77, 5184.98, 2768.75, 1709.89, 1523.5, 6143.4, 846.89, 148.88, 958.87, 1733.38, 481.96, 934.88, 32.72, 1007.56, 501.69, 144.51, 153.06, 603.15]
111 })
112
113 dataMap.dataTI = dataFormatter({
114 2011: [12363.18, 5219.24, 8483.17, 3960.87, 5015.89, 8158.98, 3679.91, 4918.09, 11142.86, 20842.21, 14180.23, 4975.96, 6878.74, 3921.2, 17370.89, 7991.72, 7247.02, 7539.54, 24097.7, 3998.33, 1148.93, 3623.81, 7014.04, 2781.29, 3701.79, 322.57, 4355.81, 1963.79, 540.18, 861.92, 2245.12],
115 2010: [10600.84, 4238.65, 7123.77, 3412.38, 4209.03, 6849.37, 3111.12, 4040.55, 9833.51, 17131.45, 12063.82, 4193.69, 5850.62, 3121.4, 14343.14, 6607.89, 6053.37, 6369.27, 20711.55, 3383.11, 953.67, 2881.08, 6030.41, 2177.07, 2892.31, 274.82, 3688.93, 1536.5, 470.88, 702.45, 1766.69],
116 2009: [9179.19, 3405.16, 6068.31, 2886.92, 3696.65, 5891.25, 2756.26, 3371.95, 8930.85, 13629.07, 9918.78, 3662.15, 5048.49, 2637.07, 11768.18, 5700.91, 5127.12, 5402.81, 18052.59, 2919.13, 748.59, 2474.44, 5198.8, 1885.79, 2519.62, 240.85, 3143.74, 1363.27, 398.54, 563.74, 1587.72],
117 2008: [8375.76, 2886.65, 5276.04, 2759.46, 3212.06, 5207.72, 2412.26, 2905.68, 7872.23, 11888.53, 8799.31, 3234.64, 4346.4, 2355.86, 10358.64, 5099.76, 4466.85, 4633.67, 16321.46, 2529.51, 643.47, 2160.48, 4561.69, 1652.34, 2218.81, 218.67, 2699.74, 1234.21, 355.93, 475, 1421.38],
118 2007: [7236.15, 2250.04, 4600.72, 2257.99, 2467.41, 4486.74, 2025.44, 2493.04, 6821.11, 9730.91, 7613.46, 2789.78, 3770, 1918.95, 8620.24, 4511.97, 3812.34, 3835.4, 14076.83, 2156.76, 528.84, 1825.21, 3881.6, 1312.94, 1896.78, 188.06, 2178.2, 1037.11, 294.91, 366.18, 1246.89],
119 2006: [5837.55, 1902.31, 3895.36, 1846.18, 1934.35, 3798.26, 1687.07, 2096.35, 5508.48, 7914.11, 6281.86, 2390.29, 3022.83, 1614.65, 7187.26, 3721.44, 3111.98, 3229.42, 11585.82, 1835.12, 433.57, 1649.2, 3319.62, 989.38, 1557.91, 159.76, 1806.36, 900.16, 249.04, 294.78, 1058.16],
120 2005: [4854.33, 1658.19, 3340.54, 1611.07, 1542.26, 3295.45, 1413.83, 1857.42, 4776.2, 6612.22, 5360.1, 2137.77, 2551.41, 1411.92, 5924.74, 3181.27, 2655.94, 2882.88, 9772.5, 1560.92, 377.17, 1440.32, 2836.73, 815.32, 1374.62, 137.24, 1546.59, 787.36, 213.37, 259.49, 929.41],
121 2004: [4092.27, 1319.76, 2805.47, 1375.67, 1270, 2811.95, 1223.64, 1657.77, 4097.26, 5198.03, 4584.22, 1963.9, 2206.02, 1225.8, 4764.7, 2722.4, 2292.55, 2428.95, 8335.3, 1361.92, 335.3, 1229.62, 2510.3, 661.8, 1192.53, 123.3, 1234.6, 688.41, 193.7, 227.73, 833.36],
122 2003: [3435.95, 1150.81, 2439.68, 1176.65, 1000.79, 2487.85, 1075.48, 1467.9, 3404.19, 4493.31, 3890.79, 1638.42, 1949.91, 1043.08, 4112.43, 2358.86, 2003.08, 1995.78, 7178.94, 1178.25, 293.85, 1081.35, 2189.68, 558.28, 1013.76, 96.76, 1063.89, 589.91, 169.81, 195.46, 753.91],
123 2002: [2982.57, 997.47, 2149.75, 992.69, 811.47, 2258.17, 958.88, 1319.4, 3038.9, 3891.92, 3227.99, 1399.02, 1765.8, 972.73, 3700.52, 1978.37, 1795.93, 1780.79, 6343.94, 1074.85, 270.96, 956.12, 1943.68, 480.37, 914.5, 89.56, 963.62, 514.83, 148.83, 171.14, 704.5]
124 })
125
126 dataMap.dataEstate = dataFormatter({
127 2011: [1074.93, 411.46, 918.02, 224.91, 384.76, 876.12, 238.61, 492.1, 1019.68, 2747.89, 1677.13, 634.92, 911.16, 402.51, 1838.14, 987, 634.67, 518.04, 3321.31, 465.68, 208.71, 396.28, 620.62, 160.3, 222.31, 17.44, 398.03, 134.25, 29.05, 79.01, 176.22],
128 2010: [1006.52, 377.59, 697.79, 192, 309.25, 733.37, 212.32, 391.89, 1002.5, 2600.95, 1618.17, 532.17, 679.03, 340.56, 1622.15, 773.23, 564.41, 464.21, 2813.95, 405.79, 188.33, 266.38, 558.56, 139.64, 223.45, 14.54, 315.95, 110.02, 25.41, 60.53, 143.44],
129 2009: [1062.47, 308.73, 612.4, 173.31, 286.65, 605.27, 200.14, 301.18, 1237.56, 2025.39, 1316.84, 497.94, 656.61, 305.9, 1329.59, 622.98, 546.11, 400.11, 2470.63, 348.98, 121.76, 229.09, 548.14, 136.15, 205.14, 13.28, 239.92, 101.37, 23.05, 47.56, 115.23],
130 2008: [844.59, 227.88, 513.81, 166.04, 273.3, 500.81, 182.7, 244.47, 939.34, 1626.13, 1052.03, 431.27, 506.98, 281.96, 1104.95, 512.42, 526.88, 340.07, 2057.45, 282.96, 95.6, 191.21, 453.63, 104.81, 195.48, 15.08, 193.27, 93.8, 19.96, 38.85, 89.79],
131 2007: [821.5, 183.44, 467.97, 134.12, 191.01, 410.43, 153.03, 225.81, 958.06, 1365.71, 981.42, 366.57, 511.5, 225.96, 953.69, 447.44, 409.65, 301.8, 2029.77, 239.45, 67.19, 196.06, 376.84, 93.19, 193.59, 13.24, 153.98, 83.52, 16.98, 29.49, 91.28],
132 2006: [658.3, 156.64, 397.14, 117.01, 136.5, 318.54, 131.01, 194.7, 773.61, 1017.91, 794.41, 281.98, 435.22, 184.67, 786.51, 348.7, 294.73, 254.81, 1722.07, 192.2, 44.45, 158.2, 336.2, 80.24, 165.92, 11.92, 125.2, 73.21, 15.17, 25.53, 68.9],
133 2005: [493.73, 122.67, 330.87, 106, 98.75, 256.77, 112.29, 163.34, 715.97, 799.73, 688.86, 231.66, 331.8, 171.88, 664.9, 298.19, 217.17, 215.63, 1430.37, 165.05, 38.2, 143.88, 286.23, 76.38, 148.69, 10.02, 108.62, 63.78, 14.1, 22.97, 55.79],
134 2004: [436.11, 106.14, 231.08, 95.1, 73.81, 203.1, 97.93, 137.74, 666.3, 534.17, 587.83, 188.28, 248.44, 167.2, 473.27, 236.44, 204.8, 191.5, 1103.75, 122.52, 30.64, 129.12, 264.3, 68.3, 116.54, 5.8, 95.9, 56.84, 13, 20.78, 53.55],
135 2003: [341.88, 92.31, 185.19, 78.73, 61.05, 188.49, 91.99, 127.2, 487.82, 447.47, 473.16, 162.63, 215.84, 138.02, 418.21, 217.58, 176.8, 186.49, 955.66, 100.93, 25.14, 113.69, 231.72, 59.86, 103.79, 4.35, 83.9, 48.09, 11.41, 16.85, 47.84],
136 2002: [298.02, 73.04, 140.89, 65.83, 51.48, 130.94, 76.11, 118.7, 384.86, 371.09, 360.63, 139.18, 188.09, 125.27, 371.13, 199.31, 145.17, 165.29, 808.16, 82.83, 21.45, 90.48, 210.82, 53.49, 95.68, 3.42, 77.68, 41.52, 9.74, 13.46, 43.04]
137 })
138
139 dataMap.dataFinancial = dataFormatter({
140 2011: [2215.41, 756.5, 746.01, 519.32, 447.46, 755.57, 207.65, 370.78, 2277.4, 2600.11, 2730.29, 503.85, 862.41, 357.44, 1640.41, 868.2, 674.57, 501.09, 2916.13, 445.37, 105.24, 704.66, 868.15, 297.27, 456.23, 31.7, 432.11, 145.05, 62.56, 134.18, 288.77],
141 2010: [1863.61, 572.99, 615.42, 448.3, 346.44, 639.27, 190.12, 304.59, 1950.96, 2105.92, 2326.58, 396.17, 767.58, 241.49, 1361.45, 697.68, 561.27, 463.16, 2658.76, 384.53, 78.12, 496.56, 654.7, 231.51, 375.08, 27.08, 384.75, 100.54, 54.53, 97.87, 225.2],
142 2009: [1603.63, 461.2, 525.67, 361.64, 291.1, 560.2, 180.83, 227.54, 1804.28, 1596.98, 1899.33, 359.6, 612.2, 165.1, 1044.9, 499.92, 479.11, 402.57, 2283.29, 336.82, 65.73, 389.97, 524.63, 194.44, 351.74, 23.17, 336.21, 88.27, 45.63, 75.54, 198.87],
143 2008: [1519.19, 368.1, 420.74, 290.91, 219.09, 455.07, 147.24, 177.43, 1414.21, 1298.48, 1653.45, 313.81, 497.65, 130.57, 880.28, 413.83, 393.05, 334.32, 1972.4, 249.01, 47.33, 303.01, 411.14, 151.55, 277.66, 22.42, 287.16, 72.49, 36.54, 64.8, 171.97],
144 2007: [1302.77, 288.17, 347.65, 218.73, 148.3, 386.34, 126.03, 155.48, 1209.08, 1054.25, 1251.43, 223.85, 385.84, 101.34, 734.9, 302.31, 337.27, 260.14, 1705.08, 190.73, 34.43, 247.46, 359.11, 122.25, 168.55, 11.51, 231.03, 61.6, 27.67, 51.05, 149.22],
145 2006: [982.37, 186.87, 284.04, 169.63, 108.21, 303.41, 100.75, 74.17, 825.2, 653.25, 906.37, 166.01, 243.9, 79.75, 524.94, 219.72, 174.99, 204.72, 899.91, 129.14, 16.37, 213.7, 299.5, 89.43, 143.62, 6.44, 152.25, 50.51, 23.69, 36.99, 99.25],
146 2005: [840.2, 147.4, 213.47, 135.07, 72.52, 232.85, 83.63, 35.03, 675.12, 492.4, 686.32, 127.05, 186.12, 69.55, 448.36, 181.74, 127.32, 162.37, 661.81, 91.93, 13.16, 185.18, 262.26, 73.67, 130.5, 7.57, 127.58, 44.73, 20.36, 32.25, 80.34],
147 2004: [713.79, 136.97, 209.1, 110.29, 55.89, 188.04, 77.17, 32.2, 612.45, 440.5, 523.49, 94.1, 171, 65.1, 343.37, 170.82, 118.85, 118.64, 602.68, 74, 11.56, 162.38, 236.5, 60.3, 118.4, 5.4, 90.1, 42.99, 19, 27.92, 70.3],
148 2003: [635.56, 112.79, 199.87, 118.48, 55.89, 145.38, 73.15, 32.2, 517.97, 392.11, 451.54, 87.45, 150.09, 64.31, 329.71, 165.11, 107.31, 99.35, 534.28, 61.59, 10.68, 147.04, 206.24, 48.01, 105.48, 4.74, 77.87, 42.31, 17.98, 24.8, 64.92],
149 2002: [561.91, 76.86, 179.6, 124.1, 48.39, 137.18, 75.45, 31.6, 485.25, 368.86, 347.53, 81.85, 138.28, 76.51, 310.07, 158.77, 96.95, 92.43, 454.65, 35.86, 10.08, 134.52, 183.13, 41.45, 102.39, 2.81, 67.3, 42.08, 16.75, 21.45, 52.18]
150 })
151
152 this.chart.setOption({
153 baseOption: {
154 timeline: {
155 axisType: 'category',
156 autoPlay: true,
157 playInterval: 1000,
158 data: [
159 '2002-01-01', '2003-01-01', '2004-01-01',
160 {
161 value: '2005-01-01',
162 tooltip: {
163 formatter: '{b} GDP达到一个高度'
164 },
165 symbol: 'diamond',
166 symbolSize: 16
167 },
168 '2006-01-01', '2007-01-01', '2008-01-01', '2009-01-01', '2010-01-01',
169 {
170 value: '2011-01-01',
171 tooltip: {
172 formatter: function(params) {
173 return params.name + 'GDP达到又一个高度'
174 }
175 },
176 symbol: 'diamond',
177 symbolSize: 18
178 }
179 ],
180 label: {
181 formatter: function(s) {
182 return (new Date(s)).getFullYear()
183 }
184 }
185 },
186 title: {
187 subtext: '数据来自国家统计局'
188 },
189 tooltip: {},
190 legend: {
191 x: 'right',
192 data: ['第一产业', '第二产业', '第三产业', 'GDP', '金融', '房地产'],
193 selected: {
194 'GDP': false, '金融': false, '房地产': false
195 }
196 },
197 calculable: true,
198 grid: {
199 top: 80,
200 bottom: 100,
201 tooltip: {
202 trigger: 'axis',
203 axisPointer: {
204 type: 'shadow',
205 label: {
206 show: true,
207 formatter: function(params) {
208 return params.value.replace('\n', '')
209 }
210 }
211 }
212 }
213 },
214 xAxis: [
215 {
216 'type': 'category',
217 'axisLabel': {
218 'interval': 0,
219 rotate: 45
220 },
221 'data': [
222 '北京', '\n天津', '河北', '\n山西', '内蒙古', '\n辽宁', '吉林', '\n黑龙江',
223 '上海', '\n江苏', '浙江', '\n安徽', '福建', '\n江西', '山东', '\n河南',
224 '湖北', '\n湖南', '广东', '\n广西', '海南', '\n重庆', '四川', '\n贵州',
225 '云南', '\n西藏', '陕西', '\n甘肃', '青海', '\n宁夏', '新疆'
226 ],
227 splitLine: { show: false }
228 }
229 ],
230 yAxis: [
231 {
232 type: 'value',
233 name: 'GDP(亿元)'
234 }
235 ],
236 series: [
237 { name: 'GDP', type: 'bar' },
238 { name: '金融', type: 'bar' },
239 { name: '房地产', type: 'bar' },
240 { name: '第一产业', type: 'bar' },
241 { name: '第二产业', type: 'bar' },
242 { name: '第三产业', type: 'bar' },
243 {
244 name: 'GDP占比',
245 type: 'pie',
246 center: ['75%', '35%'],
247 radius: '28%',
248 z: 100
249 }
250 ]
251 },
252 options: [
253 {
254 title: { text: '2002全国宏观经济指标' },
255 series: [
256 { data: dataMap.dataGDP['2002'] },
257 { data: dataMap.dataFinancial['2002'] },
258 { data: dataMap.dataEstate['2002'] },
259 { data: dataMap.dataPI['2002'] },
260 { data: dataMap.dataSI['2002'] },
261 { data: dataMap.dataTI['2002'] },
262 {
263 data: [
264 { name: '第一产业', value: dataMap.dataPI['2002sum'] },
265 { name: '第二产业', value: dataMap.dataSI['2002sum'] },
266 { name: '第三产业', value: dataMap.dataTI['2002sum'] }
267 ]
268 }
269 ]
270 },
271 {
272 title: { text: '2003全国宏观经济指标' },
273 series: [
274 { data: dataMap.dataGDP['2003'] },
275 { data: dataMap.dataFinancial['2003'] },
276 { data: dataMap.dataEstate['2003'] },
277 { data: dataMap.dataPI['2003'] },
278 { data: dataMap.dataSI['2003'] },
279 { data: dataMap.dataTI['2003'] },
280 {
281 data: [
282 { name: '第一产业', value: dataMap.dataPI['2003sum'] },
283 { name: '第二产业', value: dataMap.dataSI['2003sum'] },
284 { name: '第三产业', value: dataMap.dataTI['2003sum'] }
285 ]
286 }
287 ]
288 },
289 {
290 title: { text: '2004全国宏观经济指标' },
291 series: [
292 { data: dataMap.dataGDP['2004'] },
293 { data: dataMap.dataFinancial['2004'] },
294 { data: dataMap.dataEstate['2004'] },
295 { data: dataMap.dataPI['2004'] },
296 { data: dataMap.dataSI['2004'] },
297 { data: dataMap.dataTI['2004'] },
298 {
299 data: [
300 { name: '第一产业', value: dataMap.dataPI['2004sum'] },
301 { name: '第二产业', value: dataMap.dataSI['2004sum'] },
302 { name: '第三产业', value: dataMap.dataTI['2004sum'] }
303 ]
304 }
305 ]
306 },
307 {
308 title: { text: '2005全国宏观经济指标' },
309 series: [
310 { data: dataMap.dataGDP['2005'] },
311 { data: dataMap.dataFinancial['2005'] },
312 { data: dataMap.dataEstate['2005'] },
313 { data: dataMap.dataPI['2005'] },
314 { data: dataMap.dataSI['2005'] },
315 { data: dataMap.dataTI['2005'] },
316 {
317 data: [
318 { name: '第一产业', value: dataMap.dataPI['2005sum'] },
319 { name: '第二产业', value: dataMap.dataSI['2005sum'] },
320 { name: '第三产业', value: dataMap.dataTI['2005sum'] }
321 ]
322 }
323 ]
324 },
325 {
326 title: { text: '2006全国宏观经济指标' },
327 series: [
328 { data: dataMap.dataGDP['2006'] },
329 { data: dataMap.dataFinancial['2006'] },
330 { data: dataMap.dataEstate['2006'] },
331 { data: dataMap.dataPI['2006'] },
332 { data: dataMap.dataSI['2006'] },
333 { data: dataMap.dataTI['2006'] },
334 {
335 data: [
336 { name: '第一产业', value: dataMap.dataPI['2006sum'] },
337 { name: '第二产业', value: dataMap.dataSI['2006sum'] },
338 { name: '第三产业', value: dataMap.dataTI['2006sum'] }
339 ]
340 }
341 ]
342 },
343 {
344 title: { text: '2007全国宏观经济指标' },
345 series: [
346 { data: dataMap.dataGDP['2007'] },
347 { data: dataMap.dataFinancial['2007'] },
348 { data: dataMap.dataEstate['2007'] },
349 { data: dataMap.dataPI['2007'] },
350 { data: dataMap.dataSI['2007'] },
351 { data: dataMap.dataTI['2007'] },
352 {
353 data: [
354 { name: '第一产业', value: dataMap.dataPI['2007sum'] },
355 { name: '第二产业', value: dataMap.dataSI['2007sum'] },
356 { name: '第三产业', value: dataMap.dataTI['2007sum'] }
357 ]
358 }
359 ]
360 },
361 {
362 title: { text: '2008全国宏观经济指标' },
363 series: [
364 { data: dataMap.dataGDP['2008'] },
365 { data: dataMap.dataFinancial['2008'] },
366 { data: dataMap.dataEstate['2008'] },
367 { data: dataMap.dataPI['2008'] },
368 { data: dataMap.dataSI['2008'] },
369 { data: dataMap.dataTI['2008'] },
370 {
371 data: [
372 { name: '第一产业', value: dataMap.dataPI['2008sum'] },
373 { name: '第二产业', value: dataMap.dataSI['2008sum'] },
374 { name: '第三产业', value: dataMap.dataTI['2008sum'] }
375 ]
376 }
377 ]
378 },
379 {
380 title: { text: '2009全国宏观经济指标' },
381 series: [
382 { data: dataMap.dataGDP['2009'] },
383 { data: dataMap.dataFinancial['2009'] },
384 { data: dataMap.dataEstate['2009'] },
385 { data: dataMap.dataPI['2009'] },
386 { data: dataMap.dataSI['2009'] },
387 { data: dataMap.dataTI['2009'] },
388 {
389 data: [
390 { name: '第一产业', value: dataMap.dataPI['2009sum'] },
391 { name: '第二产业', value: dataMap.dataSI['2009sum'] },
392 { name: '第三产业', value: dataMap.dataTI['2009sum'] }
393 ]
394 }
395 ]
396 },
397 {
398 title: { text: '2010全国宏观经济指标' },
399 series: [
400 { data: dataMap.dataGDP['2010'] },
401 { data: dataMap.dataFinancial['2010'] },
402 { data: dataMap.dataEstate['2010'] },
403 { data: dataMap.dataPI['2010'] },
404 { data: dataMap.dataSI['2010'] },
405 { data: dataMap.dataTI['2010'] },
406 {
407 data: [
408 { name: '第一产业', value: dataMap.dataPI['2010sum'] },
409 { name: '第二产业', value: dataMap.dataSI['2010sum'] },
410 { name: '第三产业', value: dataMap.dataTI['2010sum'] }
411 ]
412 }
413 ]
414 },
415 {
416 title: { text: '2011全国宏观经济指标' },
417 series: [
418 { data: dataMap.dataGDP['2011'] },
419 { data: dataMap.dataFinancial['2011'] },
420 { data: dataMap.dataEstate['2011'] },
421 { data: dataMap.dataPI['2011'] },
422 { data: dataMap.dataSI['2011'] },
423 { data: dataMap.dataTI['2011'] },
424 {
425 data: [
426 { name: '第一产业', value: dataMap.dataPI['2011sum'] },
427 { name: '第二产业', value: dataMap.dataSI['2011sum'] },
428 { name: '第三产业', value: dataMap.dataTI['2011sum'] }
429 ]
430 }
431 ]
432 }
433 ]
434 })
435 }
436 }
437 }
438 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 title: {
54 text: '漏斗图',
55 subtext: '纯属虚构'
56 },
57 tooltip: {
58 trigger: 'item',
59 formatter: '{a} <br/>{b} : {c}%'
60 },
61 toolbox: {
62 feature: {
63 dataView: { readOnly: false },
64 restore: {},
65 saveAsImage: {}
66 }
67 },
68 legend: {
69 data: ['展现', '点击', '访问', '咨询', '订单']
70 },
71 calculable: true,
72 series: [
73 {
74 name: '漏斗图',
75 type: 'funnel',
76 left: '10%',
77 top: 60,
78 bottom: 60,
79 width: '80%',
80 // height: {totalHeight} - y - y2,
81 min: 0,
82 max: 100,
83 minSize: '0%',
84 maxSize: '100%',
85 sort: 'descending',
86 gap: 2,
87 label: {
88 show: true,
89 position: 'inside'
90 },
91 labelLine: {
92 length: 10,
93 lineStyle: {
94 width: 1,
95 type: 'solid'
96 }
97 },
98 itemStyle: {
99 borderColor: '#fff',
100 borderWidth: 1
101 },
102 emphasis: {
103 label: {
104 fontSize: 20
105 }
106 },
107 data: [
108 { value: 60, name: '访问' },
109 { value: 40, name: '咨询' },
110 { value: 20, name: '订单' },
111 { value: 80, name: '点击' },
112 { value: 100, name: '展现' }
113 ]
114 }
115 ]
116 })
117 }
118 }
119 }
120 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 tooltip: {
54 formatter: '{a} <br/>{b} : {c}%'
55 },
56 toolbox: {
57 feature: {
58 restore: {},
59 saveAsImage: {}
60 }
61 },
62 series: [
63 {
64 name: '业务指标',
65 type: 'gauge',
66 detail: { formatter: '{value}%' },
67 data: [{ value: 50, name: '完成率' }]
68 }
69 ]
70 })
71 }
72 }
73 }
74 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51 const axisData = ['周一', '周二', '周三', '很长很长的周四', '周五', '周六', '周日']
52 const data = axisData.map(function(item, i) {
53 return Math.round(Math.random() * 1000 * (i + 1))
54 })
55 const links = data.map(function(item, i) {
56 return {
57 source: i,
58 target: i + 1
59 }
60 })
61 links.pop()
62 this.chart.setOption({
63 title: {
64 text: '笛卡尔坐标系上的 Graph'
65 },
66 tooltip: {},
67 xAxis: {
68 type: 'category',
69 boundaryGap: false,
70 data: axisData
71 },
72 yAxis: {
73 type: 'value'
74 },
75 series: [
76 {
77 type: 'graph',
78 layout: 'none',
79 coordinateSystem: 'cartesian2d',
80 symbolSize: 40,
81 label: {
82 normal: {
83 show: true
84 }
85 },
86 edgeSymbol: ['circle', 'arrow'],
87 edgeSymbolSize: [4, 10],
88 data: data,
89 links: links,
90 lineStyle: {
91 normal: {
92 color: '#2f4554'
93 }
94 }
95 }
96 ]
97 })
98 }
99 }
100 }
101 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 tooltip: {
54 position: 'top'
55 },
56 animation: false,
57 grid: {
58 height: '50%',
59 y: '10%'
60 },
61 xAxis: {
62 type: 'category',
63 data: ['12a', '1a', '2a', '3a', '4a', '5a', '6a', '7a', '8a', '9a', '10a', '11a', '12p', '1p', '2p', '3p', '4p', '5p', '6p', '7p', '8p', '9p', '10p', '11p'],
64 splitArea: {
65 show: true
66 }
67 },
68 yAxis: {
69 type: 'category',
70 data: ['Saturday', 'Friday', 'Thursday', 'Wednesday', 'Tuesday', 'Monday', 'Sunday'],
71 splitArea: {
72 show: true
73 }
74 },
75 visualMap: {
76 min: 0,
77 max: 10,
78 calculable: true,
79 orient: 'horizontal',
80 left: 'center',
81 bottom: '15%'
82 },
83 series: [{
84 name: 'Punch Card',
85 type: 'heatmap',
86 data: [[0, 0, 5], [0, 1, 1], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 2], [0, 12, 4], [0, 13, 1], [0, 14, 1], [0, 15, 3], [0, 16, 4], [0, 17, 6], [0, 18, 4], [0, 19, 4], [0, 20, 3], [0, 21, 3], [0, 22, 2], [0, 23, 5], [1, 0, 7], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [1, 6, 0], [1, 7, 0], [1, 8, 0], [1, 9, 0], [1, 10, 5], [1, 11, 2], [1, 12, 2], [1, 13, 6], [1, 14, 9], [1, 15, 11], [1, 16, 6], [1, 17, 7], [1, 18, 8], [1, 19, 12], [1, 20, 5], [1, 21, 5], [1, 22, 7], [1, 23, 2], [2, 0, 1], [2, 1, 1], [2, 2, 0], [2, 3, 0], [2, 4, 0], [2, 5, 0], [2, 6, 0], [2, 7, 0], [2, 8, 0], [2, 9, 0], [2, 10, 3], [2, 11, 2], [2, 12, 1], [2, 13, 9], [2, 14, 8], [2, 15, 10], [2, 16, 6], [2, 17, 5], [2, 18, 5], [2, 19, 5], [2, 20, 7], [2, 21, 4], [2, 22, 2], [2, 23, 4], [3, 0, 7], [3, 1, 3], [3, 2, 0], [3, 3, 0], [3, 4, 0], [3, 5, 0], [3, 6, 0], [3, 7, 0], [3, 8, 1], [3, 9, 0], [3, 10, 5], [3, 11, 4], [3, 12, 7], [3, 13, 14], [3, 14, 13], [3, 15, 12], [3, 16, 9], [3, 17, 5], [3, 18, 5], [3, 19, 10], [3, 20, 6], [3, 21, 4], [3, 22, 4], [3, 23, 1], [4, 0, 1], [4, 1, 3], [4, 2, 0], [4, 3, 0], [4, 4, 0], [4, 5, 1], [4, 6, 0], [4, 7, 0], [4, 8, 0], [4, 9, 2], [4, 10, 4], [4, 11, 4], [4, 12, 2], [4, 13, 4], [4, 14, 4], [4, 15, 14], [4, 16, 12], [4, 17, 1], [4, 18, 8], [4, 19, 5], [4, 20, 3], [4, 21, 7], [4, 22, 3], [4, 23, 0], [5, 0, 2], [5, 1, 1], [5, 2, 0], [5, 3, 3], [5, 4, 0], [5, 5, 0], [5, 6, 0], [5, 7, 0], [5, 8, 2], [5, 9, 0], [5, 10, 4], [5, 11, 1], [5, 12, 5], [5, 13, 10], [5, 14, 5], [5, 15, 7], [5, 16, 11], [5, 17, 6], [5, 18, 0], [5, 19, 5], [5, 20, 3], [5, 21, 4], [5, 22, 2], [5, 23, 0], [6, 0, 1], [6, 1, 0], [6, 2, 0], [6, 3, 0], [6, 4, 0], [6, 5, 0], [6, 6, 0], [6, 7, 0], [6, 8, 0], [6, 9, 0], [6, 10, 1], [6, 11, 0], [6, 12, 2], [6, 13, 1], [6, 14, 3], [6, 15, 4], [6, 16, 0], [6, 17, 0], [6, 18, 0], [6, 19, 0], [6, 20, 1], [6, 21, 2], [6, 22, 2], [6, 23, 6]].map(function(item) {
87 return [item[1], item[0], item[2] || '-']
88 }),
89 label: {
90 normal: {
91 show: true
92 }
93 },
94 itemStyle: {
95 emphasis: {
96 shadowBlur: 10,
97 shadowColor: 'rgba(0, 0, 0, 0.5)'
98 }
99 }
100 }]
101 })
102 }
103 }
104 }
105 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 import 'echarts-gl'
8
9 require('echarts/theme/macarons') // echarts theme
10 import { debounce } from '@/utils'
11
12 export default {
13 props: {
14 className: {
15 type: String,
16 default: 'chart'
17 },
18 width: {
19 type: String,
20 default: '100%'
21 },
22 height: {
23 type: String,
24 default: '300px'
25 }
26 },
27 data() {
28 return {
29 chart: null
30 }
31 },
32 mounted() {
33 this.initChart()
34 this.__resizeHandler = debounce(() => {
35 if (this.chart) {
36 this.chart.resize()
37 }
38 }, 100)
39 window.addEventListener('resize', this.__resizeHandler)
40 },
41 beforeDestroy() {
42 if (!this.chart) {
43 return
44 }
45 window.removeEventListener('resize', this.__resizeHandler)
46 this.chart.dispose()
47 this.chart = null
48 },
49 methods: {
50 initChart() {
51 this.chart = echarts.init(this.$el, 'macarons')
52 const data = []
53 for (let t = 0; t < 25; t += 0.001) {
54 const x = (1 + 0.25 * Math.cos(75 * t)) * Math.cos(t)
55 const y = (1 + 0.25 * Math.cos(75 * t)) * Math.sin(t)
56 const z = t + 2.0 * Math.sin(75 * t)
57 data.push([x, y, z])
58 }
59 this.chart.setOption({
60 tooltip: {},
61 backgroundColor: '#fff',
62 visualMap: {
63 show: false,
64 dimension: 2,
65 min: 0,
66 max: 30,
67 inRange: {
68 color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
69 }
70 },
71 xAxis3D: {
72 type: 'value'
73 },
74 yAxis3D: {
75 type: 'value'
76 },
77 zAxis3D: {
78 type: 'value'
79 },
80 grid3D: {
81 viewControl: {
82 projection: 'orthographic'
83 }
84 },
85 series: [{
86 type: 'line3D',
87 data: data,
88 lineStyle: {
89 width: 4
90 }
91 }]
92 })
93 }
94 }
95 }
96 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 require('echarts/theme/macarons') // echarts theme
8 import { debounce } from '@/utils'
9
10 export default {
11 props: {
12 className: {
13 type: String,
14 default: 'chart'
15 },
16 width: {
17 type: String,
18 default: '100%'
19 },
20 height: {
21 type: String,
22 default: '300px'
23 },
24 option: {
25 type: Object,
26 default: () => {}
27 },
28 optionData: {
29 type: Array,
30 default: () => []
31 }
32 },
33 data() {
34 return {
35 chart: null
36 }
37 },
38 watch: {
39 option: {
40 handler(newVal) {
41 console.log('获取配置', newVal)
42 if (this.chart) {
43 this.setOption()
44 }
45 },
46 immediate: true,
47 deep: true
48 },
49 optionData: {
50 handler(newVal) {
51 console.log('获取数据', newVal)
52 if (this.chart) {
53 this.initData()
54 }
55 },
56 immediate: true,
57 deep: true
58 }
59 },
60 mounted() {
61 this.initChart()
62 this.__resizeHandler = debounce(() => {
63 if (this.chart) {
64 this.chart.resize()
65 }
66 }, 100)
67 window.addEventListener('resize', this.__resizeHandler)
68 },
69 beforeDestroy() {
70 if (!this.chart) {
71 return
72 }
73 window.removeEventListener('resize', this.__resizeHandler)
74 this.chart.dispose()
75 this.chart = null
76 },
77 methods: {
78 initChart() {
79 this.chart = echarts.init(this.$el, 'macarons')
80
81 const option = {
82 tooltip: {
83 trigger: 'item',
84 formatter: '{a} <br/>{b} : {c} ({d}%)'
85 },
86 legend: {
87 left: 'center',
88 bottom: '10',
89 data: []
90 },
91 calculable: true,
92 series: [
93 {
94 type: 'pie',
95 center: ['50%', '50%'],
96 data: [],
97 animationEasing: 'cubicInOut',
98 animationDuration: 2000
99 }
100 ]
101 }
102 this.chart.setOption(option)
103 if (Object.keys(this.option).length > 0) {
104 this.setOption()
105 }
106 if (this.optionData.length > 0) {
107 this.initData()
108 }
109 },
110 initData() {
111 const legendData = []
112 const seriesData = []
113 this.optionData.forEach(item => {
114 legendData.push(item.name)
115 seriesData.push(item)
116 })
117 const option = {
118 legend: {
119 data: legendData
120 },
121 series: [
122 {
123 data: seriesData
124 }
125 ]
126 }
127 this.chart.setOption(option)
128 },
129 setOption() {
130 this.chart.setOption(this.option)
131 }
132 }
133 }
134 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 title: {
54 text: 'Graph 简单示例'
55 },
56 tooltip: {},
57 animationDurationUpdate: 1500,
58 animationEasingUpdate: 'quinticInOut',
59 series: [
60 {
61 type: 'graph',
62 layout: 'none',
63 symbolSize: 50,
64 roam: true,
65 label: {
66 normal: {
67 show: true
68 }
69 },
70 edgeSymbol: ['circle', 'arrow'],
71 edgeSymbolSize: [4, 10],
72 edgeLabel: {
73 normal: {
74 textStyle: {
75 fontSize: 20
76 }
77 }
78 },
79 data: [{
80 name: '节点1',
81 x: 100,
82 y: 300
83 }, {
84 name: '节点2',
85 x: 1000,
86 y: 300
87 }, {
88 name: '节点3',
89 x: 550,
90 y: 100
91 }, {
92 name: '节点4',
93 x: 550,
94 y: 500
95 }],
96 // links: [],
97 links: [{
98 source: 0,
99 target: 1,
100 symbolSize: [5, 20],
101 label: {
102 normal: {
103 show: true
104 }
105 },
106 lineStyle: {
107 normal: {
108 width: 5,
109 curveness: 0.2
110 }
111 }
112 }, {
113 source: '节点2',
114 target: '节点1',
115 label: {
116 normal: {
117 show: true
118 }
119 },
120 lineStyle: {
121 normal: { curveness: 0.2 }
122 }
123 }, {
124 source: '节点1',
125 target: '节点3'
126 }, {
127 source: '节点2',
128 target: '节点3'
129 }, {
130 source: '节点2',
131 target: '节点4'
132 }, {
133 source: '节点1',
134 target: '节点4'
135 }],
136 lineStyle: {
137 normal: {
138 opacity: 0.9,
139 width: 2,
140 curveness: 0
141 }
142 }
143 }
144 ]
145 })
146 }
147 }
148 }
149 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 require('echarts/theme/macarons') // echarts theme
8 import { debounce } from '@/utils'
9
10 const animationDuration = 3000
11
12 export default {
13 props: {
14 className: {
15 type: String,
16 default: 'chart'
17 },
18 width: {
19 type: String,
20 default: '100%'
21 },
22 height: {
23 type: String,
24 default: '300px'
25 }
26 },
27 data() {
28 return {
29 chart: null
30 }
31 },
32 mounted() {
33 this.initChart()
34 this.__resizeHandler = debounce(() => {
35 if (this.chart) {
36 this.chart.resize()
37 }
38 }, 100)
39 window.addEventListener('resize', this.__resizeHandler)
40 },
41 beforeDestroy() {
42 if (!this.chart) {
43 return
44 }
45 window.removeEventListener('resize', this.__resizeHandler)
46 this.chart.dispose()
47 this.chart = null
48 },
49 methods: {
50 initChart() {
51 this.chart = echarts.init(this.$el, 'macarons')
52
53 this.chart.setOption({
54 tooltip: {
55 trigger: 'axis',
56 axisPointer: { // 坐标轴指示器,坐标轴触发有效
57 type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
58 }
59 },
60 radar: {
61 radius: '66%',
62 center: ['50%', '42%'],
63 splitNumber: 8,
64 splitArea: {
65 areaStyle: {
66 color: 'rgba(127,95,132,.3)',
67 opacity: 1,
68 shadowBlur: 45,
69 shadowColor: 'rgba(0,0,0,.5)',
70 shadowOffsetX: 0,
71 shadowOffsetY: 15
72 }
73 },
74 indicator: [
75 { name: 'Sales', max: 10000 },
76 { name: 'Administration', max: 20000 },
77 { name: 'Information Techology', max: 20000 },
78 { name: 'Customer Support', max: 20000 },
79 { name: 'Development', max: 20000 },
80 { name: 'Marketing', max: 20000 }
81 ]
82 },
83 legend: {
84 left: 'center',
85 bottom: '10',
86 data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']
87 },
88 series: [{
89 type: 'radar',
90 symbolSize: 0,
91 areaStyle: {
92 normal: {
93 shadowBlur: 13,
94 shadowColor: 'rgba(0,0,0,.2)',
95 shadowOffsetX: 0,
96 shadowOffsetY: 10,
97 opacity: 1
98 }
99 },
100 data: [
101 {
102 value: [5000, 7000, 12000, 11000, 15000, 14000],
103 name: 'Allocated Budget'
104 },
105 {
106 value: [4000, 9000, 15000, 15000, 13000, 11000],
107 name: 'Expected Spending'
108 },
109 {
110 value: [5500, 11000, 12000, 15000, 12000, 12000],
111 name: 'Actual Spending'
112 }
113 ],
114 animationDuration: animationDuration
115 }]
116 })
117 }
118 }
119 }
120 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '500px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 tooltip: {
54 trigger: 'item',
55 formatter: '{a} <br/>{b}: {c} ({d}%)'
56 },
57 legend: {
58 orient: 'vertical',
59 x: 'left',
60 data: ['直达', '营销广告', '搜索引擎', '邮件营销', '联盟广告', '视频广告', '百度', '谷歌', '必应', '其他']
61 },
62 series: [
63 {
64 name: '访问来源',
65 type: 'pie',
66 selectedMode: 'single',
67 radius: [0, '30%'],
68
69 label: {
70 normal: {
71 position: 'inner'
72 }
73 },
74 labelLine: {
75 normal: {
76 show: false
77 }
78 },
79 data: [
80 { value: 335, name: '直达', selected: true },
81 { value: 679, name: '营销广告' },
82 { value: 1548, name: '搜索引擎' }
83 ]
84 },
85 {
86 name: '访问来源',
87 type: 'pie',
88 radius: ['40%', '55%'],
89 label: {
90 normal: {
91 formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
92 backgroundColor: '#eee',
93 borderColor: '#aaa',
94 borderWidth: 1,
95 borderRadius: 4,
96 shadowBlur: 3,
97 shadowOffsetX: 2,
98 shadowOffsetY: 2,
99 shadowColor: '#999',
100 padding: [0, 7],
101 rich: {
102 a: {
103 color: '#999',
104 lineHeight: 22,
105 align: 'center'
106 },
107 abg: {
108 backgroundColor: '#333',
109 width: '100%',
110 align: 'right',
111 height: 22,
112 borderRadius: [4, 4, 0, 0]
113 },
114 hr: {
115 borderColor: '#aaa',
116 width: '100%',
117 borderWidth: 0.5,
118 height: 0
119 },
120 b: {
121 fontSize: 16,
122 lineHeight: 33
123 },
124 per: {
125 color: '#eee',
126 backgroundColor: '#334455',
127 padding: [2, 4],
128 borderRadius: 2
129 }
130 }
131 }
132 },
133 data: [
134 { value: 335, name: '直达' },
135 { value: 310, name: '邮件营销' },
136 { value: 234, name: '联盟广告' },
137 { value: 135, name: '视频广告' },
138 { value: 1048, name: '百度' },
139 { value: 251, name: '谷歌' },
140 { value: 147, name: '必应' },
141 { value: 102, name: '其他' }
142 ]
143 }
144 ]
145 })
146 }
147 }
148 }
149 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53 series: {
54 type: 'sankey',
55 layout: 'none',
56 focusNodeAdjacency: 'allEdges',
57 data: [{
58 name: 'a'
59 }, {
60 name: 'b'
61 }, {
62 name: 'a1'
63 }, {
64 name: 'a2'
65 }, {
66 name: 'b1'
67 }, {
68 name: 'c'
69 }],
70 links: [{
71 source: 'a',
72 target: 'a1',
73 value: 5
74 }, {
75 source: 'a',
76 target: 'a2',
77 value: 3
78 }, {
79 source: 'b',
80 target: 'b1',
81 value: 8
82 }, {
83 source: 'a',
84 target: 'b1',
85 value: 3
86 }, {
87 source: 'b1',
88 target: 'a1',
89 value: 1
90 }, {
91 source: 'b1',
92 target: 'c',
93 value: 2
94 }]
95 }
96 })
97 }
98 }
99 }
100 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51 const data = [
52 [[28604, 77, 17096869, 'Australia', 1990], [31163, 77.4, 27662440, 'Canada', 1990], [1516, 68, 1154605773, 'China', 1990], [13670, 74.7, 10582082, 'Cuba', 1990], [28599, 75, 4986705, 'Finland', 1990], [29476, 77.1, 56943299, 'France', 1990], [31476, 75.4, 78958237, 'Germany', 1990], [28666, 78.1, 254830, 'Iceland', 1990], [1777, 57.7, 870601776, 'India', 1990], [29550, 79.1, 122249285, 'Japan', 1990], [2076, 67.9, 20194354, 'North Korea', 1990], [12087, 72, 42972254, 'South Korea', 1990], [24021, 75.4, 3397534, 'New Zealand', 1990], [43296, 76.8, 4240375, 'Norway', 1990], [10088, 70.8, 38195258, 'Poland', 1990], [19349, 69.6, 147568552, 'Russia', 1990], [10670, 67.3, 53994605, 'Turkey', 1990], [26424, 75.7, 57110117, 'United Kingdom', 1990], [37062, 75.4, 252847810, 'United States', 1990]],
53 [[44056, 81.8, 23968973, 'Australia', 2015], [43294, 81.7, 35939927, 'Canada', 2015], [13334, 76.9, 1376048943, 'China', 2015], [21291, 78.5, 11389562, 'Cuba', 2015], [38923, 80.8, 5503457, 'Finland', 2015], [37599, 81.9, 64395345, 'France', 2015], [44053, 81.1, 80688545, 'Germany', 2015], [42182, 82.8, 329425, 'Iceland', 2015], [5903, 66.8, 1311050527, 'India', 2015], [36162, 83.5, 126573481, 'Japan', 2015], [1390, 71.4, 25155317, 'North Korea', 2015], [34644, 80.7, 50293439, 'South Korea', 2015], [34186, 80.6, 4528526, 'New Zealand', 2015], [64304, 81.6, 5210967, 'Norway', 2015], [24787, 77.3, 38611794, 'Poland', 2015], [23038, 73.13, 143456918, 'Russia', 2015], [19360, 76.5, 78665830, 'Turkey', 2015], [38225, 81.4, 64715810, 'United Kingdom', 2015], [53354, 79.1, 321773631, 'United States', 2015]]
54 ]
55 this.chart.setOption({
56 title: {
57 text: '1990 与 2015 年各国家人均寿命与 GDP'
58 },
59 legend: {
60 right: 10,
61 data: ['1990', '2015']
62 },
63 xAxis: {
64 splitLine: {
65 lineStyle: {
66 type: 'dashed'
67 }
68 }
69 },
70 yAxis: {
71 splitLine: {
72 lineStyle: {
73 type: 'dashed'
74 }
75 },
76 scale: true
77 },
78 series: [{
79 name: '1990',
80 data: data[0],
81 type: 'scatter',
82 symbolSize: function(data) {
83 return Math.sqrt(data[2]) / 5e2
84 },
85 label: {
86 emphasis: {
87 show: true,
88 formatter: function(param) {
89 return param.data[3]
90 },
91 position: 'top'
92 }
93 },
94 itemStyle: {
95 normal: {
96 shadowBlur: 10,
97 shadowColor: 'rgba(120, 36, 50, 0.5)',
98 shadowOffsetY: 5,
99 color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
100 offset: 0,
101 color: 'rgb(251, 118, 123)'
102 }, {
103 offset: 1,
104 color: 'rgb(204, 46, 72)'
105 }])
106 }
107 }
108 }, {
109 name: '2015',
110 data: data[1],
111 type: 'scatter',
112 symbolSize: function(data) {
113 return Math.sqrt(data[2]) / 5e2
114 },
115 label: {
116 emphasis: {
117 show: true,
118 formatter: function(param) {
119 return param.data[3]
120 },
121 position: 'top'
122 }
123 },
124 itemStyle: {
125 normal: {
126 shadowBlur: 10,
127 shadowColor: 'rgba(25, 100, 150, 0.5)',
128 shadowOffsetY: 5,
129 color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
130 offset: 0,
131 color: 'rgb(129, 227, 238)'
132 }, {
133 offset: 1,
134 color: 'rgb(25, 183, 207)'
135 }])
136 }
137 }
138 }]
139 })
140 }
141 }
142 }
143 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '300px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51 const data = [{
52 name: 'Grandpa',
53 children: [{
54 name: 'Uncle Leo',
55 value: 15,
56 children: [{
57 name: 'Cousin Jack',
58 value: 2
59 }, {
60 name: 'Cousin Mary',
61 value: 5,
62 children: [{
63 name: 'Jackson',
64 value: 2
65 }]
66 }, {
67 name: 'Cousin Ben',
68 value: 4
69 }]
70 }, {
71 name: 'Father',
72 value: 10,
73 children: [{
74 name: 'Me',
75 value: 5
76 }, {
77 name: 'Brother Peter',
78 value: 1
79 }]
80 }]
81 }, {
82 name: 'Nancy',
83 children: [{
84 name: 'Uncle Nike',
85 children: [{
86 name: 'Cousin Betty',
87 value: 1
88 }, {
89 name: 'Cousin Jenny',
90 value: 2
91 }]
92 }]
93 }]
94 this.chart.setOption({
95 series: {
96 type: 'sunburst',
97 data: data,
98 radius: [0, '90%'],
99 label: {
100 rotate: 'radial'
101 }
102 }
103 })
104 }
105 }
106 }
107 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10
11 export default {
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '500px'
24 }
25 },
26 data() {
27 return {
28 chart: null
29 }
30 },
31 mounted() {
32 this.initChart()
33 this.__resizeHandler = debounce(() => {
34 if (this.chart) {
35 this.chart.resize()
36 }
37 }, 100)
38 window.addEventListener('resize', this.__resizeHandler)
39 },
40 beforeDestroy() {
41 if (!this.chart) {
42 return
43 }
44 window.removeEventListener('resize', this.__resizeHandler)
45 this.chart.dispose()
46 this.chart = null
47 },
48 methods: {
49 initChart() {
50 this.chart = echarts.init(this.$el, 'macarons')
51
52 this.chart.setOption({
53
54 tooltip: {
55 trigger: 'axis',
56 axisPointer: {
57 type: 'line',
58 lineStyle: {
59 color: 'rgba(0,0,0,0.2)',
60 width: 1,
61 type: 'solid'
62 }
63 }
64 },
65
66 legend: {
67 data: ['DQ', 'TY', 'SS', 'QG', 'SY', 'DD']
68 },
69
70 singleAxis: {
71 top: 50,
72 bottom: 50,
73 axisTick: {},
74 axisLabel: {},
75 type: 'time',
76 axisPointer: {
77 animation: true,
78 label: {
79 show: true
80 }
81 },
82 splitLine: {
83 show: true,
84 lineStyle: {
85 type: 'dashed',
86 opacity: 0.2
87 }
88 }
89 },
90
91 series: [
92 {
93 type: 'themeRiver',
94 itemStyle: {
95 emphasis: {
96 shadowBlur: 20,
97 shadowColor: 'rgba(0, 0, 0, 0.8)'
98 }
99 },
100 data: [['2015/11/08', 10, 'DQ'], ['2015/11/09', 15, 'DQ'], ['2015/11/10', 35, 'DQ'],
101 ['2015/11/11', 38, 'DQ'], ['2015/11/12', 22, 'DQ'], ['2015/11/13', 16, 'DQ'],
102 ['2015/11/14', 7, 'DQ'], ['2015/11/15', 2, 'DQ'], ['2015/11/16', 17, 'DQ'],
103 ['2015/11/17', 33, 'DQ'], ['2015/11/18', 40, 'DQ'], ['2015/11/19', 32, 'DQ'],
104 ['2015/11/20', 26, 'DQ'], ['2015/11/21', 35, 'DQ'], ['2015/11/22', 40, 'DQ'],
105 ['2015/11/23', 32, 'DQ'], ['2015/11/24', 26, 'DQ'], ['2015/11/25', 22, 'DQ'],
106 ['2015/11/26', 16, 'DQ'], ['2015/11/27', 22, 'DQ'], ['2015/11/28', 10, 'DQ'],
107 ['2015/11/08', 35, 'TY'], ['2015/11/09', 36, 'TY'], ['2015/11/10', 37, 'TY'],
108 ['2015/11/11', 22, 'TY'], ['2015/11/12', 24, 'TY'], ['2015/11/13', 26, 'TY'],
109 ['2015/11/14', 34, 'TY'], ['2015/11/15', 21, 'TY'], ['2015/11/16', 18, 'TY'],
110 ['2015/11/17', 45, 'TY'], ['2015/11/18', 32, 'TY'], ['2015/11/19', 35, 'TY'],
111 ['2015/11/20', 30, 'TY'], ['2015/11/21', 28, 'TY'], ['2015/11/22', 27, 'TY'],
112 ['2015/11/23', 26, 'TY'], ['2015/11/24', 15, 'TY'], ['2015/11/25', 30, 'TY'],
113 ['2015/11/26', 35, 'TY'], ['2015/11/27', 42, 'TY'], ['2015/11/28', 42, 'TY'],
114 ['2015/11/08', 21, 'SS'], ['2015/11/09', 25, 'SS'], ['2015/11/10', 27, 'SS'],
115 ['2015/11/11', 23, 'SS'], ['2015/11/12', 24, 'SS'], ['2015/11/13', 21, 'SS'],
116 ['2015/11/14', 35, 'SS'], ['2015/11/15', 39, 'SS'], ['2015/11/16', 40, 'SS'],
117 ['2015/11/17', 36, 'SS'], ['2015/11/18', 33, 'SS'], ['2015/11/19', 43, 'SS'],
118 ['2015/11/20', 40, 'SS'], ['2015/11/21', 34, 'SS'], ['2015/11/22', 28, 'SS'],
119 ['2015/11/23', 26, 'SS'], ['2015/11/24', 37, 'SS'], ['2015/11/25', 41, 'SS'],
120 ['2015/11/26', 46, 'SS'], ['2015/11/27', 47, 'SS'], ['2015/11/28', 41, 'SS'],
121 ['2015/11/08', 10, 'QG'], ['2015/11/09', 15, 'QG'], ['2015/11/10', 35, 'QG'],
122 ['2015/11/11', 38, 'QG'], ['2015/11/12', 22, 'QG'], ['2015/11/13', 16, 'QG'],
123 ['2015/11/14', 7, 'QG'], ['2015/11/15', 2, 'QG'], ['2015/11/16', 17, 'QG'],
124 ['2015/11/17', 33, 'QG'], ['2015/11/18', 40, 'QG'], ['2015/11/19', 32, 'QG'],
125 ['2015/11/20', 26, 'QG'], ['2015/11/21', 35, 'QG'], ['2015/11/22', 40, 'QG'],
126 ['2015/11/23', 32, 'QG'], ['2015/11/24', 26, 'QG'], ['2015/11/25', 22, 'QG'],
127 ['2015/11/26', 16, 'QG'], ['2015/11/27', 22, 'QG'], ['2015/11/28', 10, 'QG'],
128 ['2015/11/08', 10, 'SY'], ['2015/11/09', 15, 'SY'], ['2015/11/10', 35, 'SY'],
129 ['2015/11/11', 38, 'SY'], ['2015/11/12', 22, 'SY'], ['2015/11/13', 16, 'SY'],
130 ['2015/11/14', 7, 'SY'], ['2015/11/15', 2, 'SY'], ['2015/11/16', 17, 'SY'],
131 ['2015/11/17', 33, 'SY'], ['2015/11/18', 40, 'SY'], ['2015/11/19', 32, 'SY'],
132 ['2015/11/20', 26, 'SY'], ['2015/11/21', 35, 'SY'], ['2015/11/22', 4, 'SY'],
133 ['2015/11/23', 32, 'SY'], ['2015/11/24', 26, 'SY'], ['2015/11/25', 22, 'SY'],
134 ['2015/11/26', 16, 'SY'], ['2015/11/27', 22, 'SY'], ['2015/11/28', 10, 'SY'],
135 ['2015/11/08', 10, 'DD'], ['2015/11/09', 15, 'DD'], ['2015/11/10', 35, 'DD'],
136 ['2015/11/11', 38, 'DD'], ['2015/11/12', 22, 'DD'], ['2015/11/13', 16, 'DD'],
137 ['2015/11/14', 7, 'DD'], ['2015/11/15', 2, 'DD'], ['2015/11/16', 17, 'DD'],
138 ['2015/11/17', 33, 'DD'], ['2015/11/18', 4, 'DD'], ['2015/11/19', 32, 'DD'],
139 ['2015/11/20', 26, 'DD'], ['2015/11/21', 35, 'DD'], ['2015/11/22', 40, 'DD'],
140 ['2015/11/23', 32, 'DD'], ['2015/11/24', 26, 'DD'], ['2015/11/25', 22, 'DD'],
141 ['2015/11/26', 16, 'DD'], ['2015/11/27', 22, 'DD'], ['2015/11/28', 10, 'DD']]
142 }
143 ]
144 })
145 }
146 }
147 }
148 </script>
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 require('echarts-wordcloud')
8 require('echarts/theme/macarons') // echarts theme
9 import { debounce } from '@/utils'
10 export default {
11 props: {
12 className: {
13 type: String,
14 default: 'chart'
15 },
16 width: {
17 type: String,
18 default: '100%'
19 },
20 height: {
21 type: String,
22 default: '300px'
23 }
24 },
25 data() {
26 return {
27 chart: null
28 }
29 },
30 mounted() {
31 this.initChart()
32 this.__resizeHandler = debounce(() => {
33 if (this.chart) {
34 this.chart.resize()
35 }
36 }, 100)
37 window.addEventListener('resize', this.__resizeHandler)
38 },
39 beforeDestroy() {
40 if (!this.chart) {
41 return
42 }
43 window.removeEventListener('resize', this.__resizeHandler)
44 this.chart.dispose()
45 this.chart = null
46 },
47 methods: {
48 initChart() {
49 this.chart = echarts.init(this.$el, 'macarons')
50 const data = [{
51 'name': '花鸟市场',
52 'value': 1446
53 },
54 {
55 'name': '汽车',
56 'value': 928
57 },
58 {
59 'name': '视频',
60 'value': 906
61 },
62 {
63 'name': '电视',
64 'value': 825
65 },
66 {
67 'name': 'Lover Boy',
68 'value': 514
69 },
70 {
71 'name': '动漫',
72 'value': 486
73 },
74 {
75 'name': '音乐',
76 'value': 53
77 },
78 {
79 'name': '直播',
80 'value': 163
81 },
82 {
83 'name': '广播电台',
84 'value': 86
85 },
86 {
87 'name': '戏曲曲艺',
88 'value': 17
89 },
90 {
91 'name': '演出票务',
92 'value': 6
93 },
94 {
95 'name': '给陌生的你听',
96 'value': 1
97 },
98 {
99 'name': '资讯',
100 'value': 1437
101 },
102 {
103 'name': '商业财经',
104 'value': 422
105 },
106 {
107 'name': '娱乐八卦',
108 'value': 353
109 },
110 {
111 'name': '军事',
112 'value': 331
113 },
114 {
115 'name': '科技资讯',
116 'value': 313
117 },
118 {
119 'name': '社会时政',
120 'value': 307
121 },
122 {
123 'name': '时尚',
124 'value': 43
125 },
126 {
127 'name': '网络奇闻',
128 'value': 15
129 },
130 {
131 'name': '旅游出行',
132 'value': 438
133 },
134 {
135 'name': '景点类型',
136 'value': 957
137 },
138 {
139 'name': '国内游',
140 'value': 927
141 },
142 {
143 'name': '远途出行方式',
144 'value': 908
145 },
146 {
147 'name': '酒店',
148 'value': 693
149 },
150 {
151 'name': '关注景点',
152 'value': 611
153 },
154 {
155 'name': '旅游网站偏好',
156 'value': 512
157 },
158 {
159 'name': '出国游',
160 'value': 382
161 }]
162 this.chart.setOption({
163 backgroundColor: '#fff',
164 tooltip: {
165 show: false
166 },
167 series: [{
168 type: 'wordCloud',
169 gridSize: 1,
170 sizeRange: [12, 55],
171 rotationRange: [-45, 0, 45, 90],
172 textStyle: {
173 normal: {
174 color: function() {
175 return 'rgb(' +
176 Math.round(Math.random() * 255) +
177 ', ' + Math.round(Math.random() * 255) +
178 ', ' + Math.round(Math.random() * 255) + ')'
179 }
180 }
181 },
182 left: 'center',
183 top: 'center',
184 right: null,
185 bottom: null,
186 data: data
187 }]
188 })
189 }
190 }
191 }
192 </script>
1 <template>
2 <a href="https://github.com/elunez/eladmin" target="_blank" class="github-corner" aria-label="View source on Github">
3 <svg
4 width="80"
5 height="80"
6 viewBox="0 0 250 250"
7 style="fill:#40c9c6; color:#fff;"
8 aria-hidden="true"
9 >
10 <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
11 <path
12 d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
13 fill="currentColor"
14 style="transform-origin: 130px 106px;"
15 class="octo-arm"
16 />
17 <path
18 d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
19 fill="currentColor"
20 class="octo-body"
21 />
22 </svg>
23 </a>
24 </template>
25
26 <style scoped>
27 .github-corner:hover .octo-arm {
28 animation: octocat-wave 560ms ease-in-out
29 }
30
31 @keyframes octocat-wave {
32 0%,
33 100% {
34 transform: rotate(0)
35 }
36 20%,
37 60% {
38 transform: rotate(-25deg)
39 }
40 40%,
41 80% {
42 transform: rotate(10deg)
43 }
44 }
45
46 @media (max-width:500px) {
47 .github-corner:hover .octo-arm {
48 animation: none
49 }
50 .github-corner .octo-arm {
51 animation: octocat-wave 560ms ease-in-out
52 }
53 }
54 </style>
1 <template>
2 <div style="padding: 0 15px;" @click="toggleClick">
3 <svg
4 :class="{'is-active':isActive}"
5 class="hamburger"
6 viewBox="0 0 1024 1024"
7 xmlns="http://www.w3.org/2000/svg"
8 width="64"
9 height="64"
10 >
11 <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
12 </svg>
13 </div>
14 </template>
15
16 <script>
17 export default {
18 name: 'Hamburger',
19 props: {
20 isActive: {
21 type: Boolean,
22 default: false
23 }
24 },
25 methods: {
26 toggleClick() {
27 this.$emit('toggleClick')
28 }
29 }
30 }
31 </script>
32
33 <style scoped>
34 .hamburger {
35 display: inline-block;
36 vertical-align: middle;
37 width: 20px;
38 height: 20px;
39 }
40
41 .hamburger.is-active {
42 transform: rotate(180deg);
43 }
44 </style>
1 <template>
2 <div :class="{'show':show}" class="header-search">
3 <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
4 <el-select
5 ref="headerSearchSelect"
6 v-model="search"
7 :remote-method="querySearch"
8 filterable
9 default-first-option
10 remote
11 placeholder="Search"
12 class="header-search-select"
13 @change="change"
14 >
15 <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
16 </el-select>
17 </div>
18 </template>
19
20 <script>
21 // fuse is a lightweight fuzzy-search module
22 // make search results more in line with expectations
23 import Fuse from 'fuse.js'
24 import path from 'path'
25
26 export default {
27 name: 'HeaderSearch',
28 data() {
29 return {
30 search: '',
31 options: [],
32 searchPool: [],
33 show: false,
34 fuse: undefined
35 }
36 },
37 computed: {
38 routes() {
39 return this.$store.state.permission.routers
40 }
41 },
42 watch: {
43 routes() {
44 this.searchPool = this.generateRoutes(this.routes)
45 },
46 searchPool(list) {
47 this.initFuse(list)
48 },
49 show(value) {
50 if (value) {
51 document.body.addEventListener('click', this.close)
52 } else {
53 document.body.removeEventListener('click', this.close)
54 }
55 }
56 },
57 mounted() {
58 this.searchPool = this.generateRoutes(this.routes)
59 },
60 methods: {
61 click() {
62 this.show = !this.show
63 if (this.show) {
64 this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
65 }
66 },
67 close() {
68 this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
69 this.options = []
70 this.show = false
71 },
72 change(val) {
73 if (this.ishttp(val.path)) {
74 // http(s):// 路径新窗口打开
75 window.open(val.path, '_blank')
76 } else {
77 this.$router.push(val.path)
78 }
79 this.search = ''
80 this.options = []
81 this.$nextTick(() => {
82 this.show = false
83 })
84 },
85 initFuse(list) {
86 this.fuse = new Fuse(list, {
87 shouldSort: true,
88 threshold: 0.4,
89 location: 0,
90 distance: 100,
91 maxPatternLength: 32,
92 minMatchCharLength: 1,
93 keys: [{
94 name: 'title',
95 weight: 0.7
96 }, {
97 name: 'path',
98 weight: 0.3
99 }]
100 })
101 },
102 // Filter out the routes that can be displayed in the sidebar
103 // And generate the internationalized title
104 generateRoutes(routes, basePath = '/', prefixTitle = []) {
105 let res = []
106
107 for (const router of routes) {
108 // skip hidden router
109 if (router.hidden) { continue }
110
111 const data = {
112 path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
113 title: [...prefixTitle]
114 }
115
116 if (router.meta && router.meta.title) {
117 data.title = [...data.title, router.meta.title]
118
119 if (router.redirect !== 'noRedirect') {
120 // only push the routes with title
121 // special case: need to exclude parent router without redirect
122 res.push(data)
123 }
124 }
125
126 // recursive child routes
127 if (router.children) {
128 const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
129 if (tempRoutes.length >= 1) {
130 res = [...res, ...tempRoutes]
131 }
132 }
133 }
134 return res
135 },
136 querySearch(query) {
137 if (query !== '') {
138 this.options = this.fuse.search(query)
139 } else {
140 this.options = []
141 }
142 },
143 ishttp(url) {
144 return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
145 }
146 }
147 }
148 </script>
149
150 <style lang="scss" scoped>
151 .header-search {
152 font-size: 0 !important;
153
154 .search-icon {
155 cursor: pointer;
156 font-size: 18px;
157 vertical-align: middle;
158 }
159
160 .header-search-select {
161 font-size: 18px;
162 transition: width 0.2s;
163 width: 0;
164 overflow: hidden;
165 background: transparent;
166 border-radius: 0;
167 display: inline-block;
168 vertical-align: middle;
169
170 ::v-deep .el-input__inner {
171 border-radius: 0;
172 border: 0;
173 padding-left: 0;
174 padding-right: 0;
175 box-shadow: none !important;
176 border-bottom: 1px solid #d9d9d9;
177 vertical-align: middle;
178 }
179 }
180
181 &.show {
182 .header-search-select {
183 width: 210px;
184 margin-left: 10px;
185 }
186 }
187 }
188 </style>
1 <!-- @author zhengjie -->
2 <template>
3 <div class="icon-body">
4 <el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
5 <i slot="suffix" class="el-icon-search el-input__icon" />
6 </el-input>
7 <div class="icon-list">
8 <div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
9 <svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
10 <span>{{ item }}</span>
11 </div>
12 </div>
13 </div>
14 </template>
15
16 <script>
17 import icons from './requireIcons'
18 export default {
19 name: 'IconSelect',
20 data() {
21 return {
22 name: '',
23 iconList: icons
24 }
25 },
26 methods: {
27 filterIcons() {
28 this.iconList = icons
29 if (this.name) {
30 this.iconList = this.iconList.filter(item => item.includes(this.name))
31 }
32 },
33 selectedIcon(name) {
34 this.$emit('selected', name)
35 document.body.click()
36 },
37 reset() {
38 this.name = ''
39 this.iconList = icons
40 }
41 }
42 }
43 </script>
44
45 <style rel="stylesheet/scss" lang="scss" scoped>
46 .icon-body {
47 width: 100%;
48 padding: 10px;
49 .icon-list {
50 height: 200px;
51 overflow-y: scroll;
52 div {
53 height: 30px;
54 line-height: 30px;
55 margin-bottom: -5px;
56 cursor: pointer;
57 width: 33%;
58 float: left;
59 }
60 span {
61 display: inline-block;
62 vertical-align: -0.15em;
63 fill: currentColor;
64 overflow: hidden;
65 }
66 }
67 }
68 </style>
1
2 const req = require.context('../../assets/icons/svg', false, /\.svg$/)
3 const requireAll = requireContext => requireContext.keys()
4
5 const re = /\.\/(.*)\.svg/
6
7 const icons = requireAll(req).map(i => {
8 return i.match(re)[1]
9 })
10
11 export default icons
1 <template>
2 <div v-loading="loading" :style="'height:'+ height">
3 <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" />
4 </div>
5 </template>
6 <script>
7 export default {
8 props: {
9 src: {
10 type: String,
11 required: true
12 }
13 },
14 data() {
15 return {
16 height: document.documentElement.clientHeight - 94.5 + 'px;',
17 loading: true
18 }
19 },
20 mounted: function() {
21 setTimeout(() => {
22 this.loading = false
23 }, 230)
24 const that = this
25 window.onresize = function temp() {
26 that.height = document.documentElement.clientHeight - 94.5 + 'px;'
27 }
28 }
29 }
30 </script>
1 <template>
2 <div>
3 <el-upload
4 :action="baseUrl ? baseUrl : imageApi"
5 :auto-upload="true"
6 :data="uploadData"
7 :headers="headers"
8 :on-success="handleSuccess"
9 :on-error="handleError"
10 :on-exceed="handleExceed"
11 :before-upload="beforeUpload"
12 :file-list="fileList"
13 :disabled="isDisabled"
14 :limit="limit"
15 :class="{single:singleImage}"
16 list-type="picture-card"
17 name="multipartFile"
18 accept="image/*"
19 >
20 <i slot="default" class="el-icon-plus" />
21 <div slot="file" slot-scope="{file}">
22 <!-- <span-->
23 <!-- style="position: absolute; z-index: 999; padding-left: 5px; font-size: 12px;color: #ffffff;-->
24 <!-- background-color: rgba(0, 0, 0, 0.5); padding-right: 5px; border-bottom-right-radius: 0.5em"-->
25 <!-- >-->
26 <!-- {{ file.hasOwnProperty("type") ? file.type : ((file.hasOwnProperty("response") ? file.response.type : -1))-->
27 <!-- | dictFilter(dictMap.image_type ? dictMap.image_type : [])-->
28 <!-- | dictFilter(dictMap.image_type_show ? dictMap.image_type_show : []) }}-->
29 <!-- </span>-->
30 <!-- 于2020/10/14为了ugc基地址进行修改 file.url-->
31 <img
32 :src="baseUrl ? baseUrl + file.fileUrl : file.url"
33 class="el-upload-list__item-thumbnail"
34 alt=""
35 >
36 <span class="el-upload-list__item-actions">
37 <span
38 class="el-upload-list__item-preview"
39 @click="handlePictureCardPreview(file)"
40 >
41 <i class="el-icon-zoom-in" />
42 </span>
43 <!-- <span-->
44 <!-- v-if="!readOnlyMode"-->
45 <!-- class="el-upload-list__item-delete"-->
46 <!-- @click="handleChangeType(file)"-->
47 <!-- >-->
48 <!-- <i class="el-icon-picture" />-->
49 <!-- </span>-->
50 <span
51 v-if="!readOnlyMode"
52 class="el-upload-list__item-delete"
53 @click="handleRemove(file)"
54 >
55 <i class="el-icon-delete" />
56 </span>
57 <span
58 v-if="isArticle"
59 class="el-upload-list__item-delete"
60 @click="handleAddDesc(file)"
61 >
62 <i class="el-icon-edit" />
63 </span>
64 </span>
65 </div>
66 </el-upload>
67 <el-dialog :visible.sync="dialogVisible" :append-to-body="true">
68 <img :src="dialogImageUrl" width="100%" alt="">
69 </el-dialog>
70 </div>
71 </template>
72 <script>
73 import { getAttrByValueFromDict, convertImageArr2Json, evil, deepCopy } from '../../utils/common-util'
74 import { mapGetters } from 'vuex'
75 import { getToken } from '@/utils/auth'
76 import initDict from '@/mixins/initDict'
77 import { Loading } from 'element-ui'
78
79 export default {
80 name: 'ImagesUpload',
81 mixins: [initDict],
82 props: {
83 isDisabled: {
84 type: Boolean,
85 required: false,
86 default() {
87 return false
88 }
89 },
90 isSubject: {
91 type: Boolean,
92 required: false,
93 default() {
94 return true
95 }
96 },
97 image: {
98 type: String,
99 default: '[]'
100 },
101 images: {
102 type: String,
103 default: '{"list":[],"map":{}}'
104 },
105 uploadEntity: {
106 type: String,
107 required: true
108 },
109 limit: {
110 type: Number,
111 default: 10
112 },
113 singleMode: {
114 type: Boolean,
115 default: false
116 },
117 readOnlyMode: {
118 type: Boolean,
119 default: false
120 },
121 isArticle: {
122 type: Boolean,
123 default: false
124 },
125 baseUrl: {
126 type: String,
127 default() {
128 return ''
129 }
130 }
131 },
132 data() {
133 return {
134 headers: {
135 'Authorization': 'Bearer ' + getToken()
136 },
137 fileList: [],
138 uploadData: { fileType: 'image' },
139 dialogImageUrl: '',
140 dialogVisible: false,
141 dialogImageTypeVisible: false,
142 imageType: '-1',
143 imageSelect: {},
144 imgDesc: {
145 desc: ''
146 },
147 isUploading: false,
148 loading: null,
149 imageDesc: false
150 }
151 },
152 computed: {
153 ...mapGetters(['imageApi']),
154 singleImage: function() {
155 return (this.singleMode && (this.fileList.length > 0 || this.isUploading)) || this.readOnlyMode
156 }
157 },
158 watch: {
159 image: {
160 immediate: true,
161 handler(val) {
162 if (val === null || val === '') {
163 val = '[]'
164 }
165 const arr = evil(val)
166 // 2020/12/31 00:04,为了专题管理覆盖数据的操作,增加了isSubject参数
167 if (this.fileList.length === arr.length && this.isSubject) {
168 // 当图片数量不变时,filelist不响应image的变更,避免2次刷新
169 return
170 }
171 this.fileList = arr
172 this.fileList.forEach(file => {
173 if (!file.hasOwnProperty('url') && file.fileUrl.indexOf('http') === -1) {
174 file.url = this.imageApi + '/' + file.fileUrl
175 } else {
176 file.url = file.fileUrl
177 }
178 })
179 this.fileList = deepCopy(this.fileList)
180 }
181 }
182 },
183 created() {
184 this.$nextTick(() => {
185 this.getDictMap('image_type,image_type_show,image_size_limit')
186 })
187 },
188 updated() {
189 const images = []
190 this.fileList.forEach(file => {
191 if (file.response) {
192 images.push({
193 fileType: file.response[0].fileType,
194 fileUrl: file.response[0].fileUrl,
195 fileName: file.response[0].fileName
196 })
197 } else {
198 images.push({
199 fileType: file.fileType,
200 fileUrl: file.fileUrl,
201 fileName: file.fileName
202 })
203 }
204 })
205 console.log(images)
206 this.$emit('update:image', JSON.stringify(images))
207 },
208 methods: {
209 getAttrByValueFromDict,
210 deepCopy,
211 convertImageArr2Json,
212 beforeUpload(file) {
213 const isValid = (file.type === 'image/jpeg' || file.type === 'image/gif' || file.type === 'image/png')
214
215 const dict = this.dictMap.image_size_limit.find(item => item.label === this.uploadEntity)
216 let maxSize = dict ? parseFloat(dict.value) : 20 * 1024 // 默认2M
217 const isLtMaxSize = parseFloat(file.size / 1024) < maxSize
218
219 maxSize = maxSize > 1024 ? (parseFloat(maxSize / 1024).toFixed(2) + 'MB') : (maxSize + 'KB')
220 this.isUploading = true
221 if (!isValid) {
222 this.$message.error('上传图片只能是 JPG/PNG/GIF 格式!')
223 this.isUploading = false
224 }
225 if (!isLtMaxSize) {
226 this.$message.error('上传图片大小不能超过' + maxSize + ' !')
227 this.isUploading = false
228 }
229 if (isValid && isLtMaxSize) {
230 this.loading = Loading.service({
231 lock: true,
232 text: '解析中Loading',
233 spinner: 'el-icon-loading',
234 background: 'rgba(0, 0, 0, 0.7)'
235 })
236 }
237 return isValid && isLtMaxSize
238 },
239 handleRemove(file) {
240 this.fileList.splice(this.fileList.findIndex(
241 item => !item.hasOwnProperty('response') ? item.id === file.id : item.response.id === file.response.id), 1)
242 },
243 handlePictureCardPreview(file) {
244 this.dialogImageUrl = file.url
245 this.dialogVisible = true
246 },
247 handleChangeType(file) {
248 console.log(file)
249 this.imageSelect = file
250 // 2020/10/23上午,因为发现此处报错,注释
251 // this.imageType = file.hasOwnProperty('type') ? file.type.toString() : file.response.type.toString()
252 // 2020/11/10上午,需要标示上传图片类型,所以解锁,并置为-1
253 this.imageType = file.hasOwnProperty('type') ? file.type.toString() : file.hasOwnProperty('response') ? file.response.type.toString() : '-1'
254 // this.imageType = '-1'
255 this.dialogImageTypeVisible = true
256 },
257 // 增加描述
258 handleAddDesc(file) {
259 this.imgDesc = file
260 this.imageDesc = true
261 },
262 handleError(err, file) {
263 this.isUploading = false
264 this.loading.close()
265 this.$message.error(`图片上传失败,失败信息 ${err}`)
266 console.log(err)
267 },
268 handleSuccess(res, file, fileList) {
269 // console.log(res, file, fileList)
270 this.loading.close()
271 this.isUploading = false
272 this.fileList = fileList
273 },
274 handleExceed(files, fileList) {
275 this.$message.warning('图片数量无法大于' + this.limit + '张')
276 },
277 generateImageFromFileList(item) {
278 const images = []
279 this.fileList.forEach(file => {
280 if (file.hasOwnProperty('response')) {
281 images.push((({ id, type, width, height, fileUrl, size }) =>
282 ({ id, type, width, height, fileUrl, size }))(file.response))
283 } else {
284 images.push((({ id, type, width, height, fileUrl, size }) =>
285 ({ id, type, width, height, fileUrl, size }))(file))
286 }
287 })
288 item.image = JSON.stringify(images)
289 item.images = this.convertImageArr2Json(item.image, this.dictMap.image_type)
290 },
291 changeImageType() {
292 const index = this.fileList.findIndex(
293 item => !item.hasOwnProperty('response') ? item.id === this.imageSelect.id : item.response.id === this.imageSelect.response.id)
294 if (this.fileList[index].hasOwnProperty('response')) {
295 this.fileList[index].response.type = parseInt(this.imageType)
296 } else {
297 this.fileList[index].type = parseInt(this.imageType)
298 }
299 this.dialogImageTypeVisible = false
300 }
301 }
302 }
303 </script>
304
305 <style>
306 .el-upload-list__item {
307 transition: none;
308 }
309 .single .el-upload--picture-card {
310 display: none;
311 }
312 </style>
1 <template>
2 <div>
3 <el-upload
4 :action="baseUrl ? baseUrl : baseApi + '/api/imageUpload'"
5 :auto-upload="true"
6 :data="uploadData"
7 :headers="headers"
8 :on-success="handleSuccess"
9 :on-error="handleError"
10 :on-exceed="handleExceed"
11 :before-upload="beforeUpload"
12 :file-list="fileList"
13 :disabled="isDisabled"
14 :limit="limit"
15 :class="{single:singleImage}"
16 list-type="picture-card"
17 accept="image/*"
18 >
19 <i slot="default" class="el-icon-plus" />
20 <div slot="file" slot-scope="{file}">
21 <!-- <span-->
22 <!-- style="position: absolute; z-index: 999; padding-left: 5px; font-size: 12px;color: #ffffff;-->
23 <!-- background-color: rgba(0, 0, 0, 0.5); padding-right: 5px; border-bottom-right-radius: 0.5em"-->
24 <!-- >-->
25 <!-- {{ file.hasOwnProperty("type") ? file.type : ((file.hasOwnProperty("response") ? file.response.type : -1))-->
26 <!-- | dictFilter(dictMap.image_type ? dictMap.image_type : [])-->
27 <!-- | dictFilter(dictMap.image_type_show ? dictMap.image_type_show : []) }}-->
28 <!-- </span>-->
29 <!-- 于2020/10/14为了ugc基地址进行修改 file.url-->
30 <img
31 :src="baseUrl ? baseUrl + file.fileUrl : file.url"
32 class="el-upload-list__item-thumbnail"
33 alt=""
34 >
35 <span class="el-upload-list__item-actions">
36 <span
37 class="el-upload-list__item-preview"
38 @click="handlePictureCardPreview(file)"
39 >
40 <i class="el-icon-zoom-in" />
41 </span>
42 <!-- <span-->
43 <!-- v-if="!readOnlyMode"-->
44 <!-- class="el-upload-list__item-delete"-->
45 <!-- @click="handleChangeType(file)"-->
46 <!-- >-->
47 <!-- <i class="el-icon-picture" />-->
48 <!-- </span>-->
49 <span
50 v-if="!readOnlyMode"
51 class="el-upload-list__item-delete"
52 @click="handleRemove(file)"
53 >
54 <i class="el-icon-delete" />
55 </span>
56 <span
57 v-if="isArticle"
58 class="el-upload-list__item-delete"
59 @click="handleAddDesc(file)"
60 >
61 <i class="el-icon-edit" />
62 </span>
63 </span>
64 </div>
65 </el-upload>
66 <el-dialog :visible.sync="dialogVisible" :append-to-body="true">
67 <img :src="dialogImageUrl" width="100%" alt="">
68 </el-dialog>
69 <!-- <el-dialog :visible.sync="dialogImageTypeVisible" :append-to-body="true" title="请选择图片类型" width="300px">-->
70 <!-- <el-select v-model="imageType" class="filter-item" style="width: 100%;">-->
71 <!-- <el-option v-for="item in dictMap.image_type" :key="item.value" :label="item.label | dictFilter(dictMap.image_type_show)" :value="item.value" />-->
72 <!-- </el-select>-->
73 <!-- <div slot="footer" class="dialog-footer">-->
74 <!-- <el-button @click="dialogImageTypeVisible = false">取消</el-button>-->
75 <!-- <el-button type="primary" @click="changeImageType">确认</el-button>-->
76 <!-- </div>-->
77 <!-- </el-dialog>-->
78 <el-dialog :visible.sync="imageDesc" :append-to-body="true" title="请输入图片描述" width="800px">
79 <el-input v-model="imgDesc.desc" type="textarea" />
80 <div slot="footer" class="dialog-footer">
81 <el-button @click="imageDesc = false">取消</el-button>
82 <el-button type="primary" @click="addDescription">确认</el-button>
83 </div>
84 </el-dialog>
85 </div>
86 </template>
87 <script>
88 import { getAttrByValueFromDict, convertImageArr2Json, evil, deepCopy } from '../../utils/common-util'
89 import { mapGetters } from 'vuex'
90 import { getToken } from '@/utils/auth'
91 import initDict from '@/mixins/initDict'
92 import { Loading } from 'element-ui'
93
94 export default {
95 name: 'ImagesUpload',
96 mixins: [initDict],
97 props: {
98 isDisabled: {
99 type: Boolean,
100 required: false,
101 default() {
102 return false
103 }
104 },
105 isSubject: {
106 type: Boolean,
107 required: false,
108 default() {
109 return true
110 }
111 },
112 image: {
113 type: String,
114 default: '[]'
115 },
116 images: {
117 type: String,
118 default: '{"list":[],"map":{}}'
119 },
120 uploadEntity: {
121 type: String,
122 required: true
123 },
124 limit: {
125 type: Number,
126 default: 10
127 },
128 singleMode: {
129 type: Boolean,
130 default: false
131 },
132 readOnlyMode: {
133 type: Boolean,
134 default: false
135 },
136 isArticle: {
137 type: Boolean,
138 default: false
139 },
140 baseUrl: {
141 type: String,
142 default() {
143 return ''
144 }
145 }
146 },
147 data() {
148 return {
149 headers: {
150 'Authorization': 'Bearer ' + getToken()
151 },
152 fileList: [],
153 uploadData: { entity: this.uploadEntity },
154 dialogImageUrl: '',
155 dialogVisible: false,
156 dialogImageTypeVisible: false,
157 imageType: '-1',
158 imageSelect: {},
159 imgDesc: {
160 desc: ''
161 },
162 isUploading: false,
163 loading: null,
164 imageDesc: false
165 }
166 },
167 computed: {
168 ...mapGetters(['baseApi']),
169 singleImage: function() {
170 return (this.singleMode && (this.fileList.length > 0 || this.isUploading)) || this.readOnlyMode
171 }
172 },
173 watch: {
174 image: {
175 immediate: true,
176 handler(val) {
177 if (val === null || val === '') {
178 val = '[]'
179 }
180 const arr = evil(val)
181 // 2020/12/31 00:04,为了专题管理覆盖数据的操作,增加了isSubject参数
182 if (this.fileList.length === arr.length && this.isSubject) {
183 // 当图片数量不变时,filelist不响应image的变更,避免2次刷新
184 return
185 }
186 this.fileList = arr
187 this.fileList.forEach(file => {
188 if (!file.hasOwnProperty('url') && file.fileUrl.indexOf('http') === -1) {
189 file.url = this.baseApi + '/' + file.fileUrl
190 } else {
191 file.url = file.fileUrl
192 }
193 })
194 this.fileList = deepCopy(this.fileList)
195 }
196 }
197 },
198 created() {
199 this.$nextTick(() => {
200 this.getDictMap('image_type,image_type_show,image_size_limit')
201 })
202 },
203 updated() {
204 if (!this.dictMap.image_type) {
205 // 图片类型数据字典未获取时,images无法计算,当获取到后,也会触发updated
206 return
207 }
208 const images = []
209 this.fileList.forEach(file => {
210 if (this.isArticle) {
211 if (file.hasOwnProperty('response')) {
212 images.push((({ id, type, width, height, fileUrl, desc }) =>
213 ({ id, type, width, height, fileUrl, desc }))(file.response))
214 } else {
215 images.push((({ id, type, width, height, fileUrl, desc }) =>
216 ({ id, type, width, height, fileUrl, desc }))(file))
217 }
218 } else {
219 if (file.hasOwnProperty('response')) {
220 images.push((({ id, type, width, height, fileUrl }) =>
221 ({ id, type, width, height, fileUrl }))(file.response))
222 } else {
223 images.push((({ id, type, width, height, fileUrl }) =>
224 ({ id, type, width, height, fileUrl }))(file))
225 }
226 }
227 })
228 this.$emit('update:image', JSON.stringify(images))
229 this.$emit('update:images', this.convertImageArr2Json(JSON.stringify(images), this.dictMap.image_type))
230 },
231 methods: {
232 getAttrByValueFromDict,
233 deepCopy,
234 convertImageArr2Json,
235 beforeUpload(file) {
236 const isValid = (file.type === 'image/jpeg' || file.type === 'image/gif' || file.type === 'image/png')
237
238 const dict = this.dictMap.image_size_limit.find(item => item.label === this.uploadEntity)
239 let maxSize = dict ? parseFloat(dict.value) : 20 * 1024 // 默认2M
240 const isLtMaxSize = parseFloat(file.size / 1024) < maxSize
241
242 maxSize = maxSize > 1024 ? (parseFloat(maxSize / 1024).toFixed(2) + 'MB') : (maxSize + 'KB')
243 this.isUploading = true
244 if (!isValid) {
245 this.$message.error('上传图片只能是 JPG/PNG/GIF 格式!')
246 this.isUploading = false
247 }
248 if (!isLtMaxSize) {
249 this.$message.error('上传图片大小不能超过' + maxSize + ' !')
250 this.isUploading = false
251 }
252 if (isValid && isLtMaxSize) {
253 this.loading = Loading.service({
254 lock: true,
255 text: '解析中Loading',
256 spinner: 'el-icon-loading',
257 background: 'rgba(0, 0, 0, 0.7)'
258 })
259 }
260 return isValid && isLtMaxSize
261 },
262 handleRemove(file) {
263 this.fileList.splice(this.fileList.findIndex(
264 item => !item.hasOwnProperty('response') ? item.id === file.id : item.response.id === file.response.id), 1)
265 },
266 handlePictureCardPreview(file) {
267 this.dialogImageUrl = file.url
268 this.dialogVisible = true
269 },
270 handleChangeType(file) {
271 console.log(file)
272 this.imageSelect = file
273 // 2020/10/23上午,因为发现此处报错,注释
274 // this.imageType = file.hasOwnProperty('type') ? file.type.toString() : file.response.type.toString()
275 // 2020/11/10上午,需要标示上传图片类型,所以解锁,并置为-1
276 this.imageType = file.hasOwnProperty('type') ? file.type.toString() : file.hasOwnProperty('response') ? file.response.type.toString() : '-1'
277 // this.imageType = '-1'
278 this.dialogImageTypeVisible = true
279 },
280 // 增加描述
281 handleAddDesc(file) {
282 this.imgDesc = file
283 this.imageDesc = true
284 },
285 addDescription() {
286 // debugger
287 for (let i = 0; i < this.fileList.length; i++) {
288 const item = this.fileList[i]
289 if (item.id) {
290 if (this.imgDesc.id === item.id) {
291 item.desc = this.imgDesc.desc
292 this.imageDesc = false
293 break
294 }
295 } else {
296 if (this.imgDesc.response.id === item.response.id) {
297 item.response.desc = this.imgDesc.desc
298 this.imageDesc = false
299 break
300 }
301 }
302 }
303 },
304 handleError(err, file) {
305 this.isUploading = false
306 this.loading.close()
307 this.$message.error(`图片上传失败,失败信息 ${err}`)
308 console.log(err)
309 },
310 handleSuccess(res, file, fileList) {
311 // console.log(res, file, fileList)
312 this.loading.close()
313 this.isUploading = false
314 this.fileList = fileList
315 },
316 handleExceed(files, fileList) {
317 this.$message.warning('图片数量无法大于' + this.limit + '张')
318 },
319 generateImageFromFileList(item) {
320 const images = []
321 this.fileList.forEach(file => {
322 if (file.hasOwnProperty('response')) {
323 images.push((({ id, type, width, height, fileUrl, size }) =>
324 ({ id, type, width, height, fileUrl, size }))(file.response))
325 } else {
326 images.push((({ id, type, width, height, fileUrl, size }) =>
327 ({ id, type, width, height, fileUrl, size }))(file))
328 }
329 })
330 item.image = JSON.stringify(images)
331 item.images = this.convertImageArr2Json(item.image, this.dictMap.image_type)
332 },
333 changeImageType() {
334 const index = this.fileList.findIndex(
335 item => !item.hasOwnProperty('response') ? item.id === this.imageSelect.id : item.response.id === this.imageSelect.response.id)
336 if (this.fileList[index].hasOwnProperty('response')) {
337 this.fileList[index].response.type = parseInt(this.imageType)
338 } else {
339 this.fileList[index].type = parseInt(this.imageType)
340 }
341 this.dialogImageTypeVisible = false
342 }
343 }
344 }
345 </script>
346
347 <style>
348 .el-upload-list__item {
349 transition: none;
350 }
351 .single .el-upload--picture-card {
352 display: none;
353 }
354 </style>
1 <template>
2 <div class="json-editor">
3 <textarea ref="textarea" />
4 </div>
5 </template>
6
7 <script>
8 import CodeMirror from 'codemirror'
9 import 'codemirror/lib/codemirror.css'
10 // 替换主题这里需修改名称
11 import 'codemirror/theme/idea.css'
12 import 'codemirror/mode/clike/clike'
13 export default {
14 props: {
15 value: {
16 type: String,
17 required: true
18 },
19 height: {
20 type: String,
21 required: true
22 }
23 },
24 data() {
25 return {
26 editor: false
27 }
28 },
29 watch: {
30 value(value) {
31 const editorValue = this.editor.getValue()
32 if (value !== editorValue) {
33 this.editor.setValue(this.value)
34 }
35 },
36 height(value) {
37 this.editor.setSize('auto', this.height)
38 }
39 },
40 mounted() {
41 this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
42 mode: 'text/x-java',
43 lineNumbers: true,
44 lint: true,
45 lineWrapping: true,
46 tabSize: 2,
47 cursorHeight: 0.9,
48 // 替换主题这里需修改名称
49 theme: 'idea',
50 readOnly: true
51 })
52 this.editor.setSize('auto', this.height)
53 this.editor.setValue(this.value)
54 },
55 methods: {
56 getValue() {
57 return this.editor.getValue()
58 }
59 }
60 }
61 </script>
62
63 <style scoped>
64 .json-editor{
65 height: 100%;
66 margin-bottom: 10px;
67 }
68 .json-editor >>> .CodeMirror {
69 font-size: 14px;
70 overflow-y:auto;
71 font-weight:normal
72 }
73 .json-editor >>> .CodeMirror-scroll{
74 }
75 .json-editor >>> .cm-s-rubyblue span.cm-string {
76 color: #F08047;
77 }
78 </style>
1 <template>
2 <div :class="{'hidden':hidden}" class="pagination-container">
3 <el-pagination
4 :background="background"
5 :current-page.sync="currentPage"
6 :page-size.sync="pageSize"
7 :layout="layout"
8 :page-sizes="pageSizes"
9 :total="total"
10 v-bind="$attrs"
11 @size-change="handleSizeChange"
12 @current-change="handleCurrentChange"
13 />
14 </div>
15 </template>
16
17 <script>
18 import { scrollTo } from '@/utils/scroll-to'
19
20 export default {
21 name: 'Pagination',
22 props: {
23 total: {
24 required: true,
25 type: Number
26 },
27 page: {
28 type: Number,
29 default: 1
30 },
31 limit: {
32 type: Number,
33 default: 20
34 },
35 pageSizes: {
36 type: Array,
37 default() {
38 return [10, 20, 30, 50]
39 }
40 },
41 layout: {
42 type: String,
43 default: 'total, sizes, prev, pager, next, jumper'
44 },
45 background: {
46 type: Boolean,
47 default: true
48 },
49 autoScroll: {
50 type: Boolean,
51 default: true
52 },
53 hidden: {
54 type: Boolean,
55 default: false
56 }
57 },
58 computed: {
59 currentPage: {
60 get() {
61 return this.page
62 },
63 set(val) {
64 this.$emit('update:page', val)
65 }
66 },
67 pageSize: {
68 get() {
69 return this.limit
70 },
71 set(val) {
72 this.$emit('update:limit', val)
73 }
74 }
75 },
76 methods: {
77 handleSizeChange(val) {
78 this.$emit('pagination', { page: this.currentPage, limit: val })
79 if (this.autoScroll) {
80 scrollTo(0, 800)
81 }
82 },
83 handleCurrentChange(val) {
84 this.$emit('pagination', { page: val, limit: this.pageSize })
85 if (this.autoScroll) {
86 scrollTo(0, 800)
87 }
88 }
89 }
90 }
91 </script>
92
93 <style scoped>
94 .pagination-container {
95 background: #fff;
96 padding: 32px 16px;
97 }
98 .pagination-container.hidden {
99 display: none;
100 }
101 </style>
1 <template>
2 <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
3 <div class="pan-info">
4 <div class="pan-info-roles-container">
5 <slot />
6 </div>
7 </div>
8 <img :src="image" class="pan-thumb">
9 </div>
10 </template>
11
12 <script>
13 export default {
14 name: 'PanThumb',
15 props: {
16 image: {
17 type: String,
18 required: true
19 },
20 zIndex: {
21 type: Number,
22 default: 1
23 },
24 width: {
25 type: String,
26 default: '150px'
27 },
28 height: {
29 type: String,
30 default: '150px'
31 }
32 }
33 }
34 </script>
35
36 <style scoped>
37 .pan-item {
38 width: 200px;
39 height: 200px;
40 border-radius: 50%;
41 display: inline-block;
42 position: relative;
43 cursor: default;
44 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
45 }
46
47 .pan-info-roles-container {
48 padding: 20px;
49 text-align: center;
50 }
51
52 .pan-thumb {
53 width: 100%;
54 height: 100%;
55 background-size: 100%;
56 border-radius: 50%;
57 overflow: hidden;
58 position: absolute;
59 transform-origin: 95% 40%;
60 transition: all 0.3s ease-in-out;
61 }
62
63 .pan-thumb:after {
64 content: '';
65 width: 8px;
66 height: 8px;
67 position: absolute;
68 border-radius: 50%;
69 top: 40%;
70 left: 95%;
71 margin: -4px 0 0 -4px;
72 background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
73 box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
74 }
75
76 .pan-info {
77 position: absolute;
78 width: inherit;
79 height: inherit;
80 border-radius: 50%;
81 overflow: hidden;
82 box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
83 }
84
85 .pan-info h3 {
86 color: #fff;
87 text-transform: uppercase;
88 position: relative;
89 letter-spacing: 2px;
90 font-size: 18px;
91 margin: 0 60px;
92 padding: 22px 0 0 0;
93 height: 85px;
94 font-family: 'Open Sans', Arial, sans-serif;
95 text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
96 }
97
98 .pan-info p {
99 color: #fff;
100 padding: 10px 5px;
101 font-style: italic;
102 margin: 0 30px;
103 font-size: 12px;
104 border-top: 1px solid rgba(255, 255, 255, 0.5);
105 }
106
107 .pan-info p a {
108 display: block;
109 color: #333;
110 width: 80px;
111 height: 80px;
112 background: rgba(255, 255, 255, 0.3);
113 border-radius: 50%;
114 color: #fff;
115 font-style: normal;
116 font-weight: 700;
117 text-transform: uppercase;
118 font-size: 9px;
119 letter-spacing: 1px;
120 padding-top: 24px;
121 margin: 7px auto 0;
122 font-family: 'Open Sans', Arial, sans-serif;
123 opacity: 0;
124 transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
125 transform: translateX(60px) rotate(90deg);
126 }
127
128 .pan-info p a:hover {
129 background: rgba(255, 255, 255, 0.5);
130 }
131
132 .pan-item:hover .pan-thumb {
133 transform: rotate(-110deg);
134 }
135
136 .pan-item:hover .pan-info p a {
137 opacity: 1;
138 transform: translateX(0px) rotate(0deg);
139 }
140 </style>
1 <template>
2 <router-view />
3 </template>
1 import permission from './permission'
2
3 const install = function(Vue) {
4 Vue.directive('permission', permission)
5 }
6
7 if (window.Vue) {
8 window['permission'] = permission
9 Vue.use(install); // eslint-disable-line
10 }
11
12 permission.install = install
13 export default permission
1 import store from '@/store'
2
3 export default {
4 inserted(el, binding) {
5 const { value } = binding
6 const roles = store.getters && store.getters.roles
7 if (value && value instanceof Array) {
8 if (value.length > 0) {
9 const permissionRoles = value
10 const hasPermission = roles.some(role => {
11 return permissionRoles.includes(role)
12 })
13 if (!hasPermission) {
14 el.parentNode && el.parentNode.removeChild(el)
15 }
16 }
17 } else {
18 throw new Error(`使用方式: v-permission="['admin','editor']"`)
19 }
20 }
21 }
1 <template>
2 <div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
3 <div class="rightPanel-background" />
4 <div class="rightPanel">
5 <div class="rightPanel-items">
6 <slot />
7 </div>
8 </div>
9 </div>
10 </template>
11
12 <script>
13 import { addClass, removeClass } from '@/utils'
14
15 export default {
16 name: 'RightPanel',
17 props: {
18 clickNotClose: {
19 default: false,
20 type: Boolean
21 },
22 buttonTop: {
23 default: 250,
24 type: Number
25 }
26 },
27 computed: {
28 show: {
29 get() {
30 return this.$store.state.settings.showSettings
31 },
32 set(val) {
33 this.$store.dispatch('settings/changeSetting', {
34 key: 'showSettings',
35 value: val
36 })
37 }
38 },
39 theme() {
40 return this.$store.state.settings.theme
41 }
42 },
43 watch: {
44 show(value) {
45 if (value && !this.clickNotClose) {
46 this.addEventClick()
47 }
48 if (value) {
49 addClass(document.body, 'showRightPanel')
50 } else {
51 removeClass(document.body, 'showRightPanel')
52 }
53 }
54 },
55 mounted() {
56 this.insertToBody()
57 this.addEventClick()
58 },
59 beforeDestroy() {
60 const elx = this.$refs.rightPanel
61 elx.remove()
62 },
63 methods: {
64 addEventClick() {
65 window.addEventListener('click', this.closeSidebar)
66 },
67 closeSidebar(evt) {
68 const parent = evt.target.closest('.rightPanel')
69 if (!parent) {
70 this.show = false
71 window.removeEventListener('click', this.closeSidebar)
72 }
73 },
74 insertToBody() {
75 const elx = this.$refs.rightPanel
76 const body = document.querySelector('body')
77 body.insertBefore(elx, body.firstChild)
78 }
79 }
80 }
81 </script>
82
83 <style>
84 .showRightPanel {
85 overflow: hidden;
86 position: relative;
87 width: calc(100% - 15px);
88 }
89 </style>
90
91 <style lang="scss" scoped>
92 .rightPanel-background {
93 position: fixed;
94 top: 0;
95 left: 0;
96 opacity: 0;
97 transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
98 background: rgba(0, 0, 0, .2);
99 z-index: -1;
100 }
101
102 .rightPanel {
103 width: 100%;
104 max-width: 260px;
105 height: 100vh;
106 position: fixed;
107 top: 0;
108 right: 0;
109 box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
110 transition: all .25s cubic-bezier(.7, .3, .1, 1);
111 transform: translate(100%);
112 background: #fff;
113 z-index: 40000;
114 }
115
116 .show {
117 transition: all .3s cubic-bezier(.7, .3, .1, 1);
118
119 .rightPanel-background {
120 z-index: 20000;
121 opacity: 1;
122 width: 100%;
123 height: 100%;
124 }
125
126 .rightPanel {
127 transform: translate(0);
128 }
129 }
130
131 .handle-button {
132 width: 48px;
133 height: 48px;
134 position: absolute;
135 left: -48px;
136 text-align: center;
137 font-size: 24px;
138 border-radius: 6px 0 0 6px !important;
139 z-index: 0;
140 pointer-events: auto;
141 cursor: pointer;
142 color: #fff;
143 line-height: 48px;
144 i {
145 font-size: 24px;
146 line-height: 48px;
147 }
148 }
149 </style>
1 <template>
2 <div>
3 <svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
4 </div>
5 </template>
6
7 <script>
8 import screenfull from 'screenfull'
9
10 export default {
11 name: 'Screenfull',
12 data() {
13 return {
14 isFullscreen: false
15 }
16 },
17 mounted() {
18 this.init()
19 },
20 beforeDestroy() {
21 this.destroy()
22 },
23 methods: {
24 click() {
25 if (!screenfull.enabled) {
26 this.$message({
27 message: 'you browser can not work',
28 type: 'warning'
29 })
30 return false
31 }
32 screenfull.toggle()
33 },
34 change() {
35 this.isFullscreen = screenfull.isFullscreen
36 },
37 init() {
38 if (screenfull.enabled) {
39 screenfull.on('change', this.change)
40 }
41 },
42 destroy() {
43 if (screenfull.enabled) {
44 screenfull.off('change', this.change)
45 }
46 }
47 }
48 }
49 </script>
50
51 <style scoped>
52 .screenfull-svg {
53 display: inline-block;
54 cursor: pointer;
55 fill: #5a5e66;;
56 width: 20px;
57 height: 20px;
58 vertical-align: 10px;
59 }
60 </style>
1 <template>
2 <el-dropdown trigger="click" @command="handleSetSize">
3 <div>
4 <svg-icon class-name="size-icon" icon-class="size" />
5 </div>
6 <el-dropdown-menu slot="dropdown">
7 <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
8 {{
9 item.label }}
10 </el-dropdown-item>
11 </el-dropdown-menu>
12 </el-dropdown>
13 </template>
14
15 <script>
16 export default {
17 data() {
18 return {
19 sizeOptions: [
20 { label: 'Default', value: 'default' },
21 { label: 'Medium', value: 'medium' },
22 { label: 'Small', value: 'small' },
23 { label: 'Mini', value: 'mini' }
24 ]
25 }
26 },
27 computed: {
28 size() {
29 return this.$store.getters.size
30 }
31 },
32 methods: {
33 handleSetSize(size) {
34 this.$ELEMENT.size = size
35 this.$store.dispatch('app/setSize', size)
36 this.refreshView()
37 this.$message({
38 message: '布局设置成功',
39 type: 'success'
40 })
41 },
42 refreshView() {
43 // In order to make the cached page re-rendered
44 this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
45
46 const { fullPath } = this.$route
47
48 this.$nextTick(() => {
49 this.$router.replace({
50 path: '/redirect' + fullPath
51 })
52 })
53 }
54 }
55
56 }
57 </script>
1 <template>
2 <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
3 <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
4 <use :href="iconName" />
5 </svg>
6 </template>
7
8 <script>
9 // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
10 import { isExternal } from '@/utils/validate'
11
12 export default {
13 name: 'SvgIcon',
14 props: {
15 iconClass: {
16 type: String,
17 required: true
18 },
19 className: {
20 type: String,
21 default: ''
22 }
23 },
24 computed: {
25 isExternal() {
26 return isExternal(this.iconClass)
27 },
28 iconName() {
29 return `#icon-${this.iconClass}`
30 },
31 svgClass() {
32 if (this.className) {
33 return 'svg-icon ' + this.className
34 } else {
35 return 'svg-icon'
36 }
37 },
38 styleExternalIcon() {
39 return {
40 mask: `url(${this.iconClass}) no-repeat 50% 50%`,
41 '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
42 }
43 }
44 }
45 }
46 </script>
47
48 <style scoped>
49 .svg-icon {
50 width: 1em;
51 height: 1em;
52 vertical-align: -0.15em;
53 fill: currentColor;
54 overflow: hidden;
55 }
56
57 .svg-external-icon {
58 background-color: currentColor;
59 mask-size: cover!important;
60 display: inline-block;
61 }
62 </style>
1 <template>
2 <el-color-picker
3 v-model="theme"
4 :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d']"
5 class="theme-picker"
6 popper-class="theme-picker-dropdown"
7 />
8 </template>
9
10 <script>
11 const version = require('element-ui/package.json').version // element-ui version from node_modules
12 const ORIGINAL_THEME = '#409EFF' // default color
13 import Cookies from 'js-cookie'
14 export default {
15 data() {
16 return {
17 chalk: '', // content of theme-chalk css
18 theme: ''
19 }
20 },
21 computed: {
22 defaultTheme() {
23 return this.$store.state.settings.theme
24 }
25 },
26 watch: {
27 defaultTheme: {
28 handler: function(val, oldVal) {
29 this.theme = val
30 },
31 immediate: true
32 },
33 async theme(val) {
34 Cookies.set('theme', val, { expires: 365 })
35 const oldVal = this.chalk ? this.theme : Cookies.get('theme') ? Cookies.get('theme') : ORIGINAL_THEME
36 if (typeof val !== 'string') return
37 const themeCluster = this.getThemeCluster(val.replace('#', ''))
38 const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
39
40 const getHandler = (variable, id) => {
41 return () => {
42 const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
43 const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
44
45 let styleTag = document.getElementById(id)
46 if (!styleTag) {
47 styleTag = document.createElement('style')
48 styleTag.setAttribute('id', id)
49 document.head.appendChild(styleTag)
50 }
51 styleTag.innerText = newStyle
52 }
53 }
54
55 if (!this.chalk) {
56 const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
57 await this.getCSSString(url, 'chalk')
58 }
59
60 const chalkHandler = getHandler('chalk', 'chalk-style')
61
62 chalkHandler()
63
64 const styles = [].slice.call(document.querySelectorAll('style'))
65 .filter(style => {
66 const text = style.innerText
67 return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
68 })
69 styles.forEach(style => {
70 const { innerText } = style
71 if (typeof innerText !== 'string') return
72 style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
73 })
74
75 this.$emit('change', val)
76 }
77 },
78
79 methods: {
80 updateStyle(style, oldCluster, newCluster) {
81 let newStyle = style
82 oldCluster.forEach((color, index) => {
83 newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
84 })
85 return newStyle
86 },
87
88 getCSSString(url, variable) {
89 return new Promise(resolve => {
90 const xhr = new XMLHttpRequest()
91 xhr.onreadystatechange = () => {
92 if (xhr.readyState === 4 && xhr.status === 200) {
93 this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
94 resolve()
95 }
96 }
97 xhr.open('GET', url)
98 xhr.send()
99 })
100 },
101
102 getThemeCluster(theme) {
103 const tintColor = (color, tint) => {
104 let red = parseInt(color.slice(0, 2), 16)
105 let green = parseInt(color.slice(2, 4), 16)
106 let blue = parseInt(color.slice(4, 6), 16)
107
108 if (tint === 0) { // when primary color is in its rgb space
109 return [red, green, blue].join(',')
110 } else {
111 red += Math.round(tint * (255 - red))
112 green += Math.round(tint * (255 - green))
113 blue += Math.round(tint * (255 - blue))
114
115 red = red.toString(16)
116 green = green.toString(16)
117 blue = blue.toString(16)
118
119 return `#${red}${green}${blue}`
120 }
121 }
122
123 const shadeColor = (color, shade) => {
124 let red = parseInt(color.slice(0, 2), 16)
125 let green = parseInt(color.slice(2, 4), 16)
126 let blue = parseInt(color.slice(4, 6), 16)
127
128 red = Math.round((1 - shade) * red)
129 green = Math.round((1 - shade) * green)
130 blue = Math.round((1 - shade) * blue)
131
132 red = red.toString(16)
133 green = green.toString(16)
134 blue = blue.toString(16)
135
136 return `#${red}${green}${blue}`
137 }
138
139 const clusters = [theme]
140 for (let i = 0; i <= 9; i++) {
141 clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
142 }
143 clusters.push(shadeColor(theme, 0.1))
144 return clusters
145 }
146 }
147 }
148 </script>
149
150 <style>
151 .theme-message,
152 .theme-picker-dropdown {
153 z-index: 99999 !important;
154 }
155
156 .theme-picker .el-color-picker__trigger {
157 height: 26px !important;
158 width: 26px !important;
159 padding: 2px;
160 }
161
162 .theme-picker-dropdown .el-color-dropdown__link-btn {
163 display: none;
164 }
165 </style>
1 <template>
2 <div>
3 <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
4 <div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
5 拖拽excel文件到此处 或者
6 <el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">
7 浏览
8 </el-button>
9 </div>
10 </div>
11 </template>
12
13 <script>
14 import XLSX from 'xlsx'
15
16 export default {
17 props: {
18 beforeUpload: Function, // eslint-disable-line
19 onSuccess: Function// eslint-disable-line
20 },
21 data() {
22 return {
23 loading: false,
24 excelData: {
25 header: null,
26 results: null
27 }
28 }
29 },
30 methods: {
31 generateData({ header, results }) {
32 this.excelData.header = header
33 this.excelData.results = results
34 this.onSuccess && this.onSuccess(this.excelData)
35 },
36 handleDrop(e) {
37 e.stopPropagation()
38 e.preventDefault()
39 if (this.loading) return
40 const files = e.dataTransfer.files
41 if (files.length !== 1) {
42 this.$message.error('只支持单个文件上传!')
43 return
44 }
45 const rawFile = files[0]
46
47 if (!this.isExcel(rawFile)) {
48 this.$message.error('只支持.xlsx, .xls, .csv 格式文件')
49 return false
50 }
51 this.upload(rawFile)
52 e.stopPropagation()
53 e.preventDefault()
54 },
55 handleDragover(e) {
56 e.stopPropagation()
57 e.preventDefault()
58 e.dataTransfer.dropEffect = 'copy'
59 },
60 handleUpload() {
61 this.$refs['excel-upload-input'].click()
62 },
63 handleClick(e) {
64 const files = e.target.files
65 const rawFile = files[0] // only use files[0]
66 if (!rawFile) return
67 this.upload(rawFile)
68 },
69 upload(rawFile) {
70 this.$refs['excel-upload-input'].value = null // fix can't select the same excel
71
72 if (!this.beforeUpload) {
73 this.readerData(rawFile)
74 return
75 }
76 const before = this.beforeUpload(rawFile)
77 if (before) {
78 this.readerData(rawFile)
79 }
80 },
81 readerData(rawFile) {
82 this.loading = true
83 return new Promise((resolve, reject) => {
84 const reader = new FileReader()
85 reader.onload = e => {
86 const data = e.target.result
87 const workbook = XLSX.read(data, { type: 'array' })
88 const firstSheetName = workbook.SheetNames[0]
89 const worksheet = workbook.Sheets[firstSheetName]
90 const header = this.getHeaderRow(worksheet)
91 const results = XLSX.utils.sheet_to_json(worksheet)
92 this.generateData({ header, results })
93 this.loading = false
94 resolve()
95 }
96 reader.readAsArrayBuffer(rawFile)
97 })
98 },
99 getHeaderRow(sheet) {
100 const headers = []
101 const range = XLSX.utils.decode_range(sheet['!ref'])
102 let C
103 const R = range.s.r
104 /* start in the first row */
105 for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
106 const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
107 /* find the cell in the first row */
108 let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
109 if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
110 headers.push(hdr)
111 }
112 return headers
113 },
114 isExcel(file) {
115 return /\.(xlsx|xls|csv)$/.test(file.name)
116 }
117 }
118 }
119 </script>
120
121 <style scoped>
122 .excel-upload-input{
123 display: none;
124 z-index: -9999;
125 }
126 .drop{
127 border: 2px dashed #bbb;
128 width: 600px;
129 height: 160px;
130 line-height: 160px;
131 margin: 0 auto;
132 font-size: 24px;
133 border-radius: 5px;
134 text-align: center;
135 color: #bbb;
136 position: relative;
137 }
138 </style>
1 <template>
2 <div class="json-editor">
3 <textarea ref="textarea" />
4 </div>
5 </template>
6
7 <script>
8 import CodeMirror from 'codemirror'
9 import 'codemirror/lib/codemirror.css'
10 // 替换主题这里需修改名称
11 import 'codemirror/theme/idea.css'
12 import 'codemirror/mode/yaml/yaml'
13 export default {
14 props: {
15 value: {
16 type: String,
17 required: true
18 },
19 height: {
20 type: String,
21 required: true
22 }
23 },
24 data() {
25 return {
26 editor: false
27 }
28 },
29 watch: {
30 value(value) {
31 const editorValue = this.editor.getValue()
32 if (value !== editorValue) {
33 this.editor.setValue(this.value)
34 }
35 },
36 height(value) {
37 this.editor.setSize('auto', this.height)
38 }
39 },
40 mounted() {
41 this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
42 mode: 'text/x-yaml',
43 lineNumbers: true,
44 lint: true,
45 lineWrapping: true,
46 tabSize: 2,
47 cursorHeight: 0.9,
48 // 替换主题这里需修改名称
49 theme: 'idea'
50 })
51 this.editor.setSize('auto', this.height)
52 this.editor.setValue(this.value)
53 this.editor.on('change', cm => {
54 this.$emit('changed', cm.getValue())
55 this.$emit('input', cm.getValue())
56 })
57 },
58 methods: {
59 getValue() {
60 return this.editor.getValue()
61 }
62 }
63 }
64 </script>
65
66 <style scoped>
67 .json-editor{
68 height: 100%;
69 margin-bottom: 10px;
70 }
71 .json-editor >>> .CodeMirror {
72 font-size: 13px;
73 overflow-y:auto;
74 font-weight:normal
75 }
76 .json-editor >>> .CodeMirror-scroll{
77 }
78 .json-editor >>> .cm-s-rubyblue span.cm-string {
79 color: #F08047;
80 }
81 </style>
1 <template>
2 <ul class="slide-box">
3 <li />
4 <li />
5 <li />
6 <li />
7 </ul>
8 </template>
9 <script>
10 export default {
11 name: 'CpIndexBg'
12 }
13 </script>
14 <style lang="scss">
15 .slide-box {
16 position: fixed;
17 width: 100%;
18 height: 100%;
19 background-image: url(../../assets/bg/7-2.jpg);
20 top: 0;
21 left: 0;
22 z-index: 0;
23 margin: 0;
24 li {
25 width: 100%;
26 height: 100%;
27 position: absolute;
28 top: 0;
29 left: 0;
30 color: transparent;
31 background-size: cover;
32 background-position: 50% 50%;
33 background-repeat: no-repeat;
34 opacity: 0;
35 z-index: 0;
36 -webkit-backface-visibility: hidden;
37 -webkit-animation: imageAnimation 48s linear infinite 0s;
38 -moz-animation: imageAnimation 48s linear infinite 0s;
39 -o-animation: imageAnimation 48s linear infinite 0s;
40 -ms-animation: imageAnimation 48s linear infinite 0s;
41 animation: imageAnimation 48s linear infinite 0s;
42 /*&:nth-child(1) {*/
43 /* background-image: url(../../assets/bg/bg-1.jpg);*/
44 /*}*/
45 /*&:nth-child(2) {*/
46 /* background-image: url(../../assets/bg/bg-2.jpg);*/
47 /* -webkit-animation-delay: 12s;*/
48 /* -moz-animation-delay: 12s;*/
49 /* -o-animation-delay: 12s;*/
50 /* -ms-animation-delay: 12s;*/
51 /* animation-delay: 12s;*/
52 /*}*/
53 /*&:nth-child(3) {*/
54 /* background-image: url(../../assets/bg/bg-default.jpg);*/
55 /* -webkit-animation-delay: 24s;*/
56 /* -moz-animation-delay: 24s;*/
57 /* -o-animation-delay: 24s;*/
58 /* -ms-animation-delay: 24s;*/
59 /* animation-delay: 24s;*/
60 /*}*/
61 /*&:nth-child(4) {*/
62 /* background-image: url(../../assets/bg/bg-1.jpg);*/
63 /* animation-delay: 36s;*/
64 /*}*/
65 }
66 }
67 @keyframes imageAnimation {
68 0% {
69 opacity: 0;
70 animation-timing-function: ease-in;
71 }
72 8% {
73 opacity: 1;
74 transform: scale(1.15);
75 animation-timing-function: ease-out;
76 }
77 17% {
78 opacity: 1;
79 transform: scale(1.2);
80 }
81 25% {
82 opacity: 0;
83 transform: scale(1.4);
84 }
85 100% {
86 opacity: 0;
87 }
88 }
89 </style>
1 <template>
2 <ul class="slide-box">
3 <li />
4 <li />
5 <li />
6 <li />
7 </ul>
8 </template>
9
10 <script>
11 export default {
12 name: 'IndexBg'
13 }
14 </script>
15
16 <style lang="scss">
17 .slide-box {
18 position: fixed;
19 width: 100%;
20 height: 100%;
21 background-image: url(../../assets/bg/bg-default.jpg);
22 top: 0;
23 left: 0;
24 z-index: 0;
25 margin: 0;
26 li {
27 width: 100%;
28 height: 100%;
29 position: absolute;
30 top: 0;
31 left: 0;
32 color: transparent;
33 background-size: cover;
34 background-position: 50% 50%;
35 background-repeat: no-repeat;
36 opacity: 0;
37 z-index: 0;
38 -webkit-backface-visibility: hidden;
39 -webkit-animation: imageAnimation 48s linear infinite 0s;
40 -moz-animation: imageAnimation 48s linear infinite 0s;
41 -o-animation: imageAnimation 48s linear infinite 0s;
42 -ms-animation: imageAnimation 48s linear infinite 0s;
43 animation: imageAnimation 48s linear infinite 0s;
44
45 &:nth-child(1) {
46 background-image: url(../../assets/bg/bg-1.jpg);
47 }
48 &:nth-child(2) {
49 background-image: url(../../assets/bg/bg-2.jpg);
50 -webkit-animation-delay: 12s;
51 -moz-animation-delay: 12s;
52 -o-animation-delay: 12s;
53 -ms-animation-delay: 12s;
54 animation-delay: 12s;
55 }
56 &:nth-child(3) {
57 background-image: url(../../assets/bg/bg-default.jpg);
58 -webkit-animation-delay: 24s;
59 -moz-animation-delay: 24s;
60 -o-animation-delay: 24s;
61 -ms-animation-delay: 24s;
62 animation-delay: 24s;
63 }
64 &:nth-child(4) {
65 background-image: url(../../assets/bg/bg-1.jpg);
66 animation-delay: 36s;
67 }
68 }
69 }
70 @keyframes imageAnimation {
71 0% {
72 opacity: 0;
73 animation-timing-function: ease-in;
74 }
75 8% {
76 opacity: 1;
77 transform: scale(1.15);
78 animation-timing-function: ease-out;
79 }
80 17% {
81 opacity: 1;
82 transform: scale(1.2);
83 }
84 25% {
85 opacity: 0;
86 transform: scale(1.4);
87 }
88 100% {
89 opacity: 0;
90 }
91 }
92 </style>
1 <template>
2 <div class="box">
3 <el-select v-model="query.type" :disabled="isDisabledKey" clearable placeholder="聚合筛选条件" class="filter-item selectSearch" style="width: 130px" @change="changeType">
4 <el-option v-for="item in queryTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
5 </el-select>
6 <div class="spanItem" />
7 <el-input v-model="query.value" :disabled="!query.type||isDisabledKey" clearable placeholder="输入搜索内容" style="width: 200px;" class="filter-item selectInput" @change="toQuery" />
8 </div>
9 </template>
10
11 <script>
12 export default {
13 name: 'Index',
14 props: {
15 isDisabledKey: {
16 type: Boolean,
17 required: false,
18 default() {
19 return false
20 }
21 },
22 queryTypeOptions: {
23 type: Array,
24 required: true,
25 default() {
26 return []
27 }
28 },
29 searchKey: {
30 type: String,
31 required: false,
32 default() {
33 return 'type'
34 }
35 },
36 defaultKey: {
37 type: String,
38 required: false,
39 default() {
40 return ''
41 }
42 }
43 },
44 data() {
45 return {
46 query: {
47 type: '',
48 value: ''
49 }
50 }
51 },
52 mounted() {
53 if (this.defaultKey) {
54 this.query.type = this.defaultKey
55 }
56 },
57 methods: {
58 changeType() {
59 this.query.value = ''
60 this.toQuery()
61 },
62 toQuery() {
63 const obj = {
64 value: this.query.value
65 }
66 obj[this.searchKey] = this.query.type
67 this.$emit('toQuery', obj)
68 }
69 }
70 }
71 </script>
72
73 <style scoped>
74 .box {
75 display: inline-block;
76 border: 1px solid #DCDFE6;
77 border-radius: 5px;
78 height: 33px;
79 }
80 .spanItem {
81 display: inline-block;
82 width: 2px;
83 height: 20px;
84 margin-bottom: -1px;
85 background-color: #DCDFE6
86 }
87 /deep/ .selectSearch .el-input__inner {
88 border: none!important;
89 border-top-right-radius: 0px;
90 border-bottom-right-radius: 0px;
91 width: 135px;
92 }
93 /deep/ .selectInput .el-input__inner {
94 border-top-left-radius: 0px;
95 border-bottom-left-radius: 0px;
96 border: none;
97 margin-left: -5px;
98 width: 208px;
99 }
100 </style>
1 <template>
2 <section class="app-main">
3 <transition name="fade-transform" mode="out-in">
4 <keep-alive :include="cachedViews">
5 <router-view :key="key" />
6 </keep-alive>
7 </transition>
8 <div v-if="$store.state.settings.showFooter" id="el-main-footer">
9 <span v-html="$store.state.settings.footerTxt" />
10 <span></span>
11 <a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">{{ $store.state.settings.caseNumber }}</a>
12 </div>
13 </section>
14 </template>
15
16 <script>
17 export default {
18 name: 'AppMain',
19 computed: {
20 cachedViews() {
21 return this.$store.state.tagsView.cachedViews
22 },
23 key() {
24 return this.$route.path
25 }
26 }
27 }
28 </script>
29
30 <style lang="scss" scoped>
31 .app-main {
32 /* 50= navbar 50 */
33 min-height: calc(100vh - 50px);
34 width: 100%;
35 position: relative;
36 overflow: hidden;
37 }
38
39 .fixed-header+.app-main {
40 padding-top: 50px;
41 }
42
43 .hasTagsView {
44 .app-main {
45 /* 84 = navbar + tags-view = 50 + 34 */
46 min-height: calc(100vh - 84px);
47 }
48
49 .fixed-header+.app-main {
50 padding-top: 50px;
51 }
52 }
53 </style>
54
55 <style lang="scss">
56 // fix css style bug in open el-dialog
57 .el-popup-parent--hidden {
58 .fixed-header {
59 padding-right: 15px;
60 }
61 }
62 </style>
1 <template>
2 <div class="navbar" style="z-index: 989">
3 <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
4
5 <breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
6
7 <div class="right-menu">
8 <template v-if="device!=='mobile'">
9 <!-- <search id="header-search" class="right-menu-item" />-->
10
11 <!-- <el-tooltip content="项目文档" effect="dark" placement="bottom">-->
12 <!-- <Doc class="right-menu-item hover-effect" />-->
13 <!-- </el-tooltip>-->
14
15 <!-- <el-tooltip content="全屏缩放" effect="dark" placement="bottom">-->
16 <!-- <screenfull id="screenfull" class="right-menu-item hover-effect" />-->
17 <!-- </el-tooltip>-->
18
19 <!-- <el-tooltip content="布局设置" effect="dark" placement="bottom">-->
20 <!-- <size-select id="size-select" class="right-menu-item hover-effect" />-->
21 <!-- </el-tooltip>-->
22
23 </template>
24
25 <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
26 <div class="avatar-wrapper">
27 <img :src="user.avatar ? baseApi + '/avatar/' + user.avatar : Avatar" class="user-avatar">
28 <i class="el-icon-caret-bottom" />
29 </div>
30 <el-dropdown-menu slot="dropdown">
31 <!-- <span style="display:block;" @click="show = true">-->
32 <!-- <el-dropdown-item>-->
33 <!-- 布局设置-->
34 <!-- </el-dropdown-item>-->
35 <!-- </span>-->
36 <router-link to="/user/center">
37 <el-dropdown-item>
38 个人中心
39 </el-dropdown-item>
40 </router-link>
41 <span style="display:block;" @click="open">
42 <el-dropdown-item divided>
43 退出登录
44 </el-dropdown-item>
45 </span>
46 </el-dropdown-menu>
47 </el-dropdown>
48 </div>
49 </div>
50 </template>
51
52 <script>
53 import { mapGetters } from 'vuex'
54 import Breadcrumb from '@/components/Breadcrumb'
55 import Hamburger from '@/components/Hamburger'
56 // import Screenfull from '@/components/Screenfull'
57 import Avatar from '@/assets/images/avatar.png'
58
59 export default {
60 components: {
61 Breadcrumb,
62 Hamburger
63 },
64 data() {
65 return {
66 Avatar: Avatar,
67 dialogVisible: false
68 }
69 },
70 computed: {
71 ...mapGetters([
72 'sidebar',
73 'device',
74 'user',
75 'baseApi'
76 ]),
77 show: {
78 get() {
79 return this.$store.state.settings.showSettings
80 },
81 set(val) {
82 this.$store.dispatch('settings/changeSetting', {
83 key: 'showSettings',
84 value: val
85 })
86 }
87 }
88 },
89 methods: {
90 toggleSideBar() {
91 this.$store.dispatch('app/toggleSideBar')
92 },
93 open() {
94 this.$confirm('确定注销并退出系统吗?', '提示', {
95 confirmButtonText: '确定',
96 cancelButtonText: '取消',
97 type: 'warning'
98 }).then(() => {
99 this.logout()
100 })
101 },
102 logout() {
103 this.$store.dispatch('LogOut').then(() => {
104 location.reload()
105 })
106 }
107 }
108 }
109 </script>
110
111 <style lang="scss" scoped>
112 .navbar {
113 height: 50px;
114 overflow: hidden;
115 position: relative;
116 background: #fff;
117 box-shadow: 0 1px 4px rgba(0,21,41,.08);
118
119 .hamburger-container {
120 line-height: 46px;
121 height: 100%;
122 float: left;
123 cursor: pointer;
124 transition: background .3s;
125 -webkit-tap-highlight-color:transparent;
126
127 &:hover {
128 background: rgba(0, 0, 0, .025)
129 }
130 }
131
132 .breadcrumb-container {
133 float: left;
134 }
135
136 .errLog-container {
137 display: inline-block;
138 vertical-align: top;
139 }
140
141 .right-menu {
142 float: right;
143 height: 100%;
144 line-height: 50px;
145
146 &:focus {
147 outline: none;
148 }
149
150 .right-menu-item {
151 display: inline-block;
152 padding: 0 8px;
153 height: 100%;
154 font-size: 18px;
155 color: #5a5e66;
156 vertical-align: text-bottom;
157
158 &.hover-effect {
159 cursor: pointer;
160 transition: background .3s;
161
162 &:hover {
163 background: rgba(0, 0, 0, .025)
164 }
165 }
166 }
167
168 .avatar-container {
169 margin-right: 30px;
170
171 .avatar-wrapper {
172 margin-top: 5px;
173 position: relative;
174
175 .user-avatar {
176 cursor: pointer;
177 width: 40px;
178 height: 40px;
179 border-radius: 10px;
180 }
181
182 .el-icon-caret-bottom {
183 cursor: pointer;
184 position: absolute;
185 right: -20px;
186 top: 25px;
187 font-size: 12px;
188 }
189 }
190 }
191 }
192 }
193 </style>
1 <template>
2 <div class="drawer-container">
3 <div>
4 <h3 class="drawer-title">系统布局设置</h3>
5
6 <div class="drawer-item">
7 <span>主题颜色</span>
8 <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
9 </div>
10
11 <div class="drawer-item">
12 <span>显示标签</span>
13 <el-switch v-model="tagsView" class="drawer-switch" />
14 </div>
15
16 <div class="drawer-item">
17 <span>固定头部</span>
18 <el-switch v-model="fixedHeader" class="drawer-switch" />
19 </div>
20
21 <div class="drawer-item">
22 <span>显示LOGO</span>
23 <el-switch v-model="sidebarLogo" class="drawer-switch" />
24 </div>
25
26 <div class="drawer-item">
27 <span>菜单UniqueOpened</span>
28 <el-switch v-model="uniqueOpened" class="drawer-switch" />
29 </div>
30
31 </div>
32 </div>
33 </template>
34
35 <script>
36 import ThemePicker from '@/components/ThemePicker'
37
38 export default {
39 components: { ThemePicker },
40 data() {
41 return {}
42 },
43 computed: {
44 fixedHeader: {
45 get() {
46 return this.$store.state.settings.fixedHeader
47 },
48 set(val) {
49 this.$store.dispatch('settings/changeSetting', {
50 key: 'fixedHeader',
51 value: val
52 })
53 }
54 },
55 tagsView: {
56 get() {
57 return this.$store.state.settings.tagsView
58 },
59 set(val) {
60 this.$store.dispatch('settings/changeSetting', {
61 key: 'tagsView',
62 value: val
63 })
64 }
65 },
66 sidebarLogo: {
67 get() {
68 return this.$store.state.settings.sidebarLogo
69 },
70 set(val) {
71 this.$store.dispatch('settings/changeSetting', {
72 key: 'sidebarLogo',
73 value: val
74 })
75 }
76 },
77 uniqueOpened: {
78 get() {
79 return this.$store.state.settings.uniqueOpened
80 },
81 set(val) {
82 this.$store.dispatch('settings/changeSetting', {
83 key: 'uniqueOpened',
84 value: val
85 })
86 }
87 }
88 },
89 methods: {
90 themeChange(val) {
91 this.$store.dispatch('settings/changeSetting', {
92 key: 'theme',
93 value: val
94 })
95 }
96 }
97 }
98 </script>
99
100 <style lang="scss" scoped>
101 .drawer-container {
102 padding: 24px;
103 font-size: 14px;
104 line-height: 1.5;
105 word-wrap: break-word;
106
107 .drawer-title {
108 margin-bottom: 12px;
109 color: rgba(0, 0, 0, .85);
110 font-size: 14px;
111 line-height: 22px;
112 }
113
114 .drawer-item {
115 color: rgba(0, 0, 0, .65);
116 font-size: 14px;
117 padding: 12px 0;
118 }
119
120 .drawer-switch {
121 float: right
122 }
123 }
124 </style>
1 export default {
2 computed: {
3 device() {
4 return this.$store.state.app.device
5 }
6 },
7 mounted() {
8 // In order to fix the click on menu on the ios device will trigger the mouseleave bug
9 // https://github.com/PanJiaChen/vue-element-admin/issues/1135
10 this.fixBugIniOS()
11 },
12 methods: {
13 fixBugIniOS() {
14 const $subMenu = this.$refs.subMenu
15 if ($subMenu) {
16 const handleMouseleave = $subMenu.handleMouseleave
17 $subMenu.handleMouseleave = (e) => {
18 if (this.device === 'mobile') {
19 return
20 }
21 handleMouseleave(e)
22 }
23 }
24 }
25 }
26 }
1 <script>
2 export default {
3 name: 'MenuItem',
4 functional: true,
5 props: {
6 icon: {
7 type: String,
8 default: ''
9 },
10 title: {
11 type: String,
12 default: ''
13 }
14 },
15 render(h, context) {
16 const { icon, title } = context.props
17 const vnodes = []
18
19 if (icon) {
20 vnodes.push(<svg-icon icon-class={icon}/>)
21 }
22
23 if (title) {
24 vnodes.push(<span slot='title'>{(title)}</span>)
25 }
26 return vnodes
27 }
28 }
29 </script>
1
2 <template>
3 <!-- eslint-disable vue/require-component-is -->
4 <component v-bind="linkProps(to)">
5 <slot />
6 </component>
7 </template>
8
9 <script>
10 import { isExternal } from '@/utils/validate'
11
12 export default {
13 props: {
14 to: {
15 type: String,
16 required: true
17 }
18 },
19 methods: {
20 linkProps(url) {
21 if (isExternal(url)) {
22 return {
23 is: 'a',
24 href: url,
25 target: '_blank',
26 rel: 'noopener'
27 }
28 }
29 return {
30 is: 'router-link',
31 to: url
32 }
33 }
34 }
35 }
36 </script>
1 <template>
2 <div class="sidebar-logo-container" :class="{'collapse':collapse}">
3 <transition name="sidebarLogoFade">
4 <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
5 <!-- <img v-if="logo" :src="logo" class="sidebar-logo" style="width: 80%;height: 80%">-->
6 <h1 class="sidebar-title">{{ stitle }} </h1>-->
7 </router-link>
8 <router-link v-else key="expand" class="sidebar-logo-link" to="/">
9 <!-- <img v-if="logo" :src="logo" class="sidebar-logo">-->
10 <h1 class="sidebar-title" style="font-size: 28px;color: #f56c6c;font-family: fantasy;text-align: center;">{{ title }} </h1>
11 </router-link>
12 </transition>
13 </div>
14 </template>
15
16 <script>
17 import Logo from '@/assets/bg/title.png'
18 export default {
19 name: 'SidebarLogo',
20 props: {
21 collapse: {
22 type: Boolean,
23 required: true
24 }
25 },
26 data() {
27 return {
28 title: '基础框架',
29 stitle: '基础框架',
30 logo: Logo
31 }
32 }
33 }
34 </script>
35
36 <style lang="scss" scoped>
37 .sidebarLogoFade-enter-active {
38 transition: opacity 1.5s;
39 }
40
41 .sidebarLogoFade-enter,
42 .sidebarLogoFade-leave-to {
43 opacity: 0;
44 }
45
46 .sidebar-logo-container {
47 position: relative;
48 width: 100%;
49 height: 50px;
50 line-height: 50px;
51 text-align: center;
52 overflow: hidden;
53 background-color: rgb(48, 65, 86);
54
55 & .sidebar-logo-link {
56 height: 100%;
57 width: 100%;
58
59 & .sidebar-logo {
60 width: 90%;
61 height: 100%;
62 vertical-align: middle;
63 margin-right: 6px;
64 }
65
66 & .sidebar-title {
67 display: inline-block;
68 margin: 0;
69 color: #fff;
70 font-weight: 600;
71 line-height: 50px;
72 font-size: 14px;
73 font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
74 vertical-align: middle;
75 }
76 }
77
78 &.collapse {
79 .sidebar-logo {
80 margin-right: 0px;
81 }
82 }
83 }
84 </style>
1 <template>
2 <div v-if="!item.hidden" class="menu-wrapper">
3 <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
4 <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
5 <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
6 <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
7 </el-menu-item>
8 </app-link>
9 </template>
10
11 <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
12 <template slot="title">
13 <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
14 </template>
15 <sidebar-item
16 v-for="child in item.children"
17 :key="child.path"
18 :is-nest="true"
19 :item="child"
20 :base-path="resolvePath(child.path)"
21 class="nest-menu"
22 />
23 </el-submenu>
24 </div>
25 </template>
26
27 <script>
28 import path from 'path'
29 import { isExternal } from '@/utils/validate'
30 import Item from './Item'
31 import AppLink from './Link'
32 import FixiOSBug from './FixiOSBug'
33
34 export default {
35 name: 'SidebarItem',
36 components: { Item, AppLink },
37 mixins: [FixiOSBug],
38 props: {
39 // route object
40 item: {
41 type: Object,
42 required: true
43 },
44 isNest: {
45 type: Boolean,
46 default: false
47 },
48 basePath: {
49 type: String,
50 default: ''
51 }
52 },
53 data() {
54 // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
55 // TODO: refactor with render function
56 this.onlyOneChild = null
57 return {}
58 },
59 methods: {
60 hasOneShowingChild(children = [], parent) {
61 const showingChildren = children.filter(item => {
62 if (item.hidden) {
63 return false
64 } else {
65 // Temp set(will be used if only has one showing child)
66 this.onlyOneChild = item
67 return true
68 }
69 })
70
71 // When there is only one child router, the child router is displayed by default
72 if (showingChildren.length === 1) {
73 return true
74 }
75
76 // Show parent if there are no child router to display
77 if (showingChildren.length === 0) {
78 this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
79 return true
80 }
81
82 return false
83 },
84 resolvePath(routePath) {
85 if (isExternal(routePath)) {
86 return routePath
87 }
88 if (isExternal(this.basePath)) {
89 return this.basePath
90 }
91 return path.resolve(this.basePath, routePath)
92 }
93 }
94 }
95 </script>
1 <template>
2 <div :class="{'has-logo':showLogo}">
3 <logo v-if="showLogo" :collapse="isCollapse" />
4 <el-scrollbar wrap-class="scrollbar-wrapper">
5 <el-menu
6 :default-active="activeMenu"
7 :collapse="isCollapse"
8 :background-color="variables.menuBg"
9 :text-color="variables.menuText"
10 :unique-opened="$store.state.settings.uniqueOpened"
11 :active-text-color="variables.menuActiveText"
12 :collapse-transition="false"
13 mode="vertical"
14 >
15 <sidebar-item v-for="route in sidebarRouters" :key="route.path" :item="route" :base-path="route.path" />
16 </el-menu>
17 </el-scrollbar>
18 </div>
19 </template>
20
21 <script>
22 import { mapGetters } from 'vuex'
23 import Logo from './Logo'
24 import SidebarItem from './SidebarItem'
25 import variables from '@/assets/styles/variables.scss'
26
27 export default {
28 components: { SidebarItem, Logo },
29 computed: {
30 ...mapGetters([
31 'sidebarRouters',
32 'sidebar'
33 ]),
34 activeMenu() {
35 const route = this.$route
36 const { meta, path } = route
37 // if set path, the sidebar will highlight the path you set
38 if (meta.activeMenu) {
39 return meta.activeMenu
40 }
41 return path
42 },
43 showLogo() {
44 return this.$store.state.settings.sidebarLogo
45 },
46 variables() {
47 return variables
48 },
49 isCollapse() {
50 return !this.sidebar.opened
51 }
52 }
53 }
54 </script>
1 <template>
2 <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
3 <slot />
4 </el-scrollbar>
5 </template>
6
7 <script>
8 const tagAndTagSpacing = 4 // tagAndTagSpacing
9
10 export default {
11 name: 'ScrollPane',
12 data() {
13 return {
14 left: 0
15 }
16 },
17 computed: {
18 scrollWrapper() {
19 return this.$refs.scrollContainer.$refs.wrap
20 }
21 },
22 methods: {
23 handleScroll(e) {
24 const eventDelta = e.wheelDelta || -e.deltaY * 40
25 const $scrollWrapper = this.scrollWrapper
26 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
27 },
28 moveToTarget(currentTag) {
29 const $container = this.$refs.scrollContainer.$el
30 const $containerWidth = $container.offsetWidth
31 const $scrollWrapper = this.scrollWrapper
32 const tagList = this.$parent.$refs.tag
33
34 let firstTag = null
35 let lastTag = null
36
37 // find first tag and last tag
38 if (tagList.length > 0) {
39 firstTag = tagList[0]
40 lastTag = tagList[tagList.length - 1]
41 }
42
43 if (firstTag === currentTag) {
44 $scrollWrapper.scrollLeft = 0
45 } else if (lastTag === currentTag) {
46 $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
47 } else {
48 // find preTag and nextTag
49 const currentIndex = tagList.findIndex(item => item === currentTag)
50 const prevTag = tagList[currentIndex - 1]
51 const nextTag = tagList[currentIndex + 1]
52
53 // the tag's offsetLeft after of nextTag
54 const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
55
56 // the tag's offsetLeft before of prevTag
57 const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
58
59 if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
60 $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
61 } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
62 $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
63 }
64 }
65 }
66 }
67 }
68 </script>
69
70 <style lang="scss" scoped>
71 .scroll-container {
72 white-space: nowrap;
73 position: relative;
74 overflow: hidden;
75 width: 100%;
76 ::v-deep {
77 .el-scrollbar__bar {
78 bottom: 0px;
79 }
80 .el-scrollbar__wrap {
81 height: 49px;
82 }
83 }
84 }
85 </style>
1 <template>
2 <div id="tags-view-container" class="tags-view-container">
3 <scroll-pane ref="scrollPane" class="tags-view-wrapper">
4 <router-link
5 v-for="tag in visitedViews"
6 ref="tag"
7 :key="tag.path"
8 :class="isActive(tag)?'active':''"
9 :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
10 tag="span"
11 class="tags-view-item"
12 @click.middle.native="closeSelectedTag(tag)"
13 @contextmenu.prevent.native="openMenu(tag,$event)"
14 >
15 {{ tag.title }}
16 <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
17 </router-link>
18 </scroll-pane>
19 <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
20 <li @click="refreshSelectedTag(selectedTag)">刷新</li>
21 <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">关闭</li>
22 <li @click="closeOthersTags">关闭其他</li>
23 <li @click="closeAllTags(selectedTag)">关闭全部</li>
24 </ul>
25 </div>
26 </template>
27
28 <script>
29 import ScrollPane from './ScrollPane'
30 import path from 'path'
31
32 export default {
33 components: { ScrollPane },
34 data() {
35 return {
36 visible: false,
37 top: 0,
38 left: 0,
39 selectedTag: {},
40 affixTags: []
41 }
42 },
43 computed: {
44 visitedViews() {
45 return this.$store.state.tagsView.visitedViews
46 },
47 routes() {
48 return this.$store.state.permission.routers
49 }
50 },
51 watch: {
52 $route() {
53 this.addTags()
54 this.moveToCurrentTag()
55 },
56 visible(value) {
57 if (value) {
58 document.body.addEventListener('click', this.closeMenu)
59 } else {
60 document.body.removeEventListener('click', this.closeMenu)
61 }
62 }
63 },
64 mounted() {
65 this.initTags()
66 this.addTags()
67 },
68 methods: {
69 isActive(route) {
70 return route.path === this.$route.path
71 },
72 filterAffixTags(routes, basePath = '/') {
73 let tags = []
74 routes.forEach(route => {
75 if (route.meta && route.meta.affix) {
76 const tagPath = path.resolve(basePath, route.path)
77 tags.push({
78 fullPath: tagPath,
79 path: tagPath,
80 name: route.name,
81 meta: { ...route.meta }
82 })
83 }
84 if (route.children) {
85 const tempTags = this.filterAffixTags(route.children, route.path)
86 if (tempTags.length >= 1) {
87 tags = [...tags, ...tempTags]
88 }
89 }
90 })
91 return tags
92 },
93 initTags() {
94 const affixTags = this.affixTags = this.filterAffixTags(this.routes)
95 for (const tag of affixTags) {
96 // Must have tag name
97 if (tag.name) {
98 this.$store.dispatch('tagsView/addVisitedView', tag)
99 }
100 }
101 },
102 addTags() {
103 const { name } = this.$route
104 if (name) {
105 this.$store.dispatch('tagsView/addView', this.$route)
106 }
107 return false
108 },
109 moveToCurrentTag() {
110 const tags = this.$refs.tag
111 this.$nextTick(() => {
112 for (const tag of tags) {
113 if (tag.to.path === this.$route.path) {
114 this.$refs.scrollPane.moveToTarget(tag)
115 // when query is different then update
116 if (tag.to.fullPath !== this.$route.fullPath) {
117 this.$store.dispatch('tagsView/updateVisitedView', this.$route)
118 }
119 break
120 }
121 }
122 })
123 },
124 refreshSelectedTag(view) {
125 this.$store.dispatch('tagsView/delCachedView', view).then(() => {
126 const { fullPath } = view
127 this.$nextTick(() => {
128 this.$router.replace({
129 path: '/redirect' + fullPath
130 })
131 })
132 })
133 },
134 closeSelectedTag(view) {
135 this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
136 if (this.isActive(view)) {
137 this.toLastView(visitedViews, view)
138 }
139 })
140 },
141 closeOthersTags() {
142 this.$router.push(this.selectedTag)
143 this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
144 this.moveToCurrentTag()
145 })
146 },
147 closeAllTags(view) {
148 this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
149 if (this.affixTags.some(tag => tag.path === view.path)) {
150 return
151 }
152 this.toLastView(visitedViews, view)
153 })
154 },
155 toLastView(visitedViews, view) {
156 const latestView = visitedViews.slice(-1)[0]
157 if (latestView) {
158 this.$router.push(latestView)
159 } else {
160 // now the default is to redirect to the home page if there is no tags-view,
161 // you can adjust it according to your needs.
162 if (view.name === 'Dashboard') {
163 // to reload home page
164 this.$router.replace({ path: '/redirect' + view.fullPath })
165 } else {
166 this.$router.push('/')
167 }
168 }
169 },
170 openMenu(tag, e) {
171 const menuMinWidth = 105
172 const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
173 const offsetWidth = this.$el.offsetWidth // container width
174 const maxLeft = offsetWidth - menuMinWidth // left boundary
175 const left = e.clientX - offsetLeft + 15 // 15: margin right
176
177 if (left > maxLeft) {
178 this.left = maxLeft
179 } else {
180 this.left = left
181 }
182
183 this.top = e.clientY
184 this.visible = true
185 this.selectedTag = tag
186 },
187 closeMenu() {
188 this.visible = false
189 }
190 }
191 }
192 </script>
193
194 <style lang="scss" scoped>
195 .tags-view-container {
196 //height: 34px;
197 height: 0px;
198 width: 100%;
199 background: #fff;
200 border-bottom: 1px solid #d8dce5;
201 box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
202 .tags-view-wrapper {
203 .tags-view-item {
204 display: inline-block;
205 position: relative;
206 cursor: pointer;
207 height: 26px;
208 line-height: 26px;
209 border: 1px solid #d8dce5;
210 color: #495060;
211 background: #fff;
212 padding: 0 8px;
213 font-size: 12px;
214 margin-left: 5px;
215 margin-top: 4px;
216 &:first-of-type {
217 margin-left: 15px;
218 }
219 &:last-of-type {
220 margin-right: 15px;
221 }
222 &.active {
223 background-color: #42b983;
224 color: #fff;
225 border-color: #42b983;
226 &::before {
227 content: '';
228 background: #fff;
229 display: inline-block;
230 width: 8px;
231 height: 8px;
232 border-radius: 50%;
233 position: relative;
234 margin-right: 2px;
235 }
236 }
237 }
238 }
239 .contextmenu {
240 margin: 0;
241 background: #fff;
242 z-index: 3000;
243 position: absolute;
244 list-style-type: none;
245 padding: 5px 0;
246 border-radius: 4px;
247 font-size: 12px;
248 font-weight: 400;
249 color: #333;
250 box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
251 li {
252 margin: 0;
253 padding: 7px 16px;
254 cursor: pointer;
255 &:hover {
256 background: #eee;
257 }
258 }
259 }
260 }
261 </style>
262
263 <style lang="scss">
264 //reset element css of el-icon-close
265 .tags-view-wrapper {
266 .tags-view-item {
267 .el-icon-close {
268 width: 16px;
269 height: 16px;
270 vertical-align: 2px;
271 border-radius: 50%;
272 text-align: center;
273 transition: all .3s cubic-bezier(.645, .045, .355, 1);
274 transform-origin: 100% 50%;
275 &:before {
276 transform: scale(.6);
277 display: inline-block;
278 vertical-align: -3px;
279 }
280 &:hover {
281 background-color: #b4bccc;
282 color: #fff;
283 }
284 }
285 }
286 }
287 </style>
1 export { default as AppMain } from './AppMain'
2 export { default as Navbar } from './Navbar'
3 export { default as Settings } from './Settings'
4 export { default as Sidebar } from './Sidebar/index.vue'
5 export { default as TagsView } from './TagsView/index.vue'
1 <template>
2 <div :class="classObj" class="app-wrapper">
3 <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
4 <sidebar class="sidebar-container" />
5 <div :class="{hasTagsView:needTagsView}" class="main-container">
6 <div :class="{'fixed-header':fixedHeader}">
7 <navbar />
8 <!-- <tags-view v-if="needTagsView" />-->
9 <tags-view v-if="false" />
10 </div>
11 <app-main />
12 <right-panel v-if="showSettings">
13 <settings />
14 </right-panel>
15 </div>
16 <!-- 防止刷新后主题丢失 -->
17 <Theme v-show="false" ref="theme" />
18 </div>
19 </template>
20
21 <script>
22 import RightPanel from '@/components/RightPanel'
23 import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
24 import ResizeMixin from './mixin/ResizeHandler'
25 import { mapState } from 'vuex'
26 import Theme from '@/components/ThemePicker'
27 import Cookies from 'js-cookie'
28 export default {
29 name: 'Layout',
30 components: {
31 AppMain,
32 Navbar,
33 RightPanel,
34 Settings,
35 Sidebar,
36 TagsView,
37 Theme
38 },
39 mixins: [ResizeMixin],
40 computed: {
41 ...mapState({
42 sidebar: state => state.app.sidebar,
43 device: state => state.app.device,
44 showSettings: state => state.settings.showSettings,
45 needTagsView: state => state.settings.tagsView,
46 fixedHeader: state => state.settings.fixedHeader
47 }),
48 classObj() {
49 return {
50 hideSidebar: !this.sidebar.opened,
51 openSidebar: this.sidebar.opened,
52 withoutAnimation: this.sidebar.withoutAnimation,
53 mobile: this.device === 'mobile'
54 }
55 }
56 },
57 mounted() {
58 if (Cookies.get('theme')) {
59 this.$refs.theme.theme = Cookies.get('theme')
60 this.$store.dispatch('settings/changeSetting', {
61 key: 'theme',
62 value: Cookies.get('theme')
63 })
64 }
65 },
66 methods: {
67 handleClickOutside() {
68 this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
69 }
70 }
71 }
72 </script>
73
74 <style lang="scss" scoped>
75 @import "~@/assets/styles/mixin.scss";
76 @import "~@/assets/styles/variables.scss";
77
78 .app-wrapper {
79 @include clearfix;
80 position: relative;
81 height: 100%;
82 width: 100%;
83
84 &.mobile.openSidebar {
85 position: fixed;
86 top: 0;
87 }
88 }
89
90 .drawer-bg {
91 background: #000;
92 opacity: 0.3;
93 width: 100%;
94 top: 0;
95 height: 100%;
96 position: absolute;
97 z-index: 999;
98 }
99
100 .fixed-header {
101 position: fixed;
102 top: 0;
103 right: 0;
104 z-index: 9;
105 width: calc(100% - #{$sideBarWidth});
106 transition: width 0.28s;
107 padding: 0;
108 }
109
110 .hideSidebar .fixed-header {
111 width: calc(100% - 54px)
112 }
113
114 .mobile .fixed-header {
115 width: 100%;
116 }
117 </style>
1 import store from '@/store'
2
3 const { body } = document
4 const WIDTH = 992 // refer to Bootstrap's responsive design
5
6 export default {
7 watch: {
8 $route(route) {
9 if (this.device === 'mobile' && this.sidebar.opened) {
10 store.dispatch('app/closeSideBar', { withoutAnimation: false })
11 }
12 }
13 },
14 beforeMount() {
15 window.addEventListener('resize', this.$_resizeHandler)
16 },
17 beforeDestroy() {
18 window.removeEventListener('resize', this.$_resizeHandler)
19 },
20 mounted() {
21 const isMobile = this.$_isMobile()
22 if (isMobile) {
23 store.dispatch('app/toggleDevice', 'mobile')
24 store.dispatch('app/closeSideBar', { withoutAnimation: true })
25 }
26 },
27 methods: {
28 // use $_ for mixins properties
29 // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
30 $_isMobile() {
31 const rect = body.getBoundingClientRect()
32 return rect.width - 1 < WIDTH
33 },
34 $_resizeHandler() {
35 if (!document.hidden) {
36 const isMobile = this.$_isMobile()
37 store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
38
39 if (isMobile) {
40 store.dispatch('app/closeSideBar', { withoutAnimation: true })
41 }
42 }
43 }
44 }
45 }
1 import Vue from 'vue'
2
3 import Cookies from 'js-cookie'
4
5 import 'normalize.css/normalize.css'
6
7 import Element from 'element-ui'
8 import ElSelectTree from 'el-select-tree'
9
10 Vue.use(ElSelectTree)
11 //
12 import mavonEditor from 'mavon-editor'
13 import 'mavon-editor/dist/css/index.css'
14
15 // 数据字典
16 import dict from './components/Dict'
17
18 // 权限指令
19 import checkPer from '@/utils/permission'
20 import permission from './components/Permission'
21 import './assets/styles/element-variables.scss'
22 // global css
23 import './assets/styles/index.scss'
24
25 // 代码高亮
26 import VueHighlightJS from 'vue-highlightjs'
27 import 'highlight.js/styles/atom-one-dark.css'
28
29 import App from './App'
30 import store from './store'
31 import router from './router/routers'
32
33 import './assets/icons' // icon
34 import './router/index' // permission control
35 import 'echarts-gl'
36 import echarts from 'echarts'
37
38 Vue.use(checkPer)
39 Vue.use(VueHighlightJS)
40 Vue.use(mavonEditor)
41 Vue.use(permission)
42 Vue.use(dict)
43 Vue.use(Element, {
44 size: Cookies.get('size') || 'small' // set element-ui default size
45 })
46 Vue.prototype.$echarts = echarts
47
48 Vue.config.productionTip = false
49
50 new Vue({
51 el: '#app',
52 router,
53 store,
54 render: h => h(App)
55 })
1 import {
2 initData,
3 download
4 } from '@/api/data'
5 import {
6 parseTime,
7 downloadFile
8 } from '@/utils/index'
9
10 export default {
11 data() {
12 return {
13 // 表格数据
14 data: [],
15 // 排序规则,默认 id 降序, 支持多字段排序 ['id,desc', 'createTime,asc']
16 sort: ['id,desc'],
17 // 页码
18 page: 0,
19 // 每页数据条数
20 size: 10,
21 // 总数据条数
22 total: 0,
23 // 请求数据的url
24 url: '',
25 // 查询数据的参数
26 params: {},
27 // 待查询的对象
28 query: {},
29 // 等待时间
30 time: 50,
31 // 是否为新增类型的表单
32 isAdd: false,
33 // 导出的 Loading
34 downloadLoading: false,
35 // 表格 Loading 属性
36 loading: true,
37 // 删除 Loading 属性
38 delLoading: false,
39 delAllLoading: false,
40 // 弹窗属性
41 dialog: false,
42 // Form 表单
43 form: {},
44 // 重置表单
45 resetForm: {},
46 // 标题
47 title: ''
48 }
49 },
50 methods: {
51 parseTime,
52 downloadFile,
53 async init() {
54 if (!await this.beforeInit()) {
55 return
56 }
57 return new Promise((resolve, reject) => {
58 this.loading = true
59 // 请求数据
60 initData(this.url, this.getQueryParame()).then(data => {
61 this.total = data.totalElements
62 this.data = data.content
63 // time 毫秒后显示表格
64 setTimeout(() => {
65 this.loading = false
66 }, this.time)
67 resolve(data)
68 }).catch(err => {
69 this.loading = false
70 reject(err)
71 })
72 })
73 },
74 beforeInit() {
75 return true
76 },
77 getQueryParame: function() {
78 return {
79 page: this.page,
80 size: this.size,
81 sort: this.sort,
82 ...this.query,
83 ...this.params
84 }
85 },
86 // 改变页码
87 pageChange(e) {
88 this.page = e - 1
89 this.init()
90 },
91 // 改变每页显示数
92 sizeChange(e) {
93 this.page = 0
94 this.size = e
95 this.init()
96 },
97 // 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
98 dleChangePage(size) {
99 if (size === undefined) {
100 size = 1
101 }
102 if (this.data.length === size && this.page !== 0) {
103 this.page = this.page - 1
104 }
105 },
106 // 查询方法
107 toQuery() {
108 this.page = 0
109 this.init()
110 },
111 /**
112 * 通用的提示封装
113 */
114 submitSuccessNotify() {
115 this.$notify({
116 title: '提交成功',
117 type: 'success',
118 duration: 2500
119 })
120 },
121 addSuccessNotify() {
122 this.$notify({
123 title: '新增成功',
124 type: 'success',
125 duration: 2500
126 })
127 },
128 editSuccessNotify() {
129 this.$notify({
130 title: '编辑成功',
131 type: 'success',
132 duration: 2500
133 })
134 },
135 delSuccessNotify() {
136 this.$notify({
137 title: '删除成功',
138 type: 'success',
139 duration: 2500
140 })
141 },
142 notify(title, type) {
143 this.$notify({
144 title: title,
145 type: type,
146 duration: 2500
147 })
148 },
149 /**
150 * 删除前可以调用 beforeDelMethod 做一些操作
151 */
152 beforeDelMethod() {
153 return true
154 },
155 /**
156 * 通用的删除
157 */
158 delMethod(id) {
159 if (!this.beforeDelMethod()) {
160 return
161 }
162 this.delLoading = true
163 this.crudMethod.del(id).then(() => {
164 this.delLoading = false
165 this.$refs[id].doClose()
166 this.dleChangePage()
167 this.delSuccessNotify()
168 this.afterDelMethod()
169 this.init()
170 }).catch(() => {
171 this.delLoading = false
172 this.$refs[id].doClose()
173 })
174 },
175 afterDelMethod() {},
176 /**
177 * 多选删除提示
178 */
179 beforeDelAllMethod() {
180 this.$confirm('你确定删除选中的数据吗?', '提示', {
181 confirmButtonText: '确定',
182 cancelButtonText: '取消',
183 type: 'warning'
184 }).then(() => {
185 this.delAllMethod()
186 })
187 },
188 /**
189 * 多选删除
190 */
191 delAllMethod() {
192 this.delAllLoading = true
193 const data = this.$refs.table.selection
194 const ids = []
195 for (let i = 0; i < data.length; i++) {
196 ids.push(data[i].id)
197 }
198 this.crudMethod.delAll(ids).then(() => {
199 this.delAllLoading = false
200 this.dleChangePage(ids.length)
201 this.init()
202 this.$notify({
203 title: '删除成功',
204 type: 'success',
205 duration: 2500
206 })
207 }).catch(() => {
208 this.delAllLoading = false
209 })
210 },
211 /**
212 * 显示新增弹窗前可以调用该方法
213 */
214 beforeShowAddForm() {},
215 /**
216 * 显示新增弹窗
217 */
218 showAddFormDialog() {
219 this.isAdd = true
220 this.resetForm = JSON.parse(JSON.stringify(this.form))
221 this.beforeShowAddForm()
222 this.dialog = true
223 },
224 /**
225 * 显示编辑弹窗前可以调用该方法
226 */
227 beforeShowEditForm(data) {},
228 /**
229 * 显示编辑弹窗
230 */
231 showEditFormDialog(data = '') {
232 this.isAdd = false
233 if (data) {
234 this.resetForm = JSON.parse(JSON.stringify(this.form))
235 this.form = JSON.parse(JSON.stringify(data))
236 }
237 this.beforeShowEditForm(data)
238 this.dialog = true
239 },
240 /**
241 * 新增方法
242 */
243 addMethod() {
244 this.crudMethod.add(this.form).then(() => {
245 this.addSuccessNotify()
246 this.loading = false
247 this.afterAddMethod()
248 this.cancel()
249 this.init()
250 }).catch(() => {
251 this.loading = false
252 this.afterAddErrorMethod()
253 })
254 },
255 /**
256 * 新增后可以调用该方法
257 */
258 afterAddMethod() {},
259 /**
260 * 新增失败后调用该方法
261 */
262 afterAddErrorMethod() {},
263 /**
264 * 通用的编辑方法
265 */
266 editMethod() {
267 this.crudMethod.edit(this.form).then(() => {
268 this.editSuccessNotify()
269 this.loading = false
270 this.afterEditMethod()
271 this.cancel()
272 this.init()
273 }).catch(() => {
274 this.loading = false
275 })
276 },
277 /**
278 * 编辑后可以调用该方法
279 */
280 afterEditMethod() {},
281 /**
282 * 提交前可以调用该方法
283 */
284 beforeSubmitMethod() {
285 return true
286 },
287 /**
288 * 提交
289 */
290 submitMethod() {
291 if (!this.beforeSubmitMethod()) {
292 return
293 }
294 if (this.$refs['form']) {
295 this.$refs['form'].validate((valid) => {
296 if (valid) {
297 this.loading = true
298 if (this.isAdd) {
299 this.addMethod()
300 } else this.editMethod()
301 }
302 })
303 }
304 },
305 /**
306 * 隐藏弹窗
307 */
308 cancel() {
309 this.dialog = false
310 if (this.$refs['form']) {
311 this.$refs['form'].clearValidate()
312 this.form = this.resetForm
313 }
314 },
315 /**
316 * 获取弹窗的标题
317 */
318 getFormTitle() {
319 return this.isAdd ? `新增${this.title}` : `编辑${this.title}`
320 },
321 /**
322 * 通用导出
323 */
324 downloadMethod() {
325 this.beforeInit()
326 this.downloadLoading = true
327 download(this.url + '/download', this.params).then(result => {
328 this.downloadFile(result, this.title + '数据', 'xlsx')
329 this.downloadLoading = false
330 }).catch(() => {
331 this.downloadLoading = false
332 })
333 }
334 }
335 }
1 import { initData } from '@/api/data'
2 import { isBlank, isInteger } from '@/utils/validate'
3 import { deepCopy } from '../utils/common-util'
4 export default {
5 data() {
6 return {
7 loading: true, copyData: [], data: [], page: 0, size: 10, sort: 'id,desc', total: 0, url: '', params: {}, query: { type: '', value: '' }, time: 170, isAdd: false, curQueryType: undefined, downloadLoading: false
8 }
9 },
10 methods: {
11 async init(type) {
12 if (!await this.beforeInit()) {
13 this.loading = false
14 return
15 }
16 // console.log(this.params, 'sdfhisdjkf')
17 return new Promise((resolve, reject) => {
18 this.loading = true
19 initData(this.url, this.params).then(res => {
20 this.total = res.totalElements
21 this.data = res.content
22 this.copyData = deepCopy(this.data)
23 if (type && type === 'input') {
24 this.data.forEach(item => {
25 item.copyEpisodeC2Status = []
26 for (const key in item.episodeC2Status) {
27 const obj = {
28 name: key
29 }
30 const sobj = {}
31 Object.assign(sobj, obj, item.episodeC2Status[key])
32 sobj.successNum = sobj.episodeCount ? Math.ceil((sobj.successCount / sobj.episodeCount) * 130) : 0
33 sobj.failNum = sobj.episodeCount ? Math.ceil((sobj.failureCount / sobj.episodeCount) * 130) : 0
34 item.copyEpisodeC2Status.push(sobj)
35 }
36 })
37 } else if (type && type === 'hasInput') {
38 this.data.forEach(item => {
39 item.successNum = item.cmdCount ? Math.ceil((item.successCount / item.cmdCount) * 130) : 0
40 item.failNum = item.cmdCount ? Math.ceil((item.failureCount / item.cmdCount) * 130) : 0
41 })
42 }
43 setTimeout(() => {
44 this.loading = false
45 }, this.time)
46 resolve(res)
47 }).catch(err => {
48 this.loading = false
49 reject(err)
50 }).finally(() => {
51 if (this.codesResultList !== null && this.codesResultList !== undefined) {
52 this.codesResultList = []
53 }
54 })
55 })
56 },
57 beforeInit() {
58 return true
59 },
60 pageChange(e, type) {
61 this.page = e - 1
62 this.init(type)
63 },
64 sizeChange(e, type) {
65 this.page = 0
66 this.size = e
67 this.init(type)
68 },
69 sortChange(data) {
70 const { prop, order } = data
71 let direction
72 if (order === 'ascending') {
73 direction = 'asc'
74 } else {
75 direction = 'desc'
76 }
77 this.sort = prop + ',' + direction
78 this.page = 0
79 // console.log(this.sort)
80 this.init()
81 },
82 // 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
83 dleChangePage(size) {
84 if (size === undefined) {
85 size = 1
86 }
87 if (this.data.length === size && this.page !== 0) {
88 this.page = this.page - 1
89 }
90 },
91 toQuery(type) {
92 if (this.curQueryType) {
93 const value = this.query.value
94 const display = this.curQueryType.display_name
95 // 在queryTypeOptions需要校验的对象加上type='number'
96 if (this.curQueryType.type === 'number') {
97 // 如果值非空且不为数字则弹出错误提示
98 if (!isBlank(value) && !isInteger(value)) {
99 this.$message.error(display + '必须为整数')
100 return
101 }
102 }
103 }
104 this.page = 0
105 this.init(type)
106 },
107 // el-select change函数,为curQueryType赋值,为查询输入内容进行类型校验
108 typeChange(val, options) {
109 this.curQueryType = options.find(item => {
110 return item.key === val
111 })
112 }
113 }
114 }
1 import { get, getDictMap } from '@/api/dictDetail'
2
3 export default {
4 data() {
5 return {
6 dicts: [], dictMap: {}
7 }
8 },
9 methods: {
10 async getDict(name) {
11 return new Promise((resolve, reject) => {
12 get(name).then(res => {
13 this.dicts = res.content
14 resolve(res)
15 }).catch(err => {
16 reject(err)
17 })
18 })
19 },
20 // 多个字典查询时使用逗号拼接, 如:
21 // 加载多个数据字典,如何调用如下:
22 // this.getDict('user_status,job_status')
23 // 在vue中使用加载出来的字典:
24 // dictMap.[字典名称] 如:dictMap.user_status、 dictMap.job_status
25 async getDictMap(names) {
26 // 优先放入到dictMap中,避免页面加载时 undefined
27 const arr = names.split(',')
28 for (let i = 0; i < arr.length; i++) {
29 this.dictMap[arr[i]] = []
30 }
31 return new Promise((resolve, reject) => {
32 getDictMap(names).then(res => {
33 this.dictMap = res
34 resolve(res)
35 }).catch(err => {
36 reject(err)
37 })
38 })
39 }
40 }
41 }
1 import router from './routers'
2 import store from '@/store'
3 import Config from '@/settings'
4 import NProgress from 'nprogress' // progress bar
5 import 'nprogress/nprogress.css'// progress bar style
6 import { getToken } from '@/utils/auth' // getToken from cookie
7 import { buildMenus } from '@/api/system/menu'
8 import { filterAsyncRouter } from '@/store/modules/permission'
9 NProgress.configure({ showSpinner: false })// NProgress Configuration
10 const whiteList = ['/login']// no redirect whitelist
11 router.beforeEach((to, from, next) => {
12 if (to.meta.title) {
13 document.title = to.meta.title + ' - ' + Config.title
14 }
15 NProgress.start()
16 if (getToken()) {
17 // 已登录且要跳转的页面是登录页
18 if (to.path === '/login') {
19 next({ path: '/' })
20 NProgress.done()
21 } else {
22 if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
23 store.dispatch('GetInfo').then(() => { // 拉取user_info
24 // 动态路由,拉取菜单
25 loadMenus(next, to)
26 }).catch(() => {
27 store.dispatch('LogOut').then(() => {
28 location.reload() // 为了重新实例化vue-router对象 避免bug
29 })
30 })
31 // 登录时未拉取 菜单,在此处拉取
32 } else if (store.getters.loadMenus) {
33 // 修改成false,防止死循环
34 store.dispatch('updateLoadMenus')
35 loadMenus(next, to)
36 } else {
37 next()
38 }
39 }
40 } else {
41 /* has no token*/
42 if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
43 next()
44 } else {
45 next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
46 NProgress.done()
47 }
48 }
49 })
50 export const loadMenus = (next, to) => {
51 buildMenus().then(res => {
52 const sdata = JSON.parse(JSON.stringify(res))
53 const rdata = JSON.parse(JSON.stringify(res))
54 const sidebarRoutes = filterAsyncRouter(sdata)
55 const rewriteRoutes = filterAsyncRouter(rdata, true)
56 rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
57 store.dispatch('GenerateRoutes', rewriteRoutes).then(() => { // 存储路由
58 router.addRoutes(rewriteRoutes) // 动态添加可访问路由表
59 next({ ...to, replace: true })
60 })
61 store.dispatch('SetSidebarRouters', sidebarRoutes)
62 })
63 }
64 router.afterEach(() => {
65 NProgress.done() // finish progress bar
66 })
1 import Vue from 'vue'
2 import Router from 'vue-router'
3 import Layout from '../layout/index'
4 Vue.use(Router)
5 export const constantRouterMap = [
6 { path: '/login',
7 meta: { title: '登录', noCache: true },
8 component: (resolve) => require(['@/views/login'], resolve),
9 hidden: true
10 },
11 {
12 path: '/404',
13 component: (resolve) => require(['@/views/features/404'], resolve),
14 hidden: true
15 },
16 {
17 path: '/401',
18 component: (resolve) => require(['@/views/features/401'], resolve),
19 hidden: true
20 },
21 {
22 path: '/redirect',
23 component: Layout,
24 hidden: true,
25 children: [
26 {
27 path: '/redirect/:path*',
28 component: (resolve) => require(['@/views/features/redirect'], resolve)
29 }
30 ]
31 },
32 {
33 path: '/',
34 component: Layout,
35 redirect: '/dashboard',
36 children: [
37 {
38 path: 'dashboard',
39 component: (resolve) => require(['@/views/home'], resolve),
40 name: 'Dashboard',
41 meta: { title: '首页', icon: 'index', affix: true, noCache: true }
42 }
43 ]
44 },
45 {
46 path: '/user',
47 component: Layout,
48 hidden: true,
49 redirect: 'noredirect',
50 children: [
51 {
52 path: 'center',
53 component: (resolve) => require(['@/views/system/user/center'], resolve),
54 name: '个人中心',
55 meta: { title: '个人中心' }
56 }
57 ]
58 }
59 ]
60 export default new Router({
61 // mode: 'hash',
62 mode: 'history',
63 scrollBehavior: () => ({ y: 0 }),
64 routes: constantRouterMap
65 })
1 module.exports = {
2 /**
3 * @description 网站标题
4 */
5 title: '基础框架',
6 /**
7 * @description 是否显示 tagsView
8 */
9 tagsView: true,
10 /**
11 * @description 固定头部
12 */
13 fixedHeader: true,
14 /**
15 * @description 记住密码状态下的token在Cookie中存储的天数,默认1天
16 */
17 tokenCookieExpires: 1,
18 /**
19 * @description 记住密码状态下的密码在Cookie中存储的天数,默认1天s
20 */
21 passCookieExpires: 1,
22 /**
23 * @description 是否只保持一个子菜单的展开
24 */
25 uniqueOpened: true,
26 /**
27 * @description token key
28 */
29 TokenKey: 'EL-USER-PORTRAIT',
30 /**
31 * @description 请求超时时间,毫秒(默认2分钟)
32 */
33 timeout: 1200000,
34 /**
35 * @description 是否显示logo
36 */
37 sidebarLogo: true,
38 /**
39 * 是否显示设置的底部信息
40 */
41 showFooter: false,
42 /**
43 * 底部文字,支持html语法
44 */
45 footerTxt: '',
46 /**
47 * 备案号
48 */
49 caseNumber: '浙ICP备18005431号'
50 }
1 const getters = {
2 deployUploadApi: state => state.api.deployUploadApi,
3 databaseUploadApi: state => state.api.databaseUploadApi,
4 size: state => state.app.size,
5 sidebar: state => state.app.sidebar,
6 device: state => state.app.device,
7 token: state => state.user.token,
8 visitedViews: state => state.tagsView.visitedViews,
9 cachedViews: state => state.tagsView.cachedViews,
10 roles: state => state.user.roles,
11 user: state => state.user.user,
12 loadMenus: state => state.user.loadMenus,
13 permission_routers: state => state.permission.routers,
14 addRouters: state => state.permission.addRouters,
15 socketApi: state => state.api.socketApi,
16 imagesUploadApi: state => state.api.imagesUploadApi,
17 baseApi: state => state.api.baseApi,
18 imageApi: state => state.api.imageApi,
19 fileUploadApi: state => state.api.fileUploadApi,
20 updateAvatarApi: state => state.api.updateAvatarApi,
21 qiNiuUploadApi: state => state.api.qiNiuUploadApi,
22 sqlApi: state => state.api.sqlApi,
23 swaggerApi: state => state.api.swaggerApi,
24 sidebarRouters: state => state.permission.sidebarRouters
25 }
26 export default getters
1 import Vue from 'vue'
2 import Vuex from 'vuex'
3 import getters from './getters'
4
5 Vue.use(Vuex)
6
7 // https://webpack.js.org/guides/dependency-management/#requirecontext
8 const modulesFiles = require.context('./modules', true, /\.js$/)
9
10 // you do not need `import app from './modules/app'`
11 // it will auto require all vuex module from modules file
12 const modules = modulesFiles.keys().reduce((modules, modulePath) => {
13 // set './app.js' => 'app'
14 const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
15 const value = modulesFiles(modulePath)
16 modules[moduleName] = value.default
17 return modules
18 }, {})
19
20 const store = new Vuex.Store({
21 modules,
22 getters
23 })
24
25 export default store
1 // 适配 Nginx 反向代理
2 const baseUrl = process.env.VUE_APP_BASE_API === '/' ? '' : process.env.VUE_APP_BASE_API
3 const api = {
4 state: {
5 // 部署包上传
6 deployUploadApi: baseUrl + '/api/deploy/upload',
7 // SQL脚本上传
8 databaseUploadApi: baseUrl + '/api/database/upload',
9 // 实时控制台
10 socketApi: baseUrl + '/websocket?token=kl',
11 // 图片上传
12 imagesUploadApi: baseUrl + '/api/localStorage/pictures',
13 // 修改头像
14 updateAvatarApi: baseUrl + '/api/users/updateAvatar',
15 // 上传文件到七牛云
16 qiNiuUploadApi: baseUrl + '/api/qiNiuContent',
17 // Sql 监控
18 sqlApi: baseUrl + '/druid/index.html',
19 // swagger
20 swaggerApi: baseUrl + '/swagger-ui.html',
21 // 文件上传
22 fileUploadApi: baseUrl + '/api/localStorage',
23 // baseUrl,
24 baseApi: baseUrl,
25 imageApi: baseUrl + '/api/oss/uploadMapping',
26 imageUpload: baseUrl + '/api/imageUpload'
27 }
28 }
29
30 export default api
1 import Cookies from 'js-cookie'
2
3 const state = {
4 sidebar: {
5 opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
6 withoutAnimation: false
7 },
8 device: 'desktop',
9 size: Cookies.get('size') || 'small'
10 }
11
12 const mutations = {
13 TOGGLE_SIDEBAR: state => {
14 state.sidebar.opened = !state.sidebar.opened
15 state.sidebar.withoutAnimation = false
16 if (state.sidebar.opened) {
17 Cookies.set('sidebarStatus', 1)
18 } else {
19 Cookies.set('sidebarStatus', 0)
20 }
21 },
22 CLOSE_SIDEBAR: (state, withoutAnimation) => {
23 Cookies.set('sidebarStatus', 0)
24 state.sidebar.opened = false
25 state.sidebar.withoutAnimation = withoutAnimation
26 },
27 TOGGLE_DEVICE: (state, device) => {
28 state.device = device
29 },
30 SET_SIZE: (state, size) => {
31 state.size = size
32 Cookies.set('size', size)
33 }
34 }
35
36 const actions = {
37 toggleSideBar({ commit }) {
38 commit('TOGGLE_SIDEBAR')
39 },
40 closeSideBar({ commit }, { withoutAnimation }) {
41 commit('CLOSE_SIDEBAR', withoutAnimation)
42 },
43 toggleDevice({ commit }, device) {
44 commit('TOGGLE_DEVICE', device)
45 },
46 setSize({ commit }, size) {
47 commit('SET_SIZE', size)
48 }
49 }
50
51 export default {
52 namespaced: true,
53 state,
54 mutations,
55 actions
56 }
1 import { constantRouterMap } from '@/router/routers'
2 import Layout from '@/layout/index'
3 import ParentView from '@/components/ParentView'
4
5 const permission = {
6 state: {
7 routers: constantRouterMap,
8 addRouters: [],
9 sidebarRouters: []
10 },
11 mutations: {
12 SET_ROUTERS: (state, routers) => {
13 state.addRouters = routers
14 state.routers = constantRouterMap.concat(routers)
15 },
16 SET_SIDEBAR_ROUTERS: (state, routers) => {
17 state.sidebarRouters = constantRouterMap.concat(routers)
18 }
19 },
20 actions: {
21 GenerateRoutes({ commit }, asyncRouter) {
22 commit('SET_ROUTERS', asyncRouter)
23 },
24 SetSidebarRouters({ commit }, sidebarRouter) {
25 commit('SET_SIDEBAR_ROUTERS', sidebarRouter)
26 }
27 }
28 }
29
30 export const filterAsyncRouter = (routers, isRewrite = false) => { // 遍历后台传来的路由字符串,转换为组件对象
31 return routers.filter(router => {
32 if (isRewrite && router.children) {
33 router.children = filterChildren(router.children)
34 }
35 if (router.component) {
36 if (router.component === 'Layout') { // Layout组件特殊处理
37 router.component = Layout
38 } else if (router.component === 'ParentView') {
39 router.component = ParentView
40 } else {
41 const component = router.component
42 router.component = loadView(component)
43 }
44 }
45 if (router.children && router.children.length) {
46 router.children = filterAsyncRouter(router.children, router, isRewrite)
47 }
48 return true
49 })
50 }
51
52 function filterChildren(childrenMap) {
53 var children = []
54 childrenMap.forEach((el, index) => {
55 if (el.children && el.children.length) {
56 if (el.component === 'ParentView') {
57 el.children.forEach(c => {
58 c.path = el.path + '/' + c.path
59 if (c.children && c.children.length) {
60 children = children.concat(filterChildren(c.children, c))
61 return
62 }
63 children.push(c)
64 })
65 return
66 }
67 }
68 children = children.concat(el)
69 })
70 return children
71 }
72
73 export const loadView = (view) => {
74 return (resolve) => require([`@/views/${view}`], resolve)
75 }
76
77 export default permission
1 import variables from '@/assets/styles/element-variables.scss'
2 import defaultSettings from '@/settings'
3 const { tagsView, fixedHeader, sidebarLogo, uniqueOpened, showFooter, footerTxt, caseNumber } = defaultSettings
4
5 const state = {
6 theme: variables.theme,
7 showSettings: false,
8 tagsView: tagsView,
9 fixedHeader: fixedHeader,
10 sidebarLogo: sidebarLogo,
11 uniqueOpened: uniqueOpened,
12 showFooter: showFooter,
13 footerTxt: footerTxt,
14 caseNumber: caseNumber
15 }
16
17 const mutations = {
18 CHANGE_SETTING: (state, { key, value }) => {
19 if (state.hasOwnProperty(key)) {
20 state[key] = value
21 }
22 }
23 }
24
25 const actions = {
26 changeSetting({ commit }, data) {
27 commit('CHANGE_SETTING', data)
28 }
29 }
30
31 export default {
32 namespaced: true,
33 state,
34 mutations,
35 actions
36 }
37
1 const state = {
2 visitedViews: [],
3 cachedViews: []
4 }
5
6 const mutations = {
7 ADD_VISITED_VIEW: (state, view) => {
8 if (state.visitedViews.some(v => v.path === view.path)) return
9 state.visitedViews.push(
10 Object.assign({}, view, {
11 title: view.meta.title || 'no-name'
12 })
13 )
14 },
15 ADD_CACHED_VIEW: (state, view) => {
16 if (state.cachedViews.includes(view.name)) return
17 if (!view.meta.noCache) {
18 state.cachedViews.push(view.name)
19 }
20 },
21
22 DEL_VISITED_VIEW: (state, view) => {
23 for (const [i, v] of state.visitedViews.entries()) {
24 if (v.path === view.path) {
25 state.visitedViews.splice(i, 1)
26 break
27 }
28 }
29 },
30 DEL_CACHED_VIEW: (state, view) => {
31 for (const i of state.cachedViews) {
32 if (i === view.name) {
33 const index = state.cachedViews.indexOf(i)
34 state.cachedViews.splice(index, 1)
35 break
36 }
37 }
38 },
39
40 DEL_OTHERS_VISITED_VIEWS: (state, view) => {
41 state.visitedViews = state.visitedViews.filter(v => {
42 return v.meta.affix || v.path === view.path
43 })
44 },
45 DEL_OTHERS_CACHED_VIEWS: (state, view) => {
46 for (const i of state.cachedViews) {
47 if (i === view.name) {
48 const index = state.cachedViews.indexOf(i)
49 state.cachedViews = state.cachedViews.slice(index, index + 1)
50 break
51 }
52 }
53 },
54
55 DEL_ALL_VISITED_VIEWS: state => {
56 // keep affix tags
57 const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
58 state.visitedViews = affixTags
59 },
60 DEL_ALL_CACHED_VIEWS: state => {
61 state.cachedViews = []
62 },
63
64 UPDATE_VISITED_VIEW: (state, view) => {
65 for (let v of state.visitedViews) {
66 if (v.path === view.path) {
67 v = Object.assign(v, view)
68 break
69 }
70 }
71 }
72 }
73
74 const actions = {
75 addView({ dispatch }, view) {
76 dispatch('addVisitedView', view)
77 dispatch('addCachedView', view)
78 },
79 addVisitedView({ commit }, view) {
80 commit('ADD_VISITED_VIEW', view)
81 },
82 addCachedView({ commit }, view) {
83 commit('ADD_CACHED_VIEW', view)
84 },
85
86 delView({ dispatch, state }, view) {
87 return new Promise(resolve => {
88 dispatch('delVisitedView', view)
89 dispatch('delCachedView', view)
90 resolve({
91 visitedViews: [...state.visitedViews],
92 cachedViews: [...state.cachedViews]
93 })
94 })
95 },
96 delVisitedView({ commit, state }, view) {
97 return new Promise(resolve => {
98 commit('DEL_VISITED_VIEW', view)
99 resolve([...state.visitedViews])
100 })
101 },
102 delCachedView({ commit, state }, view) {
103 return new Promise(resolve => {
104 commit('DEL_CACHED_VIEW', view)
105 resolve([...state.cachedViews])
106 })
107 },
108
109 delOthersViews({ dispatch, state }, view) {
110 return new Promise(resolve => {
111 dispatch('delOthersVisitedViews', view)
112 dispatch('delOthersCachedViews', view)
113 resolve({
114 visitedViews: [...state.visitedViews],
115 cachedViews: [...state.cachedViews]
116 })
117 })
118 },
119 delOthersVisitedViews({ commit, state }, view) {
120 return new Promise(resolve => {
121 commit('DEL_OTHERS_VISITED_VIEWS', view)
122 resolve([...state.visitedViews])
123 })
124 },
125 delOthersCachedViews({ commit, state }, view) {
126 return new Promise(resolve => {
127 commit('DEL_OTHERS_CACHED_VIEWS', view)
128 resolve([...state.cachedViews])
129 })
130 },
131
132 delAllViews({ dispatch, state }, view) {
133 return new Promise(resolve => {
134 dispatch('delAllVisitedViews', view)
135 dispatch('delAllCachedViews', view)
136 resolve({
137 visitedViews: [...state.visitedViews],
138 cachedViews: [...state.cachedViews]
139 })
140 })
141 },
142 delAllVisitedViews({ commit, state }) {
143 return new Promise(resolve => {
144 commit('DEL_ALL_VISITED_VIEWS')
145 resolve([...state.visitedViews])
146 })
147 },
148 delAllCachedViews({ commit, state }) {
149 return new Promise(resolve => {
150 commit('DEL_ALL_CACHED_VIEWS')
151 resolve([...state.cachedViews])
152 })
153 },
154
155 updateVisitedView({ commit }, view) {
156 commit('UPDATE_VISITED_VIEW', view)
157 }
158 }
159
160 export default {
161 namespaced: true,
162 state,
163 mutations,
164 actions
165 }
1 import { login, getInfo, logout } from '@/api/login'
2 import { getToken, setToken, removeToken } from '@/utils/auth'
3
4 const user = {
5 state: {
6 token: getToken(),
7 user: {},
8 roles: [],
9 // 第一次加载菜单时用到
10 loadMenus: false
11 },
12
13 mutations: {
14 SET_TOKEN: (state, token) => {
15 state.token = token
16 },
17 SET_USER: (state, user) => {
18 state.user = user
19 },
20 SET_ROLES: (state, roles) => {
21 state.roles = roles
22 },
23 SET_LOAD_MENUS: (state, loadMenus) => {
24 state.loadMenus = loadMenus
25 }
26 },
27
28 actions: {
29 // 登录
30 Login({ commit }, userInfo) {
31 const username = userInfo.username
32 const password = userInfo.password
33 const code = userInfo.code
34 const uuid = userInfo.uuid
35 const rememberMe = userInfo.rememberMe
36 return new Promise((resolve, reject) => {
37 login(username, password, code, uuid).then(res => {
38 setToken(res.token, rememberMe)
39 commit('SET_TOKEN', res.token)
40 setUserInfo(res.user, commit)
41 // 第一次加载菜单时用到, 具体见 src 目录下的 permission.js
42 commit('SET_LOAD_MENUS', true)
43 resolve()
44 }).catch(error => {
45 reject(error)
46 })
47 })
48 },
49
50 // 获取用户信息
51 GetInfo({ commit }) {
52 return new Promise((resolve, reject) => {
53 getInfo().then(res => {
54 setUserInfo(res, commit)
55 resolve(res)
56 }).catch(error => {
57 reject(error)
58 })
59 })
60 },
61 // 登出
62 LogOut({ commit }) {
63 return new Promise((resolve, reject) => {
64 logout().then(res => {
65 logOut(commit)
66 resolve()
67 }).catch(error => {
68 logOut(commit)
69 reject(error)
70 })
71 })
72 },
73
74 updateLoadMenus({ commit }) {
75 return new Promise((resolve, reject) => {
76 commit('SET_LOAD_MENUS', false)
77 })
78 }
79 }
80 }
81
82 export const logOut = (commit) => {
83 commit('SET_TOKEN', '')
84 commit('SET_ROLES', [])
85 removeToken()
86 }
87
88 export const setUserInfo = (res, commit) => {
89 // 如果没有任何权限,则赋予一个默认的权限,避免请求死循环
90 if (res.roles.length === 0) {
91 commit('SET_ROLES', ['ROLE_SYSTEM_DEFAULT'])
92 } else {
93 commit('SET_ROLES', res.roles)
94 }
95 commit('SET_USER', res)
96 }
97
98 export default user
1 import Cookies from 'js-cookie'
2 import Config from '@/settings'
3
4 const TokenKey = Config.TokenKey
5
6 export function getToken() {
7 return Cookies.get(TokenKey)
8 }
9
10 export function setToken(token, rememberMe) {
11 if (rememberMe) {
12 return Cookies.set(TokenKey, token, { expires: Config.tokenCookieExpires })
13 } else return Cookies.set(TokenKey, token)
14 }
15
16 export function removeToken() {
17 return Cookies.remove(TokenKey)
18 }
1 import Vue from 'vue'
2 import Clipboard from 'clipboard'
3
4 function clipboardSuccess() {
5 Vue.prototype.$message({
6 message: 'Copy successfully',
7 type: 'success',
8 duration: 1500
9 })
10 }
11
12 function clipboardError() {
13 Vue.prototype.$message({
14 message: 'Copy failed',
15 type: 'error'
16 })
17 }
18
19 export default function handleClipboard(text, event) {
20 const clipboard = new Clipboard(event.target, {
21 text: () => text
22 })
23 clipboard.on('success', () => {
24 clipboardSuccess()
25 clipboard.off('error')
26 clipboard.off('success')
27 clipboard.destroy()
28 })
29 clipboard.on('error', () => {
30 clipboardError()
31 clipboard.off('error')
32 clipboard.off('success')
33 clipboard.destroy()
34 })
35 clipboard.onClick(event)
36 }
1 /**
2 * @param {array} arr
3 * @param {string} key
4 * @returns {string}
5 */
6 export function getDisplayNameByKey(arr, key) {
7 const obj = arr.reduce((acc, cur) => {
8 acc[cur.key] = cur.display_name
9 return acc
10 }, {})
11 return obj[key]
12 }
13
14 export function dictFilter(value, dict) {
15 if (!dict) {
16 return ''
17 }
18 const filter = dict.reduce((pre, cur) => {
19 // 此处有几处需要注意 pre[cur.value] 这里很神奇,因为按键取值无所谓是string或者number
20 // 构造一个字典{value,label}的新对象
21 pre[cur.value] = cur['label']
22 return pre
23 }, {})
24 return filter[value]
25 }
26
27 export function getAttrByValueFromDict(arrDict, value, attrName) {
28 const obj = arrDict.reduce((acc, cur) => {
29 acc[cur.value] = cur[attrName]
30 return acc
31 }, {})
32 return obj[value]
33 }
34
35 export function getAttrByValueFromDicts(arrDict, value, attrName) {
36 const obj = arrDict.reduce((acc, cur) => {
37 acc[cur.value] = cur[attrName]
38 return acc
39 }, {})
40 return obj[value] ? obj[value] : '无'
41 }
42
43 export function tagContentTypeStatusConvert(status) {
44 const statusMap = {
45 0: 'danger',
46 1: 'success',
47 5: 'warning',
48 7: 'info'
49 }
50 return statusMap[status] === undefined ? 'info' : statusMap[status]
51 }
52
53 // c2注入完成状态
54 export function c2TaskTagStatusConvert(status) {
55 const statusMap = {
56 '0': 'info',
57 '10': 'primary',
58 '20': 'warning',
59 '100': 'success',
60 '-10': 'danger',
61 '-1': 'danger'
62 }
63 return statusMap[status] === undefined ? 'info' : statusMap[status]
64 }
65
66 // c2发送工单状态
67 export function c2SendTagStatusConvert(status) {
68 const statusMap = {
69 '0': 'info',
70 '10': 'primary',
71 '100': 'success',
72 '-1': 'warning',
73 '-100': 'danger'
74 }
75 return statusMap[status] === undefined ? 'info' : statusMap[status]
76 }
77
78 // c2发送工单状态
79 export function c2RecieveTagStatusConvert(status) {
80 const statusMap = {
81 '0': 'info',
82 '100': 'primary',
83 '200': 'success',
84 '101': 'warning',
85 '-5': 'danger',
86 '-10': 'danger',
87 '-1': 'danger',
88 '-200': 'danger',
89 '-101': 'danger'
90 }
91 return statusMap[status] === undefined ? 'info' : statusMap[status]
92 }
93
94 export function getNameById(arr, value) {
95 const obj = arr.reduce((acc, cur) => {
96 acc[cur.id] = cur.name
97 return acc
98 }, {})
99 return obj[value]
100 }
101
102 export function getNameByValue(arr, value, label) {
103 const obj = arr.reduce((acc, cur) => {
104 acc[cur.id] = cur[label]
105 return acc
106 }, {})
107 return obj[value]
108 }
109
110 export function getOrderType(status) {
111 const statusMap = {
112 '0': 'warning',
113 '1': 'success',
114 '2': 'info',
115 '3': 'danger'
116 }
117 return statusMap[status] === undefined ? 'info' : statusMap[status]
118 }
119
120 export function initDate(dates) {
121 const date = new Date(dates)
122 var y = date.getFullYear()
123 var m = date.getMonth() + 1
124 m = m < 10 ? '0' + m : m
125 var d = date.getDate()
126 d = d < 10 ? ('0' + d) : d
127 return y + '-' + m + '-' + d
128 }
129
130 export function getUserNameById(arr, value) {
131 const obj = arr.reduce((acc, cur) => {
132 acc[cur.id] = cur.username
133 return acc
134 }, {})
135 return obj[value]
136 }
137
138 export function formatDate(value) {
139 const date = new Date(value)
140 const y = date.getFullYear()
141 let MM = date.getMonth() + 1
142 MM = MM < 10 ? ('0' + MM) : MM
143 let d = date.getDate()
144 d = d < 10 ? ('0' + d) : d
145 let h = date.getHours()
146 h = h < 10 ? ('0' + h) : h
147 let m = date.getMinutes()
148 m = m < 10 ? ('0' + m) : m
149 let s = date.getSeconds()
150 s = s < 10 ? ('0' + s) : s
151 return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s
152 }
153
154 export function deepCopy(obj) {
155 if (!obj) return
156 var newObj
157 try {
158 newObj = obj.constructor && obj.constructor === Array ? [] : {}
159 newObj.constructor = obj.constructor
160 } catch (e) {
161 newObj = []
162 }
163 if (typeof obj !== 'object') {
164 return
165 } else {
166 for (var prop in obj) {
167 if (!obj[prop]) {
168 newObj[prop] = obj[prop]
169 continue
170 }
171 if (obj[prop].constructor === RegExp || obj[prop].constructor === Date) {
172 newObj[prop] = obj[prop]
173 } else if (typeof obj[prop] === 'object') {
174 // 递归
175 newObj[prop] = deepCopy(obj[prop])
176 } else {
177 newObj[prop] = obj[prop]
178 }
179 }
180 }
181 return newObj
182 }
183
184 export function assign(target, firstSource) {
185 if (target === undefined || target === null) {
186 throw new TypeError('Cannot convert first argument to object')
187 }
188 const to = Object(target)
189 for (let i = 1; i < arguments.length; i++) {
190 const nextSource = arguments[i]
191 if (nextSource === undefined || nextSource === null) continue
192 const keysArray = Object.keys(Object(nextSource))
193 for (let nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
194 const nextKey = keysArray[nextIndex]
195 const desc = Object.getOwnPropertyDescriptor(nextSource, nextKey)
196 if (desc !== undefined && desc.enumerable) {
197 if (to[nextKey] !== undefined) {
198 to[nextKey] = nextSource[nextKey]
199 }
200 }
201 }
202 }
203 return to
204 }
205
206 export function equalsIfNotNull(obj, str) {
207 if (obj === undefined || obj === null) {
208 return false
209 } else {
210 return obj.toString() === str
211 }
212 }
213
214 export function tagStatusConvert(status) {
215 const statusMap = {
216 0: 'danger',
217 1: 'success'
218 }
219 return statusMap[status] === undefined ? 'info' : statusMap[status]
220 }
221
222 export function auditStatusConvert(status) {
223 const statusMap = {
224 0: '',
225 5: 'primary',
226 '-10': 'danger',
227 10: 'info',
228 100: 'success',
229 105: 'primary',
230 '-110': 'danger',
231 110: 'info',
232 200: 'success'
233 }
234 return statusMap[status] === undefined ? '' : statusMap[status]
235 }
236
237 export function migrationStatus(status) {
238 const statusMap = {
239 '0': 'info',
240 '10': 'primary',
241 '20': 'primary',
242 '100': 'success',
243 '-100': 'danger',
244 '-10': 'danger',
245 '-20': 'danger'
246 }
247 return statusMap[status] === undefined ? '' : statusMap[status]
248 }
249
250 export function auditStatusConverts(status) {
251 if (!status) return 'primary'
252 const statusMap = {
253 'act_1': '',
254 'act_not_audited': '',
255 'act_cdn_ready': 'primary',
256 'act_1st_audit_lock': 'primary',
257 'act_1st_audit_failed': 'danger',
258 'act_before_1st_audit': 'info',
259 'act_1st_audit_passed': 'success',
260 'act_2nd_audit_lock': 'primary',
261 'act_2nd_audit_failed': 'danger',
262 'act_wait_4_2nd_audit': 'info',
263 'act_2nd_audit_passed': 'success',
264 'act_in_storage': 'success'
265 }
266 return statusMap[status] === undefined ? '' : statusMap[status]
267 }
268
269 export function c2StatusConvert(status) {
270 const statusMap = {
271 0: 'warning',
272 1: 'primary',
273 50: 'primary',
274 100: 'success',
275 '-100': 'danger'
276 }
277 return statusMap[status] === undefined ? 'info' : statusMap[status]
278 }
279
280 export function sendC2StatusConvert(status) {
281 const statusMap = {
282 0: 'warning',
283 1: 'primary',
284 50: 'primary',
285 100: 'success',
286 '-50': 'warning',
287 '-100': 'danger'
288 }
289 return statusMap[status] === undefined ? 'info' : statusMap[status]
290 }
291
292 export function receiveC2StatusConvert(status) {
293 const statusMap = {
294 0: 'info',
295 '-5': 'danger',
296 '-10': 'danger',
297 '-50': 'warning',
298 100: 'primary',
299 101: 'warning',
300 200: 'success',
301 '-101': 'danger',
302 '-200': 'warning'
303 }
304 return statusMap[status] === undefined ? 'info' : statusMap[status]
305 }
306
307 export function trascodeStatusConvert(status) {
308 const statusMap = {
309 '-10': 'danger',
310 0: 'warning',
311 1: 'primary',
312 100: 'success',
313 '-100': 'danger'
314 }
315 return statusMap[status] === undefined ? 'info' : statusMap[status]
316 }
317
318 export function collectConvert(status) {
319 const statusMap = {
320 '-1': 'danger',
321 '1': 'success'
322 }
323 return statusMap[status] === undefined ? 'info' : statusMap[status]
324 }
325
326 export function c2PriorityConvert(priority) {
327 const priorityMap = {
328 0: 'info',
329 5: 'warning',
330 10: 'danger'
331 }
332 return priorityMap[priority] === undefined ? 'info' : priorityMap[priority]
333 }
334
335 export function c2Priority(priority) {
336 const priorityMap = {
337 0: 'success',
338 1: 'success',
339 2: 'success',
340 3: 'primary',
341 4: 'primary',
342 5: 'primary',
343 6: 'warning',
344 7: 'warning',
345 8: 'warning',
346 9: 'danger'
347 }
348 return priorityMap[priority] === undefined ? 'info' : priorityMap[priority]
349 }
350
351 export function initInjectStatus(priority) {
352 const priorityMap = {
353 '0': 'info',
354 '-100': 'warning',
355 '10': 'primary',
356 '-110': 'danger',
357 '100': 'success'
358 }
359 return priorityMap[priority] === undefined ? 'info' : priorityMap[priority]
360 }
361
362 export function initStatus(priority) {
363 const priorityMap = {
364 '-10': 'info',
365 '-100': 'danger',
366 '0': 'warning',
367 '1': 'success',
368 '5': 'primary'
369 }
370 return priorityMap[priority] === undefined ? 'info' : priorityMap[priority]
371 }
372
373 export function initPackageStatus(priority) {
374 const priorityMap = {
375 '0': 'primary',
376 '1': 'success',
377 '2': 'warning'
378 }
379 return priorityMap[priority] === undefined ? 'info' : priorityMap[priority]
380 }
381
382 export function notifyWarning(component, msg) {
383 component.$notify({
384 title: msg,
385 type: 'warning',
386 duration: 2500
387 })
388 }
389
390 export function convertImageArr2Json(image, imageTypeArr) {
391 image = evil(image)
392 const retJson = {}
393 retJson.map = {}
394 retJson.list = []
395 image.forEach(img => {
396 const type = getAttrByValueFromDict(imageTypeArr, img.type, 'label')
397 if (type !== null) {
398 if (retJson.map.hasOwnProperty(type)) {
399 retJson.map[type].push(image.indexOf(img))
400 } else {
401 retJson.map[type] = [image.indexOf(img)]
402 }
403 retJson.list.push(img)
404 }
405 })
406 return JSON.stringify(retJson)
407 }
408
409 // 计算表达式的值
410 export function evil(fn) {
411 const Fn = Function // 一个变量指向Function,防止有些前端编译工具报错
412 return new Fn('return ' + fn)()
413 }
414
415 export function getStringFilter(filter) {
416 if (!filter || filter === null || filter.length === 0) return
417 var res = ''
418 for (var key in filter) {
419 if (key === 'constructor') continue
420 if (filter[key] === '') continue
421 res += key + '=' + filter[key] + '&'
422 }
423 return res
424 }
425
426 export function generateUUID() {
427 let d = new Date().getTime()
428 const uuid = 'xxxxxxx4xxxyxxxx'.replace(/[xy]/g, function(c) {
429 const r = (d + Math.random() * 16) % 16 | 0
430 d = Math.floor(d / 16)
431 return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16)
432 })
433 return uuid
434 }
435
1 /* eslint-disable */
2
3 /**
4 * Date对象的补充函数,包括类似Python中的strftime()
5 * 阿债 https://gitee.com/azhai/datetime.js
6 */
7
8 Date.prototype.toMidnight = function() {
9 this.setHours(0)
10 this.setMinutes(0)
11 this.setSeconds(0)
12 this.setMilliseconds(0)
13 return this
14 }
15
16 Date.prototype.daysAgo = function(days, midnight) {
17 days = days ? days - 0 : 0
18 const date = new Date(this.getTime() - days * 8.64E7)
19 return midnight ? date.toMidnight() : date
20 }
21
22 Date.prototype.monthBegin = function(offset) {
23 offset = offset ? offset - 0 : 0
24 const days = this.getDate() - 1 - offset
25 return this.daysAgo(days, true)
26 }
27
28 Date.prototype.quarterBegin = function() {
29 const month = this.getMonth() - this.getMonth() % 3
30 return new Date(this.getFullYear(), month, 1).toMidnight()
31 }
32
33 Date.prototype.yearBegin = function() {
34 return new Date(this.getFullYear(), 0, 1).toMidnight()
35 }
36
37 Date.prototype.strftime = function(format, local) {
38 if (!format) {
39 const str = new Date(this.getTime() + 2.88E7).toISOString()
40 return str.substr(0, 16).replace('T', ' ')
41 }
42 local = local && local.startsWith('zh') ? 'zh' : 'en'
43 const padZero = function(str, len) {
44 const pads = len - str.toString().length
45 return (pads && pads > 0 ? '0'.repeat(pads) : '') + str
46 }
47 format = format.replace('%F', '%Y-%m-%d')
48 format = format.replace(/%D|%x/, '%m/%d/%y')
49 format = format.replace(/%T|%X/, '%H:%M:%S')
50 format = format.replace('%R', '%H:%M')
51 format = format.replace('%r', '%H:%M:%S %p')
52 format = format.replace('%c', '%a %b %e %H:%M:%S %Y')
53 const _this = this
54 return format.replace(/%[A-Za-z%]/g, function(f) {
55 let ans = f
56 switch (f) {
57 case '%%':
58 ans = '%'
59 break
60
61 case '%Y':
62 case '%G':
63 ans = _this.getFullYear()
64 break
65
66 case '%y':
67 ans = _this.getFullYear() % 100
68 break
69
70 case '%C':
71 ans = _this.getFullYear() / 100
72 break
73
74 case '%m':
75 case '%n':
76 ans = _this.getMonth() + 1
77 break
78
79 case '%B':
80 local = local.startsWith('en') ? 'english' : local
81
82 case '%b':
83 const m = _this.getMonth()
84 ans = local_labels.monthes[local][m]
85 break
86
87 case '%d':
88 case '%e':
89 ans = _this.getDate()
90 break
91
92 case '%j':
93 ans = _this.getDaysOfYear()
94 break
95
96 case '%U':
97 case '%W':
98 const ws = _this.getWeeksOfYear(f === '%W')
99 ans = padZero(ws, 2)
100 break
101
102 case '%w':
103 ans = _this.getDay()
104
105 case '%u':
106 ans = ans === 0 ? 7 : ans
107 break
108
109 case '%A':
110 local = local.startsWith('en') ? 'english' : local
111
112 case '%a':
113 const d = _this.getDay()
114 ans = local_labels.weekdays[local][d]
115 break
116
117 case '%H':
118 case '%k':
119 ans = _this.getHours()
120 break
121
122 case '%I':
123 case '%l':
124 ans = _this.getHours()
125 ans = ans % 12
126 break
127
128 case '%M':
129 ans = _this.getMinutes()
130 break
131
132 case '%S':
133 ans = _this.getSeconds()
134 break
135
136 case '%s':
137 ans = parseInt(_this.getTime() / 1E3)
138 break
139
140 case '%f':
141 const ms = _this.getMilliseconds()
142 ans = padZero(ms * 1E3, 6)
143 break
144
145 case '%P':
146 local = local.startsWith('en') ? 'english' : local
147
148 case '%p':
149 const h = _this.getHours()
150 ans = local_labels.meridians[local][h < 12 ? 0 : 1]
151 break
152
153 case '%z':
154 let tzo = _this.getTimezoneOffset()
155 const sign = tzo < 0 ? '-' : '+'
156 tzo = Math.abs(tzo)
157 const ho = padZero(tzo / 60, 2)
158 const mo = padZero(tzo % 60, 2)
159 ans = sign + ho + mo
160 break
161
162 default:
163 break
164 }
165 if (f === '%C' || f === '%y' || f === '%m' || f === '%d' || f === '%H' || f === '%M' || f === '%S') {
166 ans = padZero(ans, 2)
167 }
168 return ans.toString()
169 })
170 }
171
172 Date.prototype.humanize = function(local) {
173 local = local && local.startsWith('zh') ? 'zh' : 'en'
174 const result = this.strftime('', local)
175 const days = (Date.today() - this.toMidnight().getTime()) / 8.64E7
176 if (days <= -10 || days >= 10) {
177 return result
178 }
179 const labels = local_labels.dayagos[local]
180 let lbl = ''
181 if (days === 0 || days === 1) {
182 lbl = labels[days]
183 } else if (days === -1) {
184 lbl = labels[2]
185 } else if (days >= 2) {
186 lbl = days + labels[3]
187 } else {
188 lbl = days + labels[4]
189 }
190 return lbl + result.substr(10, 6)
191 }
192
193 const local_labels = {
194 monthes: {
195 english: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
196 en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
197 zh: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
198 },
199 weekdays: {
200 english: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
201 en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
202 zh: ['日', '一', '二', '三', '四', '五', '六']
203 },
204 meridians: {
205 english: ['a.m.', 'p.m.'],
206 en: ['AM', 'PM'],
207 zh: ['上午', '下午']
208 },
209 dayagos: {
210 english: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'],
211 en: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'],
212 zh: ['今天', '昨天', '明天', '天前', '天后']
213 }
214 }
215
216 export default Date
1 /**
2 * Created by PanJiaChen on 16/11/18.
3 */
4
5 /**
6 * Parse the time to string
7 * @param {(Object|string|number)} time
8 * @param {string} cFormat
9 * @returns {string}
10 */
11 export function parseTime(time, cFormat) {
12 if (arguments.length === 0) {
13 return null
14 }
15 const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
16 let date
17 if (typeof time === 'undefined' || time === null || time === 'null') {
18 return ''
19 } else if (typeof time === 'object') {
20 date = time
21 } else {
22 if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
23 time = parseInt(time)
24 }
25 if ((typeof time === 'number') && (time.toString().length === 10)) {
26 time = time * 1000
27 }
28 date = new Date(time)
29 }
30 const formatObj = {
31 y: date.getFullYear(),
32 m: date.getMonth() + 1,
33 d: date.getDate(),
34 h: date.getHours(),
35 i: date.getMinutes(),
36 s: date.getSeconds(),
37 a: date.getDay()
38 }
39 const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
40 let value = formatObj[key]
41 // Note: getDay() returns 0 on Sunday
42 if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
43 if (result.length > 0 && value < 10) {
44 value = '0' + value
45 }
46 return value || 0
47 })
48 return time_str
49 }
50
51 /**
52 * @param {number} time
53 * @param {string} option
54 * @returns {string}
55 */
56 export function formatTime(time, option) {
57 if (('' + time).length === 10) {
58 time = parseInt(time) * 1000
59 } else {
60 time = +time
61 }
62 const d = new Date(time)
63 const now = Date.now()
64
65 const diff = (now - d) / 1000
66
67 if (diff < 30) {
68 return '刚刚'
69 } else if (diff < 3600) {
70 // less 1 hour
71 return Math.ceil(diff / 60) + '分钟前'
72 } else if (diff < 3600 * 24) {
73 return Math.ceil(diff / 3600) + '小时前'
74 } else if (diff < 3600 * 24 * 2) {
75 return '1天前'
76 }
77 if (option) {
78 return parseTime(time, option)
79 } else {
80 return (
81 d.getMonth() +
82 1 +
83 '月' +
84 d.getDate() +
85 '日' +
86 d.getHours() +
87 '时' +
88 d.getMinutes() +
89 '分'
90 )
91 }
92 }
93
94 /**
95 * @param {string} url
96 * @returns {Object}
97 */
98 export function getQueryObject(url) {
99 url = url == null ? window.location.href : url
100 const search = url.substring(url.lastIndexOf('?') + 1)
101 const obj = {}
102 const reg = /([^?&=]+)=([^?&=]*)/g
103 search.replace(reg, (rs, $1, $2) => {
104 const name = decodeURIComponent($1)
105 let val = decodeURIComponent($2)
106 val = String(val)
107 obj[name] = val
108 return rs
109 })
110 return obj
111 }
112
113 /**
114 * @param {string} input value
115 * @returns {number} output value
116 */
117 export function byteLength(str) {
118 // returns the byte length of an utf8 string
119 let s = str.length
120 for (var i = str.length - 1; i >= 0; i--) {
121 const code = str.charCodeAt(i)
122 if (code > 0x7f && code <= 0x7ff) s++
123 else if (code > 0x7ff && code <= 0xffff) s += 2
124 if (code >= 0xDC00 && code <= 0xDFFF) i--
125 }
126 return s
127 }
128
129 /**
130 * @param {Array} actual
131 * @returns {Array}
132 */
133 export function cleanArray(actual) {
134 const newArray = []
135 for (let i = 0; i < actual.length; i++) {
136 if (actual[i]) {
137 newArray.push(actual[i])
138 }
139 }
140 return newArray
141 }
142
143 /**
144 * @param {Object} json
145 * @returns {Array}
146 */
147 export function param(json) {
148 if (!json) return ''
149 return cleanArray(
150 Object.keys(json).map(key => {
151 if (json[key] === undefined) return ''
152 return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
153 })
154 ).join('&')
155 }
156
157 /**
158 * @param {string} url
159 * @returns {Object}
160 */
161 export function param2Obj(url) {
162 const search = url.split('?')[1]
163 if (!search) {
164 return {}
165 }
166 return JSON.parse(
167 '{"' +
168 decodeURIComponent(search)
169 .replace(/"/g, '\\"')
170 .replace(/&/g, '","')
171 .replace(/=/g, '":"')
172 .replace(/\+/g, ' ') +
173 '"}'
174 )
175 }
176
177 /**
178 * @param {string} val
179 * @returns {string}
180 */
181 export function html2Text(val) {
182 const div = document.createElement('div')
183 div.innerHTML = val
184 return div.textContent || div.innerText
185 }
186
187 /**
188 * Merges two objects, giving the last one precedence
189 * @param {Object} target
190 * @param {(Object|Array)} source
191 * @returns {Object}
192 */
193 export function objectMerge(target, source) {
194 if (typeof target !== 'object') {
195 target = {}
196 }
197 if (Array.isArray(source)) {
198 return source.slice()
199 }
200 Object.keys(source).forEach(property => {
201 const sourceProperty = source[property]
202 if (typeof sourceProperty === 'object') {
203 target[property] = objectMerge(target[property], sourceProperty)
204 } else {
205 target[property] = sourceProperty
206 }
207 })
208 return target
209 }
210
211 /**
212 * @param {HTMLElement} element
213 * @param {string} className
214 */
215 export function toggleClass(element, className) {
216 if (!element || !className) {
217 return
218 }
219 let classString = element.className
220 const nameIndex = classString.indexOf(className)
221 if (nameIndex === -1) {
222 classString += '' + className
223 } else {
224 classString =
225 classString.substr(0, nameIndex) +
226 classString.substr(nameIndex + className.length)
227 }
228 element.className = classString
229 }
230
231 /**
232 * @param {string} type
233 * @returns {Date}
234 */
235 export function getTime(type) {
236 if (type === 'start') {
237 return new Date().getTime() - 3600 * 1000 * 24 * 90
238 } else {
239 return new Date(new Date().toDateString())
240 }
241 }
242
243 /**
244 * @param {Function} func
245 * @param {number} wait
246 * @param {boolean} immediate
247 * @return {*}
248 */
249 export function debounce(func, wait, immediate) {
250 let timeout, args, context, timestamp, result
251
252 const later = function() {
253 // 据上一次触发时间间隔
254 const last = +new Date() - timestamp
255
256 // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
257 if (last < wait && last > 0) {
258 timeout = setTimeout(later, wait - last)
259 } else {
260 timeout = null
261 // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
262 if (!immediate) {
263 result = func.apply(context, args)
264 if (!timeout) context = args = null
265 }
266 }
267 }
268
269 return function(...args) {
270 context = this
271 timestamp = +new Date()
272 const callNow = immediate && !timeout
273 // 如果延时不存在,重新设定延时
274 if (!timeout) timeout = setTimeout(later, wait)
275 if (callNow) {
276 result = func.apply(context, args)
277 context = args = null
278 }
279
280 return result
281 }
282 }
283
284 /**
285 * This is just a simple version of deep copy
286 * Has a lot of edge cases bug
287 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
288 * @param {Object} source
289 * @returns {Object}
290 */
291 export function deepClone(source) {
292 if (!source && typeof source !== 'object') {
293 throw new Error('error arguments', 'deepClone')
294 }
295 const targetObj = source.constructor === Array ? [] : {}
296 Object.keys(source).forEach(keys => {
297 if (source[keys] && typeof source[keys] === 'object') {
298 targetObj[keys] = deepClone(source[keys])
299 } else {
300 targetObj[keys] = source[keys]
301 }
302 })
303 return targetObj
304 }
305
306 /**
307 * @param {Array} arr
308 * @returns {Array}
309 */
310 export function uniqueArr(arr) {
311 return Array.from(new Set(arr))
312 }
313
314 /**
315 * @returns {string}
316 */
317 export function createUniqueString() {
318 const timestamp = +new Date() + ''
319 const randomNum = parseInt((1 + Math.random()) * 65536) + ''
320 return (+(randomNum + timestamp)).toString(32)
321 }
322
323 /**
324 * Check if an element has a class
325 * @param {HTMLElement} elm
326 * @param {string} cls
327 * @returns {boolean}
328 */
329 export function hasClass(ele, cls) {
330 return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
331 }
332
333 /**
334 * Add class to element
335 * @param {HTMLElement} elm
336 * @param {string} cls
337 */
338 export function addClass(ele, cls) {
339 if (!hasClass(ele, cls)) ele.className += ' ' + cls
340 }
341
342 /**
343 * Remove class from element
344 * @param {HTMLElement} elm
345 * @param {string} cls
346 */
347 export function removeClass(ele, cls) {
348 if (hasClass(ele, cls)) {
349 const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
350 ele.className = ele.className.replace(reg, ' ')
351 }
352 }
353
354 // 替换邮箱字符
355 export function regEmail(email) {
356 if (String(email).indexOf('@') > 0) {
357 const str = email.split('@')
358 let _s = ''
359 if (str[0].length > 3) {
360 for (var i = 0; i < str[0].length - 3; i++) {
361 _s += '*'
362 }
363 }
364 var new_email = str[0].substr(0, 3) + _s + '@' + str[1]
365 }
366 return new_email
367 }
368
369 // 替换手机字符
370 export function regMobile(mobile) {
371 if (mobile.length > 7) {
372 var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7)
373 }
374 return new_mobile
375 }
376
377 // 下载文件
378 export function downloadFile(obj, name, suffix) {
379 const url = window.URL.createObjectURL(new Blob([obj]))
380 const link = document.createElement('a')
381 link.style.display = 'none'
382 link.href = url
383 const fileName = parseTime(new Date()) + '-' + name + '.' + suffix
384 link.setAttribute('download', fileName)
385 document.body.appendChild(link)
386 link.click()
387 document.body.removeChild(link)
388 }
1 import store from '@/store'
2
3 /**
4 * @param {Array} value
5 * @returns {Boolean}
6 * @example see @/views/permission/directive.vue
7 */
8 export default {
9 install(Vue) {
10 Vue.prototype.checkPer = (value) => {
11 if (value && value instanceof Array && value.length > 0) {
12 const roles = store.getters && store.getters.roles
13 const permissionRoles = value
14 return roles.some(role => {
15 return permissionRoles.includes(role)
16 })
17 } else {
18 console.error(`need roles! Like v-permission="['admin','editor']"`)
19 return false
20 }
21 }
22 }
23 }
1 import axios from 'axios'
2 // import router from '@/router/routers'
3 import { Notification } from 'element-ui'
4 import store from '../store'
5 import { getToken } from '@/utils/auth'
6 import Config from '@/settings'
7 import Cookies from 'js-cookie'
8
9 // 创建axios实例
10 const service = axios.create({
11 baseURL: process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : '/', // api 的 base_url
12 timeout: Config.timeout // 请求超时时间
13 })
14
15 // request拦截器
16 service.interceptors.request.use(
17 config => {
18 if (getToken()) {
19 config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
20 }
21 config.headers['Content-Type'] = 'application/json'
22 return config
23 },
24 error => {
25 console.log(error)
26 Promise.reject(error)
27 }
28 )
29
30 // response 拦截器
31 service.interceptors.response.use(
32 response => {
33 return response.data
34 },
35 error => {
36 // 兼容blob下载出错json提示
37 if (error.response.data instanceof Blob && error.response.data.type.toLowerCase().indexOf('json') !== -1) {
38 const reader = new FileReader()
39 reader.readAsText(error.response.data, 'utf-8')
40 reader.onload = function(e) {
41 const errorMsg = JSON.parse(reader.result).message
42 Notification.error({
43 title: errorMsg,
44 duration: 5000
45 })
46 }
47 } else {
48 let code = 0
49 try {
50 code = error.response.status
51 } catch (e) {
52 if (error.toString().indexOf('Error: timeout') !== -1) {
53 Notification.error({
54 title: '网络请求超时',
55 duration: 5000
56 })
57 return Promise.reject(error)
58 }
59 }
60 if (code) {
61 if (code === 401) {
62 store.dispatch('LogOut').then(() => {
63 // 用户登录界面提示
64 Cookies.set('point', 401)
65 location.reload()
66 })
67 } else {
68 const errorMsg = error.response.data.description
69 if (errorMsg !== undefined) {
70 Notification.error({
71 title: errorMsg,
72 duration: 5000
73 })
74 }
75 return Promise.reject(error)
76 }
77 } else {
78 Notification.error({
79 title: '接口请求失败',
80 duration: 5000
81 })
82 }
83 }
84 return Promise.reject(error)
85 }
86 )
87 export default service
1 import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
2
3 // 密钥对生成 http://web.chacuo.net/netrsakeypair
4
5 const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
6 '2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ=='
7
8 const privateKey = 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8\n' +
9 'mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9p\n' +
10 'B6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue\n' +
11 '/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZ\n' +
12 'UBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6\n' +
13 'vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha\n' +
14 '4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3\n' +
15 'tTbklZkD2A=='
16
17 // 加密
18 export function encrypt(txt) {
19 const encryptor = new JSEncrypt()
20 encryptor.setPublicKey(publicKey) // 设置公钥
21 return encryptor.encrypt(txt) // 对需要加密的数据进行加密
22 }
23
24 // 解密
25 export function decrypt(txt) {
26 const encryptor = new JSEncrypt()
27 encryptor.setPrivateKey(privateKey)
28 return encryptor.decrypt(txt)
29 }
30
1 import Date from './datetime.js'
2
3 export const calendarBaseShortcuts = [{
4 text: '今天',
5 onClick(picker) {
6 const startTime = new Date(new Date().setHours(0, 0, 0))
7 const endTime = new Date(new Date().setHours(23, 59, 59))
8 picker.$emit('pick', [startTime, endTime])
9 }
10 }, {
11 text: '昨天',
12 onClick(picker) {
13 const startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0))
14 const endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59))
15 picker.$emit('pick', [startTime, endTime])
16 }
17 }, {
18 text: '最近一周',
19 onClick(picker) {
20 const startTime = new Date(new Date().daysAgo(7).setHours(0, 0, 0))
21 const endTime = new Date(new Date().setHours(23, 59, 59))
22 picker.$emit('pick', [startTime, endTime])
23 }
24 }, {
25 text: '最近30天',
26 onClick(picker) {
27 const startTime = new Date(new Date().daysAgo(30).setHours(0, 0, 0))
28 const endTime = new Date(new Date().setHours(23, 59, 59))
29 picker.$emit('pick', [startTime, endTime])
30 }
31 }, {
32 text: '这个月',
33 onClick(picker) {
34 const startTime = new Date(new Date().monthBegin().setHours(0, 0, 0))
35 const endTime = new Date(new Date().setHours(23, 59, 59))
36 picker.$emit('pick', [startTime, endTime])
37 }
38 }, {
39 text: '本季度',
40 onClick(picker) {
41 const startTime = new Date(new Date().quarterBegin().setHours(0, 0, 0))
42 const endTime = new Date(new Date().setHours(23, 59, 59))
43 picker.$emit('pick', [startTime, endTime])
44 }
45 }]
46
47 export const calendarMoveShortcuts = [{
48 text: '‹ 往前一天 ',
49 onClick(picker) {
50 let startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0))
51 let endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59))
52 if (!picker.value) {
53 picker.value = [startTime, endTime]
54 }
55 startTime = picker.value[0].daysAgo(1)
56 endTime = picker.value[1].daysAgo(1)
57 picker.$emit('pick', [startTime, endTime])
58 }
59 }, {
60 text: ' 往后一天 ›',
61 onClick(picker) {
62 let startTime = new Date(new Date().setHours(0, 0, 0))
63 let endTime = new Date(new Date().setHours(23, 59, 59))
64 if (!picker.value) {
65 picker.value = [startTime, endTime]
66 }
67 startTime = picker.value[0].daysAgo(-1)
68 endTime = picker.value[1].daysAgo(-1)
69 picker.$emit('pick', [startTime, endTime])
70 }
71 }, {
72 text: '« 往前一周 ',
73 onClick(picker) {
74 let startTime = new Date(new Date().setHours(0, 0, 0))
75 let endTime = new Date(new Date().setHours(23, 59, 59))
76 if (!picker.value) {
77 picker.value = [startTime.daysAgo(new Date().getDay()),
78 endTime.daysAgo(new Date().getDay() + 1)]
79 } else {
80 picker.value = [picker.value[0].daysAgo(picker.value[0].getDay()),
81 picker.value[1].daysAgo(picker.value[1].getDay() + 1)]
82 }
83 startTime = picker.value[0].daysAgo(7)
84 endTime = picker.value[1]
85 picker.$emit('pick', [startTime, endTime])
86 }
87 }, {
88 text: ' 往后一周 »',
89 onClick(picker) {
90 let startTime = new Date(new Date().setHours(0, 0, 0))
91 let endTime = new Date(new Date().setHours(23, 59, 59))
92 if (!picker.value) {
93 picker.value = [startTime.daysAgo(new Date().getDay() - 7),
94 endTime.daysAgo(new Date().getDay() - 6)]
95 } else {
96 picker.value = [picker.value[0].daysAgo(picker.value[0].getDay() - 7),
97 picker.value[1].daysAgo(picker.value[1].getDay() - 6)]
98 }
99 startTime = picker.value[0]
100 endTime = picker.value[1].daysAgo(-7)
101 picker.$emit('pick', [startTime, endTime])
102 }
103 }]
104
105 export const calendarShortcuts = [
106 ...calendarBaseShortcuts,
107 ...calendarMoveShortcuts
108 ]
1 import axios from 'axios'
2 import { getToken } from '@/utils/auth'
3
4 export function upload(api, file) {
5 var data = new FormData()
6 data.append('file', file)
7 const config = {
8 headers: { 'Authorization': getToken() }
9 }
10 return axios.post(api, data, config)
11 }
1 /**
2 * Created by PanJiaChen on 16/11/18.
3 */
4
5 /**
6 * @param {string} path
7 * @returns {Boolean}
8 */
9 export function isExternal(path) {
10 return /^(https?:|mailto:|tel:)/.test(path)
11 }
12
13 /**
14 * @param {string} str
15 * @returns {Boolean}
16 */
17 export function validUsername(str) {
18 const valid_map = ['admin', 'editor']
19 return valid_map.indexOf(str.trim()) >= 0
20 }
21
22 /**
23 * @param {string} url
24 * @returns {Boolean}
25 */
26 export function validURL(url) {
27 const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
28 return reg.test(url)
29 }
30
31 /**
32 * @param {string} str
33 * @returns {Boolean}
34 */
35 export function validLowerCase(str) {
36 const reg = /^[a-z]+$/
37 return reg.test(str)
38 }
39
40 /**
41 * @param {string} str
42 * @returns {Boolean}
43 */
44 export function validUpperCase(str) {
45 const reg = /^[A-Z]+$/
46 return reg.test(str)
47 }
48
49 /**
50 * @param {string} str
51 * @returns {Boolean}
52 */
53 export function validAlphabets(str) {
54 const reg = /^[A-Za-z]+$/
55 return reg.test(str)
56 }
57
58 /**
59 * @param {string} email
60 * @returns {Boolean}
61 */
62 export function validEmail(email) {
63 const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
64 return reg.test(email)
65 }
66
67 export function isvalidPhone(phone) {
68 const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
69 return reg.test(phone)
70 }
71
72 /**
73 * @param {string} str
74 * @returns {Boolean}
75 */
76 export function isString(str) {
77 if (typeof str === 'string' || str instanceof String) {
78 return true
79 }
80 return false
81 }
82
83 /**
84 * @param {Array} arg
85 * @returns {Boolean}
86 */
87 export function isArray(arg) {
88 if (typeof Array.isArray === 'undefined') {
89 return Object.prototype.toString.call(arg) === '[object Array]'
90 }
91 return Array.isArray(arg)
92 }
93
94 export function isBlank(input) {
95 return input == null || /^\s*$/.test(input)
96 }
97
98 /**
99 * 是否合法IP地址
100 * @param rule
101 * @param value
102 * @param callback
103 */
104 export function validateIP(rule, value, callback) {
105 if (value === '' || value === undefined || value == null) {
106 callback()
107 } else {
108 const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
109 if ((!reg.test(value)) && value !== '') {
110 callback(new Error('请输入正确的IP地址'))
111 } else {
112 callback()
113 }
114 }
115 }
116
117 /* 是否手机号码或者固话*/
118 export function validatePhoneTwo(rule, value, callback) {
119 const reg = /^((0\d{2,3}-\d{7,8})|(1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}))$/
120 if (value === '' || value === undefined || value == null) {
121 callback()
122 } else {
123 if ((!reg.test(value)) && value !== '') {
124 callback(new Error('请输入正确的电话号码或者固话号码'))
125 } else {
126 callback()
127 }
128 }
129 }
130
131 /* 是否固话*/
132 export function validateTelephone(rule, value, callback) {
133 const reg = /0\d{2}-\d{7,8}/
134 if (value === '' || value === undefined || value == null) {
135 callback()
136 } else {
137 if ((!reg.test(value)) && value !== '') {
138 callback(new Error('请输入正确的固话(格式:区号+号码,如010-1234567)'))
139 } else {
140 callback()
141 }
142 }
143 }
144
145 /* 是否手机号码*/
146 export function validatePhone(rule, value, callback) {
147 const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
148 if (value === '' || value === undefined || value == null) {
149 callback()
150 } else {
151 if ((!reg.test(value)) && value !== '') {
152 callback(new Error('请输入正确的电话号码'))
153 } else {
154 callback()
155 }
156 }
157 }
158
159 /* 是否身份证号码*/
160 export function validateIdNo(rule, value, callback) {
161 const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
162 if (value === '' || value === undefined || value == null) {
163 callback()
164 } else {
165 if ((!reg.test(value)) && value !== '') {
166 callback(new Error('请输入正确的身份证号码'))
167 } else {
168 callback()
169 }
170 }
171 }
1 <template>
2 <div class="dashboard-container">
3 <div class="dashboard-editor-container">
4 <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
5 <heat-map />
6 </el-row>
7 <el-row :gutter="32">
8 <el-col :xs="24" :sm="24" :lg="8">
9 <div class="chart-wrapper">
10 <radar-chart />
11 </div>
12 </el-col>
13 <el-col :xs="24" :sm="24" :lg="8">
14 <div class="chart-wrapper">
15 <sunburst />
16 </div>
17 </el-col>
18 <el-col :xs="24" :sm="24" :lg="8">
19 <div class="chart-wrapper">
20 <gauge />
21 </div>
22 </el-col>
23 </el-row>
24 <el-row :gutter="12">
25 <el-col :span="12">
26 <div class="chart-wrapper">
27 <rich />
28 </div>
29 </el-col>
30 <el-col :span="12">
31 <div class="chart-wrapper">
32 <theme-river />
33 </div>
34 </el-col>
35 </el-row>
36 <el-row :gutter="32">
37 <el-col :xs="24" :sm="24" :lg="8">
38 <div class="chart-wrapper">
39 <graph />
40 </div>
41 </el-col>
42 <el-col :xs="24" :sm="24" :lg="8">
43 <div class="chart-wrapper">
44 <sankey />
45 </div>
46 </el-col>
47 <el-col :xs="24" :sm="24" :lg="8">
48 <div class="chart-wrapper">
49 <line3-d />
50 </div>
51 </el-col>
52 </el-row>
53 <el-row :gutter="12">
54 <el-col :span="12">
55 <div class="chart-wrapper">
56 <scatter />
57 </div>
58 </el-col>
59 <el-col :span="12">
60 <div class="chart-wrapper">
61 <point />
62 </div>
63 </el-col>
64 </el-row>
65 <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
66 <div class="chart-wrapper">
67 <word-cloud />
68 </div>
69 </el-row>
70 <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
71 <div class="chart-wrapper">
72 <category />
73 </div>
74 </el-row>
75 </div>
76 </div>
77 </template>
78
79 <script>
80 import RadarChart from '@/components/Echarts/RadarChart'
81 import HeatMap from '@/components/Echarts/HeatMap'
82 import Gauge from '@/components/Echarts/Gauge'
83 import Rich from '@/components/Echarts/Rich'
84 import ThemeRiver from '@/components/Echarts/ThemeRiver'
85 import Sunburst from '@/components/Echarts/Sunburst'
86 import Graph from '@/components/Echarts/Graph'
87 import Sankey from '@/components/Echarts/Sankey'
88 import Scatter from '@/components/Echarts/Scatter'
89 import Line3D from '@/components/Echarts/Line3D'
90 import Category from '@/components/Echarts/Category'
91 import Point from '@/components/Echarts/Point'
92 import WordCloud from '@/components/Echarts/WordCloud'
93
94 export default {
95 name: 'Echarts',
96 components: {
97 Point,
98 Category,
99 Graph,
100 HeatMap,
101 RadarChart,
102 Sunburst,
103 Gauge,
104 Rich,
105 ThemeRiver,
106 Sankey,
107 Line3D,
108 Scatter,
109 WordCloud
110 }
111 }
112 </script>
113
114 <style rel="stylesheet/scss" lang="scss" scoped>
115 .dashboard-editor-container {
116 padding: 18px 22px 22px 22px;
117 background-color: rgb(240, 242, 245);
118 .chart-wrapper {
119 background: #fff;
120 padding: 16px 16px 0;
121 margin-bottom: 32px;
122 }
123 }
124 </style>
1 <template>
2 <div class="app-container">
3 <p class="warn-content">
4 富文本基于
5 <el-link type="primary" href="https://www.kancloud.cn/wangfupeng/wangeditor3/332599" target="_blank">wangEditor</el-link>
6 </p>
7 <el-row :gutter="10">
8 <el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
9 <div ref="editor" class="text" />
10 </el-col>
11 <el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
12 <div v-html="editorContent" />
13 </el-col>
14 </el-row>
15 </div>
16 </template>
17
18 <script>
19 import { mapGetters } from 'vuex'
20 import { upload } from '@/utils/upload'
21 import E from 'wangeditor'
22 export default {
23 name: 'Editor',
24 data() {
25 return {
26 editorContent:
27 `
28 <ul>
29 <li>更多帮助请查看官方文档:<a style="color: #42b983" target="_blank" href="https://www.kancloud.cn/wangfupeng/wangeditor3/332599">wangEditor</a></li>
30 </ul>
31 `
32 }
33 },
34 computed: {
35 ...mapGetters([
36 'imagesUploadApi',
37 'baseApi'
38 ])
39 },
40 mounted() {
41 const _this = this
42 var editor = new E(this.$refs.editor)
43 // 自定义菜单配置
44 editor.customConfig.zIndex = 10
45 // 文件上传
46 editor.customConfig.customUploadImg = function(files, insert) {
47 // files 是 input 中选中的文件列表
48 // insert 是获取图片 url 后,插入到编辑器的方法
49 files.forEach(image => {
50 upload(_this.imagesUploadApi, image).then(res => {
51 const data = res.data
52 const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
53 insert(url)
54 })
55 })
56 }
57 editor.customConfig.onchange = (html) => {
58 this.editorContent = html
59 }
60 editor.create()
61 // 初始化数据
62 editor.txt.html(this.editorContent)
63 }
64 }
65 </script>
66
67 <style scoped>
68 .text {
69 text-align:left;
70 }
71 ::v-deep .w-e-text-container {
72 height: 420px !important;
73 }
74 </style>
1 <template>
2 <div class="app-container">
3 <p class="warn-content">
4 Markdown 基于
5 <el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>
6 </p>
7 <mavon-editor ref="md" :style="'height:' + height" @imgAdd="imgAdd" />
8 </div>
9 </template>
10
11 <script>
12 import { upload } from '@/utils/upload'
13 import { mapGetters } from 'vuex'
14 export default {
15 name: 'Markdown',
16 data() {
17 return {
18 height: document.documentElement.clientHeight - 200 + 'px'
19 }
20 },
21 computed: {
22 ...mapGetters([
23 'imagesUploadApi',
24 'baseApi'
25 ])
26 },
27 mounted() {
28 const that = this
29 window.onresize = function temp() {
30 that.height = document.documentElement.clientHeight - 200 + 'px'
31 }
32 },
33 methods: {
34 imgAdd(pos, $file) {
35 upload(this.imagesUploadApi, $file).then(res => {
36 const data = res.data
37 const url = this.baseApi + '/file/' + data.type + '/' + data.realName
38 this.$refs.md.$img2Url(pos, url)
39 })
40 }
41 }
42 }
43 </script>
44
45 <style scoped>
46 </style>
1 <template>
2 <div class="app-container">
3 <p class="warn-content">
4 Yaml编辑器 基于
5 <a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>
6 主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>
7 </p>
8 <Yaml :value="value" :height="height" />
9 </div>
10 </template>
11
12 <script>
13 import Yaml from '@/components/YamlEdit/index'
14 export default {
15 name: 'YamlEdit',
16 components: { Yaml },
17 data() {
18 return {
19 height: document.documentElement.clientHeight - 210 + 'px',
20 value: '# 展示数据,如需更换主题,请在src/components/YamlEdit 目录中搜索原主题名称进行替换\n' +
21 '\n' +
22 '# ===================================================================\n' +
23 '# Spring Boot configuration.\n' +
24 '#\n' +
25 '# This configuration will be overridden by the Spring profile you use,\n' +
26 '# for example application-dev.yml if you use the "dev" profile.\n' +
27 '#\n' +
28 '# More information on profiles: https://www.jhipster.tech/profiles/\n' +
29 '# More information on configuration properties: https://www.jhipster.tech/common-application-properties/\n' +
30 '# ===================================================================\n' +
31 '\n' +
32 '# ===================================================================\n' +
33 '# Standard Spring Boot properties.\n' +
34 '# Full reference is available at:\n' +
35 '# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n' +
36 '# ===================================================================\n' +
37 '\n' +
38 'eureka:\n' +
39 ' client:\n' +
40 ' enabled: true\n' +
41 ' healthcheck:\n' +
42 ' enabled: true\n' +
43 ' fetch-registry: true\n' +
44 ' register-with-eureka: true\n' +
45 ' instance-info-replication-interval-seconds: 10\n' +
46 ' registry-fetch-interval-seconds: 10\n' +
47 ' instance:\n' +
48 ' appname: product\n' +
49 ' instanceId: product:${spring.application.instance-id:${random.value}}\n' +
50 ' #instanceId: 127.0.0.1:9080\n' +
51 ' lease-renewal-interval-in-seconds: 5\n' +
52 ' lease-expiration-duration-in-seconds: 10\n' +
53 ' status-page-url-path: ${management.endpoints.web.base-path}/info\n' +
54 ' health-check-url-path: ${management.endpoints.web.base-path}/health\n' +
55 ' metadata-map:\n' +
56 ' zone: primary # This is needed for the load balancer\n' +
57 ' profile: ${spring.profiles.active}\n' +
58 ' version: ${info.project.version:}\n' +
59 ' git-version: ${git.commit.id.describe:}\n' +
60 ' git-commit: ${git.commit.id.abbrev:}\n' +
61 ' git-branch: ${git.branch:}\n' +
62 'ribbon:\n' +
63 ' ReadTimeout: 120000\n' +
64 ' ConnectTimeout: 300000\n' +
65 ' eureka:\n' +
66 ' enabled: true\n' +
67 'zuul:\n' +
68 ' host:\n' +
69 ' connect-timeout-millis: 5000\n' +
70 ' max-per-route-connections: 10000\n' +
71 ' max-total-connections: 5000\n' +
72 ' socket-timeout-millis: 60000\n' +
73 ' semaphore:\n' +
74 ' max-semaphores: 500\n' +
75 '\n' +
76 'feign:\n' +
77 ' hystrix:\n' +
78 ' enabled: true\n' +
79 ' client:\n' +
80 ' config:\n' +
81 ' default:\n' +
82 ' connectTimeout: 500000\n' +
83 ' readTimeout: 500000\n' +
84 '\n' +
85 '# See https://github.com/Netflix/Hystrix/wiki/Configuration\n' +
86 'hystrix:\n' +
87 ' command:\n' +
88 ' default:\n' +
89 ' circuitBreaker:\n' +
90 ' sleepWindowInMilliseconds: 100000\n' +
91 ' forceClosed: true\n' +
92 ' execution:\n' +
93 ' isolation:\n' +
94 '# strategy: SEMAPHORE\n' +
95 '# See https://github.com/spring-cloud/spring-cloud-netflix/issues/1330\n' +
96 ' thread:\n' +
97 ' timeoutInMilliseconds: 60000\n' +
98 ' shareSecurityContext: true\n' +
99 '\n' +
100 'management:\n' +
101 ' endpoints:\n' +
102 ' web:\n' +
103 ' base-path: /management\n' +
104 ' exposure:\n' +
105 ' include: ["configprops", "env", "health", "info", "threaddump"]\n' +
106 ' endpoint:\n' +
107 ' health:\n' +
108 ' show-details: when_authorized\n' +
109 ' info:\n' +
110 ' git:\n' +
111 ' mode: full\n' +
112 ' health:\n' +
113 ' mail:\n' +
114 ' enabled: false # When using the MailService, configure an SMTP server and set this to true\n' +
115 ' metrics:\n' +
116 ' enabled: false # http://micrometer.io/ is disabled by default, as we use http://metrics.dropwizard.io/ instead\n' +
117 '\n' +
118 'spring:\n' +
119 ' application:\n' +
120 ' name: product\n' +
121 ' jpa:\n' +
122 ' open-in-view: false\n' +
123 ' hibernate:\n' +
124 ' ddl-auto: update\n' +
125 ' naming:\n' +
126 ' physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy\n' +
127 ' implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy\n' +
128 ' messages:\n' +
129 ' basename: i18n/messages\n' +
130 ' mvc:\n' +
131 ' favicon:\n' +
132 ' enabled: false\n' +
133 ' thymeleaf:\n' +
134 ' mode: HTML\n' +
135 'security:\n' +
136 ' oauth2:\n' +
137 ' resource:\n' +
138 ' filter-order: 3\n' +
139 '\n' +
140 'server:\n' +
141 ' servlet:\n' +
142 ' session:\n' +
143 ' cookie:\n' +
144 ' http-only: true\n' +
145 '\n' +
146 '# Properties to be exposed on the /info management endpoint\n' +
147 'info:\n' +
148 ' # Comma separated list of profiles that will trigger the ribbon to show\n' +
149 ' display-ribbon-on-profiles: "dev"\n' +
150 '\n' +
151 '# ===================================================================\n' +
152 '# JHipster specific properties\n' +
153 '#\n' +
154 '# Full reference is available at: https://www.jhipster.tech/common-application-properties/\n' +
155 '# ===================================================================\n' +
156 '\n' +
157 'jhipster:\n' +
158 ' async:\n' +
159 ' core-pool-size: 2\n' +
160 ' max-pool-size: 50\n' +
161 ' queue-capacity: 10000\n' +
162 ' # By default CORS is disabled. Uncomment to enable.\n' +
163 ' #cors:\n' +
164 ' #allowed-origins: "*"\n' +
165 ' #allowed-methods: "*"\n' +
166 ' #allowed-headers: "*"\n' +
167 ' #exposed-headers: "Authorization,Link,X-Total-Count"\n' +
168 ' #allow-credentials: true\n' +
169 ' #max-age: 1800\n' +
170 ' mail:\n' +
171 ' from: product@localhost\n' +
172 ' swagger:\n' +
173 ' default-include-pattern: /api/.*\n' +
174 ' title: product API\n' +
175 ' description: product API documentation\n' +
176 ' version: 0.0.1\n' +
177 ' terms-of-service-url:\n' +
178 ' contact-name:\n' +
179 ' contact-url:\n' +
180 ' contact-email:\n' +
181 ' license:\n' +
182 ' license-url:\n' +
183 '\n' +
184 '# ===================================================================\n' +
185 '# Application specific properties\n' +
186 '# Add your own application properties here, see the ApplicationProperties class\n' +
187 '# to have type-safe configuration, like in the JHipsterProperties above\n' +
188 '#\n' +
189 '# More documentation is available at:\n' +
190 '# https://www.jhipster.tech/common-application-properties/\n' +
191 '# ===================================================================\n' +
192 '\n' +
193 '# application:\n'
194 }
195 },
196 mounted() {
197 const that = this
198 window.onresize = function temp() {
199 that.height = document.documentElement.clientHeight - 210 + 'px'
200 }
201 }
202 }
203 </script>
204
205 <style scoped>
206
207 </style>
1 <template>
2 <div class="app-container">
3 <upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
4 <el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
5 <el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
6 </el-table>
7 </div>
8 </template>
9
10 <script>
11 import UploadExcelComponent from '@/components/UploadExcel/index.vue'
12
13 export default {
14 name: 'UploadExcel',
15 components: { UploadExcelComponent },
16 data() {
17 return {
18 tableData: [],
19 tableHeader: []
20 }
21 },
22 methods: {
23 beforeUpload(file) {
24 const isLt1M = file.size / 1024 / 1024 < 1
25 if (isLt1M) {
26 return true
27 }
28
29 this.$message({
30 message: '请不要上传大于1m的文件.',
31 type: 'warning'
32 })
33 return false
34 },
35 handleSuccess({ results, header }) {
36 this.tableData = results
37 this.tableHeader = header
38 }
39 }
40 }
41 </script>
1 const elementIcons = [
2 'info',
3 'error',
4 'success',
5 'warning',
6 'question',
7 'back',
8 'arrow-left',
9 'arrow-down',
10 'arrow-right',
11 'arrow-up',
12 'caret-left',
13 'caret-bottom',
14 'caret-top',
15 'caret-right',
16 'd-arrow-left',
17 'd-arrow-right',
18 'minus',
19 'plus',
20 'remove',
21 'circle-plus',
22 'remove-outline',
23 'circle-plus-outline',
24 'close',
25 'check',
26 'circle-close',
27 'circle-check',
28 'circle-close-outline',
29 'circle-check-outline',
30 'zoom-out',
31 'zoom-in',
32 'd-caret',
33 'sort',
34 'sort-down',
35 'sort-up',
36 'tickets',
37 'document',
38 'goods',
39 'sold-out',
40 'news',
41 'message',
42 'date',
43 'printer',
44 'time',
45 'bell',
46 'mobile-phone',
47 'service',
48 'view',
49 'menu',
50 'more',
51 'more-outline',
52 'star-on',
53 'star-off',
54 'location',
55 'location-outline',
56 'phone',
57 'phone-outline',
58 'picture',
59 'picture-outline',
60 'delete',
61 'search',
62 'edit',
63 'edit-outline',
64 'rank',
65 'refresh',
66 'share',
67 'setting',
68 'upload',
69 'upload2',
70 'download',
71 'loading'
72 ]
73
74 export default elementIcons
1 <template>
2 <div class="icons-container">
3 <aside>
4 <a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
5 </a>
6 </aside>
7 <el-tabs type="border-card">
8 <el-tab-pane label="Icons">
9 <div class="grid">
10 <div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
11 <el-tooltip placement="top">
12 <div slot="content">
13 {{ generateIconCode(item) }}
14 </div>
15 <div class="icon-item">
16 <svg-icon :icon-class="item" class-name="disabled" />
17 <span>{{ item }}</span>
18 </div>
19 </el-tooltip>
20 </div>
21 </div>
22 </el-tab-pane>
23 <el-tab-pane label="Element-UI Icons">
24 <div class="grid">
25 <div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
26 <el-tooltip placement="top">
27 <div slot="content">
28 {{ generateElementIconCode(item) }}
29 </div>
30 <div class="icon-item">
31 <i :class="'el-icon-' + item" />
32 <span>{{ item }}</span>
33 </div>
34 </el-tooltip>
35 </div>
36 </div>
37 </el-tab-pane>
38 </el-tabs>
39 </div>
40 </template>
41
42 <script>
43 import clipboard from '@/utils/clipboard'
44 import svgIcons from './svg-icons'
45 import elementIcons from './element-icons'
46 export default {
47 name: 'Icons',
48 data() {
49 return {
50 svgIcons,
51 elementIcons
52 }
53 },
54 methods: {
55 generateIconCode(symbol) {
56 return `<svg-icon icon-class="${symbol}" />`
57 },
58 generateElementIconCode(symbol) {
59 return `<i class="el-icon-${symbol}" />`
60 },
61 handleClipboard(text, event) {
62 clipboard(text, event)
63 }
64 }
65 }
66 </script>
67
68 <style lang="scss" scoped>
69 .icons-container {
70 margin: 10px 20px 0;
71 overflow: hidden;
72
73 .grid {
74 position: relative;
75 display: grid;
76 grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
77 }
78 .icon-item {
79 margin: 20px;
80 height: 85px;
81 text-align: center;
82 width: 100px;
83 float: left;
84 font-size: 30px;
85 color: #24292e;
86 cursor: pointer;
87 }
88 span {
89 display: block;
90 font-size: 16px;
91 margin-top: 10px;
92 }
93 .disabled {
94 pointer-events: none;
95 }
96 }
97 </style>
1 const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
2 const requireAll = requireContext => requireContext.keys()
3
4 const re = /\.\/(.*)\.svg/
5
6 const svgIcons = requireAll(req).map(i => {
7 return i.match(re)[1]
8 })
9
10 export default svgIcons
1 <template>
2 <div :class="className" :style="{height:height,width:width}" />
3 </template>
4
5 <script>
6 import echarts from 'echarts'
7 require('echarts/theme/macarons') // echarts theme
8 import resize from './mixins/resize'
9
10 export default {
11 mixins: [resize],
12 props: {
13 className: {
14 type: String,
15 default: 'chart'
16 },
17 width: {
18 type: String,
19 default: '100%'
20 },
21 height: {
22 type: String,
23 default: '350px'
24 },
25 autoResize: {
26 type: Boolean,
27 default: true
28 },
29 chartData: {
30 type: Object,
31 required: true
32 }
33 },
34 data() {
35 return {
36 chart: null
37 }
38 },
39 watch: {
40 chartData: {
41 deep: true,
42 handler(val) {
43 this.setOptions(val)
44 }
45 }
46 },
47 mounted() {
48 this.$nextTick(() => {
49 this.initChart()
50 })
51 },
52 beforeDestroy() {
53 if (!this.chart) {
54 return
55 }
56 this.chart.dispose()
57 this.chart = null
58 },
59 methods: {
60 initChart() {
61 this.chart = echarts.init(this.$el, 'macarons')
62 this.setOptions(this.chartData)
63 },
64 setOptions({ expectedData, actualData } = {}) {
65 this.chart.setOption({
66 xAxis: {
67 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
68 boundaryGap: false,
69 axisTick: {
70 show: false
71 }
72 },
73 grid: {
74 left: 10,
75 right: 10,
76 bottom: 20,
77 top: 30,
78 containLabel: true
79 },
80 tooltip: {
81 trigger: 'axis',
82 axisPointer: {
83 type: 'cross'
84 },
85 padding: [5, 10]
86 },
87 yAxis: {
88 axisTick: {
89 show: false
90 }
91 },
92 legend: {
93 data: ['expected', 'actual']
94 },
95 series: [{
96 name: 'expected', itemStyle: {
97 normal: {
98 color: '#FF005A',
99 lineStyle: {
100 color: '#FF005A',
101 width: 2
102 }
103 }
104 },
105 smooth: true,
106 type: 'line',
107 data: expectedData,
108 animationDuration: 2800,
109 animationEasing: 'cubicInOut'
110 },
111 {
112 name: 'actual',
113 smooth: true,
114 type: 'line',
115 itemStyle: {
116 normal: {
117 color: '#3888fa',
118 lineStyle: {
119 color: '#3888fa',
120 width: 2
121 },
122 areaStyle: {
123 color: '#f3f8ff'
124 }
125 }
126 },
127 data: actualData,
128 animationDuration: 2800,
129 animationEasing: 'quadraticOut'
130 }]
131 })
132 }
133 }
134 }
135 </script>
1 <template>
2 <el-row :gutter="40" class="panel-group">
3 <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
4 <div class="card-panel" @click="handleSetLineChartData('newVisitis')">
5 <div class="card-panel-icon-wrapper icon-people">
6 <svg-icon icon-class="peoples" class-name="card-panel-icon" />
7 </div>
8 <div class="card-panel-description">
9 <div class="card-panel-text">
10 New Visits
11 </div>
12 <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
13 </div>
14 </div>
15 </el-col>
16 <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
17 <div class="card-panel" @click="handleSetLineChartData('messages')">
18 <div class="card-panel-icon-wrapper icon-message">
19 <svg-icon icon-class="message" class-name="card-panel-icon" />
20 </div>
21 <div class="card-panel-description">
22 <div class="card-panel-text">
23 Messages
24 </div>
25 <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
26 </div>
27 </div>
28 </el-col>
29 <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
30 <div class="card-panel" @click="handleSetLineChartData('purchases')">
31 <div class="card-panel-icon-wrapper icon-money">
32 <svg-icon icon-class="money" class-name="card-panel-icon" />
33 </div>
34 <div class="card-panel-description">
35 <div class="card-panel-text">
36 Purchases
37 </div>
38 <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
39 </div>
40 </div>
41 </el-col>
42 <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
43 <div class="card-panel" @click="handleSetLineChartData('shoppings')">
44 <div class="card-panel-icon-wrapper icon-shopping">
45 <svg-icon icon-class="shopping" class-name="card-panel-icon" />
46 </div>
47 <div class="card-panel-description">
48 <div class="card-panel-text">
49 Shoppings
50 </div>
51 <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
52 </div>
53 </div>
54 </el-col>
55 </el-row>
56 </template>
57
58 <script>
59 import CountTo from 'vue-count-to'
60
61 export default {
62 components: {
63 CountTo
64 },
65 methods: {
66 handleSetLineChartData(type) {
67 this.$emit('handleSetLineChartData', type)
68 }
69 }
70 }
71 </script>
72
73 <style lang="scss" scoped>
74 .panel-group {
75 margin-top: 18px;
76
77 .card-panel-col {
78 margin-bottom: 32px;
79 }
80
81 .card-panel {
82 height: 108px;
83 cursor: pointer;
84 font-size: 12px;
85 position: relative;
86 overflow: hidden;
87 color: #666;
88 background: #fff;
89 box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
90 border-color: rgba(0, 0, 0, .05);
91
92 &:hover {
93 .card-panel-icon-wrapper {
94 color: #fff;
95 }
96
97 .icon-people {
98 background: #40c9c6;
99 }
100
101 .icon-message {
102 background: #36a3f7;
103 }
104
105 .icon-money {
106 background: #f4516c;
107 }
108
109 .icon-shopping {
110 background: #34bfa3
111 }
112 }
113
114 .icon-people {
115 color: #40c9c6;
116 }
117
118 .icon-message {
119 color: #36a3f7;
120 }
121
122 .icon-money {
123 color: #f4516c;
124 }
125
126 .icon-shopping {
127 color: #34bfa3
128 }
129
130 .card-panel-icon-wrapper {
131 float: left;
132 margin: 14px 0 0 14px;
133 padding: 16px;
134 transition: all 0.38s ease-out;
135 border-radius: 6px;
136 }
137
138 .card-panel-icon {
139 float: left;
140 font-size: 48px;
141 }
142
143 .card-panel-description {
144 float: right;
145 font-weight: bold;
146 margin: 26px;
147 margin-left: 0px;
148
149 .card-panel-text {
150 line-height: 18px;
151 color: rgba(0, 0, 0, 0.45);
152 font-size: 16px;
153 margin-bottom: 12px;
154 }
155
156 .card-panel-num {
157 font-size: 20px;
158 }
159 }
160 }
161 }
162
163 @media (max-width:550px) {
164 .card-panel-description {
165 display: none;
166 }
167
168 .card-panel-icon-wrapper {
169 float: none !important;
170 width: 100%;
171 height: 100%;
172 margin: 0 !important;
173
174 .svg-icon {
175 display: block;
176 margin: 14px auto !important;
177 float: none !important;
178 }
179 }
180 }
181 </style>
1 import { debounce } from '@/utils'
2
3 export default {
4 data() {
5 return {
6 $_sidebarElm: null,
7 $_resizeHandler: null
8 }
9 },
10 mounted() {
11 this.$_resizeHandler = debounce(() => {
12 if (this.chart) {
13 this.chart.resize()
14 }
15 }, 100)
16 this.$_initResizeEvent()
17 this.$_initSidebarResizeEvent()
18 },
19 beforeDestroy() {
20 this.$_destroyResizeEvent()
21 this.$_destroySidebarResizeEvent()
22 },
23 // to fixed bug when cached by keep-alive
24 // https://github.com/PanJiaChen/vue-element-admin/issues/2116
25 activated() {
26 this.$_initResizeEvent()
27 this.$_initSidebarResizeEvent()
28 },
29 deactivated() {
30 this.$_destroyResizeEvent()
31 this.$_destroySidebarResizeEvent()
32 },
33 methods: {
34 // use $_ for mixins properties
35 // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
36 $_initResizeEvent() {
37 window.addEventListener('resize', this.$_resizeHandler)
38 },
39 $_destroyResizeEvent() {
40 window.removeEventListener('resize', this.$_resizeHandler)
41 },
42 $_sidebarResizeHandler(e) {
43 if (e.propertyName === 'width') {
44 this.$_resizeHandler()
45 }
46 },
47 $_initSidebarResizeEvent() {
48 this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
49 this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
50 },
51 $_destroySidebarResizeEvent() {
52 this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
53 }
54 }
55 }
1 <template>
2 <div class="errPage-container">
3 <el-button icon="arrow-left" class="pan-back-btn" @click="back">
4 返回
5 </el-button>
6 <el-row>
7 <el-col :span="12">
8 <h1 class="text-jumbo text-ginormous">
9 Oops!
10 </h1>
11 <h2>你没有权限去该页面</h2>
12 <h6>如有不满请联系你领导</h6>
13 <ul class="list-unstyled">
14 <li>或者你可以去:</li>
15 <li class="link-type">
16 <router-link to="/dashboard">
17 回首页
18 </router-link>
19 </li>
20 </ul>
21 </el-col>
22 <el-col :span="12">
23 <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
24 </el-col>
25 </el-row>
26 </div>
27 </template>
28
29 <script>
30 import errGif from '@/assets/401_images/401.gif'
31
32 export default {
33 name: 'Page401',
34 data() {
35 return {
36 errGif: errGif + '?' + +new Date()
37 }
38 },
39 methods: {
40 back() {
41 if (this.$route.query.noGoBack) {
42 this.$router.push({ path: '/dashboard' })
43 } else {
44 this.$router.go(-1)
45 }
46 }
47 }
48 }
49 </script>
50
51 <style lang="scss" scoped>
52 .errPage-container {
53 width: 800px;
54 max-width: 100%;
55 margin: 100px auto;
56 .pan-back-btn {
57 background: #008489;
58 color: #fff;
59 border: none!important;
60 }
61 .pan-gif {
62 margin: 0 auto;
63 display: block;
64 }
65 .pan-img {
66 display: block;
67 margin: 0 auto;
68 width: 100%;
69 }
70 .text-jumbo {
71 font-size: 60px;
72 font-weight: 700;
73 color: #484848;
74 }
75 .list-unstyled {
76 font-size: 14px;
77 li {
78 padding-bottom: 5px;
79 }
80 a {
81 color: #008489;
82 text-decoration: none;
83 &:hover {
84 text-decoration: underline;
85 }
86 }
87 }
88 }
89 </style>
1 <template>
2 <div class="wscn-http404-container">
3 <div class="wscn-http404">
4 <div class="pic-404">
5 <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
6 <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
7 <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
8 <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
9 </div>
10 <div class="bullshit">
11 <div class="bullshit__oops">OOPS!</div>
12 <div class="bullshit__headline">{{ message }}</div>
13 <div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
14 <a href="/" class="bullshit__return-home">返回首页</a>
15 </div>
16 </div>
17 </div>
18 </template>
19
20 <script>
21
22 export default {
23 name: 'Page404',
24 computed: {
25 message() {
26 return '网管说这个页面你不能进......'
27 }
28 }
29 }
30 </script>
31
32 <style rel="stylesheet/scss" lang="scss" scoped>
33 .wscn-http404-container{
34 transform: translate(-50%,-50%);
35 position: absolute;
36 top: 40%;
37 left: 50%;
38 }
39 .wscn-http404 {
40 position: relative;
41 width: 1200px;
42 padding: 0 50px;
43 overflow: hidden;
44 .pic-404 {
45 position: relative;
46 float: left;
47 width: 600px;
48 overflow: hidden;
49 &__parent {
50 width: 100%;
51 }
52 &__child {
53 position: absolute;
54 &.left {
55 width: 80px;
56 top: 17px;
57 left: 220px;
58 opacity: 0;
59 animation-name: cloudLeft;
60 animation-duration: 2s;
61 animation-timing-function: linear;
62 animation-fill-mode: forwards;
63 animation-delay: 1s;
64 }
65 &.mid {
66 width: 46px;
67 top: 10px;
68 left: 420px;
69 opacity: 0;
70 animation-name: cloudMid;
71 animation-duration: 2s;
72 animation-timing-function: linear;
73 animation-fill-mode: forwards;
74 animation-delay: 1.2s;
75 }
76 &.right {
77 width: 62px;
78 top: 100px;
79 left: 500px;
80 opacity: 0;
81 animation-name: cloudRight;
82 animation-duration: 2s;
83 animation-timing-function: linear;
84 animation-fill-mode: forwards;
85 animation-delay: 1s;
86 }
87 @keyframes cloudLeft {
88 0% {
89 top: 17px;
90 left: 220px;
91 opacity: 0;
92 }
93 20% {
94 top: 33px;
95 left: 188px;
96 opacity: 1;
97 }
98 80% {
99 top: 81px;
100 left: 92px;
101 opacity: 1;
102 }
103 100% {
104 top: 97px;
105 left: 60px;
106 opacity: 0;
107 }
108 }
109 @keyframes cloudMid {
110 0% {
111 top: 10px;
112 left: 420px;
113 opacity: 0;
114 }
115 20% {
116 top: 40px;
117 left: 360px;
118 opacity: 1;
119 }
120 70% {
121 top: 130px;
122 left: 180px;
123 opacity: 1;
124 }
125 100% {
126 top: 160px;
127 left: 120px;
128 opacity: 0;
129 }
130 }
131 @keyframes cloudRight {
132 0% {
133 top: 100px;
134 left: 500px;
135 opacity: 0;
136 }
137 20% {
138 top: 120px;
139 left: 460px;
140 opacity: 1;
141 }
142 80% {
143 top: 180px;
144 left: 340px;
145 opacity: 1;
146 }
147 100% {
148 top: 200px;
149 left: 300px;
150 opacity: 0;
151 }
152 }
153 }
154 }
155 .bullshit {
156 position: relative;
157 float: left;
158 width: 300px;
159 padding: 30px 0;
160 overflow: hidden;
161 &__oops {
162 font-size: 32px;
163 font-weight: bold;
164 line-height: 40px;
165 color: #1482f0;
166 opacity: 0;
167 margin-bottom: 20px;
168 animation-name: slideUp;
169 animation-duration: 0.5s;
170 animation-fill-mode: forwards;
171 }
172 &__headline {
173 font-size: 20px;
174 line-height: 24px;
175 color: #222;
176 font-weight: bold;
177 opacity: 0;
178 margin-bottom: 10px;
179 animation-name: slideUp;
180 animation-duration: 0.5s;
181 animation-delay: 0.1s;
182 animation-fill-mode: forwards;
183 }
184 &__info {
185 font-size: 13px;
186 line-height: 21px;
187 color: grey;
188 opacity: 0;
189 margin-bottom: 30px;
190 animation-name: slideUp;
191 animation-duration: 0.5s;
192 animation-delay: 0.2s;
193 animation-fill-mode: forwards;
194 }
195 &__return-home {
196 display: block;
197 float: left;
198 width: 110px;
199 height: 36px;
200 background: #1482f0;
201 border-radius: 100px;
202 text-align: center;
203 color: #ffffff;
204 opacity: 0;
205 font-size: 14px;
206 line-height: 36px;
207 cursor: pointer;
208 animation-name: slideUp;
209 animation-duration: 0.5s;
210 animation-delay: 0.3s;
211 animation-fill-mode: forwards;
212 }
213 @keyframes slideUp {
214 0% {
215 transform: translateY(60px);
216 opacity: 0;
217 }
218 100% {
219 transform: translateY(0);
220 opacity: 1;
221 }
222 }
223 }
224 }
225 </style>
1 <script>
2 export default {
3 created() {
4 const { params, query } = this.$route
5 const { path } = params
6 this.$router.replace({ path: '/' + path, query })
7 },
8 render: function(h) {
9 return h() // avoid warning message
10 }
11 }
12 </script>
1 <template>
2 <div class="app-container">
3 <el-row :gutter="15">
4 <el-col style="margin-bottom: 10px">
5 <el-card class="box-card" shadow="never">
6 <div slot="header" class="clearfix">
7 <span class="role-span">字段配置:{{ tableName }}</span>
8 <el-button
9 :loading="genLoading"
10 icon="el-icon-s-promotion"
11 size="mini"
12 style="float: right; padding: 6px 9px;"
13 type="success"
14 @click="toGen"
15 >保存&生成</el-button>
16 <el-button
17 :loading="columnLoading"
18 icon="el-icon-check"
19 size="mini"
20 style="float: right; padding: 6px 9px;margin-right: 9px"
21 type="primary"
22 @click="saveColumnConfig"
23 >保存</el-button>
24 <el-tooltip class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
25 <el-button
26 :loading="syncLoading"
27 icon="el-icon-refresh"
28 size="mini"
29 style="float: right; padding: 6px 9px;"
30 type="info"
31 @click="sync"
32 >同步</el-button>
33 </el-tooltip>
34 </div>
35 <el-form size="small" label-width="90px">
36 <el-table v-loading="loading" :data="data" :max-height="tableHeight" size="small" style="width: 100%;margin-bottom: 15px">
37 <el-table-column prop="columnName" label="字段名称" />
38 <el-table-column prop="columnType" label="字段类型" />
39 <el-table-column prop="remark" label="字段描述">
40 <template slot-scope="scope">
41 <el-input v-model="data[scope.$index].remark" size="mini" class="edit-input" />
42 </template>
43 </el-table-column>
44 <el-table-column align="center" label="必填" width="70px">
45 <template slot-scope="scope">
46 <el-checkbox v-model="data[scope.$index].notNull" />
47 </template>
48 </el-table-column>
49 <el-table-column align="center" label="列表" width="70px">
50 <template slot-scope="scope">
51 <el-checkbox v-model="data[scope.$index].listShow" />
52 </template>
53 </el-table-column>
54 <el-table-column align="center" label="表单" width="70px">
55 <template slot-scope="scope">
56 <el-checkbox v-model="data[scope.$index].formShow" />
57 </template>
58 </el-table-column>
59 <el-table-column label="表单类型">
60 <template slot-scope="scope">
61 <el-select v-model="data[scope.$index].formType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
62 <el-option
63 label="文本框"
64 value="Input"
65 />
66 <el-option
67 label="文本域"
68 value="Textarea"
69 />
70 <el-option
71 label="单选框"
72 value="Radio"
73 />
74 <el-option
75 label="下拉框"
76 value="Select"
77 />
78 <el-option
79 label="日期框"
80 value="Date"
81 />
82 </el-select>
83 </template>
84 </el-table-column>
85 <el-table-column label="查询方式">
86 <template slot-scope="scope">
87 <el-select v-model="data[scope.$index].queryType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
88 <el-option
89 label="="
90 value="="
91 />
92 <el-option
93 label="!="
94 value="!="
95 />
96 <el-option
97 label=">="
98 value=">="
99 />
100 <el-option
101 label="<="
102 value="<="
103 />
104 <el-option
105 label="Like"
106 value="Like"
107 />
108 <el-option
109 label="NotNull"
110 value="NotNull"
111 />
112 <el-option
113 label="BetWeen"
114 value="BetWeen"
115 />
116 </el-select>
117 </template>
118 </el-table-column>
119 <el-table-column label="日期注解">
120 <template slot-scope="scope">
121 <el-select v-model="data[scope.$index].dateAnnotation" filterable class="edit-input" clearable size="mini" placeholder="请选择">
122 <el-option
123 label="自动创建时间"
124 value="CreationTimestamp"
125 />
126 <el-option
127 label="自动更新时间"
128 value="UpdateTimestamp"
129 />
130 </el-select>
131 </template>
132 </el-table-column>
133 <el-table-column label="关联字典">
134 <template slot-scope="scope">
135 <el-select v-model="data[scope.$index].dictName" filterable class="edit-input" clearable size="mini" placeholder="请选择">
136 <el-option v-for="item in dicts" :key="item.id" :label="item.remark === '' ? item.name : item.remark" :value="item.name" />
137 </el-select>
138 </template>
139 </el-table-column>
140 </el-table>
141 </el-form>
142 </el-card>
143 </el-col>
144 <el-col>
145 <el-card class="box-card" shadow="never">
146 <div slot="header" class="clearfix">
147 <span class="role-span">生成配置</span>
148 <el-button
149 :loading="configLoading"
150 icon="el-icon-check"
151 size="mini"
152 style="float: right; padding: 6px 9px"
153 type="primary"
154 @click="doSubmit"
155 >保存</el-button>
156 </div>
157 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="78px">
158 <el-form-item label="作者名称" prop="author">
159 <el-input v-model="form.author" style="width: 40%" />
160 <span style="color: #C0C0C0;margin-left: 10px;">类上面的作者名称</span>
161 </el-form-item>
162 <el-form-item label="模块名称" prop="moduleName">
163 <el-input v-model="form.moduleName" style="width: 40%" />
164 <span style="color: #C0C0C0;margin-left: 10px;">模块的名称,请选择项目中已存在的模块</span>
165 </el-form-item>
166 <el-form-item label="至于包下" prop="pack">
167 <el-input v-model="form.pack" style="width: 40%" />
168 <span style="color: #C0C0C0;margin-left: 10px;">项目包的名称,生成的代码放到哪个包里面</span>
169 </el-form-item>
170 <el-form-item label="接口名称" prop="apiAlias">
171 <el-input v-model="form.apiAlias" style="width: 40%" />
172 <span style="color: #C0C0C0;margin-left: 10px;">接口的名称,用于控制器与接口文档中</span>
173 </el-form-item>
174 <el-form-item label="前端路径" prop="path">
175 <el-input v-model="form.path" style="width: 40%" />
176 <span style="color: #C0C0C0;margin-left: 10px;">输入views文件夹下的目录,不存在即创建</span>
177 </el-form-item>
178 <!-- <el-form-item label="接口目录">-->
179 <!-- <el-input v-model="form.apiPath" style="width: 40%" />-->
180 <!-- <span style="color: #C0C0C0;margin-left: 10px;">Api存放路径[src/api],为空则自动生成路径</span>-->
181 <!-- </el-form-item>-->
182 <el-form-item label="去表前缀" prop="prefix">
183 <el-input v-model="form.prefix" placeholder="默认不去除表前缀" style="width: 40%" />
184 <span style="color: #C0C0C0;margin-left: 10px;">默认不去除表前缀,可自定义</span>
185 </el-form-item>
186 <el-form-item label="是否覆盖" prop="cover">
187 <el-radio-group v-model="form.cover" size="mini" style="width: 40%">
188 <el-radio-button label="true"></el-radio-button>
189 <el-radio-button label="false"></el-radio-button>
190 </el-radio-group>
191 <span style="color: #C0C0C0;margin-left: 10px;">谨防误操作,请慎重选择</span>
192 </el-form-item>
193 </el-form>
194 </el-card>
195 </el-col>
196 </el-row>
197 </div>
198 </template>
199
200 <script>
201 import crud from '@/mixins/crud'
202 import { update, get } from '@/api/generator/genConfig'
203 import { save, sync, generator } from '@/api/generator/generator'
204 import { getDicts } from '@/api/system/dict'
205 export default {
206 name: 'GeneratorConfig',
207 components: {},
208 mixins: [crud],
209 data() {
210 return {
211 activeName: 'first', tableName: '', tableHeight: 550, columnLoading: false, configLoading: false, dicts: [], syncLoading: false, genLoading: false,
212 form: { id: null, tableName: '', author: '', pack: '', path: '', moduleName: '', cover: 'false', apiPath: '', prefix: '', apiAlias: null },
213 rules: {
214 author: [
215 { required: true, message: '作者不能为空', trigger: 'blur' }
216 ],
217 pack: [
218 { required: true, message: '包路径不能为空', trigger: 'blur' }
219 ],
220 moduleName: [
221 { required: true, message: '包路径不能为空', trigger: 'blur' }
222 ],
223 path: [
224 { required: true, message: '前端路径不能为空', trigger: 'blur' }
225 ],
226 apiAlias: [
227 { required: true, message: '接口名称不能为空', trigger: 'blur' }
228 ],
229 cover: [
230 { required: true, message: '不能为空', trigger: 'blur' }
231 ]
232 }
233 }
234 },
235 created() {
236 this.tableHeight = document.documentElement.clientHeight - 385
237 this.tableName = this.$route.params.tableName
238 this.$nextTick(() => {
239 this.init()
240 get(this.tableName).then(data => {
241 this.form = data
242 this.form.cover = this.form.cover.toString()
243 })
244 getDicts().then(data => {
245 this.dicts = data
246 })
247 })
248 },
249 methods: {
250 beforeInit() {
251 this.url = 'api/generator/columns'
252 const tableName = this.tableName
253 this.params = { tableName }
254 return true
255 },
256 saveColumnConfig() {
257 this.columnLoading = true
258 save(this.data).then(res => {
259 this.notify('保存成功', 'success')
260 this.columnLoading = false
261 }).catch(err => {
262 this.columnLoading = false
263 console.log(err.response.data.message)
264 })
265 },
266 doSubmit() {
267 this.$refs['form'].validate((valid) => {
268 if (valid) {
269 this.configLoading = true
270 update(this.form).then(res => {
271 this.notify('保存成功', 'success')
272 this.form = res
273 this.form.cover = this.form.cover.toString()
274 this.configLoading = false
275 }).catch(err => {
276 this.configLoading = false
277 console.log(err.response.data.message)
278 })
279 }
280 })
281 },
282 sync() {
283 this.syncLoading = true
284 sync([this.tableName]).then(() => {
285 this.init()
286 this.notify('同步成功', 'success')
287 this.syncLoading = false
288 }).then(() => {
289 this.syncLoading = false
290 })
291 },
292 toGen() {
293 this.genLoading = true
294 save(this.data).then(res => {
295 this.notify('保存成功', 'success')
296 // 生成代码
297 generator(this.tableName, 0).then(data => {
298 this.genLoading = false
299 this.notify('生成成功', 'success')
300 }).catch(err => {
301 this.genLoading = false
302 console.log(err.response.data.message)
303 })
304 }).catch(err => {
305 this.genLoading = false
306 console.log(err.response.data.message)
307 })
308 }
309 }
310 }
311 </script>
312
313 <style rel="stylesheet/scss" lang="scss">
314 .edit-input {
315 .el-input__inner {
316 border: 1px solid #e5e6e7;
317 }
318 }
319 </style>
320
321 <style scoped>
322 ::v-deep .input-with-select .el-input-group__prepend {
323 background-color: #fff;
324 }
325 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <el-input v-model="query.name" clearable size="small" placeholder="请输入表名" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
7 <rrOperation />
8 </div>
9 <crudOperation>
10 <el-tooltip slot="right" class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
11 <el-button
12 class="filter-item"
13 size="mini"
14 type="success"
15 icon="el-icon-refresh"
16 :loading="syncLoading"
17 :disabled="crud.selections.length === 0"
18 @click="sync"
19 >同步</el-button>
20 </el-tooltip>
21 </crudOperation>
22 </div>
23 <!--表格渲染-->
24 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
25 <el-table-column type="selection" width="55" />
26 <el-table-column :show-overflow-tooltip="true" prop="tableName" label="表名" />
27 <el-table-column :show-overflow-tooltip="true" prop="engine" label="数据库引擎" />
28 <el-table-column :show-overflow-tooltip="true" prop="coding" label="字符编码集" />
29 <el-table-column :show-overflow-tooltip="true" prop="remark" label="备注" />
30 <el-table-column prop="createTime" label="创建日期" />
31 <el-table-column label="操作" width="160px" align="center" fixed="right">
32 <template slot-scope="scope">
33 <el-button size="mini" style="margin-right: 2px" type="text">
34 <router-link :to="'/sys-tools/generator/preview/' + scope.row.tableName">
35 预览
36 </router-link>
37 </el-button>
38 <el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text" @click="toDownload(scope.row.tableName)">下载</el-button>
39 <el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text">
40 <router-link :to="'/sys-tools/generator/config/' + scope.row.tableName">
41 配置
42 </router-link>
43 </el-button>
44 <el-button type="text" style="margin-left: -1px" size="mini" @click="toGen(scope.row.tableName)">生成</el-button>
45 </template>
46 </el-table-column>
47 </el-table>
48 <!--分页组件-->
49 <pagination />
50 </div>
51 </template>
52
53 <script>
54
55 import { generator, sync } from '@/api/generator/generator'
56 import { downloadFile } from '@/utils/index'
57 import CRUD, { presenter, header } from '@crud/crud'
58 import rrOperation from '@crud/RR.operation'
59 import crudOperation from '@crud/CRUD.operation'
60 import pagination from '@crud/Pagination'
61
62 export default {
63 name: 'GeneratorIndex',
64 components: { pagination, crudOperation, rrOperation },
65 cruds() {
66 return CRUD({ url: 'api/generator/tables' })
67 },
68 mixins: [presenter(), header()],
69 data() {
70 return {
71 syncLoading: false
72 }
73 },
74 created() {
75 this.crud.optShow = { add: false, edit: false, del: false, download: false }
76 },
77 methods: {
78 toGen(tableName) {
79 // 生成代码
80 generator(tableName, 0).then(data => {
81 this.$notify({
82 title: '生成成功',
83 type: 'success',
84 duration: 2500
85 })
86 })
87 },
88 toDownload(tableName) {
89 // 打包下载
90 generator(tableName, 2).then(data => {
91 downloadFile(data, tableName, 'zip')
92 })
93 },
94 sync() {
95 const tables = []
96 this.crud.selections.forEach(val => {
97 tables.push(val.tableName)
98 })
99 this.syncLoading = true
100 sync(tables).then(() => {
101 this.crud.refresh()
102 this.crud.notify('同步成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
103 this.syncLoading = false
104 }).then(() => {
105 this.syncLoading = false
106 })
107 }
108 }
109 }
110 </script>
111
112 <style scoped>
113
114 </style>
1 <template>
2 <el-tabs v-model="activeName" type="card">
3 <el-tab-pane v-for="item in data" :key="item.name" :lazy="true" :label="item.name" :name="item.name">
4 <Java :value="item.content" :height="height" />
5 </el-tab-pane>
6 </el-tabs>
7 </template>
8
9 <script>
10 import Java from '@/components/JavaEdit/index'
11 import { generator } from '@/api/generator/generator'
12 export default {
13 name: 'Preview',
14 components: { Java },
15 data() {
16 return {
17 data: null, height: '', activeName: 'Entity'
18 }
19 },
20 created() {
21 this.height = document.documentElement.clientHeight - 180 + 'px'
22 const tableName = this.$route.params.tableName
23 generator(tableName, 1).then(data => {
24 this.data = data
25 }).catch(() => {
26 this.$router.go(-1)
27 })
28 }
29 }
30 </script>
1 <template>
2 <div class="dashboard-container">
3 <div class="dashboard-editor-container">
4 <github-corner class="github-corner" />
5 <panel-group @handleSetLineChartData="handleSetLineChartData" />
6 <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
7 <line-chart :chart-data="lineChartData" />
8 </el-row>
9 <el-row :gutter="32">
10 <el-col :xs="24" :sm="24" :lg="8">
11 <div class="chart-wrapper">
12 <radar-chart />
13 </div>
14 </el-col>
15 <el-col :xs="24" :sm="24" :lg="8">
16 <div class="chart-wrapper">
17 <pie-chart />
18 </div>
19 </el-col>
20 <el-col :xs="24" :sm="24" :lg="8">
21 <div class="chart-wrapper">
22 <bar-chart />
23 </div>
24 </el-col>
25 </el-row>
26 </div>
27 </div>
28 </template>
29 <script>
30 import GithubCorner from '@/components/GithubCorner'
31 import PanelGroup from './dashboard/PanelGroup'
32 import LineChart from './dashboard/LineChart'
33 import RadarChart from '@/components/Echarts/RadarChart'
34 import PieChart from '@/components/Echarts/PieChart'
35 import BarChart from '@/components/Echarts/BarChart'
36 const lineChartData = {
37 newVisitis: {
38 expectedData: [100, 120, 161, 134, 105, 160, 165],
39 actualData: [120, 82, 91, 154, 162, 140, 145]
40 },
41 messages: {
42 expectedData: [200, 192, 120, 144, 160, 130, 140],
43 actualData: [180, 160, 151, 106, 145, 150, 130]
44 },
45 purchases: {
46 expectedData: [80, 100, 121, 104, 105, 90, 100],
47 actualData: [120, 90, 100, 138, 142, 130, 130]
48 },
49 shoppings: {
50 expectedData: [130, 140, 141, 142, 145, 150, 160],
51 actualData: [120, 82, 91, 154, 162, 140, 130]
52 }
53 }
54 export default {
55 name: 'Dashboard',
56 components: {
57 GithubCorner,
58 PanelGroup,
59 LineChart,
60 RadarChart,
61 PieChart,
62 BarChart
63 },
64 data() {
65 return {
66 lineChartData: lineChartData.newVisitis
67 }
68 },
69 methods: {
70 handleSetLineChartData(type) {
71 this.lineChartData = lineChartData[type]
72 }
73 }
74 }
75 </script>
76 <style rel="stylesheet/scss" lang="scss" scoped>
77 .dashboard-editor-container {
78 padding: 32px;
79 background-color: rgb(240, 242, 245);
80 position: relative;
81 .github-corner {
82 position: absolute;
83 top: 0;
84 border: 0;
85 right: 0;
86 }
87 .chart-wrapper {
88 background: #fff;
89 padding: 16px 16px 0;
90 margin-bottom: 32px;
91 }
92 }
93 @media (max-width:1024px) {
94 .chart-wrapper {
95 padding: 8px;
96 }
97 }
98 </style>
1 <template>
2 <div class="login">
3 <index-bg />
4 <div class="loginBox">
5 <el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form">
6 <h3 class="title" style="position: relative">基础框架<span style="color: #fff;z-index: 999;font-size: 12px;position: absolute;top: 4px;right: 10px" /></h3>
7 <el-form-item prop="username">
8 <el-input v-model="loginForm.username" :validate-event="false" class="login-input" type="text" size="mini" placeholder="账号" style="width: 400px;">
9 <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
10 </el-input>
11 </el-form-item>
12 <el-form-item prop="password">
13 <el-input v-model="loginForm.password" :validate-event="false" class="login-input" type="password" size="mini" placeholder="密码" style="width: 400px;" @keyup.enter.native="handleLogin">
14 <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
15 </el-input>
16 </el-form-item>
17 <el-form-item prop="code">
18 <el-input v-model="loginForm.code" :validate-event="false" class="login-input" placeholder="验证码" size="mini" style="width: 65%" @keyup.enter.native="handleLogin">
19 <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
20 </el-input>
21 <div class="login-code">
22 <img :src="codeUrl" @click="getCode">
23 </div>
24 </el-form-item>
25 <el-checkbox v-model="loginForm.rememberMe" style="margin:0 0 25px 0;">
26 记住我
27 </el-checkbox>
28 <el-form-item style="width:100%;">
29 <el-button :loading="loading" size="medium" type="primary" style="width:100%;" @click.native.prevent="handleLogin">
30 <span v-if="!loading">登 录</span>
31 <span v-else>登 录 中...</span>
32 </el-button>
33 </el-form-item>
34 </el-form>
35 </div>
36 <!-- 底部 -->
37 <div v-if="$store.state.settings.showFooter" id="el-login-footer">
38 <span v-html="$store.state.settings.footerTxt" />
39 <span></span>
40 <a href="http://www.beian.miit.gov.cn" target="_blank">{{ $store.state.settings.caseNumber }}</a>
41 </div>
42 </div>
43 </template>
44
45 <script>
46 // import { encrypt } from '@/utils/rsaEncrypt'
47 import Config from '@/settings'
48 import { getCodeImg } from '@/api/login'
49 import Cookies from 'js-cookie'
50 // import qs from 'qs'
51 // import Background from '@/assets/images/background.jpg'
52 import IndexBg from '@/components/bg/index'
53 export default {
54 name: 'Login',
55 components: { IndexBg },
56 data() {
57 return {
58 // Background: Background,
59 codeUrl: '',
60 cookiePass: '',
61 loginForm: {
62 username: '',
63 password: '',
64 rememberMe: false,
65 code: '',
66 uuid: ''
67 },
68 loginRules: {
69 username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],
70 password: [{ required: true, trigger: 'blur', message: '密码不能为空' }],
71 code: [{ required: true, trigger: 'change', message: '验证码不能为空' }]
72 },
73 loading: false,
74 redirect: undefined
75 }
76 },
77 watch: {
78 $route: {
79 handler: function(route) {
80 this.redirect = route.query && route.query.redirect
81 },
82 immediate: true
83 }
84 },
85 created() {
86 // 获取验证码
87 this.getCode()
88 // 获取用户名密码等Cookie
89 this.getCookie()
90 // token 过期提示
91 this.point()
92 },
93 methods: {
94 clickLink() {
95 window.open('http://xk.scview.net')
96 },
97 getCode() {
98 getCodeImg().then(res => {
99 this.codeUrl = res.img
100 this.loginForm.uuid = res.uuid
101 })
102 },
103 getCookie() {
104 const username = Cookies.get('username')
105 let password = Cookies.get('password')
106 const rememberMe = Cookies.get('rememberMe')
107 // 保存cookie里面的加密后的密码
108 this.cookiePass = password === undefined ? '' : password
109 password = password === undefined ? this.loginForm.password : password
110 this.loginForm = {
111 username: username === undefined ? this.loginForm.username : username,
112 password: password,
113 rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
114 code: ''
115 }
116 },
117 handleLogin() {
118 this.$refs.loginForm.validate(valid => {
119 const user = {
120 username: this.loginForm.username,
121 password: this.loginForm.password,
122 rememberMe: this.loginForm.rememberMe,
123 code: this.loginForm.code,
124 uuid: this.loginForm.uuid
125 }
126 // if (user.password !== this.cookiePass) {
127 // user.password = encrypt(user.password)
128 // }
129 if (valid) {
130 this.loading = true
131 if (user.rememberMe) {
132 Cookies.set('username', user.username, { expires: Config.passCookieExpires })
133 Cookies.set('password', user.password, { expires: Config.passCookieExpires })
134 Cookies.set('rememberMe', user.rememberMe, { expires: Config.passCookieExpires })
135 } else {
136 Cookies.remove('username')
137 Cookies.remove('password')
138 Cookies.remove('rememberMe')
139 }
140 this.$store.dispatch('Login', user).then(() => {
141 this.loading = false
142 this.$router.push({ path: this.redirect || '/' })
143 }).catch(() => {
144 this.loading = false
145 this.getCode()
146 })
147 } else {
148 console.log('error submit!!')
149 return false
150 }
151 })
152 },
153 point() {
154 const point = Cookies.get('point') !== undefined
155 if (point) {
156 this.$notify({
157 title: '提示',
158 message: '当前登录状态已过期,请重新登录!',
159 type: 'warning',
160 duration: 5000
161 })
162 Cookies.remove('point')
163 }
164 }
165 }
166 }
167 </script>
168
169 <style rel="stylesheet/scss" lang="scss">
170 .loginBox {
171 z-index: 9;
172 margin: auto;
173 padding: 40px 20px;
174 height: 531px;
175 /*border: 1px solid #3873BC;*/
176 /*background-color: rgba(10, 27, 55, 0.8);*/
177 background-image:url('../assets/bg/bg.png');
178 position: relative;
179 /*border-radius: 8px;*/
180 }
181 .login {
182 display: flex;
183 justify-content: center;
184 align-items: center;
185 height: 100%;
186 background-color: #122547;
187 /*background-image: url(/src/assets/bg/bg-2.jpg);*/
188 /*background-size: cover;*/
189 }
190 .title {
191 margin: 0px auto 50px auto;
192 font-size: 2.2rem;
193 font-weight: normal;
194 text-align: center;
195 color: #ffffff;
196 }
197 .login-input .el-input__inner {
198 background-color: rgba(0,0,0,0.20);
199 border: none;
200 border-radius: 6px;
201 font-size: 16px;
202 color: #ffffff;
203 padding-left: 60px;
204 text-overflow: ellipsis;
205 overflow: hidden;
206 white-space: nowrap;
207 }
208 .login-input .el-input__inner:focus {
209 color: #fff;
210 }
211 .login-form {
212 position: relative;
213 /*left: 15%;*/
214 z-index: 1001;
215 border-radius: 6px;
216 background-color: transparent;
217 width: 450px;
218 margin-top: 10px;
219 padding: 25px 25px 5px 25px;
220 .el-input {
221 height: 44px;
222 input {
223 height: 44px;
224 }
225 }
226 .input-icon{
227 height: 44px;
228 width: 25px;
229 margin-left: 16px;
230 margin-right: 16px;
231 color: #1389de;
232 }
233 }
234 .el-input__inner {
235 &::placeholder {
236 color: #707C90;
237 }
238
239 &::-webkit-input-placeholder {
240 /* WebKit browsers 适配谷歌 */
241 color: #707C90;
242 }
243
244 &:-moz-placeholder {
245 /* Mozilla Firefox 4 to 18 适配火狐 */
246 color: #707C90;
247 }
248
249 &::-moz-placeholder {
250 /* Mozilla Firefox 19+ 适配火狐 */
251 color: #707C90;
252 }
253
254 &:-ms-input-placeholder {
255 /* Internet Explorer 10+ 适配ie*/
256 color: #707C90;
257 }
258 }
259 .login-tip {
260 font-size: 13px;
261 text-align: center;
262 color: #bfbfbf;
263 }
264 .login-code {
265 width: 31%;
266 display: inline-block;
267 border-radius: 6px;
268 overflow: hidden;
269 box-sizing: content-box;
270 float: right;
271 img{
272 height: 40px;
273 bottom: 12px;
274 cursor: pointer;
275 vertical-align:middle;
276 }
277 }
278 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.name" clearable placeholder="输入名称搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission">
12 <el-button
13 slot="left"
14 v-permission="['admin','app:add']"
15 :disabled="!currentRow"
16 class="filter-item"
17 size="mini"
18 type="primary"
19 icon="el-icon-plus"
20 @click="copy"
21 >复制</el-button>
22 </crudOperation>
23 </div>
24 <!--表单组件-->
25 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="800px">
26 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
27 <el-form-item label="应用名称" prop="name">
28 <el-input v-model="form.name" style="width: 670px" placeholder="部署后的文件或者目录名称,用于备份" />
29 </el-form-item>
30 <el-form-item label="应用端口" prop="port">
31 <el-input-number v-model.number="form.port" placeholder="例如:8080" />
32 </el-form-item>
33 <el-form-item label="上传目录" prop="uploadPath">
34 <el-input v-model="form.uploadPath" style="width: 670px" placeholder="例如: /opt/upload" />
35 </el-form-item>
36 <el-form-item label="部署目录" prop="deployPath">
37 <el-input v-model="form.deployPath" style="width: 670px" placeholder="例如: /opt/app" />
38 </el-form-item>
39 <el-form-item label="备份目录" prop="backupPath">
40 <el-input v-model="form.backupPath" style="width: 670px" placeholder="例如: /opt/backup" />
41 </el-form-item>
42 <el-form-item label="部署脚本" prop="deployScript">
43 <el-input v-model="form.deployScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
44 </el-form-item>
45 <el-form-item label="启动脚本" prop="startScript">
46 <el-input v-model="form.startScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
47 </el-form-item>
48 </el-form>
49 <div slot="footer" class="dialog-footer">
50 <el-button type="text" @click="crud.cancelCU">取消</el-button>
51 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
52 </div>
53 </el-dialog>
54 <!--表格渲染-->
55 <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
56 <el-table-column type="selection" width="55" />
57 <el-table-column prop="name" label="应用名称" />
58 <el-table-column prop="port" label="端口号" />
59 <el-table-column prop="uploadPath" label="上传目录" />
60 <el-table-column prop="deployPath" label="部署目录" />
61 <el-table-column prop="backupPath" label="备份目录" />
62 <el-table-column prop="createTime" label="创建日期" />
63 <el-table-column v-if="checkPer(['admin','app:edit','app:del'])" label="操作" width="150px" align="center">
64 <template slot-scope="scope">
65 <udOperation
66 :data="scope.row"
67 :permission="permission"
68 />
69 </template>
70 </el-table-column>
71 </el-table>
72 <!--分页组件-->
73 <pagination />
74 </div>
75 </template>
76
77 <script>
78 import crudApp from '@/api/mnt/app'
79 import CRUD, { presenter, header, form, crud } from '@crud/crud'
80 import rrOperation from '@crud/RR.operation'
81 import crudOperation from '@crud/CRUD.operation'
82 import udOperation from '@crud/UD.operation'
83 import pagination from '@crud/Pagination'
84 import DateRangePicker from '@/components/DateRangePicker'
85
86 const defaultForm = { id: null, name: null, port: 8080, uploadPath: '/opt/upload', deployPath: '/opt/app', backupPath: '/opt/backup', startScript: null, deployScript: null }
87 export default {
88 name: 'App',
89 components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
90 cruds() {
91 return CRUD({ title: '应用', url: 'api/app', crudMethod: { ...crudApp }})
92 },
93 mixins: [presenter(), header(), form(defaultForm), crud()],
94 data() {
95 return {
96 currentRow: null,
97 permission: {
98 add: ['admin', 'app:add'],
99 edit: ['admin', 'app:edit'],
100 del: ['admin', 'app:del']
101 },
102 rules: {
103 name: [
104 { required: true, message: '请输入应用名称', trigger: 'blur' }
105 ],
106 port: [
107 { required: true, message: '请输入应用端口', trigger: 'blur', type: 'number' }
108 ],
109 uploadPath: [
110 { required: true, message: '请输入上传目录', trigger: 'blur' }
111 ],
112 deployPath: [
113 { required: true, message: '请输入部署目录', trigger: 'blur' }
114 ],
115 backupPath: [
116 { required: true, message: '请输入备份目录', trigger: 'blur' }
117 ],
118 startScript: [
119 { required: true, message: '请输入启动脚本', trigger: 'blur' }
120 ],
121 deployScript: [
122 { required: true, message: '请输入部署脚本', trigger: 'blur' }
123 ]
124 }
125 }
126 },
127 methods: {
128 copy() {
129 for (const key in this.currentRow) {
130 this.form[key] = this.currentRow[key]
131 }
132 this.form.id = null
133 this.form.createTime = null
134 this.crud.toAdd()
135 },
136 handleCurrentChange(row) {
137 this.currentRow = JSON.parse(JSON.stringify(row))
138 }
139 }
140 }
141 </script>
142
143 <style scoped>
144 </style>
1 <template>
2 <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="执行脚本" width="400px">
3 <el-form ref="form" :rules="rules" size="small">
4 <el-upload
5 :action="databaseUploadApi"
6 :data="databaseInfo"
7 :headers="headers"
8 :on-success="handleSuccess"
9 :on-error="handleError"
10 class="upload-demo"
11 drag
12 >
13 <i class="el-icon-upload" />
14 <div class="el-upload__text">
15 将文件拖到此处,或
16 <em>点击上传</em>
17 </div>
18 <div slot="tip" class="el-upload__tip">上传后,系统会自动执行SQL脚本</div>
19 </el-upload>
20 </el-form>
21 <div slot="footer" class="dialog-footer">
22 <el-button type="primary" @click="cancel">关闭</el-button>
23 </div>
24 </el-dialog>
25 </template>
26
27 <script>
28 import { mapGetters } from 'vuex'
29 import { getToken } from '@/utils/auth'
30 export default {
31 props: {
32 databaseInfo: {
33 type: Object,
34 default() {
35 return {}
36 }
37 }
38 },
39 data() {
40 return {
41 loading: false,
42 dialog: false,
43 headers: {
44 Authorization: getToken()
45 },
46 rules: {}
47 }
48 },
49 computed: {
50 ...mapGetters(['databaseUploadApi'])
51 },
52 mounted() {
53 },
54 methods: {
55 cancel() {
56 this.dialog = false
57 },
58 handleSuccess(response, file, fileList) {
59 if (response === 'success') {
60 this.$notify({
61 title: '执行成功',
62 type: 'success',
63 duration: 2500
64 })
65 } else {
66 this.$notify({
67 title: response,
68 type: 'error',
69 duration: 0
70 })
71 }
72 },
73 handleError(e, file, fileList) {
74 const msg = JSON.parse(e.message)
75 this.$notify({
76 title: msg.message,
77 type: 'error',
78 duration: 0
79 })
80 }
81 }
82 }
83 </script>
84
85 <style scoped>
86 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.blurry" clearable placeholder="模糊搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission">
12 <el-button
13 slot="right"
14 v-permission="['admin','database:add']"
15 :disabled="!selectIndex"
16 class="filter-item"
17 size="mini"
18 type="warning"
19 icon="el-icon-upload"
20 @click="execute"
21 >执行脚本
22 </el-button>
23 </crudOperation>
24 </div>
25 <!--表单组件-->
26 <eForm ref="execute" :database-info="currentRow" />
27 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="530px">
28 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
29 <el-form-item label="连接名称" prop="name">
30 <el-input v-model="form.name" style="width: 370px" />
31 </el-form-item>
32 <el-form-item label="JDBC地址" prop="jdbcUrl">
33 <el-input v-model="form.jdbcUrl" style="width: 300px" />
34 <el-button :loading="loading" type="success" @click="testConnectDatabase">测试</el-button>
35 </el-form-item>
36 <el-form-item label="用户" prop="userName">
37 <el-input v-model="form.userName" style="width: 370px" />
38 </el-form-item>
39 <el-form-item label="密码" prop="pwd">
40 <el-input v-model="form.pwd" type="password" style="width: 370px" />
41 </el-form-item>
42 </el-form>
43 <div slot="footer" class="dialog-footer">
44 <el-button type="text" @click="crud.cancelCU">取消</el-button>
45 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
46 </div>
47 </el-dialog>
48 <!--表格渲染-->
49 <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
50 <el-table-column type="selection" width="55" />
51 <el-table-column prop="name" width="130px" label="数据库名称" />
52 <el-table-column prop="jdbcUrl" label="连接地址" />
53 <el-table-column prop="userName" width="200px" label="用户名" />
54 <el-table-column prop="createTime" width="200px" label="创建日期" />
55 <el-table-column v-if="checkPer(['admin','database:edit','database:del'])" label="操作" width="150px" align="center">
56 <template slot-scope="scope">
57 <udOperation
58 :data="scope.row"
59 :permission="permission"
60 />
61 </template>
62 </el-table-column>
63 </el-table>
64 <!--分页组件-->
65 <pagination />
66 </div>
67 </template>
68
69 <script>
70 import crudDatabase from '@/api/mnt/database'
71 import { testDbConnect } from '@/api/mnt/connect'
72 import eForm from './execute'
73 import CRUD, { presenter, header, form, crud } from '@crud/crud'
74 import rrOperation from '@crud/RR.operation'
75 import crudOperation from '@crud/CRUD.operation'
76 import udOperation from '@crud/UD.operation'
77 import pagination from '@crud/Pagination'
78 import DateRangePicker from '@/components/DateRangePicker'
79
80 const defaultForm = { id: null, name: null, jdbcUrl: 'jdbc:mysql://', userName: null, pwd: null }
81 export default {
82 name: 'DataBase',
83 components: { eForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
84 cruds() {
85 return CRUD({ title: '数据库', url: 'api/database', crudMethod: { ...crudDatabase }})
86 },
87 mixins: [presenter(), header(), form(defaultForm), crud()],
88 data() {
89 return {
90 currentRow: {},
91 selectIndex: '',
92 databaseInfo: '',
93 loading: false,
94 permission: {
95 add: ['admin', 'database:add'],
96 edit: ['admin', 'database:edit'],
97 del: ['admin', 'database:del']
98 },
99 rules: {
100 name: [
101 { required: true, message: '请输入数据库名称', trigger: 'blur' }
102 ],
103 jdbcUrl: [
104 { required: true, message: '请输入数据库连接地址', trigger: 'blur' }
105 ],
106 userName: [
107 { required: true, message: '请输入用户名', trigger: 'blur' }
108 ],
109 pwd: [
110 { required: true, message: '请输入数据库密码', trigger: 'blur' }
111 ]
112 }
113 }
114 },
115 methods: {
116 testConnectDatabase() {
117 this.$refs['form'].validate((valid) => {
118 if (valid) {
119 this.loading = true
120 testDbConnect(this.form).then((res) => {
121 this.loading = false
122 this.crud.notify(res ? '连接成功' : '连接失败', res ? 'success' : 'error')
123 }).catch(() => {
124 this.loading = false
125 })
126 }
127 })
128 },
129 execute() {
130 this.$refs.execute.dialog = true
131 },
132 handleCurrentChange(selection) {
133 this.crud.selections = selection
134 if (selection.length === 1) {
135 const row = selection[0]
136 this.selectIndex = row.id
137 this.currentRow = row
138 } else {
139 this.currentRow = {}
140 this.selectIndex = ''
141 }
142 }
143 }
144 }
145 </script>
146
147 <style scoped>
148 </style>
1 <template>
2 <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="应用部署" width="400px">
3 <el-form ref="form" :model="form" :rules="rules" size="small">
4 <el-upload
5 :action="deployUploadApi"
6 :data="deployInfo"
7 :headers="headers"
8 :on-success="handleSuccess"
9 :on-error="handleError"
10 class="upload-demo"
11 drag
12 >
13 <i class="el-icon-upload" />
14 <div class="el-upload__text">
15 将文件拖到此处,或
16 <em>点击上传</em>
17 </div>
18 <div slot="tip" class="el-upload__tip">多个应用上传文件名称为all.zip,数据库更新脚本扩展名为.sql,上传成功后系统自动部署系统。</div>
19 </el-upload>
20 </el-form>
21 <div slot="footer" class="dialog-footer">
22 <el-button type="primary" @click="cancel">关闭</el-button>
23 </div>
24 </el-dialog>
25 </template>
26
27 <script>
28 import { add, edit, getApps, getServers } from '@/api/mnt/deploy'
29 import { mapGetters } from 'vuex'
30 import { getToken } from '@/utils/auth'
31
32 export default {
33 props: {},
34 data() {
35 return {
36 loading: false,
37 dialog: false,
38 apps: [],
39 servers: [],
40 headers: {
41 Authorization: getToken()
42 },
43 deployInfo: {},
44 form: {
45 id: '',
46 appId: '',
47 ip: '',
48 selectIp: []
49 },
50 rules: {}
51 }
52 },
53 computed: {
54 ...mapGetters(['deployUploadApi'])
55 },
56 created() {
57 this.initWebSocket()
58 },
59 mounted() {
60 this.initSelect()
61 },
62 methods: {
63 cancel() {
64 this.resetForm()
65 },
66 doSubmit() {
67 this.loading = true
68 if (this.isAdd) {
69 this.doAdd()
70 } else {
71 this.doEdit()
72 }
73 },
74 joinIp() {
75 this.form.ip = ''
76 this.form.selectIp.forEach(ip => {
77 if (this.form.ip !== '') {
78 this.form.ip += ','
79 }
80 this.form.ip += ip
81 })
82 },
83 doAdd() {
84 this.joinIp()
85 add(this.form)
86 .then(res => {
87 this.resetForm()
88 this.$notify({
89 title: '添加成功',
90 type: 'success',
91 duration: 2500
92 })
93 this.loading = false
94 this.$parent.init()
95 })
96 .catch(err => {
97 this.loading = false
98 console.log(err.response.data.message)
99 })
100 },
101 doEdit() {
102 this.joinIp()
103 edit(this.form)
104 .then(res => {
105 this.resetForm()
106 this.$notify({
107 title: '修改成功',
108 type: 'success',
109 duration: 2500
110 })
111 this.loading = false
112 this.$parent.init()
113 })
114 .catch(err => {
115 this.loading = false
116 console.log(err.response.data.message)
117 })
118 },
119 resetForm() {
120 this.dialog = false
121 this.$refs['form'].resetFields()
122 this.form = {
123 id: '',
124 appId: '',
125 ip: '',
126 selectIp: []
127 }
128 },
129 initSelect() {
130 getApps().then(res => {
131 this.apps = res.content
132 })
133 getServers().then(res => {
134 this.servers = res.content
135 })
136 },
137 handleSuccess(response, file, fileList) {
138 this.cancel()
139 },
140 // 监听上传失败
141 handleError(e, file, fileList) {
142 const msg = JSON.parse(e.message)
143 this.$notify({
144 title: msg.message,
145 type: 'error',
146 duration: 2500
147 })
148 },
149 initWebSocket() {
150 const wsUri = process.env.VUE_APP_WS_API + '/webSocket/deploy'
151 this.websock = new WebSocket(wsUri)
152 this.websock.onerror = this.webSocketOnError
153 this.websock.onmessage = this.webSocketOnMessage
154 },
155 webSocketOnError(e) {
156 this.$notify({
157 title: 'WebSocket连接发生错误',
158 type: 'error',
159 duration: 0
160 })
161 },
162 webSocketOnMessage(e) {
163 const data = JSON.parse(e.data)
164 if (data.msgType === 'INFO') {
165 this.$notify({
166 title: '',
167 message: data.msg,
168 type: 'success',
169 dangerouslyUseHTMLString: true,
170 duration: 5500
171 })
172 } else if (data.msgType === 'ERROR') {
173 this.$notify({
174 title: '',
175 message: data.msg,
176 dangerouslyUseHTMLString: true,
177 type: 'error',
178 duration: 0
179 })
180 }
181 },
182 webSocketSend(agentData) {
183 this.websock.send(agentData)
184 }
185 }
186 }
187 </script>
188
189 <style scoped>
190 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.appName" clearable placeholder="输入应用名称查询" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission">
12 <template slot="right">
13 <el-button
14 v-permission="['admin','deploy:add']"
15 :disabled="!selectIndex"
16 class="filter-item"
17 size="mini"
18 type="primary"
19 icon="el-icon-upload"
20 @click="sysRestore"
21 >系统还原
22 </el-button>
23 <el-button
24 v-permission="['admin','deploy:add']"
25 :disabled="!selectIndex"
26 class="filter-item"
27 size="mini"
28 type="primary"
29 icon="el-icon-upload"
30 @click="serverStatus"
31 >状态查询
32 </el-button>
33 <el-button
34 v-permission="['admin','deploy:add']"
35 :disabled="!selectIndex"
36 class="filter-item"
37 size="mini"
38 type="success"
39 icon="el-icon-upload"
40 @click="startServer"
41 >启动
42 </el-button>
43 <el-button
44 v-permission="['admin','deploy:add']"
45 :disabled="!selectIndex"
46 class="filter-item"
47 size="mini"
48 type="danger"
49 icon="el-icon-upload"
50 @click="stopServer"
51 >停止
52 </el-button>
53 <el-button
54 v-permission="['admin','deploy:add']"
55 :disabled="!selectIndex"
56 class="filter-item"
57 size="mini"
58 type="warning"
59 icon="el-icon-upload"
60 @click="deploy"
61 >一键部署
62 </el-button>
63 </template>
64 </crudOperation>
65 </div>
66 <!--表单组件-->
67 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
68 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
69 <el-form-item label="应用" prop="app.id">
70 <el-select v-model.number="form.app.id" placeholder="请选择" style="width: 370px">
71 <el-option v-for="item in apps" :key="item.id" :label="item.name" :value="item.id" />
72 </el-select>
73 </el-form-item>
74 <el-form-item label="服务器" prop="deploys">
75 <el-select v-model="form.deploys" multiple placeholder="请选择" style="width: 370px">
76 <el-option v-for="item in servers" :key="item.id" :label="item.name" :value="item.id" />
77 </el-select>
78 </el-form-item>
79 </el-form>
80 <div slot="footer" class="dialog-footer">
81 <el-button type="text" @click="crud.cancelCU">取消</el-button>
82 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
83 </div>
84 </el-dialog>
85 <!--统还原组件-->
86 <fForm ref="sysRestore" :key="times" :app-name="appName" />
87 <dForm ref="deploy" />
88 <!--表格渲染-->
89 <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
90 <el-table-column type="selection" width="55" />
91 <el-table-column prop="app.name" label="应用名称" />
92 <el-table-column prop="servers" label="服务器列表" />
93 <el-table-column prop="createTime" label="部署日期" />
94 <el-table-column v-if="checkPer(['admin','deploy:edit','deploy:del'])" label="操作" width="150px" align="center">
95 <template slot-scope="scope">
96 <udOperation
97 :data="scope.row"
98 :permission="permission"
99 />
100 </template>
101 </el-table-column>
102 </el-table>
103 <!--分页组件-->
104 <pagination />
105 </div>
106 </template>
107
108 <script>
109 import crudDeploy from '@/api/mnt/deploy'
110 import dForm from './deploy'
111 import fForm from './sysRestore'
112 import CRUD, { presenter, header, form, crud } from '@crud/crud'
113 import rrOperation from '@crud/RR.operation'
114 import crudOperation from '@crud/CRUD.operation'
115 import udOperation from '@crud/UD.operation'
116 import pagination from '@crud/Pagination'
117 import DateRangePicker from '@/components/DateRangePicker'
118
119 const defaultForm = { id: null, app: { id: null }, deploys: [] }
120 export default {
121 name: 'Deploy',
122 components: { dForm, fForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
123 cruds() {
124 return CRUD({ title: '部署', url: 'api/deploy', crudMethod: { ...crudDeploy }})
125 },
126 mixins: [presenter(), header(), form(defaultForm), crud()],
127 data() {
128 return {
129 currentRow: {}, selectIndex: '', appName: '', urlHistory: '',
130 times: 0, appId: '', deployId: '', apps: [], servers: [],
131 permission: {
132 add: ['admin', 'deploy:add'],
133 edit: ['admin', 'deploy:edit'],
134 del: ['admin', 'deploy:del']
135 },
136 rules: {
137 'app.id': [
138 { required: true, message: '应用不能为空', trigger: 'blur', type: 'number' }
139 ],
140 deploys: [
141 { required: true, message: '服务器不能为空', trigger: 'blur' }
142 ]
143 }
144 }
145 },
146 methods: {
147 [CRUD.HOOK.beforeRefresh]() {
148 this.selectIndex = ''
149 return true
150 },
151 // 新增编辑前做的操作
152 [CRUD.HOOK.beforeToCU](crud, form) {
153 this.initSelect()
154 const deploys = []
155 form.deploys.forEach(function(deploy, index) {
156 deploys.push(deploy.id)
157 })
158 this.form.deploys = deploys
159 },
160 // 提交前
161 [CRUD.HOOK.beforeSubmit]() {
162 const deploys = []
163 this.form.deploys.forEach(function(data, index) {
164 const deploy = { id: data }
165 deploys.push(deploy)
166 })
167 this.form.deploys = deploys
168 return true
169 },
170 deploy() {
171 this.$refs.deploy.dialog = true
172 this.$refs.deploy.deployInfo = this.currentRow
173 },
174 sysRestore() {
175 this.$refs.sysRestore.dialog = true
176 },
177 handleCurrentChange(selection) {
178 this.crud.selections = selection
179 if (selection.length === 1) {
180 const row = selection[0]
181 this.selectIndex = row.id
182 this.currentRow = row
183 this.appName = row.app.name
184 this.times = this.times + 1
185 this.appId = row.appId
186 this.deployId = row.id
187 } else {
188 this.currentRow = {}
189 this.selectIndex = ''
190 }
191 },
192 startServer() {
193 crudDeploy.startServer(JSON.stringify(this.currentRow))
194 .then(res => {
195 })
196 .catch(err => {
197 console.log('error:' + err.response.data.message)
198 })
199 },
200 stopServer() {
201 crudDeploy.stopServer(JSON.stringify(this.currentRow))
202 .then(res => {
203 })
204 .catch(err => {
205 console.log('error:' + err.response.data.message)
206 })
207 },
208 serverStatus() {
209 crudDeploy.serverStatus(JSON.stringify(this.currentRow))
210 .then(res => {
211 })
212 .catch(err => {
213 console.log('error:' + err.response.data.message)
214 })
215 },
216 initSelect() {
217 crudDeploy.getApps().then(res => {
218 this.apps = res.content
219 })
220 crudDeploy.getServers().then(res => {
221 this.servers = res.content
222 })
223 }
224 }
225 }
226 </script>
227
228 <style scoped>
229 </style>
1 <template>
2 <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="系统还原" width="800px">
3 <!--工具栏-->
4 <div class="head-container">
5 <date-range-picker v-model="query.createTime" class="date-item" />
6 <el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
7 </div>
8 <el-form size="small" label-width="80px">
9 <!--表格渲染-->
10 <el-table v-loading="loading" :data="data" style="width: 100%" @row-click="showRow">
11 <el-table-column width="30px">
12 <template slot-scope="scope">
13 <el-radio v-model="radio" :label="scope.$index" />
14 </template>
15 </el-table-column>
16 <el-table-column prop="appName" label="应用名称" />
17 <el-table-column prop="ip" label="部署IP" />
18 <el-table-column prop="deployDate" label="部署时间" />
19 <el-table-column prop="deployUser" label="部署人员" />
20 </el-table>
21 </el-form>
22 <div slot="footer" class="dialog-footer">
23 <el-button type="text" @click="cancel">取消</el-button>
24 <el-button v-permission="['admin','deploy:add']" :loading="submitLoading" type="primary" @click="doSubmit">确认</el-button>
25 </div>
26 <!--分页组件-->
27 <el-pagination
28 :total="total"
29 :current-page="page + 1"
30 style="margin-top: 8px"
31 layout="total, prev, pager, next, sizes"
32 @size-change="sizeChange"
33 @current-change="pageChange"
34 />
35 </el-dialog>
36 </template>
37
38 <script>
39 import crud from '@/mixins/crud'
40 import { reducte } from '@/api/mnt/deployHistory'
41 import DateRangePicker from '@/components/DateRangePicker'
42 export default {
43 components: { DateRangePicker },
44 mixins: [crud],
45 props: {
46 appName: {
47 type: String,
48 default: ''
49 }
50 },
51 data() {
52 return {
53 submitLoading: false,
54 dialog: false,
55 history: [],
56 radio: '',
57 appNames: '',
58 selectIndex: ''
59 }
60 },
61 created() {
62 this.$nextTick(() => {
63 this.init()
64 })
65 },
66 methods: {
67 beforeInit() {
68 this.url = 'api/deployHistory'
69 this.deployId = this.$parent.deployId
70 if (this.deployId === '') {
71 return false
72 }
73 this.sort = 'deployDate,desc'
74 this.params['deployId'] = this.deployId
75 return true
76 },
77 showRow(row) {
78 this.radio = this.data.indexOf(row)
79 this.selectIndex = row.id
80 },
81 cancel() {
82 this.dialog = false
83 this.submitLoading = false
84 },
85 doSubmit() {
86 if (this.selectIndex === '') {
87 this.$message.error('请选择要还原的备份')
88 } else {
89 this.submitLoading = true
90 reducte(JSON.stringify(this.data[this.radio]))
91 .then(res => {
92 this.dialog = false
93 this.submitLoading = false
94 this.appNames = ''
95 this.$parent.crud.toQuery()
96 })
97 .catch(err => {
98 this.submitLoading = false
99 console.log('error:' + err.response.data.message)
100 })
101 }
102 }
103 }
104 }
105 </script>
106
107 <style scoped>
108 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.blurry" clearable placeholder="输入搜索内容" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.deployDate" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission" />
12 </div>
13 <!--表格渲染-->
14 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
15 <el-table-column type="selection" width="55" />
16 <el-table-column prop="appName" label="应用名称" />
17 <el-table-column prop="ip" label="部署IP" />
18 <el-table-column prop="deployUser" label="部署人员" />
19 <el-table-column prop="deployDate" label="部署时间" />
20 <el-table-column v-if="checkPer(['admin','deployHistory:del'])" label="操作" width="100px" align="center">
21 <template slot-scope="scope">
22 <el-popover
23 :ref="scope.row.id"
24 v-permission="['admin','deployHistory:del']"
25 placement="top"
26 width="180"
27 >
28 <p>确定删除本条数据吗?</p>
29 <div style="text-align: right; margin: 0">
30 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
31 <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
32 </div>
33 <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
34 </el-popover>
35 </template>
36 </el-table-column>
37 </el-table>
38 <!--分页组件-->
39 <pagination />
40 </div>
41 </template>
42
43 <script>
44 import { del } from '@/api/mnt/deployHistory'
45 import CRUD, { presenter, header } from '@crud/crud'
46 import rrOperation from '@crud/RR.operation'
47 import crudOperation from '@crud/CRUD.operation'
48 import pagination from '@crud/Pagination'
49 import DateRangePicker from '@/components/DateRangePicker'
50
51 export default {
52 name: 'DeployHistory',
53 components: { pagination, crudOperation, rrOperation, DateRangePicker },
54 cruds() {
55 return CRUD({ title: '部署历史', url: 'api/deployHistory', crudMethod: { del }})
56 },
57 mixins: [presenter(), header()],
58 data() {
59 return {
60 delLoading: false,
61 permission: {
62 del: ['admin', 'deployHistory:del']
63 }
64 }
65 },
66 created() {
67 this.crud.optShow = {
68 add: false,
69 edit: false,
70 del: true,
71 download: true
72 }
73 },
74 methods: {
75 delMethod(id) {
76 this.delLoading = true
77 del([id]).then(() => {
78 this.delLoading = false
79 this.$refs[id].doClose()
80 this.crud.dleChangePage(1)
81 this.crud.delSuccessNotify()
82 this.crud.toQuery()
83 }).catch(() => {
84 this.delLoading = false
85 this.$refs[id].doClose()
86 })
87 }
88 }
89 }
90 </script>
91
92 <style scoped>
93 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.id" clearable placeholder="输入名称或IP搜索" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission" />
12 </div>
13 <!--表单组件-->
14 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="470px">
15 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="55px">
16 <el-form-item label="名称" prop="name">
17 <el-input v-model="form.name" style="width: 370px" />
18 </el-form-item>
19 <el-form-item label="IP" prop="ip">
20 <el-input v-model="form.ip" style="width: 370px" />
21 </el-form-item>
22 <el-form-item label="端口" prop="port">
23 <el-input-number v-model.number="form.port" controls-position="right" style="width: 370px;" />
24 </el-form-item>
25 <el-form-item label="账号" prop="account">
26 <el-input v-model="form.account" style="width: 370px" />
27 </el-form-item>
28 <el-form-item label="密码" prop="password">
29 <el-input v-model="form.password" type="password" style="width: 200px" />
30 <el-button :loading="loading" type="success" style="align: right;" @click="testConnectServer">测试连接</el-button>
31 </el-form-item>
32 </el-form>
33 <div slot="footer" class="dialog-footer">
34 <el-button type="text" @click="crud.cancelCU">取消</el-button>
35 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
36 </div>
37 </el-dialog>
38 <!--表格渲染-->
39 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%" @selection-change="crud.selectionChangeHandler">
40 <el-table-column type="selection" width="55" />
41 <el-table-column prop="name" label="名称" />
42 <el-table-column prop="ip" label="IP" />
43 <el-table-column prop="port" label="端口" />
44 <el-table-column prop="account" label="账号" />
45 <el-table-column prop="createTime" label="创建日期" />
46 <el-table-column v-if="checkPer(['admin','serverDeploy:edit','serverDeploy:del'])" label="操作" width="150px" align="center">
47 <template slot-scope="scope">
48 <udOperation
49 :data="scope.row"
50 :permission="permission"
51 />
52 </template>
53 </el-table-column>
54 </el-table>
55 <!--分页组件-->
56 <pagination />
57 </div>
58 </template>
59
60 <script>
61
62 import crudServer from '@/api/mnt/serverDeploy'
63 import { testServerConnect } from '@/api/mnt/connect'
64 import { validateIP } from '@/utils/validate'
65 import CRUD, { presenter, header, form, crud } from '@crud/crud'
66 import rrOperation from '@crud/RR.operation'
67 import crudOperation from '@crud/CRUD.operation'
68 import udOperation from '@crud/UD.operation'
69 import pagination from '@crud/Pagination'
70 import DateRangePicker from '@/components/DateRangePicker'
71
72 const defaultForm = { id: null, name: null, ip: null, port: 22, account: 'root', password: null }
73 export default {
74 name: 'Server',
75 components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
76 cruds() {
77 return CRUD({ title: '服务器', url: 'api/serverDeploy', crudMethod: { ...crudServer }})
78 },
79 mixins: [presenter(), header(), form(defaultForm), crud()],
80 data() {
81 return {
82 accountList: [],
83 accountMap: {},
84 loading: false,
85 permission: {
86 add: ['admin', 'serverDeploy:add'],
87 edit: ['admin', 'serverDeploy:edit'],
88 del: ['admin', 'serverDeploy:del']
89 },
90 rules: {
91 name: [
92 { required: true, message: '请输入名称', trigger: 'blur' }
93 ],
94 ip: [
95 { required: true, message: '请输入IP', trigger: 'blur' },
96 { validator: validateIP, trigger: 'change' }
97 ],
98 port: [
99 { required: true, message: '请输入端口', trigger: 'blur', type: 'number' }
100 ],
101 account: [
102 { required: true, message: '请输入账号', trigger: 'blur' }
103 ],
104 password: [
105 { required: true, message: '请输入密码', trigger: 'blur' }
106 ]
107 }
108 }
109 },
110 methods: {
111 testConnectServer() {
112 this.$refs['form'].validate((valid) => {
113 if (valid) {
114 this.loading = true
115 testServerConnect(this.form).then((res) => {
116 this.loading = false
117 this.$notify({
118 title: res ? '连接成功' : '连接失败',
119 type: res ? 'success' : 'error',
120 duration: 2500
121 })
122 }).catch(() => {
123 this.loading = false
124 })
125 }
126 })
127 }
128 }
129 }
130 </script>
131
132 <style rel="stylesheet/scss" lang="scss" scoped>
133 ::v-deep .el-input-number .el-input__inner {
134 text-align: left;
135 }
136 </style>
1 <template>
2 <div class="app-container">
3 <div class="head-container">
4 <Search />
5 <crudOperation>
6 <el-button
7 slot="left"
8 class="filter-item"
9 type="danger"
10 icon="el-icon-delete"
11 size="mini"
12 :loading="crud.delAllLoading"
13 @click="confirmDelAll()"
14 >
15 清空
16 </el-button>
17 </crudOperation>
18 </div>
19 <!--表格渲染-->
20 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
21 <el-table-column type="expand">
22 <template slot-scope="props">
23 <el-form label-position="left" inline class="demo-table-expand">
24 <el-form-item label="请求方法">
25 <span>{{ props.row.method }}</span>
26 </el-form-item>
27 <el-form-item label="请求参数">
28 <span>{{ props.row.params }}</span>
29 </el-form-item>
30 </el-form>
31 </template>
32 </el-table-column>
33 <el-table-column prop="username" label="用户名" />
34 <el-table-column prop="requestIp" label="IP" />
35 <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
36 <el-table-column prop="description" label="描述" />
37 <el-table-column prop="browser" label="浏览器" />
38 <el-table-column prop="createTime" label="创建日期" />
39 <el-table-column label="异常详情" width="100px">
40 <template slot-scope="scope">
41 <el-button size="mini" type="text" @click="info(scope.row.id)">查看详情</el-button>
42 </template>
43 </el-table-column>
44 </el-table>
45 <el-dialog :visible.sync="dialog" title="异常详情" append-to-body top="30px" width="85%">
46 <pre v-highlightjs="errorInfo"><code class="java" /></pre>
47 </el-dialog>
48 <!--分页组件-->
49 <pagination />
50 </div>
51 </template>
52
53 <script>
54 import { getErrDetail, delAllError } from '@/api/monitor/log'
55 import Search from './search'
56 import CRUD, { presenter } from '@crud/crud'
57 import crudOperation from '@crud/CRUD.operation'
58 import pagination from '@crud/Pagination'
59
60 export default {
61 name: 'ErrorLog',
62 components: { Search, crudOperation, pagination },
63 cruds() {
64 return CRUD({ title: '异常日志', url: 'api/logs/error' })
65 },
66 mixins: [presenter()],
67 data() {
68 return {
69 errorInfo: '', dialog: false
70 }
71 },
72 created() {
73 this.crud.optShow = {
74 add: false,
75 edit: false,
76 del: false,
77 download: true
78 }
79 },
80 methods: {
81 // 获取异常详情
82 info(id) {
83 this.dialog = true
84 getErrDetail(id).then(res => {
85 this.errorInfo = res.exception
86 })
87 },
88 confirmDelAll() {
89 this.$confirm(`确认清空所有异常日志吗?`, '提示', {
90 confirmButtonText: '确定',
91 cancelButtonText: '取消',
92 type: 'warning'
93 }).then(() => {
94 this.crud.delAllLoading = true
95 delAllError().then(res => {
96 this.crud.delAllLoading = false
97 this.crud.dleChangePage(1)
98 this.crud.delSuccessNotify()
99 this.crud.toQuery()
100 }).catch(err => {
101 this.crud.delAllLoading = false
102 console.log(err.response.data.message)
103 })
104 }).catch(() => {
105 })
106 }
107 }
108 }
109 </script>
110
111 <style scoped>
112 .demo-table-expand {
113 font-size: 0;
114 }
115 .demo-table-expand label {
116 width: 70px;
117 color: #99a9bf;
118 }
119 .demo-table-expand .el-form-item {
120 margin-right: 0;
121 margin-bottom: 0;
122 width: 100%;
123 }
124 .demo-table-expand .el-form-item__content {
125 font-size: 12px;
126 }
127 /deep/ .el-dialog__body {
128 padding: 0 20px 10px 20px !important;
129 }
130 .java.hljs {
131 color: #444;
132 background: #ffffff !important;
133 height: 630px !important;
134 }
135 </style>
1 <template>
2 <div class="app-container">
3 <div class="head-container">
4 <Search />
5 <crudOperation>
6 <el-button
7 slot="left"
8 class="filter-item"
9 type="danger"
10 icon="el-icon-delete"
11 size="mini"
12 :loading="crud.delAllLoading"
13 @click="confirmDelAll()"
14 >
15 清空
16 </el-button>
17 </crudOperation>
18 </div>
19 <!--表格渲染-->
20 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
21 <el-table-column type="expand">
22 <template slot-scope="props">
23 <el-form label-position="left" inline class="demo-table-expand">
24 <el-form-item label="请求方法">
25 <span>{{ props.row.method }}</span>
26 </el-form-item>
27 <el-form-item label="请求参数">
28 <span>{{ props.row.params }}</span>
29 </el-form-item>
30 </el-form>
31 </template>
32 </el-table-column>
33 <el-table-column prop="username" label="用户名" />
34 <el-table-column prop="requestIp" label="IP" />
35 <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
36 <el-table-column prop="description" label="描述" />
37 <el-table-column prop="browser" label="浏览器" />
38 <el-table-column prop="time" label="请求耗时" align="center">
39 <template slot-scope="scope">
40 <el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
41 <el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
42 <el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
43 </template>
44 </el-table-column>
45 <el-table-column prop="createTime" label="创建日期" width="180px" />
46 </el-table>
47 <!--分页组件-->
48 <pagination />
49 </div>
50 </template>
51
52 <script>
53 import Search from './search'
54 import { delAllInfo } from '@/api/monitor/log'
55 import CRUD, { presenter } from '@crud/crud'
56 import crudOperation from '@crud/CRUD.operation'
57 import pagination from '@crud/Pagination'
58
59 export default {
60 name: 'Log',
61 components: { Search, crudOperation, pagination },
62 cruds() {
63 return CRUD({ title: '日志', url: 'api/logs' })
64 },
65 mixins: [presenter()],
66 created() {
67 this.crud.optShow = {
68 add: false,
69 edit: false,
70 del: false,
71 download: true
72 }
73 },
74 methods: {
75 confirmDelAll() {
76 this.$confirm(`确认清空所有操作日志吗?`, '提示', {
77 confirmButtonText: '确定',
78 cancelButtonText: '取消',
79 type: 'warning'
80 }).then(() => {
81 this.crud.delAllLoading = true
82 delAllInfo().then(res => {
83 this.crud.delAllLoading = false
84 this.crud.dleChangePage(1)
85 this.crud.delSuccessNotify()
86 this.crud.toQuery()
87 }).catch(err => {
88 this.crud.delAllLoading = false
89 console.log(err.response.data.message)
90 })
91 }).catch(() => {
92 })
93 }
94 }
95 }
96 </script>
97
98 <style>
99 .demo-table-expand {
100 font-size: 0;
101 }
102 .demo-table-expand label {
103 width: 70px;
104 color: #99a9bf;
105 }
106 .demo-table-expand .el-form-item {
107 margin-right: 0;
108 margin-bottom: 0;
109 width: 100%;
110 }
111 .demo-table-expand .el-form-item__content {
112 font-size: 12px;
113 }
114 </style>
1 <template>
2 <div v-if="crud.props.searchToggle">
3 <el-input
4 v-model="query.blurry"
5 clearable
6 size="small"
7 placeholder="请输入你要搜索的内容"
8 style="width: 200px;"
9 class="filter-item"
10 />
11 <date-range-picker v-model="query.createTime" class="date-item" />
12 <rrOperation />
13 </div>
14 </template>
15
16 <script>
17 import { header } from '@crud/crud'
18 import rrOperation from '@crud/RR.operation'
19 import DateRangePicker from '@/components/DateRangePicker'
20 export default {
21 components: { rrOperation, DateRangePicker },
22 mixins: [header()]
23 }
24 </script>
1 <template>
2 <div class="app-container">
3 <div class="head-container">
4 <div v-if="crud.props.searchToggle">
5 <el-input v-model="query.filter" clearable size="small" placeholder="全表模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
6 <rrOperation />
7 </div>
8 <crudOperation>
9 <el-button
10 slot="left"
11 class="filter-item"
12 type="danger"
13 icon="el-icon-delete"
14 size="mini"
15 :loading="delLoading"
16 :disabled="crud.selections.length === 0"
17 @click="doDelete(crud.selections)"
18 >
19 强退
20 </el-button>
21 </crudOperation>
22 </div>
23 <!--表格渲染-->
24 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
25 <el-table-column type="selection" width="55" />
26 <el-table-column prop="userName" label="用户名" />
27 <el-table-column prop="nickName" label="用户昵称" />
28 <el-table-column prop="dept" label="部门" />
29 <el-table-column prop="ip" label="登录IP" />
30 <el-table-column :show-overflow-tooltip="true" prop="address" label="登录地点" />
31 <el-table-column prop="browser" label="浏览器" />
32 <el-table-column prop="loginTime" label="登录时间" />
33 <el-table-column label="操作" width="70px" fixed="right">
34 <template slot-scope="scope">
35 <el-popover
36 :ref="scope.$index"
37 v-permission="['admin']"
38 placement="top"
39 width="180"
40 >
41 <p>确定强制退出该用户吗?</p>
42 <div style="text-align: right; margin: 0">
43 <el-button size="mini" type="text" @click="$refs[scope.$index].doClose()">取消</el-button>
44 <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.key, scope.$index)">确定</el-button>
45 </div>
46 <el-button slot="reference" size="mini" type="text">强退</el-button>
47 </el-popover>
48 </template>
49 </el-table-column>
50 </el-table>
51 <!--分页组件-->
52 <pagination />
53 </div>
54 </template>
55
56 <script>
57 import { del } from '@/api/monitor/online'
58 import CRUD, { presenter, header, crud } from '@crud/crud'
59 import rrOperation from '@crud/RR.operation'
60 import crudOperation from '@crud/CRUD.operation'
61 import pagination from '@crud/Pagination'
62
63 export default {
64 name: 'OnlineUser',
65 components: { pagination, crudOperation, rrOperation },
66 cruds() {
67 return CRUD({ url: 'auth/online', title: '在线用户' })
68 },
69 mixins: [presenter(), header(), crud()],
70 data() {
71 return {
72 delLoading: false,
73 permission: {}
74 }
75 },
76 created() {
77 this.crud.msg.del = '强退成功!'
78 this.crud.optShow = {
79 add: false,
80 edit: false,
81 del: false,
82 download: true
83 }
84 },
85 methods: {
86 doDelete(datas) {
87 this.$confirm(`确认强退选中的${datas.length}个用户?`, '提示', {
88 confirmButtonText: '确定',
89 cancelButtonText: '取消',
90 type: 'warning'
91 }).then(() => {
92 this.delMethod(datas)
93 }).catch(() => {})
94 },
95 // 踢出用户
96 delMethod(key, index) {
97 const ids = []
98 if (key instanceof Array) {
99 key.forEach(val => {
100 ids.push(val.key)
101 })
102 } else ids.push(key)
103 this.delLoading = true
104 del(ids).then(() => {
105 this.delLoading = false
106 if (this.$refs[index]) {
107 this.$refs[index].doClose()
108 }
109 this.crud.dleChangePage(1)
110 this.crud.delSuccessNotify()
111 this.crud.toQuery()
112 }).catch(() => {
113 this.delLoading = false
114 if (this.$refs[index]) {
115 this.$refs[index].doClose()
116 }
117 })
118 }
119 }
120 }
121 </script>
1 <template>
2 <div v-loading="!show" element-loading-text="数据加载中..." :style="!show ? 'height: 500px' : 'height: 100%'" class="app-container">
3 <div v-if="show">
4 <el-card class="box-card">
5 <div style="color: #666;font-size: 13px;">
6 <svg-icon icon-class="system" style="margin-right: 5px" />
7 <span>
8 系统:{{ data.sys.os }}
9 </span>
10 <span>
11 IP:{{ data.sys.ip }}
12 </span>
13 <span>
14 项目已不间断运行:{{ data.sys.day }}
15 </span>
16 <i class="el-icon-refresh" style="margin-left: 40px" @click="init" />
17 </div>
18 </el-card>
19 <el-card class="box-card">
20 <div slot="header" class="clearfix">
21 <span style="font-weight: bold;color: #666;font-size: 15px">状态</span>
22 </div>
23 <div>
24 <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
25 <div class="title">CPU使用率</div>
26 <el-tooltip placement="top-end">
27 <div slot="content" style="font-size: 12px;">
28 <div style="padding: 3px;">
29 {{ data.cpu.name }}
30 </div>
31 <div style="padding: 3px">
32 {{ data.cpu.package }}
33 </div>
34 <div style="padding: 3px">
35 {{ data.cpu.core }}
36 </div>
37 <div style="padding: 3px">
38 {{ data.cpu.logic }}
39 </div>
40 </div>
41 <div class="content">
42 <el-progress type="dashboard" :percentage="parseFloat(data.cpu.used)" />
43 </div>
44 </el-tooltip>
45 <div class="footer">{{ data.cpu.coreNumber }} 核心</div>
46 </el-col>
47 <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
48 <div class="title">内存使用率</div>
49 <el-tooltip placement="top-end">
50 <div slot="content" style="font-size: 12px;">
51 <div style="padding: 3px;">
52 总量:{{ data.memory.total }}
53 </div>
54 <div style="padding: 3px">
55 已使用:{{ data.memory.used }}
56 </div>
57 <div style="padding: 3px">
58 空闲:{{ data.memory.available }}
59 </div>
60 </div>
61 <div class="content">
62 <el-progress type="dashboard" :percentage="parseFloat(data.memory.usageRate)" />
63 </div>
64 </el-tooltip>
65 <div class="footer">{{ data.memory.used }} / {{ data.memory.total }}</div>
66 </el-col>
67 <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
68 <div class="title">交换区使用率</div>
69 <el-tooltip placement="top-end">
70 <div slot="content" style="font-size: 12px;">
71 <div style="padding: 3px;">
72 总量:{{ data.swap.total }}
73 </div>
74 <div style="padding: 3px">
75 已使用:{{ data.swap.used }}
76 </div>
77 <div style="padding: 3px">
78 空闲:{{ data.swap.available }}
79 </div>
80 </div>
81 <div class="content">
82 <el-progress type="dashboard" :percentage="parseFloat(data.swap.usageRate)" />
83 </div>
84 </el-tooltip>
85 <div class="footer">{{ data.swap.used }} / {{ data.swap.total }}</div>
86 </el-col>
87 <el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px">
88 <div class="title">磁盘使用率</div>
89 <div class="content">
90 <el-tooltip placement="top-end">
91 <div slot="content" style="font-size: 12px;">
92 <div style="padding: 3px">
93 总量:{{ data.disk.total }}
94 </div>
95 <div style="padding: 3px">
96 空闲:{{ data.disk.available }}
97 </div>
98 </div>
99 <div class="content">
100 <el-progress type="dashboard" :percentage="parseFloat(data.disk.usageRate)" />
101 </div>
102 </el-tooltip>
103 </div>
104 <div class="footer">{{ data.disk.used }} / {{ data.disk.total }}</div>
105 </el-col>
106 </div>
107 </el-card>
108
109 <div>
110 <el-row :gutter="6">
111 <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
112 <el-card class="box-card">
113 <div slot="header" class="clearfix">
114 <span style="font-weight: bold;color: #666;font-size: 15px">CPU使用率监控</span>
115 </div>
116 <div>
117 <v-chart :options="cpuInfo" />
118 </div>
119 </el-card>
120 </el-col>
121 <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
122 <el-card class="box-card">
123 <div slot="header" class="clearfix">
124 <span style="font-weight: bold;color: #666;font-size: 15px">内存使用率监控</span>
125 </div>
126 <div>
127 <v-chart :options="memoryInfo" />
128 </div>
129 </el-card>
130 </el-col>
131 </el-row>
132 </div>
133 </div>
134 </div>
135 </template>
136
137 <script>
138 import ECharts from 'vue-echarts'
139 import 'echarts/lib/chart/line'
140 import 'echarts/lib/component/polar'
141 import { initData } from '@/api/data'
142 export default {
143 name: 'ServerMonitor',
144 components: {
145 'v-chart': ECharts
146 },
147 data() {
148 return {
149 show: false,
150 monitor: null,
151 url: 'api/monitor',
152 data: {},
153 cpuInfo: {
154 tooltip: {
155 trigger: 'axis'
156 },
157 xAxis: {
158 type: 'category',
159 boundaryGap: false,
160 data: []
161 },
162 yAxis: {
163 type: 'value',
164 min: 0,
165 max: 100,
166 interval: 20
167 },
168 series: [{
169 data: [],
170 type: 'line',
171 areaStyle: {
172 normal: {
173 color: 'rgb(32, 160, 255)' // 改变区域颜色
174 }
175 },
176 itemStyle: {
177 normal: {
178 color: '#6fbae1',
179 lineStyle: {
180 color: '#6fbae1' // 改变折线颜色
181 }
182 }
183 }
184 }]
185 },
186 memoryInfo: {
187 tooltip: {
188 trigger: 'axis'
189 },
190 xAxis: {
191 type: 'category',
192 boundaryGap: false,
193 data: []
194 },
195 yAxis: {
196 type: 'value',
197 min: 0,
198 max: 100,
199 interval: 20
200 },
201 series: [{
202 data: [],
203 type: 'line',
204 areaStyle: {
205 normal: {
206 color: 'rgb(32, 160, 255)' // 改变区域颜色
207 }
208 },
209 itemStyle: {
210 normal: {
211 color: '#6fbae1',
212 lineStyle: {
213 color: '#6fbae1' // 改变折线颜色
214 }
215 }
216 }
217 }]
218 }
219 }
220 },
221 created() {
222 this.init()
223 this.monitor = window.setInterval(() => {
224 setTimeout(() => {
225 this.init()
226 }, 2)
227 }, 3500)
228 },
229 destroyed() {
230 clearInterval(this.monitor)
231 },
232 methods: {
233 init() {
234 initData(this.url, {}).then(data => {
235 this.data = data
236 this.show = true
237 if (this.cpuInfo.xAxis.data.length >= 8) {
238 this.cpuInfo.xAxis.data.shift()
239 this.memoryInfo.xAxis.data.shift()
240 this.cpuInfo.series[0].data.shift()
241 this.memoryInfo.series[0].data.shift()
242 }
243 this.cpuInfo.xAxis.data.push(data.time)
244 this.memoryInfo.xAxis.data.push(data.time)
245 this.cpuInfo.series[0].data.push(parseFloat(data.cpu.used))
246 this.memoryInfo.series[0].data.push(parseFloat(data.memory.usageRate))
247 })
248 }
249 }
250 }
251 </script>
252
253 <style rel="stylesheet/scss" lang="scss" scoped>
254 ::v-deep .box-card {
255 margin-bottom: 5px;
256 span {
257 margin-right: 28px;
258 }
259 .el-icon-refresh {
260 margin-right: 10px;
261 float: right;
262 cursor:pointer;
263 }
264 }
265 .cpu, .memory, .swap, .disk {
266 width: 20%;
267 float: left;
268 padding-bottom: 20px;
269 margin-right: 5%;
270 }
271 .title {
272 text-align: center;
273 font-size: 15px;
274 font-weight: 500;
275 color: #999;
276 margin-bottom: 16px;
277 }
278 .footer {
279 text-align: center;
280 font-size: 15px;
281 font-weight: 500;
282 color: #999;
283 margin-top: -5px;
284 margin-bottom: 10px;
285 }
286 .content {
287 text-align: center;
288 margin-top: 5px;
289 margin-bottom: 5px;
290 }
291 </style>
1 <template>
2 <elFrame :src="sqlApi" />
3 </template>
4 <script>
5 import { mapGetters } from 'vuex'
6 import elFrame from '@/components/Iframe/index'
7 export default {
8 name: 'Sql',
9 components: { elFrame },
10 computed: {
11 ...mapGetters([
12 'sqlApi'
13 ])
14 }
15 }
16 </script>
1 <template>
2 <div class="app-container">
3 <el-alert :closable="false" title="三级菜单1" type="success" />
4 <el-form label-width="170px" style="margin-top: 20px">
5 <el-form-item label="三级菜单缓存功能测试区">
6 <el-input v-model="input" placeholder="请输入内容" style="width: 360px;" />
7 </el-form-item>
8 </el-form>
9 <div>
10 <blockquote class="my-blockquote"> 三级菜单缓存配置教程</blockquote>
11 <pre class="my-code">
12 1、将前后端代码更新为最新版版本,或对照提交记录修改,点击查看-> <a href="https://gitee.com/elunez/eladmin/commit/43d1a63577f9d5347924355708429a2d210e29f7" target="_blank">提交(1)</a><a href="https://gitee.com/elunez/eladmin/commit/46393875148fcca5eaa327d4073f72edb3752f5c" target="_blank">提交(2)</a><a href="https://gitee.com/elunez/eladmin-web/commit/c93c99d8921abbb2c52afc806635f5ca08d6bda8" target="_blank">提交(3)</a>
13 2、将 二级菜单 的 菜单类型 设置为 目录 级别,并且原有的 组件路径 需要清空
14 3、将 三级菜单 的 菜单缓存 设置为 是,最后将 组件名称 填写正确
15 4、具体设置可参考 菜单管理 的 多级菜单 配置进行进行相应的修改
16 </pre>
17 <blockquote class="my-blockquote">更多帮助</blockquote>
18 <pre class="my-code">QQ交流群:一群:891137268、二群:947578238</pre>
19 </div>
20 </div>
21 </template>
22 <script>
23 export default {
24 name: 'Test',
25 data() {
26 return {
27 input: ''
28 }
29 }
30 }
31 </script>
32 <style scoped>
33 .my-code a{
34 color:#009688;
35 }
36 </style>
1 <template>
2 <div style="padding:30px;">
3 <el-alert :closable="false" title="三级菜单2" type="success" />
4 </div>
5 </template>
1 <template>
2 <div style="padding:30px;">
3 <el-alert :closable="false" title="二级菜单" />
4 </div>
5 </template>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
10 <el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
11 </el-select>
12 <rrOperation>
13 <el-button
14 slot="sright"
15 :loading="downloadLoading"
16 size="mini"
17 class="filter-item"
18 type="warning"
19 icon="el-icon-download"
20 @click="download"
21 >导出</el-button>
22 </rrOperation>
23 </div>
24 <crudOperation :permission="permission" />
25 </div>
26 <!--表单组件-->
27 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
28 <el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
29 <el-form-item label="部门名称" prop="name">
30 <el-input v-model="form.name" style="width: 370px;" />
31 </el-form-item>
32 <!-- <el-form-item label="部门排序" prop="sort">-->
33 <!-- <el-input-number-->
34 <!-- v-model.number="form.sort"-->
35 <!-- :min="0"-->
36 <!-- :max="999"-->
37 <!-- controls-position="right"-->
38 <!-- style="width: 370px;"-->
39 <!-- />-->
40 <!-- </el-form-item>-->
41 <!-- <el-form-item label="顶级部门">-->
42 <!-- <el-radio-group v-model="form.isTop" style="width: 140px">-->
43 <!-- <el-radio label="1"></el-radio>-->
44 <!-- <el-radio label="0"></el-radio>-->
45 <!-- </el-radio-group>-->
46 <!-- </el-form-item>-->
47 <el-form-item label="状态" prop="enabled">
48 <el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
49 </el-form-item>
50 <el-form-item style="margin-bottom: 0;" label="上级部门" prop="pid">
51 <treeselect
52 v-model="form.pid"
53 :load-options="loadDepts"
54 :options="depts"
55 style="width: 370px;"
56 placeholder="选择上级类目"
57 />
58 </el-form-item>
59 </el-form>
60 <div slot="footer" class="dialog-footer">
61 <el-button type="text" @click="crud.cancelCU">取消</el-button>
62 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
63 </div>
64 </el-dialog>
65 <!--表格渲染-->
66 <el-table
67 ref="table"
68 v-loading="crud.loading"
69 size="small"
70 :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
71 :default-expand-all="true"
72 :data="crud.data"
73 row-key="id"
74 >
75 <el-table-column :selectable="checkboxT" type="selection" width="55" />
76 <el-table-column label="名称" prop="name" />
77 <el-table-column label="排序" prop="sort" />
78 <el-table-column label="状态" align="center" prop="enabled">
79 <template slot-scope="scope">
80 <el-switch
81 v-model="scope.row.enabled"
82 :disabled="scope.row.id === 1"
83 active-color="#409EFF"
84 inactive-color="#F56C6C"
85 @change="changeEnabled(scope.row, scope.row.enabled,)"
86 />
87 </template>
88 </el-table-column>
89 <el-table-column prop="createTime" label="创建日期">
90 <template slot-scope="scope">
91 <span>{{ parseTime(scope.row.createTime) }}</span>
92 </template>
93 </el-table-column>
94 <el-table-column v-permission="['admin','dept:edit','dept:del']" label="操作" width="130px" align="center" fixed="right">
95 <template slot-scope="scope">
96 <udOperation
97 :data="scope.row"
98 :permission="permission"
99 :disabled-dle="scope.row.id === 1"
100 :operation="{ edit: true, del: false }"
101 >
102 <el-popover
103 slot="footer"
104 :ref="scope.row.id"
105 v-permission="['admin','job:del']"
106 placement="top"
107 width="180"
108 >
109 <p>确定删除本条数据吗?</p>
110 <div style="text-align: right; margin: 0">
111 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
112 <el-button :loading="delLoading" type="primary" size="mini" @click="subDelete(scope.row.id)">确定</el-button>
113 </div>
114 <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
115 </el-popover>
116 </udOperation>
117 </template>
118 </el-table-column>
119 </el-table>
120 </div>
121 </template>
122
123 <script>
124 import crudDept from '@/api/system/dept'
125 import Treeselect from '@riophae/vue-treeselect'
126 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
127 import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
128 import CRUD, { presenter, header, form, crud } from '@crud/crud'
129 import rrOperation from '@crud/RR.operation'
130 import crudOperation from '@crud/CRUD.operation'
131 import udOperation from '@crud/UD.operation'
132 import DateRangePicker from '@/components/DateRangePicker'
133 import { del, downloadDept } from '@/api/system/dept'
134 import { downloadFile } from '@/utils/index'
135
136 const defaultForm = { id: '', name: '', pid: '', enabled: 'true' }
137 export default {
138 name: 'Dept',
139 components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
140 cruds() {
141 return CRUD({ title: '部门', url: 'api/dept', crudMethod: { ...crudDept }, optShow: { add: true, edit: false, del: false, reset: false }})
142 },
143 mixins: [presenter(), header(), form(defaultForm), crud()],
144 // 设置数据字典
145 dicts: ['dept_status'],
146 data() {
147 return {
148 depts: [],
149 query: {},
150 form: {},
151 delLoading: false,
152 downloadLoading: false,
153 rules: {
154 name: [
155 { required: true, message: '请输入名称', trigger: 'blur' }
156 ],
157 sort: [
158 { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
159 ]
160 },
161 permission: {
162 add: ['admin', 'dept:add'],
163 edit: ['admin', 'dept:edit'],
164 del: ['admin', 'dept:del']
165 },
166 enabledTypeOptions: [
167 { key: 'true', display_name: '正常' },
168 { key: 'false', display_name: '禁用' }
169 ]
170 }
171 },
172 methods: {
173 getDeptDatas(tree, treeNode, resolve) {
174 const params = { pid: tree.id }
175 setTimeout(() => {
176 crudDept.getDepts(params).then(res => {
177 resolve(res.content)
178 })
179 }, 100)
180 },
181 subDelete(id) {
182 this.delLoading = true
183 del(id).then(res => {
184 this.delLoading = false
185 this.$refs[id].doClose()
186 this.crud.toQuery()
187 this.$notify({
188 title: '删除成功',
189 type: 'success',
190 duration: 2500
191 })
192 }).catch(err => {
193 this.delLoading = false
194 this.$refs[id].doClose()
195 console.log(err.response.data.message)
196 })
197 },
198 download() {
199 this.crud.toQuery()
200 this.downloadLoading = true
201 downloadDept(this.crud.params).then(result => {
202 downloadFile(result, '部门列表', 'xlsx')
203 this.downloadLoading = false
204 }).catch(() => {
205 this.downloadLoading = false
206 })
207 },
208 [CRUD.HOOK.beforeValidateCU](crud) {
209 crud.form = this.form
210 return true
211 },
212 [CRUD.HOOK.beforeRefresh]() {
213 const query = this.query
214 if (query.name) {
215 this.crud.params.name = query.name
216 } else {
217 this.crud.params.name = ''
218 }
219 if (query.enabled) {
220 this.crud.params.enabled = query.enabled
221 } else {
222 this.crud.params.enabled = ''
223 }
224 if (query.createTime) {
225 this.crud.params.startTime = query.createTime[0]
226 this.crud.params.endTime = query.createTime[1]
227 } else {
228 this.crud.params.startTime = ''
229 this.crud.params.endTime = ''
230 }
231 return true
232 },
233 [CRUD.HOOK.beforeToAdd]() {
234 this.form = {
235 id: '',
236 name: '',
237 pid: '',
238 createTime: '',
239 enabled: ''
240 }
241 this.dialog = true
242 },
243 // 新增与编辑前做的操作
244 [CRUD.HOOK.afterToCU](crud, form) {
245 form.enabled = `${form.enabled}`
246 this.form = {
247 id: form.id,
248 name: form.name,
249 pid: form.pid,
250 createTime: form.createTime,
251 enabled: form.enabled.toString()
252 }
253 this.getDepts()
254 },
255 getSupDepts(id) {
256 crudDept.getDeptSuperior(id).then(res => {
257 const date = res.content
258 this.buildDepts(date)
259 this.depts = date
260 })
261 },
262 buildDepts(depts) {
263 depts.forEach(data => {
264 if (data.children) {
265 this.buildDepts(data.children)
266 }
267 if (data.hasChildren && !data.children) {
268 data.children = null
269 }
270 })
271 },
272 getDepts() {
273 crudDept.getDepts({ enabled: true }).then(res => {
274 this.depts = res.content.map(function(obj) {
275 if (obj.hasChildren) {
276 obj.children = null
277 }
278 return obj
279 })
280 })
281 },
282 // 获取弹窗内部门数据
283 loadDepts({ action, parentNode, callback }) {
284 if (action === LOAD_CHILDREN_OPTIONS) {
285 crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
286 parentNode.children = res.content.map(function(obj) {
287 if (obj.hasChildren) {
288 obj.children = null
289 }
290 return obj
291 })
292 setTimeout(() => {
293 callback()
294 }, 100)
295 })
296 }
297 },
298 // 提交前的验证
299 [CRUD.HOOK.afterValidateCU]() {
300 if (this.form.pid !== null && this.form.pid === this.form.id) {
301 this.$message({
302 message: '上级部门不能为空',
303 type: 'warning'
304 })
305 return false
306 }
307 if (this.form.isTop === '1') {
308 this.form.pid = null
309 }
310 return true
311 },
312 // 改变状态
313 changeEnabled(data, val) {
314 this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
315 confirmButtonText: '确定',
316 cancelButtonText: '取消',
317 type: 'warning'
318 }).then(() => {
319 crudDept.edit(data).then(res => {
320 this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
321 }).catch(err => {
322 data.enabled = !data.enabled
323 console.log(err.response.data.message)
324 })
325 }).catch(() => {
326 data.enabled = !data.enabled
327 })
328 },
329 checkboxT(row, rowIndex) {
330 return row.id !== 1
331 }
332 }
333 }
334 </script>
335
336 <style rel="stylesheet/scss" lang="scss" scoped>
337 ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
338 height: 30px;
339 line-height: 30px;
340 }
341 </style>
342 <style rel="stylesheet/scss" lang="scss" scoped>
343 ::v-deep .el-input-number .el-input__inner {
344 text-align: left;
345 }
346 </style>
1 <template>
2 <div>
3 <div v-if="query.dictName === ''">
4 <div class="my-code">点击字典查看详情</div>
5 </div>
6 <div v-else>
7 <!--工具栏-->
8 <div class="head-container">
9 <div v-if="crud.props.searchToggle">
10 <!-- 搜索 -->
11 <el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
12 <rrOperation />
13 </div>
14 </div>
15 <!--表单组件-->
16 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
17 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
18 <el-form-item label="字典标签" prop="label">
19 <el-input v-model="form.label" style="width: 370px;" />
20 </el-form-item>
21 <el-form-item label="字典值" prop="value">
22 <el-input v-model="form.value" style="width: 370px;" />
23 </el-form-item>
24 <el-form-item label="排序" prop="dictSort">
25 <el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
26 </el-form-item>
27 </el-form>
28 <div slot="footer" class="dialog-footer">
29 <el-button type="text" @click="crud.cancelCU">取消</el-button>
30 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
31 </div>
32 </el-dialog>
33 <!--表格渲染-->
34 <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
35 <el-table-column label="所属字典">
36 {{ query.dictName }}
37 </el-table-column>
38 <el-table-column prop="label" label="字典标签" />
39 <el-table-column prop="value" label="字典值" />
40 <el-table-column prop="dictSort" label="排序" />
41 <el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
42 <template slot-scope="scope">
43 <udOperation
44 :data="scope.row"
45 :permission="permission"
46 />
47 </template>
48 </el-table-column>
49 </el-table>
50 <!--分页组件-->
51 <pagination />
52 </div>
53 </div>
54 </template>
55
56 <script>
57 import crudDictDetail from '@/api/system/dictDetail'
58 import CRUD, { presenter, header, form } from '@crud/crud'
59 import pagination from '@crud/Pagination'
60 import rrOperation from '@crud/RR.operation'
61 import udOperation from '@crud/UD.operation'
62
63 const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
64
65 export default {
66 components: { pagination, rrOperation, udOperation },
67 cruds() {
68 return [
69 CRUD({ title: '字典详情', url: 'api/dictDetail', query: { dictName: '' }, sort: ['id,desc'],
70 crudMethod: { ...crudDictDetail },
71 optShow: {
72 add: true,
73 edit: true,
74 del: true,
75 reset: false
76 },
77 queryOnPresenterCreated: false
78 })
79 ]
80 },
81 mixins: [
82 presenter(),
83 header(),
84 form(function() {
85 return Object.assign({ dict: { id: this.dictId }}, defaultForm)
86 })],
87 data() {
88 return {
89 dictId: null,
90 rules: {
91 label: [
92 { required: true, message: '请输入字典标签', trigger: 'blur' }
93 ],
94 value: [
95 { required: true, message: '请输入字典值', trigger: 'blur' }
96 ],
97 dictSort: [
98 { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
99 ]
100 },
101 permission: {
102 add: ['admin', 'dict:add'],
103 edit: ['admin', 'dict:edit'],
104 del: ['admin', 'dict:del']
105 }
106 }
107 }
108 }
109 </script>
110
111 <style rel="stylesheet/scss" lang="scss" scoped>
112 ::v-deep .el-input-number .el-input__inner {
113 text-align: left;
114 }
115 </style>
1 <template>
2 <div class="app-container">
3 <!--表单组件-->
4 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
5 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
6 <el-form-item label="字典名称" prop="name">
7 <el-input v-model="form.name" style="width: 370px;" />
8 </el-form-item>
9 <el-form-item label="描述">
10 <el-input v-model="form.remark" style="width: 370px;" />
11 </el-form-item>
12 </el-form>
13 <div slot="footer" class="dialog-footer">
14 <el-button type="text" @click="crud.cancelCU">取消</el-button>
15 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
16 </div>
17 </el-dialog>
18 <!-- 字典列表 -->
19 <el-row :gutter="10">
20 <el-col :xs="24" :sm="24" :md="10" :lg="11" :xl="11" style="margin-bottom: 10px">
21 <el-card class="box-card">
22 <!--工具栏-->
23 <div class="head-container">
24 <div v-if="crud.props.searchToggle">
25 <!-- 搜索 -->
26 <el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
27 <rrOperation />
28 </div>
29 <crudOperation :permission="permission" />
30 </div>
31 <!--表格渲染-->
32 <el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
33 <el-table-column type="selection" width="55" />
34 <el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
35 <el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" />
36 <el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
37 <template slot-scope="scope">
38 <udOperation
39 :data="scope.row"
40 :permission="permission"
41 :operation="{ del: false, edit: true }"
42 >
43 <el-popover
44 slot="footer"
45 :ref="scope.row.id"
46 v-permission="['admin','dict:del']"
47 placement="top"
48 width="180"
49 >
50 <p>此操作将删除字典与对应的字典详情,确定要删除吗?</p>
51 <div style="text-align: right; margin: 0">
52 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
53 <el-button :loading="delLoading" type="primary" size="mini" @click="subDelete(scope.row.id)">确定</el-button>
54 </div>
55 <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
56 </el-popover>
57 </udOperation>
58 </template>
59 </el-table-column>
60 </el-table>
61 <!--分页组件-->
62 <pagination />
63 </el-card>
64 </el-col>
65 <!-- 字典详情列表 -->
66 <el-col :xs="24" :sm="24" :md="14" :lg="13" :xl="13">
67 <el-card class="box-card">
68 <div slot="header" class="clearfix">
69 <span>字典详情</span>
70 <el-button
71 v-if="checkPer(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.query.dictName"
72 class="filter-item"
73 size="mini"
74 style="float: right;padding: 4px 10px"
75 type="primary"
76 icon="el-icon-plus"
77 @click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
78 >新增</el-button>
79 </div>
80 <dictDetail ref="dictDetail" :permission="permission" />
81 </el-card>
82 </el-col>
83 </el-row>
84 </div>
85 </template>
86
87 <script>
88 import dictDetail from './dictDetail'
89 import crudDict from '@/api/system/dict'
90 import { del } from '@/api/system/dict'
91 import CRUD, { presenter, header, form } from '@crud/crud'
92 import crudOperation from '@crud/CRUD.operation'
93 import pagination from '@crud/Pagination'
94 import rrOperation from '@crud/RR.operation'
95 import udOperation from '@crud/UD.operation'
96
97 const defaultForm = { id: null, name: null, remark: null, dictDetails: [] }
98
99 export default {
100 name: 'Dict',
101 components: { crudOperation, pagination, rrOperation, udOperation, dictDetail },
102 cruds() {
103 return [
104 CRUD({ title: '字典', url: 'api/dict', crudMethod: { ...crudDict }, optShow: { add: true, edit: false, del: false, reset: false }})
105 ]
106 },
107 mixins: [presenter(), header(), form(defaultForm)],
108 data() {
109 return {
110 queryTypeOptions: [
111 { key: 'name', display_name: '字典名称' },
112 { key: 'remark', display_name: '描述' }
113 ],
114 rules: {
115 name: [
116 { required: true, message: '请输入名称', trigger: 'blur' }
117 ]
118 },
119 delLoading: false,
120 permission: {
121 add: ['admin', 'dict:add'],
122 edit: ['admin', 'dict:edit'],
123 del: ['admin', 'dict:del']
124 }
125 }
126 },
127 methods: {
128 // 获取数据前设置好接口地址
129 [CRUD.HOOK.beforeRefresh]() {
130 if (this.$refs.dictDetail) {
131 this.$refs.dictDetail.query.dictName = ''
132 }
133 return true
134 },
135 // 选中字典后,设置字典详情数据
136 handleCurrentChange(val) {
137 if (val) {
138 this.$refs.dictDetail.query.dictName = val.name
139 this.$refs.dictDetail.dictId = val.id
140 this.$refs.dictDetail.crud.toQuery()
141 }
142 },
143 subDelete(id) {
144 this.delLoading = true
145 del(id).then(res => {
146 this.delLoading = false
147 this.$refs[id].doClose()
148 this.crud.toQuery()
149 this.$notify({
150 title: '删除成功',
151 type: 'success',
152 duration: 2500
153 })
154 }).catch(err => {
155 this.delLoading = false
156 this.$refs[id].doClose()
157 console.log(err.response.data.message)
158 })
159 },
160 // 编辑前将字典明细临时清空,避免日志入库数据过长
161 [CRUD.HOOK.beforeToEdit](crud, form) {
162 // 将角色的菜单清空,避免日志入库数据过长
163 form.dictDetails = null
164 }
165 }
166 }
167 </script>
168
169 <style scoped>
170 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <eHeader :dict="dict" :permission="permission" />
6 <crudOperation :permission="permission" />
7 </div>
8 <!--表格渲染-->
9 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
10 <el-table-column type="selection" width="55" />
11 <el-table-column prop="name" label="名称" />
12 <el-table-column prop="sort" label="排序">
13 <template slot-scope="scope">
14 {{ scope.row.sort }}
15 </template>
16 </el-table-column>
17 <el-table-column prop="status" label="状态" align="center">
18 <template slot-scope="scope">
19 <el-switch
20 v-model="scope.row.enabled"
21 active-color="#409EFF"
22 inactive-color="#F56C6C"
23 @change="changeEnabled(scope.row, scope.row.enabled)"
24 />
25 </template>
26 </el-table-column>
27 <el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期">
28 <template slot-scope="scope">
29 <span>{{ parseTime(scope.row.createTime) }}</span>
30 </template>
31 </el-table-column>
32 <!-- 编辑与删除 -->
33 <el-table-column
34 v-if="checkPer(['admin','job:edit','job:del'])"
35 label="操作"
36 width="130px"
37 align="center"
38 fixed="right"
39 >
40 <template slot-scope="scope">
41 <udOperation
42 :data="scope.row"
43 :permission="permission"
44 />
45 </template>
46 </el-table-column>
47 </el-table>
48 <!--分页组件-->
49 <pagination />
50 <!--表单渲染-->
51 <eForm :job-status="dict.job_status" />
52 </div>
53 </template>
54
55 <script>
56 import crudJob from '@/api/system/job'
57 import eHeader from './module/header'
58 import eForm from './module/form'
59 import CRUD, { presenter } from '@crud/crud'
60 import crudOperation from '@crud/CRUD.operation'
61 import pagination from '@crud/Pagination'
62 import udOperation from '@crud/UD.operation'
63 import { parseTime } from '@/utils/index'
64 export default {
65 name: 'Job',
66 components: { eHeader, eForm, crudOperation, pagination, udOperation },
67 cruds() {
68 return CRUD({
69 title: '岗位',
70 url: 'api/job',
71 sort: ['sort,asc', 'id,desc'],
72 crudMethod: { ...crudJob },
73 optShow: { add: true, edit: false, del: false, reset: false }
74 })
75 },
76 mixins: [presenter()],
77 // 数据字典
78 dicts: ['job_status'],
79 data() {
80 return {
81 permission: {
82 add: ['admin', 'job:add'],
83 edit: ['admin', 'job:edit'],
84 del: ['admin', 'job:del']
85 }
86 }
87 },
88 methods: {
89 parseTime,
90 // 改变状态
91 changeEnabled(data, val) {
92 this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', {
93 confirmButtonText: '确定',
94 cancelButtonText: '取消',
95 type: 'warning'
96 }).then(() => {
97 // eslint-disable-next-line no-undef
98 crudJob.edit(data).then(() => {
99 // eslint-disable-next-line no-undef
100 this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
101 }).catch(err => {
102 data.enabled = !data.enabled
103 console.log(err.data.message)
104 })
105 }).catch(() => {
106 data.enabled = !data.enabled
107 })
108 }
109 }
110 }
111 </script>
112
113 <style rel="stylesheet/scss" lang="scss" scoped>
114 ::v-deep .el-input-number .el-input__inner {
115 text-align: left;
116 }
117 </style>
1 <template>
2 <el-dialog
3 append-to-body
4 :close-on-click-modal="false"
5 :before-close="crud.cancelCU"
6 :visible="crud.status.cu > 0"
7 :title="crud.status.title"
8 width="500px"
9 >
10 <el-form
11 ref="form"
12 :model="form"
13 :rules="rules"
14 size="small"
15 label-width="80px"
16 >
17 <el-form-item
18 label="名称"
19 prop="name"
20 >
21 <el-input
22 v-model="form.name"
23 style="width: 370px;"
24 />
25 </el-form-item>
26 <el-form-item
27 label="排序"
28 prop="jobSort"
29 >
30 <el-input-number
31 v-model.number="form.sort"
32 :min="0"
33 :max="999"
34 controls-position="right"
35 style="width: 370px;"
36 />
37 </el-form-item>
38 <el-form-item
39 v-if="form.pid !== 0"
40 label="状态"
41 prop="enabled"
42 >
43 <el-radio
44 v-for="item in jobStatus"
45 :key="item.id"
46 v-model="form.enabled"
47 :label="item.value === 'true'"
48 >
49 {{ item.label }}
50 </el-radio>
51 </el-form-item>
52 <el-form-item label="所属部门">
53 <treeselect v-model="deptId" :options="depts" style="width: 370px" placeholder="选择部门" />
54 </el-form-item>
55 </el-form>
56 <div
57 slot="footer"
58 class="dialog-footer"
59 >
60 <el-button
61 type="text"
62 @click="crud.cancelCU"
63 >
64 取消
65 </el-button>
66 <el-button
67 :loading="crud.status.cu === 2"
68 type="primary"
69 @click="crud.submitCU"
70 >
71 确认
72 </el-button>
73 </div>
74 </el-dialog>
75 </template>
76
77 <script>
78 import CRUD, { form } from '@crud/crud'
79 import Treeselect from '@riophae/vue-treeselect'
80 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
81 import { getDepts } from '@/api/system/dept'
82 const defaultForm = {
83 id: null,
84 name: '',
85 jobSort: 999,
86 enabled: true
87 }
88 export default {
89 components: { Treeselect },
90 mixins: [form(defaultForm)],
91 props: {
92 jobStatus: {
93 type: Array,
94 required: true
95 }
96 },
97 data() {
98 return {
99 deptId: null,
100 depts: [],
101 form: {
102 createTime: '',
103 id: '',
104 name: '',
105 sort: null,
106 pid: '',
107 enabled: true,
108 dept: {
109 id: ''
110 }
111 },
112 rules: {
113 name: [
114 { required: true, message: '请输入名称', trigger: 'blur' }
115 ],
116 sort: [
117 { required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
118 ]
119 }
120 }
121 },
122 mounted() {
123 this.getDepts()
124 },
125 methods: {
126 getDepts() {
127 getDepts({ enabled: true }).then(res => {
128 this.depts = res.content
129 })
130 },
131 [CRUD.HOOK.afterValidateCU](crud) {
132 this.form.dept.id = this.deptId
133 if (this.deptId === null || this.deptId === undefined) {
134 this.$message({
135 message: '所属部门不能为空',
136 type: 'warning'
137 })
138 return false
139 }
140 crud.form = this.form
141 return true
142 }
143 }
144 }
145 </script>
146
147 <style rel="stylesheet/scss" lang="scss" scoped>
148 ::v-deep .el-input-number .el-input__inner {
149 text-align: left;
150 }
151 </style>
1 <template>
2 <div
3 v-if="crud.props.searchToggle"
4 >
5 <el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
6 <date-range-picker v-model="query.createTime" class="date-item" />
7 <el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
8 <el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
9 </el-select>
10 <rrOperation />
11 </div>
12 </template>
13
14 <script>
15 import { header } from '@crud/crud'
16 import CRUD from '@crud/crud'
17 import rrOperation from '@crud/RR.operation'
18 import DateRangePicker from '@/components/DateRangePicker'
19 import { downloadJob } from '@/api/system/job'
20 import { downloadFile } from '@/utils/index'
21 export default {
22 components: { rrOperation, DateRangePicker },
23 mixins: [header()],
24 props: {
25 dict: {
26 type: Object,
27 required: true
28 },
29 permission: {
30 type: Object,
31 required: true
32 }
33 },
34 methods: {
35 download() {
36 this.crud.toQuery()
37 this.downloadLoading = true
38 downloadJob(this.crud.params).then(result => {
39 downloadFile(result, '岗位列表', 'xlsx')
40 this.downloadLoading = false
41 }).catch(() => {
42 this.downloadLoading = false
43 })
44 },
45 [CRUD.HOOK.beforeRefresh]() {
46 const query = this.query
47 if (query.name) {
48 this.crud.params.name = query.name
49 } else {
50 this.crud.params.name = ''
51 }
52 if (query.enabled) {
53 this.crud.params.enabled = query.enabled
54 } else {
55 this.crud.params.enabled = ''
56 }
57 if (query.createTime) {
58 this.crud.params.startTime = query.createTime[0]
59 this.crud.params.endTime = query.createTime[1]
60 } else {
61 this.crud.params.startTime = ''
62 this.crud.params.endTime = ''
63 }
64 return true
65 }
66 }
67 }
68 </script>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission" />
12 </div>
13 <!--表单渲染-->
14 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px">
15 <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
16 <el-form-item label="菜单类型">
17 <el-radio-group v-model="form.type" size="mini" style="width: 178px">
18 <el-radio-button label="0">目录</el-radio-button>
19 <el-radio-button label="1">菜单</el-radio-button>
20 <el-radio-button label="2">按钮</el-radio-button>
21 </el-radio-group>
22 </el-form-item>
23 <el-form-item v-show="form.type.toString() !== '2'" label="菜单图标">
24 <el-popover
25 placement="bottom-start"
26 width="450"
27 trigger="click"
28 @show="$refs['iconSelect'].reset()"
29 >
30 <IconSelect ref="iconSelect" @selected="selected" />
31 <el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly>
32 <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
33 <i v-else slot="prefix" class="el-icon-search el-input__icon" />
34 </el-input>
35 </el-popover>
36 </el-form-item>
37 <el-form-item v-show="form.type.toString() !== '2'" label="外链菜单">
38 <el-radio-group v-model="form.iframe" size="mini">
39 <el-radio-button label="true"></el-radio-button>
40 <el-radio-button label="false"></el-radio-button>
41 </el-radio-group>
42 </el-form-item>
43 <el-form-item v-show="form.type.toString() === '1'" label="菜单缓存">
44 <el-radio-group v-model="form.cache" size="mini">
45 <el-radio-button label="true"></el-radio-button>
46 <el-radio-button label="false"></el-radio-button>
47 </el-radio-group>
48 </el-form-item>
49 <el-form-item v-show="form.type.toString() !== '2'" label="菜单可见">
50 <el-radio-group v-model="form.hidden" size="mini">
51 <el-radio-button label="false"></el-radio-button>
52 <el-radio-button label="true"></el-radio-button>
53 </el-radio-group>
54 </el-form-item>
55 <el-form-item v-show="form.type.toString() !== '2'" label="菜单标题" prop="name">
56 <el-input v-model="form.name" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" />
57 </el-form-item>
58 <el-form-item v-show="form.type.toString() === '2'" label="按钮名称">
59 <el-input v-model="form.name" placeholder="按钮名称" style="width: 178px;" />
60 </el-form-item>
61 <el-form-item v-show="form.type.toString() !== '0'" label="权限标识">
62 <el-input v-model="form.permission" :disabled="form.iframe === 'true'" placeholder="权限标识" style="width: 178px;" />
63 </el-form-item>
64 <el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
65 <el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
66 </el-form-item>
67 <el-form-item label="菜单排序">
68 <el-input-number v-model.number="form.sort" :min="0" :max="999" controls-position="right" style="width: 178px;" />
69 </el-form-item>
70 <el-form-item v-show="form.iframe === 'false' && Number(form.type) === 1" label="组件名称">
71 <el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" />
72 </el-form-item>
73 <el-form-item v-show="form.iframe === 'false' && Number(form.type) === 1" label="组件路径">
74 <el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" />
75 </el-form-item>
76 <el-form-item label="上级类目">
77 <treeselect v-model="form.pid" :options="menus" style="width: 450px;" placeholder="选择上级类目" />
78 </el-form-item>
79 </el-form>
80 <div slot="footer" class="dialog-footer">
81 <el-button type="text" @click="crud.cancelCU">取消</el-button>
82 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
83 </div>
84 </el-dialog>
85 <!--表格渲染-->
86 <el-table
87 v-loading="crud.loading"
88 row-key="id"
89 :data="crud.data"
90 :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
91 >
92 <el-table-column type="selection" width="55" />
93 <el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="name" />
94 <el-table-column prop="icon" label="图标" align="center" width="60px">
95 <template slot-scope="scope">
96 <svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
97 </template>
98 </el-table-column>
99 <el-table-column prop="sort" align="center" label="排序">
100 <template slot-scope="scope">
101 {{ scope.row.sort }}
102 </template>
103 </el-table-column>
104 <el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" />
105 <el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" />
106 <el-table-column prop="iframe" label="外链" width="75px">
107 <template slot-scope="scope">
108 <span v-if="scope.row.iframe"></span>
109 <span v-else></span>
110 </template>
111 </el-table-column>
112 <el-table-column prop="cache" label="缓存" width="75px">
113 <template slot-scope="scope">
114 <span v-if="scope.row.cache"></span>
115 <span v-else></span>
116 </template>
117 </el-table-column>
118 <el-table-column prop="hidden" label="可见" width="75px">
119 <template slot-scope="scope">
120 <span v-if="scope.row.hidden"></span>
121 <span v-else></span>
122 </template>
123 </el-table-column>
124 <el-table-column prop="createTime" label="创建日期" width="135px" />
125 <el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right">
126 <template slot-scope="scope">
127 <udOperation
128 :data="scope.row"
129 :permission="permission"
130 msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
131 />
132 </template>
133 </el-table-column>
134 </el-table>
135 </div>
136 </template>
137
138 <script>
139 import crudMenu from '@/api/system/menu'
140 import { getMenusTree } from '@/api/system/menu'
141 import IconSelect from '@/components/IconSelect'
142 import Treeselect from '@riophae/vue-treeselect'
143 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
144 import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
145 import CRUD, { presenter, header, form, crud } from '@crud/crud'
146 import rrOperation from '@crud/RR.operation'
147 import crudOperation from '@crud/CRUD.operation'
148 import udOperation from '@crud/UD.operation'
149 import DateRangePicker from '@/components/DateRangePicker'
150
151 // crud交由presenter持有
152 // const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iframe: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
153 export default {
154 name: 'Menu',
155 components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
156 cruds() {
157 return CRUD({ title: '菜单', url: 'api/menus', crudMethod: { ...crudMenu }, optShow: { add: true, edit: false, del: false, reset: false }})
158 },
159 mixins: [presenter(), header(), form({}), crud()],
160 data() {
161 return {
162 menus: [],
163 permission: {
164 add: ['admin', 'menu:add'],
165 edit: ['admin', 'menu:edit'],
166 del: ['admin', 'menu:del']
167 },
168 form: {
169 name: '',
170 sort: 999,
171 path: '',
172 component: '',
173 componentName: '',
174 iframe: 'false',
175 roles: [],
176 pid: 0, icon: '',
177 cache: false,
178 hidden: false,
179 type: 0,
180 permission: ''
181 },
182 rules: {
183 title: [
184 { required: true, message: '请输入标题', trigger: 'blur' }
185 ],
186 path: [
187 { required: true, message: '请输入地址', trigger: 'blur' }
188 ]
189 }
190 }
191 },
192 methods: {
193 // 新增与编辑前做的操作
194 [CRUD.HOOK.afterToCU](crud, form) {
195 this.getMenus()
196 },
197 getSupDepts(id) {
198 crudMenu.getMenuSuperior(id).then(res => {
199 const children = res.map(function(obj) {
200 if (!obj.leaf && !obj.children) {
201 obj.children = null
202 }
203 return obj
204 })
205 this.menus = [{ id: 0, label: '顶级类目', children: children }]
206 })
207 },
208 getMenus() {
209 getMenusTree().then(res => {
210 this.menus = []
211 const menu = { id: 0, label: '顶级类目', children: [] }
212 menu.children = res
213 this.menus.push(menu)
214 })
215 },
216 [CRUD.HOOK.beforeValidateCU](crud) {
217 crud.form = this.form
218 return true
219 },
220 [CRUD.HOOK.beforeToEdit](curd, form) {
221 this.isAdd = false
222 this.form = {
223 id: form.id,
224 name: form.name,
225 sort: form.sort,
226 path: form.path,
227 component: form.component,
228 componentName: form.componentName,
229 iframe: form.iframe.toString(),
230 roles: form.roles ? form.roles : [],
231 pid: form.pid,
232 icon: form.icon,
233 cache: form.cache,
234 hidden: form.hidden,
235 type: form.type,
236 permission: form.permission ? form.permission : ''
237 }
238 },
239 loadMenus({ action, parentNode, callback }) {
240 if (action === LOAD_CHILDREN_OPTIONS) {
241 crudMenu.getMenusTree(parentNode.id).then(res => {
242 parentNode.children = res.map(function(obj) {
243 if (!obj.leaf) {
244 obj.children = null
245 }
246 return obj
247 })
248 setTimeout(() => {
249 callback()
250 }, 100)
251 })
252 }
253 },
254 // 选中图标
255 selected(name) {
256 this.form.icon = name
257 }
258 }
259 }
260 </script>
261
262 <style rel="stylesheet/scss" lang="scss" scoped>
263 ::v-deep .el-input-number .el-input__inner {
264 text-align: left;
265 }
266 ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
267 height: 30px;
268 line-height: 30px;
269 }
270 </style>
1 <template>
2 <el-dialog :append-to-body="true" :close-on-click-modal="false" :visible.sync="dialog" :title="isAdd ? '新增' : '编辑'" width="960px" @closed="doClose">
3 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px" style="padding: 30px;margin: -30px 0px">
4 <el-row>
5 <el-col :span="12">
6 <el-form-item label="模板名称">
7 <el-input v-model="form.name" />
8 </el-form-item>
9 </el-col>
10 <el-col :span="12">
11 <el-form-item label="标识" prop="code">
12 <el-input v-model="form.code" />
13 </el-form-item>
14 </el-col>
15 <el-col :span="12">
16 <el-form-item label="顺序">
17 <el-input v-model="form.sort" type="number" />
18 </el-form-item>
19 </el-col>
20 <el-col :span="12">
21 <el-form-item label="状态">
22 <el-switch
23 v-model="form.status"
24 active-color="#13ce66"
25 inactive-color="#ff4949"
26 active-text="可用"
27 inactive-text="不可用"
28 active-value="1"
29 inactive-value="0"
30 />
31 </el-form-item>
32 </el-col>
33 <!-- <el-col :span="12">-->
34 <!-- <el-form-item label="创建时间" >-->
35 <!-- <el-date-picker v-model="form.createTime" type="datetime"/>-->
36 <!-- </el-form-item>-->
37 <!-- </el-col>-->
38 <!-- <el-col :span="12">-->
39 <!-- <el-form-item label="更新时间" >-->
40 <!-- <el-date-picker v-model="form.updateTime" type="datetime"/>-->
41 <!-- </el-form-item>-->
42 <!-- </el-col>-->
43 </el-row>
44 </el-form>
45 <div slot="footer" class="dialog-footer">
46 <el-button type="text" @click="cancel">取消</el-button>
47 <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
48 </div>
49 </el-dialog>
50 </template>
51
52 <script>
53 import { add, edit, getByCode } from '@/api/system/permissionTemplate'
54 export default {
55 props: {
56 isAdd: {
57 type: Boolean,
58 required: true
59 },
60 dictMap: {
61 type: Object,
62 required: true
63 }
64 },
65 data() {
66 return {
67 loading: false, dialog: false,
68 form: {
69 id: '',
70 name: '',
71 code: '',
72 sort: '',
73 status: ''
74 },
75 rules: {
76 code: [
77 { required: true, message: '请输入标识', trigger: 'blur' }, { validator: this.validateCode, trigger: 'blur' }
78 ]
79 }
80 }
81 },
82 methods: {
83 validateCode(rule, value, callback) {
84 // 当为编辑状态且code未改变时不进行校验
85 if (!this.isAdd && this.form.originalCode === value) {
86 callback()
87 } else {
88 getByCode(value)
89 .then(res => {
90 typeof (res) === 'undefined' || res.id === null || this.form.id === res.id
91 ? callback()
92 : callback(new Error('该code已存在!'))
93 })
94 .catch((err) => {
95 console.log(err)
96 callback()
97 })
98 }
99 },
100 cancel() {
101 this.dialog = false
102 },
103 doSubmit() {
104 if (this.isAdd) {
105 this.doAdd()
106 } else this.doEdit()
107 },
108 doAdd() {
109 this.$refs['form'].validate((valid) => {
110 if (valid) {
111 this.loading = true
112 add(this.form).then(() => {
113 this.$notify({
114 title: '添加成功',
115 type: 'success',
116 duration: 2500
117 })
118 this.dialog = false
119 this.$parent.init()
120 }).catch(err => {
121 this.dialog = false
122 console.log(err)
123 })
124 } else {
125 this.$notify({
126 title: '警告',
127 message: '信息不合法',
128 type: 'warning',
129 duration: 2000
130 })
131 }
132 this.loading = false
133 })
134 },
135 doEdit() {
136 this.$refs['form'].validate((valid) => {
137 if (valid) {
138 this.loading = true
139 edit(this.form).then(() => {
140 this.$notify({
141 title: '修改成功',
142 type: 'success',
143 duration: 2500
144 })
145 this.dialog = false
146 this.$parent.init()
147 }).catch(err => {
148 this.dialog = false
149 console.log(err)
150 })
151 } else {
152 this.$notify({
153 title: '警告',
154 message: '信息不合法',
155 type: 'warning',
156 duration: 2000
157 })
158 }
159 this.loading = false
160 })
161 },
162 doClose() {
163 this.resetForm()
164 },
165 resetForm() {
166 this.$refs['form'].resetFields()
167 this.form = {
168 originalCode: '',
169 id: '',
170 name: '',
171 code: '',
172 sort: '',
173 status: ''
174 }
175 }
176 }
177 }
178 </script>
179
180 <style scoped>
181
182 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <poly-search :query-type-options="queryTypeOptions" default-key="name" @toQuery="searchQuery" />
6 <el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
7 <!-- 新增 -->
8 <div style="margin: 0 2px;">
9 <el-button
10 v-permission="['admin','permissionTemplate:add']"
11 class="filter-item"
12 size="mini"
13 type="primary"
14 icon="el-icon-plus"
15 @click="add"
16 >新增</el-button>
17 </div>
18 <!-- 导出 -->
19 <!-- <div style="display: inline-block;">-->
20 <!-- <el-button-->
21 <!-- :loading="downloadLoading"-->
22 <!-- size="mini"-->
23 <!-- class="filter-item"-->
24 <!-- type="warning"-->
25 <!-- icon="el-icon-download"-->
26 <!-- @click="download">导出</el-button>-->
27 <!-- </div>-->
28 </div>
29 <div style="width: 75%;float: left">
30 <!--表单组件-->
31 <eForm ref="form" :is-add="isAdd" :dict-map="dictMap" />
32 <!--表格渲染-->
33 <el-table v-loading="loading" :data="data" size="small" highlight-current-row style="width: 100%;" @row-dblclick="edit" @current-change="handleCurrentChange">
34 <el-table-column prop="id" label="id" />
35 <el-table-column prop="name" label="模板名称" />
36 <el-table-column prop="code" label="标识" />
37 <el-table-column prop="sort" label="顺序" />
38 <el-table-column prop="status" label="状态">
39 <template slot-scope="scope">
40 <el-tag v-if="scope.row.status==='1'" type="success">可用</el-tag>
41 <el-tag v-else type="danger">不可用</el-tag>
42 </template>
43 </el-table-column>
44 <el-table-column prop="createTime" label="创建时间">
45 <template slot-scope="scope">
46 <span>{{ parseTime(scope.row.createTime) }}</span>
47 </template>
48 </el-table-column>
49 <el-table-column prop="updateTime" label="更新时间">
50 <template slot-scope="scope">
51 <span>{{ parseTime(scope.row.updateTime) }}</span>
52 </template>
53 </el-table-column>
54 <el-table-column label="操作" width="150px" align="center">
55 <template slot-scope="scope">
56 <el-button v-permission="['admin','permissionTemplate:edit']" size="mini" type="primary" icon="el-icon-edit" @click="edit(scope.row)" />
57 <el-popover
58 :ref="scope.row.id"
59 v-permission="['admin','permissionTemplate:del']"
60 placement="top"
61 width="180"
62 >
63 <p>确定删除本条数据吗?</p>
64 <div style="text-align: right; margin: 0">
65 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
66 <el-button :loading="delLoading" type="primary" size="mini" @click="subDelete(scope.row.id)">确定</el-button>
67 </div>
68 <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
69 </el-popover>
70 </template>
71 </el-table-column>
72 </el-table>
73 <!--分页组件-->
74 <el-pagination
75 :total="total"
76 :current-page="page + 1"
77 style="margin-top: 8px;"
78 layout="total, prev, pager, next, sizes"
79 @size-change="sizeChange"
80 @current-change="pageChange"
81 />
82 </div>
83 <div class="preTress">
84 <el-card class="box-card" shadow="never">
85 <div slot="header" class="clearfix">
86 <el-tooltip class="item" effect="dark" content="选择指定模版分配权限" placement="top">
87 <span class="role-span">权限分配</span>
88 </el-tooltip>
89 <el-button
90 v-permission="['admin','roles:edit']"
91 :disabled="!showButton"
92 :loading="apiLoading"
93 icon="el-icon-check"
94 size="mini"
95 style="float: right; padding: 6px 9px"
96 type="primary"
97 @click="getCheckedTree"
98 >保存</el-button>
99 <el-button
100 v-permission="['admin','roles:edit']"
101 :loading="updateLoading"
102 icon="el-icon-refresh"
103 size="mini"
104 style="float: right; padding: 6px 9px;margin-right: 3px"
105 type="primary"
106 @click="updateTree"
107 >更新</el-button>
108 </div>
109 <premission-tree ref="preTress" :is-show-check-box="showButton" :api-ids="apiIds" />
110 </el-card>
111 </div>
112 </div>
113 </template>
114
115 <script>
116 import initData from '@/mixins/initData'
117 import initDict from '@/mixins/initDict'
118 import { getAttrByValueFromDict } from '@/utils/common-util'
119 import { del, downloadPermissionTemplate, getpremissionById, putPremission, updatePemission } from '@/api/system/permissionTemplate'
120 import { parseTime, downloadFile } from '@/utils/index'
121 import eForm from './form'
122 import premissionTree from './premissionTree'
123 import polySearch from '@/components/polySearch/index'
124 export default {
125 components: { eForm, premissionTree, polySearch },
126 mixins: [initData, initDict],
127 data() {
128 return {
129 delLoading: false,
130 polyObject: {},
131 queryTypeOptions: [
132 { key: 'id', display_name: 'id' },
133 { key: 'name', display_name: '名称' },
134 { key: 'code', display_name: '标识' },
135 { key: 'status', display_name: '状态' }
136 ],
137 apiLoading: false,
138 updateLoading: false,
139 showButton: false,
140 selectCheckList: [],
141 lineId: '',
142 apiIds: []
143 }
144 },
145 created() {
146 this.$nextTick(() => {
147 this.init()
148 this.getDictMap('')
149 })
150 },
151 methods: {
152 parseTime,
153 getAttrByValueFromDict,
154 getCheckedTree() {
155 const arr = this.$refs.preTress.getCheckedNodes()
156 this.selectCheckList = arr.filter(item => {
157 return typeof item.id === 'number'
158 })
159 const obj = {
160 ptId: this.lineId,
161 permissionIds: []
162 }
163 this.selectCheckList.forEach(item => {
164 obj.permissionIds.push(item.id)
165 })
166 putPremission(obj).then(res => {
167 this.$message.success('保存成功')
168 })
169 },
170 updateTree() {
171 this.updateLoading = true
172 updatePemission().then(res => {
173 this.$refs.preTress.getPremission()
174 this.updateLoading = false
175 this.$notify({
176 title: '提示',
177 message: '更新成功',
178 type: 'success',
179 duration: 2000
180 })
181 }).catch(e => {
182 console.log(e)
183 this.updateLoading = false
184 })
185 },
186 handleCurrentChange(val) {
187 this.lineId = ''
188 if (val) {
189 this.lineId = val.id
190 // 清空菜单的选中
191 this.$refs.preTress.$refs['preTree'].setCheckedKeys([])
192 // 保存当前的角色id
193 // 点击后显示按钮
194 this.showButton = true
195 // 初始化
196 this.apiIds = []
197 // 菜单数据需要特殊处理
198 this.getpremissionById(val.id)
199 }
200 },
201 getpremissionById(id) {
202 getpremissionById(id).then(res => {
203 res.forEach(data => {
204 this.apiIds.push(data)
205 })
206 this.$refs.preTress.$refs['preTree'].setCheckedKeys(this.apiIds)
207 })
208 },
209 searchQuery(data) {
210 this.polyObject = data
211 },
212 beforeInit() {
213 this.showButton = false
214 this.url = 'api/permissionTemplate'
215 const sort = 'id,desc'
216 this.params = { page: this.page, size: this.size, sort: sort }
217 const polyObj = this.polyObject
218 const type = polyObj.type
219 const value = polyObj.value
220 if (type && value) { this.params[type] = value }
221 return true
222 },
223 subDelete(id) {
224 this.delLoading = true
225 del(id).then(res => {
226 this.delLoading = false
227 this.$refs[id].doClose()
228 this.dleChangePage()
229 this.init()
230 this.$notify({
231 title: '删除成功',
232 type: 'success',
233 duration: 2500
234 })
235 }).catch(err => {
236 this.delLoading = false
237 this.$refs[id].doClose()
238 console.log(err.response.data.message)
239 })
240 },
241 add() {
242 this.isAdd = true
243 this.$refs.form.dialog = true
244 },
245 edit(data) {
246 this.isAdd = false
247 const _this = this.$refs.form
248 _this.form = {
249 originalCode: data.code,
250 id: data.id,
251 name: data.name,
252 code: data.code,
253 sort: data.sort,
254 status: data.status,
255 createTime: data.createTime,
256 updateTime: data.updateTime
257 }
258 _this.dialog = true
259 },
260 // 导出
261 download() {
262 this.beforeInit()
263 this.downloadLoading = true
264 downloadPermissionTemplate(this.params).then(result => {
265 downloadFile(result, 'PermissionTemplate列表', 'xlsx')
266 this.downloadLoading = false
267 }).catch(() => {
268 this.downloadLoading = false
269 })
270 }
271 }
272 }
273 </script>
274
275 <style scoped>
276 .preTress {
277 margin-left: 10px;
278 width: 24%;
279 float:left;
280 height: 700px;
281 overflow: scroll;
282 }
283 </style>
1 <template>
2 <div>
3 <el-tree
4 ref="preTree"
5 :data="treeData"
6 :props="dprops"
7 :default-checked-keys="apiIds"
8 :show-checkbox="showCheckBox"
9 node-key="id"
10 @check="checkItem"
11 @getCheckedNodes="getCheckedNodes"
12 />
13 </div>
14 </template>
15
16 <script>
17 import { getPremission } from '@/api/system/permissionTemplate'
18 export default {
19 name: 'PremissionTree',
20 props: {
21 apiIds: {
22 type: Array,
23 default() {
24 return []
25 }
26 },
27 isUserSet: {
28 type: Boolean,
29 required: false,
30 default() {
31 return false
32 }
33 },
34 isShowCheckBox: {
35 type: Boolean,
36 required: false,
37 default() {
38 return false
39 }
40 }
41 },
42 data() {
43 return {
44 treeLoading: false,
45 treeData: [],
46 defaultIds: [],
47 dprops: {
48 label: 'label',
49 children: 'children'
50 }
51 }
52 },
53 computed: {
54 showCheckBox() {
55 if (this.isShowCheckBox) {
56 return true
57 } else {
58 return false
59 }
60 }
61 },
62 created() {
63 this.getPremission()
64 },
65 methods: {
66 getPremission() {
67 getPremission().then(res => {
68 const arr = res
69 const lastArr = []
70 let basePname = arr[0].pname
71 let baseObj = {
72 label: basePname,
73 id: basePname,
74 children: []
75 }
76 for (let i = 0; i < arr.length; i++) {
77 const item = arr[i]
78 if (item.pname === basePname) {
79 item.label = item.alias
80 baseObj.children.push(item)
81 } else {
82 lastArr.push(baseObj)
83 basePname = item.pname
84 item.label = item.alias
85 baseObj = {
86 label: basePname,
87 id: basePname,
88 children: []
89 }
90 baseObj.children.push(item)
91 }
92 }
93 this.treeData = lastArr
94 this.defaultIds = this.apiIds
95 })
96 },
97 checkItem(currentObj, treeStatus) {
98 if (!this.isUserSet) return
99 // 去除一级节点
100 const allSelectChecked = treeStatus.checkedKeys.filter(item => {
101 return typeof item === 'number'
102 })
103 // 获取手动点击的(非模版的)
104 const handSelectChecked = this.getArrDifference(allSelectChecked, this.apiIds)
105 this.$refs['preTree'].setCheckedKeys(allSelectChecked)
106 this.$emit('handChecked', handSelectChecked)
107 },
108 getCheckedNodes() {
109 return this.$refs.preTree.getCheckedNodes()
110 },
111 getArrDifference(arr1, arr2) {
112 return arr1.concat(arr2).filter(function(v, i, arr) {
113 return arr.indexOf(v) === arr.lastIndexOf(v)
114 })
115 }
116 }
117 }
118 </script>
119
120 <style scoped>
121
122 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission" />
12 </div>
13 <!-- 表单渲染 -->
14 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
15 <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
16 <el-form-item label="角色名称" prop="name">
17 <el-input v-model="form.name" style="width: 140px;" />
18 </el-form-item>
19 <el-form-item label="角色权限" prop="permission">
20 <el-input v-model="form.permission" style="width: 140px;" />
21 </el-form-item>
22 <el-form-item label="角色级别" prop="level">
23 <el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
24 </el-form-item>
25 <el-form-item label="数据范围" prop="dataScope">
26 <el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
27 <el-option
28 v-for="item in dateScopes"
29 :key="item"
30 :label="item"
31 :value="item"
32 />
33 </el-select>
34 </el-form-item>
35 <el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts">
36 <treeselect
37 v-model="deptIds"
38 :load-options="loadDepts"
39 :options="depts"
40 multiple
41 style="width: 380px"
42 placeholder="请选择"
43 />
44 </el-form-item>
45 <el-form-item label="描述信息" prop="remark">
46 <el-input v-model="form.remark" style="width: 380px;" rows="5" type="textarea" />
47 </el-form-item>
48 </el-form>
49 <div slot="footer" class="dialog-footer">
50 <el-button type="text" @click="crud.cancelCU">取消</el-button>
51 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
52 </div>
53 </el-dialog>
54
55 <el-row :gutter="15">
56 <!--角色管理-->
57 <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
58 <el-card class="box-card" shadow="never">
59 <div slot="header" class="clearfix">
60 <span class="role-span">角色列表</span>
61 <span style="font-size: 12px;color: #ccc">单击行对角色进行菜单分配</span>
62 </div>
63 <el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
64 <el-table-column v-if="false" :selectable="checkboxT" type="selection" width="55" />
65 <el-table-column prop="name" label="名称" />
66 <el-table-column prop="dataScope" label="数据权限" />
67 <el-table-column prop="level" label="角色级别" />
68 <el-table-column :show-overflow-tooltip="true" prop="remark" label="描述" />
69 <el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期">
70 <template slot-scope="scope">
71 <span>{{ parseTime(scope.row.createTime) }}</span>
72 </template>
73 </el-table-column>
74 <el-table-column v-permission="['admin','roles:edit','roles:del']" label="操作" width="130px" align="center" fixed="right">
75 <template slot-scope="scope">
76 <udOperation
77 v-if="scope.row.level >= level"
78 :data="scope.row"
79 :permission="permission"
80 />
81 </template>
82 </el-table-column>
83 </el-table>
84 <!--分页组件-->
85 <pagination />
86 </el-card>
87 </el-col>
88 <!-- 菜单授权 -->
89 <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
90 <el-card class="box-card" shadow="never">
91 <div slot="header" class="clearfix">
92 <el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top">
93 <span class="role-span">菜单分配</span>
94 </el-tooltip>
95 <el-button
96 v-permission="['admin','roles:edit']"
97 :disabled="!showButton"
98 :loading="menuLoading"
99 icon="el-icon-check"
100 size="mini"
101 style="float: right; padding: 6px 9px"
102 type="primary"
103 @click="saveMenu"
104 >保存</el-button>
105 </div>
106 <el-tree
107 ref="menu"
108 :data="menus"
109 :default-checked-keys="menuIds"
110 :props="defaultProps"
111 check-strictly
112 accordion
113 show-checkbox
114 node-key="id"
115 @check="clickDeal"
116 />
117 </el-card>
118 </el-col>
119 </el-row>
120 </div>
121 </template>
122
123 <script>
124 import crudRoles from '@/api/system/role'
125 import { getCodeImg } from '@/api/login'
126 import { getDepts, getDeptSuperior } from '@/api/system/dept'
127 import { getMenusTree, getChild } from '@/api/system/menu'
128 import CRUD, { presenter, header, form, crud } from '@crud/crud'
129 import rrOperation from '@crud/RR.operation'
130 import crudOperation from '@crud/CRUD.operation'
131 import udOperation from '@crud/UD.operation'
132 import pagination from '@crud/Pagination'
133 import Treeselect from '@riophae/vue-treeselect'
134 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
135 import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
136 import DateRangePicker from '@/components/DateRangePicker'
137
138 export default {
139 name: 'Role',
140 components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
141 dicts: ['coupon_status'],
142 cruds() {
143 return CRUD({ title: '角色', url: 'api/roles', page: { page: 0, size: 2, total: 200, pages: [100, 300] }, sort: 'level,asc', crudMethod: { ...crudRoles }, optShow: { add: true, edit: false, del: false, reset: false }})
144 },
145 mixins: [presenter(), header(), form({}), crud()],
146 data() {
147 return {
148 defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
149 dateScopes: ['全部', '本级', '自定义'], level: 3,
150 currentId: 0, menuLoading: false, showButton: false,
151 menus: [], menuIds: [], depts: [], deptIds: [], // 多选时使用
152 permission: {
153 add: ['admin', 'roles:add'],
154 edit: ['admin', 'roles:edit'],
155 del: ['admin', 'roles:del']
156 },
157 data_range: [],
158 form: {
159 name: '', depts: [], remark: '', dataScope: '本级', level: 3, permission: '', id: ''
160 },
161 rules: {
162 name: [
163 { required: true, message: '请输入名称', trigger: 'blur' }
164 ],
165 permission: [
166 { required: true, message: '请输入权限', trigger: 'blur' }
167 ]
168 }
169 }
170 },
171 created() {
172 console.log(this.dict.coupon_status.label)
173 this.getMenus()
174 getCodeImg().then(res => {
175 console.log(res)
176 })
177 crudRoles.getLevel().then(data => {
178 this.level = data.level
179 })
180 },
181 methods: {
182 getMenus() {
183 getMenusTree().then(res => {
184 this.menus = res
185 })
186 },
187 getMenuDatas(node, resolve) {
188 setTimeout(() => {
189 getMenusTree(node.data.id ? node.data.id : 0).then(res => {
190 resolve(res)
191 })
192 }, 100)
193 },
194 [CRUD.HOOK.beforeRefresh]() {
195 if (this.query.createTime && this.query.createTime.length > 0) {
196 this.crud.params.startTime = this.query.createTime[0]
197 this.crud.params.endTime = this.query.createTime[1]
198 } else {
199 this.crud.params.startTime = ''
200 this.crud.params.endTime = ''
201 }
202 return true
203 },
204 [CRUD.HOOK.afterRefresh]() {
205 this.$refs.menu.setCheckedKeys([])
206 console.log('刷新之后')
207 },
208 // 新增前初始化部门信息
209 [CRUD.HOOK.beforeToAdd]() {
210 this.deptIds = []
211 },
212 // 编辑前初始化自定义数据权限的部门信息
213 [CRUD.HOOK.beforeToEdit](crud, form) {
214 this.deptIds = []
215 if (form.dataScope === '自定义') {
216 this.getSupDepts(form.depts)
217 }
218 const _this = this
219 form.depts.forEach(function(dept) {
220 _this.deptIds.push(dept.id)
221 })
222 },
223 // 提交前做的操作
224 [CRUD.HOOK.afterValidateCU](crud) {
225 if (crud.form.dataScope === '自定义' && this.deptIds.length === 0) {
226 this.$message({
227 message: '自定义数据权限不能为空',
228 type: 'warning'
229 })
230 return false
231 } else if (crud.form.dataScope === '自定义') {
232 const depts = []
233 this.deptIds.forEach(function(data) {
234 const dept = { id: data }
235 depts.push(dept)
236 })
237 crud.form.depts = depts
238 } else {
239 crud.form.depts = []
240 }
241 crud.form = this.form
242 return true
243 },
244 [CRUD.HOOK.beforeToAdd]() {
245 // const _this = this.$refs.form
246 this.form = { name: '', depts: [], remark: '', dataScope: '本级', level: 3, permission: '', id: '' }
247 // this.dialog = true
248 },
249 [CRUD.HOOK.beforeToEdit](curd, form) {
250 this.form = {
251 id: form.id,
252 depts: form.depts,
253 name: form.name,
254 remark: form.remark,
255 dataScope: form.dataScope,
256 level: form.level,
257 permission: form.permission
258 }
259 },
260 // :check-strictly="true" 设置之后父子节点不相关联,设置触发事件进行绑定
261 clickDeal(currentObj, treeStatus) {
262 const selected = treeStatus.checkedKeys.indexOf(currentObj.id)
263 // 选中
264 if (selected !== -1) {
265 // 子节点只要被选中父节点就被选中
266 this.selectedParent(currentObj)
267 // 统一处理子节点为相同的勾选状态
268 this.uniteChildSame(currentObj, true)
269 } else {
270 // 未选中 处理子节点全部未选中
271 if (currentObj.children.length !== 0) {
272 this.uniteChildSame(currentObj, false)
273 }
274 }
275 },
276 // 统一处理子节点为相同的勾选状态
277 uniteChildSame(treeList, isSelected) {
278 this.$refs.menu.setChecked(treeList.id, isSelected)
279 if (treeList.children) {
280 for (let i = 0; i < treeList.children.length; i++) {
281 this.uniteChildSame(treeList.children[i], isSelected)
282 }
283 }
284 },
285 // 统一处理父节点为选中
286 selectedParent(currentObj) {
287 const currentNode = this.$refs.menu.getNode(currentObj)
288 if (currentNode.parent && currentNode.parent.key !== undefined) {
289 this.$refs.menu.setChecked(currentNode.parent, true)
290 this.selectedParent(currentNode.parent)
291 }
292 },
293 // 触发单选
294 handleCurrentChange(val) {
295 if (val) {
296 const _this = this
297 // 清空菜单的选中
298 this.$refs.menu.setCheckedKeys([])
299 // 保存当前的角色id
300 this.currentId = val.id
301 // 初始化默认选中的key
302 this.menuIds = []
303 val.menus.forEach(function(data) {
304 _this.menuIds.push(data.id)
305 })
306 this.showButton = true
307 }
308 },
309 menuChange(menu) {
310 // 获取该节点的所有子节点,id 包含自身
311 getChild(menu.id).then(childIds => {
312 // 判断是否在 menuIds 中,如果存在则删除,否则添加
313 if (this.menuIds.indexOf(menu.id) !== -1) {
314 for (let i = 0; i < childIds.length; i++) {
315 const index = this.menuIds.indexOf(childIds[i])
316 if (index !== -1) {
317 this.menuIds.splice(index, 1)
318 }
319 }
320 } else {
321 for (let i = 0; i < childIds.length; i++) {
322 const index = this.menuIds.indexOf(childIds[i])
323 if (index === -1) {
324 this.menuIds.push(childIds[i])
325 }
326 }
327 }
328 this.$refs.menu.setCheckedKeys(this.menuIds)
329 })
330 },
331 // 保存菜单
332 saveMenu() {
333 this.menuLoading = true
334 const role = { id: this.currentId, menus: [] }
335 // 得到半选的父节点数据,保存起来
336 this.$refs.menu.getHalfCheckedNodes().forEach(function(data, index) {
337 const menu = { id: data.id }
338 role.menus.push(menu)
339 })
340 // 得到已选中的 key 值
341 this.$refs.menu.getCheckedKeys().forEach(function(data, index) {
342 const menu = { id: data }
343 role.menus.push(menu)
344 })
345 crudRoles.editMenu(role).then(() => {
346 this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
347 this.menuLoading = false
348 this.update()
349 }).catch(err => {
350 this.menuLoading = false
351 console.log(err.response.data.message)
352 })
353 },
354 // 改变数据
355 update() {
356 // 无刷新更新 表格数据
357 crudRoles.get(this.currentId).then(res => {
358 for (let i = 0; i < this.crud.data.length; i++) {
359 if (res.id === this.crud.data[i].id) {
360 this.crud.data[i] = res
361 break
362 }
363 }
364 })
365 },
366 // 获取部门数据
367 getDepts() {
368 getDepts({ enabled: true }).then(res => {
369 this.depts = res.content.map(function(obj) {
370 if (obj.hasChildren) {
371 obj.children = null
372 }
373 return obj
374 })
375 })
376 },
377 getSupDepts(depts) {
378 const ids = []
379 depts.forEach(dept => {
380 ids.push(dept.id)
381 })
382 getDeptSuperior(ids).then(res => {
383 const date = res.content
384 this.buildDepts(date)
385 this.depts = date
386 })
387 },
388 buildDepts(depts) {
389 depts.forEach(data => {
390 if (data.children) {
391 this.buildDepts(data.children)
392 }
393 if (data.hasChildren && !data.children) {
394 data.children = null
395 }
396 })
397 },
398 // 获取弹窗内部门数据
399 loadDepts({ action, parentNode, callback }) {
400 if (action === LOAD_CHILDREN_OPTIONS) {
401 getDepts({ enabled: true, pid: parentNode.id }).then(res => {
402 parentNode.children = res.content.map(function(obj) {
403 if (obj.hasChildren) {
404 obj.children = null
405 }
406 return obj
407 })
408 setTimeout(() => {
409 callback()
410 }, 200)
411 })
412 }
413 },
414 // 如果数据权限为自定义则获取部门数据
415 changeScope() {
416 if (this.form.dataScope === '自定义') {
417 this.getDepts()
418 }
419 },
420 checkboxT(row) {
421 return row.level >= this.level
422 }
423 }
424 }
425 </script>
426
427 <style rel="stylesheet/scss" lang="scss">
428 .role-span {
429 font-weight: bold;color: #303133;
430 font-size: 15px;
431 }
432 </style>
433
434 <style rel="stylesheet/scss" lang="scss" scoped>
435 ::v-deep .el-input-number .el-input__inner {
436 text-align: left;
437 }
438 ::v-deep .vue-treeselect__multi-value{
439 margin-bottom: 0;
440 }
441 ::v-deep .vue-treeselect__multi-value-item{
442 border: 0;
443 padding: 0;
444 }
445 </style>
1 <template>
2 <div class="app-container">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission">
12 <!-- 任务日志 -->
13 <el-button
14 slot="right"
15 class="filter-item"
16 size="mini"
17 type="info"
18 icon="el-icon-tickets"
19 @click="doLog"
20 >日志</el-button>
21 </crudOperation>
22 <Log ref="log" />
23 </div>
24 <!--Form表单-->
25 <el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
26 <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
27 <el-form-item label="任务名称" prop="jobName">
28 <el-input v-model="form.jobName" style="width: 220px;" />
29 </el-form-item>
30 <el-form-item label="任务描述" prop="description">
31 <el-input v-model="form.description" style="width: 220px;" />
32 </el-form-item>
33 <el-form-item label="Bean名称" prop="beanName">
34 <el-input v-model="form.beanName" style="width: 220px;" />
35 </el-form-item>
36 <el-form-item label="执行方法" prop="methodName">
37 <el-input v-model="form.methodName" style="width: 220px;" />
38 </el-form-item>
39 <el-form-item label="Cron表达式" prop="cronExpression">
40 <el-input v-model="form.cronExpression" style="width: 220px;" />
41 </el-form-item>
42 <el-form-item label="子任务ID">
43 <el-input v-model="form.subTask" placeholder="多个用逗号隔开,按顺序执行" style="width: 220px;" />
44 </el-form-item>
45 <el-form-item label="任务负责人" prop="personInCharge">
46 <el-input v-model="form.personInCharge" style="width: 220px;" />
47 </el-form-item>
48 <el-form-item label="告警邮箱" prop="email">
49 <el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
50 </el-form-item>
51 <el-form-item label="失败后暂停">
52 <el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
53 <el-radio :label="true"></el-radio>
54 <el-radio :label="false"></el-radio>
55 </el-radio-group>
56 </el-form-item>
57 <el-form-item label="任务状态">
58 <el-radio-group v-model="form.isPause" style="width: 220px">
59 <el-radio :label="false">启用</el-radio>
60 <el-radio :label="true">暂停</el-radio>
61 </el-radio-group>
62 </el-form-item>
63 <el-form-item label="参数内容">
64 <el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
65 </el-form-item>
66 </el-form>
67 <div slot="footer" class="dialog-footer">
68 <el-button type="text" @click="crud.cancelCU">取消</el-button>
69 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
70 </div>
71 </el-dialog>
72 <!--表格渲染-->
73 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
74 <el-table-column :selectable="checkboxT" type="selection" width="55" />
75 <el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
76 <el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
77 <el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
78 <el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
79 <el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
80 <el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
81 <el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
82 <template slot-scope="scope">
83 <el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
84 </template>
85 </el-table-column>
86 <el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
87 <el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
88 <el-table-column v-if="checkPer(['admin','timing:edit','timing:del'])" label="操作" width="170px" align="center" fixed="right">
89 <template slot-scope="scope">
90 <el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
91 <el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
92 <el-button v-permission="['admin','timing:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
93 {{ scope.row.isPause ? '恢复' : '暂停' }}
94 </el-button>
95 <el-popover
96 :ref="scope.row.id"
97 v-permission="['admin','timing:del']"
98 placement="top"
99 width="200"
100 >
101 <p>确定停止并删除该任务吗?</p>
102 <div style="text-align: right; margin: 0">
103 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
104 <el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
105 </div>
106 <el-button slot="reference" type="text" size="mini">删除</el-button>
107 </el-popover>
108 </template>
109 </el-table-column>
110 </el-table>
111 <!--分页组件-->
112 <pagination />
113 </div>
114 </template>
115
116 <script>
117 import crudJob from '@/api/system/timing'
118 import Log from './log'
119 import CRUD, { presenter, header, form, crud } from '@crud/crud'
120 import rrOperation from '@crud/RR.operation'
121 import crudOperation from '@crud/CRUD.operation'
122 import pagination from '@crud/Pagination'
123 import DateRangePicker from '@/components/DateRangePicker'
124
125 const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
126 export default {
127 name: 'Timing',
128 components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
129 cruds() {
130 return CRUD({ title: '定时任务', url: 'api/jobs', crudMethod: { ...crudJob }})
131 },
132 mixins: [presenter(), header(), form(defaultForm), crud()],
133 data() {
134 return {
135 delLoading: false,
136 permission: {
137 add: ['admin', 'timing:add'],
138 edit: ['admin', 'timing:edit'],
139 del: ['admin', 'timing:del']
140 },
141 rules: {
142 jobName: [
143 { required: true, message: '请输入任务名称', trigger: 'blur' }
144 ],
145 description: [
146 { required: true, message: '请输入任务描述', trigger: 'blur' }
147 ],
148 beanName: [
149 { required: true, message: '请输入Bean名称', trigger: 'blur' }
150 ],
151 methodName: [
152 { required: true, message: '请输入方法名称', trigger: 'blur' }
153 ],
154 cronExpression: [
155 { required: true, message: '请输入Cron表达式', trigger: 'blur' }
156 ],
157 personInCharge: [
158 { required: true, message: '请输入负责人名称', trigger: 'blur' }
159 ]
160 }
161 }
162 },
163 methods: {
164 // 执行
165 execute(id) {
166 crudJob.execution(id).then(res => {
167 this.crud.notify('执行成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
168 }).catch(err => {
169 console.log(err.response.data.message)
170 })
171 },
172 // 改变状态
173 updateStatus(id, status) {
174 if (status === '恢复') {
175 this.updateParams(id)
176 }
177 crudJob.updateIsPause(id).then(res => {
178 this.crud.toQuery()
179 this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
180 }).catch(err => {
181 console.log(err.response.data.message)
182 })
183 },
184 updateParams(id) {
185 console.log(id)
186 },
187 delMethod(id) {
188 this.delLoading = true
189 crudJob.del([id]).then(() => {
190 this.delLoading = false
191 this.$refs[id].doClose()
192 this.crud.dleChangePage(1)
193 this.crud.delSuccessNotify()
194 this.crud.toQuery()
195 }).catch(() => {
196 this.delLoading = false
197 this.$refs[id].doClose()
198 })
199 },
200 // 显示日志
201 doLog() {
202 this.$refs.log.dialog = true
203 this.$refs.log.doInit()
204 },
205 checkboxT(row, rowIndex) {
206 return row.id !== 1
207 }
208 }
209 }
210 </script>
1 <template>
2 <el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
3 <!-- 搜索 -->
4 <div class="head-container">
5 <el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
6 <date-range-picker v-model="query.createTime" class="date-item" />
7 <el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
8 <el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
9 </el-select>
10 <el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
11 <!-- 导出 -->
12 <div style="display: inline-block;">
13 <el-button
14 :loading="downloadLoading"
15 size="mini"
16 class="filter-item"
17 type="warning"
18 icon="el-icon-download"
19 @click="downloadMethod"
20 >导出</el-button>
21 </div>
22 </div>
23 <!--表格渲染-->
24 <el-table v-loading="loading" :data="data" style="width: 100%;margin-top: -10px;">
25 <el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
26 <el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
27 <el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
28 <el-table-column :show-overflow-tooltip="true" prop="params" width="120px" label="参数" />
29 <el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
30 <el-table-column prop="createTime" label="异常详情" width="110px">
31 <template slot-scope="scope">
32 <el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
33 </template>
34 </el-table-column>
35 <el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
36 <el-table-column align="center" prop="isSuccess" width="80px" label="状态">
37 <template slot-scope="scope">
38 <el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
39 </template>
40 </el-table-column>
41 <el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
42 </el-table>
43 <el-dialog :visible.sync="errorDialog" append-to-body title="异常详情" width="85%">
44 <pre v-highlightjs="errorInfo"><code class="java" /></pre>
45 </el-dialog>
46 <!--分页组件-->
47 <el-pagination
48 :total="total"
49 :current-page="page + 1"
50 :page-size="6"
51 style="margin-top:8px;"
52 layout="total, prev, pager, next"
53 @size-change="sizeChange"
54 @current-change="pageChange"
55 />
56 </el-dialog>
57 </template>
58
59 <script>
60 import crud from '@/mixins/crud'
61 import DateRangePicker from '@/components/DateRangePicker'
62 export default {
63 components: { DateRangePicker },
64 mixins: [crud],
65 data() {
66 return {
67 title: '任务日志',
68 errorInfo: '', errorDialog: false,
69 enabledTypeOptions: [
70 { key: 'true', display_name: '成功' },
71 { key: 'false', display_name: '失败' }
72 ]
73 }
74 },
75 methods: {
76 doInit() {
77 this.$nextTick(() => {
78 this.init()
79 })
80 },
81 // 获取数据前设置好接口地址
82 beforeInit() {
83 this.url = 'api/jobs/logs'
84 this.size = 6
85 return true
86 },
87 // 异常详情
88 info(errorInfo) {
89 this.errorInfo = errorInfo
90 this.errorDialog = true
91 }
92 }
93 }
94 </script>
95
96 <style scoped>
97 .java.hljs{
98 color: #444;
99 background: #ffffff !important;
100 }
101 ::v-deep .el-dialog__body{
102 padding: 0 20px 10px 20px !important;
103 }
104 </style>
1 <template>
2 <div class="app-container">
3 <el-row :gutter="20">
4 <el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
5 <el-card class="box-card">
6 <div slot="header" class="clearfix">
7 <span>个人信息</span>
8 </div>
9 <div>
10 <div style="text-align: center">
11 <div class="el-upload">
12 <img :src="user.avatar ? baseApi + '/avatar/' + user.avatar : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
13 <myUpload
14 v-model="show"
15 :headers="headers"
16 :url="updateAvatarApi"
17 @crop-upload-success="cropUploadSuccess"
18 />
19 </div>
20 </div>
21 <ul class="user-info">
22 <li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
23 <li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
24 <li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
25 <li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
26 <li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
27 <li>
28 <svg-icon icon-class="anq" /> 安全设置
29 <div class="user-right">
30 <a @click="$refs.pass.dialog = true">修改密码</a>
31 <a @click="$refs.email.dialog = true">修改邮箱</a>
32 </div>
33 </li>
34 </ul>
35 </div>
36 </el-card>
37 </el-col>
38 <el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
39 <!-- 用户资料 -->
40 <el-card class="box-card">
41 <el-tabs v-model="activeName" @tab-click="handleClick">
42 <el-tab-pane label="用户资料" name="first">
43 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
44 <el-form-item label="昵称" prop="nickName">
45 <el-input v-model="form.nickName" style="width: 35%" />
46 <span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
47 </el-form-item>
48 <el-form-item label="手机号" prop="phone">
49 <el-input v-model="form.phone" style="width: 35%;" />
50 <span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
51 </el-form-item>
52 <el-form-item label="性别">
53 <el-radio-group v-model="form.gender" style="width: 178px">
54 <el-radio label="男"></el-radio>
55 <el-radio label="女"></el-radio>
56 </el-radio-group>
57 </el-form-item>
58 <el-form-item label="">
59 <el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
60 </el-form-item>
61 </el-form>
62 </el-tab-pane>
63 <!-- 操作日志 -->
64 <el-tab-pane label="操作日志" name="second">
65 <el-table v-loading="loading" :data="data" style="width: 100%;">
66 <el-table-column prop="description" label="行为" />
67 <el-table-column prop="requestIp" label="IP" />
68 <el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
69 <el-table-column prop="browser" label="浏览器" />
70 <el-table-column prop="time" label="请求耗时" align="center">
71 <template slot-scope="scope">
72 <el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
73 <el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
74 <el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
75 </template>
76 </el-table-column>
77 <el-table-column
78 align="right"
79 >
80 <template slot="header">
81 <div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
82 </template>
83 <template slot-scope="scope">
84 <span>{{ scope.row.createTime }}</span>
85 </template>
86 </el-table-column>
87 </el-table>
88 <!--分页组件-->
89 <el-pagination
90 :total="total"
91 :current-page="page + 1"
92 style="margin-top: 8px;"
93 layout="total, prev, pager, next, sizes"
94 @size-change="sizeChange"
95 @current-change="pageChange"
96 />
97 </el-tab-pane>
98 </el-tabs>
99 </el-card>
100 </el-col>
101 </el-row>
102 <updateEmail ref="email" :email="user.email" />
103 <updatePass ref="pass" />
104 </div>
105 </template>
106
107 <script>
108 import myUpload from 'vue-image-crop-upload'
109 import { mapGetters } from 'vuex'
110 import updatePass from './center/updatePass'
111 import updateEmail from './center/updateEmail'
112 import { getToken } from '@/utils/auth'
113 import store from '@/store'
114 import { isvalidPhone } from '@/utils/validate'
115 import crud from '@/mixins/crud'
116 import { editUser } from '@/api/system/user'
117 import Avatar from '@/assets/images/avatar.png'
118 export default {
119 name: 'Center',
120 components: { updatePass, updateEmail, myUpload },
121 mixins: [crud],
122 data() {
123 // 自定义验证
124 const validPhone = (rule, value, callback) => {
125 if (!value) {
126 callback(new Error('请输入电话号码'))
127 } else if (!isvalidPhone(value)) {
128 callback(new Error('请输入正确的11位手机号码'))
129 } else {
130 callback()
131 }
132 }
133 return {
134 show: false,
135 Avatar: Avatar,
136 activeName: 'first',
137 saveLoading: false,
138 headers: {
139 'Authorization': getToken()
140 },
141 form: {},
142 rules: {
143 nickName: [
144 { required: true, message: '请输入用户昵称', trigger: 'blur' },
145 { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
146 ],
147 phone: [
148 { required: true, trigger: 'blur', validator: validPhone }
149 ]
150 }
151 }
152 },
153 computed: {
154 ...mapGetters([
155 'user',
156 'updateAvatarApi',
157 'baseApi'
158 ])
159 },
160 created() {
161 this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
162 store.dispatch('GetInfo').then(() => {})
163 },
164 methods: {
165 toggleShow() {
166 this.show = !this.show
167 },
168 handleClick(tab, event) {
169 if (tab.name === 'second') {
170 this.init()
171 }
172 },
173 beforeInit() {
174 this.url = 'api/logs/user'
175 return true
176 },
177 cropUploadSuccess(jsonData, field) {
178 store.dispatch('GetInfo').then(() => {})
179 },
180 doSubmit() {
181 if (this.$refs['form']) {
182 this.$refs['form'].validate((valid) => {
183 if (valid) {
184 this.saveLoading = true
185 editUser(this.form).then(() => {
186 this.editSuccessNotify()
187 store.dispatch('GetInfo').then(() => {})
188 this.saveLoading = false
189 }).catch(() => {
190 this.saveLoading = false
191 })
192 }
193 })
194 }
195 }
196 }
197 }
198 </script>
199
200 <style rel="stylesheet/scss" lang="scss">
201 .avatar {
202 width: 120px;
203 height: 120px;
204 border-radius: 50%;
205 }
206 .user-info {
207 padding-left: 0;
208 list-style: none;
209 li{
210 border-bottom: 1px solid #F0F3F4;
211 padding: 11px 0;
212 font-size: 13px;
213 }
214 .user-right {
215 float: right;
216 a{
217 color: #317EF3;
218 }
219 }
220 }
221 </style>
1 <template>
2 <div style="display: inline-block;">
3 <el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
4 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
5 <el-form-item label="新邮箱" prop="email">
6 <el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
7 <el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
8 </el-form-item>
9 <el-form-item label="验证码" prop="code">
10 <el-input v-model="form.code" style="width: 320px;" />
11 </el-form-item>
12 <el-form-item label="当前密码" prop="pass">
13 <el-input v-model="form.pass" type="password" style="width: 320px;" />
14 </el-form-item>
15 </el-form>
16 <div slot="footer" class="dialog-footer">
17 <el-button type="text" @click="cancel">取消</el-button>
18 <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
19 </div>
20 </el-dialog>
21 </div>
22 </template>
23
24 <script>
25 import store from '@/store'
26 import { validEmail } from '@/utils/validate'
27 import { updateEmail } from '@/api/system/user'
28 import { resetEmail } from '@/api/system/code'
29 export default {
30 props: {
31 email: {
32 type: String,
33 required: true
34 }
35 },
36 data() {
37 const validMail = (rule, value, callback) => {
38 if (value === '' || value === null) {
39 callback(new Error('新邮箱不能为空'))
40 } else if (value === this.email) {
41 callback(new Error('新邮箱不能与旧邮箱相同'))
42 } else if (validEmail(value)) {
43 callback()
44 } else {
45 callback(new Error('邮箱格式错误'))
46 }
47 }
48 return {
49 loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
50 user: { email: '', password: '' }, codeLoading: false,
51 buttonName: '获取验证码', isDisabled: false, time: 60,
52 rules: {
53 pass: [
54 { required: true, message: '当前密码不能为空', trigger: 'blur' }
55 ],
56 email: [
57 { required: true, validator: validMail, trigger: 'blur' }
58 ],
59 code: [
60 { required: true, message: '验证码不能为空', trigger: 'blur' }
61 ]
62 }
63 }
64 },
65 methods: {
66 cancel() {
67 this.resetForm()
68 },
69 sendCode() {
70 if (this.form.email && this.form.email !== this.email) {
71 this.codeLoading = true
72 this.buttonName = '验证码发送中'
73 const _this = this
74 resetEmail(this.form.email).then(res => {
75 this.$message({
76 showClose: true,
77 message: '发送成功,验证码有效期5分钟',
78 type: 'success'
79 })
80 this.codeLoading = false
81 this.isDisabled = true
82 this.buttonName = this.time-- + '秒后重新发送'
83 this.timer = window.setInterval(function() {
84 _this.buttonName = _this.time + '秒后重新发送'
85 --_this.time
86 if (_this.time < 0) {
87 _this.buttonName = '重新发送'
88 _this.time = 60
89 _this.isDisabled = false
90 window.clearInterval(_this.timer)
91 }
92 }, 1000)
93 }).catch(err => {
94 this.resetForm()
95 this.codeLoading = false
96 console.log(err.response.data.message)
97 })
98 }
99 },
100 doSubmit() {
101 this.$refs['form'].validate((valid) => {
102 if (valid) {
103 this.loading = true
104 updateEmail(this.form).then(res => {
105 this.loading = false
106 this.resetForm()
107 this.$notify({
108 title: '邮箱修改成功',
109 type: 'success',
110 duration: 1500
111 })
112 store.dispatch('GetInfo').then(() => {})
113 }).catch(err => {
114 this.loading = false
115 console.log(err.response.data.message)
116 })
117 } else {
118 return false
119 }
120 })
121 },
122 resetForm() {
123 this.dialog = false
124 this.$refs['form'].resetFields()
125 window.clearInterval(this.timer)
126 this.time = 60
127 this.buttonName = '获取验证码'
128 this.isDisabled = false
129 this.form = { pass: '', email: '', code: '' }
130 }
131 }
132 }
133 </script>
134
135 <style scoped>
136
137 </style>
1 <template>
2 <div style="display: inline-block">
3 <el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="500px" @close="cancel">
4 <el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
5 <el-form-item label="旧密码" prop="oldPass">
6 <el-input v-model="form.oldPass" type="password" auto-complete="on" style="width: 370px;" />
7 </el-form-item>
8 <el-form-item label="新密码" prop="newPass">
9 <el-input v-model="form.newPass" type="password" auto-complete="on" style="width: 370px;" />
10 </el-form-item>
11 <el-form-item label="确认密码" prop="confirmPass">
12 <el-input v-model="form.confirmPass" type="password" auto-complete="on" style="width: 370px;" />
13 </el-form-item>
14 </el-form>
15 <div slot="footer" class="dialog-footer">
16 <el-button type="text" @click="cancel">取消</el-button>
17 <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
18 </div>
19 </el-dialog>
20 </div>
21 </template>
22
23 <script>
24 import store from '@/store'
25 import { updatePass } from '@/api/system/user'
26 export default {
27 data() {
28 const confirmPass = (rule, value, callback) => {
29 if (value) {
30 if (this.form.newPass !== value) {
31 callback(new Error('两次输入的密码不一致'))
32 } else {
33 callback()
34 }
35 } else {
36 callback(new Error('请再次输入密码'))
37 }
38 }
39 return {
40 loading: false, dialog: false, title: '修改密码', form: { oldPass: '', newPass: '', confirmPass: '' },
41 rules: {
42 oldPass: [
43 { required: true, message: '请输入旧密码', trigger: 'blur' }
44 ],
45 newPass: [
46 { required: true, message: '请输入新密码', trigger: 'blur' },
47 { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
48 ],
49 confirmPass: [
50 { required: true, validator: confirmPass, trigger: 'blur' }
51 ]
52 }
53 }
54 },
55 methods: {
56 cancel() {
57 this.resetForm()
58 },
59 doSubmit() {
60 this.$refs['form'].validate((valid) => {
61 if (valid) {
62 this.loading = true
63 console.log(this.form, 'asdfasd')
64 updatePass(this.form).then(res => {
65 this.resetForm()
66 this.$notify({
67 title: '密码修改成功,请重新登录',
68 type: 'success',
69 duration: 1500
70 })
71 setTimeout(() => {
72 store.dispatch('LogOut').then(() => {
73 location.reload() // 为了重新实例化vue-router对象 避免bug
74 })
75 }, 1500)
76 }).catch(err => {
77 this.loading = false
78 console.log(err.response.data.message)
79 })
80 } else {
81 return false
82 }
83 })
84 },
85 resetForm() {
86 this.dialog = false
87 this.$refs['form'].resetFields()
88 this.form = { oldPass: '', newPass: '', confirmPass: '' }
89 }
90 }
91 }
92 </script>
93
94 <style scoped>
95
96 </style>
1 <template>
2 <div class="app-container">
3 <el-row :gutter="20">
4 <!--侧边部门数据-->
5 <el-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4">
6 <div class="head-container">
7 <el-input
8 v-model="deptName"
9 clearable
10 size="small"
11 placeholder="输入部门名称搜索"
12 prefix-icon="el-icon-search"
13 class="filter-item"
14 @input="getDeptDatas"
15 />
16 </div>
17 <el-tree
18 :data="deptDatas"
19 :load="getDeptDatas"
20 :props="defaultProps"
21 :expand-on-click-node="false"
22 lazy
23 @node-click="handleNodeClick"
24 />
25 </el-col>
26 <!--用户数据-->
27 <el-col :xs="15" :sm="18" :md="19" :lg="20" :xl="20">
28 <!--工具栏-->
29 <div class="head-container">
30 <div v-if="crud.props.searchToggle">
31 <!-- 搜索 -->
32 <el-input
33 v-model="query.blurry"
34 clearable
35 size="small"
36 placeholder="输入名称或者邮箱搜索"
37 style="width: 200px;"
38 class="filter-item"
39 @keyup.enter.native="crud.toQuery"
40 />
41 <date-range-picker v-model="query.createTime" class="date-item" />
42 <el-select
43 v-model="query.enabled"
44 clearable
45 size="small"
46 placeholder="状态"
47 class="filter-item"
48 style="width: 90px"
49 @change="crud.toQuery"
50 >
51 <el-option
52 v-for="item in enabledTypeOptions"
53 :key="item.key"
54 :label="item.display_name"
55 :value="item.key"
56 />
57 </el-select>
58 <rrOperation />
59 </div>
60 <crudOperation show="" :permission="permission" />
61 </div>
62 <!--表单渲染-->
63 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="570px">
64 <el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px">
65 <el-form-item label="用户名" prop="username">
66 <el-input v-model="form.username" />
67 </el-form-item>
68 <el-form-item label="电话" prop="phone">
69 <el-input v-model.number="form.phone" />
70 </el-form-item>
71 <!-- <el-form-item label="昵称" prop="nickName">-->
72 <!-- <el-input v-model="form.nickName" />-->
73 <!-- </el-form-item>-->
74 <el-form-item label="邮箱" prop="email">
75 <el-input v-model="form.email" />
76 </el-form-item>
77 <el-form-item label="部门" prop="dept.id">
78 <treeselect
79 v-model="form.dept.id"
80 :options="depts"
81 :load-options="loadDepts"
82 style="width: 178px"
83 placeholder="选择部门"
84 />
85 </el-form-item>
86 <el-form-item label="岗位" prop="job.id">
87 <el-select
88 v-model="form.job.id"
89 style="width: 178px"
90 placeholder="请选择"
91 @remove-tag="deleteTag"
92 @change="changeJob"
93 >
94 <el-option
95 v-for="item in jobs"
96 :key="item.name"
97 :label="item.name"
98 :value="item.id"
99 />
100 </el-select>
101 </el-form-item>
102 <el-form-item label="性别">
103 <el-radio-group v-model="form.gender" style="width: 178px">
104 <el-radio label="男"></el-radio>
105 <el-radio label="女"></el-radio>
106 </el-radio-group>
107 </el-form-item>
108 <el-form-item label="状态">
109 <el-radio-group v-model="form.enabled" :disabled="form.id === user.id">
110 <el-radio
111 v-for="item in dict.user_status"
112 :key="item.id"
113 :label="item.value"
114 >{{ item.label }}</el-radio>
115 </el-radio-group>
116 </el-form-item>
117 <el-form-item style="margin-bottom: 0;" label="角色" prop="roles">
118 <el-select
119 v-model="roleDatas"
120 style="width: 437px"
121 multiple
122 placeholder="请选择"
123 @remove-tag="deleteTag"
124 @change="changeRole"
125 >
126 <el-option
127 v-for="item in roles"
128 :key="item.name"
129 :disabled="level !== 1 && item.level <= level"
130 :label="item.name"
131 :value="item.id"
132 />
133 </el-select>
134 </el-form-item>
135 </el-form>
136 <div slot="footer" class="dialog-footer">
137 <el-button type="text" @click="crud.cancelCU">取消</el-button>
138 <el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
139 </div>
140 </el-dialog>
141 <user-permission ref="userTree" :permission-obj="permissionObj" />
142 <!--表格渲染-->
143 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
144 <el-table-column v-if="false" :selectable="checkboxT" type="selection" width="55" />
145 <el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
146 <el-table-column prop="gender" label="性别" />
147 <el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
148 <el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
149 <el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
150 <template slot-scope="scope">
151 <div>{{ scope.row.dept.name }}</div>
152 </template>
153 </el-table-column>
154 <el-table-column label="状态" align="center" prop="enabled">
155 <template slot-scope="scope">
156 <el-switch
157 v-model="scope.row.enabled"
158 :disabled="user.id === scope.row.id"
159 active-color="#409EFF"
160 inactive-color="#F56C6C"
161 @change="changeEnabled(scope.row, scope.row.enabled)"
162 />
163 </template>
164 </el-table-column>
165 <el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期">
166 <template slot-scope="scope">
167 <span>{{ parseTime(scope.row.createTime) }}</span>
168 </template>
169 </el-table-column>
170 <el-table-column
171 label="操作"
172 width="220"
173 align="center"
174 fixed="right"
175 >
176 <template slot-scope="scope">
177 <udOperation
178 :data="scope.row"
179 :permission="permission"
180 :disabled-dle="scope.row.id === user.id"
181 :operation="{edit: true, del: false}"
182 >
183 <el-button v-if="user.roles.indexOf('admin') !== -1" slot="header" v-permission="['admin','user:edit']" :loading="passLoading" size="mini" type="primary" icon="el-icon-refresh-right" style="margin-right: -6px" @click="recyclePassword(scope.row)" />
184 <el-button slot="header" v-permission="['admin','user:edit']" size="mini" type="primary" icon="el-icon-edit-outline" style="margin-right: -9px" @click="editPermission(scope.row)" />
185 <el-popover
186 slot="footer"
187 :ref="scope.row.id"
188 v-permission="['admin','user:del']"
189 placement="top"
190 width="180"
191 >
192 <p>确定删除本条数据吗?</p>
193 <div style="text-align: right; margin: 0">
194 <el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
195 <el-button :loading="delLoading" type="primary" size="mini" @click="subDelete(scope.row.id)">确定</el-button>
196 </div>
197 <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" />
198 </el-popover>
199 </udOperation>
200 </template>
201 </el-table-column>
202 </el-table>
203 <!--分页组件-->
204 <pagination />
205 </el-col>
206 </el-row>
207 </div>
208 </template>
209
210 <script>
211 import crudUser from '@/api/system/user'
212 import { isvalidPhone } from '@/utils/validate'
213 import { getDepts } from '@/api/system/dept'
214 import { parseTime } from '@/utils/index'
215 import { getAll, getLevel } from '@/api/system/role'
216 import { del } from '@/api/system/user'
217 import { getAllJob } from '@/api/system/job'
218 import CRUD, { presenter, header, form, crud } from '@crud/crud'
219 import rrOperation from '@crud/RR.operation'
220 import crudOperation from '@crud/CRUD.operation'
221 import udOperation from '@crud/UD.operation'
222 import pagination from '@crud/Pagination'
223 import DateRangePicker from '@/components/DateRangePicker'
224 import userPermission from './userPermission'
225 import Treeselect from '@riophae/vue-treeselect'
226 import { mapGetters } from 'vuex'
227 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
228 import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
229 import { resetPassword } from '@/api/system/user'
230 let userRoles = []
231 let userJobs = []
232 const defaultForm = {
233 id: null,
234 username: null,
235 // nickName: null,
236 gender: '男',
237 email: null,
238 enabled: 'false',
239 roles: [],
240 jobs: [],
241 job: { id: null },
242 dept: { id: null },
243 phone: null
244 }
245 export default {
246 name: 'User',
247 components: { Treeselect, crudOperation, rrOperation, udOperation, pagination, DateRangePicker, userPermission },
248 cruds() {
249 return CRUD({ title: '用户', url: 'api/users', crudMethod: { ...crudUser }, optShow: { add: true, edit: false, del: false, reset: false }})
250 },
251 mixins: [presenter(), header(), form(defaultForm), crud()],
252 // 数据字典
253 dicts: ['user_status'],
254 data() {
255 // 自定义验证
256 const validPhone = (rule, value, callback) => {
257 if (!value) {
258 callback(new Error('请输入电话号码'))
259 } else if (!isvalidPhone(value)) {
260 callback(new Error('请输入正确的11位手机号码'))
261 } else {
262 callback()
263 }
264 }
265 return {
266 height: document.documentElement.clientHeight - 180 + 'px;',
267 deptName: '', depts: [], deptDatas: [], jobs: [], level: 3, roles: [],
268 jobDatas: '', roleDatas: [], // 多选时使用
269 defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' },
270 data_range: [],
271 permission: {
272 add: ['admin', 'user:add'],
273 edit: ['admin', 'user:edit'],
274 del: ['admin', 'user:del']
275 },
276 enabledTypeOptions: [
277 { key: 'true', display_name: '激活' },
278 { key: 'false', display_name: '锁定' }
279 ],
280 delLoading: false,
281 passLoading: false,
282 permissionObj: {},
283 rules: {
284 username: [
285 { required: true, message: '请输入用户名', trigger: 'blur' },
286 { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
287 ],
288 nickName: [
289 { required: true, message: '请输入用户昵称', trigger: 'blur' },
290 { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
291 ],
292 email: [
293 { required: true, message: '请输入邮箱地址', trigger: 'blur' },
294 { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
295 ],
296 'dept.id': [
297 { required: true, message: '请选择部门', trigger: 'change' }
298 ],
299 'job.id': [
300 { required: true, message: '请选择岗位', trigger: 'change' }
301 ],
302 phone: [
303 { required: true, trigger: 'blur', validator: validPhone }
304 ]
305 }
306 }
307 },
308 computed: {
309 ...mapGetters([
310 'user'
311 ])
312 },
313 created() {
314 this.crud.msg.add = '新增成功,默认密码:123456'
315 },
316 mounted: function() {
317 const that = this
318 window.onresize = function temp() {
319 that.height = document.documentElement.clientHeight - 180 + 'px;'
320 }
321 },
322 methods: {
323 parseTime,
324 [CRUD.HOOK.beforeRefresh]() {
325 if (this.query.createTime && this.query.createTime.length > 0) {
326 this.crud.params.startTime = this.query.createTime[0]
327 this.crud.params.endTime = this.query.createTime[1]
328 } else {
329 this.crud.params.startTime = ''
330 this.crud.params.endTime = ''
331 }
332 return true
333 },
334 recyclePassword(row) {
335 this.passLoading = true
336 resetPassword(row).then(res => {
337 this.passLoading = false
338 this.$notify({
339 title: '重置密码成功',
340 type: 'success',
341 duration: 2000
342 })
343 }).catch(e => {
344 console.log(e)
345 this.passLoading = false
346 })
347 },
348 editPermission(row) {
349 this.permissionObj = row
350 this.$refs.userTree.dialogVisible = true
351 },
352 changeRole(value) {
353 userRoles = []
354 value.forEach(function(data, index) {
355 const role = { id: data }
356 userRoles.push(role)
357 })
358 },
359 changeJob(value) {
360 // userJobs = []
361 // value.forEach(function(data, index) {
362 // const job = { id: data }
363 // userJobs.push(job)
364 // })
365 },
366 deleteTag(value) {
367 userRoles.forEach(function(data, index) {
368 if (data.id === value) {
369 userRoles.splice(index, value)
370 }
371 })
372 },
373 // 新增与编辑前做的操作
374 [CRUD.HOOK.afterToCU](crud, form) {
375 console.log(form, ';lkll')
376 this.getRoles()
377 if (form.id == null) {
378 this.getDepts()
379 } else {
380 this.getSupDepts(form.dept.id)
381 }
382 this.getRoleLevel()
383 this.getJobs()
384 form.enabled = form.enabled.toString()
385 },
386 // 新增前将多选的值设置为空
387 [CRUD.HOOK.beforeToAdd]() {
388 // this.jobDatas = ''
389 // crud.form.job.id = ''
390 this.roleDatas = []
391 },
392 // 初始化编辑时候的角色与岗位
393 [CRUD.HOOK.beforeToEdit](crud, form) {
394 this.getJobs(this.form.dept.id)
395 // this.jobDatas = ''
396 // crud.form.job.id = ''
397 this.roleDatas = []
398 userRoles = []
399 userJobs = []
400 const _this = this
401 form.roles.forEach(function(role, index) {
402 _this.roleDatas.push(role.id)
403 const rol = { id: role.id }
404 userRoles.push(rol)
405 })
406 // _this.jobDatas = form.job.id
407 const data = { id: form.job.id }
408 userJobs.push(data)
409 // form.jobs.forEach(function(job, index) {
410 // _this.jobDatas.push(job.id)
411 // const data = { id: job.id }
412 // userJobs.push(data)
413 // })
414 },
415 // 提交前做的操作
416 [CRUD.HOOK.afterValidateCU](crud) {
417 if (!crud.form.dept.id) {
418 this.$message({
419 message: '部门不能为空',
420 type: 'warning'
421 })
422 return false
423 } else if (this.roleDatas.length === 0) {
424 this.$message({
425 message: '角色不能为空',
426 type: 'warning'
427 })
428 return false
429 }
430 crud.form.roles = userRoles
431 delete crud.form.job.name
432 // crud.form.jobs = userJobs
433 // crud.form.job = { id: this.jobDatas }
434 return true
435 },
436 // 获取左侧部门数据
437 getDeptDatas(node, resolve) {
438 const sort = 'id,desc'
439 const params = { sort: sort }
440 if (typeof node !== 'object') {
441 if (node) {
442 params['name'] = node
443 }
444 } else if (node.level !== 0) {
445 params['pid'] = node.data.id
446 }
447 setTimeout(() => {
448 getDepts(params).then(res => {
449 if (resolve) {
450 resolve(res.content)
451 } else {
452 this.deptDatas = res.content
453 }
454 })
455 }, 100)
456 },
457 getDepts() {
458 getDepts({ enabled: true }).then(res => {
459 this.depts = res.content.map(function(obj) {
460 if (obj.hasChildren) {
461 obj.children = null
462 }
463 return obj
464 })
465 })
466 },
467 subDelete(id) {
468 this.delLoading = true
469 del(id).then(res => {
470 this.delLoading = false
471 this.$refs[id].doClose()
472 this.crud.toQuery()
473 this.$notify({
474 title: '删除成功',
475 type: 'success',
476 duration: 2500
477 })
478 }).catch(err => {
479 this.delLoading = false
480 this.$refs[id].doClose()
481 console.log(err.response.data.message)
482 })
483 },
484 getSupDepts(deptId) {
485 getDepts(deptId).then(res => {
486 const date = res.content
487 this.buildDepts(date)
488 this.depts = date
489 })
490 },
491 buildDepts(depts) {
492 depts.forEach(data => {
493 if (data.children) {
494 this.buildDepts(data.children)
495 }
496 if (data.hasChildren && !data.children) {
497 data.children = null
498 }
499 })
500 },
501 // 获取弹窗内部门数据
502 loadDepts({ action, parentNode, callback }) {
503 if (action === LOAD_CHILDREN_OPTIONS) {
504 getDepts({ enabled: true, pid: parentNode.id }).then(res => {
505 parentNode.children = res.content.map(function(obj) {
506 if (obj.hasChildren) {
507 obj.children = null
508 }
509 return obj
510 })
511 setTimeout(() => {
512 callback()
513 }, 200)
514 })
515 }
516 },
517 // 切换部门
518 handleNodeClick(data) {
519 if (data.pid === 0) {
520 this.query.deptId = null
521 } else {
522 this.query.deptId = data.id
523 }
524 this.crud.toQuery()
525 },
526 // 改变状态
527 changeEnabled(data, val) {
528 this.$confirm('此操作将 "' + this.dict.label.user_status[val] + '" ' + data.username + ', 是否继续?', '提示', {
529 confirmButtonText: '确定',
530 cancelButtonText: '取消',
531 type: 'warning'
532 }).then(() => {
533 crudUser.edit(data).then(res => {
534 this.crud.notify(this.dict.label.user_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
535 }).catch(() => {
536 data.enabled = !data.enabled
537 })
538 }).catch(() => {
539 data.enabled = !data.enabled
540 })
541 },
542 // 获取弹窗内角色数据
543 getRoles() {
544 getAll().then(res => {
545 this.roles = res
546 }).catch(() => { })
547 },
548 // 获取弹窗内岗位数据
549 getJobs() {
550 getAllJob().then(res => {
551 this.jobs = res.content
552 }).catch(() => { })
553 },
554 // 获取权限级别
555 getRoleLevel() {
556 getLevel().then(res => {
557 this.level = res.level
558 }).catch(() => { })
559 },
560 checkboxT(row, rowIndex) {
561 return row.id !== this.user.id
562 }
563 }
564 }
565 </script>
566
567 <style rel="stylesheet/scss" lang="scss" scoped>
568 ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
569 height: 30px;
570 line-height: 30px;
571 }
572 </style>
1 <template>
2 <div>
3 <el-dialog v-if="dialogVisible" :visible.sync="dialogVisible" :close-on-click-modal="false" title="编辑账号权限" width="60%">
4 <div style="float: left;width: 50%">
5 <el-table :data="permissionData" @select="handleSelectionChange" @select-all="handSelectAll">
6 <el-table-column type="selection" width="55" />
7 <el-table-column prop="name" label="模板名称" />
8 <el-table-column prop="code" label="标识" />
9 </el-table>
10 </div>
11 <div style="float: left;width: 40%;margin-left: 5%;border: 1px solid #ccc;padding: 15px;border-radius: 10px;height: 380px;overflow: auto">
12 <premission-tree ref="userTree" :api-ids="apiIds" :is-show-check-box="true" :is-user-set="true" @handChecked="handChecked" />
13 </div>
14 <div style="clear: both" />
15 <div slot="footer" class="dialog-footer">
16 <el-button @click="dialogVisible = false">取 消</el-button>
17 <el-button type="primary" @click="confirmPermission">确 定</el-button>
18 </div>
19 </el-dialog>
20 </div>
21 </template>
22
23 <script>
24 import { getPremissionList, getpremissionById, getUserPremissionList, putUserPremission } from '@/api/system/permissionTemplate'
25 import premissionTree from '../permission_template/premissionTree'
26 export default {
27 name: 'UserPermission',
28 components: { premissionTree },
29 props: {
30 permissionObj: {
31 type: Object,
32 require: false,
33 default() {
34 return {}
35 }
36 }
37 },
38 data() {
39 return {
40 dialogVisible: false,
41 permissionData: [],
42 apiIds: [],
43 childrenData: [],
44 userPermissions: []
45 }
46 },
47 watch: {
48 dialogVisible() {
49 if (this.dialogVisible) {
50 this.apiIds = []
51 this.childrenData = []
52 this.userPermissions = []
53 this.getPermissionData()
54 } else {
55 this.apiIds = []
56 this.childrenData = []
57 this.userPermissions = []
58 }
59 }
60 },
61 // mounted() {
62 // this.$nextTick(() => {
63 // console.log(324234)
64 // })
65 // if (this.dialogVisible) {
66 // this.getPermissionData()
67 // }
68 // },
69 methods: {
70 getPermissionData() {
71 getPremissionList().then(res => {
72 this.permissionData = res.content
73 this.getUserPermission()
74 })
75 },
76 getUserPermission() {
77 getUserPremissionList(this.permissionObj.id).then(res => {
78 this.userPermissions = res
79 this.$refs.userTree.$refs['preTree'].setCheckedKeys(res)
80 })
81 },
82 handChecked(data) {
83 this.childrenData = data
84 },
85 handleSelectionChange(selection, row) {
86 this.handSelectAll(selection)
87 },
88 handSelectAll(selection) {
89 if (selection.length === 0) {
90 this.apiIds = this.childrenData.concat(this.userPermissions)
91 this.$refs.userTree.$refs['preTree'].setCheckedKeys(this.apiIds)
92 return
93 }
94 this.apiIds = []
95 for (let i = 0; i < selection.length; i++) {
96 const item = selection[i]
97 getpremissionById(item.id).then(res => {
98 this.apiIds.push(...res)
99 this.apiIds.push(...this.childrenData)
100 this.apiIds.push(...this.userPermissions)
101 this.apiIds = this.unique(this.apiIds)
102 this.$refs.userTree.$refs['preTree'].setCheckedKeys(this.apiIds)
103 })
104 }
105 },
106 unique(arr) {
107 return Array.from(new Set(arr))
108 },
109 confirmPermission() {
110 const arr = this.$refs.userTree.getCheckedNodes()
111 const sarr = arr.filter(item => {
112 return typeof item.id === 'number'
113 })
114 const obj = {
115 userId: this.permissionObj.id,
116 permissionIds: []
117 }
118 sarr.forEach(item => {
119 obj.permissionIds.push(item.id)
120 })
121 putUserPremission(obj).then(res => {
122 this.$message.success('保存成功')
123 this.dialogVisible = false
124 })
125 }
126 }
127 }
128 </script>
129
130 <style scoped>
131
132 </style>
1 <template>
2 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
3 <el-form-item label="appID" prop="appId">
4 <el-input v-model="form.appId" style="width: 40%" />
5 <span style="color: #C0C0C0;margin-left: 10px;">应用APPID,收款账号既是APPID对应支付宝账号</span>
6 </el-form-item>
7 <el-form-item label="商家账号" prop="sysServiceProviderId">
8 <el-input v-model="form.sysServiceProviderId" style="width: 40%;" />
9 <span style="color: #C0C0C0;margin-left: 10px;">商家账号</span>
10 </el-form-item>
11 <el-form-item label="商户私钥" prop="privateKey">
12 <el-input v-model="form.privateKey" type="password" style="width: 40%;" />
13 <span style="color: #C0C0C0;margin-left: 10px;">商户私钥,你的PKCS8格式RSA2私钥</span>
14 </el-form-item>
15 <el-form-item label="支付宝公钥" prop="publicKey">
16 <el-input v-model="form.publicKey" type="password" style="width: 40%;" />
17 <span style="color: #C0C0C0;margin-left: 10px;">支付宝公钥</span>
18 </el-form-item>
19 <el-form-item label="回调地址" prop="returnUrl">
20 <el-input v-model="form.returnUrl" style="width: 40%;" />
21 <span style="color: #C0C0C0;margin-left: 10px;">订单完成后返回的地址</span>
22 </el-form-item>
23 <el-form-item label="异步通知" prop="notifyUrl">
24 <el-input v-model="form.notifyUrl" style="width: 40%;" />
25 <span style="color: #C0C0C0;margin-left: 10px;">支付结果异步通知地址</span>
26 </el-form-item>
27 <el-form-item label="">
28 <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
29 </el-form-item>
30 </el-form>
31 </template>
32
33 <script>
34 import { get, update } from '@/api/tools/alipay'
35 export default {
36 name: 'Config',
37 data() {
38 return {
39 loading: false,
40 form: { appId: '', sysServiceProviderId: '', privateKey: '', publicKey: '', returnUrl: '', notifyUrl: '' },
41 rules: {
42 appId: [
43 { required: true, message: '请输入appID', trigger: 'blur' }
44 ],
45 sysServiceProviderId: [
46 { required: true, message: '请输入商家账号', trigger: 'blur' }
47 ],
48 privateKey: [
49 { required: true, message: '商户私钥不能为空', trigger: 'blur' }
50 ],
51 publicKey: [
52 { required: true, message: '支付宝公钥不能为空', trigger: 'blur' }
53 ],
54 returnUrl: [
55 { required: true, message: '回调地址不能为空', trigger: 'blur' }
56 ],
57 notifyUrl: [
58 { required: true, message: '回调地址不能为空', trigger: 'blur' }
59 ]
60 }
61 }
62 },
63 created() {
64 this.init()
65 },
66 methods: {
67 init() {
68 get().then(res => {
69 this.form = res
70 })
71 },
72 doSubmit() {
73 this.$refs['form'].validate((valid) => {
74 if (valid) {
75 this.loading = true
76 update(this.form).then(res => {
77 this.$notify({
78 title: '修改成功',
79 type: 'success',
80 duration: 2500
81 })
82 this.loading = false
83 }).catch(err => {
84 this.loading = false
85 console.log(err.response.data.message)
86 })
87 } else {
88 return false
89 }
90 })
91 }
92 }
93 }
94 </script>
95
96 <style scoped>
97
98 </style>
1 <template>
2 <el-tabs v-model="activeName" style="padding-left: 5px;">
3 <el-tab-pane label="参数配置" name="first">
4 <Config />
5 </el-tab-pane>
6 <el-tab-pane label="支付测试" name="second">
7 <ToPay />
8 </el-tab-pane>
9 <el-tab-pane label="使用说明" name="third">
10 <div>
11 <blockquote class="my-blockquote">注意</blockquote>
12 <pre class="my-code">
13 测试所用参数都是沙箱环境,仅供测试使用,申请地址:<a style="color: #00a0e9" href="https://openhome.alipay.com/platform/appDaily.htm?tab=info" target="_blank">支付宝开发平台</a>
14 如需付款测试,请使用
15 账号:uuxesw9745@sandbox.com
16 密码与支付密码:111111</pre>
17 <blockquote class="my-blockquote"> 支付设置</blockquote>
18 <pre class="my-code">
19 // 支付提供两个接口,
20 // PC端与手机端,并且在前端使用代码识别
21 if (/(Android)/i.test(navigator.userAgent)){ // 判断是否为Android手机
22 url = "/aliPay/toPayAsWeb"
23 }else if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){ // 判断是否为苹果手机
24 url = "/aliPay/toPayAsWeb"
25 } else {
26 url = "/aliPay/toPayAsPC"
27 }</pre>
28 </div>
29 </el-tab-pane>
30 </el-tabs>
31 </template>
32
33 <script>
34 import Config from './config'
35 import ToPay from './toPay'
36 export default {
37 name: 'AliPay',
38 components: { Config, ToPay },
39 data() {
40 return {
41 activeName: 'second'
42 }
43 }
44 }
45 </script>
46
47 <style scoped>
48 </style>
1 <template>
2 <div>
3 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="90px">
4 <el-form-item label="商品名称" prop="subject">
5 <el-input v-model="form.subject" style="width: 35%" />
6 </el-form-item>
7 <el-form-item label="商品价格" prop="totalAmount">
8 <el-input v-model="form.totalAmount" style="width: 35%" />
9 <span style="color: #C0C0C0;margin-left: 10px;">测试允许区间(0,5000]</span>
10 </el-form-item>
11 <el-form-item label="商品描述" prop="body">
12 <el-input v-model="form.body" style="width: 35%" rows="8" type="textarea" />
13 </el-form-item>
14 <el-form-item label="">
15 <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">去支付</el-button>
16 </el-form-item>
17 </el-form>
18 </div>
19 </template>
20
21 <script>
22 import { toAliPay } from '@/api/tools/alipay'
23 export default {
24 data() {
25 return {
26 url: '',
27 // 新窗口的引用
28 newWin: null,
29 loading: false, form: { subject: '', totalAmount: '', body: '' },
30 rules: {
31 subject: [
32 { required: true, message: '商品名称不能为空', trigger: 'blur' }
33 ],
34 totalAmount: [
35 { required: true, message: '商品价格不能为空', trigger: 'blur' }
36 ],
37 body: [
38 { required: true, message: '商品描述不能为空', trigger: 'blur' }
39 ]
40 }
41 }
42 },
43 watch: {
44 url(newVal, oldVal) {
45 if (newVal && this.newWin) {
46 this.newWin.sessionStorage.clear()
47 this.newWin.location.href = newVal
48 // 重定向后把url和newWin重置
49 this.url = ''
50 this.newWin = null
51 }
52 }
53 },
54 methods: {
55 doSubmit() {
56 this.$refs['form'].validate((valid) => {
57 if (valid) {
58 this.loading = true
59 // 先打开一个空的新窗口,再请求
60 this.newWin = window.open()
61 let url = ''
62 if (/(Android)/i.test(navigator.userAgent)) { // 判断是否为Android手机
63 url = 'aliPay/toPayAsWeb'
64 } else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { // 判断是否为苹果手机
65 url = 'aliPay/toPayAsWeb'
66 } else {
67 url = 'aliPay/toPayAsPC'
68 }
69 toAliPay(url, this.form).then(res => {
70 this.loading = false
71 this.url = res
72 }).catch(err => {
73 this.loading = false
74 console.log(err.response.data.message)
75 })
76 } else {
77 return false
78 }
79 })
80 }
81 }
82 }
83 </script>
84
85 <style scoped>
86 </style>
1 <template>
2 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
3 <el-form-item label="发件人邮箱" prop="fromUser">
4 <el-input v-model="form.fromUser" style="width: 40%" />
5 <span style="color: #C0C0C0;margin-left: 10px;">Sender mailbox</span>
6 </el-form-item>
7 <el-form-item label="发件用户名" prop="user">
8 <el-input v-model="form.user" style="width: 40%;" />
9 <span style="color: #C0C0C0;margin-left: 10px;">Sender usernamex</span>
10 </el-form-item>
11 <el-form-item label="邮箱密码" prop="pass">
12 <el-input v-model="form.pass" type="password" style="width: 40%;" />
13 <span style="color: #C0C0C0;margin-left: 10px;">email Password</span>
14 </el-form-item>
15 <el-form-item label="SMTP地址" prop="host">
16 <el-input v-model="form.host" style="width: 40%;" />
17 <span style="color: #C0C0C0;margin-left: 10px;">SMTP address</span>
18 </el-form-item>
19 <el-form-item label="SMTP端口" prop="port">
20 <el-input v-model="form.port" style="width: 40%;" />
21 <span style="color: #C0C0C0;margin-left: 10px;">SMTP port</span>
22 </el-form-item>
23 <el-form-item label="">
24 <el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
25 </el-form-item>
26 </el-form>
27 </template>
28
29 <script>
30 import { get, update } from '@/api/tools/email'
31 export default {
32 name: 'Config',
33 data() {
34 return {
35 loading: false, form: { id: 1, fromUser: '', user: '', pass: '', host: '', port: '', sslEnable: '' },
36 rules: {
37 fromUser: [
38 { required: true, message: '请输入发件人邮箱', trigger: 'blur' },
39 { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
40 ],
41 user: [
42 { required: true, message: '请输入发件用户名', trigger: 'blur' }
43 ],
44 pass: [
45 { required: true, message: '密码不能为空', trigger: 'blur' }
46 ],
47 host: [
48 { required: true, message: 'SMTP地址不能为空', trigger: 'blur' }
49 ],
50 port: [
51 { required: true, message: 'SMTP端口不能为空', trigger: 'blur' }
52 ]
53 }
54 }
55 },
56 created() {
57 this.init()
58 },
59 methods: {
60 init() {
61 get().then(res => {
62 this.form = res
63 })
64 },
65 doSubmit() {
66 this.$refs['form'].validate((valid) => {
67 if (valid) {
68 this.loading = true
69 update(this.form).then(res => {
70 this.$notify({
71 title: '修改成功',
72 type: 'success',
73 duration: 2500
74 })
75 this.loading = false
76 }).catch(err => {
77 this.loading = false
78 console.log(err.response.data.message)
79 })
80 } else {
81 return false
82 }
83 })
84 }
85 }
86 }
87 </script>
88
89 <style scoped>
90
91 </style>
1 <template>
2 <el-tabs v-model="activeName" style="padding-left: 8px;">
3 <el-tab-pane label="邮箱配置" name="first">
4 <Config />
5 </el-tab-pane>
6 <el-tab-pane label="发送邮件" name="second">
7 <Send />
8 </el-tab-pane>
9 <el-tab-pane label="使用说明" name="third">
10 <div>
11 <blockquote class="my-blockquote"> 邮件服务器配置</blockquote>
12 <pre class="my-code">
13 # 邮件服务器的SMTP地址,可选,默认为smtp
14 # 邮件服务器的SMTP端口,可选,默认465或者25
15 # 发件人(必须正确,否则发送失败)
16 # 用户名,默认为发件人邮箱前缀
17 # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,如QQ和163等等)
18 # 是否开启ssl,默认开启</pre>
19 <blockquote class="my-blockquote">更多帮助</blockquote>
20 <pre class="my-code">更多帮助请查看文档:<a style="color:#009688" href="http://hutool.mydoc.io/#text_319499" target="_black">hutool工具包</a></pre>
21 </div>
22 </el-tab-pane>
23 </el-tabs>
24 </template>
25
26 <script>
27 import Config from './config'
28 import Send from './send'
29 export default {
30 name: 'Email',
31 components: { Config, Send },
32 data() {
33 return {
34 activeName: 'second'
35 }
36 }
37 }
38 </script>
39
40 <style scoped>
41 </style>
1 <template>
2 <div>
3 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
4 <el-form-item label="邮件标题" prop="subject">
5 <el-input v-model="form.subject" style="width: 646px" />
6 </el-form-item>
7 <el-form-item
8 v-for="(domain, index) in tos"
9 :key="domain.key"
10 :label="'收件邮箱' + (index === 0 ? '': index)"
11 >
12 <el-input v-model="domain.value" style="width: 550px" />
13 <el-button icon="el-icon-plus" @click="addDomain" />
14 <el-button style="margin-left:0;" icon="el-icon-minus" @click.prevent="removeDomain(domain)" />
15 </el-form-item>
16 <div ref="editor" class="editor" />
17 <el-button :loading="loading" style="margin-left:1.6%;" size="medium" type="primary" @click="doSubmit">发送邮件</el-button>
18 </el-form>
19 </div>
20 </template>
21
22 <script>
23 import { send } from '@/api/tools/email'
24 import { upload } from '@/utils/upload'
25 import { validEmail } from '@/utils/validate'
26 import { mapGetters } from 'vuex'
27 import E from 'wangeditor'
28 export default {
29 name: 'Index',
30 data() {
31 return {
32 loading: false, form: { subject: '', tos: [], content: '' },
33 tos: [{
34 value: ''
35 }],
36 rules: {
37 subject: [
38 { required: true, message: '标题不能为空', trigger: 'blur' }
39 ]
40 }
41 }
42 },
43 computed: {
44 ...mapGetters([
45 'imagesUploadApi'
46 ])
47 },
48 mounted() {
49 const _this = this
50 var editor = new E(this.$refs.editor)
51 // 自定义菜单配置
52 editor.customConfig.zIndex = 10
53 // 文件上传
54 editor.customConfig.customUploadImg = function(files, insert) {
55 // files 是 input 中选中的文件列表
56 // insert 是获取图片 url 后,插入到编辑器的方法
57 files.forEach(image => {
58 files.forEach(image => {
59 upload(_this.imagesUploadApi, image).then(data => {
60 insert(data.data.url)
61 })
62 })
63 })
64 }
65 editor.customConfig.onchange = (html) => {
66 this.form.content = html
67 }
68 editor.create()
69 },
70 methods: {
71 removeDomain(item) {
72 var index = this.tos.indexOf(item)
73 if (index !== -1 && this.tos.length !== 1) {
74 this.tos.splice(index, 1)
75 } else {
76 this.$message({
77 message: '请至少保留一位联系人',
78 type: 'warning'
79 })
80 }
81 },
82 addDomain() {
83 this.tos.push({
84 value: '',
85 key: Date.now()
86 })
87 },
88 doSubmit() {
89 const _this = this
90 this.$refs['form'].validate((valid) => {
91 this.form.tos = []
92 if (valid) {
93 let sub = false
94 this.tos.forEach(function(data, index) {
95 if (data.value === '') {
96 _this.$message({
97 message: '收件邮箱不能为空',
98 type: 'warning'
99 })
100 sub = true
101 } else if (validEmail(data.value)) {
102 _this.form.tos.push(data.value)
103 } else {
104 _this.$message({
105 message: '收件邮箱格式错误',
106 type: 'warning'
107 })
108 sub = true
109 }
110 })
111 if (sub) { return false }
112 this.loading = true
113 send(this.form).then(res => {
114 this.$notify({
115 title: '发送成功',
116 type: 'success',
117 duration: 2500
118 })
119 this.loading = false
120 }).catch(err => {
121 this.loading = false
122 console.log(err.response.data.message)
123 })
124 } else {
125 return false
126 }
127 })
128 }
129 }
130 }
131 </script>
132
133 <style scoped>
134 .editor{
135 text-align:left;
136 margin: 20px;
137 width: 730px;
138 }
139 ::v-deep .w-e-text-container {
140 height: 360px !important;
141 }
142 </style>
1 <template>
2 <el-tabs v-model="activeName" style="padding-left: 8px;" @tab-click="tabClick">
3 <el-tab-pane label="本地存储" name="first">
4 <Local ref="local" />
5 </el-tab-pane>
6 <el-tab-pane label="七牛云存储" name="second">
7 <QiNiu ref="qiNiu" />
8 </el-tab-pane>
9 </el-tabs>
10 </template>
11
12 <script>
13 import QiNiu from './qiniu/index'
14 import Local from './local/index'
15 export default {
16 name: 'Storage',
17 components: { QiNiu, Local },
18 data() {
19 return {
20 activeName: 'first'
21 }
22 },
23 methods: {
24 tabClick(name) {
25 if (this.activeName === 'first') {
26 this.$refs.local.crud.toQuery()
27 } else {
28 this.$refs.qiNiu.crud.toQuery()
29 }
30 }
31 }
32 }
33 </script>
34
35 <style scoped>
36 </style>
1 <template>
2 <div class="app-container" style="padding: 8px;">
3 <!--工具栏-->
4 <div class="head-container">
5 <div v-if="crud.props.searchToggle">
6 <!-- 搜索 -->
7 <el-input v-model="query.blurry" clearable size="small" placeholder="输入内容模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
8 <date-range-picker v-model="query.createTime" class="date-item" />
9 <rrOperation />
10 </div>
11 <crudOperation :permission="permission">
12 <!-- 新增 -->
13 <el-button
14 slot="left"
15 v-permission="['admin','storage:add']"
16 class="filter-item"
17 size="mini"
18 type="primary"
19 icon="el-icon-upload"
20 @click="crud.toAdd"
21 >上传
22 </el-button>
23 </crudOperation>
24 </div>
25 <!--表单组件-->
26 <el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.add ? '文件上传' : '编辑文件'" width="500px">
27 <el-form ref="form" :model="form" size="small" label-width="80px">
28 <el-form-item label="文件名">
29 <el-input v-model="form.name" style="width: 370px;" />
30 </el-form-item>
31 <!-- 上传文件 -->
32 <el-form-item v-if="crud.status.add" label="上传">
33 <el-upload
34 ref="upload"
35 :limit="1"
36 :before-upload="beforeUpload"
37 :auto-upload="false"
38 :headers="headers"
39 :on-success="handleSuccess"
40 :on-error="handleError"
41 :action="fileUploadApi + '?name=' + form.name"
42 >
43 <div class="eladmin-upload"><i class="el-icon-upload" /> 添加文件</div>
44 <div slot="tip" class="el-upload__tip">可上传任意格式文件,且不超过100M</div>
45 </el-upload>
46 </el-form-item>
47 </el-form>
48 <div slot="footer" class="dialog-footer">
49 <el-button type="text" @click="crud.cancelCU">取消</el-button>
50 <el-button v-if="crud.status.add" :loading="loading" type="primary" @click="upload">确认</el-button>
51 <el-button v-else :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
52 </div>
53 </el-dialog>
54 <!--表格渲染-->
55 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
56 <el-table-column type="selection" width="55" />
57 <el-table-column prop="name" label="文件名">
58 <template slot-scope="scope">
59 <el-popover
60 :content="'file/' + scope.row.type + '/' + scope.row.realName"
61 placement="top-start"
62 title="路径"
63 width="200"
64 trigger="hover"
65 >
66 <a
67 slot="reference"
68 :href="baseApi + '/file/' + scope.row.type + '/' + scope.row.realName"
69 class="el-link--primary"
70 style="word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color: #1890ff;font-size: 13px;"
71 target="_blank"
72 >
73 {{ scope.row.name }}
74 </a>
75 </el-popover>
76 </template>
77 </el-table-column>
78 <el-table-column prop="path" label="预览图">
79 <template slot-scope="{row}">
80 <el-image
81 :src=" baseApi + '/file/' + row.type + '/' + row.realName"
82 :preview-src-list="[baseApi + '/file/' + row.type + '/' + row.realName]"
83 fit="contain"
84 lazy
85 class="el-avatar"
86 >
87 <div slot="error">
88 <i class="el-icon-document" />
89 </div>
90 </el-image>
91 </template>
92 </el-table-column>
93 <el-table-column prop="suffix" label="文件类型" />
94 <el-table-column prop="type" label="类别" />
95 <el-table-column prop="size" label="大小" />
96 <el-table-column prop="operate" label="操作人" />
97 <el-table-column prop="createTime" label="创建日期" />
98 </el-table>
99 <!--分页组件-->
100 <pagination />
101 </div>
102 </template>
103
104 <script>
105 import { mapGetters } from 'vuex'
106 import { getToken } from '@/utils/auth'
107 import crudFile from '@/api/tools/localStorage'
108 import CRUD, { presenter, header, form, crud } from '@crud/crud'
109 import rrOperation from '@crud/RR.operation'
110 import crudOperation from '@crud/CRUD.operation'
111 import pagination from '@crud/Pagination'
112 import DateRangePicker from '@/components/DateRangePicker'
113
114 const defaultForm = { id: null, name: '' }
115 export default {
116 components: { pagination, crudOperation, rrOperation, DateRangePicker },
117 cruds() {
118 return CRUD({ title: '文件', url: 'api/localStorage', crudMethod: { ...crudFile }})
119 },
120 mixins: [presenter(), header(), form(defaultForm), crud()],
121 data() {
122 return {
123 delAllLoading: false,
124 loading: false,
125 headers: { 'Authorization': getToken() },
126 permission: {
127 edit: ['admin', 'storage:edit'],
128 del: ['admin', 'storage:del']
129 }
130 }
131 },
132 computed: {
133 ...mapGetters([
134 'baseApi',
135 'fileUploadApi'
136 ])
137 },
138 created() {
139 this.crud.optShow.add = false
140 },
141 methods: {
142 // 上传文件
143 upload() {
144 this.$refs.upload.submit()
145 },
146 beforeUpload(file) {
147 let isLt2M = true
148 isLt2M = file.size / 1024 / 1024 < 100
149 if (!isLt2M) {
150 this.loading = false
151 this.$message.error('上传文件大小不能超过 100MB!')
152 }
153 this.form.name = file.name
154 return isLt2M
155 },
156 handleSuccess(response, file, fileList) {
157 this.crud.notify('上传成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
158 this.$refs.upload.clearFiles()
159 this.crud.status.add = CRUD.STATUS.NORMAL
160 this.crud.resetForm()
161 this.crud.toQuery()
162 },
163 // 监听上传失败
164 handleError(e, file, fileList) {
165 const msg = JSON.parse(e.message)
166 this.$notify({
167 title: msg.message,
168 type: 'error',
169 duration: 2500
170 })
171 this.loading = false
172 }
173 }
174 }
175 </script>
176
177 <style scoped>
178 ::v-deep .el-image__error, .el-image__placeholder{
179 background: none;
180 }
181 ::v-deep .el-image-viewer__wrapper{
182 top: 55px;
183 }
184 </style>
1 <template>
2 <el-dialog :visible.sync="dialog" :close-on-click-modal="false" title="七牛云配置" append-to-body width="580px">
3 <el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="110px">
4 <el-form-item label="Access Key" prop="accessKey">
5 <el-input v-model="form.accessKey" style="width: 95%" placeholder="accessKey,在安全中心,秘钥管理中查看" />
6 </el-form-item>
7 <el-form-item label="Secret Key" prop="secretKey">
8 <el-input v-model="form.secretKey" type="password" style="width: 95%;" placeholder="secretKey,在安全中心,秘钥管理中查看" />
9 </el-form-item>
10 <el-form-item label="空间名称" prop="bucket">
11 <el-input v-model="form.bucket" style="width: 95%;" placeholder="存储空间名称作为唯一的 Bucket 识别符" />
12 </el-form-item>
13 <el-form-item label="外链域名" prop="host">
14 <el-input v-model="form.host" style="width: 95%;" placeholder="外链域名,可自定义,需在七牛云绑定" />
15 </el-form-item>
16 <el-form-item label="存储区域">
17 <el-select v-model="form.zone" placeholder="请选择存储区域">
18 <el-option
19 v-for="item in zones"
20 :key="item"
21 :label="item"
22 :value="item"
23 />
24 </el-select>
25 </el-form-item>
26 <el-form-item label="空间类型" prop="type">
27 <el-radio v-model="form.type" label="公开">公开</el-radio>
28 <el-radio v-model="form.type" label="私有">私有</el-radio>
29 </el-form-item>
30 </el-form>
31 <div slot="footer" class="dialog-footer">
32 <el-button type="text" @click="dialog = false">取消</el-button>
33 <el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
34 </div>
35 </el-dialog>
36 </template>
37
38 <script>
39 import { get, update } from '@/api/tools/qiniu'
40 export default {
41 data() {
42 return {
43 zones: ['华东', '华北', '华南', '北美', '东南亚'], dialog: false,
44 loading: false, form: { accessKey: '', secretKey: '', bucket: '', host: '', zone: '', type: '' },
45 rules: {
46 accessKey: [
47 { required: true, message: '请输入accessKey', trigger: 'blur' }
48 ],
49 secretKey: [
50 { required: true, message: '请输入secretKey', trigger: 'blur' }
51 ],
52 bucket: [
53 { required: true, message: '请输入空间名称', trigger: 'blur' }
54 ],
55 host: [
56 { required: true, message: '请输入外链域名', trigger: 'blur' }
57 ],
58 type: [
59 { required: true, message: '空间类型不能为空', trigger: 'blur' }
60 ]
61 }
62 }
63 },
64 methods: {
65 init() {
66 get().then(res => {
67 this.form = res
68 })
69 },
70 doSubmit() {
71 this.$refs['form'].validate((valid) => {
72 if (valid) {
73 this.loading = true
74 update(this.form).then(res => {
75 this.$notify({
76 title: '修改成功',
77 type: 'success',
78 duration: 2500
79 })
80 this.$parent.crud.toQuery()
81 this.loading = false
82 this.dialog = false
83 }).catch(err => {
84 this.loading = false
85 console.log(err.response.data.message)
86 })
87 } else {
88 return false
89 }
90 })
91 }
92 }
93 }
94 </script>
95
96 <style scoped>
97
98 </style>
1 <template>
2 <div class="app-container" style="padding: 8px;">
3 <!--表单组件-->
4 <eForm ref="form" />
5 <!-- 工具栏 -->
6 <div class="head-container">
7 <div v-if="crud.props.searchToggle">
8 <!-- 搜索 -->
9 <el-input v-model="query.key" clearable size="small" placeholder="输入文件名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
10 <date-range-picker v-model="query.createTime" class="date-item" />
11 <rrOperation />
12 </div>
13 <crudOperation :permission="permission">
14 <template slot="left">
15 <!-- 上传 -->
16 <el-button class="filter-item" size="mini" type="primary" icon="el-icon-upload" @click="dialog = true">上传</el-button>
17 <!-- 同步 -->
18 <el-button :icon="icon" class="filter-item" size="mini" type="warning" @click="synchronize">同步</el-button>
19 <!-- 配置 -->
20 <el-button
21 class="filter-item"
22 size="mini"
23 type="success"
24 icon="el-icon-s-tools"
25 @click="doConfig"
26 >配置</el-button>
27 </template>
28 </crudOperation>
29 <!-- 文件上传 -->
30 <el-dialog :visible.sync="dialog" :close-on-click-modal="false" append-to-body width="500px" @close="doSubmit">
31 <el-upload
32 :before-remove="handleBeforeRemove"
33 :on-success="handleSuccess"
34 :on-error="handleError"
35 :file-list="fileList"
36 :headers="headers"
37 :action="qiNiuUploadApi"
38 class="upload-demo"
39 multiple
40 >
41 <el-button size="small" type="primary">点击上传</el-button>
42 <div slot="tip" style="display: block;" class="el-upload__tip">请勿上传违法文件,且文件不超过15M</div>
43 </el-upload>
44 <div slot="footer" class="dialog-footer">
45 <el-button type="primary" @click="doSubmit">确认</el-button>
46 </div>
47 </el-dialog>
48 <!--表格渲染-->
49 <el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
50 <el-table-column type="selection" width="55" />
51 <el-table-column prop="name" :show-overflow-tooltip="true" label="文件名">
52 <template slot-scope="scope">
53 <a href="JavaScript:" class="el-link el-link--primary" target="_blank" type="primary" @click="download(scope.row.id)">{{ scope.row.key }}</a>
54 </template>
55 </el-table-column>
56 <el-table-column :show-overflow-tooltip="true" prop="suffix" label="文件类型" @selection-change="crud.selectionChangeHandler" />
57 <el-table-column prop="bucket" label="空间名称" />
58 <el-table-column prop="size" label="文件大小" />
59 <el-table-column prop="type" label="空间类型" />
60 <el-table-column prop="updateTime" label="创建日期" />
61 </el-table>
62 <!--分页组件-->
63 <pagination />
64 </div>
65 </div>
66 </template>
67
68 <script>
69 import crudQiNiu from '@/api/tools/qiniu'
70 import { mapGetters } from 'vuex'
71 import { getToken } from '@/utils/auth'
72 import eForm from './form'
73 import CRUD, { presenter, header, crud } from '@crud/crud'
74 import rrOperation from '@crud/RR.operation'
75 import crudOperation from '@crud/CRUD.operation'
76 import pagination from '@crud/Pagination'
77 import DateRangePicker from '@/components/DateRangePicker'
78
79 export default {
80 components: { eForm, pagination, crudOperation, rrOperation, DateRangePicker },
81 cruds() {
82 return CRUD({ title: '七牛云文件', url: 'api/qiNiuContent', crudMethod: { ...crudQiNiu }})
83 },
84 mixins: [presenter(), header(), crud()],
85 data() {
86 return {
87 permission: {
88 del: ['admin', 'storage:del']
89 },
90 title: '文件', dialog: false,
91 icon: 'el-icon-refresh',
92 url: '', headers: { 'Authorization': getToken() },
93 dialogImageUrl: '', dialogVisible: false, fileList: [], files: [], newWin: null
94 }
95 },
96 computed: {
97 ...mapGetters([
98 'qiNiuUploadApi'
99 ])
100 },
101 watch: {
102 url(newVal, oldVal) {
103 if (newVal && this.newWin) {
104 this.newWin.sessionStorage.clear()
105 this.newWin.location.href = newVal
106 // 重定向后把url和newWin重置
107 this.url = ''
108 this.newWin = null
109 }
110 }
111 },
112 created() {
113 this.crud.optShow.add = false
114 this.crud.optShow.edit = false
115 },
116 methods: {
117 // 七牛云配置
118 doConfig() {
119 const _this = this.$refs.form
120 _this.init()
121 _this.dialog = true
122 },
123 handleSuccess(response, file, fileList) {
124 const uid = file.uid
125 const id = response.id
126 this.files.push({ uid, id })
127 },
128 handleBeforeRemove(file, fileList) {
129 for (let i = 0; i < this.files.length; i++) {
130 if (this.files[i].uid === file.uid) {
131 crudQiNiu.del([this.files[i].id]).then(res => {})
132 return true
133 }
134 }
135 },
136 handlePictureCardPreview(file) {
137 this.dialogImageUrl = file.url
138 this.dialogVisible = true
139 },
140 // 刷新列表数据
141 doSubmit() {
142 this.fileList = []
143 this.dialogVisible = false
144 this.dialogImageUrl = ''
145 this.dialog = false
146 this.crud.toQuery()
147 },
148 // 监听上传失败
149 handleError(e, file, fileList) {
150 const msg = JSON.parse(e.message)
151 this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR)
152 },
153 // 下载文件
154 download(id) {
155 this.downloadLoading = true
156 // 先打开一个空的新窗口,再请求
157 this.newWin = window.open()
158 crudQiNiu.download(id).then(res => {
159 this.downloadLoading = false
160 this.url = res.url
161 }).catch(err => {
162 this.downloadLoading = false
163 console.log(err.response.data.message)
164 })
165 },
166 // 同步数据
167 synchronize() {
168 this.icon = 'el-icon-loading'
169 crudQiNiu.sync().then(res => {
170 this.icon = 'el-icon-refresh'
171 this.$message({
172 showClose: true,
173 message: '数据同步成功',
174 type: 'success',
175 duration: 1500
176 })
177 this.crud.toQuery()
178 }).catch(err => {
179 this.icon = 'el-icon-refresh'
180 console.log(err.response.data.message)
181 })
182 }
183 }
184 }
185 </script>
186
187 <style scoped>
188
189 </style>
1 <template>
2 <elFrame :src="swaggerApi" />
3 </template>
4 <script>
5 import { mapGetters } from 'vuex'
6 import elFrame from '@/components/Iframe/index'
7 export default {
8 name: 'Swagger',
9 components: { elFrame },
10 computed: {
11 ...mapGetters([
12 'swaggerApi'
13 ])
14 }
15 }
16 </script>
1 'use strict'
2 const path = require('path')
3 const defaultSettings = require('./src/settings.js')
4
5 function resolve(dir) {
6 return path.join(__dirname, dir)
7 }
8
9 console.log(process.env, 'process.env')
10
11 const name = defaultSettings.title // 网址标题
12 const port = 8013 // 端口配置
13
14 // All configuration item explanations can be find in https://cli.vuejs.org/config/
15 module.exports = {
16 // hash 模式下可使用
17 // publicPath: process.env.NODE_ENV === 'development' ? '/' : './',
18 publicPath: '/',
19 outputDir: 'dist',
20 assetsDir: 'static',
21 lintOnSave: process.env.NODE_ENV === 'development',
22 productionSourceMap: false,
23 devServer: {
24 port: port,
25 open: true,
26 overlay: {
27 warnings: false,
28 errors: true
29 },
30 proxy: {
31 '/api': {
32 target: process.env.VUE_APP_BASE_API,
33 changeOrigin: true,
34 pathRewrite: {
35 '^/api': 'api'
36 }
37 },
38 '/auth': {
39 target: process.env.VUE_APP_BASE_API,
40 changeOrigin: true,
41 pathRewrite: {
42 '^/auth': 'auth'
43 }
44 }
45 }
46 },
47 configureWebpack: {
48 // provide the app's title in webpack's name field, so that
49 // it can be accessed in index.html to inject the correct title.
50 name: name,
51 resolve: {
52 alias: {
53 '@': resolve('src'),
54 '@crud': resolve('src/components/Crud')
55 }
56 }
57 },
58 chainWebpack(config) {
59 config.plugins.delete('preload') // TODO: need test
60 config.plugins.delete('prefetch') // TODO: need test
61
62 // set svg-sprite-loader
63 config.module
64 .rule('svg')
65 .exclude.add(resolve('src/assets/icons'))
66 .end()
67 config.module
68 .rule('icons')
69 .test(/\.svg$/)
70 .include.add(resolve('src/assets/icons'))
71 .end()
72 .use('svg-sprite-loader')
73 .loader('svg-sprite-loader')
74 .options({
75 symbolId: 'icon-[name]'
76 })
77 .end()
78
79 // set preserveWhitespace
80 config.module
81 .rule('vue')
82 .use('vue-loader')
83 .loader('vue-loader')
84 .tap(options => {
85 options.compilerOptions.preserveWhitespace = true
86 return options
87 })
88 .end()
89
90 config
91 // https://webpack.js.org/configuration/devtool/#development
92 .when(process.env.NODE_ENV === 'development',
93 config => config.devtool('cheap-source-map')
94 )
95
96 config
97 .when(process.env.NODE_ENV !== 'development',
98 config => {
99 config
100 .plugin('ScriptExtHtmlWebpackPlugin')
101 .after('html')
102 .use('script-ext-html-webpack-plugin', [{
103 // `runtime` must same as runtimeChunk name. default is `runtime`
104 inline: /runtime\..*\.js$/
105 }])
106 .end()
107 config
108 .optimization.splitChunks({
109 chunks: 'all',
110 cacheGroups: {
111 libs: {
112 name: 'chunk-libs',
113 test: /[\\/]node_modules[\\/]/,
114 priority: 10,
115 chunks: 'initial' // only package third parties that are initially dependent
116 },
117 elementUI: {
118 name: 'chunk-elementUI', // split elementUI into a single package
119 priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
120 test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
121 },
122 commons: {
123 name: 'chunk-commons',
124 test: resolve('src/components'), // can customize your rules
125 minChunks: 3, // minimum common number
126 priority: 5,
127 reuseExistingChunk: true
128 }
129 }
130 })
131 config.optimization.runtimeChunk('single')
132 }
133 )
134 },
135 transpileDependencies: [
136 'vue-echarts',
137 'resize-detector'
138 ]
139 }