刘力
3 years ago
commit
517b7b74e9
50 changed files with 29045 additions and 0 deletions
-
12.babelrc
-
9.editorconfig
-
8.env.development
-
7.env.production
-
4.eslintignore
-
198.eslintrc.js
-
14.gitignore
-
10.postcssrc.js
-
5.travis.yml
-
21README.md
-
0babel.config.js
-
41build/build.js
-
54build/check-versions.js
-
BINbuild/logo.png
-
101build/utils.js
-
22build/vue-loader.conf.js
-
82build/webpack.base.conf.js
-
95build/webpack.dev.conf.js
-
145build/webpack.prod.conf.js
-
7config/dev.env.js
-
69config/index.js
-
4config/prod.env.js
-
12index.html
-
26186package-lock.json
-
112package.json
-
0postcss.config.js
-
11src/App.vue
-
BINsrc/assets/logo.png
-
50src/main.js
-
50src/settings.js
-
25src/store/getters.js
-
22src/store/index.js
-
28src/store/modules/api.js
-
56src/store/modules/app.js
-
83src/store/modules/permission.js
-
37src/store/modules/settings.js
-
165src/store/modules/tagsView.js
-
94src/store/modules/user.js
-
18src/utils/auth.js
-
36src/utils/clipboard.js
-
216src/utils/datetime.js
-
388src/utils/index.js
-
23src/utils/permission.js
-
88src/utils/request.js
-
14src/utils/rsaEncrypt.js
-
108src/utils/shortcuts.js
-
11src/utils/upload.js
-
167src/utils/validate.js
-
0static/.gitkeep
-
137vue.config.js
@ -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"] |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
charset = utf-8 |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
end_of_line = lf |
||||
|
insert_final_newline = true |
||||
|
trim_trailing_whitespace = true |
@ -0,0 +1,8 @@ |
|||||
|
ENV = 'development' |
||||
|
|
||||
|
# 接口地址 |
||||
|
VUE_APP_BASE_API = 'http://localhost:8000' |
||||
|
VUE_APP_WS_API = 'ws://localhost:8000' |
||||
|
|
||||
|
# 是否启用 babel-plugin-dynamic-import-node插件 |
||||
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true |
@ -0,0 +1,7 @@ |
|||||
|
ENV = 'production' |
||||
|
|
||||
|
# 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置 |
||||
|
# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http |
||||
|
VUE_APP_BASE_API = 'https://el-admin.vip' |
||||
|
# 如果接口是 http 形式, wss 需要改为 ws |
||||
|
VUE_APP_WS_API = 'wss://el-admin.vip' |
@ -0,0 +1,4 @@ |
|||||
|
build/*.js |
||||
|
src/assets |
||||
|
public |
||||
|
dist |
@ -0,0 +1,198 @@ |
|||||
|
module.exports = { |
||||
|
root: true, |
||||
|
parserOptions: { |
||||
|
parser: 'babel-eslint', |
||||
|
sourceType: 'module' |
||||
|
}, |
||||
|
env: { |
||||
|
browser: true, |
||||
|
node: true, |
||||
|
es6: true, |
||||
|
}, |
||||
|
extends: ['plugin:vue/recommended', 'eslint:recommended'], |
||||
|
|
||||
|
// add your custom rules here
|
||||
|
//it is base on https://github.com/vuejs/eslint-config-vue
|
||||
|
rules: { |
||||
|
"vue/max-attributes-per-line": [2, { |
||||
|
"singleline": 10, |
||||
|
"multiline": { |
||||
|
"max": 1, |
||||
|
"allowFirstLine": false |
||||
|
} |
||||
|
}], |
||||
|
"vue/singleline-html-element-content-newline": "off", |
||||
|
"vue/multiline-html-element-content-newline":"off", |
||||
|
"vue/name-property-casing": ["error", "PascalCase"], |
||||
|
"vue/no-v-html": "off", |
||||
|
'accessor-pairs': 2, |
||||
|
'arrow-spacing': [2, { |
||||
|
'before': true, |
||||
|
'after': true |
||||
|
}], |
||||
|
'block-spacing': [2, 'always'], |
||||
|
'brace-style': [2, '1tbs', { |
||||
|
'allowSingleLine': true |
||||
|
}], |
||||
|
'camelcase': [0, { |
||||
|
'properties': 'always' |
||||
|
}], |
||||
|
'comma-dangle': [2, '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': ["error", "always", {"null": "ignore"}], |
||||
|
'generator-star-spacing': [2, { |
||||
|
'before': true, |
||||
|
'after': true |
||||
|
}], |
||||
|
'handle-callback-err': [2, '^(err|error)$'], |
||||
|
'indent': [2, 2, { |
||||
|
'SwitchCase': 1 |
||||
|
}], |
||||
|
'jsx-quotes': [2, 'prefer-single'], |
||||
|
'key-spacing': [2, { |
||||
|
'beforeColon': false, |
||||
|
'afterColon': true |
||||
|
}], |
||||
|
'keyword-spacing': [2, { |
||||
|
'before': true, |
||||
|
'after': true |
||||
|
}], |
||||
|
'new-cap': [2, { |
||||
|
'newIsCap': true, |
||||
|
'capIsNew': false |
||||
|
}], |
||||
|
'new-parens': 2, |
||||
|
'no-array-constructor': 2, |
||||
|
'no-caller': 2, |
||||
|
'no-console': 'off', |
||||
|
'no-class-assign': 2, |
||||
|
'no-cond-assign': 2, |
||||
|
'no-const-assign': 2, |
||||
|
'no-control-regex': 0, |
||||
|
'no-delete-var': 2, |
||||
|
'no-dupe-args': 2, |
||||
|
'no-dupe-class-members': 2, |
||||
|
'no-dupe-keys': 2, |
||||
|
'no-duplicate-case': 2, |
||||
|
'no-empty-character-class': 2, |
||||
|
'no-empty-pattern': 2, |
||||
|
'no-eval': 2, |
||||
|
'no-ex-assign': 2, |
||||
|
'no-extend-native': 2, |
||||
|
'no-extra-bind': 2, |
||||
|
'no-extra-boolean-cast': 2, |
||||
|
'no-extra-parens': [2, 'functions'], |
||||
|
'no-fallthrough': 2, |
||||
|
'no-floating-decimal': 2, |
||||
|
'no-func-assign': 2, |
||||
|
'no-implied-eval': 2, |
||||
|
'no-inner-declarations': [2, 'functions'], |
||||
|
'no-invalid-regexp': 2, |
||||
|
'no-irregular-whitespace': 2, |
||||
|
'no-iterator': 2, |
||||
|
'no-label-var': 2, |
||||
|
'no-labels': [2, { |
||||
|
'allowLoop': false, |
||||
|
'allowSwitch': false |
||||
|
}], |
||||
|
'no-lone-blocks': 2, |
||||
|
'no-mixed-spaces-and-tabs': 2, |
||||
|
'no-multi-spaces': 2, |
||||
|
'no-multi-str': 2, |
||||
|
'no-multiple-empty-lines': [2, { |
||||
|
'max': 1 |
||||
|
}], |
||||
|
'no-native-reassign': 2, |
||||
|
'no-negated-in-lhs': 2, |
||||
|
'no-new-object': 2, |
||||
|
'no-new-require': 2, |
||||
|
'no-new-symbol': 2, |
||||
|
'no-new-wrappers': 2, |
||||
|
'no-obj-calls': 2, |
||||
|
'no-octal': 2, |
||||
|
'no-octal-escape': 2, |
||||
|
'no-path-concat': 2, |
||||
|
'no-proto': 2, |
||||
|
'no-redeclare': 2, |
||||
|
'no-regex-spaces': 2, |
||||
|
'no-return-assign': [2, 'except-parens'], |
||||
|
'no-self-assign': 2, |
||||
|
'no-self-compare': 2, |
||||
|
'no-sequences': 2, |
||||
|
'no-shadow-restricted-names': 2, |
||||
|
'no-spaced-func': 2, |
||||
|
'no-sparse-arrays': 2, |
||||
|
'no-this-before-super': 2, |
||||
|
'no-throw-literal': 2, |
||||
|
'no-trailing-spaces': 2, |
||||
|
'no-undef': 2, |
||||
|
'no-undef-init': 2, |
||||
|
'no-unexpected-multiline': 2, |
||||
|
'no-unmodified-loop-condition': 2, |
||||
|
'no-unneeded-ternary': [2, { |
||||
|
'defaultAssignment': false |
||||
|
}], |
||||
|
'no-unreachable': 2, |
||||
|
'no-unsafe-finally': 2, |
||||
|
'no-unused-vars': [2, { |
||||
|
'vars': 'all', |
||||
|
'args': 'none' |
||||
|
}], |
||||
|
'no-useless-call': 2, |
||||
|
'no-useless-computed-key': 2, |
||||
|
'no-useless-constructor': 2, |
||||
|
'no-useless-escape': 0, |
||||
|
'no-whitespace-before-property': 2, |
||||
|
'no-with': 2, |
||||
|
'one-var': [2, { |
||||
|
'initialized': 'never' |
||||
|
}], |
||||
|
'operator-linebreak': [2, 'after', { |
||||
|
'overrides': { |
||||
|
'?': 'before', |
||||
|
':': 'before' |
||||
|
} |
||||
|
}], |
||||
|
'padded-blocks': [2, 'never'], |
||||
|
'quotes': [2, 'single', { |
||||
|
'avoidEscape': true, |
||||
|
'allowTemplateLiterals': true |
||||
|
}], |
||||
|
'semi': [2, 'never'], |
||||
|
'semi-spacing': [2, { |
||||
|
'before': false, |
||||
|
'after': true |
||||
|
}], |
||||
|
'space-before-blocks': [2, 'always'], |
||||
|
'space-before-function-paren': [2, 'never'], |
||||
|
'space-in-parens': [2, 'never'], |
||||
|
'space-infix-ops': 2, |
||||
|
'space-unary-ops': [2, { |
||||
|
'words': true, |
||||
|
'nonwords': false |
||||
|
}], |
||||
|
'spaced-comment': [2, 'always', { |
||||
|
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] |
||||
|
}], |
||||
|
'template-curly-spacing': [2, 'never'], |
||||
|
'use-isnan': 2, |
||||
|
'valid-typeof': 2, |
||||
|
'wrap-iife': [2, 'any'], |
||||
|
'yield-star-spacing': [2, 'both'], |
||||
|
'yoda': [2, 'never'], |
||||
|
'prefer-const': 2, |
||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, |
||||
|
'object-curly-spacing': [2, 'always', { |
||||
|
objectsInObjects: false |
||||
|
}], |
||||
|
'array-bracket-spacing': [2, 'never'] |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
.DS_Store |
||||
|
node_modules/ |
||||
|
/dist/ |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
# Editor directories and files |
||||
|
.idea |
||||
|
.vscode |
||||
|
*.suo |
||||
|
*.ntvs* |
||||
|
*.njsproj |
||||
|
*.sln |
@ -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": {} |
||||
|
} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
language: node_js |
||||
|
node_js: 10 |
||||
|
script: npm run test |
||||
|
notifications: |
||||
|
email: false |
@ -0,0 +1,21 @@ |
|||||
|
# yak_storeroom_web |
||||
|
|
||||
|
> A Vue.js project |
||||
|
|
||||
|
## Build Setup |
||||
|
|
||||
|
``` bash |
||||
|
# install dependencies |
||||
|
npm install |
||||
|
|
||||
|
# serve with hot reload at localhost:8080 |
||||
|
npm run dev |
||||
|
|
||||
|
# build for production with minification |
||||
|
npm run build |
||||
|
|
||||
|
# build for production and view the bundle analyzer report |
||||
|
npm run build --report |
||||
|
``` |
||||
|
|
||||
|
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). |
@ -0,0 +1,41 @@ |
|||||
|
'use strict' |
||||
|
require('./check-versions')() |
||||
|
|
||||
|
process.env.NODE_ENV = 'production' |
||||
|
|
||||
|
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 spinner = ora('building for production...') |
||||
|
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, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
|
||||
|
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' |
||||
|
)) |
||||
|
}) |
||||
|
}) |
@ -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) |
||||
|
} |
||||
|
} |
After Width: 200 | Height: 200 | Size: 6.7 KiB |
@ -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') |
||||
|
}) |
||||
|
} |
||||
|
} |
@ -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' |
||||
|
} |
||||
|
} |
@ -0,0 +1,82 @@ |
|||||
|
'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) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
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: [ |
||||
|
{ |
||||
|
test: /\.vue$/, |
||||
|
loader: 'vue-loader', |
||||
|
options: vueLoaderConfig |
||||
|
}, |
||||
|
{ |
||||
|
test: /\.js$/, |
||||
|
loader: 'babel-loader', |
||||
|
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] |
||||
|
}, |
||||
|
{ |
||||
|
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, |
||||
|
loader: 'url-loader', |
||||
|
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' |
||||
|
} |
||||
|
} |
@ -0,0 +1,95 @@ |
|||||
|
'use strict' |
||||
|
const utils = require('./utils') |
||||
|
const webpack = require('webpack') |
||||
|
const config = require('../config') |
||||
|
const merge = require('webpack-merge') |
||||
|
const path = require('path') |
||||
|
const baseWebpackConfig = require('./webpack.base.conf') |
||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin') |
||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin') |
||||
|
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') |
||||
|
const portfinder = require('portfinder') |
||||
|
|
||||
|
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: { |
||||
|
rewrites: [ |
||||
|
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, |
||||
|
], |
||||
|
}, |
||||
|
hot: true, |
||||
|
contentBase: false, // since we use CopyWebpackPlugin.
|
||||
|
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 |
||||
|
}), |
||||
|
// copy custom static assets
|
||||
|
new CopyWebpackPlugin([ |
||||
|
{ |
||||
|
from: path.resolve(__dirname, '../static'), |
||||
|
to: config.dev.assetsSubDirectory, |
||||
|
ignore: ['.*'] |
||||
|
} |
||||
|
]) |
||||
|
] |
||||
|
}) |
||||
|
|
||||
|
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) |
||||
|
} |
||||
|
}) |
||||
|
}) |
@ -0,0 +1,145 @@ |
|||||
|
'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 env = require('../config/prod.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.
|
||||
|
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
|
||||
|
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
|
||||
|
allChunks: true, |
||||
|
}), |
||||
|
// 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, |
||||
|
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 vendor 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 |
||||
|
}), |
||||
|
|
||||
|
// copy custom static assets
|
||||
|
new CopyWebpackPlugin([ |
||||
|
{ |
||||
|
from: path.resolve(__dirname, '../static'), |
||||
|
to: config.build.assetsSubDirectory, |
||||
|
ignore: ['.*'] |
||||
|
} |
||||
|
]) |
||||
|
] |
||||
|
}) |
||||
|
|
||||
|
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 |
@ -0,0 +1,7 @@ |
|||||
|
'use strict' |
||||
|
const merge = require('webpack-merge') |
||||
|
const prodEnv = require('./prod.env') |
||||
|
|
||||
|
module.exports = merge(prodEnv, { |
||||
|
NODE_ENV: '"development"' |
||||
|
}) |
@ -0,0 +1,69 @@ |
|||||
|
'use strict' |
||||
|
// Template version: 1.3.1
|
||||
|
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
|
|
||||
|
const path = require('path') |
||||
|
|
||||
|
module.exports = { |
||||
|
dev: { |
||||
|
|
||||
|
// Paths
|
||||
|
assetsSubDirectory: 'static', |
||||
|
assetsPublicPath: '/', |
||||
|
proxyTable: {}, |
||||
|
|
||||
|
// Various Dev Server settings
|
||||
|
host: 'localhost', // can be overwritten by process.env.HOST
|
||||
|
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||
|
autoOpenBrowser: false, |
||||
|
errorOverlay: true, |
||||
|
notifyOnErrors: true, |
||||
|
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Source Maps |
||||
|
*/ |
||||
|
|
||||
|
// https://webpack.js.org/configuration/devtool/#development
|
||||
|
devtool: 'cheap-module-eval-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, |
||||
|
|
||||
|
cssSourceMap: true |
||||
|
}, |
||||
|
|
||||
|
build: { |
||||
|
// Template for index.html
|
||||
|
index: path.resolve(__dirname, '../dist/index.html'), |
||||
|
|
||||
|
// Paths
|
||||
|
assetsRoot: path.resolve(__dirname, '../dist'), |
||||
|
assetsSubDirectory: 'static', |
||||
|
assetsPublicPath: '/', |
||||
|
|
||||
|
/** |
||||
|
* Source Maps |
||||
|
*/ |
||||
|
|
||||
|
productionSourceMap: true, |
||||
|
// 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 |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
'use strict' |
||||
|
module.exports = { |
||||
|
NODE_ENV: '"production"' |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
||||
|
<title>yak_storeroom_web</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<!-- built files will be auto injected --> |
||||
|
</body> |
||||
|
</html> |
26186
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,112 @@ |
|||||
|
{ |
||||
|
"name": "yak_storeroom_web", |
||||
|
"version": "1.0.0", |
||||
|
"description": "A Vue.js project", |
||||
|
"author": "刘力 <soldierliuli@hotmail.com>", |
||||
|
"private": true, |
||||
|
"scripts": { |
||||
|
"dev": "vue-cli-service serve", |
||||
|
"lint": "eslint --ext .js,.vue src", |
||||
|
"build:prod": "vue-cli-service build", |
||||
|
"build:stage": "vue-cli-service build --mode staging", |
||||
|
"preview": "node build/index.js --preview", |
||||
|
"new": "plop", |
||||
|
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", |
||||
|
"test:unit": "jest --clearCache && vue-cli-service test:unit", |
||||
|
"test:ci": "npm run lint && npm run test:unit" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"axios": "0.18.1", |
||||
|
"clipboard": "2.0.4", |
||||
|
"codemirror": "5.45.0", |
||||
|
"core-js": "3.6.5", |
||||
|
"driver.js": "0.9.5", |
||||
|
"dropzone": "5.5.1", |
||||
|
"echarts": "4.2.1", |
||||
|
"element-ui": "2.13.2", |
||||
|
"file-saver": "2.0.1", |
||||
|
"fuse.js": "3.4.4", |
||||
|
"js-cookie": "2.2.0", |
||||
|
"jsonlint": "1.6.3", |
||||
|
"jszip": "3.2.1", |
||||
|
"normalize.css": "7.0.0", |
||||
|
"nprogress": "0.2.0", |
||||
|
"path-to-regexp": "2.4.0", |
||||
|
"screenfull": "4.2.0", |
||||
|
"script-loader": "0.7.2", |
||||
|
"sortablejs": "1.8.4", |
||||
|
"tui-editor": "1.3.3", |
||||
|
"vue": "2.6.10", |
||||
|
"vue-count-to": "1.0.13", |
||||
|
"vue-router": "3.0.2", |
||||
|
"vue-splitpane": "1.0.4", |
||||
|
"vuedraggable": "2.20.0", |
||||
|
"vuex": "3.1.0", |
||||
|
"xlsx": "0.14.1" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@vue/cli-plugin-babel": "4.4.4", |
||||
|
"@vue/cli-plugin-eslint": "4.4.4", |
||||
|
"@vue/cli-plugin-unit-jest": "4.4.4", |
||||
|
"@vue/cli-service": "4.4.4", |
||||
|
"@vue/test-utils": "1.0.0-beta.29", |
||||
|
"autoprefixer": "9.5.1", |
||||
|
"babel-eslint": "10.1.0", |
||||
|
"babel-jest": "23.6.0", |
||||
|
"babel-plugin-dynamic-import-node": "2.3.3", |
||||
|
"chalk": "2.4.2", |
||||
|
"chokidar": "2.1.5", |
||||
|
"connect": "3.6.6", |
||||
|
"eslint": "6.7.2", |
||||
|
"eslint-plugin-vue": "6.2.2", |
||||
|
"html-webpack-plugin": "3.2.0", |
||||
|
"husky": "1.3.1", |
||||
|
"lint-staged": "8.1.5", |
||||
|
"mockjs": "1.0.1-beta3", |
||||
|
"plop": "2.3.0", |
||||
|
"runjs": "4.3.2", |
||||
|
"sass": "1.26.2", |
||||
|
"sass-loader": "8.0.2", |
||||
|
"script-ext-html-webpack-plugin": "2.1.3", |
||||
|
"serve-static": "1.13.2", |
||||
|
"svg-sprite-loader": "4.1.3", |
||||
|
"svgo": "1.2.0", |
||||
|
"vue-template-compiler": "2.6.10" |
||||
|
}, |
||||
|
"browserslist": [ |
||||
|
"> 1%", |
||||
|
"last 2 versions" |
||||
|
], |
||||
|
"bugs": { |
||||
|
"url": "https://github.com/PanJiaChen/vue-element-admin/issues" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=8.9", |
||||
|
"npm": ">= 3.0.0" |
||||
|
}, |
||||
|
"keywords": [ |
||||
|
"vue", |
||||
|
"admin", |
||||
|
"dashboard", |
||||
|
"element-ui", |
||||
|
"boilerplate", |
||||
|
"admin-template", |
||||
|
"management-system" |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"lint-staged": { |
||||
|
"src/**/*.{js,vue}": [ |
||||
|
"eslint --fix", |
||||
|
"git add" |
||||
|
] |
||||
|
}, |
||||
|
"husky": { |
||||
|
"hooks": { |
||||
|
"pre-commit": "lint-staged" |
||||
|
} |
||||
|
}, |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git" |
||||
|
} |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
<template> |
||||
|
<div id="app"> |
||||
|
<router-view /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'App' |
||||
|
} |
||||
|
</script> |
After Width: 200 | Height: 200 | Size: 6.7 KiB |
@ -0,0 +1,50 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
import Cookies from 'js-cookie' |
||||
|
|
||||
|
import 'normalize.css/normalize.css' |
||||
|
|
||||
|
import Element from 'element-ui' |
||||
|
//
|
||||
|
import mavonEditor from 'mavon-editor' |
||||
|
import 'mavon-editor/dist/css/index.css' |
||||
|
|
||||
|
// 数据字典
|
||||
|
import dict from './components/Dict' |
||||
|
|
||||
|
// 权限指令
|
||||
|
import checkPer from '@/utils/permission' |
||||
|
import permission from './components/Permission' |
||||
|
import './assets/styles/element-variables.scss' |
||||
|
// global css
|
||||
|
import './assets/styles/index.scss' |
||||
|
|
||||
|
// 代码高亮
|
||||
|
import VueHighlightJS from 'vue-highlightjs' |
||||
|
import 'highlight.js/styles/atom-one-dark.css' |
||||
|
|
||||
|
import App from './App' |
||||
|
import store from './store' |
||||
|
import router from './router/routers' |
||||
|
|
||||
|
import './assets/icons' // icon
|
||||
|
import './router/index' // permission control
|
||||
|
import 'echarts-gl' |
||||
|
|
||||
|
Vue.use(checkPer) |
||||
|
Vue.use(VueHighlightJS) |
||||
|
Vue.use(mavonEditor) |
||||
|
Vue.use(permission) |
||||
|
Vue.use(dict) |
||||
|
Vue.use(Element, { |
||||
|
size: Cookies.get('size') || 'small' // set element-ui default size
|
||||
|
}) |
||||
|
|
||||
|
Vue.config.productionTip = false |
||||
|
|
||||
|
new Vue({ |
||||
|
el: '#app', |
||||
|
router, |
||||
|
store, |
||||
|
render: h => h(App) |
||||
|
}) |
@ -0,0 +1,50 @@ |
|||||
|
module.exports = { |
||||
|
/** |
||||
|
* @description 网站标题 |
||||
|
*/ |
||||
|
title: 'yxk-storeroom', |
||||
|
/** |
||||
|
* @description 是否显示 tagsView |
||||
|
*/ |
||||
|
tagsView: true, |
||||
|
/** |
||||
|
* @description 固定头部 |
||||
|
*/ |
||||
|
fixedHeader: true, |
||||
|
/** |
||||
|
* @description 记住密码状态下的token在Cookie中存储的天数,默认1天 |
||||
|
*/ |
||||
|
tokenCookieExpires: 1, |
||||
|
/** |
||||
|
* @description 记住密码状态下的密码在Cookie中存储的天数,默认1天s |
||||
|
*/ |
||||
|
passCookieExpires: 1, |
||||
|
/** |
||||
|
* @description 是否只保持一个子菜单的展开 |
||||
|
*/ |
||||
|
uniqueOpened: true, |
||||
|
/** |
||||
|
* @description token key |
||||
|
*/ |
||||
|
TokenKey: 'YXK-STOREROOM-TOEKN', |
||||
|
/** |
||||
|
* @description 请求超时时间,毫秒(默认2分钟) |
||||
|
*/ |
||||
|
timeout: 1200000, |
||||
|
/** |
||||
|
* @description 是否显示logo |
||||
|
*/ |
||||
|
sidebarLogo: true, |
||||
|
/** |
||||
|
* 是否显示设置的底部信息 |
||||
|
*/ |
||||
|
showFooter: true, |
||||
|
/** |
||||
|
* 底部文字,支持html语法 |
||||
|
*/ |
||||
|
footerTxt: '© 2022 liu li <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache License 2.0</a>', |
||||
|
/** |
||||
|
* 备案号 |
||||
|
*/ |
||||
|
caseNumber: 'ICP备1456456号' |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
const getters = { |
||||
|
deployUploadApi: state => state.api.deployUploadApi, |
||||
|
databaseUploadApi: state => state.api.databaseUploadApi, |
||||
|
size: state => state.app.size, |
||||
|
sidebar: state => state.app.sidebar, |
||||
|
device: state => state.app.device, |
||||
|
token: state => state.user.token, |
||||
|
visitedViews: state => state.tagsView.visitedViews, |
||||
|
cachedViews: state => state.tagsView.cachedViews, |
||||
|
roles: state => state.user.roles, |
||||
|
user: state => state.user.user, |
||||
|
loadMenus: state => state.user.loadMenus, |
||||
|
permission_routers: state => state.permission.routers, |
||||
|
addRouters: state => state.permission.addRouters, |
||||
|
socketApi: state => state.api.socketApi, |
||||
|
imagesUploadApi: state => state.api.imagesUploadApi, |
||||
|
baseApi: state => state.api.baseApi, |
||||
|
fileUploadApi: state => state.api.fileUploadApi, |
||||
|
updateAvatarApi: state => state.api.updateAvatarApi, |
||||
|
qiNiuUploadApi: state => state.api.qiNiuUploadApi, |
||||
|
sqlApi: state => state.api.sqlApi, |
||||
|
swaggerApi: state => state.api.swaggerApi, |
||||
|
sidebarRouters: state => state.permission.sidebarRouters |
||||
|
} |
||||
|
export default getters |
@ -0,0 +1,22 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import Vuex from 'vuex' |
||||
|
import getters from './getters' |
||||
|
|
||||
|
Vue.use(Vuex) |
||||
|
|
||||
|
const modulesFiles = require.context('./modules', true, /\.js$/) |
||||
|
|
||||
|
const modules = modulesFiles.keys().reduce((modules, modulePath) => { |
||||
|
// set './app.js' => 'app'
|
||||
|
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') |
||||
|
const value = modulesFiles(modulePath) |
||||
|
modules[moduleName] = value.default |
||||
|
return modules |
||||
|
}, {}) |
||||
|
|
||||
|
const store = new Vuex.Store({ |
||||
|
modules, |
||||
|
getters |
||||
|
}) |
||||
|
|
||||
|
export default store |
@ -0,0 +1,28 @@ |
|||||
|
// 适配 Nginx 反向代理
|
||||
|
const baseUrl = process.env.VUE_APP_BASE_API === '/' ? '' : process.env.VUE_APP_BASE_API |
||||
|
const api = { |
||||
|
state: { |
||||
|
// 部署包上传
|
||||
|
deployUploadApi: baseUrl + '/api/deploy/upload', |
||||
|
// SQL脚本上传
|
||||
|
databaseUploadApi: baseUrl + '/api/database/upload', |
||||
|
// 实时控制台
|
||||
|
socketApi: baseUrl + '/websocket?token=kl', |
||||
|
// 图片上传
|
||||
|
imagesUploadApi: baseUrl + '/api/localStorage/pictures', |
||||
|
// 修改头像
|
||||
|
updateAvatarApi: baseUrl + '/api/users/updateAvatar', |
||||
|
// 上传文件到七牛云
|
||||
|
qiNiuUploadApi: baseUrl + '/api/qiNiuContent', |
||||
|
// Sql 监控
|
||||
|
sqlApi: baseUrl + '/druid/index.html', |
||||
|
// swagger
|
||||
|
swaggerApi: baseUrl + '/swagger-ui.html', |
||||
|
// 文件上传
|
||||
|
fileUploadApi: baseUrl + '/api/localStorage', |
||||
|
// baseUrl,
|
||||
|
baseApi: baseUrl |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default api |
@ -0,0 +1,56 @@ |
|||||
|
import Cookies from 'js-cookie' |
||||
|
|
||||
|
const state = { |
||||
|
sidebar: { |
||||
|
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, |
||||
|
withoutAnimation: false |
||||
|
}, |
||||
|
device: 'desktop', |
||||
|
size: Cookies.get('size') || 'small' |
||||
|
} |
||||
|
|
||||
|
const mutations = { |
||||
|
TOGGLE_SIDEBAR: state => { |
||||
|
state.sidebar.opened = !state.sidebar.opened |
||||
|
state.sidebar.withoutAnimation = false |
||||
|
if (state.sidebar.opened) { |
||||
|
Cookies.set('sidebarStatus', 1) |
||||
|
} else { |
||||
|
Cookies.set('sidebarStatus', 0) |
||||
|
} |
||||
|
}, |
||||
|
CLOSE_SIDEBAR: (state, withoutAnimation) => { |
||||
|
Cookies.set('sidebarStatus', 0) |
||||
|
state.sidebar.opened = false |
||||
|
state.sidebar.withoutAnimation = withoutAnimation |
||||
|
}, |
||||
|
TOGGLE_DEVICE: (state, device) => { |
||||
|
state.device = device |
||||
|
}, |
||||
|
SET_SIZE: (state, size) => { |
||||
|
state.size = size |
||||
|
Cookies.set('size', size) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const actions = { |
||||
|
toggleSideBar({ commit }) { |
||||
|
commit('TOGGLE_SIDEBAR') |
||||
|
}, |
||||
|
closeSideBar({ commit }, { withoutAnimation }) { |
||||
|
commit('CLOSE_SIDEBAR', withoutAnimation) |
||||
|
}, |
||||
|
toggleDevice({ commit }, device) { |
||||
|
commit('TOGGLE_DEVICE', device) |
||||
|
}, |
||||
|
setSize({ commit }, size) { |
||||
|
commit('SET_SIZE', size) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
mutations, |
||||
|
actions |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
import { constantRouterMap } from '@/router/routers' |
||||
|
import Layout from '@/layout/index' |
||||
|
import ParentView from '@/components/ParentView' |
||||
|
|
||||
|
const permission = { |
||||
|
state: { |
||||
|
routers: constantRouterMap, |
||||
|
addRouters: [], |
||||
|
sidebarRouters: [] |
||||
|
}, |
||||
|
mutations: { |
||||
|
SET_ROUTERS: (state, routers) => { |
||||
|
state.addRouters = routers |
||||
|
state.routers = constantRouterMap.concat(routers) |
||||
|
}, |
||||
|
SET_SIDEBAR_ROUTERS: (state, routers) => { |
||||
|
state.sidebarRouters = constantRouterMap.concat(routers) |
||||
|
} |
||||
|
}, |
||||
|
actions: { |
||||
|
GenerateRoutes({ commit }, asyncRouter) { |
||||
|
commit('SET_ROUTERS', asyncRouter) |
||||
|
}, |
||||
|
SetSidebarRouters({ commit }, sidebarRouter) { |
||||
|
commit('SET_SIDEBAR_ROUTERS', sidebarRouter) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const filterAsyncRouter = (routers, lastRouter = false, type = false) => { // 遍历后台传来的路由字符串,转换为组件对象
|
||||
|
return routers.filter(router => { |
||||
|
if (type && router.children) { |
||||
|
router.children = filterChildren(router.children) |
||||
|
} |
||||
|
if (router.component) { |
||||
|
if (router.component === 'Layout') { // Layout组件特殊处理
|
||||
|
router.component = Layout |
||||
|
} else if (router.component === 'ParentView') { |
||||
|
router.component = ParentView |
||||
|
} else { |
||||
|
const component = router.component |
||||
|
router.component = loadView(component) |
||||
|
} |
||||
|
} |
||||
|
if (router.children != null && router.children && router.children.length) { |
||||
|
router.children = filterAsyncRouter(router.children, router, type) |
||||
|
} else { |
||||
|
delete router['children'] |
||||
|
delete router['redirect'] |
||||
|
} |
||||
|
return true |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function filterChildren(childrenMap, lastRouter = false) { |
||||
|
var children = [] |
||||
|
childrenMap.forEach((el, index) => { |
||||
|
if (el.children && el.children.length) { |
||||
|
if (el.component === 'ParentView') { |
||||
|
el.children.forEach(c => { |
||||
|
c.path = el.path + '/' + c.path |
||||
|
if (c.children && c.children.length) { |
||||
|
children = children.concat(filterChildren(c.children, c)) |
||||
|
return |
||||
|
} |
||||
|
children.push(c) |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
if (lastRouter) { |
||||
|
el.path = lastRouter.path + '/' + el.path |
||||
|
} |
||||
|
children = children.concat(el) |
||||
|
}) |
||||
|
return children |
||||
|
} |
||||
|
|
||||
|
export const loadView = (view) => { |
||||
|
return (resolve) => require([`@/views/${view}`], resolve) |
||||
|
} |
||||
|
|
||||
|
export default permission |
@ -0,0 +1,37 @@ |
|||||
|
import variables from '@/assets/styles/element-variables.scss' |
||||
|
import defaultSettings from '@/settings' |
||||
|
const { tagsView, fixedHeader, sidebarLogo, uniqueOpened, showFooter, footerTxt, caseNumber } = defaultSettings |
||||
|
|
||||
|
const state = { |
||||
|
theme: variables.theme, |
||||
|
showSettings: false, |
||||
|
tagsView: tagsView, |
||||
|
fixedHeader: fixedHeader, |
||||
|
sidebarLogo: sidebarLogo, |
||||
|
uniqueOpened: uniqueOpened, |
||||
|
showFooter: showFooter, |
||||
|
footerTxt: footerTxt, |
||||
|
caseNumber: caseNumber |
||||
|
} |
||||
|
|
||||
|
const mutations = { |
||||
|
CHANGE_SETTING: (state, { key, value }) => { |
||||
|
if (state.hasOwnProperty(key)) { |
||||
|
state[key] = value |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const actions = { |
||||
|
changeSetting({ commit }, data) { |
||||
|
commit('CHANGE_SETTING', data) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
mutations, |
||||
|
actions |
||||
|
} |
||||
|
|
@ -0,0 +1,165 @@ |
|||||
|
const state = { |
||||
|
visitedViews: [], |
||||
|
cachedViews: [] |
||||
|
} |
||||
|
|
||||
|
const mutations = { |
||||
|
ADD_VISITED_VIEW: (state, view) => { |
||||
|
if (state.visitedViews.some(v => v.path === view.path)) return |
||||
|
state.visitedViews.push( |
||||
|
Object.assign({}, view, { |
||||
|
title: view.meta.title || 'no-name' |
||||
|
}) |
||||
|
) |
||||
|
}, |
||||
|
ADD_CACHED_VIEW: (state, view) => { |
||||
|
if (state.cachedViews.includes(view.name)) return |
||||
|
if (!view.meta.noCache) { |
||||
|
state.cachedViews.push(view.name) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
DEL_VISITED_VIEW: (state, view) => { |
||||
|
for (const [i, v] of state.visitedViews.entries()) { |
||||
|
if (v.path === view.path) { |
||||
|
state.visitedViews.splice(i, 1) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
DEL_CACHED_VIEW: (state, view) => { |
||||
|
for (const i of state.cachedViews) { |
||||
|
if (i === view.name) { |
||||
|
const index = state.cachedViews.indexOf(i) |
||||
|
state.cachedViews.splice(index, 1) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
DEL_OTHERS_VISITED_VIEWS: (state, view) => { |
||||
|
state.visitedViews = state.visitedViews.filter(v => { |
||||
|
return v.meta.affix || v.path === view.path |
||||
|
}) |
||||
|
}, |
||||
|
DEL_OTHERS_CACHED_VIEWS: (state, view) => { |
||||
|
for (const i of state.cachedViews) { |
||||
|
if (i === view.name) { |
||||
|
const index = state.cachedViews.indexOf(i) |
||||
|
state.cachedViews = state.cachedViews.slice(index, index + 1) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
DEL_ALL_VISITED_VIEWS: state => { |
||||
|
// keep affix tags
|
||||
|
const affixTags = state.visitedViews.filter(tag => tag.meta.affix) |
||||
|
state.visitedViews = affixTags |
||||
|
}, |
||||
|
DEL_ALL_CACHED_VIEWS: state => { |
||||
|
state.cachedViews = [] |
||||
|
}, |
||||
|
|
||||
|
UPDATE_VISITED_VIEW: (state, view) => { |
||||
|
for (let v of state.visitedViews) { |
||||
|
if (v.path === view.path) { |
||||
|
v = Object.assign(v, view) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const actions = { |
||||
|
addView({ dispatch }, view) { |
||||
|
dispatch('addVisitedView', view) |
||||
|
dispatch('addCachedView', view) |
||||
|
}, |
||||
|
addVisitedView({ commit }, view) { |
||||
|
commit('ADD_VISITED_VIEW', view) |
||||
|
}, |
||||
|
addCachedView({ commit }, view) { |
||||
|
commit('ADD_CACHED_VIEW', view) |
||||
|
}, |
||||
|
|
||||
|
delView({ dispatch, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
dispatch('delVisitedView', view) |
||||
|
dispatch('delCachedView', view) |
||||
|
resolve({ |
||||
|
visitedViews: [...state.visitedViews], |
||||
|
cachedViews: [...state.cachedViews] |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
delVisitedView({ commit, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_VISITED_VIEW', view) |
||||
|
resolve([...state.visitedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
delCachedView({ commit, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_CACHED_VIEW', view) |
||||
|
resolve([...state.cachedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
delOthersViews({ dispatch, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
dispatch('delOthersVisitedViews', view) |
||||
|
dispatch('delOthersCachedViews', view) |
||||
|
resolve({ |
||||
|
visitedViews: [...state.visitedViews], |
||||
|
cachedViews: [...state.cachedViews] |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
delOthersVisitedViews({ commit, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_OTHERS_VISITED_VIEWS', view) |
||||
|
resolve([...state.visitedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
delOthersCachedViews({ commit, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_OTHERS_CACHED_VIEWS', view) |
||||
|
resolve([...state.cachedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
delAllViews({ dispatch, state }, view) { |
||||
|
return new Promise(resolve => { |
||||
|
dispatch('delAllVisitedViews', view) |
||||
|
dispatch('delAllCachedViews', view) |
||||
|
resolve({ |
||||
|
visitedViews: [...state.visitedViews], |
||||
|
cachedViews: [...state.cachedViews] |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
delAllVisitedViews({ commit, state }) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_ALL_VISITED_VIEWS') |
||||
|
resolve([...state.visitedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
delAllCachedViews({ commit, state }) { |
||||
|
return new Promise(resolve => { |
||||
|
commit('DEL_ALL_CACHED_VIEWS') |
||||
|
resolve([...state.cachedViews]) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
updateVisitedView({ commit }, view) { |
||||
|
commit('UPDATE_VISITED_VIEW', view) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
mutations, |
||||
|
actions |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
import { login, getInfo, logout } from '@/api/login' |
||||
|
import { getToken, setToken, removeToken } from '@/utils/auth' |
||||
|
|
||||
|
const user = { |
||||
|
state: { |
||||
|
token: getToken(), |
||||
|
user: {}, |
||||
|
roles: [], |
||||
|
// 第一次加载菜单时用到
|
||||
|
loadMenus: false |
||||
|
}, |
||||
|
|
||||
|
mutations: { |
||||
|
SET_TOKEN: (state, token) => { |
||||
|
state.token = token |
||||
|
}, |
||||
|
SET_USER: (state, user) => { |
||||
|
state.user = user |
||||
|
}, |
||||
|
SET_ROLES: (state, roles) => { |
||||
|
state.roles = roles |
||||
|
}, |
||||
|
SET_LOAD_MENUS: (state, loadMenus) => { |
||||
|
state.loadMenus = loadMenus |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
actions: { |
||||
|
// 登录
|
||||
|
Login({ commit }, userInfo) { |
||||
|
const rememberMe = userInfo.rememberMe |
||||
|
return new Promise((resolve, reject) => { |
||||
|
login(userInfo.username, userInfo.password, userInfo.code, userInfo.uuid).then(res => { |
||||
|
setToken(res.token, rememberMe) |
||||
|
commit('SET_TOKEN', res.token) |
||||
|
setUserInfo(res.user, commit) |
||||
|
// 第一次加载菜单时用到, 具体见 src 目录下的 permission.js
|
||||
|
commit('SET_LOAD_MENUS', true) |
||||
|
resolve() |
||||
|
}).catch(error => { |
||||
|
reject(error) |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 获取用户信息
|
||||
|
GetInfo({ commit }) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
getInfo().then(res => { |
||||
|
setUserInfo(res, commit) |
||||
|
resolve(res) |
||||
|
}).catch(error => { |
||||
|
reject(error) |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
// 登出
|
||||
|
LogOut({ commit }) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
logout().then(res => { |
||||
|
logOut(commit) |
||||
|
resolve() |
||||
|
}).catch(error => { |
||||
|
logOut(commit) |
||||
|
reject(error) |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
updateLoadMenus({ commit }) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
commit('SET_LOAD_MENUS', false) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const logOut = (commit) => { |
||||
|
commit('SET_TOKEN', '') |
||||
|
commit('SET_ROLES', []) |
||||
|
removeToken() |
||||
|
} |
||||
|
|
||||
|
export const setUserInfo = (res, commit) => { |
||||
|
// 如果没有任何权限,则赋予一个默认的权限,避免请求死循环
|
||||
|
if (res.roles.length === 0) { |
||||
|
commit('SET_ROLES', ['ROLE_SYSTEM_DEFAULT']) |
||||
|
} else { |
||||
|
commit('SET_ROLES', res.roles) |
||||
|
} |
||||
|
commit('SET_USER', res.user) |
||||
|
} |
||||
|
|
||||
|
export default user |
@ -0,0 +1,18 @@ |
|||||
|
import Cookies from 'js-cookie' |
||||
|
import Config from '@/settings' |
||||
|
|
||||
|
const TokenKey = Config.TokenKey |
||||
|
|
||||
|
export function getToken() { |
||||
|
return Cookies.get(TokenKey) |
||||
|
} |
||||
|
|
||||
|
export function setToken(token, rememberMe) { |
||||
|
if (rememberMe) { |
||||
|
return Cookies.set(TokenKey, token, { expires: Config.tokenCookieExpires }) |
||||
|
} else return Cookies.set(TokenKey, token) |
||||
|
} |
||||
|
|
||||
|
export function removeToken() { |
||||
|
return Cookies.remove(TokenKey) |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import Clipboard from 'clipboard' |
||||
|
|
||||
|
function clipboardSuccess() { |
||||
|
Vue.prototype.$message({ |
||||
|
message: 'Copy successfully', |
||||
|
type: 'success', |
||||
|
duration: 1500 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function clipboardError() { |
||||
|
Vue.prototype.$message({ |
||||
|
message: 'Copy failed', |
||||
|
type: 'error' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export default function handleClipboard(text, event) { |
||||
|
const clipboard = new Clipboard(event.target, { |
||||
|
text: () => text |
||||
|
}) |
||||
|
clipboard.on('success', () => { |
||||
|
clipboardSuccess() |
||||
|
clipboard.off('error') |
||||
|
clipboard.off('success') |
||||
|
clipboard.destroy() |
||||
|
}) |
||||
|
clipboard.on('error', () => { |
||||
|
clipboardError() |
||||
|
clipboard.off('error') |
||||
|
clipboard.off('success') |
||||
|
clipboard.destroy() |
||||
|
}) |
||||
|
clipboard.onClick(event) |
||||
|
} |
@ -0,0 +1,216 @@ |
|||||
|
/* eslint-disable */ |
||||
|
|
||||
|
/** |
||||
|
* Date对象的补充函数,包括类似Python中的strftime() |
||||
|
* 阿债 https://gitee.com/azhai/datetime.js
|
||||
|
*/ |
||||
|
|
||||
|
Date.prototype.toMidnight = function() { |
||||
|
this.setHours(0) |
||||
|
this.setMinutes(0) |
||||
|
this.setSeconds(0) |
||||
|
this.setMilliseconds(0) |
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
Date.prototype.daysAgo = function(days, midnight) { |
||||
|
days = days ? days - 0 : 0 |
||||
|
const date = new Date(this.getTime() - days * 8.64E7) |
||||
|
return midnight ? date.toMidnight() : date |
||||
|
} |
||||
|
|
||||
|
Date.prototype.monthBegin = function(offset) { |
||||
|
offset = offset ? offset - 0 : 0 |
||||
|
const days = this.getDate() - 1 - offset |
||||
|
return this.daysAgo(days, true) |
||||
|
} |
||||
|
|
||||
|
Date.prototype.quarterBegin = function() { |
||||
|
const month = this.getMonth() - this.getMonth() % 3 |
||||
|
return new Date(this.getFullYear(), month, 1).toMidnight() |
||||
|
} |
||||
|
|
||||
|
Date.prototype.yearBegin = function() { |
||||
|
return new Date(this.getFullYear(), 0, 1).toMidnight() |
||||
|
} |
||||
|
|
||||
|
Date.prototype.strftime = function(format, local) { |
||||
|
if (!format) { |
||||
|
const str = new Date(this.getTime() + 2.88E7).toISOString() |
||||
|
return str.substr(0, 16).replace('T', ' ') |
||||
|
} |
||||
|
local = local && local.startsWith('zh') ? 'zh' : 'en' |
||||
|
const padZero = function(str, len) { |
||||
|
const pads = len - str.toString().length |
||||
|
return (pads && pads > 0 ? '0'.repeat(pads) : '') + str |
||||
|
} |
||||
|
format = format.replace('%F', '%Y-%m-%d') |
||||
|
format = format.replace(/%D|%x/, '%m/%d/%y') |
||||
|
format = format.replace(/%T|%X/, '%H:%M:%S') |
||||
|
format = format.replace('%R', '%H:%M') |
||||
|
format = format.replace('%r', '%H:%M:%S %p') |
||||
|
format = format.replace('%c', '%a %b %e %H:%M:%S %Y') |
||||
|
const _this = this |
||||
|
return format.replace(/%[A-Za-z%]/g, function(f) { |
||||
|
let ans = f |
||||
|
switch (f) { |
||||
|
case '%%': |
||||
|
ans = '%' |
||||
|
break |
||||
|
|
||||
|
case '%Y': |
||||
|
case '%G': |
||||
|
ans = _this.getFullYear() |
||||
|
break |
||||
|
|
||||
|
case '%y': |
||||
|
ans = _this.getFullYear() % 100 |
||||
|
break |
||||
|
|
||||
|
case '%C': |
||||
|
ans = _this.getFullYear() / 100 |
||||
|
break |
||||
|
|
||||
|
case '%m': |
||||
|
case '%n': |
||||
|
ans = _this.getMonth() + 1 |
||||
|
break |
||||
|
|
||||
|
case '%B': |
||||
|
local = local.startsWith('en') ? 'english' : local |
||||
|
|
||||
|
case '%b': |
||||
|
const m = _this.getMonth() |
||||
|
ans = local_labels.monthes[local][m] |
||||
|
break |
||||
|
|
||||
|
case '%d': |
||||
|
case '%e': |
||||
|
ans = _this.getDate() |
||||
|
break |
||||
|
|
||||
|
case '%j': |
||||
|
ans = _this.getDaysOfYear() |
||||
|
break |
||||
|
|
||||
|
case '%U': |
||||
|
case '%W': |
||||
|
const ws = _this.getWeeksOfYear(f === '%W') |
||||
|
ans = padZero(ws, 2) |
||||
|
break |
||||
|
|
||||
|
case '%w': |
||||
|
ans = _this.getDay() |
||||
|
|
||||
|
case '%u': |
||||
|
ans = ans === 0 ? 7 : ans |
||||
|
break |
||||
|
|
||||
|
case '%A': |
||||
|
local = local.startsWith('en') ? 'english' : local |
||||
|
|
||||
|
case '%a': |
||||
|
const d = _this.getDay() |
||||
|
ans = local_labels.weekdays[local][d] |
||||
|
break |
||||
|
|
||||
|
case '%H': |
||||
|
case '%k': |
||||
|
ans = _this.getHours() |
||||
|
break |
||||
|
|
||||
|
case '%I': |
||||
|
case '%l': |
||||
|
ans = _this.getHours() |
||||
|
ans = ans % 12 |
||||
|
break |
||||
|
|
||||
|
case '%M': |
||||
|
ans = _this.getMinutes() |
||||
|
break |
||||
|
|
||||
|
case '%S': |
||||
|
ans = _this.getSeconds() |
||||
|
break |
||||
|
|
||||
|
case '%s': |
||||
|
ans = parseInt(_this.getTime() / 1E3) |
||||
|
break |
||||
|
|
||||
|
case '%f': |
||||
|
const ms = _this.getMilliseconds() |
||||
|
ans = padZero(ms * 1E3, 6) |
||||
|
break |
||||
|
|
||||
|
case '%P': |
||||
|
local = local.startsWith('en') ? 'english' : local |
||||
|
|
||||
|
case '%p': |
||||
|
const h = _this.getHours() |
||||
|
ans = local_labels.meridians[local][h < 12 ? 0 : 1] |
||||
|
break |
||||
|
|
||||
|
case '%z': |
||||
|
let tzo = _this.getTimezoneOffset() |
||||
|
const sign = tzo < 0 ? '-' : '+' |
||||
|
tzo = Math.abs(tzo) |
||||
|
const ho = padZero(tzo / 60, 2) |
||||
|
const mo = padZero(tzo % 60, 2) |
||||
|
ans = sign + ho + mo |
||||
|
break |
||||
|
|
||||
|
default: |
||||
|
break |
||||
|
} |
||||
|
if (f === '%C' || f === '%y' || f === '%m' || f === '%d' || f === '%H' || f === '%M' || f === '%S') { |
||||
|
ans = padZero(ans, 2) |
||||
|
} |
||||
|
return ans.toString() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
Date.prototype.humanize = function(local) { |
||||
|
local = local && local.startsWith('zh') ? 'zh' : 'en' |
||||
|
const result = this.strftime('', local) |
||||
|
const days = (Date.today() - this.toMidnight().getTime()) / 8.64E7 |
||||
|
if (days <= -10 || days >= 10) { |
||||
|
return result |
||||
|
} |
||||
|
const labels = local_labels.dayagos[local] |
||||
|
let lbl = '' |
||||
|
if (days === 0 || days === 1) { |
||||
|
lbl = labels[days] |
||||
|
} else if (days === -1) { |
||||
|
lbl = labels[2] |
||||
|
} else if (days >= 2) { |
||||
|
lbl = days + labels[3] |
||||
|
} else { |
||||
|
lbl = days + labels[4] |
||||
|
} |
||||
|
return lbl + result.substr(10, 6) |
||||
|
} |
||||
|
|
||||
|
const local_labels = { |
||||
|
monthes: { |
||||
|
english: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], |
||||
|
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], |
||||
|
zh: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] |
||||
|
}, |
||||
|
weekdays: { |
||||
|
english: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], |
||||
|
en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], |
||||
|
zh: ['日', '一', '二', '三', '四', '五', '六'] |
||||
|
}, |
||||
|
meridians: { |
||||
|
english: ['a.m.', 'p.m.'], |
||||
|
en: ['AM', 'PM'], |
||||
|
zh: ['上午', '下午'] |
||||
|
}, |
||||
|
dayagos: { |
||||
|
english: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'], |
||||
|
en: ['Today', 'Yesterday', 'Tomorrow', ' days ago', ' days late'], |
||||
|
zh: ['今天', '昨天', '明天', '天前', '天后'] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default Date |
@ -0,0 +1,388 @@ |
|||||
|
/** |
||||
|
* Created by PanJiaChen on 16/11/18. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* Parse the time to string |
||||
|
* @param {(Object|string|number)} time |
||||
|
* @param {string} cFormat |
||||
|
* @returns {string} |
||||
|
*/ |
||||
|
export function parseTime(time, cFormat) { |
||||
|
if (arguments.length === 0) { |
||||
|
return null |
||||
|
} |
||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' |
||||
|
let date |
||||
|
if (typeof time === 'undefined' || time === null || time === 'null') { |
||||
|
return '' |
||||
|
} else if (typeof time === 'object') { |
||||
|
date = time |
||||
|
} else { |
||||
|
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { |
||||
|
time = parseInt(time) |
||||
|
} |
||||
|
if ((typeof time === 'number') && (time.toString().length === 10)) { |
||||
|
time = time * 1000 |
||||
|
} |
||||
|
date = new Date(time) |
||||
|
} |
||||
|
const formatObj = { |
||||
|
y: date.getFullYear(), |
||||
|
m: date.getMonth() + 1, |
||||
|
d: date.getDate(), |
||||
|
h: date.getHours(), |
||||
|
i: date.getMinutes(), |
||||
|
s: date.getSeconds(), |
||||
|
a: date.getDay() |
||||
|
} |
||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { |
||||
|
let value = formatObj[key] |
||||
|
// Note: getDay() returns 0 on Sunday
|
||||
|
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } |
||||
|
if (result.length > 0 && value < 10) { |
||||
|
value = '0' + value |
||||
|
} |
||||
|
return value || 0 |
||||
|
}) |
||||
|
return time_str |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {number} time |
||||
|
* @param {string} option |
||||
|
* @returns {string} |
||||
|
*/ |
||||
|
export function formatTime(time, option) { |
||||
|
if (('' + time).length === 10) { |
||||
|
time = parseInt(time) * 1000 |
||||
|
} else { |
||||
|
time = +time |
||||
|
} |
||||
|
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() + |
||||
|
'分' |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} url |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
export function getQueryObject(url) { |
||||
|
url = url == null ? window.location.href : url |
||||
|
const search = url.substring(url.lastIndexOf('?') + 1) |
||||
|
const obj = {} |
||||
|
const reg = /([^?&=]+)=([^?&=]*)/g |
||||
|
search.replace(reg, (rs, $1, $2) => { |
||||
|
const name = decodeURIComponent($1) |
||||
|
let val = decodeURIComponent($2) |
||||
|
val = String(val) |
||||
|
obj[name] = val |
||||
|
return rs |
||||
|
}) |
||||
|
return obj |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} input value |
||||
|
* @returns {number} output value |
||||
|
*/ |
||||
|
export function byteLength(str) { |
||||
|
// returns the byte length of an utf8 string
|
||||
|
let s = str.length |
||||
|
for (var i = str.length - 1; i >= 0; i--) { |
||||
|
const code = str.charCodeAt(i) |
||||
|
if (code > 0x7f && code <= 0x7ff) s++ |
||||
|
else if (code > 0x7ff && code <= 0xffff) s += 2 |
||||
|
if (code >= 0xDC00 && code <= 0xDFFF) i-- |
||||
|
} |
||||
|
return s |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {Array} actual |
||||
|
* @returns {Array} |
||||
|
*/ |
||||
|
export function cleanArray(actual) { |
||||
|
const newArray = [] |
||||
|
for (let i = 0; i < actual.length; i++) { |
||||
|
if (actual[i]) { |
||||
|
newArray.push(actual[i]) |
||||
|
} |
||||
|
} |
||||
|
return newArray |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {Object} json |
||||
|
* @returns {Array} |
||||
|
*/ |
||||
|
export function param(json) { |
||||
|
if (!json) return '' |
||||
|
return cleanArray( |
||||
|
Object.keys(json).map(key => { |
||||
|
if (json[key] === undefined) return '' |
||||
|
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) |
||||
|
}) |
||||
|
).join('&') |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} url |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
export function param2Obj(url) { |
||||
|
const search = url.split('?')[1] |
||||
|
if (!search) { |
||||
|
return {} |
||||
|
} |
||||
|
return JSON.parse( |
||||
|
'{"' + |
||||
|
decodeURIComponent(search) |
||||
|
.replace(/"/g, '\\"') |
||||
|
.replace(/&/g, '","') |
||||
|
.replace(/=/g, '":"') |
||||
|
.replace(/\+/g, ' ') + |
||||
|
'"}' |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} val |
||||
|
* @returns {string} |
||||
|
*/ |
||||
|
export function html2Text(val) { |
||||
|
const div = document.createElement('div') |
||||
|
div.innerHTML = val |
||||
|
return div.textContent || div.innerText |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Merges two objects, giving the last one precedence |
||||
|
* @param {Object} target |
||||
|
* @param {(Object|Array)} source |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
export function objectMerge(target, source) { |
||||
|
if (typeof target !== 'object') { |
||||
|
target = {} |
||||
|
} |
||||
|
if (Array.isArray(source)) { |
||||
|
return source.slice() |
||||
|
} |
||||
|
Object.keys(source).forEach(property => { |
||||
|
const sourceProperty = source[property] |
||||
|
if (typeof sourceProperty === 'object') { |
||||
|
target[property] = objectMerge(target[property], sourceProperty) |
||||
|
} else { |
||||
|
target[property] = sourceProperty |
||||
|
} |
||||
|
}) |
||||
|
return target |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {HTMLElement} element |
||||
|
* @param {string} className |
||||
|
*/ |
||||
|
export function toggleClass(element, className) { |
||||
|
if (!element || !className) { |
||||
|
return |
||||
|
} |
||||
|
let classString = element.className |
||||
|
const nameIndex = classString.indexOf(className) |
||||
|
if (nameIndex === -1) { |
||||
|
classString += '' + className |
||||
|
} else { |
||||
|
classString = |
||||
|
classString.substr(0, nameIndex) + |
||||
|
classString.substr(nameIndex + className.length) |
||||
|
} |
||||
|
element.className = classString |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} type |
||||
|
* @returns {Date} |
||||
|
*/ |
||||
|
export function getTime(type) { |
||||
|
if (type === 'start') { |
||||
|
return new Date().getTime() - 3600 * 1000 * 24 * 90 |
||||
|
} else { |
||||
|
return new Date(new Date().toDateString()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {Function} func |
||||
|
* @param {number} wait |
||||
|
* @param {boolean} immediate |
||||
|
* @return {*} |
||||
|
*/ |
||||
|
export function debounce(func, wait, immediate) { |
||||
|
let timeout, args, context, timestamp, result |
||||
|
|
||||
|
const later = function() { |
||||
|
// 据上一次触发时间间隔
|
||||
|
const last = +new Date() - timestamp |
||||
|
|
||||
|
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
||||
|
if (last < wait && last > 0) { |
||||
|
timeout = setTimeout(later, wait - last) |
||||
|
} else { |
||||
|
timeout = null |
||||
|
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
||||
|
if (!immediate) { |
||||
|
result = func.apply(context, args) |
||||
|
if (!timeout) context = args = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return function(...args) { |
||||
|
context = this |
||||
|
timestamp = +new Date() |
||||
|
const callNow = immediate && !timeout |
||||
|
// 如果延时不存在,重新设定延时
|
||||
|
if (!timeout) timeout = setTimeout(later, wait) |
||||
|
if (callNow) { |
||||
|
result = func.apply(context, args) |
||||
|
context = args = null |
||||
|
} |
||||
|
|
||||
|
return result |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* This is just a simple version of deep copy |
||||
|
* Has a lot of edge cases bug |
||||
|
* If you want to use a perfect deep copy, use lodash's _.cloneDeep |
||||
|
* @param {Object} source |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
export function deepClone(source) { |
||||
|
if (!source && typeof source !== 'object') { |
||||
|
throw new Error('error arguments', 'deepClone') |
||||
|
} |
||||
|
const targetObj = source.constructor === Array ? [] : {} |
||||
|
Object.keys(source).forEach(keys => { |
||||
|
if (source[keys] && typeof source[keys] === 'object') { |
||||
|
targetObj[keys] = deepClone(source[keys]) |
||||
|
} else { |
||||
|
targetObj[keys] = source[keys] |
||||
|
} |
||||
|
}) |
||||
|
return targetObj |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {Array} arr |
||||
|
* @returns {Array} |
||||
|
*/ |
||||
|
export function uniqueArr(arr) { |
||||
|
return Array.from(new Set(arr)) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @returns {string} |
||||
|
*/ |
||||
|
export function createUniqueString() { |
||||
|
const timestamp = +new Date() + '' |
||||
|
const randomNum = parseInt((1 + Math.random()) * 65536) + '' |
||||
|
return (+(randomNum + timestamp)).toString(32) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check if an element has a class |
||||
|
* @param {HTMLElement} elm |
||||
|
* @param {string} cls |
||||
|
* @returns {boolean} |
||||
|
*/ |
||||
|
export function hasClass(ele, cls) { |
||||
|
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Add class to element |
||||
|
* @param {HTMLElement} elm |
||||
|
* @param {string} cls |
||||
|
*/ |
||||
|
export function addClass(ele, cls) { |
||||
|
if (!hasClass(ele, cls)) ele.className += ' ' + cls |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Remove class from element |
||||
|
* @param {HTMLElement} elm |
||||
|
* @param {string} cls |
||||
|
*/ |
||||
|
export function removeClass(ele, cls) { |
||||
|
if (hasClass(ele, cls)) { |
||||
|
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') |
||||
|
ele.className = ele.className.replace(reg, ' ') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 替换邮箱字符
|
||||
|
export function regEmail(email) { |
||||
|
if (String(email).indexOf('@') > 0) { |
||||
|
const str = email.split('@') |
||||
|
let _s = '' |
||||
|
if (str[0].length > 3) { |
||||
|
for (var i = 0; i < str[0].length - 3; i++) { |
||||
|
_s += '*' |
||||
|
} |
||||
|
} |
||||
|
var new_email = str[0].substr(0, 3) + _s + '@' + str[1] |
||||
|
} |
||||
|
return new_email |
||||
|
} |
||||
|
|
||||
|
// 替换手机字符
|
||||
|
export function regMobile(mobile) { |
||||
|
if (mobile.length > 7) { |
||||
|
var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7) |
||||
|
} |
||||
|
return new_mobile |
||||
|
} |
||||
|
|
||||
|
// 下载文件
|
||||
|
export function downloadFile(obj, name, suffix) { |
||||
|
const url = window.URL.createObjectURL(new Blob([obj])) |
||||
|
const link = document.createElement('a') |
||||
|
link.style.display = 'none' |
||||
|
link.href = url |
||||
|
const fileName = parseTime(new Date()) + '-' + name + '.' + suffix |
||||
|
link.setAttribute('download', fileName) |
||||
|
document.body.appendChild(link) |
||||
|
link.click() |
||||
|
document.body.removeChild(link) |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
import store from '@/store' |
||||
|
|
||||
|
/** |
||||
|
* @param {Array} value |
||||
|
* @returns {Boolean} |
||||
|
* @example see @/views/permission/directive.vue |
||||
|
*/ |
||||
|
export default { |
||||
|
install(Vue) { |
||||
|
Vue.prototype.checkPer = (value) => { |
||||
|
if (value && value instanceof Array && value.length > 0) { |
||||
|
const roles = store.getters && store.getters.roles |
||||
|
const permissionRoles = value |
||||
|
return roles.some(role => { |
||||
|
return permissionRoles.includes(role) |
||||
|
}) |
||||
|
} else { |
||||
|
console.error(`need roles! Like v-permission="['admin','editor']"`) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
import axios from 'axios' |
||||
|
import router from '@/router/routers' |
||||
|
import { Notification } from 'element-ui' |
||||
|
import store from '../store' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
import Config from '@/settings' |
||||
|
import Cookies from 'js-cookie' |
||||
|
|
||||
|
// 创建axios实例
|
||||
|
const service = axios.create({ |
||||
|
baseURL: process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : '/', // api 的 base_url
|
||||
|
timeout: Config.timeout // 请求超时时间
|
||||
|
}) |
||||
|
|
||||
|
// request拦截器
|
||||
|
service.interceptors.request.use( |
||||
|
config => { |
||||
|
if (getToken()) { |
||||
|
config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
|
} |
||||
|
config.headers['Content-Type'] = 'application/json' |
||||
|
return config |
||||
|
}, |
||||
|
error => { |
||||
|
Promise.reject(error) |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
// response 拦截器
|
||||
|
service.interceptors.response.use( |
||||
|
response => { |
||||
|
return response.data |
||||
|
}, |
||||
|
error => { |
||||
|
// 兼容blob下载出错json提示
|
||||
|
if (error.response.data instanceof Blob && error.response.data.type.toLowerCase().indexOf('json') !== -1) { |
||||
|
const reader = new FileReader() |
||||
|
reader.readAsText(error.response.data, 'utf-8') |
||||
|
reader.onload = function(e) { |
||||
|
const errorMsg = JSON.parse(reader.result).message |
||||
|
Notification.error({ |
||||
|
title: errorMsg, |
||||
|
duration: 5000 |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
let code = 0 |
||||
|
try { |
||||
|
code = error.response.data.status |
||||
|
} catch (e) { |
||||
|
if (error.toString().indexOf('Error: timeout') !== -1) { |
||||
|
Notification.error({ |
||||
|
title: '网络请求超时', |
||||
|
duration: 5000 |
||||
|
}) |
||||
|
return Promise.reject(error) |
||||
|
} |
||||
|
} |
||||
|
console.log(code) |
||||
|
if (code) { |
||||
|
if (code === 401) { |
||||
|
store.dispatch('LogOut').then(() => { |
||||
|
// 用户登录界面提示
|
||||
|
Cookies.set('point', 401) |
||||
|
location.reload() |
||||
|
}) |
||||
|
} else if (code === 403) { |
||||
|
router.push({ path: '/401' }) |
||||
|
} else { |
||||
|
const errorMsg = error.response.data.message |
||||
|
if (errorMsg !== undefined) { |
||||
|
Notification.error({ |
||||
|
title: errorMsg, |
||||
|
duration: 5000 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
Notification.error({ |
||||
|
title: '接口请求失败', |
||||
|
duration: 5000 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
return Promise.reject(error) |
||||
|
} |
||||
|
) |
||||
|
export default service |
@ -0,0 +1,14 @@ |
|||||
|
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' |
||||
|
|
||||
|
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||
|
|
||||
|
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' + |
||||
|
'2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ==' |
||||
|
|
||||
|
// 加密
|
||||
|
export function encrypt(txt) { |
||||
|
const encryptor = new JSEncrypt() |
||||
|
encryptor.setPublicKey(publicKey) // 设置公钥
|
||||
|
return encryptor.encrypt(txt) // 对需要加密的数据进行加密
|
||||
|
} |
||||
|
|
@ -0,0 +1,108 @@ |
|||||
|
import Date from './datetime.js' |
||||
|
|
||||
|
export const calendarBaseShortcuts = [{ |
||||
|
text: '今天', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '昨天', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '最近一周', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().daysAgo(7).setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '最近30天', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().daysAgo(30).setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '这个月', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().monthBegin().setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '本季度', |
||||
|
onClick(picker) { |
||||
|
const startTime = new Date(new Date().quarterBegin().setHours(0, 0, 0)) |
||||
|
const endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}] |
||||
|
|
||||
|
export const calendarMoveShortcuts = [{ |
||||
|
text: '‹ 往前一天 ', |
||||
|
onClick(picker) { |
||||
|
let startTime = new Date(new Date().daysAgo(1).setHours(0, 0, 0)) |
||||
|
let endTime = new Date(new Date().daysAgo(1).setHours(23, 59, 59)) |
||||
|
if (!picker.value) { |
||||
|
picker.value = [startTime, endTime] |
||||
|
} |
||||
|
startTime = picker.value[0].daysAgo(1) |
||||
|
endTime = picker.value[1].daysAgo(1) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: ' 往后一天 ›', |
||||
|
onClick(picker) { |
||||
|
let startTime = new Date(new Date().setHours(0, 0, 0)) |
||||
|
let endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
if (!picker.value) { |
||||
|
picker.value = [startTime, endTime] |
||||
|
} |
||||
|
startTime = picker.value[0].daysAgo(-1) |
||||
|
endTime = picker.value[1].daysAgo(-1) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: '« 往前一周 ', |
||||
|
onClick(picker) { |
||||
|
let startTime = new Date(new Date().setHours(0, 0, 0)) |
||||
|
let endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
if (!picker.value) { |
||||
|
picker.value = [startTime.daysAgo(new Date().getDay()), |
||||
|
endTime.daysAgo(new Date().getDay() + 1)] |
||||
|
} else { |
||||
|
picker.value = [picker.value[0].daysAgo(picker.value[0].getDay()), |
||||
|
picker.value[1].daysAgo(picker.value[1].getDay() + 1)] |
||||
|
} |
||||
|
startTime = picker.value[0].daysAgo(7) |
||||
|
endTime = picker.value[1] |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}, { |
||||
|
text: ' 往后一周 »', |
||||
|
onClick(picker) { |
||||
|
let startTime = new Date(new Date().setHours(0, 0, 0)) |
||||
|
let endTime = new Date(new Date().setHours(23, 59, 59)) |
||||
|
if (!picker.value) { |
||||
|
picker.value = [startTime.daysAgo(new Date().getDay() - 7), |
||||
|
endTime.daysAgo(new Date().getDay() - 6)] |
||||
|
} else { |
||||
|
picker.value = [picker.value[0].daysAgo(picker.value[0].getDay() - 7), |
||||
|
picker.value[1].daysAgo(picker.value[1].getDay() - 6)] |
||||
|
} |
||||
|
startTime = picker.value[0] |
||||
|
endTime = picker.value[1].daysAgo(-7) |
||||
|
picker.$emit('pick', [startTime, endTime]) |
||||
|
} |
||||
|
}] |
||||
|
|
||||
|
export const calendarShortcuts = [ |
||||
|
...calendarBaseShortcuts, |
||||
|
...calendarMoveShortcuts |
||||
|
] |
@ -0,0 +1,11 @@ |
|||||
|
import axios from 'axios' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
|
||||
|
export function upload(api, file) { |
||||
|
var data = new FormData() |
||||
|
data.append('file', file) |
||||
|
const config = { |
||||
|
headers: { 'Authorization': getToken() } |
||||
|
} |
||||
|
return axios.post(api, data, config) |
||||
|
} |
@ -0,0 +1,167 @@ |
|||||
|
/** |
||||
|
* Created by PanJiaChen on 16/11/18. |
||||
|
*/ |
||||
|
|
||||
|
/** |
||||
|
* @param {string} path |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function isExternal(path) { |
||||
|
return /^(https?:|mailto:|tel:)/.test(path) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} str |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validUsername(str) { |
||||
|
const valid_map = ['admin', 'editor'] |
||||
|
return valid_map.indexOf(str.trim()) >= 0 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} url |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validURL(url) { |
||||
|
const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ |
||||
|
return reg.test(url) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} str |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validLowerCase(str) { |
||||
|
const reg = /^[a-z]+$/ |
||||
|
return reg.test(str) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} str |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validUpperCase(str) { |
||||
|
const reg = /^[A-Z]+$/ |
||||
|
return reg.test(str) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} str |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validAlphabets(str) { |
||||
|
const reg = /^[A-Za-z]+$/ |
||||
|
return reg.test(str) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} email |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function validEmail(email) { |
||||
|
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ |
||||
|
return reg.test(email) |
||||
|
} |
||||
|
|
||||
|
export function isvalidPhone(phone) { |
||||
|
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/ |
||||
|
return reg.test(phone) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {string} str |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function isString(str) { |
||||
|
if (typeof str === 'string' || str instanceof String) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param {Array} arg |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
export function isArray(arg) { |
||||
|
if (typeof Array.isArray === 'undefined') { |
||||
|
return Object.prototype.toString.call(arg) === '[object Array]' |
||||
|
} |
||||
|
return Array.isArray(arg) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否合法IP地址 |
||||
|
* @param rule |
||||
|
* @param value |
||||
|
* @param callback |
||||
|
*/ |
||||
|
export function validateIP(rule, value, callback) { |
||||
|
if (value === '' || value === undefined || value == null) { |
||||
|
callback() |
||||
|
} else { |
||||
|
const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ |
||||
|
if ((!reg.test(value)) && value !== '') { |
||||
|
callback(new Error('请输入正确的IP地址')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 是否手机号码或者固话*/ |
||||
|
export function validatePhoneTwo(rule, value, callback) { |
||||
|
const reg = /^((0\d{2,3}-\d{7,8})|(1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}))$/ |
||||
|
if (value === '' || value === undefined || value == null) { |
||||
|
callback() |
||||
|
} else { |
||||
|
if ((!reg.test(value)) && value !== '') { |
||||
|
callback(new Error('请输入正确的电话号码或者固话号码')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 是否固话*/ |
||||
|
export function validateTelephone(rule, value, callback) { |
||||
|
const reg = /0\d{2}-\d{7,8}/ |
||||
|
if (value === '' || value === undefined || value == null) { |
||||
|
callback() |
||||
|
} else { |
||||
|
if ((!reg.test(value)) && value !== '') { |
||||
|
callback(new Error('请输入正确的固话(格式:区号+号码,如010-1234567)')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 是否手机号码*/ |
||||
|
export function validatePhone(rule, value, callback) { |
||||
|
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/ |
||||
|
if (value === '' || value === undefined || value == null) { |
||||
|
callback() |
||||
|
} else { |
||||
|
if ((!reg.test(value)) && value !== '') { |
||||
|
callback(new Error('请输入正确的电话号码')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 是否身份证号码*/ |
||||
|
export function validateIdNo(rule, value, callback) { |
||||
|
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/ |
||||
|
if (value === '' || value === undefined || value == null) { |
||||
|
callback() |
||||
|
} else { |
||||
|
if ((!reg.test(value)) && value !== '') { |
||||
|
callback(new Error('请输入正确的身份证号码')) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,137 @@ |
|||||
|
'use strict' |
||||
|
const path = require('path') |
||||
|
const defaultSettings = require('./src/settings.js') |
||||
|
|
||||
|
function resolve(dir) { |
||||
|
return path.join(__dirname, dir) |
||||
|
} |
||||
|
|
||||
|
const name = defaultSettings.title // 网址标题
|
||||
|
const port = 8016 // 端口配置
|
||||
|
|
||||
|
|
||||
|
module.exports = { |
||||
|
// hash 模式下可使用
|
||||
|
// publicPath: process.env.NODE_ENV === 'development' ? '/' : './',
|
||||
|
publicPath: '/', |
||||
|
outputDir: 'dist', |
||||
|
assetsDir: 'static', |
||||
|
lintOnSave: process.env.NODE_ENV === 'development', |
||||
|
productionSourceMap: false, |
||||
|
devServer: { |
||||
|
port: port, |
||||
|
open: true, |
||||
|
overlay: { |
||||
|
warnings: false, |
||||
|
errors: true |
||||
|
}, |
||||
|
proxy: { |
||||
|
'/api': { |
||||
|
target: process.env.VUE_APP_BASE_API, |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { |
||||
|
'^/api': 'api' |
||||
|
} |
||||
|
}, |
||||
|
'/auth': { |
||||
|
target: process.env.VUE_APP_BASE_API, |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { |
||||
|
'^/auth': 'auth' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
configureWebpack: { |
||||
|
// provide the app's title in webpack's name field, so that
|
||||
|
// it can be accessed in index.html to inject the correct title.
|
||||
|
name: name, |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'@': resolve('src'), |
||||
|
'@crud': resolve('src/components/Crud') |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
chainWebpack(config) { |
||||
|
config.plugins.delete('preload') // TODO: need test
|
||||
|
config.plugins.delete('prefetch') // TODO: need test
|
||||
|
|
||||
|
// set svg-sprite-loader
|
||||
|
config.module |
||||
|
.rule('svg') |
||||
|
.exclude.add(resolve('src/assets/icons')) |
||||
|
.end() |
||||
|
config.module |
||||
|
.rule('icons') |
||||
|
.test(/\.svg$/) |
||||
|
.include.add(resolve('src/assets/icons')) |
||||
|
.end() |
||||
|
.use('svg-sprite-loader') |
||||
|
.loader('svg-sprite-loader') |
||||
|
.options({ |
||||
|
symbolId: 'icon-[name]' |
||||
|
}) |
||||
|
.end() |
||||
|
|
||||
|
// set preserveWhitespace
|
||||
|
config.module |
||||
|
.rule('vue') |
||||
|
.use('vue-loader') |
||||
|
.loader('vue-loader') |
||||
|
.tap(options => { |
||||
|
options.compilerOptions.preserveWhitespace = true |
||||
|
return options |
||||
|
}) |
||||
|
.end() |
||||
|
|
||||
|
config |
||||
|
// https://webpack.js.org/configuration/devtool/#development
|
||||
|
.when(process.env.NODE_ENV === 'development', |
||||
|
config => config.devtool('cheap-source-map') |
||||
|
) |
||||
|
|
||||
|
config |
||||
|
.when(process.env.NODE_ENV !== 'development', |
||||
|
config => { |
||||
|
config |
||||
|
.plugin('ScriptExtHtmlWebpackPlugin') |
||||
|
.after('html') |
||||
|
.use('script-ext-html-webpack-plugin', [{ |
||||
|
// `runtime` must same as runtimeChunk name. default is `runtime`
|
||||
|
inline: /runtime\..*\.js$/ |
||||
|
}]) |
||||
|
.end() |
||||
|
config |
||||
|
.optimization.splitChunks({ |
||||
|
chunks: 'all', |
||||
|
cacheGroups: { |
||||
|
libs: { |
||||
|
name: 'chunk-libs', |
||||
|
test: /[\\/]node_modules[\\/]/, |
||||
|
priority: 10, |
||||
|
chunks: 'initial' // only package third parties that are initially dependent
|
||||
|
}, |
||||
|
elementUI: { |
||||
|
name: 'chunk-elementUI', // split elementUI into a single package
|
||||
|
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
|
||||
|
test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
|
||||
|
}, |
||||
|
commons: { |
||||
|
name: 'chunk-commons', |
||||
|
test: resolve('src/components'), // can customize your rules
|
||||
|
minChunks: 3, // minimum common number
|
||||
|
priority: 5, |
||||
|
reuseExistingChunk: true |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
config.optimization.runtimeChunk('single') |
||||
|
} |
||||
|
) |
||||
|
}, |
||||
|
transpileDependencies: [ |
||||
|
'vue-echarts', |
||||
|
'resize-detector' |
||||
|
] |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue