├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .postcssrc.js
├── LICENSE
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── favicon.ico
├── gifs
├── 2login.gif
├── dynamictable.gif
├── echarts.gif
├── editor.gif
├── errorlog.gif
├── excel.png
├── leftmenu.gif
├── login.png
├── order.gif
├── table.gif
├── tabs.gif
├── theme.gif
├── upload1.gif
└── uploadAvatar.gif
├── index.html
├── package-lock.json
├── package.json
├── src
├── App.vue
├── api
│ ├── article.js
│ ├── login.js
│ ├── qiniu.js
│ ├── remoteSearch.js
│ └── transaction.js
├── assets
│ ├── 401_images
│ │ └── 401.gif
│ ├── 404_images
│ │ ├── 404.png
│ │ └── 404_cloud.png
│ ├── custom-theme
│ │ ├── fonts
│ │ │ ├── element-icons.ttf
│ │ │ └── element-icons.woff
│ │ └── index.css
│ └── echarts-macarons.js
├── components
│ ├── BackToTop
│ │ └── index.vue
│ ├── Breadcrumb
│ │ └── index.vue
│ ├── Charts
│ │ ├── keyboard.vue
│ │ ├── lineMarker.vue
│ │ └── mixChart.vue
│ ├── DndList
│ │ └── index.vue
│ ├── Dropzone
│ │ └── index.vue
│ ├── Echarts
│ │ └── Echarts.vue
│ ├── ErrorLog
│ │ └── index.vue
│ ├── GithubCorner
│ │ └── index.vue
│ ├── Hamburger
│ │ └── index.vue
│ ├── ImageCropper
│ │ ├── index.vue
│ │ ├── lang.js
│ │ ├── upload.css
│ │ └── utils.js
│ ├── JsonEditor
│ │ └── index.vue
│ ├── LangSelect
│ │ └── index.vue
│ ├── MDinput
│ │ └── index.vue
│ ├── MarkdownEditor
│ │ └── index.vue
│ ├── PanThumb
│ │ └── index.vue
│ ├── Screenfull
│ │ └── index.vue
│ ├── ScrollBar
│ │ └── index.vue
│ ├── ScrollPane
│ │ └── index.vue
│ ├── Share
│ │ └── dropdownMenu.vue
│ ├── Sticky
│ │ └── index.vue
│ ├── SvgIcon
│ │ └── index.vue
│ ├── TextHoverEffect
│ │ └── Mallki.vue
│ ├── ThemePicker
│ │ └── index.vue
│ ├── Tinymce
│ │ ├── components
│ │ │ └── editorImage.vue
│ │ └── index.vue
│ ├── Upload
│ │ ├── singleImage.vue
│ │ ├── singleImage2.vue
│ │ └── singleImage3.vue
│ └── UploadExcel
│ │ └── index.vue
├── directive
│ ├── clipboard
│ │ ├── clipboard.js
│ │ └── index.js
│ ├── sticky.js
│ └── waves
│ │ ├── index.js
│ │ ├── waves.css
│ │ └── waves.js
├── errorLog.js
├── filters
│ └── index.js
├── icons
│ ├── index.js
│ └── svg
│ │ ├── 404.svg
│ │ ├── bug.svg
│ │ ├── chart.svg
│ │ ├── clipboard.svg
│ │ ├── component.svg
│ │ ├── dashboard.svg
│ │ ├── documentation.svg
│ │ ├── drag.svg
│ │ ├── email.svg
│ │ ├── example.svg
│ │ ├── excel.svg
│ │ ├── eye.svg
│ │ ├── form.svg
│ │ ├── icon.svg
│ │ ├── international.svg
│ │ ├── language.svg
│ │ ├── lock.svg
│ │ ├── message.svg
│ │ ├── money.svg
│ │ ├── password.svg
│ │ ├── people.svg
│ │ ├── peoples.svg
│ │ ├── qq.svg
│ │ ├── shoppingCard.svg
│ │ ├── star.svg
│ │ ├── tab.svg
│ │ ├── table.svg
│ │ ├── theme.svg
│ │ ├── user.svg
│ │ ├── wechat.svg
│ │ └── zip.svg
├── lang
│ ├── en.js
│ ├── index.js
│ └── zh.js
├── main.js
├── mock
│ ├── article.js
│ ├── index.js
│ ├── login.js
│ ├── remoteSearch.js
│ └── transaction.js
├── permission.js
├── router
│ ├── _import_development.js
│ ├── _import_production.js
│ ├── index.js
│ └── router.js
├── store
│ ├── getters.js
│ ├── index.js
│ └── modules
│ │ ├── app.js
│ │ ├── errorLog.js
│ │ ├── permission.js
│ │ ├── tagsView.js
│ │ └── user.js
├── stores
│ └── index.js
├── styles
│ ├── btn.scss
│ ├── color-definition.scss
│ ├── common.scss
│ ├── element-ui.scss
│ ├── index.scss
│ ├── mixin.scss
│ ├── paddingMargin.scss
│ ├── sidebar.scss
│ ├── transition.scss
│ └── variables.scss
├── utils
│ ├── auth.js
│ ├── clipboard.js
│ ├── createUniqueString.js
│ ├── i18n.js
│ ├── index.js
│ ├── openWindow.js
│ ├── request.js
│ └── validate.js
├── vendor
│ ├── Blob.js
│ ├── Export2Excel.js
│ └── Export2Zip.js
└── views
│ ├── charts
│ ├── keyboard.vue
│ ├── line.vue
│ └── mixChart.vue
│ ├── clipboard
│ └── index.vue
│ ├── components-demo
│ ├── avatarUpload.vue
│ ├── backToTop.vue
│ ├── countTo.vue
│ ├── dndList.vue
│ ├── dropzone.vue
│ ├── jsonEditor.vue
│ ├── markdown.vue
│ ├── mixin.vue
│ ├── splitpane.vue
│ ├── sticky.vue
│ └── tinymce.vue
│ ├── dashboard
│ ├── admin
│ │ ├── components
│ │ │ ├── BarChart.vue
│ │ │ ├── BoxCard.vue
│ │ │ ├── LineChart.vue
│ │ │ ├── PanelGroup.vue
│ │ │ ├── PieChart.vue
│ │ │ ├── RaddarChart.vue
│ │ │ ├── TodoList
│ │ │ │ ├── Todo.vue
│ │ │ │ ├── index.scss
│ │ │ │ └── index.vue
│ │ │ └── TransactionTable.vue
│ │ └── index.vue
│ ├── editor
│ │ └── index.vue
│ └── index.vue
│ ├── documentation
│ └── index.vue
│ ├── errorLog
│ ├── errorTestA.vue
│ ├── errorTestB.vue
│ └── index.vue
│ ├── errorPage
│ ├── 401.vue
│ └── 404.vue
│ ├── example
│ ├── tab
│ │ ├── components
│ │ │ └── tabPane.vue
│ │ └── index.vue
│ └── table
│ │ ├── complexTable.vue
│ │ ├── dragTable.vue
│ │ ├── dynamicTable
│ │ ├── fixedThead.vue
│ │ ├── index.vue
│ │ └── unfixedThead.vue
│ │ ├── index.vue
│ │ └── inlineEditTable.vue
│ ├── excel
│ ├── exportExcel.vue
│ ├── selectExcel.vue
│ └── uploadExcel.vue
│ ├── form
│ ├── components
│ │ └── ArticleDetail.vue
│ ├── create.vue
│ └── edit.vue
│ ├── i18n-demo
│ ├── index.vue
│ └── local.js
│ ├── layout
│ ├── Layout.vue
│ └── components
│ │ ├── AppMain.vue
│ │ ├── Navbar.vue
│ │ ├── Sidebar
│ │ ├── SidebarItem.vue
│ │ └── index.vue
│ │ ├── TagsView.vue
│ │ └── index.js
│ ├── login
│ ├── Login.vue
│ ├── authredirect.vue
│ ├── index.vue
│ └── socialsignin.vue
│ ├── main
│ ├── Main.vue
│ ├── charts
│ │ └── Charts.vue
│ └── upload
│ │ └── UpLoad.vue
│ ├── permission
│ └── index.vue
│ ├── qiniu
│ └── upload.vue
│ ├── register
│ └── register.vue
│ ├── svg-icons
│ ├── generateIconsView.js
│ └── index.vue
│ ├── theme
│ └── index.vue
│ └── zip
│ └── index.vue
├── static
└── .gitkeep
└── test
├── e2e
├── custom-assertions
│ └── elementCount.js
├── nightwatch.conf.js
├── runner.js
└── specs
│ └── test.js
└── unit
├── .eslintrc
├── jest.conf.js
├── setup.js
└── specs
└── HelloWorld.spec.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime",["component", [
12 | {
13 | "libraryName": "element-ui",
14 | "styleLibraryName": "theme-chalk"
15 | }
16 | ]]],
17 | "env": {
18 | "test": {
19 | "presets": ["env", "stage-2"],
20 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /test/unit/coverage/
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | rules: {
20 | // allow async-await
21 | 'generator-star-spacing': 'off',
22 | // allow debugger during development
23 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | test/unit/coverage
8 | test/e2e/reports
9 | selenium-debug.log
10 |
11 | # Editor directories and files
12 | .idea
13 | .vscode
14 | *.suo
15 | *.ntvs*
16 | *.njsproj
17 | *.sln
18 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | chromedriver_cdnurl=https://npm.taobao.org/mirrors/chromedriver
2 | phantomjs_cdnurl=http://npm.taobao.org/mirrors/phantomjs
3 | operadriver_cdnurl=http://npm.taobao.org/mirrors/operadriver
4 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present PanJiaChen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # manage-platform
2 | > 基于vue、element-ui、vue-router、axios、echarts的管理系统
3 |
4 | [线上地址] https://github.com/liwudi/manage-platform.git
5 |
6 | ## 相关demo
7 |
8 | > https://github.com/liwudi/Vue.git
9 |
10 | ## Build Setup
11 |
12 | ### 1、Clone project
13 | git clone https://github.com/liwudi/manage-platform.git
14 |
15 | ### 2、Install dependencies
16 | npm install
17 |
18 | ### 3、建议不要用cnpm 安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
19 | npm install --registry=https://registry.npm.taobao.org
20 |
21 | ### 4、serve with hot reload at localhost:9528
22 | npm run dev
23 |
24 | ### 5、build for production with minification
25 | npm run build
26 |
27 | ### 6、build for production and view the bundle analyzer report
28 | npm run build --report
29 |
30 | ## 相关文档
31 |
32 | ### vue-router文档
33 | https://router.vuejs.org/zh-cn/
34 |
35 | ### ElementUi
36 | http://element-cn.eleme.io/#/zh-CN
37 |
38 | ### axios中文使用文档
39 | https://www.kancloud.cn/yunye/axios/234845
40 |
41 | ### moment中文文档
42 | http://momentjs.cn/docs/
43 |
44 | 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).
45 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/build/logo.png
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | const createLintingRule = () => ({
12 | test: /\.(js|vue)$/,
13 | loader: 'eslint-loader',
14 | enforce: 'pre',
15 | include: [resolve('src'), resolve('test')],
16 | options: {
17 | formatter: require('eslint-friendly-formatter'),
18 | emitWarning: !config.dev.showEslintErrorsInOverlay
19 | }
20 | })
21 |
22 | module.exports = {
23 | context: path.resolve(__dirname, '../'),
24 | entry: {
25 | app: './src/main.js'
26 | },
27 | output: {
28 | path: config.build.assetsRoot,
29 | filename: '[name].js',
30 | publicPath: process.env.NODE_ENV === 'production'
31 | ? config.build.assetsPublicPath
32 | : config.dev.assetsPublicPath
33 | },
34 | resolve: {
35 | extensions: ['.js', '.vue', '.json'],
36 | alias: {
37 | 'vue$': 'vue/dist/vue.esm.js',
38 | '@': resolve('src'),
39 | }
40 | },
41 | module: {
42 | rules: [
43 | ...(config.dev.useEslint ? [createLintingRule()] : []),
44 | {
45 | test: /\.vue$/,
46 | loader: 'vue-loader',
47 | options: vueLoaderConfig
48 | },
49 | {
50 | test: /\.js$/,
51 | loader: 'babel-loader',
52 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
53 | },
54 | {
55 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
56 | loader: 'url-loader',
57 | options: {
58 | limit: 10000,
59 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
60 | }
61 | },
62 | {
63 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
64 | loader: 'url-loader',
65 | options: {
66 | limit: 10000,
67 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
68 | }
69 | },
70 | {
71 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
72 | loader: 'url-loader',
73 | options: {
74 | limit: 10000,
75 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
76 | }
77 | }
78 | ]
79 | },
80 | node: {
81 | // prevent webpack from injecting useless setImmediate polyfill because Vue
82 | // source contains it (although only uses it if it's native).
83 | setImmediate: false,
84 | // prevent webpack from injecting mocks to Node native modules
85 | // that does not make sense for the client
86 | dgram: 'empty',
87 | fs: 'empty',
88 | net: 'empty',
89 | tls: 'empty',
90 | child_process: 'empty'
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.2.8
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: false,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'cheap-module-eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | cssSourceMap: true,
44 | },
45 |
46 | build: {
47 | // Template for index.html
48 | index: path.resolve(__dirname, '../dist/index.html'),
49 |
50 | // Paths
51 | assetsRoot: path.resolve(__dirname, '../dist'),
52 | assetsSubDirectory: 'static',
53 | assetsPublicPath: '/',
54 |
55 | /**
56 | * Source Maps
57 | */
58 |
59 | productionSourceMap: true,
60 | // https://webpack.js.org/configuration/devtool/#production
61 | devtool: '#source-map',
62 |
63 | // Gzip off by default as many popular static hosts such as
64 | // Surge or Netlify already gzip all static assets for you.
65 | // Before setting to `true`, make sure to:
66 | // npm install --save-dev compression-webpack-plugin
67 | productionGzip: false,
68 | productionGzipExtensions: ['js', 'css'],
69 |
70 | // Run the build command with an extra argument to
71 | // View the bundle analyzer report after build finishes:
72 | // `npm run build --report`
73 | // Set to `true` or `false` to always turn it on or off
74 | bundleAnalyzerReport: process.env.npm_config_report
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const devEnv = require('./dev.env')
4 |
5 | module.exports = merge(devEnv, {
6 | NODE_ENV: '"testing"'
7 | })
8 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/favicon.ico
--------------------------------------------------------------------------------
/gifs/2login.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/2login.gif
--------------------------------------------------------------------------------
/gifs/dynamictable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/dynamictable.gif
--------------------------------------------------------------------------------
/gifs/echarts.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/echarts.gif
--------------------------------------------------------------------------------
/gifs/editor.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/editor.gif
--------------------------------------------------------------------------------
/gifs/errorlog.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/errorlog.gif
--------------------------------------------------------------------------------
/gifs/excel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/excel.png
--------------------------------------------------------------------------------
/gifs/leftmenu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/leftmenu.gif
--------------------------------------------------------------------------------
/gifs/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/login.png
--------------------------------------------------------------------------------
/gifs/order.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/order.gif
--------------------------------------------------------------------------------
/gifs/table.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/table.gif
--------------------------------------------------------------------------------
/gifs/tabs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/tabs.gif
--------------------------------------------------------------------------------
/gifs/theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/theme.gif
--------------------------------------------------------------------------------
/gifs/upload1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/upload1.gif
--------------------------------------------------------------------------------
/gifs/uploadAvatar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/gifs/uploadAvatar.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | manage-platform
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
24 |
--------------------------------------------------------------------------------
/src/api/article.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function fetchList(query) {
4 | return request({
5 | url: '/article/list',
6 | method: 'get',
7 | params: query
8 | })
9 | }
10 |
11 | export function fetchArticle() {
12 | return request({
13 | url: '/article/detail',
14 | method: 'get'
15 | })
16 | }
17 |
18 | export function fetchPv(pv) {
19 | return request({
20 | url: '/article/pv',
21 | method: 'get',
22 | params: { pv }
23 | })
24 | }
25 |
26 | export function createArticle(data) {
27 | return request({
28 | url: '/article/create',
29 | method: 'post',
30 | data
31 | })
32 | }
33 |
34 | export function updateArticle(data) {
35 | return request({
36 | url: '/article/update',
37 | method: 'post',
38 | data
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function loginByUsername(username, password) {
4 | const data = {
5 | username,
6 | password
7 | }
8 | return request({
9 | url: '/login/login',
10 | method: 'post',
11 | data
12 | })
13 | }
14 |
15 | export function logout() {
16 | return request({
17 | url: '/login/logout',
18 | method: 'post'
19 | })
20 | }
21 |
22 | export function getUserInfo(token) {
23 | return request({
24 | url: '/user/info',
25 | method: 'get',
26 | params: { token }
27 | })
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/api/qiniu.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getToken() {
4 | return request({
5 | url: '/qiniu/upload/token', // 假地址 自行替换
6 | method: 'get'
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/src/api/remoteSearch.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function userSearch(name) {
4 | return request({
5 | url: '/search/user',
6 | method: 'get',
7 | params: { name }
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/api/transaction.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function fetchList(query) {
4 | return request({
5 | url: '/transaction/list',
6 | method: 'get',
7 | params: query
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/assets/401_images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/src/assets/401_images/401.gif
--------------------------------------------------------------------------------
/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/src/assets/custom-theme/fonts/element-icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/src/assets/custom-theme/fonts/element-icons.ttf
--------------------------------------------------------------------------------
/src/assets/custom-theme/fonts/element-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/src/assets/custom-theme/fonts/element-icons.woff
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{generateTitle(item.meta.title)}}
6 | {{generateTitle(item.meta.title)}}
7 |
8 |
9 |
10 |
11 |
12 |
42 |
43 |
55 |
--------------------------------------------------------------------------------
/src/components/Echarts/Echarts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
26 |
--------------------------------------------------------------------------------
/src/components/GithubCorner/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
43 |
--------------------------------------------------------------------------------
/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
30 |
31 |
46 |
--------------------------------------------------------------------------------
/src/components/ImageCropper/lang.js:
--------------------------------------------------------------------------------
1 | const langBag = {
2 | zh: {
3 | hint: '点击,或拖动图片至此处',
4 | loading: '正在上传……',
5 | noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!',
6 | success: '上传成功',
7 | fail: '图片上传失败',
8 | preview: '头像预览',
9 | btn: {
10 | off: '取消',
11 | close: '关闭',
12 | back: '上一步',
13 | save: '保存'
14 | },
15 | error: {
16 | onlyImg: '仅限图片格式',
17 | outOfSize: '单文件大小不能超过 ',
18 | lowestPx: '图片最低像素为(宽*高):'
19 | }
20 | },
21 | en: {
22 | hint: 'Click, or drag the file here',
23 | loading: 'Uploading……',
24 | noSupported: 'Browser does not support, please use IE10+ or other browsers',
25 | success: 'Upload success',
26 | fail: 'Upload failed',
27 | preview: 'Preview',
28 | btn: {
29 | off: 'Cancel',
30 | close: 'Close',
31 | back: 'Back',
32 | save: 'Save'
33 | },
34 | error: {
35 | onlyImg: 'Image only',
36 | outOfSize: 'Image exceeds size limit: ',
37 | lowestPx: 'The lowest pixel in the image: '
38 | }
39 | }
40 | }
41 | export default langBag
42 |
--------------------------------------------------------------------------------
/src/components/ImageCropper/utils.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | /**
4 | *
5 | * @param e
6 | * @param arg_opts
7 | * @returns {boolean}
8 | */
9 | export function effectRipple(e, arg_opts) {
10 | let opts = Object.assign({
11 | ele: e.target, // 波纹作用元素
12 | type: 'hit', // hit点击位置扩散 center中心点扩展
13 | bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
14 | }, arg_opts),
15 | target = opts.ele;
16 | if (target) {
17 | let rect = target.getBoundingClientRect(),
18 | ripple = target.querySelector('.e-ripple');
19 | if (!ripple) {
20 | ripple = document.createElement('span');
21 | ripple.className = 'e-ripple';
22 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
23 | target.appendChild(ripple);
24 | } else {
25 | ripple.className = 'e-ripple';
26 | }
27 | switch (opts.type) {
28 | case 'center':
29 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
30 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
31 | break;
32 | default:
33 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
34 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
35 | }
36 | ripple.style.backgroundColor = opts.bgc;
37 | ripple.className = 'e-ripple z-active';
38 | return false;
39 | }
40 | }
41 | // database64文件格式转换为2进制
42 | /**
43 | *
44 | * @param data
45 | * @param mime
46 | * @returns {*}
47 | */
48 | export function data2blob(data, mime) {
49 | // dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
50 | data = data.split(',')[1];
51 | data = window.atob(data);
52 | var ia = new Uint8Array(data.length);
53 | for (var i = 0; i < data.length; i++) {
54 | ia[i] = data.charCodeAt(i);
55 | }
56 | // canvas.toDataURL 返回的默认格式就是 image/png
57 | return new Blob([ia], {type: mime});
58 | };
59 |
--------------------------------------------------------------------------------
/src/components/JsonEditor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
55 |
56 |
64 |
--------------------------------------------------------------------------------
/src/components/LangSelect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 中文
8 | English
9 |
10 |
11 |
12 |
13 |
32 |
33 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/MarkdownEditor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
80 |
81 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/src/components/Screenfull/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
55 |
56 |
66 |
--------------------------------------------------------------------------------
/src/components/ScrollBar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
43 |
44 |
58 |
--------------------------------------------------------------------------------
/src/components/ScrollPane/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
61 |
62 |
73 |
--------------------------------------------------------------------------------
/src/components/Share/dropdownMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
36 |
37 |
98 |
--------------------------------------------------------------------------------
/src/components/Sticky/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
77 |
--------------------------------------------------------------------------------
/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
33 |
34 |
43 |
--------------------------------------------------------------------------------
/src/components/TextHoverEffect/Mallki.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{text}}
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
24 |
114 |
--------------------------------------------------------------------------------
/src/directive/clipboard/clipboard.js:
--------------------------------------------------------------------------------
1 | // Inspired by https://github.com/Inndy/vue-clipboard2
2 | const Clipboard = require('clipboard')
3 | if (!Clipboard) {
4 | throw new Error('you shold npm install `clipboard` --save at first ')
5 | }
6 |
7 | export default {
8 | bind(el, binding) {
9 | if (binding.arg === 'success') {
10 | el._v_clipboard_success = binding.value
11 | } else if (binding.arg === 'error') {
12 | el._v_clipboard_error = binding.value
13 | } else {
14 | const clipboard = new Clipboard(el, {
15 | text() { return binding.value },
16 | action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
17 | })
18 | clipboard.on('success', e => {
19 | const callback = el._v_clipboard_success
20 | callback && callback(e) // eslint-disable-line
21 | })
22 | clipboard.on('error', e => {
23 | const callback = el._v_clipboard_error
24 | callback && callback(e) // eslint-disable-line
25 | })
26 | el._v_clipboard = clipboard
27 | }
28 | },
29 | update(el, binding) {
30 | if (binding.arg === 'success') {
31 | el._v_clipboard_success = binding.value
32 | } else if (binding.arg === 'error') {
33 | el._v_clipboard_error = binding.value
34 | } else {
35 | el._v_clipboard.text = function() { return binding.value }
36 | el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
37 | }
38 | },
39 | unbind(el, binding) {
40 | if (binding.arg === 'success') {
41 | delete el._v_clipboard_success
42 | } else if (binding.arg === 'error') {
43 | delete el._v_clipboard_error
44 | } else {
45 | el._v_clipboard.destroy()
46 | delete el._v_clipboard
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/directive/clipboard/index.js:
--------------------------------------------------------------------------------
1 | import Clipboard from './clipboard'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('Clipboard', Clipboard)
5 | }
6 |
7 | if (window.Vue) {
8 | window.clipboard = Clipboard
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | Clipboard.install = install
13 | export default Clipboard
14 |
--------------------------------------------------------------------------------
/src/directive/sticky.js:
--------------------------------------------------------------------------------
1 | const vueSticky = {}
2 | let listenAction
3 | vueSticky.install = Vue => {
4 | Vue.directive('sticky', {
5 | inserted(el, binding) {
6 | const params = binding.value || {}
7 | const stickyTop = params.stickyTop || 0
8 | const zIndex = params.zIndex || 1000
9 | const elStyle = el.style
10 |
11 | elStyle.position = '-webkit-sticky'
12 | elStyle.position = 'sticky'
13 | // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
14 | // if (~elStyle.position.indexOf('sticky')) {
15 | // elStyle.top = `${stickyTop}px`;
16 | // elStyle.zIndex = zIndex;
17 | // return
18 | // }
19 | const elHeight = el.getBoundingClientRect().height
20 | const elWidth = el.getBoundingClientRect().width
21 | elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
22 |
23 | const parentElm = el.parentNode || document.documentElement
24 | const placeholder = document.createElement('div')
25 | placeholder.style.display = 'none'
26 | placeholder.style.width = `${elWidth}px`
27 | placeholder.style.height = `${elHeight}px`
28 | parentElm.insertBefore(placeholder, el)
29 |
30 | let active = false
31 |
32 | const getScroll = (target, top) => {
33 | const prop = top ? 'pageYOffset' : 'pageXOffset'
34 | const method = top ? 'scrollTop' : 'scrollLeft'
35 | let ret = target[prop]
36 | if (typeof ret !== 'number') {
37 | ret = window.document.documentElement[method]
38 | }
39 | return ret
40 | }
41 |
42 | const sticky = () => {
43 | if (active) {
44 | return
45 | }
46 | if (!elStyle.height) {
47 | elStyle.height = `${el.offsetHeight}px`
48 | }
49 |
50 | elStyle.position = 'fixed'
51 | elStyle.width = `${elWidth}px`
52 | placeholder.style.display = 'inline-block'
53 | active = true
54 | }
55 |
56 | const reset = () => {
57 | if (!active) {
58 | return
59 | }
60 |
61 | elStyle.position = ''
62 | placeholder.style.display = 'none'
63 | active = false
64 | }
65 |
66 | const check = () => {
67 | const scrollTop = getScroll(window, true)
68 | const offsetTop = el.getBoundingClientRect().top
69 | if (offsetTop < stickyTop) {
70 | sticky()
71 | } else {
72 | if (scrollTop < elHeight + stickyTop) {
73 | reset()
74 | }
75 | }
76 | }
77 | listenAction = () => {
78 | check()
79 | }
80 |
81 | window.addEventListener('scroll', listenAction)
82 | },
83 |
84 | unbind() {
85 | window.removeEventListener('scroll', listenAction)
86 | }
87 | })
88 | }
89 |
90 | export default vueSticky
91 |
92 |
--------------------------------------------------------------------------------
/src/directive/waves/index.js:
--------------------------------------------------------------------------------
1 | import waves from './waves'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('waves', waves)
5 | }
6 |
7 | if (window.Vue) {
8 | window.waves = waves
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | waves.install = install
13 | export default waves
14 |
--------------------------------------------------------------------------------
/src/directive/waves/waves.css:
--------------------------------------------------------------------------------
1 | .waves-ripple {
2 | position: absolute;
3 | border-radius: 100%;
4 | background-color: rgba(0, 0, 0, 0.15);
5 | background-clip: padding-box;
6 | pointer-events: none;
7 | -webkit-user-select: none;
8 | -moz-user-select: none;
9 | -ms-user-select: none;
10 | user-select: none;
11 | -webkit-transform: scale(0);
12 | -ms-transform: scale(0);
13 | transform: scale(0);
14 | opacity: 1;
15 | }
16 |
17 | .waves-ripple.z-active {
18 | opacity: 0;
19 | -webkit-transform: scale(2);
20 | -ms-transform: scale(2);
21 | transform: scale(2);
22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out;
25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
26 | }
--------------------------------------------------------------------------------
/src/directive/waves/waves.js:
--------------------------------------------------------------------------------
1 | import './waves.css'
2 |
3 | export default{
4 | bind(el, binding) {
5 | el.addEventListener('click', e => {
6 | const customOpts = Object.assign({}, binding.value)
7 | const opts = Object.assign({
8 | ele: el, // 波纹作用元素
9 | type: 'hit', // hit点击位置扩散center中心点扩展
10 | color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
11 | }, customOpts)
12 | const target = opts.ele
13 | if (target) {
14 | target.style.position = 'relative'
15 | target.style.overflow = 'hidden'
16 | const rect = target.getBoundingClientRect()
17 | let ripple = target.querySelector('.waves-ripple')
18 | if (!ripple) {
19 | ripple = document.createElement('span')
20 | ripple.className = 'waves-ripple'
21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
22 | target.appendChild(ripple)
23 | } else {
24 | ripple.className = 'waves-ripple'
25 | }
26 | switch (opts.type) {
27 | case 'center':
28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
30 | break
31 | default:
32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
34 | }
35 | ripple.style.backgroundColor = opts.color
36 | ripple.className = 'waves-ripple z-active'
37 | return false
38 | }
39 | }, false)
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/src/errorLog.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from './store'
3 |
4 | // you can set only in production env show the error-log
5 | // if (process.env.NODE_ENV === 'production') {
6 |
7 | Vue.config.errorHandler = function(err, vm, info, a) {
8 | // Don't ask me why I use Vue.nextTick, it just a hack.
9 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500
10 | Vue.nextTick(() => {
11 | store.dispatch('addErrorLog', {
12 | err,
13 | vm,
14 | info,
15 | url: window.location.href
16 | })
17 | console.error(err, info)
18 | })
19 | }
20 |
21 | // }
22 |
--------------------------------------------------------------------------------
/src/icons/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import SvgIcon from '@/components/SvgIcon'// svg组件
3 | import generateIconsView from '@/views/svg-icons/generateIconsView.js'// just for @/views/icons , you can delete it
4 |
5 | // register globally
6 | Vue.component('svg-icon', SvgIcon)
7 |
8 | const requireAll = requireContext => requireContext.keys().map(requireContext)
9 | const req = require.context('./svg', false, /\.svg$/)
10 | const iconMap = requireAll(req)
11 |
12 | generateIconsView.generate(iconMap) // just for @/views/icons , you can delete it
13 |
--------------------------------------------------------------------------------
/src/icons/svg/404.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/bug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/chart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/clipboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/component.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/dashboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/documentation.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/svg/drag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/excel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/eye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/form.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/international.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/language.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/svg/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/message.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/money.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/password.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/people.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/peoples.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/shoppingCard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/theme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/wechat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/zip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/lang/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueI18n from 'vue-i18n'
3 | import Cookies from 'js-cookie'
4 | import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
5 | import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
6 | import enLocale from './en'
7 | import zhLocale from './zh'
8 |
9 | Vue.use(VueI18n)
10 |
11 | const messages = {
12 | en: {
13 | ...enLocale,
14 | ...elementEnLocale
15 | },
16 | zh: {
17 | ...zhLocale,
18 | ...elementZhLocale
19 | }
20 | }
21 |
22 | const i18n = new VueI18n({
23 | locale: Cookies.get('language') || 'en', // set locale
24 | messages // set locale messages
25 | })
26 |
27 | export default i18n
28 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | import 'normalize.css/normalize.css'// A modern alternative to CSS resets
4 |
5 |
6 |
7 | import ElementUi from 'element-ui';
8 | import 'element-ui/lib/theme-chalk/index.css';
9 |
10 | import '@/styles/common.scss'; // global css
11 |
12 | import App from './App';
13 | import router from './router/router'
14 | import store from './stores'
15 |
16 |
17 | Vue.use(ElementUi, {
18 | size: 'medium'
19 | });
20 |
21 | Vue.config.productionTip = false;
22 |
23 | new Vue({
24 | el: '#app',
25 | router,
26 | store,
27 | template: '',
28 | components: { App }
29 | });
30 |
--------------------------------------------------------------------------------
/src/mock/article.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import { param2Obj } from '@/utils'
3 |
4 | const List = []
5 | const count = 100
6 |
7 | for (let i = 0; i < count; i++) {
8 | List.push(Mock.mock({
9 | id: '@increment',
10 | timestamp: +Mock.Random.date('T'),
11 | author: '@first',
12 | reviewer: '@first',
13 | title: '@title(5, 10)',
14 | forecast: '@float(0, 100, 2, 2)',
15 | importance: '@integer(1, 3)',
16 | 'type|1': ['CN', 'US', 'JP', 'EU'],
17 | 'status|1': ['published', 'draft', 'deleted'],
18 | display_time: '@datetime',
19 | pageviews: '@integer(300, 5000)'
20 | }))
21 | }
22 |
23 | export default {
24 | getList: config => {
25 | const { importance, type, title, page = 1, limit = 20, sort } = param2Obj(config.url)
26 |
27 | let mockList = List.filter(item => {
28 | if (importance && item.importance !== +importance) return false
29 | if (type && item.type !== type) return false
30 | if (title && item.title.indexOf(title) < 0) return false
31 | return true
32 | })
33 |
34 | if (sort === '-id') {
35 | mockList = mockList.reverse()
36 | }
37 |
38 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
39 |
40 | return {
41 | total: mockList.length,
42 | items: pageList
43 | }
44 | },
45 | getPv: () => ({
46 | pvData: [{ key: 'PC', pv: 1024 }, { key: 'mobile', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
47 | }),
48 | getArticle: () => ({
49 | id: 120000000001,
50 | author: { key: 'mockPan' },
51 | source_name: '原创作者',
52 | category_item: [{ key: 'global', name: '全球' }],
53 | comment_disabled: true,
54 | content: '我是测试数据我是测试数据

"',
55 | content_short: '我是测试数据',
56 | display_time: +new Date(),
57 | image_uri: 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3',
58 | platforms: ['a-platform'],
59 | source_uri: 'https://github.com/PanJiaChen/vue-element-admin',
60 | status: 'published',
61 | tags: [],
62 | title: 'vue-element-admin'
63 | }),
64 | createArticle: () => ({
65 | data: 'success'
66 | }),
67 | updateArticle: () => ({
68 | data: 'success'
69 | })
70 | }
71 |
--------------------------------------------------------------------------------
/src/mock/index.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import loginAPI from './login'
3 | import articleAPI from './article'
4 | import remoteSearchAPI from './remoteSearch'
5 | import transactionAPI from './transaction'
6 |
7 | // Mock.setup({
8 | // timeout: '350-600'
9 | // })
10 |
11 | // 登录相关
12 | Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
13 | Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
14 | Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
15 |
16 | // 文章相关
17 | Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
18 | Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)
19 | Mock.mock(/\/article\/pv/, 'get', articleAPI.getPv)
20 | Mock.mock(/\/article\/create/, 'post', articleAPI.createArticle)
21 | Mock.mock(/\/article\/update/, 'post', articleAPI.updateArticle)
22 |
23 | // 搜索相关
24 | Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser)
25 |
26 | // 账单相关
27 | Mock.mock(/\/transaction\/list/, 'get', transactionAPI.getList)
28 |
29 | export default Mock
30 |
--------------------------------------------------------------------------------
/src/mock/login.js:
--------------------------------------------------------------------------------
1 | import { param2Obj } from '@/utils'
2 |
3 | const userMap = {
4 | admin: {
5 | role: ['admin'],
6 | token: 'admin',
7 | introduction: '我是超级管理员',
8 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
9 | name: 'Super Admin'
10 | },
11 | editor: {
12 | role: ['editor'],
13 | token: 'editor',
14 | introduction: '我是编辑',
15 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
16 | name: 'Normal Editor'
17 | }
18 | }
19 |
20 | export default {
21 | loginByUsername: config => {
22 | const { username } = JSON.parse(config.body)
23 | return userMap[username]
24 | },
25 | getUserInfo: config => {
26 | const { token } = param2Obj(config.url)
27 | if (userMap[token]) {
28 | return userMap[token]
29 | } else {
30 | return false
31 | }
32 | },
33 | logout: () => 'success'
34 | }
35 |
--------------------------------------------------------------------------------
/src/mock/remoteSearch.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import { param2Obj } from '@/utils'
3 |
4 | const NameList = []
5 | const count = 100
6 |
7 | for (let i = 0; i < count; i++) {
8 | NameList.push(Mock.mock({
9 | name: '@first'
10 | }))
11 | }
12 | NameList.push({ name: 'mockPan' })
13 |
14 | export default {
15 | searchUser: config => {
16 | const { name } = param2Obj(config.url)
17 | const mockNameList = NameList.filter(item => {
18 | const lowerCaseName = item.name.toLowerCase()
19 | if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false
20 | return true
21 | })
22 | return { items: mockNameList }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/mock/transaction.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 |
3 | const List = []
4 | const count = 20
5 |
6 | for (let i = 0; i < count; i++) {
7 | List.push(Mock.mock({
8 | order_no: '@guid()',
9 | timestamp: +Mock.Random.date('T'),
10 | username: '@name()',
11 | price: '@float(1000, 15000, 0, 2)',
12 | 'status|1': ['success', 'pending']
13 | }))
14 | }
15 |
16 | export default {
17 | getList: () => {
18 | return {
19 | total: List.length,
20 | items: List
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/permission.js:
--------------------------------------------------------------------------------
1 | import router from './router'
2 | import store from './store'
3 | import NProgress from 'nprogress' // progress bar
4 | import 'nprogress/nprogress.css'// progress bar style
5 | import { getToken } from '@/utils/auth' // getToken from cookie
6 | import { Message } from 'element-ui'
7 |
8 | // permissiom judge
9 | function hasPermission(roles, permissionRoles) {
10 | if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
11 | if (!permissionRoles) return true
12 | return roles.some(role => permissionRoles.indexOf(role) >= 0)
13 | }
14 |
15 | const whiteList = ['/login', '/authredirect']// no redirect whitelist
16 |
17 | router.beforeEach((to, from, next) => {
18 | NProgress.start() // start progress bar
19 | if (getToken()) { // 判断是否有token
20 | if (to.path === '/login') {
21 | next({ path: '/' })
22 | NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
23 | } else {
24 | if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
25 | store.dispatch('GetUserInfo').then(res => { // 拉取user_info
26 | const roles = res.data.role
27 | store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
28 | router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
29 | next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
30 | })
31 | }).catch(() => {
32 | store.dispatch('FedLogOut').then(() => {
33 | Message.error('Verification failed, please login again')
34 | next({ path: '/login' })
35 | })
36 | })
37 | } else {
38 | // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
39 | if (hasPermission(store.getters.roles, to.meta.role)) {
40 | next()//
41 | } else {
42 | next({ path: '/401', query: { noGoBack: true }})
43 | NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
44 | }
45 | // 可删 ↑
46 | }
47 | }
48 | } else {
49 | if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
50 | next()
51 | } else {
52 | next('/login') // 否则全部重定向到登录页
53 | NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
54 | }
55 | }
56 | })
57 |
58 | router.afterEach(() => {
59 | NProgress.done() // finish progress bar
60 | })
61 |
--------------------------------------------------------------------------------
/src/router/_import_development.js:
--------------------------------------------------------------------------------
1 | module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
2 |
--------------------------------------------------------------------------------
/src/router/_import_production.js:
--------------------------------------------------------------------------------
1 | module.exports = file => () => import('@/views/' + file + '.vue')
2 |
--------------------------------------------------------------------------------
/src/router/router.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by mapbar_front on 2018/1/19.
3 | */
4 | import Vue from 'vue';
5 | import Router from 'vue-router';
6 |
7 | import Login from '@/views/login/Login.vue';
8 | import Register from '@/views/register/register.vue';
9 | import Main from '@/views/main/Main.vue';
10 |
11 | //Main下面展示的主要界面。
12 | import Charts from '@/views/main/charts/Charts.vue';
13 | import UpLoad from '@/views/main/upload/UpLoad.vue';
14 |
15 |
16 | Vue.use(Router);
17 |
18 |
19 | export const constantRouterMap = [
20 | { path:'/login', component:Login },
21 | { path:'/register', component:Register },
22 | {
23 | path:'/',
24 | component:Main,
25 | children: [{
26 | path: '',
27 | component: Charts
28 | },{
29 | path: 'upload',
30 | component: UpLoad
31 | },{
32 | path: 'charts1',
33 | component: Charts
34 | },{
35 | path: 'charts2',
36 | component: Charts
37 | },{
38 | path: 'charts3',
39 | component: Charts
40 | }]
41 | },
42 | { path: '*', redirect: '/login' }
43 | ];
44 |
45 | export default new Router({
46 | // mode: 'history', //后端支持可开
47 | routes: constantRouterMap
48 | });
49 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | sidebar: state => state.app.sidebar,
3 | language: state => state.app.language,
4 | visitedViews: state => state.tagsView.visitedViews,
5 | cachedViews: state => state.tagsView.cachedViews,
6 | token: state => state.user.token,
7 | avatar: state => state.user.avatar,
8 | name: state => state.user.name,
9 | introduction: state => state.user.introduction,
10 | status: state => state.user.status,
11 | roles: state => state.user.roles,
12 | setting: state => state.user.setting,
13 | permission_routers: state => state.permission.routers,
14 | addRouters: state => state.permission.addRouters,
15 | errorLogs: state => state.errorLog.logs
16 | }
17 | export default getters
18 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import app from './modules/app'
4 | import errorLog from './modules/errorLog'
5 | import permission from './modules/permission'
6 | import tagsView from './modules/tagsView'
7 | import user from './modules/user'
8 | import getters from './getters'
9 |
10 | Vue.use(Vuex)
11 |
12 | const store = new Vuex.Store({
13 | modules: {
14 | app,
15 | errorLog,
16 | permission,
17 | tagsView,
18 | user
19 | },
20 | getters
21 | })
22 |
23 | export default store
24 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const app = {
4 | state: {
5 | sidebar: {
6 | opened: !+Cookies.get('sidebarStatus')
7 | },
8 | language: Cookies.get('language') || 'zh'
9 | },
10 | mutations: {
11 | TOGGLE_SIDEBAR: state => {
12 | if (state.sidebar.opened) {
13 | Cookies.set('sidebarStatus', 1)
14 | } else {
15 | Cookies.set('sidebarStatus', 0)
16 | }
17 | state.sidebar.opened = !state.sidebar.opened
18 | },
19 | SET_LANGUAGE: (state, language) => {
20 | state.language = language
21 | Cookies.set('language', language)
22 | }
23 | },
24 | actions: {
25 | toggleSideBar({ commit }) {
26 | commit('TOGGLE_SIDEBAR')
27 | },
28 | setLanguage({ commit }, language) {
29 | commit('SET_LANGUAGE', language)
30 | }
31 | }
32 | }
33 |
34 | export default app
35 |
--------------------------------------------------------------------------------
/src/store/modules/errorLog.js:
--------------------------------------------------------------------------------
1 | const errorLog = {
2 | state: {
3 | logs: []
4 | },
5 | mutations: {
6 | ADD_ERROR_LOG: (state, log) => {
7 | state.logs.push(log)
8 | }
9 | },
10 | actions: {
11 | addErrorLog({ commit }, log) {
12 | commit('ADD_ERROR_LOG', log)
13 | }
14 | }
15 | }
16 |
17 | export default errorLog
18 |
--------------------------------------------------------------------------------
/src/store/modules/permission.js:
--------------------------------------------------------------------------------
1 | import { asyncRouterMap, constantRouterMap } from '@/router'
2 |
3 | /**
4 | * 通过meta.role判断是否与当前用户权限匹配
5 | * @param roles
6 | * @param route
7 | */
8 | function hasPermission(roles, route) {
9 | if (route.meta && route.meta.role) {
10 | return roles.some(role => route.meta.role.indexOf(role) >= 0)
11 | } else {
12 | return true
13 | }
14 | }
15 |
16 | /**
17 | * 递归过滤异步路由表,返回符合用户角色权限的路由表
18 | * @param asyncRouterMap
19 | * @param roles
20 | */
21 | function filterAsyncRouter(asyncRouterMap, roles) {
22 | const accessedRouters = asyncRouterMap.filter(route => {
23 | if (hasPermission(roles, route)) {
24 | if (route.children && route.children.length) {
25 | route.children = filterAsyncRouter(route.children, roles)
26 | }
27 | return true
28 | }
29 | return false
30 | })
31 | return accessedRouters
32 | }
33 |
34 | const permission = {
35 | state: {
36 | routers: constantRouterMap,
37 | addRouters: []
38 | },
39 | mutations: {
40 | SET_ROUTERS: (state, routers) => {
41 | state.addRouters = routers
42 | state.routers = constantRouterMap.concat(routers)
43 | }
44 | },
45 | actions: {
46 | GenerateRoutes({ commit }, data) {
47 | return new Promise(resolve => {
48 | const { roles } = data
49 | let accessedRouters
50 | if (roles.indexOf('admin') >= 0) {
51 | accessedRouters = asyncRouterMap
52 | } else {
53 | accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
54 | }
55 | commit('SET_ROUTERS', accessedRouters)
56 | resolve()
57 | })
58 | }
59 | }
60 | }
61 |
62 | export default permission
63 |
--------------------------------------------------------------------------------
/src/store/modules/tagsView.js:
--------------------------------------------------------------------------------
1 | const tagsView = {
2 | state: {
3 | visitedViews: [],
4 | cachedViews: []
5 | },
6 | mutations: {
7 | ADD_VISITED_VIEWS: (state, view) => {
8 | if (state.visitedViews.some(v => v.path === view.path)) return
9 | state.visitedViews.push({
10 | name: view.name,
11 | path: view.path,
12 | title: view.meta.title || 'no-name'
13 | })
14 | if (!view.meta.noCache) {
15 | state.cachedViews.push(view.name)
16 | }
17 | },
18 | DEL_VISITED_VIEWS: (state, view) => {
19 | for (const [i, v] of state.visitedViews.entries()) {
20 | if (v.path === view.path) {
21 | state.visitedViews.splice(i, 1)
22 | break
23 | }
24 | }
25 | for (const i of state.cachedViews) {
26 | if (i === view.name) {
27 | const index = state.cachedViews.indexOf(i)
28 | state.cachedViews.splice(index, index + 1)
29 | break
30 | }
31 | }
32 | },
33 | DEL_OTHERS_VIEWS: (state, view) => {
34 | for (const [i, v] of state.visitedViews.entries()) {
35 | if (v.path === view.path) {
36 | state.visitedViews = state.visitedViews.slice(i, i + 1)
37 | break
38 | }
39 | }
40 | for (const i of state.cachedViews) {
41 | if (i === view.name) {
42 | const index = state.cachedViews.indexOf(i)
43 | state.cachedViews = state.cachedViews.slice(index, i + 1)
44 | break
45 | }
46 | }
47 | },
48 | DEL_ALL_VIEWS: (state) => {
49 | state.visitedViews = []
50 | state.cachedViews = []
51 | }
52 | },
53 | actions: {
54 | addVisitedViews({ commit }, view) {
55 | commit('ADD_VISITED_VIEWS', view)
56 | },
57 | delVisitedViews({ commit, state }, view) {
58 | return new Promise((resolve) => {
59 | commit('DEL_VISITED_VIEWS', view)
60 | resolve([...state.visitedViews])
61 | })
62 | },
63 | delOthersViews({ commit, state }, view) {
64 | return new Promise((resolve) => {
65 | commit('DEL_OTHERS_VIEWS', view)
66 | resolve([...state.visitedViews])
67 | })
68 | },
69 | delAllViews({ commit, state }) {
70 | return new Promise((resolve) => {
71 | commit('DEL_ALL_VIEWS')
72 | resolve([...state.visitedViews])
73 | })
74 | }
75 | }
76 | }
77 |
78 | export default tagsView
79 |
--------------------------------------------------------------------------------
/src/stores/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by mapbar_front on 2018/1/26.
3 | */
4 | import Vue from 'vue'
5 |
6 | import Vuex from 'vuex'
7 |
8 | Vue.use(Vuex);
9 |
10 | const store = new Vuex.Store({
11 | state:{
12 | userInfo: {
13 | userName: '李武帝',
14 | password: 123456
15 | },
16 | num: 0
17 | },
18 | mutations: {
19 | add: function (state) {
20 | state.num++
21 | },
22 | dec: function (state) {
23 | state.num--;
24 | }
25 | }
26 | });
27 |
28 | export default store
29 |
--------------------------------------------------------------------------------
/src/styles/btn.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 |
3 | @mixin colorBtn($color) {
4 | background: $color;
5 | &:hover {
6 | color: $color;
7 | &:before,
8 | &:after {
9 | background: $color;
10 | }
11 | }
12 | }
13 |
14 | .blue-btn {
15 | @include colorBtn($blue)
16 | }
17 |
18 | .light-blue-btn {
19 | @include colorBtn($light-blue)
20 | }
21 |
22 | .red-btn {
23 | @include colorBtn($red)
24 | }
25 |
26 | .pink-btn {
27 | @include colorBtn($pink)
28 | }
29 |
30 | .green-btn {
31 | @include colorBtn($green)
32 | }
33 |
34 | .tiffany-btn {
35 | @include colorBtn($tiffany)
36 | }
37 |
38 | .yellow-btn {
39 | @include colorBtn($yellow)
40 | }
41 |
42 | .pan-btn {
43 | font-size: 14px;
44 | color: #fff;
45 | padding: 14px 36px;
46 | border-radius: 8px;
47 | border: none;
48 | outline: none;
49 | margin-right: 25px;
50 | transition: 600ms ease all;
51 | position: relative;
52 | display: inline-block;
53 | &:hover {
54 | background: #fff;
55 | &:before,
56 | &:after {
57 | width: 100%;
58 | transition: 600ms ease all;
59 | }
60 | }
61 | &:before,
62 | &:after {
63 | content: '';
64 | position: absolute;
65 | top: 0;
66 | right: 0;
67 | height: 2px;
68 | width: 0;
69 | transition: 400ms ease all;
70 | }
71 | &::after {
72 | right: inherit;
73 | top: inherit;
74 | left: 0;
75 | bottom: 0;
76 | }
77 | }
78 |
79 | .custom-button {
80 | display: inline-block;
81 | line-height: 1;
82 | white-space: nowrap;
83 | cursor: pointer;
84 | background: #fff;
85 | color: #fff;
86 | -webkit-appearance: none;
87 | text-align: center;
88 | box-sizing: border-box;
89 | outline: 0;
90 | margin: 0;
91 | padding: 10px 15px;
92 | font-size: 14px;
93 | border-radius: 4px;
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/src/styles/color-definition.scss:
--------------------------------------------------------------------------------
1 | $mainColor:#409EFF;
2 |
3 | $successColor:#67C23A;
4 |
5 | $warningColor:#E6A23C;
6 |
7 | $dangerColor:#F56C6C;
8 |
9 | $infoColor:#909399;
10 |
11 |
12 |
13 |
14 |
15 | $fontColor:#303133;
16 |
17 | $mainFontColor:#606266;
18 |
19 | $assistFontColor:#909399;
20 |
21 | $seizeFontColor:#C0C4CC;
22 |
--------------------------------------------------------------------------------
/src/styles/common.scss:
--------------------------------------------------------------------------------
1 | @import "./paddingMargin.scss";
2 |
3 | @import "./color-definition.scss";
4 | .wrapper,#app,html,body{
5 | width: 100%;
6 | height: 100%;
7 | }
8 | a{
9 | text-decoration: none;
10 | }
11 | body {
12 | height: 100%;
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-font-smoothing: antialiased;
15 | text-rendering: optimizeLegibility;
16 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
17 | }
18 |
19 | //flex
20 | .flex-box{
21 | display: flex;
22 | }
23 | .flex-col-box{
24 | flex-direction: column;
25 | }
26 | .flex1{
27 | flex: 1;
28 | }
29 |
30 | .center{
31 | display: flex;
32 | align-items: center;
33 | justify-content: center;
34 | }
35 |
36 |
37 | .border{
38 | border: 1px solid #cccccc;
39 | }
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | //覆盖一些element-ui样式
2 |
3 | .el-breadcrumb__inner, .el-breadcrumb__inner a{
4 | font-weight: 400!important;
5 | }
6 |
7 | .el-upload {
8 | input[type="file"] {
9 | display: none !important;
10 | }
11 | }
12 |
13 | .el-upload__input {
14 | display: none;
15 | }
16 |
17 | .cell {
18 | .el-tag {
19 | margin-right: 0px;
20 | }
21 | }
22 |
23 | .small-padding {
24 | .cell {
25 | padding-left: 5px;
26 | padding-right: 5px;
27 | }
28 | }
29 |
30 | .fixed-width{
31 | .el-button--mini{
32 | padding: 7px 10px;
33 | width: 60px;
34 | }
35 | }
36 |
37 | .status-col {
38 | .cell {
39 | padding: 0 10px;
40 | text-align: center;
41 | .el-tag {
42 | margin-right: 0px;
43 | }
44 | }
45 | }
46 |
47 | //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
48 | .el-dialog {
49 | transform: none;
50 | left: 0;
51 | position: relative;
52 | margin: 0 auto;
53 | }
54 |
55 | //文章页textarea修改样式
56 | .article-textarea {
57 | textarea {
58 | padding-right: 40px;
59 | resize: none;
60 | border: none;
61 | border-radius: 0px;
62 | border-bottom: 1px solid #bfcbd9;
63 | }
64 | }
65 |
66 | //element ui upload
67 | .upload-container {
68 | .el-upload {
69 | width: 100%;
70 | .el-upload-dragger {
71 | width: 100%;
72 | height: 200px;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix {
2 | &:after {
3 | content: "";
4 | display: table;
5 | clear: both;
6 | }
7 | }
8 |
9 | @mixin scrollBar {
10 | &::-webkit-scrollbar-track-piece {
11 | background: #d3dce6;
12 | }
13 | &::-webkit-scrollbar {
14 | width: 6px;
15 | }
16 | &::-webkit-scrollbar-thumb {
17 | background: #99a9bf;
18 | border-radius: 20px;
19 | }
20 | }
21 |
22 | @mixin relative {
23 | position: relative;
24 | width: 100%;
25 | height: 100%;
26 | }
27 |
28 | @mixin pct($pct) {
29 | width: #{$pct};
30 | position: relative;
31 | margin: 0 auto;
32 | }
33 |
34 | @mixin triangle($width, $height, $color, $direction) {
35 | $width: $width/2;
36 | $color-border-style: $height solid $color;
37 | $transparent-border-style: $width solid transparent;
38 | height: 0;
39 | width: 0;
40 | @if $direction==up {
41 | border-bottom: $color-border-style;
42 | border-left: $transparent-border-style;
43 | border-right: $transparent-border-style;
44 | }
45 | @else if $direction==right {
46 | border-left: $color-border-style;
47 | border-top: $transparent-border-style;
48 | border-bottom: $transparent-border-style;
49 | }
50 | @else if $direction==down {
51 | border-top: $color-border-style;
52 | border-left: $transparent-border-style;
53 | border-right: $transparent-border-style;
54 | }
55 | @else if $direction==left {
56 | border-right: $color-border-style;
57 | border-top: $transparent-border-style;
58 | border-bottom: $transparent-border-style;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/styles/paddingMargin.scss:
--------------------------------------------------------------------------------
1 | //margin
2 | .margin0{
3 | margin: 0;
4 | }
5 | .margin20{
6 | margin: 20px !important;
7 | }
8 | .margin-left40{
9 | margin-left: 40px !important;
10 | }
11 | .margin-left20{
12 | margin-left: 20px !important;
13 | }
14 | .margin-top40{
15 | margin-top: 40px !important;
16 | }
17 | .margin-top20{
18 | margin-top: 20px !important;
19 | }
20 | .margin-right20{
21 | margin-right: 20px !important;
22 | }
23 | .margin-right40{
24 | margin-right: 40px !important;
25 | }
26 | .margin-bottom20{
27 | margin-bottom: 20px !important;
28 | }
29 | .margin-bottom40{
30 | margin-bottom: 40px !important;
31 | }
32 |
33 |
34 |
35 | //padding
36 | .padding0{
37 | padding: 0;
38 | }
39 | .padding20{
40 | padding: 20px !important;
41 | }
42 | .padding-left40{
43 | padding-left: 40px !important;
44 | }
45 | .padding-left20{
46 | padding-left: 20px !important;
47 | }
48 | .padding-top40{
49 | padding-top: 40px !important;
50 | }
51 | .padding-top20{
52 | padding-top: 20px !important;
53 | }
54 | .padding-right20{
55 | padding-right: 20px !important;
56 | }
57 | .padding-right40{
58 | padding-right: 40px !important;
59 | }
60 | .padding-bottom20{
61 | padding-bottom: 20px !important;
62 | }
63 | .padding-bottom40{
64 | padding-bottom: 40px !important;
65 | }
66 |
67 |
68 | //border
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/styles/sidebar.scss:
--------------------------------------------------------------------------------
1 | #app {
2 | // 主体区域
3 | .main-container {
4 | min-height: 100%;
5 | transition: margin-left 0.28s;
6 | margin-left: 180px;
7 | } // 侧边栏
8 | .sidebar-container {
9 | transition: width 0.28s;
10 | width: 180px!important;
11 | height: 100%;
12 | position: fixed;
13 | top: 0;
14 | bottom: 0;
15 | left: 0;
16 | z-index: 1001;
17 | a {
18 | display: inline-block;
19 | width: 100%;
20 | }
21 | .svg-icon {
22 | margin-right: 16px;
23 | }
24 | .el-menu {
25 | border: none;
26 | width: 100%;
27 | }
28 | }
29 | .hideSidebar {
30 | .sidebar-container,.sidebar-container .el-menu {
31 | width: 36px!important;
32 | // overflow: inherit;
33 | }
34 | .main-container {
35 | margin-left: 36px;
36 | }
37 | }
38 | .hideSidebar {
39 | .submenu-title-noDropdown {
40 | padding-left: 10px!important;
41 | position: relative;
42 | span {
43 | height: 0;
44 | width: 0;
45 | overflow: hidden;
46 | visibility: hidden;
47 | transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
48 | opacity: 0;
49 | display: inline-block;
50 | }
51 | &:hover {
52 | span {
53 | display: block;
54 | border-radius: 3px;
55 | z-index: 1002;
56 | width: 140px;
57 | height: 56px;
58 | visibility: visible;
59 | position: absolute;
60 | right: -145px;
61 | text-align: left;
62 | text-indent: 20px;
63 | top: 0px;
64 | background-color: $subMenuBg!important;
65 | opacity: 1;
66 | }
67 | }
68 | }
69 | .el-submenu {
70 | &>.el-submenu__title {
71 | padding-left: 10px!important;
72 | &>span {
73 | display: none;
74 | }
75 | .el-submenu__icon-arrow {
76 | display: none;
77 | }
78 | }
79 | .nest-menu {
80 | .el-submenu__icon-arrow {
81 | display: block!important;
82 | }
83 | span {
84 | display: inline-block!important;
85 | }
86 | }
87 | }
88 | }
89 | .nest-menu .el-submenu>.el-submenu__title,
90 | .el-submenu .el-menu-item {
91 | min-width: 180px!important;
92 | background-color: $subMenuBg!important;
93 | &:hover {
94 | background-color: $menuHover!important;
95 | }
96 | }
97 | .el-menu--collapse .el-menu .el-submenu{
98 | min-width: 180px!important;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | //globl transition css
2 |
3 | /*fade*/
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /*fade*/
15 | .breadcrumb-enter-active,
16 | .breadcrumb-leave-active {
17 | transition: all .5s;
18 | }
19 |
20 | .breadcrumb-enter,
21 | .breadcrumb-leave-active {
22 | opacity: 0;
23 | transform: translateX(20px);
24 | }
25 |
26 | .breadcrumb-move {
27 | transition: all .5s;
28 | }
29 |
30 | .breadcrumb-leave-active {
31 | position: absolute;
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $blue:#324157;
2 | $light-blue:#3A71A8;
3 | $red:#C03639;
4 | $pink: #E65D6E;
5 | $green: #30B08F;
6 | $tiffany: #4AB7BD;
7 | $yellow:#FEC171;
8 | $panGreen: #30B08F;
9 |
10 | //sidebar
11 | $menuBg:#304156;
12 | $subMenuBg:#1f2d3d;
13 | $menuHover:#001528;
14 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'Admin-Token'
4 |
5 | export function getToken() {
6 | return Cookies.get(TokenKey)
7 | }
8 |
9 | export function setToken(token) {
10 | return Cookies.set(TokenKey, token)
11 | }
12 |
13 | export function removeToken() {
14 | return Cookies.remove(TokenKey)
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/clipboard.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Clipboard from 'clipboard'
3 |
4 | function clipboardSuccess() {
5 | Vue.prototype.$message({
6 | message: 'Copy successfully',
7 | type: 'success',
8 | duration: 1500
9 | })
10 | }
11 |
12 | function clipboardError() {
13 | Vue.prototype.$message({
14 | message: 'Copy failed',
15 | type: 'error'
16 | })
17 | }
18 |
19 | export default function handleClipboard(text, event) {
20 | const clipboard = new Clipboard(event.target, {
21 | text: () => text
22 | })
23 | clipboard.on('success', () => {
24 | clipboardSuccess()
25 | clipboard.off('error')
26 | clipboard.off('success')
27 | clipboard.destroy()
28 | })
29 | clipboard.on('error', () => {
30 | clipboardError()
31 | clipboard.off('error')
32 | clipboard.off('success')
33 | clipboard.destroy()
34 | })
35 | clipboard.onClick(event)
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/createUniqueString.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jiachenpan on 17/3/8.
3 | */
4 | export default function createUniqueString() {
5 | const timestamp = +new Date() + ''
6 | const randomNum = parseInt((1 + Math.random()) * 65536) + ''
7 | return (+(randomNum + timestamp)).toString(32)
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/i18n.js:
--------------------------------------------------------------------------------
1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview
2 | export function generateTitle(title) {
3 | return this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js
4 | }
5 |
--------------------------------------------------------------------------------
/src/utils/openWindow.js:
--------------------------------------------------------------------------------
1 | /**
2 | *Created by jiachenpan on 16/11/29.
3 | * @param {Sting} url
4 | * @param {Sting} title
5 | * @param {Number} w
6 | * @param {Number} h
7 | */
8 |
9 | export default function openWindow(url, title, w, h) {
10 | // Fixes dual-screen position Most browsers Firefox
11 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
12 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
13 |
14 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
15 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
16 |
17 | const left = ((width / 2) - (w / 2)) + dualScreenLeft
18 | const top = ((height / 2) - (h / 2)) + dualScreenTop
19 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)
20 |
21 | // Puts focus on the newWindow
22 | if (window.focus) {
23 | newWindow.focus()
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { Message } from 'element-ui'
3 | import store from '@/store'
4 | import { getToken } from '@/utils/auth'
5 |
6 | // create an axios instance
7 | const service = axios.create({
8 | baseURL: process.env.BASE_API, // api的base_url
9 | timeout: 5000 // request timeout
10 | })
11 |
12 | // request interceptor
13 | service.interceptors.request.use(config => {
14 | // Do something before request is sent
15 | if (store.getters.token) {
16 | config.headers['X-Token'] = getToken() // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
17 | }
18 | return config
19 | }, error => {
20 | // Do something with request error
21 | console.log(error) // for debug
22 | Promise.reject(error)
23 | })
24 |
25 | // respone interceptor
26 | service.interceptors.response.use(
27 | response => response,
28 | /**
29 | * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
30 | * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
31 | */
32 | // const res = response.data;
33 | // if (res.code !== 20000) {
34 | // Message({
35 | // message: res.message,
36 | // type: 'error',
37 | // duration: 5 * 1000
38 | // });
39 | // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
40 | // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
41 | // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
42 | // confirmButtonText: '重新登录',
43 | // cancelButtonText: '取消',
44 | // type: 'warning'
45 | // }).then(() => {
46 | // store.dispatch('FedLogOut').then(() => {
47 | // location.reload();// 为了重新实例化vue-router对象 避免bug
48 | // });
49 | // })
50 | // }
51 | // return Promise.reject('error');
52 | // } else {
53 | // return response.data;
54 | // }
55 | error => {
56 | console.log('err' + error)// for debug
57 | Message({
58 | message: error.message,
59 | type: 'error',
60 | duration: 5 * 1000
61 | })
62 | return Promise.reject(error)
63 | })
64 |
65 | export default service
66 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jiachenpan on 16/11/18.
3 | */
4 |
5 | export function isvalidUsername(str) {
6 | const valid_map = ['admin', 'editor']
7 | return valid_map.indexOf(str.trim()) >= 0
8 | }
9 |
10 | /* 合法uri*/
11 | export function validateURL(textval) {
12 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
13 | return urlregex.test(textval)
14 | }
15 |
16 | /* 小写字母*/
17 | export function validateLowerCase(str) {
18 | const reg = /^[a-z]+$/
19 | return reg.test(str)
20 | }
21 |
22 | /* 大写字母*/
23 | export function validateUpperCase(str) {
24 | const reg = /^[A-Z]+$/
25 | return reg.test(str)
26 | }
27 |
28 | /* 大小写字母*/
29 | export function validatAlphabets(str) {
30 | const reg = /^[A-Za-z]+$/
31 | return reg.test(str)
32 | }
33 |
34 | /**
35 | * validate email
36 | * @param email
37 | * @returns {boolean}
38 | */
39 | export function validateEmail(email) {
40 | const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
41 | return re.test(email)
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/src/vendor/Export2Zip.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('script-loader!file-saver');
3 | import JSZip from 'jszip'
4 |
5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) {
6 | const zip = new JSZip()
7 | const txt_name = txtName || 'file'
8 | const zip_name = zipName || 'file'
9 | const data = jsonData
10 | let txtData = `${th}\r\n`
11 | data.forEach((row) => {
12 | let tempStr = ''
13 | tempStr = row.toString()
14 | txtData += `${tempStr}\r\n`
15 | })
16 | zip.file(`${txt_name}.txt`, txtData)
17 | zip.generateAsync({type:"blob"}).then((blob) => {
18 | saveAs(blob, `${zip_name}.zip`)
19 | }, (err) => {
20 | alert('导出失败')
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/src/views/charts/keyboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/charts/line.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/charts/mixChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/clipboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | copy
7 |
8 |
9 |
10 | copy
11 |
12 |
13 |
14 |
15 |
16 |
45 |
46 |
--------------------------------------------------------------------------------
/src/views/components-demo/avatarUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is based on
4 | vue-image-crop-upload.
5 | {{$t('components.imageUploadTips')}}
6 |
7 |
8 |
9 |
10 |
Change avatar
11 |
12 |
13 |
15 |
16 |
17 |
18 |
44 |
45 |
52 |
53 |
--------------------------------------------------------------------------------
/src/views/components-demo/dndList.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/views/components-demo/dropzone.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Based on dropzone .
5 | {{$t('components.dropzoneTips')}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/components-demo/jsonEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
JsonEditor is base on CodeMirrorr , lint base on json-lint
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
26 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/components-demo/markdown.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/views/components-demo/splitpane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
SplitPane If you've used
4 | codepen,
5 | jsfiddle will not be unfamiliar.
6 | Github repository
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
39 |
40 |
68 |
--------------------------------------------------------------------------------
/src/views/components-demo/tinymce.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
34 |
35 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/components/BarChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
107 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/components/PieChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
85 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/components/TodoList/Todo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
71 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/components/TransactionTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{scope.row.order_no}}
6 |
7 |
8 |
9 |
10 | ¥{{scope.row.price | toThousandslsFilter}}
11 |
12 |
13 |
14 |
15 | {{scope.row.status}}
16 |
17 |
18 |
19 |
20 |
21 |
51 |
--------------------------------------------------------------------------------
/src/views/dashboard/editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Your roles:
5 | {{item}}
6 |
7 |
8 |
9 | {{name}}
10 | editor : dashboard
11 |
12 |
13 |
14 |
![]()
15 |
16 |
17 |
18 |
19 |
41 |
42 |
75 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
32 |
--------------------------------------------------------------------------------
/src/views/documentation/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
28 |
29 |
48 |
--------------------------------------------------------------------------------
/src/views/errorLog/errorTestA.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{a.a}}
5 |
6 |
7 |
8 |
9 |
14 |
--------------------------------------------------------------------------------
/src/views/errorLog/errorTestB.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
--------------------------------------------------------------------------------
/src/views/errorLog/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{$t('errorLog.tips')}}
7 |
8 | {{$t('errorLog.description')}}
9 |
10 | {{$t('errorLog.documentation')}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/src/views/errorPage/401.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
返回
4 |
5 |
6 | Oops!
7 | gif来源airbnb 页面
8 | 你没有权限去该页面
9 | 如有不满请联系你领导
10 |
11 | - 或者你可以去:
12 | -
13 | 回首页
14 |
15 | - 随便看看
16 | - 点我看图
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
52 |
53 |
89 |
--------------------------------------------------------------------------------
/src/views/example/tab/components/tabPane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | {{scope.row.id}}
8 |
9 |
10 |
11 |
12 |
13 | {{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}
14 |
15 |
16 |
17 |
18 |
19 | {{scope.row.title}}
20 | {{scope.row.type}}
21 |
22 |
23 |
24 |
25 |
26 | {{scope.row.author}}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {{scope.row.pageviews}}
39 |
40 |
41 |
42 |
43 |
44 | {{scope.row.status}}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
98 |
99 |
--------------------------------------------------------------------------------
/src/views/example/tab/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | mounted times :{{createdTimes}}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
39 |
40 |
45 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamicTable/fixedThead.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | apple
7 | banana
8 | orange
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{scope.row[fruit]}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
58 |
59 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamicTable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{$t('table.dynamicTips1')}}
4 |
5 |
6 |
{{$t('table.dynamicTips2')}}
7 |
8 |
9 |
10 |
11 |
20 |
21 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamicTable/unfixedThead.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | apple
7 | banana
8 | orange
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{scope.row[fruit]}}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
48 |
--------------------------------------------------------------------------------
/src/views/example/table/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
19 |
--------------------------------------------------------------------------------
/src/views/excel/exportExcel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$t('excel.export')}} excel
6 |
7 |
8 |
9 | {{scope.$index}}
10 |
11 |
12 |
13 |
14 | {{scope.row.title}}
15 |
16 |
17 |
18 |
19 | {{scope.row.author}}
20 |
21 |
22 |
23 |
24 | {{scope.row.pageviews}}
25 |
26 |
27 |
28 |
29 |
30 | {{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
85 |
--------------------------------------------------------------------------------
/src/views/excel/uploadExcel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
31 |
--------------------------------------------------------------------------------
/src/views/form/create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
--------------------------------------------------------------------------------
/src/views/form/edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
--------------------------------------------------------------------------------
/src/views/i18n-demo/local.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | zh: {
4 | i18nView: {
5 | title: '切换语言',
6 | note: '目前只翻译了当前页面和侧边栏和导航,未完待续,敬请期待...',
7 | datePlaceholder: '请选择日期',
8 | tableDate: '日期',
9 | tableName: '姓名',
10 | tableAddress: '地址',
11 | default: '默认按钮',
12 | primary: '主要按钮',
13 | success: '成功按钮',
14 | info: '信息按钮',
15 | warning: '警告按钮',
16 | danger: '危险按钮'
17 | }
18 |
19 | },
20 | en: {
21 | i18nView: {
22 | title: 'Switch Language',
23 | note: 'Currently only translated the i18n page and the sidebar and levelbar, please look forword to...',
24 | datePlaceholder: 'Pick a day',
25 | tableDate: 'tableDate',
26 | tableName: 'tableName',
27 | tableAddress: 'tableAddress',
28 | default: 'default:',
29 | primary: 'primary',
30 | success: 'success',
31 | info: 'info',
32 | warning: 'warning',
33 | danger: 'danger'
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/views/layout/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
30 |
31 |
40 |
--------------------------------------------------------------------------------
/src/views/layout/components/AppMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
24 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/SidebarItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
32 |
33 |
34 |
53 |
54 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
27 |
--------------------------------------------------------------------------------
/src/views/layout/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navbar } from './Navbar'
2 | export { default as Sidebar } from './Sidebar/index.vue'
3 | export { default as TagsView } from './TagsView'
4 | export { default as AppMain } from './AppMain'
5 |
--------------------------------------------------------------------------------
/src/views/login/authredirect.vue:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/views/login/socialsignin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 微信
5 |
6 |
7 | QQ
8 |
9 |
10 |
11 |
12 |
35 |
36 |
69 |
--------------------------------------------------------------------------------
/src/views/main/charts/Charts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 这是charts页面123
4 |
5 |
6 |
7 |
8 |
36 |
37 |
43 |
--------------------------------------------------------------------------------
/src/views/main/upload/UpLoad.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 这是upload上传文件界面
4 |
16 | 点击上传
17 | 只能上传jpg/png文件,且不超过500kb
18 |
19 |
20 |
21 |
22 |
55 |
56 |
59 |
--------------------------------------------------------------------------------
/src/views/permission/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{$t('permission.roles')}}: {{roles}}
4 | {{$t('permission.switchRoles')}}:
5 |
6 |
7 |
8 |
9 |
10 |
11 |
35 |
--------------------------------------------------------------------------------
/src/views/qiniu/upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 将文件拖到此处,或点击上传
5 |
6 |
7 |
8 |
9 |
41 |
--------------------------------------------------------------------------------
/src/views/svg-icons/generateIconsView.js:
--------------------------------------------------------------------------------
1 | const data = {
2 | state: {
3 | iconsMap: []
4 | },
5 | generate(iconsMap) {
6 | this.state.iconsMap = iconsMap
7 | }
8 | }
9 |
10 | export default data
11 |
--------------------------------------------------------------------------------
/src/views/svg-icons/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Add and use
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{generateIconCode(item)}}
12 |
13 |
14 |
15 | {{item}}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
50 |
51 |
75 |
--------------------------------------------------------------------------------
/src/views/theme/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | {{$t('theme.change')}} :
11 |
12 | {{$t('theme.tips')}}
13 |
14 |
15 |
16 |
17 | Primary
18 | Success
19 | Info
20 | Warning
21 | Danger
22 |
23 |
24 |
25 |
26 |
27 |
28 | Search
29 |
30 | Upload
31 |
32 |
33 |
34 |
35 |
36 |
37 | {{tag.name}}
38 |
39 |
40 |
41 |
42 |
43 | Option A
44 | Option B
45 | Option C
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
83 |
84 |
101 |
--------------------------------------------------------------------------------
/src/views/zip/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$t('zip.export')}} zip
6 |
7 |
8 |
9 | {{scope.$index}}
10 |
11 |
12 |
13 |
14 | {{scope.row.title}}
15 |
16 |
17 |
18 |
19 | {{scope.row.author}}
20 |
21 |
22 |
23 |
24 | {{scope.row.pageviews}}
25 |
26 |
27 |
28 |
29 |
30 | {{scope.row.display_time}}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
78 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liwudi/manage-platform/ff270bcd741afdd13280de2098f055409dc1faa7/static/.gitkeep
--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // The assertion name is the filename.
3 | // Example usage:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // For more information on custom assertions see:
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 |
10 | exports.assertion = function (selector, count) {
11 | this.message = 'Testing if element <' + selector + '> has count: ' + count
12 | this.expected = count
13 | this.pass = function (val) {
14 | return val === this.expected
15 | }
16 | this.value = function (res) {
17 | return res.value
18 | }
19 | this.command = function (cb) {
20 | var self = this
21 | return this.api.execute(function (selector) {
22 | return document.querySelectorAll(selector).length
23 | }, [selector], function (res) {
24 | cb.call(self, res)
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | var config = require('../../config')
3 |
4 | // http://nightwatchjs.org/gettingstarted#settings-file
5 | module.exports = {
6 | src_folders: ['test/e2e/specs'],
7 | output_folder: 'test/e2e/reports',
8 | custom_assertions_path: ['test/e2e/custom-assertions'],
9 |
10 | selenium: {
11 | start_process: true,
12 | server_path: require('selenium-server').path,
13 | host: '127.0.0.1',
14 | port: 4444,
15 | cli_args: {
16 | 'webdriver.chrome.driver': require('chromedriver').path
17 | }
18 | },
19 |
20 | test_settings: {
21 | default: {
22 | selenium_port: 4444,
23 | selenium_host: 'localhost',
24 | silent: true,
25 | globals: {
26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 | }
28 | },
29 |
30 | chrome: {
31 | desiredCapabilities: {
32 | browserName: 'chrome',
33 | javascriptEnabled: true,
34 | acceptSslCerts: true
35 | }
36 | },
37 |
38 | firefox: {
39 | desiredCapabilities: {
40 | browserName: 'firefox',
41 | javascriptEnabled: true,
42 | acceptSslCerts: true
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 |
4 | const webpack = require('webpack')
5 | const DevServer = require('webpack-dev-server')
6 |
7 | const webpackConfig = require('../../build/webpack.prod.conf')
8 | const devConfigPromise = require('../../build/webpack.dev.conf')
9 |
10 | let server
11 |
12 | devConfigPromise.then(devConfig => {
13 | const devServerOptions = devConfig.devServer
14 | const compiler = webpack(webpackConfig)
15 | server = new DevServer(compiler, devServerOptions)
16 | const port = devServerOptions.port
17 | const host = devServerOptions.host
18 | return server.listen(port, host)
19 | })
20 | .then(() => {
21 | // 2. run the nightwatch test suite against it
22 | // to run in additional browsers:
23 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
24 | // 2. add it to the --env flag below
25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
26 | // For more information on Nightwatch's config file, see
27 | // http://nightwatchjs.org/guide#settings-file
28 | let opts = process.argv.slice(2)
29 | if (opts.indexOf('--config') === -1) {
30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
31 | }
32 | if (opts.indexOf('--env') === -1) {
33 | opts = opts.concat(['--env', 'chrome'])
34 | }
35 |
36 | const spawn = require('cross-spawn')
37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
38 |
39 | runner.on('exit', function (code) {
40 | server.close()
41 | process.exit(code)
42 | })
43 |
44 | runner.on('error', function (err) {
45 | server.close()
46 | throw err
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': function (browser) {
6 | // automatically uses dev Server port from /config.index.js
7 | // default: http://localhost:8080
8 | // see nightwatch.conf.js
9 | const devServer = browser.globals.devServerURL
10 |
11 | browser
12 | .url(devServer)
13 | .waitForElementVisible('#app', 5000)
14 | .assert.elementPresent('.hello')
15 | .assert.containsText('h1', 'Welcome to Your Vue.js App')
16 | .assert.elementCount('img', 1)
17 | .end()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | },
5 | "globals": {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/unit/jest.conf.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | rootDir: path.resolve(__dirname, '../../'),
5 | moduleFileExtensions: [
6 | 'js',
7 | 'json',
8 | 'vue'
9 | ],
10 | moduleNameMapper: {
11 | '^@/(.*)$': '/src/$1'
12 | },
13 | transform: {
14 | '^.+\\.js$': '/node_modules/babel-jest',
15 | '.*\\.(vue)$': '/node_modules/vue-jest'
16 | },
17 | testPathIgnorePatterns: [
18 | '/test/e2e'
19 | ],
20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'],
21 | setupFiles: ['/test/unit/setup'],
22 | mapCoverage: true,
23 | coverageDirectory: '/test/unit/coverage',
24 | collectCoverageFrom: [
25 | 'src/**/*.{js,vue}',
26 | '!src/main.js',
27 | '!src/router/index.js',
28 | '!**/node_modules/**'
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/test/unit/setup.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
--------------------------------------------------------------------------------
/test/unit/specs/HelloWorld.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import HelloWorld from '@/components/HelloWorld'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('should render correct contents', () => {
6 | const Constructor = Vue.extend(HelloWorld)
7 | const vm = new Constructor().$mount()
8 | expect(vm.$el.querySelector('.hello h1').textContent)
9 | .toEqual('Welcome to Your Vue.js App')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------