Commit 3fcb3599 authored by tianxinyuan's avatar tianxinyuan

第一次提交代码

parents
Pipeline #250 failed with stages
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = '/dev-api'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
# It only does one thing by converting all import() to require().
# This configuration can significantly increase the speed of hot updates,
# when you have a large number of pages.
# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
VUE_CLI_BABEL_TRANSPILE_MODULES = true
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = '/prod-api'
NODE_ENV = production
# just a flag
ENV = 'staging'
# base api
VUE_APP_BASE_API = '/stage-api'
build/*.js
src/assets
public
dist
module.exports = {
root: false,
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']
}
}
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
language: node_js
node_js: 10
script: npm run test
notifications:
email: false
MIT License
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# vue-admin-template
> 这是一个极简的 vue admin 管理后台。它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
[线上地址](http://panjiachen.github.io/vue-admin-template)
[国内访问](https://panjiachen.gitee.io/vue-admin-template)
目前版本为 `v4.0+` 基于 `vue-cli` 进行构建,若你想使用旧版本,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0),它不依赖 `vue-cli`
## Extra
如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
## 相关项目
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
## Build Setup
```bash
# 克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git
# 进入项目目录
cd vue-admin-template
# 安装依赖
npm install
# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
```
浏览器访问 [http://localhost:9528](http://localhost:9528)
## 发布
```bash
# 构建测试环境
npm run build:stage
# 构建生产环境
npm run build:prod
```
## 其它
```bash
# 预览发布环境效果
npm run preview
# 预览发布环境效果 + 静态资源分析
npm run preview -- --report
# 代码格式检查
npm run lint
# 代码格式检查并自动修复
npm run lint -- --fix
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
## Demo
![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
Copyright (c) 2017-present PanJiaChen
# vue-admin-template
English | [简体中文](./README-zh.md)
> A minimal vue admin template with Element UI & axios & iconfont & permission control & lint
**Live demo:** http://panjiachen.github.io/vue-admin-template
**The current version is `v4.0+` build on `vue-cli`. If you want to use the old version , you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0), it does not rely on `vue-cli`**
## Build Setup
```bash
# clone the project
git clone https://github.com/PanJiaChen/vue-admin-template.git
# enter the project directory
cd vue-admin-template
# install dependency
npm install
# develop
npm run dev
```
This will automatically open http://localhost:9528
## Build
```bash
# build for test environment
npm run build:stage
# build for production environment
npm run build:prod
```
## Advanced
```bash
# preview the release environment effect
npm run preview
# preview the release environment effect + static resource analysis
npm run preview -- --report
# code format check
npm run lint
# code format check and auto fix
npm run lint -- --fix
```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
## Demo
![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
## Extra
If you want router permission && generate menu by user roles , you can use this branch [permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
For `typescript` version, you can use [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
## Related Project
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
Copyright (c) 2017-present PanJiaChen
module.exports = {
presets: [
'@vue/app'
]
}
'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'
))
})
})
'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)
}
}
const { run } = require('runjs')
const chalk = require('chalk')
const config = require('../vue.config.js')
const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ')
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report')
run(`vue-cli-service build ${args}`)
const port = 9526
const publicPath = config.publicPath
var connect = require('connect')
var serveStatic = require('serve-static')
const app = connect()
app.use(
publicPath,
serveStatic('./dist', {
index: ['index.html', '/']
})
)
app.listen(port, function () {
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
if (report) {
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
}
})
} else {
run(`vue-cli-service build ${args}`)
}
'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',
publicPath: '../../'
})
} 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')
})
}
}
'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'
}
}
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
var webpack = require('webpack')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
// app: './src/main.js'
app: ['babel-polyfill', './src/main.js']
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('common.js'),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
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'
}
}
'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)
}
})
})
'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
File added
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: ['jest-serializer-vue'],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
coverageDirectory: '<rootDir>/tests/unit/coverage',
// 'collectCoverage': true,
'coverageReporters': [
'lcov',
'text-summary'
],
testURL: 'http://localhost/'
}
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
import Mock from 'mockjs'
import { param2Obj } from '../src/utils'
import user from './user'
import table from './table'
const mocks = [
...user,
...table
]
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
export function mockXHR() {
// mock patch
// https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function() {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
if (this.responseType) {
this.custom.xhr.responseType = this.responseType
}
}
this.proxy_send(...arguments)
}
function XHR2ExpressReqWrap(respond) {
return function(options) {
let result = null
if (respond instanceof Function) {
const { body, type, url } = options
// https://expressjs.com/en/4x/api.html#req
result = respond({
method: type,
body: JSON.parse(body),
query: param2Obj(url)
})
} else {
result = respond
}
return Mock.mock(result)
}
}
for (const i of mocks) {
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
}
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
type: type || 'get',
response(req, res) {
console.log('request invoke:' + req.path)
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
}
}
}
export default mocks.map(route => {
return responseFake(route.url, route.type, route.response)
})
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const mockDir = path.join(process.cwd(), 'mock')
function registerRoutes(app) {
let mockLastIndex
const { default: mocks } = require('./index.js')
for (const mock of mocks) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocks).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
}
})
}
module.exports = app => {
// es6 polyfill
require('@babel/register')
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
}
}
})
}
import Mock from 'mockjs'
const data = Mock.mock({
'items|30': [{
id: '@id',
title: '@sentence(10, 20)',
'status|1': ['published', 'draft', 'deleted'],
author: 'name',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}]
})
export default [
{
url: '/vue-admin-template/table/list',
type: 'get',
response: config => {
const items = data.items
return {
code: 20000,
data: {
total: items.length,
items: items
}
}
}
}
]
// const tokens = {
// admin: {
// token: 'admin-token'
// },
// }
//
// const users = {
// 'admin-token': {
// roles: ['admin'],
// introduction: 'I am a super administrator',
// avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
// name: 'admin'
// },
// }
export default [
// user login
{
url: '/vue-admin-template/user/login',
type: 'post',
response: config => {
const { username } = config.body
// const token = tokens['admin']
const token = {
token: username
}
// mock error
if (!token) {
return {
code: 60204,
message: 'Account and password are incorrect.'
}
}
return {
code: 20000,
data: token
}
}
},
// get user info
{
url: '/vue-admin-template/user/info\.*',
type: 'get',
response: config => {
const { token } = config.username
const { role } = config.role
const info = {
roles: role,
name: token
}
// mock error
if (!info) {
return {
code: 50008,
message: 'Login failed, unable to get user details.'
}
}
return {
code: 20000,
data: info
}
}
},
// user logout
{
url: '/vue-admin-template/user/logout',
type: 'post',
response: _ => {
return {
code: 20000,
data: 'success'
}
}
}
]
{
"name": "vue-admin-template",
"version": "4.2.1",
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": {
"serve": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"dependencies": {
"ant-design-vue": "^1.6.5",
"axios": "0.18.1",
"babel-polyfill": "^6.26.0",
"codemirror": "^5.54.0",
"crypto-js": "^4.0.0",
"echarts": "^4.9.0",
"fuse.js": "^3.4.4",
"jquery": "^2.2.4",
"js-cookie": "2.2.0",
"json2yaml": "^1.1.0",
"monaco-editor": "^0.20.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"ol": "^6.3.1",
"path-to-regexp": "2.4.0",
"vue-clipboard2": "^0.3.1",
"vue-count-to": "^1.0.13",
"vue-router": "3.0.6",
"vuex": "3.1.0",
"webpack-dev-server": "^2.11.5",
"yamljs": "^0.3.0"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",
"@vue/cli-plugin-unit-jest": "3.6.3",
"@vue/cli-service": "3.6.0",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "^9.5.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"babel-plugin-dynamic-import-node": "^2.3.3",
"chalk": "2.4.2",
"connect": "3.6.6",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",
"html-webpack-plugin": "3.2.0",
"mockjs": "1.0.1-beta3",
"monaco-editor-webpack-plugin": "^1.9.0",
"node-sass": "^4.14.1",
"runjs": "^4.3.2",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue": "^2.6.10",
"vue-resource": "^1.5.1",
"vue-template-compiler": "^2.6.14"
},
"engines": {
"node": ">=8.9",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
'plugins': {
// to edit target browsers: use "browserslist" field in package.json
'autoprefixer': {}
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
<title>SWTZ-平台日志</title>
</head>
<body>
<noscript>
<strong>We're sorry but SWTZ-平台日志 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<div>
<router-view />
</div>
</template>
const baseURL = 'http://123.183.159.199:8200'
// const baseURL = ''
export default baseURL
import request from '@/utils/request'
import baseURL from '@/api/global_variable'
// 接入服务 页面数据:
export function getAccessData(transaction_order_number, page, size) {
return request({
url: baseURL + '/log/search/v1/yxinfo',
method: 'get',
params: {
transaction_order_number: transaction_order_number,
page: page,
size: size
}
})
}
// 核心业务 页面数据:
export function getCoreData(transaction_order_number, page, size) {
return request({
url: baseURL + '/log/search/v1/hxyw',
method: 'get',
params: {
transaction_order_number: transaction_order_number,
page: page,
size: size
}
})
}
// 云从业务 页面数据:
export function getOceanData(transaction_order_number, page, size) {
return request({
url: baseURL + '/log/search/v1/ocean',
method: 'get',
params: {
transaction_order_number: transaction_order_number,
page: page,
size: size
}
})
}
// 核心业务 页面数据:
export function getMultiAlgoData(transaction_order_number, page, size) {
return request({
url: baseURL + '/log/search/v1/multiAlgo',
method: 'get',
params: {
transaction_order_number: transaction_order_number,
page: page,
size: size
}
})
}
//日志筛选
export function logsearchfilter(gte,lte, page, size,code,appid) {
return request({
url: baseURL + '/log/search/v1/filter',
method: 'get',
params: {
code: appid,
page: page,
size: size,
appid:code,
gte:gte,
lte:lte
}
})
}
import { mapGetters } from 'vuex'
// import Vue from 'vue'
import GLOABL from './global_variable'
// import { getToken, setToken, removeToken } from '@/utils/auth'
export default {
computed: {
...mapGetters([
'name',
'role',
'token',
'key'
])
},
install(Vue, options) {
Vue.prototype.url = 'http://www.baidu.com/' // 可以自定义变量
Vue.prototype.myFun = function(ev) { // 给自定义方法起个名
ev + 1
return ev
}
Vue.http.interceptors.push(function(request, next) {
// console.log(request)//附赠一个可以控制页面所有路由开始之前结束之后的方法
// 请求发送前的处理逻辑
// if(request.url !== GLOABL.baseURL+'/nacos/v1/ns/getToken'&& request.url !== GLOABL.baseURL+'/nacos/v1/ns/queryAllHistory' && request.url !== GLOABL.baseURL+'/nacos/v1/ns/getSerStatistics'){
if (request.url !== GLOABL.baseURL + '/nacos/v1/ns/getToken') {
next(function(response) {
// 请求发送后的处理逻辑
// 更具请求的状态, response参数会返回给 successCallback或errorCallback
// if(response.data.sign === 'false'){
// var username;
// var localToken;
// if(this.token!==null){
// if(this.token.username === null){
// username = JSON.parse(this.token).username;
// Vue.http.headers.common['token'] = JSON.parse(this.token).token;
// localToken = JSON.parse(this.token);
// }else{
// username = this.token.username;
// Vue.http.headers.common['token'] = this.token.token;
// localToken = this.token;
// }
// }
//
// var data = {
// username: username
// }
// var json = JSON.stringify(data);
// this.$http.post(this.url+'/nacos/v1/ns/getToken',json,{emulateJSON:true}).then( function(res) {
// if(res.data.sign!=='false'){
// var msg = {
// username:localToken.username,
// password:localToken.password,
// role:localToken.role,
// token:res.data.longToken,
// shrotToken:res.data.shrotToken,
// }
// console.log(msg)
// this.$store.dispatch('user/setNewToken', msg).then(() => {
// Vue.http.headers.common['token'] = res.data.shrotToken;
// this.$http.post(request.url,request.body,{emulateJSON:true}).then( function(res) {
// console.log(res)
// })
// })
//
// }else{
// // console.log("longToken失效更新")
// // this.$message({
// // message: '登录超时,请重新登录',
// // type: 'error',
// // offset:80,
// // });
// // this.$store.dispatch('user/logout')
// // this.$router.push(`/login?redirect=${this.$route.fullPath}`)
// }
//
// })
// }
if (response.data.sign === 'false') {
this.$store.dispatch('user/logout')
this.$router.push('/login')
this.$message({
message: 'token失效,请重新登录',
type: 'error',
offset: 80
})
}
return response
})
}
})
}
}
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
<script>
const serverSrc='www.baidu.com';
const token='12345678';
const hasEnter=false;
const userSite="中国钓鱼岛";
export default
{
userSite,//用户地址
token,//用户token身份
serverSrc,//服务器地址
hasEnter,//用户登录状态
}
</script>
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<!--<svg-->
<!--:class="{'is-active':isActive}"-->
<!--class="hamburger"-->
<!--viewBox="0 0 1024 1024"-->
<!--xmlns="http://www.w3.org/2000/svg"-->
<!--width="64"-->
<!--height="64"-->
<!--&gt;-->
<!--<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />-->
<!--</svg>-->
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.hamburger.is-active {
transform: rotate(180deg);
}
</style>
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :href="iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>
.ant-upload-list-picture-card-container,
.ant-upload-list-picture-card .ant-upload-list-item {
width: 70%;
height: 70%;
}
.distpicker-address-wrapper select {
width: 30%;
}
.ant-upload.ant-upload-drag {
height: 150px;
}
.border_corner {
position: absolute;
background-color: #7e80b1;
}
/* 右上靠上 */
.border_corner:nth-child(1) {
width: 30px;
height: 3px;
right: -2px;
top: -2px;
}
/* 右上靠右 */
.border_corner:nth-child(2) {
width: 3px;
height: 30px;
right: -2px;
top: -2px;
}
/* 右下靠右 */
.border_corner:nth-child(3) {
width: 3px;
height: 30px;
right: -2px;
bottom: -2px;
}
/* 右下靠下 */
.border_corner:nth-child(4) {
width: 30px;
height: 3px;
right: -2px;
bottom: -2px;
}
/* 左下靠下 */
.border_corner:nth-child(5) {
width: 30px;
height: 3px;
left: -2px;
bottom: -2px;
}
/* 左下靠左 */
.border_corner:nth-child(6) {
width: 3px;
height: 30px;
left: -2px;
bottom: -2px;
}
.radar {
background: -webkit-radial-gradient(center, rgba(32, 255, 77, 0.3) 0%, rgba(32, 255, 77, 0) 75%), -webkit-repeating-radial-gradient(rgba(32, 255, 77, 0) 5.8%, rgba(32, 255, 77, 0) 18%, #20ff4d 18.6%, rgba(32, 255, 77, 0) 18.9%), -webkit-linear-gradient(90deg, rgba(32, 255, 77, 0) 49.5%, #20ff4d 50%, #20ff4d 50%, rgba(32, 255, 77, 0) 50.2%), -webkit-linear-gradient(0deg, rgba(32, 255, 77, 0) 49.5%, #20ff4d 50%, #20ff4d 50%, rgba(32, 255, 77, 0) 50.2%);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 0.1rem solid #20ff4d;
overflow: hidden;
}
.radar:before {
content: ' ';
display: block;
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
}
.radar:after {
content: ' ';
display: block;
background-image: linear-gradient(44deg, rgba(0, 255, 51, 0) 50%, #00ff33 100%);
width: 50%;
height: 50%;
position: absolute;
top: 0;
left: 0;
animation: radar-beam 4s infinite;
animation-timing-function: linear;
transform-origin: bottom right;
border-radius: 100% 0 0 0;
}
@keyframes radar-beam {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/*表格的自定义属性;全局*/
.ant-table-body {
margin: 0 !important;
background-color: #F3F4F9 !important;
}
/*表格的数据*/
.thCss {
font-weight: normal;
font-size: 14px;
background-color: white;
}
/*设置查询结果展示表格的数据样式*/
.searchTableRowCss {
background-color: #e4ffff;
}
.theadCssSearch {
background-color: #99FFFF;
font-weight: bolder;
font-size: 13px;
}
/*@font-face {*/
/*font-family:'huawen';*/
/*src: url('../font/huawen.ttf');*/
/*font-weight: normal;*/
/*font-style: normal;*/
/*}*/
/* 20ff4d */
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Antd from 'ant-design-vue'
import VueClipboard from 'vue-clipboard2'
import App from './App'
import router from './router/router'
import store from './store'
import 'ant-design-vue/dist/antd.css'
import VueResource from 'vue-resource'
import 'babel-polyfill'
import 'jquery'
import echarts from 'echarts'
Vue.prototype.$echarts = echarts
import './css/index.css'
Vue.config.productionTip = false
// Vue.http.options.emulateJSON = false
Vue.use(Antd)
Vue.use(VueResource)
Vue.use(VueClipboard)// 剪切板;
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// get user info
await store.dispatch('user/getInfo')
next()
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
import Vue from 'vue'
import Router from 'vue-router'
import logQuery from '@/views/logQuery/logQuery'
import logFiltering from '@/views/logFiltering/logFiltering'
import home from '@/views/home/home'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
redirect: '/logQuery'
},
{
path: '/home',
name: 'home',
component: home,
redirect: '/logQuery',
children: [
{
path: '/logQuery',
name: 'logQuery',
component: logQuery
},
{
path: '/logFiltering',
name: 'logFiltering',
component: logFiltering
}
]
}
]
})
// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
// router.beforeEach((to, from, next) => {
// // debugger
// if (to.path === '/login') {
// next();
// window.scrollTo(0,0)
// }
// else {
// let token = store.state.user.token;
// if (token === undefined) {
// next('/login');
// window.scrollTo(0,0)
// }
// else {
// next();
// window.scrollTo(0,0)
// }
// }
// });
export default router
module.exports = {
title: 'SWTZ-平台日志',
/**
* @type {boolean} true | false
* @description Whether fix the header
*/
fixedHeader: true,
/**
* @type {boolean} true | false
* @description Whether show the logo in sidebar
*/
sidebarLogo: true
}
const getters = {
sidebar: state => state.app.sidebar,
token: state => state.user.token,
role: state => state.user.role,
sysActiveFlag: state => state.user.sysActiveFlag,
openItem: state => state.user.openItem,
name: state => state.user.name,
userInfoMsg: state => state.user.userInfo,
ReportSerialNumber: state => state.user.ReportSerialNumber,
searchValue: state => state.user.searchValue
}
export default getters
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
settings,
user
},
getters
})
export default store
import Cookies from 'js-cookie'
const state = {
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
withoutAnimation: false
},
device: 'desktop'
}
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
}
}
const actions = {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
import defaultSettings from '@/settings'
const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
const state = {
showSettings: showSettings,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
}
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
}
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken ,getIsCertification,setIsCertification,removeIsCertification,getReportSerialNumber} from '@/utils/auth'
const getDefaultState = () => {
return {
token: getToken(),
name: '',
role: 0,
openItem: [],
sysActiveFlag: getIsCertification(),
userInfo: '',
ReportSerialNumber: getReportSerialNumber(),
searchValue:'first',
}
}
const state = getDefaultState()
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_ROLE: (state, role) => {
state.role = role
},
SET_OPENITEM: (state, openItem) => {
state.openItem = openItem
},
SET_SEARCHVALUE: (state, searchValue) => {
state.searchValue = searchValue
},
SET_ACTIVEFLAG: (state, sysActiveFlag) => {
state.sysActiveFlag = sysActiveFlag
},
SET_USERINFO: (state, userInfo) => {
state.userInfo = userInfo
}
}
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password, isCertification} = userInfo
return new Promise((resolve, reject) => {
var token ={
username:username,
password:password,
isCertification:isCertification
}
commit('SET_TOKEN', token)
setIsCertification(isCertification)
setToken(token)
resolve()
})
},
active({ commit },data) {
return new Promise((resolve, reject) => {
commit('SET_ACTIVEFLAG', data)
resolve()
})
},
setUserInfo({ commit },data) {
return new Promise((resolve, reject) => {
commit('SET_USERINFO', data)
resolve()
})
},
setOpenList({ commit },data) {
return new Promise((resolve, reject) => {
commit('SET_OPENITEM', data)
resolve()
})
},
setSearchValue({ commit },data) {
return new Promise((resolve, reject) => {
commit('SET_SEARCHVALUE', data)
resolve()
})
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
removeToken() // must remove token first
commit('RESET_STATE')
resolve()
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
removeToken() // must remove token first
commit('RESET_STATE')
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
import Cookies from 'js-cookie'
const TokenKey = 'sixProject_token'
const IsCertification = 'sixProject_isCertification'
const ReportSerialNumber = "sixProject_ReportSerialNumber"
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
export function getIsCertification() {
return Cookies.get(IsCertification)
}
export function setIsCertification(isCertification) {
return Cookies.set(IsCertification, isCertification)
}
export function removeIsCertification() {
return Cookies.remove(IsCertification)
}
export function getReportSerialNumber() {
return Cookies.get(ReportSerialNumber)
}
export function setReportSerialNumber(reportSerialNumber) {
return Cookies.set(ReportSerialNumber, reportSerialNumber)
}
export function removeReportSerialNumber() {
return Cookies.remove(ReportSerialNumber)
}
import defaultSettings from '@/settings'
const title = defaultSettings.title || 'Vue Admin Template'
export default function getPageTitle(pageTitle) {
if (pageTitle) {
return `${pageTitle} - ${title}`
}
return `${title}`
}
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string | null}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((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(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
return value.toString().padStart(2, '0')
})
return time_str
}
/**
* @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} nS 时间戳
* @returns {Object} 格式化时间
*/
export function getLocalTime(nS) {
return new Date(parseInt(nS)).toLocaleString().replace(/:\d{1,2}$/, ' ')
}
export function dateFormat(timestamp, formats) {
// formats格式包括
// 1. Y-m-d
// 2. Y-m-d H:i:s
// 3. Y年m月d日
// 4. Y年m月d日 H时i分
formats = formats || 'Y-m-d'
var zero = function(value) {
if (value < 10) {
return '0' + value
}
return value
}
var myDate = timestamp ? new Date(timestamp) : new Date()
var year = myDate.getFullYear()
var month = zero(myDate.getMonth() + 1)
var day = zero(myDate.getDate())
var hour = zero(myDate.getHours())
var minite = zero(myDate.getMinutes())
var second = zero(myDate.getSeconds())
return formats.replace(/Y|m|d|H|i|s/ig, function(matches) {
return ({
Y: year,
m: month,
d: day,
H: hour,
i: minite,
s: second
})[matches]
})
}
import axios from 'axios'
// create an axios instance
const service = axios.create({
baseURL: ''// url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
// timeout: 5000 // request timeout
})
export default service
/**
* 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
}
<template>
<a-layout id="components-layout-demo-custom-trigger">
<a-layout-sider v-model="collapsed" :trigger="null" collapsible style="background: #fff;">
<div style="text-align:center;line-height:62px;border-bottom:1px solid gainsboro;border-right:1px solid gainsboro;">
<img class="logo" src="../../assets/logo.png">
<!-- <strong v-if="!collapsed" style="font-size:15px;">平台日志查询系统</strong> -->
</div>
<a-menu theme="light" mode="inline" @click="handleClick" :style="{maxWidth:(collapsed ? '80px' : '200px' )}" :default-open-keys="['sub1']" :default-selected-keys="openKeys" >
<a-sub-menu key="sub1">
<span slot="title"><a-icon type="search" /><span>查询</span></span>
<a-menu-item key="logQuery" @click="routerClick('/logQuery')">综合查询</a-menu-item>
<a-menu-item key="logFiltering" @click="routerClick('/logFiltering')">日志筛选</a-menu-item>
<!--<a-menu-item key="/orgManager"><router-link to="/orgManager" ><a-icon type="monitor"/>组织机构管理</router-link></a-menu-item>-->
</a-sub-menu>
</a-menu>
</a-layout-sider>
<a-layout>
<a-layout-header style="background: #fff; padding: 0 20px;border-bottom:1px solid gainsboro;">
<a-row>
<a-col :span="16">
<a-button size="large" @click="toggleCollapsed">
<a-icon :type="collapsed ? 'menu-unfold' : 'menu-fold'" />
</a-button>
<strong style="font-size:17px;margin-left:10px;">平台日志查询系统</strong>
</a-col>
<a-col :span="8">
<h3 style="text-align:right;">
<span>当前用户:</span>
<a-icon type="user" />
<span class="user">管理员</span>
<a-button type="danger" shape="round" @click="logout">退出</a-button>
</h3>
</a-col>
</a-row>
<!-- <a-icon
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="() => (collapsed = !collapsed)"
/>
-->
</a-layout-header>
<a-layout-content
:style="{ minHeight: minHeight + 'px' }"
>
<router-view />
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script>
const minHeight = document.documentElement.clientHeight - 64
export default {
data() {
return {
minHeight: minHeight,
collapsed: false,
openKeys: ['1'],
flagMenu:'1',
openKeys:[],
}
},
watch: {
openKeys(val) {
console.log('openKeys', val)
}
},
methods: {
toggleCollapsed() {
this.collapsed = !this.collapsed
},
handleClick(e) {
console.log('click', e);
},
routerClick(value){
this.flagMenu = value
if (this.$route.path != value) {
this.$router.push({path: value})
}
},
logout() {
this.$confirm({
title: '是否确定退出系统?',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: this.logoutFun
})
},
logoutFun() {
this.$router.push('/login')
// this.$store.dispatch('user/logout')
}
},
created() {
this.flagMenu = this.$route.path
this.openKeys = []
var route = this.$route.path;
var lastroute=route.substr(1,route.length)
this.openKeys.push(lastroute)
}
}
</script>
<style>
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
#components-layout-demo-custom-trigger .trigger:hover {
color: #1890ff;
}
#components-layout-demo-custom-trigger .logo {
height: 32px;
margin: 14px;
}
</style>
<template>
<div>
<a-locale-provider :locale="locale">
<a-layout>
<div style="background-color: white;padding: 20px;margin-bottom:20px;">
<a-breadcrumb>
<!-- <a-icon type="arrow-left" style="margin-right:10px;" @click="() => $router.go(-1)" /> -->
<a-breadcrumb-item>查询</a-breadcrumb-item>
<a-breadcrumb-item>日志筛选</a-breadcrumb-item>
</a-breadcrumb>
</div>
<div style="background-color: white;margin:10px 24px;padding:24px;text-align:center">
<span>选择日期区间:</span>
<a-range-picker
@change="getSTime"
:show-time="{
hideDisabledOptions: true,
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('11:59:59', 'HH:mm:ss')],
}"
format="YYYY-MM-DD HH:mm:ss"
/>
<span>请输入应用ID:</span>
<a-input style="width: 20%" v-model="codeId" placeholder="请输入应用ID"></a-input>
<span>请输入响应码:</span>
<a-input
v-model="searchInput"
placeholder="请输入响应码"
style="width:15%;"
/>
<a-button @click="doSearch">查询</a-button>
</div>
<div style="background-color: white;margin:0 24px;">
<a-layout-content
:style="{ margin:'10px 24px',backgroundColor:'white'}"
>
<span><a-icon type="info-circle" />已检索到{{ pagination.total }}</span>
<a-button v-if="flagMode" style="float: right;cursor: pointer;margin-top: -5px"><a @click="mobandaochu">批量导出</a></a-button>
<a-spin :spinning="isLoading" :indicator="indicator">
<a-table class="table" :columns="columns" :data-source="data" :pagination="pagination" bordered @change="handleTableChange">
<span slot="time_stamp" slot-scope="text">
{{ text | dateFilter }}
</span>
<template slot="operation" slot-scope="text, record">
<div class="editable-row-operations">
<a @click="() => edit(record)">详情</a>
</div>
</template>
</a-table>
</a-spin>>
</a-layout-content>
</div>
</a-layout>
</a-locale-provider>
</div>
</template>
<script>
import {logsearchfilter } from '@/api/user'
import { dateFormat } from '@/utils/index'
import moment from 'moment';
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN';
import baseURL from '@/api/global_variable'
const columns = [
{
title: '业务流水号',
dataIndex: 'transaction_order_number',
width: '26%',
scopedSlots: { customRender: 'transaction_order_number' }
},
{
title: '应用id',
dataIndex: 'appid',
width: '26%',
scopedSlots: { customRender: 'appid' }
},
{
title: '模式',
dataIndex: 'mode',
width: '16%',
scopedSlots: { customRender: 'mode' }
},
{
title: '响应码',
dataIndex: 'code',
width: '16%',
scopedSlots: { customRender: 'code' }
},
{
title: '时间',
dataIndex: 'time_stamp',
width: '16%',
scopedSlots: { customRender: 'time_stamp' }
},
]
export default {
filters: {
// 日期过滤器,将时间戳改为时间格式
dateFilter: function(value) {
return dateFormat(parseInt(value), 'Y-m-d H:i:s')
}
},
data() {
return {
columns,
indicator: <a-icon type="sync" style="font-size: 30px;color:rgb(24,144,255)" spin />,
data: [],
searchInput: '',
locale: zhCN,
flagMode:false,
isLoading:false,
pagination: {
current: 1,
total: 0
},
codeId:'',
lte:'',
gte:''
}
},
mounted() {
this.doSearch()
},
methods: {
moment,
doSearch(){
this.isLoading = true
logsearchfilter(this.gte,this.lte,this.pagination.current,10,this.codeId,this.searchInput).then(res => {
if (res.data.code ==200){
this.pagination.total = res.data.data.total
this.data = res.data.data.list
this.isLoading = false
if (res.data.data.total>0){
this.flagMode = true
}
}
})
},
mobandaochu(){
var url = baseURL +'/log/search/v1/excelExport?code='+this.searchInput+'&page='+this.pagination.current+'&size='+this.pagination.total+'&appid='+this.codeId+'&gte='+this.gte+'&lte='+this.lte
window.location.href = url
},
getSTime (val) {
if (val.length<1){
this.gte = ''
this.lte = ''
}else{
var dategte = new Date(val[0]);
this.gte = Date.parse(dategte);
var datelte = new Date(val[1]);
this.lte = Date.parse(datelte);
}
},
// 页码变化后执行的操作
handleTableChange(pagination) {
const pager = { ...this.pagination }
pager.current = pagination.current
this.pagination = pager
console.log(this.pagination)
this.doSearch()
},
}
}
</script>
<style scoped>
.table {
margin: 10px 0;
}
</style>
This diff is collapsed.
module.exports = {
env: {
jest: true
}
}
import { mount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import ElementUI from 'element-ui'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
const localVue = createLocalVue()
localVue.use(VueRouter)
localVue.use(ElementUI)
const routes = [
{
path: '/',
name: 'home',
children: [{
path: 'dashboard',
name: 'dashboard'
}]
},
{
path: '/menu',
name: 'menu',
children: [{
path: 'menu1',
name: 'menu1',
meta: { title: 'menu1' },
children: [{
path: 'menu1-1',
name: 'menu1-1',
meta: { title: 'menu1-1' }
},
{
path: 'menu1-2',
name: 'menu1-2',
redirect: 'noredirect',
meta: { title: 'menu1-2' },
children: [{
path: 'menu1-2-1',
name: 'menu1-2-1',
meta: { title: 'menu1-2-1' }
},
{
path: 'menu1-2-2',
name: 'menu1-2-2'
}]
}]
}]
}]
const router = new VueRouter({
routes
})
describe('Breadcrumb.vue', () => {
const wrapper = mount(Breadcrumb, {
localVue,
router
})
it('dashboard', () => {
router.push('/dashboard')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(1)
})
it('normal route', () => {
router.push('/menu/menu1')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(2)
})
it('nested route', () => {
router.push('/menu/menu1/menu1-2/menu1-2-1')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(4)
})
it('no meta.title', () => {
router.push('/menu/menu1/menu1-2/menu1-2-2')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(3)
})
// it('click link', () => {
// router.push('/menu/menu1/menu1-2/menu1-2-2')
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
// const second = breadcrumbArray.at(1)
// console.log(breadcrumbArray)
// const href = second.find('a').attributes().href
// expect(href).toBe('#/menu/menu1')
// })
// it('noRedirect', () => {
// router.push('/menu/menu1/menu1-2/menu1-2-1')
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
// const redirectBreadcrumb = breadcrumbArray.at(2)
// expect(redirectBreadcrumb.contains('a')).toBe(false)
// })
it('last breadcrumb', () => {
router.push('/menu/menu1/menu1-2/menu1-2-1')
const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
const redirectBreadcrumb = breadcrumbArray.at(3)
expect(redirectBreadcrumb.contains('a')).toBe(false)
})
})
import { shallowMount } from '@vue/test-utils'
import Hamburger from '@/components/Hamburger/index.vue'
describe('Hamburger.vue', () => {
it('toggle click', () => {
const wrapper = shallowMount(Hamburger)
const mockFn = jest.fn()
wrapper.vm.$on('toggleClick', mockFn)
wrapper.find('.hamburger').trigger('click')
expect(mockFn).toBeCalled()
})
it('prop isActive', () => {
const wrapper = shallowMount(Hamburger)
wrapper.setProps({ isActive: true })
expect(wrapper.contains('.is-active')).toBe(true)
wrapper.setProps({ isActive: false })
expect(wrapper.contains('.is-active')).toBe(false)
})
})
import { shallowMount } from '@vue/test-utils'
import SvgIcon from '@/components/SvgIcon/index.vue'
describe('SvgIcon.vue', () => {
it('iconClass', () => {
const wrapper = shallowMount(SvgIcon, {
propsData: {
iconClass: 'test'
}
})
expect(wrapper.find('use').attributes().href).toBe('#icon-test')
})
it('className', () => {
const wrapper = shallowMount(SvgIcon, {
propsData: {
iconClass: 'test'
}
})
expect(wrapper.classes().length).toBe(1)
wrapper.setProps({ className: 'test' })
expect(wrapper.classes().includes('test')).toBe(true)
})
})
import { formatTime } from '@/utils/index.js'
describe('Utils:formatTime', () => {
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
const retrofit = 5 * 1000
it('ten digits timestamp', () => {
expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分')
})
it('test now', () => {
expect(formatTime(+new Date() - 1)).toBe('刚刚')
})
it('less two minute', () => {
expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前')
})
it('less two hour', () => {
expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前')
})
it('less one day', () => {
expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前')
})
it('more than one day', () => {
expect(formatTime(d)).toBe('7月13日17时54分')
})
it('format', () => {
expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
})
})
import { parseTime } from '@/utils/index.js'
describe('Utils:parseTime', () => {
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
it('timestamp', () => {
expect(parseTime(d)).toBe('2018-07-13 17:54:01')
})
it('ten digits timestamp', () => {
expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
})
it('new Date', () => {
expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01')
})
it('format', () => {
expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
})
it('get the day of the week', () => {
expect(parseTime(d, '{a}')).toBe('五') // 星期五
})
it('get the day of the week', () => {
expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日
})
it('empty argument', () => {
expect(parseTime()).toBeNull()
})
})
import { validUsername, isExternal } from '@/utils/validate.js'
describe('Utils:validate', () => {
it('validUsername', () => {
expect(validUsername('admin')).toBe(true)
expect(validUsername('editor')).toBe(true)
expect(validUsername('xxxx')).toBe(false)
})
it('isExternal', () => {
expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true)
expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true)
expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false)
expect(isExternal('/dashboard')).toBe(false)
expect(isExternal('./dashboard')).toBe(false)
expect(isExternal('dashboard')).toBe(false)
})
})
'use strict'
const path = require('path')
const defaultSettings = require('./src/settings.js')
function resolve(dir) {
return path.join(__dirname, dir)
}
const name = defaultSettings.title || 'vue Admin Template' // page title
// If your port is set to 80,
// use administrator privileges to execute the command line.
// For example, Mac: sudo npm run——
// You can change the port by the following methods:
// port = 9528 npm run dev OR npm run dev --port = 8080
const port = process.env.port || process.env.npm_config_port || 8080 // dev port
// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {
/**
* You will need to set publicPath if you plan to deploy your site under a sub path,
* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then publicPath should be set to "/bar/".
* In most cases please use '/' !!!
* Detail: https://cli.vuejs.org/config/#publicpath
*/
publicPath: '',
outputDir: 'dist',
// assetsDir: 'static',
lintOnSave: false,
productionSourceMap: false,
devServer: {
port: port,
// open: true,
overlay: {
warnings: false,
errors: true
},
before: require('./mock/mock-server.js')
},
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')
}
}
},
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/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/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')
}
)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment