刘力
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