commit b061b271bc26f13a6f2df33728d05dd26e9ed8f6 Author: wangqing <408564126@qq.com> Date: Thu Dec 28 09:15:09 2023 +0800 雀乐管理后台WEB工程初始化 diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..a2a380f --- /dev/null +++ b/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins":["transform-vue-jsx", "transform-runtime"] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ea6e20f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e3a4037 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +build/*.js +config/*.js +src/assets diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..54790a4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,198 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: 'eslint:recommended', + // required to lint *.vue files + plugins: [ + 'html' + ], + // check if imports actually resolve + 'settings': { + 'import/resolver': { + 'webpack': { + 'config': 'build/webpack.base.conf.js' + } + } + }, + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': [2, 'allow-null'], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 2, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78a0ead --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..249472d --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,9 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + // to edit target browsers: use "browserslist" field in package.json + "postcss-import": {}, + "autoprefixer": {} + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..16574d9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: stable +script: npm run test +notifications: + email: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6151575 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-present PanJiaChen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 0000000..dd5c85a --- /dev/null +++ b/README-zh.md @@ -0,0 +1,54 @@ +# vueAdmin-template + +> 这是一个 极简的vue admin 管理后台 它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。 + +[线上地址](http://panjiachen.github.io/vueAdmin-template) + +## Extra +如果你想要根据用户角色来动态生成侧边栏和router,你可以使用改分支[permission-control](https://github.com/PanJiaChen/vueAdmin-template/tree/permission-control) +  + ## 相关项目 + [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) + + [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) + +写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目: + - [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2) + - [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac) + - [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35) + - [手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56) + - [手摸手,带你封装一个vue component](https://segmentfault.com/a/1190000009090836) + + +## Build Setup + +``` bash + +# Clone project +git clone https://github.com/PanJiaChen/vueAdmin-template.git + +# Install dependencies +npm install + +# 建议不要用cnpm 安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题 +npm install --registry=https://registry.npm.taobao.org + +# serve with hot reload at localhost:9528 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +## Demo +![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif) + +## License +[MIT](https://github.com/PanJiaChen/vueAdmin-template/blob/master/LICENSE) license. + +Copyright (c) 2017-present PanJiaChen + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e57b37c --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# vueAdmin-template + +> A minimal vue admin template with Element UI & axios & iconfont & permission control & lint + +**Live demo:** http://panjiachen.github.io/vueAdmin-template + +[中文文档](https://github.com/PanJiaChen/vueAdmin-template/blob/master/README-zh.md) + +## Build Setup + +``` bash + +# Clone project +git clone https://github.com/PanJiaChen/vueAdmin-template.git + +# Install dependencies +npm install + +# serve with hot reload at localhost:9528 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +## Demo +![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif) + +## Extra +If you want router permission && generate menu by user roles , you can use this branch [permission-control](https://github.com/PanJiaChen/vueAdmin-template/tree/permission-control) + +## Related Project + [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) + + [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) + + +## License +[MIT](https://github.com/PanJiaChen/vueAdmin-template/blob/master/LICENSE) license. + +Copyright (c) 2017-present PanJiaChen diff --git a/build/build.js b/build/build.js new file mode 100644 index 0000000..955e9de --- /dev/null +++ b/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/build/check-versions.js b/build/check-versions.js new file mode 100644 index 0000000..3ef972a --- /dev/null +++ b/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/build/logo.png b/build/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/build/logo.png differ diff --git a/build/utils.js b/build/utils.js new file mode 100644 index 0000000..e534fb0 --- /dev/null +++ b/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/build/vue-loader.conf.js b/build/vue-loader.conf.js new file mode 100644 index 0000000..33ed58b --- /dev/null +++ b/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js new file mode 100644 index 0000000..a931c27 --- /dev/null +++ b/build/webpack.base.conf.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const createLintingRule = () => ({ + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter'), + emitWarning: !config.dev.showEslintErrorsInOverlay + } +}) + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + ...(config.dev.useEslint ? [createLintingRule()] : []), + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test') ,resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js new file mode 100644 index 0000000..212d081 --- /dev/null +++ b/build/webpack.dev.conf.js @@ -0,0 +1,87 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: true, + hot: true, + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true, + favicon: resolve('favicon.ico'), + title: 'vue-element-admin' + }), + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js new file mode 100644 index 0000000..7541838 --- /dev/null +++ b/build/webpack.prod.conf.js @@ -0,0 +1,150 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: false, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + favicon: resolve('favicon.ico'), + title: 'vue-element-admin', + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vender modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code splitted chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/config/dev.env.js b/config/dev.env.js new file mode 100644 index 0000000..771f72f --- /dev/null +++ b/config/dev.env.js @@ -0,0 +1,8 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"', + BASE_API: '"http://localhost:9011"', +}) diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..caaf621 --- /dev/null +++ b/config/index.js @@ -0,0 +1,83 @@ +'use strict' +// Template version: 1.2.6 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: true, + errorOverlay: true, + notifyOnErrors: false, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + // Use Eslint Loader? + // If true, your code will be linted during bundling and + // linting errors and warnings will be shown in the console. + useEslint: false, + // If true, eslint errors and warnings will also be shown in the error overlay + // in the browser. + showEslintErrorsInOverlay: false, + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + // CSS Sourcemaps off by default because relative paths are "buggy" + // with this option, according to the CSS-Loader README + // (https://github.com/webpack/css-loader#sourcemaps) + // In our experience, they generally work as expected, + // just be aware of this issue when enabling this option. + cssSourceMap: false, + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + + // you can set by youself according to actual condition + assetsPublicPath: './', + + /** + * Source Maps + */ + + productionSourceMap: false, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/config/prod.env.js b/config/prod.env.js new file mode 100644 index 0000000..836ec67 --- /dev/null +++ b/config/prod.env.js @@ -0,0 +1,5 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"', + BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"', +} diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..34b63ac Binary files /dev/null and b/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9fe688c --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + 落网管理后台 + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..68af14e --- /dev/null +++ b/package.json @@ -0,0 +1,80 @@ +{ + "name": "vue-admin-template", + "version": "3.0.0", + "license": "MIT", + "description": "A vue admin template with Element UI & axios & iconfont & permission control & lint", + "author": "Pan ", + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "build:report": "npm_config_report=true node build/build.js", + "lint": "eslint --ext .js,.vue src", + "test": "npm run lint" + }, + "dependencies": { + "axios": "0.17.1", + "element-ui": "2.0.8", + "js-cookie": "2.2.0", + "node-sass": "^8.0.0", + "normalize.css": "7.0.0", + "nprogress": "0.2.0", + "vue": "2.5.10", + "vue-router": "3.0.1", + "vuex": "3.0.1" + }, + "devDependencies": { + "autoprefixer": "7.2.3", + "babel-core": "6.26.0", + "babel-eslint": "8.0.3", + "babel-helper-vue-jsx-merge-props": "2.0.3", + "babel-loader": "7.1.2", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-transform-runtime": "6.23.0", + "babel-plugin-transform-vue-jsx": "3.5.0", + "babel-preset-env": "1.6.1", + "babel-preset-stage-2": "6.24.1", + "chalk": "2.3.0", + "copy-webpack-plugin": "4.2.3", + "css-loader": "0.28.7", + "eslint": "4.13.1", + "eslint-friendly-formatter": "3.0.0", + "eslint-loader": "1.9.0", + "eslint-plugin-html": "4.0.1", + "eventsource-polyfill": "0.9.6", + "extract-text-webpack-plugin": "3.0.2", + "file-loader": "1.1.5", + "friendly-errors-webpack-plugin": "1.6.1", + "html-webpack-plugin": "2.30.1", + "node-notifier": "5.1.2", + "node-sass": "^4.7.2", + "optimize-css-assets-webpack-plugin": "3.2.0", + "ora": "1.3.0", + "portfinder": "1.0.13", + "postcss-import": "11.0.0", + "postcss-loader": "2.0.9", + "rimraf": "2.6.2", + "sass-loader": "6.0.6", + "semver": "5.4.1", + "shelljs": "0.7.8", + "svg-sprite-loader": "3.5.2", + "uglifyjs-webpack-plugin": "1.1.3", + "url-loader": "0.6.2", + "vue-loader": "13.5.0", + "vue-style-loader": "3.0.3", + "vue-template-compiler": "2.5.10", + "webpack": "3.10.0", + "webpack-bundle-analyzer": "2.9.1", + "webpack-dev-server": "2.9.7", + "webpack-merge": "4.1.1" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..76bb961 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/api/city.js b/src/api/city.js new file mode 100644 index 0000000..a1dad08 --- /dev/null +++ b/src/api/city.js @@ -0,0 +1,11 @@ +import request from "@/utils/request" +export default { + getList(){ + return request( + { + url:'/base/city', + method:'get' + } + ); + } +} \ No newline at end of file diff --git a/src/api/gathering.js b/src/api/gathering.js new file mode 100644 index 0000000..5ebb273 --- /dev/null +++ b/src/api/gathering.js @@ -0,0 +1,52 @@ +import request from "@/utils/request" +const group_name='gathering' +const api_name='gathering' +export default { + getList(){ + return request( + { + url:`/${group_name}/${api_name}`, + method:'get' + } + ); + }, + search(page,size,searchMap){ + return request( + { + url: `/${group_name}/${api_name}/search/${page}/${size}`, + method: 'post', + data: searchMap + } + ); + + }, + save(pojo){ + return request({ + url: `/${group_name}/${api_name}`, + method: 'post', + data: pojo + }) + }, + findById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'get' + }) + }, + update(id,pojo){ + if(id==null || id==''){ + return this.save(pojo) + } + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'put', + data: pojo + }) + }, + deleteById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'delete' + }) + } +} diff --git a/src/api/login.js b/src/api/login.js new file mode 100644 index 0000000..f6fcc4c --- /dev/null +++ b/src/api/login.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function login(username, password) { + return request({ + url: '/user/admin/login', + method: 'post', + data: { + username, + password + } + }) +} + +export function getInfo(token) { + return request({ + url: '/user/admin/info', + method: 'get', + params: { token } + }) +} + +export function logout() { + return request({ + url: '/user/admin/logout', + method: 'post' + }) +} diff --git a/src/api/music.js b/src/api/music.js new file mode 100644 index 0000000..32b9c43 --- /dev/null +++ b/src/api/music.js @@ -0,0 +1,52 @@ +import request from "@/utils/request" +const group_name='article' +const api_name='article' +export default { + getList(){ + return request( + { + url:`/${group_name}/${api_name}`, + method:'get' + } + ); + }, + search(page,size,searchMap){ + return request( + { + url: `/${group_name}/${api_name}/search/${page}/${size}`, + method: 'post', + data: searchMap + } + ); + + }, + save(pojo){ + return request({ + url: `/${group_name}/${api_name}`, + method: 'post', + data: pojo + }) + }, + findById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'get' + }) + }, + update(id,pojo){ + if(id==null || id==''){ + return this.save(pojo) + } + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'put', + data: pojo + }) + }, + deleteById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'delete' + }) + } +} diff --git a/src/api/song.js b/src/api/song.js new file mode 100644 index 0000000..499796a --- /dev/null +++ b/src/api/song.js @@ -0,0 +1,52 @@ +import request from "@/utils/request" +const group_name='article' +const api_name='song' +export default { + getList(){ + return request( + { + url:`/${group_name}/${api_name}`, + method:'get' + } + ); + }, + search(page,size,searchMap){ + return request( + { + url: `/${group_name}/${api_name}/search/${page}/${size}`, + method: 'post', + data: searchMap + } + ); + + }, + save(pojo){ + return request({ + url: `/${group_name}/${api_name}`, + method: 'post', + data: pojo + }) + }, + findById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'get' + }) + }, + update(id,pojo){ + if(id==null || id==''){ + return this.save(pojo) + } + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'put', + data: pojo + }) + }, + deleteById(id){ + return request({ + url: `/${group_name}/${api_name}/${id}`, + method: 'delete' + }) + } +} diff --git a/src/api/table.js b/src/api/table.js new file mode 100644 index 0000000..e29c294 --- /dev/null +++ b/src/api/table.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getList(params) { + return request({ + url: '/table/list', + method: 'get', + params + }) +} diff --git a/src/assets/404_images/404.png b/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/src/assets/404_images/404.png differ diff --git a/src/assets/404_images/404_cloud.png b/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/src/assets/404_images/404_cloud.png differ diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..b5d9471 --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..9ad0bb2 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/ScrollBar/index.vue b/src/components/ScrollBar/index.vue new file mode 100644 index 0000000..dc32f5c --- /dev/null +++ b/src/components/ScrollBar/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e331a27 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/icons/index.js b/src/icons/index.js new file mode 100644 index 0000000..14e2e13 --- /dev/null +++ b/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg组件 + +// register globally +Vue.component('svg-icon', SvgIcon) + +const requireAll = requireContext => requireContext.keys().map(requireContext) +const req = require.context('./svg', false, /\.svg$/) +requireAll(req) diff --git a/src/icons/svg/example.svg b/src/icons/svg/example.svg new file mode 100644 index 0000000..19cd9ed --- /dev/null +++ b/src/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/eye.svg b/src/icons/svg/eye.svg new file mode 100644 index 0000000..194aa45 --- /dev/null +++ b/src/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/form.svg b/src/icons/svg/form.svg new file mode 100644 index 0000000..d848c8a --- /dev/null +++ b/src/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/password.svg b/src/icons/svg/password.svg new file mode 100644 index 0000000..920b500 --- /dev/null +++ b/src/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/table.svg b/src/icons/svg/table.svg new file mode 100644 index 0000000..da6ffff --- /dev/null +++ b/src/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/tree.svg b/src/icons/svg/tree.svg new file mode 100644 index 0000000..11cedc0 --- /dev/null +++ b/src/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/user.svg b/src/icons/svg/user.svg new file mode 100644 index 0000000..5971dee --- /dev/null +++ b/src/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..a6d55d5 --- /dev/null +++ b/src/main.js @@ -0,0 +1,28 @@ +import Vue from 'vue' + +import 'normalize.css/normalize.css'// A modern alternative to CSS resets + +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' +import locale from 'element-ui/lib/locale/lang/zh-CN' + +import '@/styles/index.scss' // global css + +import App from './App' +import router from './router' +import store from './store' + +import '@/icons' // icon +import '@/permission' // permission control + +Vue.use(ElementUI, { locale }) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + template: '', + components: { App } +}) diff --git a/src/permission.js b/src/permission.js new file mode 100644 index 0000000..4b9e5b1 --- /dev/null +++ b/src/permission.js @@ -0,0 +1,40 @@ +import router from './router' +import store from './store' +import NProgress from 'nprogress' // Progress 进度条 +import 'nprogress/nprogress.css'// Progress 进度条样式 +import { Message } from 'element-ui' +import { getToken } from '@/utils/auth' // 验权 + +const whiteList = ['/login'] // 不重定向白名单 +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + if (to.path === '/login') { + next({ path: '/' }) + } else { + if (store.getters.roles.length === 0) { + store.dispatch('GetInfo').then(res => { // 拉取用户信息 + next() + }).catch(() => { + store.dispatch('FedLogOut').then(() => { + Message.error('验证失败,请重新登录') + next({ path: '/login' }) + }) + }) + } else { + next() + } + } + } else { + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + next('/login') + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() // 结束Progress +}) diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..ddf3b9a --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,130 @@ +import Vue from 'vue' +import Router from 'vue-router' + +// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; +// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading + +Vue.use(Router) + +/* Layout */ +import Layout from '../views/layout/Layout' + +/** +* hidden: true if `hidden:true` will not show in the sidebar(default is false) +* alwaysShow: true if set true, will always show the root menu, whatever its child routes length +* if not set alwaysShow, only more than one route under the children +* it will becomes nested mode, otherwise not show the root menu +* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb +* name:'router-name' the name is used by (must set!!!) +* meta : { + title: 'title' the name show in submenu and breadcrumb (recommend set) + icon: 'svg-name' the icon show in the sidebar, + } +**/ +export const constantRouterMap = [ + { path: '/login', component: () => import('@/views/login/index'), hidden: true }, + { path: '/404', component: () => import('@/views/404'), hidden: true }, + + { + path: '/', + component: Layout, + redirect: '/dashboard', + name: 'Dashboard', + hidden: true, + children: [{ + path: 'dashboard', + component: () => import('@/views/dashboard/index') + }] + }, + + { + path: '/example', + component: Layout, + redirect: '/example/table', + name: 'Example', + meta: { title: '基本信息管理', icon: 'example' }, + children: [ + { + path: 'table', + name: 'Table', + component: () => import('@/views/table/index'), + meta: { title: '城市管理', icon: 'table' } + }, + { + path: 'tree', + name: 'Tree', + component: () => import('@/views/tree/index'), + meta: { title: '标签管理', icon: 'tree' } + } + ] + }, + { + path: '/gathering', + component: Layout, + children: [ + { + path: 'index', + name: 'Form', + component: () => import('@/views/table/gathering'), + meta: { title: '活动管理', icon: 'form' } + } + ] + }, + { + path: '/music', + component: Layout, + children: [ + { + path: 'index', + name: 'Form', + component: () => import('@/views/table/music'), + meta: { title: '期刊管理', icon: 'table' } + } + ] + }, + { + path: '/song', + component: Layout, + children: [ + { + path: 'index', + name: 'Form', + component: () => import('@/views/table/song'), + meta: { title: '歌曲管理', icon: 'table' } + } + ] + }, + { + path: '/gathering', + component: Layout, + children: [ + { + path: 'index', + name: 'Form', + component: () => import('@/views/table/gathering'), + meta: { title: '单曲管理', icon: 'form' } + } + ] + }, + { + path: '/gathering', + component: Layout, + children: [ + { + path: 'index', + name: 'Form', + component: () => import('@/views/table/gathering'), + meta: { title: '专栏管理', icon: 'form' } + } + ] + }, + + { path: '*', redirect: '/404', hidden: true } +] + +export default new Router({ + // mode: 'history', //后端支持可开 + scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap +}) + diff --git a/src/store/getters.js b/src/store/getters.js new file mode 100644 index 0000000..d99762d --- /dev/null +++ b/src/store/getters.js @@ -0,0 +1,8 @@ +const getters = { + sidebar: state => state.app.sidebar, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + roles: state => state.user.roles +} +export default getters diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..6b6be08 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,17 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import user from './modules/user' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + user + }, + getters +}) + +export default store diff --git a/src/store/modules/app.js b/src/store/modules/app.js new file mode 100644 index 0000000..e49b16a --- /dev/null +++ b/src/store/modules/app.js @@ -0,0 +1,26 @@ +import Cookies from 'js-cookie' + +const app = { + state: { + sidebar: { + opened: !+Cookies.get('sidebarStatus') + } + }, + mutations: { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + state.sidebar.opened = !state.sidebar.opened + } + }, + actions: { + ToggleSideBar: ({ commit }) => { + commit('TOGGLE_SIDEBAR') + } + } +} + +export default app diff --git a/src/store/modules/user.js b/src/store/modules/user.js new file mode 100644 index 0000000..fa9c701 --- /dev/null +++ b/src/store/modules/user.js @@ -0,0 +1,83 @@ +import { login, logout, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + token: getToken(), + name: '', + avatar: '', + roles: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + return new Promise((resolve, reject) => { + login(username, userInfo.password).then(response => { + const data = response.data + setToken(data.token) + commit('SET_TOKEN', data.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo(state.token).then(response => { + const data = response.data + commit('SET_ROLES', data.roles) + commit('SET_NAME', data.name) + commit('SET_AVATAR', data.avatar) + resolve(response) + }).catch(error => { + reject(error) + }) + }) + }, + + // 登出 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss new file mode 100644 index 0000000..ef7bb5d --- /dev/null +++ b/src/styles/element-ui.scss @@ -0,0 +1,29 @@ + //to reset element-ui default css +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +//element ui upload +.upload-container { + .el-upload { + width: 100%; + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..7ab8db8 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,59 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; + +body { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +html { + box-sizing: border-box; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +div:focus{ + outline: none; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +//main-container全局样式 +.app-main{ + min-height: 100% +} + +.app-container { + padding: 20px; +} diff --git a/src/styles/mixin.scss b/src/styles/mixin.scss new file mode 100644 index 0000000..601d7a0 --- /dev/null +++ b/src/styles/mixin.scss @@ -0,0 +1,27 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + &::-webkit-scrollbar { + width: 6px; + } + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss new file mode 100644 index 0000000..2fed2b1 --- /dev/null +++ b/src/styles/sidebar.scss @@ -0,0 +1,100 @@ +#app { + // 主体区域 + .main-container { + min-height: 100%; + transition: margin-left 0.28s; + margin-left: 180px; + } // 侧边栏 + .sidebar-container { + transition: width 0.28s; + width: 180px!important; + height: 100%; + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + a { + display: inline-block; + width: 100%; + } + .svg-icon { + margin-right: 16px; + } + .el-menu { + border: none; + width: 100%; + } + } + .hideSidebar { + .sidebar-container,.sidebar-container .el-menu { + width: 36px!important; + // overflow: inherit; + } + .main-container { + margin-left: 36px; + } + } + .hideSidebar { + .submenu-title-noDropdown { + padding-left: 10px!important; + position: relative; + span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + transition: opacity .3s cubic-bezier(.55, 0, .1, 1); + opacity: 0; + display: inline-block; + } + &:hover { + span { + display: block; + border-radius: 3px; + z-index: 1002; + width: 140px; + height: 56px; + visibility: visible; + position: absolute; + right: -145px; + text-align: left; + text-indent: 20px; + top: 0px; + background-color: $subMenuBg!important; + opacity: 1; + } + } + } + .el-submenu { + &>.el-submenu__title { + padding-left: 10px!important; + &>span { + display: none; + } + .el-submenu__icon-arrow { + display: none; + } + } + .nest-menu { + .el-submenu__icon-arrow { + display: block!important; + } + span { + display: inline-block!important; + } + } + } + } + .nest-menu .el-submenu>.el-submenu__title, + .el-submenu .el-menu-item { + min-width: 180px!important; + background-color: $subMenuBg!important; + &:hover { + background-color: $menuHover!important; + } + } + .el-menu--collapse .el-menu .el-submenu{ + min-width: 180px!important; + } +} diff --git a/src/styles/transition.scss b/src/styles/transition.scss new file mode 100644 index 0000000..c4d47ad --- /dev/null +++ b/src/styles/transition.scss @@ -0,0 +1,32 @@ +//globl transition css + +/*fade*/ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/*fade*/ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..2fee827 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,4 @@ +//sidebar +$menuBg:#304156; +$subMenuBg:#1f2d3d; +$menuHover:#001528; diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..9657c9c --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,58 @@ +/** + * Created by jiachenpan on 16/11/18. + */ + +export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if (('' + time).length === 10) time = parseInt(time) * 1000 + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +export function formatTime(time, option) { + time = +time * 1000 + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' + } +} diff --git a/src/utils/request.js b/src/utils/request.js new file mode 100644 index 0000000..7b9c801 --- /dev/null +++ b/src/utils/request.js @@ -0,0 +1,66 @@ +import axios from 'axios' +import { Message, MessageBox } from 'element-ui' +import store from '../store' +import { getToken } from '@/utils/auth' + +// 创建axios实例 +const service = axios.create({ + baseURL: process.env.BASE_API, // api的base_url + timeout: 15000 // 请求超时时间 +}) + +// request拦截器 +service.interceptors.request.use(config => { + if (store.getters.token) { + config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + return config +}, error => { + // Do something with request error + console.log(error) // for debug + Promise.reject(error) +}) + +// respone拦截器 +service.interceptors.response.use( + response => { + /** + * code为非20000是抛错 可结合自己业务进行修改 + */ + const res = response.data + if (res.code !== 20000) { + Message({ + message: res.message, + type: 'error', + duration: 5 * 1000 + }) + + // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; + if (res.code === 50008 || res.code === 50012 || res.code === 50014) { + MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + store.dispatch('FedLogOut').then(() => { + location.reload()// 为了重新实例化vue-router对象 避免bug + }) + }) + } + return Promise.reject('error') + } else { + return response.data + } + }, + error => { + console.log('err' + error)// for debug + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + +export default service diff --git a/src/utils/validate.js b/src/utils/validate.js new file mode 100644 index 0000000..834a8dd --- /dev/null +++ b/src/utils/validate.js @@ -0,0 +1,33 @@ +/** + * Created by jiachenpan on 16/11/18. + */ + +export function isvalidUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/* 合法uri*/ +export function validateURL(textval) { + 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.,?'\\+&%$#=~_-]+))*$/ + return urlregex.test(textval) +} + +/* 小写字母*/ +export function validateLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/* 大写字母*/ +export function validateUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/* 大小写字母*/ +export function validatAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + diff --git a/src/views/404.vue b/src/views/404.vue new file mode 100644 index 0000000..ca160fa --- /dev/null +++ b/src/views/404.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue new file mode 100644 index 0000000..4b4625c --- /dev/null +++ b/src/views/dashboard/index.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/views/form/index.vue b/src/views/form/index.vue new file mode 100644 index 0000000..bfec9a2 --- /dev/null +++ b/src/views/form/index.vue @@ -0,0 +1,85 @@ + + + + + + diff --git a/src/views/layout/Layout.vue b/src/views/layout/Layout.vue new file mode 100644 index 0000000..dcdd549 --- /dev/null +++ b/src/views/layout/Layout.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/views/layout/components/AppMain.vue b/src/views/layout/components/AppMain.vue new file mode 100644 index 0000000..4babff5 --- /dev/null +++ b/src/views/layout/components/AppMain.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/views/layout/components/Navbar.vue b/src/views/layout/components/Navbar.vue new file mode 100644 index 0000000..eb36b1a --- /dev/null +++ b/src/views/layout/components/Navbar.vue @@ -0,0 +1,94 @@ + + + + + + diff --git a/src/views/layout/components/Sidebar/SidebarItem.vue b/src/views/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..3555e73 --- /dev/null +++ b/src/views/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/views/layout/components/Sidebar/index.vue b/src/views/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..8359cec --- /dev/null +++ b/src/views/layout/components/Sidebar/index.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/views/layout/components/index.js b/src/views/layout/components/index.js new file mode 100644 index 0000000..97ee3cd --- /dev/null +++ b/src/views/layout/components/index.js @@ -0,0 +1,3 @@ +export { default as Navbar } from './Navbar' +export { default as Sidebar } from './Sidebar' +export { default as AppMain } from './AppMain' diff --git a/src/views/login/index.vue b/src/views/login/index.vue new file mode 100644 index 0000000..8ac1b48 --- /dev/null +++ b/src/views/login/index.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/src/views/table/gathering.vue b/src/views/table/gathering.vue new file mode 100644 index 0000000..de3c17e --- /dev/null +++ b/src/views/table/gathering.vue @@ -0,0 +1,207 @@ + + diff --git a/src/views/table/index.vue b/src/views/table/index.vue new file mode 100644 index 0000000..899c341 --- /dev/null +++ b/src/views/table/index.vue @@ -0,0 +1,72 @@ + + + diff --git a/src/views/table/music.vue b/src/views/table/music.vue new file mode 100644 index 0000000..02942b1 --- /dev/null +++ b/src/views/table/music.vue @@ -0,0 +1,172 @@ + + diff --git a/src/views/table/song.vue b/src/views/table/song.vue new file mode 100644 index 0000000..1f82905 --- /dev/null +++ b/src/views/table/song.vue @@ -0,0 +1,188 @@ + + diff --git a/src/views/tree/index.vue b/src/views/tree/index.vue new file mode 100644 index 0000000..2bfa696 --- /dev/null +++ b/src/views/tree/index.vue @@ -0,0 +1,71 @@ + + + + diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29