init
This commit is contained in:
parent
304b17520c
commit
d10370086e
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"presets": ["es2015", "stage-2"],
|
||||||
|
"plugins": ["transform-runtime"],
|
||||||
|
"comments": false
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
build/*.js
|
||||||
|
config/*.js
|
||||||
|
src/assets
|
|
@ -0,0 +1,318 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: 'eslint:recommended',
|
||||||
|
// required to lint *.vue files
|
||||||
|
plugins: [
|
||||||
|
'html'
|
||||||
|
],
|
||||||
|
// check if imports actually resolve
|
||||||
|
'settings': {
|
||||||
|
'import/resolver': {
|
||||||
|
'webpack': {
|
||||||
|
'config': 'build/webpack.base.conf.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// add your custom rules here
|
||||||
|
'rules': {
|
||||||
|
// don't require .vue extension when importing
|
||||||
|
// 'import/extensions': ['error', 'always', {
|
||||||
|
// 'js': 'never',
|
||||||
|
// 'vue': 'never'
|
||||||
|
// }],
|
||||||
|
// allow debugger during development
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
/*
|
||||||
|
* Possible Errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
// disallow unnecessary parentheses
|
||||||
|
'no-extra-parens': ['error', 'all', {'nestedBinaryExpressions': false}],
|
||||||
|
|
||||||
|
// disallow negating the left operand of relational operators
|
||||||
|
'no-unsafe-negation': 'error',
|
||||||
|
|
||||||
|
// enforce valid JSDoc comments
|
||||||
|
'valid-jsdoc': 'off',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Best Practices
|
||||||
|
*/
|
||||||
|
|
||||||
|
// enforce return statements in callbacks of array methods
|
||||||
|
'array-callback-return': 'error',
|
||||||
|
|
||||||
|
// enforce consistent brace style for all control statements
|
||||||
|
curly: ['error', 'multi-line'],
|
||||||
|
|
||||||
|
// enforce consistent newlines before and after dots
|
||||||
|
'dot-location': ['error', 'property'],
|
||||||
|
|
||||||
|
// enforce dot notation whenever possible
|
||||||
|
'dot-notation': 'error',
|
||||||
|
|
||||||
|
// require the use of === and !==
|
||||||
|
'eqeqeq': ['error', 'smart'],
|
||||||
|
|
||||||
|
// disallow the use of arguments.caller or arguments.callee
|
||||||
|
'no-caller': 'error',
|
||||||
|
|
||||||
|
// disallow empty functions
|
||||||
|
'no-empty-function': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary calls to .bind()
|
||||||
|
'no-extra-bind': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary labels
|
||||||
|
'no-extra-label': 'error',
|
||||||
|
|
||||||
|
// disallow leading or trailing decimal points in numeric literals
|
||||||
|
'no-floating-decimal': 'error',
|
||||||
|
|
||||||
|
// disallow assignments to native objects or read-only global variables
|
||||||
|
'no-global-assign': 'error',
|
||||||
|
|
||||||
|
// disallow the use of eval()-like methods
|
||||||
|
'no-implied-eval': 'error',
|
||||||
|
|
||||||
|
// disallow the use of the __iterator__ property
|
||||||
|
'no-iterator': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary nested blocks
|
||||||
|
'no-lone-blocks': 'error',
|
||||||
|
|
||||||
|
// disallow multiple spaces
|
||||||
|
'no-multi-spaces': 'error',
|
||||||
|
|
||||||
|
// disallow new operators with the String, Number, and Boolean objects
|
||||||
|
'no-new-wrappers': 'error',
|
||||||
|
|
||||||
|
// disallow octal escape sequences in string literals
|
||||||
|
'no-octal-escape': 'error',
|
||||||
|
|
||||||
|
// disallow the use of the __proto__ property
|
||||||
|
'no-proto': 'error',
|
||||||
|
|
||||||
|
// disallow comparisons where both sides are exactly the same
|
||||||
|
'no-self-compare': 'error',
|
||||||
|
|
||||||
|
// disallow throwing literals as exceptions
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
|
||||||
|
// disallow unused expressions
|
||||||
|
'no-unused-expressions': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary calls to .call() and .apply()
|
||||||
|
'no-useless-call': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary concatenation of literals or template literals
|
||||||
|
'no-useless-concat': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary escape characters
|
||||||
|
'no-useless-escape': 'error',
|
||||||
|
|
||||||
|
// disallow void operators
|
||||||
|
'no-void': 'error',
|
||||||
|
|
||||||
|
// require parentheses around immediate function invocations
|
||||||
|
'wrap-iife': 'error',
|
||||||
|
|
||||||
|
// require or disallow “Yoda” conditions
|
||||||
|
yoda: 'error',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
// disallow labels that share a name with a variable
|
||||||
|
'no-label-var': 'error',
|
||||||
|
|
||||||
|
// disallow initializing variables to undefined
|
||||||
|
'no-undef-init': 'error',
|
||||||
|
'no-undef': 'off',
|
||||||
|
// disallow the use of variables before they are defined
|
||||||
|
'no-use-before-define': 'error',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node.js and CommonJS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// disallow new operators with calls to require
|
||||||
|
'no-new-require': 'error',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stylistic Issues
|
||||||
|
*/
|
||||||
|
|
||||||
|
// enforce consistent spacing inside array brackets
|
||||||
|
'array-bracket-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing inside single-line blocks
|
||||||
|
'block-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent brace style for blocks
|
||||||
|
'brace-style': ['error', '1tbs', {'allowSingleLine': true}],
|
||||||
|
|
||||||
|
// require or disallow trailing commas
|
||||||
|
'comma-dangle': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing before and after commas
|
||||||
|
'comma-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent comma style
|
||||||
|
'comma-style': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing inside computed property brackets
|
||||||
|
'computed-property-spacing': 'error',
|
||||||
|
|
||||||
|
// require or disallow spacing between function identifiers and their invocations
|
||||||
|
'func-call-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent indentation
|
||||||
|
indent: ['error', 2, {SwitchCase: 1}],
|
||||||
|
|
||||||
|
// enforce the consistent use of either double or single quotes in JSX attributes
|
||||||
|
'jsx-quotes': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing between keys and values in object literal properties
|
||||||
|
'key-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing before and after keywords
|
||||||
|
'keyword-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent linebreak style
|
||||||
|
'linebreak-style': 'error',
|
||||||
|
|
||||||
|
// require or disallow newlines around directives
|
||||||
|
'lines-around-directive': 'error',
|
||||||
|
|
||||||
|
// require constructor names to begin with a capital letter
|
||||||
|
'new-cap': 'off',
|
||||||
|
|
||||||
|
// require parentheses when invoking a constructor with no arguments
|
||||||
|
'new-parens': 'error',
|
||||||
|
|
||||||
|
// disallow Array constructors
|
||||||
|
'no-array-constructor': 'error',
|
||||||
|
|
||||||
|
// disallow Object constructors
|
||||||
|
'no-new-object': 'error',
|
||||||
|
|
||||||
|
// disallow trailing whitespace at the end of lines
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
|
||||||
|
// disallow ternary operators when simpler alternatives exist
|
||||||
|
'no-unneeded-ternary': 'error',
|
||||||
|
|
||||||
|
// disallow whitespace before properties
|
||||||
|
'no-whitespace-before-property': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing inside braces
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
|
||||||
|
// require or disallow padding within blocks
|
||||||
|
'padded-blocks': ['error', 'never'],
|
||||||
|
|
||||||
|
// require quotes around object literal property names
|
||||||
|
'quote-props': ['error', 'as-needed'],
|
||||||
|
|
||||||
|
// enforce the consistent use of either backticks, double, or single quotes
|
||||||
|
quotes: ['error', 'single'],
|
||||||
|
|
||||||
|
// enforce consistent spacing before and after semicolons
|
||||||
|
'semi-spacing': 'error',
|
||||||
|
|
||||||
|
// require or disallow semicolons instead of ASI
|
||||||
|
// semi: ['error', 'never'],
|
||||||
|
|
||||||
|
// enforce consistent spacing before blocks
|
||||||
|
'space-before-blocks': 'error',
|
||||||
|
|
||||||
|
'no-console': 'off',
|
||||||
|
|
||||||
|
// enforce consistent spacing before function definition opening parenthesis
|
||||||
|
'space-before-function-paren': ['error', 'never'],
|
||||||
|
|
||||||
|
// enforce consistent spacing inside parentheses
|
||||||
|
'space-in-parens': 'error',
|
||||||
|
|
||||||
|
// require spacing around infix operators
|
||||||
|
'space-infix-ops': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing before or after unary operators
|
||||||
|
'space-unary-ops': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing after the // or /* in a comment
|
||||||
|
'spaced-comment': 'error',
|
||||||
|
|
||||||
|
// require or disallow Unicode byte order mark (BOM)
|
||||||
|
'unicode-bom': 'error',
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ECMAScript 6
|
||||||
|
*/
|
||||||
|
|
||||||
|
// require braces around arrow function bodies
|
||||||
|
'arrow-body-style': 'error',
|
||||||
|
|
||||||
|
// require parentheses around arrow function arguments
|
||||||
|
'arrow-parens': ['error', 'as-needed'],
|
||||||
|
|
||||||
|
// enforce consistent spacing before and after the arrow in arrow functions
|
||||||
|
'arrow-spacing': 'error',
|
||||||
|
|
||||||
|
// enforce consistent spacing around * operators in generator functions
|
||||||
|
'generator-star-spacing': ['error', 'after'],
|
||||||
|
|
||||||
|
// disallow duplicate module imports
|
||||||
|
'no-duplicate-imports': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary computed property keys in object literals
|
||||||
|
'no-useless-computed-key': 'error',
|
||||||
|
|
||||||
|
// disallow unnecessary constructors
|
||||||
|
'no-useless-constructor': 'error',
|
||||||
|
|
||||||
|
// disallow renaming import, export, and destructured assignments to the same name
|
||||||
|
'no-useless-rename': 'error',
|
||||||
|
|
||||||
|
// require let or const instead of var
|
||||||
|
'no-var': 'error',
|
||||||
|
|
||||||
|
// require or disallow method and property shorthand syntax for object literals
|
||||||
|
'object-shorthand': 'error',
|
||||||
|
|
||||||
|
// require arrow functions as callbacks
|
||||||
|
'prefer-arrow-callback': 'error',
|
||||||
|
|
||||||
|
// require const declarations for variables that are never reassigned after declared
|
||||||
|
'prefer-const': 'error',
|
||||||
|
|
||||||
|
// disallow parseInt() in favor of binary, octal, and hexadecimal literals
|
||||||
|
'prefer-numeric-literals': 'error',
|
||||||
|
|
||||||
|
// require rest parameters instead of arguments
|
||||||
|
'prefer-rest-params': 'error',
|
||||||
|
|
||||||
|
// require spread operators instead of .apply()
|
||||||
|
'prefer-spread': 'error',
|
||||||
|
|
||||||
|
// enforce spacing between rest and spread operators and their expressions
|
||||||
|
'rest-spread-spacing': 'error',
|
||||||
|
|
||||||
|
// require or disallow spacing around embedded expressions of template strings
|
||||||
|
'template-curly-spacing': 'error',
|
||||||
|
|
||||||
|
// require or disallow spacing around the * in yield* expressions
|
||||||
|
'yield-star-spacing': 'error'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
static/ckeditor
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
|
test/unit/coverage
|
||||||
|
test/e2e/reports
|
||||||
|
selenium-debug.log
|
||||||
|
.idea
|
|
@ -0,0 +1,42 @@
|
||||||
|
require('./check-versions')();
|
||||||
|
var server = require('pushstate-server');
|
||||||
|
var opn = require('opn')
|
||||||
|
var ora = require('ora')
|
||||||
|
var rm = require('rimraf')
|
||||||
|
var path = require('path')
|
||||||
|
var chalk = require('chalk')
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var config = require('../config');
|
||||||
|
var webpackConfig = require('./webpack.prod.conf');
|
||||||
|
|
||||||
|
console.log(process.env.NODE_ENV)
|
||||||
|
console.log(process.env.npm_config_preview)
|
||||||
|
|
||||||
|
var spinner = ora('building for ' + process.env.NODE_ENV + '...')
|
||||||
|
spinner.start()
|
||||||
|
|
||||||
|
|
||||||
|
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||||
|
if (err) throw err
|
||||||
|
webpack(webpackConfig, function (err, stats) {
|
||||||
|
spinner.stop()
|
||||||
|
if (err) throw err
|
||||||
|
process.stdout.write(stats.toString({
|
||||||
|
colors: true,
|
||||||
|
modules: false,
|
||||||
|
children: false,
|
||||||
|
chunks: false,
|
||||||
|
chunkModules: false
|
||||||
|
}) + '\n\n')
|
||||||
|
|
||||||
|
console.log(chalk.cyan(' Build complete.\n'))
|
||||||
|
if(process.env.npm_config_preview){
|
||||||
|
server.start({
|
||||||
|
port: 80,
|
||||||
|
directory: './dist',
|
||||||
|
file: '/index.html'
|
||||||
|
});
|
||||||
|
opn('http://kushnerpreview.wallstreetcn.com/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,45 @@
|
||||||
|
var chalk = require('chalk')
|
||||||
|
var semver = require('semver')
|
||||||
|
var packageConfig = require('../package.json')
|
||||||
|
|
||||||
|
function exec(cmd) {
|
||||||
|
return require('child_process').execSync(cmd).toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionRequirements = [
|
||||||
|
{
|
||||||
|
name: 'node',
|
||||||
|
currentVersion: semver.clean(process.version),
|
||||||
|
versionRequirement: packageConfig.engines.node
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npm',
|
||||||
|
currentVersion: exec('npm --version'),
|
||||||
|
versionRequirement: packageConfig.engines.npm
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
var warnings = []
|
||||||
|
for (var i = 0; i < versionRequirements.length; i++) {
|
||||||
|
var mod = versionRequirements[i]
|
||||||
|
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||||
|
warnings.push(mod.name + ': ' +
|
||||||
|
chalk.red(mod.currentVersion) + ' should be ' +
|
||||||
|
chalk.green(mod.versionRequirement)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings.length) {
|
||||||
|
console.log('')
|
||||||
|
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||||
|
console.log()
|
||||||
|
for (var i = 0; i < warnings.length; i++) {
|
||||||
|
var warning = warnings[i]
|
||||||
|
console.log(' ' + warning)
|
||||||
|
}
|
||||||
|
console.log()
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
require('eventsource-polyfill')
|
||||||
|
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
|
||||||
|
|
||||||
|
hotClient.subscribe(function (event) {
|
||||||
|
if (event.action === 'reload') {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,85 @@
|
||||||
|
require('./check-versions')(); // 检查 Node 和 npm 版本
|
||||||
|
var config = require('../config');
|
||||||
|
if (!process.env.NODE_ENV) {
|
||||||
|
process.env.NODE_ENV = config.dev.env;
|
||||||
|
// process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
|
||||||
|
}
|
||||||
|
|
||||||
|
var opn = require('opn')
|
||||||
|
var path = require('path');
|
||||||
|
var express = require('express');
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var proxyMiddleware = require('http-proxy-middleware');
|
||||||
|
var webpackConfig = require('./webpack.dev.conf');
|
||||||
|
|
||||||
|
// default port where dev server listens for incoming traffic
|
||||||
|
var port = process.env.PORT || config.dev.port;
|
||||||
|
// automatically open browser, if not set will be false
|
||||||
|
var autoOpenBrowser = !!config.dev.autoOpenBrowser;
|
||||||
|
// Define HTTP proxies to your custom API backend
|
||||||
|
// https://github.com/chimurai/http-proxy-middleware
|
||||||
|
var proxyTable = config.dev.proxyTable;
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
var compiler = webpack(webpackConfig);
|
||||||
|
|
||||||
|
var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||||
|
publicPath: webpackConfig.output.publicPath,
|
||||||
|
quiet: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
|
||||||
|
log: () => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// force page reload when html-webpack-plugin template changes
|
||||||
|
compiler.plugin('compilation', function (compilation) {
|
||||||
|
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
|
||||||
|
hotMiddleware.publish({action: 'reload'});
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// compiler.apply(new DashboardPlugin());
|
||||||
|
|
||||||
|
// proxy api requests
|
||||||
|
Object.keys(proxyTable).forEach(function (context) {
|
||||||
|
var options = proxyTable[context]
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
options = {target: options}
|
||||||
|
}
|
||||||
|
app.use(proxyMiddleware(options.filter || context, options))
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle fallback for HTML5 history API
|
||||||
|
app.use(require('connect-history-api-fallback')());
|
||||||
|
|
||||||
|
// serve webpack bundle output
|
||||||
|
app.use(devMiddleware);
|
||||||
|
|
||||||
|
// enable hot-reload and state-preserving
|
||||||
|
// compilation error display
|
||||||
|
app.use(hotMiddleware);
|
||||||
|
|
||||||
|
// serve pure static assets
|
||||||
|
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory);
|
||||||
|
app.use(staticPath, express.static('./static'));
|
||||||
|
|
||||||
|
var uri = 'http://localhost:' + port
|
||||||
|
|
||||||
|
devMiddleware.waitUntilValid(function () {
|
||||||
|
console.log('> Listening at ' + uri + '\n')
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app.listen(port, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// when env is testing, don't need open it
|
||||||
|
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
|
||||||
|
opn(uri)
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,71 @@
|
||||||
|
var path = require('path')
|
||||||
|
var config = require('../config')
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
|
|
||||||
|
exports.assetsPath = function (_path) {
|
||||||
|
var assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||||
|
? config.build.assetsSubDirectory
|
||||||
|
: config.dev.assetsSubDirectory
|
||||||
|
return path.posix.join(assetsSubDirectory, _path)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.cssLoaders = function (options) {
|
||||||
|
options = options || {}
|
||||||
|
|
||||||
|
var cssLoader = {
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
minimize: process.env.NODE_ENV === 'production',
|
||||||
|
sourceMap: options.sourceMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate loader string to be used with extract text plugin
|
||||||
|
function generateLoaders(loader, loaderOptions) {
|
||||||
|
var loaders = [cssLoader]
|
||||||
|
if (loader) {
|
||||||
|
loaders.push({
|
||||||
|
loader: loader + '-loader',
|
||||||
|
options: Object.assign({}, loaderOptions, {
|
||||||
|
sourceMap: options.sourceMap
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract CSS when that option is specified
|
||||||
|
// (which is the case during production build)
|
||||||
|
if (options.extract) {
|
||||||
|
return ExtractTextPlugin.extract({
|
||||||
|
use: loaders,
|
||||||
|
fallback: 'vue-style-loader'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return ['vue-style-loader'].concat(loaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
|
||||||
|
return {
|
||||||
|
css: generateLoaders(),
|
||||||
|
postcss: generateLoaders(),
|
||||||
|
less: generateLoaders('less'),
|
||||||
|
sass: generateLoaders('sass', {indentedSyntax: true}),
|
||||||
|
scss: generateLoaders('sass'),
|
||||||
|
stylus: generateLoaders('stylus'),
|
||||||
|
styl: generateLoaders('stylus')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate loaders for standalone style files (outside of .vue)
|
||||||
|
exports.styleLoaders = function (options) {
|
||||||
|
var output = []
|
||||||
|
var loaders = exports.cssLoaders(options)
|
||||||
|
for (var extension in loaders) {
|
||||||
|
var loader = loaders[extension]
|
||||||
|
output.push({
|
||||||
|
test: new RegExp('\\.' + extension + '$'),
|
||||||
|
use: loader
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
var utils = require('./utils')
|
||||||
|
var config = require('../config')
|
||||||
|
var isProduction = process.env.NODE_ENV === 'production'
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
loaders: utils.cssLoaders({
|
||||||
|
sourceMap: isProduction
|
||||||
|
? config.build.productionSourceMap
|
||||||
|
: config.dev.cssSourceMap,
|
||||||
|
extract: isProduction
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
var path = require('path');
|
||||||
|
var utils = require('./utils');
|
||||||
|
var config = require('../config');
|
||||||
|
var vueLoaderConfig = require('./vue-loader.conf');
|
||||||
|
|
||||||
|
function resolve(dir) {
|
||||||
|
return path.join(__dirname, '..', dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
var src = path.resolve(__dirname, '../src');
|
||||||
|
var env = process.env.NODE_ENV
|
||||||
|
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
|
||||||
|
// various preprocessor loaders added to vue-loader at the end of this file
|
||||||
|
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
|
||||||
|
var cssSourceMapProd = (env === 'production||sit' && config.build.productionSourceMap)
|
||||||
|
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
app: './src/main.js'
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: config.build.assetsRoot,
|
||||||
|
filename: '[name].js',
|
||||||
|
publicPath: process.env.NODE_ENV === 'production||sit' ? config.build.assetsPublicPath : config.dev.assetsPublicPath
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.vue', '.json'],
|
||||||
|
alias: {
|
||||||
|
'vue$': 'vue/dist/vue.esm.js',
|
||||||
|
'@': resolve('src'),
|
||||||
|
'src': path.resolve(__dirname, '../src'),
|
||||||
|
'assets': path.resolve(__dirname, '../src/assets'),
|
||||||
|
'components': path.resolve(__dirname, '../src/components'),
|
||||||
|
'views': path.resolve(__dirname, '../src/views'),
|
||||||
|
'styles': path.resolve(__dirname, '../src/styles'),
|
||||||
|
'api': path.resolve(__dirname, '../src/api'),
|
||||||
|
'utils': path.resolve(__dirname, '../src/utils'),
|
||||||
|
'store': path.resolve(__dirname, '../src/store'),
|
||||||
|
'router': path.resolve(__dirname, '../src/router'),
|
||||||
|
'mock': path.resolve(__dirname, '../src/mock'),
|
||||||
|
'vendor': path.resolve(__dirname, '../src/vendor'),
|
||||||
|
'static': path.resolve(__dirname, '../static')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
jquery: 'jQuery'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
// {
|
||||||
|
// test: /\.(js|vue)$/,
|
||||||
|
// loader: 'eslint-loader',
|
||||||
|
// enforce: "pre",
|
||||||
|
// include: [resolve('src'), resolve('test')],
|
||||||
|
// options: {
|
||||||
|
// formatter: require('eslint-friendly-formatter')
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
{ test: /\.vue$/,
|
||||||
|
loader: 'vue-loader',
|
||||||
|
options: vueLoaderConfig
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'babel-loader?cacheDirectory',
|
||||||
|
include: [resolve('src'), resolve('test')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
query: {
|
||||||
|
limit: 10000,
|
||||||
|
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
query: {
|
||||||
|
limit: 10000,
|
||||||
|
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
//注入全局mixin
|
||||||
|
// sassResources: path.join(__dirname, '../src/styles/mixin.scss'),
|
||||||
|
// sassLoader: {
|
||||||
|
// data: path.join(__dirname, '../src/styles/index.scss')
|
||||||
|
// },
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
var utils = require('./utils')
|
||||||
|
var path = require('path')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var config = require('../config')
|
||||||
|
var merge = require('webpack-merge')
|
||||||
|
var baseWebpackConfig = require('./webpack.base.conf')
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||||
|
|
||||||
|
// add hot-reload related code to entry chunks
|
||||||
|
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
|
||||||
|
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
|
||||||
|
})
|
||||||
|
|
||||||
|
function resolveApp(relativePath) {
|
||||||
|
return path.resolve(relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = merge(baseWebpackConfig, {
|
||||||
|
module: {
|
||||||
|
rules: utils.styleLoaders({sourceMap: config.dev.cssSourceMap})
|
||||||
|
},
|
||||||
|
// cheap-source-map is faster for development
|
||||||
|
devtool: '#cheap-source-map',
|
||||||
|
cache: true,
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': config.dev.env
|
||||||
|
}),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: 'jquery',
|
||||||
|
'jQuery': 'jquery'
|
||||||
|
}),
|
||||||
|
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.NoErrorsPlugin(),
|
||||||
|
// https://github.com/ampedandwired/html-webpack-plugin
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: 'index.html',
|
||||||
|
template: 'index.html',
|
||||||
|
favicon: resolveApp('favicon.ico'),
|
||||||
|
inject: true,
|
||||||
|
path:config.dev.staticPath
|
||||||
|
}),
|
||||||
|
new FriendlyErrorsPlugin()
|
||||||
|
]
|
||||||
|
})
|
|
@ -0,0 +1,113 @@
|
||||||
|
var path = require('path')
|
||||||
|
var utils = require('./utils')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var config = require('../config')
|
||||||
|
var merge = require('webpack-merge')
|
||||||
|
var baseWebpackConfig = require('./webpack.base.conf')
|
||||||
|
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
|
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||||
|
|
||||||
|
var env = process.env.NODE_ENV === 'production' ? config.build.prodEnv : config.build.sitEnv
|
||||||
|
|
||||||
|
function resolveApp(relativePath) {
|
||||||
|
return path.resolve(relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var webpackConfig = merge(baseWebpackConfig, {
|
||||||
|
module: {
|
||||||
|
rules: utils.styleLoaders({
|
||||||
|
sourceMap: config.build.productionSourceMap,
|
||||||
|
extract: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||||
|
output: {
|
||||||
|
path: config.build.assetsRoot,
|
||||||
|
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||||
|
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': env
|
||||||
|
}),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
compress: {
|
||||||
|
warnings: false
|
||||||
|
},
|
||||||
|
sourceMap: true
|
||||||
|
}),
|
||||||
|
// extract css into its own file
|
||||||
|
new ExtractTextPlugin({
|
||||||
|
filename: utils.assetsPath('css/[name].[contenthash].css')
|
||||||
|
}),
|
||||||
|
// Compress extracted CSS. We are using this plugin so that possible
|
||||||
|
// duplicated CSS from different components can be deduped.
|
||||||
|
new OptimizeCSSPlugin(),
|
||||||
|
// generate dist index.html with correct asset hash for caching.
|
||||||
|
// you can customize output by editing /index.html
|
||||||
|
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: process.env.NODE_ENV === 'testing'
|
||||||
|
? 'index.html'
|
||||||
|
: config.build.index,
|
||||||
|
template: 'index.html',
|
||||||
|
inject: true,
|
||||||
|
favicon: resolveApp('favicon.ico'),
|
||||||
|
minify: {
|
||||||
|
removeComments: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
removeStyleLinkTypeAttributes: true,
|
||||||
|
keepClosingSlash: true,
|
||||||
|
minifyJS: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyURLs: true
|
||||||
|
},
|
||||||
|
path:config.build.staticPath,
|
||||||
|
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||||
|
chunksSortMode: 'dependency'
|
||||||
|
}),
|
||||||
|
// split vendor js into its own file
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'vendor',
|
||||||
|
minChunks: function (module, count) {
|
||||||
|
// any required modules inside node_modules are extracted to vendor
|
||||||
|
return (
|
||||||
|
module.resource &&
|
||||||
|
/\.js$/.test(module.resource) &&
|
||||||
|
module.resource.indexOf(
|
||||||
|
path.join(__dirname, '../node_modules')
|
||||||
|
) === 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// extract webpack runtime and module manifest to its own file in order to
|
||||||
|
// prevent vendor hash from being updated whenever app bundle is updated
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'manifest',
|
||||||
|
chunks: ['vendor']
|
||||||
|
}),
|
||||||
|
// copy custom static assets
|
||||||
|
new CopyWebpackPlugin([
|
||||||
|
{
|
||||||
|
from: path.resolve(__dirname, '../static'),
|
||||||
|
to: config.build.assetsSubDirectory,
|
||||||
|
ignore: ['.*']
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: 'jquery',
|
||||||
|
'jQuery': 'jquery'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (config.build.bundleAnalyzerReport) {
|
||||||
|
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||||
|
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||||
|
}
|
||||||
|
module.exports = webpackConfig
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
NODE_ENV: '"development"',
|
||||||
|
BASE_API: '"https://api-dev"',
|
||||||
|
APP_ORIGIN: '"https://wallstreetcn.com"'
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||||
|
var path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
build: {
|
||||||
|
sitEnv: require('./sit.env'),
|
||||||
|
prodEnv: require('./prod.env'),
|
||||||
|
index: path.resolve(__dirname, '../dist/index.html'),
|
||||||
|
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||||
|
assetsSubDirectory: '',
|
||||||
|
assetsPublicPath: '/',
|
||||||
|
staticPath:'',
|
||||||
|
productionSourceMap: true,
|
||||||
|
// Gzip off by default as many popular static hosts such as
|
||||||
|
// Surge or Netlify already gzip all static assets for you.
|
||||||
|
// Before setting to `true`, make sure to:
|
||||||
|
// npm install --save-dev compression-webpack-plugin
|
||||||
|
productionGzip: false,
|
||||||
|
productionGzipExtensions: ['js', 'css'],
|
||||||
|
// Run the build command with an extra argument to
|
||||||
|
// View the bundle analyzer report after build finishes:
|
||||||
|
// `npm run build --report`
|
||||||
|
// Set to `true` or `false` to always turn it on or off
|
||||||
|
bundleAnalyzerReport: process.env.npm_config_report
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
env: require('./dev.env'),
|
||||||
|
port: 9527,
|
||||||
|
autoOpenBrowser: true,
|
||||||
|
assetsSubDirectory: 'static',
|
||||||
|
staticPath:'/static',
|
||||||
|
assetsPublicPath: '/',
|
||||||
|
proxyTable: {},
|
||||||
|
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||||
|
// with this option, according to the CSS-Loader README
|
||||||
|
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||||
|
// In our experience, they generally work as expected,
|
||||||
|
// just be aware of this issue when enabling this option.
|
||||||
|
cssSourceMap: false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
NODE_ENV: '"production"',
|
||||||
|
BASE_API: '"https://api-prod',
|
||||||
|
APP_ORIGIN: '"https://wallstreetcn.com"'
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
NODE_ENV: '"production"',
|
||||||
|
BASE_API: '"https://api-sit"',
|
||||||
|
APP_ORIGIN: '"https://wallstreetcn.com"'
|
||||||
|
};
|
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -0,0 +1,17 @@
|
||||||
|
<!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">
|
||||||
|
<title>Juicy</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src=<%= htmlWebpackPlugin.options.path %>/jquery.min.js></script>
|
||||||
|
<script src=<%= htmlWebpackPlugin.options.path %>/tinymce1.3/tinymce.min.js></script>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"name": "juicy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A Vue.js admin",
|
||||||
|
"author": "Pan <panfree23@gmail.com>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node build/dev-server.js",
|
||||||
|
"build:prod": "NODE_ENV=production node build/build.js",
|
||||||
|
"build:sit": "NODE_ENV=sit node build/build.js",
|
||||||
|
"build:sit-preview": "NODE_ENV=sit npm_config_preview=true npm_config_report=true node build/build.js",
|
||||||
|
"lint": "eslint --ext .js,.vue src"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "0.15.3",
|
||||||
|
"codemirror": "5.22.0",
|
||||||
|
"dropzone": "4.3.0",
|
||||||
|
"echarts": "3.4.0",
|
||||||
|
"element-ui": "1.2.7",
|
||||||
|
"file-saver": "1.3.3",
|
||||||
|
"jquery": "3.1.1",
|
||||||
|
"js-cookie": "2.1.3",
|
||||||
|
"jsonlint": "1.6.2",
|
||||||
|
"normalize.css": "3.0.2",
|
||||||
|
"nprogress": "0.2.0",
|
||||||
|
"showdown": "1.6.4",
|
||||||
|
"simplemde": "1.11.2",
|
||||||
|
"vue": "2.2.6",
|
||||||
|
"vue-multiselect": "2.0.0-beta.14",
|
||||||
|
"vue-router": "2.3.0",
|
||||||
|
"vuedraggable": "2.8.4",
|
||||||
|
"vuex": "2.2.1",
|
||||||
|
"xlsx": "0.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "6.7.2",
|
||||||
|
"babel-core": "6.22.1",
|
||||||
|
"babel-eslint": "7.1.1",
|
||||||
|
"babel-loader": "6.2.10",
|
||||||
|
"babel-plugin-transform-runtime": "6.22.0",
|
||||||
|
"babel-preset-es2015": "6.22.0",
|
||||||
|
"babel-preset-stage-2": "6.22.0",
|
||||||
|
"babel-register": "6.22.0",
|
||||||
|
"chalk": "1.1.3",
|
||||||
|
"connect-history-api-fallback": "1.3.0",
|
||||||
|
"copy-webpack-plugin": "4.0.1",
|
||||||
|
"css-loader": "0.26.1",
|
||||||
|
"eslint": "3.14.1",
|
||||||
|
"eslint-friendly-formatter": "2.0.7",
|
||||||
|
"eslint-loader": "1.6.1",
|
||||||
|
"eslint-plugin-html": "2.0.0",
|
||||||
|
"eslint-config-airbnb-base": "11.0.1",
|
||||||
|
"eslint-import-resolver-webpack": "0.8.1",
|
||||||
|
"eslint-plugin-import": "2.2.0",
|
||||||
|
"eventsource-polyfill": "0.9.6",
|
||||||
|
"express": "4.14.1",
|
||||||
|
"extract-text-webpack-plugin": "2.0.0",
|
||||||
|
"file-loader": "0.10.0",
|
||||||
|
"friendly-errors-webpack-plugin": "^1.1.3",
|
||||||
|
"function-bind": "1.1.0",
|
||||||
|
"html-webpack-plugin": "2.28.0",
|
||||||
|
"http-proxy-middleware": "0.17.3",
|
||||||
|
"webpack-bundle-analyzer": "2.2.1",
|
||||||
|
"semver": "5.3.0",
|
||||||
|
"opn": "4.0.2",
|
||||||
|
"optimize-css-assets-webpack-plugin": "1.3.0",
|
||||||
|
"ora": "1.1.0",
|
||||||
|
"rimraf": "2.6.0",
|
||||||
|
"url-loader": "0.5.7",
|
||||||
|
"vue-loader": "11.3.4",
|
||||||
|
"vue-style-loader": "2.0.0",
|
||||||
|
"vue-template-compiler": "2.2.6",
|
||||||
|
"webpack": "2.2.1",
|
||||||
|
"webpack-dev-middleware": "1.10.0",
|
||||||
|
"webpack-hot-middleware": "2.16.1",
|
||||||
|
"webpack-merge": "2.6.1",
|
||||||
|
"webpack-dashboard": "0.2.1",
|
||||||
|
"node-sass": "3.7.0",
|
||||||
|
"pushstate-server": "2.1.0",
|
||||||
|
"sass-loader": "4.0.2",
|
||||||
|
"script-loader": "0.7.0",
|
||||||
|
"style-loader": "0.13.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0",
|
||||||
|
"npm": ">= 3.0.0"
|
||||||
|
},
|
||||||
|
"browserlist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 8"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default{
|
||||||
|
name: 'APP'
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,28 @@
|
||||||
|
import fetch, { tpFetch } from 'utils/fetch';
|
||||||
|
|
||||||
|
export function getToken() {
|
||||||
|
return fetch({
|
||||||
|
url: '/qiniu/upload/token',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function upload(data) {
|
||||||
|
return tpFetch({
|
||||||
|
url: 'https://upload.qbox.me',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 外部uri转七牛uri*/
|
||||||
|
export function netUpload(token, net_url) {
|
||||||
|
const imgData = {
|
||||||
|
net_url
|
||||||
|
};
|
||||||
|
return fetch({
|
||||||
|
url: '/qiniu/upload/net/async',
|
||||||
|
method: 'post',
|
||||||
|
data: imgData
|
||||||
|
});
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
Binary file not shown.
After Width: | Height: | Size: 793 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,126 @@
|
||||||
|
;(function(window) {
|
||||||
|
|
||||||
|
var svgSprite = '<svg>' +
|
||||||
|
'' +
|
||||||
|
'<symbol id="icon-zujian" viewBox="0 0 1024 1024">' +
|
||||||
|
'' +
|
||||||
|
'<path d="M568.6 0h454.9v454.9H568.6V0z m0 568.6h454.9v454.9H568.6V568.6zM0 568.6h454.9v454.9H0V568.6zM0 0h454.9v454.9H0V0z" fill="" ></path>' +
|
||||||
|
'' +
|
||||||
|
'</symbol>' +
|
||||||
|
'' +
|
||||||
|
'</svg>'
|
||||||
|
var script = function() {
|
||||||
|
var scripts = document.getElementsByTagName('script')
|
||||||
|
return scripts[scripts.length - 1]
|
||||||
|
}()
|
||||||
|
var shouldInjectCss = script.getAttribute("data-injectcss")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document ready
|
||||||
|
*/
|
||||||
|
var ready = function(fn) {
|
||||||
|
if (document.addEventListener) {
|
||||||
|
if (~["complete", "loaded", "interactive"].indexOf(document.readyState)) {
|
||||||
|
setTimeout(fn, 0)
|
||||||
|
} else {
|
||||||
|
var loadFn = function() {
|
||||||
|
document.removeEventListener("DOMContentLoaded", loadFn, false)
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", loadFn, false)
|
||||||
|
}
|
||||||
|
} else if (document.attachEvent) {
|
||||||
|
IEContentLoaded(window, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
function IEContentLoaded(w, fn) {
|
||||||
|
var d = w.document,
|
||||||
|
done = false,
|
||||||
|
// only fire once
|
||||||
|
init = function() {
|
||||||
|
if (!done) {
|
||||||
|
done = true
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// polling for no errors
|
||||||
|
var polling = function() {
|
||||||
|
try {
|
||||||
|
// throws errors until after ondocumentready
|
||||||
|
d.documentElement.doScroll('left')
|
||||||
|
} catch (e) {
|
||||||
|
setTimeout(polling, 50)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// no errors, fire
|
||||||
|
|
||||||
|
init()
|
||||||
|
};
|
||||||
|
|
||||||
|
polling()
|
||||||
|
// trying to always fire before onload
|
||||||
|
d.onreadystatechange = function() {
|
||||||
|
if (d.readyState == 'complete') {
|
||||||
|
d.onreadystatechange = null
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert el before target
|
||||||
|
*
|
||||||
|
* @param {Element} el
|
||||||
|
* @param {Element} target
|
||||||
|
*/
|
||||||
|
|
||||||
|
var before = function(el, target) {
|
||||||
|
target.parentNode.insertBefore(el, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepend el to target
|
||||||
|
*
|
||||||
|
* @param {Element} el
|
||||||
|
* @param {Element} target
|
||||||
|
*/
|
||||||
|
|
||||||
|
var prepend = function(el, target) {
|
||||||
|
if (target.firstChild) {
|
||||||
|
before(el, target.firstChild)
|
||||||
|
} else {
|
||||||
|
target.appendChild(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendSvg() {
|
||||||
|
var div, svg
|
||||||
|
|
||||||
|
div = document.createElement('div')
|
||||||
|
div.innerHTML = svgSprite
|
||||||
|
svgSprite = null
|
||||||
|
svg = div.getElementsByTagName('svg')[0]
|
||||||
|
if (svg) {
|
||||||
|
svg.setAttribute('aria-hidden', 'true')
|
||||||
|
svg.style.position = 'absolute'
|
||||||
|
svg.style.width = 0
|
||||||
|
svg.style.height = 0
|
||||||
|
svg.style.overflow = 'hidden'
|
||||||
|
prepend(svg, document.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldInjectCss && !window.__iconfont__svg__cssinject__) {
|
||||||
|
window.__iconfont__svg__cssinject__ = true
|
||||||
|
try {
|
||||||
|
document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>");
|
||||||
|
} catch (e) {
|
||||||
|
console && console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(appendSvg)
|
||||||
|
|
||||||
|
|
||||||
|
})(window)
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 引入 ECharts 主模块
|
||||||
|
const echarts = require('echarts/lib/echarts');
|
||||||
|
// 引入柱状图
|
||||||
|
require('echarts/lib/chart/bar');
|
||||||
|
// 引入提示框和标题组件
|
||||||
|
require('echarts/lib/component/tooltip');
|
||||||
|
export default {
|
||||||
|
name: 'barPercent',
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: 'bar-percent-chart'
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'bar-percent-chart'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100px'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '80px'
|
||||||
|
},
|
||||||
|
dataNum: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chart: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
dataNum() {
|
||||||
|
this.setOptions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initBar();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initBar() {
|
||||||
|
this.chart = echarts.init(document.getElementById(this.id));
|
||||||
|
},
|
||||||
|
setOptions() {
|
||||||
|
this.chart.setOption({
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
formatter(params) {
|
||||||
|
return '已完成' + params.value + '篇<br/>目标90篇<br/>完成进度' + Math.round((params.value / 90) * 100) + '%'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
top: 0,
|
||||||
|
containLabel: false
|
||||||
|
},
|
||||||
|
xAxis: [{
|
||||||
|
type: 'category',
|
||||||
|
data: ['文章完成比例']
|
||||||
|
}],
|
||||||
|
yAxis: [{
|
||||||
|
type: 'value',
|
||||||
|
data: [],
|
||||||
|
show: false
|
||||||
|
}],
|
||||||
|
animationDelay: 1000,
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
name: '初诊',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#e5e5e5'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
silent: true,
|
||||||
|
barGap: '-100%', // Make series be overlap
|
||||||
|
data: [150]
|
||||||
|
}, {
|
||||||
|
type: 'bar',
|
||||||
|
name: 'app',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#30b08f'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
z: 10,
|
||||||
|
data: [this.dataNum]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,113 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 引入 ECharts 主模块
|
||||||
|
const echarts = require('echarts/lib/echarts');
|
||||||
|
// 引入柱状图
|
||||||
|
require('echarts/lib/chart/bar');
|
||||||
|
require('echarts/lib/chart/line');
|
||||||
|
// 引入提示框和标题组件
|
||||||
|
require('echarts/lib/component/tooltip');
|
||||||
|
require('echarts/lib/component/title');
|
||||||
|
|
||||||
|
require('echarts/lib/component/visualMap');
|
||||||
|
export default {
|
||||||
|
name: 'barPercent',
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: 'bar-percent-chart'
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'bar-percent-chart'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '200px'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '200px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initBar();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initBar() {
|
||||||
|
this.chart = echarts.init(document.getElementById(this.id));
|
||||||
|
|
||||||
|
const xAxisData = [];
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < 30; i++) {
|
||||||
|
xAxisData.push(i + '号');
|
||||||
|
data.push(Math.round(Math.random() * 2 + 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chart.setOption(
|
||||||
|
{
|
||||||
|
backgroundColor: '#08263a',
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
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: {}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
data,
|
||||||
|
name: '撸文数',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
barBorderRadius: 5,
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowColor: '#111'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animationEasing: 'elasticOut',
|
||||||
|
animationEasingUpdate: 'elasticOut',
|
||||||
|
animationDelay(idx) {
|
||||||
|
return idx * 20;
|
||||||
|
},
|
||||||
|
animationDelayUpdate(idx) {
|
||||||
|
return idx * 20;
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 引入 ECharts 主模块
|
||||||
|
const echarts = require('echarts/lib/echarts');
|
||||||
|
// 引入图
|
||||||
|
require('echarts/lib/chart/line');
|
||||||
|
// 引入提示框和标题组件
|
||||||
|
require('echarts/lib/component/markLine');
|
||||||
|
require('echarts/lib/component/markPoint');
|
||||||
|
require('echarts/lib/component/tooltip');
|
||||||
|
export default {
|
||||||
|
name: 'lineChart',
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: 'line-chart'
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'line-chart'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '280px'
|
||||||
|
},
|
||||||
|
listData: {
|
||||||
|
type: Array,
|
||||||
|
require: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chart: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
listData(dataList) {
|
||||||
|
this.setLine(dataList)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.chart = echarts.init(document.getElementById(this.id));
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setLine(dataList) {
|
||||||
|
const xAxisData = [];
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < dataList.length; i++) {
|
||||||
|
const item = dataList[i]
|
||||||
|
xAxisData.push(item.week.substring(item.week.length - 2) + '周');
|
||||||
|
data.push(item.count)
|
||||||
|
}
|
||||||
|
const markLineData = [];
|
||||||
|
for (let i = 1; i < data.length; i++) {
|
||||||
|
markLineData.push([{
|
||||||
|
xAxis: i - 1,
|
||||||
|
yAxis: data[i - 1],
|
||||||
|
value: data[i] - data[i - 1]
|
||||||
|
}, {
|
||||||
|
xAxis: i,
|
||||||
|
yAxis: data[i]
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
this.chart.setOption({
|
||||||
|
title: {
|
||||||
|
text: 'Awesome Chart'
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 20,
|
||||||
|
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
animationDelay: 1000,
|
||||||
|
xAxis: {
|
||||||
|
data: xAxisData,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
// axisLabel:{
|
||||||
|
// show:false
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
|
||||||
|
yAxis: {
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
// min: 90
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: '撸文数',
|
||||||
|
type: 'line',
|
||||||
|
data,
|
||||||
|
markPoint: {
|
||||||
|
data: [
|
||||||
|
{ type: 'max', name: '最大值' },
|
||||||
|
{ type: 'min', name: '最小值' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#30b08f'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
silent: true,
|
||||||
|
smooth: true,
|
||||||
|
effect: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
animationDuration(idx) {
|
||||||
|
return idx * 100;
|
||||||
|
},
|
||||||
|
animationDelay: 1000,
|
||||||
|
animationEasing: 'quadraticInOut',
|
||||||
|
distance: 1,
|
||||||
|
label: {
|
||||||
|
normal: {
|
||||||
|
position: 'middle'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
symbol: ['none', 'none'],
|
||||||
|
data: markLineData
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,291 @@
|
||||||
|
<template>
|
||||||
|
<div :ref="id" :action="url" class="dropzone" :id="id">
|
||||||
|
<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 {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dropzone: '',
|
||||||
|
initOnce: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
document.removeEventListener('paste', this.pasteImg);
|
||||||
|
this.dropzone.destroy();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
defaultImg(val) {
|
||||||
|
if (val.length === 0) {
|
||||||
|
this.initOnce = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.initOnce) return;
|
||||||
|
this.initImages(val);
|
||||||
|
this.initOnce = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
clickable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
defaultMsg: {
|
||||||
|
type: String,
|
||||||
|
default: '上传图片'
|
||||||
|
},
|
||||||
|
acceptedFiles: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
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: false
|
||||||
|
},
|
||||||
|
couldPaste: {
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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;
|
||||||
|
-webkit-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,43 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
|
||||||
|
<el-button size="small" type="primary">
|
||||||
|
<wscn-icon-svg icon-class="bug" class="meta-item__icon"/>
|
||||||
|
</el-button>
|
||||||
|
</el-badge>
|
||||||
|
<el-dialog title="bug日志" v-model="dialogTableVisible">
|
||||||
|
<el-table :data="logsList">
|
||||||
|
<el-table-column label="message">
|
||||||
|
<template scope="scope">
|
||||||
|
<div>msg:{{ scope.row.err.message }}</div>
|
||||||
|
<br/>
|
||||||
|
<div>url: {{scope.row.url}}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="stack">
|
||||||
|
<template scope="scope">
|
||||||
|
{{ scope.row.err.stack}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'errLog',
|
||||||
|
props: {
|
||||||
|
logsList: {
|
||||||
|
type: Array
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogTableVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<svg @click="toggleClick" class="wscn-icon hamburger" :class="{'is-active':isActive}" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-hamburger"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'hamburger',
|
||||||
|
props: {
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
toggleClick: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.hamburger {
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: .38s;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
.hamburger.is-active {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
function registerAllComponents(requireContext) {
|
||||||
|
return requireContext.keys().forEach(comp => {
|
||||||
|
const vueComp = requireContext(comp)
|
||||||
|
const compName = vueComp.name ? vueComp.name.toLowerCase() : /\/([\w-]+)\.vue$/.exec(comp)[1]
|
||||||
|
Vue.component(compName, vueComp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
registerAllComponents(require.context('./', false, /\.vue$/))
|
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div class="icon-container" :style="containerStyle">
|
||||||
|
<slot class="icon"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'wscn-icon-stack',
|
||||||
|
props: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 20
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
type: String,
|
||||||
|
default: 'circle',
|
||||||
|
validator: val => {
|
||||||
|
const validShapes = ['circle', 'square']
|
||||||
|
return validShapes.indexOf(val) > -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
containerStyle() {
|
||||||
|
return {
|
||||||
|
width: `${this.width}px`,
|
||||||
|
height: `${this.width}px`,
|
||||||
|
fontSize: `${this.width * 0.6}px`,
|
||||||
|
borderRadius: `${this.shape === 'circle' && '50%'}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.icon-container {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #1482F0;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
color: #ffffff;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<svg class="wscn-icon" aria-hidden="true">
|
||||||
|
<use :xlink:href="iconName"></use>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'wscn-icon-svg',
|
||||||
|
props: {
|
||||||
|
iconClass: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iconName() {
|
||||||
|
return `#icon-${this.iconClass}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,676 @@
|
||||||
|
<template>
|
||||||
|
<div class="vue-image-crop-upload" v-show="show">
|
||||||
|
<div class="vicp-wrap">
|
||||||
|
<div class="vicp-close" @click="off">
|
||||||
|
<i class="vicp-icon4"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vicp-step1" v-show="step == 1">
|
||||||
|
<div class="vicp-drop-area"
|
||||||
|
@dragleave="preventDefault"
|
||||||
|
@dragover="preventDefault"
|
||||||
|
@dragenter="preventDefault"
|
||||||
|
@click="handleClick"
|
||||||
|
@drop="handleChange">
|
||||||
|
<i class="vicp-icon1" v-show="loading != 1">
|
||||||
|
<i class="vicp-icon1-arrow"></i>
|
||||||
|
<i class="vicp-icon1-body"></i>
|
||||||
|
<i class="vicp-icon1-bottom"></i>
|
||||||
|
</i>
|
||||||
|
<span class="vicp-hint" v-show="loading !== 1">{{ lang.hint }}</span>
|
||||||
|
<span class="vicp-no-supported-hint" v-show="!isSupported">{{ lang.noSupported }}</span>
|
||||||
|
<input type="file" v-show="false" @change="handleChange" ref="fileinput">
|
||||||
|
</div>
|
||||||
|
<div class="vicp-error" v-show="hasError">
|
||||||
|
<i class="vicp-icon2"></i> {{ errorMsg }}
|
||||||
|
</div>
|
||||||
|
<div class="vicp-operate">
|
||||||
|
<a @click="off" @mousedown="ripple">{{ lang.btn.off }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vicp-step2" v-if="step == 2">
|
||||||
|
<div class="vicp-crop">
|
||||||
|
<div class="vicp-crop-left" v-show="true">
|
||||||
|
<div class="vicp-img-container">
|
||||||
|
<img :src="sourceImgUrl"
|
||||||
|
:style="sourceImgStyle"
|
||||||
|
class="vicp-img"
|
||||||
|
draggable="false"
|
||||||
|
@drag="preventDefault"
|
||||||
|
@dragstart="preventDefault"
|
||||||
|
@dragend="preventDefault"
|
||||||
|
@dragleave="preventDefault"
|
||||||
|
@dragover="preventDefault"
|
||||||
|
@dragenter="preventDefault"
|
||||||
|
@drop="preventDefault"
|
||||||
|
@mousedown="imgStartMove"
|
||||||
|
@mousemove="imgMove"
|
||||||
|
@mouseup="createImg"
|
||||||
|
@mouseout="createImg"
|
||||||
|
ref="img">
|
||||||
|
<div class="vicp-img-shade vicp-img-shade-1" :style="sourceImgShadeStyle"></div>
|
||||||
|
<div class="vicp-img-shade vicp-img-shade-2" :style="sourceImgShadeStyle"></div>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-range">
|
||||||
|
<input type="range" :value="scale.range" step="1" min="0" max="100" @change="zoomChange">
|
||||||
|
<i @mousedown="startZoomSub" @mouseout="endZoomSub" @mouseup="endZoomSub"
|
||||||
|
class="vicp-icon5"></i>
|
||||||
|
<i @mousedown="startZoomAdd" @mouseout="endZoomAdd" @mouseup="endZoomAdd"
|
||||||
|
class="vicp-icon6"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-crop-right" v-show="true">
|
||||||
|
<div class="vicp-preview">
|
||||||
|
<div class="vicp-preview-item">
|
||||||
|
<img :src="createImgUrl" :style="previewStyle">
|
||||||
|
<span>{{ lang.preview }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-preview-item">
|
||||||
|
<img :src="createImgUrl" :style="previewStyle" v-if="!noCircle">
|
||||||
|
<span>{{ lang.preview }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-operate">
|
||||||
|
<a @click="setStep(1)" @mousedown="ripple">{{ lang.btn.back }}</a>
|
||||||
|
<a class="vicp-operate-btn" @click="upload" @mousedown="ripple">{{ lang.btn.save }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vicp-step3" v-if="step == 3">
|
||||||
|
<div class="vicp-upload">
|
||||||
|
<span class="vicp-loading" v-show="loading === 1">{{ lang.loading }}</span>
|
||||||
|
<div class="vicp-progress-wrap">
|
||||||
|
<span class="vicp-progress" v-show="loading === 1" :style="progressStyle"></span>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-error" v-show="hasError">
|
||||||
|
<i class="vicp-icon2"></i> {{ errorMsg }}
|
||||||
|
</div>
|
||||||
|
<div class="vicp-success" v-show="loading === 2">
|
||||||
|
<i class="vicp-icon3"></i> {{ lang.success }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="vicp-operate">
|
||||||
|
<a @click="setStep(2)" @mousedown="ripple">{{ lang.btn.back }}</a>
|
||||||
|
<a @click="off" @mousedown="ripple">{{ lang.btn.close }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<canvas v-show="false" :width="width" :height="height" ref="canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* eslint-disable */
|
||||||
|
import {getToken, upload} from 'api/qiniu';
|
||||||
|
import {effectRipple, data2blob} from './utils';
|
||||||
|
import langBag from './lang';
|
||||||
|
const mimes = {
|
||||||
|
'jpg': 'image/jpeg',
|
||||||
|
'png': 'image/png',
|
||||||
|
'gif': 'image/gif',
|
||||||
|
'svg': 'image/svg+xml',
|
||||||
|
'psd': 'image/photoshop'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
// 域,上传文件name,触发事件会带上(如果一个页面多个图片上传控件,可以做区分
|
||||||
|
field: {
|
||||||
|
type: String,
|
||||||
|
default: 'avatar'
|
||||||
|
},
|
||||||
|
// 上传地址
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 其他要上传文件附带的数据,对象格式
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// 剪裁图片的宽
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
// 剪裁图片的高
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 200
|
||||||
|
},
|
||||||
|
// 不预览圆形图片
|
||||||
|
noCircle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 单文件大小限制
|
||||||
|
maxSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10240
|
||||||
|
},
|
||||||
|
// 语言类型
|
||||||
|
langType: {
|
||||||
|
type: String,
|
||||||
|
'default': 'zh'
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
langType,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = that,
|
||||||
|
isSupported = true,
|
||||||
|
lang = langBag[langType] ? langBag[langType] : lang['zh'];
|
||||||
|
|
||||||
|
if (typeof FormData != 'function') {
|
||||||
|
isSupported = false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
show: true,
|
||||||
|
// 图片的mime
|
||||||
|
mime:mimes['jpg'],
|
||||||
|
// 语言包
|
||||||
|
lang,
|
||||||
|
// 浏览器是否支持该控件
|
||||||
|
isSupported,
|
||||||
|
// 步骤
|
||||||
|
step: 1, //1选择文件 2剪裁 3上传
|
||||||
|
// 上传状态及进度
|
||||||
|
loading: 0, //0未开始 1正在 2成功 3错误
|
||||||
|
progress: 0,
|
||||||
|
// 是否有错误及错误信息
|
||||||
|
hasError: false,
|
||||||
|
errorMsg: '',
|
||||||
|
// 需求图宽高比
|
||||||
|
ratio: width / height,
|
||||||
|
// 原图地址、生成图片地址
|
||||||
|
sourceImg: null,
|
||||||
|
sourceImgUrl: '',
|
||||||
|
createImgUrl: '',
|
||||||
|
// 原图片拖动事件初始值
|
||||||
|
sourceImgMouseDown: {
|
||||||
|
on: false,
|
||||||
|
mX: 0, //鼠标按下的坐标
|
||||||
|
mY: 0,
|
||||||
|
x: 0, //scale原图坐标
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
// 生成图片预览的容器大小
|
||||||
|
previewContainer: {
|
||||||
|
width: 100,
|
||||||
|
height: 100
|
||||||
|
},
|
||||||
|
// 原图容器宽高
|
||||||
|
sourceImgContainer: { // sic
|
||||||
|
width: 240,
|
||||||
|
height: 180
|
||||||
|
},
|
||||||
|
// 原图展示属性
|
||||||
|
scale: {
|
||||||
|
zoomAddOn: false, //按钮缩放事件开启
|
||||||
|
zoomSubOn: false, //按钮缩放事件开启
|
||||||
|
range: 1, //最大100
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
maxWidth: 0,
|
||||||
|
maxHeight: 0,
|
||||||
|
minWidth: 0, //最宽
|
||||||
|
minHeight: 0,
|
||||||
|
naturalWidth: 0, //原宽
|
||||||
|
naturalHeight: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 进度条样式
|
||||||
|
progressStyle() {
|
||||||
|
let {
|
||||||
|
progress
|
||||||
|
} = this;
|
||||||
|
return {
|
||||||
|
width: progress + '%'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 原图样式
|
||||||
|
sourceImgStyle() {
|
||||||
|
let {
|
||||||
|
scale,
|
||||||
|
sourceImgMasking
|
||||||
|
} = this;
|
||||||
|
return {
|
||||||
|
top: scale.y + sourceImgMasking.y + 'px',
|
||||||
|
left: scale.x + sourceImgMasking.x + 'px',
|
||||||
|
width: scale.width + 'px',
|
||||||
|
height: scale.height + 'px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 原图蒙版属性
|
||||||
|
sourceImgMasking() {
|
||||||
|
let {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
ratio,
|
||||||
|
sourceImgContainer
|
||||||
|
} = this,
|
||||||
|
sic = sourceImgContainer,
|
||||||
|
sicRatio = sic.width / sic.height, // 原图容器宽高比
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
w = sic.width,
|
||||||
|
h = sic.height,
|
||||||
|
scale = 1;
|
||||||
|
if (ratio < sicRatio) {
|
||||||
|
scale = sic.height / height;
|
||||||
|
w = sic.height * ratio;
|
||||||
|
x = (sic.width - w) / 2;
|
||||||
|
}
|
||||||
|
if (ratio > sicRatio) {
|
||||||
|
scale = sic.width / width;
|
||||||
|
h = sic.width / ratio;
|
||||||
|
y = (sic.height - h) / 2;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
scale, // 蒙版相对需求宽高的缩放
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width: w,
|
||||||
|
height: h
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 原图遮罩样式
|
||||||
|
sourceImgShadeStyle() {
|
||||||
|
let sic = this.sourceImgContainer,
|
||||||
|
sim = this.sourceImgMasking,
|
||||||
|
w = sim.width == sic.width ? sim.width : (sic.width - sim.width) / 2,
|
||||||
|
h = sim.height == sic.height ? sim.height : (sic.height - sim.height) / 2;
|
||||||
|
return {
|
||||||
|
width: w + 'px',
|
||||||
|
height: h + 'px'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
previewStyle() {
|
||||||
|
let {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
ratio,
|
||||||
|
previewContainer
|
||||||
|
} = this,
|
||||||
|
pc = previewContainer,
|
||||||
|
w = pc.width,
|
||||||
|
h = pc.height,
|
||||||
|
pcRatio = w / h;
|
||||||
|
if (ratio < pcRatio) {
|
||||||
|
w = pc.height * ratio;
|
||||||
|
}
|
||||||
|
if (ratio > pcRatio) {
|
||||||
|
h = pc.width / ratio;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
width: w + 'px',
|
||||||
|
height: h + 'px'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 点击波纹效果
|
||||||
|
ripple(e) {
|
||||||
|
effectRipple(e);
|
||||||
|
},
|
||||||
|
// 关闭控件
|
||||||
|
off() {
|
||||||
|
this.show = false;
|
||||||
|
},
|
||||||
|
// 设置步骤
|
||||||
|
setStep(step) {
|
||||||
|
let that = this;
|
||||||
|
setTimeout(function () {
|
||||||
|
that.step = step;
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
/* 图片选择区域函数绑定
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
preventDefault(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
handleClick(e) {
|
||||||
|
if (this.loading !== 1) {
|
||||||
|
if (e.target !== this.$refs.fileinput) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (document.activeElement !== this.$refs) {
|
||||||
|
this.$refs.fileinput.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleChange(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.loading !== 1) {
|
||||||
|
let files = e.target.files || e.dataTransfer.files;
|
||||||
|
this.reset();
|
||||||
|
if (this.checkFile(files[0])) {
|
||||||
|
this.setSourceImg(files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* ---------------------------------------------------------------*/
|
||||||
|
// 检测选择的文件是否合适
|
||||||
|
checkFile(file) {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
lang,
|
||||||
|
maxSize
|
||||||
|
} = that;
|
||||||
|
// 仅限图片
|
||||||
|
if (file.type.indexOf('image') === -1) {
|
||||||
|
that.hasError = true;
|
||||||
|
that.errorMsg = lang.error.onlyImg;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.mime=file.type;
|
||||||
|
// 超出大小
|
||||||
|
if (file.size / 1024 > maxSize) {
|
||||||
|
that.hasError = true;
|
||||||
|
that.errorMsg = lang.error.outOfSize + maxSize + 'kb';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 重置控件
|
||||||
|
reset() {
|
||||||
|
let that = this;
|
||||||
|
that.step = 1;
|
||||||
|
that.loading = 0;
|
||||||
|
that.hasError = false;
|
||||||
|
that.errorMsg = '';
|
||||||
|
that.progress = 0;
|
||||||
|
},
|
||||||
|
// 设置图片源
|
||||||
|
setSourceImg(file) {
|
||||||
|
let that = this,
|
||||||
|
fr = new FileReader();
|
||||||
|
fr.onload = function (e) {
|
||||||
|
that.sourceImgUrl = fr.result;
|
||||||
|
that.startCrop();
|
||||||
|
};
|
||||||
|
fr.readAsDataURL(file);
|
||||||
|
},
|
||||||
|
// 剪裁前准备工作
|
||||||
|
startCrop() {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
ratio,
|
||||||
|
scale,
|
||||||
|
sourceImgUrl,
|
||||||
|
sourceImgMasking,
|
||||||
|
lang
|
||||||
|
} = that,
|
||||||
|
sim = sourceImgMasking,
|
||||||
|
img = new Image();
|
||||||
|
img.src = sourceImgUrl;
|
||||||
|
img.onload = function () {
|
||||||
|
let nWidth = img.naturalWidth,
|
||||||
|
nHeight = img.naturalHeight,
|
||||||
|
nRatio = nWidth / nHeight,
|
||||||
|
w = sim.width,
|
||||||
|
h = sim.height,
|
||||||
|
x = 0,
|
||||||
|
y = 0;
|
||||||
|
// 图片像素不达标
|
||||||
|
// if (nWidth < width || nHeight < height) {
|
||||||
|
// that.hasError = true;
|
||||||
|
// that.errorMsg = lang.error.lowestPx + width + '*' + height;
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
if (ratio > nRatio) {
|
||||||
|
h = w / nRatio;
|
||||||
|
y = (sim.height - h) / 2;
|
||||||
|
}
|
||||||
|
if (ratio < nRatio) {
|
||||||
|
w = h * nRatio;
|
||||||
|
x = (sim.width - w) / 2;
|
||||||
|
}
|
||||||
|
scale.range = 0;
|
||||||
|
scale.x = x;
|
||||||
|
scale.y = y;
|
||||||
|
scale.width = w;
|
||||||
|
scale.height = h;
|
||||||
|
scale.minWidth = w;
|
||||||
|
scale.minHeight = h;
|
||||||
|
scale.maxWidth = nWidth * sim.scale;
|
||||||
|
scale.maxHeight = nHeight * sim.scale;
|
||||||
|
scale.naturalWidth = nWidth;
|
||||||
|
scale.naturalHeight = nHeight;
|
||||||
|
that.sourceImg = img;
|
||||||
|
that.createImg();
|
||||||
|
that.setStep(2);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 鼠标按下图片准备移动
|
||||||
|
imgStartMove(e) {
|
||||||
|
let {
|
||||||
|
sourceImgMouseDown,
|
||||||
|
scale
|
||||||
|
} = this,
|
||||||
|
simd = sourceImgMouseDown;
|
||||||
|
simd.mX = e.screenX;
|
||||||
|
simd.mY = e.screenY;
|
||||||
|
simd.x = scale.x;
|
||||||
|
simd.y = scale.y;
|
||||||
|
simd.on = true;
|
||||||
|
},
|
||||||
|
// 鼠标按下状态下移动,图片移动
|
||||||
|
imgMove(e) {
|
||||||
|
let {
|
||||||
|
sourceImgMouseDown: {
|
||||||
|
on,
|
||||||
|
mX,
|
||||||
|
mY,
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
},
|
||||||
|
scale,
|
||||||
|
sourceImgMasking
|
||||||
|
} = this,
|
||||||
|
sim = sourceImgMasking,
|
||||||
|
nX = e.screenX,
|
||||||
|
nY = e.screenY,
|
||||||
|
dX = nX - mX,
|
||||||
|
dY = nY - mY,
|
||||||
|
rX = x + dX,
|
||||||
|
rY = y + dY;
|
||||||
|
if (!on) return;
|
||||||
|
if (rX > 0) {
|
||||||
|
rX = 0;
|
||||||
|
}
|
||||||
|
if (rY > 0) {
|
||||||
|
rY = 0;
|
||||||
|
}
|
||||||
|
if (rX < sim.width - scale.width) {
|
||||||
|
rX = sim.width - scale.width;
|
||||||
|
}
|
||||||
|
if (rY < sim.height - scale.height) {
|
||||||
|
rY = sim.height - scale.height;
|
||||||
|
}
|
||||||
|
scale.x = rX;
|
||||||
|
scale.y = rY;
|
||||||
|
},
|
||||||
|
// 按钮按下开始放大
|
||||||
|
startZoomAdd(e) {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
scale
|
||||||
|
} = that;
|
||||||
|
scale.zoomAddOn = true;
|
||||||
|
function zoom() {
|
||||||
|
if (scale.zoomAddOn) {
|
||||||
|
let range = scale.range >= 100 ? 100 : ++scale.range;
|
||||||
|
that.zoomImg(range);
|
||||||
|
setTimeout(function () {
|
||||||
|
zoom();
|
||||||
|
}, 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zoom();
|
||||||
|
},
|
||||||
|
// 按钮松开或移开取消放大
|
||||||
|
endZoomAdd(e) {
|
||||||
|
this.scale.zoomAddOn = false;
|
||||||
|
},
|
||||||
|
// 按钮按下开始缩小
|
||||||
|
startZoomSub(e) {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
scale
|
||||||
|
} = that;
|
||||||
|
scale.zoomSubOn = true;
|
||||||
|
function zoom() {
|
||||||
|
if (scale.zoomSubOn) {
|
||||||
|
let range = scale.range <= 0 ? 0 : --scale.range;
|
||||||
|
that.zoomImg(range);
|
||||||
|
setTimeout(function () {
|
||||||
|
zoom();
|
||||||
|
}, 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zoom();
|
||||||
|
},
|
||||||
|
// 按钮松开或移开取消缩小
|
||||||
|
endZoomSub(e) {
|
||||||
|
let {
|
||||||
|
scale
|
||||||
|
} = this;
|
||||||
|
scale.zoomSubOn = false;
|
||||||
|
},
|
||||||
|
zoomChange(e) {
|
||||||
|
this.zoomImg(e.target.value);
|
||||||
|
},
|
||||||
|
// 缩放原图
|
||||||
|
zoomImg(newRange) {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
sourceImgMasking,
|
||||||
|
sourceImgMouseDown,
|
||||||
|
scale
|
||||||
|
} = this,
|
||||||
|
{
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
minWidth,
|
||||||
|
minHeight,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
range
|
||||||
|
} = scale,
|
||||||
|
sim = sourceImgMasking,
|
||||||
|
// 蒙版宽高
|
||||||
|
sWidth = sim.width,
|
||||||
|
sHeight = sim.height,
|
||||||
|
// 新宽高
|
||||||
|
nWidth = minWidth + (maxWidth - minWidth) * newRange / 100,
|
||||||
|
nHeight = minHeight + (maxHeight - minHeight) * newRange / 100,
|
||||||
|
// 新坐标(根据蒙版中心点缩放)
|
||||||
|
nX = sWidth / 2 - (nWidth / width) * (sWidth / 2 - x),
|
||||||
|
nY = sHeight / 2 - (nHeight / height) * (sHeight / 2 - y);
|
||||||
|
// 判断新坐标是否超过蒙版限制
|
||||||
|
if (nX > 0) {
|
||||||
|
nX = 0;
|
||||||
|
}
|
||||||
|
if (nY > 0) {
|
||||||
|
nY = 0;
|
||||||
|
}
|
||||||
|
if (nX < sWidth - nWidth) {
|
||||||
|
nX = sWidth - nWidth;
|
||||||
|
}
|
||||||
|
if (nY < sHeight - nHeight) {
|
||||||
|
nY = sHeight - nHeight;
|
||||||
|
}
|
||||||
|
// 赋值处理
|
||||||
|
scale.x = nX;
|
||||||
|
scale.y = nY;
|
||||||
|
scale.width = nWidth;
|
||||||
|
scale.height = nHeight;
|
||||||
|
scale.range = newRange;
|
||||||
|
setTimeout(function () {
|
||||||
|
if (scale.range == newRange) {
|
||||||
|
that.createImg();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
},
|
||||||
|
// 生成需求图片
|
||||||
|
createImg(e) {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
mime,
|
||||||
|
sourceImg,
|
||||||
|
scale: {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
},
|
||||||
|
sourceImgMasking: {
|
||||||
|
scale
|
||||||
|
}
|
||||||
|
} = that,
|
||||||
|
canvas = that.$refs.canvas,
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
if (e) {
|
||||||
|
// 取消鼠标按下移动状态
|
||||||
|
that.sourceImgMouseDown.on = false;
|
||||||
|
}
|
||||||
|
ctx.drawImage(sourceImg, x / scale, y / scale, width / scale, height / scale);
|
||||||
|
that.createImgUrl = canvas.toDataURL(mime);
|
||||||
|
},
|
||||||
|
// 上传图片
|
||||||
|
upload() {
|
||||||
|
let that = this,
|
||||||
|
{
|
||||||
|
lang,
|
||||||
|
mime,
|
||||||
|
createImgUrl
|
||||||
|
} = this,
|
||||||
|
formData = new FormData();
|
||||||
|
// 上传文件
|
||||||
|
that.loading = 1;
|
||||||
|
that.progress = 0;
|
||||||
|
that.setStep(3);
|
||||||
|
formData.append('file', data2blob(createImgUrl, mime));
|
||||||
|
const token = this.$store.getters.token;
|
||||||
|
getToken(token).then((response)=> {
|
||||||
|
const url = response.data.qiniu_url;
|
||||||
|
formData.append('token', response.data.qiniu_token);
|
||||||
|
formData.append('key', response.data.qiniu_key);
|
||||||
|
upload(formData).then((response)=> {
|
||||||
|
that.loading = 2;
|
||||||
|
that.$emit('crop-upload-success', url);
|
||||||
|
}).catch(err => {
|
||||||
|
that.loading = 3;
|
||||||
|
that.hasError = true;
|
||||||
|
that.errorMsg = lang.fail;
|
||||||
|
that.$emit('crop-upload-fail');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@import "./upload.css";
|
||||||
|
</style>
|
|
@ -0,0 +1,41 @@
|
||||||
|
const langBag = {
|
||||||
|
zh: {
|
||||||
|
hint: '点击,或拖动图片至此处',
|
||||||
|
loading: '正在上传……',
|
||||||
|
noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!',
|
||||||
|
success: '上传成功',
|
||||||
|
fail: '图片上传失败',
|
||||||
|
preview: '头像预览',
|
||||||
|
btn: {
|
||||||
|
off: '取消',
|
||||||
|
close: '关闭',
|
||||||
|
back: '上一步',
|
||||||
|
save: '保存'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
onlyImg: '仅限图片格式',
|
||||||
|
outOfSize: '单文件大小不能超过 ',
|
||||||
|
lowestPx: '图片最低像素为(宽*高):'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
hint: 'Click, or drag the file here',
|
||||||
|
loading: 'Uploading……',
|
||||||
|
noSupported: 'Browser does not support, 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: 'The lowest pixel in the image: '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default langBag;
|
|
@ -0,0 +1,691 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
@-webkit-keyframes vicp_progress {
|
||||||
|
0% {
|
||||||
|
background-position-y: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position-y: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes vicp_progress {
|
||||||
|
0% {
|
||||||
|
background-position-y: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position-y: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes vicp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: scale(0) translatey(-60px);
|
||||||
|
transform: scale(0) translatey(-60px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale(1) translatey(0);
|
||||||
|
transform: scale(1) translatey(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes vicp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: scale(0) translatey(-60px);
|
||||||
|
transform: scale(0) translatey(-60px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale(1) translatey(0);
|
||||||
|
transform: scale(1) translatey(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload {
|
||||||
|
position: fixed;
|
||||||
|
display: block;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 10000;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.65);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
-moz-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap {
|
||||||
|
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
position: fixed;
|
||||||
|
display: block;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 10000;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 600px;
|
||||||
|
height: 330px;
|
||||||
|
padding: 25px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
-webkit-animation: vicp 0.12s ease-in;
|
||||||
|
animation: vicp 0.12s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-close {
|
||||||
|
position: absolute;
|
||||||
|
right: -30px;
|
||||||
|
top: -30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-transition: -webkit-transform 0.18s;
|
||||||
|
transition: -webkit-transform 0.18s;
|
||||||
|
transition: transform 0.18s;
|
||||||
|
transition: transform 0.18s, -webkit-transform 0.18s;
|
||||||
|
-webkit-transform: rotate(0);
|
||||||
|
-ms-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after, .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before {
|
||||||
|
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 4px;
|
||||||
|
width: 20px;
|
||||||
|
height: 3px;
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after {
|
||||||
|
-webkit-transform: rotate(-45deg);
|
||||||
|
-ms-transform: rotate(-45deg);
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
-ms-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area {
|
||||||
|
position: relative;
|
||||||
|
padding: 35px;
|
||||||
|
height: 200px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
text-align: center;
|
||||||
|
border: 1px dashed rgba(0, 0, 0, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto 6px;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-arrow {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-bottom: 14.7px solid rgba(0, 0, 0, 0.3);
|
||||||
|
border-left: 14.7px solid transparent;
|
||||||
|
border-right: 14.7px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-body {
|
||||||
|
display: block;
|
||||||
|
width: 12.6px;
|
||||||
|
height: 14.7px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-bottom {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
height: 12.6px;
|
||||||
|
border: 6px solid rgba(0, 0, 0, 0.3);
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint {
|
||||||
|
display: block;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-no-supported-hint {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 30px;
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 30px;
|
||||||
|
background-color: #eee;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
border-color: rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 240px;
|
||||||
|
height: 180px;
|
||||||
|
background-color: #e5e5e0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
cursor: move;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade {
|
||||||
|
-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(241, 242, 243, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-1 {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-2 {
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range {
|
||||||
|
position: relative;
|
||||||
|
margin: 30px 0;
|
||||||
|
width: 240px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5,
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5:hover,
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6:hover {
|
||||||
|
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(0, 0, 0, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5 {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5::before {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
left: 3px;
|
||||||
|
top: 8px;
|
||||||
|
width: 12px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::before {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
left: 3px;
|
||||||
|
top: 8px;
|
||||||
|
width: 12px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
top: 3px;
|
||||||
|
left: 8px;
|
||||||
|
width: 2px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range] {
|
||||||
|
display: block;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 180px;
|
||||||
|
height: 8px;
|
||||||
|
vertical-align: top;
|
||||||
|
background: transparent;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
cursor: pointer;
|
||||||
|
/* 滑块
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
/* 轨道
|
||||||
|
---------------------------------------------------------------*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-thumb {
|
||||||
|
-webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
margin-top: -3px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #61c091;
|
||||||
|
border-radius: 100%;
|
||||||
|
border: none;
|
||||||
|
-webkit-transition: 0.2s;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-thumb {
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #61c091;
|
||||||
|
border-radius: 100%;
|
||||||
|
border: none;
|
||||||
|
-webkit-transition: 0.2s;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-thumb {
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
|
||||||
|
appearance: none;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #61c091;
|
||||||
|
border: none;
|
||||||
|
border-radius: 100%;
|
||||||
|
-webkit-transition: 0.2s;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-moz-range-thumb {
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-ms-thumb {
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-webkit-slider-thumb {
|
||||||
|
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
|
||||||
|
margin-top: -4px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-runnable-track {
|
||||||
|
-webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(68, 170, 119, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-track {
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(68, 170, 119, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-track {
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: transparent;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-lower {
|
||||||
|
background-color: rgba(68, 170, 119, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-upper {
|
||||||
|
background-color: rgba(68, 170, 119, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-webkit-slider-runnable-track {
|
||||||
|
background-color: rgba(68, 170, 119, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-moz-range-track {
|
||||||
|
background-color: rgba(68, 170, 119, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-lower {
|
||||||
|
background-color: rgba(68, 170, 119, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-upper {
|
||||||
|
background-color: rgba(68, 170, 119, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview {
|
||||||
|
height: 150px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item {
|
||||||
|
position: relative;
|
||||||
|
padding: 5px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item span {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #bbb;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item img {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child img {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload {
|
||||||
|
position: relative;
|
||||||
|
padding: 35px;
|
||||||
|
height: 200px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
text-align: center;
|
||||||
|
border: 1px dashed #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading {
|
||||||
|
display: block;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #999;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap {
|
||||||
|
margin-top: 12px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #4a7;
|
||||||
|
-webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
|
||||||
|
-webkit-transition: width 0.15s linear;
|
||||||
|
transition: width 0.15s linear;
|
||||||
|
background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);
|
||||||
|
background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent);
|
||||||
|
background-size: 40px 40px;
|
||||||
|
-webkit-animation: vicp_progress 0.5s linear infinite;
|
||||||
|
animation: vicp_progress 0.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: -3px;
|
||||||
|
right: -3px;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
border: 1px solid rgba(245, 246, 247, 0.7);
|
||||||
|
-webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
|
||||||
|
box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: #4a7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error,
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success {
|
||||||
|
height: 100px;
|
||||||
|
line-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-operate {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-operate a {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 100px;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #4a7;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-operate a:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-error,
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-success {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: #d10;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-success {
|
||||||
|
color: #4a7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-icon3 {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-icon3::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 6px;
|
||||||
|
width: 6px;
|
||||||
|
height: 10px;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
border-color: #4a7;
|
||||||
|
border-style: solid;
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-icon2 {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after, .vue-image-crop-upload .vicp-wrap .vicp-icon2::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
left: 4px;
|
||||||
|
width: 13px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #d10;
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-image-crop-upload .vicp-wrap .vicp-icon2::after {
|
||||||
|
-webkit-transform: rotate(-45deg);
|
||||||
|
-ms-transform: rotate(-45deg);
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.e-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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.e-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,58 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* @param arg_opts
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function effectRipple(e, arg_opts) {
|
||||||
|
let opts = Object.assign({
|
||||||
|
ele: e.target, // 波纹作用元素
|
||||||
|
type: 'hit', // hit点击位置扩散 center中心点扩展
|
||||||
|
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
|
||||||
|
}, arg_opts),
|
||||||
|
target = opts.ele;
|
||||||
|
if (target) {
|
||||||
|
let rect = target.getBoundingClientRect(),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// database64文件格式转换为2进制
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param mime
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function data2blob(data, mime) {
|
||||||
|
// dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
|
||||||
|
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,407 @@
|
||||||
|
<template>
|
||||||
|
<div class="material-input__component" :class="computedClasses">
|
||||||
|
<input
|
||||||
|
v-if="type === 'email'"
|
||||||
|
type="email"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="type === 'url'"
|
||||||
|
type="url"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="type === 'number'"
|
||||||
|
type="number"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:max="max"
|
||||||
|
:min="min"
|
||||||
|
:minlength="minlength"
|
||||||
|
:maxlength="maxlength"
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="type === 'password'"
|
||||||
|
type="password"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:max="max"
|
||||||
|
:min="min"
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="type === 'tel'"
|
||||||
|
type="tel"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="type === 'text'"
|
||||||
|
type="text"
|
||||||
|
class="material-input"
|
||||||
|
:name="name"
|
||||||
|
:id="id"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="valueCopy"
|
||||||
|
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
|
||||||
|
:minlength="minlength"
|
||||||
|
:maxlength="maxlength"
|
||||||
|
:required="required"
|
||||||
|
|
||||||
|
@focus="handleFocus(true)"
|
||||||
|
@blur="handleFocus(false)"
|
||||||
|
@input="handleModelInput"
|
||||||
|
>
|
||||||
|
|
||||||
|
<span class="material-input-bar"></span>
|
||||||
|
|
||||||
|
<label class="material-label">
|
||||||
|
<slot></slot>
|
||||||
|
</label>
|
||||||
|
<div v-if="errorMessages" class="material-errors">
|
||||||
|
<div v-for="error in computedErrors" class="material-error">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'material-input',
|
||||||
|
computed: {
|
||||||
|
computedErrors() {
|
||||||
|
return typeof this.errorMessages === 'string'
|
||||||
|
? [this.errorMessages] : this.errorMessages
|
||||||
|
},
|
||||||
|
computedClasses() {
|
||||||
|
return {
|
||||||
|
'material--active': this.focus,
|
||||||
|
'material--disabled': this.disabled,
|
||||||
|
'material--has-errors': Boolean(
|
||||||
|
!this.valid ||
|
||||||
|
(this.errorMessages && this.errorMessages.length)),
|
||||||
|
'material--raised': Boolean(
|
||||||
|
this.focus ||
|
||||||
|
this.valueCopy || // has value
|
||||||
|
(this.placeholder && !this.valueCopy)) // has placeholder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
valueCopy: null,
|
||||||
|
focus: false,
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
// Here we are following the Vue2 convention on custom v-model:
|
||||||
|
// https://github.com/vuejs/vue/issues/2873#issuecomment-223759341
|
||||||
|
this.copyValue(this.value)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleModelInput(event) {
|
||||||
|
this.$emit('input', event.target.value, event)
|
||||||
|
this.handleValidation()
|
||||||
|
},
|
||||||
|
handleFocus(focused) {
|
||||||
|
this.focus = focused
|
||||||
|
},
|
||||||
|
handleValidation() {
|
||||||
|
this.valid = this.$el ? this.$el.querySelector(
|
||||||
|
'.material-input').validity.valid : this.valid
|
||||||
|
},
|
||||||
|
copyValue(value) {
|
||||||
|
this.valueCopy = value
|
||||||
|
this.handleValidation()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(newValue) {
|
||||||
|
this.copyValue(newValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
readonly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
minlength: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
maxlength: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
autocomplete: {
|
||||||
|
type: String,
|
||||||
|
default: 'off'
|
||||||
|
},
|
||||||
|
errorMessages: {
|
||||||
|
type: [Array, String],
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
// Fonts:
|
||||||
|
$font-size-base: 16px;
|
||||||
|
$font-size-small: 18px;
|
||||||
|
$font-size-smallest: 12px;
|
||||||
|
$font-weight-normal: normal;
|
||||||
|
// Utils
|
||||||
|
$spacer: 12px;
|
||||||
|
$transition: 0.2s ease all;
|
||||||
|
// Base clases:
|
||||||
|
%base-bar-pseudo {
|
||||||
|
content: '';
|
||||||
|
height: 1px;
|
||||||
|
width: 0;
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
transition: $transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixins:
|
||||||
|
@mixin slided-top() {
|
||||||
|
top: -2 * $spacer;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component:
|
||||||
|
.material-input__component {
|
||||||
|
/*margin-top: 30px;*/
|
||||||
|
position: relative;
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.material-input {
|
||||||
|
font-size: $font-size-base;
|
||||||
|
padding: $spacer $spacer $spacer $spacer / 2;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid transparent; // fixes the height issue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.material-label {
|
||||||
|
font-size: $font-size-base;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
left: 0;
|
||||||
|
top: $spacer;
|
||||||
|
transition: $transition;
|
||||||
|
}
|
||||||
|
.material-input-bar {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
&:before {
|
||||||
|
@extend %base-bar-pseudo;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
@extend %base-bar-pseudo;
|
||||||
|
right: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Disabled state:
|
||||||
|
&.material--disabled {
|
||||||
|
.material-input {
|
||||||
|
border-bottom-style: dashed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Raised state:
|
||||||
|
&.material--raised {
|
||||||
|
.material-label {
|
||||||
|
@include slided-top();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Active state:
|
||||||
|
&.material--active {
|
||||||
|
.material-input-bar {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Errors:
|
||||||
|
.material-errors {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
.material-error {
|
||||||
|
font-size: $font-size-smallest;
|
||||||
|
line-height: $font-size-smallest + 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: $spacer / 2;
|
||||||
|
padding-right: $spacer / 2;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme:
|
||||||
|
$color-white: white;
|
||||||
|
$color-grey: #9E9E9E;
|
||||||
|
$color-grey-light: #E0E0E0;
|
||||||
|
$color-blue: #2196F3;
|
||||||
|
$color-red: #F44336;
|
||||||
|
$color-black: black;
|
||||||
|
.material-input__component {
|
||||||
|
background: $color-white;
|
||||||
|
.material-input {
|
||||||
|
background: none;
|
||||||
|
color: $color-black;
|
||||||
|
text-indent: 30px;
|
||||||
|
border-bottom: 1px solid $color-grey-light;
|
||||||
|
}
|
||||||
|
.material-label {
|
||||||
|
color: $color-grey;
|
||||||
|
}
|
||||||
|
.material-input-bar {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
background: $color-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Active state:
|
||||||
|
&.material--active {
|
||||||
|
.material-label {
|
||||||
|
color: $color-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Errors:
|
||||||
|
&.material--has-errors {
|
||||||
|
// These styles are required
|
||||||
|
// for custom validation:
|
||||||
|
&.material--active .material-label {
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
.material-input-bar {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
background: $color-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.material-errors {
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<div class='simplemde-container'>
|
||||||
|
<textarea :id='id'>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import 'simplemde/dist/simplemde.min.css'
|
||||||
|
import SimpleMDE from 'simplemde'
|
||||||
|
export default {
|
||||||
|
name: 'Sticky',
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'markdown-editor'
|
||||||
|
},
|
||||||
|
autofocus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
type: Array
|
||||||
|
// default() {
|
||||||
|
// return ['bold', '|', 'link']
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
simplemde: null,
|
||||||
|
hasChange: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
if (val === this.simplemde.value() && !this.hasChange) return;
|
||||||
|
this.simplemde.value(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.simplemde = new SimpleMDE({
|
||||||
|
element: document.getElementById(this.id),
|
||||||
|
autofocus: this.autofocus,
|
||||||
|
toolbar: this.toolbar,
|
||||||
|
spellChecker: false,
|
||||||
|
insertTexts: {
|
||||||
|
link: ['[', ']( )']
|
||||||
|
},
|
||||||
|
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
|
||||||
|
placeholder: this.placeholder
|
||||||
|
});
|
||||||
|
if (this.value) {
|
||||||
|
this.simplemde.value(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.simplemde.codemirror.on('change', () => {
|
||||||
|
if (this.hasChange) {
|
||||||
|
this.hasChange = true
|
||||||
|
}
|
||||||
|
this.$emit('input', this.simplemde.value());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.simplemde = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.simplemde-container .CodeMirror {
|
||||||
|
height: 150px;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
.simplemde-container .CodeMirror-scroll{
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplemde-container .CodeMirror-code{
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
.simplemde-container .editor-statusbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplemde-container .CodeMirror .CodeMirror-code .cm-link {
|
||||||
|
color: #1482F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplemde-container .CodeMirror .CodeMirror-code .cm-string.cm-url {
|
||||||
|
color: #2d3b4d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplemde-container .CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
|
||||||
|
padding: 0 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #E61E1E;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div class="pan-item" :style="{zIndex:zIndex,height:height,width:width}">
|
||||||
|
<div class="pan-info">
|
||||||
|
<div class="pan-info-roles-container">
|
||||||
|
<slot>pan</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pan-thumb" :style="{ backgroundImage: 'url('+ image+')' }"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PanThumb',
|
||||||
|
props: {
|
||||||
|
image: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
zIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '150px'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '150px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</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-size: 100%;
|
||||||
|
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,61 @@
|
||||||
|
<template>
|
||||||
|
<div :class="classes">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Pane',
|
||||||
|
props: {
|
||||||
|
// split: {
|
||||||
|
// validator: function (value) {
|
||||||
|
// return ['vertical', 'horizontal'].indexOf(value) >= 0
|
||||||
|
// },
|
||||||
|
// required: true
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
// computed:{
|
||||||
|
// classes () {
|
||||||
|
// return this.$parent.split
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
data() {
|
||||||
|
const classes = ['Pane', this.$parent.split, 'className'];
|
||||||
|
return {
|
||||||
|
classes: classes.join(' '),
|
||||||
|
percent: 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// console.log(this.$parent.split)
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.splitter-pane.vertical.splitter-paneL{
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.splitter-pane.vertical.splitter-paneR{
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.splitter-pane.horizontal.splitter-paneL{
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.splitter-pane.horizontal.splitter-paneR{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div :class="classes" @mousedown="onMouseDown"></div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.Resizer {
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #000;
|
||||||
|
position: absolute;
|
||||||
|
opacity: .2;
|
||||||
|
z-index: 1;
|
||||||
|
/*-moz-background-clip: padding;*/
|
||||||
|
/*-webkit-background-clip: padding;*/
|
||||||
|
/*background-clip: padding-box;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.Resizer:hover {*/
|
||||||
|
/*-webkit-transition: all 2s ease;*/
|
||||||
|
/*transition: all 2s ease;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
.Resizer.horizontal {
|
||||||
|
height: 11px;
|
||||||
|
margin: -5px 0;
|
||||||
|
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: row-resize;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.horizontal:hover {
|
||||||
|
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical {
|
||||||
|
width: 11px;
|
||||||
|
height: 100%;
|
||||||
|
/*margin: 0 -5px;*/
|
||||||
|
|
||||||
|
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical:hover {
|
||||||
|
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
split: {
|
||||||
|
validator(value) {
|
||||||
|
return ['vertical', 'horizontal'].indexOf(value) >= 0
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
onMouseDown: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const classes = ['Resizer', this.split, 'className'];
|
||||||
|
return {
|
||||||
|
classes: classes.join(' ')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,175 @@
|
||||||
|
<!--<template>-->
|
||||||
|
<!--<div :style="{ cursor, userSelect }" class="vue-splitter-container clearfix" @mouseup="onMouseUp"-->
|
||||||
|
<!--@mousemove="onMouseMove">-->
|
||||||
|
|
||||||
|
<!--<Pane split="vertical" :style="{ width: percent+'%' }" class="left-container splitter-pane">-->
|
||||||
|
<!--orange-->
|
||||||
|
<!--</Pane>-->
|
||||||
|
|
||||||
|
<!--<Resizer split="vertical" :onMouseDown="onMouseDown" @click="onClick"></Resizer>-->
|
||||||
|
<!--<div class="todel" :style="{ width: 100-percent+'%'}">-->
|
||||||
|
<!--<Pane split="horizontal" class="top-container">-->
|
||||||
|
<!--<div slot>apple banana</div>-->
|
||||||
|
<!--</Pane>-->
|
||||||
|
<!--<Resizer split="horizontal" :onMouseDown="onMouseDown" @click="onClick"></Resizer>-->
|
||||||
|
<!--<Pane split="horizontal" class="bottom-container">-->
|
||||||
|
<!--<div slot>apple banana</div>-->
|
||||||
|
<!--</Pane>-->
|
||||||
|
<!--</div>-->
|
||||||
|
|
||||||
|
<!--</div>-->
|
||||||
|
|
||||||
|
<!--</template>-->
|
||||||
|
<style scoped>
|
||||||
|
.clearfix:after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.vue-splitter-container {
|
||||||
|
height: inherit;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* eslint-disable */
|
||||||
|
import Resizer from './Resizer';
|
||||||
|
import vue from 'vue'
|
||||||
|
export default {
|
||||||
|
name: 'splitPane',
|
||||||
|
components: {Resizer},
|
||||||
|
props: {
|
||||||
|
margin: {
|
||||||
|
type: Number,
|
||||||
|
default: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
percent: 50,
|
||||||
|
hasMoved: false,
|
||||||
|
panes: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
split: {
|
||||||
|
validator: function (value) {
|
||||||
|
return ['vertical', 'horizontal'].indexOf(value) >= 0
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userSelect () {
|
||||||
|
return this.active ? 'none' : ''
|
||||||
|
},
|
||||||
|
cursor () {
|
||||||
|
return this.active ? 'col-resize' : ''
|
||||||
|
},
|
||||||
|
// $paneItems () {
|
||||||
|
// return this.$children.filter(child => {
|
||||||
|
// console.log(child)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
render(h){
|
||||||
|
const temp = [];
|
||||||
|
this.$slots.default.map((item, i) => {
|
||||||
|
if (item.tag && item.tag.toUpperCase().indexOf('PANE') >= 0) {
|
||||||
|
temp.push(item)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const newSlots = [];
|
||||||
|
const length = temp.length;
|
||||||
|
temp.map((item, index)=> {
|
||||||
|
newSlots.push(item)
|
||||||
|
if (index != length - 1) {
|
||||||
|
newSlots.push(
|
||||||
|
h('Resizer', {
|
||||||
|
props: {
|
||||||
|
split: this.split,
|
||||||
|
onMouseDown: this.onMouseDown
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return h('div', {
|
||||||
|
on: {
|
||||||
|
mousemove: this.onMouseMove
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
h('div', {
|
||||||
|
'class': {
|
||||||
|
'vue-splitter-container': true
|
||||||
|
},
|
||||||
|
}, newSlots)
|
||||||
|
])
|
||||||
|
},
|
||||||
|
// beforeMount(){
|
||||||
|
// this.$slots.default=this.$slots.default.map((item, i) => {
|
||||||
|
// if (item.tag&&item.tag.toUpperCase().indexOf('PANE') >= 0) {
|
||||||
|
// return item
|
||||||
|
// }else{
|
||||||
|
// return null
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
created(){
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick () {
|
||||||
|
if (!this.hasMoved) {
|
||||||
|
this.percent = 50;
|
||||||
|
this.$emit('resize');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseDown () {
|
||||||
|
this.active = true;
|
||||||
|
this.hasMoved = false;
|
||||||
|
},
|
||||||
|
onMouseUp () {
|
||||||
|
this.active = false;
|
||||||
|
},
|
||||||
|
onMouseMove (e) {
|
||||||
|
if (e.buttons === 0 || e.which === 0) {
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
if (this.active) {
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
let target = e.currentTarget;
|
||||||
|
while (target) {
|
||||||
|
offset += target.offsetLeft;
|
||||||
|
target = target.offsetParent;
|
||||||
|
}
|
||||||
|
const percent = Math.floor(((e.pageX - offset) / e.currentTarget.offsetWidth) * 10000) / 100;
|
||||||
|
if (percent > this.margin && percent < 100 - this.margin) {
|
||||||
|
this.percent = percent;
|
||||||
|
}
|
||||||
|
console.log(percent)
|
||||||
|
this.$children.map((v, i)=> {
|
||||||
|
if (i == 0) {
|
||||||
|
v.percent = percent
|
||||||
|
} else {
|
||||||
|
v.percent = 100 - percent
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
this.$emit('resize');
|
||||||
|
this.hasMoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div ref :style="{ cursor, userSelect}" class="vue-splitter-container clearfix" @mouseup="onMouseUp"
|
||||||
|
@mousemove="onMouseMove">
|
||||||
|
<Pane class="splitter-pane splitter-paneL" :split="split" :style="{ [type]: percent+'%'}">
|
||||||
|
<slot name="paneL"></slot>
|
||||||
|
</Pane>
|
||||||
|
<Resizer :style="{ [resizeType]: percent+'%'}" :split="split" :onMouseDown="onMouseDown"
|
||||||
|
@click="onClick"></Resizer>
|
||||||
|
<Pane class="splitter-pane splitter-paneR" :split="split" :style="{ [type]: 100-percent+'%'}">
|
||||||
|
<slot name="paneR"></slot>
|
||||||
|
</Pane>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.clearfix:after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.vue-splitter-container {
|
||||||
|
height: 100%;
|
||||||
|
/*display: flex;*/
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Resizer from './Resizer';
|
||||||
|
import Pane from './Pane';
|
||||||
|
export default {
|
||||||
|
name: 'splitPane',
|
||||||
|
components: { Resizer, Pane },
|
||||||
|
props: {
|
||||||
|
margin: {
|
||||||
|
type: Number,
|
||||||
|
default: 10
|
||||||
|
},
|
||||||
|
split: {
|
||||||
|
validator(value) {
|
||||||
|
return ['vertical', 'horizontal'].indexOf(value) >= 0
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
hasMoved: false,
|
||||||
|
height: null,
|
||||||
|
percent: 50,
|
||||||
|
type: this.split === 'vertical' ? 'width' : 'height',
|
||||||
|
resizeType: this.split === 'vertical' ? 'left' : 'top'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userSelect() {
|
||||||
|
return this.active ? 'none' : ''
|
||||||
|
},
|
||||||
|
cursor() {
|
||||||
|
return this.active ? 'col-resize' : ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const element = this.$el;
|
||||||
|
const elementOffset = element.getBoundingClientRect();
|
||||||
|
console.log(elementOffset.height)
|
||||||
|
// this.height = elementOffset.height+'px';
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick() {
|
||||||
|
if (!this.hasMoved) {
|
||||||
|
this.percent = 50;
|
||||||
|
this.$emit('resize');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseDown() {
|
||||||
|
this.active = true;
|
||||||
|
this.hasMoved = false;
|
||||||
|
},
|
||||||
|
onMouseUp() {
|
||||||
|
this.active = false;
|
||||||
|
},
|
||||||
|
onMouseMove(e) {
|
||||||
|
if (e.buttons === 0 || e.which === 0) {
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
if (this.active) {
|
||||||
|
let offset = 0;
|
||||||
|
let target = e.currentTarget;
|
||||||
|
if (this.split === 'vertical') {
|
||||||
|
while (target) {
|
||||||
|
offset += target.offsetLeft;
|
||||||
|
target = target.offsetParent;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (target) {
|
||||||
|
offset += target.offsetTop;
|
||||||
|
target = target.offsetParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPage = this.split === 'vertical' ? e.pageX : e.pageY;
|
||||||
|
const targetOffset = this.split === 'vertical' ? e.currentTarget.offsetWidth : e.currentTarget.offsetHeight;
|
||||||
|
const percent = Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100;
|
||||||
|
if (percent > this.margin && percent < 100 - this.margin) {
|
||||||
|
this.percent = percent;
|
||||||
|
}
|
||||||
|
this.$emit('resize');
|
||||||
|
this.hasMoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,7 @@
|
||||||
|
import SplitPane from './a.vue';
|
||||||
|
import Pane from './Pane.vue';
|
||||||
|
|
||||||
|
export {
|
||||||
|
SplitPane,
|
||||||
|
Pane
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{height:height+'px',zIndex:zIndex}">
|
||||||
|
<div :class="className" :style="{top: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: 1000
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
position: '',
|
||||||
|
currentTop: '',
|
||||||
|
width: undefined,
|
||||||
|
height: undefined,
|
||||||
|
child: null,
|
||||||
|
stickyHeight: 0
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sticky() {
|
||||||
|
if (this.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.position = 'fixed';
|
||||||
|
this.active = true;
|
||||||
|
this.width = this.width + 'px';
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
if (!this.active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.position = '';
|
||||||
|
this.width = 'auto'
|
||||||
|
this.active = false
|
||||||
|
},
|
||||||
|
handleScroll() {
|
||||||
|
this.width = this.$el.getBoundingClientRect().width;
|
||||||
|
const offsetTop = this.$el.getBoundingClientRect().top;
|
||||||
|
if (offsetTop <= this.stickyTop) {
|
||||||
|
this.sticky();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.height = this.$el.getBoundingClientRect().height;
|
||||||
|
window.addEventListener('scroll', this.handleScroll);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
window.removeEventListener('scroll', this.handleScroll);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,119 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传音频
|
||||||
|
</el-button>
|
||||||
|
<el-dialog v-model="dialogVisible">
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="100px" label-position="right">
|
||||||
|
<el-upload
|
||||||
|
class="editor-audio-upload"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:data="dataObj"
|
||||||
|
:show-file-list="true"
|
||||||
|
:file-list="audioList"
|
||||||
|
:on-success="handleAudioScucess"
|
||||||
|
:on-change="handleAudioChange"
|
||||||
|
:before-upload="audioBeforeUpload">
|
||||||
|
<el-button size="small" type="primary">上传音频</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-form-item prop="url" label="音频URL">
|
||||||
|
<el-input v-model="form.url"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="title" label="音频标题">
|
||||||
|
<el-input v-model="form.title"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="音频文本">
|
||||||
|
<el-input type="textarea" :autosize="{ minRows: 2}" v-model="form.text"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<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';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'editorAudioUpload',
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#20a0ff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
dataObj: { token: '', key: '' },
|
||||||
|
audioList: [],
|
||||||
|
tempAudioUrl: '',
|
||||||
|
form: {
|
||||||
|
title: '',
|
||||||
|
url: '',
|
||||||
|
text: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
title: [
|
||||||
|
{ required: true, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
url: [
|
||||||
|
{ required: true, trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.$emit('successCBK', this.form);
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.form = {
|
||||||
|
title: '',
|
||||||
|
url: '',
|
||||||
|
text: ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message('填写有误');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleAudioChange(file, fileList) {
|
||||||
|
this.audioList = fileList.slice(-1);
|
||||||
|
},
|
||||||
|
handleAudioScucess() {
|
||||||
|
this.form.url = this.tempAudioUrl
|
||||||
|
},
|
||||||
|
audioBeforeUpload() {
|
||||||
|
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.tempAudioUrl = response.data.qiniu_url;
|
||||||
|
resolve(true);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.upload-container {
|
||||||
|
.editor-audio-upload {
|
||||||
|
button {
|
||||||
|
float: left;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-button icon='upload' :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传图片
|
||||||
|
</el-button>
|
||||||
|
<el-dialog v-model="dialogVisible">
|
||||||
|
<el-upload
|
||||||
|
class="editor-slide-upload"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:data="dataObj"
|
||||||
|
:multiple="true"
|
||||||
|
:file-list="fileList"
|
||||||
|
:show-file-list="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:before-upload="beforeUpload">
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<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';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'editorSlideUpload',
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#20a0ff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
dataObj: { token: '', key: '' },
|
||||||
|
list: [],
|
||||||
|
fileList: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
const arr = this.list.map(v => v.url);
|
||||||
|
this.$emit('successCBK', arr);
|
||||||
|
this.list = [];
|
||||||
|
this.fileList = [];
|
||||||
|
this.dialogVisible = false;
|
||||||
|
},
|
||||||
|
handleRemove(file) {
|
||||||
|
const key = file.response.key;
|
||||||
|
for (let i = 0, len = this.list.length; i < len; i++) {
|
||||||
|
if (this.list[i].key === key) {
|
||||||
|
this.list.splice(i, 1);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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.list.push({ key, url: response.data.qiniu_url });
|
||||||
|
resolve(true);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.upload-container {
|
||||||
|
.editor-slide-upload {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传轮播图
|
||||||
|
</el-button>
|
||||||
|
<el-dialog v-model="dialogVisible">
|
||||||
|
<el-upload
|
||||||
|
class="editor-slide-upload"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:data="dataObj"
|
||||||
|
:multiple="true"
|
||||||
|
:show-file-list="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:before-upload="beforeUpload">
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<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';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'editorSlideUpload',
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#20a0ff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
dataObj: { token: '', key: '' },
|
||||||
|
list: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
const arr = this.list.map(v => v.url);
|
||||||
|
this.$emit('successCBK', arr);
|
||||||
|
this.list = [];
|
||||||
|
this.dialogVisible = false;
|
||||||
|
},
|
||||||
|
handleRemove(file) {
|
||||||
|
const key = file.response.key;
|
||||||
|
for (let i = 0, len = this.list.length; i < len; i++) {
|
||||||
|
if (this.list[i].key === key) {
|
||||||
|
this.list.splice(i, 1);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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.list.push({ key, url: response.data.qiniu_url });
|
||||||
|
resolve(true);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.upload-container {
|
||||||
|
.editor-slide-upload {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,167 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-button :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传视频</el-button>
|
||||||
|
<el-dialog v-model="dialogVisible">
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="140px" label-position="left">
|
||||||
|
<el-upload
|
||||||
|
class="editor-video-upload"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:data="dataObj"
|
||||||
|
:show-file-list="true"
|
||||||
|
:file-list="videoList"
|
||||||
|
:on-success="handleVideoScucess"
|
||||||
|
:on-change="handleVideoChange"
|
||||||
|
:before-upload="videoBeforeUpload">
|
||||||
|
<el-button size="small" type="primary">上传视频</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<el-form-item prop="url" label="视频URL">
|
||||||
|
<el-input v-model="form.url"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="title" label="视频标题">
|
||||||
|
<el-input v-model="form.title"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上传视频封面图">
|
||||||
|
</el-form-item>
|
||||||
|
<el-upload
|
||||||
|
class="image-uploader"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:show-file-list="false"
|
||||||
|
:data="dataObj"
|
||||||
|
:on-success="handleImageScucess"
|
||||||
|
:before-upload="beforeImageUpload">
|
||||||
|
<img v-if="form.image" :src="form.image" class="image-uploader-image">
|
||||||
|
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||||
|
</el-upload>
|
||||||
|
</el-form>
|
||||||
|
<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';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'editorVideoUpload',
|
||||||
|
props: {
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#20a0ff'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
dataObj: { token: '', key: '' },
|
||||||
|
videoList: [],
|
||||||
|
tempVideoUrl: '',
|
||||||
|
tempImageUrl: '',
|
||||||
|
form: {
|
||||||
|
title: '',
|
||||||
|
url: '',
|
||||||
|
image: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
url: [
|
||||||
|
{ required: true, trigger: 'blur' }
|
||||||
|
],
|
||||||
|
title: [
|
||||||
|
{ required: true, trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.form.image.length > 0) {
|
||||||
|
this.$emit('successCBK', this.form);
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.form = {
|
||||||
|
title: '',
|
||||||
|
url: '',
|
||||||
|
image: ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message('请上传图片');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message('填写有误');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleVideoChange(file, fileList) {
|
||||||
|
this.videoList = fileList.slice(-1);
|
||||||
|
},
|
||||||
|
handleVideoScucess() {
|
||||||
|
this.form.url = this.tempVideoUrl
|
||||||
|
},
|
||||||
|
videoBeforeUpload() {
|
||||||
|
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.tempVideoUrl = response.data.qiniu_url;
|
||||||
|
resolve(true);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleImageScucess() {
|
||||||
|
this.form.image = this.tempImageUrl
|
||||||
|
},
|
||||||
|
beforeImageUpload() {
|
||||||
|
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.tempImageUrl = response.data.qiniu_url;
|
||||||
|
resolve(true);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
reject(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.upload-container {
|
||||||
|
.editor-video-upload {
|
||||||
|
button {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.image-uploader {
|
||||||
|
margin: 5px auto;
|
||||||
|
width: 400px;
|
||||||
|
height: 200px;
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 200px;
|
||||||
|
i {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
}
|
||||||
|
.image-uploader-image {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,251 @@
|
||||||
|
<template>
|
||||||
|
<div class='tinymce-container editor-container'>
|
||||||
|
<textarea class='tinymce-textarea' :id="id"></textarea>
|
||||||
|
<div class="editor-custom-btn-container">
|
||||||
|
<editorSlide v-if="customButton.indexOf('editorSlide')>=0" color="#3A71A8" class="editor-upload-btn" @successCBK="slideSuccessCBK"></editorSlide>
|
||||||
|
<editorAudio v-if="customButton.indexOf('editorAudio')>=0" color="#30B08F" class="editor-upload-btn" @successCBK="aduioSuccessCBK"></editorAudio>
|
||||||
|
<editorVideo v-if="customButton.indexOf('editorVideo')>=0" color="#E65D6E" class="editor-upload-btn" @successCBK="videoSuccessCBK"></editorVideo>
|
||||||
|
<editorImage v-if="customButton.indexOf('editorImage')>=0" color="#20a0ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import editorAudio from './components/editorAudio';
|
||||||
|
import editorVideo from './components/editorVideo';
|
||||||
|
import editorSlide from './components/editorSlide';
|
||||||
|
import editorImage from './components/editorImage';
|
||||||
|
import { getToken, upload } from 'api/qiniu';
|
||||||
|
export default {
|
||||||
|
name: 'tinymce',
|
||||||
|
components: { editorImage, editorAudio, editorSlide, editorVideo },
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'tinymceEditor'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
customButton: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return ['editorAudio', 'editorImage']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default() {
|
||||||
|
return ['removeformat undo redo | bullist numlist | outdent indent | forecolor | fullscreen code', 'bold italic blockquote | h2 p media link | alignleft aligncenter alignright']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hasChange: false,
|
||||||
|
hasInit: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menubar: {
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 360
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
if (!this.hasChange && this.hasInit) {
|
||||||
|
this.$nextTick(() => tinymce.get(this.id).setContent(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const _this = this;
|
||||||
|
tinymce.init({
|
||||||
|
selector: `#${this.id}`,
|
||||||
|
height: this.height,
|
||||||
|
body_class: 'panel-body ',
|
||||||
|
object_resizing: false,
|
||||||
|
// language: 'zh_CN',
|
||||||
|
// language_url: '/static/tinymce/langs/zh_CN.js',
|
||||||
|
toolbar: this.toolbar,
|
||||||
|
menubar: this.menubar,
|
||||||
|
plugins: 'advlist,autolink,code,powerpaste,textcolor, colorpicker,fullscreen,link,lists,media,wordcount, imagetools,watermark',
|
||||||
|
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',
|
||||||
|
block_formats: '普通标签=p;小标题=h2;',
|
||||||
|
imagetools_cors_hosts: ['wpimg.wallstcn.com', 'wallstreetcn.com'],
|
||||||
|
imagetools_toolbar: 'watermark',
|
||||||
|
default_link_target: '_blank',
|
||||||
|
link_title: false,
|
||||||
|
textcolor_map: [
|
||||||
|
'1482f0', '1482f0',
|
||||||
|
'4595e6', '4595e6'],
|
||||||
|
init_instance_callback: editor => {
|
||||||
|
if (_this.value) {
|
||||||
|
editor.setContent(_this.value)
|
||||||
|
}
|
||||||
|
_this.hasInit = true;
|
||||||
|
editor.on('Change', () => {
|
||||||
|
this.hasChange = true;
|
||||||
|
this.$emit('input', editor.getContent({ format: 'raw' }));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
// setTimeout(() => {
|
||||||
|
// const doc = tinymce.activeEditor.getDoc();
|
||||||
|
// const $$ = tinymce.dom.DomQuery;
|
||||||
|
// const $image = $$(doc).find('img[src="' + url + '"]')
|
||||||
|
// $image.addClass('wscnph');
|
||||||
|
// $image.attr('data-wscntype', 'image');
|
||||||
|
// $image.attr('data-wscnh', $image[0].height || 640);
|
||||||
|
// $image.attr('data-wscnw', $image[0].width || 640);
|
||||||
|
// }, 0);
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
failure('出现未知问题,刷新页面,或者联系程序员')
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setup(editor) {
|
||||||
|
editor.addButton('h2', {
|
||||||
|
title: '小标题', // tooltip text seen on mouseover
|
||||||
|
text: '小标题',
|
||||||
|
onclick() {
|
||||||
|
editor.execCommand('mceToggleFormat', false, 'h2');
|
||||||
|
},
|
||||||
|
onPostRender() {
|
||||||
|
const btn = this;
|
||||||
|
editor.on('init', () => {
|
||||||
|
editor.formatter.formatChanged('h2', state => {
|
||||||
|
btn.active(state);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
editor.addButton('p', {
|
||||||
|
title: '正文', // tooltip text seen on mouseover
|
||||||
|
text: '正文',
|
||||||
|
onclick() {
|
||||||
|
editor.execCommand('mceToggleFormat', false, 'p');
|
||||||
|
},
|
||||||
|
onPostRender() {
|
||||||
|
const btn = this;
|
||||||
|
editor.on('init', () => {
|
||||||
|
editor.formatter.formatChanged('p', state => {
|
||||||
|
btn.active(state);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
imageSuccessCBK(arr) {
|
||||||
|
console.log(arr)
|
||||||
|
const _this = this;
|
||||||
|
arr.forEach(v => {
|
||||||
|
const node = document.createElement('img');
|
||||||
|
node.setAttribute('src', v);
|
||||||
|
node.onload = function() {
|
||||||
|
$(this).addClass('wscnph');
|
||||||
|
$(this).attr('data-wscntype', 'image');
|
||||||
|
$(this).attr('data-wscnh', this.height);
|
||||||
|
$(this).attr('data-wscnw', this.width);
|
||||||
|
tinymce.get(_this.id).insertContent(node.outerHTML)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
slideSuccessCBK(arr) {
|
||||||
|
const node = document.createElement('img');
|
||||||
|
node.setAttribute('data-wscntype', 'slide');
|
||||||
|
node.setAttribute('data-uri', arr.toString());
|
||||||
|
node.setAttribute('data-wscnh', '190');
|
||||||
|
node.setAttribute('data-wscnw', '200');
|
||||||
|
node.setAttribute('src', ' https://wdl.wallstreetcn.com/6410b47d-a54c-4826-9bc1-c3e5df31280c');
|
||||||
|
node.className = 'wscnph editor-placeholder';
|
||||||
|
tinymce.get(this.id).insertContent(node.outerHTML)
|
||||||
|
},
|
||||||
|
videoSuccessCBK(form) {
|
||||||
|
const node = document.createElement('img');
|
||||||
|
node.setAttribute('data-wscntype', 'video');
|
||||||
|
node.setAttribute('data-uri', form.url);
|
||||||
|
node.setAttribute('data-cover-img-uri', form.image);
|
||||||
|
node.setAttribute('data-title', form.title);
|
||||||
|
node.setAttribute('src', 'https://wdl.wallstreetcn.com/07aeb3e7-f4ca-4207-befb-c987b3dc7011');
|
||||||
|
node.className = 'wscnph editor-placeholder';
|
||||||
|
tinymce.get(this.id).insertContent(node.outerHTML)
|
||||||
|
},
|
||||||
|
aduioSuccessCBK(form) {
|
||||||
|
const node = document.createElement('img');
|
||||||
|
node.setAttribute('data-wscntype', 'audio');
|
||||||
|
node.setAttribute('data-uri', form.url);
|
||||||
|
node.setAttribute('data-title', form.title);
|
||||||
|
node.setAttribute('data-text', form.text);
|
||||||
|
node.setAttribute('src', 'https://wdl.wallstreetcn.com/2ed0c8c8-fb82-499d-b81c-3fd1de114eae');
|
||||||
|
node.className = 'wscnph editor-placeholder';
|
||||||
|
tinymce.get(this.id).insertContent(node.outerHTML)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
tinymce.get(this.id).destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tinymce-container {
|
||||||
|
position: relative
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-textarea {
|
||||||
|
visibility: hidden;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-custom-btn-container {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
/*z-index: 2005;*/
|
||||||
|
top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-upload-btn {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-upload
|
||||||
|
class="image-uploader"
|
||||||
|
:data="dataObj"
|
||||||
|
drag
|
||||||
|
:multiple="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:on-success="handleImageScucess">
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<div class="image-preview">
|
||||||
|
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
|
||||||
|
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
|
||||||
|
<div class="image-preview-action">
|
||||||
|
<i @click="rmImage" class="el-icon-delete"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 预览效果见付费文章
|
||||||
|
import { getToken } from 'api/qiniu';
|
||||||
|
export default {
|
||||||
|
name: 'singleImageUpload',
|
||||||
|
props: {
|
||||||
|
value: String
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
imageUrl() {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tempUrl: '',
|
||||||
|
dataObj: { token: '', key: '' }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
rmImage() {
|
||||||
|
this.emitInput('');
|
||||||
|
},
|
||||||
|
emitInput(val) {
|
||||||
|
this.$emit('input', val);
|
||||||
|
},
|
||||||
|
handleImageScucess() {
|
||||||
|
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 rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
@import "src/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,126 @@
|
||||||
|
<template>
|
||||||
|
<div class="singleImageUpload2 upload-container">
|
||||||
|
<el-upload
|
||||||
|
class="image-uploader"
|
||||||
|
:data="dataObj"
|
||||||
|
drag
|
||||||
|
:multiple="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:on-success="handleImageScucess">
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">Drag或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<div v-show="imageUrl.length>0" class="image-preview">
|
||||||
|
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
|
||||||
|
<img :src="imageUrl">
|
||||||
|
<div class="image-preview-action">
|
||||||
|
<i @click="rmImage" class="el-icon-delete"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 预览效果见专题
|
||||||
|
import { getToken } from 'api/qiniu';
|
||||||
|
export default {
|
||||||
|
name: 'singleImageUpload2',
|
||||||
|
props: {
|
||||||
|
value: String
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
imageUrl() {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tempUrl: '',
|
||||||
|
dataObj: { token: '', key: '' }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
rmImage() {
|
||||||
|
this.emitInput('');
|
||||||
|
},
|
||||||
|
emitInput(val) {
|
||||||
|
this.$emit('input', val);
|
||||||
|
},
|
||||||
|
handleImageScucess() {
|
||||||
|
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 rel="stylesheet/scss" 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,154 @@
|
||||||
|
<template>
|
||||||
|
<div class="upload-container">
|
||||||
|
<el-upload
|
||||||
|
class="image-uploader"
|
||||||
|
:data="dataObj"
|
||||||
|
drag
|
||||||
|
:multiple="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
action="https://upload.qbox.me"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:on-success="handleImageScucess">
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<div class="image-preview image-app-preview">
|
||||||
|
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
|
||||||
|
<div class='app-fake-conver'>  全球 付费节目单 最热 经济</div>
|
||||||
|
<img :src="imageUrl+'?imageView2/1/h/180/w/320/q/100'">
|
||||||
|
<div class="image-preview-action">
|
||||||
|
<i @click="rmImage" class="el-icon-delete"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-preview">
|
||||||
|
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
|
||||||
|
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
|
||||||
|
<div class="image-preview-action">
|
||||||
|
<i @click="rmImage" class="el-icon-delete"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// 预览效果见文章
|
||||||
|
import { getToken } from 'api/qiniu';
|
||||||
|
export default {
|
||||||
|
name: 'singleImageUpload',
|
||||||
|
props: {
|
||||||
|
value: String
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
imageUrl() {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tempUrl: '',
|
||||||
|
dataObj: { token: '', key: '' }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
rmImage() {
|
||||||
|
this.emitInput('');
|
||||||
|
},
|
||||||
|
emitInput(val) {
|
||||||
|
this.$emit('input', val);
|
||||||
|
},
|
||||||
|
handleImageScucess() {
|
||||||
|
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 rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
@import "src/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,64 @@
|
||||||
|
<template>
|
||||||
|
<div class='json-editor'>
|
||||||
|
<textarea ref='textarea'></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',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
jsonEditor: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['value'],
|
||||||
|
watch: {
|
||||||
|
value(value) {
|
||||||
|
const editor_value = this.jsonEditor.getValue();
|
||||||
|
if (value !== editor_value) {
|
||||||
|
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>
|
||||||
|
.CodeMirror {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.json-editor .cm-s-rubyblue span.cm-string{
|
||||||
|
color: #F08047;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,157 @@
|
||||||
|
<template>
|
||||||
|
<div class="twoDndList">
|
||||||
|
<div class="twoDndList-list" :style="{width:width1}">
|
||||||
|
<h3>{{list1Title}}</h3>
|
||||||
|
<draggable :list="list1" class="dragArea" :options="{group:'article'}">
|
||||||
|
<div class="list-complete-item" v-for="element in list1">
|
||||||
|
<div class="list-complete-item-handle">[{{element.id}}] {{element.title}}</div>
|
||||||
|
<div style="position:absolute;right:0px;">
|
||||||
|
<a style="float: left ;margin-top: -20px;margin-right:5px;" :href="'/article/edit/'+element.id" target="_blank"><i style="color:#20a0ff" class="el-icon-information"></i></a>
|
||||||
|
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
|
||||||
|
<i style="color:#ff4949" class="el-icon-delete"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="twoDndList-list" :style="{width:width2}">
|
||||||
|
<h3>{{list2Title}}</h3>
|
||||||
|
<draggable :list="filterList2" class="dragArea" :options="{group:'article'}">
|
||||||
|
<div class="list-complete-item" v-for="element in filterList2">
|
||||||
|
<div class='list-complete-item-handle2' @click="pushEle(element)"> [{{element.id}}] {{element.title}}</div>
|
||||||
|
<a style="float: right ;margin-top: -20px;" :href="'/article/edit/'+element.id" target="_blank"><i style="color:#20a0ff" class="el-icon-information"></i></a>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
export default {
|
||||||
|
name: 'twoDndList',
|
||||||
|
components: { draggable },
|
||||||
|
computed: {
|
||||||
|
filterList2() {
|
||||||
|
return this.list2.filter(v => {
|
||||||
|
if (this.isNotInList1(v)) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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) {
|
||||||
|
this.list1.push(ele)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.twoDndList {
|
||||||
|
background: #fff;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.twoDndList-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;
|
||||||
|
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,101 @@
|
||||||
|
(function() {
|
||||||
|
const vueSticky = {};
|
||||||
|
let listenAction;
|
||||||
|
vueSticky.install = Vue => {
|
||||||
|
Vue.directive('sticky', {
|
||||||
|
inserted(el, binding) {
|
||||||
|
const params = binding.value || {},
|
||||||
|
stickyTop = params.stickyTop || 0,
|
||||||
|
zIndex = params.zIndex || 1000,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if (typeof exports == 'object') {
|
||||||
|
module.exports = vueSticky
|
||||||
|
} else if (typeof define == 'function' && define.amd) {
|
||||||
|
define([], () => vueSticky)
|
||||||
|
} else if (window.Vue) {
|
||||||
|
window.vueSticky = vueSticky;
|
||||||
|
Vue.use(vueSticky)
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
|
@ -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,54 @@
|
||||||
|
import './waves.css';
|
||||||
|
(function() {
|
||||||
|
const vueWaves = {};
|
||||||
|
vueWaves.install = (Vue, options = {}) => {
|
||||||
|
Vue.directive('waves', {
|
||||||
|
bind(el, binding) {
|
||||||
|
el.addEventListener('click', e => {
|
||||||
|
const customOpts = Object.assign(options, binding.value);
|
||||||
|
const opts = Object.assign({
|
||||||
|
ele: el, // 波纹作用元素
|
||||||
|
type: 'hit', // hit点击位置扩散center中心点扩展
|
||||||
|
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
|
||||||
|
}, customOpts),
|
||||||
|
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.body.scrollTop) + 'px';
|
||||||
|
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
|
||||||
|
}
|
||||||
|
ripple.style.backgroundColor = opts.color;
|
||||||
|
ripple.className = 'waves-ripple z-active';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if (typeof exports == 'object') {
|
||||||
|
module.exports = vueWaves
|
||||||
|
} else if (typeof define == 'function' && define.amd) {
|
||||||
|
define([], () => vueWaves)
|
||||||
|
} else if (window.Vue) {
|
||||||
|
window.vueWaves = vueWaves;
|
||||||
|
Vue.use(vueWaves)
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
function pluralize(time, label) {
|
||||||
|
if (time === 1) {
|
||||||
|
return time + label
|
||||||
|
}
|
||||||
|
return time + label + 's'
|
||||||
|
}
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseTime(time, cFormat) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((time + '').length === 10) {
|
||||||
|
time = +time * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
|
||||||
|
let date;
|
||||||
|
if (typeof time == 'object') {
|
||||||
|
date = time;
|
||||||
|
} else {
|
||||||
|
date = new Date(parseInt(time));
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
};
|
||||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key];
|
||||||
|
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1];
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value;
|
||||||
|
}
|
||||||
|
return value || 0;
|
||||||
|
});
|
||||||
|
return time_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTime(time, option) {
|
||||||
|
time = +time * 1000;
|
||||||
|
const d = new Date(time);
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
const diff = (now - d) / 1000;
|
||||||
|
|
||||||
|
if (diff < 30) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (diff < 3600) { // less 1 hour
|
||||||
|
return Math.ceil(diff / 60) + '分钟前'
|
||||||
|
} else if (diff < 3600 * 24) {
|
||||||
|
return Math.ceil(diff / 3600) + '小时前'
|
||||||
|
} else if (diff < 3600 * 24 * 2) {
|
||||||
|
return '1天前'
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
return parseTime(time, option)
|
||||||
|
} else {
|
||||||
|
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 数字 格式化*/
|
||||||
|
export function nFormatter(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 + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function html2Text(val) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = val;
|
||||||
|
return div.textContent || div.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function toThousandslsFilter(num) {
|
||||||
|
return (+num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// The Vue build version to load with the `import` command
|
||||||
|
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
||||||
|
import Vue from 'vue';
|
||||||
|
import App from './App';
|
||||||
|
import router from './router';
|
||||||
|
import store from './store';
|
||||||
|
import ElementUI from 'element-ui';
|
||||||
|
import 'element-ui/lib/theme-default/index.css';
|
||||||
|
import 'assets/custom-theme/index.css'; // https://github.com/PanJiaChen/custom-element-theme
|
||||||
|
import NProgress from 'nprogress';
|
||||||
|
import 'nprogress/nprogress.css';
|
||||||
|
import 'normalize.css/normalize.css';
|
||||||
|
import 'styles/index.scss';
|
||||||
|
import 'components/Icon-svg/index';
|
||||||
|
import 'assets/iconfont/iconfont';
|
||||||
|
import * as filters from './filters';
|
||||||
|
import Multiselect from 'vue-multiselect';
|
||||||
|
import Sticky from 'components/Sticky';
|
||||||
|
import 'vue-multiselect/dist/vue-multiselect.min.css';
|
||||||
|
import vueWaves from './directive/waves';
|
||||||
|
import vueSticky from './directive/sticky';
|
||||||
|
import errLog from 'store/errLog';
|
||||||
|
// import './styles/mixin.scss';
|
||||||
|
|
||||||
|
// register globally
|
||||||
|
Vue.component('multiselect', Multiselect);
|
||||||
|
Vue.component('Sticky', Sticky);
|
||||||
|
Vue.use(ElementUI);
|
||||||
|
Vue.use(vueWaves);
|
||||||
|
Vue.use(vueSticky);
|
||||||
|
|
||||||
|
|
||||||
|
// register global utility filters.
|
||||||
|
Object.keys(filters).forEach(key => {
|
||||||
|
Vue.filter(key, filters[key])
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function hasPermission(roles, permissionRoles) {
|
||||||
|
if (roles.indexOf('admin') >= 0) return true;
|
||||||
|
return roles.some(role => permissionRoles.indexOf(role) >= 0)
|
||||||
|
}
|
||||||
|
// register global progress.
|
||||||
|
const whiteList = ['/login', '/authredirect', '/reset', '/sendpwd'];// 不重定向白名单
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
NProgress.start();
|
||||||
|
if (store.getters.token) {
|
||||||
|
if (to.path === '/login') {
|
||||||
|
next({ path: '/' });
|
||||||
|
} else {
|
||||||
|
console.log('a')
|
||||||
|
if (to.meta && to.meta.role) {
|
||||||
|
if (hasPermission(store.getters.roles, to.meta.role)) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next('/401');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (whiteList.indexOf(to.path) !== -1) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
next('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.afterEach(() => {
|
||||||
|
NProgress.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 异步组件
|
||||||
|
// Vue.component('async-Editor', function (resolve) {
|
||||||
|
// require(['components/Editor'], resolve)
|
||||||
|
// });
|
||||||
|
|
||||||
|
// window.onunhandledrejection = e => {
|
||||||
|
// console.log('unhandled', e.reason, e.promise);
|
||||||
|
// e.preventDefault()
|
||||||
|
// };
|
||||||
|
|
||||||
|
// 生产环境错误日志
|
||||||
|
if (process.env === 'production') {
|
||||||
|
Vue.config.errorHandler = function(err, vm) {
|
||||||
|
console.log(err, window.location.href);
|
||||||
|
errLog.pushLog({
|
||||||
|
err,
|
||||||
|
url: window.location.href,
|
||||||
|
vm
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||||
|
// console.log('window')
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// console.error = (function (origin) {
|
||||||
|
// return function (errorlog) {
|
||||||
|
// // handler();//基于业务的日志记录及数据报错
|
||||||
|
// console.log('console'+errorlog)
|
||||||
|
// origin.call(console, errorlog);
|
||||||
|
// }
|
||||||
|
// })(console.error);
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
const userMap = {
|
||||||
|
admin: {
|
||||||
|
role: ['admin'],
|
||||||
|
token: 'admin',
|
||||||
|
introduction: '我是超级管理员',
|
||||||
|
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b',
|
||||||
|
name: '超级管理员小潘'
|
||||||
|
},
|
||||||
|
editor: {
|
||||||
|
role: ['editor'],
|
||||||
|
token: 'editor',
|
||||||
|
introduction: '我是编辑',
|
||||||
|
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b',
|
||||||
|
name: '普通编辑小张'
|
||||||
|
|
||||||
|
},
|
||||||
|
developer: {
|
||||||
|
role: ['develop'],
|
||||||
|
token: 'develop',
|
||||||
|
introduction: '我是开发',
|
||||||
|
avatar: 'https://wdl.wallstreetcn.com/48a3e1e0-ea2c-4a4e-9928-247645e3428b',
|
||||||
|
name: '工程师小王'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default userMap
|
|
@ -0,0 +1,81 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Router from 'vue-router';
|
||||||
|
|
||||||
|
/* layout*/
|
||||||
|
import Layout from '../views/layout/Layout';
|
||||||
|
|
||||||
|
// dashboard
|
||||||
|
// import dashboard from '../views/dashboard/index';
|
||||||
|
const dashboard = resolve => require(['../views/dashboard/index'], resolve);
|
||||||
|
/* error*/
|
||||||
|
const Err404 = resolve => require(['../views/error/404'], resolve);
|
||||||
|
const Err401 = resolve => require(['../views/error/401'], resolve);
|
||||||
|
/* login*/
|
||||||
|
import Login from '../views/login/';
|
||||||
|
import authRedirect from '../views/login/authredirect';
|
||||||
|
import sendPWD from '../views/login/sendpwd';
|
||||||
|
import reset from '../views/login/reset';
|
||||||
|
|
||||||
|
/* components*/
|
||||||
|
const Tinymce = resolve => require(['../views/components/tinymce'], resolve);
|
||||||
|
const Markdown = resolve => require(['../views/components/markdown'], resolve);
|
||||||
|
|
||||||
|
/* admin*/
|
||||||
|
// const AdminCreateUser = resolve => require(['../views/admin/createUser'], resolve);
|
||||||
|
// const QuicklyCreateUser = resolve => require(['../views/admin/quicklycreate'], resolve);
|
||||||
|
// const UserProfile = resolve => require(['../views/admin/profile'], resolve);
|
||||||
|
// const UsersList = resolve => require(['../views/admin/usersList'], resolve);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Vue.use(Router);
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
mode: 'history',
|
||||||
|
scrollBehavior: () => ({ y: 0 }),
|
||||||
|
routes: [
|
||||||
|
{ path: '/login', component: Login, hidden: true },
|
||||||
|
{ path: '/authredirect', component: authRedirect, hidden: true },
|
||||||
|
{ path: '/sendpwd', component: sendPWD, hidden: true },
|
||||||
|
{ path: '/reset', component: reset, hidden: true },
|
||||||
|
{ path: '/404', component: Err404, hidden: true },
|
||||||
|
{ path: '/401', component: Err401, hidden: true },
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/dashboard',
|
||||||
|
name: '首页',
|
||||||
|
hidden: true,
|
||||||
|
children: [
|
||||||
|
{ path: 'dashboard', component: dashboard }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin',
|
||||||
|
component: Layout,
|
||||||
|
redirect: 'noredirect',
|
||||||
|
name: '组件',
|
||||||
|
icon: 'zujian',
|
||||||
|
children: [
|
||||||
|
{ path: 'tinymce', component: Tinymce, name: '富文本编辑器' },
|
||||||
|
{ path: 'markdown', component: Markdown, name: 'Markdown' }
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: '/admin',
|
||||||
|
// component: Layout,
|
||||||
|
// redirect: 'noredirect',
|
||||||
|
// name: '后台管理',
|
||||||
|
// icon: 'geren1',
|
||||||
|
// children: [
|
||||||
|
// { path: 'createuser', component: AdminCreateUser, name: '管理员', meta: { role: ['admin'] } },
|
||||||
|
// { path: 'list', component: UsersList, name: '后台用户列表', meta: { role: ['super_editor', 'product', 'author_assistant'] } },
|
||||||
|
// { path: 'qicklyCreate', component: QuicklyCreateUser, name: '一键创建账户', meta: { role: ['super_editor', 'gold_editor', 'weex_editor', 'wscn_editor', 'author_assistant', 'product'] } },
|
||||||
|
// { path: 'profile', component: UserProfile, name: '个人' }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
{ path: '*', redirect: '/404', hidden: true }
|
||||||
|
]
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
const errLog = {
|
||||||
|
state: {
|
||||||
|
errLog: []
|
||||||
|
},
|
||||||
|
pushLog(log) {
|
||||||
|
this.state.errLog.unshift(log)
|
||||||
|
},
|
||||||
|
clearLog() {
|
||||||
|
this.state.errLog = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default errLog;
|
|
@ -0,0 +1,15 @@
|
||||||
|
const getters = {
|
||||||
|
sidebar: state => state.app.sidebar,
|
||||||
|
livenewsChannels: state => state.app.livenewsChannels,
|
||||||
|
token: state => state.user.token,
|
||||||
|
avatar: state => state.user.avatar,
|
||||||
|
name: state => state.user.name,
|
||||||
|
uid: state => state.user.uid,
|
||||||
|
email: state => state.user.email,
|
||||||
|
introduction: state => state.user.introduction,
|
||||||
|
auth_type: state => state.user.auth_type,
|
||||||
|
status: state => state.user.status,
|
||||||
|
roles: state => state.user.roles,
|
||||||
|
setting: state => state.user.setting
|
||||||
|
};
|
||||||
|
export default getters
|
|
@ -0,0 +1,17 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
import app from './modules/app';
|
||||||
|
import user from './modules/user';
|
||||||
|
import getters from './getters';
|
||||||
|
|
||||||
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
const store = new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
app,
|
||||||
|
user
|
||||||
|
},
|
||||||
|
getters
|
||||||
|
});
|
||||||
|
|
||||||
|
export default store
|
|
@ -0,0 +1,38 @@
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
|
const app = {
|
||||||
|
state: {
|
||||||
|
sidebar: {
|
||||||
|
opened: !+Cookies.get('sidebarStatus')
|
||||||
|
},
|
||||||
|
theme: 'default',
|
||||||
|
livenewsChannels: Cookies.get('livenewsChannels') || '[]'
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
TOGGLE_SIDEBAR: state => {
|
||||||
|
if (state.sidebar.opened) {
|
||||||
|
Cookies.set('sidebarStatus', 1);
|
||||||
|
} else {
|
||||||
|
Cookies.set('sidebarStatus', 0);
|
||||||
|
}
|
||||||
|
state.sidebar.opened = !state.sidebar.opened;
|
||||||
|
},
|
||||||
|
SET_LIVENEWS_CHANNELS: (status, channels) => {
|
||||||
|
status.livenewsChannels = JSON.stringify(channels);
|
||||||
|
Cookies.set('livenewsChannels', JSON.stringify(channels));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
ToggleSideBar: ({ commit }) => {
|
||||||
|
commit('TOGGLE_SIDEBAR')
|
||||||
|
},
|
||||||
|
setTheme: ({ commit }, theme) => {
|
||||||
|
commit('SET_THEME', theme)
|
||||||
|
},
|
||||||
|
setlivenewsChannels: ({ commit }, channels) => {
|
||||||
|
commit('SET_LIVENEWS_CHANNELS', channels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default app;
|
|
@ -0,0 +1,129 @@
|
||||||
|
// import { loginByEmail, loginByThirdparty } from 'api/login';
|
||||||
|
// import { userInfo, userLogout } from 'api/adminUser';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import userMap from 'mock/login';
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
state: {
|
||||||
|
user: '',
|
||||||
|
status: '',
|
||||||
|
email: '',
|
||||||
|
code: '',
|
||||||
|
uid: undefined,
|
||||||
|
auth_type: '',
|
||||||
|
token: Cookies.get('X-Ivanka-Token'),
|
||||||
|
name: '',
|
||||||
|
avatar: '',
|
||||||
|
introduction: '',
|
||||||
|
roles: [],
|
||||||
|
setting: {
|
||||||
|
articlePlatform: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mutations: {
|
||||||
|
SET_AUTH_TYPE: (state, type) => {
|
||||||
|
state.auth_type = type;
|
||||||
|
},
|
||||||
|
SET_CODE: (state, code) => {
|
||||||
|
state.code = code;
|
||||||
|
},
|
||||||
|
SET_TOKEN: (state, token) => {
|
||||||
|
state.token = token;
|
||||||
|
},
|
||||||
|
SET_UID: (state, uid) => {
|
||||||
|
state.uid = uid;
|
||||||
|
},
|
||||||
|
SET_EMAIL: (state, email) => {
|
||||||
|
state.email = email;
|
||||||
|
},
|
||||||
|
SET_INTRODUCTION: (state, introduction) => {
|
||||||
|
state.introduction = introduction;
|
||||||
|
},
|
||||||
|
SET_SETTING: (state, setting) => {
|
||||||
|
state.setting = setting;
|
||||||
|
},
|
||||||
|
SET_STATUS: (state, status) => {
|
||||||
|
state.status = status;
|
||||||
|
},
|
||||||
|
SET_NAME: (state, name) => {
|
||||||
|
state.name = name;
|
||||||
|
},
|
||||||
|
SET_AVATAR: (state, avatar) => {
|
||||||
|
state.avatar = avatar;
|
||||||
|
},
|
||||||
|
SET_ROLES: (state, roles) => {
|
||||||
|
state.roles = roles;
|
||||||
|
},
|
||||||
|
LOGIN_SUCCESS: () => {
|
||||||
|
console.log('login success')
|
||||||
|
},
|
||||||
|
LOGOUT_USER: state => {
|
||||||
|
state.user = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
// 邮箱登录
|
||||||
|
LoginByEmail({ commit }, userInfo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const email = userInfo.email.split('@')[0];
|
||||||
|
if (userMap[email]) {
|
||||||
|
commit('SET_ROLES', userMap[email].role);
|
||||||
|
commit('SET_TOKEN', userMap[email].token);
|
||||||
|
Cookies.set('X-Ivanka-Token', userMap[email].token);
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject('账号不正确');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 第三方验证登录
|
||||||
|
LoginByThirdparty({ commit, state }, code) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
commit('SET_CODE', code);
|
||||||
|
loginByThirdparty(state.status, state.email, state.code, state.auth_type).then(response => {
|
||||||
|
commit('SET_TOKEN', response.data.token);
|
||||||
|
Cookies.set('X-Ivanka-Token', response.data.token);
|
||||||
|
resolve();
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 获取用户信息
|
||||||
|
GetInfo({ commit, state }) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const token = state.token;
|
||||||
|
commit('SET_ROLES', userMap[token].role);
|
||||||
|
commit('SET_NAME', userMap[token].name);
|
||||||
|
commit('SET_AVATAR', userMap[token].avatar);
|
||||||
|
commit('SET_INTRODUCTION', userMap[token].introduction);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 登出
|
||||||
|
LogOut({ commit, state }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
userLogout(state.token).then(() => {
|
||||||
|
commit('SET_TOKEN', '');
|
||||||
|
Cookies.remove('X-Ivanka-Token');
|
||||||
|
resolve();
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 前端 登出
|
||||||
|
FedLogOut({ commit }) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
commit('SET_TOKEN', '');
|
||||||
|
Cookies.remove('X-Ivanka-Token');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default user;
|
|
@ -0,0 +1,39 @@
|
||||||
|
const permission = {
|
||||||
|
state: {
|
||||||
|
permissionRoutes: []
|
||||||
|
},
|
||||||
|
init(data) {
|
||||||
|
const roles = data.roles;
|
||||||
|
const router = data.router;
|
||||||
|
const permissionRoutes = router.filter(v => {
|
||||||
|
if (roles.indexOf('admin') >= 0) return true;
|
||||||
|
if (this.hasPermission(roles, v)) {
|
||||||
|
if (v.children && v.children.length > 0) {
|
||||||
|
v.children = v.children.filter(child => {
|
||||||
|
if (this.hasPermission(roles, child)) {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return v
|
||||||
|
} else {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
this.permissionRoutes = permissionRoutes;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.permissionRoutes
|
||||||
|
},
|
||||||
|
hasPermission(roles, route) {
|
||||||
|
if (route.meta && route.meta.role) {
|
||||||
|
return roles.some(role => route.meta.role.indexOf(role) >= 0)
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default permission;
|
|
@ -0,0 +1,103 @@
|
||||||
|
$blue:#324157;
|
||||||
|
$light-blue:#3A71A8;
|
||||||
|
$red:#C03639;
|
||||||
|
$pink: #E65D6E;
|
||||||
|
$green: #30B08F;
|
||||||
|
$tiffany: #4AB7BD;
|
||||||
|
$yellow:#FEC171;
|
||||||
|
|
||||||
|
$panGreen: #30B08F;
|
||||||
|
|
||||||
|
@mixin colorBtn($color) {
|
||||||
|
background: $color;
|
||||||
|
&:hover {
|
||||||
|
color: $color;
|
||||||
|
&:before, &:after {
|
||||||
|
background: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.blue-btn {
|
||||||
|
@include colorBtn($blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-blue-btn{
|
||||||
|
@include colorBtn($light-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.red-btn {
|
||||||
|
@include colorBtn($red)
|
||||||
|
}
|
||||||
|
|
||||||
|
.pink-btn {
|
||||||
|
@include colorBtn($pink)
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-btn {
|
||||||
|
@include colorBtn($green)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tiffany-btn {
|
||||||
|
@include colorBtn($tiffany)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.yellow-btn {
|
||||||
|
@include colorBtn($yellow)
|
||||||
|
}
|
||||||
|
|
||||||
|
.pan-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
padding: 14px 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
margin-right: 25px;
|
||||||
|
transition: 600ms ease all;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
&:hover {
|
||||||
|
background: #fff;
|
||||||
|
&:before, &:after {
|
||||||
|
width: 100%;
|
||||||
|
transition: 600ms ease all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:before, &:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
width: 0;
|
||||||
|
transition: 400ms ease all;
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
right: inherit;
|
||||||
|
top: inherit;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-button{
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
color: #fff;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
//富文本
|
||||||
|
//移除 至static/tinymce/skins/lightgray.content.min.css
|
||||||
|
.small-size {
|
||||||
|
width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
img{
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
.note-color .dropdown-menu li .btn-group{
|
||||||
|
&:first-child{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//禁止图片缩放
|
||||||
|
.note-control-sizing {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.panel-body {
|
||||||
|
$blue: #1478F0;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.4em;
|
||||||
|
& > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
embed {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
// margin-bottom: 1em;
|
||||||
|
text-align: justify;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
margin: 1em auto;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background: #DCDCDC;
|
||||||
|
}
|
||||||
|
//add type.css start
|
||||||
|
blockquote p {
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 28px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
blockquote p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
/* HTML5 媒体文件跟 img 保持一致 */
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
video {
|
||||||
|
display: inline-block;
|
||||||
|
*display: inline;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
/* 要注意表单元素并不继承父级 font 的问题 */
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font: 500 14px/1.8 'Hiragino Sans GB', Microsoft YaHei, sans-serif;
|
||||||
|
}
|
||||||
|
/* 去掉各Table cell 的边距并让其边重合 */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
/* IE bug fixed: th 不继承 text-align*/
|
||||||
|
th {
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
/* 去除默认边框 */
|
||||||
|
fieldset,
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
/* 解决 IE6-7 图片缩放锯齿问题 */
|
||||||
|
img {
|
||||||
|
-ms-interpolation-mode: bicubic;
|
||||||
|
}
|
||||||
|
/* ie6 7 8(q) bug 显示为行内表现 */
|
||||||
|
iframe {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* 块/段落引用 */
|
||||||
|
blockquote {
|
||||||
|
position: relative;
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 28px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
&:before{
|
||||||
|
position: absolute;
|
||||||
|
content: " \300D";
|
||||||
|
top: 10px;
|
||||||
|
left: 2px;
|
||||||
|
-webkit-transform: rotate(180deg);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
&:after{
|
||||||
|
position: absolute;
|
||||||
|
content: " \300D";
|
||||||
|
right: 6px;
|
||||||
|
bottom: 12px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockquote blockquote {
|
||||||
|
padding: 0 0 0 1em;
|
||||||
|
margin-left: 2em;
|
||||||
|
border-left: 3px solid $blue;
|
||||||
|
}
|
||||||
|
/* Firefox 以外,元素没有下划线,需添加 */
|
||||||
|
acronym,
|
||||||
|
abbr {
|
||||||
|
border-bottom: 1px dotted;
|
||||||
|
font-variant: normal;
|
||||||
|
}
|
||||||
|
/* 添加鼠标问号,进一步确保应用的语义是正确的(要知道,交互他们也有洁癖,如果你不去掉,那得多花点口舌) */
|
||||||
|
abbr {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
/* 一致的 del 样式 */
|
||||||
|
del {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
address,
|
||||||
|
caption,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
del,
|
||||||
|
em,
|
||||||
|
th,
|
||||||
|
var {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
em {
|
||||||
|
font-style: normal;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
/* 对齐是排版最重要的因素, 别让什么都居中 */
|
||||||
|
caption,
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
q:before,
|
||||||
|
q:after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
/* 统一上标和下标 */
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
:root sub,
|
||||||
|
:root sup {
|
||||||
|
vertical-align: baseline;
|
||||||
|
/* for ie9 and other mordern browsers */
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
/* 让链接在 hover 状态下显示下划线 */
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
/* 默认不显示下划线,保持页面简洁 */
|
||||||
|
ins,
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
u,
|
||||||
|
.typo-u {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
/* 标记,类似于手写的荧光笔的作用 */
|
||||||
|
mark {
|
||||||
|
background: #fffdd1;
|
||||||
|
}
|
||||||
|
/* 代码片断 */
|
||||||
|
pre,
|
||||||
|
code {
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-left-width: 0.4em;
|
||||||
|
background: #fbfbfb;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
/* 底部印刷体、版本等标记 */
|
||||||
|
small {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1478f0;
|
||||||
|
border-left: 5px solid #1478f0;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
/* 保证块/段落之间的空白隔行 */
|
||||||
|
.typo p,
|
||||||
|
.typo pre,
|
||||||
|
.typo ul,
|
||||||
|
.typo ol,
|
||||||
|
.typo dl,
|
||||||
|
.typo form,
|
||||||
|
.typo hr,
|
||||||
|
.typo table,
|
||||||
|
.typo-p,
|
||||||
|
.typo-pre,
|
||||||
|
.typo-ul,
|
||||||
|
.typo-ol,
|
||||||
|
.typo-dl,
|
||||||
|
.typo-form,
|
||||||
|
.typo-hr,
|
||||||
|
.typo-table {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
/* 标题应该更贴紧内容,并与其他块区分,margin 值要相应做优化 */
|
||||||
|
.typo h1,
|
||||||
|
.typo h2,
|
||||||
|
.typo h3,
|
||||||
|
.typo h4,
|
||||||
|
.typo h5,
|
||||||
|
.typo h6,
|
||||||
|
.typo-h1,
|
||||||
|
.typo-h2,
|
||||||
|
.typo-h3,
|
||||||
|
.typo-h4,
|
||||||
|
.typo-h5,
|
||||||
|
.typo-h6 {
|
||||||
|
margin-bottom: 0.4em;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.typo h1,
|
||||||
|
.typo-h1 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
.typo h2,
|
||||||
|
.typo-h2 {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
.typo h3,
|
||||||
|
.typo-h3 {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
.typo h4,
|
||||||
|
.typo-h0 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.typo h5,
|
||||||
|
.typo h6,
|
||||||
|
.typo-h5,
|
||||||
|
.typo-h6 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
/* 在文章中,应该还原 ul 和 ol 的样式 */
|
||||||
|
.typo ul,
|
||||||
|
.typo-ul {
|
||||||
|
margin-left: 1.3em;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
.typo ol,
|
||||||
|
.typo-ol {
|
||||||
|
list-style: decimal;
|
||||||
|
margin-left: 1.9em;
|
||||||
|
}
|
||||||
|
.typo li ul,
|
||||||
|
.typo li ol,
|
||||||
|
.typo-ul ul,
|
||||||
|
.typo-ul ol,
|
||||||
|
.typo-ol ul,
|
||||||
|
.typo-ol ol {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
.typo li ul,
|
||||||
|
.typo-ul ul,
|
||||||
|
.typo-ol ul {
|
||||||
|
list-style: circle;
|
||||||
|
}
|
||||||
|
/* 同 ul/ol,在文章中应用 table 基本格式 */
|
||||||
|
.typo table th,
|
||||||
|
.typo table td,
|
||||||
|
.typo-table th,
|
||||||
|
.typo-table td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
.typo table th,
|
||||||
|
.typo-table th {
|
||||||
|
background: #fbfbfb;
|
||||||
|
}
|
||||||
|
.typo table thead th,
|
||||||
|
.typo-table thead th {
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,392 @@
|
||||||
|
@import './btn.scss';
|
||||||
|
// @import './editor.scss';
|
||||||
|
@import "./mixin.scss";
|
||||||
|
|
||||||
|
body {
|
||||||
|
//height: 100%;
|
||||||
|
//overflow-y: scroll;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif;
|
||||||
|
//@include scrollBar;
|
||||||
|
}
|
||||||
|
label{
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-padding {
|
||||||
|
padding: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-content {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus,
|
||||||
|
a:active {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:focus,
|
||||||
|
a:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fr {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fl {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pr-5 {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-5 {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inlineBlock {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-container{
|
||||||
|
margin: 30px 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code{
|
||||||
|
background: #eef1f6;
|
||||||
|
padding: 20px 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.fade-enter-active, .fade-leave-active {
|
||||||
|
transition: all .2s ease
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter, .fade-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//editor
|
||||||
|
//.editor-placeholder {
|
||||||
|
// margin: 0 auto;
|
||||||
|
// display: block;
|
||||||
|
// .editor-placeholder-title {
|
||||||
|
// text-align: center;
|
||||||
|
// font-size: 20px;
|
||||||
|
// padding-bottom: 5px;
|
||||||
|
// }
|
||||||
|
// .editor-placeholder-image {
|
||||||
|
// display: block;
|
||||||
|
// max-height: 100px;
|
||||||
|
// margin: 0 auto;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//main-container全局样式
|
||||||
|
.app-container {
|
||||||
|
padding: 20px;
|
||||||
|
//min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
//element ui upload
|
||||||
|
.upload-container {
|
||||||
|
.el-upload {
|
||||||
|
width: 100%;
|
||||||
|
.el-upload-dragger {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.singleImageUpload2.upload-container {
|
||||||
|
.el-upload {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.el-upload-dragger {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.el-icon-upload {
|
||||||
|
margin: 30% 0 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-video-upload {
|
||||||
|
@include clearfix;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
.el-upload {
|
||||||
|
float: left;
|
||||||
|
width: 100px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.el-upload-list {
|
||||||
|
float: left;
|
||||||
|
.el-upload-list__item:first-child {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload-list--picture-card {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wscn-icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-navbar {
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 20px;
|
||||||
|
transition: 600ms ease position;
|
||||||
|
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
|
||||||
|
.subtitle {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
&.draft {
|
||||||
|
background: #d0d0d0;
|
||||||
|
}
|
||||||
|
&.deleted {
|
||||||
|
background: #d0d0d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-type,.link-type:focus {
|
||||||
|
color: #337ab7;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover{
|
||||||
|
color: rgb(32, 160, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.publishedTag, .draftTag, .deletedTag {
|
||||||
|
color: #fff;
|
||||||
|
background-color: $panGreen;
|
||||||
|
line-height: 1;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draftTag {
|
||||||
|
background-color: $yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deletedTag {
|
||||||
|
background-color: $red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #48576a;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 11px 5px 11px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix {
|
||||||
|
&:after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-marginLeft {
|
||||||
|
.el-checkbox {
|
||||||
|
margin: 0 20px 15px 0;
|
||||||
|
}
|
||||||
|
.el-checkbox + .el-checkbox {
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-container {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
.filter-item {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//文章页textarea修改样式
|
||||||
|
.article-textarea {
|
||||||
|
textarea {
|
||||||
|
padding-right: 40px;
|
||||||
|
resize: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-bottom: 1px solid #bfcbd9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//实时新闻创建页特殊处理
|
||||||
|
.recentNews-container {
|
||||||
|
p {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.el-collapse-item__content{
|
||||||
|
padding-right:0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//refine vue-multiselect plugin
|
||||||
|
.multiselect {
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect--active {
|
||||||
|
z-index: 1000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset element ui
|
||||||
|
.block-checkbox {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
//上传页面不显示删除icon
|
||||||
|
.mediaUpload-container {
|
||||||
|
.el-upload__btn-delete {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-container {
|
||||||
|
.cell {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
.el-button {
|
||||||
|
&:nth-child(3) {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
&:nth-child(4) {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload {
|
||||||
|
input[type="file"] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
.el-tag {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.small-padding{
|
||||||
|
.cell{
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.status-col {
|
||||||
|
.cell {
|
||||||
|
padding: 0 10px;
|
||||||
|
text-align: center;
|
||||||
|
.el-tag {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//.el-form-item__content{
|
||||||
|
// margin-left: 0px!important;
|
||||||
|
//}
|
||||||
|
.no-border {
|
||||||
|
.el-input-group__prepend, .el-input__inner, .el-date-editor__editor, .multiselect__tags {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__tags {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-space .el-form-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-padding {
|
||||||
|
.el-dropdown-menu__item {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.el-dropdown-menu {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-hover {
|
||||||
|
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tooltip-fullwidth {
|
||||||
|
width: 100%;
|
||||||
|
.el-tooltip__rel {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
|
||||||
|
.el-dialog{
|
||||||
|
transform: none;
|
||||||
|
left: 0;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
@mixin clearfix {
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin scrollBar {
|
||||||
|
&::-webkit-scrollbar-track-piece {
|
||||||
|
background: #d3dce6;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #99a9bf;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin relative {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin pct($pct) {
|
||||||
|
width: #{$pct};
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin triangle($width, $height, $color, $direction) {
|
||||||
|
$width: $width/2;
|
||||||
|
$color-border-style: $height solid $color;
|
||||||
|
$transparent-border-style: $width solid transparent;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
@if $direction == up {
|
||||||
|
border-bottom: $color-border-style;
|
||||||
|
border-left: $transparent-border-style;
|
||||||
|
border-right: $transparent-border-style;
|
||||||
|
} @else if $direction == right {
|
||||||
|
border-left: $color-border-style;
|
||||||
|
border-top: $transparent-border-style;
|
||||||
|
border-bottom: $transparent-border-style;
|
||||||
|
} @else if $direction == down {
|
||||||
|
border-top: $color-border-style;
|
||||||
|
border-left: $transparent-border-style;
|
||||||
|
border-right: $transparent-border-style;
|
||||||
|
} @else if $direction == left {
|
||||||
|
border-right: $color-border-style;
|
||||||
|
border-top: $transparent-border-style;
|
||||||
|
border-bottom: $transparent-border-style;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Created by jiachenpan on 17/3/8.
|
||||||
|
*/
|
||||||
|
export default function createUniqueString() {
|
||||||
|
const timestamp = +new Date() + '';
|
||||||
|
const randomNum = parseInt((1 + Math.random()) * 65536) + '';
|
||||||
|
return (+(randomNum + timestamp)).toString(32);
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Message } from 'element-ui';
|
||||||
|
import store from '../store';
|
||||||
|
import router from '../router';
|
||||||
|
|
||||||
|
export default function fetch(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const instance = axios.create({
|
||||||
|
baseURL: process.env.BASE_API,
|
||||||
|
// timeout: 2000,
|
||||||
|
headers: { 'X-Ivanka-Token': store.getters.token }
|
||||||
|
});
|
||||||
|
instance(options)
|
||||||
|
.then(response => {
|
||||||
|
const res = response.data;
|
||||||
|
if (res.code !== 20000) {
|
||||||
|
console.log(options); // for debug
|
||||||
|
Message({
|
||||||
|
message: res.message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
});
|
||||||
|
// 50014:Token 过期了 50012:其他客户端登录了 50008:非法的token
|
||||||
|
if (res.code === 50008 || res.code === 50014 || res.code === 50012) {
|
||||||
|
Message({
|
||||||
|
message: res.message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
});
|
||||||
|
// router.push({path: '/'})
|
||||||
|
// TODO
|
||||||
|
store.dispatch('FedLogOut').then(() => {
|
||||||
|
router.push({ path: '/login' })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reject(res);
|
||||||
|
}
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Message({
|
||||||
|
message: '发生异常错误,请刷新页面重试,或联系程序员',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
});
|
||||||
|
console.log(error); // for debug
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tpFetch(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const instance = axios.create({
|
||||||
|
// timeout: 2000,
|
||||||
|
});
|
||||||
|
instance(options)
|
||||||
|
.then(response => {
|
||||||
|
const res = response.data;
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Message({
|
||||||
|
message: '发生异常错误,请刷新页面重试,或联系程序员',
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
});
|
||||||
|
console.log(error); // for debug
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
/**
|
||||||
|
* Created by jiachenpan on 16/11/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import showdown from 'showdown' // markdown转化
|
||||||
|
const converter = new showdown.Converter();
|
||||||
|
|
||||||
|
export function parseTime(time, cFormat) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
|
||||||
|
let date;
|
||||||
|
if (typeof time == 'object') {
|
||||||
|
date = time;
|
||||||
|
} else {
|
||||||
|
if (('' + time).length === 10) time = parseInt(time) * 1000;
|
||||||
|
date = new Date(time);
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
};
|
||||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key];
|
||||||
|
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1];
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value;
|
||||||
|
}
|
||||||
|
return value || 0;
|
||||||
|
});
|
||||||
|
return time_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTime(time, option) {
|
||||||
|
time = +time * 1000;
|
||||||
|
const d = new Date(time);
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
const diff = (now - d) / 1000;
|
||||||
|
|
||||||
|
if (diff < 30) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (diff < 3600) { // less 1 hour
|
||||||
|
return Math.ceil(diff / 60) + '分钟前'
|
||||||
|
} else if (diff < 3600 * 24) {
|
||||||
|
return Math.ceil(diff / 3600) + '小时前'
|
||||||
|
} else if (diff < 3600 * 24 * 2) {
|
||||||
|
return '1天前'
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
return parseTime(time, option)
|
||||||
|
} else {
|
||||||
|
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
|
||||||
|
|
||||||
|
export function getQueryObject(url) {
|
||||||
|
url = url == null ? window.location.href : url;
|
||||||
|
const search = url.substring(url.lastIndexOf('?') + 1);
|
||||||
|
const obj = {};
|
||||||
|
const reg = /([^?&=]+)=([^?&=]*)/g;
|
||||||
|
search.replace(reg, (rs, $1, $2) => {
|
||||||
|
const name = decodeURIComponent($1);
|
||||||
|
let val = decodeURIComponent($2);
|
||||||
|
val = String(val);
|
||||||
|
obj[name] = val;
|
||||||
|
return rs;
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*get getByteLen
|
||||||
|
* @param {Sting} val input value
|
||||||
|
* @returns {number} output value
|
||||||
|
*/
|
||||||
|
export function getByteLen(val) {
|
||||||
|
let len = 0;
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
if (val[i].match(/[^\x00-\xff]/ig) != null) {
|
||||||
|
len += 1;
|
||||||
|
} else { len += 0.5; }
|
||||||
|
}
|
||||||
|
return Math.floor(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanArray(actual) {
|
||||||
|
const newArray = [];
|
||||||
|
for (let i = 0; i < actual.length; i++) {
|
||||||
|
if (actual[i]) {
|
||||||
|
newArray.push(actual[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function param(json) {
|
||||||
|
if (!json) return '';
|
||||||
|
return cleanArray(Object.keys(json).map(key => {
|
||||||
|
if (json[key] === undefined) return '';
|
||||||
|
return encodeURIComponent(key) + '=' +
|
||||||
|
encodeURIComponent(json[key]);
|
||||||
|
})).join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function html2Text(val) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = val;
|
||||||
|
return div.textContent || div.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function objectMerge(target, source) {
|
||||||
|
/* Merges two objects,
|
||||||
|
giving the last one precedence */
|
||||||
|
|
||||||
|
if (typeof target !== 'object') {
|
||||||
|
target = {};
|
||||||
|
}
|
||||||
|
if (Array.isArray(source)) {
|
||||||
|
return source.slice();
|
||||||
|
}
|
||||||
|
for (const property in source) {
|
||||||
|
if (source.hasOwnProperty(property)) {
|
||||||
|
const sourceProperty = source[property];
|
||||||
|
if (typeof sourceProperty === 'object') {
|
||||||
|
target[property] = objectMerge(target[property], sourceProperty);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target[property] = sourceProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function scrollTo(element, to, duration) {
|
||||||
|
if (duration <= 0) return;
|
||||||
|
const difference = to - element.scrollTop;
|
||||||
|
const perTick = difference / duration * 10;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log(new Date())
|
||||||
|
element.scrollTop = element.scrollTop + perTick;
|
||||||
|
if (element.scrollTop === to) return;
|
||||||
|
scrollTo(element, to, duration - 10);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleClass(element, className) {
|
||||||
|
if (!element || !className) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let classString = element.className;
|
||||||
|
const nameIndex = classString.indexOf(className);
|
||||||
|
if (nameIndex === -1) {
|
||||||
|
classString += '' + className;
|
||||||
|
} else {
|
||||||
|
classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length);
|
||||||
|
}
|
||||||
|
element.className = classString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pickerOptions = [
|
||||||
|
{
|
||||||
|
text: '今天',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(new Date().toDateString());
|
||||||
|
end.setTime(start.getTime());
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近一周',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date(new Date().toDateString());
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近一个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date(new Date().toDateString());
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近三个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date(new Date().toDateString());
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
export function getTime(type) {
|
||||||
|
if (type === 'start') {
|
||||||
|
return new Date().getTime() - 3600 * 1000 * 24 * 90
|
||||||
|
} else {
|
||||||
|
return new Date(new Date().toDateString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showdownMD(md) {
|
||||||
|
return converter.makeHtml(md)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
*Created by jiachenpan on 16/11/29.
|
||||||
|
* @param {Sting} url
|
||||||
|
* @param {Sting} title
|
||||||
|
* @param {Number} w
|
||||||
|
* @param {Number} h
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function openWindow(url, title, w, h) {
|
||||||
|
// Fixes dual-screen position Most browsers Firefox
|
||||||
|
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
|
||||||
|
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
|
||||||
|
|
||||||
|
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
|
||||||
|
const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
|
||||||
|
|
||||||
|
const left = ((width / 2) - (w / 2)) + dualScreenLeft;
|
||||||
|
const top = ((height / 2) - (h / 2)) + dualScreenTop;
|
||||||
|
const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);
|
||||||
|
|
||||||
|
// Puts focus on the newWindow
|
||||||
|
if (window.focus) {
|
||||||
|
newWindow.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Created by jiachenpan on 16/11/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 是否是公司邮箱*/
|
||||||
|
export function isWscnEmail(str) {
|
||||||
|
const reg = /^[a-z0-9](?:[-_.+]?[a-z0-9]+)*@wallstreetcn\.com$/i;
|
||||||
|
return reg.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 合法uri*/
|
||||||
|
export function validateURL(textval) {
|
||||||
|
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
|
||||||
|
return urlregex.test(textval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小写字母*/
|
||||||
|
export function validateLowerCase(str) {
|
||||||
|
const reg = /^[a-z]+$/;
|
||||||
|
return reg.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证key*/
|
||||||
|
// export function validateKey(str) {
|
||||||
|
// var reg = /^[a-z_\-:]+$/;
|
||||||
|
// return reg.test(str);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* 大写字母*/
|
||||||
|
export function validateUpperCase(str) {
|
||||||
|
const reg = /^[A-Z]+$/;
|
||||||
|
return reg.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大小写字母*/
|
||||||
|
export function validatAlphabets(str) {
|
||||||
|
const reg = /^[A-Za-z]+$/;
|
||||||
|
return reg.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
/* Blob.js
|
||||||
|
* A Blob implementation.
|
||||||
|
* 2014-05-27
|
||||||
|
*
|
||||||
|
* By Eli Grey, http://eligrey.com
|
||||||
|
* By Devin Samarin, https://github.com/eboyjr
|
||||||
|
* License: X11/MIT
|
||||||
|
* See LICENSE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global self, unescape */
|
||||||
|
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||||
|
plusplus: true */
|
||||||
|
|
||||||
|
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
|
||||||
|
|
||||||
|
(function (view) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
view.URL = view.URL || view.webkitURL;
|
||||||
|
|
||||||
|
if (view.Blob && view.URL) {
|
||||||
|
try {
|
||||||
|
new Blob;
|
||||||
|
return;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internally we use a BlobBuilder implementation to base Blob off of
|
||||||
|
// in order to support older browsers that only have BlobBuilder
|
||||||
|
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
|
||||||
|
var
|
||||||
|
get_class = function(object) {
|
||||||
|
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
||||||
|
}
|
||||||
|
, FakeBlobBuilder = function BlobBuilder() {
|
||||||
|
this.data = [];
|
||||||
|
}
|
||||||
|
, FakeBlob = function Blob(data, type, encoding) {
|
||||||
|
this.data = data;
|
||||||
|
this.size = data.length;
|
||||||
|
this.type = type;
|
||||||
|
this.encoding = encoding;
|
||||||
|
}
|
||||||
|
, FBB_proto = FakeBlobBuilder.prototype
|
||||||
|
, FB_proto = FakeBlob.prototype
|
||||||
|
, FileReaderSync = view.FileReaderSync
|
||||||
|
, FileException = function(type) {
|
||||||
|
this.code = this[this.name = type];
|
||||||
|
}
|
||||||
|
, file_ex_codes = (
|
||||||
|
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
|
||||||
|
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
||||||
|
).split(" ")
|
||||||
|
, file_ex_code = file_ex_codes.length
|
||||||
|
, real_URL = view.URL || view.webkitURL || view
|
||||||
|
, real_create_object_URL = real_URL.createObjectURL
|
||||||
|
, real_revoke_object_URL = real_URL.revokeObjectURL
|
||||||
|
, URL = real_URL
|
||||||
|
, btoa = view.btoa
|
||||||
|
, atob = view.atob
|
||||||
|
|
||||||
|
, ArrayBuffer = view.ArrayBuffer
|
||||||
|
, Uint8Array = view.Uint8Array
|
||||||
|
;
|
||||||
|
FakeBlob.fake = FB_proto.fake = true;
|
||||||
|
while (file_ex_code--) {
|
||||||
|
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
||||||
|
}
|
||||||
|
if (!real_URL.createObjectURL) {
|
||||||
|
URL = view.URL = {};
|
||||||
|
}
|
||||||
|
URL.createObjectURL = function(blob) {
|
||||||
|
var
|
||||||
|
type = blob.type
|
||||||
|
, data_URI_header
|
||||||
|
;
|
||||||
|
if (type === null) {
|
||||||
|
type = "application/octet-stream";
|
||||||
|
}
|
||||||
|
if (blob instanceof FakeBlob) {
|
||||||
|
data_URI_header = "data:" + type;
|
||||||
|
if (blob.encoding === "base64") {
|
||||||
|
return data_URI_header + ";base64," + blob.data;
|
||||||
|
} else if (blob.encoding === "URI") {
|
||||||
|
return data_URI_header + "," + decodeURIComponent(blob.data);
|
||||||
|
} if (btoa) {
|
||||||
|
return data_URI_header + ";base64," + btoa(blob.data);
|
||||||
|
} else {
|
||||||
|
return data_URI_header + "," + encodeURIComponent(blob.data);
|
||||||
|
}
|
||||||
|
} else if (real_create_object_URL) {
|
||||||
|
return real_create_object_URL.call(real_URL, blob);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
URL.revokeObjectURL = function(object_URL) {
|
||||||
|
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
||||||
|
real_revoke_object_URL.call(real_URL, object_URL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FBB_proto.append = function(data/*, endings*/) {
|
||||||
|
var bb = this.data;
|
||||||
|
// decode data to a binary string
|
||||||
|
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
||||||
|
var
|
||||||
|
str = ""
|
||||||
|
, buf = new Uint8Array(data)
|
||||||
|
, i = 0
|
||||||
|
, buf_len = buf.length
|
||||||
|
;
|
||||||
|
for (; i < buf_len; i++) {
|
||||||
|
str += String.fromCharCode(buf[i]);
|
||||||
|
}
|
||||||
|
bb.push(str);
|
||||||
|
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
||||||
|
if (FileReaderSync) {
|
||||||
|
var fr = new FileReaderSync;
|
||||||
|
bb.push(fr.readAsBinaryString(data));
|
||||||
|
} else {
|
||||||
|
// async FileReader won't work as BlobBuilder is sync
|
||||||
|
throw new FileException("NOT_READABLE_ERR");
|
||||||
|
}
|
||||||
|
} else if (data instanceof FakeBlob) {
|
||||||
|
if (data.encoding === "base64" && atob) {
|
||||||
|
bb.push(atob(data.data));
|
||||||
|
} else if (data.encoding === "URI") {
|
||||||
|
bb.push(decodeURIComponent(data.data));
|
||||||
|
} else if (data.encoding === "raw") {
|
||||||
|
bb.push(data.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof data !== "string") {
|
||||||
|
data += ""; // convert unsupported types to strings
|
||||||
|
}
|
||||||
|
// decode UTF-16 to binary string
|
||||||
|
bb.push(unescape(encodeURIComponent(data)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FBB_proto.getBlob = function(type) {
|
||||||
|
if (!arguments.length) {
|
||||||
|
type = null;
|
||||||
|
}
|
||||||
|
return new FakeBlob(this.data.join(""), type, "raw");
|
||||||
|
};
|
||||||
|
FBB_proto.toString = function() {
|
||||||
|
return "[object BlobBuilder]";
|
||||||
|
};
|
||||||
|
FB_proto.slice = function(start, end, type) {
|
||||||
|
var args = arguments.length;
|
||||||
|
if (args < 3) {
|
||||||
|
type = null;
|
||||||
|
}
|
||||||
|
return new FakeBlob(
|
||||||
|
this.data.slice(start, args > 1 ? end : this.data.length)
|
||||||
|
, type
|
||||||
|
, this.encoding
|
||||||
|
);
|
||||||
|
};
|
||||||
|
FB_proto.toString = function() {
|
||||||
|
return "[object Blob]";
|
||||||
|
};
|
||||||
|
FB_proto.close = function() {
|
||||||
|
this.size = this.data.length = 0;
|
||||||
|
};
|
||||||
|
return FakeBlobBuilder;
|
||||||
|
}(view));
|
||||||
|
|
||||||
|
view.Blob = function Blob(blobParts, options) {
|
||||||
|
var type = options ? (options.type || "") : "";
|
||||||
|
var builder = new BlobBuilder();
|
||||||
|
if (blobParts) {
|
||||||
|
for (var i = 0, len = blobParts.length; i < len; i++) {
|
||||||
|
builder.append(blobParts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.getBlob(type);
|
||||||
|
};
|
||||||
|
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
|
@ -0,0 +1,141 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
require('script-loader!file-saver');
|
||||||
|
require('script-loader!vendor/Blob');
|
||||||
|
require('script-loader!xlsx/dist/xlsx.core.min');
|
||||||
|
function generateArray(table) {
|
||||||
|
var out = [];
|
||||||
|
var rows = table.querySelectorAll('tr');
|
||||||
|
var ranges = [];
|
||||||
|
for (var R = 0; R < rows.length; ++R) {
|
||||||
|
var outRow = [];
|
||||||
|
var row = rows[R];
|
||||||
|
var columns = row.querySelectorAll('td');
|
||||||
|
for (var C = 0; C < columns.length; ++C) {
|
||||||
|
var cell = columns[C];
|
||||||
|
var colspan = cell.getAttribute('colspan');
|
||||||
|
var rowspan = cell.getAttribute('rowspan');
|
||||||
|
var cellValue = cell.innerText;
|
||||||
|
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
|
||||||
|
|
||||||
|
//Skip ranges
|
||||||
|
ranges.forEach(function (range) {
|
||||||
|
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
|
||||||
|
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Handle Row Span
|
||||||
|
if (rowspan || colspan) {
|
||||||
|
rowspan = rowspan || 1;
|
||||||
|
colspan = colspan || 1;
|
||||||
|
ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
//Handle Value
|
||||||
|
outRow.push(cellValue !== "" ? cellValue : null);
|
||||||
|
|
||||||
|
//Handle Colspan
|
||||||
|
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
|
||||||
|
}
|
||||||
|
out.push(outRow);
|
||||||
|
}
|
||||||
|
return [out, ranges];
|
||||||
|
};
|
||||||
|
|
||||||
|
function datenum(v, date1904) {
|
||||||
|
if (date1904) v += 1462;
|
||||||
|
var epoch = Date.parse(v);
|
||||||
|
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sheet_from_array_of_arrays(data, opts) {
|
||||||
|
var ws = {};
|
||||||
|
var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
|
||||||
|
for (var R = 0; R != data.length; ++R) {
|
||||||
|
for (var C = 0; C != data[R].length; ++C) {
|
||||||
|
if (range.s.r > R) range.s.r = R;
|
||||||
|
if (range.s.c > C) range.s.c = C;
|
||||||
|
if (range.e.r < R) range.e.r = R;
|
||||||
|
if (range.e.c < C) range.e.c = C;
|
||||||
|
var cell = {v: data[R][C]};
|
||||||
|
if (cell.v == null) continue;
|
||||||
|
var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
|
||||||
|
|
||||||
|
if (typeof cell.v === 'number') cell.t = 'n';
|
||||||
|
else if (typeof cell.v === 'boolean') cell.t = 'b';
|
||||||
|
else if (cell.v instanceof Date) {
|
||||||
|
cell.t = 'n';
|
||||||
|
cell.z = XLSX.SSF._table[14];
|
||||||
|
cell.v = datenum(cell.v);
|
||||||
|
}
|
||||||
|
else cell.t = 's';
|
||||||
|
|
||||||
|
ws[cell_ref] = cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Workbook() {
|
||||||
|
if (!(this instanceof Workbook)) return new Workbook();
|
||||||
|
this.SheetNames = [];
|
||||||
|
this.Sheets = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function s2ab(s) {
|
||||||
|
var buf = new ArrayBuffer(s.length);
|
||||||
|
var view = new Uint8Array(buf);
|
||||||
|
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function export_table_to_excel(id) {
|
||||||
|
var theTable = document.getElementById(id);
|
||||||
|
console.log('a')
|
||||||
|
var oo = generateArray(theTable);
|
||||||
|
var ranges = oo[1];
|
||||||
|
|
||||||
|
/* original data */
|
||||||
|
var data = oo[0];
|
||||||
|
var ws_name = "SheetJS";
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
|
||||||
|
|
||||||
|
/* add ranges to worksheet */
|
||||||
|
// ws['!cols'] = ['apple', 'banan'];
|
||||||
|
ws['!merges'] = ranges;
|
||||||
|
|
||||||
|
/* add worksheet to workbook */
|
||||||
|
wb.SheetNames.push(ws_name);
|
||||||
|
wb.Sheets[ws_name] = ws;
|
||||||
|
|
||||||
|
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
|
||||||
|
|
||||||
|
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatJson(jsonData) {
|
||||||
|
console.log(jsonData)
|
||||||
|
}
|
||||||
|
export function export_json_to_excel(th, jsonData, defaultTitle) {
|
||||||
|
|
||||||
|
/* original data */
|
||||||
|
|
||||||
|
var data = jsonData;
|
||||||
|
data.unshift(th);
|
||||||
|
var ws_name = "SheetJS";
|
||||||
|
|
||||||
|
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
|
||||||
|
|
||||||
|
|
||||||
|
/* add worksheet to workbook */
|
||||||
|
wb.SheetNames.push(ws_name);
|
||||||
|
wb.Sheets[ws_name] = ws;
|
||||||
|
|
||||||
|
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
|
||||||
|
var title = defaultTitle || '列表'
|
||||||
|
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<h1 class="page-heading">
|
||||||
|
创建后台用户
|
||||||
|
</h1>
|
||||||
|
<el-form ref="createForm" :rules="createRules" label-position="left" style='width:80%' :model="form" label-width="100px">
|
||||||
|
<el-form-item label="用户邮箱" prop="email">
|
||||||
|
<el-input v-model="form.email" placeholder="公司邮箱"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="权限选择" >
|
||||||
|
<el-select style="width: 100%" v-model="form.role" multiple placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in roleList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="loading" @click.native.prevent="onSubmit">立即创建</el-button>
|
||||||
|
<el-button>
|
||||||
|
<router-link class="normal_link" to="/index">
|
||||||
|
取消
|
||||||
|
</router-link>
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { createNewUser, getRoleList } from 'api/adminUser';
|
||||||
|
import { isWscnEmail } from 'utils/validate';
|
||||||
|
|
||||||
|
export default{
|
||||||
|
name: 'createUser',
|
||||||
|
data() {
|
||||||
|
const validateEmail = (rule, value, callback) => {
|
||||||
|
if (!isWscnEmail(value)) {
|
||||||
|
callback(new Error('邮箱错误'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
roleList: [],
|
||||||
|
loading: false,
|
||||||
|
form: {
|
||||||
|
email: '',
|
||||||
|
role: ''
|
||||||
|
},
|
||||||
|
createRules: {
|
||||||
|
email: [
|
||||||
|
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||||
|
{ validator: validateEmail }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit() {
|
||||||
|
this.$refs.createForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.loading = true;
|
||||||
|
const data = {
|
||||||
|
email: this.form.email,
|
||||||
|
roles: this.form.role
|
||||||
|
};
|
||||||
|
createNewUser(data).then(() => {
|
||||||
|
this.$message.success('创建成功');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$message.error('error submit!!');
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
getRoleList().then(response => {
|
||||||
|
const roleMap = response.data.role_map;
|
||||||
|
const keyArr = Object.keys(roleMap);
|
||||||
|
this.roleList = keyArr.map(v => ({ value: v, label: roleMap[v] }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,404 @@
|
||||||
|
<template>
|
||||||
|
<div class="profile-container clearfix">
|
||||||
|
<div style="position: relative;margin-left: 30px;">
|
||||||
|
<PanThumb :image="avatar"> 你的权限:
|
||||||
|
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||||
|
</PanThumb>
|
||||||
|
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;"
|
||||||
|
@click="handleImagecropper">修改头像
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!--popover-->
|
||||||
|
<el-popover
|
||||||
|
ref="popoverWX"
|
||||||
|
placement="top"
|
||||||
|
width="160"
|
||||||
|
trigger="click"
|
||||||
|
v-model="popoverVisibleWX">
|
||||||
|
<p>你确定要解绑么?</p>
|
||||||
|
<div style="text-align: right; margin: 0">
|
||||||
|
<el-button size="mini" type="text" @click="popoverVisibleWX = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="mini" @click="handleUnbind('wechat')">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<el-popover
|
||||||
|
ref="popoverQQ"
|
||||||
|
placement="top"
|
||||||
|
width="160"
|
||||||
|
trigger="click"
|
||||||
|
v-model="popoverVisibleQQ">
|
||||||
|
<p>你确定要解绑么?</p>
|
||||||
|
<div style="text-align: right; margin: 0">
|
||||||
|
<el-button size="mini" type="text" @click="popoverVisibleQQ = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="mini" @click="handleUnbind('tencent')">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
<!--popover End-->
|
||||||
|
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="line-height: 36px;">个人资料</span>
|
||||||
|
</div>
|
||||||
|
<div class="box-item">
|
||||||
|
<span class="field-label">昵称</span>
|
||||||
|
<div class="field-content">
|
||||||
|
{{name}}
|
||||||
|
<el-button class="edit-btn" @click="handleEditName" type="primary" icon="edit"
|
||||||
|
size="small"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-item">
|
||||||
|
<span class="field-label">简介</span>
|
||||||
|
<div class="field-content">
|
||||||
|
{{introduction.length==0?'未填写':introduction}}
|
||||||
|
<el-button class="edit-btn" @click="handleIntroductionName" type="primary" icon="edit"
|
||||||
|
size="small"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-item" style="margin-bottom: 10px;">
|
||||||
|
<span class="field-label">密码</span>
|
||||||
|
<div class="field-content">
|
||||||
|
<el-button type="primary" @click="resetPSWdialogVisible=true">修改密码</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-item" style="margin-top: 5px;">
|
||||||
|
<div class="field-content">
|
||||||
|
<span class="wx-svg-container"><wscn-icon-svg icon-class="weixin" class="icon"/></span>
|
||||||
|
<el-button class="unbind-btn" v-popover:popoverWX type="danger">解绑微信</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box-item">
|
||||||
|
<div class="field-content">
|
||||||
|
<span class="qq-svg-container"><wscn-icon-svg icon-class="QQ" class="icon"/></span>
|
||||||
|
<el-button class="unbind-btn" v-popover:popoverQQ style="padding: 10px 18px" type="danger">
|
||||||
|
解绑QQ
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span style="line-height: 36px;">偏好设置</span>
|
||||||
|
<el-button @click="updateSetting" style="float: right;margin-top: 5px;" size="small" type="success">
|
||||||
|
更新偏好
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="box-item">
|
||||||
|
<span class="field-label">文章平台默认项选择:</span>
|
||||||
|
<el-checkbox-group v-model="articlePlatform">
|
||||||
|
<el-checkbox label="wscn-platform">见闻</el-checkbox>
|
||||||
|
<el-checkbox label="gold-platform">黄金头条</el-checkbox>
|
||||||
|
<el-checkbox label="weex-platform">WEEX</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
<span class="field-label">使用自定义主题:</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="theme"
|
||||||
|
on-text=""
|
||||||
|
off-text="">
|
||||||
|
</el-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
|
||||||
|
<ImageCropper field="img"
|
||||||
|
:width="300"
|
||||||
|
:height="300"
|
||||||
|
url="/upload"
|
||||||
|
@crop-upload-success="cropSuccess"
|
||||||
|
:key="imagecropperKey"
|
||||||
|
v-show="imagecropperShow"></ImageCropper>
|
||||||
|
|
||||||
|
<el-dialog title="昵称" v-model="nameDialogFormVisible">
|
||||||
|
<el-form label-position="left" label-width="50px">
|
||||||
|
<el-form-item label="昵称" style="width: 300px;">
|
||||||
|
<input class="input" ref="nameInput" :value="name" autocomplete="off" :maxlength=10>
|
||||||
|
<span>(最多填写十个字符)</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="nameDialogFormVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="setName">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="简介" v-model="introductionDialogFormVisible">
|
||||||
|
<el-form label-position="left" label-width="50px">
|
||||||
|
<el-form-item label="简介" style="width: 500px;">
|
||||||
|
<textarea :row=3 class="textarea" ref="introductionInput" :value="introduction"></textarea>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="introductionDialogFormVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="setIntroduction">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="提示" v-model="resetPSWdialogVisible" size="tiny">
|
||||||
|
<span>你确定要重设密码么? <strong>     ( 注:重设密码将会登出,请注意!!! )</strong></span>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="resetPSWdialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="resetPSW">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import { updateInfo, unbind, updateSetting } from 'api/adminUser';
|
||||||
|
import ImageCropper from 'components/ImageCropper';
|
||||||
|
import PanThumb from 'components/PanThumb';
|
||||||
|
import { toggleClass } from 'utils'
|
||||||
|
|
||||||
|
export default{
|
||||||
|
components: { ImageCropper, PanThumb },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nameDialogFormVisible: false,
|
||||||
|
introductionDialogFormVisible: false,
|
||||||
|
resetPSWdialogVisible: false,
|
||||||
|
popoverVisibleQQ: false,
|
||||||
|
popoverVisibleWX: false,
|
||||||
|
imagecropperShow: false,
|
||||||
|
imagecropperKey: 0,
|
||||||
|
articlePlatform: [],
|
||||||
|
theme: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.setting.articlePlatform) {
|
||||||
|
this.articlePlatform = this.setting.articlePlatform
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'name',
|
||||||
|
'avatar',
|
||||||
|
'email',
|
||||||
|
'introduction',
|
||||||
|
'roles',
|
||||||
|
'uid',
|
||||||
|
'setting'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
theme() {
|
||||||
|
toggleClass(document.body, 'custom-theme')
|
||||||
|
// this.$store.dispatch('setTheme', value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resetPSW() {
|
||||||
|
this.$store.dispatch('LogOut').then(() => {
|
||||||
|
this.$router.push({ path: '/sendpwd' })
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleResetDialog(state) {
|
||||||
|
this.resetDialogVisible = state;
|
||||||
|
},
|
||||||
|
handleEditName() {
|
||||||
|
this.nameDialogFormVisible = true;
|
||||||
|
},
|
||||||
|
handleIntroductionName() {
|
||||||
|
this.introductionDialogFormVisible = true;
|
||||||
|
},
|
||||||
|
setName() {
|
||||||
|
const displayName = this.$refs.nameInput.value;
|
||||||
|
const data = {
|
||||||
|
display_name: displayName,
|
||||||
|
uid: this.uid
|
||||||
|
};
|
||||||
|
updateInfo(data).then(() => {
|
||||||
|
this.$store.commit('SET_NAME', displayName);
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '昵称修改成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.nameDialogFormVisible = false;
|
||||||
|
},
|
||||||
|
setIntroduction() {
|
||||||
|
const introduction = this.$refs.introductionInput.value;
|
||||||
|
const data = {
|
||||||
|
introduction,
|
||||||
|
uid: this.uid
|
||||||
|
};
|
||||||
|
updateInfo(data).then(() => {
|
||||||
|
this.$store.commit('SET_INTRODUCTION', introduction);
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '简介修改成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.introductionDialogFormVisible = false;
|
||||||
|
},
|
||||||
|
handleUnbind(unbindType) {
|
||||||
|
const data = {
|
||||||
|
unbind_type: unbindType
|
||||||
|
};
|
||||||
|
unbind(data).then(() => {
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '解绑成功,即将登出',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$store.dispatch('LogOut').then(() => {
|
||||||
|
this.$router.push({ path: '/login' })
|
||||||
|
});
|
||||||
|
}, 3000)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.popoverVisibleQQ = false;
|
||||||
|
this.popoverVisibleWX = false;
|
||||||
|
},
|
||||||
|
handleImagecropper() {
|
||||||
|
this.imagecropperShow = true;
|
||||||
|
this.imagecropperKey = this.imagecropperKey + 1;
|
||||||
|
},
|
||||||
|
cropSuccess(url) {
|
||||||
|
this.imagecropperShow = false;
|
||||||
|
const data = {
|
||||||
|
image: url,
|
||||||
|
uid: this.uid
|
||||||
|
};
|
||||||
|
updateInfo(data).then(() => {
|
||||||
|
this.$store.commit('SET_AVATAR', url);
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '头像修改成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateSetting() {
|
||||||
|
const obj = Object.assign(this.setting, { articlePlatform: this.articlePlatform });
|
||||||
|
updateSetting({ setting: JSON.stringify(obj) }).then(() => {
|
||||||
|
this.$store.commit('SET_SETTING', this.setting);
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '更新偏好成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.input {
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #bfcbd9;
|
||||||
|
color: #1f2d3d;
|
||||||
|
display: block;
|
||||||
|
font-size: inherit;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 3px 10px;
|
||||||
|
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
height: 90px;
|
||||||
|
display: block;
|
||||||
|
resize: vertical;
|
||||||
|
padding: 5px 7px;
|
||||||
|
line-height: 1.5;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #1f2d3d;
|
||||||
|
background-color: #fff;
|
||||||
|
background-image: none;
|
||||||
|
border: 1px solid #bfcbd9;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wx-svg-container, .qq-svg-container {
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-right: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wx-svg-container {
|
||||||
|
background-color: #8dc349;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qq-svg-container {
|
||||||
|
background-color: #6BA2D6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unbind-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: -60px;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-container {
|
||||||
|
padding: 20px;
|
||||||
|
.box-card {
|
||||||
|
width: 400px;
|
||||||
|
margin: 30px;
|
||||||
|
float: left;
|
||||||
|
.field-label {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 36px;
|
||||||
|
color: #333;
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
.box-item {
|
||||||
|
.field-content {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
.edit-btn {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
right: -50px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.edit-btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pan-info-roles {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container quicklyCreateUser-container">
|
||||||
|
<el-form ref="form" :rules="rules" :model="form" label-position="left" label-width="60px">
|
||||||
|
<el-card style=" margin-top: 50px;width: 600px;">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="20">
|
||||||
|
<el-form-item label="昵称" prop="display_name">
|
||||||
|
<el-input v-model="form.display_name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-button type="success" @click="onSubmit">立即创建</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-button style="height: 150px;width: 150px;" @click="handleImagecropper" type="primary">上传头像
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<img style=" float:right;width: 150px;height: 150px;border-radius: 50%;margin-left: 50px;"
|
||||||
|
:src="form.image">
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
|
||||||
|
<el-tooltip style="position: absolute;margin-left: 750px;top: 380px" placement="top">
|
||||||
|
<el-button>Tooltip</el-button>
|
||||||
|
<div slot="content">昵称为必填项<br/><br/>一键创建只能创建后台虚拟账号<br/><br/>没有任何实际操作能力</div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<ImageCropper field="img"
|
||||||
|
:width="300"
|
||||||
|
:height="300"
|
||||||
|
url="/upload"
|
||||||
|
@crop-upload-success="cropSuccess"
|
||||||
|
:key="imagecropperKey"
|
||||||
|
v-show="imagecropperShow">
|
||||||
|
|
||||||
|
</ImageCropper>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { createNewUser } from 'api/adminUser';
|
||||||
|
import ImageCropper from 'components/ImageCropper';
|
||||||
|
|
||||||
|
export default{
|
||||||
|
name: 'quicklyCreateUser',
|
||||||
|
components: { ImageCropper },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
display_name: '',
|
||||||
|
image: '',
|
||||||
|
role: ['virtual_editor']
|
||||||
|
},
|
||||||
|
imagecropperShow: false,
|
||||||
|
imagecropperKey: 0,
|
||||||
|
rules: {
|
||||||
|
display_name: [{ required: true, message: '昵称必填', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit() {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
createNewUser(this.form).then(() => {
|
||||||
|
this.$message.success('创建成功')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('error submit!!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleImagecropper() {
|
||||||
|
this.imagecropperShow = true;
|
||||||
|
this.imagecropperKey = this.imagecropperKey + 1;
|
||||||
|
},
|
||||||
|
cropSuccess(url) {
|
||||||
|
this.imagecropperShow = false;
|
||||||
|
this.form.image = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container adminUsers-list-container">
|
||||||
|
<div class="filter-container">
|
||||||
|
<el-input @keyup.enter.native="handleFilter" style="width:135px;" class="filter-item" placeholder="ID" type="number" v-model="listQuery.uid">
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<el-input style="width:135px;" class="filter-item" placeholder="Name" @keyup.enter.native="handleFilter" v-model="listQuery.display_name">
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<el-input class="filter-item" style="width:300px;display: inline-table" placeholder="email" @keyup.enter.native="handleFilter"
|
||||||
|
v-model="listQuery.email">
|
||||||
|
<template slot="append">@wallstreetcn.com</template>
|
||||||
|
</el-input>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<el-select style="width: 250px" class="filter-item" v-model="listQuery.role" clearable placeholder="权限">
|
||||||
|
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-button class="filter-item" type="primary" icon="search" @click="handleFilter">
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button class="filter-item" type="danger" @click="resetFilter">重置筛选项</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row>
|
||||||
|
<el-table-column label="ID" width="130">
|
||||||
|
<template scope="scope">
|
||||||
|
<span>{{scope.row.uid}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="Name">
|
||||||
|
<template scope="scope">
|
||||||
|
<span>{{scope.row.display_name}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="Email">
|
||||||
|
<template scope="scope">
|
||||||
|
<span>{{scope.row.email}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="Role">
|
||||||
|
<template scope="scope">
|
||||||
|
<el-tag style="margin-right: 5px;" v-for='item in scope.row.roles' :key='item+scope.row.uid' type="primary">
|
||||||
|
{{item}}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="操作" width="170">
|
||||||
|
<template scope="scope">
|
||||||
|
<el-button size="small" type="success" @click="handleEdit(scope.row)">编辑权限</el-button>
|
||||||
|
<el-button size="small" v-if='scope.row.roles.indexOf("virtual_editor")>=0||hasRoleEdit' type="primary" @click="handleInfo(scope.row)">修改</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div v-show="!listLoading" class="pagination-container">
|
||||||
|
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page" :page-sizes="[10,20,30, 50]"
|
||||||
|
:page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog title="编辑" v-model="dialogFormVisible" size='small'>
|
||||||
|
<el-form :model="tempForm" label-position="left" label-width="70px">
|
||||||
|
<el-form-item label="权限">
|
||||||
|
<el-select style="width: 100%" v-model="tempForm.roles" multiple placeholder="请选择">
|
||||||
|
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="updateUser">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="编辑简介" v-model="dialogInfoVisible">
|
||||||
|
<el-form :model="infoForm">
|
||||||
|
<el-form-item label="昵称">
|
||||||
|
<el-input v-model="infoForm.display_name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="简介">
|
||||||
|
<el-input type="textarea" :autosize="{ minRows: 2}" v-model="infoForm.introduction"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="头像">
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<div style='width:200px;height:200px;'>
|
||||||
|
<singleImageUpload2 v-model="infoForm.image"></singleImageUpload2>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogInfoVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="submitInfo">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import { getRoleList, updateInfo } from 'api/adminUser';
|
||||||
|
import { getUserList } from 'api/remoteSearch';
|
||||||
|
import { objectMerge } from 'utils';
|
||||||
|
import singleImageUpload2 from 'components/Upload/singleImage2';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'adminUsersList',
|
||||||
|
components: { singleImageUpload2 },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list: null,
|
||||||
|
total: null,
|
||||||
|
listLoading: true,
|
||||||
|
listQuery: {
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
role: '',
|
||||||
|
uid: undefined,
|
||||||
|
display_name: ''
|
||||||
|
},
|
||||||
|
roleOptions: [],
|
||||||
|
dialogFormVisible: false,
|
||||||
|
tempForm: {
|
||||||
|
uid: null,
|
||||||
|
roles: []
|
||||||
|
},
|
||||||
|
dialogInfoVisible: false,
|
||||||
|
infoForm: {
|
||||||
|
uid: null,
|
||||||
|
image: '',
|
||||||
|
display_name: '',
|
||||||
|
introduction: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'roles'
|
||||||
|
]),
|
||||||
|
hasRoleEdit() {
|
||||||
|
if (this.roles.indexOf('admin') >= 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
this.getAdminRoleList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
getUserList(this.listQuery).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
this.list = data.items;
|
||||||
|
this.total = data.count;
|
||||||
|
this.listLoading = false;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetFilter() {
|
||||||
|
this.listQuery = {
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
role: '',
|
||||||
|
uid: undefined,
|
||||||
|
display_name: ''
|
||||||
|
}
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.listQuery.limit = val;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.listQuery.page = val;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
handleFilter() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
getAdminRoleList() {
|
||||||
|
getRoleList().then(response => {
|
||||||
|
const roleMap = response.data.role_map;
|
||||||
|
const keyArr = Object.keys(roleMap);
|
||||||
|
this.roleOptions = keyArr.map(v => ({ value: v, label: roleMap[v] }));
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleEdit(row) {
|
||||||
|
this.dialogFormVisible = true;
|
||||||
|
objectMerge(this.tempForm, row);
|
||||||
|
},
|
||||||
|
updateUser() {
|
||||||
|
updateInfo(this.tempForm).then(() => {
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '修改成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
for (const item of this.list) {
|
||||||
|
if (item.uid === this.tempForm.uid) {
|
||||||
|
const index = this.list.indexOf(item);
|
||||||
|
this.list[index] = objectMerge(this.list[index], this.tempForm);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dialogFormVisible = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleInfo(row) {
|
||||||
|
this.dialogInfoVisible = true;
|
||||||
|
objectMerge(this.infoForm, row);
|
||||||
|
},
|
||||||
|
submitInfo() {
|
||||||
|
updateInfo(this.infoForm).then(() => {
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '修改成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
for (const item of this.list) {
|
||||||
|
if (item.uid === this.infoForm.uid) {
|
||||||
|
const index = this.list.indexOf(item);
|
||||||
|
this.list[index] = objectMerge(this.list[index], this.infoForm);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dialogInfoVisible = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div class="errorpage-container"> 404
|
||||||
|
<splitPane v-on:resize="resize" split="vertical">
|
||||||
|
<template slot="paneL">
|
||||||
|
<div class="left-container"></div>
|
||||||
|
</template>
|
||||||
|
<template slot="paneR">
|
||||||
|
<splitPane split="horizontal">
|
||||||
|
<template slot="paneL">
|
||||||
|
<div class="top-container"></div>
|
||||||
|
</template>
|
||||||
|
<template slot="paneR">
|
||||||
|
<div class="bottom-container">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</splitPane>
|
||||||
|
</template>
|
||||||
|
</splitPane>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import splitPane from 'components/SplitPane/SplitPane'
|
||||||
|
export default {
|
||||||
|
components: { splitPane },
|
||||||
|
methods: {
|
||||||
|
resize() {
|
||||||
|
console.log('resize')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.errorpage-container{
|
||||||
|
position: relative;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.left-container {
|
||||||
|
background-color: #F38181;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-container {
|
||||||
|
background-color: #FCE38A;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-container {
|
||||||
|
background-color: #FCE38A;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-container {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #95E1D3;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<div class="components-container">
|
||||||
|
<code>公司做的后台主要是一个cms系统,公司也是已自媒体为核心的,所以富文本是后台很核心的功能。在选择富文本的过程中也走了不少的弯路,市面上常见的富文本都基本用过了,最终选择了tinymce</code>
|
||||||
|
<div class="editor-container">
|
||||||
|
<MdEditor id='contentEditor' ref="contentEditor" v-model='content' :height="150"></MdEditor>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import MdEditor from 'components/MdEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { MdEditor },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
content: 'Simplemde'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<div class="components-container">
|
||||||
|
<code>公司做的后台主要是一个cms系统,公司也是已自媒体为核心的,所以富文本是后台很核心的功能。在选择富文本的过程中也走了不少的弯路,市面上常见的富文本都基本用过了,最终选择了tinymce</code>
|
||||||
|
<div class="editor-container">
|
||||||
|
<Tinymce :height=200 ref="editor" v-model="content"></Tinymce>
|
||||||
|
</div>
|
||||||
|
<!--<div class='editor-content'>
|
||||||
|
{{content}}
|
||||||
|
</div>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import Tinymce from 'components/Tinymce';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Tinymce },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
content: 'Tinymce'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-editor-container">
|
||||||
|
<div class=" clearfix">
|
||||||
|
<PanThumb style="float: left" :image="avatar"> 你的权限:
|
||||||
|
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||||
|
</PanThumb>
|
||||||
|
<div class="info-container">
|
||||||
|
<span class="display_name">{{name}}</span>
|
||||||
|
<span style='font-size:20px;padding-top:20px;display:inline-block;'>赶紧把你们想要的快捷键报给产品锦鲤!</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img class='emptyGif' :src="emptyGif" >
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import PanThumb from 'components/PanThumb';
|
||||||
|
import emptyGif from 'assets/gifs/business_fella.gif';
|
||||||
|
export default {
|
||||||
|
name: 'dashboard-default',
|
||||||
|
components: { PanThumb },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
emptyGif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'name',
|
||||||
|
'avatar',
|
||||||
|
'email',
|
||||||
|
'uid',
|
||||||
|
'introduction',
|
||||||
|
'roles'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.emptyGif {
|
||||||
|
display: block;
|
||||||
|
width: 45%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.dashboard-editor-container {
|
||||||
|
background-color: #e3e3e3;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin-top: -50px;
|
||||||
|
padding: 100px 60px 0px;
|
||||||
|
.pan-info-roles {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.info-container {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 190px;
|
||||||
|
height: 150px;
|
||||||
|
line-height: 200px;
|
||||||
|
.display_name {
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
color: #212121;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<div class="articlesChart-container">
|
||||||
|
<span class="articlesChart-container-title">每天撸文</span>
|
||||||
|
<line-chart :listData='listData' ></line-chart>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import LineChart from 'components/Charts/line';
|
||||||
|
export default {
|
||||||
|
name: 'articlesChart',
|
||||||
|
components: { LineChart },
|
||||||
|
props: {
|
||||||
|
listData: {
|
||||||
|
type: Array,
|
||||||
|
default: [],
|
||||||
|
require: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.articlesChart-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.articlesChart-container-title {
|
||||||
|
color: #7F8C8D;
|
||||||
|
font-size: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,284 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-editor-container">
|
||||||
|
<div class=" clearfix">
|
||||||
|
<PanThumb style="float: left" :image="avatar"> 你的权限:
|
||||||
|
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
|
||||||
|
</PanThumb>
|
||||||
|
<div class="info-container">
|
||||||
|
<span class="display_name">{{name}}</span>
|
||||||
|
<div class="info-wrapper">
|
||||||
|
<router-link class="info-item" :to="'/article/wscnlist?uid='+uid">
|
||||||
|
<span class="info-item-num">{{statisticsData.article_count | toThousandslsFilter}}</span>
|
||||||
|
<span class="info-item-text">文章</span>
|
||||||
|
<wscn-icon-svg icon-class="a" class="dashboard-editor-icon"/>
|
||||||
|
</router-link>
|
||||||
|
<div class="info-item" style="cursor: auto">
|
||||||
|
<span class="info-item-num"> {{statisticsData.pageviews_count | toThousandslsFilter}}</span>
|
||||||
|
<span class="info-item-text">浏览量</span>
|
||||||
|
<wscn-icon-svg icon-class="b" class="dashboard-editor-icon"/>
|
||||||
|
</div>
|
||||||
|
<router-link class="info-item" :to="'/comment/commentslist?res_author_id='+uid">
|
||||||
|
<span class="info-item-num">{{statisticsData.comment_count | toThousandslsFilter}}</span>
|
||||||
|
<span class="info-item-text">评论</span>
|
||||||
|
<wscn-icon-svg icon-class="c" class="dashboard-editor-icon"/>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<router-link class="pan-btn blue-btn" to="/article/create">发表文章</router-link>
|
||||||
|
<router-link class="pan-btn light-blue-btn" to="/livenews/create">发布快讯</router-link>
|
||||||
|
<router-link class="pan-btn red-btn" to="/push/create">推送</router-link>
|
||||||
|
<router-link class="pan-btn pink-btn" to="/comment/commentslist">评论管理</router-link>
|
||||||
|
<router-link class="pan-btn green-btn" to="/article/wscnlist">文章列表</router-link>
|
||||||
|
<router-link class="pan-btn tiffany-btn" to="/livenews/list">实时列表</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix main-dashboard-container">
|
||||||
|
<div class="chart-container">
|
||||||
|
<MonthKpi style="border-bottom: 1px solid #DEE1E2;"
|
||||||
|
:articlesComplete='statisticsData.month_article_count'></MonthKpi>
|
||||||
|
<ArticlesChart :listData='statisticsData.week_article'></ArticlesChart>
|
||||||
|
</div>
|
||||||
|
<div class="recent-articles-container">
|
||||||
|
<div class="recent-articles-title">最近撸了</div>
|
||||||
|
<div class="recent-articles-wrapper">
|
||||||
|
<template v-if="recentArticles.length!=0">
|
||||||
|
<div class="recent-articles-item" v-for="item in recentArticles">
|
||||||
|
<span class="recent-articles-status">{{item.status | statusFilter}}</span>
|
||||||
|
<router-link class="recent-articles-content" :to="'/article/edit/'+item.id">
|
||||||
|
<span>{{item.title}}</span>
|
||||||
|
</router-link>
|
||||||
|
<span class="recent-articles-time"><i style="padding-right: 4px;" class="el-icon-time"></i>{{item.display_time | parseTime('{m}-{d} {h}:{i}')}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="recent-articles-emptyTitle">你太懒了最近都没有撸</div>
|
||||||
|
<!--<img class="emptyGif" :src="emptyGif">-->
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<router-link class="recent-articles-more" :to="'/article/wscnlist?uid='+uid">
|
||||||
|
Show more
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import PanThumb from 'components/PanThumb';
|
||||||
|
import MonthKpi from './monthKpi';
|
||||||
|
import ArticlesChart from './articlesChart';
|
||||||
|
// import { getStatistics } from 'api/article';
|
||||||
|
|
||||||
|
import emptyGif from 'assets/compbig.gif';
|
||||||
|
export default {
|
||||||
|
name: 'dashboard-editor',
|
||||||
|
components: { PanThumb, MonthKpi, ArticlesChart },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chart: null,
|
||||||
|
statisticsData: {
|
||||||
|
article_count: undefined,
|
||||||
|
comment_count: undefined,
|
||||||
|
latest_article: [],
|
||||||
|
month_article_count: undefined,
|
||||||
|
pageviews_count: undefined,
|
||||||
|
week_article: []
|
||||||
|
},
|
||||||
|
emptyGif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'name',
|
||||||
|
'avatar',
|
||||||
|
'email',
|
||||||
|
'uid',
|
||||||
|
'introduction',
|
||||||
|
'roles'
|
||||||
|
]),
|
||||||
|
recentArticles() {
|
||||||
|
return this.statisticsData.latest_article.slice(0, 7)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
// getStatistics().then(response => {
|
||||||
|
// this.statisticsData = response.data;
|
||||||
|
// this.statisticsData.week_article = this.statisticsData.week_article.slice().reverse();
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
statusFilter(status) {
|
||||||
|
const statusMap = {
|
||||||
|
published: '已发布',
|
||||||
|
draft: '草稿中',
|
||||||
|
deleted: '已删除'
|
||||||
|
};
|
||||||
|
return statusMap[status];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.emptyGif {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-articles-emptyTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #95A5A6;
|
||||||
|
padding-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-editor-container {
|
||||||
|
padding: 30px 50px;
|
||||||
|
.pan-info-roles {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.info-container {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 190px;
|
||||||
|
height: 150px;
|
||||||
|
line-height: 200px;
|
||||||
|
.display_name {
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
color: #212121;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
.info-wrapper {
|
||||||
|
line-height: 40px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
.info-item {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 95px;
|
||||||
|
.info-item-num {
|
||||||
|
color: #212121;
|
||||||
|
font-size: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.info-item-text {
|
||||||
|
color: #727272;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-right: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dashboard-editor-icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
margin: 30px 36px 30px 0;
|
||||||
|
}
|
||||||
|
.main-dashboard-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #DEE1E2;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.chart-container {
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
padding-right: 10px;
|
||||||
|
width: 40%;
|
||||||
|
border-right: 1px solid #DEE1E2;
|
||||||
|
}
|
||||||
|
.recent-articles-container {
|
||||||
|
padding: 12px 12px 0px;
|
||||||
|
float: left;
|
||||||
|
width: 60%;
|
||||||
|
position: relative;
|
||||||
|
.recent-articles- {
|
||||||
|
&title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #95A5A6;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #DEE1E2;
|
||||||
|
}
|
||||||
|
&more {
|
||||||
|
color: #2C3E50;
|
||||||
|
font-size: 12px;
|
||||||
|
float: right;
|
||||||
|
margin-right: 25px;
|
||||||
|
line-height: 40px;
|
||||||
|
&:hover {
|
||||||
|
color: #3A71A8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&wrapper {
|
||||||
|
padding: 0 20px 0 22px;
|
||||||
|
.recent-articles- {
|
||||||
|
&item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 16px 100px 16px 16px;
|
||||||
|
border-bottom: 1px solid #DEE1E2;
|
||||||
|
position: relative;
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
height: 104%;
|
||||||
|
width: 0px;
|
||||||
|
background: #30B08F;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
left: 0px;
|
||||||
|
top: -2px;
|
||||||
|
transition: 0.3s ease all;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
&:before {
|
||||||
|
opacity: 1;
|
||||||
|
width: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&status {
|
||||||
|
font-size: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
color: #9B9B9B;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
&content {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #2C3E50;
|
||||||
|
&:hover {
|
||||||
|
color: #3A71A8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&time {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
color: #9B9B9B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div class="monthKpi-container">
|
||||||
|
<span class="monthKpi-container-title">{{month}}月</span>
|
||||||
|
<BarPercent class="monthKpi-container-chart" :dataNum='articlesComplete'></BarPercent>
|
||||||
|
<span class="monthKpi-container-text">文章完成比例</span>
|
||||||
|
<span class="monthKpi-container-count">{{articlesComplete}}<b>篇</b></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BarPercent from 'components/Charts/barPercent';
|
||||||
|
export default {
|
||||||
|
name: 'monthKpi',
|
||||||
|
components: { BarPercent },
|
||||||
|
props: {
|
||||||
|
articlesComplete: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
month: new Date().getMonth() + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.monthKpi-container{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.monthKpi-container-title {
|
||||||
|
color: #7F8C8D;
|
||||||
|
font-size: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthKpi-container-chart {
|
||||||
|
margin-left: 100px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthKpi-container-text {
|
||||||
|
margin-left: 112px;
|
||||||
|
color: #9EA7B3;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthKpi-container-count {
|
||||||
|
color: #30B08F;
|
||||||
|
font-size: 34px;
|
||||||
|
position: absolute;
|
||||||
|
left: 260px;
|
||||||
|
top: 60px;
|
||||||
|
b {
|
||||||
|
padding-left: 10px;
|
||||||
|
color: #9EA7B3;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-container">
|
||||||
|
<component v-bind:is="currentRole"> </component>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import EditorDashboard from './editor/index';
|
||||||
|
import DefaultDashboard from './default/index';
|
||||||
|
export default {
|
||||||
|
name: 'dashboard',
|
||||||
|
components: { EditorDashboard, DefaultDashboard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentRole: 'EditorDashboard'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'name',
|
||||||
|
'avatar',
|
||||||
|
'email',
|
||||||
|
'introduction',
|
||||||
|
'roles'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.roles.indexOf('admin') >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isEditor = this.roles.some(v => v.indexOf('editor') >= 0)
|
||||||
|
if (!isEditor) {
|
||||||
|
this.currentRole = 'DefaultDashboard';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="errPage-container">
|
||||||
|
<el-button @click="back" icon='arrow-left' class="pan-back-btn">返回</el-button>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<h1 class="text-jumbo text-ginormous">Oops!</h1>
|
||||||
|
<h2>你没有权限去该页面</h2>
|
||||||
|
<h6>如有不满请联系你领导</h6>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>或者你可以去:</li>
|
||||||
|
<li class="link-type">
|
||||||
|
<router-link to="/dashboard">回首页</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="link-type"><a href="https://www.taobao.com/">随便看看</a></li>
|
||||||
|
<li><a @click="dialogVisible=true" href="#">点我看图</a></li>
|
||||||
|
</ul>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-dialog title="随便看" v-model="dialogVisible" size="large">
|
||||||
|
<img class="pan-img" :src="ewizardClap">
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import errGif from 'assets/401.gif';
|
||||||
|
import ewizardClap from 'assets/gifs/wizard_clap.gif';
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
errGif: errGif + '?' + +new Date(),
|
||||||
|
ewizardClap,
|
||||||
|
dialogVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
back() {
|
||||||
|
this.$router.go(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.errPage-container {
|
||||||
|
width: 800px;
|
||||||
|
margin: 100px auto;
|
||||||
|
.pan-back-btn {
|
||||||
|
background: #008489;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.pan-gif {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.pan-img{
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.text-jumbo {
|
||||||
|
font-size: 60px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #484848;
|
||||||
|
}
|
||||||
|
.list-unstyled {
|
||||||
|
font-size: 14px;
|
||||||
|
li {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #008489;
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div class="errorpage-container"> 404
|
||||||
|
<splitPane v-on:resize="resize" split="vertical">
|
||||||
|
<template slot="paneL">
|
||||||
|
<div class="left-container"></div>
|
||||||
|
</template>
|
||||||
|
<template slot="paneR">
|
||||||
|
<splitPane split="horizontal">
|
||||||
|
<template slot="paneL">
|
||||||
|
<div class="top-container"></div>
|
||||||
|
</template>
|
||||||
|
<template slot="paneR">
|
||||||
|
<div class="bottom-container">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</splitPane>
|
||||||
|
</template>
|
||||||
|
</splitPane>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import splitPane from 'components/SplitPane/SplitPane'
|
||||||
|
export default {
|
||||||
|
components: { splitPane },
|
||||||
|
methods: {
|
||||||
|
resize() {
|
||||||
|
console.log('resize')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
.errorpage-container{
|
||||||
|
position: relative;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.left-container {
|
||||||
|
background-color: #F38181;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-container {
|
||||||
|
background-color: #FCE38A;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-container {
|
||||||
|
background-color: #FCE38A;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-container {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #95E1D3;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<section class="app-main" style="min-height: 100%">
|
||||||
|
<transition name="fade" mode="out-in">
|
||||||
|
<router-view :key="key"></router-view>
|
||||||
|
</transition>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AppMain',
|
||||||
|
computed: {
|
||||||
|
key() {
|
||||||
|
return this.$route.name !== undefined
|
||||||
|
? this.$route.name + +new Date()
|
||||||
|
: this.$route + +new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
|
||||||
|
<div class="sidebar-wrapper">
|
||||||
|
<Sidebar class="sidebar-container"/>
|
||||||
|
</div>
|
||||||
|
<div class="main-container">
|
||||||
|
<Navbar/>
|
||||||
|
<App-main/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Navbar, Sidebar, AppMain } from 'views/layout';
|
||||||
|
import store from 'store';
|
||||||
|
import router from 'router';
|
||||||
|
import permission from 'store/permission';
|
||||||
|
// import { Loading } from 'element-ui';
|
||||||
|
// let loadingInstance;
|
||||||
|
export default {
|
||||||
|
name: 'layout',
|
||||||
|
components: {
|
||||||
|
Navbar,
|
||||||
|
Sidebar,
|
||||||
|
AppMain
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sidebar() {
|
||||||
|
return this.$store.state.app.sidebar;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteEnter: (to, from, next) => {
|
||||||
|
console.log('b')
|
||||||
|
const roles = store.getters.roles;
|
||||||
|
if (roles.length !== 0) {
|
||||||
|
next();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// loadingInstance = Loading.service({ fullscreen: true, text: '玩命加载中' });
|
||||||
|
store.dispatch('GetInfo').then(() => {
|
||||||
|
permission.init({
|
||||||
|
roles: store.getters.roles,
|
||||||
|
router: router.options.routes
|
||||||
|
});
|
||||||
|
// loadingInstance.close();
|
||||||
|
next();
|
||||||
|
}).catch(err => {
|
||||||
|
// loadingInstance.close();
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||||
|
@import "src/styles/mixin.scss";
|
||||||
|
|
||||||
|
.app-wrapper {
|
||||||
|
@include clearfix;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding-left: 180px;
|
||||||
|
&.hideSidebar {
|
||||||
|
padding-left: 40px;
|
||||||
|
.sidebar-wrapper {
|
||||||
|
transform: translate(-140px, 0);
|
||||||
|
.sidebar-container {
|
||||||
|
transform: translate(132px, 0);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
.sidebar-container {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sidebar-wrapper {
|
||||||
|
width: 180px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1001;
|
||||||
|
overflow-x: hidden;
|
||||||
|
transition: all .28s ease-out;
|
||||||
|
@include scrollBar;
|
||||||
|
}
|
||||||
|
.sidebar-container {
|
||||||
|
transition: all .28s ease-out;
|
||||||
|
}
|
||||||
|
.main-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
transition: all .28s ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue