feat: init+修改node版本

This commit is contained in:
2025-12-22 12:48:14 +08:00
parent 6d9d7ad37e
commit 0e57c19027
329 changed files with 64992 additions and 10 deletions

12
.babelrc Normal file
View File

@@ -0,0 +1,12 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}

14
.editorconfig Normal file
View File

@@ -0,0 +1,14 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
build/*.js
config/*.js
src/assets

199
.eslintrc.js Normal file
View File

@@ -0,0 +1,199 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: 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
//it is base on https://github.com/vuejs/eslint-config-vue
'rules': {
'accessor-pairs': 2,
'arrow-spacing': [2, {
'before': true,
'after': true
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, {
'before': false,
'after': true
}],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': [2, 'allow-null'],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
}

26
.gitignore vendored
View File

@@ -1,11 +1,21 @@
# ---> Vue .DS_Store
# gitignore template for Vue.js projects node_modules/
# admin/
# Recommended template: Node.gitignore npm-debug.log*
yarn-debug.log*
yarn-error.log*
# TODO: where does this rule come from? test/unit/coverage
docs/_book test/e2e/reports
selenium-debug.log
# TODO: where does this rule come from? # Editor directories and files
test/ .idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.zip
package-lock.json

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18.18.2

10
.postcssrc.js Normal file
View File

@@ -0,0 +1,10 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

5
.travis.yml Normal file
View File

@@ -0,0 +1,5 @@
language: node_js
node_js: stable
script: npm run test
notifications:
email: false

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,2 +1 @@
# coin-manager-new 后台管理系统

1
README.zh-CN.md Normal file
View File

@@ -0,0 +1 @@
后台管理系统

48
build/build.js Normal file
View File

@@ -0,0 +1,48 @@
'use strict'
require('./check-versions')()
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const server = require('pushstate-server')
var spinner = ora('building for '+ process.env.env_config+ ' environment...' )
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (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')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
if(process.env.npm_config_preview){
server.start({
port: 9526,
directory: './admin',
file: '/index.html'
});
console.log('> Listening at ' + 'http://localhost:9526' + '\n')
}
})
})

54
build/check-versions.js Normal file
View File

@@ -0,0 +1,54 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const 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 (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

BIN
build/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

101
build/utils.js Normal file
View File

@@ -0,0 +1,101 @@
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [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)
}
}
// https://vue-loader.vuejs.org/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) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

22
build/vue-loader.conf.js Normal file
View File

@@ -0,0 +1,22 @@
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

101
build/webpack.base.conf.js Normal file
View File

@@ -0,0 +1,101 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
rules: [
// ...(config.dev.useEslint ? [createLintingRule()] : []),请选择3个合并深度
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [resolve('src/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

88
build/webpack.dev.conf.js Normal file
View File

@@ -0,0 +1,88 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
}),
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

186
build/webpack.prod.conf.js Normal file
View File

@@ -0,0 +1,186 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const FileManagerPlugin = require('filemanager-webpack-plugin');
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const env = require('../config/'+process.env.env_config+'.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : 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 UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: false,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// 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: config.build.index,
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// 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',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// split echarts into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'echarts',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
}
}),
// split xlsx into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'xlsx',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('xlsx') >= 0);
}
}),
// split codemirror into its own file
new webpack.optimize.CommonsChunkPlugin({
async: 'codemirror',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('codemirror') >= 0);
}
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
]),
new FileManagerPlugin({
onEnd: {
archive: [
{
source: 'admin',
destination: 'admin.zip',
format: 'zip',
}
]
}
})
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

BIN
coin-manager.7z Normal file

Binary file not shown.

6
config/dev.env.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
NODE_ENV: '"development"',
ENV_CONFIG: '"dev"',
BASE_API: '"http://127.0.0.1"',
PROTOCOL:'"https://"',
}

109
config/index.js Normal file
View File

@@ -0,0 +1,109 @@
'use strict';
// Template version: 1.2.6
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path');
let dev = "http://127.0.0.1";
let haigen = "http://192.168.2.113:9000";
let aliyun = "http://47.75.115.45:8091";
let uploadServer = "http://47.106.158.72"
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath : '/',
proxyTable : {
'/admin/*': {
target : dev,
secure : true,
changeOrigin: true
},
'/user/*': {
target : dev,
secure : true,
changeOrigin: true
},
'/finance/*': {
target : dev,
secure : true,
changeOrigin: true
},
'/exchange/*': {
target : dev,
secure : true,
changeOrigin: true
}
},
// Various Dev Server settings
//192.168.2.144
host : 'localhost', // can be overwritten by process.env.HOST
port : 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
errorOverlay : true,
notifyOnErrors : false,
poll : false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint : true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: '#cheap-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
// 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
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../admin/index.html'),
// Paths
assetsRoot : path.resolve(__dirname, '../admin'),
assetsSubDirectory: 'static',
// you can set by youself according to actual condition
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool : '#source-map',
// 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
}
};

6
config/prod.env.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
NODE_ENV: '"production"',
ENV_CONFIG: '"prod"',
BASE_API: '"http://114.119.32.11:9000"',
PROTOCOL:'"https://"',
}

5
config/sit.env.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
NODE_ENV: '"production"',
// ENV_CONFIG: '"http://test.tingshuogo.com"',
// BASE_API: '"http://test.tingshuogo.com:8091"'
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

17
index.html Normal file
View File

@@ -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>后台管理系统</title>
</head>
<body>
<script src=<%= htmlWebpackPlugin.options.path %>/js/three.min.js></script>
<script src=<%= htmlWebpackPlugin.options.path %>/tinymce4.7.5/tinymce.min.js></script>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

119
package.json Normal file
View File

@@ -0,0 +1,119 @@
{
"name": "vue-element-admin",
"version": "3.6.6",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"lint": "eslint --ext .js,.vue src",
"test": "npm run lint"
},
"keywords": [
"vue",
"element-ui",
"admin",
"management-system",
"admin-template"
],
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
},
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
},
"dependencies": {
"axios": "0.17.1",
"babel-runtime": "6",
"clipboard": "1.7.1",
"codemirror": "5.32.0",
"dropzone": "5.2.0",
"echarts": "3.8.5",
"element-ui": "2.3.2",
"file-saver": "1.3.3",
"filemanager-webpack-plugin": "^1.0.27",
"font-awesome": "4.7.0",
"js-cookie": "2.2.0",
"js-md5": "^0.7.3",
"jsonlint": "1.6.2",
"jszip": "3.1.5",
"mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"screenfull": "3.3.2",
"showdown": "1.8.5",
"simplemde": "1.11.2",
"sortablejs": "1.7.0",
"svg-baker-runtime": "^1.4.7",
"vue": "2.5.10",
"vue-count-to": "1.0.13",
"vue-hot-reload-api": "^2.3.4",
"vue-i18n": "7.3.2",
"vue-multiselect": "2.0.8",
"vue-router": "3.0.1",
"vue-splitpane": "1.0.2",
"vuedraggable": "^2.16.0",
"vuex": "3.0.1",
"xlsx": "^0.11.16"
},
"devDependencies": {
"autoprefixer": "7.2.3",
"babel-core": "6.26.0",
"babel-eslint": "8.0.3",
"babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.2",
"babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-plugin-transform-vue-jsx": "3.5.0",
"babel-preset-env": "1.6.1",
"babel-preset-stage-2": "6.24.1",
"chalk": "2.3.0",
"copy-webpack-plugin": "4.3.0",
"cross-env": "5.1.1",
"css-loader": "0.28.7",
"eslint": "4.13.1",
"eslint-friendly-formatter": "3.0.0",
"eslint-loader": "1.9.0",
"eslint-plugin-html": "4.0.1",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "1.1.5",
"friendly-errors-webpack-plugin": "1.6.1",
"html-webpack-plugin": "2.30.1",
"node-notifier": "5.1.2",
"optimize-css-assets-webpack-plugin": "3.2.0",
"ora": "1.3.0",
"portfinder": "1.0.13",
"postcss-import": "11.0.0",
"postcss-loader": "2.0.9",
"postcss-url": "7.3.0",
"pushstate-server": "3.0.1",
"rimraf": "2.6.2",
"sass-loader": "^7.3.1",
"sass": "^1.32.0",
"script-loader": "0.7.2",
"semver": "5.4.1",
"shelljs": "0.7.8",
"svg-sprite-loader": "3.5.2",
"uglifyjs-webpack-plugin": "1.1.3",
"url-loader": "0.6.2",
"vue-loader": "13.5.0",
"vue-style-loader": "3.0.3",
"vue-template-compiler": "2.5.10",
"webpack": "3.10.0",
"webpack-bundle-analyzer": "2.9.1",
"webpack-dev-server": "2.9.7",
"webpack-merge": "4.1.1"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

10473
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

7
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,7 @@
ignoredBuiltDependencies:
- core-js
- ejs
- es5-ext
- fsevents
- node-sass
- uglifyjs-webpack-plugin

11
src/App.vue Normal file
View File

@@ -0,0 +1,11 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default{
name: 'App'
}
</script>

106
src/api/agentVerifyApi.js Normal file
View File

@@ -0,0 +1,106 @@
import request from '@/utils/request';
export const agentVerifyApi = {
/**
* 获取代理商注册审核列表
* @param form
* @param current
* @param size
*/
getAgentSellerList(form, current, size) {
let params = {};
for (let item in form) {
params[item] = form[item];
}
params.current = current;
params.size = size;
console.log("结果", params);
return request({
url : '/user/reviewSubagentList',
method: 'post',
params: params
});
},
/**
* 代理商注册审核认证
* @param id
* @param reviewsStatus
* @param note
*/
updateVerify(id, reviewsStatus, note) {
return request({
url : '/user/updateReviewsStatus',
method: 'post',
params: {id, reviewsStatus, note}
});
},
/**
* 获取代理商列表
* @param form
* @param current
* @param size
*/
getSellerList(form, current, size) {
let params = {};
for (let item in form) {
if(form[item]){
params[item] = form[item];
}
}
params.current = current;
params.size = size;
return request({
url : '/user/subagentList',
method: 'post',
params: params
});
},
/**
* 获取当前代理信息
* @param id
*/
getCurrentAgent(id) {
return request({
url : '/user/getSubagentInfo',
method: 'get',
params: {id}
});
},
/**
* 更新编辑代理信息
* @param form
* @param current
* @param size
*/
postAgentInfo(form, current, size) {
let params = {};
for (let item in form) {
params[item] = form[item];
}
params.current = current;
params.size = size;
return request({
url : '/user/updatesubagent',
method: 'post',
params: params
});
},
/**
* 启用禁用用户
**/
getOperateUser(id,status){
return request({
url:'/user/updateStatus',
method:'get',
params:{id,status}
})
}
};

0
src/api/article.js Normal file
View File

129
src/api/assetsApi.js Normal file
View File

@@ -0,0 +1,129 @@
import request from '@/utils/request'
import {formUtils} from '@/utils/formUtil'
export const assetsApi = {
// 虚拟币充值
rechargeCoin(data) {
return request({
url: '/account/rechargeCoin',
method: 'post',
data,
})
},
/**
*获取账户资产列表
*/
getAccountAssetsList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/account',
method: 'get',
params: params
})
},
/**
*账户资金列表导出xls
*/
accountAssetsExport(form) {
let params = {}
formUtils.formDateRange(params, form);
return request({
url: '/account/exportList',
method: 'get',
params: params
})
},
/**
* 资金状态操作设置
*/
accountAssetsOpearte(data) {
return request({
url: '/account/setStatus',
method: 'post',
data: data
})
},
/**
*获取账户资产流水管理列表
*/
getAccountAssetsFlowList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/finance/accountDetails/records',
method: 'get',
params: params
})
},
/**
*获取币币交易委托管理
*/
getEntrustManagerList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/entrustOrder',
method: 'get',
params: params
})
},
/**
* 获取币币交易成交记录列表
*/
getTurnoverRecordList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/turnoverOrder',
method: 'get',
params: params
})
},
/**
* 获取创新委托管理列表
**/
getInnovateEntrustManagerList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/forexEntrustOrder',
method: 'get',
params: params
})
},
/**
* 获取创新成交记录列表
**/
getInnovateRecordList(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/turnoverOrder',
method: 'get',
params: params
})
},
}

349
src/api/coinConfigApi.js Normal file
View File

@@ -0,0 +1,349 @@
import request from '@/utils/request'
// fetchList(query) {
// return request({
// url: '/article/list',
// method: 'get',
// params: query
// })
// }
// 币币交易参数 --> 币种配置
export const coinConfigApi = {
// 获取币币交易参数列表
getCoinList(form, current = 1, size = 10) {
let params = {}
for (let item in form) {
if (form[item]) {
params[item] = form[item]
}
}
params.current = current
params.size = size
return request({
url: '/finance/coins',
method: 'get',
params: params
})
},
// 启用/禁用 币种 1:启用2:禁用
setStatus(data) {
return request({
url: '/finance/coins/setStatus',
method: 'post',
data,
})
},
// 获取币种信息
getCoin(id) {
return request({
url: `/finance/coins/info/${id}`,
method: 'get',
})
},
// 新增币种信息
addCoin(data) {
return request({
url: '/finance/coins',
method: 'post',
data
})
},
// 修改币种信息
editCoin(data) {
return request({
url: '/finance/coins',
method: 'patch',
data
})
},
// 获取钱包配置信息
getCoinConfig(id) {
return request({
url: `/finance/coinConfigs/info/${id}`,
method: 'get',
})
},
// 新增钱包配置
addCoinConfig(data) {
return request({
url: '/finance/coinConfigs',
method: 'post',
data
})
},
// 修改钱包归集地址
editCoinConfig(data) {
return request({
url: '/finance/coinConfigs',
method: 'patch',
data
})
},
// 钱包归集提币地址表格
getAdminAddressByCoinId(id, current = 1, size = 10) {
let params = {
coinId: id,
current,
size,
};
return request({
url: `/finance/adminAddress`,
method: 'get',
params,
})
},
// 钱包归集地址类型
getAdminAddresstype() {
return request({
url: '/finance/coins/allQbb'
})
},
// 新增钱包归集地址
addAdminAddress(data) {
return request({
url: '/finance/adminAddress',
method: 'post',
data,
})
},
// 修改钱包归集地址
editAdminAddress(data) {
return request({
url: '/finance/adminAddress',
method: 'patch',
data,
})
},
// 获取所有币种类型
getCoinTypeAll(status = 1) {
return request({
url: '/finance/coinTypes/all',
method: 'get',
params: {status}
})
},
// 获取币种类型列表
getCoinType(form, current, size) {
let params = {...form};
params.current = current
params.size = size
return request({
url: '/finance/coinTypes',
method: 'get',
params
})
},
// 获取所有币种类型
deleteCoinType(data) {
return request({
url: '/finance/coinTypes/delete',
method: 'post',
data
})
},
// 新增币种类型
createCoinType(data) {
return request({
url: '/finance/coinTypes',
method: 'post',
data
})
},
//更新币种类型
updateCoinType(data) {
return request({
url: '/finance/coinTypes',
method: 'patch',
data
})
},
//设置币种类型状态
setCoinTypeStatus(data) {
return request({
url: '/finance/coinTypes/setStatus',
method: 'post',
data
})
},
}
// 币币交易参数 --> 交易市场
export const marketApi = {
// 获取币币交易市场列表
getCoinMarketList(form, current = 1, size = 10) {
let params = {...form};
params.current = current
params.size = size
return request({
url: '/exchange/markets',
method: 'get',
params,
})
},
// 启用/禁用 币种 1:启用2:禁用
setStatus(data) {
return request({
url: '/exchange/markets/setStatus',
method: 'post',
data,
})
},
// 获取所有币币交易市场名称
getMarketAll() {
return request({
url: '/exchange/markets/all',
method: 'get',
})
},
// 获取所有币币交易币种
getCoinAll(status = 1) {
return request({
url: '/finance/coins/all',
method: 'get',
params: {status,}
})
},
// 获取所有币币交易区域
getTradeAreaAll(status = 1) {
return request({
url: '/exchange/tradeAreas/all',
method: 'get',
params: {status}
})
},
// 新增币种
addMarket(data) {
return request({
url: '/exchange/markets',
method: 'post',
data,
})
},
// 修改币种
editMarket(data) {
return request({
url: '/exchange/markets',
method: 'patch',
data
})
},
};
// 创新交易
export const forexConfigApi = {
// 获取创新交易 --> 币种配置列表
getForexCoinList(form, current = 1, size = 10) {
let params = {}
for (let item in form) {
if (form[item]) {
params[item] = form[item]
}
}
params.current = current
params.size = size
return request({
url: '/forexCoin',
method: 'get',
params: params
})
},
// 获取创新交易 --> 市场配置列表
getCoinMarketList(form, current = 1, size = 10) {
let params = {...form}
params.current = current
params.size = size
return request({
url: '/market/forex',
method: 'get',
params
})
},
// 启用/禁用 币种 1:启用2:禁用
setStatus(data) {
return request({
url: '/forexCoin/setStatus',
method: 'post',
data,
})
},
// 获取所有创新交易市场名称
getMarketAll() {
return request({
url: '/market/forex/all',
method: 'get',
})
},
// 获取所有创新交易币种
getCoinAll(status = 1) {
return request({
url: '/forexCoin/all',
method: 'get',
params: {status}
})
},
getCoinById(id, status = 1) {
return request({
url: `/forexCoin/load`,
method: 'get',
params: {
tradeAreaId: id,
status,
}
})
},
// 获取所有创新交易区域
getTradeAreaAll(status = 1) {
return request({
url: '/forex/area/all',
method: 'get',
params: {status}
})
},
// 新增币种
addForexCoin(data) {
return request({
url: '/forexCoin',
method: 'post',
data,
})
},
// 修改币种
editForexCoin(data) {
return request({
url: '/forexCoin',
method: 'put',
data,
})
},
};

41
src/api/commonApi.js Normal file
View File

@@ -0,0 +1,41 @@
import request from '@/utils/request'
import {formUtils} from '@/utils/formUtil'
export const commonApi = {
/**
* 获取所有币种信息
*/
getCoinAll(status = 1) {
return request({
url: '/finance/coins/all',
method: 'get',
params: {status}
})
},
/**
* 获取所有交易市场
*/
getMarketAll() {
return request({
url: '/exchange/markets/all',
method: 'get'
})
},
/**
* 导出excel
*/
exportExcel(url, form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: url,
method: 'get',
params: params
})
},
}

30
src/api/countApi.js Normal file
View File

@@ -0,0 +1,30 @@
import request from '@/utils/request'
export const countApi = {
/**
* 获取统计列表
*/
getCountList(form, current, size, url) {
let params = {};
for (let itme in form) {
if (form[itme]) {
let startTime = form.dateRange[0];
let endTime = form.dateRange[1];
params["startTime"] = startTime;
params["endTime"] = endTime;
} else {
params[itme] = form[itme];
}
}
params["current"] = current;
params["size"] = size;
console.log("参数-结果:", params);
return request({
url: `${url}`,
method: 'get',
params: params
})
}
}

106
src/api/financeApi.js Normal file
View File

@@ -0,0 +1,106 @@
import request from '@/utils/request'
import {formUtils} from '@/utils/formUtil'
export const financeApi = {
/**
* 场外交易充值管理列表
*/
getFinanceRechargeList(form, current, size) {
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url: '/finance/cashRecharges/records',
method: 'get',
params: params
})
},
/**
* cny 充值审核操作
*/
checkFinanceRecharge(data){
return request({
url:'/finance/cashRecharge/cashRechargeUpdateStatus',
method:'post',
data:data
})
},
/**
* 场外交易提现审核列表
*/
getFinanceWithdrawalsList(form, current, size) {
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url: '/finance/cashWithdrawals/records',
method: 'get',
params: params
})
},
/**
* cny 提现审核操作
**/
checkFinanceWithdrawals(data){
return request({
url:'/finance/cashWithdrawals/updateWithdrawalsStatus',
method:'post',
data:data
})
},
/**
* 虚拟币提现列表
*/
getCoinWithdraw(form,current,size){
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url: '/finance/coinWithdraws/records',
method: 'get',
params: params
})
},
/**
* 虚拟币审核操作
*/
checkCoinWithdraw(data){
return request({
url:'/finance/coinWithdraws/audit',
method:'post',
data:data
})
},
/* 手动打款接口*/
manualWithdraw(data){
return request({
url: '/finance/coinWithdraws',
method:'patch',
data:data
})
},
/**
* 虚拟币充值列表
*/
getCoinRecharge(form, current, size) {
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url:'/finance/coinRecharges/records',
method:'get',
params:params
})
}
}

48
src/api/login.js Normal file
View File

@@ -0,0 +1,48 @@
import request from '@/utils/request'
export const loginApi={
login(username,password) {
return request({
url: '/admin/login',
method: 'post',
params:{username,password}
})
},
}
export function loginByUsername(username, password) {
const data = {
username,
password
}
return request({
url: '/login/login',
method: 'post',
data
})
}
export function logout() {
return request({
url: '/login/logout',
method: 'post'
})
}
export function getUserInfo(token) {
return request({
url: '/user/info',
method: 'get',
params: { token }
})
}

124
src/api/normalConfigApi.js Normal file
View File

@@ -0,0 +1,124 @@
import request from '@/utils/request';
export const normalConfigApi = {
/**
* 新增银行卡
* @param data
*/
createBank(data) {
return request({
url: '/admin/adminBanks',
method: 'post',
data
});
},
/**
* 编辑状态
* @param data
*/
setBanks(params) {
return request({
url: '/admin/adminBanks/adminUpdateBankStatus',
method: 'post',
params: params
});
},
/**
* 获取银行卡列表
* @param form
* @param current
* @param size
*/
getBankList(form, current, size) {
let params = {};
params["bankCard"] = form["bankCard"];
params.current = current;
params.size = size;
return request({
url: '/admin/adminBanks',
method: 'get',
params: params
});
},
/**
* 更新银行卡
* @param data
*/
updateBank(data) {
return request({
url: '/admin/adminBanks',
method: 'patch',
data
});
},
/**
* 新增配置
* @param data
*/
createConfig(data) {
return request({
url: '/admin/configs',
method: 'post',
data
});
},
/**
* 获取配置列表
* @param current
* @param size
*/
getConfigList(current, size, form = {}) {
let params = {
...form,
};
params.current = current;
params.size = size;
return request({
url: '/admin/configs',
method: 'get',
params: params
});
},
/**
* 更新配置
* @param data
*/
updateConfig(data) {
return request({
url: '/admin/configs',
method: 'patch',
data
});
},
/**
* 新增系统权限
**/
createSysPrivileges(data) {
return request({
url: '/admin/privileges',
method: 'post',
data: data
})
},
/**
* 编辑系统权限
*/
editSysPrivileges(data) {
return request({
url: '/admin/privileges',
method: 'patch',
data: data
})
},
};

93
src/api/noticeApi.js Normal file
View File

@@ -0,0 +1,93 @@
import request from '@/utils/request'
// fetchList(query) {
// return request({
// url: '/article/list',
// method: 'get',
// params: query
// })
// }
export const noticeApi = {
/**
* 新增公告
* @param data
*/
createNotice(data) {
return request({
url: '/admin/notices',
method: 'post',
data
})
},
/**
* 删除系统资讯公告信息
* @param ids
*/
deleteNotice(data) {
return request({
url: '/admin/notices/delete',
method: 'post',
data
})
},
updateStatus(id,status){
return request({
url:'/admin/notices/updateStatus',
method:'post',
params:{id,status}
})
},
/**
* 系统资讯公告信息列表
*/
getNoticeList(form, current, size) {
let params = {}
for (let item in form) {
if (form[item]) {
if (item === "dateRange") {
let startTime = form.dateRange[0]
let endTime = form.dateRange[1]
params["startTime"] = startTime
params["endTime"] = endTime
} else {
params[item] = form[item]
}
}
}
params.current = current
params.size = size
console.log("结果", params)
return request({
url: '/admin/notices',
method: 'get',
params: params
})
},
/**
*
获取一个系统资讯公告信息
* @param data
*/
getOneNotice(id) {
return request({
url: '/admin/notices',
method: 'get',
params: {id}
})
},
/**
* 更新公告
* @param data
*/
updateNotice(data) {
return request({
url: '/admin/notices',
method: 'patch',
data
})
}
}

8
src/api/qiniu.js Normal file
View File

@@ -0,0 +1,8 @@
import request from '@/utils/request'
export function getToken() {
return request({
url: '/qiniu/upload/token', // 假地址 自行替换
method: 'get'
})
}

9
src/api/remoteSearch.js Normal file
View File

@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function userSearch(name) {
return request({
url: '/search/user',
method: 'get',
params: { name }
})
}

View File

@@ -0,0 +1,81 @@
import request from '@/utils/request'
// fetchList(query) {
// return request({
// url: '/article/list',
// method: 'get',
// params: query
// })
// }
export const resourceConfigApi = {
/**
* 新增资源配置
* @param data
*/
createWebConfig(data) {
if(!data.status){
data.status = 0;
}
return request({
url: '/admin/webConfigs',
method: 'post',
data
})
},
/**
* 删除资源配置信息
* @param ids
*/
deleteWebConfig(ids) {
return request({
url: '/admin/webConfigs/delete',
method: 'post',
data: ids
})
},
/**
*网站配置信息列表
*/
getWebConfigList(form, current, size) {
let params = {}
params.name = form.name
if(form.type != -1){
params.type = form.type
}
params.current = current
params.size = size
console.log("params",params)
return request({
url: '/admin/webConfigs',
method: 'get',
params: params
})
},
/**
*
获取一个系统资讯公告信息
* @param data
*/
getOneNotice(id) {
return request({
url: '/notice/getOneObj',
method: 'get',
params: {id}
})
},
/**
* 更新配置信息
* @param data
*/
updateWebConfig(data) {
return request({
url: '/admin/webConfigs',
method: 'patch',
data
})
}
}

60
src/api/serviceApi.js Normal file
View File

@@ -0,0 +1,60 @@
import request from '@/utils/request'
export const serviceApi = {
/**
* 工单列表
*/
getWorkIssueList(form, current, size) {
let params = {}
for (let item in form) {
if (form[item]) {
if (item === "dateRange") {
let startTime = form.dateRange[0]
let endTime = form.dateRange[1]
params["startTime"] = startTime
params["endTime"] = endTime
} else {
params[item] = form[item]
}
}
}
params.current = current
params.size = size
console.log("结果", params)
return request({
url: '/admin/workIssues',
method: 'get',
params: params
})
},
/**
*
获取工单详情
* @param data
*/
getOneNotice(id) {
return request({
url: '/admin/workIssues/info',
method: 'get',
params: {id}
})
},
/**
* 更新工单
* @param data
*/
updateWorkIssue(form) {
let params = {}
params.id = form.id
params.answer = form.answer
return request({
url: '/admin/workIssues',
method: 'patch',
params
})
}
}

173
src/api/sysConfigApi.js Normal file
View File

@@ -0,0 +1,173 @@
import request from '@/utils/request'
import {formUtils} from '@/utils/formUtil'
export const sysConfigApi = {
/**
* 获取功能权限列表
* @param current
* @param size
*/
getPowerList(current, size) {
const params = {}
params.current = current
params.size = size
return request({
url: '/admin/privileges',
method: 'get',
params: params
})
},
getUsers(form, current, size) {
const params = {}
for (const item in form) {
if (form[item]) {
params[item] = form[item]
}
}
params.current = current
params.size = size
return request({
url: '/admin/users',
method: 'get',
params: params
})
},
/**
* 新建员工
*/
createEmployee(data) {
console.log('员工状态:' + data.status)
return request({
url: '/admin/users',
method: 'post',
data: data
})
},
/**
* 更新员工
*/
updateEmployee(data) {
return request({
url: '/admin/users',
method: 'patch',
data: data
})
},
/**
* 删除员工
*/
deleteEmployee(ids) {
return request({
url: '/admin/users/delete',
method: 'post',
data: ids
})
},
// 角色相关
/**
* 获取角色权限列表
* @param current
* @param size
*/
getRoleList(form, current, size) {
const params = {}
for (const item in form) {
if (form[item]) {
params[item] = form[item]
}
}
params.current = current
params.size = size
return request({
url: '/admin/roles',
method: 'get',
params: params
})
},
/**
* 新建角色
*/
createRole(data) {
return request({
url: '/admin/roles',
method: 'post',
data: data
})
},
/**
* 删除角色
*/
deleteRole(ids) {
return request({
url: '/admin/roles/delete',
method: 'post',
data: ids
})
},
/**
* 新增员工
*/
updateRole(data) {
return request({
url: '/admin/users/update',
method: 'put',
data: data
})
},
/**
* 获取所有权限列
* @param id
*/
getRolePrivileges(id) {
return request({
url: '/admin/roles_privileges',
method: 'get',
params: {
roleId: id
}
})
},
/**
* 配置权限
* @param id
* @param privilegeIds
*/
postRolePrivileges(id, privilegeIds) {
return request({
url: '/admin/grant_privileges',
method: 'post',
data: {
roleId: id,
privilegeIds
}
})
},
/**
* 获取所有系统日志
* @param id
*/
getSysUserLog(form, current, size) {
let params = {}
formUtils.formDateRange(params, form);
params.current = current
params.size = size
return request({
url: '/admin/sysUserLog',
method: 'get',
params: params
})
}
}

101
src/api/tradeAreaApi.js Normal file
View File

@@ -0,0 +1,101 @@
import request from '@/utils/request';
import {formUtils} from '@/utils/formUtil'
export const tradeAreaApi = {
/**
* 币币交易获取交易区域列表
* @param current
* @param size
*/
getTradeAreaList(form, current, size) {
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url: '/exchange/tradeAreas',
method: 'get',
params: params
})
},
/**
* 币币交易新增交易区域
*/
createdTradeArea(data) {
return request({
url: '/exchange/tradeAreas',
method: 'post',
data: data
})
},
/**
* 币币交易修改交易区域
*/
updateTradeArea(data) {
return request({
url: '/exchange/tradeAreas',
method: 'patch',
data: data
})
},
/**
*交易区状态设置 /tradeArea/setStatus交易区状态设置(status:1启用 0禁用)
**/
setTradeAreaStats(params){
return request({
url:'/exchange/tradeAreas/status',
method:'post',
data:params
})
},
/**
*交易区删除
**/
deleteTradeArea(data){
return request({
url:'/exchange/tradeAreas/delete',
method:'post',
data:data
})
},
/**
* 创新交易 获取交易区域列表 /forex/area
*/
getInnovateTradeAreaList(form, current, size) {
let params = {}
formUtils.formDateRange(params,form);
params.current = current
params.size = size
return request({
url: '/forex/area',
method: 'get',
params: params
})
},
/**
* 币币交易新增交易区域
*/
createdInnovateTradeArea(data) {
return request({
url: '/forex/area',
method: 'post',
data: data
})
},
/**
* 币币交易修改交易区域
*/
updateInnovateTradeArea(data) {
return request({
url: '/forex/area',
method: 'put',
data: data
})
},
}

9
src/api/transaction.js Normal file
View File

@@ -0,0 +1,9 @@
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/transaction/list',
method: 'get',
params: query
})
}

11
src/api/uploadApi.js Normal file
View File

@@ -0,0 +1,11 @@
import request from '@/utils/request'
export const uploadApi = {
// 此接口不带admin前缀
getPreUpload() {
return request({
url: '/admin/image/pre/upload',
method: 'GET'
})
}
}

227
src/api/userApi.js Normal file
View File

@@ -0,0 +1,227 @@
import request from '@/utils/request';
export const userApi = {
// 获取邀请用户列表
getDirectInviteidList(data, current, size) {
return request({
url: '/user/users/directInvites',
method: 'get',
params: {
...data,
current,
size,
}
})
},
/**
* 用户列表
* ga_status 0 未启用 1 启用
*/
getUserList(form, current, size) {
let params = {};
for (let item in form) {
if (form[item]) {
if (item === "dateRange") {
let startTime = form.dateRange[0];
let endTime = form.dateRange[1];
params["startTime"] = startTime;
params["endTime"] = endTime;
} else {
params[item] = form[item];
}
}
}
params.current = current;
params.size = size;
console.log("结果", params);
return request({
url: 'user/users',
method: 'get',
params: params
});
},
// 获取用户详情
getUserDetail(id) {
return request({
url: '/user/users/info',
method: 'get',
params: {id},
})
},
// 获取用户银行卡列表
getBankList(id, current, size) {
return request({
url: 'user/userBanks',
method: 'get',
params: {usrId: id, current, size}
})
},
// 禁用/启用 银行卡
updateBankStatus(id, status) {
return request({
url: `user/userBanks/status`,
method: 'post',
params: {id, status},
})
},
// 更新银行卡
updateBank(data) {
return request({
url: 'user/userBanks',
method: 'patch',
data,
})
},
// 获取提币地址
getWalletList(userId, current, size) {
return request({
url: 'user/userWallets',
method: 'get',
params: {userId, current, size}
})
},
// 获取钱包地址
getUserAddress(userId, current, size) {
return request({
url: '/user/userAddress',
method: 'get',
params: {userId, current, size}
})
},
/**
* 更新用户信息
* @param data
*/
updateUser(data) {
const keys = ["id", "username", "mobile", "countryCode", "realName", "mobile", "email", "idCard", "authStatus", "status"]
let reqData = {}
keys.map((key) => {
reqData[key] = data[key]
});
return request({
url: '/user/users',
method: 'patch',
data: reqData
});
},
/**
*禁/启用户
*/
updateStatus(id, status) {
return request({
url: '/user/users/status',
method: 'post',
params: {id, status}
});
},
/**
* 实名认证审核列表
* type 1 普通用户 2 代理商
* reviewStatus 0 待审核 1 通过 2 拒绝
* status : 0 禁用 1 启用
* agent_note: 代理商拒绝原因
* authStatus: 0 未认证 1初级 2高级
*
*/
getUserAuthList(form, current, size) {
let params = {}
for (let item in form) {
if (form[item]) {
if (item === "dateRange") {
let startTime = form.dateRange[0]
let endTime = form.dateRange[1]
params["startTime"] = startTime
params["endTime"] = endTime
} else {
params[item] = form[item]
}
}
}
params.current = current;
params.size = size;
return request({
url: '/user/users/auths',
method: 'get',
params: params
});
},
/**
* 获取认证详情
* @param id
*/
selUserAuthDetail(id) {
return request({
url: '/user/users/auth/info',
method: 'get',
params: {
id
}
});
},
/**
* 用户认证审核记录列表
* @param id
*/
getUserAuthRecordList(userId) {
let current = 1
let size = 5
return request({
url: '/user/getUserAuthRecordList',
method: 'get',
params: {
userId,
current,
size
}
});
},
/**
* 审核认证
* @param id
* @param review
* @param note
*/
updateVerify(id, authStatus, remark, authCode) {
if(authStatus!=2){
remark = null ;
}
return request({
url: '/user/users/auths/status',
method: 'post',
params: {id, authStatus, remark, authCode}
});
},
/**
* 获取矿池列表
*/
minePollList(form, current, size) {
let params = {};
for (let item in form) {
params[item] = form[item];
}
params.current = current;
params.size = size;
return request({
url: '/mine/pool',
method: 'get',
params: params
});
},
/**
* 审核矿池
*/
verifyPool(data) {
return request({
url: '/mine/pool',
method: 'put',
data,
})
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
/* eslint-disable */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['exports', 'echarts'], factory);
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports, require('echarts'));
} else {
// Browser globals
factory({}, root.echarts);
}
}(this, function (exports, echarts) {
var log = function (msg) {
if (typeof console !== 'undefined') {
console && console.error && console.error(msg);
}
};
if (!echarts) {
log('ECharts is not Loaded');
return;
}
var colorPalette = [
'#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80',
'#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
'#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
'#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
];
var theme = {
color: colorPalette,
title: {
textStyle: {
fontWeight: 'normal',
color: '#008acd'
}
},
visualMap: {
itemWidth: 15,
color: ['#5ab1ef','#e0ffff']
},
toolbox: {
iconStyle: {
normal: {
borderColor: colorPalette[0]
}
}
},
tooltip: {
backgroundColor: 'rgba(50,50,50,0.5)',
axisPointer : {
type : 'line',
lineStyle : {
color: '#008acd'
},
crossStyle: {
color: '#008acd'
},
shadowStyle : {
color: 'rgba(200,200,200,0.2)'
}
}
},
dataZoom: {
dataBackgroundColor: '#efefff',
fillerColor: 'rgba(182,162,222,0.2)',
handleColor: '#008acd'
},
grid: {
borderColor: '#eee'
},
categoryAxis: {
axisLine: {
lineStyle: {
color: '#008acd'
}
},
splitLine: {
lineStyle: {
color: ['#eee']
}
}
},
valueAxis: {
axisLine: {
lineStyle: {
color: '#008acd'
}
},
splitArea : {
show : true,
areaStyle : {
color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
}
},
splitLine: {
lineStyle: {
color: ['#eee']
}
}
},
timeline : {
lineStyle : {
color : '#008acd'
},
controlStyle : {
normal : { color : '#008acd'},
emphasis : { color : '#008acd'}
},
symbol : 'emptyCircle',
symbolSize : 3
},
line: {
smooth : true,
symbol: 'emptyCircle',
symbolSize: 3
},
candlestick: {
itemStyle: {
normal: {
color: '#d87a80',
color0: '#2ec7c9',
lineStyle: {
color: '#d87a80',
color0: '#2ec7c9'
}
}
}
},
scatter: {
symbol: 'circle',
symbolSize: 4
},
map: {
label: {
normal: {
textStyle: {
color: '#d87a80'
}
}
},
itemStyle: {
normal: {
borderColor: '#eee',
areaColor: '#ddd'
},
emphasis: {
areaColor: '#fe994e'
}
}
},
graph: {
color: colorPalette
},
gauge : {
axisLine: {
lineStyle: {
color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']],
width: 10
}
},
axisTick: {
splitNumber: 10,
length :15,
lineStyle: {
color: 'auto'
}
},
splitLine: {
length :22,
lineStyle: {
color: 'auto'
}
},
pointer : {
width : 5
}
}
};
echarts.registerTheme('macarons', theme);
}));

View File

@@ -0,0 +1,55 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if='item.meta.title'>
<span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{generateTitle(item.meta.title)}}</span>
<!--<router-link v-else :to="item.redirect||item.path">{{generateTitle(item.meta.title)}}</router-link>-->
<span v-else>{{generateTitle(item.meta.title)}}</span>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import { generateTitle } from '@/utils/i18n'
export default {
created() {
this.getBreadcrumb()
},
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
methods: {
generateTitle,
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name)
const first = matched[0]
if (first && first.name !== 'dashboard') {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
}
this.levelList = matched
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
/*color: #97a8be;*/
cursor: text;
}
}
</style>

View File

@@ -0,0 +1,152 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
const xAxisData = []
const data = []
const data2 = []
for (let i = 0; i < 50; i++) {
xAxisData.push(i)
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)
}
this.chart.setOption(
{
backgroundColor: '#08263a',
xAxis: [{
show: false,
data: xAxisData
}, {
show: false,
data: xAxisData
}],
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {
show: false
}
},
series: [{
name: 'back',
type: 'bar',
data: data2,
z: 1,
itemStyle: {
normal: {
opacity: 0.4,
barBorderRadius: 5,
shadowBlur: 3,
shadowColor: '#111'
}
}
}, {
name: 'Simulate Shadow',
type: 'line',
data,
z: 2,
showSymbol: false,
animationDelay: 0,
animationEasing: 'linear',
animationDuration: 1200,
lineStyle: {
normal: {
color: 'transparent'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20
},
animationDelayUpdate(idx) {
return idx * 20
}
})
}
}
}
</script>

View File

@@ -0,0 +1,227 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
this.chart.setOption({
backgroundColor: '#394056',
title: {
top: 20,
text: 'Requests',
textStyle: {
fontWeight: 'normal',
fontSize: 16,
color: '#F1F1F3'
},
left: '1%'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#57617B'
}
}
},
legend: {
top: 20,
icon: 'rect',
itemWidth: 14,
itemHeight: 5,
itemGap: 13,
data: ['CMCC', 'CTCC', 'CUCC'],
right: '4%',
textStyle: {
fontSize: 12,
color: '#F1F1F3'
}
},
grid: {
top: 100,
left: '3%',
right: '4%',
bottom: '2%',
containLabel: true
},
xAxis: [{
type: 'category',
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#57617B'
}
},
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
}],
yAxis: [{
type: 'value',
name: '(%)',
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#57617B'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
},
splitLine: {
lineStyle: {
color: '#57617B'
}
}
}],
series: [{
name: 'CMCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(137, 189, 27, 0.3)'
}, {
offset: 0.8,
color: 'rgba(137, 189, 27, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(137,189,27)',
borderColor: 'rgba(137,189,2,0.27)',
borderWidth: 12
}
},
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, {
name: 'CTCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 136, 212, 0.3)'
}, {
offset: 0.8,
color: 'rgba(0, 136, 212, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(0,136,212)',
borderColor: 'rgba(0,136,212,0.2)',
borderWidth: 12
}
},
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, {
name: 'CUCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(219, 50, 51, 0.3)'
}, {
offset: 0.8,
color: 'rgba(219, 50, 51, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(219,50,51)',
borderColor: 'rgba(219,50,51,0.2)',
borderWidth: 12
}
},
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
}]
})
}
}
}
</script>

View File

@@ -0,0 +1,269 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
const xData = (function() {
const data = []
for (let i = 1; i < 13; i++) {
data.push(i + 'month')
}
return data
}())
this.chart.setOption({
backgroundColor: '#344b58',
title: {
text: 'statistics',
x: '20',
top: '20',
textStyle: {
color: '#fff',
fontSize: '22'
},
subtextStyle: {
color: '#90979c',
fontSize: '16'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
textStyle: {
color: '#fff'
}
}
},
grid: {
borderWidth: 0,
top: 110,
bottom: 95,
textStyle: {
color: '#fff'
}
},
legend: {
x: '5%',
top: '10%',
textStyle: {
color: '#90979c'
},
data: ['female', 'male', 'average']
},
calculable: true,
xAxis: [{
type: 'category',
axisLine: {
lineStyle: {
color: '#90979c'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}],
yAxis: [{
type: 'value',
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: '#90979c'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
}
}],
dataZoom: [{
show: true,
height: 30,
xAxisIndex: [
0
],
bottom: 30,
start: 10,
end: 80,
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
handleSize: '110%',
handleStyle: {
color: '#d3dee5'
},
textStyle: {
color: '#fff' },
borderColor: '#90979c'
}, {
type: 'inside',
show: true,
height: 15,
start: 1,
end: 35
}],
series: [{
name: 'female',
type: 'bar',
stack: 'total',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
normal: {
color: 'rgba(255,144,128,1)',
label: {
show: true,
textStyle: {
color: '#fff'
},
position: 'insideTop',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
},
{
name: 'male',
type: 'bar',
stack: 'total',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
}, {
name: 'average',
type: 'line',
stack: 'total',
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
]
})
}
}
}
</script>

View File

@@ -0,0 +1,15 @@
import { debounce } from '@/utils'
export default {
mounted() {
this.__resizeHanlder = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
},
beforeDestroy() {
window.removeEventListener('resize', this.__resizeHanlder)
}
}

View File

@@ -0,0 +1,77 @@
<template>
<div v-if="errorLogs.length>0">
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="danger" class="bug-btn">
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
p-id="1864"></path>
</svg>
</el-button>
</el-badge>
<el-dialog title="Error Log" :visible.sync="dialogTableVisible" width="80%">
<el-table :data="errorLogs" border>
<el-table-column label="Message">
<template slot-scope="scope">
<div>
<span class="message-title">Msg:</span>
<el-tag type="danger">{{ scope.row.err.message }}</el-tag>
</div>
<br/>
<div>
<span class="message-title" style="padding-right: 10px;">Info: </span>
<el-tag type="warning">{{scope.row.vm.$vnode.tag}} error in {{scope.row.info}}</el-tag>
</div>
<br/>
<div>
<span class="message-title" style="padding-right: 16px;">Url: </span>
<el-tag type="success">{{scope.row.url}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="Stack">
<template slot-scope="scope">
{{ scope.row.err.stack}}
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'errorLog',
data() {
return {
dialogTableVisible: false
}
},
computed: {
errorLogs() {
return this.$store.getters.errorLogs
}
}
}
</script>
<style scoped>
.bug-btn.el-button--small {
padding: 9px 10px;
}
.bug-svg {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.message-title {
font-size: 16px;
color: #333;
font-weight: bold;
padding-right: 8px;
}
</style>

View File

@@ -0,0 +1,45 @@
<template>
<div>
<svg t="1492500959545" @click="toggleClick" class="hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"></path>
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"></path>
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"></path>
</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(90deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(0deg);
}
</style>

View File

@@ -0,0 +1,89 @@
<template>
<div class="board-column">
<div class="board-column-header">
{{headerText}}
</div>
<draggable
class="board-column-content"
:list="list"
:options="options">
<div class="board-item" v-for="element in list" :key="element.id">
{{element.name}} {{element.id}}
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'dragKanban-demo',
components: {
draggable
},
props: {
headerText: {
type: String,
default: 'Header'
},
options: {
type: Object,
default() {
return {}
}
},
list: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style lang="scss">
.board-column {
min-width: 300px;
min-height: 100px;
height: auto;
overflow: hidden;
background: #f0f0f0;
border-radius: 3px;
.board-column-header {
height: 50px;
line-height: 50px;
overflow: hidden;
padding: 0 20px;
text-align: center;
background: #333;
color: #fff;
border-radius: 3px 3px 0 0;
}
.board-column-content {
height: auto;
overflow: hidden;
border: 10px solid transparent;
min-height: 60px;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: center;
.board-item {
cursor: pointer;
width: 100%;
height: 64px;
margin: 5px 0;
background-color: #fff;
text-align: left;
line-height: 54px;
padding: 5px 10px;
box-sizing: border-box;
box-shadow: 0px 1px 3px 0 rgba(0,0,0,0.2);
}
}
}
</style>

View File

@@ -0,0 +1,41 @@
<template>
<el-dropdown trigger="click" class='international' @command="handleSetLanguage">
<div>
<svg-icon class-name='international-icon' icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh" :disabled="language==='zh'">中文</el-dropdown-item>
<el-dropdown-item command="en" :disabled="language==='en'">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
this.$message({
message: 'switch language success',
type: 'success'
})
}
}
}
</script>
<style scoped>
.international-icon {
font-size: 20px;
cursor: pointer;
vertical-align: -5px!important;
}
</style>

View File

@@ -0,0 +1,65 @@
<template>
<div>
<svg t="1508738709248" @click='click' class="screenfull-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="2069" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32">
<path d="M333.493443 428.647617 428.322206 333.832158 262.572184 168.045297 366.707916 64.444754 64.09683 64.444754 63.853283 366.570793 167.283957 262.460644Z"
p-id="2070"></path>
<path d="M854.845439 760.133334 688.61037 593.95864 593.805144 688.764889 759.554142 854.56096 655.44604 958.161503 958.055079 958.161503 958.274066 656.035464Z"
p-id="2071"></path>
<path d="M688.535669 428.550403 854.31025 262.801405 957.935352 366.921787 957.935352 64.34754 655.809313 64.081481 759.919463 167.535691 593.70793 333.731874Z"
p-id="2072"></path>
<path d="M333.590658 594.033341 167.8171 759.804852 64.218604 655.67219 64.218604 958.270996 366.342596 958.502263 262.234493 855.071589 428.421466 688.86108Z"
p-id="2073"></path>
</svg>
</div>
</template>
<script>
import screenfull from 'screenfull'
export default {
name: 'screenfull',
props: {
width: {
type: Number,
default: 22
},
height: {
type: Number,
default: 22
},
fill: {
type: String,
default: '#48576a'
}
},
data() {
return {
isFullscreen: false
}
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle()
}
}
}
</script>
<style scoped>
.screenfull-svg {
display: inline-block;
cursor: pointer;
fill: #5a5e66;;
width: 20px;
height: 20px;
vertical-align: 10px;
}
</style>

View File

@@ -0,0 +1,72 @@
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
<div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const padding = 15 // tag's padding
export default {
name: 'scrollPane',
data() {
return {
left: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $wrapper = this.$refs.scrollWrapper
const $wrapperWidth = $wrapper.offsetWidth
if (eventDelta > 0) {
this.left = Math.min(0, this.left + eventDelta)
} else {
if ($containerWidth - padding < $wrapperWidth) {
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
this.left = this.left
} else {
this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
}
} else {
this.left = 0
}
}
},
moveToTarget($target) {
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $targetLeft = $target.offsetLeft
const $targetWidth = $target.offsetWidth
if ($targetLeft < -this.left) {
// tag in the left
this.left = -$targetLeft + padding
} else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
// tag in the current view
// eslint-disable-line
} else {
// tag in the right
this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
.scroll-wrapper {
position: absolute;
}
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<div class="share-dropdown-menu" :class="{active:isActive}">
<div class="share-dropdown-menu-wrapper">
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{title}}</span>
<div class="share-dropdown-menu-item" v-for="(item,index) of items" :key='index'>
<a v-if="item.href" :href="item.href" target="_blank">{{item.title}}</a>
<span v-else>{{item.title}}</span>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array
},
title: {
type: String,
default: 'vue'
}
},
data() {
return {
isActive: false
}
},
methods: {
clickTitle() {
this.isActive = !this.isActive
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" >
$n: 6; //和items.length 相同
$t: .1s;
.share-dropdown-menu {
width: 250px;
position: relative;
z-index: 1;
&-title {
width: 100%;
display: block;
cursor: pointer;
background: black;
color: white;
height: 60px;
line-height: 60px;
font-size: 20px;
text-align: center;
z-index: 2;
transform: translate3d(0,0,0);
}
&-wrapper {
position: relative;
}
&-item {
text-align: center;
position: absolute;
width: 100%;
background: #e0e0e0;
line-height: 60px;
height: 60px;
cursor: pointer;
font-size: 20px;
opacity: 1;
transition: transform 0.28s ease;
&:hover {
background: black;
color: white;
}
@for $i from 1 through $n {
&:nth-of-type(#{$i}) {
z-index: -1;
transition-delay: $i*$t;
transform: translate3d(0, -60px, 0);
}
}
}
&.active {
.share-dropdown-menu-wrapper {
z-index: 1;
}
.share-dropdown-menu-item {
@for $i from 1 through $n {
&:nth-of-type(#{$i}) {
transition-delay: ($n - $i)*$t;
transform: translate3d(0, ($i - 1)*60px, 0);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,42 @@
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script>
export default {
name: 'svg-icon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,113 @@
<template>
<a class="link--mallki" :class="className" href="#">
{{text}}
<span :data-letters="text"></span>
<span :data-letters="text"></span>
</a>
</template>
<script>
export default {
props: {
className: {
type: String
},
text: {
type: String,
default: 'vue-element-admin'
}
}
}
</script>
<style>
/* Mallki */
.link--mallki {
font-weight: 800;
color: #4dd9d5;
font-family: 'Dosis', sans-serif;
-webkit-transition: color 0.5s 0.25s;
transition: color 0.5s 0.25s;
overflow: hidden;
position: relative;
display: inline-block;
line-height: 1;
outline: none;
text-decoration: none;
}
.link--mallki:hover {
-webkit-transition: none;
transition: none;
color: transparent;
}
.link--mallki::before {
content: '';
width: 100%;
height: 6px;
margin: -3px 0 0 0;
background: #3888fa;
position: absolute;
left: 0;
top: 50%;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
-webkit-transition: -webkit-transform 0.4s;
transition: transform 0.4s;
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
}
.link--mallki:hover::before {
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
.link--mallki span {
position: absolute;
height: 50%;
width: 100%;
left: 0;
top: 0;
overflow: hidden;
}
.link--mallki span::before {
content: attr(data-letters);
color: red;
position: absolute;
left: 0;
width: 100%;
color: #3888fa;
-webkit-transition: -webkit-transform 0.5s;
transition: transform 0.5s;
}
.link--mallki span:nth-child(2) {
top: 50%;
}
.link--mallki span:first-child::before {
top: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
.link--mallki span:nth-child(2)::before {
bottom: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
.link--mallki:hover span::before {
-webkit-transition-delay: 0.3s;
transition-delay: 0.3s;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<el-color-picker
class="theme-picker"
popper-class="theme-picker-dropdown"
v-model="theme"></el-color-picker>
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: ORIGINAL_THEME
}
},
watch: {
theme(val, oldVal) {
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
console.log(themeCluster, originalCluster)
const getHandler = (variable, id) => {
return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
const chalkHandler = getHandler('chalk', 'chalk-style')
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
this.getCSSString(url, chalkHandler, 'chalk')
} else {
chalkHandler()
}
const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => {
const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
})
styles.forEach(style => {
const { innerText } = style
if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
this.$message({
message: '换肤成功',
type: 'success'
})
}
},
methods: {
updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
})
return newStyle
},
getCSSString(url, callback, variable) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
},
getThemeCluster(theme) {
const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',')
} else {
red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue))
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
}
const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue)
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
const clusters = [theme]
for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
}
clusters.push(shadeColor(theme, 0.1))
return clusters
}
}
}
</script>
<style>
.theme-picker .el-color-picker__trigger {
vertical-align: middle;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
</style>

View File

@@ -0,0 +1,96 @@
<template>
<div class="upload-container">
<el-button icon='el-icon-upload' size="mini" :style="{background:color,borderColor:color}" @click=" dialogVisible=true" type="primary">上传图片
</el-button>
<el-dialog append-to-body :visible.sync="dialogVisible">
<el-upload class="editor-slide-upload" action="/admin/image/AliYunImgUpload" :multiple="true" :headers="uploadHeaders" :file-list="fileList" :show-file-list="true"
list-type="picture-card" :on-remove="handleRemove" :on-success="handleSuccess" :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'
var token = sessionStorage.token
export default {
name: 'editorSlideUpload',
props: {
color: {
type: String,
default: '#1890ff'
}
},
data() {
return {
dialogVisible: false,
listObj: {},
fileList: [],
uploadHeaders:{Authorization:token}
}
},
methods: {
checkAllSuccess() {
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
},
handleSubmit() {
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
if (!this.checkAllSuccess()) {
this.$message('请等待所有图片上传成功 或 出现了网络问题,请刷新页面重新上传!')
return
}
console.log(arr)
this.$emit('successCBK', arr)
this.listObj = {}
this.fileList = []
this.dialogVisible = false
},
handleSuccess(response, file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
this.listObj[objKeyArr[i]].url = response.data
this.listObj[objKeyArr[i]].hasSuccess = true
return
}
}
},
handleRemove(file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
delete this.listObj[objKeyArr[i]]
return
}
}
},
beforeUpload(file) {
const _self = this
const _URL = window.URL || window.webkitURL
const fileName = file.uid
this.listObj[fileName] = {}
return new Promise((resolve, reject) => {
const img = new Image()
img.src = _URL.createObjectURL(file)
img.onload = function() {
_self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
}
resolve(true)
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
.editor-slide-upload {
margin-bottom: 20px;
}
}
</style>

View File

@@ -0,0 +1,184 @@
<template>
<div class="tinymce-container editor-container" :class="{fullscreen:fullscreen}">
<textarea class="tinymce-textarea" :id="tinymceId"></textarea>
<div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"></editorImage>
</div>
</div>
</template>
<script>
import editorImage from './components/editorImage'
import plugins from './plugins'
import toolbar from './toolbar'
export default {
name: 'tinymce',
components: { editorImage },
props: {
id: {
type: String
},
value: {
type: String,
default: ''
},
toolbar: {
type: Array,
required: false,
default() {
return []
}
},
menubar: {
default: 'file edit insert view format table'
},
height: {
type: Number,
required: false,
default: 360
}
},
data() {
return {
hasChange: false,
hasInit: false,
tinymceId: this.id || 'vue-tinymce-' + +new Date(),
fullscreen: false
}
},
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val))
}
}
},
mounted() {
this.initTinymce()
},
activated() {
this.initTinymce()
},
deactivated() {
this.destroyTinymce()
},
methods: {
initTinymce() {
const _this = this
window.tinymce.init({
selector: `#${this.tinymceId}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar,
plugins: plugins,
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
default_link_target: '_blank',
link_title: false,
init_instance_callback: editor => {
if (_this.value) {
editor.setContent(_this.value)
}
_this.hasInit = true
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true
this.$emit('input', editor.getContent())
})
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
}
// 整合七牛上传
// images_dataimg_filter(img) {
// setTimeout(() => {
// const $image = $(img);
// $image.removeAttr('width');
// $image.removeAttr('height');
// if ($image[0].height && $image[0].width) {
// $image.attr('data-wscntype', 'image');
// $image.attr('data-wscnh', $image[0].height);
// $image.attr('data-wscnw', $image[0].width);
// $image.addClass('wscnph');
// }
// }, 0);
// return img
// },
// images_upload_handler(blobInfo, success, failure, progress) {
// progress(0);
// const token = _this.$store.getters.token;
// getToken(token).then(response => {
// const url = response.data.qiniu_url;
// const formData = new FormData();
// formData.append('token', response.data.qiniu_token);
// formData.append('key', response.data.qiniu_key);
// formData.append('file', blobInfo.blob(), url);
// upload(formData).then(() => {
// success(url);
// progress(100);
// })
// }).catch(err => {
// failure('出现未知问题,刷新页面,或者联系程序员')
// console.log(err);
// });
// },
})
},
destroyTinymce() {
if (window.tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy()
}
},
setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value)
},
getContent() {
window.tinymce.get(this.tinymceId).getContent()
},
imageSuccessCBK(arr) {
const _this = this
arr.forEach(v => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
})
}
},
destroyed() {
this.destroyTinymce()
}
}
</script>
<style scoped>
.tinymce-container {
position: relative;
}
.tinymce-container>>>.mce-fullscreen {
z-index: 10000;
}
.tinymce-textarea {
visibility: hidden;
z-index: -1;
}
.editor-custom-btn-container {
position: absolute;
right: 4px;
top: 4px;
/*z-index: 2005;*/
}
.fullscreen .editor-custom-btn-container {
z-index: 10000;
position: fixed;
}
.editor-upload-btn {
display: inline-block;
}
</style>

View File

@@ -0,0 +1,7 @@
// Any plugins you want to use has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
export default plugins

View File

@@ -0,0 +1,6 @@
// Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
export default toolbar

View File

@@ -0,0 +1,29 @@
/**
* @Author: jianglei
* @Date: 2017-10-12 12:06:49
*/
'use strict'
import Vue from 'vue'
export default function treeToArray(data, expandAll, parent = null, level = null) {
let tmp = []
Array.from(data).forEach(function(record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level)
tmp = tmp.concat(children)
}
})
return tmp
}

View File

@@ -0,0 +1,124 @@
<template>
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
<el-table-column v-if="columns.length===0" width="150">
<template slot-scope="scope">
<span v-for="space in scope.row._level" class="ms-tree-space" :key="space"></span>
<span class="tree-ctrl" v-if="iconShow(0,scope.row)" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<i v-else class="el-icon-minus"></i>
</span>
{{scope.$index}}
</template>
</el-table-column>
<el-table-column v-else v-for="(column, index) in columns" :key="column.value" :label="column.text" :width="column.width">
<template slot-scope="scope">
<span v-if="index === 0" v-for="space in scope.row._level" class="ms-tree-space" :key="space"></span>
<span class="tree-ctrl" v-if="iconShow(index,scope.row)" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<i v-else class="el-icon-minus"></i>
</span>
{{scope.row[column.value]}}
</template>
</el-table-column>
<slot></slot>
</el-table>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-13:59
*/
import treeToArray from './eval'
export default {
name: 'treeTable',
props: {
data: {
type: [Array, Object],
required: true
},
columns: {
type: Array,
default: () => []
},
evalFunc: Function,
evalArgs: Array,
expandAll: {
type: Boolean,
default: false
}
},
computed: {
// 格式化数据源
formatData: function() {
let tmp
if (!Array.isArray(this.data)) {
tmp = [this.data]
} else {
tmp = this.data
}
const func = this.evalFunc || treeToArray
const args = this.evalArgs ? Array.concat([tmp, this.expandAll], this.evalArgs) : [tmp, this.expandAll]
return func.apply(null, args)
}
},
methods: {
showRow: function(row) {
const show = (row.row.parent ? (row.row.parent._expanded && row.row.parent._show) : true)
row.row._show = show
return show ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;' : 'display:none;'
},
// 切换下级是否展开
toggleExpanded: function(trIndex) {
const record = this.formatData[trIndex]
record._expanded = !record._expanded
},
// 图标显示
iconShow(index, record) {
return (index === 0 && record.children && record.children.length > 0)
}
}
}
</script>
<style rel="stylesheet/css">
@keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
@-webkit-keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
</style>
<style lang="scss" rel="stylesheet/scss" scoped>
$color-blue: #2196F3;
$space-width: 18px;
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: $space-width;
height: 14px;
&::before {
content: ""
}
}
.processContainer{
width: 100%;
height: 100%;
}
table td {
line-height: 26px;
}
.tree-ctrl{
position: relative;
cursor: pointer;
color: $color-blue;
margin-left: -$space-width;
}
</style>

View File

@@ -0,0 +1,89 @@
## 写在前面
此组件仅提供一个创建TreeTable的解决思路
## prop说明
#### *data*
**必填**
原始数据,要求是一个数组或者对象
```javascript
[{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
},
{
key1: value1
}]
```
或者
```javascript
{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
}
```
#### columns
列属性,要求是一个数组
1. text: 显示在表头的文字
2. value: 对应data的key。treeTable将显示相应的value
3. width: 每列的宽度,为一个数字(可选)
如果你想要每个字段都有自定义的样式或者嵌套其他组件columns可不提供直接像在el-table一样写即可如果没有自定义内容提供columns将更加的便捷方便
如果你有几个字段是需要自定义的几个不需要那么可以将不需要自定义的字段放入columns将需要自定义的内容放入到slot中详情见后文
```javascript
[{
value:string,
text:string,
width:number
},{
value:string,
text:string,
width:number
}]
```
#### expandAll
是否默认全部展开boolean值默认为false
#### evalFunc
解析函数function非必须
如果不提供,将使用默认的[evalFunc](./eval.js)
如果提供了evalFunc,那么会用提供的evalFunc去解析data并返回treeTable渲染所需要的值。如何编写一个evalFunc请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customEval.js)
#### evalArgs
解析函数的参数,是一个数组
**请注意自定义的解析函数参数第一个为this.data第二个参数为 this.expandAll,你不需要在evalArgs填写。一定记住这两个参数是强制性的并且位置不可颠倒** *this.data为需要解析的数据this.expandAll为是否默认展开*
如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了
如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue)的`evalArgs`属性值
## slot
这是一个自定义列的插槽。
默认情况下treeTable只有一行行展示数据的功能。但是一般情况下我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue)[实例效果](http://panjiachen.github.io/vue-element-admin/#/example/table/custom-tree-table)
`slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示
## 其他
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue

View File

@@ -0,0 +1,382 @@
import {datePickerOptions} from '@/utils/elementUtil'
import {formUtils} from '@/utils/formUtil'
import {commonApi} from '@/api/commonApi'
import * as cvs from '@/utils/cvs'
import {uploadApi} from "../../api/uploadApi";
/**
* 全局CRUD组件抽取
*/
export default {
mounted() {
this._getList()
},
filters: {
elTagFilter(status) {
const statusMap = {
0: 'danger',
1: 'success',
2: 'info',
3: 'primary',
4: 'warning',
}
return statusMap[status]
},
elTagFilter2(status) {
const statusMap = {
1: 'danger',
0: 'success',
}
return statusMap[status]
},
statusFilter(status) {
const statusMap = {
0: '禁用',
1: '启用',
2: '错误状态',
}
return statusMap[status]
},
resourceTypeFilter(type) {
const statusMap = {
'WEB_BANNER': '大banner',
'LINK_BANNER': '小banner'
}
return statusMap[type]
},
// 实名认证审核状态 userAuthStatus 0 待审核 1 通过 2 拒绝
userAuthStatusFilter(reviewStatus) {
const statusMap = {
0: '待审核',
1: '审核通过',
2: '审核拒绝'
}
return statusMap[reviewStatus]
},
// 实名认证状态 authStatus: 0 未认证 1初级 2高级
authStatusFilter(authStatus) {
const statusMap = {
0: '未认证',
1: '初级',
2: '高级'
}
return statusMap[authStatus]
},
// 代理商注册审核
reviewsStatusFilter(reviewStatus) {
const statusMap = {
0: '待审核',
1: '审核通过',
2: '审核拒绝'
}
return statusMap[reviewStatus]
},
// 场外交易充值审核 充值来源类型 alipay支付宝cai1pay财易付bank银联
rechargeTypeFilter(type) {
const map = {
linepay: '在线充值',
alipay: '支付宝',
cai1pay: '财易付',
bank: '银联'
}
return map[type]
},
// 场外交易充值审核 充值来源类型 alipay支付宝cai1pay财易付bank银联
businessTypeFilter(type) {
const map = {
recharge_into: '充值',
withdrawals_out: '提现审核通过',
order_create: '下单',
order_turnover: '成交',
order_turnover_poundage: '成交手续费',
order_cancel: '撤单',
register_reward: '注册奖励',
invite_reward:'邀请奖励',
withdrawals: '提币冻结解冻',
recharge: '充人民币' ,
withdrawals_poundage: '提币手续费',
cny_btcx_exchange: '兑换' ,
bonus_into: '奖励充值' ,
bonus_freeze: '奖励冻结'
}
return map[type]
},
// 场外交易充值审核 状态 0-待审核1-审核通过2-拒绝3-充值成功;
rechargeStatusFilter(type) {
const map = {
0: '待审核',
1: '审核通过',
2: '拒绝',
3: '充值成功'
}
return map[type]
},
// cny 提现审核 状态 0-待审核1-审核通过2-拒绝3-提现成功;
withdrawStatusFilter(type) {
const map = {
0: '待审核',
1: '审核通过',
2: '拒绝',
3: '提现成功'
}
return map[type]
},
// 虚拟币提现管理列表0-审核中1-审核通过2-拒绝3-提币成功4撤销5-打币中;
coninWithdrawTypeFilter(type) {
const map = {
0: '审核中',
1: '转出成功',
2: '审核拒绝',
3: '撤销',
4: '审核通过',
5: '打币中',
6: '钱包异常,审核中'
}
return map[type]
},
// 资金流水列表收付类型1 入账 2 出账
accountFlowDirectionFilter(type) {
const map = {
1: '入账',
2: '出账',
}
return map[type]
},
// 币币交易列表状态 0未成交 1已成交 2已取消 4异常单
entrustManagerListStatusFilter(type) {
const map = {
0: '未成交',
1: '已成交',
2: '已取消',
4: '异常单',
}
return map[type]
},
// 币币交易列表交易方式 1-买入2-卖出
entrustManagerListTradtypeFilter(type) {
const map = {
1: '买入',
2: '卖出',
}
return map[type]
},
// 创新交易列表价格类型(1 市价 2 限价)
invovateListFilter(type) {
const map = {
1: '市价',
2: '限价',
}
return map[type]
},
// 创新交易列表订单类型1 开仓 2 平仓
invovateOrderTypeFilter(type) {
const map = {
1: '开仓',
2: '平仓',
}
return map[type]
},
// 虚拟币充值记录状态 0-待入账1-充值失败2-到账失败3-到账成功;
coinRechargeTypeListFilter(type) {
const map = {
0: '成功',
1: '失败',
}
return map[type]
},
// 客服回复状态1 未处理 2已处理
serviceStatusFilter(status) {
const map = {
1: '未处理',
2: '已处理',
}
return map[status]
},
// 资金账户列表状态
accountAssetsListStatus(type) {
const map = {
1: '正常',
2: '冻结'
};
return map[type]
},
// 交易区域配置类型 1 币币交易 2 创新交易
tradeAreaListType(type) {
const map = {
1: '币币交易',
2: '创新交易'
}
return map[type]
},
// 创建矿池状态
poolStatusFilter(authStatus) {
const statusMap = {
0: '待审核',
1: '通过',
2: '拒绝'
}
return statusMap[authStatus]
},
// 手动打款
withdrawType(type) {
const map = {
0: '站内',
1: '站外自动',
2: '站外手动'
}
return map[type]
}
},
data() {
return {
listLoading: true,
datePickerOptions: datePickerOptions,
listQuery: {
size: 15,
total: 0,
current: 1
},
listData: [],
deleteItems: [],
//oss预上传数据
uploadHost:"",
// 上传携带参数
uploadData:{},
}
},
methods: {
async _getList() {
this.listLoading = true
let res = await this.listCallback()
this.listLoading = false
if (typeof this.needListProcess === "function") {
this.listData = this.needListProcess(res.records)
} else {
this.listData = res.records
}
this.listQuery.current = res.current
this.listQuery.size = res.size
this.listQuery.total = res.total
},
submitForm(formName) {
this.listQuery.current = 1;
this.$refs[formName].validate((valid) => {
if (valid) {
this._getList()
} else {
console.log('error submit!!');
return false;
}
});
},
// 导出excel
exportExcel(formName, url, fileName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let {ruleForm, current, size} = this.$refs[formName].$parent;
let params = {}
formUtils.formDateRange(params, ruleForm);
params.current = current
params.size = size
commonApi.exportExcel(url, params).then((res) => {
let filename = fileName || decodeURIComponent(res.headers['content-filename']);
cvs.exportCsv(res.data, filename);
});
} else {
console.log('error submit!!');
return false;
}
});
},
handleSelectionChange(val) {
this.deleteItems = val;
},
handleDelete() {
if (this.deleteItems.length === 0) {
this.$alert('请选择需要删除的列表项!', '提示', {
confirmButtonText: '确定',
type: 'warning',
callback: action => {
}
});
} else {
this.$alert('确定要删除吗?', '提示', {
confirmButtonText: '确定',
type: 'warning',
callback: action => {
if (action === "confirm") {
this._deleteItems()
}
}
});
}
},
async handleChangeStatus(index, row) {
let {id, status} = row;
status = (status === 1 ? 0 : 1);
await this.changeStatusCallback(id, status)
this.$notify({
type: 'success',
title: '提示',
message: "更新成功!"
});
this._getList();
},
handlePageChange(currentPage) {
this.listQuery.current = currentPage
this._getList()
},
async _deleteItems() {
let ids = this.deleteItems.map((item) => {
console.log(item)
return item.id
});
console.log("ids", ids)
await this.deleteCallback(ids)
this.$notify({
type: 'success',
title: '提示',
message: '删除成功!',
});
this._getList()
},
async beforeUpload(){
let res = await uploadApi.getPreUpload()
if(res){
let preUploadData = res
console.log(preUploadData)
let {dir,policy,signature,callback,accessid,host} = preUploadData
this.uploadHost = host
this.uploadData.name=signature
this.uploadData.key=`${dir}${new Date().getTime()}.jpg`
this.uploadData.policy=policy
this.uploadData.OSSAccessKeyId=accessid
this.uploadData.success_action_status=200
this.uploadData.callback=callback
this.uploadData.signature=signature
}else{
return Promise.reject()
}
},
handleUploadSuccess(response) {
const { status,uri } = response
const aliyunFileUrl = 'https://coin-exchange-imgs.oss-cn-beijing.aliyuncs.com/'
if(status === "OK"){
this.coinForm.img = aliyunFileUrl + uri
console.log(this.coinForm.img)
}
}
}
}

View File

@@ -0,0 +1,134 @@
import {uploadApi} from "../../api/uploadApi";
/**
* 全局CRUD对话框组件抽取
*/
export default {
mounted() {
},
data(){
return {
dialogType:1,
dialogVisible: false,
uploadImgUrl:uploadApi.aliyunUrl,
statusFlag:'',
//oss预上传数据
uploadHost:"",
// 上传携带参数
uploadData:{},
}
},
filters:{
// 实名认证状态 authStatus: 0 未认证 1初级 2高级
authStatusFilter(authStatus) {
const statusMap = {
0: '未认证',
1: '初级',
2: '高级'
}
return statusMap[authStatus]
},
},
watch: {
statusFlag(val) {
console.log("当前状态",val)
if (val) {
this.ruleForm.status = 1
} else {
this.ruleForm.status = 0
}
},
// 银行卡状态 1启用 2禁用
bankCardStatus(val){
if (val) {
this.ruleForm.status = 1
} else {
this.ruleForm.status = 2
}
}
},
methods: {
showDialog(type,row){
this.dialogVisible = true
if (row){
this.ruleForm = row
}
if(type ===1){
this.dialogType = 1
}else{
this.dialogType = 2
// 编辑状态的特殊处理
if(typeof this.editCallback === "function"){
this.editCallback(row)
}
}
},
closeDialog(){
this.ruleForm = {}
// 关闭状态的特殊处理
if(typeof this.closeCallback === "function"){
this.closeCallback()
}
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
if(this.dialogType ===1){
this._createItem()
}else{
this._updateItem()
}
} else {
console.log('error submit!!');
return false;
}
});
},
async _createItem(){
await this.createCallback()
this.$notify({
type: 'success',
title: '提示',
message: '新建成功!',
});
this.dialogVisible = false
this.$emit("refreshList")
},
async _updateItem(){
await this.updateCallback()
this.$notify({
type: 'success',
title: '提示',
message: "更新成功!"
});
this.dialogVisible = false
this.$emit("refreshList")
},
handleUploadSuccess(response) {
const { code,data } = response
if(code == 200){
this.ruleForm.value = data
}
},
async beforeUpload(){
let res = await uploadApi.getPreUpload()
if(res.data.data){
let preUploadData = res.data.data
let {dir,policy,signature,callback,accessid,host} = preUploadData
this.uploadHost = host
this.uploadData.name=signature
this.uploadData.key=`${dir}${new Date().getTime()}.jpg`
this.uploadData.policy=policy
this.uploadData.OSSAccessKeyId=accessid
this.uploadData.success_action_status=200
this.uploadData.callback=callback
this.uploadData.signature=signature
}else{
return Promise.reject()
}
}
}
}

View File

@@ -0,0 +1,151 @@
import { datePickerOptions } from '@/utils/elementUtil'
import { formUtils } from '@/utils/formUtil'
import { commonApi } from '@/api/commonApi'
/**
* 全局key-value映射
*/
export default {
filters: {
elTagFilter(status) {
const statusMap = {
0: 'danger',
1: 'success',
2: 'info',
3: 'primary',
4: 'warning',
}
return statusMap[status]
},
statusFilter(status) {
const statusMap = {
0: '错误状态',
1: '启用',
2: '禁用',
true: '启用',
false: '禁用'
}
return statusMap[status]
},
resourceTypeFilter(type) {
const statusMap = {
1: '主banner',
2: '小banner'
}
return statusMap[type]
},
// 场外交易充值审核 充值来源类型 alipay支付宝cai1pay财易付bank银联
rechargeTypeFilter(type) {
const rechargeType = {
linepay: '在线充值',
alipay: '支付宝',
cai1pay: '财易付',
bank: '银联'
}
return rechargeType[type]
},
// 场外交易充值审核 状态 0-待审核1-审核通过2-拒绝3-充值成功;
rechargeStatusFilter(type) {
const rechargeStatus = {
0: '待审核',
1: '审核通过',
2: '拒绝',
3: '充值成功'
}
return rechargeStatus[type]
},
// 场外交易充值审核 状态 0-待审核1-审核通过2-拒绝3-充值成功;
withdrawStatusFilter(type) {
const withDrawStatus = {
0: '待审核',
1: '审核通过',
2: '拒绝',
3: '提现成功'
}
return withDrawStatus[type]
},
// 场外交易充值审核 充值来源类型 alipay支付宝cai1pay财易付bank银联
businessTypeFilter(type) {
const map = {
recharge_into: '充值',
withdrawals_out: '提现审核通过',
order_create: '下单',
order_turnover: '成交',
order_turnover_poundage: '成交手续费',
order_cancel: '撤单',
register_reward: '注册奖励',
invite_reward:'邀请奖励',
withdrawals: '提币冻结解冻',
recharge: '充人民币' ,
withdrawals_poundage: '提币手续费',
cny_btcx_exchange: '兑换' ,
bonus_into: '奖励充值' ,
bonus_freeze: '奖励冻结'
}
return map[type]
},
// 虚拟币提现管理列表0-审核中1-审核通过2-拒绝3-提币成功4撤销5-打币中;
coninWithdrawTypeFilter(type) {
const coninWithdrawType = {
0: '审核中',
1: '审核通过',
2: '拒绝',
3: '提币成功',
4: '撤销',
5: '打币中',
}
return coninWithdrawType[type]
},
// 资金流水列表收付类型1 入账 2 出账
accountFlowDirectionFilter(type) {
const accountFlowDirection = {
1: '入账',
2: '出账',
}
return accountFlowDirection[type]
},
// 币币交易列表状态 0未成交 1已成交 2已取消 4异常单
entrustManagerListStatusFilter(type) {
const map = {
0: '未成交',
1: '已成交',
2: '已取消',
4: '异常单',
}
return map[type]
},
// 币币交易列表交易方式 1-买入2-卖出
entrustManagerListTradtypeFilter(type) {
const map = {
1: '买入',
2: '卖出',
}
return map[type]
},
// 创新交易列表价格类型(1 市价 2 限价)
invovateListFilter(type) {
const map = {
1: '市价',
2: '限价',
}
return map[type]
},
// 创新交易列表订单类型1 开仓 2 平仓
invovateOrderTypeFilter(type) {
const map = {
1: '开仓',
2: '平仓',
}
return map[type]
},
},
data() {
return {}
},
methods: {
}
}

4
src/config/constants.js Normal file
View File

@@ -0,0 +1,4 @@
export const codeMap = {
40001: '登录失效,请重新登录!',
41001: '用户名密码错误!',
}

View File

@@ -0,0 +1,49 @@
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
if (!Clipboard) {
throw new Error('you shold npm install `clipboard` --save at first ')
}
export default {
bind(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
const clipboard = new Clipboard(el, {
text() { return binding.value },
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
})
clipboard.on('success', e => {
const callback = el._v_clipboard_success
callback && callback(e) // eslint-disable-line
})
clipboard.on('error', e => {
const callback = el._v_clipboard_error
callback && callback(e) // eslint-disable-line
})
el._v_clipboard = clipboard
}
},
update(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
el._v_clipboard.text = function() { return binding.value }
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
}
},
unbind(el, binding) {
if (binding.arg === 'success') {
delete el._v_clipboard_success
} else if (binding.arg === 'error') {
delete el._v_clipboard_error
} else {
el._v_clipboard.destroy()
delete el._v_clipboard
}
}
}

View File

@@ -0,0 +1,13 @@
import Clipboard from './clipboard'
const install = function(Vue) {
Vue.directive('Clipboard', Clipboard)
}
if (window.Vue) {
window.clipboard = Clipboard
Vue.use(install); // eslint-disable-line
}
Clipboard.install = install
export default Clipboard

View File

@@ -0,0 +1,74 @@
export default{
bind(el, binding) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomheight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
}

View File

@@ -0,0 +1,13 @@
import drag from './drag'
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag

View File

@@ -0,0 +1,13 @@
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission

View File

@@ -0,0 +1,23 @@
import store from '@/store'
export default{
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
}
}
}

91
src/directive/sticky.js Normal file
View File

@@ -0,0 +1,91 @@
const vueSticky = {}
let listenAction
vueSticky.install = Vue => {
Vue.directive('sticky', {
inserted(el, binding) {
const params = binding.value || {}
const stickyTop = params.stickyTop || 0
const zIndex = params.zIndex || 1000
const elStyle = el.style
elStyle.position = '-webkit-sticky'
elStyle.position = 'sticky'
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const elHeight = el.getBoundingClientRect().height
const elWidth = el.getBoundingClientRect().width
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
const parentElm = el.parentNode || document.documentElement
const placeholder = document.createElement('div')
placeholder.style.display = 'none'
placeholder.style.width = `${elWidth}px`
placeholder.style.height = `${elHeight}px`
parentElm.insertBefore(placeholder, el)
let active = false
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset'
const method = top ? 'scrollTop' : 'scrollLeft'
let ret = target[prop]
if (typeof ret !== 'number') {
ret = window.document.documentElement[method]
}
return ret
}
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed'
elStyle.width = `${elWidth}px`
placeholder.style.display = 'inline-block'
active = true
}
const reset = () => {
if (!active) {
return
}
elStyle.position = ''
placeholder.style.display = 'none'
active = false
}
const check = () => {
const scrollTop = getScroll(window, true)
const offsetTop = el.getBoundingClientRect().top
if (offsetTop < stickyTop) {
sticky()
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
}
}
listenAction = () => {
check()
}
window.addEventListener('scroll', listenAction)
},
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
}
export default vueSticky

View File

@@ -0,0 +1,13 @@
import waves from './waves'
const install = function(Vue) {
Vue.directive('waves', waves)
}
if (window.Vue) {
window.waves = waves
Vue.use(install); // eslint-disable-line
}
waves.install = install
export default waves

View File

@@ -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;
}

View File

@@ -0,0 +1,42 @@
import './waves.css'
export default{
bind(el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts)
const target = opts.ele
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'waves-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'waves-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
break
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.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)
}
}

21
src/errorLog.js Normal file
View File

@@ -0,0 +1,21 @@
import Vue from 'vue'
import store from './store'
// you can set only in production env show the error-log
// if (process.env.NODE_ENV === 'production') {
Vue.config.errorHandler = function(err, vm, info, a) {
// Don't ask me why I use Vue.nextTick, it just a hack.
// detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500
Vue.nextTick(() => {
store.dispatch('addErrorLog', {
err,
vm,
info,
url: window.location.href
})
console.error(err, info)
})
}
// }

104
src/filters/index.js Normal file
View File

@@ -0,0 +1,104 @@
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+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

12
src/icons/index.js Normal file
View File

@@ -0,0 +1,12 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件
import generateIconsView from '@/views/svg-icons/generateIconsView.js'// just for @/views/icons , you can delete it
// register globally
Vue.component('svg-icon', SvgIcon)
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
const iconMap = requireAll(req)
generateIconsView.generate(iconMap) // just for @/views/icons , you can delete it

1
src/icons/svg/404.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994850540" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10206" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M931.6 585.6l0 79c28.6-60.2 44.8-127.4 44.8-198.4C976.4 211 769.4 4 514.2 4S52 211 52 466.2c0 3.2 0.2 6.4 0.2 9.6l166-206 96.4 0L171.8 485.6l46.4 0 0-54.8 99.2-154.6 0 209.4 0 100 0 82.4-99.2 0 0-82.4L67.6 585.6c43 161 170.6 287.4 332.4 328.6-10.4 26.2-40.6 89.4-90.8 100.6-62.2 14 168.8 3.4 333.6-104.6 126.6-36.6 230.8-125.8 287.4-242.2l-97.6 0 0-82.4-166.2 0 0-87.2 0-12.8L666.4 476l166.2-206.2 94 0-140.4 215.8 46.4 0 0-59 99.2-154 0 213.2L931.8 585.6zM366.2 608c-4.8-11.2-7.2-23.2-7.2-36L359 357.6c0-12.8 2.4-24.8 7.2-36 4.8-11.2 11.4-21 19.6-29.2 8.2-8.2 18-14.8 29.2-19.6 11.2-4.8 23.2-7.2 36-7.2l81.6 0c12.8 0 24.8 2.4 36 7.2 11 4.8 20.6 11.2 28.8 19.2l-88.6 129.4 0-23c0-4.8-1.6-8.8-4.8-12-3.2-3.2-7.2-4.8-12-4.8-4.8 0-8.8 1.6-12 4.8-3.2 3.2-4.8 7.2-4.8 12l0 72L372.6 620C370.2 616.2 368 612.2 366.2 608zM624.4 572c0 12.8-2.4 24.8-7.2 36-4.8 11.2-11.4 21-19.6 29.2-8.2 8.2-18 14.8-29.2 19.6-11.2 4.8-23.2 7.2-36 7.2l-81.6 0c-12.8 0-24.8-2.4-36-7.2-11.2-4.8-21-11.4-29.2-19.6-3.6-3.6-7-7.8-10-12l99.2-144.6 0 50.6c0 4.8 1.6 8.8 4.8 12 3.2 3.2 7.2 4.8 12 4.8 4.8 0 8.8-1.6 12-4.8 3.2-3.2 4.8-7.2 4.8-12l0-99.6 92.6-135.2c6.6 7.4 12 15.8 16 25.2 4.8 11.2 7.2 23.2 7.2 36L624.2 572z" p-id="10207"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

1
src/icons/svg/bug.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994864347" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10314" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z" p-id="10315"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
src/icons/svg/chart.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994873331" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10422" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M64 448 320 448 320 960 64 960 64 448 64 448ZM704 256 960 256 960 960 704 960 704 256 704 256ZM384 64 640 64 640 960 384 960 384 64 384 64Z" p-id="10423"></path></svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1506419860538" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4662" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M438.857143 950.857143l512 0 0-365.714286-237.714286 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-237.714286-219.428571 0 0 658.285714zM585.142857 128l0-36.571429q0-7.460571-5.412571-12.873143t-12.873143-5.412571l-402.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 36.571429q0 7.460571 5.412571 12.873143t12.873143 5.412571l402.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143zM731.428571 512l170.861714 0-170.861714-170.861714 0 170.861714zM1024 585.142857l0 384q0 22.820571-16.018286 38.838857t-38.838857 16.018286l-548.571429 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-91.428571-310.857143 0q-22.820571 0-38.838857-16.018286t-16.018286-38.838857l0-768q0-22.820571 16.018286-38.838857t38.838857-16.018286l621.714286 0q22.820571 0 38.838857 16.018286t16.018286 38.838857l0 187.465143q11.995429 7.460571 20.553143 16.018286l233.179429 233.179429q16.018286 16.018286 27.428571 43.446857t11.410286 50.322286z" p-id="4663"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1506329916765" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1661" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M64 64 448 64 448 448 64 448 64 64ZM64 576 448 576 448 960 64 960 64 576ZM576 576 960 576 960 960 576 960 576 576ZM768 448C874.038669 448 960 362.038672 960 256 960 149.961328 874.038669 64 768 64 661.961328 64 576 149.961328 576 256 576 362.038672 661.961328 448 768 448Z" p-id="1662"></path></svg>

After

Width:  |  Height:  |  Size: 683 B

Some files were not shown because too many files have changed in this diff Show More