├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
├── build.js
├── check-versions.js
├── dev-client.js
├── dev-server.js
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
├── webpack.prod.conf.js
└── webpack.test.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── favicon.ico
├── index.html
├── mock
├── get_classrooms.js
├── get_region_teachers.js
├── get_regions.js
├── get_roles.js
├── get_schools.js
└── login.js
├── package.json
├── src
├── App.vue
├── assets
│ ├── common.scss
│ └── logo.png
├── components
│ ├── Breadcrumb.vue
│ ├── Header.vue
│ ├── PageProgress.vue
│ ├── Sidebar.vue
│ └── UIComponents.js
├── filters
│ └── index.js
├── main.js
├── routes
│ └── index.js
├── services
│ ├── API.js
│ ├── account.js
│ ├── campus.js
│ ├── classroom.js
│ ├── common.js
│ ├── model
│ │ ├── AccountVO.js
│ │ ├── CampusVO.js
│ │ ├── ClassroomVO.js
│ │ ├── CommonVO.js
│ │ ├── RoleVO.js
│ │ ├── SchoolVO.js
│ │ ├── SubjectVO.js
│ │ └── TeacherVO.js
│ ├── role.js
│ ├── subject.js
│ └── teacher.js
├── store
│ ├── actions.js
│ ├── getters.js
│ ├── index.js
│ ├── modules
│ │ └── account.js
│ ├── mutation-types.js
│ └── mutations.js
├── utils
│ ├── http.js
│ ├── md5.js
│ ├── proxy.js
│ └── util.js
└── views
│ ├── Home.vue
│ ├── Index.vue
│ ├── Login.vue
│ ├── NotFound.vue
│ ├── Regist.vue
│ └── main
│ ├── CampusManage.vue
│ ├── ClassroomManage.vue
│ ├── Index.vue
│ ├── RoleManage.vue
│ └── TeacherManage.vue
├── static
├── .gitkeep
└── img
│ └── screenshot.png
└── test
├── e2e
├── custom-assertions
│ └── elementCount.js
├── nightwatch.conf.js
├── runner.js
└── specs
│ └── test.js
└── unit
├── .eslintrc
├── index.js
├── karma.conf.js
└── specs
└── Hello.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": [
12 | "transform-runtime",
13 | "syntax-dynamic-import",
14 | ["component", [{
15 | "libraryName": "element-ui",
16 | "styleLibraryName": "theme-default"
17 | }]]
18 | ],
19 | "env": {
20 | "test": {
21 | "presets": ["env", "stage-2"],
22 | "plugins": ["istanbul"]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.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/*.js
2 | config/*.js
3 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://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/feross/standard/blob/master/RULES.md#javascript-standard-style
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | 'rules': {
20 | // allow paren-less arrow functions
21 | 'arrow-parens': 0,
22 | // allow async-await
23 | 'generator-star-spacing': 0,
24 | // allow debugger during development
25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.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 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-spa-project
2 | > 基于vue.js技术栈,采用MVC等分层结构设计、数据解耦设计、组件化和自动化构建的单页应用项目。
3 |
4 | 技术栈
5 |
6 | - vue.js(2.4)
7 | - vuex(状态管理)
8 | - vue-router(前端路由,异步加载)
9 | - fetch(替代ajax,更高效地进行网络请求)
10 | - element-ui(基于vue的PC端UI组件库)
11 |
12 |
13 | Webpack(自动构建)
14 |
15 | - Babel预编译(ES6、SCSS等),和异步组件实现路由懒加载
16 | - ESlint代码质量控制,所有代码编写必须按照标准规范才能通过编译,并避免一些低级错误
17 | - 本地开发服务器会监听文件修改,自动编译和刷新浏览器
18 | - 前端mock数据,无需等待后端接口实现
19 | - 测试服务器自动部署deploy(需要在服务器上启动一个node写的接收脚本: [file-receiver](https://github.com/xiaoping6688/file-receiver))
20 |
21 | 如果需要服务端渲染(SSR),推荐使用:[NUXT](https://zh.nuxtjs.org/)
22 |
23 | ## Build Setup
24 |
25 | ``` bash
26 | # install dependencies (use cnpm: npm install -g cnpm --registry=https://registry.npm.taobao.org)
27 | cnpm install
28 |
29 | # serve with hot reload at localhost:8080
30 | npm run dev
31 |
32 | # build for production with minification
33 | npm run build
34 |
35 | # run all tests for unit test and e2e test
36 | npm test
37 |
38 | # check update for npm packages, please install 'npm install npm-check-updates -g' at first
39 | npm run update
40 | ```
41 |
42 | 
43 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | var ora = require('ora')
6 | var rm = require('rimraf')
7 | var path = require('path')
8 | var chalk = require('chalk')
9 | var webpack = require('webpack')
10 | var config = require('../config')
11 | var webpackConfig = require('./webpack.prod.conf')
12 |
13 | var spinner = ora('building for production...')
14 | spinner.start()
15 |
16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
17 | if (err) throw err
18 | webpack(webpackConfig, function (err, stats) {
19 | spinner.stop()
20 | if (err) throw err
21 | process.stdout.write(stats.toString({
22 | colors: true,
23 | modules: false,
24 | children: false,
25 | chunks: false,
26 | chunkModules: false
27 | }) + '\n\n')
28 |
29 | console.log(chalk.cyan(' Build complete.\n'))
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ))
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk')
2 | var semver = require('semver')
3 | var packageConfig = require('../package.json')
4 | var shell = require('shelljs')
5 | function exec (cmd) {
6 | return require('child_process').execSync(cmd).toString().trim()
7 | }
8 |
9 | var versionRequirements = [
10 | {
11 | name: 'node',
12 | currentVersion: semver.clean(process.version),
13 | versionRequirement: packageConfig.engines.node
14 | },
15 | ]
16 |
17 | if (shell.which('npm')) {
18 | versionRequirements.push({
19 | name: 'npm',
20 | currentVersion: exec('npm --version'),
21 | versionRequirement: packageConfig.engines.npm
22 | })
23 | }
24 |
25 | module.exports = function () {
26 | var warnings = []
27 | for (var i = 0; i < versionRequirements.length; i++) {
28 | var mod = versionRequirements[i]
29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
30 | warnings.push(mod.name + ': ' +
31 | chalk.red(mod.currentVersion) + ' should be ' +
32 | chalk.green(mod.versionRequirement)
33 | )
34 | }
35 | }
36 |
37 | if (warnings.length) {
38 | console.log('')
39 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
40 | console.log()
41 | for (var i = 0; i < warnings.length; i++) {
42 | var warning = warnings[i]
43 | console.log(' ' + warning)
44 | }
45 | console.log()
46 | process.exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/build/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('eventsource-polyfill')
3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
4 |
5 | hotClient.subscribe(function (event) {
6 | if (event.action === 'reload') {
7 | window.location.reload()
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/build/dev-server.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | var config = require('../config')
4 | if (!process.env.NODE_ENV) {
5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
6 | }
7 |
8 | var opn = require('opn')
9 | var path = require('path')
10 | var fs = require('fs')
11 | var express = require('express')
12 | var webpack = require('webpack')
13 | var proxyMiddleware = require('http-proxy-middleware')
14 | var webpackConfig = process.env.NODE_ENV === 'testing'
15 | ? require('./webpack.prod.conf')
16 | : require('./webpack.dev.conf')
17 |
18 | // default port where dev server listens for incoming traffic
19 | var port = process.env.PORT || config.dev.port
20 | // automatically open browser, if not set will be false
21 | var autoOpenBrowser = !!config.dev.autoOpenBrowser
22 | // Define HTTP proxies to your custom API backend
23 | // https://github.com/chimurai/http-proxy-middleware
24 | var proxyTable = config.dev.proxyTable
25 |
26 | var app = express()
27 | var compiler = webpack(webpackConfig)
28 |
29 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
30 | publicPath: webpackConfig.output.publicPath,
31 | quiet: true
32 | })
33 |
34 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
35 | log: () => {},
36 | heartbeat: 2000
37 | })
38 | // force page reload when html-webpack-plugin template changes
39 | compiler.plugin('compilation', function (compilation) {
40 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
41 | hotMiddleware.publish({ action: 'reload' })
42 | cb()
43 | })
44 | })
45 |
46 | // proxy api requests
47 | Object.keys(proxyTable).forEach(function (context) {
48 | var options = proxyTable[context]
49 | if (typeof options === 'string') {
50 | options = { target: options }
51 | }
52 | app.use(proxyMiddleware(options.filter || context, options))
53 | })
54 |
55 | // mock data
56 | var mockDir = path.resolve(__dirname, '../mock')
57 | fs.readdirSync(mockDir).forEach(function (file) {
58 | var mock = require(path.resolve(mockDir, file))
59 | app.use(mock.api, mock.response)
60 | })
61 |
62 | // handle fallback for HTML5 history API
63 | app.use(require('connect-history-api-fallback')())
64 |
65 | // serve webpack bundle output
66 | app.use(devMiddleware)
67 |
68 | // enable hot-reload and state-preserving
69 | // compilation error display
70 | app.use(hotMiddleware)
71 |
72 | // serve pure static assets
73 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
74 | app.use(staticPath, express.static('./static'))
75 |
76 | var uri = 'http://localhost:' + port
77 |
78 | var _resolve
79 | var readyPromise = new Promise(resolve => {
80 | _resolve = resolve
81 | })
82 |
83 | console.log('> Starting dev server...')
84 | devMiddleware.waitUntilValid(() => {
85 | console.log('> Listening at ' + uri + '\n')
86 | // when env is testing, don't need open it
87 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
88 | opn(uri)
89 | }
90 | _resolve()
91 | })
92 |
93 | var server = app.listen(port)
94 |
95 | module.exports = {
96 | ready: readyPromise,
97 | close: () => {
98 | server.close()
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
4 |
5 | exports.assetsPath = function (_path) {
6 | var assetsSubDirectory = process.env.NODE_ENV === 'production'
7 | ? config.build.assetsSubDirectory
8 | : config.dev.assetsSubDirectory
9 | return path.posix.join(assetsSubDirectory, _path)
10 | }
11 |
12 | exports.cssLoaders = function (options) {
13 | options = options || {}
14 |
15 | var cssLoader = {
16 | loader: 'css-loader',
17 | options: {
18 | minimize: process.env.NODE_ENV === 'production',
19 | sourceMap: options.sourceMap
20 | }
21 | }
22 |
23 | // generate loader string to be used with extract text plugin
24 | function generateLoaders (loader, loaderOptions) {
25 | var loaders = [cssLoader]
26 | if (loader) {
27 | loaders.push({
28 | loader: loader + '-loader',
29 | options: Object.assign({}, loaderOptions, {
30 | sourceMap: options.sourceMap
31 | })
32 | })
33 | }
34 |
35 | // Extract CSS when that option is specified
36 | // (which is the case during production build)
37 | if (options.extract) {
38 | return ExtractTextPlugin.extract({
39 | use: loaders,
40 | fallback: 'vue-style-loader'
41 | })
42 | } else {
43 | return ['vue-style-loader'].concat(loaders)
44 | }
45 | }
46 |
47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
48 | return {
49 | css: generateLoaders(),
50 | postcss: generateLoaders(),
51 | less: generateLoaders('less'),
52 | sass: generateLoaders('sass', { indentedSyntax: true }),
53 | scss: generateLoaders('sass'),
54 | stylus: generateLoaders('stylus'),
55 | styl: generateLoaders('stylus')
56 | }
57 | }
58 |
59 | // Generate loaders for standalone style files (outside of .vue)
60 | exports.styleLoaders = function (options) {
61 | var output = []
62 | var loaders = exports.cssLoaders(options)
63 | for (var extension in loaders) {
64 | var loader = loaders[extension]
65 | output.push({
66 | test: new RegExp('\\.' + extension + '$'),
67 | use: loader
68 | })
69 | }
70 | return output
71 | }
72 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var config = require('../config')
3 | var isProduction = process.env.NODE_ENV === 'production'
4 |
5 | module.exports = {
6 | loaders: utils.cssLoaders({
7 | sourceMap: isProduction
8 | ? config.build.productionSourceMap
9 | : config.dev.cssSourceMap,
10 | extract: isProduction
11 | }),
12 | transformToRequire: {
13 | video: 'src',
14 | source: 'src',
15 | img: 'src',
16 | image: 'xlink:href'
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var config = require('../config')
4 | var vueLoaderConfig = require('./vue-loader.conf')
5 |
6 | function resolve (dir) {
7 | return path.join(__dirname, '..', dir)
8 | }
9 |
10 | module.exports = {
11 | entry: {
12 | app: './src/main.js'
13 | },
14 | output: {
15 | path: config.build.assetsRoot,
16 | filename: '[name].js',
17 | chunkFilename: '[name].chunk.js', // 可选,webpack v2.4 的新功能,可以设置 chunk 的名字
18 | publicPath: process.env.NODE_ENV === 'production'
19 | ? config.build.assetsPublicPath
20 | : config.dev.assetsPublicPath
21 | },
22 | resolve: {
23 | extensions: ['.js', '.vue', '.json'],
24 | alias: {
25 | 'vue$': 'vue/dist/vue.esm.js',
26 | '@': resolve('src')
27 | }
28 | },
29 | module: {
30 | rules: [
31 | {
32 | test: /\.(js|vue)$/,
33 | loader: 'eslint-loader',
34 | enforce: 'pre',
35 | include: [resolve('src'), resolve('test')],
36 | options: {
37 | formatter: require('eslint-friendly-formatter')
38 | }
39 | },
40 | {
41 | test: /\.vue$/,
42 | loader: 'vue-loader',
43 | options: vueLoaderConfig
44 | },
45 | {
46 | test: /\.js$/,
47 | loader: 'babel-loader',
48 | include: [resolve('src'), resolve('test')]
49 | },
50 | {
51 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
52 | loader: 'url-loader',
53 | options: {
54 | limit: 10000,
55 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
56 | }
57 | },
58 | {
59 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
60 | loader: 'url-loader',
61 | options: {
62 | limit: 10000,
63 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
64 | }
65 | },
66 | {
67 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
68 | loader: 'url-loader',
69 | options: {
70 | limit: 10000,
71 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
72 | }
73 | }
74 | ]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var webpack = require('webpack')
3 | var config = require('../config')
4 | var merge = require('webpack-merge')
5 | var baseWebpackConfig = require('./webpack.base.conf')
6 | var HtmlWebpackPlugin = require('html-webpack-plugin')
7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
8 |
9 | // add hot-reload related code to entry chunks
10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
12 | })
13 |
14 | module.exports = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: '#cheap-module-eval-source-map',
20 | plugins: [
21 | new webpack.DefinePlugin({
22 | 'process.env': config.dev.env
23 | }),
24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
25 | new webpack.HotModuleReplacementPlugin(),
26 | new webpack.NoEmitOnErrorsPlugin(),
27 | // https://github.com/ampedandwired/html-webpack-plugin
28 | new HtmlWebpackPlugin({
29 | favicon: 'favicon.ico',
30 | filename: 'index.html',
31 | template: 'index.html',
32 | inject: true
33 | }),
34 | new FriendlyErrorsPlugin()
35 | ]
36 | })
37 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var webpack = require('webpack')
4 | var config = require('../config')
5 | var merge = require('webpack-merge')
6 | var baseWebpackConfig = require('./webpack.base.conf')
7 | var CopyWebpackPlugin = require('copy-webpack-plugin')
8 | var HtmlWebpackPlugin = require('html-webpack-plugin')
9 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
11 |
12 | var env = process.env.NODE_ENV === 'testing'
13 | ? require('../config/test.env')
14 | : config.build.env
15 |
16 | var webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true
21 | })
22 | },
23 | devtool: config.build.productionSourceMap ? '#source-map' : false,
24 | output: {
25 | path: config.build.assetsRoot,
26 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
28 | },
29 | plugins: [
30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
31 | new webpack.DefinePlugin({
32 | 'process.env': env
33 | }),
34 | new webpack.optimize.UglifyJsPlugin({
35 | compress: {
36 | warnings: false
37 | },
38 | sourceMap: true
39 | }),
40 | // extract css into its own file
41 | new ExtractTextPlugin({
42 | filename: utils.assetsPath('css/[name].[contenthash].css')
43 | }),
44 | // Compress extracted CSS. We are using this plugin so that possible
45 | // duplicated CSS from different components can be deduped.
46 | new OptimizeCSSPlugin({
47 | cssProcessorOptions: {
48 | safe: true
49 | }
50 | }),
51 | // generate dist index.html with correct asset hash for caching.
52 | // you can customize output by editing /index.html
53 | // see https://github.com/ampedandwired/html-webpack-plugin
54 | new HtmlWebpackPlugin({
55 | favicon: 'favicon.ico',
56 | filename: process.env.NODE_ENV === 'testing'
57 | ? 'index.html'
58 | : config.build.index,
59 | template: 'index.html',
60 | inject: true,
61 | minify: {
62 | removeComments: true,
63 | collapseWhitespace: true,
64 | removeAttributeQuotes: true
65 | // more options:
66 | // https://github.com/kangax/html-minifier#options-quick-reference
67 | },
68 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
69 | chunksSortMode: 'dependency'
70 | }),
71 | // split vendor js into its own file
72 | new webpack.optimize.CommonsChunkPlugin({
73 | name: 'vendor',
74 | minChunks: function (module, count) {
75 | // any required modules inside node_modules are extracted to vendor
76 | return (
77 | module.resource &&
78 | /\.js$/.test(module.resource) &&
79 | module.resource.indexOf(
80 | path.join(__dirname, '../node_modules')
81 | ) === 0
82 | )
83 | }
84 | }),
85 | // extract webpack runtime and module manifest to its own file in order to
86 | // prevent vendor hash from being updated whenever app bundle is updated
87 | new webpack.optimize.CommonsChunkPlugin({
88 | name: 'manifest',
89 | chunks: ['vendor']
90 | }),
91 | // copy custom static assets
92 | new CopyWebpackPlugin([
93 | {
94 | from: path.resolve(__dirname, '../static'),
95 | to: config.build.assetsSubDirectory,
96 | ignore: ['.*']
97 | }
98 | ])
99 | ]
100 | })
101 |
102 | if (config.build.productionGzip) {
103 | var CompressionWebpackPlugin = require('compression-webpack-plugin')
104 |
105 | webpackConfig.plugins.push(
106 | new CompressionWebpackPlugin({
107 | asset: '[path].gz[query]',
108 | algorithm: 'gzip',
109 | test: new RegExp(
110 | '\\.(' +
111 | config.build.productionGzipExtensions.join('|') +
112 | ')$'
113 | ),
114 | threshold: 10240,
115 | minRatio: 0.8
116 | })
117 | )
118 | }
119 |
120 | if (config.build.bundleAnalyzerReport) {
121 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
122 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
123 | }
124 |
125 | // deploy to server
126 | if (process.env.COMMAND_ENV === 'deploy') {
127 | var DeployPlugin = require('deploy-webpack-plugin')
128 | webpackConfig.plugins.push(new DeployPlugin({
129 | "receiver": "http://ip:8999/receiver", // optional, deploy to local directory when omitted
130 | "staticDir": "/data/webapps", // .js,.css,images will end up here
131 | "tplDir": "/data/webapps" // .html ends up here
132 | }))
133 | }
134 |
135 | module.exports = webpackConfig
136 |
--------------------------------------------------------------------------------
/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | // This is the webpack config used for unit tests.
2 |
3 | var utils = require('./utils')
4 | var webpack = require('webpack')
5 | var merge = require('webpack-merge')
6 | var baseConfig = require('./webpack.base.conf')
7 |
8 | var webpackConfig = merge(baseConfig, {
9 | // use inline sourcemap for karma-sourcemap-loader
10 | module: {
11 | rules: utils.styleLoaders()
12 | },
13 | devtool: '#inline-source-map',
14 | resolveLoader: {
15 | alias: {
16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
18 | 'scss-loader': 'sass-loader'
19 | }
20 | },
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': require('../config/test.env')
24 | })
25 | ]
26 | })
27 |
28 | // no need for app entry during tests
29 | delete webpackConfig.entry
30 |
31 | module.exports = webpackConfig
32 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var prodEnv = require('./prod.env')
3 |
4 | module.exports = merge(prodEnv, {
5 | NODE_ENV: '"development"'
6 | })
7 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: require('./prod.env'),
7 | index: path.resolve(__dirname, '../dist/index.html'),
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'static',
10 | assetsPublicPath: '/',
11 | productionSourceMap: true,
12 | // Gzip off by default as many popular static hosts such as
13 | // Surge or Netlify already gzip all static assets for you.
14 | // Before setting to `true`, make sure to:
15 | // npm install --save-dev compression-webpack-plugin
16 | productionGzip: false,
17 | productionGzipExtensions: ['js', 'css'],
18 | // Run the build command with an extra argument to
19 | // View the bundle analyzer report after build finishes:
20 | // `npm run build --report`
21 | // Set to `true` or `false` to always turn it on or off
22 | bundleAnalyzerReport: process.env.npm_config_report
23 | },
24 | dev: {
25 | env: require('./dev.env'),
26 | port: 8080,
27 | autoOpenBrowser: true,
28 | assetsSubDirectory: 'static',
29 | assetsPublicPath: '/',
30 | proxyTable: {
31 | '/edu_rest/**': {
32 | target: 'http://other.domain/api',
33 | changeOrigin: true
34 | }
35 | },
36 | // CSS Sourcemaps off by default because relative paths are "buggy"
37 | // with this option, according to the CSS-Loader README
38 | // (https://github.com/webpack/css-loader#sourcemaps)
39 | // In our experience, they generally work as expected,
40 | // just be aware of this issue when enabling this option.
41 | cssSourceMap: false
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var devEnv = require('./dev.env')
3 |
4 | module.exports = merge(devEnv, {
5 | NODE_ENV: '"testing"'
6 | })
7 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoping6688/vue-spa-project/2e4a884259a953b64a1a491bba7f3b73d76fbfa8/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-project
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/mock/get_classrooms.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 | faker.locale = 'zh_CN'
3 |
4 | module.exports = {
5 | api: '/mock/edu_rest/acquire_school_room/:id',
6 | response: function (req, res) {
7 | res.json({
8 | rlt: 'true',
9 | msg: 'ok',
10 | data: [
11 | { classroom_id: faker.random.uuid(), classroom_name: faker.random.words() },
12 | { classroom_id: faker.random.uuid(), classroom_name: faker.random.words() },
13 | { classroom_id: faker.random.uuid(), classroom_name: faker.random.words() }
14 | ]
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/get_region_teachers.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 | faker.locale = 'zh_CN'
3 |
4 | module.exports = {
5 | api: '/mock/edu_rest/acquire_teacher_by_org/:id',
6 | response: function (req, res) {
7 | res.json({
8 | rlt: 'true',
9 | msg: 'ok',
10 | data: [
11 | { teacher_id: faker.random.uuid(), teacher_name: faker.name.findName(), mobile:faker.phone.phoneNumber(), role_name:faker.name.jobType(), subject_name:faker.name.jobTitle(), campus_name:faker.random.words() },
12 | { teacher_id: faker.random.uuid(), teacher_name: faker.name.findName(), mobile:faker.phone.phoneNumber(), role_name:faker.name.jobType(), subject_name:faker.name.jobTitle(), campus_name:faker.random.words() },
13 | { teacher_id: faker.random.uuid(), teacher_name: faker.name.findName(), mobile:faker.phone.phoneNumber(), role_name:faker.name.jobType(), subject_name:faker.name.jobTitle(), campus_name:faker.random.words() }
14 | ]
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/get_regions.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 | faker.locale = 'zh_CN'
3 |
4 | module.exports = {
5 | api: '/mock/edu_rest/acquire_campus/:id',
6 | response: function (req, res) {
7 | res.json({
8 | rlt: 'true',
9 | msg: 'ok',
10 | data: [
11 | { campus_id: faker.random.uuid(), campus_name: faker.address.city() },
12 | { campus_id: faker.random.uuid(), campus_name: faker.address.city() },
13 | { campus_id: faker.random.uuid(), campus_name: faker.address.city() }
14 | ]
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/get_roles.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 | faker.locale = 'zh_CN'
3 |
4 | module.exports = {
5 | api: '/mock/edu_rest/acquire_role/:id',
6 | response: function (req, res) {
7 | res.json({
8 | rlt: 'true',
9 | msg: 'ok',
10 | data: [
11 | { role_id: faker.random.uuid(), role_name: faker.random.words() },
12 | { role_id: faker.random.uuid(), role_name: faker.random.words() },
13 | { role_id: faker.random.uuid(), role_name: faker.random.words() }
14 | ]
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/get_schools.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 | faker.locale = 'zh_CN'
3 |
4 | module.exports = {
5 | api: '/mock/edu_rest/acquire_school/:id',
6 | response: function (req, res) {
7 | res.json({
8 | rlt: 'true',
9 | msg: 'ok',
10 | data: [
11 | { school_id: faker.random.uuid(), school_name: faker.address.streetName() },
12 | { school_id: faker.random.uuid(), school_name: faker.address.streetName() },
13 | { school_id: faker.random.uuid(), school_name: faker.address.streetName() }
14 | ]
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mock/login.js:
--------------------------------------------------------------------------------
1 | const faker = require('faker')
2 |
3 | module.exports = {
4 | api: '/mock/edu_rest/org_login',
5 | response: function (req, res) {
6 | res.json({
7 | rlt: 'true',
8 | msg: 'ok',
9 | data: faker.random.uuid()
10 | })
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-spa-project",
3 | "version": "1.2.1",
4 | "description": "基于Vue.js技术栈的管理系统Demo",
5 | "author": "super_xp@126.com",
6 | "keywords": [
7 | "vue.js",
8 | "spa",
9 | "vue-demo"
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/xiaoping6688/vue-spa-project"
14 | },
15 | "license": "MIT",
16 | "private": false,
17 | "scripts": {
18 | "dev": "node build/dev-server.js",
19 | "start": "node build/dev-server.js",
20 | "build": "node build/build.js",
21 | "deploy": "cross-env COMMAND_ENV=deploy node build/build.js",
22 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
23 | "e2e": "node test/e2e/runner.js",
24 | "test": "npm run unit && npm run e2e",
25 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
26 | "update": "ncu -u -a"
27 | },
28 | "dependencies": {
29 | "blueimp-md5": "^2.7.0",
30 | "element-ui": "^1.3.7",
31 | "normalize.css": "^7.0.0",
32 | "querystring": "^0.2.0",
33 | "vue": "^2.4.1",
34 | "vue-router": "^2.7.0",
35 | "vuex": "^2.3.1",
36 | "vuex-router-sync": "^4.2.0",
37 | "whatwg-fetch": "^2.0.3"
38 | },
39 | "devDependencies": {
40 | "autoprefixer": "^7.1.2",
41 | "babel-core": "^6.25.0",
42 | "babel-eslint": "^7.2.3",
43 | "babel-loader": "^7.1.1",
44 | "babel-plugin-component": "^0.10.0",
45 | "babel-plugin-istanbul": "^4.1.4",
46 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
47 | "babel-plugin-transform-runtime": "^6.23.0",
48 | "babel-preset-env": "^1.6.0",
49 | "babel-preset-stage-2": "^6.24.1",
50 | "babel-register": "^6.24.1",
51 | "chai": "^4.1.0",
52 | "chalk": "^2.0.1",
53 | "chromedriver": "^2.30.1",
54 | "connect-history-api-fallback": "^1.3.0",
55 | "copy-webpack-plugin": "^4.0.1",
56 | "cross-env": "^5.0.1",
57 | "cross-spawn": "^5.1.0",
58 | "css-loader": "^0.28.4",
59 | "cssnano": "^3.10.0",
60 | "deploy-webpack-plugin": "0.0.1",
61 | "eslint": "^4.2.0",
62 | "eslint-config-standard": "^10.2.1",
63 | "eslint-friendly-formatter": "^3.0.0",
64 | "eslint-loader": "^1.9.0",
65 | "eslint-plugin-html": "^3.1.1",
66 | "eslint-plugin-import": "^2.7.0",
67 | "eslint-plugin-node": "^5.2.0",
68 | "eslint-plugin-promise": "^3.5.0",
69 | "eslint-plugin-standard": "^3.0.1",
70 | "eventsource-polyfill": "^0.9.6",
71 | "express": "^4.15.3",
72 | "extract-text-webpack-plugin": "^3.0.0",
73 | "faker": "^4.1.0",
74 | "file-loader": "^0.11.2",
75 | "friendly-errors-webpack-plugin": "^1.6.1",
76 | "html-webpack-plugin": "^2.29.0",
77 | "http-proxy-middleware": "^0.17.4",
78 | "inject-loader": "^3.0.0",
79 | "karma": "^1.7.0",
80 | "karma-coverage": "^1.1.1",
81 | "karma-mocha": "^1.3.0",
82 | "karma-phantomjs-launcher": "^1.0.4",
83 | "karma-phantomjs-shim": "^1.4.0",
84 | "karma-sinon-chai": "^1.3.1",
85 | "karma-sourcemap-loader": "^0.3.7",
86 | "karma-spec-reporter": "0.0.31",
87 | "karma-webpack": "^2.0.4",
88 | "lolex": "^2.1.1",
89 | "mocha": "^3.4.2",
90 | "nightwatch": "^0.9.16",
91 | "node-sass": "^4.5.3",
92 | "opn": "^5.1.0",
93 | "optimize-css-assets-webpack-plugin": "^2.0.0",
94 | "ora": "^1.3.0",
95 | "phantomjs-prebuilt": "^2.1.14",
96 | "prerender-spa-plugin": "^2.0.1",
97 | "rimraf": "^2.6.1",
98 | "sass-loader": "^6.0.6",
99 | "selenium-server": "3.4.0",
100 | "semver": "^5.3.0",
101 | "shelljs": "^0.7.8",
102 | "sinon": "^2.3.8",
103 | "sinon-chai": "^2.12.0",
104 | "url-loader": "^0.5.9",
105 | "vue-loader": "^12.2.2",
106 | "vue-style-loader": "^3.0.1",
107 | "vue-template-compiler": "^2.4.1",
108 | "webpack": "^3.3.0",
109 | "webpack-bundle-analyzer": "^2.8.3",
110 | "webpack-dev-middleware": "^1.11.0",
111 | "webpack-hot-middleware": "^2.18.2",
112 | "webpack-merge": "^4.1.0"
113 | },
114 | "engines": {
115 | "node": ">= 6.0.0",
116 | "npm": ">= 3.0.0"
117 | },
118 | "browserslist": [
119 | "> 1%",
120 | "last 2 versions",
121 | "not ie <= 8"
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/src/assets/common.scss:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | -moz-osx-font-smoothing: grayscale;
5 | background-color: #d2d6de;;
6 | height: 100%;
7 | }
8 |
9 | .textspan {
10 | color: #99a9bf;
11 | padding: 10px 15px;
12 | float: right;
13 | font-size: 14px;
14 | }
15 |
16 | .queryinput {
17 | width: 90%;
18 | }
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoping6688/vue-spa-project/2e4a884259a953b64a1a491bba7f3b73d76fbfa8/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 | /**
2 | * 面包屑组件
3 | */
4 |
5 |
6 |
7 | {{ item.label }}
8 |
9 |
10 |
11 |
21 |
22 |
29 |
--------------------------------------------------------------------------------
/src/components/Header.vue:
--------------------------------------------------------------------------------
1 | /**
2 | * 顶栏导航组件
3 | */
4 |
5 |
6 |
20 |
21 |
22 |
36 |
37 |
57 |
--------------------------------------------------------------------------------
/src/components/PageProgress.vue:
--------------------------------------------------------------------------------
1 | /**
2 | * 页面进度组件
3 | */
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
41 |
--------------------------------------------------------------------------------
/src/components/Sidebar.vue:
--------------------------------------------------------------------------------
1 | /**
2 | * 侧栏导航组件
3 | */
4 |
5 |
6 |
7 |
8 |
9 | 机构
10 | 校区管理
11 | 教室管理
12 |
13 |
14 | 教师
15 | 角色管理
16 | 教师管理
17 |
18 |
19 |
20 |
21 |
22 |
38 |
39 |
47 |
--------------------------------------------------------------------------------
/src/components/UIComponents.js:
--------------------------------------------------------------------------------
1 |
2 | import Vue from 'vue'
3 |
4 | // 使用element组件库(完整引入)
5 | import ElementUI from 'element-ui'
6 | import 'element-ui/lib/theme-default/index.css'
7 | Vue.use(ElementUI)
8 |
9 | // 按需引入
10 | // import {
11 | // Row,
12 | // Col,
13 | // Icon,
14 | // Button,
15 | // Radio,
16 | // RadioGroup,
17 | // Checkbox,
18 | // CheckboxGroup,
19 | // Input,
20 | // Select,
21 | // Option,
22 | // TimePicker,
23 | // DatePicker,
24 | // Form,
25 | // FormItem,
26 | // Table,
27 | // TableColumn,
28 | // Progress,
29 | // Pagination,
30 | // Alert,
31 | // Loading,
32 | // Message,
33 | // MessageBox,
34 | // Notification,
35 | // Menu,
36 | // Submenu,
37 | // MenuItem,
38 | // Breadcrumb,
39 | // BreadcrumbItem,
40 | // Dialog
41 | // } from 'element-ui'
42 |
43 | // const components = [
44 | // Row,
45 | // Col,
46 | // Icon,
47 | // Button,
48 | // Radio,
49 | // RadioGroup,
50 | // Checkbox,
51 | // CheckboxGroup,
52 | // Input,
53 | // Select,
54 | // Option,
55 | // TimePicker,
56 | // DatePicker,
57 | // Form,
58 | // FormItem,
59 | // Table,
60 | // TableColumn,
61 | // Progress,
62 | // Pagination,
63 | // Alert,
64 | // Loading,
65 | // Message,
66 | // MessageBox,
67 | // Notification,
68 | // Menu,
69 | // Submenu,
70 | // MenuItem,
71 | // Breadcrumb,
72 | // BreadcrumbItem,
73 | // Dialog
74 | // ]
75 |
76 | // components.forEach((item) => {
77 | // Vue.component(item.name, item)
78 | // })
79 |
80 | // 解决组件自动弹出问题
81 | // Vue.prototype.$message = Message
82 | // Vue.prototype.$notify = Notification
83 |
--------------------------------------------------------------------------------
/src/filters/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 过滤器
3 | */
4 |
5 | /**
6 | * 对日期进行格式化,
7 | * @param date 要格式化的日期
8 | * @param format 进行格式化的模式字符串
9 | * 支持的模式字母有:
10 | * y:年,
11 | * M:年中的月份(1-12),
12 | * d:月份中的天(1-31),
13 | * h:小时(0-23),
14 | * m:分(0-59),
15 | * s:秒(0-59),
16 | * S:毫秒(0-999),
17 | * q:季度(1-4)
18 | * @return String
19 | */
20 | export function dateFormat (date, format) {
21 | date = new Date(date)
22 | let map = {
23 | 'M': date.getMonth() + 1, // 月份
24 | 'd': date.getDate(), // 日
25 | 'h': date.getHours(), // 小时
26 | 'm': date.getMinutes(), // 分
27 | 's': date.getSeconds(), // 秒
28 | 'q': Math.floor((date.getMonth() + 3) / 3), // 季度
29 | 'S': date.getMilliseconds() // 毫秒
30 | }
31 |
32 | format = format.replace(/([yMdhmsqS])+/g, function (all, t) {
33 | let v = map[t]
34 | if (v !== undefined) {
35 | if (all.length > 1) {
36 | v = '0' + v
37 | v = v.substr(v.length - 2)
38 | }
39 | return v
40 | } else if (t === 'y') {
41 | return (date.getFullYear() + '').substr(4 - all.length)
42 | }
43 | return all
44 | })
45 |
46 | return format
47 | }
48 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { sync } from 'vuex-router-sync'
3 |
4 | import App from './App'
5 | import store from './store'
6 | import router from './routes'
7 | import * as filters from './filters'
8 |
9 | // sync the router with the vuex store.
10 | // this registers `store.state.route`
11 | sync(store, router)
12 |
13 | // register global utility filters.
14 | Object.keys(filters).forEach(key => {
15 | Vue.filter(key, filters[key])
16 | })
17 |
18 | // bootstrap vue app
19 | const app = new Vue({
20 | router,
21 | store,
22 | el: '#app',
23 | template: '',
24 | components: { App }
25 | })
26 |
27 | // expose the app, the router and the store.
28 | // note we are not mounting the app here, since bootstrapping will be
29 | // different depending on whether we are in a browser or on the server.
30 | export { app, router, store }
31 |
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 路由配置
3 | */
4 |
5 | import Vue from 'vue'
6 | import Router from 'vue-router'
7 | import store from '../store'
8 |
9 | // 异步组件实现路由懒加载
10 | // const Login = resolve => require(['../views/Login'], resolve)
11 | // const Regist = resolve => require(['../views/Regist'], resolve)
12 | // const NotFound = resolve => require(['../views/NotFound'], resolve)
13 |
14 | // 组件按组分块
15 | // const Home = r => require.ensure([], () => r(require('../views/Home')), 'group-main')
16 | // const CampusManage = r => require.ensure([], () => r(require('../views/main/CampusManage')), 'group-main')
17 | // const ClassroomManage = r => require.ensure([], () => r(require('../views/main/ClassroomManage')), 'group-main')
18 | // const RoleManage = r => require.ensure([], () => r(require('../views/main/RoleManage')), 'group-main')
19 | // const TeacherManage = r => require.ensure([], () => r(require('../views/main/TeacherManage')), 'group-main')
20 |
21 | // Webpack 2 + ES2015 写法,可设置 Chunk 名
22 | const Login = () => import(/* webpackChunkName: 'Login' */ '../views/Login')
23 | const Regist = () => import(/* webpackChunkName: 'Regist' */ '../views/Regist')
24 | const NotFound = () => import('../views/NotFound')
25 |
26 | const Home = () => import(/* webpackChunkName: 'group-main' */ '../views/Home')
27 | const CampusManage = () => import(/* webpackChunkName: 'group-main' */ '../views/main/CampusManage')
28 | const ClassroomManage = () => import(/* webpackChunkName: 'group-main' */ '../views/main/ClassroomManage')
29 | const RoleManage = () => import(/* webpackChunkName: 'group-main' */ '../views/main/RoleManage')
30 | const TeacherManage = () => import(/* webpackChunkName: 'group-main' */ '../views/main/TeacherManage')
31 |
32 | Vue.use(Router)
33 |
34 | const router = new Router({
35 | mode: 'hash', // history
36 | routes: [
37 | { path: '/login', name: 'login', component: Login },
38 | { path: '/regist', name: 'regist', component: Regist },
39 | {
40 | path: '/main',
41 | component: Home,
42 | children: [
43 | {
44 | path: '',
45 | redirect: '/campus-manage'
46 | },
47 | {
48 | path: 'campus-manage',
49 | name: 'campusmanage',
50 | alias: '/campus-manage',
51 | component: CampusManage,
52 | meta: { requiresAuth: true }
53 | },
54 | {
55 | path: 'classroom-manage',
56 | name: 'classroommanage',
57 | alias: '/classroom-manage',
58 | component: ClassroomManage,
59 | meta: { requiresAuth: true }
60 | },
61 | {
62 | path: 'role-manage',
63 | name: 'rolemanage',
64 | alias: '/role-manage',
65 | component: RoleManage,
66 | meta: { requiresAuth: true }
67 | },
68 | {
69 | path: 'teacher-manage',
70 | name: 'teachermanage',
71 | alias: '/teacher-manage',
72 | component: TeacherManage,
73 | meta: { requiresAuth: true }
74 | }
75 | ]
76 | },
77 | { path: '/', redirect: '/campus-manage' },
78 | { path: '*', component: NotFound }
79 | ],
80 | scrollBehavior (to, from, savedPosition) {
81 | if (savedPosition) {
82 | return savedPosition
83 | } else {
84 | return { x: 0, y: 0 }
85 | }
86 | }
87 | })
88 |
89 | // 路由钩子:每个路由开始前处理
90 | router.beforeEach((to, from, next) => {
91 | // 登录中间验证,页面需要登录而没有登录的情况直接跳转登录
92 | if (to.matched.some(record => record.meta.requiresAuth)) {
93 | if (store.getters.hasLoggedIn) {
94 | store.dispatch('setPageProgress', 60)
95 | next()
96 | } else {
97 | next({
98 | path: '/login',
99 | query: { redirect: to.fullPath }
100 | })
101 | }
102 | } else {
103 | next()
104 | }
105 | })
106 |
107 | // 路由钩子:每个路由结束后处理
108 | router.afterEach(route => {
109 | // window.scrollTo(0, 0)
110 | store.dispatch('setPageProgress', 100)
111 | })
112 |
113 | export default router
114 |
--------------------------------------------------------------------------------
/src/services/API.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 接口配置
3 | */
4 |
5 | // const BaseUrl = 'http://www/api' // for production
6 | // const BaseUrl = '' // for local poxy
7 | const BaseUrl = '/mock' // for local mock test
8 |
9 | export const LOGIN = BaseUrl + '/edu_rest/org_login' // 登录
10 | export const REGIST = BaseUrl + '/edu_rest/org_register' // 注册
11 |
12 | export const GET_REGIONS = BaseUrl + '/edu_rest/acquire_campus' // 获取校区
13 | export const ADD_REGION = BaseUrl + '/edu_rest/establish_campus' // 添加校区
14 | export const ADD_SCHOOL = BaseUrl + '/edu_rest/establish_school' // 添加学校
15 | export const GET_SCHOOLS = BaseUrl + '/edu_rest/acquire_school' // 获取学校(服务中心)
16 |
17 | export const GET_CLASSROOMS = BaseUrl + '/edu_rest/acquire_school_room' // 获取教室
18 | export const ADD_CLASSROOM = BaseUrl + '/edu_rest/establish_school_room' // 添加教室
19 |
20 | export const GET_ROLES = BaseUrl + '/edu_rest/acquire_role' // 获取角色
21 | export const ADD_ROLE = BaseUrl + '/edu_rest/establish_role' // 添加角色
22 |
23 | export const GET_REGION_TEACHERS = BaseUrl + '/edu_rest/acquire_teacher_by_org' // 获取机构教师
24 | export const GET_TEACHERS = BaseUrl + '/edu_rest/acquire_teacher' // 获取教师 辅导老师?
25 | export const ADD_TEACHER = BaseUrl + '/edu_rest/establish_teacher' // 添加教师
26 |
27 | export const GET_SUBJECTS = BaseUrl + '/edu_rest/acquire_subject' // 获取科目
28 | export const ADD_SUBJECT = BaseUrl + '/edu_rest/establish_subject' // 添加科目
29 |
30 | export const GET_YEARS = BaseUrl + '/edu_rest/acquire_year' // 获取年份
31 | export const GET_TERMS = BaseUrl + '/edu_rest/acquire_term' // 获取学期
32 | export const GET_GRADES = BaseUrl + '/edu_rest/acquire_grade' // 获取年级
33 |
--------------------------------------------------------------------------------
/src/services/account.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 用户Service
3 | */
4 |
5 | import * as API from './API'
6 | import { post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import AccountVO from './model/AccountVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 用户登录
16 | * @return {Object} 返回结果Promise
17 | */
18 | export function login (payload) {
19 | let it = payload[Symbol.iterator]()
20 | let args = {
21 | username: it.next().value,
22 | password: it.next().value
23 | }
24 |
25 | let promise = post(API.LOGIN, args, { headers: headers })
26 | return httpResultProxy(promise, AccountVO)
27 | }
28 |
29 | /**
30 | * 注册
31 | */
32 | export function regist (payload) {
33 | let it = payload[Symbol.iterator]()
34 | let args = {
35 | username: it.next().value,
36 | password: it.next().value
37 | }
38 |
39 | let promise = post(API.REGIST, args, { headers: headers })
40 | return httpResultProxy(promise)
41 | }
42 |
43 | /**
44 | * 退出
45 | */
46 | export function logout () {
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/services/campus.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 校区管理Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get, post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import CampusVO from './model/CampusVO'
9 | import SchoolVO from './model/SchoolVO'
10 |
11 | let headers = {
12 | 'plat': 'principal-plat'
13 | }
14 |
15 | /**
16 | * 获取校区
17 | * @param {String} orgId 机构ID
18 | */
19 | export function getRegions (orgId) {
20 | let promise = get(API.GET_REGIONS + `/${orgId}`, null, { headers: headers })
21 | return httpResultProxy(promise, CampusVO)
22 | }
23 |
24 | /**
25 | * 添加校区
26 | */
27 | export function addRegion (payload) {
28 | let it = payload[Symbol.iterator]()
29 | let args = {
30 | org_id: it.next().value,
31 | campus_name: it.next().value
32 | }
33 |
34 | let promise = post(API.ADD_REGION, args, { headers: headers })
35 | return httpResultProxy(promise)
36 | }
37 |
38 | /**
39 | * 获取服务中心
40 | * @param {String} campusId 校区ID
41 | */
42 | export function getSchools (campusId) {
43 | let promise = get(API.GET_SCHOOLS + `/${campusId}`, null, { headers: headers })
44 | return httpResultProxy(promise, SchoolVO)
45 | }
46 |
47 | /**
48 | * 添加服务中心
49 | */
50 | export function addSchool (payload) {
51 | let it = payload[Symbol.iterator]()
52 | let args = {
53 | org_id: it.next().value,
54 | campus_id: it.next().value,
55 | school_name: it.next().value
56 | }
57 |
58 | let promise = post(API.ADD_SCHOOL, args, { headers: headers })
59 | return httpResultProxy(promise)
60 | }
61 |
--------------------------------------------------------------------------------
/src/services/classroom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 教室管理Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get, post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import ClassroomVO from './model/ClassroomVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 获取教室
16 | */
17 | export function getClassrooms (schoolId) {
18 | let promise = get(API.GET_CLASSROOMS + `/${schoolId}`, null, { headers: headers })
19 | return httpResultProxy(promise, ClassroomVO)
20 | }
21 |
22 | /**
23 | * 添加教室
24 | */
25 | export function addClassroom (payload) {
26 | let it = payload[Symbol.iterator]()
27 | let args = {
28 | org_id: it.next().value,
29 | campus_id: it.next().value,
30 | school_id: it.next().value,
31 | school_room_name: it.next().value
32 | }
33 |
34 | let promise = post(API.ADD_CLASSROOM, args, { headers: headers })
35 | return httpResultProxy(promise)
36 | }
37 |
--------------------------------------------------------------------------------
/src/services/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 公共Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import CommonVO from './model/CommonVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 获取年份
16 | */
17 | export function getYears (orgId) {
18 | let promise = get(API.GET_YEARS + `/${orgId}`, null, { headers: headers })
19 | return httpResultProxy(promise, CommonVO)
20 | }
21 |
22 | /**
23 | * 获取学期
24 | */
25 | export function getTerms (orgId) {
26 | let promise = get(API.GET_TERMS + `/${orgId}`, null, { headers: headers })
27 | return httpResultProxy(promise, CommonVO)
28 | }
29 |
30 | /**
31 | * 获取年级
32 | */
33 | export function getGrades (orgId) {
34 | let promise = get(API.GET_GRADES + `/${orgId}`, null, { headers: headers })
35 | return httpResultProxy(promise, CommonVO)
36 | }
37 |
--------------------------------------------------------------------------------
/src/services/model/AccountVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 账号信息VO
3 | */
4 |
5 | export default class AccountVO {
6 | constructor (data) {
7 | this.userId = data || ''
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/services/model/CampusVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 校区信息VO
3 | */
4 |
5 | export default class CampusVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.campus_id || ''
10 | this.name = data.campus_name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/ClassroomVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 教室信息VO
3 | */
4 |
5 | export default class ClassroomVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.classroom_id || ''
10 | this.name = data.classroom_name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/CommonVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 通用信息VO
3 | */
4 |
5 | export default class CommonVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.id || ''
10 | this.name = data.name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/RoleVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 角色信息VO
3 | */
4 |
5 | export default class RoleVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.role_id || ''
10 | this.name = data.role_name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/SchoolVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 服务中心VO
3 | */
4 |
5 | export default class SchoolVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.school_id || ''
10 | this.name = data.school_name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/SubjectVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 学科信息VO
3 | */
4 |
5 | export default class SubjectVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.id = data.subject_id || ''
10 | this.name = data.subject_name || ''
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/model/TeacherVO.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 教师信息VO
3 | */
4 |
5 | export default class TeacherVO {
6 | constructor (data) {
7 | data = data || {}
8 |
9 | this.teacherId = data.teacher_id || ''
10 | this.teacherName = data.teacher_name || ''
11 | this.mobile = data.mobile || ''
12 | this.roleName = data.role_name || ''
13 | this.subjectName = data.subject_name || ''
14 | this.campusName = data.campus_name || ''
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/services/role.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 角色管理Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get, post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import RoleVO from './model/RoleVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 获取角色
16 | */
17 | export function getRoles (orgId) {
18 | let promise = get(API.GET_ROLES + `/${orgId}`, null, { headers: headers })
19 | return httpResultProxy(promise, RoleVO)
20 | }
21 |
22 | /**
23 | * 添加角色
24 | */
25 | export function addRole (payload) {
26 | let it = payload[Symbol.iterator]()
27 | let args = {
28 | org_id: it.next().value,
29 | role_name: it.next().value
30 | }
31 |
32 | let promise = post(API.ADD_ROLE, args, { headers: headers })
33 | return httpResultProxy(promise)
34 | }
35 |
--------------------------------------------------------------------------------
/src/services/subject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 学科管理Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get, post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import SubjectVO from './model/SubjectVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 获取学科
16 | */
17 | export function getSubjects (orgId) {
18 | let promise = get(API.GET_SUBJECTS + `/${orgId}`, null, { headers: headers })
19 | return httpResultProxy(promise, SubjectVO)
20 | }
21 |
22 | /**
23 | * 添加学科
24 | */
25 | export function addSubject (payload) {
26 | let it = payload[Symbol.iterator]()
27 | let args = {
28 | org_id: it.next().value,
29 | subject_name: it.next().value
30 | }
31 |
32 | let promise = post(API.ADD_SUBJECT, args, { headers: headers })
33 | return httpResultProxy(promise)
34 | }
35 |
--------------------------------------------------------------------------------
/src/services/teacher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 教师管理Service
3 | */
4 |
5 | import * as API from './API'
6 | import { get, post } from '../utils/http'
7 | import { httpResultProxy } from '../utils/proxy'
8 | import TeacherVO from './model/TeacherVO'
9 |
10 | let headers = {
11 | 'plat': 'principal-plat'
12 | }
13 |
14 | /**
15 | * 获取教师
16 | */
17 | export function getTeachers (orgId) {
18 | let promise = get(API.GET_REGION_TEACHERS + `/${orgId}`, null, { headers: headers })
19 | return httpResultProxy(promise, TeacherVO)
20 | }
21 |
22 | /**
23 | * 添加教师
24 | */
25 | export function addTeacher (payload) {
26 | let it = payload[Symbol.iterator]()
27 | let args = {
28 | campus_id: it.next().value,
29 | desc: it.next().value,
30 | grade_id: it.next().value,
31 | nature_work: it.next().value,
32 | org_id: it.next().value,
33 | role_id: it.next().value,
34 | subject_id: it.next().value,
35 | teacher_email: it.next().value,
36 | teacher_mobil: it.next().value,
37 | teacher_name: it.next().value,
38 | teacher_sex: it.next().value
39 | }
40 |
41 | let promise = post(API.ADD_TEACHER, args, { headers: headers })
42 | return httpResultProxy(promise)
43 | }
44 |
--------------------------------------------------------------------------------
/src/store/actions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局action处理
3 | */
4 |
5 | import * as types from './mutation-types'
6 |
7 | /**
8 | * 设置页面进度
9 | */
10 | export const setPageProgress = ({ commit }, progress) => {
11 | commit(types.SET_PAGE_PROGRESS, progress)
12 | if (progress === 100) {
13 | setTimeout(() => {
14 | commit(types.SET_PAGE_PROGRESS, 0)
15 | }, 500)
16 | }
17 | }
18 |
19 | /**
20 | * 重置STORE
21 | */
22 | export const resetStore = ({ commit }) => {
23 | commit(types.RESET_STATE)
24 | }
25 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局getters函数
3 | */
4 |
5 | export {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import * as actions from './actions'
4 | import * as getters from './getters'
5 | import { state, mutations } from './mutations'
6 | import account from './modules/account'
7 |
8 | Vue.use(Vuex)
9 |
10 | let isDebugMode = process.env.NODE_ENV !== 'production'
11 |
12 | const store = new Vuex.Store({
13 | state,
14 | getters,
15 | actions,
16 | mutations,
17 | modules: {
18 | account
19 | },
20 | strict: isDebugMode
21 | })
22 |
23 | // 热重载
24 | if (module.hot) {
25 | module.hot.accept([
26 | './getters',
27 | './actions',
28 | './mutations',
29 | './modules/account'
30 | ], () => {
31 | // 因为 babel 6 的模块编译格式问题,这里需要加上 .default
32 | store.hotUpdate({
33 | getters: require('./getters').default,
34 | actions: require('./actions').default,
35 | mutations: require('./mutations').default,
36 | modules: {
37 | account: require('./modules/account').default
38 | }
39 | })
40 | })
41 | }
42 |
43 | export default store
44 |
--------------------------------------------------------------------------------
/src/store/modules/account.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 用户模块
3 | */
4 |
5 | import * as service from '../../services/account'
6 | import * as types from '../mutation-types'
7 | import AccountVO from '../../services/model/AccountVO'
8 |
9 | let userId = window.sessionStorage.userId || ''
10 |
11 | // initial state
12 | const state = new AccountVO(userId)
13 |
14 | // getters
15 | const getters = {
16 | hasLoggedIn: state => state.userId
17 | }
18 |
19 | // actions
20 | const actions = {
21 | login ({ commit, state }, payload) {
22 | return service.login(payload).then(account => {
23 | window.sessionStorage.userId = account.userId
24 | commit(types.SET_ACCOUNT, account)
25 | })
26 | },
27 | regist ({ commit, state }, payload) {
28 | return service.regist(payload).then(data => {
29 | window.sessionStorage.userId = data
30 | commit(types.SET_ACCOUNT, { userId: data })
31 | })
32 | },
33 | logout ({ commit, state }) {
34 | window.sessionStorage.clear()
35 | commit(types.RESET_STATE)
36 | }
37 | }
38 |
39 | // mutations
40 | const mutations = {
41 | [types.SET_ACCOUNT] (state, data) {
42 | Object.assign(state, data)
43 | },
44 | [types.RESET_STATE] (state) {
45 | state.userId = '' // TODO
46 | }
47 | }
48 |
49 | export default {
50 | state,
51 | getters,
52 | actions,
53 | mutations
54 | }
55 |
--------------------------------------------------------------------------------
/src/store/mutation-types.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mutation类型配置
3 | */
4 |
5 | export const SET_PAGE_PROGRESS = 'SET_PAGE_PROGRESS'
6 | export const RESET_STATE = 'RESET_STATE'
7 |
8 | export const SET_ACCOUNT = 'SET_ACCOUNT'
9 |
--------------------------------------------------------------------------------
/src/store/mutations.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 全局mutation处理
3 | */
4 |
5 | import * as types from './mutation-types'
6 |
7 | export const state = {
8 | pageProgress: 0
9 | }
10 |
11 | export const mutations = {
12 | // 设置页面进度
13 | [types.SET_PAGE_PROGRESS] (state, progress) {
14 | state.pageProgress = progress
15 | },
16 | // 重置state
17 | [types.RESET_STATE] (state) {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/utils/http.js:
--------------------------------------------------------------------------------
1 | /**
2 | * HTTP数据通信模块
3 | */
4 |
5 | import 'whatwg-fetch'
6 | import qs from 'querystring'
7 |
8 | // 缺省请求头
9 | const defaultHeaders = {
10 | 'Accept': 'application/json',
11 | 'Content-Type': 'application/json' // application/x-www-form-urlencoded
12 | }
13 |
14 | // 请求参数加工
15 | function mutate (url, { headers, body, query, ...options }) {
16 | headers = { ...defaultHeaders, ...headers }
17 | options.headers = headers
18 |
19 | if (body) {
20 | if (typeof body === 'object') {
21 | body = JSON.stringify(body)
22 | }
23 | options.body = body
24 | }
25 |
26 | if (query) {
27 | if (typeof query === 'object') {
28 | query = qs.stringify(query)
29 | }
30 | url += (url.indexOf('?') !== -1) ? '&' : '?'
31 | url += query
32 | }
33 |
34 | options.credentials = 'omit' // omit,same-origin,include
35 | options.mode = 'same-origin' // "same-origin", "cors", "no-cors", "navigate", or "websocket"
36 |
37 | return [url, options]
38 | }
39 |
40 | function checkStatus (response) {
41 | if (response.status >= 200 && response.status < 300) {
42 | return response
43 | } else {
44 | var error = new Error(response.statusText)
45 | error.response = response
46 | throw error
47 | }
48 | }
49 |
50 | function parseJSON (response) {
51 | return response.json()
52 | }
53 |
54 | /**
55 | * 接口调用服务
56 | * @param {String} url 接口API(必填)
57 | * @param {Object} options 请求参数(选填){headers, body, query, ...options}
58 | * @return {Object} Promise
59 | * @throws {Error}
60 | */
61 | const callService = (url, options = {}) => {
62 | return window.fetch(...mutate(url, options))
63 | .then(checkStatus)
64 | .then(parseJSON)
65 | .catch(error => {
66 | console.log(error)
67 | throw error
68 | })
69 | }
70 |
71 | export default callService
72 |
73 | export const get = (url, args, options = {}) => {
74 | options.method = 'GET'
75 | options.query = args
76 | return callService(url, options)
77 | }
78 |
79 | export const post = (url, args, options = {}) => {
80 | options.method = 'POST'
81 | options.body = args
82 | return callService(url, options)
83 | }
84 |
85 | export const put = (url, args, options = {}) => {
86 | options.method = 'PUT'
87 | options.body = args
88 | return callService(url, options)
89 | }
90 |
91 | export const patch = (url, args, options = {}) => {
92 | options.method = 'PATCH'
93 | options.body = args
94 | return callService(url, options)
95 | }
96 |
97 | export const del = (url, args, options = {}) => {
98 | options.method = 'DELETE'
99 | options.query = args
100 | return callService(url, options)
101 | }
102 |
--------------------------------------------------------------------------------
/src/utils/md5.js:
--------------------------------------------------------------------------------
1 | /**
2 | * md5加密(with secretkey)
3 | */
4 |
5 | import md5 from 'blueimp-md5'
6 |
7 | export default value => md5(value, 'tal')
8 |
--------------------------------------------------------------------------------
/src/utils/proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 对http请求参数代理
3 | */
4 | export function argumentsProxy (vo) {
5 | if (vo) {
6 | if (typeof vo.revert === 'function') {
7 | return vo.revert()
8 | } else {
9 | return vo
10 | }
11 | } else {
12 | return null
13 | }
14 | }
15 |
16 | /**
17 | * 对http结果代理
18 | */
19 | export function httpResultProxy (resultPromise, VO = null) {
20 | return new Promise((resolve, reject) => {
21 | resultPromise.then(res => {
22 | if (res) {
23 | if (res.rlt === 'true') {
24 | resolve(mapVO(VO, res.data))
25 | } else {
26 | reject(new Error(res.msg))
27 | }
28 | } else {
29 | reject(new Error('服务器返回数据异常'))
30 | }
31 | }, (error) => {
32 | throw error
33 | }).catch(error => {
34 | if (error.response) {
35 | // throw new Error('服务器异常!')
36 | reject(new Error('服务器异常!'))
37 | } else {
38 | // throw new Error('系统异常!')
39 | reject(new Error('服务器异常!'))
40 | }
41 | })
42 | })
43 | }
44 |
45 | function mapVO (VO, data) {
46 | if (VO) {
47 | if (Array.isArray(data)) {
48 | let arr = []
49 | for (let item of data) {
50 | arr.push(new VO(item))
51 | }
52 | return arr
53 | } else {
54 | return new VO(data)
55 | }
56 | } else {
57 | return data
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/utils/util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 工具类、实用函数
3 | * @see lodash
4 | */
5 |
6 | /**
7 | * 找出第一个符合条件的数组成员
8 | */
9 | export function findItem (arr, key, value) {
10 | return arr.find(item => item[key] === value)
11 | }
12 |
13 | export function findNameById (arr, id) {
14 | let item = findItem(arr, 'id', id)
15 | return item ? item.name : ''
16 | }
17 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
31 |
32 |
42 |
--------------------------------------------------------------------------------
/src/views/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
首页
4 |
5 |
使用手册
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 用户满意度评价:
14 |
15 |
16 |
17 |
18 |
28 |
29 |
42 |
--------------------------------------------------------------------------------
/src/views/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
云教务
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 登录
22 | 新用户注册
23 |
24 |
25 |
26 |
27 |
28 |
85 |
86 |
105 |
--------------------------------------------------------------------------------
/src/views/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 | 404,没有找到文件!
3 |
4 |
5 |
10 |
11 |
18 |
--------------------------------------------------------------------------------
/src/views/Regist.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
用户注册
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 立即注册
19 | 返回登录
20 |
21 |
22 |
23 |
24 |
25 |
102 |
103 |
124 |
--------------------------------------------------------------------------------
/src/views/main/CampusManage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 校区
8 |
9 |
10 |
11 |
12 |
13 | 查询
14 |
15 |
16 | 添加
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 添加服务中心
26 | 删除
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 | 确定是要删除该校区吗
47 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
189 |
190 |
193 |
--------------------------------------------------------------------------------
/src/views/main/ClassroomManage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 校区
7 |
8 |
9 |
10 |
11 |
12 |
13 | 服务中心
14 |
15 |
16 |
17 |
18 |
19 |
20 | 教室
21 |
22 |
23 |
24 |
25 | 查询
26 |
27 |
28 | 添加
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 删除
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
66 |
67 |
68 |
69 |
70 | 确定是要删除该教室吗
71 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
218 |
219 |
222 |
--------------------------------------------------------------------------------
/src/views/main/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
主页
4 |
5 | 显示一些数据统计、分析等图文信息...
6 |
7 |
8 |
9 |
10 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/src/views/main/RoleManage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 角色
7 |
8 |
9 |
10 |
11 | 查询
12 |
13 |
14 | 添加
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 删除
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 | 确定是要删除该角色吗
46 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
138 |
139 |
142 |
--------------------------------------------------------------------------------
/src/views/main/TeacherManage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 教师姓名
7 |
8 |
9 |
10 |
11 | 查询
12 |
13 |
14 | 添加
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 编辑
34 | 删除
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 男
97 | 女
98 |
99 |
100 |
101 |
102 | 全职
103 | 兼职
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
115 |
116 |
117 |
118 |
119 | 确定是要删除该教师吗
120 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
277 |
278 |
281 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoping6688/vue-spa-project/2e4a884259a953b64a1a491bba7f3b73d76fbfa8/static/.gitkeep
--------------------------------------------------------------------------------
/static/img/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoping6688/vue-spa-project/2e4a884259a953b64a1a491bba7f3b73d76fbfa8/static/img/screenshot.png
--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // the name of the method is the filename.
3 | // can be used in tests like this:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // for how to write custom assertions see
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 | exports.assertion = function (selector, count) {
10 | this.message = 'Testing if element <' + selector + '> has count: ' + count
11 | this.expected = count
12 | this.pass = function (val) {
13 | return val === this.expected
14 | }
15 | this.value = function (res) {
16 | return res.value
17 | }
18 | this.command = function (cb) {
19 | var self = this
20 | return this.api.execute(function (selector) {
21 | return document.querySelectorAll(selector).length
22 | }, [selector], function (res) {
23 | cb.call(self, res)
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/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 | var server = require('../../build/dev-server.js')
4 |
5 | server.ready.then(() => {
6 | // 2. run the nightwatch test suite against it
7 | // to run in additional browsers:
8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
9 | // 2. add it to the --env flag below
10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
11 | // For more information on Nightwatch's config file, see
12 | // http://nightwatchjs.org/guide#settings-file
13 | var opts = process.argv.slice(2)
14 | if (opts.indexOf('--config') === -1) {
15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
16 | }
17 | if (opts.indexOf('--env') === -1) {
18 | opts = opts.concat(['--env', 'chrome'])
19 | }
20 |
21 | var spawn = require('cross-spawn')
22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
23 |
24 | runner.on('exit', function (code) {
25 | server.close()
26 | process.exit(code)
27 | })
28 |
29 | runner.on('error', function (err) {
30 | server.close()
31 | throw err
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/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 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/unit/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
5 | // require all test files (files that ends with .spec.js)
6 | const testsContext = require.context('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | // This is a karma config file. For more details see
2 | // http://karma-runner.github.io/0.13/config/configuration-file.html
3 | // we are also using it with karma-webpack
4 | // https://github.com/webpack/karma-webpack
5 |
6 | var webpackConfig = require('../../build/webpack.test.conf')
7 |
8 | module.exports = function (config) {
9 | config.set({
10 | // to run in additional browsers:
11 | // 1. install corresponding karma launcher
12 | // http://karma-runner.github.io/0.13/config/browsers.html
13 | // 2. add it to the `browsers` array below.
14 | browsers: ['PhantomJS'],
15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
16 | reporters: ['spec', 'coverage'],
17 | files: ['./index.js'],
18 | preprocessors: {
19 | './index.js': ['webpack', 'sourcemap']
20 | },
21 | webpack: webpackConfig,
22 | webpackMiddleware: {
23 | noInfo: true
24 | },
25 | coverageReporter: {
26 | dir: './coverage',
27 | reporters: [
28 | { type: 'lcov', subdir: '.' },
29 | { type: 'text-summary' }
30 | ]
31 | }
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/test/unit/specs/Hello.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Hello from '@/components/Hello'
3 |
4 | describe('Hello.vue', () => {
5 | it('should render correct contents', () => {
6 | const Constructor = Vue.extend(Hello)
7 | const vm = new Constructor().$mount()
8 | expect(vm.$el.querySelector('.hello h1').textContent)
9 | .to.equal('Welcome to Your Vue.js App')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------