init
This commit is contained in:
		
						commit
						3a8034ddbd
					
				|  | @ -0,0 +1,14 @@ | ||||||
|  | # https://editorconfig.org | ||||||
|  | root = true | ||||||
|  | 
 | ||||||
|  | [*] | ||||||
|  | charset = utf-8 | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 2 | ||||||
|  | end_of_line = lf | ||||||
|  | insert_final_newline = true | ||||||
|  | trim_trailing_whitespace = true | ||||||
|  | 
 | ||||||
|  | [*.md] | ||||||
|  | insert_final_newline = false | ||||||
|  | trim_trailing_whitespace = false | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | # just a flag | ||||||
|  | ENV = 'development' | ||||||
|  | 
 | ||||||
|  | # base api | ||||||
|  | # VUE_APP_BASE_API = '/dev-api' | ||||||
|  | #VUE_APP_BASE_API = 'http://admin.pc.seemore.club/' | ||||||
|  | VUE_APP_BASE_API = 'http://office.xianci.info:19091/' | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | # just a flag | ||||||
|  | ENV = 'production' | ||||||
|  | 
 | ||||||
|  | # base api | ||||||
|  | #VUE_APP_BASE_API = '/prod-api' | ||||||
|  | VUE_APP_BASE_API = 'http://admin.pc.seemore.club/' | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | NODE_ENV = production | ||||||
|  | 
 | ||||||
|  | # just a flag | ||||||
|  | ENV = 'staging' | ||||||
|  | 
 | ||||||
|  | # base api | ||||||
|  | VUE_APP_BASE_API = '/stage-api' | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | build/*.js | ||||||
|  | src/assets | ||||||
|  | public | ||||||
|  | dist | ||||||
|  | @ -0,0 +1,198 @@ | ||||||
|  | module.exports = { | ||||||
|  |   root: true, | ||||||
|  |   parserOptions: { | ||||||
|  |     parser: 'babel-eslint', | ||||||
|  |     sourceType: 'module' | ||||||
|  |   }, | ||||||
|  |   env: { | ||||||
|  |     browser: true, | ||||||
|  |     node: true, | ||||||
|  |     es6: true, | ||||||
|  |   }, | ||||||
|  |   extends: ['plugin:vue/recommended', 'eslint:recommended'], | ||||||
|  | 
 | ||||||
|  |   // add your custom rules here
 | ||||||
|  |   //it is base on https://github.com/vuejs/eslint-config-vue
 | ||||||
|  |   rules: { | ||||||
|  |     "vue/max-attributes-per-line": [2, { | ||||||
|  |       "singleline": 10, | ||||||
|  |       "multiline": { | ||||||
|  |         "max": 1, | ||||||
|  |         "allowFirstLine": false | ||||||
|  |       } | ||||||
|  |     }], | ||||||
|  |     "vue/singleline-html-element-content-newline": "off", | ||||||
|  |     "vue/multiline-html-element-content-newline":"off", | ||||||
|  |     "vue/name-property-casing": ["error", "PascalCase"], | ||||||
|  |     "vue/no-v-html": "off", | ||||||
|  |     'accessor-pairs': 2, | ||||||
|  |     'arrow-spacing': [2, { | ||||||
|  |       'before': true, | ||||||
|  |       'after': true | ||||||
|  |     }], | ||||||
|  |     'block-spacing': [2, 'always'], | ||||||
|  |     'brace-style': [2, '1tbs', { | ||||||
|  |       'allowSingleLine': true | ||||||
|  |     }], | ||||||
|  |     'camelcase': [0, { | ||||||
|  |       'properties': 'always' | ||||||
|  |     }], | ||||||
|  |     'comma-dangle': [2, 'only-multiline'], | ||||||
|  |     'comma-spacing': [2, { | ||||||
|  |       'before': false, | ||||||
|  |       'after': true | ||||||
|  |     }], | ||||||
|  |     'comma-style': [2, 'last'], | ||||||
|  |     'constructor-super': 2, | ||||||
|  |     'curly': [2, 'multi-line'], | ||||||
|  |     'dot-location': [2, 'property'], | ||||||
|  |     'eol-last': 2, | ||||||
|  |     'eqeqeq': ["error", "always", {"null": "ignore"}], | ||||||
|  |     'generator-star-spacing': [2, { | ||||||
|  |       'before': true, | ||||||
|  |       'after': true | ||||||
|  |     }], | ||||||
|  |     'handle-callback-err': [2, '^(err|error)$'], | ||||||
|  |     'indent': [2, 2, { | ||||||
|  |       'SwitchCase': 1 | ||||||
|  |     }], | ||||||
|  |     'jsx-quotes': [2, 'prefer-single'], | ||||||
|  |     'key-spacing': [2, { | ||||||
|  |       'beforeColon': false, | ||||||
|  |       'afterColon': true | ||||||
|  |     }], | ||||||
|  |     'keyword-spacing': [2, { | ||||||
|  |       'before': true, | ||||||
|  |       'after': true | ||||||
|  |     }], | ||||||
|  |     'new-cap': [2, { | ||||||
|  |       'newIsCap': true, | ||||||
|  |       'capIsNew': false | ||||||
|  |     }], | ||||||
|  |     'new-parens': 2, | ||||||
|  |     'no-array-constructor': 2, | ||||||
|  |     'no-caller': 2, | ||||||
|  |     'no-console': 'off', | ||||||
|  |     'no-class-assign': 2, | ||||||
|  |     'no-cond-assign': 2, | ||||||
|  |     'no-const-assign': 2, | ||||||
|  |     'no-control-regex': 0, | ||||||
|  |     'no-delete-var': 2, | ||||||
|  |     'no-dupe-args': 2, | ||||||
|  |     'no-dupe-class-members': 2, | ||||||
|  |     'no-dupe-keys': 2, | ||||||
|  |     'no-duplicate-case': 2, | ||||||
|  |     'no-empty-character-class': 2, | ||||||
|  |     'no-empty-pattern': 2, | ||||||
|  |     'no-eval': 2, | ||||||
|  |     'no-ex-assign': 2, | ||||||
|  |     'no-extend-native': 2, | ||||||
|  |     'no-extra-bind': 2, | ||||||
|  |     'no-extra-boolean-cast': 2, | ||||||
|  |     'no-extra-parens': [2, 'functions'], | ||||||
|  |     'no-fallthrough': 2, | ||||||
|  |     'no-floating-decimal': 2, | ||||||
|  |     'no-func-assign': 2, | ||||||
|  |     'no-implied-eval': 2, | ||||||
|  |     'no-inner-declarations': [2, 'functions'], | ||||||
|  |     'no-invalid-regexp': 2, | ||||||
|  |     'no-irregular-whitespace': 2, | ||||||
|  |     'no-iterator': 2, | ||||||
|  |     'no-label-var': 2, | ||||||
|  |     'no-labels': [2, { | ||||||
|  |       'allowLoop': false, | ||||||
|  |       'allowSwitch': false | ||||||
|  |     }], | ||||||
|  |     'no-lone-blocks': 2, | ||||||
|  |     'no-mixed-spaces-and-tabs': 2, | ||||||
|  |     'no-multi-spaces': 2, | ||||||
|  |     'no-multi-str': 2, | ||||||
|  |     'no-multiple-empty-lines': [2, { | ||||||
|  |       'max': 1 | ||||||
|  |     }], | ||||||
|  |     'no-native-reassign': 2, | ||||||
|  |     'no-negated-in-lhs': 2, | ||||||
|  |     'no-new-object': 2, | ||||||
|  |     'no-new-require': 2, | ||||||
|  |     'no-new-symbol': 2, | ||||||
|  |     'no-new-wrappers': 2, | ||||||
|  |     'no-obj-calls': 2, | ||||||
|  |     'no-octal': 2, | ||||||
|  |     'no-octal-escape': 2, | ||||||
|  |     'no-path-concat': 2, | ||||||
|  |     'no-proto': 2, | ||||||
|  |     'no-redeclare': 2, | ||||||
|  |     'no-regex-spaces': 2, | ||||||
|  |     'no-return-assign': [2, 'except-parens'], | ||||||
|  |     'no-self-assign': 2, | ||||||
|  |     'no-self-compare': 2, | ||||||
|  |     'no-sequences': 2, | ||||||
|  |     'no-shadow-restricted-names': 2, | ||||||
|  |     'no-spaced-func': 2, | ||||||
|  |     'no-sparse-arrays': 2, | ||||||
|  |     'no-this-before-super': 2, | ||||||
|  |     'no-throw-literal': 2, | ||||||
|  |     'no-trailing-spaces': 2, | ||||||
|  |     'no-undef': 2, | ||||||
|  |     'no-undef-init': 2, | ||||||
|  |     'no-unexpected-multiline': 2, | ||||||
|  |     'no-unmodified-loop-condition': 2, | ||||||
|  |     'no-unneeded-ternary': [2, { | ||||||
|  |       'defaultAssignment': false | ||||||
|  |     }], | ||||||
|  |     'no-unreachable': 2, | ||||||
|  |     'no-unsafe-finally': 2, | ||||||
|  |     'no-unused-vars': [2, { | ||||||
|  |       'vars': 'all', | ||||||
|  |       'args': 'none' | ||||||
|  |     }], | ||||||
|  |     'no-useless-call': 2, | ||||||
|  |     'no-useless-computed-key': 2, | ||||||
|  |     'no-useless-constructor': 2, | ||||||
|  |     'no-useless-escape': 0, | ||||||
|  |     'no-whitespace-before-property': 2, | ||||||
|  |     'no-with': 2, | ||||||
|  |     'one-var': [2, { | ||||||
|  |       'initialized': 'never' | ||||||
|  |     }], | ||||||
|  |     'operator-linebreak': [2, 'after', { | ||||||
|  |       'overrides': { | ||||||
|  |         '?': 'before', | ||||||
|  |         ':': 'before' | ||||||
|  |       } | ||||||
|  |     }], | ||||||
|  |     'padded-blocks': [2, 'never'], | ||||||
|  |     'quotes': [2, 'single', { | ||||||
|  |       'avoidEscape': true, | ||||||
|  |       'allowTemplateLiterals': true | ||||||
|  |     }], | ||||||
|  |     'semi': [2, 'never'], | ||||||
|  |     'semi-spacing': [2, { | ||||||
|  |       'before': false, | ||||||
|  |       'after': true | ||||||
|  |     }], | ||||||
|  |     'space-before-blocks': [2, 'always'], | ||||||
|  |     'space-before-function-paren': [2, 'never'], | ||||||
|  |     'space-in-parens': [2, 'never'], | ||||||
|  |     'space-infix-ops': 2, | ||||||
|  |     'space-unary-ops': [2, { | ||||||
|  |       'words': true, | ||||||
|  |       'nonwords': false | ||||||
|  |     }], | ||||||
|  |     'spaced-comment': [2, 'always', { | ||||||
|  |       'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] | ||||||
|  |     }], | ||||||
|  |     'template-curly-spacing': [2, 'never'], | ||||||
|  |     'use-isnan': 2, | ||||||
|  |     'valid-typeof': 2, | ||||||
|  |     'wrap-iife': [2, 'any'], | ||||||
|  |     'yield-star-spacing': [2, 'both'], | ||||||
|  |     'yoda': [2, 'never'], | ||||||
|  |     'prefer-const': 2, | ||||||
|  |     'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, | ||||||
|  |     'object-curly-spacing': [2, 'always', { | ||||||
|  |       objectsInObjects: false | ||||||
|  |     }], | ||||||
|  |     'array-bracket-spacing': [2, 'never'] | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | .DS_Store | ||||||
|  | node_modules/ | ||||||
|  | dist/ | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | **/*.log | ||||||
|  | /web | ||||||
|  | /web.*/ | ||||||
|  | /web-*-* | ||||||
|  | 
 | ||||||
|  | tests/**/coverage/ | ||||||
|  | tests/e2e/reports | ||||||
|  | selenium-debug.log | ||||||
|  | 
 | ||||||
|  | # Editor directories and files | ||||||
|  | .idea | ||||||
|  | .vscode | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.local | ||||||
|  | 
 | ||||||
|  | package-lock.json | ||||||
|  | yarn.lock | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | language: node_js | ||||||
|  | node_js: 10 | ||||||
|  | script: npm run test | ||||||
|  | notifications: | ||||||
|  |   email: false | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2017-present PanJiaChen | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | module.exports = { | ||||||
|  |   presets: [ | ||||||
|  |     // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
 | ||||||
|  |     '@vue/cli-plugin-babel/preset' | ||||||
|  |   ], | ||||||
|  |   'env': { | ||||||
|  |     'development': { | ||||||
|  |       // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
 | ||||||
|  |       // This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
 | ||||||
|  |       // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html
 | ||||||
|  |       'plugins': ['dynamic-import-node'] | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | const { run } = require('runjs') | ||||||
|  | const chalk = require('chalk') | ||||||
|  | const config = require('../vue.config.js') | ||||||
|  | const rawArgv = process.argv.slice(2) | ||||||
|  | const args = rawArgv.join(' ') | ||||||
|  | 
 | ||||||
|  | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { | ||||||
|  |   const report = rawArgv.includes('--report') | ||||||
|  | 
 | ||||||
|  |   run(`vue-cli-service build ${args}`) | ||||||
|  | 
 | ||||||
|  |   const port = 9526 | ||||||
|  |   const publicPath = config.publicPath | ||||||
|  | 
 | ||||||
|  |   var connect = require('connect') | ||||||
|  |   var serveStatic = require('serve-static') | ||||||
|  |   const app = connect() | ||||||
|  | 
 | ||||||
|  |   app.use( | ||||||
|  |     publicPath, | ||||||
|  |     serveStatic('./dist', { | ||||||
|  |       index: ['index.html', '/'] | ||||||
|  |     }) | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |   app.listen(port, function () { | ||||||
|  |     console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`)) | ||||||
|  |     if (report) { | ||||||
|  |       console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   }) | ||||||
|  | } else { | ||||||
|  |   run(`vue-cli-service build ${args}`) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | {  | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "baseUrl": "./", | ||||||
|  |     "paths": { | ||||||
|  |         "@/*": ["src/*"] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "exclude": ["node_modules", "dist"] | ||||||
|  | } | ||||||
|  | @ -0,0 +1,117 @@ | ||||||
|  | { | ||||||
|  |   "name": "vue-element-admin", | ||||||
|  |   "version": "4.4.0", | ||||||
|  |   "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features", | ||||||
|  |   "author": "Pan <panfree23@gmail.com>", | ||||||
|  |   "scripts": { | ||||||
|  |     "dev": "vue-cli-service serve", | ||||||
|  |     "lint": "eslint --ext .js,.vue src", | ||||||
|  |     "build:prod": "vue-cli-service build", | ||||||
|  |     "build:dev": "vue-cli-service build --mode development", | ||||||
|  |     "build:stage": "vue-cli-service build --mode staging", | ||||||
|  |     "preview": "node build/index.js --preview", | ||||||
|  |     "new": "plop", | ||||||
|  |     "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", | ||||||
|  |     "test:unit": "jest --clearCache && vue-cli-service test:unit", | ||||||
|  |     "test:ci": "npm run lint && npm run test:unit" | ||||||
|  |   }, | ||||||
|  |   "dependencies": { | ||||||
|  |     "axios": "0.18.1", | ||||||
|  |     "clipboard": "2.0.4", | ||||||
|  |     "co": "^4.6.0", | ||||||
|  |     "codemirror": "5.45.0", | ||||||
|  |     "core-js": "3.6.5", | ||||||
|  |     "crypto-js": "^4.1.1", | ||||||
|  |     "driver.js": "0.9.5", | ||||||
|  |     "dropzone": "5.5.1", | ||||||
|  |     "echarts": "^4.2.1", | ||||||
|  |     "element-ui": "2.13.2", | ||||||
|  |     "file-saver": "2.0.1", | ||||||
|  |     "fuse.js": "3.4.4", | ||||||
|  |     "js-cookie": "2.2.0", | ||||||
|  |     "js-sha1": "^0.6.0", | ||||||
|  |     "jsonlint": "1.6.3", | ||||||
|  |     "jszip": "3.2.1", | ||||||
|  |     "normalize.css": "7.0.0", | ||||||
|  |     "nprogress": "0.2.0", | ||||||
|  |     "path-to-regexp": "2.4.0", | ||||||
|  |     "screenfull": "4.2.0", | ||||||
|  |     "script-loader": "0.7.2", | ||||||
|  |     "sortablejs": "1.8.4", | ||||||
|  |     "tui-editor": "1.3.3", | ||||||
|  |     "vue": "2.6.10", | ||||||
|  |     "vue-calendar-component": "^2.8.2", | ||||||
|  |     "vue-count-to": "1.0.13", | ||||||
|  |     "vue-router": "3.0.2", | ||||||
|  |     "vue-splitpane": "1.0.4", | ||||||
|  |     "vuedraggable": "2.20.0", | ||||||
|  |     "vuex": "3.1.0", | ||||||
|  |     "wqcalendar": "^1.0.1", | ||||||
|  |     "xlsx": "0.14.1" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@vue/cli-plugin-babel": "4.4.4", | ||||||
|  |     "@vue/cli-plugin-eslint": "4.4.4", | ||||||
|  |     "@vue/cli-plugin-unit-jest": "4.4.4", | ||||||
|  |     "@vue/cli-service": "4.4.4", | ||||||
|  |     "@vue/test-utils": "1.0.0-beta.29", | ||||||
|  |     "autoprefixer": "9.5.1", | ||||||
|  |     "babel-eslint": "10.1.0", | ||||||
|  |     "babel-jest": "23.6.0", | ||||||
|  |     "babel-plugin-dynamic-import-node": "2.3.3", | ||||||
|  |     "chalk": "2.4.2", | ||||||
|  |     "chokidar": "2.1.5", | ||||||
|  |     "connect": "3.6.6", | ||||||
|  |     "eslint": "6.7.2", | ||||||
|  |     "eslint-plugin-vue": "6.2.2", | ||||||
|  |     "html-webpack-plugin": "3.2.0", | ||||||
|  |     "husky": "1.3.1", | ||||||
|  |     "lint-staged": "8.1.5", | ||||||
|  |     "mockjs": "1.0.1-beta3", | ||||||
|  |     "plop": "2.3.0", | ||||||
|  |     "runjs": "4.3.2", | ||||||
|  |     "sass": "1.26.2", | ||||||
|  |     "sass-loader": "8.0.2", | ||||||
|  |     "script-ext-html-webpack-plugin": "2.1.3", | ||||||
|  |     "serve-static": "1.13.2", | ||||||
|  |     "svg-sprite-loader": "4.1.3", | ||||||
|  |     "svgo": "1.2.0", | ||||||
|  |     "vue-template-compiler": "2.6.10" | ||||||
|  |   }, | ||||||
|  |   "browserslist": [ | ||||||
|  |     "> 1%", | ||||||
|  |     "last 2 versions" | ||||||
|  |   ], | ||||||
|  |   "bugs": { | ||||||
|  |     "url": "https://github.com/PanJiaChen/vue-element-admin/issues" | ||||||
|  |   }, | ||||||
|  |   "engines": { | ||||||
|  |     "node": ">=8.9", | ||||||
|  |     "npm": ">= 3.0.0" | ||||||
|  |   }, | ||||||
|  |   "keywords": [ | ||||||
|  |     "vue", | ||||||
|  |     "admin", | ||||||
|  |     "dashboard", | ||||||
|  |     "element-ui", | ||||||
|  |     "boilerplate", | ||||||
|  |     "admin-template", | ||||||
|  |     "management-system" | ||||||
|  |   ], | ||||||
|  |   "license": "MIT", | ||||||
|  |   "lint-staged": { | ||||||
|  |     "src/**/*.{js,vue}": [ | ||||||
|  |       "eslint --fix", | ||||||
|  |       "git add" | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   "husky": { | ||||||
|  |     "hooks": { | ||||||
|  |       "pre-commit": "lint-staged" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "git+https://github.com/PanJiaChen/vue-element-admin.git" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | module.exports = { | ||||||
|  |   plugins: { | ||||||
|  |     autoprefixer: {} | ||||||
|  |   } | ||||||
|  | } | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.0 KiB | 
|  | @ -0,0 +1,15 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8"> | ||||||
|  |     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | ||||||
|  |     <meta name="renderer" content="webkit"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | ||||||
|  |     <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||||||
|  |     <title><%= webpackConfig.name %></title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div id="app"></div> | ||||||
|  |     <!-- built files will be auto injected --> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | <template> | ||||||
|  |   <div id="app"> | ||||||
|  |     <router-view /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | // 系统管理
 | ||||||
|  | import request from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 系统列表
 | ||||||
|  | export function info() { | ||||||
|  |   return request({ | ||||||
|  |     url: "/sysConfig", | ||||||
|  |     method: "get", | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function update(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/sysConfig", | ||||||
|  |     method: "post", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | // 用户管理
 | ||||||
|  | import request from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 用户列表
 | ||||||
|  | export function list(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/consumer", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function info(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: `/consumer/getPhoneInfoById`, | ||||||
|  |     method: "post", | ||||||
|  |     data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | // 标签管理
 | ||||||
|  | import request from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 标签列表
 | ||||||
|  | export function list(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/evaluateLabel/list", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function update(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/evaluateLabel", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function groupList(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/evaluateLabel/groupList", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | export function groupCreate(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/evaluateLabel/groupCreate", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | // 订单管理
 | ||||||
|  | import request from "@/utils/request"; | ||||||
|  | 
 | ||||||
|  | // 订单列表
 | ||||||
|  | export function list(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: "/order", | ||||||
|  |     method: "post", | ||||||
|  |     data: data, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | import request from '@/utils/request' | ||||||
|  | 
 | ||||||
|  | export function login(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: '/commonLogin/login', | ||||||
|  |     method: 'post', | ||||||
|  |     data | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // export function getInfo() {
 | ||||||
|  | //   return request({
 | ||||||
|  | //     url: `/system/info/getInfo`,
 | ||||||
|  | //     method: 'get',
 | ||||||
|  | //   })
 | ||||||
|  | // }
 | ||||||
|  | export function getInfo() { | ||||||
|  |   return request({ | ||||||
|  |     url: `/staff/info`, | ||||||
|  |     method: 'get', | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export function editPassword(data) { | ||||||
|  |   return request({ | ||||||
|  |     url: '/admin/admin/editPassword', | ||||||
|  |     method: 'post', | ||||||
|  |     data | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function logout() { | ||||||
|  |   return request({ | ||||||
|  |     url: '/vue-element-admin/user/logout', | ||||||
|  |     method: 'post' | ||||||
|  |   }) | ||||||
|  | } | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 160 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 96 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.7 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -0,0 +1,145 @@ | ||||||
|  | <template> | ||||||
|  |   <el-cascader ref="address-cascader" v-model="data" :props="props" :options="dataOptions" :multiple="multiple" :placeholder="placeholder" :clearable="clearable" :style="{'min-width': (level +1) * 75 + 'px'}" @change="cascaderChange" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { getSysArea } from '@/api/basic' | ||||||
|  | export default { | ||||||
|  |   name: 'AddressCascader', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: Array, | ||||||
|  |       default: () => [], | ||||||
|  |     }, | ||||||
|  |     level: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 2, | ||||||
|  |     }, | ||||||
|  |     multiple: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false, | ||||||
|  |     }, | ||||||
|  |     placeholder: { | ||||||
|  |       type: String, | ||||||
|  |       default: '请选择' | ||||||
|  |     }, | ||||||
|  |     clearable: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       data: [], | ||||||
|  |       props: { | ||||||
|  |         lazy: true, | ||||||
|  |         multiple: this.multiple, | ||||||
|  |         lazyLoad: new Function(), | ||||||
|  |         value: 'id', | ||||||
|  |         label: 'name', | ||||||
|  |         checkStrictly: true | ||||||
|  |       }, | ||||||
|  |       dataOptions: [] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value: { | ||||||
|  |       deep: true, | ||||||
|  |       handler() { | ||||||
|  |         this.initValue() | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.initValue() | ||||||
|  |     this.initLazyLoad() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initValue() { | ||||||
|  |       // console.log(this.level) | ||||||
|  |       this.data = this.value || [] | ||||||
|  |       if (this.level && this.multiple) this.initDataOptions() | ||||||
|  |     }, | ||||||
|  |     async initDataOptions() { | ||||||
|  |       const that = this | ||||||
|  |       const { data, props } = this | ||||||
|  |       // console.log(data) | ||||||
|  |       if (!data || !data.length) return | ||||||
|  |       if (props.multiple) { | ||||||
|  |         if (data.findIndex(add => add === undefined) !== -1) return | ||||||
|  |         const dataOptions = await this.getAddress(0) | ||||||
|  |         const pros = data.map((item) => { | ||||||
|  |           return item.reduce(async(prev, current, index) => { | ||||||
|  |             const prevData = await prev | ||||||
|  |             const addressItem = prevData.find((it) => current === it.id) | ||||||
|  |             let children = addressItem.children | ||||||
|  |             if (!children && index < that.level) { | ||||||
|  |               children = await this.getAddress(current) | ||||||
|  |               if (index >= that.level - 1) { | ||||||
|  |                 children = children.map(ars => { | ||||||
|  |                   ars.leaf = true | ||||||
|  |                   return ars | ||||||
|  |                 }) | ||||||
|  |               } | ||||||
|  |               addressItem.children = children | ||||||
|  |             } | ||||||
|  |             return children | ||||||
|  |           }, dataOptions) | ||||||
|  |         }) | ||||||
|  |         await Promise.all(pros) | ||||||
|  |         this.$set(this, 'dataOptions', dataOptions) | ||||||
|  |       } else { | ||||||
|  |         this.dataOptions = [] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     initLazyLoad() { | ||||||
|  |       this.props.lazyLoad = (node, resolve) => { | ||||||
|  |         console.log(node) | ||||||
|  |         const { level, value, path } = node | ||||||
|  |         const { dataOptions } = this | ||||||
|  |         if (path) { | ||||||
|  |           let childrens = dataOptions | ||||||
|  |           path.forEach((p) => { | ||||||
|  |             if (childrens) { | ||||||
|  |               const childrenItem = childrens.find(c => c[this.props.value] === p) | ||||||
|  |               if (childrenItem) { | ||||||
|  |                 childrens = childrenItem.children | ||||||
|  |               } else { | ||||||
|  |                 childrens = childrenItem | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }) | ||||||
|  |           if (childrens) { | ||||||
|  |             resolve([]) | ||||||
|  |           } else { | ||||||
|  |             this.getNodes(level, resolve, value) | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           this.getNodes(level, resolve, value) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     getNodes(level, resolve, value = 0) { | ||||||
|  |       getSysArea({ pid: value, _t: Math.random() }).then((res) => { | ||||||
|  |         const { lists } = res.data | ||||||
|  |         // console.log(this.$refs['address-cascader']) | ||||||
|  |         const nodeArr = lists.map(item => ({ | ||||||
|  |           id: item.id, | ||||||
|  |           name: item.name, | ||||||
|  |           leaf: level >= this.level, | ||||||
|  |         })) | ||||||
|  |         resolve(nodeArr) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     getAddress(pid) { | ||||||
|  |       return getSysArea({ pid, _t: Math.random() }).then(res => res.data.lists) | ||||||
|  |     }, | ||||||
|  |     cascaderChange(e) { | ||||||
|  |       this.$emit('input', e) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,111 @@ | ||||||
|  | <template> | ||||||
|  |   <transition :name="transitionName"> | ||||||
|  |     <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop"> | ||||||
|  |       <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg> | ||||||
|  |     </div> | ||||||
|  |   </transition> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'BackToTop', | ||||||
|  |   props: { | ||||||
|  |     visibilityHeight: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 400 | ||||||
|  |     }, | ||||||
|  |     backPosition: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 0 | ||||||
|  |     }, | ||||||
|  |     customStyle: { | ||||||
|  |       type: Object, | ||||||
|  |       default: function() { | ||||||
|  |         return { | ||||||
|  |           right: '50px', | ||||||
|  |           bottom: '50px', | ||||||
|  |           width: '40px', | ||||||
|  |           height: '40px', | ||||||
|  |           'border-radius': '4px', | ||||||
|  |           'line-height': '45px', | ||||||
|  |           background: '#e7eaf1' | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     transitionName: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'fade' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       visible: false, | ||||||
|  |       interval: null, | ||||||
|  |       isMoving: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     window.addEventListener('scroll', this.handleScroll) | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     window.removeEventListener('scroll', this.handleScroll) | ||||||
|  |     if (this.interval) { | ||||||
|  |       clearInterval(this.interval) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     handleScroll() { | ||||||
|  |       this.visible = window.pageYOffset > this.visibilityHeight | ||||||
|  |     }, | ||||||
|  |     backToTop() { | ||||||
|  |       if (this.isMoving) return | ||||||
|  |       const start = window.pageYOffset | ||||||
|  |       let i = 0 | ||||||
|  |       this.isMoving = true | ||||||
|  |       this.interval = setInterval(() => { | ||||||
|  |         const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500)) | ||||||
|  |         if (next <= this.backPosition) { | ||||||
|  |           window.scrollTo(0, this.backPosition) | ||||||
|  |           clearInterval(this.interval) | ||||||
|  |           this.isMoving = false | ||||||
|  |         } else { | ||||||
|  |           window.scrollTo(0, next) | ||||||
|  |         } | ||||||
|  |         i++ | ||||||
|  |       }, 16.7) | ||||||
|  |     }, | ||||||
|  |     easeInOutQuad(t, b, c, d) { | ||||||
|  |       if ((t /= d / 2) < 1) return c / 2 * t * t + b | ||||||
|  |       return -c / 2 * (--t * (t - 2) - 1) + b | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .back-to-ceiling { | ||||||
|  |   position: fixed; | ||||||
|  |   display: inline-block; | ||||||
|  |   text-align: center; | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .back-to-ceiling:hover { | ||||||
|  |   background: #d5dbe7; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .fade-enter-active, | ||||||
|  | .fade-leave-active { | ||||||
|  |   transition: opacity .5s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .fade-enter, | ||||||
|  | .fade-leave-to { | ||||||
|  |   opacity: 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .back-to-ceiling .Icon { | ||||||
|  |   fill: #9aaabf; | ||||||
|  |   background: none; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,82 @@ | ||||||
|  | <template> | ||||||
|  |   <el-breadcrumb class="app-breadcrumb" separator="/"> | ||||||
|  |     <transition-group name="breadcrumb"> | ||||||
|  |       <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> | ||||||
|  |         <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span> | ||||||
|  |         <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> | ||||||
|  |       </el-breadcrumb-item> | ||||||
|  |     </transition-group> | ||||||
|  |   </el-breadcrumb> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import pathToRegexp from 'path-to-regexp' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       levelList: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     $route(route) { | ||||||
|  |       // if you go to the redirect page, do not update the breadcrumbs | ||||||
|  |       if (route.path.startsWith('/redirect/')) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.getBreadcrumb() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.getBreadcrumb() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getBreadcrumb() { | ||||||
|  |       // only show routes with meta.title | ||||||
|  |       let matched = this.$route.matched.filter(item => item.meta && item.meta.title) | ||||||
|  |       const first = matched[0] | ||||||
|  | 
 | ||||||
|  |       if (!this.isDashboard(first)) { | ||||||
|  |         matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched) | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) | ||||||
|  |     }, | ||||||
|  |     isDashboard(route) { | ||||||
|  |       const name = route && route.name | ||||||
|  |       if (!name) { | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  |       return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase() | ||||||
|  |     }, | ||||||
|  |     pathCompile(path) { | ||||||
|  |       // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561 | ||||||
|  |       const { params } = this.$route | ||||||
|  |       var toPath = pathToRegexp.compile(path) | ||||||
|  |       return toPath(params) | ||||||
|  |     }, | ||||||
|  |     handleLink(item) { | ||||||
|  |       const { redirect, path } = item | ||||||
|  |       if (redirect) { | ||||||
|  |         this.$router.push(redirect) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.$router.push(this.pathCompile(path)) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .app-breadcrumb.el-breadcrumb { | ||||||
|  |   display: inline-block; | ||||||
|  |   font-size: 14px; | ||||||
|  |   line-height: 50px; | ||||||
|  |   margin-left: 8px; | ||||||
|  | 
 | ||||||
|  |   .no-redirect { | ||||||
|  |     color: #97a8be; | ||||||
|  |     cursor: text; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | <template> | ||||||
|  |   <div id="app"> | ||||||
|  |     <WCalendar | ||||||
|  |       class="calendar" | ||||||
|  |       :Month="monthValue" | ||||||
|  |       :is-multiple-choice="false" | ||||||
|  |       :pointMap="pointMap" | ||||||
|  |        | ||||||
|  |       @chooseDays="chooseDays" | ||||||
|  |     > | ||||||
|  |       <template v-slot:left> | ||||||
|  |         <el-date-picker | ||||||
|  |           v-model="monthValue" | ||||||
|  |           type="month" | ||||||
|  |           format="yyyy-MM" | ||||||
|  |           value-format="yyyy-MM" | ||||||
|  |           placeholder="选择月份" | ||||||
|  |         > | ||||||
|  |         </el-date-picker> | ||||||
|  |       </template> | ||||||
|  |     </WCalendar> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import Vue from "vue"; | ||||||
|  | import WCalendar from "wqcalendar"; | ||||||
|  | import "wqcalendar/dist/wqcalendar.css"; | ||||||
|  | Vue.use(WCalendar); | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     /** | ||||||
|  |      * 获取当天时间 | ||||||
|  |      * @returns {string} | ||||||
|  |      */ | ||||||
|  |     function getCurDay(num = 0) { | ||||||
|  |       var datetime = new Date(); | ||||||
|  |       var year = datetime.getFullYear(); | ||||||
|  |       var month = | ||||||
|  |         datetime.getMonth() + 1 < 10 | ||||||
|  |           ? "0" + (datetime.getMonth() + 1) | ||||||
|  |           : datetime.getMonth() + 1; | ||||||
|  |       let day = datetime.getDate(); | ||||||
|  |       if (day + num > 0) { | ||||||
|  |         day = | ||||||
|  |           day + num < 10 | ||||||
|  |             ? "0" + (datetime.getDate() + num) | ||||||
|  |             : datetime.getDate() + num; | ||||||
|  |       } else { | ||||||
|  |         day = | ||||||
|  |           day - num < 10 | ||||||
|  |             ? "0" + (datetime.getDate() - num) | ||||||
|  |             : datetime.getDate() - num; | ||||||
|  |       } | ||||||
|  |       return `${year}-${month}-${day}`; | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |       monthValue: "", | ||||||
|  |       pointMap: { [new Date(getCurDay()).getTime()]: true }, | ||||||
|  |       restrictDate: { 4: [getCurDay(), getCurDay(+10)] }, | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     chooseDays(list) { | ||||||
|  |       console.log(list); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,155 @@ | ||||||
|  | <template> | ||||||
|  |   <div :id="id" :class="className" :style="{height:height,width:width}" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import echarts from 'echarts' | ||||||
|  | import resize from './mixins/resize' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   mixins: [resize], | ||||||
|  |   props: { | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     width: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       chart: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.initChart() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     if (!this.chart) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     this.chart.dispose() | ||||||
|  |     this.chart = null | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initChart() { | ||||||
|  |       this.chart = echarts.init(document.getElementById(this.id)) | ||||||
|  | 
 | ||||||
|  |       const xAxisData = [] | ||||||
|  |       const data = [] | ||||||
|  |       const data2 = [] | ||||||
|  |       for (let i = 0; i < 50; i++) { | ||||||
|  |         xAxisData.push(i) | ||||||
|  |         data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5) | ||||||
|  |         data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3) | ||||||
|  |       } | ||||||
|  |       this.chart.setOption({ | ||||||
|  |         backgroundColor: '#08263a', | ||||||
|  |         grid: { | ||||||
|  |           left: '5%', | ||||||
|  |           right: '5%' | ||||||
|  |         }, | ||||||
|  |         xAxis: [{ | ||||||
|  |           show: false, | ||||||
|  |           data: xAxisData | ||||||
|  |         }, { | ||||||
|  |           show: false, | ||||||
|  |           data: xAxisData | ||||||
|  |         }], | ||||||
|  |         visualMap: { | ||||||
|  |           show: false, | ||||||
|  |           min: 0, | ||||||
|  |           max: 50, | ||||||
|  |           dimension: 0, | ||||||
|  |           inRange: { | ||||||
|  |             color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055'] | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         yAxis: { | ||||||
|  |           axisLine: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisLabel: { | ||||||
|  |             textStyle: { | ||||||
|  |               color: '#4a657a' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           splitLine: { | ||||||
|  |             show: true, | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#08263f' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           axisTick: { | ||||||
|  |             show: false | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         series: [{ | ||||||
|  |           name: 'back', | ||||||
|  |           type: 'bar', | ||||||
|  |           data: data2, | ||||||
|  |           z: 1, | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               opacity: 0.4, | ||||||
|  |               barBorderRadius: 5, | ||||||
|  |               shadowBlur: 3, | ||||||
|  |               shadowColor: '#111' | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, { | ||||||
|  |           name: 'Simulate Shadow', | ||||||
|  |           type: 'line', | ||||||
|  |           data, | ||||||
|  |           z: 2, | ||||||
|  |           showSymbol: false, | ||||||
|  |           animationDelay: 0, | ||||||
|  |           animationEasing: 'linear', | ||||||
|  |           animationDuration: 1200, | ||||||
|  |           lineStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'transparent' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           areaStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: '#08263a', | ||||||
|  |               shadowBlur: 50, | ||||||
|  |               shadowColor: '#000' | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, { | ||||||
|  |           name: 'front', | ||||||
|  |           type: 'bar', | ||||||
|  |           data, | ||||||
|  |           xAxisIndex: 1, | ||||||
|  |           z: 3, | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               barBorderRadius: 5 | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }], | ||||||
|  |         animationEasing: 'elasticOut', | ||||||
|  |         animationEasingUpdate: 'elasticOut', | ||||||
|  |         animationDelay(idx) { | ||||||
|  |           return idx * 20 | ||||||
|  |         }, | ||||||
|  |         animationDelayUpdate(idx) { | ||||||
|  |           return idx * 20 | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,227 @@ | ||||||
|  | <template> | ||||||
|  |   <div :id="id" :class="className" :style="{height:height,width:width}" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import echarts from 'echarts' | ||||||
|  | import resize from './mixins/resize' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   mixins: [resize], | ||||||
|  |   props: { | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     width: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       chart: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.initChart() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     if (!this.chart) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     this.chart.dispose() | ||||||
|  |     this.chart = null | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initChart() { | ||||||
|  |       this.chart = echarts.init(document.getElementById(this.id)) | ||||||
|  | 
 | ||||||
|  |       this.chart.setOption({ | ||||||
|  |         backgroundColor: '#394056', | ||||||
|  |         title: { | ||||||
|  |           top: 20, | ||||||
|  |           text: 'Requests', | ||||||
|  |           textStyle: { | ||||||
|  |             fontWeight: 'normal', | ||||||
|  |             fontSize: 16, | ||||||
|  |             color: '#F1F1F3' | ||||||
|  |           }, | ||||||
|  |           left: '1%' | ||||||
|  |         }, | ||||||
|  |         tooltip: { | ||||||
|  |           trigger: 'axis', | ||||||
|  |           axisPointer: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#57617B' | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         legend: { | ||||||
|  |           top: 20, | ||||||
|  |           icon: 'rect', | ||||||
|  |           itemWidth: 14, | ||||||
|  |           itemHeight: 5, | ||||||
|  |           itemGap: 13, | ||||||
|  |           data: ['CMCC', 'CTCC', 'CUCC'], | ||||||
|  |           right: '4%', | ||||||
|  |           textStyle: { | ||||||
|  |             fontSize: 12, | ||||||
|  |             color: '#F1F1F3' | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         grid: { | ||||||
|  |           top: 100, | ||||||
|  |           left: '2%', | ||||||
|  |           right: '2%', | ||||||
|  |           bottom: '2%', | ||||||
|  |           containLabel: true | ||||||
|  |         }, | ||||||
|  |         xAxis: [{ | ||||||
|  |           type: 'category', | ||||||
|  |           boundaryGap: false, | ||||||
|  |           axisLine: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#57617B' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55'] | ||||||
|  |         }], | ||||||
|  |         yAxis: [{ | ||||||
|  |           type: 'value', | ||||||
|  |           name: '(%)', | ||||||
|  |           axisTick: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisLine: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#57617B' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           axisLabel: { | ||||||
|  |             margin: 10, | ||||||
|  |             textStyle: { | ||||||
|  |               fontSize: 14 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           splitLine: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#57617B' | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }], | ||||||
|  |         series: [{ | ||||||
|  |           name: 'CMCC', | ||||||
|  |           type: 'line', | ||||||
|  |           smooth: true, | ||||||
|  |           symbol: 'circle', | ||||||
|  |           symbolSize: 5, | ||||||
|  |           showSymbol: false, | ||||||
|  |           lineStyle: { | ||||||
|  |             normal: { | ||||||
|  |               width: 1 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           areaStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | ||||||
|  |                 offset: 0, | ||||||
|  |                 color: 'rgba(137, 189, 27, 0.3)' | ||||||
|  |               }, { | ||||||
|  |                 offset: 0.8, | ||||||
|  |                 color: 'rgba(137, 189, 27, 0)' | ||||||
|  |               }], false), | ||||||
|  |               shadowColor: 'rgba(0, 0, 0, 0.1)', | ||||||
|  |               shadowBlur: 10 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgb(137,189,27)', | ||||||
|  |               borderColor: 'rgba(137,189,2,0.27)', | ||||||
|  |               borderWidth: 12 | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122] | ||||||
|  |         }, { | ||||||
|  |           name: 'CTCC', | ||||||
|  |           type: 'line', | ||||||
|  |           smooth: true, | ||||||
|  |           symbol: 'circle', | ||||||
|  |           symbolSize: 5, | ||||||
|  |           showSymbol: false, | ||||||
|  |           lineStyle: { | ||||||
|  |             normal: { | ||||||
|  |               width: 1 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           areaStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | ||||||
|  |                 offset: 0, | ||||||
|  |                 color: 'rgba(0, 136, 212, 0.3)' | ||||||
|  |               }, { | ||||||
|  |                 offset: 0.8, | ||||||
|  |                 color: 'rgba(0, 136, 212, 0)' | ||||||
|  |               }], false), | ||||||
|  |               shadowColor: 'rgba(0, 0, 0, 0.1)', | ||||||
|  |               shadowBlur: 10 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgb(0,136,212)', | ||||||
|  |               borderColor: 'rgba(0,136,212,0.2)', | ||||||
|  |               borderWidth: 12 | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150] | ||||||
|  |         }, { | ||||||
|  |           name: 'CUCC', | ||||||
|  |           type: 'line', | ||||||
|  |           smooth: true, | ||||||
|  |           symbol: 'circle', | ||||||
|  |           symbolSize: 5, | ||||||
|  |           showSymbol: false, | ||||||
|  |           lineStyle: { | ||||||
|  |             normal: { | ||||||
|  |               width: 1 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           areaStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ | ||||||
|  |                 offset: 0, | ||||||
|  |                 color: 'rgba(219, 50, 51, 0.3)' | ||||||
|  |               }, { | ||||||
|  |                 offset: 0.8, | ||||||
|  |                 color: 'rgba(219, 50, 51, 0)' | ||||||
|  |               }], false), | ||||||
|  |               shadowColor: 'rgba(0, 0, 0, 0.1)', | ||||||
|  |               shadowBlur: 10 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgb(219,50,51)', | ||||||
|  |               borderColor: 'rgba(219,50,51,0.2)', | ||||||
|  |               borderWidth: 12 | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122] | ||||||
|  |         }] | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,271 @@ | ||||||
|  | <template> | ||||||
|  |   <div :id="id" :class="className" :style="{height:height,width:width}" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import echarts from 'echarts' | ||||||
|  | import resize from './mixins/resize' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   mixins: [resize], | ||||||
|  |   props: { | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'chart' | ||||||
|  |     }, | ||||||
|  |     width: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: String, | ||||||
|  |       default: '200px' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       chart: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.initChart() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     if (!this.chart) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     this.chart.dispose() | ||||||
|  |     this.chart = null | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initChart() { | ||||||
|  |       this.chart = echarts.init(document.getElementById(this.id)) | ||||||
|  |       const xData = (function() { | ||||||
|  |         const data = [] | ||||||
|  |         for (let i = 1; i < 13; i++) { | ||||||
|  |           data.push(i + 'month') | ||||||
|  |         } | ||||||
|  |         return data | ||||||
|  |       }()) | ||||||
|  |       this.chart.setOption({ | ||||||
|  |         backgroundColor: '#344b58', | ||||||
|  |         title: { | ||||||
|  |           text: 'statistics', | ||||||
|  |           x: '20', | ||||||
|  |           top: '20', | ||||||
|  |           textStyle: { | ||||||
|  |             color: '#fff', | ||||||
|  |             fontSize: '22' | ||||||
|  |           }, | ||||||
|  |           subtextStyle: { | ||||||
|  |             color: '#90979c', | ||||||
|  |             fontSize: '16' | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         tooltip: { | ||||||
|  |           trigger: 'axis', | ||||||
|  |           axisPointer: { | ||||||
|  |             textStyle: { | ||||||
|  |               color: '#fff' | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         grid: { | ||||||
|  |           left: '5%', | ||||||
|  |           right: '5%', | ||||||
|  |           borderWidth: 0, | ||||||
|  |           top: 150, | ||||||
|  |           bottom: 95, | ||||||
|  |           textStyle: { | ||||||
|  |             color: '#fff' | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         legend: { | ||||||
|  |           x: '5%', | ||||||
|  |           top: '10%', | ||||||
|  |           textStyle: { | ||||||
|  |             color: '#90979c' | ||||||
|  |           }, | ||||||
|  |           data: ['female', 'male', 'average'] | ||||||
|  |         }, | ||||||
|  |         calculable: true, | ||||||
|  |         xAxis: [{ | ||||||
|  |           type: 'category', | ||||||
|  |           axisLine: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#90979c' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           splitLine: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisTick: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           splitArea: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisLabel: { | ||||||
|  |             interval: 0 | ||||||
|  | 
 | ||||||
|  |           }, | ||||||
|  |           data: xData | ||||||
|  |         }], | ||||||
|  |         yAxis: [{ | ||||||
|  |           type: 'value', | ||||||
|  |           splitLine: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisLine: { | ||||||
|  |             lineStyle: { | ||||||
|  |               color: '#90979c' | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           axisTick: { | ||||||
|  |             show: false | ||||||
|  |           }, | ||||||
|  |           axisLabel: { | ||||||
|  |             interval: 0 | ||||||
|  |           }, | ||||||
|  |           splitArea: { | ||||||
|  |             show: false | ||||||
|  |           } | ||||||
|  |         }], | ||||||
|  |         dataZoom: [{ | ||||||
|  |           show: true, | ||||||
|  |           height: 30, | ||||||
|  |           xAxisIndex: [ | ||||||
|  |             0 | ||||||
|  |           ], | ||||||
|  |           bottom: 30, | ||||||
|  |           start: 10, | ||||||
|  |           end: 80, | ||||||
|  |           handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z', | ||||||
|  |           handleSize: '110%', | ||||||
|  |           handleStyle: { | ||||||
|  |             color: '#d3dee5' | ||||||
|  | 
 | ||||||
|  |           }, | ||||||
|  |           textStyle: { | ||||||
|  |             color: '#fff' }, | ||||||
|  |           borderColor: '#90979c' | ||||||
|  | 
 | ||||||
|  |         }, { | ||||||
|  |           type: 'inside', | ||||||
|  |           show: true, | ||||||
|  |           height: 15, | ||||||
|  |           start: 1, | ||||||
|  |           end: 35 | ||||||
|  |         }], | ||||||
|  |         series: [{ | ||||||
|  |           name: 'female', | ||||||
|  |           type: 'bar', | ||||||
|  |           stack: 'total', | ||||||
|  |           barMaxWidth: 35, | ||||||
|  |           barGap: '10%', | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgba(255,144,128,1)', | ||||||
|  |               label: { | ||||||
|  |                 show: true, | ||||||
|  |                 textStyle: { | ||||||
|  |                   color: '#fff' | ||||||
|  |                 }, | ||||||
|  |                 position: 'insideTop', | ||||||
|  |                 formatter(p) { | ||||||
|  |                   return p.value > 0 ? p.value : '' | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [ | ||||||
|  |             709, | ||||||
|  |             1917, | ||||||
|  |             2455, | ||||||
|  |             2610, | ||||||
|  |             1719, | ||||||
|  |             1433, | ||||||
|  |             1544, | ||||||
|  |             3285, | ||||||
|  |             5208, | ||||||
|  |             3372, | ||||||
|  |             2484, | ||||||
|  |             4078 | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |           name: 'male', | ||||||
|  |           type: 'bar', | ||||||
|  |           stack: 'total', | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgba(0,191,183,1)', | ||||||
|  |               barBorderRadius: 0, | ||||||
|  |               label: { | ||||||
|  |                 show: true, | ||||||
|  |                 position: 'top', | ||||||
|  |                 formatter(p) { | ||||||
|  |                   return p.value > 0 ? p.value : '' | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [ | ||||||
|  |             327, | ||||||
|  |             1776, | ||||||
|  |             507, | ||||||
|  |             1200, | ||||||
|  |             800, | ||||||
|  |             482, | ||||||
|  |             204, | ||||||
|  |             1390, | ||||||
|  |             1001, | ||||||
|  |             951, | ||||||
|  |             381, | ||||||
|  |             220 | ||||||
|  |           ] | ||||||
|  |         }, { | ||||||
|  |           name: 'average', | ||||||
|  |           type: 'line', | ||||||
|  |           stack: 'total', | ||||||
|  |           symbolSize: 10, | ||||||
|  |           symbol: 'circle', | ||||||
|  |           itemStyle: { | ||||||
|  |             normal: { | ||||||
|  |               color: 'rgba(252,230,48,1)', | ||||||
|  |               barBorderRadius: 0, | ||||||
|  |               label: { | ||||||
|  |                 show: true, | ||||||
|  |                 position: 'top', | ||||||
|  |                 formatter(p) { | ||||||
|  |                   return p.value > 0 ? p.value : '' | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           data: [ | ||||||
|  |             1036, | ||||||
|  |             3693, | ||||||
|  |             2962, | ||||||
|  |             3810, | ||||||
|  |             2519, | ||||||
|  |             1915, | ||||||
|  |             1748, | ||||||
|  |             4675, | ||||||
|  |             6209, | ||||||
|  |             4323, | ||||||
|  |             2865, | ||||||
|  |             4298 | ||||||
|  |           ] | ||||||
|  |         } | ||||||
|  |         ] | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,56 @@ | ||||||
|  | import { debounce } from '@/utils' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       $_sidebarElm: null, | ||||||
|  |       $_resizeHandler: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.initListener() | ||||||
|  |   }, | ||||||
|  |   activated() { | ||||||
|  |     if (!this.$_resizeHandler) { | ||||||
|  |       // avoid duplication init
 | ||||||
|  |       this.initListener() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // when keep-alive chart activated, auto resize
 | ||||||
|  |     this.resize() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     this.destroyListener() | ||||||
|  |   }, | ||||||
|  |   deactivated() { | ||||||
|  |     this.destroyListener() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     // use $_ for mixins properties
 | ||||||
|  |     // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
 | ||||||
|  |     $_sidebarResizeHandler(e) { | ||||||
|  |       if (e.propertyName === 'width') { | ||||||
|  |         this.$_resizeHandler() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     initListener() { | ||||||
|  |       this.$_resizeHandler = debounce(() => { | ||||||
|  |         this.resize() | ||||||
|  |       }, 100) | ||||||
|  |       window.addEventListener('resize', this.$_resizeHandler) | ||||||
|  | 
 | ||||||
|  |       this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] | ||||||
|  |       this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) | ||||||
|  |     }, | ||||||
|  |     destroyListener() { | ||||||
|  |       window.removeEventListener('resize', this.$_resizeHandler) | ||||||
|  |       this.$_resizeHandler = null | ||||||
|  | 
 | ||||||
|  |       this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) | ||||||
|  |     }, | ||||||
|  |     resize() { | ||||||
|  |       const { chart } = this | ||||||
|  |       chart && chart.resize() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,166 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="dndList"> | ||||||
|  |     <div :style="{width:width1}" class="dndList-list"> | ||||||
|  |       <h3>{{ list1Title }}</h3> | ||||||
|  |       <draggable :set-data="setData" :list="list1" group="article" class="dragArea"> | ||||||
|  |         <div v-for="element in list1" :key="element.id" class="list-complete-item"> | ||||||
|  |           <div class="list-complete-item-handle"> | ||||||
|  |             {{ element.id }}[{{ element.author }}] {{ element.title }} | ||||||
|  |           </div> | ||||||
|  |           <div style="position:absolute;right:0px;"> | ||||||
|  |             <span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)"> | ||||||
|  |               <i style="color:#ff4949" class="el-icon-delete" /> | ||||||
|  |             </span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </draggable> | ||||||
|  |     </div> | ||||||
|  |     <div :style="{width:width2}" class="dndList-list"> | ||||||
|  |       <h3>{{ list2Title }}</h3> | ||||||
|  |       <draggable :list="list2" group="article" class="dragArea"> | ||||||
|  |         <div v-for="element in list2" :key="element.id" class="list-complete-item"> | ||||||
|  |           <div class="list-complete-item-handle2" @click="pushEle(element)"> | ||||||
|  |             {{ element.id }} [{{ element.author }}] {{ element.title }} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </draggable> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import draggable from 'vuedraggable' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'DndList', | ||||||
|  |   components: { draggable }, | ||||||
|  |   props: { | ||||||
|  |     list1: { | ||||||
|  |       type: Array, | ||||||
|  |       default() { | ||||||
|  |         return [] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     list2: { | ||||||
|  |       type: Array, | ||||||
|  |       default() { | ||||||
|  |         return [] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     list1Title: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'list1' | ||||||
|  |     }, | ||||||
|  |     list2Title: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'list2' | ||||||
|  |     }, | ||||||
|  |     width1: { | ||||||
|  |       type: String, | ||||||
|  |       default: '48%' | ||||||
|  |     }, | ||||||
|  |     width2: { | ||||||
|  |       type: String, | ||||||
|  |       default: '48%' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     isNotInList1(v) { | ||||||
|  |       return this.list1.every(k => v.id !== k.id) | ||||||
|  |     }, | ||||||
|  |     isNotInList2(v) { | ||||||
|  |       return this.list2.every(k => v.id !== k.id) | ||||||
|  |     }, | ||||||
|  |     deleteEle(ele) { | ||||||
|  |       for (const item of this.list1) { | ||||||
|  |         if (item.id === ele.id) { | ||||||
|  |           const index = this.list1.indexOf(item) | ||||||
|  |           this.list1.splice(index, 1) | ||||||
|  |           break | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (this.isNotInList2(ele)) { | ||||||
|  |         this.list2.unshift(ele) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     pushEle(ele) { | ||||||
|  |       for (const item of this.list2) { | ||||||
|  |         if (item.id === ele.id) { | ||||||
|  |           const index = this.list2.indexOf(item) | ||||||
|  |           this.list2.splice(index, 1) | ||||||
|  |           break | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if (this.isNotInList1(ele)) { | ||||||
|  |         this.list1.push(ele) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     setData(dataTransfer) { | ||||||
|  |       // to avoid Firefox bug | ||||||
|  |       // Detail see : https://github.com/RubaXa/Sortable/issues/1012 | ||||||
|  |       dataTransfer.setData('Text', '') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .dndList { | ||||||
|  |   background: #fff; | ||||||
|  |   padding-bottom: 40px; | ||||||
|  |   &:after { | ||||||
|  |     content: ""; | ||||||
|  |     display: table; | ||||||
|  |     clear: both; | ||||||
|  |   } | ||||||
|  |   .dndList-list { | ||||||
|  |     float: left; | ||||||
|  |     padding-bottom: 30px; | ||||||
|  |     &:first-of-type { | ||||||
|  |       margin-right: 2%; | ||||||
|  |     } | ||||||
|  |     .dragArea { | ||||||
|  |       margin-top: 15px; | ||||||
|  |       min-height: 50px; | ||||||
|  |       padding-bottom: 30px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-item { | ||||||
|  |   cursor: pointer; | ||||||
|  |   position: relative; | ||||||
|  |   font-size: 14px; | ||||||
|  |   padding: 5px 12px; | ||||||
|  |   margin-top: 4px; | ||||||
|  |   border: 1px solid #bfcbd9; | ||||||
|  |   transition: all 1s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-item-handle { | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   margin-right: 50px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-item-handle2 { | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   margin-right: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-item.sortable-chosen { | ||||||
|  |   background: #4AB7BD; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-item.sortable-ghost { | ||||||
|  |   background: #30B08F; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .list-complete-enter, | ||||||
|  | .list-complete-leave-active { | ||||||
|  |   opacity: 0; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | <template> | ||||||
|  |   <el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners"> | ||||||
|  |     <slot /> | ||||||
|  |   </el-select> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import Sortable from 'sortablejs' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'DragSelect', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: Array, | ||||||
|  |       required: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     selectVal: { | ||||||
|  |       get() { | ||||||
|  |         return [...this.value] | ||||||
|  |       }, | ||||||
|  |       set(val) { | ||||||
|  |         this.$emit('input', [...val]) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.setSort() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     setSort() { | ||||||
|  |       const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0] | ||||||
|  |       this.sortable = Sortable.create(el, { | ||||||
|  |         ghostClass: 'sortable-ghost', // Class name for the drop placeholder, | ||||||
|  |         setData: function(dataTransfer) { | ||||||
|  |           dataTransfer.setData('Text', '') | ||||||
|  |           // to avoid Firefox bug | ||||||
|  |           // Detail see : https://github.com/RubaXa/Sortable/issues/1012 | ||||||
|  |         }, | ||||||
|  |         onEnd: evt => { | ||||||
|  |           const targetRow = this.value.splice(evt.oldIndex, 1)[0] | ||||||
|  |           console.log(this.value); | ||||||
|  |           this.value.splice(evt.newIndex, 0, targetRow) | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .drag-select { | ||||||
|  |   ::v-deep { | ||||||
|  |     .sortable-ghost { | ||||||
|  |       opacity: .8; | ||||||
|  |       color: #fff !important; | ||||||
|  |       background: #42b983 !important; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .el-tag { | ||||||
|  |       cursor: pointer; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,297 @@ | ||||||
|  | <template> | ||||||
|  |   <div :id="id" :ref="id" :action="url" class="dropzone"> | ||||||
|  |     <input type="file" name="file"> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import Dropzone from 'dropzone' | ||||||
|  | import 'dropzone/dist/dropzone.css' | ||||||
|  | // import { getToken } from 'api/qiniu'; | ||||||
|  | 
 | ||||||
|  | Dropzone.autoDiscover = false | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       required: true | ||||||
|  |     }, | ||||||
|  |     url: { | ||||||
|  |       type: String, | ||||||
|  |       required: true | ||||||
|  |     }, | ||||||
|  |     clickable: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true | ||||||
|  |     }, | ||||||
|  |     defaultMsg: { | ||||||
|  |       type: String, | ||||||
|  |       default: '上传图片' | ||||||
|  |     }, | ||||||
|  |     acceptedFiles: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     thumbnailHeight: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 200 | ||||||
|  |     }, | ||||||
|  |     thumbnailWidth: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 200 | ||||||
|  |     }, | ||||||
|  |     showRemoveLink: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true | ||||||
|  |     }, | ||||||
|  |     maxFilesize: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 2 | ||||||
|  |     }, | ||||||
|  |     maxFiles: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 3 | ||||||
|  |     }, | ||||||
|  |     autoProcessQueue: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true | ||||||
|  |     }, | ||||||
|  |     useCustomDropzoneOptions: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     }, | ||||||
|  |     defaultImg: { | ||||||
|  |       default: '', | ||||||
|  |       type: [String, Array] | ||||||
|  |     }, | ||||||
|  |     couldPaste: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       dropzone: '', | ||||||
|  |       initOnce: true | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     defaultImg(val) { | ||||||
|  |       if (val.length === 0) { | ||||||
|  |         this.initOnce = false | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       if (!this.initOnce) return | ||||||
|  |       this.initImages(val) | ||||||
|  |       this.initOnce = false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     const element = document.getElementById(this.id) | ||||||
|  |     const vm = this | ||||||
|  |     this.dropzone = new Dropzone(element, { | ||||||
|  |       clickable: this.clickable, | ||||||
|  |       thumbnailWidth: this.thumbnailWidth, | ||||||
|  |       thumbnailHeight: this.thumbnailHeight, | ||||||
|  |       maxFiles: this.maxFiles, | ||||||
|  |       maxFilesize: this.maxFilesize, | ||||||
|  |       dictRemoveFile: 'Remove', | ||||||
|  |       addRemoveLinks: this.showRemoveLink, | ||||||
|  |       acceptedFiles: this.acceptedFiles, | ||||||
|  |       autoProcessQueue: this.autoProcessQueue, | ||||||
|  |       dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload', | ||||||
|  |       dictMaxFilesExceeded: '只能一个图', | ||||||
|  |       previewTemplate: '<div class="dz-preview dz-file-preview">  <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div>  <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>  <div class="dz-error-message"><span data-dz-errormessage></span></div>  <div class="dz-success-mark"> <i class="material-icons">done</i> </div>  <div class="dz-error-mark"><i class="material-icons">error</i></div></div>', | ||||||
|  |       init() { | ||||||
|  |         const val = vm.defaultImg | ||||||
|  |         if (!val) return | ||||||
|  |         if (Array.isArray(val)) { | ||||||
|  |           if (val.length === 0) return | ||||||
|  |           val.map((v, i) => { | ||||||
|  |             const mockFile = { name: 'name' + i, size: 12345, url: v } | ||||||
|  |             this.options.addedfile.call(this, mockFile) | ||||||
|  |             this.options.thumbnail.call(this, mockFile, v) | ||||||
|  |             mockFile.previewElement.classList.add('dz-success') | ||||||
|  |             mockFile.previewElement.classList.add('dz-complete') | ||||||
|  |             vm.initOnce = false | ||||||
|  |             return true | ||||||
|  |           }) | ||||||
|  |         } else { | ||||||
|  |           const mockFile = { name: 'name', size: 12345, url: val } | ||||||
|  |           this.options.addedfile.call(this, mockFile) | ||||||
|  |           this.options.thumbnail.call(this, mockFile, val) | ||||||
|  |           mockFile.previewElement.classList.add('dz-success') | ||||||
|  |           mockFile.previewElement.classList.add('dz-complete') | ||||||
|  |           vm.initOnce = false | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       accept: (file, done) => { | ||||||
|  |         /* 七牛*/ | ||||||
|  |         // const token = this.$store.getters.token; | ||||||
|  |         // getToken(token).then(response => { | ||||||
|  |         //   file.token = response.data.qiniu_token; | ||||||
|  |         //   file.key = response.data.qiniu_key; | ||||||
|  |         //   file.url = response.data.qiniu_url; | ||||||
|  |         //   done(); | ||||||
|  |         // }) | ||||||
|  |         done() | ||||||
|  |       }, | ||||||
|  |       sending: (file, xhr, formData) => { | ||||||
|  |         // formData.append('token', file.token); | ||||||
|  |         // formData.append('key', file.key); | ||||||
|  |         vm.initOnce = false | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     if (this.couldPaste) { | ||||||
|  |       document.addEventListener('paste', this.pasteImg) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.dropzone.on('success', file => { | ||||||
|  |       vm.$emit('dropzone-success', file, vm.dropzone.element) | ||||||
|  |     }) | ||||||
|  |     this.dropzone.on('addedfile', file => { | ||||||
|  |       vm.$emit('dropzone-fileAdded', file) | ||||||
|  |     }) | ||||||
|  |     this.dropzone.on('removedfile', file => { | ||||||
|  |       vm.$emit('dropzone-removedFile', file) | ||||||
|  |     }) | ||||||
|  |     this.dropzone.on('error', (file, error, xhr) => { | ||||||
|  |       vm.$emit('dropzone-error', file, error, xhr) | ||||||
|  |     }) | ||||||
|  |     this.dropzone.on('successmultiple', (file, error, xhr) => { | ||||||
|  |       vm.$emit('dropzone-successmultiple', file, error, xhr) | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   destroyed() { | ||||||
|  |     document.removeEventListener('paste', this.pasteImg) | ||||||
|  |     this.dropzone.destroy() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     removeAllFiles() { | ||||||
|  |       this.dropzone.removeAllFiles(true) | ||||||
|  |     }, | ||||||
|  |     processQueue() { | ||||||
|  |       this.dropzone.processQueue() | ||||||
|  |     }, | ||||||
|  |     pasteImg(event) { | ||||||
|  |       const items = (event.clipboardData || event.originalEvent.clipboardData).items | ||||||
|  |       if (items[0].kind === 'file') { | ||||||
|  |         this.dropzone.addFile(items[0].getAsFile()) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     initImages(val) { | ||||||
|  |       if (!val) return | ||||||
|  |       if (Array.isArray(val)) { | ||||||
|  |         val.map((v, i) => { | ||||||
|  |           const mockFile = { name: 'name' + i, size: 12345, url: v } | ||||||
|  |           this.dropzone.options.addedfile.call(this.dropzone, mockFile) | ||||||
|  |           this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v) | ||||||
|  |           mockFile.previewElement.classList.add('dz-success') | ||||||
|  |           mockFile.previewElement.classList.add('dz-complete') | ||||||
|  |           return true | ||||||
|  |         }) | ||||||
|  |       } else { | ||||||
|  |         const mockFile = { name: 'name', size: 12345, url: val } | ||||||
|  |         this.dropzone.options.addedfile.call(this.dropzone, mockFile) | ||||||
|  |         this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val) | ||||||
|  |         mockFile.previewElement.classList.add('dz-success') | ||||||
|  |         mockFile.previewElement.classList.add('dz-complete') | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  |     .dropzone { | ||||||
|  |         border: 2px solid #E5E5E5; | ||||||
|  |         font-family: 'Roboto', sans-serif; | ||||||
|  |         color: #777; | ||||||
|  |         transition: background-color .2s linear; | ||||||
|  |         padding: 5px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone:hover { | ||||||
|  |         background-color: #F6F6F6; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     i { | ||||||
|  |         color: #CCC; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-image img { | ||||||
|  |         width: 100%; | ||||||
|  |         height: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone input[name='file'] { | ||||||
|  |         display: none; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-image { | ||||||
|  |         border-radius: 0px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview:hover .dz-image img { | ||||||
|  |         transform: none; | ||||||
|  |         filter: none; | ||||||
|  |         width: 100%; | ||||||
|  |         height: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-details { | ||||||
|  |         bottom: 0px; | ||||||
|  |         top: 0px; | ||||||
|  |         color: white; | ||||||
|  |         background-color: rgba(33, 150, 243, 0.8); | ||||||
|  |         transition: opacity .2s linear; | ||||||
|  |         text-align: left; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span { | ||||||
|  |         background-color: transparent; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { | ||||||
|  |         border: none; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-details .dz-filename:hover span { | ||||||
|  |         background-color: transparent; | ||||||
|  |         border: none; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-remove { | ||||||
|  |         position: absolute; | ||||||
|  |         z-index: 30; | ||||||
|  |         color: white; | ||||||
|  |         margin-left: 15px; | ||||||
|  |         padding: 10px; | ||||||
|  |         top: inherit; | ||||||
|  |         bottom: 15px; | ||||||
|  |         border: 2px white solid; | ||||||
|  |         text-decoration: none; | ||||||
|  |         text-transform: uppercase; | ||||||
|  |         font-size: 0.8rem; | ||||||
|  |         font-weight: 800; | ||||||
|  |         letter-spacing: 1.1px; | ||||||
|  |         opacity: 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview:hover .dz-remove { | ||||||
|  |         opacity: 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark { | ||||||
|  |         margin-left: -40px; | ||||||
|  |         margin-top: -50px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i { | ||||||
|  |         color: white; | ||||||
|  |         font-size: 5rem; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | <template> | ||||||
|  |   <div v-if="errorLogs.length>0"> | ||||||
|  |     <el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true"> | ||||||
|  |       <el-button style="padding: 8px 10px;" size="small" type="danger"> | ||||||
|  |         <svg-icon icon-class="bug" /> | ||||||
|  |       </el-button> | ||||||
|  |     </el-badge> | ||||||
|  | 
 | ||||||
|  |     <el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body> | ||||||
|  |       <div slot="title"> | ||||||
|  |         <span style="padding-right: 10px;">Error Log</span> | ||||||
|  |         <el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button> | ||||||
|  |       </div> | ||||||
|  |       <el-table :data="errorLogs" border> | ||||||
|  |         <el-table-column label="Message"> | ||||||
|  |           <template slot-scope="{row}"> | ||||||
|  |             <div> | ||||||
|  |               <span class="message-title">Msg:</span> | ||||||
|  |               <el-tag type="danger"> | ||||||
|  |                 {{ row.err.message }} | ||||||
|  |               </el-tag> | ||||||
|  |             </div> | ||||||
|  |             <br> | ||||||
|  |             <div> | ||||||
|  |               <span class="message-title" style="padding-right: 10px;">Info: </span> | ||||||
|  |               <el-tag type="warning"> | ||||||
|  |                 {{ row.vm.$vnode.tag }} error in {{ row.info }} | ||||||
|  |               </el-tag> | ||||||
|  |             </div> | ||||||
|  |             <br> | ||||||
|  |             <div> | ||||||
|  |               <span class="message-title" style="padding-right: 16px;">Url: </span> | ||||||
|  |               <el-tag type="success"> | ||||||
|  |                 {{ row.url }} | ||||||
|  |               </el-tag> | ||||||
|  |             </div> | ||||||
|  |           </template> | ||||||
|  |         </el-table-column> | ||||||
|  |         <el-table-column label="Stack"> | ||||||
|  |           <template slot-scope="scope"> | ||||||
|  |             {{ scope.row.err.stack }} | ||||||
|  |           </template> | ||||||
|  |         </el-table-column> | ||||||
|  |       </el-table> | ||||||
|  |     </el-dialog> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'ErrorLog', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       dialogTableVisible: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     errorLogs() { | ||||||
|  |       return this.$store.getters.errorLogs | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     clearAll() { | ||||||
|  |       this.dialogTableVisible = false | ||||||
|  |       this.$store.dispatch('errorLog/clearErrorLog') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .message-title { | ||||||
|  |   font-size: 16px; | ||||||
|  |   color: #333; | ||||||
|  |   font-weight: bold; | ||||||
|  |   padding-right: 8px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | <template> | ||||||
|  |   <el-button v-loading="loading" :type="type" :size="size" @click="download"> | ||||||
|  |     模板下载 | ||||||
|  |   </el-button> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { get_template } from '@/api/import-data' | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     action: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     code: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     type: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'success' | ||||||
|  |     }, | ||||||
|  |     size: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: false, | ||||||
|  |       downloadList: [] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.getData() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getData() { | ||||||
|  |       get_template().then(res => { | ||||||
|  |         console.log(res) | ||||||
|  |         this.downloadList = res.data.filter(item => item.mark === this.action) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     download() { | ||||||
|  |       this.loading = true | ||||||
|  |       this.downloadList.forEach((item, index) => { | ||||||
|  |         setTimeout(() => { | ||||||
|  |           const aEl = document.createElement('a') | ||||||
|  |           const e = document.createEvent('MouseEvents') // 创建鼠标事件对象 | ||||||
|  |           e.initEvent('click', false, false) // 初始化事件对象 | ||||||
|  |           aEl.href = item.url | ||||||
|  |           // aEl.click() | ||||||
|  |           aEl.dispatchEvent(e) | ||||||
|  |           aEl.remove() | ||||||
|  |         }, index * 500) | ||||||
|  |       }) | ||||||
|  |       this.loading = false | ||||||
|  |     }, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,132 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="filter-container"> | ||||||
|  |     <template v-for="(item, index) of inputs"> | ||||||
|  |       <!-- <el-input | ||||||
|  |         :key="item.prop || ('i-' + index)" | ||||||
|  |         v-model="info[item.prop]" | ||||||
|  |         :placeholder="item.placeholder" | ||||||
|  |         prefix-icon="el-icon-search" | ||||||
|  |         clearable | ||||||
|  |         @keyup.enter.native="handleClick('enter')" | ||||||
|  |       /> --> | ||||||
|  |       <my-field :key="'i-' + item.prop + index" v-model="info[item.prop]" :field="{...item, clearable: true}" style="width: 240px;" class="filter-item input" @keyup.enter.native="handleClick('search')" /> | ||||||
|  |     </template> | ||||||
|  |     <el-button | ||||||
|  |       class="filter-item" | ||||||
|  |       style="margin-left: 10px;" | ||||||
|  |       type="primary" | ||||||
|  |       icon="el-icon-search" | ||||||
|  |       @click="handleClick('search')" | ||||||
|  |     >搜索 | ||||||
|  |     </el-button> | ||||||
|  |     <el-button | ||||||
|  |       class="filter-item" | ||||||
|  |       style="margin-left: 10px;" | ||||||
|  |       type="primary" | ||||||
|  |       @click="handleClick('reset')" | ||||||
|  |     >重置 | ||||||
|  |     </el-button> | ||||||
|  |     <el-button | ||||||
|  |       v-if="hasExport" | ||||||
|  |       class="filter-item" | ||||||
|  |       style="margin-left: 10px;" | ||||||
|  |       type="primary" | ||||||
|  |       icon="el-icon-download" | ||||||
|  |       @click="handleClick('export')" | ||||||
|  |     >导出 | ||||||
|  |     </el-button> | ||||||
|  |     <slot /> | ||||||
|  |     <template v-for="(item, index) of handlers"> | ||||||
|  |       <el-button | ||||||
|  |         :key="item.name || ('b-' + index)" | ||||||
|  |         class="filter-item" | ||||||
|  |         style="margin-left: 10px;" | ||||||
|  |         :type="item.type || 'primary'" | ||||||
|  |         :icon="item.icon" | ||||||
|  |         @click="handleClick(index)" | ||||||
|  |       >{{ item.title }} | ||||||
|  |       </el-button> | ||||||
|  |     </template> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import MyField from '@/components/MyField' | ||||||
|  | import { objectFindLikeValue, unfoldObjectKey } from '@/utils' | ||||||
|  | export default { | ||||||
|  |   components: { MyField }, | ||||||
|  |   props: { | ||||||
|  |     inputs: { | ||||||
|  |       type: Array, | ||||||
|  |       default: () => [] | ||||||
|  |     }, | ||||||
|  |     handlers: { | ||||||
|  |       type: Array, | ||||||
|  |       default: () => [] | ||||||
|  |     }, | ||||||
|  |     hasExport: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       info: {} | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     inputs() { | ||||||
|  |       this.setData() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.setData() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     setData() { | ||||||
|  |       const { inputs } = this | ||||||
|  |       if (inputs && inputs.length) { | ||||||
|  |         inputs.forEach((item) => { | ||||||
|  |           this.$set(this.info, item.prop, item.defaultValue) | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     handleClick(type) { | ||||||
|  |       let detail | ||||||
|  |       if (typeof type === 'number') { | ||||||
|  |         detail = this.getInfoObject() | ||||||
|  |         if (this.handlers[type].handler) { | ||||||
|  |           this.handlers[type].handler(detail) | ||||||
|  |         } | ||||||
|  |         type = `handler` | ||||||
|  |       } else if (type === 'reset') { | ||||||
|  |         Object.keys(this.info).forEach(key => { | ||||||
|  |           this.info[key] = undefined | ||||||
|  |         }) | ||||||
|  |         detail = this.getInfoObject() | ||||||
|  |       } else { | ||||||
|  |         detail = this.getInfoObject() | ||||||
|  |       } | ||||||
|  |       // const detail = this.getInfoObject() | ||||||
|  |       this.$emit(type, detail) | ||||||
|  |       this.$emit('all', { type, detail }) | ||||||
|  |     }, | ||||||
|  |     getInfoObject() { | ||||||
|  |       const { info } = this | ||||||
|  |       return unfoldObjectKey(info) | ||||||
|  |     }, | ||||||
|  |     getModel(keyString) { | ||||||
|  |       return objectFindLikeValue(this.info, keyString) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .filter-item.input{ | ||||||
|  |   margin-left: 3px; | ||||||
|  | } | ||||||
|  | .filter-container .filter-item.input:first-child{ | ||||||
|  |   margin-left: 0px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,54 @@ | ||||||
|  | <template> | ||||||
|  |   <a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github"> | ||||||
|  |     <svg | ||||||
|  |       width="80" | ||||||
|  |       height="80" | ||||||
|  |       viewBox="0 0 250 250" | ||||||
|  |       style="fill:#40c9c6; color:#fff;" | ||||||
|  |       aria-hidden="true" | ||||||
|  |     > | ||||||
|  |       <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" /> | ||||||
|  |       <path | ||||||
|  |         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" | ||||||
|  |         fill="currentColor" | ||||||
|  |         style="transform-origin: 130px 106px;" | ||||||
|  |         class="octo-arm" | ||||||
|  |       /> | ||||||
|  |       <path | ||||||
|  |         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" | ||||||
|  |         fill="currentColor" | ||||||
|  |         class="octo-body" | ||||||
|  |       /> | ||||||
|  |     </svg> | ||||||
|  |   </a> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .github-corner:hover .octo-arm { | ||||||
|  |   animation: octocat-wave 560ms ease-in-out | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes octocat-wave { | ||||||
|  |   0%, | ||||||
|  |   100% { | ||||||
|  |     transform: rotate(0) | ||||||
|  |   } | ||||||
|  |   20%, | ||||||
|  |   60% { | ||||||
|  |     transform: rotate(-25deg) | ||||||
|  |   } | ||||||
|  |   40%, | ||||||
|  |   80% { | ||||||
|  |     transform: rotate(10deg) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media (max-width:500px) { | ||||||
|  |   .github-corner:hover .octo-arm { | ||||||
|  |     animation: none | ||||||
|  |   } | ||||||
|  |   .github-corner .octo-arm { | ||||||
|  |     animation: octocat-wave 560ms ease-in-out | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | <template> | ||||||
|  |   <div style="padding: 0 15px;" @click="toggleClick"> | ||||||
|  |     <svg | ||||||
|  |       :class="{'is-active':isActive}" | ||||||
|  |       class="hamburger" | ||||||
|  |       viewBox="0 0 1024 1024" | ||||||
|  |       xmlns="http://www.w3.org/2000/svg" | ||||||
|  |       width="64" | ||||||
|  |       height="64" | ||||||
|  |     > | ||||||
|  |       <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" /> | ||||||
|  |     </svg> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'Hamburger', | ||||||
|  |   props: { | ||||||
|  |     isActive: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     toggleClick() { | ||||||
|  |       this.$emit('toggleClick') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .hamburger { | ||||||
|  |   display: inline-block; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   width: 20px; | ||||||
|  |   height: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hamburger.is-active { | ||||||
|  |   transform: rotate(180deg); | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,180 @@ | ||||||
|  | <template> | ||||||
|  |   <div :class="{'show':show}" class="header-search"> | ||||||
|  |     <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> | ||||||
|  |     <el-select | ||||||
|  |       ref="headerSearchSelect" | ||||||
|  |       v-model="search" | ||||||
|  |       :remote-method="querySearch" | ||||||
|  |       filterable | ||||||
|  |       default-first-option | ||||||
|  |       remote | ||||||
|  |       placeholder="Search" | ||||||
|  |       class="header-search-select" | ||||||
|  |       @change="change" | ||||||
|  |     > | ||||||
|  |       <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" /> | ||||||
|  |     </el-select> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | // fuse is a lightweight fuzzy-search module | ||||||
|  | // make search results more in line with expectations | ||||||
|  | import Fuse from 'fuse.js' | ||||||
|  | import path from 'path' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'HeaderSearch', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       search: '', | ||||||
|  |       options: [], | ||||||
|  |       searchPool: [], | ||||||
|  |       show: false, | ||||||
|  |       fuse: undefined | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     routes() { | ||||||
|  |       return this.$store.getters.permission_routes | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     routes() { | ||||||
|  |       this.searchPool = this.generateRoutes(this.routes) | ||||||
|  |     }, | ||||||
|  |     searchPool(list) { | ||||||
|  |       this.initFuse(list) | ||||||
|  |     }, | ||||||
|  |     show(value) { | ||||||
|  |       if (value) { | ||||||
|  |         document.body.addEventListener('click', this.close) | ||||||
|  |       } else { | ||||||
|  |         document.body.removeEventListener('click', this.close) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.searchPool = this.generateRoutes(this.routes) | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     click() { | ||||||
|  |       this.show = !this.show | ||||||
|  |       if (this.show) { | ||||||
|  |         this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     close() { | ||||||
|  |       this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur() | ||||||
|  |       this.options = [] | ||||||
|  |       this.show = false | ||||||
|  |     }, | ||||||
|  |     change(val) { | ||||||
|  |       this.$router.push(val.path) | ||||||
|  |       this.search = '' | ||||||
|  |       this.options = [] | ||||||
|  |       this.$nextTick(() => { | ||||||
|  |         this.show = false | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     initFuse(list) { | ||||||
|  |       this.fuse = new Fuse(list, { | ||||||
|  |         shouldSort: true, | ||||||
|  |         threshold: 0.4, | ||||||
|  |         location: 0, | ||||||
|  |         distance: 100, | ||||||
|  |         maxPatternLength: 32, | ||||||
|  |         minMatchCharLength: 1, | ||||||
|  |         keys: [{ | ||||||
|  |           name: 'title', | ||||||
|  |           weight: 0.7 | ||||||
|  |         }, { | ||||||
|  |           name: 'path', | ||||||
|  |           weight: 0.3 | ||||||
|  |         }] | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     // Filter out the routes that can be displayed in the sidebar | ||||||
|  |     // And generate the internationalized title | ||||||
|  |     generateRoutes(routes, basePath = '/', prefixTitle = []) { | ||||||
|  |       let res = [] | ||||||
|  | 
 | ||||||
|  |       for (const router of routes) { | ||||||
|  |         // skip hidden router | ||||||
|  |         if (router.hidden) { continue } | ||||||
|  | 
 | ||||||
|  |         const data = { | ||||||
|  |           path: path.resolve(basePath, router.path), | ||||||
|  |           title: [...prefixTitle] | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (router.meta && router.meta.title) { | ||||||
|  |           data.title = [...data.title, router.meta.title] | ||||||
|  | 
 | ||||||
|  |           if (router.redirect !== 'noRedirect') { | ||||||
|  |             // only push the routes with title | ||||||
|  |             // special case: need to exclude parent router without redirect | ||||||
|  |             res.push(data) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // recursive child routes | ||||||
|  |         if (router.children) { | ||||||
|  |           const tempRoutes = this.generateRoutes(router.children, data.path, data.title) | ||||||
|  |           if (tempRoutes.length >= 1) { | ||||||
|  |             res = [...res, ...tempRoutes] | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return res | ||||||
|  |     }, | ||||||
|  |     querySearch(query) { | ||||||
|  |       if (query !== '') { | ||||||
|  |         this.options = this.fuse.search(query) | ||||||
|  |       } else { | ||||||
|  |         this.options = [] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .header-search { | ||||||
|  |   font-size: 0 !important; | ||||||
|  | 
 | ||||||
|  |   .search-icon { | ||||||
|  |     cursor: pointer; | ||||||
|  |     font-size: 18px; | ||||||
|  |     vertical-align: middle; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .header-search-select { | ||||||
|  |     font-size: 18px; | ||||||
|  |     transition: width 0.2s; | ||||||
|  |     width: 0; | ||||||
|  |     overflow: hidden; | ||||||
|  |     background: transparent; | ||||||
|  |     border-radius: 0; | ||||||
|  |     display: inline-block; | ||||||
|  |     vertical-align: middle; | ||||||
|  | 
 | ||||||
|  |     ::v-deep .el-input__inner { | ||||||
|  |       border-radius: 0; | ||||||
|  |       border: 0; | ||||||
|  |       padding-left: 0; | ||||||
|  |       padding-right: 0; | ||||||
|  |       box-shadow: none !important; | ||||||
|  |       border-bottom: 1px solid #d9d9d9; | ||||||
|  |       vertical-align: middle; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.show { | ||||||
|  |     .header-search-select { | ||||||
|  |       width: 210px; | ||||||
|  |       margin-left: 10px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | /** | ||||||
|  |  * database64文件格式转换为2进制 | ||||||
|  |  * | ||||||
|  |  * @param  {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 | ||||||
|  |  * @param  {[String]} mime [description] | ||||||
|  |  * @return {[blob]}      [description] | ||||||
|  |  */ | ||||||
|  | export default function(data, mime) { | ||||||
|  |   data = data.split(',')[1] | ||||||
|  |   data = window.atob(data) | ||||||
|  |   var ia = new Uint8Array(data.length) | ||||||
|  |   for (var i = 0; i < data.length; i++) { | ||||||
|  |     ia[i] = data.charCodeAt(i) | ||||||
|  |   } | ||||||
|  |   // canvas.toDataURL 返回的默认格式就是 image/png
 | ||||||
|  |   return new Blob([ia], { | ||||||
|  |     type: mime | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | /** | ||||||
|  |  * 点击波纹效果 | ||||||
|  |  * | ||||||
|  |  * @param  {[event]} e        [description] | ||||||
|  |  * @param  {[Object]} arg_opts [description] | ||||||
|  |  * @return {[bollean]}          [description] | ||||||
|  |  */ | ||||||
|  | export default function(e, arg_opts) { | ||||||
|  |   var opts = Object.assign({ | ||||||
|  |     ele: e.target, // 波纹作用元素
 | ||||||
|  |     type: 'hit', // hit点击位置扩散center中心点扩展
 | ||||||
|  |     bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
 | ||||||
|  |   }, arg_opts) | ||||||
|  |   var target = opts.ele | ||||||
|  |   if (target) { | ||||||
|  |     var rect = target.getBoundingClientRect() | ||||||
|  |     var ripple = target.querySelector('.e-ripple') | ||||||
|  |     if (!ripple) { | ||||||
|  |       ripple = document.createElement('span') | ||||||
|  |       ripple.className = 'e-ripple' | ||||||
|  |       ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' | ||||||
|  |       target.appendChild(ripple) | ||||||
|  |     } else { | ||||||
|  |       ripple.className = 'e-ripple' | ||||||
|  |     } | ||||||
|  |     switch (opts.type) { | ||||||
|  |       case 'center': | ||||||
|  |         ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' | ||||||
|  |         ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' | ||||||
|  |         break | ||||||
|  |       default: | ||||||
|  |         ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' | ||||||
|  |         ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' | ||||||
|  |     } | ||||||
|  |     ripple.style.backgroundColor = opts.bgc | ||||||
|  |     ripple.className = 'e-ripple z-active' | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,232 @@ | ||||||
|  | export default { | ||||||
|  |   zh: { | ||||||
|  |     hint: '点击,或拖动图片至此处', | ||||||
|  |     loading: '正在上传……', | ||||||
|  |     noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!', | ||||||
|  |     success: '上传成功', | ||||||
|  |     fail: '图片上传失败', | ||||||
|  |     preview: '头像预览', | ||||||
|  |     btn: { | ||||||
|  |       off: '取消', | ||||||
|  |       close: '关闭', | ||||||
|  |       back: '上一步', | ||||||
|  |       save: '保存' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: '仅限图片格式', | ||||||
|  |       outOfSize: '单文件大小不能超过 ', | ||||||
|  |       lowestPx: '图片最低像素为(宽*高):' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   'zh-tw': { | ||||||
|  |     hint: '點擊,或拖動圖片至此處', | ||||||
|  |     loading: '正在上傳……', | ||||||
|  |     noSupported: '瀏覽器不支持該功能,請使用IE10以上或其他現代瀏覽器!', | ||||||
|  |     success: '上傳成功', | ||||||
|  |     fail: '圖片上傳失敗', | ||||||
|  |     preview: '頭像預覽', | ||||||
|  |     btn: { | ||||||
|  |       off: '取消', | ||||||
|  |       close: '關閉', | ||||||
|  |       back: '上一步', | ||||||
|  |       save: '保存' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: '僅限圖片格式', | ||||||
|  |       outOfSize: '單文件大小不能超過 ', | ||||||
|  |       lowestPx: '圖片最低像素為(寬*高):' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   en: { | ||||||
|  |     hint: 'Click or drag the file here to upload', | ||||||
|  |     loading: 'Uploading…', | ||||||
|  |     noSupported: 'Browser is not supported, please use IE10+ or other browsers', | ||||||
|  |     success: 'Upload success', | ||||||
|  |     fail: 'Upload failed', | ||||||
|  |     preview: 'Preview', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Cancel', | ||||||
|  |       close: 'Close', | ||||||
|  |       back: 'Back', | ||||||
|  |       save: 'Save' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Image only', | ||||||
|  |       outOfSize: 'Image exceeds size limit: ', | ||||||
|  |       lowestPx: 'Image\'s size is too low. Expected at least: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   ro: { | ||||||
|  |     hint: 'Atinge sau trage fișierul aici', | ||||||
|  |     loading: 'Se încarcă', | ||||||
|  |     noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.', | ||||||
|  |     success: 'S-a încărcat cu succes', | ||||||
|  |     fail: 'A apărut o problemă la încărcare', | ||||||
|  |     preview: 'Previzualizează', | ||||||
|  | 
 | ||||||
|  |     btn: { | ||||||
|  |       off: 'Anulează', | ||||||
|  |       close: 'Închide', | ||||||
|  |       back: 'Înapoi', | ||||||
|  |       save: 'Salvează' | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Doar imagini', | ||||||
|  |       outOfSize: 'Imaginea depășește limita de: ', | ||||||
|  |       loewstPx: 'Imaginea este prea mică; Minim: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   ru: { | ||||||
|  |     hint: 'Нажмите, или перетащите файл в это окно', | ||||||
|  |     loading: 'Загружаю……', | ||||||
|  |     noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры', | ||||||
|  |     success: 'Загрузка выполнена успешно', | ||||||
|  |     fail: 'Ошибка загрузки', | ||||||
|  |     preview: 'Предпросмотр', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Отменить', | ||||||
|  |       close: 'Закрыть', | ||||||
|  |       back: 'Назад', | ||||||
|  |       save: 'Сохранить' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Только изображения', | ||||||
|  |       outOfSize: 'Изображение превышает предельный размер: ', | ||||||
|  |       lowestPx: 'Минимальный размер изображения: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   'pt-br': { | ||||||
|  |     hint: 'Clique ou arraste o arquivo aqui para carregar', | ||||||
|  |     loading: 'Carregando…', | ||||||
|  |     noSupported: 'Browser não suportado, use o IE10+ ou outro browser', | ||||||
|  |     success: 'Sucesso ao carregar imagem', | ||||||
|  |     fail: 'Falha ao carregar imagem', | ||||||
|  |     preview: 'Pré-visualizar', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Cancelar', | ||||||
|  |       close: 'Fechar', | ||||||
|  |       back: 'Voltar', | ||||||
|  |       save: 'Salvar' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Apenas imagens', | ||||||
|  |       outOfSize: 'A imagem excede o limite de tamanho: ', | ||||||
|  |       lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   fr: { | ||||||
|  |     hint: 'Cliquez ou glissez le fichier ici.', | ||||||
|  |     loading: 'Téléchargement…', | ||||||
|  |     noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.', | ||||||
|  |     success: 'Téléchargement réussit', | ||||||
|  |     fail: 'Téléchargement echoué', | ||||||
|  |     preview: 'Aperçu', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Annuler', | ||||||
|  |       close: 'Fermer', | ||||||
|  |       back: 'Retour', | ||||||
|  |       save: 'Enregistrer' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Image uniquement', | ||||||
|  |       outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ', | ||||||
|  |       lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   nl: { | ||||||
|  |     hint: 'Klik hier of sleep een afbeelding in dit vlak', | ||||||
|  |     loading: 'Uploaden…', | ||||||
|  |     noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.', | ||||||
|  |     success: 'Upload succesvol', | ||||||
|  |     fail: 'Upload mislukt', | ||||||
|  |     preview: 'Voorbeeld', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Annuleren', | ||||||
|  |       close: 'Sluiten', | ||||||
|  |       back: 'Terug', | ||||||
|  |       save: 'Opslaan' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Alleen afbeeldingen', | ||||||
|  |       outOfSize: 'De afbeelding is groter dan: ', | ||||||
|  |       lowestPx: 'De afbeelding is te klein! Minimale afmetingen: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   tr: { | ||||||
|  |     hint: 'Tıkla veya yüklemek istediğini buraya sürükle', | ||||||
|  |     loading: 'Yükleniyor…', | ||||||
|  |     noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın', | ||||||
|  |     success: 'Yükleme başarılı', | ||||||
|  |     fail: 'Yüklemede hata oluştu', | ||||||
|  |     preview: 'Önizle', | ||||||
|  |     btn: { | ||||||
|  |       off: 'İptal', | ||||||
|  |       close: 'Kapat', | ||||||
|  |       back: 'Geri', | ||||||
|  |       save: 'Kaydet' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Sadece resim', | ||||||
|  |       outOfSize: 'Resim yükleme limitini aşıyor: ', | ||||||
|  |       lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   'es-MX': { | ||||||
|  |     hint: 'Selecciona o arrastra una imagen', | ||||||
|  |     loading: 'Subiendo...', | ||||||
|  |     noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes', | ||||||
|  |     success: 'Subido exitosamente', | ||||||
|  |     fail: 'Sucedió un error', | ||||||
|  |     preview: 'Vista previa', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Cancelar', | ||||||
|  |       close: 'Cerrar', | ||||||
|  |       back: 'Atras', | ||||||
|  |       save: 'Guardar' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Unicamente imagenes', | ||||||
|  |       outOfSize: 'La imagen excede el tamaño maximo:', | ||||||
|  |       lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   de: { | ||||||
|  |     hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen', | ||||||
|  |     loading: 'Hochladen…', | ||||||
|  |     noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser', | ||||||
|  |     success: 'Upload erfolgreich', | ||||||
|  |     fail: 'Upload fehlgeschlagen', | ||||||
|  |     preview: 'Vorschau', | ||||||
|  |     btn: { | ||||||
|  |       off: 'Abbrechen', | ||||||
|  |       close: 'Schließen', | ||||||
|  |       back: 'Zurück', | ||||||
|  |       save: 'Speichern' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: 'Nur Bilder', | ||||||
|  |       outOfSize: 'Das Bild ist zu groß: ', | ||||||
|  |       lowestPx: 'Das Bild ist zu klein. Mindestens: ' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   ja: { | ||||||
|  |     hint: 'クリック・ドラッグしてファイルをアップロード', | ||||||
|  |     loading: 'アップロード中...', | ||||||
|  |     noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。', | ||||||
|  |     success: 'アップロード成功', | ||||||
|  |     fail: 'アップロード失敗', | ||||||
|  |     preview: 'プレビュー', | ||||||
|  |     btn: { | ||||||
|  |       off: 'キャンセル', | ||||||
|  |       close: '閉じる', | ||||||
|  |       back: '戻る', | ||||||
|  |       save: '保存' | ||||||
|  |     }, | ||||||
|  |     error: { | ||||||
|  |       onlyImg: '画像のみ', | ||||||
|  |       outOfSize: '画像サイズが上限を超えています。上限: ', | ||||||
|  |       lowestPx: '画像が小さすぎます。最小サイズ: ' | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | export default { | ||||||
|  |   'jpg': 'image/jpeg', | ||||||
|  |   'png': 'image/png', | ||||||
|  |   'gif': 'image/gif', | ||||||
|  |   'svg': 'image/svg+xml', | ||||||
|  |   'psd': 'image/photoshop' | ||||||
|  | } | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | <template> | ||||||
|  |   <el-upload v-loading="loading" action="/" :auto-upload="false" :show-file-list="false" :on-change="onChange" :on-progress="onProgress"> | ||||||
|  |     <el-button :type="type" :size="size"> | ||||||
|  |       <slot>导入</slot> | ||||||
|  |     </el-button> | ||||||
|  |   </el-upload> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { import_data } from '@/api/import-data' | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     action: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     params: { | ||||||
|  |       type: Object, | ||||||
|  |       default: () => ({}) | ||||||
|  |     }, | ||||||
|  |     code: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     type: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'primary' | ||||||
|  |     }, | ||||||
|  |     size: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     onChange(file) { | ||||||
|  |       this.loading = true | ||||||
|  |       import_data({ | ||||||
|  |         file: file.raw, | ||||||
|  |         action: this.action, | ||||||
|  |         code: this.code, | ||||||
|  |         ...this.params | ||||||
|  |       }).then(res => { | ||||||
|  |         this.$message({ | ||||||
|  |           type: 'success', | ||||||
|  |           message: `上传成功: ${res.data}` | ||||||
|  |         }) | ||||||
|  |         this.$emit('success') | ||||||
|  |         this.loading = false | ||||||
|  |       }).catch(err => { | ||||||
|  |         this.$emit('fail', err) | ||||||
|  |         this.loading = false | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     onProgress(file) { | ||||||
|  |       console.log('progress') | ||||||
|  |       console.log(file) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="json-editor"> | ||||||
|  |     <textarea ref="textarea" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import CodeMirror from 'codemirror' | ||||||
|  | import 'codemirror/addon/lint/lint.css' | ||||||
|  | import 'codemirror/lib/codemirror.css' | ||||||
|  | import 'codemirror/theme/rubyblue.css' | ||||||
|  | require('script-loader!jsonlint') | ||||||
|  | import 'codemirror/mode/javascript/javascript' | ||||||
|  | import 'codemirror/addon/lint/lint' | ||||||
|  | import 'codemirror/addon/lint/json-lint' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'JsonEditor', | ||||||
|  |   /* eslint-disable vue/require-prop-types */ | ||||||
|  |   props: ['value'], | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       jsonEditor: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value(value) { | ||||||
|  |       const editorValue = this.jsonEditor.getValue() | ||||||
|  |       if (value !== editorValue) { | ||||||
|  |         this.jsonEditor.setValue(JSON.stringify(this.value, null, 2)) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, { | ||||||
|  |       lineNumbers: true, | ||||||
|  |       mode: 'application/json', | ||||||
|  |       gutters: ['CodeMirror-lint-markers'], | ||||||
|  |       theme: 'rubyblue', | ||||||
|  |       lint: true | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     this.jsonEditor.setValue(JSON.stringify(this.value, null, 2)) | ||||||
|  |     this.jsonEditor.on('change', cm => { | ||||||
|  |       this.$emit('changed', cm.getValue()) | ||||||
|  |       this.$emit('input', cm.getValue()) | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     getValue() { | ||||||
|  |       return this.jsonEditor.getValue() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .json-editor { | ||||||
|  |   height: 100%; | ||||||
|  |   position: relative; | ||||||
|  | 
 | ||||||
|  |   ::v-deep { | ||||||
|  |     .CodeMirror { | ||||||
|  |       height: auto; | ||||||
|  |       min-height: 300px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .CodeMirror-scroll { | ||||||
|  |       min-height: 300px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .cm-s-rubyblue span.cm-string { | ||||||
|  |       color: #F08047; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,99 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="board-column"> | ||||||
|  |     <div class="board-column-header"> | ||||||
|  |       {{ headerText }} | ||||||
|  |     </div> | ||||||
|  |     <draggable | ||||||
|  |       :list="list" | ||||||
|  |       v-bind="$attrs" | ||||||
|  |       class="board-column-content" | ||||||
|  |       :set-data="setData" | ||||||
|  |     > | ||||||
|  |       <div v-for="element in list" :key="element.id" class="board-item"> | ||||||
|  |         {{ element.name }} {{ element.id }} | ||||||
|  |       </div> | ||||||
|  |     </draggable> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import draggable from 'vuedraggable' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'DragKanbanDemo', | ||||||
|  |   components: { | ||||||
|  |     draggable | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     headerText: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'Header' | ||||||
|  |     }, | ||||||
|  |     options: { | ||||||
|  |       type: Object, | ||||||
|  |       default() { | ||||||
|  |         return {} | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     list: { | ||||||
|  |       type: Array, | ||||||
|  |       default() { | ||||||
|  |         return [] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     setData(dataTransfer) { | ||||||
|  |       // to avoid Firefox bug | ||||||
|  |       // Detail see : https://github.com/RubaXa/Sortable/issues/1012 | ||||||
|  |       dataTransfer.setData('Text', '') | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .board-column { | ||||||
|  |   min-width: 300px; | ||||||
|  |   min-height: 100px; | ||||||
|  |   height: auto; | ||||||
|  |   overflow: hidden; | ||||||
|  |   background: #f0f0f0; | ||||||
|  |   border-radius: 3px; | ||||||
|  | 
 | ||||||
|  |   .board-column-header { | ||||||
|  |     height: 50px; | ||||||
|  |     line-height: 50px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     padding: 0 20px; | ||||||
|  |     text-align: center; | ||||||
|  |     background: #333; | ||||||
|  |     color: #fff; | ||||||
|  |     border-radius: 3px 3px 0 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .board-column-content { | ||||||
|  |     height: auto; | ||||||
|  |     overflow: hidden; | ||||||
|  |     border: 10px solid transparent; | ||||||
|  |     min-height: 60px; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: flex-start; | ||||||
|  |     flex-direction: column; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     .board-item { | ||||||
|  |       cursor: pointer; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 64px; | ||||||
|  |       margin: 5px 0; | ||||||
|  |       background-color: #fff; | ||||||
|  |       text-align: left; | ||||||
|  |       line-height: 54px; | ||||||
|  |       padding: 5px 10px; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
 | ||||||
|  | export default { | ||||||
|  |   minHeight: '200px', | ||||||
|  |   previewStyle: 'vertical', | ||||||
|  |   useCommandShortcut: true, | ||||||
|  |   useDefaultHTMLSanitizer: true, | ||||||
|  |   usageStatistics: false, | ||||||
|  |   hideModeSwitch: false, | ||||||
|  |   toolbarItems: [ | ||||||
|  |     'heading', | ||||||
|  |     'bold', | ||||||
|  |     'italic', | ||||||
|  |     'strike', | ||||||
|  |     'divider', | ||||||
|  |     'hr', | ||||||
|  |     'quote', | ||||||
|  |     'divider', | ||||||
|  |     'ul', | ||||||
|  |     'ol', | ||||||
|  |     'task', | ||||||
|  |     'indent', | ||||||
|  |     'outdent', | ||||||
|  |     'divider', | ||||||
|  |     'table', | ||||||
|  |     'image', | ||||||
|  |     'link', | ||||||
|  |     'divider', | ||||||
|  |     'code', | ||||||
|  |     'codeblock' | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | @ -0,0 +1,118 @@ | ||||||
|  | <template> | ||||||
|  |   <div :id="id" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | // deps for editor | ||||||
|  | import 'codemirror/lib/codemirror.css' // codemirror | ||||||
|  | import 'tui-editor/dist/tui-editor.css' // editor ui | ||||||
|  | import 'tui-editor/dist/tui-editor-contents.css' // editor content | ||||||
|  | 
 | ||||||
|  | import Editor from 'tui-editor' | ||||||
|  | import defaultOptions from './default-options' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'MarkdownEditor', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       required: false, | ||||||
|  |       default() { | ||||||
|  |         return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     options: { | ||||||
|  |       type: Object, | ||||||
|  |       default() { | ||||||
|  |         return defaultOptions | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     mode: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'markdown' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: String, | ||||||
|  |       required: false, | ||||||
|  |       default: '300px' | ||||||
|  |     }, | ||||||
|  |     language: { | ||||||
|  |       type: String, | ||||||
|  |       required: false, | ||||||
|  |       default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       editor: null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     editorOptions() { | ||||||
|  |       const options = Object.assign({}, defaultOptions, this.options) | ||||||
|  |       options.initialEditType = this.mode | ||||||
|  |       options.height = this.height | ||||||
|  |       options.language = this.language | ||||||
|  |       return options | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value(newValue, preValue) { | ||||||
|  |       if (newValue !== preValue && newValue !== this.editor.getValue()) { | ||||||
|  |         this.editor.setValue(newValue) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     language(val) { | ||||||
|  |       this.destroyEditor() | ||||||
|  |       this.initEditor() | ||||||
|  |     }, | ||||||
|  |     height(newValue) { | ||||||
|  |       this.editor.height(newValue) | ||||||
|  |     }, | ||||||
|  |     mode(newValue) { | ||||||
|  |       this.editor.changeMode(newValue) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.initEditor() | ||||||
|  |   }, | ||||||
|  |   destroyed() { | ||||||
|  |     this.destroyEditor() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initEditor() { | ||||||
|  |       this.editor = new Editor({ | ||||||
|  |         el: document.getElementById(this.id), | ||||||
|  |         ...this.editorOptions | ||||||
|  |       }) | ||||||
|  |       if (this.value) { | ||||||
|  |         this.editor.setValue(this.value) | ||||||
|  |       } | ||||||
|  |       this.editor.on('change', () => { | ||||||
|  |         this.$emit('input', this.editor.getValue()) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     destroyEditor() { | ||||||
|  |       if (!this.editor) return | ||||||
|  |       this.editor.off('change') | ||||||
|  |       this.editor.remove() | ||||||
|  |     }, | ||||||
|  |     setValue(value) { | ||||||
|  |       this.editor.setValue(value) | ||||||
|  |     }, | ||||||
|  |     getValue() { | ||||||
|  |       return this.editor.getValue() | ||||||
|  |     }, | ||||||
|  |     setHtml(value) { | ||||||
|  |       this.editor.setHtml(value) | ||||||
|  |     }, | ||||||
|  |     getHtml() { | ||||||
|  |       return this.editor.getHtml() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | <template> | ||||||
|  |   <div :class="{'hidden':hidden}" class="pagination-container"> | ||||||
|  |     <el-pagination | ||||||
|  |       :background="background" | ||||||
|  |       :current-page.sync="currentPage" | ||||||
|  |       :page-size.sync="pageSize" | ||||||
|  |       :layout="layout" | ||||||
|  |       :page-sizes="pageSizes" | ||||||
|  |       :total="total" | ||||||
|  |       v-bind="$attrs" | ||||||
|  |       @size-change="handleSizeChange" | ||||||
|  |       @current-change="handleCurrentChange" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { scrollTo } from '@/utils/scroll-to' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'Pagination', | ||||||
|  |   props: { | ||||||
|  |     total: { | ||||||
|  |       required: true, | ||||||
|  |       type: Number | ||||||
|  |     }, | ||||||
|  |     page: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1 | ||||||
|  |     }, | ||||||
|  |     limit: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 20 | ||||||
|  |     }, | ||||||
|  |     pageSizes: { | ||||||
|  |       type: Array, | ||||||
|  |       default() { | ||||||
|  |         return [10, 20, 30, 50] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     layout: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'total, sizes, prev, pager, next, jumper' | ||||||
|  |     }, | ||||||
|  |     background: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true | ||||||
|  |     }, | ||||||
|  |     autoScroll: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: true | ||||||
|  |     }, | ||||||
|  |     hidden: { | ||||||
|  |       type: Boolean, | ||||||
|  |       default: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     currentPage: { | ||||||
|  |       get() { | ||||||
|  |         return this.page | ||||||
|  |       }, | ||||||
|  |       set(val) { | ||||||
|  |         this.$emit('update:page', val) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     pageSize: { | ||||||
|  |       get() { | ||||||
|  |         return this.limit | ||||||
|  |       }, | ||||||
|  |       set(val) { | ||||||
|  |         this.$emit('update:limit', val) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     handleSizeChange(val) { | ||||||
|  |       this.$emit('pagination', { page: this.currentPage, limit: val }) | ||||||
|  |       if (this.autoScroll) { | ||||||
|  |         scrollTo(0, 800) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     handleCurrentChange(val) { | ||||||
|  |       this.$emit('pagination', { page: val, limit: this.pageSize }) | ||||||
|  |       if (this.autoScroll) { | ||||||
|  |         scrollTo(0, 800) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .pagination-container { | ||||||
|  |   background: #fff; | ||||||
|  |   /* padding: 32px 16px; */ | ||||||
|  | } | ||||||
|  | .pagination-container.hidden { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,142 @@ | ||||||
|  | <template> | ||||||
|  |   <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item"> | ||||||
|  |     <div class="pan-info"> | ||||||
|  |       <div class="pan-info-roles-container"> | ||||||
|  |         <slot /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <!-- eslint-disable-next-line --> | ||||||
|  |     <div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'PanThumb', | ||||||
|  |   props: { | ||||||
|  |     image: { | ||||||
|  |       type: String, | ||||||
|  |       required: true | ||||||
|  |     }, | ||||||
|  |     zIndex: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1 | ||||||
|  |     }, | ||||||
|  |     width: { | ||||||
|  |       type: String, | ||||||
|  |       default: '150px' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: String, | ||||||
|  |       default: '150px' | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .pan-item { | ||||||
|  |   width: 200px; | ||||||
|  |   height: 200px; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   display: inline-block; | ||||||
|  |   position: relative; | ||||||
|  |   cursor: default; | ||||||
|  |   box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-info-roles-container { | ||||||
|  |   padding: 20px; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-thumb { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   background-position: center center; | ||||||
|  |   background-size: cover; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   overflow: hidden; | ||||||
|  |   position: absolute; | ||||||
|  |   transform-origin: 95% 40%; | ||||||
|  |   transition: all 0.3s ease-in-out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* .pan-thumb:after { | ||||||
|  |   content: ''; | ||||||
|  |   width: 8px; | ||||||
|  |   height: 8px; | ||||||
|  |   position: absolute; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   top: 40%; | ||||||
|  |   left: 95%; | ||||||
|  |   margin: -4px 0 0 -4px; | ||||||
|  |   background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%); | ||||||
|  |   box-shadow: 0 0 1px rgba(255, 255, 255, 0.9); | ||||||
|  | } */ | ||||||
|  | 
 | ||||||
|  | .pan-info { | ||||||
|  |   position: absolute; | ||||||
|  |   width: inherit; | ||||||
|  |   height: inherit; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   overflow: hidden; | ||||||
|  |   box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-info h3 { | ||||||
|  |   color: #fff; | ||||||
|  |   text-transform: uppercase; | ||||||
|  |   position: relative; | ||||||
|  |   letter-spacing: 2px; | ||||||
|  |   font-size: 18px; | ||||||
|  |   margin: 0 60px; | ||||||
|  |   padding: 22px 0 0 0; | ||||||
|  |   height: 85px; | ||||||
|  |   font-family: 'Open Sans', Arial, sans-serif; | ||||||
|  |   text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-info p { | ||||||
|  |   color: #fff; | ||||||
|  |   padding: 10px 5px; | ||||||
|  |   font-style: italic; | ||||||
|  |   margin: 0 30px; | ||||||
|  |   font-size: 12px; | ||||||
|  |   border-top: 1px solid rgba(255, 255, 255, 0.5); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-info p a { | ||||||
|  |   display: block; | ||||||
|  |   color: #333; | ||||||
|  |   width: 80px; | ||||||
|  |   height: 80px; | ||||||
|  |   background: rgba(255, 255, 255, 0.3); | ||||||
|  |   border-radius: 50%; | ||||||
|  |   color: #fff; | ||||||
|  |   font-style: normal; | ||||||
|  |   font-weight: 700; | ||||||
|  |   text-transform: uppercase; | ||||||
|  |   font-size: 9px; | ||||||
|  |   letter-spacing: 1px; | ||||||
|  |   padding-top: 24px; | ||||||
|  |   margin: 7px auto 0; | ||||||
|  |   font-family: 'Open Sans', Arial, sans-serif; | ||||||
|  |   opacity: 0; | ||||||
|  |   transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s; | ||||||
|  |   transform: translateX(60px) rotate(90deg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-info p a:hover { | ||||||
|  |   background: rgba(255, 255, 255, 0.5); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-item:hover .pan-thumb { | ||||||
|  |   transform: rotate(-110deg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .pan-item:hover .pan-info p a { | ||||||
|  |   opacity: 1; | ||||||
|  |   transform: translateX(0px) rotate(0deg); | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,145 @@ | ||||||
|  | <template> | ||||||
|  |   <div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> | ||||||
|  |     <div class="rightPanel-background" /> | ||||||
|  |     <div class="rightPanel"> | ||||||
|  |       <div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show"> | ||||||
|  |         <i :class="show?'el-icon-close':'el-icon-setting'" /> | ||||||
|  |       </div> | ||||||
|  |       <div class="rightPanel-items"> | ||||||
|  |         <slot /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { addClass, removeClass } from '@/utils' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'RightPanel', | ||||||
|  |   props: { | ||||||
|  |     clickNotClose: { | ||||||
|  |       default: false, | ||||||
|  |       type: Boolean | ||||||
|  |     }, | ||||||
|  |     buttonTop: { | ||||||
|  |       default: 250, | ||||||
|  |       type: Number | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       show: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     theme() { | ||||||
|  |       return this.$store.state.settings.theme | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     show(value) { | ||||||
|  |       if (value && !this.clickNotClose) { | ||||||
|  |         this.addEventClick() | ||||||
|  |       } | ||||||
|  |       if (value) { | ||||||
|  |         addClass(document.body, 'showRightPanel') | ||||||
|  |       } else { | ||||||
|  |         removeClass(document.body, 'showRightPanel') | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.insertToBody() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     const elx = this.$refs.rightPanel | ||||||
|  |     elx.remove() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     addEventClick() { | ||||||
|  |       window.addEventListener('click', this.closeSidebar) | ||||||
|  |     }, | ||||||
|  |     closeSidebar(evt) { | ||||||
|  |       const parent = evt.target.closest('.rightPanel') | ||||||
|  |       if (!parent) { | ||||||
|  |         this.show = false | ||||||
|  |         window.removeEventListener('click', this.closeSidebar) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     insertToBody() { | ||||||
|  |       const elx = this.$refs.rightPanel | ||||||
|  |       const body = document.querySelector('body') | ||||||
|  |       body.insertBefore(elx, body.firstChild) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | .showRightPanel { | ||||||
|  |   overflow: hidden; | ||||||
|  |   position: relative; | ||||||
|  |   width: calc(100% - 15px); | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .rightPanel-background { | ||||||
|  |   position: fixed; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   opacity: 0; | ||||||
|  |   transition: opacity .3s cubic-bezier(.7, .3, .1, 1); | ||||||
|  |   background: rgba(0, 0, 0, .2); | ||||||
|  |   z-index: -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .rightPanel { | ||||||
|  |   width: 100%; | ||||||
|  |   max-width: 260px; | ||||||
|  |   height: 100vh; | ||||||
|  |   position: fixed; | ||||||
|  |   top: 0; | ||||||
|  |   right: 0; | ||||||
|  |   box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); | ||||||
|  |   transition: all .25s cubic-bezier(.7, .3, .1, 1); | ||||||
|  |   transform: translate(100%); | ||||||
|  |   background: #fff; | ||||||
|  |   z-index: 40000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .show { | ||||||
|  |   transition: all .3s cubic-bezier(.7, .3, .1, 1); | ||||||
|  | 
 | ||||||
|  |   .rightPanel-background { | ||||||
|  |     z-index: 20000; | ||||||
|  |     opacity: 1; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .rightPanel { | ||||||
|  |     transform: translate(0); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .handle-button { | ||||||
|  |   width: 48px; | ||||||
|  |   height: 48px; | ||||||
|  |   position: absolute; | ||||||
|  |   left: -48px; | ||||||
|  |   text-align: center; | ||||||
|  |   font-size: 24px; | ||||||
|  |   border-radius: 6px 0 0 6px !important; | ||||||
|  |   z-index: 0; | ||||||
|  |   pointer-events: auto; | ||||||
|  |   cursor: pointer; | ||||||
|  |   color: #fff; | ||||||
|  |   line-height: 48px; | ||||||
|  |   i { | ||||||
|  |     font-size: 24px; | ||||||
|  |     line-height: 48px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,60 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import screenfull from 'screenfull' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'Screenfull', | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       isFullscreen: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.init() | ||||||
|  |   }, | ||||||
|  |   beforeDestroy() { | ||||||
|  |     this.destroy() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     click() { | ||||||
|  |       if (!screenfull.enabled) { | ||||||
|  |         this.$message({ | ||||||
|  |           message: 'you browser can not work', | ||||||
|  |           type: 'warning' | ||||||
|  |         }) | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  |       screenfull.toggle() | ||||||
|  |     }, | ||||||
|  |     change() { | ||||||
|  |       this.isFullscreen = screenfull.isFullscreen | ||||||
|  |     }, | ||||||
|  |     init() { | ||||||
|  |       if (screenfull.enabled) { | ||||||
|  |         screenfull.on('change', this.change) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     destroy() { | ||||||
|  |       if (screenfull.enabled) { | ||||||
|  |         screenfull.off('change', this.change) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .screenfull-svg { | ||||||
|  |   display: inline-block; | ||||||
|  |   cursor: pointer; | ||||||
|  |   fill: #5a5e66;; | ||||||
|  |   width: 20px; | ||||||
|  |   height: 20px; | ||||||
|  |   vertical-align: 10px; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,103 @@ | ||||||
|  | <template> | ||||||
|  |   <div :class="{active:isActive}" class="share-dropdown-menu"> | ||||||
|  |     <div class="share-dropdown-menu-wrapper"> | ||||||
|  |       <span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span> | ||||||
|  |       <div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item"> | ||||||
|  |         <a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a> | ||||||
|  |         <span v-else>{{ item.title }}</span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     items: { | ||||||
|  |       type: Array, | ||||||
|  |       default: function() { | ||||||
|  |         return [] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     title: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'vue' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       isActive: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     clickTitle() { | ||||||
|  |       this.isActive = !this.isActive | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" > | ||||||
|  | $n: 9; //和items.length 相同 | ||||||
|  | $t: .1s; | ||||||
|  | .share-dropdown-menu { | ||||||
|  |   width: 250px; | ||||||
|  |   position: relative; | ||||||
|  |   z-index: 1; | ||||||
|  |   height: auto!important; | ||||||
|  |   &-title { | ||||||
|  |     width: 100%; | ||||||
|  |     display: block; | ||||||
|  |     cursor: pointer; | ||||||
|  |     background: black; | ||||||
|  |     color: white; | ||||||
|  |     height: 60px; | ||||||
|  |     line-height: 60px; | ||||||
|  |     font-size: 20px; | ||||||
|  |     text-align: center; | ||||||
|  |     z-index: 2; | ||||||
|  |     transform: translate3d(0,0,0); | ||||||
|  |   } | ||||||
|  |   &-wrapper { | ||||||
|  |     position: relative; | ||||||
|  |   } | ||||||
|  |   &-item { | ||||||
|  |     text-align: center; | ||||||
|  |     position: absolute; | ||||||
|  |     width: 100%; | ||||||
|  |     background: #e0e0e0; | ||||||
|  |     color: #000; | ||||||
|  |     line-height: 60px; | ||||||
|  |     height: 60px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     font-size: 18px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     opacity: 1; | ||||||
|  |     transition: transform 0.28s ease; | ||||||
|  |     &:hover { | ||||||
|  |       background: black; | ||||||
|  |       color: white; | ||||||
|  |     } | ||||||
|  |     @for $i from 1 through $n { | ||||||
|  |       &:nth-of-type(#{$i}) { | ||||||
|  |         z-index: -1; | ||||||
|  |         transition-delay: $i*$t; | ||||||
|  |         transform: translate3d(0, -60px, 0); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   &.active { | ||||||
|  |     .share-dropdown-menu-wrapper { | ||||||
|  |       z-index: 1; | ||||||
|  |     } | ||||||
|  |     .share-dropdown-menu-item { | ||||||
|  |       @for $i from 1 through $n { | ||||||
|  |         &:nth-of-type(#{$i}) { | ||||||
|  |           transition-delay: ($n - $i)*$t; | ||||||
|  |           transform: translate3d(0, ($i - 1)*60px, 0); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | <template> | ||||||
|  |   <el-dropdown trigger="click" @command="handleSetSize"> | ||||||
|  |     <div> | ||||||
|  |       <svg-icon class-name="size-icon" icon-class="size" /> | ||||||
|  |     </div> | ||||||
|  |     <el-dropdown-menu slot="dropdown"> | ||||||
|  |       <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value"> | ||||||
|  |         {{ | ||||||
|  |           item.label }} | ||||||
|  |       </el-dropdown-item> | ||||||
|  |     </el-dropdown-menu> | ||||||
|  |   </el-dropdown> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       sizeOptions: [ | ||||||
|  |         { label: 'Default', value: 'default' }, | ||||||
|  |         { label: 'Medium', value: 'medium' }, | ||||||
|  |         { label: 'Small', value: 'small' }, | ||||||
|  |         { label: 'Mini', value: 'mini' } | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     size() { | ||||||
|  |       return this.$store.getters.size | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     handleSetSize(size) { | ||||||
|  |       this.$ELEMENT.size = size | ||||||
|  |       this.$store.dispatch('app/setSize', size) | ||||||
|  |       this.refreshView() | ||||||
|  |       this.$message({ | ||||||
|  |         message: 'Switch Size Success', | ||||||
|  |         type: 'success' | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     refreshView() { | ||||||
|  |       // In order to make the cached page re-rendered | ||||||
|  |       this.$store.dispatch('tagsView/delAllCachedViews', this.$route) | ||||||
|  | 
 | ||||||
|  |       const { fullPath } = this.$route | ||||||
|  | 
 | ||||||
|  |       this.$nextTick(() => { | ||||||
|  |         this.$router.replace({ | ||||||
|  |           path: '/redirect' + fullPath | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | <template> | ||||||
|  |   <div :style="{height:height+'px',zIndex:zIndex}"> | ||||||
|  |     <div | ||||||
|  |       :class="className" | ||||||
|  |       :style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}" | ||||||
|  |     > | ||||||
|  |       <slot> | ||||||
|  |         <div>sticky</div> | ||||||
|  |       </slot> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'Sticky', | ||||||
|  |   props: { | ||||||
|  |     stickyTop: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 0 | ||||||
|  |     }, | ||||||
|  |     zIndex: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1 | ||||||
|  |     }, | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       active: false, | ||||||
|  |       position: '', | ||||||
|  |       width: undefined, | ||||||
|  |       height: undefined, | ||||||
|  |       isSticky: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.height = this.$el.getBoundingClientRect().height | ||||||
|  |     window.addEventListener('scroll', this.handleScroll) | ||||||
|  |     window.addEventListener('resize', this.handleResize) | ||||||
|  |   }, | ||||||
|  |   activated() { | ||||||
|  |     this.handleScroll() | ||||||
|  |   }, | ||||||
|  |   destroyed() { | ||||||
|  |     window.removeEventListener('scroll', this.handleScroll) | ||||||
|  |     window.removeEventListener('resize', this.handleResize) | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     sticky() { | ||||||
|  |       if (this.active) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.position = 'fixed' | ||||||
|  |       this.active = true | ||||||
|  |       this.width = this.width + 'px' | ||||||
|  |       this.isSticky = true | ||||||
|  |     }, | ||||||
|  |     handleReset() { | ||||||
|  |       if (!this.active) { | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.reset() | ||||||
|  |     }, | ||||||
|  |     reset() { | ||||||
|  |       this.position = '' | ||||||
|  |       this.width = 'auto' | ||||||
|  |       this.active = false | ||||||
|  |       this.isSticky = false | ||||||
|  |     }, | ||||||
|  |     handleScroll() { | ||||||
|  |       const width = this.$el.getBoundingClientRect().width | ||||||
|  |       this.width = width || 'auto' | ||||||
|  |       const offsetTop = this.$el.getBoundingClientRect().top | ||||||
|  |       if (offsetTop < this.stickyTop) { | ||||||
|  |         this.sticky() | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       this.handleReset() | ||||||
|  |     }, | ||||||
|  |     handleResize() { | ||||||
|  |       if (this.isSticky) { | ||||||
|  |         this.width = this.$el.getBoundingClientRect().width + 'px' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -0,0 +1,62 @@ | ||||||
|  | <template> | ||||||
|  |   <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" /> | ||||||
|  |   <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"> | ||||||
|  |     <use :xlink:href="iconName" /> | ||||||
|  |   </svg> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage | ||||||
|  | import { isExternal } from '@/utils/validate' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'SvgIcon', | ||||||
|  |   props: { | ||||||
|  |     iconClass: { | ||||||
|  |       type: String, | ||||||
|  |       required: true | ||||||
|  |     }, | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     isExternal() { | ||||||
|  |       return isExternal(this.iconClass) | ||||||
|  |     }, | ||||||
|  |     iconName() { | ||||||
|  |       return `#icon-${this.iconClass}` | ||||||
|  |     }, | ||||||
|  |     svgClass() { | ||||||
|  |       if (this.className) { | ||||||
|  |         return 'svg-icon ' + this.className | ||||||
|  |       } else { | ||||||
|  |         return 'svg-icon' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     styleExternalIcon() { | ||||||
|  |       return { | ||||||
|  |         mask: `url(${this.iconClass}) no-repeat 50% 50%`, | ||||||
|  |         '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%` | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .svg-icon { | ||||||
|  |   width: 1em; | ||||||
|  |   height: 1em; | ||||||
|  |   vertical-align: -0.15em; | ||||||
|  |   fill: currentColor; | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .svg-external-icon { | ||||||
|  |   background-color: currentColor; | ||||||
|  |   mask-size: cover!important; | ||||||
|  |   display: inline-block; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,113 @@ | ||||||
|  | <template> | ||||||
|  |   <a :class="className" class="link--mallki" href="#"> | ||||||
|  |     {{ text }} | ||||||
|  |     <span :data-letters="text" /> | ||||||
|  |     <span :data-letters="text" /> | ||||||
|  |   </a> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     className: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     text: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'vue-element-admin' | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | /* Mallki */ | ||||||
|  | 
 | ||||||
|  | .link--mallki { | ||||||
|  |   font-weight: 800; | ||||||
|  |   color: #4dd9d5; | ||||||
|  |   font-family: 'Dosis', sans-serif; | ||||||
|  |   -webkit-transition: color 0.5s 0.25s; | ||||||
|  |   transition: color 0.5s 0.25s; | ||||||
|  |   overflow: hidden; | ||||||
|  |   position: relative; | ||||||
|  |   display: inline-block; | ||||||
|  |   line-height: 1; | ||||||
|  |   outline: none; | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki:hover { | ||||||
|  |   -webkit-transition: none; | ||||||
|  |   transition: none; | ||||||
|  |   color: transparent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki::before { | ||||||
|  |   content: ''; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 6px; | ||||||
|  |   margin: -3px 0 0 0; | ||||||
|  |   background: #3888fa; | ||||||
|  |   position: absolute; | ||||||
|  |   left: 0; | ||||||
|  |   top: 50%; | ||||||
|  |   -webkit-transform: translate3d(-100%, 0, 0); | ||||||
|  |   transform: translate3d(-100%, 0, 0); | ||||||
|  |   -webkit-transition: -webkit-transform 0.4s; | ||||||
|  |   transition: transform 0.4s; | ||||||
|  |   -webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1); | ||||||
|  |   transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki:hover::before { | ||||||
|  |   -webkit-transform: translate3d(100%, 0, 0); | ||||||
|  |   transform: translate3d(100%, 0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki span { | ||||||
|  |   position: absolute; | ||||||
|  |   height: 50%; | ||||||
|  |   width: 100%; | ||||||
|  |   left: 0; | ||||||
|  |   top: 0; | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki span::before { | ||||||
|  |   content: attr(data-letters); | ||||||
|  |   color: red; | ||||||
|  |   position: absolute; | ||||||
|  |   left: 0; | ||||||
|  |   width: 100%; | ||||||
|  |   color: #3888fa; | ||||||
|  |   -webkit-transition: -webkit-transform 0.5s; | ||||||
|  |   transition: transform 0.5s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki span:nth-child(2) { | ||||||
|  |   top: 50%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki span:first-child::before { | ||||||
|  |   top: 0; | ||||||
|  |   -webkit-transform: translate3d(0, 100%, 0); | ||||||
|  |   transform: translate3d(0, 100%, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki span:nth-child(2)::before { | ||||||
|  |   bottom: 0; | ||||||
|  |   -webkit-transform: translate3d(0, -100%, 0); | ||||||
|  |   transform: translate3d(0, -100%, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .link--mallki:hover span::before { | ||||||
|  |   -webkit-transition-delay: 0.3s; | ||||||
|  |   transition-delay: 0.3s; | ||||||
|  |   -webkit-transform: translate3d(0, 0, 0); | ||||||
|  |   transform: translate3d(0, 0, 0); | ||||||
|  |   -webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); | ||||||
|  |   transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,175 @@ | ||||||
|  | <template> | ||||||
|  |   <el-color-picker | ||||||
|  |     v-model="theme" | ||||||
|  |     :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]" | ||||||
|  |     class="theme-picker" | ||||||
|  |     popper-class="theme-picker-dropdown" | ||||||
|  |   /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | const version = require('element-ui/package.json').version // element-ui version from node_modules | ||||||
|  | const ORIGINAL_THEME = '#409EFF' // default color | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       chalk: '', // content of theme-chalk css | ||||||
|  |       theme: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     defaultTheme() { | ||||||
|  |       return this.$store.state.settings.theme | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     defaultTheme: { | ||||||
|  |       handler: function(val, oldVal) { | ||||||
|  |         this.theme = val | ||||||
|  |       }, | ||||||
|  |       immediate: true | ||||||
|  |     }, | ||||||
|  |     async theme(val) { | ||||||
|  |       const oldVal = this.chalk ? this.theme : ORIGINAL_THEME | ||||||
|  |       if (typeof val !== 'string') return | ||||||
|  |       const themeCluster = this.getThemeCluster(val.replace('#', '')) | ||||||
|  |       const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) | ||||||
|  |       console.log(themeCluster, originalCluster) | ||||||
|  | 
 | ||||||
|  |       const $message = this.$message({ | ||||||
|  |         message: '  Compiling the theme', | ||||||
|  |         customClass: 'theme-message', | ||||||
|  |         type: 'success', | ||||||
|  |         duration: 0, | ||||||
|  |         iconClass: 'el-icon-loading' | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       const getHandler = (variable, id) => { | ||||||
|  |         return () => { | ||||||
|  |           const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', '')) | ||||||
|  |           const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster) | ||||||
|  | 
 | ||||||
|  |           let styleTag = document.getElementById(id) | ||||||
|  |           if (!styleTag) { | ||||||
|  |             styleTag = document.createElement('style') | ||||||
|  |             styleTag.setAttribute('id', id) | ||||||
|  |             document.head.appendChild(styleTag) | ||||||
|  |           } | ||||||
|  |           styleTag.innerText = newStyle | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!this.chalk) { | ||||||
|  |         const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css` | ||||||
|  |         await this.getCSSString(url, 'chalk') | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const chalkHandler = getHandler('chalk', 'chalk-style') | ||||||
|  | 
 | ||||||
|  |       chalkHandler() | ||||||
|  | 
 | ||||||
|  |       const styles = [].slice.call(document.querySelectorAll('style')) | ||||||
|  |         .filter(style => { | ||||||
|  |           const text = style.innerText | ||||||
|  |           return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text) | ||||||
|  |         }) | ||||||
|  |       styles.forEach(style => { | ||||||
|  |         const { innerText } = style | ||||||
|  |         if (typeof innerText !== 'string') return | ||||||
|  |         style.innerText = this.updateStyle(innerText, originalCluster, themeCluster) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       this.$emit('change', val) | ||||||
|  | 
 | ||||||
|  |       $message.close() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   methods: { | ||||||
|  |     updateStyle(style, oldCluster, newCluster) { | ||||||
|  |       let newStyle = style | ||||||
|  |       oldCluster.forEach((color, index) => { | ||||||
|  |         newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index]) | ||||||
|  |       }) | ||||||
|  |       return newStyle | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getCSSString(url, variable) { | ||||||
|  |       return new Promise(resolve => { | ||||||
|  |         const xhr = new XMLHttpRequest() | ||||||
|  |         xhr.onreadystatechange = () => { | ||||||
|  |           if (xhr.readyState === 4 && xhr.status === 200) { | ||||||
|  |             this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') | ||||||
|  |             resolve() | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         xhr.open('GET', url) | ||||||
|  |         xhr.send() | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getThemeCluster(theme) { | ||||||
|  |       const tintColor = (color, tint) => { | ||||||
|  |         let red = parseInt(color.slice(0, 2), 16) | ||||||
|  |         let green = parseInt(color.slice(2, 4), 16) | ||||||
|  |         let blue = parseInt(color.slice(4, 6), 16) | ||||||
|  | 
 | ||||||
|  |         if (tint === 0) { // when primary color is in its rgb space | ||||||
|  |           return [red, green, blue].join(',') | ||||||
|  |         } else { | ||||||
|  |           red += Math.round(tint * (255 - red)) | ||||||
|  |           green += Math.round(tint * (255 - green)) | ||||||
|  |           blue += Math.round(tint * (255 - blue)) | ||||||
|  | 
 | ||||||
|  |           red = red.toString(16) | ||||||
|  |           green = green.toString(16) | ||||||
|  |           blue = blue.toString(16) | ||||||
|  | 
 | ||||||
|  |           return `#${red}${green}${blue}` | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const shadeColor = (color, shade) => { | ||||||
|  |         let red = parseInt(color.slice(0, 2), 16) | ||||||
|  |         let green = parseInt(color.slice(2, 4), 16) | ||||||
|  |         let blue = parseInt(color.slice(4, 6), 16) | ||||||
|  | 
 | ||||||
|  |         red = Math.round((1 - shade) * red) | ||||||
|  |         green = Math.round((1 - shade) * green) | ||||||
|  |         blue = Math.round((1 - shade) * blue) | ||||||
|  | 
 | ||||||
|  |         red = red.toString(16) | ||||||
|  |         green = green.toString(16) | ||||||
|  |         blue = blue.toString(16) | ||||||
|  | 
 | ||||||
|  |         return `#${red}${green}${blue}` | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const clusters = [theme] | ||||||
|  |       for (let i = 0; i <= 9; i++) { | ||||||
|  |         clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))) | ||||||
|  |       } | ||||||
|  |       clusters.push(shadeColor(theme, 0.1)) | ||||||
|  |       return clusters | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | .theme-message, | ||||||
|  | .theme-picker-dropdown { | ||||||
|  |   z-index: 99999 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .theme-picker .el-color-picker__trigger { | ||||||
|  |   height: 26px !important; | ||||||
|  |   width: 26px !important; | ||||||
|  |   padding: 2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .theme-picker-dropdown .el-color-dropdown__link-btn { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,119 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="upload-container"> | ||||||
|  |     <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true"> | ||||||
|  |       上传图片 | ||||||
|  |     </el-button> | ||||||
|  |     <el-dialog :visible.sync="dialogVisible"> | ||||||
|  |       <!-- <el-upload | ||||||
|  |         :multiple="true" | ||||||
|  |         :file-list="fileList" | ||||||
|  |         :show-file-list="true" | ||||||
|  |         :on-remove="handleRemove" | ||||||
|  |         :on-success="handleSuccess" | ||||||
|  |         :before-upload="beforeUpload" | ||||||
|  |         class="editor-slide-upload" | ||||||
|  |         action="https://httpbin.org/post" | ||||||
|  |         list-type="picture-card" | ||||||
|  |       > | ||||||
|  |         <el-button size="small" type="primary"> | ||||||
|  |           点击上传 | ||||||
|  |         </el-button> | ||||||
|  |       </el-upload> --> | ||||||
|  |       <upload-image v-model="fileList" :limit="10"> | ||||||
|  |         <el-button size="small" type="primary"> | ||||||
|  |           点击上传 | ||||||
|  |         </el-button> | ||||||
|  |       </upload-image> | ||||||
|  |       <el-button @click="dialogVisible = false"> | ||||||
|  |         取消 | ||||||
|  |       </el-button> | ||||||
|  |       <el-button type="primary" @click="handleSubmit"> | ||||||
|  |         确定 | ||||||
|  |       </el-button> | ||||||
|  |     </el-dialog> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | // import { getToken } from 'api/qiniu' | ||||||
|  | import UploadImage from '@/components/UploadImage' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'EditorSlideUpload', | ||||||
|  |   components: { UploadImage }, | ||||||
|  |   props: { | ||||||
|  |     color: { | ||||||
|  |       type: String, | ||||||
|  |       default: '#1890ff' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       dialogVisible: false, | ||||||
|  |       listObj: {}, | ||||||
|  |       fileList: [] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     checkAllSuccess() { | ||||||
|  |       return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess) | ||||||
|  |     }, | ||||||
|  |     handleSubmit() { | ||||||
|  |       // const arr = Object.keys(this.listObj).map(v => this.listObj[v]) | ||||||
|  |       // if (!this.checkAllSuccess()) { | ||||||
|  |       //   this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!') | ||||||
|  |       //   return | ||||||
|  |       // } | ||||||
|  |       // this.$emit('successCBK', arr) | ||||||
|  |       this.$emit('successCBK', this.fileList) | ||||||
|  |       this.listObj = {} | ||||||
|  |       this.fileList = [] | ||||||
|  |       this.dialogVisible = false | ||||||
|  |     }, | ||||||
|  |     handleSuccess(response, file) { | ||||||
|  |       const uid = file.uid | ||||||
|  |       const objKeyArr = Object.keys(this.listObj) | ||||||
|  |       for (let i = 0, len = objKeyArr.length; i < len; i++) { | ||||||
|  |         if (this.listObj[objKeyArr[i]].uid === uid) { | ||||||
|  |           this.listObj[objKeyArr[i]].url = response.files.file | ||||||
|  |           this.listObj[objKeyArr[i]].hasSuccess = true | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     handleRemove(file) { | ||||||
|  |       const uid = file.uid | ||||||
|  |       const objKeyArr = Object.keys(this.listObj) | ||||||
|  |       for (let i = 0, len = objKeyArr.length; i < len; i++) { | ||||||
|  |         if (this.listObj[objKeyArr[i]].uid === uid) { | ||||||
|  |           delete this.listObj[objKeyArr[i]] | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     beforeUpload(file) { | ||||||
|  |       const _self = this | ||||||
|  |       const _URL = window.URL || window.webkitURL | ||||||
|  |       const fileName = file.uid | ||||||
|  |       this.listObj[fileName] = {} | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         const img = new Image() | ||||||
|  |         img.src = _URL.createObjectURL(file) | ||||||
|  |         img.onload = function() { | ||||||
|  |           _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height } | ||||||
|  |         } | ||||||
|  |         resolve(true) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .editor-slide-upload { | ||||||
|  |   margin-bottom: 20px; | ||||||
|  |   ::v-deep .el-upload--picture-card { | ||||||
|  |     width: 100%; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | let callbacks = [] | ||||||
|  | 
 | ||||||
|  | function loadedTinymce() { | ||||||
|  |   // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
 | ||||||
|  |   // check is successfully downloaded script
 | ||||||
|  |   return window.tinymce | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const dynamicLoadScript = (src, callback) => { | ||||||
|  |   const existingScript = document.getElementById(src) | ||||||
|  |   const cb = callback || function() {} | ||||||
|  | 
 | ||||||
|  |   if (!existingScript) { | ||||||
|  |     const script = document.createElement('script') | ||||||
|  |     script.src = src // src url for the third-party library being loaded.
 | ||||||
|  |     script.id = src | ||||||
|  |     document.body.appendChild(script) | ||||||
|  |     callbacks.push(cb) | ||||||
|  |     const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd | ||||||
|  |     onEnd(script) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (existingScript && cb) { | ||||||
|  |     if (loadedTinymce()) { | ||||||
|  |       cb(null, existingScript) | ||||||
|  |     } else { | ||||||
|  |       callbacks.push(cb) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function stdOnEnd(script) { | ||||||
|  |     script.onload = function() { | ||||||
|  |       // this.onload = null here is necessary
 | ||||||
|  |       // because even IE9 works not like others
 | ||||||
|  |       this.onerror = this.onload = null | ||||||
|  |       for (const cb of callbacks) { | ||||||
|  |         cb(null, script) | ||||||
|  |       } | ||||||
|  |       callbacks = null | ||||||
|  |     } | ||||||
|  |     script.onerror = function() { | ||||||
|  |       this.onerror = this.onload = null | ||||||
|  |       cb(new Error('Failed to load ' + src), script) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function ieOnEnd(script) { | ||||||
|  |     script.onreadystatechange = function() { | ||||||
|  |       if (this.readyState !== 'complete' && this.readyState !== 'loaded') return | ||||||
|  |       this.onreadystatechange = null | ||||||
|  |       for (const cb of callbacks) { | ||||||
|  |         cb(null, script) // there is no way to catch loading errors in IE8
 | ||||||
|  |       } | ||||||
|  |       callbacks = null | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default dynamicLoadScript | ||||||
|  | @ -0,0 +1,257 @@ | ||||||
|  | <template> | ||||||
|  |   <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}"> | ||||||
|  |     <textarea :id="tinymceId" class="tinymce-textarea" /> | ||||||
|  |     <div class="editor-custom-btn-container"> | ||||||
|  |       <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | /** | ||||||
|  |  * docs: | ||||||
|  |  * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce | ||||||
|  |  */ | ||||||
|  | import editorImage from './components/EditorImage' | ||||||
|  | import plugins from './plugins' | ||||||
|  | import toolbar from './toolbar' | ||||||
|  | import load from './dynamicLoadScript' | ||||||
|  | 
 | ||||||
|  | // why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one | ||||||
|  | const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'Tinymce', | ||||||
|  |   components: { editorImage }, | ||||||
|  |   inject: { | ||||||
|  |     elForm: { | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     id: { | ||||||
|  |       type: String, | ||||||
|  |       default: function() { | ||||||
|  |         return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     value: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     }, | ||||||
|  |     toolbar: { | ||||||
|  |       type: Array, | ||||||
|  |       required: false, | ||||||
|  |       default() { | ||||||
|  |         return [] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     menubar: { | ||||||
|  |       type: String, | ||||||
|  |       default: 'file edit insert view format table' | ||||||
|  |     }, | ||||||
|  |     height: { | ||||||
|  |       type: [Number, String], | ||||||
|  |       required: false, | ||||||
|  |       default: 360 | ||||||
|  |     }, | ||||||
|  |     width: { | ||||||
|  |       type: [Number, String], | ||||||
|  |       required: false, | ||||||
|  |       default: 'auto' | ||||||
|  |     }, | ||||||
|  |     disabled: Boolean | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       hasChange: false, | ||||||
|  |       hasInit: false, | ||||||
|  |       tinymceId: this.id, | ||||||
|  |       fullscreen: false, | ||||||
|  |       languageTypeList: { | ||||||
|  |         'en': 'en', | ||||||
|  |         'zh': 'zh_CN', | ||||||
|  |         'es': 'es_MX', | ||||||
|  |         'ja': 'ja' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     inputDisabled() { | ||||||
|  |       return this.disabled || (this.elForm || {}).disabled | ||||||
|  |     }, | ||||||
|  |     containerWidth() { | ||||||
|  |       const width = this.width | ||||||
|  |       if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` | ||||||
|  |         return `${width}px` | ||||||
|  |       } | ||||||
|  |       return width | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value(val) { | ||||||
|  |       if (!this.hasChange && this.hasInit) { | ||||||
|  |         this.$nextTick(() => | ||||||
|  |           window.tinymce.get(this.tinymceId).setContent(val || '')) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted() { | ||||||
|  |     this.init() | ||||||
|  |   }, | ||||||
|  |   activated() { | ||||||
|  |     if (window.tinymce) { | ||||||
|  |       this.initTinymce() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   deactivated() { | ||||||
|  |     this.destroyTinymce() | ||||||
|  |   }, | ||||||
|  |   destroyed() { | ||||||
|  |     this.destroyTinymce() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     init() { | ||||||
|  |       // dynamic load tinymce from cdn | ||||||
|  |       load(tinymceCDN, (err) => { | ||||||
|  |         if (err) { | ||||||
|  |           this.$message.error(err.message) | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  |         this.initTinymce() | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     initTinymce() { | ||||||
|  |       const _this = this | ||||||
|  |       window.tinymce.init({ | ||||||
|  |         selector: `#${this.tinymceId}`, | ||||||
|  |         language: this.languageTypeList['zh'], | ||||||
|  |         height: this.height, | ||||||
|  |         body_class: 'panel-body ', | ||||||
|  |         object_resizing: false, | ||||||
|  |         toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, | ||||||
|  |         menubar: this.menubar, | ||||||
|  |         plugins: plugins, | ||||||
|  |         end_container_on_empty_block: true, | ||||||
|  |         powerpaste_word_import: 'clean', | ||||||
|  |         code_dialog_height: 450, | ||||||
|  |         code_dialog_width: 1000, | ||||||
|  |         advlist_bullet_styles: 'square', | ||||||
|  |         advlist_number_styles: 'default', | ||||||
|  |         imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], | ||||||
|  |         default_link_target: '_blank', | ||||||
|  |         link_title: false, | ||||||
|  |         nonbreaking_force_tab: true, // inserting nonbreaking space   need Nonbreaking Space Plugin | ||||||
|  |         init_instance_callback: editor => { | ||||||
|  |           if (_this.value) { | ||||||
|  |             editor.setContent(_this.value) | ||||||
|  |           } | ||||||
|  |           _this.hasInit = true | ||||||
|  |           editor.on('NodeChange Change KeyUp SetContent', () => { | ||||||
|  |             this.hasChange = true | ||||||
|  |             this.$emit('input', editor.getContent()) | ||||||
|  |           }) | ||||||
|  |         }, | ||||||
|  |         setup(editor) { | ||||||
|  |           editor.on('FullscreenStateChanged', (e) => { | ||||||
|  |             _this.fullscreen = e.state | ||||||
|  |           }) | ||||||
|  |         }, | ||||||
|  |         readonly: this.inputDisabled, | ||||||
|  |         // it will try to keep these URLs intact | ||||||
|  |         // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/ | ||||||
|  |         // https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions | ||||||
|  |         convert_urls: false | ||||||
|  |         // 整合七牛上传 | ||||||
|  |         // images_dataimg_filter(img) { | ||||||
|  |         //   setTimeout(() => { | ||||||
|  |         //     const $image = $(img); | ||||||
|  |         //     $image.removeAttr('width'); | ||||||
|  |         //     $image.removeAttr('height'); | ||||||
|  |         //     if ($image[0].height && $image[0].width) { | ||||||
|  |         //       $image.attr('data-wscntype', 'image'); | ||||||
|  |         //       $image.attr('data-wscnh', $image[0].height); | ||||||
|  |         //       $image.attr('data-wscnw', $image[0].width); | ||||||
|  |         //       $image.addClass('wscnph'); | ||||||
|  |         //     } | ||||||
|  |         //   }, 0); | ||||||
|  |         //   return img | ||||||
|  |         // }, | ||||||
|  |         // images_upload_handler(blobInfo, success, failure, progress) { | ||||||
|  |         //   progress(0); | ||||||
|  |         //   const token = _this.$store.getters.token; | ||||||
|  |         //   getToken(token).then(response => { | ||||||
|  |         //     const url = response.data.qiniu_url; | ||||||
|  |         //     const formData = new FormData(); | ||||||
|  |         //     formData.append('token', response.data.qiniu_token); | ||||||
|  |         //     formData.append('key', response.data.qiniu_key); | ||||||
|  |         //     formData.append('file', blobInfo.blob(), url); | ||||||
|  |         //     upload(formData).then(() => { | ||||||
|  |         //       success(url); | ||||||
|  |         //       progress(100); | ||||||
|  |         //     }) | ||||||
|  |         //   }).catch(err => { | ||||||
|  |         //     failure('出现未知问题,刷新页面,或者联系程序员') | ||||||
|  |         //     console.log(err); | ||||||
|  |         //   }); | ||||||
|  |         // }, | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     destroyTinymce() { | ||||||
|  |       const tinymce = window.tinymce.get(this.tinymceId) | ||||||
|  |       if (this.fullscreen) { | ||||||
|  |         tinymce.execCommand('mceFullScreen') | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (tinymce) { | ||||||
|  |         tinymce.destroy() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     setContent(value) { | ||||||
|  |       window.tinymce.get(this.tinymceId).setContent(value) | ||||||
|  |     }, | ||||||
|  |     getContent() { | ||||||
|  |       window.tinymce.get(this.tinymceId).getContent() | ||||||
|  |     }, | ||||||
|  |     imageSuccessCBK(arr) { | ||||||
|  |       arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v}" >`)) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .tinymce-container { | ||||||
|  |   position: relative; | ||||||
|  |   line-height: normal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .tinymce-container { | ||||||
|  |   ::v-deep { | ||||||
|  |     .mce-fullscreen { | ||||||
|  |       z-index: 10000; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .tinymce-textarea { | ||||||
|  |   visibility: hidden; | ||||||
|  |   z-index: -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-custom-btn-container { | ||||||
|  |   position: absolute; | ||||||
|  |   right: 4px; | ||||||
|  |   top: 4px; | ||||||
|  |   /*z-index: 2005;*/ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .fullscreen .editor-custom-btn-container { | ||||||
|  |   z-index: 10000; | ||||||
|  |   position: fixed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .editor-upload-btn { | ||||||
|  |   display: inline-block; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | // Any plugins you want to use has to be imported
 | ||||||
|  | // Detail plugins list see https://www.tinymce.com/docs/plugins/
 | ||||||
|  | // Custom builds see https://www.tinymce.com/download/custom-builds/
 | ||||||
|  | 
 | ||||||
|  | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] | ||||||
|  | 
 | ||||||
|  | export default plugins | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | // Here is a list of the toolbar
 | ||||||
|  | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
 | ||||||
|  | 
 | ||||||
|  | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] | ||||||
|  | 
 | ||||||
|  | export default toolbar | ||||||
|  | @ -0,0 +1,134 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="upload-container"> | ||||||
|  |     <el-upload | ||||||
|  |       :data="dataObj" | ||||||
|  |       :multiple="false" | ||||||
|  |       :show-file-list="false" | ||||||
|  |       :on-success="handleImageSuccess" | ||||||
|  |       class="image-uploader" | ||||||
|  |       drag | ||||||
|  |       action="https://httpbin.org/post" | ||||||
|  |     > | ||||||
|  |       <i class="el-icon-upload" /> | ||||||
|  |       <div class="el-upload__text"> | ||||||
|  |         将文件拖到此处,或<em>点击上传</em> | ||||||
|  |       </div> | ||||||
|  |     </el-upload> | ||||||
|  |     <div class="image-preview"> | ||||||
|  |       <div v-show="imageUrl.length>1" class="image-preview-wrapper"> | ||||||
|  |         <img :src="imageUrl+'?imageView2/1/w/200/h/200'"> | ||||||
|  |         <div class="image-preview-action"> | ||||||
|  |           <i class="el-icon-delete" @click="rmImage" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { getToken } from '@/api/qiniu' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'SingleImageUpload', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       tempUrl: '', | ||||||
|  |       dataObj: { token: '', key: '' } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     imageUrl() { | ||||||
|  |       return this.value | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     rmImage() { | ||||||
|  |       this.emitInput('') | ||||||
|  |     }, | ||||||
|  |     emitInput(val) { | ||||||
|  |       this.$emit('input', val) | ||||||
|  |     }, | ||||||
|  |     handleImageSuccess() { | ||||||
|  |       this.emitInput(this.tempUrl) | ||||||
|  |     }, | ||||||
|  |     beforeUpload() { | ||||||
|  |       const _self = this | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         getToken().then(response => { | ||||||
|  |           const key = response.data.qiniu_key | ||||||
|  |           const token = response.data.qiniu_token | ||||||
|  |           _self._data.dataObj.token = token | ||||||
|  |           _self._data.dataObj.key = key | ||||||
|  |           this.tempUrl = response.data.qiniu_url | ||||||
|  |           resolve(true) | ||||||
|  |         }).catch(err => { | ||||||
|  |           console.log(err) | ||||||
|  |           reject(false) | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  |     @import "~@/styles/mixin.scss"; | ||||||
|  |     .upload-container { | ||||||
|  |         width: 100%; | ||||||
|  |         position: relative; | ||||||
|  |         @include clearfix; | ||||||
|  |         .image-uploader { | ||||||
|  |             width: 60%; | ||||||
|  |             float: left; | ||||||
|  |         } | ||||||
|  |         .image-preview { | ||||||
|  |             width: 200px; | ||||||
|  |             height: 200px; | ||||||
|  |             position: relative; | ||||||
|  |             border: 1px dashed #d9d9d9; | ||||||
|  |             float: left; | ||||||
|  |             margin-left: 50px; | ||||||
|  |             .image-preview-wrapper { | ||||||
|  |                 position: relative; | ||||||
|  |                 width: 100%; | ||||||
|  |                 height: 100%; | ||||||
|  |                 img { | ||||||
|  |                     width: 100%; | ||||||
|  |                     height: 100%; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .image-preview-action { | ||||||
|  |                 position: absolute; | ||||||
|  |                 width: 100%; | ||||||
|  |                 height: 100%; | ||||||
|  |                 left: 0; | ||||||
|  |                 top: 0; | ||||||
|  |                 cursor: default; | ||||||
|  |                 text-align: center; | ||||||
|  |                 color: #fff; | ||||||
|  |                 opacity: 0; | ||||||
|  |                 font-size: 20px; | ||||||
|  |                 background-color: rgba(0, 0, 0, .5); | ||||||
|  |                 transition: opacity .3s; | ||||||
|  |                 cursor: pointer; | ||||||
|  |                 text-align: center; | ||||||
|  |                 line-height: 200px; | ||||||
|  |                 .el-icon-delete { | ||||||
|  |                     font-size: 36px; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             &:hover { | ||||||
|  |                 .image-preview-action { | ||||||
|  |                     opacity: 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,130 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="singleImageUpload2 upload-container"> | ||||||
|  |     <el-upload | ||||||
|  |       :data="dataObj" | ||||||
|  |       :multiple="false" | ||||||
|  |       :show-file-list="false" | ||||||
|  |       :on-success="handleImageSuccess" | ||||||
|  |       class="image-uploader" | ||||||
|  |       drag | ||||||
|  |       action="https://httpbin.org/post" | ||||||
|  |     > | ||||||
|  |       <i class="el-icon-upload" /> | ||||||
|  |       <div class="el-upload__text"> | ||||||
|  |         Drag或<em>点击上传</em> | ||||||
|  |       </div> | ||||||
|  |     </el-upload> | ||||||
|  |     <div v-show="imageUrl.length>0" class="image-preview"> | ||||||
|  |       <div v-show="imageUrl.length>1" class="image-preview-wrapper"> | ||||||
|  |         <img :src="imageUrl"> | ||||||
|  |         <div class="image-preview-action"> | ||||||
|  |           <i class="el-icon-delete" @click="rmImage" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { getToken } from '@/api/qiniu' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'SingleImageUpload2', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       tempUrl: '', | ||||||
|  |       dataObj: { token: '', key: '' } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     imageUrl() { | ||||||
|  |       return this.value | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     rmImage() { | ||||||
|  |       this.emitInput('') | ||||||
|  |     }, | ||||||
|  |     emitInput(val) { | ||||||
|  |       this.$emit('input', val) | ||||||
|  |     }, | ||||||
|  |     handleImageSuccess() { | ||||||
|  |       this.emitInput(this.tempUrl) | ||||||
|  |     }, | ||||||
|  |     beforeUpload() { | ||||||
|  |       const _self = this | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         getToken().then(response => { | ||||||
|  |           const key = response.data.qiniu_key | ||||||
|  |           const token = response.data.qiniu_token | ||||||
|  |           _self._data.dataObj.token = token | ||||||
|  |           _self._data.dataObj.key = key | ||||||
|  |           this.tempUrl = response.data.qiniu_url | ||||||
|  |           resolve(true) | ||||||
|  |         }).catch(() => { | ||||||
|  |           reject(false) | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .upload-container { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   position: relative; | ||||||
|  |   .image-uploader { | ||||||
|  |     height: 100%; | ||||||
|  |   } | ||||||
|  |   .image-preview { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |     position: absolute; | ||||||
|  |     left: 0px; | ||||||
|  |     top: 0px; | ||||||
|  |     border: 1px dashed #d9d9d9; | ||||||
|  |     .image-preview-wrapper { | ||||||
|  |       position: relative; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       img { | ||||||
|  |         width: 100%; | ||||||
|  |         height: 100%; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .image-preview-action { | ||||||
|  |       position: absolute; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       left: 0; | ||||||
|  |       top: 0; | ||||||
|  |       cursor: default; | ||||||
|  |       text-align: center; | ||||||
|  |       color: #fff; | ||||||
|  |       opacity: 0; | ||||||
|  |       font-size: 20px; | ||||||
|  |       background-color: rgba(0, 0, 0, .5); | ||||||
|  |       transition: opacity .3s; | ||||||
|  |       cursor: pointer; | ||||||
|  |       text-align: center; | ||||||
|  |       line-height: 200px; | ||||||
|  |       .el-icon-delete { | ||||||
|  |         font-size: 36px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     &:hover { | ||||||
|  |       .image-preview-action { | ||||||
|  |         opacity: 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,157 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="upload-container"> | ||||||
|  |     <el-upload | ||||||
|  |       :data="dataObj" | ||||||
|  |       :multiple="false" | ||||||
|  |       :show-file-list="false" | ||||||
|  |       :on-success="handleImageSuccess" | ||||||
|  |       class="image-uploader" | ||||||
|  |       drag | ||||||
|  |       action="https://httpbin.org/post" | ||||||
|  |     > | ||||||
|  |       <i class="el-icon-upload" /> | ||||||
|  |       <div class="el-upload__text"> | ||||||
|  |         将文件拖到此处,或<em>点击上传</em> | ||||||
|  |       </div> | ||||||
|  |     </el-upload> | ||||||
|  |     <div class="image-preview image-app-preview"> | ||||||
|  |       <div v-show="imageUrl.length>1" class="image-preview-wrapper"> | ||||||
|  |         <img :src="imageUrl"> | ||||||
|  |         <div class="image-preview-action"> | ||||||
|  |           <i class="el-icon-delete" @click="rmImage" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="image-preview"> | ||||||
|  |       <div v-show="imageUrl.length>1" class="image-preview-wrapper"> | ||||||
|  |         <img :src="imageUrl"> | ||||||
|  |         <div class="image-preview-action"> | ||||||
|  |           <i class="el-icon-delete" @click="rmImage" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import { getToken } from '@/api/qiniu' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'SingleImageUpload3', | ||||||
|  |   props: { | ||||||
|  |     value: { | ||||||
|  |       type: String, | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       tempUrl: '', | ||||||
|  |       dataObj: { token: '', key: '' } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     imageUrl() { | ||||||
|  |       return this.value | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     rmImage() { | ||||||
|  |       this.emitInput('') | ||||||
|  |     }, | ||||||
|  |     emitInput(val) { | ||||||
|  |       this.$emit('input', val) | ||||||
|  |     }, | ||||||
|  |     handleImageSuccess(file) { | ||||||
|  |       this.emitInput(file.files.file) | ||||||
|  |     }, | ||||||
|  |     beforeUpload() { | ||||||
|  |       const _self = this | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         getToken().then(response => { | ||||||
|  |           const key = response.data.qiniu_key | ||||||
|  |           const token = response.data.qiniu_token | ||||||
|  |           _self._data.dataObj.token = token | ||||||
|  |           _self._data.dataObj.key = key | ||||||
|  |           this.tempUrl = response.data.qiniu_url | ||||||
|  |           resolve(true) | ||||||
|  |         }).catch(err => { | ||||||
|  |           console.log(err) | ||||||
|  |           reject(false) | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | @import "~@/styles/mixin.scss"; | ||||||
|  | .upload-container { | ||||||
|  |   width: 100%; | ||||||
|  |   position: relative; | ||||||
|  |   @include clearfix; | ||||||
|  |   .image-uploader { | ||||||
|  |     width: 35%; | ||||||
|  |     float: left; | ||||||
|  |   } | ||||||
|  |   .image-preview { | ||||||
|  |     width: 200px; | ||||||
|  |     height: 200px; | ||||||
|  |     position: relative; | ||||||
|  |     border: 1px dashed #d9d9d9; | ||||||
|  |     float: left; | ||||||
|  |     margin-left: 50px; | ||||||
|  |     .image-preview-wrapper { | ||||||
|  |       position: relative; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       img { | ||||||
|  |         width: 100%; | ||||||
|  |         height: 100%; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     .image-preview-action { | ||||||
|  |       position: absolute; | ||||||
|  |       width: 100%; | ||||||
|  |       height: 100%; | ||||||
|  |       left: 0; | ||||||
|  |       top: 0; | ||||||
|  |       cursor: default; | ||||||
|  |       text-align: center; | ||||||
|  |       color: #fff; | ||||||
|  |       opacity: 0; | ||||||
|  |       font-size: 20px; | ||||||
|  |       background-color: rgba(0, 0, 0, .5); | ||||||
|  |       transition: opacity .3s; | ||||||
|  |       cursor: pointer; | ||||||
|  |       text-align: center; | ||||||
|  |       line-height: 200px; | ||||||
|  |       .el-icon-delete { | ||||||
|  |         font-size: 36px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     &:hover { | ||||||
|  |       .image-preview-action { | ||||||
|  |         opacity: 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .image-app-preview { | ||||||
|  |     width: 320px; | ||||||
|  |     height: 180px; | ||||||
|  |     position: relative; | ||||||
|  |     border: 1px dashed #d9d9d9; | ||||||
|  |     float: left; | ||||||
|  |     margin-left: 50px; | ||||||
|  |     .app-fake-conver { | ||||||
|  |       height: 44px; | ||||||
|  |       position: absolute; | ||||||
|  |       width: 100%; // background: rgba(0, 0, 0, .1); | ||||||
|  |       text-align: center; | ||||||
|  |       line-height: 64px; | ||||||
|  |       color: #fff; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,138 @@ | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick"> | ||||||
|  |     <div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover"> | ||||||
|  |       Drop excel file here or | ||||||
|  |       <el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload"> | ||||||
|  |         Browse | ||||||
|  |       </el-button> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  | import XLSX from 'xlsx' | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   props: { | ||||||
|  |     beforeUpload: Function, // eslint-disable-line | ||||||
|  |     onSuccess: Function// eslint-disable-line | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       loading: false, | ||||||
|  |       excelData: { | ||||||
|  |         header: null, | ||||||
|  |         results: null | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     generateData({ header, results }) { | ||||||
|  |       this.excelData.header = header | ||||||
|  |       this.excelData.results = results | ||||||
|  |       this.onSuccess && this.onSuccess(this.excelData) | ||||||
|  |     }, | ||||||
|  |     handleDrop(e) { | ||||||
|  |       e.stopPropagation() | ||||||
|  |       e.preventDefault() | ||||||
|  |       if (this.loading) return | ||||||
|  |       const files = e.dataTransfer.files | ||||||
|  |       if (files.length !== 1) { | ||||||
|  |         this.$message.error('Only support uploading one file!') | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       const rawFile = files[0] // only use files[0] | ||||||
|  | 
 | ||||||
|  |       if (!this.isExcel(rawFile)) { | ||||||
|  |         this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files') | ||||||
|  |         return false | ||||||
|  |       } | ||||||
|  |       this.upload(rawFile) | ||||||
|  |       e.stopPropagation() | ||||||
|  |       e.preventDefault() | ||||||
|  |     }, | ||||||
|  |     handleDragover(e) { | ||||||
|  |       e.stopPropagation() | ||||||
|  |       e.preventDefault() | ||||||
|  |       e.dataTransfer.dropEffect = 'copy' | ||||||
|  |     }, | ||||||
|  |     handleUpload() { | ||||||
|  |       this.$refs['excel-upload-input'].click() | ||||||
|  |     }, | ||||||
|  |     handleClick(e) { | ||||||
|  |       const files = e.target.files | ||||||
|  |       const rawFile = files[0] // only use files[0] | ||||||
|  |       if (!rawFile) return | ||||||
|  |       this.upload(rawFile) | ||||||
|  |     }, | ||||||
|  |     upload(rawFile) { | ||||||
|  |       this.$refs['excel-upload-input'].value = null // fix can't select the same excel | ||||||
|  | 
 | ||||||
|  |       if (!this.beforeUpload) { | ||||||
|  |         this.readerData(rawFile) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  |       const before = this.beforeUpload(rawFile) | ||||||
|  |       if (before) { | ||||||
|  |         this.readerData(rawFile) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     readerData(rawFile) { | ||||||
|  |       this.loading = true | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         const reader = new FileReader() | ||||||
|  |         reader.onload = e => { | ||||||
|  |           const data = e.target.result | ||||||
|  |           const workbook = XLSX.read(data, { type: 'array' }) | ||||||
|  |           const firstSheetName = workbook.SheetNames[0] | ||||||
|  |           const worksheet = workbook.Sheets[firstSheetName] | ||||||
|  |           const header = this.getHeaderRow(worksheet) | ||||||
|  |           const results = XLSX.utils.sheet_to_json(worksheet) | ||||||
|  |           this.generateData({ header, results }) | ||||||
|  |           this.loading = false | ||||||
|  |           resolve() | ||||||
|  |         } | ||||||
|  |         reader.readAsArrayBuffer(rawFile) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     getHeaderRow(sheet) { | ||||||
|  |       const headers = [] | ||||||
|  |       const range = XLSX.utils.decode_range(sheet['!ref']) | ||||||
|  |       let C | ||||||
|  |       const R = range.s.r | ||||||
|  |       /* start in the first row */ | ||||||
|  |       for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */ | ||||||
|  |         const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })] | ||||||
|  |         /* find the cell in the first row */ | ||||||
|  |         let hdr = 'UNKNOWN ' + C // <-- replace with your desired default | ||||||
|  |         if (cell && cell.t) hdr = XLSX.utils.format_cell(cell) | ||||||
|  |         headers.push(hdr) | ||||||
|  |       } | ||||||
|  |       return headers | ||||||
|  |     }, | ||||||
|  |     isExcel(file) { | ||||||
|  |       return /\.(xlsx|xls|csv)$/.test(file.name) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | .excel-upload-input{ | ||||||
|  |   display: none; | ||||||
|  |   z-index: -9999; | ||||||
|  | } | ||||||
|  | .drop{ | ||||||
|  |   border: 2px dashed #bbb; | ||||||
|  |   width: 600px; | ||||||
|  |   height: 160px; | ||||||
|  |   line-height: 160px; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   font-size: 24px; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   text-align: center; | ||||||
|  |   color: #bbb; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,172 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="my-upload-file-cmp"> | ||||||
|  |     <!-- :before-upload="beforeUpload" --> | ||||||
|  |     <el-upload | ||||||
|  |       :action="uploadUrl" | ||||||
|  |       :data="dataObj" | ||||||
|  |       :headers="{ Authorization }" | ||||||
|  |       :limit="limit" | ||||||
|  |       :file-list="fileList" | ||||||
|  |       :on-preview="handlePictureCardPreview" | ||||||
|  |       :on-remove="handleRemove" | ||||||
|  |       :on-change="onChange" | ||||||
|  |       :on-success="onSuccess" | ||||||
|  |       :on-exceed="onExceed" | ||||||
|  |       multiple | ||||||
|  |     > | ||||||
|  |       <!-- :class="{disUoloadSty: noneBtnImg}" --> | ||||||
|  |       <!-- list-type="picture-card" | ||||||
|  |       accept="image/jpeg,image/gif,image/png,image/bmp" --> | ||||||
|  |       <!-- <slot> | ||||||
|  |         <i slot="default" class="el-icon-plus" /> | ||||||
|  |       </slot> --> | ||||||
|  |       <el-button size="small" type="primary">点击上传</el-button> | ||||||
|  |     </el-upload> | ||||||
|  |     <!-- <el-dialog :visible.sync="dialogVisible" append-to-body> | ||||||
|  |       <img width="100%" :src="dialogImageUrl" alt=""> | ||||||
|  |     </el-dialog> --> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | import { getToken } from '@/utils/auth' | ||||||
|  | import { getFileHash, fileNameFilter } from '@/utils' | ||||||
|  | const baseApi = process.env.VUE_APP_BASE_API | ||||||
|  | export default { | ||||||
|  |   name: 'UploadFile', | ||||||
|  |   props: { | ||||||
|  |     limit: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1 | ||||||
|  |     }, | ||||||
|  |     value: { | ||||||
|  |       type: [String, Array], | ||||||
|  |       default: '' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       fileList: [], | ||||||
|  |       noneBtnImg: false, | ||||||
|  |       dialogImageUrl: '', | ||||||
|  |       dialogVisible: false, | ||||||
|  |       disabled: false, | ||||||
|  | 
 | ||||||
|  |       dataObj: { | ||||||
|  |         // token: '', | ||||||
|  |         // key: null | ||||||
|  |         // key: '123.png' | ||||||
|  |         uploadType: 'PC' | ||||||
|  |       }, | ||||||
|  |       uploadUrl: baseApi + '/commonLogin/upload', | ||||||
|  |       Authorization: getToken() | ||||||
|  |       // uploadUrl: 'http://upload.qiniup.com/' | ||||||
|  |       // uploadUrl: 'http://up-z2.qiniup.com' | ||||||
|  |       // uploadUrl: 'http://storage.cdlangli.com' | ||||||
|  |       // uploadUrl: 'http://qiniu.region.z2' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value() { | ||||||
|  |       this.initData() | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.initData() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initData() { | ||||||
|  |       const { value } = this | ||||||
|  |       if (!value) { | ||||||
|  |         this.fileList = [] | ||||||
|  |       } else if (typeof value === 'string') { | ||||||
|  |         this.fileList = value.split(',').map(item => ({ | ||||||
|  |           status: 'success', | ||||||
|  |           name: fileNameFilter(item), | ||||||
|  |           url: item | ||||||
|  |         })) | ||||||
|  |       } else { | ||||||
|  |         this.fileList = value.map(item => ({ | ||||||
|  |           status: 'success', | ||||||
|  |           name: fileNameFilter(item), | ||||||
|  |           url: item | ||||||
|  |         })) | ||||||
|  |       } | ||||||
|  |       this.noneBtnImg = this.fileList.length >= this.limit | ||||||
|  |     }, | ||||||
|  |     beforeUpload(file) { | ||||||
|  |       // console.log(file) | ||||||
|  |       const { name } = file | ||||||
|  |       const extension = name.split('.')[1] | ||||||
|  |       getFileHash(file) | ||||||
|  |       const _self = this | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         getToken().then(response => { | ||||||
|  |           getFileHash(file).then(res => { | ||||||
|  |             const key = `${res}.${extension}` | ||||||
|  |             const token = response.data | ||||||
|  |             _self._data.dataObj.token = token | ||||||
|  |             _self._data.dataObj.key = key | ||||||
|  |             resolve(true) | ||||||
|  |           }).catch(err => { | ||||||
|  |             console.log(err) | ||||||
|  |             reject(false) | ||||||
|  |           }) | ||||||
|  |         }).catch(err => { | ||||||
|  |           console.log(err) | ||||||
|  |           reject(false) | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     }, | ||||||
|  |     handleRemove(file, fileList) { | ||||||
|  |       this.noneBtnImg = fileList.length >= this.limit | ||||||
|  |       // console.log(fileList) | ||||||
|  |       this.submitData(fileList) | ||||||
|  |     }, | ||||||
|  |     handlePictureCardPreview(file) { | ||||||
|  |       this.dialogImageUrl = file.url | ||||||
|  |       this.dialogVisible = true | ||||||
|  |     }, | ||||||
|  |     handleDownload(file) { | ||||||
|  |       console.log(file) | ||||||
|  |     }, | ||||||
|  |     onChange(file, fileList) { | ||||||
|  |       // console.log(fileList) | ||||||
|  |       this.noneBtnImg = fileList.length >= this.limit | ||||||
|  |       // this.submitData(fileList) | ||||||
|  |     }, | ||||||
|  |     onSuccess(response, file, fileList) { | ||||||
|  |       this.submitData(fileList) | ||||||
|  |     }, | ||||||
|  |     onProgress(event, file, fileList) { | ||||||
|  |       // console.log(event, file, fileList) | ||||||
|  |     }, | ||||||
|  |     onExceed(files, fileList) { | ||||||
|  |       this.$message.warning(`当前限制选择 ${this.limit} 个文件`) | ||||||
|  |     }, | ||||||
|  |     submitData(fileList) { | ||||||
|  |       const urls = fileList.map((item) => { | ||||||
|  |         if (item.response) { | ||||||
|  |           return item.response.d | ||||||
|  |         } else { | ||||||
|  |           return item.url | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |       if (typeof this.value === 'string') { | ||||||
|  |         this.$emit('input', urls.join(',')) | ||||||
|  |       } else { | ||||||
|  |         this.$emit('input', urls) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss"> | ||||||
|  | .my-upload-file-cmp{ | ||||||
|  |   .disUoloadSty{ | ||||||
|  |     .el-upload--picture-card{ | ||||||
|  |       display:none;   /* 上传按钮隐藏 */ | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,209 @@ | ||||||
|  | <template> | ||||||
|  |   <div class="my-upload-image-cmp"> | ||||||
|  |     <!-- :before-upload="beforeUpload" --> | ||||||
|  |     <el-upload | ||||||
|  |       :action="uploadUrl" | ||||||
|  |       :data="dataObj" | ||||||
|  |       :headers="{ Authorization }" | ||||||
|  |       :limit="limit" | ||||||
|  |       :file-list="fileList" | ||||||
|  |       :on-preview="handlePictureCardPreview" | ||||||
|  |       :on-remove="handleRemove" | ||||||
|  |       :on-change="onChange" | ||||||
|  |       :on-success="onSuccess" | ||||||
|  |       :before-upload="beforeUpload" | ||||||
|  |       :class="{ disUoloadSty: noneBtnImg }" | ||||||
|  |       list-type="picture-card" | ||||||
|  |       accept="image/jpeg,image/gif,image/png,image/bmp" | ||||||
|  |     > | ||||||
|  |       <!-- :on-progress="onProgress" | ||||||
|  |       :on-change="onChange" --> | ||||||
|  |       <!-- :auto-upload="false" --> | ||||||
|  |       <slot> | ||||||
|  |         <i slot="default" class="el-icon-plus" /> | ||||||
|  |       </slot> | ||||||
|  |       <!-- <div slot="file" slot-scope="{file}"> | ||||||
|  |         <img | ||||||
|  |           class="el-upload-list__item-thumbnail" | ||||||
|  |           :src="file.url" | ||||||
|  |           alt="" | ||||||
|  |         > | ||||||
|  |         <span class="el-upload-list__item-actions"> | ||||||
|  |           <span | ||||||
|  |             class="el-upload-list__item-preview" | ||||||
|  |             @click="handlePictureCardPreview(file)" | ||||||
|  |           > | ||||||
|  |             <i class="el-icon-zoom-in" /> | ||||||
|  |           </span> | ||||||
|  |           <span | ||||||
|  |             v-if="!disabled" | ||||||
|  |             class="el-upload-list__item-delete" | ||||||
|  |             @click="handleRemove(file)" | ||||||
|  |           > | ||||||
|  |             <i class="el-icon-delete" /> | ||||||
|  |           </span> | ||||||
|  |         </span> | ||||||
|  |       </div> --> | ||||||
|  |     </el-upload> | ||||||
|  |     <el-dialog :visible.sync="dialogVisible" append-to-body> | ||||||
|  |       <img width="100%" :src="dialogImageUrl" alt="" /> | ||||||
|  |     </el-dialog> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <script> | ||||||
|  | // import { getToken } from '@/api/qiniu' | ||||||
|  | import { getFileHash } from "@/utils"; | ||||||
|  | import { getToken } from "@/utils/auth"; | ||||||
|  | import { upYunUpLoad } from "@/utils/upyun"; | ||||||
|  | const baseApi = process.env.VUE_APP_BASE_API; | ||||||
|  | export default { | ||||||
|  |   name: "UploadImage", | ||||||
|  |   props: { | ||||||
|  |     limit: { | ||||||
|  |       type: Number, | ||||||
|  |       default: 1, | ||||||
|  |     }, | ||||||
|  |     value: { | ||||||
|  |       type: [String, Array], | ||||||
|  |       default: "", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       fileList: [], | ||||||
|  |       noneBtnImg: false, | ||||||
|  |       dialogImageUrl: "", | ||||||
|  |       dialogVisible: false, | ||||||
|  |       disabled: false, | ||||||
|  | 
 | ||||||
|  |       dataObj: { | ||||||
|  |         policy: undefined, | ||||||
|  |         signature: undefined, | ||||||
|  |         file: undefined, | ||||||
|  |         // token: '', | ||||||
|  |         // key: null, | ||||||
|  |         // uploadType: 'PC' | ||||||
|  |         // key: '123.png' | ||||||
|  |       }, | ||||||
|  |       // uploadUrl: 'http://upload.qiniup.com/' | ||||||
|  |       // uploadUrl: 'http://up-z2.qiniup.com' | ||||||
|  |       // uploadUrl: 'http://storage.cdlangli.com' | ||||||
|  |       // uploadUrl: 'http://qiniu.region.z2' | ||||||
|  |       // uploadUrl: baseApi + '/commonLogin/upload1', | ||||||
|  |       uploadUrl: "http://v0.api.upyun.com/many-goods-image", | ||||||
|  |       Authorization: getToken(), | ||||||
|  |     }; | ||||||
|  |   }, | ||||||
|  |   watch: { | ||||||
|  |     value() { | ||||||
|  |       this.initData(); | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.initData(); | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     initData() { | ||||||
|  |       const { value } = this; | ||||||
|  |       if (!value) { | ||||||
|  |         this.fileList = []; | ||||||
|  |       } else if (typeof value === "string") { | ||||||
|  |         this.fileList = value.split(",").map((item) => ({ | ||||||
|  |           status: "success", | ||||||
|  |           url: item, | ||||||
|  |         })); | ||||||
|  |       } else { | ||||||
|  |         this.fileList = value.map((item) => ({ | ||||||
|  |           status: "success", | ||||||
|  |           url: item, | ||||||
|  |         })); | ||||||
|  |       } | ||||||
|  |       this.noneBtnImg = this.fileList.length >= this.limit; | ||||||
|  |     }, | ||||||
|  |     beforeUpload(file) { | ||||||
|  |       // console.log(file) | ||||||
|  |       const { name } = file; | ||||||
|  |       const extension = name.split(".")[1]; | ||||||
|  |       getFileHash(file); | ||||||
|  |       const _self = this; | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         getToken() | ||||||
|  |           .then((response) => { | ||||||
|  |             getFileHash(file) | ||||||
|  |               .then((res) => { | ||||||
|  |                 const key = `${res}.${extension}`; | ||||||
|  |                 const token = response.data; | ||||||
|  |                 _self._data.dataObj.token = token; | ||||||
|  |                 _self._data.dataObj.key = key; | ||||||
|  |                 resolve(true); | ||||||
|  |               }) | ||||||
|  |               .catch((err) => { | ||||||
|  |                 console.log(err); | ||||||
|  |                 reject(false); | ||||||
|  |               }); | ||||||
|  |           }) | ||||||
|  |           .catch((err) => { | ||||||
|  |             console.log(err); | ||||||
|  |             reject(false); | ||||||
|  |           }); | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     beforeUpload(str, file) { | ||||||
|  |       const auth = upYunUpLoad(file); | ||||||
|  |       console.log(auth); | ||||||
|  |       this.dataObj.policy = auth.policy; | ||||||
|  |       this.dataObj.signature = auth.signature; | ||||||
|  |       this.dataObj.file = file; | ||||||
|  |     }, | ||||||
|  |     handleRemove(file, fileList) { | ||||||
|  |       this.noneBtnImg = fileList.length >= this.limit; | ||||||
|  |       // console.log(fileList) | ||||||
|  |       this.submitData(fileList); | ||||||
|  |     }, | ||||||
|  |     handlePictureCardPreview(file) { | ||||||
|  |       this.dialogImageUrl = file.url; | ||||||
|  |       this.dialogVisible = true; | ||||||
|  |     }, | ||||||
|  |     handleDownload(file) { | ||||||
|  |       console.log(file); | ||||||
|  |     }, | ||||||
|  |     onChange(file, fileList) { | ||||||
|  |       // console.log(fileList) | ||||||
|  |       this.noneBtnImg = fileList.length >= this.limit; | ||||||
|  |       // this.submitData(fileList) | ||||||
|  |     }, | ||||||
|  |     onSuccess(response, file, fileList) { | ||||||
|  |       console.log("onSuccess", response, file, fileList); | ||||||
|  |       this.submitData(fileList); | ||||||
|  |     }, | ||||||
|  |     onProgress(event, file, fileList) { | ||||||
|  |       // console.log(event, file, fileList) | ||||||
|  |     }, | ||||||
|  |     submitData(fileList) { | ||||||
|  |       const urls = fileList.map((item) => { | ||||||
|  |         if (item.response) { | ||||||
|  |           return "https://yzlp.store.seemore.club" + item.response.url; | ||||||
|  |         } else { | ||||||
|  |           return item.url; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |       // console.log('submitData',urls); | ||||||
|  |       if (typeof this.value === "string") { | ||||||
|  |         this.$emit("input", urls.join(",")); | ||||||
|  |       } else { | ||||||
|  |         this.$emit("input", urls); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss"> | ||||||
|  | .my-upload-image-cmp { | ||||||
|  |   .disUoloadSty { | ||||||
|  |     .el-upload--picture-card { | ||||||
|  |       display: none; /* 上传按钮隐藏 */ | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | import { optionsFilter } from "@/quickComponents/utils"; | ||||||
|  | import { parseTime } from "@/utils"; | ||||||
|  | // 状态
 | ||||||
|  | export const statusOptions = [ | ||||||
|  |   // 0 待支付,1 已退款,2 已失效,3 支付成功
 | ||||||
|  |   { | ||||||
|  |     label: "待支付", | ||||||
|  |     value: 0, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已退款", | ||||||
|  |     value: 1, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已失效", | ||||||
|  |     value: 2, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "支付成功", | ||||||
|  |     value: 3, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | export let levelOptions = []; | ||||||
|  | // 表格字段
 | ||||||
|  | export const tableFields = { | ||||||
|  | //   id: "标签id",
 | ||||||
|  |   checkCondition: "文本", | ||||||
|  |   refreshAmount: "重置费用", | ||||||
|  |   //   showStatus: {
 | ||||||
|  |   //     label: "显示状态",
 | ||||||
|  |   //     filter: (item) => (item ? "显示" : "隐藏"),
 | ||||||
|  |   //   },
 | ||||||
|  |   createDate: { | ||||||
|  |     label: "创建时间", | ||||||
|  |     filter: (time) => parseTime(time), | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export const createFields = { | ||||||
|  |   nikeName: { | ||||||
|  |     label: "用户昵称", | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  |   userName: { | ||||||
|  |     label: "用户名", | ||||||
|  |     required: true, | ||||||
|  |     props: { placeholder: "请输入11位手机号" }, | ||||||
|  |   }, | ||||||
|  |   email: { | ||||||
|  |     label: "电子邮箱", | ||||||
|  |   }, | ||||||
|  |   // status: {
 | ||||||
|  |   //   label: "状态",
 | ||||||
|  |   //   type: "radio",
 | ||||||
|  |   //   required: true,
 | ||||||
|  |   //   props: {
 | ||||||
|  |   //     options: statusOptions,
 | ||||||
|  |   //   },
 | ||||||
|  |   // },
 | ||||||
|  | }; | ||||||
|  | // 头部筛选字段
 | ||||||
|  | export const filterFields = [ | ||||||
|  |   { | ||||||
|  |     prop: "phone", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入用户昵称", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,56 @@ | ||||||
|  | // import { optionsFilter } from '@/quickComponents/utils'
 | ||||||
|  | import { parseTime } from "@/utils"; | ||||||
|  | // 状态
 | ||||||
|  | export const statusOptions = [ | ||||||
|  |   { | ||||||
|  |     label: "在职", | ||||||
|  |     value: "1", | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "离职", | ||||||
|  |     value: "0", | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | export let levelOptions = []; | ||||||
|  | // 表格字段
 | ||||||
|  | export const tableFields = { | ||||||
|  |   id: "用户ID", | ||||||
|  |   phone: "手机号", | ||||||
|  |   realName: { | ||||||
|  |     label: "是否实名", | ||||||
|  |     filter: (item) => (item ? "是" : "否"), | ||||||
|  |   }, | ||||||
|  |   realNameName: { | ||||||
|  |     label: "实名名称", | ||||||
|  |     required: true, | ||||||
|  |     props: { placeholder: "请输入11位手机号" }, | ||||||
|  |   }, | ||||||
|  |   score: "评价平均分", | ||||||
|  |   evaluateNumber: "评价次数", | ||||||
|  |   createDate: { | ||||||
|  |     label: "注册时间", | ||||||
|  |     filter: (time) => parseTime(time), | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export const createFields = { | ||||||
|  |   realNameName: { | ||||||
|  |     label: "实名名称", | ||||||
|  |     required: true, | ||||||
|  |     props: { placeholder: "请输入11位手机号" }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | // 头部筛选字段
 | ||||||
|  | export const filterFields = [ | ||||||
|  |   { | ||||||
|  |     prop: "id", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入用户id", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     prop: "phone", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入手机号", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,67 @@ | ||||||
|  | import { optionsFilter } from "@/quickComponents/utils"; | ||||||
|  | import { parseTime } from "@/utils"; | ||||||
|  | // 状态
 | ||||||
|  | export const statusOptions = [ | ||||||
|  |   // 0 待支付,1 已退款,2 已失效,3 支付成功
 | ||||||
|  |   { | ||||||
|  |     label: "待支付", | ||||||
|  |     value: 0, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已退款", | ||||||
|  |     value: 1, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已失效", | ||||||
|  |     value: 2, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "支付成功", | ||||||
|  |     value: 3, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | export let levelOptions = []; | ||||||
|  | // 表格字段
 | ||||||
|  | export const tableFields = { | ||||||
|  |   id: "标签id", | ||||||
|  |   content: "文本", | ||||||
|  |   showStatus: { | ||||||
|  |     label: "显示状态", | ||||||
|  |     filter: (item) => (item ? "显示" : "隐藏"), | ||||||
|  |   }, | ||||||
|  |   createDate: { | ||||||
|  |     label: "创建时间", | ||||||
|  |     filter: (time) => parseTime(time), | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export const createFields = { | ||||||
|  |   nikeName: { | ||||||
|  |     label: "用户昵称", | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  |   userName: { | ||||||
|  |     label: "用户名", | ||||||
|  |     required: true, | ||||||
|  |     props: { placeholder: "请输入11位手机号" }, | ||||||
|  |   }, | ||||||
|  |   email: { | ||||||
|  |     label: "电子邮箱", | ||||||
|  |   }, | ||||||
|  |   // status: {
 | ||||||
|  |   //   label: "状态",
 | ||||||
|  |   //   type: "radio",
 | ||||||
|  |   //   required: true,
 | ||||||
|  |   //   props: {
 | ||||||
|  |   //     options: statusOptions,
 | ||||||
|  |   //   },
 | ||||||
|  |   // },
 | ||||||
|  | }; | ||||||
|  | // 头部筛选字段
 | ||||||
|  | export const filterFields = [ | ||||||
|  |   { | ||||||
|  |     prop: "content", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入标签名称", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | import { optionsFilter } from "@/quickComponents/utils"; | ||||||
|  | import { parseTime } from "@/utils"; | ||||||
|  | // 状态
 | ||||||
|  | export const statusOptions = [ | ||||||
|  |   // 0 待支付,1 已退款,2 已失效,3 支付成功
 | ||||||
|  |   { | ||||||
|  |     label: "待支付", | ||||||
|  |     value: 0, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已退款", | ||||||
|  |     value: 1, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已失效", | ||||||
|  |     value: 2, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "支付成功", | ||||||
|  |     value: 3, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | export let levelOptions = []; | ||||||
|  | // 表格字段
 | ||||||
|  | export const tableFields = { | ||||||
|  |   groupName: "组名", | ||||||
|  |   createDate: { | ||||||
|  |     label: "创建时间", | ||||||
|  |     filter: (time) => parseTime(time), | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export const createFields = { | ||||||
|  | }; | ||||||
|  | // 头部筛选字段
 | ||||||
|  | export const filterFields = [ | ||||||
|  |   { | ||||||
|  |     prop: "groupName", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入组名称", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | import { optionsFilter } from "@/quickComponents/utils"; | ||||||
|  | import { parseTime } from "@/utils"; | ||||||
|  | // 状态
 | ||||||
|  | export const statusOptions = [ | ||||||
|  |   // 0 待支付,1 已退款,2 已失效,3 支付成功
 | ||||||
|  |   { | ||||||
|  |     label: "待支付", | ||||||
|  |     value: 0, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已退款", | ||||||
|  |     value: 1, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "已失效", | ||||||
|  |     value: 2, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "支付成功", | ||||||
|  |     value: 3, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | export let levelOptions = []; | ||||||
|  | // 表格字段
 | ||||||
|  | export const tableFields = { | ||||||
|  |   id: "订单号", | ||||||
|  |   consumerId: "用户ID", | ||||||
|  |   phone: "手机号", | ||||||
|  |   status: { | ||||||
|  |     label: "用户类型", | ||||||
|  |     filter: (status) => optionsFilter(status, statusOptions), | ||||||
|  |   }, | ||||||
|  |   payDate: { | ||||||
|  |     label: "支付时间", | ||||||
|  |     filter: (time) => parseTime(time), | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | export const createFields = { | ||||||
|  |   nikeName: { | ||||||
|  |     label: "用户昵称", | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  |   userName: { | ||||||
|  |     label: "用户名", | ||||||
|  |     required: true, | ||||||
|  |     props: { placeholder: "请输入11位手机号" }, | ||||||
|  |   }, | ||||||
|  |   email: { | ||||||
|  |     label: "电子邮箱", | ||||||
|  |   }, | ||||||
|  |   // status: {
 | ||||||
|  |   //   label: "状态",
 | ||||||
|  |   //   type: "radio",
 | ||||||
|  |   //   required: true,
 | ||||||
|  |   //   props: {
 | ||||||
|  |   //     options: statusOptions,
 | ||||||
|  |   //   },
 | ||||||
|  |   // },
 | ||||||
|  | }; | ||||||
|  | // 头部筛选字段
 | ||||||
|  | export const filterFields = [ | ||||||
|  |   { | ||||||
|  |     prop: "phone", | ||||||
|  |     props: { | ||||||
|  |       placeholder: "请输入用户昵称", | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | // Inspired by https://github.com/Inndy/vue-clipboard2
 | ||||||
|  | const Clipboard = require('clipboard') | ||||||
|  | if (!Clipboard) { | ||||||
|  |   throw new Error('you should npm install `clipboard` --save at first ') | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   bind(el, binding) { | ||||||
|  |     if (binding.arg === 'success') { | ||||||
|  |       el._v_clipboard_success = binding.value | ||||||
|  |     } else if (binding.arg === 'error') { | ||||||
|  |       el._v_clipboard_error = binding.value | ||||||
|  |     } else { | ||||||
|  |       const clipboard = new Clipboard(el, { | ||||||
|  |         text() { return binding.value }, | ||||||
|  |         action() { return binding.arg === 'cut' ? 'cut' : 'copy' } | ||||||
|  |       }) | ||||||
|  |       clipboard.on('success', e => { | ||||||
|  |         const callback = el._v_clipboard_success | ||||||
|  |         callback && callback(e) // eslint-disable-line
 | ||||||
|  |       }) | ||||||
|  |       clipboard.on('error', e => { | ||||||
|  |         const callback = el._v_clipboard_error | ||||||
|  |         callback && callback(e) // eslint-disable-line
 | ||||||
|  |       }) | ||||||
|  |       el._v_clipboard = clipboard | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   update(el, binding) { | ||||||
|  |     if (binding.arg === 'success') { | ||||||
|  |       el._v_clipboard_success = binding.value | ||||||
|  |     } else if (binding.arg === 'error') { | ||||||
|  |       el._v_clipboard_error = binding.value | ||||||
|  |     } else { | ||||||
|  |       el._v_clipboard.text = function() { return binding.value } | ||||||
|  |       el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   unbind(el, binding) { | ||||||
|  |     if (binding.arg === 'success') { | ||||||
|  |       delete el._v_clipboard_success | ||||||
|  |     } else if (binding.arg === 'error') { | ||||||
|  |       delete el._v_clipboard_error | ||||||
|  |     } else { | ||||||
|  |       el._v_clipboard.destroy() | ||||||
|  |       delete el._v_clipboard | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | import Clipboard from './clipboard' | ||||||
|  | 
 | ||||||
|  | const install = function(Vue) { | ||||||
|  |   Vue.directive('Clipboard', Clipboard) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (window.Vue) { | ||||||
|  |   window.clipboard = Clipboard | ||||||
|  |   Vue.use(install); // eslint-disable-line
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Clipboard.install = install | ||||||
|  | export default Clipboard | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | export default { | ||||||
|  |   bind(el, binding, vnode) { | ||||||
|  |     const dialogHeaderEl = el.querySelector('.el-dialog__header') | ||||||
|  |     const dragDom = el.querySelector('.el-dialog') | ||||||
|  |     dialogHeaderEl.style.cssText += ';cursor:move;' | ||||||
|  |     dragDom.style.cssText += ';top:0px;' | ||||||
|  | 
 | ||||||
|  |     // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
 | ||||||
|  |     const getStyle = (function() { | ||||||
|  |       if (window.document.currentStyle) { | ||||||
|  |         return (dom, attr) => dom.currentStyle[attr] | ||||||
|  |       } else { | ||||||
|  |         return (dom, attr) => getComputedStyle(dom, false)[attr] | ||||||
|  |       } | ||||||
|  |     })() | ||||||
|  | 
 | ||||||
|  |     dialogHeaderEl.onmousedown = (e) => { | ||||||
|  |       // 鼠标按下,计算当前元素距离可视区的距离
 | ||||||
|  |       const disX = e.clientX - dialogHeaderEl.offsetLeft | ||||||
|  |       const disY = e.clientY - dialogHeaderEl.offsetTop | ||||||
|  | 
 | ||||||
|  |       const dragDomWidth = dragDom.offsetWidth | ||||||
|  |       const dragDomHeight = dragDom.offsetHeight | ||||||
|  | 
 | ||||||
|  |       const screenWidth = document.body.clientWidth | ||||||
|  |       const screenHeight = document.body.clientHeight | ||||||
|  | 
 | ||||||
|  |       const minDragDomLeft = dragDom.offsetLeft | ||||||
|  |       const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth | ||||||
|  | 
 | ||||||
|  |       const minDragDomTop = dragDom.offsetTop | ||||||
|  |       const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight | ||||||
|  | 
 | ||||||
|  |       // 获取到的值带px 正则匹配替换
 | ||||||
|  |       let styL = getStyle(dragDom, 'left') | ||||||
|  |       let styT = getStyle(dragDom, 'top') | ||||||
|  | 
 | ||||||
|  |       if (styL.includes('%')) { | ||||||
|  |         styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100) | ||||||
|  |         styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100) | ||||||
|  |       } else { | ||||||
|  |         styL = +styL.replace(/\px/g, '') | ||||||
|  |         styT = +styT.replace(/\px/g, '') | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       document.onmousemove = function(e) { | ||||||
|  |         // 通过事件委托,计算移动的距离
 | ||||||
|  |         let left = e.clientX - disX | ||||||
|  |         let top = e.clientY - disY | ||||||
|  | 
 | ||||||
|  |         // 边界处理
 | ||||||
|  |         if (-(left) > minDragDomLeft) { | ||||||
|  |           left = -minDragDomLeft | ||||||
|  |         } else if (left > maxDragDomLeft) { | ||||||
|  |           left = maxDragDomLeft | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (-(top) > minDragDomTop) { | ||||||
|  |           top = -minDragDomTop | ||||||
|  |         } else if (top > maxDragDomTop) { | ||||||
|  |           top = maxDragDomTop | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 移动当前元素
 | ||||||
|  |         dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;` | ||||||
|  | 
 | ||||||
|  |         // emit onDrag event
 | ||||||
|  |         vnode.child.$emit('dragDialog') | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       document.onmouseup = function(e) { | ||||||
|  |         document.onmousemove = null | ||||||
|  |         document.onmouseup = null | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | import drag from './drag' | ||||||
|  | 
 | ||||||
|  | const install = function(Vue) { | ||||||
|  |   Vue.directive('el-drag-dialog', drag) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (window.Vue) { | ||||||
|  |   window['el-drag-dialog'] = drag | ||||||
|  |   Vue.use(install); // eslint-disable-line
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | drag.install = install | ||||||
|  | export default drag | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * How to use | ||||||
|  |  * <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table> | ||||||
|  |  * el-table height is must be set | ||||||
|  |  * bottomOffset: 30(default)   // The height of the table from the bottom of the page.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | const doResize = (el, binding, vnode) => { | ||||||
|  |   const { componentInstance: $table } = vnode | ||||||
|  | 
 | ||||||
|  |   const { value } = binding | ||||||
|  | 
 | ||||||
|  |   if (!$table.height) { | ||||||
|  |     throw new Error(`el-$table must set the height. Such as height='100px'`) | ||||||
|  |   } | ||||||
|  |   const bottomOffset = (value && value.bottomOffset) || 30 | ||||||
|  | 
 | ||||||
|  |   if (!$table) return | ||||||
|  | 
 | ||||||
|  |   const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset | ||||||
|  |   $table.layout.setHeight(height) | ||||||
|  |   $table.doLayout() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   bind(el, binding, vnode) { | ||||||
|  |     el.resizeListener = () => { | ||||||
|  |       doResize(el, binding, vnode) | ||||||
|  |     } | ||||||
|  |     // parameter 1 is must be "Element" type
 | ||||||
|  |     addResizeListener(window.document.body, el.resizeListener) | ||||||
|  |   }, | ||||||
|  |   inserted(el, binding, vnode) { | ||||||
|  |     doResize(el, binding, vnode) | ||||||
|  |   }, | ||||||
|  |   unbind(el) { | ||||||
|  |     removeResizeListener(window.document.body, el.resizeListener) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | import adaptive from './adaptive' | ||||||
|  | 
 | ||||||
|  | const install = function(Vue) { | ||||||
|  |   Vue.directive('el-height-adaptive-table', adaptive) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (window.Vue) { | ||||||
|  |   window['el-height-adaptive-table'] = adaptive | ||||||
|  |   Vue.use(install); // eslint-disable-line
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | adaptive.install = install | ||||||
|  | export default adaptive | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | import permission from './permission' | ||||||
|  | 
 | ||||||
|  | const install = function(Vue) { | ||||||
|  |   Vue.directive('permission', permission) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (window.Vue) { | ||||||
|  |   window['permission'] = permission | ||||||
|  |   Vue.use(install); // eslint-disable-line
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | permission.install = install | ||||||
|  | export default permission | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | import store from '@/store' | ||||||
|  | 
 | ||||||
|  | function checkPermission(el, binding) { | ||||||
|  |   const { value } = binding | ||||||
|  |   const roles = store.getters && store.getters.roles | ||||||
|  | 
 | ||||||
|  |   if (value && value instanceof Array) { | ||||||
|  |     if (value.length > 0) { | ||||||
|  |       const permissionRoles = value | ||||||
|  | 
 | ||||||
|  |       const hasPermission = roles.some(role => { | ||||||
|  |         return permissionRoles.includes(role) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       if (!hasPermission) { | ||||||
|  |         el.parentNode && el.parentNode.removeChild(el) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     throw new Error(`need roles! Like v-permission="['admin','editor']"`) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   inserted(el, binding) { | ||||||
|  |     checkPermission(el, binding) | ||||||
|  |   }, | ||||||
|  |   update(el, binding) { | ||||||
|  |     checkPermission(el, binding) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | const vueSticky = {} | ||||||
|  | let listenAction | ||||||
|  | vueSticky.install = Vue => { | ||||||
|  |   Vue.directive('sticky', { | ||||||
|  |     inserted(el, binding) { | ||||||
|  |       const params = binding.value || {} | ||||||
|  |       const stickyTop = params.stickyTop || 0 | ||||||
|  |       const zIndex = params.zIndex || 1000 | ||||||
|  |       const elStyle = el.style | ||||||
|  | 
 | ||||||
|  |       elStyle.position = '-webkit-sticky' | ||||||
|  |       elStyle.position = 'sticky' | ||||||
|  |       // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
 | ||||||
|  |       // if (~elStyle.position.indexOf('sticky')) {
 | ||||||
|  |       //     elStyle.top = `${stickyTop}px`;
 | ||||||
|  |       //     elStyle.zIndex = zIndex;
 | ||||||
|  |       //     return
 | ||||||
|  |       // }
 | ||||||
|  |       const elHeight = el.getBoundingClientRect().height | ||||||
|  |       const elWidth = el.getBoundingClientRect().width | ||||||
|  |       elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` | ||||||
|  | 
 | ||||||
|  |       const parentElm = el.parentNode || document.documentElement | ||||||
|  |       const placeholder = document.createElement('div') | ||||||
|  |       placeholder.style.display = 'none' | ||||||
|  |       placeholder.style.width = `${elWidth}px` | ||||||
|  |       placeholder.style.height = `${elHeight}px` | ||||||
|  |       parentElm.insertBefore(placeholder, el) | ||||||
|  | 
 | ||||||
|  |       let active = false | ||||||
|  | 
 | ||||||
|  |       const getScroll = (target, top) => { | ||||||
|  |         const prop = top ? 'pageYOffset' : 'pageXOffset' | ||||||
|  |         const method = top ? 'scrollTop' : 'scrollLeft' | ||||||
|  |         let ret = target[prop] | ||||||
|  |         if (typeof ret !== 'number') { | ||||||
|  |           ret = window.document.documentElement[method] | ||||||
|  |         } | ||||||
|  |         return ret | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const sticky = () => { | ||||||
|  |         if (active) { | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  |         if (!elStyle.height) { | ||||||
|  |           elStyle.height = `${el.offsetHeight}px` | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         elStyle.position = 'fixed' | ||||||
|  |         elStyle.width = `${elWidth}px` | ||||||
|  |         placeholder.style.display = 'inline-block' | ||||||
|  |         active = true | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const reset = () => { | ||||||
|  |         if (!active) { | ||||||
|  |           return | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         elStyle.position = '' | ||||||
|  |         placeholder.style.display = 'none' | ||||||
|  |         active = false | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const check = () => { | ||||||
|  |         const scrollTop = getScroll(window, true) | ||||||
|  |         const offsetTop = el.getBoundingClientRect().top | ||||||
|  |         if (offsetTop < stickyTop) { | ||||||
|  |           sticky() | ||||||
|  |         } else { | ||||||
|  |           if (scrollTop < elHeight + stickyTop) { | ||||||
|  |             reset() | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       listenAction = () => { | ||||||
|  |         check() | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       window.addEventListener('scroll', listenAction) | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     unbind() { | ||||||
|  |       window.removeEventListener('scroll', listenAction) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default vueSticky | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | import waves from './waves' | ||||||
|  | 
 | ||||||
|  | const install = function(Vue) { | ||||||
|  |   Vue.directive('waves', waves) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | if (window.Vue) { | ||||||
|  |   window.waves = waves | ||||||
|  |   Vue.use(install); // eslint-disable-line
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | waves.install = install | ||||||
|  | export default waves | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | .waves-ripple { | ||||||
|  |     position: absolute; | ||||||
|  |     border-radius: 100%; | ||||||
|  |     background-color: rgba(0, 0, 0, 0.15); | ||||||
|  |     background-clip: padding-box; | ||||||
|  |     pointer-events: none; | ||||||
|  |     -webkit-user-select: none; | ||||||
|  |     -moz-user-select: none; | ||||||
|  |     -ms-user-select: none; | ||||||
|  |     user-select: none; | ||||||
|  |     -webkit-transform: scale(0); | ||||||
|  |     -ms-transform: scale(0); | ||||||
|  |     transform: scale(0); | ||||||
|  |     opacity: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .waves-ripple.z-active { | ||||||
|  |     opacity: 0; | ||||||
|  |     -webkit-transform: scale(2); | ||||||
|  |     -ms-transform: scale(2); | ||||||
|  |     transform: scale(2); | ||||||
|  |     -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; | ||||||
|  |     transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; | ||||||
|  |     transition: opacity 1.2s ease-out, transform 0.6s ease-out; | ||||||
|  |     transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,72 @@ | ||||||
|  | import './waves.css' | ||||||
|  | 
 | ||||||
|  | const context = '@@wavesContext' | ||||||
|  | 
 | ||||||
|  | function handleClick(el, binding) { | ||||||
|  |   function handle(e) { | ||||||
|  |     const customOpts = Object.assign({}, binding.value) | ||||||
|  |     const opts = Object.assign({ | ||||||
|  |       ele: el, // 波纹作用元素
 | ||||||
|  |       type: 'hit', // hit 点击位置扩散 center中心点扩展
 | ||||||
|  |       color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
 | ||||||
|  |     }, | ||||||
|  |     customOpts | ||||||
|  |     ) | ||||||
|  |     const target = opts.ele | ||||||
|  |     if (target) { | ||||||
|  |       target.style.position = 'relative' | ||||||
|  |       target.style.overflow = 'hidden' | ||||||
|  |       const rect = target.getBoundingClientRect() | ||||||
|  |       let ripple = target.querySelector('.waves-ripple') | ||||||
|  |       if (!ripple) { | ||||||
|  |         ripple = document.createElement('span') | ||||||
|  |         ripple.className = 'waves-ripple' | ||||||
|  |         ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' | ||||||
|  |         target.appendChild(ripple) | ||||||
|  |       } else { | ||||||
|  |         ripple.className = 'waves-ripple' | ||||||
|  |       } | ||||||
|  |       switch (opts.type) { | ||||||
|  |         case 'center': | ||||||
|  |           ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px' | ||||||
|  |           ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px' | ||||||
|  |           break | ||||||
|  |         default: | ||||||
|  |           ripple.style.top = | ||||||
|  |             (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || | ||||||
|  |               document.body.scrollTop) + 'px' | ||||||
|  |           ripple.style.left = | ||||||
|  |             (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || | ||||||
|  |               document.body.scrollLeft) + 'px' | ||||||
|  |       } | ||||||
|  |       ripple.style.backgroundColor = opts.color | ||||||
|  |       ripple.className = 'waves-ripple z-active' | ||||||
|  |       return false | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!el[context]) { | ||||||
|  |     el[context] = { | ||||||
|  |       removeHandle: handle | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     el[context].removeHandle = handle | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return handle | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   bind(el, binding) { | ||||||
|  |     el.addEventListener('click', handleClick(el, binding), false) | ||||||
|  |   }, | ||||||
|  |   update(el, binding) { | ||||||
|  |     el.removeEventListener('click', el[context].removeHandle, false) | ||||||
|  |     el.addEventListener('click', handleClick(el, binding), false) | ||||||
|  |   }, | ||||||
|  |   unbind(el) { | ||||||
|  |     el.removeEventListener('click', el[context].removeHandle, false) | ||||||
|  |     el[context] = null | ||||||
|  |     delete el[context] | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | // import parseTime, formatTime and set to filter
 | ||||||
|  | export { parseTime, formatTime, objectFindLikeValue } from '@/utils' | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Show plural label if time is plural number | ||||||
|  |  * @param {number} time | ||||||
|  |  * @param {string} label | ||||||
|  |  * @return {string} | ||||||
|  |  */ | ||||||
|  | function pluralize(time, label) { | ||||||
|  |   if (time === 1) { | ||||||
|  |     return time + label | ||||||
|  |   } | ||||||
|  |   return time + label + 's' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @param {number} time | ||||||
|  |  */ | ||||||
|  | export function timeAgo(time) { | ||||||
|  |   const between = Date.now() / 1000 - Number(time) | ||||||
|  |   if (between < 3600) { | ||||||
|  |     return pluralize(~~(between / 60), ' minute') | ||||||
|  |   } else if (between < 86400) { | ||||||
|  |     return pluralize(~~(between / 3600), ' hour') | ||||||
|  |   } else { | ||||||
|  |     return pluralize(~~(between / 86400), ' day') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Number formatting | ||||||
|  |  * like 10000 => 10k | ||||||
|  |  * @param {number} num | ||||||
|  |  * @param {number} digits | ||||||
|  |  */ | ||||||
|  | export function numberFormatter(num, digits) { | ||||||
|  |   const si = [ | ||||||
|  |     { value: 1E18, symbol: 'E' }, | ||||||
|  |     { value: 1E15, symbol: 'P' }, | ||||||
|  |     { value: 1E12, symbol: 'T' }, | ||||||
|  |     { value: 1E9, symbol: 'G' }, | ||||||
|  |     { value: 1E6, symbol: 'M' }, | ||||||
|  |     { value: 1E3, symbol: 'k' } | ||||||
|  |   ] | ||||||
|  |   for (let i = 0; i < si.length; i++) { | ||||||
|  |     if (num >= si[i].value) { | ||||||
|  |       return (num / si[i].value).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return num.toString() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 10000 => "10,000" | ||||||
|  |  * @param {number} num | ||||||
|  |  */ | ||||||
|  | export function toThousandFilter(num) { | ||||||
|  |   return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Upper case first char | ||||||
|  |  * @param {String} string | ||||||
|  |  */ | ||||||
|  | export function uppercaseFirst(string) { | ||||||
|  |   return string.charAt(0).toUpperCase() + string.slice(1) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | import Vue from 'vue' | ||||||
|  | import SvgIcon from '@/components/SvgIcon'// svg component
 | ||||||
|  | 
 | ||||||
|  | // register globally
 | ||||||
|  | Vue.component('svg-icon', SvgIcon) | ||||||
|  | 
 | ||||||
|  | const req = require.context('./svg', false, /\.svg$/) | ||||||
|  | const requireAll = requireContext => requireContext.keys().map(requireContext) | ||||||
|  | requireAll(req) | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg> | ||||||
| After Width: | Height: | Size: 1.4 KiB | 
|  | @ -0,0 +1 @@ | ||||||
|  | <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1630378680211" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14288" xmlns:xlink="http://www.w3.org/1999/xlink" width="32.03125" height="32"><defs><style type="text/css"></style></defs><path d="M828.19122 471.539512c-17.655758 0-31.96878-14.313022-31.968781-31.96878V135.867317a7.992195 7.992195 0 0 0-7.992195-7.992195H138.86439a7.992195 7.992195 0 0 0-7.992195 7.992195v751.266342a7.992195 7.992195 0 0 0 7.992195 7.992195h288.718049c17.655758 0 31.96878 14.313022 31.968781 31.96878 0 17.655758-14.313022 31.96878-31.968781 31.968781H98.903415c-17.655758 0-31.96878-14.313022-31.968781-31.968781V95.906341c0-17.655758 14.313022-31.96878 31.968781-31.96878h729.287805c17.655758 0 31.96878 14.313022 31.96878 31.96878v343.664391c0 17.655758-14.313022 31.96878-31.96878 31.96878zM213.79122 269.736585h470.540487c17.655758 0 31.96878 14.313022 31.968781 31.968781 0 17.655758-14.313022 31.96878-31.968781 31.96878H213.79122c-17.655758 0-31.96878-14.313022-31.968781-31.96878 0-17.655758 14.313022-31.96878 31.968781-31.968781z m4.995121 205.799025h211.793171c17.655758 0 31.96878 14.313022 31.968781 31.96878 0 17.655758-14.313022 31.96878-31.968781 31.968781H218.786341c-17.655758 0-31.96878-14.313022-31.96878-31.968781 0-17.655758 14.313022-31.96878 31.96878-31.96878z m0 205.799024h211.793171c17.655758 0 31.96878 14.313022 31.968781 31.968781 0 17.655758-14.313022 31.96878-31.968781 31.96878H218.786341c-17.655758 0-31.96878-14.313022-31.96878-31.96878 0-17.655758 14.313022-31.96878 31.96878-31.968781z m303.703415-205.799024h43.957073c17.655758 0 31.96878 14.313022 31.968781 31.96878 0 17.655758-14.313022 31.96878-31.968781 31.968781h-43.957073c-17.655758 0-31.96878-14.313022-31.96878-31.968781 0-17.655758 14.313022-31.96878 31.96878-31.96878z m270.425912 71.322349l160.288469 320.578935c15.791579 31.584156 2.99008 69.989651-28.594077 85.781229a63.937561 63.937561 0 0 1-28.593077 6.750408H575.438049c-35.311516 0-63.937561-28.626045-63.937561-63.937561a63.937561 63.937561 0 0 1 6.750408-28.594076l160.288468-320.578935c15.792578-31.583157 54.197073-44.385655 85.781229-28.593077a63.937561 63.937561 0 0 1 28.594076 28.593077zM743.374049 589.641179a7.992195 7.992195 0 0 0-14.296039 0L581.665967 884.464265a7.992195 7.992195 0 0 0 7.148019 11.566705h294.824086a7.992195 7.992195 0 0 0 7.14802-11.566705L743.375048 589.641179zM735.281951 650.364878c17.655758 0 31.96878 14.313022 31.968781 31.968781v85.916097c0 17.655758-14.313022 31.96878-31.968781 31.968781-17.655758 0-31.96878-14.313022-31.96878-31.968781v-85.916097c0-17.655758 14.313022-31.96878 31.96878-31.968781z m0 229.77561c-17.655758 0-31.96878-14.313022-31.96878-31.968781 0-17.655758 14.313022-31.96878 31.96878-31.96878 17.655758 0 31.96878 14.313022 31.968781 31.96878 0 17.655758-14.313022 31.96878-31.968781 31.968781z" p-id="14289" fill="#bfbfbf"></path></svg> | ||||||
| After Width: | Height: | Size: 3.0 KiB | 
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue