Commit 3d1a7644 by 姜雷

init project

parents
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
/build/
/config/
/dist/
/*.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
extends: ['plugin:vue/essential'],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
"postcss-aspect-ratio-mini": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}
# admin
> 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).
'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)
}
}
'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')
})
}
}
'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')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
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: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/assets/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [resolve('src/assets/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
'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
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
})
'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: {
// '/dcxy': {
// target: 'http://192.168.1.110:18413', //设置你调用的接口域名和端口号 别忘了加http
// // target: 'https://easy-mock.com/mock/59840bf5a1d30433d8531e78/dcxy/app',
// changeOrigin: true,
// pathRewrite: {
// '^http://192.168.1.110:18413':
// 'https://easy-mock.com/mock/59840bf5a1d30433d8531e78',
// },
// },
},
// 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-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-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,
},
};
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>管理系统</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
{
"name": "admin",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "kuangshu <252019869@qq.com>",
"private": true,
"scripts": {
"dev": "cross-env PORT=8888 webpack-dev-server --host 0.0.0.0 --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js",
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
},
"dependencies": {
"axios": "^0.18.0",
"better-scroll": "^1.9.1",
"blueimp-md5": "^2.10.0",
"element-ui": "^2.4.5",
"js-cookie": "^2.2.0",
"lodash": "^4.17.5",
"normalize.css": "^8.0.0",
"nprogress": "^0.2.0",
"popmotion": "^8.1.22",
"qiniu-js": "^2.2.0",
"vue": "^2.5.2",
"vue-qr": "^1.2.8",
"vue-router": "^3.0.1",
"vuedraggable": "^2.16.0",
"vuex": "^3.0.1",
"wangeditor": "^3.1.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^5.1.4",
"css-loader": "^0.28.11",
"eslint": "^4.15.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"less": "^3.0.4",
"less-loader": "^4.1.0",
"mockjs": "^1.0.1-beta3",
"node-notifier": "^5.1.2",
"node-sass": "^4.9.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-aspect-ratio-mini": "0.0.2",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"sass-loader": "^6.0.7",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"style-loader": "^0.20.3",
"stylus-loader": "^3.0.2",
"svg-sprite-loader": "^3.9.0",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="scss">
p,
ul,
li {
margin: 0;
padding: 0;
}
ul,
dl {
list-style: none;
}
#app .main-wrap {
margin: 5px;
border: 1px solid #ccc;
box-shadow: 0 0 10px #ccc;
.search-bar {
display: flex;
padding: 20px 15px 0;
}
.grid-content {
margin-right: 20px;
}
.tabel-wrap {
padding: 15px;
}
.custom-tree-node {
display: flex;
width: 100%;
justify-content: space-between;
}
}
.el-table .cell {
box-sizing: border-box;
white-space: nowrap;
word-break: break-all;
line-height: 23px;
}
.el-table td,
.el-table th {
padding: 6px 0;
min-width: 0;
box-sizing: border-box;
text-overflow: ellipsis;
vertical-align: middle;
position: relative;
}
.el-table .cell {
box-sizing: border-box;
white-space: nowrap;
word-break: break-all;
line-height: 15px;
}
.el-table .el-table__row {
height: 43px;
}
.el-dialog__header {
border-bottom: 1px solid #ccc;
}
.el-dialog {
min-width: 635px;
}
.table-footer {
display: flex;
justify-content: space-between;
margin: 30px 30px 0;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type='number'] {
-webkit-appearance: none;
-moz-appearance: textfield;
}
</style>
<script>
export default {
name: 'App',
};
</script>
import createFetch from '../baseFetch';
import CONSTANTS from '@/config/index';
const path = CONSTANTS.BASE_SERVER_URL + '/dcxy/api';
const service = createFetch({
baseURL: path,
});
export default service;
import CONSTANTS from '@/config/index';
import axios from 'axios';
import { Message, MessageBox } from 'element-ui';
import store from '@/store';
import { errorHandle } from './validate';
import md5 from 'blueimp-md5';
const createBaseFetch = config => {
const service = axios.create({
timeout: 10000,
...config,
});
let logout = false;
// request拦截器
service.interceptors.request.use(
conf => {
store.dispatch('fetchStart');
conf.headers = {
...conf.headers,
reqSource: 'pc',
token: store.getters.token ? store.getters.token : '',
};
return conf;
},
error => {
// Do something with request error
console.log(error); // for debug
Promise.reject(error);
}
);
// respone拦截器
service.interceptors.response.use(response => {
const res = response.data;
/**
* code为非 1000 是抛错
*/
setTimeout(() => {
store.dispatch('fetchDone');
}, 500);
if (res.code !== 1000) {
if (
response.headers &&
/application\/octet-stream/.test(response.headers['content-type'])
) {
return response;
}
Message({
message: res.msg || '网络错误!',
type: 'error',
duration: 5 * 1000,
});
// -2:其他客户端登录了;Token 过期了;
if (res.code === -2) {
if (logout) return;
logout = true;
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload(); // 为了重新实例化vue-router对象 避免bug
});
})
.catch(err => {
console.log(err);
logout = false;
});
}
return Promise.reject(response.data);
} else {
return response.data;
}
}, errorHandle);
return service;
};
export default createBaseFetch;
import axios from 'axios';
import { Message, MessageBox } from 'element-ui';
import store from '../store';
import config from '../config/index';
import { errorHandle } from './validate';
import md5 from 'blueimp-md5';
// 创建axios实例
const service = axios.create({
baseURL: config.SERVER_URL, // api的base_url
timeout: 10000,
});
let logout = false;
// request拦截器
service.interceptors.request.use(
conf => {
store.dispatch('fetchStart');
// if(conf.method)
conf.headers = {
...conf.headers,
reqSource: 'pc',
token: store.getters.token ? store.getters.token : '',
};
if (conf.method != 'get') {
conf.params = null;
}
return conf;
},
error => {
// Do something with request error
console.log(error); // for debug
Promise.reject(error);
}
);
// respone拦截器
service.interceptors.response.use(response => {
const res = response.data;
/**
* code为非'0'是抛错
*/
setTimeout(() => {
store.dispatch('fetchDone');
}, 500);
if (res.code !== '0') {
if (
response.headers &&
response.headers['content-type'] === 'application/octet-stream'
) {
return response;
}
Message({
message: res.msg || '网络错误!',
type: 'error',
duration: 5 * 1000,
});
// -2:其他客户端登录了;Token 过期了;
if (res.code === '-2') {
if (logout) return;
logout = true;
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload(); // 为了重新实例化vue-router对象 避免bug
});
})
.catch(err => {
console.log(err);
logout = false;
});
}
return Promise.reject(response.data);
} else {
return response.data;
}
}, errorHandle);
export default service;
import fetch from '@/api/fetch';
// 登录
export const login = entity =>
fetch({
method: 'post',
data: {
...entity,
},
withCredentials: true,
});
// 登出
export const logout = () =>
fetch({
method: 'post',
data: {},
});
// 修改密码
export const changePwd = entity =>
fetch({
method: 'post',
data: entity,
});
import { Message, MessageBox } from 'element-ui';
import store from '../store';
export const validateCode = res => {
const { code } = res;
switch (code) {
case '0':
return res;
case '-2':
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload(); // 为了重新实例化vue-router对象 避免bug
});
});
return Promise.reject(res);
default:
return Promise.reject(res);
}
};
export const errorHandle = error => {
console.log('err' + error); // for debug
store.dispatch('fetchDone');
if (error.message && /timeout/.test(error.message)) {
Message({
message: '请求超时!',
type: 'error',
duration: 5 * 1000,
});
return Promise.reject({ error, msg: '请求超时!' });
}
Message({
message: error.message,
type: 'error',
duration: 5 * 1000,
});
return Promise.reject(error);
};
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件
// register globally
Vue.component('svg-icon', SvgIcon)
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
requireAll(req)
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511504199105" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1815" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M817.968553 215.897142l-169.357176 0 0-58.869782c0-25.391297-20.657482-46.048779-46.048779-46.048779l-181.125197 0c-25.391297 0-46.048779 20.657482-46.048779 46.048779l0 58.869782-169.357176 0c-25.391297 0-46.048779 20.657482-46.048779 46.048779l0 71.631434c0 25.391297 20.657482 46.048779 46.048779 46.048779l28.321022 0 0 425.947112c0 59.246359 48.200792 107.447151 107.447151 107.447151l340.40076 0c59.246359 0 107.447151-48.200792 107.447151-107.447151L789.647531 379.626133l28.321022 0c25.391297 0 46.048779-20.657482 46.048779-46.048779l0-71.631434C864.017332 236.554624 843.35985 215.897142 817.968553 215.897142zM426.553932 162.14389l170.892135 0 0 53.753251-170.892135 0L426.553932 162.14389zM738.482221 805.574269c0 31.033807-25.248034 56.281841-56.281841 56.281841L341.79962 861.85611c-31.033807 0-56.281841-25.248034-56.281841-56.281841L285.517779 379.626133l452.964442 0L738.482221 805.574269zM812.852022 328.460824l-601.704045 0 0-61.398372 203.227588 0c2.302439 0.356111 4.66116 0.542352 7.061836 0.542352l181.125197 0c2.400676 0 4.759397-0.186242 7.062859-0.542352l203.226564 0L812.852022 328.460824zM513.023306 783.320429c14.128789 0 25.582655-11.453866 25.582655-25.582655l0-288.572348c0-14.128789-11.453866-25.582655-25.582655-25.582655-14.128789 0-25.582655 11.453866-25.582655 25.582655l0 288.572348C487.440651 771.866562 498.894518 783.320429 513.023306 783.320429zM645.541459 783.320429c14.128789 0 25.582655-11.453866 25.582655-25.582655l0-288.572348c0-14.128789-11.453866-25.582655-25.582655-25.582655s-25.582655 11.453866-25.582655 25.582655l0 288.572348C619.958804 771.866562 631.41267 783.320429 645.541459 783.320429zM380.505154 783.320429c14.128789 0 25.582655-11.453866 25.582655-25.582655l0-288.572348c0-14.128789-11.453866-25.582655-25.582655-25.582655s-25.582655 11.453866-25.582655 25.582655l0 288.572348C354.922499 771.866562 366.376365 783.320429 380.505154 783.320429z" p-id="3757"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511504199105" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1815" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M770.56 460.8h250.88C998.4 220.16 803.84 25.6 563.2 2.56v250.88c104.96 20.48 186.88 102.4 207.36 207.36z m0 0M460.8 253.44V2.56C220.16 25.6 25.6 220.16 2.56 460.8h250.88c20.48-104.96 102.4-186.88 207.36-207.36z m0 0M563.2 770.56v250.88c243.2-23.04 435.2-217.6 460.8-460.8H773.12C750.08 668.16 668.16 750.08 563.2 770.56z m0 0M253.44 563.2H2.56c23.04 243.2 217.6 435.2 460.8 460.8V773.12C355.84 750.08 273.92 668.16 253.44 563.2z m0 0" fill="" p-id="1816"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503993826520" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7878" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M941.677063 391.710356c9.337669-14.005992 6.224772-32.68133-6.224772-43.575447-14.005992-10.894118-32.68133-7.78122-43.575447 6.224771-1.556449 1.556449-174.300768 205.426673-379.727441 205.426673-199.200878 0-379.727441-205.426673-381.28389-206.982098-10.894118-12.450567-31.124881-14.005992-43.575448-3.112898-12.450567 10.894118-14.005992 31.124881-3.112897 43.575448 3.112897 4.668323 40.46255 46.687322 99.600439 93.375667l-79.369676 82.48155c-12.450567 12.450567-10.894118 32.68133 1.556449 43.575448 3.112897 6.224772 10.894118 9.337669 18.675338 9.337669 7.78122 0 15.562441-3.112897 21.787213-9.337669l85.594447-88.706321c40.46255 28.013007 88.706321 54.469566 141.619438 73.14388L340.959485 707.631586c-4.668323 17.118889 4.669346 34.237779 21.787213 38.906101h9.337669c14.005992 0 26.456558-9.337669 29.568432-23.343661l32.68133-110.494556c24.90011 4.668323 51.356668 7.78122 77.813227 7.78122s52.913117-3.112897 77.813227-7.78122l32.68133 108.938108c3.112897 14.005992 17.118889 23.343661 29.569456 23.343661 3.112897 0 6.224772 0 7.78122-1.556449 17.118889-4.669346 26.456558-21.787212 21.788236-38.906102l-32.68133-108.938108c52.913117-18.675338 101.156888-45.131897 141.619438-73.14388l84.037998 87.150896c6.224772 6.224772 14.005992 9.337669 21.787212 9.337669 7.78122 0 15.562441-3.112897 21.787212-9.337669 12.450567-12.450567 12.450567-31.124881 1.556449-43.575448l-79.369675-82.48155c63.808258-46.688345 101.158934-91.820242 101.158934-91.820242z" p-id="7879"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511504319223" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3230" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M942.827259 80.3367c-11.42419-11.406794-26.41051-17.117866-41.377386-17.117866-14.985296 0-29.952172 5.711072-41.358967 17.117866L719.392444 221.014696l-19.441794 19.441794L681.577187 258.832 569.516971 370.909611 375.99749 564.411697l0 0.019443 0 84.372619 81.145112 0 0.010233 0 95.418186-95.435583 213.398228-213.400275 3.14155-3.14155-0.019443 0 9.979282-9.977235 0 0L942.827259 163.073052C965.697129 140.259464 965.697129 103.186104 942.827259 80.3367z" p-id="3231"></path><path d="M793.542234 367.521444 580.14196 580.939115 484.72582 676.376745 473.299583 687.800935 457.152834 687.800935 375.99749 687.800935 337.000314 687.800935 337.000314 648.803759 337.000314 564.411697 337.000314 548.264948 348.424504 536.838711 541.943986 343.338672 654.004201 231.259014 665.428392 219.834824 64.020082 219.834824 64.020082 960.781166 804.966425 960.781166 804.966425 356.116697 796.607036 364.475062Z" p-id="3232"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1521704598179" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4010" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 557.223994l249.203712 249.203712c12.491499 12.491499 32.730449 12.489452 45.221948-0.002047s12.493545-32.730449 0.002047-45.221948L557.223994 512l249.203712-249.203712c12.491499-12.491499 12.489452-32.730449-0.002047-45.221948s-32.730449-12.493545-45.221948-0.002047L512 466.776006 262.796288 217.572294c-12.491499-12.491499-32.729425-12.490475-45.220924 0.001023-6.246261 6.246261-9.370415 14.429641-9.370415 22.610974s3.121084 16.365736 9.367345 22.610974L466.774983 512 217.572294 761.203712c-6.246261 6.246261-9.367345 14.428617-9.367345 22.610974s3.125177 16.365736 9.370415 22.610974c12.491499 12.491499 32.729425 12.493545 45.220924 0.002047L512 557.223994z" p-id="4011" fill="#d81e06"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1534225155003" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="755" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M516.672 605.76c11.136 42.624 0.64 89.856-32.64 123.136L348.288 864.64c-49.792 49.792-131.264 49.792-180.992 0l-45.248-45.248c-49.792-49.792-49.792-131.264 0-180.992l135.744-135.744c33.216-33.216 80.512-43.776 123.136-32.64L303.04 547.904 277.568 573.312l-110.336 110.336c-24.96 24.96-24.96 65.536 0 90.496l45.248 45.248c24.96 24.96 65.536 24.96 90.496 0l110.336-110.336 25.472-25.472L516.672 605.76zM891.328 140.608 846.08 95.36c-49.792-49.792-131.264-49.792-180.992 0L529.28 231.104C496.064 264.32 485.568 311.616 496.704 354.24l77.888-77.888L600 250.88l110.336-110.336c24.96-24.96 65.536-24.96 90.496 0l45.248 45.248c24.96 24.96 24.96 65.536 0 90.496l-110.336 110.336-25.472 25.472L632.448 489.984c42.624 11.136 89.856 0.64 123.136-32.64l135.744-135.744C941.12 271.808 941.12 190.336 891.328 140.608zM690.496 296.128c-12.416-12.416-32.832-12.48-45.248 0L574.528 366.848 529.28 412.096 438.784 502.656 393.536 547.904 322.816 618.624C319.744 621.696 317.376 625.28 315.84 629.184c-3.136 7.68-3.136 16.384 0 24.128 1.536 3.84 3.904 7.488 6.976 10.56 3.136 3.136 6.72 5.44 10.56 6.976 7.68 3.136 16.384 3.136 24.128 0 3.84-1.536 7.488-3.904 10.56-6.976l70.72-70.72 45.248-45.248 90.496-90.496 45.248-45.248 70.72-70.72C702.976 328.96 702.976 308.608 690.496 296.128z" p-id="756"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994678729" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9229" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M780.8 354.579692 665.6 354.579692 665.6 311.689846c0-72.310154-19.849846-193.299692-153.6-193.299692-138.870154 0-153.6 135.049846-153.6 193.299692l0 42.889846L243.2 354.579692 243.2 311.689846C243.2 122.249846 348.790154 0 512 0s268.8 122.249846 268.8 311.689846L780.8 354.579692zM588.8 669.420308C588.8 625.900308 554.220308 590.769231 512 590.769231s-76.8 35.131077-76.8 78.651077c0 29.459692 15.399385 54.468923 38.439385 67.820308l0 89.639385c0 21.740308 17.250462 39.699692 38.4 39.699692s38.4-17.959385 38.4-39.699692l0-89.639385C573.44 723.889231 588.8 698.88 588.8 669.420308zM896 512l0 393.609846c0 65.260308-51.869538 118.390154-115.2 118.390154L243.2 1024c-63.291077 0-115.2-53.129846-115.2-118.390154L128 512c0-65.220923 51.869538-118.390154 115.2-118.390154l537.6 0C844.130462 393.609846 896 446.779077 896 512z" p-id="9230"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511504199105" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1815" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M185.89696 532.2752h286.72v286.72a20.48 20.48 0 1 0 40.96 0v-286.72h286.72a20.48 20.48 0 1 0 0-40.96h-286.72v-286.72a20.48 20.48 0 1 0-40.96 0v286.72h-286.72a20.48 20.48 0 1 0 0 40.96z" fill="" p-id="3098"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503994797471" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9770" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M565.272827 34.627285l112.095872 237.542288c8.706637 18.321022 25.411424 31.051641 44.82285 33.996289l250.776598 38.081157c48.697387 7.411435 68.22505 70.046082 32.933559 105.979639l-181.494353 184.937155c-13.998147 14.230618-20.352386 34.815477-17.05903 54.93539l42.819161 261.127145c8.346858 50.695541-42.64204 89.451974-86.225039 65.51841l-224.307979-123.271141c-17.285968-9.525824-37.992596-9.525824-55.278564 0l-224.313514 123.271141c-43.582999 23.933565-94.571897-14.822869-86.219504-65.51841l42.813626-261.127145c3.321031-20.119914-3.088559-40.704772-17.086706-54.93539l-181.439002-184.937155c-35.285956-35.933557-15.819179-98.57374 32.933559-105.979639l250.748923-38.081157c19.350541-2.939112 36.083003-15.675267 44.75643-33.996289l112.123547-237.542288C480.497972-11.540583 543.509003-11.540583 565.272827 34.627285z" p-id="9771"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511504440567" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5070" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M568.6 0h454.9v454.9H568.6V0z m0 568.6h454.9v454.9H568.6V568.6zM0 568.6h454.9v454.9H0V568.6zM0 0h454.9v454.9H0V0z" fill="" p-id="5071"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1511512690058" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3507" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M1013.703 693.345c6.865 6.865 10.297 14.874 10.297 24.027l0 205.944c0 9.916-3.432 18.115-10.297 24.599-6.865 6.483-15.255 9.725-25.171 9.725L782.588 957.64c-9.153 0-17.162-3.242-24.027-9.725-6.865-6.483-10.297-14.683-10.297-24.599L748.264 717.372c0-6.102 1.526-11.823 4.577-17.162s7.246-9.534 12.586-12.586 11.06-4.577 17.162-4.577l77.801 0L860.39 546.896c0-4.577-1.144-8.772-3.432-12.586s-5.339-6.865-9.153-9.153-8.009-3.432-12.585-3.432L543.464 521.725l0 161.323 77.801 0c9.153 0 17.162 3.432 24.027 10.297s10.297 14.874 10.297 24.027l0 205.944c0 6.102-1.526 11.823-4.577 17.162s-7.246 9.534-12.585 12.585-11.06 4.577-17.162 4.577L415.321 957.64c-6.102 0-11.823-1.526-17.162-4.577s-9.725-7.246-13.158-12.585-5.149-11.06-5.149-17.162L379.852 717.372c0-9.153 3.432-17.162 10.297-24.027s15.255-10.297 25.171-10.297l76.657 0L491.977 521.725 188.782 521.725c-7.628 0-13.92 2.479-18.878 7.437-4.958 4.958-7.437 10.869-7.437 17.734l0 136.152 77.801 0c9.916 0 18.115 3.432 24.599 10.297s9.725 14.874 9.725 24.027l0 205.944c0 9.916-3.242 18.115-9.725 24.599-6.483 6.483-14.683 9.725-24.599 9.725L34.324 957.64c-3.814 0-7.437-0.572-10.869-1.716-3.432-1.144-6.483-2.67-9.153-4.577-2.67-1.907-5.149-4.386-7.437-7.437-2.288-3.051-4.004-6.293-5.149-9.725C0.572 930.753 0 927.13 0 923.316L0 717.372c0-3.051 0.381-6.102 1.144-9.153s1.907-5.721 3.432-8.009 3.432-4.577 5.721-6.865 4.577-4.195 6.865-5.721 4.958-2.67 8.009-3.432 6.102-1.144 9.153-1.144l77.801 0L112.125 495.41c0-6.865 2.479-12.776 7.437-17.734s10.869-7.437 17.734-7.437l354.682 0L491.978 342.096l-76.657 0c-9.916 0-18.306-3.432-25.171-10.297s-10.297-14.874-10.297-24.027L379.853 101.828c0-9.916 3.432-18.306 10.297-25.171s15.255-10.297 25.171-10.297l205.944 0c6.102 0 11.823 1.716 17.162 5.149 5.339 3.432 9.534 7.818 12.585 13.158 3.051 5.339 4.577 11.06 4.577 17.162l0 205.944c0 9.153-3.432 17.162-10.297 24.027s-14.874 10.297-24.027 10.297l-77.801 0 0 128.143L885.56 470.24c7.628 0 13.92 2.479 18.878 7.437s7.437 10.869 7.437 17.734l0 187.638 76.657 0C998.448 683.048 1006.838 686.48 1013.703 693.345z" p-id="3508"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1503993891882" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7986" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M504.951 511.98c93.49 0 169.28-74.002 169.28-165.26 0-91.276-75.79-165.248-169.28-165.248-93.486 0-169.287 73.972-169.279 165.248-0.001 91.258 75.793 165.26 169.28 165.26z m77.6 55.098H441.466c-120.767 0-218.678 95.564-218.678 213.45V794.3c0 48.183 97.911 48.229 218.678 48.229H582.55c120.754 0 218.66-1.78 218.66-48.229v-13.77c0-117.887-97.898-213.45-218.66-213.45z" p-id="7987"></path></svg>
\ No newline at end of file
//to reset element-ui default css
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
//element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
@import './variables.scss';
@import './mixin.scss';
@import './transition.scss';
@import './element-ui.scss';
@import './sidebar.scss';
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
div:focus{
outline: none;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
//main-container全局样式
.app-main{
flex: 1;
overflow-y: auto;
}
.app-container {
padding: 20px;
}
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
$sideBar-width: 250px;
#app {
// 主体区域
.main-container {
display: flex;
flex-direction: column;
max-height: calc(100vh - 70px);
transition: margin-left 0.28s;
margin-left: $sideBar-width;
} // 侧边栏
.sidebar-container {
transition: width 0.28s;
margin-top: 70px;
width: $sideBar-width;
height: calc(100vh - 70px);
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
a {
display: inline-block;
width: 100%;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
width: 100%;
}
}
.hideSidebar {
.sidebar-container,
.sidebar-container .el-menu {
width: 40px;
// overflow: inherit;
}
.main-container {
margin-left: 40px;
}
}
.hideSidebar {
.submenu-title-noDropdown {
padding-left: 10px;
position: relative;
span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
transition: opacity 3s cubic-bezier(0.55, 0, 0.1, 1);
opacity: 0;
display: inline-block;
}
&:hover {
span {
display: block;
border-radius: 3px;
z-index: 1002;
width: 140px;
height: 56px;
visibility: visible;
position: absolute;
right: -145px;
text-align: left;
text-indent: 20px;
top: 0px;
background-color: $subMenuBg !important;
opacity: 1;
}
}
}
.el-submenu {
& > .el-submenu__title {
padding-left: 10px !important;
& > span {
display: none;
}
.el-submenu__icon-arrow {
display: none;
}
}
.nest-menu {
.el-submenu__icon-arrow {
display: block !important;
}
span {
display: inline-block !important;
}
}
}
}
.nest-menu .el-submenu > .el-submenu__title,
.el-submenu .el-menu-item {
min-width: 180px !important;
background-color: $subMenuBg !important;
&:hover {
background-color: $menuHover !important;
}
}
.el-menu--collapse .el-menu .el-submenu {
min-width: 180px !important;
}
}
//globl transition css
/*fade*/
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/*fade*/
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all .5s;
}
.breadcrumb-leave-active {
position: absolute;
}
//sidebar
$menuBg:#304156;
$subMenuBg:#1f2d3d;
$menuHover:#001528;
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if="item.meta.title">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title}}</span>
<router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
export default {
created() {
this.getBreadcrumb()
},
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name)
const first = matched[0]
if (first && first.name !== 'home') {
matched = [{ path: '/home', meta: { title: '首页' }}].concat(matched)
}
this.levelList = matched
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
<template>
<div>
<svg t="1492500959545" @click="toggleClick" class="svg-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"></path>
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"></path>
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"></path>
</svg>
</div>
</template>
<script>
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(0deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(90deg);
}
</style>
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const delta = 15;
export default {
name: 'scrollBar',
data() {
return {
top: 0,
};
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3;
const $container = this.$refs.scrollContainer;
const $containerHeight = $container.offsetHeight;
const $wrapper = this.$refs.scrollWrapper;
const $wrapperHeight = $wrapper.offsetHeight;
if (eventDelta > 0) {
this.top = Math.min(0, this.top + eventDelta);
} else {
if ($containerHeight - delta < $wrapperHeight) {
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
this.top = this.top;
} else {
this.top = Math.max(
this.top + eventDelta,
$containerHeight - $wrapperHeight - delta
);
}
} else {
this.top = 0;
}
}
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import '../../assets/styles/variables.scss';
.scroll-container {
position: relative;
width: 100%;
height: 100%;
background-color: $menuBg;
.scroll-wrapper {
position: absolute;
width: 100% !important;
}
}
</style>
<template>
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
<div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
<slot></slot>
</div>
</div>
</template>
<script>
const padding = 15 // tag's padding
export default {
name: 'scrollPane',
data() {
return {
left: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 3
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $wrapper = this.$refs.scrollWrapper
const $wrapperWidth = $wrapper.offsetWidth
if (eventDelta > 0) {
this.left = Math.min(0, this.left + eventDelta)
} else {
if ($containerWidth - padding < $wrapperWidth) {
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
this.left = this.left
} else {
this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
}
} else {
this.left = 0
}
}
},
moveToTarget($target) {
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $targetLeft = $target.offsetLeft
const $targetWidth = $target.offsetWidth
if ($targetLeft < -this.left) {
// tag in the left
this.left = -$targetLeft + padding
} else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
// tag in the current view
// eslint-disable-line
} else {
// tag in the right
this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
.scroll-wrapper {
position: absolute;
}
}
</style>
<template>
<div class="slide" ref="slide">
<div class="slide-group" ref="slideGroup">
<slot>
</slot>
</div>
<div v-if="showDot" class="dots">
<span class="dot" :class="{active: currentPageIndex === index }" v-for="(item, index) in dots" :key="index"></span>
</div>
</div>
</template>
<script type="text/ecmascript-6">
import BScroll from 'better-scroll'
const COMPONENT_NAME = 'slide'
export default {
name: COMPONENT_NAME,
props: {
loop: {
type: Boolean,
default: false,
},
autoPlay: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 4000
},
showDot: {
type: Boolean,
default: false,
},
click: {
type: Boolean,
default: true
},
threshold: {
type: Number,
default: 0.3
},
speed: {
type: Number,
default: 400
},
change: {
type: Function,
},
index: {
type: Number,
default: 0,
},
},
data() {
return {
dots: [],
currentPageIndex: 0
}
},
mounted() {
this.update()
window.addEventListener('resize', () => {
if (!this.slide || !this.slide.enabled) {
return
}
clearTimeout(this.resizeTimer)
this.resizeTimer = setTimeout(() => {
if (this.slide.isInTransition) {
this._onScrollEnd()
} else {
if (this.autoPlay) {
this._play()
}
}
this.refresh()
}, 60)
});
},
activated() {
if (!this.slide) {
return
}
this.slide.enable()
let pageIndex = this.slide.getCurrentPage().pageX
this.slide.goToPage(pageIndex, 0, 0)
this.currentPageIndex = pageIndex
if (this.autoPlay) {
this._play()
}
},
deactivated() {
this.slide.disable()
clearTimeout(this.timer)
},
beforeDestroy() {
this.slide.disable()
clearTimeout(this.timer)
},
methods: {
update() {
if (this.slide) {
this.slide.destroy()
}
this.$nextTick(() => {
this.init()
})
},
refresh() {
this._setSlideWidth(true)
this.slide.refresh()
},
prev() {
this.slide.prev()
},
next() {
this.slide.next()
},
init() {
clearTimeout(this.timer)
this.currentPageIndex = 0
this._setSlideWidth()
if (this.showDot) {
this._initDots()
}
this._initSlide()
if (this.autoPlay) {
this._play()
}
},
_setSlideWidth(isResize) {
this.children = this.$refs.slideGroup.children
let width = 0
let slideWidth = this.$refs.slide.clientWidth
for (let i = 0; i < this.children.length; i++) {
let child = this.children[i]
addClass(child, 'slide-item')
child.style.width = slideWidth + 'px'
width += slideWidth
}
if (this.loop && !isResize) {
width += 2 * slideWidth
}
this.$refs.slideGroup.style.width = width + 'px'
},
_initSlide() {
// console.log(this.threshold)
this.slide = new BScroll(this.$refs.slide, {
scrollX: true,
scrollY: false,
momentum: false,
snap: {
loop: this.loop,
threshold: this.threshold,
speed: this.speed
},
bounce: false,
click: this.click
})
this.slide.on('scrollEnd', this._onScrollEnd)
this.slide.on('touchEnd', () => {
if (this.autoPlay) {
this._play()
}
})
this.slide.on('beforeScrollStart', () => {
if (this.autoPlay) {
clearTimeout(this.timer)
}
})
},
_onScrollEnd() {
let pageIndex = this.slide.getCurrentPage().pageX
this.currentPageIndex = pageIndex;
if(this.change) {
this.change(pageIndex);
}
if (this.autoPlay) {
this._play()
}
},
_initDots() {
this.dots = new Array(this.children.length)
},
_play() {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.slide.next()
}, this.interval)
}
},
watch: {
loop() {
this.update()
},
autoPlay() {
this.update()
},
speed() {
this.update()
},
threshold() {
this.update()
},
index(nVal) {
this.slide.goToPage(nVal);
this.currentPageIndex = nVal;
}
}
}
function hasClass(el, className) {
let reg = new RegExp('(^|\\s)' + className + '(\\s|$)')
return reg.test(el.className)
}
function addClass(el, className) {
if (hasClass(el, className)) {
return
}
let newClass = el.className.split(' ')
newClass.push(className)
el.className = newClass.join(' ')
}
</script>
<style lang="scss">
.slide {
position: relative;
min-height: 1px;
height: 100%;
.slide-group {
position: relative;
overflow: hidden;
white-space: nowrap;
height: 100%;
.slide-item {
height: 100%;
float: left;
box-sizing: border-box;
overflow: hidden;
a {
display: block;
width: 100%;
overflow: hidden;
text-decoration: none;
}
img {
display: block;
width: 100%;
}
}
}
.dots {
position: absolute;
right: 0;
left: 0;
bottom: 34px;
transform: translateZ(1px);
text-align: center;
font-size: 0;
.dot {
display: inline-block;
margin: 0 7px;
width: 12px;
height: 12px;
border-radius: 50%;
border: .5px solid #ee7e1e;
&.active {
border: none;
background: #fff;
}
}
}
}
</style>
\ No newline at end of file
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script>
export default {
name: 'svg-icon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
<template>
<canvas ref="bubble" :width="width" :height="height" :style="style"></canvas>
</template>
<script type="text/ecmascript-6">
export default {
props: {
y: {
type: Number,
default: 0
}
},
data() {
return {
width: 50,
height: 80
}
},
computed: {
distance() {
return Math.max(0, Math.min(this.y * this.ratio, this.maxDistance))
},
style() {
return `width:${this.width / this.ratio}px;height:${this.height / this.ratio}px`
}
},
created() {
this.ratio = window.devicePixelRatio
this.width *= this.ratio
this.height *= this.ratio
this.initRadius = 18 * this.ratio
this.minHeadRadius = 12 * this.ratio
this.minTailRadius = 5 * this.ratio
this.initArrowRadius = 10 * this.ratio
this.minArrowRadius = 6 * this.ratio
this.arrowWidth = 3 * this.ratio
this.maxDistance = 40 * this.ratio
this.initCenterX = 25 * this.ratio
this.initCenterY = 25 * this.ratio
this.headCenter = {
x: this.initCenterX,
y: this.initCenterY
}
},
mounted() {
this._draw()
},
methods: {
_draw() {
const bubble = this.$refs.bubble
let ctx = bubble.getContext('2d')
ctx.clearRect(0, 0, bubble.width, bubble.height)
this._drawBubble(ctx)
this._drawArrow(ctx)
},
_drawBubble(ctx) {
ctx.save()
ctx.beginPath()
const rate = this.distance / this.maxDistance
const headRadius = this.initRadius - (this.initRadius - this.minHeadRadius) * rate
this.headCenter.y = this.initCenterY - (this.initRadius - this.minHeadRadius) * rate
// 画上半弧线
ctx.arc(this.headCenter.x, this.headCenter.y, headRadius, 0, Math.PI, true)
// 画左侧贝塞尔
const tailRadius = this.initRadius - (this.initRadius - this.minTailRadius) * rate
const tailCenter = {
x: this.headCenter.x,
y: this.headCenter.y + this.distance
}
const tailPointL = {
x: tailCenter.x - tailRadius,
y: tailCenter.y
}
const controlPointL = {
x: tailPointL.x,
y: tailPointL.y - this.distance / 2
}
ctx.quadraticCurveTo(controlPointL.x, controlPointL.y, tailPointL.x, tailPointL.y)
// 画下半弧线
ctx.arc(tailCenter.x, tailCenter.y, tailRadius, Math.PI, 0, true)
// 画右侧贝塞尔
const headPointR = {
x: this.headCenter.x + headRadius,
y: this.headCenter.y
}
const controlPointR = {
x: tailCenter.x + tailRadius,
y: headPointR.y + this.distance / 2
}
ctx.quadraticCurveTo(controlPointR.x, controlPointR.y, headPointR.x, headPointR.y)
ctx.fillStyle = 'rgb(170,170,170)'
ctx.fill()
ctx.strokeStyle = 'rgb(153,153,153)'
ctx.stroke()
ctx.restore()
},
_drawArrow(ctx) {
ctx.save()
ctx.beginPath()
const rate = this.distance / this.maxDistance
const arrowRadius = this.initArrowRadius - (this.initArrowRadius - this.minArrowRadius) * rate
// 画内圆
ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius - (this.arrowWidth - rate), -Math.PI / 2, 0, true)
// 画外圆
ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius, 0, Math.PI * 3 / 2, false)
ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius - this.arrowWidth / 2 + rate)
ctx.lineTo(this.headCenter.x + this.arrowWidth * 2 - rate * 2, this.headCenter.y - arrowRadius + this.arrowWidth / 2)
ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius + this.arrowWidth * 3 / 2 - rate)
ctx.fillStyle = 'rgb(255,255,255)'
ctx.fill()
ctx.strokeStyle = 'rgb(170,170,170)'
ctx.stroke()
ctx.restore()
}
},
watch: {
y() {
this._draw()
}
}
}
</script>
<style scoped lang="less">
</style>
\ No newline at end of file
<template>
<div class="image-uploader">
<div v-if="done" @mouseover="toogleDelIcon" @mouseout="toogleDelIcon" class="image-uploader-done">
<div v-if="showDel && canDelete" class="mask">
<svg @click="delImg" class="delimg icon">
<use xlink:href="#icon-delimg"></use>
</svg>
</div>
<img class="image-uploader-img" :src="url" />
<!-- <img class="del" src="@/assets/images/icon_delete.png" @click="delImg" /> -->
</div>
<label v-else class="image-uploader-pre">
<slot>
<svg class="icon image-uploader-icon" aria-hidden="true">
<use xlink:href="#icon-plus"></use>
</svg>
</slot>
<input ref='input' type="file" accept='image/*' @change="uploadImg" style="display: none;" />
</label>
</div>
</template>
<style lang="scss" scoped>
.image-uploader {
width: 123px;
height: 123px;
border: 1px solid #ccc;
.mask {
position: absolute;
z-index: 20;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.image-uploader-pre,
.image-uploader-done {
display: block;
position: relative;
height: 100%;
.delimg {
width: 50px;
height: 50px;
position: absolute;
left: 50%;
top: 50%;
font-size: 50px;
fill: #999;
margin-left: -25px;
margin-top: -25px;
fill: #fff;
}
.image-uploader-img {
width: 100%;
}
}
.image-uploader-icon {
position: absolute;
display: block;
width: 50px;
height: 50px;
font-size: 50px;
left: 50%;
top: 50%;
margin-top: -25px;
margin-left: -25px;
fill: #999;
}
}
</style>
<script>
import * as qiniu from 'qiniu-js';
import { getDelImgToken } from '@/api/mall/common';
import { mapGetters } from 'vuex';
export default {
name: 'image-uploader',
data() {
return {
file: null,
fileName: '',
url: '',
domain: 'http://qiniu.dcrym.com/',
showDel: false,
};
},
props: {
value: {
// 图片地址
type: String,
default: '',
},
canDelete: {
// 是否显示删除按钮
type: Boolean,
default: true,
},
option: {
// 剪裁比例大小
type: Object,
default: () => ({
CropBox: {
width: 200,
height: 100,
},
}),
},
},
created() {
this.getImgToken();
this.setUrl(this.value);
},
computed: {
...mapGetters(['getToken', 'userInfo']),
done() {
return this.value ? true : false;
},
},
watch: {
value(newVal) {
this.setUrl(newVal);
},
},
deactivated() {
this.refreshData();
},
methods: {
setUrl(val) {
this.url = val ? val : '';
this.fileName = val ? val.replace(this.domain, '') : '';
},
toogleDelIcon() {
this.showDel = !this.showDel;
},
getImgToken(isReload) {
if (isReload) {
return this.$store.dispatch('getUploadImgToken').then(res => {
this.uploadImg(null, this.file);
return;
});
}
if (window.hasToken) return;
window.hasToken = true;
this.$store.dispatch('getUploadImgToken');
},
refreshData() {
this.file = null;
this.fileName = '';
this.url = '';
if (this.$refs.input && this.$refs.input.value) {
this.$refs.input.value = '';
}
},
uploadImg(e, blob) {
let file = blob ? blob : this.$refs.input.files[0];
this.file = file;
const id = this.userInfo.id;
const timeStemp = new Date().getTime();
let key = id + timeStemp;
let putExtra = {
fname: '',
params: {},
mimeType: null,
};
let config = {
useCdnDomain: true,
region: qiniu.region.z2,
};
// 调用sdk上传接口获得相应的observable,控制上传和暂停
let observable = qiniu.upload(file, key, this.getToken, putExtra, config);
observable.subscribe({
next: this._next,
error: this._error,
complete: this._complete,
});
},
delImg() {
getDelImgToken({
fileName: this.fileName,
})
.then(res => {
if (res.code === '0') {
this.$emit('input', '');
this.$nextTick(() => {
if (this.$refs.input) this.$refs.input.value = '';
});
} else {
console.log('删除错误:' + res.msg);
this.$message.error('删除错误!');
}
})
.catch(err => {
console.log('删除错误:' + err.msg);
});
},
_error(err) {
if (err.isRequestError && err.code == '401') {
this.getImgToken(true);
} else {
console.log(err);
this.$refs.input.value = '';
}
},
_next(res) {
// total: {
// loaded: number,已上传大小,单位为字节。
// total: number,本次上传的总量控制信息,单位为字节。
// percent: number,当前上传进度,范围:0~100。
// }
console.log(res.total.percent);
},
_complete(response) {
console.log(response);
this.$emit('input', this.domain + response.key);
this.fileName = response.key;
},
},
};
</script>
<template>
<div class="editor-wrapper" ref="editor" style="text-align:left;margin-top: 20px;"></div>
</template>
<script>
import E from 'wangeditor';
import * as qiniu from 'qiniu-js';
import config from '@/config/index';
import { getUploadImgToken, getDelImgToken } from '@/api/mall/common';
export default {
name: 'editor',
props: {
onchange: Function,
config: Object,
},
data() {
return {
IMG_URL: config.IMG_URL,
editor: null,
};
},
methods: {
getImgToken(isReload) {
if (isReload) {
getUploadImgToken().then(res => {
console.log(res);
window.$imageToken = res.data;
this.uploadImg();
});
return;
}
if (window.hasToken) return;
window.hasToken = true;
getUploadImgToken().then(res => {
window.$imageToken = res.data;
});
},
uploadImg(files, insert) {
console.log(files);
// files 是 input 中选中的文件列表
// insert 是获取图片 url 后,插入到编辑器的方法
let file = files[0];
let key = null;
let putExtra = {
fname: '',
params: {},
mimeType: null,
};
let config = {
useCdnDomain: true,
region: qiniu.region.z2,
};
// 调用sdk上传接口获得相应的observable,控制上传和暂停
let observable = qiniu.upload(
file,
key,
window.$imageToken,
putExtra,
config
);
// 上传代码返回结果之后,将图片插入到编辑器中
let complete = response => {
insert(this.IMG_URL + response.key);
};
let _error = err => {
console.log(err);
if (err.isRequestError && err.code == 401) {
this.getImgToken(true);
} else {
console.log(err);
this.$refs.input.value = '';
}
};
let _next = res => {
// total: {
// loaded: number,已上传大小,单位为字节。
// total: number,本次上传的总量控制信息,单位为字节。
// percent: number,当前上传进度,范围:0~100。
// }
console.log(res.total.percent);
};
observable.subscribe({
next: this._next,
error: _error,
complete,
});
},
},
mounted() {
this.editor = new E(this.$refs.editor);
this.editor.customConfig = {
menus: [
'head', // 标题
'bold', // 粗体
'fontSize', // 字号
'fontName', // 字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
'foreColor', // 文字颜色
'backColor', // 背景颜色
// 'link', // 插入链接
// 'list', // 列表
'justify', // 对齐方式
'quote', // 引用
// 'emoticon', // 表情
'image', // 插入图片
// 'table', // 表格
// 'video', // 插入视频
// 'code', // 插入代码
'undo', // 撤销
'redo', // 重复
],
customUploadImg: this.uploadImg,
onchange: this.onchange,
zIndex: 300,
...this.config,
};
this.editor.create();
},
};
</script>
<style>
.editor-wrapper {
width: 80%;
}
.editor-wrapper img {
width: auto;
}
</style>
<template>
<div class="mf-loading-container">
<img src="../images/loading.gif">
</div>
</template>
<script type="text/ecmascript-6">
const COMPONENT_NAME = 'loading'
export default {
name: COMPONENT_NAME
}
</script>
<style lang="less">
.mf-loading-container {
img {
width: 50px;
height: 50px;
display: block;
}
}
</style>
\ No newline at end of file
<template>
<div class="KYandLS">
<div class="commonTitle">
<img :src="data.sectionPic" />
<span>
<span class="fontsize22 color666">查看全部</span>
<span class="iconfont icon-youjiantou1 color666"></span>
</span>
</div>
<div class="KYandLS_contentBigBox_wrap">
<div v-if="dataList.length" class="KYandLS_contentBigBox" ref="scrollWrap">
<div class="KYandLS_content bb1px" v-for="index in row" :key="index">
<div class="KYandLS_prdSmalBox" w-224-179 aspectratio v-for="(item) in dataList.slice((index-1) * 3, 3 * index)" :key='item.id' @tap="goNewPage({dataType: data.dataType, id: item.id})">
<div v-if="item.activityType" class="tag-icon">
<img src="@/images/tag-icon-index@2x.png" alt="">
</div>
<img aspectratio-content :src="item.coverPic" />
</div>
</div>
</div>
<div class="empty_block" v-else>
<img src="@/images/hasNoProduct@2x.png" alt="暂无商品">
</div>
</div>
</div>
</template>
<style lang="less">
.empty_block {
padding: 20px 0;
img {
width: 315px;
height: 256px;
margin: 0 auto;
}
}
.KYandLS {
.tag-icon {
position: absolute;
width: 54px;
height: 46px;
right: 0;
top: 0;
}
.KYandLS_contentBigBox_wrap {
width: 100%;
padding: 0 24px;
}
.KYandLS_content {
width: 100%;
display: flex;
background-color: #fff;
padding: 20px 0;
// justify-content: space-between;
.KYandLS_prdSmalBox {
width: 224px;
margin-right: 15px;
border: 1px solid #efeff4;
}
.KYandLS_prdSmalBox:last-child {
margin-right: 0;
}
}
}
</style>
<script>
import BScroll from 'better-scroll';
const list = [];
export default {
name: 'template-3',
props: {
data: {
type: null,
default: false,
},
dataList: {
type: Array,
default: () => list,
},
goAllPage: {
type: Function,
},
goNewPage: {
type: Function,
},
},
computed: {
row() {
const length = this.dataList.length;
return Math.ceil(length / 3);
},
},
mounted() {
},
};
</script>
<template>
<div ref="wrapper" class="list-wrapper">
<div class="scroll-content">
<div ref="listWrapper">
<slot>
<ul class="list-content">
<li @click="clickItem($event,item)" class="list-item" v-for="(item, index) in data" :key="index">{{item}}</li>
</ul>
</slot>
</div>
<slot name="pullup"
:pullUpLoad="pullUpLoad"
:isPullUpLoad="isPullUpLoad">
<div class="pullup-wrapper" v-if="pullUpLoad">
<div class="before-trigger" v-if="!isPullUpLoad">
<span>{{pullUpTxt}}</span>
</div>
<div class="after-trigger" v-else>
<loading></loading>
</div>
</div>
</slot>
</div>
<slot name="pulldown"
:pullDownRefresh="pullDownRefresh"
:pullDownStyle="pullDownStyle"
:beforePullDown="beforePullDown"
:isPullingDown="isPullingDown"
:bubbleY="bubbleY">
<div ref="pulldown" class="pulldown-wrapper" :style="pullDownStyle" v-if="pullDownRefresh">
<div class="before-trigger" v-if="beforePullDown">
<bubble :y="bubbleY"></bubble>
</div>
<div class="after-trigger" v-else>
<div v-if="isPullingDown" class="loading">
<loading></loading>
</div>
<div v-else><span>{{refreshTxt}}</span></div>
</div>
</div>
</slot>
</div>
</template>
<script>
import BScroll from 'better-scroll';
import Loading from '@/components/loading';
import Bubble from '@/components/bubble';
const COMPONENT_NAME = 'scroll';
const DIRECTION_H = 'horizontal';
const DIRECTION_V = 'vertical';
export default {
name: COMPONENT_NAME,
props: {
data: {
type: Array,
default: function () {
return []
}
},
probeType: {
type: Number,
default: 3,
},
click: {
type: Boolean,
default: true
},
listenScroll: {
type: Boolean,
default: false
},
listenBeforeScroll: {
type: Boolean,
default: false
},
direction: {
type: String,
default: DIRECTION_V
},
scrollbar: {
type: null,
default: false
},
pullDownRefresh: {
type: null,
default: false
},
pullUpLoad: {
type: null,
default: false
},
startY: {
type: Number,
default: 0
},
refreshDelay: {
type: Number,
default: 20
},
freeScroll: {
type: Boolean,
default: false
},
mouseWheel: {
type: Boolean,
default: false
},
bounce: {
type: Boolean,
default: true,
}
},
data() {
return {
beforePullDown: true,
isRebounding: false,
isPullingDown: false,
isPullUpLoad: false,
pullUpDirty: true,
pullDownStyle: '',
bubbleY: 0
}
},
computed: {
pullUpTxt() {
const moreTxt = this.pullUpLoad && this.pullUpLoad.txt && this.pullUpLoad.txt.more || '加载更多';
const noMoreTxt = this.pullUpLoad && this.pullUpLoad.txt && this.pullUpLoad.txt.noMore || '没有更多数据';
return this.pullUpDirty ? moreTxt : noMoreTxt
},
refreshTxt() {
return this.pullDownRefresh && this.pullDownRefresh.txt || 'scrollComponent.defaultRefreshTxt';
}
},
created() {
this.pullDownInitTop = -50
},
mounted() {
setTimeout(() => {
this.initScroll()
}, 20)
},
methods: {
initScroll() {
if (!this.$refs.wrapper) {
return
}
if (this.$refs.listWrapper && (this.pullDownRefresh || this.pullUpLoad)) {
this.$refs.listWrapper.style.minHeight = `${getRect(this.$refs.wrapper).height + 1}px`
}
let options = {
probeType: this.probeType,
click: this.click,
scrollY: this.freeScroll || this.direction === DIRECTION_V,
scrollX: this.freeScroll || this.direction === DIRECTION_H,
scrollbar: this.scrollbar,
pullDownRefresh: this.pullDownRefresh,
pullUpLoad: this.pullUpLoad,
startY: this.startY,
freeScroll: this.freeScroll,
mouseWheel: this.mouseWheel,
bounce: this.bounce,
}
this.scroll = new BScroll(this.$refs.wrapper, options)
if (this.listenScroll) {
this.scroll.on('scroll', (pos) => {
this.$emit('scroll', pos)
})
}
if (this.listenBeforeScroll) {
this.scroll.on('beforeScrollStart', () => {
this.$emit('beforeScrollStart')
})
}
if (this.pullDownRefresh) {
this._initPullDownRefresh()
}
if (this.pullUpLoad) {
this._initPullUpLoad()
}
// 添加滚动结束后事件 需要点击两次的问题
this.scroll.on('scrollEnd', (x,y) => {
this.scroll.stop()
})
},
disable() {
this.scroll && this.scroll.disable()
},
enable() {
this.scroll && this.scroll.enable()
},
refresh() {
this.scroll && this.scroll.refresh()
},
scrollTo() {
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement() {
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
},
clickItem(e, item) {
console.log(e)
this.$emit('click', item)
},
destroy() {
this.scroll.destroy()
},
forceUpdate(dirty) {
if (this.pullDownRefresh && this.isPullingDown) {
this.isPullingDown = false
this._reboundPullDown().then(() => {
this._afterPullDown()
})
} else if (this.pullUpLoad && this.isPullUpLoad) {
this.isPullUpLoad = false
this.scroll.finishPullUp()
this.pullUpDirty = dirty
this.refresh()
} else {
this.refresh()
}
},
_initPullDownRefresh() {
this.scroll.on('pullingDown', () => {
this.beforePullDown = false
this.isPullingDown = true
this.$emit('pullingDown')
})
this.scroll.on('scroll', (pos) => {
if (this.beforePullDown) {
this.bubbleY = Math.max(0, pos.y + this.pullDownInitTop)
this.pullDownStyle = `top:${Math.min(pos.y + this.pullDownInitTop, 10)}px`
} else {
this.bubbleY = 0
}
if (this.isRebounding) {
this.pullDownStyle = `top:${10 - (this.pullDownRefresh.stop - pos.y)}px`
}
})
},
_initPullUpLoad() {
this.scroll.on('pullingUp', () => {
this.isPullUpLoad = true
this.$emit('pullingUp')
})
},
_reboundPullDown() {
const {stopTime = 600} = this.pullDownRefresh
return new Promise((resolve) => {
setTimeout(() => {
this.isRebounding = true
this.scroll.finishPullDown()
resolve()
}, stopTime)
})
},
_afterPullDown() {
setTimeout(() => {
this.pullDownStyle = `top:${this.pullDownInitTop}px`
this.beforePullDown = true
this.isRebounding = false
this.refresh()
}, this.scroll.options.bounceTime)
}
},
watch: {
data() {
setTimeout(() => {
this.forceUpdate(true)
}, this.refreshDelay)
}
},
components: {
Loading,
Bubble
}
}
function getRect(el) {
if (el instanceof window.SVGElement) {
let rect = el.getBoundingClientRect()
return {
top: rect.top,
left: rect.left,
width: rect.width,
height: rect.height
}
} else {
return {
top: el.offsetTop,
left: el.offsetLeft,
width: el.offsetWidth,
height: el.offsetHeight
}
}
}
</script>
<style lang="less">
.list-wrapper {
position: relative;
height: 100%;
width: 100%;
/*position: absolute*/
/*left: 0*/
/*top: 0*/
/*right: 0*/
/*bottom: 0*/
overflow: hidden;
background: #fff;
.scroll-content {
position: relative;
z-index: 1;
}
.list-content {
position: relative;
z-index: 10;
background: #fff;
.list-item {
height: 60px;
line-height: 60px;
font-size: 18px;
padding-left: 20px;
border-bottom: 1px solid #e5e5e5;
}
}
}
.pulldown-wrapper {
position: absolute;
width: 100%;
left: 0;
display: flex;
justify-content: center;
align-items: center;
transition: all;
.after-trigger {
margin-top: 10px;
}
}
.pullup-wrapper {
width: 100%;
height: 90px;
display: flex;
justify-content: center;
align-items: center;
padding: 16px 0;
}
</style>
\ No newline at end of file
<template>
<div class="slide" ref="slide">
<div class="slide-group" ref="slideGroup">
<slot>
</slot>
</div>
<div v-if="showDot" class="dots">
<span class="dot" :class="{active: currentPageIndex === index }" v-for="(item, index) in dots" :key="index"></span>
</div>
</div>
</template>
<script type="text/ecmascript-6">
import BScroll from 'better-scroll';
const COMPONENT_NAME = 'slide';
export default {
name: COMPONENT_NAME,
props: {
loop: {
type: Boolean,
default: false,
},
autoPlay: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 4000,
},
showDot: {
type: Boolean,
default: false,
},
click: {
type: Boolean,
default: true,
},
threshold: {
type: Number,
default: 0.3,
},
speed: {
type: Number,
default: 400,
},
change: {
type: Function,
},
index: {
type: Number,
default: 0,
},
},
data() {
return {
dots: [],
currentPageIndex: 0,
};
},
mounted() {
this.update();
window.addEventListener('resize', () => {
if (!this.slide || !this.slide.enabled) {
return;
}
clearTimeout(this.resizeTimer);
this.resizeTimer = setTimeout(() => {
if (this.slide.isInTransition) {
this._onScrollEnd();
} else {
if (this.autoPlay) {
this._play();
}
}
this.refresh();
}, 60);
});
},
activated() {
if (!this.slide) {
return;
}
this.slide.enable();
let pageIndex = this.slide.getCurrentPage().pageX;
this.slide.goToPage(pageIndex, 0, 0);
this.currentPageIndex = pageIndex;
if (this.autoPlay) {
this._play();
}
},
deactivated() {
this.slide && this.slide.disable();
clearTimeout(this.timer);
},
beforeDestroy() {
this.slide && this.slide.disable();
clearTimeout(this.timer);
},
methods: {
update() {
if (this.slide) {
this.slide.destroy();
}
this.$nextTick(() => {
this.init();
});
},
refresh() {
this._setSlideWidth(true);
this.slide.refresh();
},
prev() {
this.slide.prev();
},
next() {
this.slide.next();
},
init() {
// console.log(this.$slots.default);
// if (this.$refs.slideGroup.children.length > 1) {
clearTimeout(this.timer);
this.currentPageIndex = 0;
this._setSlideWidth();
if (this.showDot) {
this._initDots();
}
this._initSlide();
if (this.autoPlay) {
this._play();
}
// }
// this.$nextTick(() => {
// console.log(this.$refs.slideGroup.children.length);
// });
},
_setSlideWidth(isResize) {
this.children = this.$refs.slideGroup.children;
let width = 0;
let slideWidth = this.$refs.slide.clientWidth;
for (let i = 0; i < this.children.length; i++) {
let child = this.children[i];
addClass(child, 'slide-item');
child.style.width = slideWidth + 'px';
width += slideWidth;
}
if (this.loop && !isResize) {
width += 2 * slideWidth;
}
this.$refs.slideGroup.style.width = width + 'px';
},
_initSlide() {
// console.log(this.threshold)
this.slide = new BScroll(this.$refs.slide, {
scrollX: true,
scrollY: false,
momentum: false,
snap: {
loop: this.loop,
threshold: this.threshold,
speed: this.speed,
},
bounce: false,
click: this.click,
});
this.slide.on('scrollEnd', this._onScrollEnd);
this.slide.on('touchEnd', () => {
if (this.autoPlay) {
this._play();
}
});
this.slide.on('beforeScrollStart', () => {
if (this.autoPlay) {
clearTimeout(this.timer);
}
});
},
_onScrollEnd() {
let pageIndex = this.slide.getCurrentPage().pageX;
this.currentPageIndex = pageIndex;
if (this.change) {
this.change(pageIndex);
}
if (this.autoPlay) {
this._play();
}
},
_initDots() {
this.dots = new Array(this.children.length);
},
_play() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.slide.next();
}, this.interval);
},
},
watch: {
loop() {
this.update();
},
autoPlay() {
this.update();
},
speed() {
this.update();
},
threshold() {
this.update();
},
index(nVal) {
if (nVal == this.currentPageIndex) return;
this.$nextTick(() => {
this.slide.goToPage(nVal);
this.currentPageIndex = nVal;
});
},
},
};
function hasClass(el, className) {
let reg = new RegExp('(^|\\s)' + className + '(\\s|$)');
return reg.test(el.className);
}
function addClass(el, className) {
if (hasClass(el, className)) {
return;
}
let newClass = el.className.split(' ');
newClass.push(className);
el.className = newClass.join(' ');
}
</script>
<style lang="less">
.slide {
position: relative;
min-height: 1px;
height: 100%;
.slide-group {
position: relative;
overflow: hidden;
white-space: nowrap;
height: 100%;
.slide-item {
height: 100%;
float: left;
box-sizing: border-box;
overflow: hidden;
a {
display: block;
width: 100%;
overflow: hidden;
text-decoration: none;
}
img {
display: block;
width: 100%;
}
}
}
.dots {
position: absolute;
right: 0;
left: 0;
bottom: 34px;
transform: translateZ(1px);
text-align: center;
font-size: 0;
.dot {
display: inline-block;
margin: 0 7px;
width: 12px;
height: 12px;
border-radius: 50%;
border: 0.5px solid #ee7e1e;
&.active {
border: none;
background: #fff;
}
}
}
}
</style>
export default {
SERVER_URL: '', // DEV
TOKEN_KEY: 'Admin-Token',
};
import constants from './constants';
export const RYM_GZKEY = 'dc2017';
export const IMG_URL = 'http://qiniu.dcrym.com/';
export const CONSTANTS = constants;
export default {
...constants,
};
<template>
<div class="errPage-container">
<el-button @click="back" icon='arrow-left' class="pan-back-btn">返回</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">Oops!</h1>
gif来源<a href='https://zh.airbnb.com/' target='_blank'>airbnb</a> 页面
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li class="link-type">
<router-link to="/dashboard">回首页</router-link>
</li>
<li class="link-type"><a href="https://www.taobao.com/">随便看看</a></li>
<li><a @click.prevent="dialogVisible=true" href="#">点我看图</a></li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
<el-dialog title="随便看" :visible.sync="dialogVisible">
<img class="pan-img" :src="ewizardClap">
</el-dialog>
</div>
</template>
<script>
import errGif from '@/assets/images/401.gif';
export default {
name: 'page401',
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap:
'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false,
};
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' });
} else {
this.$router.go(-1);
}
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.errPage-container {
width: 800px;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
<template>
<div style="background:#f0f2f5;margin-top: -20px;">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" :src="img_404" alt="404">
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有<a class="link-type" href="https://wallstreetcn.com" target='_blank'>华尔街见闻</a></div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
import img_404 from '@/assets/images/404_images/404.png'
import img_404_cloud from '@/assets/images/404_images/404_cloud.png'
export default {
data() {
return {
img_404,
img_404_cloud
}
},
computed: {
message() {
return '特朗普说这个页面你不能进......'
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
<template>
<div class="tab-container">
<el-tag>mounted times :{{createdTimes}}</el-tag>
<el-tabs style='margin-top:15px;' v-model="activeName" type="border-card">
<el-tab-pane v-for="item in tabMapOptions" :label="item.label" :key='item.key' :name="item.key">
<keep-alive>
<!-- <tab-pane v-if='activeName==item.key' :type='item.key' @create='showCreatedTimes'></tab-pane> -->
</keep-alive>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
// import tabPane from './components/tabPane'
export default {
name: 'tab',
// components: { tabPane },
data() {
return {
tabMapOptions: [
{ label: 'China', key: 'CN' },
{ label: 'USA', key: 'US' },
{ label: 'Japan', key: 'JP' },
{ label: 'Eurozone', key: 'EU' },
],
activeName: 'CN',
createdTimes: 0,
};
},
methods: {
showCreatedTimes() {
this.createdTimes = this.createdTimes + 1;
},
},
};
</script>
<style scoped>
.tab-container {
margin: 30px;
}
</style>
<template>
<div class="tab-container">
homePage
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'tab',
data() {
return {
dialogVisible: false,
props: {
label: 'campusName',
value: 'id',
children: 'children',
},
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: true,
type: ['步步高'],
resource: '小天才',
desc: '',
},
index: 1,
options: [],
};
},
computed: {
campusSelectList() {
return this.campusList.map(item => ({
label: item.campusName,
value: item.id,
children: [],
}));
},
cabinetSelectList() {
return this.cabinetList.map(item => ({
label: item.cabinetLocation,
value: item.cabinetId,
children: [],
}));
},
latticeSelectList() {
return this.latticeList.map(item => ({
label: item.location,
value: item.id,
}));
},
},
methods: {
showDialog() {
console.log('show');
this.dialogVisible = true;
},
...mapActions('listData1', ['updatePagination']),
getChange(val) {
console.log(val);
},
handleChange(val) {
if (val.length === 1) {
let campusValue = val[0];
console.log(campusValue);
this.$store
.dispatch('fetchCabinetList', { campusId: campusValue })
.then(res => {
this.campusSelectList.find(
item => item.value == campusValue
).children = this.cabinetSelectList;
});
} else if (val.length === 2) {
let campusValue = val[0];
let cabinetValue = val[1];
this.$store
.dispatch('fetchLatticeList', {
campusId: campusValue,
cabinetId: cabinetValue,
})
.then(res => {
this.cabinetSelectList.find(
item => item.value == cabinetValue
).children = this.latticeSelectList;
});
}
},
onSubmit() {
this.$message.success('提交成功!');
},
add() {
this.index += 1;
this.$store.dispatch('listData1/updatePagination', {
pageNum: this.index,
});
// this.updatePagination({pageNum: this.index})
},
getCompus() {
this.$store.dispatch('fetchCampusList');
},
ret() {
this.$store.dispatch('resetLatticeCabinet');
},
},
};
</script>
<style>
.line {
text-align: center;
}
.tab-container {
margin: 30px;
}
.from-box {
width: 800px;
}
</style>
<template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<div class="topTitle">
<div class="com-logo">
<img src="../../assets/images/logo.png" alt="logo">
</div>
<h1 class="com-title">管理系统</h1>
<UserBox></UserBox>
</div>
<div>
<sidebar class="sidebar-container"></sidebar>
<div class="main-container">
<navbar></navbar>
<tags-view></tags-view>
<app-main></app-main>
</div>
</div>
</div>
</template>
<style lang="scss">
@import 'src/assets/styles/mixin.scss';
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
min-width: 1280px;
.topTitle {
display: flex;
align-items: center;
height: 70px;
background-color: #182b3e;
}
.com-logo {
width: 138px;
height: 44px;
margin-left: 42px;
}
.com-title {
flex: 1;
height: 24px;
line-height: 24px;
font-size: 14px;
font-weight: normal;
color: #fff;
border-left: 1px solid #fff;
margin-left: 67px;
text-indent: 20px;
}
}
</style>
<script>
import {
Navbar,
Sidebar,
AppMain,
TagsView,
UserBox,
} from '@/containers/layout/components';
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain,
TagsView,
UserBox,
},
computed: {
sidebar() {
return this.$store.state.app.sidebar;
},
},
};
</script>
<template>
<section class="app-main">
<transition name="fade" mode="out-in">
<!-- <router-view :key="key"></router-view> -->
<router-view></router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
// key() {
// return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
// }
}
}
</script>
<template>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<breadcrumb class="breadcrumb-container"></breadcrumb>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
export default {
components: {
Breadcrumb,
Hamburger
},
computed: {
...mapGetters([
'sidebar',
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
}
.screenfull {
position: absolute;
right: 90px;
top: 16px;
color: red;
}
}
</style>
<template>
<div class="menu-wrapper">
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<!-- <router-link v-if="item.children.length===1 && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link> -->
<el-submenu :index="item.name||item.path" :key="item.name">
<template slot="title">
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta&&item.meta.title">{{item.meta.title}}</span>
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
},
isNest: {
type: Boolean,
default: false
}
},
}
</script>
<template>
<scroll-bar>
<el-menu
mode="vertical"
:default-active="$route.path"
:collapse="isCollapse"
unique-opened
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF">
<sidebar-item :routes="permission_routers"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
<script>
import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem';
import ScrollBar from '@/components/ScrollBar';
export default {
components: { SidebarItem, ScrollBar },
computed: {
...mapGetters(['permission_routers', 'sidebar']),
isCollapse() {
return !this.sidebar.opened;
},
},
};
</script>
<template>
<div class="tags-view-container">
<scroll-pane class='tags-view-wrapper' ref='scrollPane'>
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path" :key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
{{tag.title}}
<span class='el-icon-close' @click.prevent.stop='closeSelectedTag(tag)'></span>
</router-link>
</scroll-pane>
<ul class='contextmenu' v-show="visible" :style="{left:left+'px',top:top+'px'}">
<li @click="closeSelectedTag(selectedTag)">关闭</li>
<li @click="closeOthersTags">关闭其余所有标签</li>
<li @click="closeAllTags">关闭所有标签</li>
</ul>
</div>
</template>
<script>
import ScrollPane from "@/components/ScrollPane";
export default {
components: { ScrollPane },
data() {
return {
visible: false,
top: 0,
left: 0,
selectedTag: {}
};
},
computed: {
visitedViews() {
return this.$store.state.app.tagsView.visitedViews;
}
},
watch: {
$route() {
this.addViewTags();
this.moveToCurrentTag();
},
visible(value) {
if (value) {
document.body.addEventListener("click", this.closeMenu);
} else {
document.body.removeEventListener("click", this.closeMenu);
}
}
},
mounted() {
this.addViewTags();
},
methods: {
generateRoute() {
if (this.$route.name) {
return this.$route;
}
return false;
},
isActive(route) {
return route.path === this.$route.path || route.name === this.$route.name;
},
addViewTags() {
const route = this.generateRoute();
if (!route) {
return false;
}
this.$store.dispatch("addVisitedViews", route);
},
moveToCurrentTag() {
const tags = this.$refs.tag;
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag.$el);
break;
}
}
});
},
closeSelectedTag(view) {
this.$store.dispatch("delVisitedViews", view).then(views => {
if (this.isActive(view)) {
const latestView = views.slice(-1)[0];
if (latestView) {
this.$router.push(latestView.path);
} else {
this.$router.push("/");
}
}
});
},
closeOthersTags() {
this.$router.push(this.selectedTag.path);
this.$store.dispatch("delOthersViews", this.selectedTag).then(() => {
this.moveToCurrentTag();
});
},
closeAllTags() {
this.$store.dispatch("delAllViews");
this.$router.push("/");
},
openMenu(tag, e) {
this.visible = true;
this.selectedTag = tag;
this.left = e.clientX;
this.top = e.clientY;
},
closeMenu() {
this.visible = false;
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.tags-view-container {
.tags-view-wrapper {
background: #fff;
height: 34px;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-item {
display: inline-block;
position: relative;
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
color: #495060;
background: #fff;
padding: 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
&:first-of-type {
margin-left: 15px;
}
&.active {
background-color: #42b983;
color: #fff;
border-color: #42b983;
&::before {
content: "";
background: #fff;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 2px;
}
}
}
}
.contextmenu {
margin: 0;
background: #fff;
z-index: 2;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}
</style>
<style rel="stylesheet/scss" lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
.tags-view-item {
.el-icon-close {
width: 16px;
height: 16px;
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(0.6);
display: inline-block;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
}
</style>
<template>
<div class="avatar-container">
<el-dropdown trigger="click">
<div class="avatar-wrapper">
<span>{{userInfo.name}}</span>
<!-- <img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'"> -->
<i class="el-icon-caret-bottom"></i>
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>首页</el-dropdown-item>
</router-link>
<el-dropdown-item divided>
<span @click="dialogVisible = true" style="display:block;">修改密码</span>
</el-dropdown-item>
<el-dropdown-item divided>
<span @click="logout" style="display:block;">登出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dialog title="修改密码" :visible.sync="dialogVisible" width="50%" :before-close="cancelPwdUpdate">
<el-form :model="pwd" :rules="rules" ref="pwdForm">
<el-form-item label="原密码" prop="oldPwd" :label-width="formLabelWidth">
<el-input type="password" v-model="pwd.oldPwd" placeholder="请输入原密码"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPwd" :label-width="formLabelWidth">
<el-input type="password" v-model="pwd.newPwd" placeholder="请输入新密码"></el-input>
</el-form-item>
<el-form-item label="重复新密码" prop="rePwd" :label-width="formLabelWidth">
<el-input type="password" v-model="pwd.rePwd" placeholder="请再输入新密码"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="resetPwdForm">取 消</el-button>
<el-button type="primary" @click="checkPwdForm">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
data() {
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.pwd.newPwd) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
dialogVisible: false,
formLabelWidth: '200px',
pwd: {
oldPwd: '',
newPwd: '',
rePwd: '',
},
rules: {
oldPwd: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
newPwd: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
rePwd: [
{ required: true, message: '请再输入新密码', trigger: 'blur' },
{ validator: validatePass },
],
},
};
},
computed: {
...mapGetters(['userInfo']),
},
methods: {
...mapActions(['updatePassword']),
cancelPwdUpdate(done) {
this.resetPwdForm();
done();
},
resetPwdForm() {
this.$refs.pwdForm.resetFields();
this.dialogVisible = false;
},
checkPwdForm() {
this.$refs.pwdForm.validate(valid => {
if (valid) {
this.updatePassword(this.pwd)
.then(() => {
this.$message.success('修改密码成功!');
this.resetPwdForm();
})
.catch(err => {
this.$message.error('修改密码失败!');
});
}
});
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload(); // 为了重新实例化vue-router对象 避免bug
});
},
},
};
</script>
<style lang="scss">
.avatar-container {
margin-right: 54px;
.avatar-wrapper {
cursor: pointer;
color: #a0acb9;
position: relative;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
position: absolute;
right: -18px;
top: 6px;
font-size: 12px;
}
}
}
</style>
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'
export { default as TagsView } from './TagsView'
export { default as UserBox } from './UserBox'
<template>
<div class="login-container">
<el-form autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left" label-width="0px" class="card-box login-form">
<h3 class="title">多彩校园综合管理系统</h3>
<el-form-item prop="username">
<span class="svg-container svg-container_login">
<svg-icon icon-class="user" />
</span>
<el-input name="username" type="text" v-model="loginForm.name" autoComplete="on" placeholder="username" />
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password"></svg-icon>
</span>
<el-input name="password" :type="pwdType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="on" placeholder="password"></el-input>
<span class="show-pwd" @click="showPwd">
<svg-icon icon-class="eye" />
</span>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width:100%;" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
</el-form-item>
<!-- <div class="tips">
<span style="margin-right:20px;">username: admin</span>
<span> password: admin</span>
</div> -->
</el-form>
</div>
</template>
<script>
import { isvalidUsername } from '@/utils/validate';
import { mapActions } from 'vuex';
export default {
name: 'login',
data() {
const validateUsername = (rule, value, callback) => {
if (!isvalidUsername(value)) {
callback(new Error('请输入正确的用户名'));
} else {
callback();
}
};
const validatePass = (rule, value, callback) => {
if (value.length == 0) {
callback(new Error('请输入密码'));
} else {
callback();
}
};
return {
loginForm: {
name: '',
password: '',
},
loginRules: {
name: [
{ required: true, trigger: 'blur', validator: validateUsername },
],
password: [
{ required: true, trigger: 'blur', validator: validatePass },
],
},
loading: false,
pwdType: 'password',
};
},
methods: {
...mapActions(['Login']),
showPwd() {
if (this.pwdType === 'password') {
this.pwdType = '';
} else {
this.pwdType = 'password';
}
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
this.Login(this.loginForm)
.then(() => {
console.log('login done');
this.loading = false;
this.$router.push({ path: '/' });
})
.catch(() => {
this.loading = false;
});
} else {
console.log('error submit!!');
return false;
}
});
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss">
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
.login-container {
position: fixed;
height: 100%;
width: 100%;
background-color: $bg;
input:-webkit-autofill {
box-shadow: 0 0 0px 1000px #293444 inset !important;
-webkit-text-fill-color: #fff !important;
}
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
}
.el-input {
display: inline-block;
height: 47px;
width: 85%;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 30px;
display: inline-block;
&_login {
font-size: 20px;
}
}
.title {
font-size: 26px;
font-weight: 400;
color: $light_gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
.login-form {
position: absolute;
left: 0;
right: 0;
width: 400px;
padding: 35px 35px 15px 35px;
margin: 120px auto;
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
.thirdparty-button {
position: absolute;
right: 35px;
bottom: 28px;
}
}
</style>
import Vue from 'vue';
import 'normalize.css/normalize.css';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App';
import router from './router';
import store from './store';
import '@/assets/styles/index.scss'; // global css
import '@/assets/icons'; // icon
import '@/permission' // 用户登录认证
// import './mock' // mockjs
import extendCom from '@/utils/extends';
Vue.use(ElementUI);
extendCom(Vue);
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>',
});
import router from './router';
import store from './store';
import NProgress from 'nprogress'; // Progress 进度条
import 'nprogress/nprogress.css'; // Progress 进度条样式
import { getToken } from '@/utils/auth'; // 验权
NProgress.configure({ showSpinner: false }); // NProgress Configuration
const whiteList = ['/login']; // 不重定向白名单
router.beforeEach((to, from, next) => {
NProgress.start(); // 进度条Progress
if (getToken()) {
if (to.path === '/login') {
next({ path: '/' });
NProgress.done();
} else {
if (store.getters.isRouteDone) {
next(); //
} else {
// if(store.getters.userInfo.loginState)
store
.dispatch('GenerateRoutes')
.then(() => {
router.addRoutes(store.getters.addRouters.slice()); // 动态添加可访问路由表
next({ ...to, replace: true });
})
.catch((err) => {
console.log('验证失败,请重新登录')
console.log(err)
store.dispatch('FedLogOut').then(() => {
// Message.error('验证失败,请重新登录');
next({ path: '/login' });
});
});
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
// next('/login');
next();
NProgress.done();
}
}
});
router.afterEach(() => {
NProgress.done(); // 结束Progress
});
module.exports = file => require('@/containers/' + file + '.vue').default;
module.exports = file => () => import('@/containers/' + file + '.vue');
import Vue from 'vue';
import Router from 'vue-router';
import Layout from '../containers/layout/Layout.vue';
import Login from '@/containers/login/index';
import errorPage404 from '@/containers/errorPage/404';
import errorPage401 from '@/containers/errorPage/401';
const _import = require('./_import_' + process.env.NODE_ENV);
Vue.use(Router);
export const constantRouterMap = [
{ path: '/login', component: Login, hidden: true },
{ path: '/404', component: errorPage404, hidden: true },
{ path: '/401', component: errorPage401, hidden: true },
{
path: '',
component: Layout,
name: 'home',
redirect: 'home',
meta: { title: '首页', icon: 'example' },
children: [
{
path: 'home',
name: 'from',
component: _import('home/index'),
meta: { title: '首页' },
},
],
},
];
export const asyncRouterMap = [
{
component: Layout,
meta: { icon: 'example' },
},
{ path: '*', redirect: '/404', hidden: true },
];
export default new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap,
});
import Vue from 'vue';
import Vuex from 'vuex';
import createLogger from 'vuex/dist/logger';
import app from './modules/app/index';
import user from './modules/user/index';
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production';
const store = new Vuex.Store({
modules: {
app,
user,
},
strict: debug,
plugins: debug ? [createLogger()] : [],
});
export default store;
import tagsView from './tagsView';
import sidebar from './sidebar';
import { FETCH_START, FETCH_DONE } from './mutation-types';
const state = () => ({
loading: true,
});
const getters = {
loading: state => state.loading,
};
const actions = {
fetchStart({ commit }) {
commit(FETCH_START);
},
fetchDone({ commit }) {
commit(FETCH_DONE);
},
};
const mutations = {
[FETCH_START](state) {
state.loading = true;
},
[FETCH_DONE](state) {
state.loading = false;
},
};
export default {
modules: {
tagsView,
sidebar,
},
state,
getters,
actions,
mutations,
};
export const FETCH_START = 'FETCH_START';
export const FETCH_DONE = 'FETCH_DONE';
export const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR';
export const ADD_VISITED_VIEWS = 'ADD_VISITED_VIEWS';
export const DEL_VISITED_VIEWS = 'DEL_VISITED_VIEWS';
export const DEL_OTHERS_VIEWS = 'DEL_OTHERS_VIEWS';
export const DEL_ALL_VIEWS = 'DEL_ALL_VIEWS';
import Cookies from 'js-cookie';
import { TOGGLE_SIDEBAR } from './mutation-types';
const state = () => ({
opened: !+Cookies.get('sidebarStatus'),
});
const getters = {
sidebar: state => state,
};
const actions = {
ToggleSideBar: ({ commit }) => {
commit(TOGGLE_SIDEBAR);
},
};
const mutations = {
[TOGGLE_SIDEBAR]: state => {
if (state.opened) {
Cookies.set('sidebarStatus', 1);
} else {
Cookies.set('sidebarStatus', 0);
}
state.opened = !state.opened;
},
};
export default {
state,
getters,
actions,
mutations,
};
import {
ADD_VISITED_VIEWS,
DEL_VISITED_VIEWS,
DEL_OTHERS_VIEWS,
DEL_ALL_VIEWS,
} from './mutation-types';
const state = {
visitedViews: [],
cachedViews: [],
};
const getters = {
visitedViews: state => state.visitedViews,
cachedViews: state => state.cachedViews,
};
const actions = {
addVisitedViews({ commit }, view) {
commit(ADD_VISITED_VIEWS, view);
},
delVisitedViews({ commit, state }, view) {
return new Promise(resolve => {
commit(DEL_VISITED_VIEWS, view);
resolve([...state.visitedViews]);
});
},
delOthersViews({ commit, state }, view) {
return new Promise(resolve => {
commit(DEL_OTHERS_VIEWS, view);
resolve([...state.visitedViews]);
});
},
delAllViews({ commit, state }) {
return new Promise(resolve => {
commit(DEL_ALL_VIEWS);
resolve([...state.visitedViews]);
});
},
};
const mutations = {
[ADD_VISITED_VIEWS]: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return;
state.visitedViews.push({
name: view.name,
path: view.path,
title: view.meta.title || 'no-name',
});
if (!view.meta.noCache) {
state.cachedViews.push(view.name);
}
},
[DEL_VISITED_VIEWS]: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1);
break;
}
}
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i);
state.cachedViews.splice(index, 1);
break;
}
}
},
[DEL_OTHERS_VIEWS]: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews = state.visitedViews.slice(i, i + 1);
break;
}
}
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i);
state.cachedViews = state.cachedViews.slice(index, i + 1);
break;
}
}
},
[DEL_ALL_VIEWS]: state => {
state.visitedViews = [];
state.cachedViews = [];
},
};
export default {
state,
getters,
actions,
mutations,
};
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