├── .gitignore
├── LICENSE
├── README.md
├── client
├── .babelrc
├── .editorconfig
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── dist
│ ├── index.html
│ └── static
│ │ ├── css
│ │ ├── app.28f2284184a8d1356fab6b9af38dc00b.css
│ │ ├── app.28f2284184a8d1356fab6b9af38dc00b.css.map
│ │ └── main.css
│ │ ├── fonts
│ │ ├── element-icons.535877f.woff
│ │ └── element-icons.732389d.ttf
│ │ ├── img
│ │ └── logo.39aabf3.png
│ │ ├── js
│ │ ├── app.a775ce576d6ca3078d44.js
│ │ ├── app.a775ce576d6ca3078d44.js.map
│ │ ├── axios.js
│ │ ├── base64.js
│ │ ├── config.js
│ │ ├── httpRequest.js
│ │ ├── main.js
│ │ ├── manifest.2ae2e69a05c33dfc65f8.js
│ │ ├── manifest.2ae2e69a05c33dfc65f8.js.map
│ │ ├── vendor.50affbea1a3146e9f2e5.js
│ │ └── vendor.50affbea1a3146e9f2e5.js.map
│ │ └── student.xlsx
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── components
│ │ ├── admin
│ │ │ ├── Main.vue
│ │ │ ├── assets
│ │ │ │ └── girl.png
│ │ │ ├── common
│ │ │ │ ├── Left.vue
│ │ │ │ ├── ShowQuestion.vue
│ │ │ │ ├── ShowQuestionGroup.vue
│ │ │ │ ├── Top.vue
│ │ │ │ └── config.js
│ │ │ └── pages
│ │ │ │ ├── course
│ │ │ │ ├── Main.vue
│ │ │ │ ├── Q_A
│ │ │ │ │ ├── CurrentQuestionAnswer.vue
│ │ │ │ │ ├── QuestionAnswerHistory.vue
│ │ │ │ │ └── common
│ │ │ │ │ │ ├── Badge.vue
│ │ │ │ │ │ ├── MessageDetail.vue
│ │ │ │ │ │ ├── MessageList.vue
│ │ │ │ │ │ └── StudentBadge.vue
│ │ │ │ ├── announcement
│ │ │ │ │ ├── AddAnnouncement.vue
│ │ │ │ │ └── OverView.vue
│ │ │ │ ├── base
│ │ │ │ │ ├── Base.vue
│ │ │ │ │ ├── CourseIntro.vue
│ │ │ │ │ ├── CoursewareManage.vue
│ │ │ │ │ ├── ExamType.vue
│ │ │ │ │ ├── FirstCourse.vue
│ │ │ │ │ ├── ReferenceBook.vue
│ │ │ │ │ ├── Section.vue
│ │ │ │ │ ├── TeachPlan.vue
│ │ │ │ │ └── TeacherIntro.vue
│ │ │ │ ├── questionBank
│ │ │ │ │ ├── AddChoiceQuestion.vue
│ │ │ │ │ ├── AddGapFilling.vue
│ │ │ │ │ ├── AddProgram.vue
│ │ │ │ │ ├── OrganizeQuestion.vue
│ │ │ │ │ ├── Overview.vue
│ │ │ │ │ ├── QuestionGroupOverview.vue
│ │ │ │ │ └── common
│ │ │ │ │ │ ├── ChosePoint.vue
│ │ │ │ │ │ ├── QuestionGroupTable.vue
│ │ │ │ │ │ ├── QuestionList.vue
│ │ │ │ │ │ └── QuestionTable.vue
│ │ │ │ ├── statistics
│ │ │ │ │ └── StudentStatistics.vue
│ │ │ │ ├── student
│ │ │ │ │ ├── AddManyStudent.vue
│ │ │ │ │ ├── AddNode.vue
│ │ │ │ │ ├── AddSingleStudent.vue
│ │ │ │ │ ├── ChoseClass.vue
│ │ │ │ │ ├── EditStudent.vue
│ │ │ │ │ ├── OverView.vue
│ │ │ │ │ └── ShowClass.vue
│ │ │ │ └── test
│ │ │ │ │ ├── AddExam.vue
│ │ │ │ │ ├── AddHomework.vue
│ │ │ │ │ ├── OverView.vue
│ │ │ │ │ └── common
│ │ │ │ │ ├── ChoseClass.vue
│ │ │ │ │ ├── ChoseExamQuestion.vue
│ │ │ │ │ ├── ChoseHomeworkQuestion.vue
│ │ │ │ │ ├── TestDetail.vue
│ │ │ │ │ └── TestDetailCommon
│ │ │ │ │ ├── CodeCheck.vue
│ │ │ │ │ ├── KnowledgePontPercent.vue
│ │ │ │ │ ├── QuestionRightPercent.vue
│ │ │ │ │ └── ScoreTable.vue
│ │ │ │ └── manage
│ │ │ │ ├── AnnoumcementManage.vue
│ │ │ │ ├── CourseManage.vue
│ │ │ │ ├── EditCourse.vue
│ │ │ │ ├── EditUser.vue
│ │ │ │ ├── PersonInfo.vue
│ │ │ │ ├── Safe.vue
│ │ │ │ ├── TotalStatistic.vue
│ │ │ │ └── UserManage.vue
│ │ ├── client
│ │ │ ├── Main.vue
│ │ │ ├── assets
│ │ │ │ ├── logo.jpg
│ │ │ │ └── logo.png
│ │ │ ├── common
│ │ │ │ ├── Badge.vue
│ │ │ │ ├── DoQuestion.vue
│ │ │ │ ├── DoTest.vue
│ │ │ │ ├── StudentBadge.vue
│ │ │ │ └── Top.vue
│ │ │ └── pages
│ │ │ │ ├── Announcement.vue
│ │ │ │ ├── ClientMain.vue
│ │ │ │ ├── Home.vue
│ │ │ │ ├── courseInfo
│ │ │ │ ├── CourseIntro.vue
│ │ │ │ ├── Courseware.vue
│ │ │ │ ├── ExamType.vue
│ │ │ │ ├── FirstCourse.vue
│ │ │ │ ├── ReferenceBook.vue
│ │ │ │ ├── TeachPlan.vue
│ │ │ │ └── TeacherIntro.vue
│ │ │ │ ├── exam
│ │ │ │ ├── CurrentExam.vue
│ │ │ │ ├── ExamDetail.vue
│ │ │ │ └── LastExam.vue
│ │ │ │ ├── knowledgePoint
│ │ │ │ ├── History.vue
│ │ │ │ ├── KnowledgePointView.vue
│ │ │ │ └── KnowledgeTestDetail.vue
│ │ │ │ ├── questionAnswer
│ │ │ │ ├── CurrentQuestionAnswer.vue
│ │ │ │ ├── NewQuestionAnswer.vue
│ │ │ │ ├── QuestionAnswerHistory.vue
│ │ │ │ └── common
│ │ │ │ │ ├── MessageDetail.vue
│ │ │ │ │ └── MessageList.vue
│ │ │ │ ├── rankList
│ │ │ │ ├── CoverageList.vue
│ │ │ │ └── EvaluateList.vue
│ │ │ │ ├── studentInfo
│ │ │ │ └── StudentInfo.vue
│ │ │ │ └── work
│ │ │ │ ├── CurrentWork.vue
│ │ │ │ ├── LastWork.vue
│ │ │ │ └── WorkDetail.vue
│ │ ├── common
│ │ │ ├── Breadcrumb.vue
│ │ │ ├── Editor.vue
│ │ │ └── ShowText.vue
│ │ └── public
│ │ │ ├── 404.vue
│ │ │ ├── About.vue
│ │ │ ├── Index.vue
│ │ │ ├── Login.vue
│ │ │ ├── Main.vue
│ │ │ ├── ResetPass.vue
│ │ │ ├── assets
│ │ │ └── background.jpg
│ │ │ └── test.vue
│ ├── main.js
│ └── router
│ │ ├── admin.js
│ │ ├── client.js
│ │ ├── index.js
│ │ └── public.js
└── static
│ ├── .gitkeep
│ ├── css
│ └── main.css
│ ├── js
│ ├── axios.js
│ ├── base64.js
│ ├── config.js
│ ├── httpRequest.js
│ └── main.js
│ └── student.xlsx
├── docker-compose.yml
├── files
├── Dockerfile
├── front
│ └── nginx.conf
├── mysql
│ ├── data
│ │ └── .gitkeep
│ ├── init.sql
│ └── my.cnf
├── redis
│ ├── data
│ │ └── .gitkeep
│ └── redis.conf
└── sim.tar
├── start.sh
└── teachSystem
├── .gitignore
├── api
├── announcement.js
├── announcementHelper.js
├── badge.js
├── class.js
├── classHelper.js
├── course.js
├── courseHelper.js
├── courseware.js
├── grade.js
├── knowledgePoint.js
├── public.js
├── questionAnswer.js
├── questionGroup.js
├── questionGroupHelper.js
├── questionLibrary.js
├── questionLibraryHelper.js
├── section.js
├── test.js
├── testHelper.js
├── user.js
└── userHelper.js
├── authority
├── dbAuth.js
├── index.js
├── readme.md
└── static
│ ├── css
│ └── main.css
│ ├── js
│ ├── axios.js
│ ├── main.js
│ └── vue.js
│ └── main.html
├── config
├── config.js
└── judgeConfig.js
├── dao
├── announcementDao.js
├── badgeDao.js
├── badgeHelper.js
├── classDao.js
├── courseDao.js
├── coursewareDao.js
├── gradeDao.js
├── knowledgePointDao.js
├── publicDao.js
├── questionAnswerDao.js
├── questionGroupDao.js
├── questionLibraryDao.js
├── sectionDao.js
├── studentClassDao.js
├── testDao.js
├── testHelper.js
└── userDao.js
├── files
├── badgeIcon
│ ├── badge_10.png
│ ├── badge_2.png
│ ├── badge_5.png
│ ├── badge_6.png
│ ├── badge_7.png
│ ├── badge_8.png
│ └── badge_9.png
├── code
│ └── .gitkeep
├── course
│ └── .gitkeep
├── imgs
│ └── .gitkeep
├── temp
│ └── uploads
│ │ └── .gitkeep
├── tests
│ └── .gitkeep
└── userIcon
│ └── default.jpg
├── index.js
├── log
├── log.js
├── operate
│ └── .gitkeep
├── responseTime
│ └── .gitkeep
└── visit
│ └── .gitkeep
├── middleware
├── auth.js
├── controllerEngine.js
├── handleError.js
└── init.js
├── package.json
├── test
└── courseDao.test.js
└── util
├── ErrorHelper.js
├── MailHelper.js
├── MysqlHelper.js
├── ObjectOperate.js
├── RedisHelper.js
├── RequestHelper.js
├── StringHelper.js
├── TextVerify.js
└── TimeHelper.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Editor directories and files
8 | .idea
9 | .vscode
10 | *.suo
11 | *.ntvs*
12 | *.njsproj
13 | *.sln
14 | */node_modules/
15 | *.log
16 | **.package-lock.json
17 | yarn.lock
18 | timezone
19 |
20 | files/mysql/data/*
21 | !files/mysql/data/.gitkeep
22 | files/redis/data/*
23 | !files/redis/data/.gitkeep
24 | files/front/log/*
25 | !files/front/data/.gitkeep
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 概述
2 |
3 | 本仓库用于存放毕业设计代码,同时也给想要使用node.js,vue.js构建系统的同志做个参考。。。
4 |
5 | 编程题判题使用的是青岛大学的判题核心,[https://github.com/QingdaoU/JudgeServer](https://github.com/QingdaoU/JudgeServer)
6 |
7 | 代码判重使用SIM实现,[地址](https://dickgrune.com/Programs/similarity_tester/)
8 |
9 | demo地址:[ali.tapme.top:8008](http://ali.tapme.top:8008) 管理员账户密码:123456/123456
10 |
11 | # 安装
12 |
13 | 使用docker一键部署。需要先设置mysql 密码和判题核心的密钥环境变量.然后执行start.sh即可。
14 |
15 | 注意下面只是临时设置环境变量,终端关闭就会失效。
16 |
17 | ```bash
18 | export MYSQL_PASS=123456
19 | #export JUDGE_TOKEN=12345678
20 | chmod +x start.sh
21 | ./start.sh
22 | ```
23 | 部署完毕后访问:部署机器ip:8089。
24 |
25 | **PS:如果docker pull 镜像速度很慢,请给docker设置国内加速地址。**
26 |
27 | ## 前端技术栈如下
28 |
29 | - vue.js
30 | - element.ui
31 | - axios
32 |
33 | ## 后端技术栈如下
34 |
35 | - node.js
36 | - koa
37 | - jwt
38 | - mysql
39 | - redis
40 |
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/client/.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 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Editor directories and files
8 | .idea
9 | .vscode
10 | *.suo
11 | *.ntvs*
12 | *.njsproj
13 | *.sln
14 | package-lock.json
15 |
--------------------------------------------------------------------------------
/client/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # client
2 |
3 | > A Vue.js project
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 | ```
20 |
21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
22 |
--------------------------------------------------------------------------------
/client/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/client/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/client/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/build/logo.png
--------------------------------------------------------------------------------
/client/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/client/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 |
12 |
13 | module.exports = {
14 | context: path.resolve(__dirname, '../'),
15 | entry: {
16 | app: './src/main.js'
17 | },
18 | output: {
19 | path: config.build.assetsRoot,
20 | filename: '[name].js',
21 | publicPath: process.env.NODE_ENV === 'production'
22 | ? config.build.assetsPublicPath
23 | : config.dev.assetsPublicPath
24 | },
25 | resolve: {
26 | extensions: ['.js', '.vue', '.json'],
27 | alias: {
28 | 'vue$': 'vue/dist/vue.esm.js',
29 | '@': resolve('src'),
30 | }
31 | },
32 | module: {
33 | rules: [
34 | {
35 | test: /\.vue$/,
36 | loader: 'vue-loader',
37 | options: vueLoaderConfig
38 | },
39 | {
40 | test: /\.js$/,
41 | loader: 'babel-loader',
42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
43 | },
44 | {
45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
46 | loader: 'url-loader',
47 | options: {
48 | limit: 10000,
49 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
50 | }
51 | },
52 | {
53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
54 | loader: 'url-loader',
55 | options: {
56 | limit: 10000,
57 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
58 | }
59 | },
60 | {
61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
62 | loader: 'url-loader',
63 | options: {
64 | limit: 10000,
65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
66 | }
67 | }
68 | ]
69 | },
70 | node: {
71 | // prevent webpack from injecting useless setImmediate polyfill because Vue
72 | // source contains it (although only uses it if it's native).
73 | setImmediate: false,
74 | // prevent webpack from injecting mocks to Node native modules
75 | // that does not make sense for the client
76 | dgram: 'empty',
77 | fs: 'empty',
78 | net: 'empty',
79 | tls: 'empty',
80 | child_process: 'empty'
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/client/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/client/config/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require("path");
6 |
7 | module.exports = {
8 | dev: {
9 | // Paths
10 | assetsSubDirectory: "static",
11 | assetsPublicPath: "/",
12 | proxyTable: {
13 | "/api/v1": {
14 | target: "http://localhost:8082",
15 | changeOrigin: true
16 | },
17 | "/files": {
18 | target: "http://localhost:8082",
19 | changeOrigin: true,
20 | pathRewrite: {
21 | "^/files": ""
22 | }
23 | }
24 | },
25 |
26 | // Various Dev Server settings
27 | host: "localhost", // can be overwritten by process.env.HOST
28 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
29 | autoOpenBrowser: false,
30 | errorOverlay: true,
31 | notifyOnErrors: true,
32 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
33 |
34 | /**
35 | * Source Maps
36 | */
37 |
38 | // https://webpack.js.org/configuration/devtool/#development
39 | devtool: "cheap-module-eval-source-map",
40 |
41 | // If you have problems debugging vue-files in devtools,
42 | // set this to false - it *may* help
43 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
44 | cacheBusting: true,
45 |
46 | cssSourceMap: true
47 | },
48 |
49 | build: {
50 | // Template for index.html
51 | index: path.resolve(__dirname, "../dist/index.html"),
52 |
53 | // Paths
54 | assetsRoot: path.resolve(__dirname, "../dist"),
55 | assetsSubDirectory: "static",
56 | assetsPublicPath: "/",
57 |
58 | /**
59 | * Source Maps
60 | */
61 |
62 | productionSourceMap: true,
63 | // https://webpack.js.org/configuration/devtool/#production
64 | devtool: "#source-map",
65 |
66 | // Gzip off by default as many popular static hosts such as
67 | // Surge or Netlify already gzip all static assets for you.
68 | // Before setting to `true`, make sure to:
69 | // npm install --save-dev compression-webpack-plugin
70 | productionGzip: false,
71 | productionGzipExtensions: ["js", "css"],
72 |
73 | // Run the build command with an extra argument to
74 | // View the bundle analyzer report after build finishes:
75 | // `npm run build --report`
76 | // Set to `true` or `false` to always turn it on or off
77 | bundleAnalyzerReport: process.env.npm_config_report
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/client/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/client/dist/index.html:
--------------------------------------------------------------------------------
1 |
教学辅助系统
--------------------------------------------------------------------------------
/client/dist/static/fonts/element-icons.535877f.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/fonts/element-icons.535877f.woff
--------------------------------------------------------------------------------
/client/dist/static/fonts/element-icons.732389d.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/fonts/element-icons.732389d.ttf
--------------------------------------------------------------------------------
/client/dist/static/img/logo.39aabf3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/dist/static/img/logo.39aabf3.png
--------------------------------------------------------------------------------
/client/dist/static/js/config.js:
--------------------------------------------------------------------------------
1 | window.actionUrl = "/api/v1";
2 |
--------------------------------------------------------------------------------
/client/dist/static/js/main.js:
--------------------------------------------------------------------------------
1 | function clearInfo() {
2 | window.token = undefined;
3 | localStorage.removeItem('token');
4 | localStorage.removeItem('userInfo');
5 | }
6 |
7 | function clearClientInfo(){
8 | window.clientToken = undefined;
9 | localStorage.removeItem('clientToken');
10 | localStorage.removeItem('clientUserInfo');
11 | }
12 |
13 | function getToken() {
14 | if (window.token == undefined || window.token==null)
15 | window.token = localStorage.getItem('token');
16 | return window.token;
17 | }
18 |
19 | function getClientToken() {
20 | if (window.clientToken == undefined || window.clientToken==null)
21 | window.clientToken = localStorage.getItem('clientToken');
22 | return window.clientToken;
23 | }
24 |
25 | function getUserInfo() {
26 | return JSON.parse(localStorage.getItem('userInfo'));
27 | }
28 |
29 | function getClientUserInfo() {
30 | return JSON.parse(localStorage.getItem('clientUserInfo'));
31 | }
32 |
33 | function alertConfirm(text) {
34 | return vm.$confirm(text, '提示', {
35 | confirmButtonText: "确定",
36 | cancelButtonText: "取消"
37 | });
38 | }
39 |
40 | function alertMessage(message, type) {
41 | vm.$message({
42 | message,
43 | type,
44 | center: true,
45 | duration:2000
46 | });
47 | }
48 |
49 | function alertNotify(title, message, type) {
50 | vm.$notify({
51 | title,
52 | message,
53 | type,
54 | duration:2000
55 | })
56 | }
57 |
--------------------------------------------------------------------------------
/client/dist/static/js/manifest.2ae2e69a05c33dfc65f8.js:
--------------------------------------------------------------------------------
1 | !function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a
2 |
3 |
4 |
5 |
6 |
7 | 教学辅助系统
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "fxb",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js"
11 | },
12 | "dependencies": {
13 | "element-ui": "^2.2.2",
14 | "moment": "^2.21.0",
15 | "vue": "^2.5.2",
16 | "vue-router": "^3.0.1",
17 | "wangeditor": "^3.1.0",
18 | "xss": "^0.3.7"
19 | },
20 | "devDependencies": {
21 | "autoprefixer": "^7.1.2",
22 | "babel-core": "^6.22.1",
23 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
24 | "babel-loader": "^7.1.1",
25 | "babel-plugin-syntax-jsx": "^6.18.0",
26 | "babel-plugin-transform-runtime": "^6.22.0",
27 | "babel-plugin-transform-vue-jsx": "^3.5.0",
28 | "babel-preset-env": "^1.3.2",
29 | "babel-preset-stage-2": "^6.22.0",
30 | "chalk": "^2.0.1",
31 | "copy-webpack-plugin": "^4.0.1",
32 | "css-loader": "^0.28.0",
33 | "extract-text-webpack-plugin": "^3.0.0",
34 | "file-loader": "^1.1.4",
35 | "friendly-errors-webpack-plugin": "^1.6.1",
36 | "html-webpack-plugin": "^2.30.1",
37 | "node-notifier": "^5.1.2",
38 | "optimize-css-assets-webpack-plugin": "^3.2.0",
39 | "ora": "^1.2.0",
40 | "portfinder": "^1.0.13",
41 | "postcss-import": "^11.0.0",
42 | "postcss-loader": "^2.0.8",
43 | "postcss-url": "^7.2.1",
44 | "rimraf": "^2.6.0",
45 | "semver": "^5.3.0",
46 | "shelljs": "^0.7.6",
47 | "uglifyjs-webpack-plugin": "^1.1.1",
48 | "url-loader": "^0.5.8",
49 | "vue-loader": "^13.3.0",
50 | "vue-style-loader": "^3.0.1",
51 | "vue-template-compiler": "^2.5.2",
52 | "webpack": "^3.6.0",
53 | "webpack-bundle-analyzer": "^2.9.0",
54 | "webpack-dev-server": "^2.9.1",
55 | "webpack-merge": "^4.1.0"
56 | },
57 | "engines": {
58 | "node": ">= 6.0.0",
59 | "npm": ">= 3.0.0"
60 | },
61 | "browserslist": [
62 | "> 1%",
63 | "last 2 versions",
64 | "not ie <= 8"
65 | ]
66 | }
67 |
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
29 |
--------------------------------------------------------------------------------
/client/src/components/admin/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 版权所有,侵权必究
14 |
15 |
16 |
17 |
18 |
19 |
20 |
61 |
62 |
91 |
92 |
--------------------------------------------------------------------------------
/client/src/components/admin/assets/girl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/admin/assets/girl.png
--------------------------------------------------------------------------------
/client/src/components/admin/common/Left.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
32 |
33 |
34 |
65 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/client/src/components/admin/common/ShowQuestion.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{index}}.
4 |
5 |
6 |
7 |
8 | {{String.fromCharCode(65+index)}}.
9 | {{item}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
61 |
62 |
75 |
--------------------------------------------------------------------------------
/client/src/components/admin/common/ShowQuestionGroup.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
46 |
47 |
50 |
--------------------------------------------------------------------------------
/client/src/components/admin/common/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | person_space: [{
3 | index: "person_space",
4 | name: "个人资料",
5 | }, {
6 | index: "person_space/safe",
7 | name: "安全设置",
8 | data: [{
9 | index: "person_space/safe/change_email",
10 | name: "修改邮箱"
11 | }, {
12 | index: "person_space/safe/change_password",
13 | name: "修改密码"
14 | }]
15 | }],
16 | manage: [{
17 | index: 'manage/course_manage',
18 | name: '课程管理'
19 | }, {
20 | index: 'manage/user_manage',
21 | name: '用户管理'
22 | }],
23 | student: [{
24 | index: 'student',
25 | name: '学生概览'
26 | }, {
27 | index: 'student/add',
28 | name: '录入学生',
29 | data: [{
30 | index: 'student/add_one',
31 | name: "单个录入"
32 | }, {
33 | index: 'student/add_many',
34 | name: "批量录入"
35 | }]
36 | }],
37 | announcement: [{
38 | index: 'announcement',
39 | name: '公告概览'
40 | }, {
41 | index: 'announcement/add',
42 | name: '新增公告'
43 | }],
44 | base:[{
45 | index:'base',
46 | name:'课程简介'
47 | },{
48 | index:'base/teacher_intro',
49 | name:'教师简介',
50 | },{
51 | index:'base/first_course',
52 | name:'先导课程'
53 | },{
54 | index:'base/teach_plan',
55 | name:'教学计划'
56 | },{
57 | index:'base/exam_type',
58 | name:'考试方式'
59 | },{
60 | index:'base/reference_book',
61 | name:'参考书目'
62 | },{
63 | index:'base/section',
64 | name:'章节管理'
65 | },{
66 | index:'base/courseware_manage',
67 | name:'课件管理'
68 | }],
69 | question_bank:[{
70 | index:'question_bank',
71 | name:'题库总览'
72 | },{
73 | index:'add',
74 | name:'录入题目',
75 | data:[{
76 | index:'question_bank/add/choice_question',
77 | name:'选择题'
78 | },{
79 | index:'question_bank/add/gap_filling',
80 | name:'填空题'
81 | },{
82 | index:'question_bank/add/program',
83 | name:'编程题'
84 | }]
85 | },{
86 | index:'question_bank/question_group_overview',
87 | name:'题组总览'
88 | },{
89 | index:'question_bank/organize_question',
90 | name:"组织题目"
91 | }],
92 | test:[{
93 | index:'test',
94 | name:"测试总览"
95 | },{
96 | index:'test/add_homework',
97 | name:"发布作业"
98 | },{
99 | index:'test/add_exam',
100 | name:"发布考试"
101 | }],
102 | statistics:[
103 | {
104 | index:'statistics',
105 | name:'总览'
106 | },
107 | ],
108 | //答疑
109 | Q_A:[
110 | {
111 | index:'Q_A',
112 | name:"当前答疑"
113 | }
114 | ],
115 | //主页统计
116 | admin:[
117 | {
118 | index:'overview',
119 | name:'总览'
120 | }
121 | ]
122 | }
123 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Q_A/CurrentQuestionAnswer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | this.currentMessage=data">
5 |
6 |
7 |
8 |
9 |
10 |
61 |
62 |
64 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Q_A/QuestionAnswerHistory.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | this.currentMessage=data">
5 |
6 |
7 |
8 |
9 |
10 |
41 |
42 |
45 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Q_A/common/Badge.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
当前:
6 |
{{data.b_name}}
7 |
8 |
获取条件:{{data.get_condition}}
9 |
下一阶段:无
10 |
下一阶段:{{nextData.next.b_name}}
11 |
获取条件:{{nextData.next.get_condition}}
12 |
当前:{{nextData.current}}
13 |
14 |
15 | 获取条件:{{data.get_condition}}
16 |
17 |
18 |
![]()
19 |
{{data.b_name}}
20 |
21 |
22 |
23 |
24 |
25 |
51 |
52 |
71 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Q_A/common/MessageList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | topic:{{item.topic}}
8 |
9 |
10 |
11 |
12 | 尚无提问
13 |
14 |
15 |
16 |
17 |
63 |
64 |
84 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/Q_A/common/StudentBadge.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 已获得徽章总数:{{badgeList.length}}
5 |
6 |
7 |
8 |
9 |
{{data.u_name}}
10 |
![]()
11 |
12 |
13 |
14 |
15 |
16 |
46 |
47 |
53 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/announcement/AddAnnouncement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 提交
19 |
20 |
21 |
22 |
55 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/Base.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
43 |
50 |
51 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/CourseIntro.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/CoursewareManage.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 |
85 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/ExamType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/FirstCourse.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/ReferenceBook.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/TeachPlan.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/base/TeacherIntro.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/questionBank/Overview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
60 |
63 |
64 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/questionBank/QuestionGroupOverview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
26 |
27 |
30 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/questionBank/common/ChosePoint.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/questionBank/common/QuestionList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 删除
14 |
15 |
16 |
17 |
18 |
19 |
30 |
31 |
34 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/statistics/StudentStatistics.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{data.studentNum}}
6 |
7 |
8 |
9 |
10 |
11 | {{item.range}}:{{item.num}}人
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
103 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/AddManyStudent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 点击下载模板
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 将文件拖到此处,或
14 | 点击选择
15 |
16 | 只能上传xls/xlsx文件,且不超过1MB
17 |
18 |
19 |
20 | 提交
21 |
22 |
23 |
24 | {{index+1}} {{item.info}} {{item.errorInfo}}
25 |
26 |
27 |
28 |
67 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/AddNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 取消
12 | 提交
13 |
14 |
15 |
16 |
50 |
57 |
58 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/AddSingleStudent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
如果该学生不存在,将新增一个用户并加入到该班级,如存在直接将该学生加入班级,学生信息不起作用
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 提交
20 |
21 |
22 |
23 |
65 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/ChoseClass.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/EditStudent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 取消
19 | 提交
20 |
21 |
22 |
23 |
54 |
60 |
61 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/student/ShowClass.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 |
91 |
93 |
94 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/test/common/ChoseClass.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
50 |
51 |
54 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/test/common/TestDetailCommon/KnowledgePontPercent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/test/common/TestDetailCommon/QuestionRightPercent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
23 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/course/test/common/TestDetailCommon/ScoreTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
57 |
58 |
61 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/manage/AnnoumcementManage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/manage/EditUser.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 |
85 |
94 |
95 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/manage/Safe.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 |
85 |
96 |
--------------------------------------------------------------------------------
/client/src/components/admin/pages/manage/TotalStatistic.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{onlineNum}}
6 |
7 |
8 |
9 |
10 |
11 | 系统共进行:{{testData.count}}次测试
12 | 最近7天测试情况
13 |
14 |
15 |
16 |
17 |
18 |
114 |
119 |
120 |
--------------------------------------------------------------------------------
/client/src/components/client/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 关于
11 |
12 |
13 |
14 |
52 |
53 |
78 |
79 |
--------------------------------------------------------------------------------
/client/src/components/client/assets/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/client/assets/logo.jpg
--------------------------------------------------------------------------------
/client/src/components/client/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/client/assets/logo.png
--------------------------------------------------------------------------------
/client/src/components/client/common/Badge.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
当前:
6 |
{{data.b_name}}
7 |
8 |
获取条件:{{data.get_condition}}
9 |
下一阶段:无
10 |
下一阶段:
{{nextData.next.b_name}}
11 |
获取条件:{{nextData.next.get_condition}}
12 |
当前:{{nextData.current}}
13 |
14 |
15 | 获取条件:{{data.get_condition}}
16 |
17 |
18 |
![]()
19 |
{{data.b_name}}
20 |
21 |
22 |
23 |
24 |
25 |
57 |
58 |
77 |
--------------------------------------------------------------------------------
/client/src/components/client/common/DoQuestion.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{index}}.【{{data.score}}】
4 |
5 |
6 |
7 |
8 |
9 | {{String.fromCharCode(65+index)}}.{{data.alternative_answer[index]}}
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 |
47 |
48 |
70 |
--------------------------------------------------------------------------------
/client/src/components/client/common/StudentBadge.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 已获得徽章总数:{{badgeList.length}}
5 |
6 |
7 |
8 |
9 |
{{data.u_name}}
10 |
![]()
11 |
12 |
13 |
14 |
15 |
16 |
46 |
47 |
53 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/Announcement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 课程公告
5 |
6 |
7 |
当前无公告
8 |
9 |
10 | {{index+1}}.
11 | {{item.topic}}
12 |
13 |
{{moment(item.start_time).format("YYYY-MM-DD hh:mm")}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
61 |
62 |
80 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/ClientMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{item.c_name}}
8 |
9 |
10 | 无
11 |
12 |
13 |
14 |
15 | {{item.c_name}}
16 |
17 |
18 | 无
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
62 |
63 |
73 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/CourseIntro.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/Courseware.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 下载
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
44 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/ExamType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/FirstCourse.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/ReferenceBook.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/TeachPlan.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/courseInfo/TeacherIntro.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/exam/CurrentExam.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 开始
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
57 |
58 |
61 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/exam/ExamDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
26 |
27 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/exam/LastExam.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 |
63 |
64 |
67 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/knowledgePoint/History.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 待完成练习:
6 |
7 |
8 | {{undoTest.test_name}}
9 |
10 | 放弃
11 |
12 |
13 | 无
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 查看
25 |
26 |
27 |
28 |
29 |
30 |
31 |
69 |
70 |
73 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/knowledgePoint/KnowledgeTestDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/questionAnswer/CurrentQuestionAnswer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | this.currentMessage=data">
6 |
7 |
8 |
9 |
10 |
11 |
61 |
62 |
64 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/questionAnswer/QuestionAnswerHistory.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | this.currentMessage=data">
6 |
7 |
8 |
9 |
10 |
11 |
43 |
44 |
46 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/questionAnswer/common/MessageList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | topic:{{item.topic}}
8 |
9 |
10 |
11 |
12 | 当前尚无数据
13 |
14 |
15 |
16 |
17 |
63 |
64 |
84 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/rankList/CoverageList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
我的排名:{{myRank}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
63 |
64 |
67 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/rankList/EvaluateList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
我的排名:{{myRank}}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
62 |
63 |
66 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/work/CurrentWork.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 开始作业
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
53 |
54 |
57 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/work/LastWork.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 |
63 |
64 |
67 |
--------------------------------------------------------------------------------
/client/src/components/client/pages/work/WorkDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
31 |
32 |
--------------------------------------------------------------------------------
/client/src/components/common/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 课程首页
4 |
5 | {{item[0]}}
6 | {{item}}
7 |
8 |
9 |
10 |
11 |
31 |
32 |
35 |
--------------------------------------------------------------------------------
/client/src/components/common/Editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
您已输入{{currentNum}}字,最多可输入5000
4 |
5 |
6 |
7 |
8 |
78 |
88 |
89 |
--------------------------------------------------------------------------------
/client/src/components/common/ShowText.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/client/src/components/public/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
您访问的页面不存在
4 | 点击返回
5 |
6 |
7 |
8 |
18 |
24 |
--------------------------------------------------------------------------------
/client/src/components/public/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
本系统github地址:{{github}}
4 |
由于本人能力和时间的问题,本系统为1.0版本,必然存在许多的bug,欢迎大家进行错误反馈。
5 |
6 |
7 |
8 |
18 |
19 |
30 |
--------------------------------------------------------------------------------
/client/src/components/public/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 客户端
5 |
6 |
7 | 管理端
8 |
9 |
10 |
11 |
12 |
17 |
18 |
27 |
--------------------------------------------------------------------------------
/client/src/components/public/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
31 |
--------------------------------------------------------------------------------
/client/src/components/public/assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/public/assets/background.jpg
--------------------------------------------------------------------------------
/client/src/components/public/test.vue:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/src/components/public/test.vue
--------------------------------------------------------------------------------
/client/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 | import ElementUI from 'element-ui'
7 | import moment from 'moment'
8 | import 'element-ui/lib/theme-chalk/index.css'
9 | //全局注册组件
10 | import Editor from './components/common/Editor'
11 | import ShowText from './components/common/ShowText'
12 | import Breadcrumb from './components/common/Breadcrumb'
13 | Vue.component('Editor',Editor);
14 | Vue.component('ShowText',ShowText);
15 | Vue.component('Breadcrumb',Breadcrumb);
16 |
17 | Object.defineProperty(Vue.prototype,'$moment',{value:moment});
18 | Vue.config.productionTip = false
19 | Vue.use(ElementUI);
20 |
21 | window.bus = new Vue();
22 |
23 | window.vm = new Vue({
24 | el: '#app',
25 | router,
26 | components: {
27 | App
28 | },
29 | template: ''
30 | })
31 | console.log('vue示例构建完毕');
32 |
--------------------------------------------------------------------------------
/client/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Public from './public'
4 | import Admin from './admin'
5 | import Client from './client'
6 | import NotFound from '@/components/public/404'
7 | import About from '@/components/public/About'
8 | import IndexPage from '@/components/public/Index'
9 | Vue.use(Router)
10 |
11 | export default new Router({
12 | mode: 'history',
13 | base: '/app/',
14 | routes: [{
15 | path: '',
16 | name: 'indexPage',
17 | component: IndexPage
18 | },
19 | {
20 | path: '/about',
21 | name: 'about',
22 | component: About
23 | },
24 | Admin,
25 | Client,
26 | Public,
27 | {
28 | path: '*',
29 | name: "NotFound",
30 | component: NotFound
31 | }
32 | ]
33 | })
34 |
--------------------------------------------------------------------------------
/client/src/router/public.js:
--------------------------------------------------------------------------------
1 | import Public from '@/components/public/Main'
2 | import Login from '@/components/public/Login'
3 | import ResetPass from '@/components/public/ResetPass'
4 | import About from '@/components/public/About'
5 |
6 | export default {
7 | path: '/public',
8 | component: Public,
9 | children: [{
10 | path: 'admin_login',
11 | component: Login
12 | },{
13 | path:'client_login',
14 | component:Login
15 | }, {
16 | path: "admin_login/reset_pass",
17 | component: ResetPass
18 | }, {
19 | path: "client_login/reset_pass",
20 | component: ResetPass
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/client/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/static/.gitkeep
--------------------------------------------------------------------------------
/client/static/js/config.js:
--------------------------------------------------------------------------------
1 | window.actionUrl = "/api/v1";
2 |
--------------------------------------------------------------------------------
/client/static/js/main.js:
--------------------------------------------------------------------------------
1 | function clearInfo() {
2 | window.token = undefined;
3 | localStorage.removeItem('token');
4 | localStorage.removeItem('userInfo');
5 | }
6 |
7 | function clearClientInfo(){
8 | window.clientToken = undefined;
9 | localStorage.removeItem('clientToken');
10 | localStorage.removeItem('clientUserInfo');
11 | }
12 |
13 | function getToken() {
14 | if (window.token == undefined || window.token==null)
15 | window.token = localStorage.getItem('token');
16 | return window.token;
17 | }
18 |
19 | function getClientToken() {
20 | if (window.clientToken == undefined || window.clientToken==null)
21 | window.clientToken = localStorage.getItem('clientToken');
22 | return window.clientToken;
23 | }
24 |
25 | function getUserInfo() {
26 | return JSON.parse(localStorage.getItem('userInfo'));
27 | }
28 |
29 | function getClientUserInfo() {
30 | return JSON.parse(localStorage.getItem('clientUserInfo'));
31 | }
32 |
33 | function alertConfirm(text) {
34 | return vm.$confirm(text, '提示', {
35 | confirmButtonText: "确定",
36 | cancelButtonText: "取消"
37 | });
38 | }
39 |
40 | function alertMessage(message, type) {
41 | vm.$message({
42 | message,
43 | type,
44 | center: true,
45 | duration:2000
46 | });
47 | }
48 |
49 | function alertNotify(title, message, type) {
50 | vm.$notify({
51 | title,
52 | message,
53 | type,
54 | duration:2000
55 | })
56 | }
57 |
--------------------------------------------------------------------------------
/client/static/student.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/client/static/student.xlsx
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 |
3 | services:
4 | teach-system-mysql:
5 | image: mysql:5.7.25
6 | container_name: teach-system-mysql
7 | volumes:
8 | - /etc/localtime:/etc/localtime
9 | - ./timezone:/ect/timezone
10 | - ./files/mysql/data:/var/lib/mysql
11 | - ./files/mysql/my.cnf:/etc/mysql/my.cnf
12 | environment:
13 | - MYSQL_ROOT_PASSWORD=$MYSQL_PASS
14 | ports:
15 | - 3306:3306
16 | networks:
17 | - teachSystem
18 |
19 | teach-system-redis:
20 | image: redis:3.2.10
21 | container_name: teach-system-redis
22 | networks:
23 | - teachSystem
24 | volumes:
25 | - /etc/localtime:/etc/localtime
26 | - ./timezone:/ect/timezone
27 | - ./files/redis/data:/data
28 | - ./files/redis/redis.conf:/etc/redis/redis.conf
29 | ports:
30 | - 6379:6379
31 | command: redis-server /etc/redis/redis.conf
32 |
33 | teach-system-front:
34 | image: nginx
35 | container_name: teach-system-front
36 | depends_on:
37 | - teach-system-backend
38 | networks:
39 | - teachSystem
40 | volumes:
41 | - /etc/localtime:/etc/localtime
42 | - ./timezone:/ect/timezone
43 | - ./client/dist:/opt/dist
44 | - ./files/front/log:/var/log/nginx
45 | - ./files/front/nginx.conf:/etc/nginx/nginx.conf
46 | ports:
47 | - 8085:8080
48 |
49 | teach-system-backend:
50 | image: registry.cn-hangzhou.aliyuncs.com/fleyx/node:v1
51 | container_name: teach-system-backend
52 | depends_on:
53 | - teach-system-mysql
54 | - teach-system-redis
55 | networks:
56 | - teachSystem
57 | environment:
58 | - mysqlHost=teach-system-mysql
59 | - redisHost=teach-system-redis
60 | - mysqlPassword=$MYSQL_PASS
61 | - judgeToken=$JUDGE_TOKEN
62 | - judgeUrl=http://judge-server:8080
63 | - testSavePath=/opt/tests/test_case
64 | volumes:
65 | - /etc/localtime:/etc/localtime
66 | - ./timezone:/ect/timezone
67 | - ./teachSystem:/opt/workspace
68 | - ./JudgeServer/tests/:/opt/tests
69 | ports:
70 | - 8089:8088
71 | command: "/bin/bash /opt/start.sh"
72 |
73 | # 启动判题核心
74 | judge-server:
75 | image: registry.cn-hangzhou.aliyuncs.com/onlinejudge/judge_server
76 | container_name: judge-server
77 | read_only: true
78 | networks:
79 | - teachSystem
80 | cap_drop:
81 | - SETPCAP
82 | - MKNOD
83 | - NET_BIND_SERVICE
84 | - SYS_CHROOT
85 | - SETFCAP
86 | - FSETID
87 | tmpfs:
88 | - /tmp
89 | volumes:
90 | - ../JudgeServer/tests/test_case:/test_case:ro
91 | - ../JudgeServer/log:/log
92 | - ../JudgeServer/run:/judger
93 | environment:
94 | - BACKEND_URL=http://backend:80/api/judge_server_heartbeat
95 | - SERVICE_URL=http://judge-server:12358
96 | - TOKEN=$JUDGE_TOKEN
97 | ports:
98 | - "0.0.0.0:12358:8080"
99 |
100 | networks:
101 | teachSystem:
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/files/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 | COPY sim.tar /opt/sim.tar
3 | RUN cd /opt && \
4 | apt update && \
5 | apt install -y wget && \
6 | apt install -y xz-utils && \
7 | tar -xvf sim.tar && \
8 | wget https://npm.taobao.org/mirrors/node/v10.15.1/node-v10.15.1-linux-x64.tar.xz && \
9 | xz -d node-v10.15.1-linux-x64.tar.xz && \
10 | tar -xvf node-v10.15.1-linux-x64.tar && \
11 | rm node-v10.15.1-linux-x64.tar && \
12 | mv node-v10.15.1-linux-x64 node10 && \
13 | printf '#!/bin/bash\ncd /opt/workspace\nnpm --registry https://registry.npm.taobao.org install >/dev/null 2>&1\nnode index.js' > start.sh && \
14 | chmod +x start.sh
15 | ENV PATH=$PATH:/opt/node10/bin:/opt/sim NODE_ENV=production
16 | CMD [ "cd /opt/workspace && ./start.sh" ]
17 |
--------------------------------------------------------------------------------
/files/front/nginx.conf:
--------------------------------------------------------------------------------
1 | user root;
2 | worker_processes auto;
3 | pid /run/nginx.pid;
4 | events {
5 | worker_connections 768;
6 | }
7 | http {
8 | # Basic Settings
9 | sendfile on;
10 | # SSL Settings
11 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
12 | # Logging Settings
13 | access_log /var/log/nginx/access.log;
14 | error_log /var/log/nginx/error.log;
15 | # Gzip Settings
16 | gzip on;
17 | tcp_nopush on;
18 | tcp_nodelay on;
19 | keepalive_timeout 65;
20 | types_hash_max_size 2048;
21 | gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
22 | gzip_min_length 1K;
23 | include /etc/nginx/mime.types;
24 | default_type application/octet-stream;
25 | ssl_prefer_server_ciphers on;
26 | upstream teach-system {
27 | server teach-system-backend:8088;
28 | keepalive 2000;
29 | }
30 | server {
31 | listen 8080;
32 | listen [::]:8080;
33 | index index.html;
34 | root /opt/dist/;
35 | server_name _;
36 | location /api/v1/ {
37 | proxy_pass http://teach-system;
38 | proxy_set_header Host $host;
39 | proxy_set_header X-Real-IP $remote_addr;
40 | client_max_body_size 100m;
41 | }
42 | location /files/ {
43 | proxy_pass http://teach-system/;
44 | proxy_set_header Host $host;
45 | proxy_set_header X-Real-IP $remote_addr;
46 | client_max_body_size 100m;
47 | }
48 | # location /dist/ {
49 | # alias /opt/dist/;
50 | # }
51 | location / {
52 | root /opt/dist;
53 | index index.html;
54 | try_files $uri $uri/ /index.html;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/files/mysql/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/mysql/data/.gitkeep
--------------------------------------------------------------------------------
/files/mysql/my.cnf:
--------------------------------------------------------------------------------
1 | [mysqld]
2 | user=mysql
3 | port=3306
4 | character-set-server=utf8
5 | default_authentication_plugin=mysql_native_password
6 | skip_ssl
7 | #不区分大小写
8 | lower_case_table_names=1
9 | [client]
10 | default-character-set=utf8
11 | [mysql]
12 | default-character-set=utf8
13 |
--------------------------------------------------------------------------------
/files/redis/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/redis/data/.gitkeep
--------------------------------------------------------------------------------
/files/redis/redis.conf:
--------------------------------------------------------------------------------
1 | tcp-backlog 511
2 | bind 0.0.0.0
3 | # 空闲多久后关闭连接
4 | timeout 0
5 | # 心跳包时间间隔
6 | tcp-keepalive 0
7 |
--------------------------------------------------------------------------------
/files/sim.tar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/files/sim.tar
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # mysql root密码通过环境变量配置
4 | #export MYSQL_PASS=123456
5 | # 判题服务密钥通过环境变量配置
6 | #export JUDGE_TOKEN=12345678
7 |
8 | #获取脚本所在目录
9 | path=$(cd `dirname $0`;pwd)
10 | pwd
11 | echo "Asia/Shanghai" >$path/timezone
12 | echo "mysql容器建立"
13 |
14 | #判断data中有没有teach_system数据库,没有就进行初始化数据库的操作
15 | omsCount=$(ls -l $path/files/mysql/data | grep -wc teach_system)
16 | if [ $omsCount == 0 ]; then
17 | echo "首次安装系统,进行sql初始化,请稍候,需等候一分钟左右"
18 | docker stop temp_mysql
19 | docker rm temp_mysql
20 | docker run --name=temp_mysql -itd -p 3306:3306 --privileged=true -v $path/files/mysql/data:/var/lib/mysql -v $path/files/mysql/my.cnf:/etc/mysql/my.cnf -v $path/files/mysql/init.sql:/opt/init.sql -e MYSQL_ROOT_PASSWORD=$MYSQL_PASS mysql:5.7.25
21 | sleep 50s
22 | docker exec temp_mysql bash -c "mysql -u root -p$MYSQL_PASS {
17 | let params = announcementHelper.checkGetParam(ctx.allParams.data);
18 | let res = await announcementDao.getByParam(params);
19 | ctx.onSuccess(res);
20 | }
21 |
22 | //获取某门课下所有可显示公告
23 | exports['GET /course/:c_id/announcement/open']=async(ctx,next)=>{
24 | let res = await announcementDao.getOpenAnnouncement(ctx.params.c_id);
25 | ctx.onSuccess(res);
26 | }
27 |
28 | /**
29 | * 获取公告内容
30 | */
31 | exports['GET /announcement/:a_id/content']= async (ctx, next) => {
32 | let res = await announcementDao.getContent(ctx.params.a_id);
33 | ctx.onSuccess(res);
34 | }
35 |
36 | /**
37 | * 切换公告显示状态
38 | * url param:
39 | * action:切换到true/false
40 | */
41 | exports['PUT /announcement/:a_id/switch']= async (ctx, next) => {
42 | let res = await announcementDao.switchOne(ctx.params.a_id,ctx.allParams.action);
43 | ctx.onSuccess(res);
44 | }
45 | /**
46 | * 插入一条公告记录 /announcement
47 | */
48 | exports['POST /announcement']= async (ctx, next) => {
49 | await announcementDao.addOne(ctx.allParams);
50 | ctx.onSuccess();
51 | }
52 | /**
53 | * 删除一个公告
54 | */
55 | exports['DELETE /announcement/:a_id']= async (ctx, next) => {
56 | await announcementDao.deleteOne(ctx.params.a_id);
57 | ctx.onSuccess();
58 | }
--------------------------------------------------------------------------------
/teachSystem/api/announcementHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const ErrorHelper = require('../util/ErrorHelper.js');
3 |
4 | class announcementHelper {
5 | static checkGetParam(data) {
6 | try {
7 | data = JSON.parse(new Buffer(data, 'base64').toString());
8 | } catch (err) {
9 | throw ErrorHelper.Error400("参数不合法");
10 | }
11 | let params = {};
12 | try {
13 | params.c_id = data.c_id;
14 | params.start = data.start;
15 | params.count = data.count;
16 | let column = data.column;
17 | if (column == 'topic' || column == 'start_time' || column == "end_time") {
18 | params.column = column;
19 | } else {
20 | throw new Error();
21 | }
22 | params.sort = data.sort == 'desc' ? 'desc' : 'asc';
23 | } catch (err) {
24 | throw ErrorHelper.Error400("参数有误:"+JSON.stringify(params));
25 | }
26 | return params;
27 | }
28 | }
29 |
30 | module.exports = announcementHelper;
--------------------------------------------------------------------------------
/teachSystem/api/badge.js:
--------------------------------------------------------------------------------
1 | const badgeDao = require('../dao/badgeDao.js');
2 | const config = require('../config/config.js');
3 |
4 | //获取某用户所拥有的徽章
5 | exports['GET /user/:u_id/badge'] = async ctx => {
6 | let res = await badgeDao.getUserBadge(ctx.params.u_id);
7 | res.forEach(item => {
8 | item.b_img = config.host + '/badgeIcon/' + item.b_img;
9 | })
10 | ctx.onSuccess(res);
11 | }
12 |
13 | //获取课程门数徽章详细数据
14 | exports['GET /user/:u_id/badge/:b_id'] = async ctx => {
15 | let {u_id,b_id,type} = ctx.params;
16 | let res = await badgeDao.getBadgeDetail(u_id,b_id,ctx.allParams.type);
17 | res.next.b_img = config.host + '/badgeIcon/' + res.next.b_img;
18 | ctx.onSuccess(res);
19 | }
--------------------------------------------------------------------------------
/teachSystem/api/class.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const classDao = require('../dao/classDao.js');
3 | const classHelper = require('./classHelper.js');
4 | /**
5 | * 获取一个班的学生 /class/:class_id/user
6 | */
7 | let get_classId_user = async (ctx, next) => {
8 | let res = await classDao.getStudentOfClass(ctx.params.class_id);
9 | ctx.onSuccess(res);
10 | }
11 |
12 | /**
13 | * 新增一个班级
14 | */
15 | let post = async (ctx, next) => {
16 | let res = await classDao.addOne(ctx.allParams);
17 | ctx.onSuccess(res);
18 | }
19 |
20 | /**
21 | * 新增一名学生 /class/:class_id/add_one_student
22 | */
23 | let post_addOneStudent = async (ctx, next) => {
24 | await classDao.addOneStudent(ctx.params.class_id, ctx.allParams);
25 | ctx.onSuccess();
26 | }
27 |
28 | /**
29 | *execl提交,批量新增 /class/:class_id/add_many_student
30 | *execl格式:(无标题)第一列:姓名,第二列:学号,第三列:密码(如账号已存在 ,密码设置无效)
31 | */
32 | let post_addManyStudent = async (ctx, next) => {
33 | let tableData = classHelper.getTableInfo(ctx.allParams.xlsx);
34 | let res = await classDao.addManyStudent(ctx.allParams.class_id, tableData);
35 | ctx.onSuccess(res);
36 | }
37 |
38 | /**
39 | * 从班级中删除一名学生 /class/:class_id/user/:u_id
40 | */
41 | let delete_classId_user_uId = async (ctx, next) => {
42 | await classDao.deleteOneStudent(ctx.params.class_id, ctx.params.u_id);
43 | ctx.onSuccess();
44 | }
45 |
46 | /**
47 | * 删除一个班级 /class/:class_id
48 | */
49 | let delete_classId=async(ctx,next)=>{
50 | await classDao.deleteOneClass(ctx.params.class_id);
51 | ctx.onSuccess();
52 | }
53 |
54 | module.exports = {
55 | 'GET /class/:class_id/user': get_classId_user,
56 | 'POST /class': post,
57 | 'POST /class/:class_id/add_one_student': post_addOneStudent,
58 | 'POST /class/:class_id/add_many_student': post_addManyStudent,
59 | 'DELETE /class/:class_id/user/:u_id': delete_classId_user_uId,
60 | "DELETE /class/:class_id":delete_classId
61 | }
--------------------------------------------------------------------------------
/teachSystem/api/classHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const js_xlsx = require('js-xlsx');
3 | const xlsx = require('xlsx');
4 | const fs = require('fs-extra');
5 |
6 | class classHelper {
7 | static getTableInfo(file) {
8 | let content = xlsx.readFile(file.path);
9 | let sheetName = content.SheetNames[0];
10 | let res = xlsx.utils.sheet_to_json(content.Sheets[sheetName],{header:['u_name','code','password']});
11 | return res;
12 | }
13 | }
14 |
15 | module.exports = classHelper;
--------------------------------------------------------------------------------
/teachSystem/api/courseHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const ErrorHelper = require('../util/ErrorHelper.js');
3 | class courseHelper{
4 | static checkGetBaseInfoParam(param){
5 | let keys = Object.keys(param);
6 | if(keys.length != 2){
7 | return ErrorHelper.newError('只能携带c_id,column',406);
8 | }
9 | let validParam = ['c_name','code','c_picture','c_intro','teacher_intro','first_course','teach_plan','exam_type','reference_book'];
10 | if(!validParam.includes(param.column)){
11 | return ErrorHelper.newError('参数不可用',406);
12 | }
13 | }
14 | }
15 | module.exports=courseHelper;
--------------------------------------------------------------------------------
/teachSystem/api/courseware.js:
--------------------------------------------------------------------------------
1 | const coursewareDao = require('../dao/coursewareDao.js');
2 | const uuid = require('uuid/v1');
3 | const fs = require('fs-extra');
4 |
5 | /**
6 | * 获取某一门课课件 /course/:c_id/courseware
7 | */
8 | exports['GET /course/:c_id/courseware'] = async (ctx, next) => {
9 | let res = await coursewareDao.getAllofClass(ctx.params.c_id);
10 | ctx.onSuccess(res);
11 | }
12 |
13 |
14 | /**
15 | * 提交课件 /course/:c_id/courseware
16 | */
17 | exports['POST /course/:c_id/courseware'] = async (ctx, next) => {
18 | let res = await coursewareDao.add(ctx.params.c_id, ctx.request.body.files);
19 | ctx.onSuccess(res);
20 | }
21 |
22 | /**
23 | * 删除课件/course/:c_id/courseware/:cw_id
24 | */
25 | exports['DELETE /course/:c_id/courseware/:cw_id'] = async (ctx, next) => {
26 | await coursewareDao.deleteOne(ctx.params.c_id, ctx.params.cw_id);
27 | ctx.onSuccess();
28 | }
--------------------------------------------------------------------------------
/teachSystem/api/grade.js:
--------------------------------------------------------------------------------
1 | const uuid = require('uuid/v1');
2 | const gradeDao = require('../dao/gradeDao.js');
3 |
4 | /**
5 | * 获取某门课程下所有的年级
6 | * /course/:c_id/grade
7 | */
8 | let get_course_cId_grade = async (ctx, next) => {
9 | let res = await gradeDao.getByCourseId(ctx.params.c_id);
10 | ctx.onSuccess(res);
11 | }
12 |
13 | let post = async (ctx, next) => {
14 | let res = await gradeDao.addOne(ctx.allParams);
15 | ctx.onSuccess(res);
16 | }
17 |
18 | /**
19 | * 删除一个年级,仅当该年级下无班级时才可删除
20 | */
21 | let delete_gId= async (ctx, next) => {
22 | await gradeDao.deleteOneGrade(ctx.params.g_id);
23 | ctx.onSuccess();
24 | }
25 |
26 | module.exports = {
27 | 'GET /course/:c_id/grade': get_course_cId_grade,
28 | 'POST /grade': post,
29 | 'DELETE /grade/:g_id': delete_gId
30 | }
--------------------------------------------------------------------------------
/teachSystem/api/knowledgePoint.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const knowledgePointDao = require('../dao/knowledgePointDao.js');
3 |
4 | /**
5 | * 返回某门课的全部知识点,按章节分类
6 | */
7 | exports["GET /course/:c_id/knowledge_point"] = async (ctx, next) => {
8 | let res = await knowledgePointDao.getPontsOrderBySection(ctx.params.c_id);
9 | ctx.onSuccess(res);
10 | }
11 |
12 | //返回某位学生知识点答题情况
13 | exports["GET /user/:u_id/course/:c_id/knowledge_point/condition"]=async(ctx,next)=>{
14 | let {u_id,c_id}=ctx.params;
15 | let res = await knowledgePointDao.getStudentCondition(u_id,c_id);
16 | ctx.onSuccess(res);
17 | }
18 |
19 | /**
20 | * 新增一个知识点
21 | */
22 | exports["POST /knowledge_point"] = async (ctx, next) => {
23 | let res = await knowledgePointDao.addOne(ctx.allParams);
24 | ctx.onSuccess(res);
25 | }
26 |
27 | /**
28 | * 删除一个知识点
29 | */
30 | exports[`DELETE /knowledge_point/:kp_id`] = async (ctx, next) => {
31 | await knowledgePointDao.deleteOne(ctx.params.kp_id);
32 | ctx.onSuccess();
33 | }
--------------------------------------------------------------------------------
/teachSystem/api/public.js:
--------------------------------------------------------------------------------
1 | const publicDao = require("../dao/publicDao.js");
2 | /**
3 | * 图片上传
4 | */
5 | exports["POST /public/img_upload"] = async ctx => {
6 | let res = await publicDao.uploadImg(ctx.allParams);
7 | ctx.onSuccess(res);
8 | };
9 |
10 | /**
11 | * 获取系统时间
12 | */
13 | exports["GET /public/system_time"] = async ctx => {
14 | ctx.onSuccess(Date.now());
15 | };
16 |
17 | /**
18 | * 登录
19 | */
20 | exports["POST /public/login"] = async ctx => {
21 | await publicDao.login(ctx);
22 | };
23 |
24 | /**
25 | * 获取验证码
26 | */
27 | exports["GET /public/authcode"] = async ctx => {
28 | await publicDao.authCode(ctx);
29 | };
30 |
31 | /**
32 | * 重置密码
33 | */
34 | exports["POST /public/reset-password"] = async ctx => {
35 | await publicDao.resetPass(ctx);
36 | };
37 |
38 | exports["POST /public/mail-auth-code"] = async ctx => {
39 | await publicDao.sendMail(ctx);
40 | };
41 |
--------------------------------------------------------------------------------
/teachSystem/api/questionAnswer.js:
--------------------------------------------------------------------------------
1 | const questionAnswerDao = require("../dao/questionAnswerDao.js");
2 |
3 | //获取一个用户的对话type=start/reply/all(我发起的/我解答的/全部)
4 | //?type=start&c_id=&is_closed=1
5 | exports['GET /question_answer'] = async ctx => {
6 | let {
7 | c_id,
8 | type,
9 | is_closed
10 | } = ctx.allParams;
11 | let res = await questionAnswerDao.getQuestionAnswer(ctx.userInfo.u_id, c_id, type, is_closed);
12 | ctx.onSuccess(res);
13 | }
14 |
15 | //获取某对话消息中时间大于time的 time=?
16 | exports['GET /question_answer/:qa_id/detail'] = async ctx => {
17 | let {
18 | time,
19 | type
20 | } = ctx.allParams;
21 | let res = await questionAnswerDao.getMessages(ctx.userInfo.u_id, ctx.params.qa_id, time, type);
22 | ctx.onSuccess(res);
23 | }
24 |
25 | //新建一个答疑对话
26 | exports['POST /question_answer'] = async ctx => {
27 | let res = await questionAnswerDao.addOne(ctx.userInfo.u_id, ctx.allParams);
28 | ctx.onSuccess(res);
29 | }
30 |
31 | //对话中新增一条消息
32 | exports['POST /question_answer/:qa_id/question_answer_detail'] = async ctx => {
33 | let res = await questionAnswerDao.addOneMessage(ctx.userInfo.u_id, ctx.params.qa_id, ctx.allParams.content);
34 | ctx.onSuccess(res);
35 | }
36 |
37 | //关闭一个答疑对话
38 | exports['PUT /question_answer/:qa_id/close'] = async ctx => {
39 | await questionAnswerDao.closeOne(ctx.userInfo.u_id, ctx.allParams.start_u_id, ctx.params.qa_id);
40 | ctx.onSuccess();
41 | }
--------------------------------------------------------------------------------
/teachSystem/api/questionGroup.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const questionGroupDao = require('../dao/questionGroupDao.js');
3 | const questionGroupHelper = require('./questionGroupHelper.js');
4 | /**
5 | * 条件检索
6 | */
7 | exports["GET /question_group"] = async (ctx, next) => {
8 | let param = questionGroupHelper.getParam(ctx.allParams);
9 | let res = await questionGroupDao.getByParam(param);
10 | ctx.onSuccess(res);
11 | }
12 |
13 | /**
14 | * 获取本题组所有题目
15 | */
16 | exports["GET /question_group/:qg_id/all_question"] = async (ctx,next)=>{
17 | let res = await questionGroupDao.getQuestion(ctx.params.qg_id);
18 | ctx.onSuccess(res);
19 | }
20 |
21 | /**
22 | * 新增一组题目
23 | */
24 | exports["POST /course/:c_id/question_group"] = async (ctx, next) => {
25 | let res = await questionGroupDao.addOne(ctx.params.c_id, ctx.allParams);
26 | ctx.onSuccess(res);
27 | }
28 |
29 | /**
30 | * 删除一个题组
31 | */
32 | exports["DELETE /course/:c_id/question_group/:qg_id"] = async(ctx,next)=>{
33 | await questionGroupDao.deleteOne(ctx.params.c_id,ctx.params.qg_id);
34 | ctx.onSuccess();
35 | }
--------------------------------------------------------------------------------
/teachSystem/api/questionGroupHelper.js:
--------------------------------------------------------------------------------
1 | const ErrorHelper = require('../util/ErrorHelper.js');
2 | class questionGroupHelper{
3 | static getParam(params){
4 | try{
5 | params= JSON.parse(new Buffer(params.data,'base64').toString());
6 | }catch(err){
7 | ErrorHelper.Error400("参数不合法:");
8 | }
9 | return params;
10 | }
11 | }
12 |
13 | module.exports=questionGroupHelper;
--------------------------------------------------------------------------------
/teachSystem/api/questionLibrary.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const questionLibraryHelper = require('./questionLibraryHelper.js');
3 | const questionLibraryDao = require('../dao/questionLibraryDao.js');
4 |
5 | /**
6 | * 条件检索用户
7 | * {
8 | * searchContent:'', 搜索内容
9 | * searchColumn:'', 搜索字段
10 | * c_id:'',课程id
11 | * q_type:[],显示题型
12 | * isExam:0/1/2 0:练习用 1:考试用 2:所有
13 | * count:false 是否返回总记录数
14 | * start:0
15 | * pageSize:10
16 | * }
17 | */
18 | exports["GET /question_library"] = async (ctx, next) => {
19 | let params = questionLibraryHelper.checkGetParam(ctx.allParams);
20 | let res = await questionLibraryDao.getByParam(params);
21 | ctx.onSuccess(res);
22 | }
23 |
24 | //获取展示题目需要信息
25 | exports["GET /question_library/:ql_id/show_question"] = async (ctx, next) => {
26 | let res = await questionLibraryDao.getShowQuestion(ctx.params.ql_id);
27 | ctx.onSuccess(res);
28 | }
29 |
30 | /**
31 | * 插入题目
32 | */
33 | exports["POST /question_library"] = async (ctx, next) => {
34 | questionLibraryHelper.checkPostParam(ctx.allParams);
35 | let res = await questionLibraryDao.addOne(ctx.allParams);
36 | ctx.onSuccess(res);
37 | }
38 |
39 | /**
40 | * 删除题目
41 | */
42 | exports["DELETE /question_library/:ql_id"] = async (ctx, next) => {
43 | await questionLibraryDao.deleteOne(ctx.params.ql_id);
44 | ctx.onSuccess();
45 | }
46 |
47 | /**
48 | * 批量删除题目
49 | */
50 | exports["DELETE /course/:c_id/question_library"] = async (ctx, next) => {
51 | let id = ctx.allParams.id.split(',');
52 | let failList = [];
53 | for (let i = 0; i < id.length; i++) {
54 | try {
55 | await questionLibraryDao.deleteOne(id[i]);
56 | } catch (err) {
57 | failList.push(id[i]);
58 | }
59 | }
60 | ctx.onSuccess(failList);
61 | }
--------------------------------------------------------------------------------
/teachSystem/api/questionLibraryHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const ErrorHelper = require('../util/ErrorHelper.js');
3 | class questionLibraryHelper {
4 | //检查检索参数是否合法
5 | static checkGetParam(params) {
6 | try {
7 | params = JSON.parse(new Buffer(params.data, 'base64').toString());
8 | if (params.searchColumn !== '' && (params.searchColumn !== 'ql_id' && params.searchColumn !== 'q_simple_description'))
9 | throw new Error();
10 | let index = params.q_type.findIndex(item => typeof(item) !== 'number');
11 | if (index != -1)
12 | throw new Error();
13 | } catch (err) {
14 | throw ErrorHelper.newError("参数不合法", 406);
15 | }
16 | return params;
17 | }
18 |
19 | static checkPostParam(params) {
20 | if (params.c_id == undefined)
21 | throw ErrorHelper.newError("c_id不存在", 406);
22 | if (params.q_simple_description == undefined)
23 | throw ErrorHelper.newError("题目简述不存在", 406);
24 | if (params.q_description == undefined || params.q_description.length == 0)
25 | throw ErrorHelper.newError("题目内容为空", 406);
26 | if (params.q_description.length >= 65535)
27 | throw ErrorHelper.newError("题目内容超过65535", 406);
28 | if (params.q_type < 3) {
29 | if (params.alternative_answer == undefined || params.alternative_answer.length == 0)
30 | throw ErrorHelper.newError("备选项为空", 406);
31 | let length = 0;
32 | params.alternative_answer.forEach(item => length += item.length);
33 | if (length >= 4096)
34 | throw ErrorHelper.newError("备选项长度超过4096", 406);
35 | } else {
36 | params.alternative_answer = '';
37 | }
38 | if (params.answer == undefined || params.answer.length == 0)
39 | throw ErrorHelper.newError("未指定正确答案", 406);
40 | }
41 | }
42 |
43 | module.exports = questionLibraryHelper;
--------------------------------------------------------------------------------
/teachSystem/api/section.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const sectionDao = require('../dao/sectionDao.js');
3 |
4 | /**
5 | * 获取某门课程所有章节
6 | */
7 | exports['GET /course/:c_id/section'] = async (ctx, next) => {
8 | let res = await sectionDao.getByCourseId(ctx.params.c_id);
9 | ctx.onSuccess(res);
10 | }
11 |
12 | /**
13 | * 新增一个章节
14 | */
15 | exports['POST /section'] = async (ctx, next) => {
16 | let res = await sectionDao.addOne(ctx.allParams);
17 | ctx.onSuccess(res);
18 | }
19 | /**
20 | * 删除一个章节
21 | */
22 | exports['DELETE /section/:s_id'] = async (ctx, next) => {
23 | await sectionDao.deleteOne(ctx.params.s_id);
24 | ctx.onSuccess();
25 | }
--------------------------------------------------------------------------------
/teachSystem/api/testHelper.js:
--------------------------------------------------------------------------------
1 | const ErrorHelper = require('../util/ErrorHelper.js');
2 | const MysqlHelper = require("../util/MysqlHelper.js");
3 |
4 | class testHelper {
5 | static checkGetParam(param) {
6 | try {
7 | param = JSON.parse(new Buffer(param.data, 'base64').toString());
8 | } catch (err) {
9 | throw ErrorHelper.Error400("参数不符合要求");
10 | }
11 | return param;
12 | }
13 | //检查能否获取测试题
14 | static async checkTest(u_id, c_id, test_id) {
15 | let testData = await MysqlHelper.first(`
16 | select sc_id,submit_time,start_time,end_time,t_type,start_do_time from student_test_view where u_id=? and c_id=? and test_id=?
17 | `, u_id, c_id, test_id);
18 | if (testData == null)
19 | throw ErrorHelper.Error400("本次测试不存在");
20 | let now = Date.now();
21 | if (testData.start_time > now)
22 | throw ErrorHelper.Error400("测试还未开始");
23 | if (testData.t_type == 2 && testData.submit_time > 0 && testData.end_time > now)
24 | throw ErrorHelper.Error403("考试正在进行,且您已提交,无法获取题目");
25 | if (testData.end_time > now && testData.start_do_time == null)
26 | await MysqlHelper.update(`update student_test set start_time = ? where sc_id=? and test_id=?`, Date.now(), testData.sc_id, test_id);
27 | }
28 | //检查是否可以提交答案
29 | static async checkSubmit(u_id, c_id, test_id) {
30 | let testData = await MysqlHelper.first(`
31 | select sc_id,submit_time,start_time,end_time,t_type,start_do_time from student_test_view where u_id=? and c_id=? and test_id=?
32 | `, u_id, c_id, test_id);
33 | if (testData == null)
34 | throw ErrorHelper.Error400("本次测试不存在");
35 | let now = Date.now();
36 | if (testData.start_time > now)
37 | throw ErrorHelper.Error400("测试还未开始");
38 | if ((testData.end_time - now) < -60 * 1000) //一分钟缓冲时间,避免提交失败
39 | throw ErrorHelper.Error400("测试已经结束");
40 | // if (testData.submit_time > 0)
41 | // throw ErrorHelper.Error400("已经提交答案,请勿重复提交");
42 | }
43 | }
44 |
45 | module.exports = testHelper;
--------------------------------------------------------------------------------
/teachSystem/api/userHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | let stringHelper = require('../util/StringHelper.js');
3 | let ErrorHelper = require('../util/ErrorHelper.js');
4 |
5 | class userHelper {
6 | //检查增加用户参数
7 | static checkAddUserParams(params) {
8 | if (stringHelper.isEmpty(params.u_name) || stringHelper.isEmpty(params.u_type) ||
9 | stringHelper.isEmpty(params.code) || stringHelper.isEmpty(params.password)) {
10 | throw ErrorHelper.Error400("某项输入为空");
11 | }
12 | }
13 |
14 | static checkChangePasswordParams(params,userInfo) {
15 | if (stringHelper.isEmpty(params.password) || stringHelper.isEmpty(params.newPassword) || stringHelper.isEmpty(params.repeatNewPassword)) {
16 | throw ErrorHelper.Error400("某项输入为空");
17 | }
18 | if (params.newPassword != params.repeatNewPassword) {
19 | throw ErrorHelper.Error400("两次密码输入不一致");
20 | }
21 | }
22 |
23 | //检查检索参数
24 | static getParams(data) {
25 | let params = {};
26 | try {
27 | let param = new Buffer(data, 'base64').toString();
28 | param = JSON.parse(param);
29 | params.orderBy = param.orderBy;
30 | params.start = param.start;
31 | params.count = param.count;
32 | params.sort = param.sort == 'desc' ? 'desc' : 'asc';
33 | if (param.search != undefined && param.search.content.length > 0 && (param.search.filed == 'code' || param.search.filed == 'u_id' || param.search.filed == 'u_name') && typeof (param.search.content) == 'string') {
34 | params.search = param.search;
35 | }
36 | if (param.u_type.length > 0) {
37 | params.u_type = '(';
38 | for (let i = 0; i < param.u_type.length; i++) {
39 | if (typeof (param.u_type[i]) != 'number') {
40 | throw new Error();
41 | }
42 | if (i == 0) {
43 | params.u_type += param.u_type[i];
44 | } else {
45 | params.u_type += ',' + param.u_type[i];
46 | }
47 | }
48 | params.u_type += ')';
49 | } else {
50 | params.u_type = null;
51 | }
52 | return params;
53 | } catch (error) {
54 | throw ErrorHelper.Error400('参数不合法');
55 | }
56 | }
57 | }
58 |
59 | module.exports = userHelper;
--------------------------------------------------------------------------------
/teachSystem/authority/dbAuth.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 扫描所有路由加入到资源表中
3 | */
4 | const fs = require('fs');
5 | const path = require('path');
6 | const config = require('../config/config.js');
7 | const mysqlHelper = require('../util/MysqlHelper.js');
8 |
9 | (async() => {
10 | let folderPath = path.join(config.rootPath, '/api');
11 | let files = fs.readdirSync(folderPath);
12 | for (let i = 0; i < files.length; i++) {
13 | let file = path.join(folderPath, files[i]);
14 | let temp = require(file);
15 | let urls = Object.keys(temp);
16 | for (let j = 0; j < urls.length; j++) {
17 | let params = urls[j].split(' ');
18 | try {
19 | await mysqlHelper.execute(`
20 | insert into resource(url,method,description) values(?,?,?)
21 | `, params[1], params[0], '');
22 | console.log(`新增资源:${params[1]},方法:${params[0]}`);
23 | } catch (error) {
24 | }
25 | }
26 | }
27 | console.log('更新权限资源完毕');
28 | process.exit();
29 | })();
--------------------------------------------------------------------------------
/teachSystem/authority/index.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const koaBody = require('koa-body');
3 | const Router = require('koa-router');
4 | const static = require('koa-static');
5 | const MysqlHelper = require('../util/MysqlHelper.js');
6 |
7 | let app = new Koa();
8 | let router = new Router();
9 |
10 | let path = require('path');
11 |
12 | //获取角色列表
13 | router.get('/role', async (ctx, next) => {
14 | let res = await MysqlHelper.row('select * from Jurisdiction');
15 | ctx.body = res;
16 | console.log(ctx.body);
17 | await next();
18 | })
19 |
20 | //获取资源列表
21 | router.get('/url', async (ctx, next) => {
22 | let res = await MysqlHelper.row('select * from resource');
23 | ctx.body = res;
24 | await next();
25 | })
26 |
27 | //获取某个角色权限
28 | router.get('/role_url', async (ctx, next) => {
29 | let roleId = ctx.query.roleId;
30 | ctx.body = await MysqlHelper.row(`select b.r_id from Jurisdiction_resource a inner join
31 | resource b on a.r_id = b.r_id where a.j_id = ?`, roleId);
32 | })
33 |
34 | //新增一个角色
35 | router.get('/add_role', async ctx => {
36 | let name = ctx.query.name;
37 | let res = await MysqlHelper.execute(`insert into jurisdiction(content) values(?)`,
38 | name);
39 | ctx.body = {
40 | j_id:res.insertId,
41 | content: name
42 | }
43 | })
44 |
45 | //修改权限
46 | router.get('/change_role_url', async (ctx, next) => {
47 | let j_id = ctx.query.j_id;
48 | let urls = JSON.parse(ctx.query.arr);
49 | await MysqlHelper.execute(`delete from Jurisdiction_resource where
50 | j_id=?`, j_id);
51 | for (let i = 0; i < urls.length; i++) {
52 | await MysqlHelper.execute(`insert into Jurisdiction_resource values(?,?)`,
53 | j_id, urls[i]);
54 | }
55 | ctx.body = "操作成功";
56 | })
57 |
58 | app.use(static(path.join(__dirname, 'static'), {
59 | maxAge: 0
60 | })).use(koaBody()).use(router.routes()).listen(8084);
61 | console.log("授权管理启动于:8084");
--------------------------------------------------------------------------------
/teachSystem/authority/readme.md:
--------------------------------------------------------------------------------
1 | 本文件夹用于给角色进行权限管理,不适用于用户权限管理。请使用后及时关闭。
--------------------------------------------------------------------------------
/teachSystem/authority/static/css/main.css:
--------------------------------------------------------------------------------
1 | #app{
2 | min-width:900px;
3 | max-height:900px;
4 | margin-left:10%;
5 | margin-right:10%;
6 | }
7 | .top{
8 | border-bottom:5px solid black;
9 | margin-bottom:20px;
10 | color: red;
11 | font-size: 1.5em;
12 | text-align: center;
13 | }
14 | .left{
15 | width:30%;
16 | float: left;
17 | }
18 | .center{
19 | width:2px;
20 | height:70%;
21 | background:black;
22 | float:left;
23 | }
24 | .right{
25 | width:69%;
26 | float: right;
27 | }
28 | .submit{
29 | margin-top:50px;
30 | text-align: center;
31 | }
32 | .role-item{
33 | padding:5px;
34 | margin:5px;
35 | font-size:1.3em;
36 | line-height: 1.5em;
37 | }
38 | .url-item{
39 | border-bottom:1px solid #dbd4d4;
40 | margin-bottom:5px;
41 | }
42 | .unchecked{
43 | color:blue;
44 | background:#dceced;
45 | }
46 | .checked{
47 | background:#959b9b;
48 | }
49 | .hover-pointer{
50 | cursor:pointer;
51 | }
52 |
--------------------------------------------------------------------------------
/teachSystem/authority/static/main.html:
--------------------------------------------------------------------------------
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 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/teachSystem/config/judgeConfig.js:
--------------------------------------------------------------------------------
1 | const default_env = ["LANG=en_US.UTF-8", "LANGUAGE=en_US:en", "LC_ALL=en_US.UTF-8"];
2 | module.exports = {
3 | c: {
4 | compile: {
5 | src_name: "main.c",
6 | exe_name: "main",
7 | max_cpu_time: 3000,
8 | max_real_time: 5000,
9 | max_memory: 128 * 1024 * 1024,
10 | compile_command: "/usr/bin/gcc -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c99 {src_path} -lm -o {exe_path}"
11 | },
12 | run: {
13 | command: "{exe_path}",
14 | seccomp_rule: "c_cpp",
15 | env: default_env
16 | }
17 | },
18 | cpp: {
19 | compile: {
20 | src_name: "main.cpp",
21 | exe_name: "main",
22 | max_cpu_time: 3000,
23 | max_real_time: 5000,
24 | max_memory: 128 * 1024 * 1024,
25 | compile_command: "/usr/bin/g++ -DONLINE_JUDGE -O2 -w -fmax-errors=3 -std=c++11 {src_path} -lm -o {exe_path}"
26 | },
27 | run: {
28 | command: "{exe_path}",
29 | seccomp_rule: "c_cpp",
30 | env: default_env
31 | }
32 | },
33 | java: {
34 | name: "java",
35 | compile: {
36 | src_name: "Main.java",
37 | exe_name: "Main",
38 | max_cpu_time: 3000,
39 | max_real_time: 5000,
40 | max_memory: -1,
41 | compile_command: "/usr/bin/javac {src_path} -d {exe_dir} -encoding UTF8"
42 | },
43 | run: {
44 | command: "/usr/bin/java -cp {exe_dir} -XX:MaxRAM={max_memory}k -Djava.security.manager -Dfile.encoding=UTF-8 -Djava.security.policy==/etc/java_policy -Djava.awt.headless=true Main",
45 | seccomp_rule: null,
46 | env: default_env,
47 | memory_limit_check_only: 1
48 | }
49 | },
50 | py2: {
51 | compile: {
52 | src_name: "solution.py",
53 | exe_name: "solution.pyc",
54 | max_cpu_time: 3000,
55 | max_real_time: 5000,
56 | max_memory: 128 * 1024 * 1024,
57 | compile_command: "/usr/bin/python -m py_compile {src_path}"
58 | },
59 | run: {
60 | command: "/usr/bin/python {exe_path}",
61 | seccomp_rule: "general",
62 | env: default_env
63 | }
64 | },
65 | py3: {
66 | compile: {
67 | src_name: "solution.py",
68 | exe_name: "__pycache__/solution.cpython-35.pyc",
69 | max_cpu_time: 3000,
70 | max_real_time: 5000,
71 | max_memory: 128 * 1024 * 1024,
72 | compile_command: "/usr/bin/python3 -m py_compile {src_path}"
73 | },
74 | run: {
75 | command: "/usr/bin/python3 {exe_path}",
76 | seccomp_rule: "general",
77 | env: ["PYTHONIOENCODING=UTF-8", ...default_env]
78 | }
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/teachSystem/dao/announcementDao.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const MysqlHelper = require('../util/MysqlHelper.js');
3 |
4 | class announcementDao {
5 | static async getByParam(params) {
6 | let res;
7 | let data = await MysqlHelper.row(`
8 | select a_id,topic,start_time,end_time from announcement where c_id=? order by ${params.column} ${params.sort} limit ?,10
9 | `, params.c_id, params.start);
10 | data.forEach(item => {
11 | if (item.end_time > Date.now() && item.start_time < Date.now()) {
12 | item.isClosed = false;
13 | } else {
14 | item.isClosed = true;
15 | }
16 | })
17 | if (params.count) {
18 | res = {};
19 | res.count = await MysqlHelper.single(`select count(a_id) from announcement where c_id=?`, params.c_id);
20 | res.data = data;
21 | } else {
22 | res = data;
23 | }
24 | return res;
25 | }
26 |
27 | static async getOpenAnnouncement(c_id) {
28 | let now = Date.now();
29 | let res = await MysqlHelper.row(`
30 | SELECT a_id,topic,start_time from announcement where c_id=? and start_time and end_time>? order by start_time desc
31 | `, c_id, now, now);
32 | return res;
33 | }
34 |
35 | static async getContent(a_id) {
36 | return await MysqlHelper.single(`select content from announcement where a_id=?`, a_id);
37 | }
38 |
39 | static async switchOne(a_id, action) {
40 | let end_time = Date.now();
41 | let start_time = Date.now();
42 | if (action == 'true') {
43 | end_time += 30 * 365 * 24 * 60 * 60 * 1000;
44 | }
45 | await MysqlHelper.execute(`update announcement set end_time=?,start_time=? where a_id=?`, end_time, start_time, a_id);
46 | return {end_time,start_time};
47 | }
48 |
49 | static async addOne(data) {
50 | if (data.start_time == '') {
51 | data.start_time = Date.now();
52 | data.end_time = 4100731932000;
53 | }
54 | await MysqlHelper.execute(`
55 | insert into announcement(c_id,topic,content,start_time,end_time) values(?,?,?,?,?)
56 | `, data.c_id, data.topic, data.content, data.start_time, data.end_time);
57 | }
58 |
59 | static async deleteOne(a_id) {
60 | await MysqlHelper.execute(`delete from announcement where a_id=?`, a_id);
61 | }
62 |
63 | }
64 |
65 | module.exports = announcementDao;
--------------------------------------------------------------------------------
/teachSystem/dao/badgeDao.js:
--------------------------------------------------------------------------------
1 | const MysqlHelper = require('../util/MysqlHelper.js');
2 | const RedisHelper = require('../util/RedisHelper.js');
3 | const badgeHelper = require('./badgeHelper.js');
4 |
5 | class badgeDao {
6 | static async getUserBadge(u_id) {
7 | let redisKey = u_id + '_badge_update';
8 | let res = await RedisHelper.getString(redisKey);
9 | if (res == null) {
10 | await badgeHelper.checkCourseNum(u_id);
11 | await badgeHelper.checkEvaluate(u_id);
12 | res = await MysqlHelper.row(`
13 | SELECT a.get_time,b.* FROM student_badge a INNER JOIN badge b ON a.b_id = b.b_id WHERE u_id = ?
14 | `, u_id);
15 | res = JSON.stringify(res);
16 | await RedisHelper.setString(redisKey,res,60*60);
17 | }
18 | return JSON.parse(res);
19 | }
20 |
21 | static async getBadgeDetail(u_id, b_id, type) {
22 | let res = {};
23 | res.next = await MysqlHelper.first(`select b_name,b_img,get_condition,next_level from badge where b_id=?`, b_id);
24 | if (type == '1') {
25 | res.current = await MysqlHelper.single(`select count(*) from student_evaluate_view where u_id=?`, u_id)
26 | } else if (type == '2') {
27 | let data = await MysqlHelper.row(`select * from student_class where u_id=?`, u_id);
28 | let evaluate = 0,
29 | count = 0;
30 | data.forEach(item => {
31 | let questionNum = item.difficulty_1 + item.difficulty_2 + item.difficulty_3 + item.difficulty_4 + item.difficulty_5;
32 | if (questionNum >= 20) {
33 | evaluate += item.evaluate;
34 | count++;
35 | }
36 | })
37 | res.current = count > 0 ? evaluate / count : 0;
38 | }
39 | return res;
40 | }
41 | }
42 | module.exports = badgeDao;
--------------------------------------------------------------------------------
/teachSystem/dao/badgeHelper.js:
--------------------------------------------------------------------------------
1 | const MysqlHelper = require('../util/MysqlHelper.js');
2 | class badgeHelper {
3 | //检查课程注册门数徽章获取情况
4 | static async checkCourseNum(u_id) {
5 | let count = await MysqlHelper.single(`select count(*) from student_evaluate_view where u_id=?`, u_id);
6 | let b_id = await MysqlHelper.single(`select b_id from student_badge where b_id>0 and b_id<5 and u_id=?`, u_id);
7 | if (count == 0 && b_id != null) {
8 | await MysqlHelper.delete(`delete from student_badge where b_id=? and u_id=?`,b_id, u_id);
9 | return;
10 | }
11 | let current;
12 | if (count < 4)
13 | current= 2;
14 | else if (count < 7)
15 | current= 3;
16 | else if (count >= 7)
17 | current= 4;
18 | if (b_id != null) {
19 | if (b_id != current)
20 | await MysqlHelper.update(`update student_badge set b_id=?,get_time=? where b_id=? and u_id=?`, current, Date.now(), b_id, u_id);
21 | } else {
22 | await MysqlHelper.insert(`insert into student_badge value(?,?,?)`, u_id, current, Date.now());
23 | }
24 | }
25 |
26 | //平均能力值徽章<2:青铜 <4白银 <6黄金 <8铂金 <9砖石 <=10王者并且做题数大于20
27 | static async checkEvaluate(u_id) {
28 | let data = await MysqlHelper.row(`select * from student_class where u_id=?`, u_id);
29 | let id = await MysqlHelper.single(`select b_id from student_badge where b_id>4 and b_id<11 and u_id=?`, u_id);
30 | let evaluate = 0;
31 | let count = 0;
32 | data.forEach(item => {
33 | let questionNum = item.difficulty_1 + item.difficulty_2 + item.difficulty_3 + item.difficulty_4 + item.difficulty_5;
34 | if (questionNum >= 20) {
35 | evaluate += item.evaluate;
36 | count++;
37 | }
38 | })
39 | evaluate = evaluate / count;
40 | let currentId = 5;
41 | if (evaluate < 2)
42 | currentId = 5;
43 | else if (evaluate < 4)
44 | currentId = 6;
45 | else if (evaluate < 6)
46 | currentId = 7;
47 | else if (evaluate < 8)
48 | currentId = 8;
49 | else if (evaluate < 9)
50 | currentId = 9;
51 | else if (evaluate <= 10)
52 | currentId = 10;
53 | if (id != null) {
54 | if (id != currentId)
55 | await MysqlHelper.execute(`update student_badge set b_id=?,get_time=? where b_id=? and u_id=?`, currentId, Date.now(), id, u_id);
56 | } else {
57 | await MysqlHelper.insert(`insert into student_badge value(?,?,?)`, u_id, currentId, Date.now());
58 | }
59 | }
60 |
61 |
62 | }
63 |
64 | module.exports = badgeHelper;
--------------------------------------------------------------------------------
/teachSystem/dao/coursewareDao.js:
--------------------------------------------------------------------------------
1 | const mysqlHelper = require('../util/MysqlHelper.js');
2 | const stringHelper = require('../util/StringHelper.js');
3 | const config = require('../config/config.js');
4 | const fs = require('fs-extra');
5 | const path = require('path');
6 |
7 | class coursewareDao {
8 | static async getAllofClass(c_id) {
9 | let res = await mysqlHelper.row(`select * from courseware where c_id=?`, c_id);
10 | res.forEach(item=>{
11 | item.downloadUrl = `${config.host}/course/${c_id}/courseware/${item.cw_name}`;
12 | })
13 | return res;
14 | }
15 |
16 | static async add(c_id, files) {
17 | let dir = path.join(config.rootPath, 'files/course', c_id, 'courseware');
18 | await fs.ensureDir(dir);
19 | let res = [];
20 | for (let key in files) {
21 | let file = files[key];
22 | let filePath = path.join(dir, file.name);
23 | await fs.remove(filePath);
24 | await fs.rename(file.path, filePath);
25 | let reply = await mysqlHelper.execute(`insert into courseware(c_id,cw_name,create_time) value(?,?,?)`, c_id, file.name, Date.now());
26 | res.push({
27 | id: reply.insertId,
28 | create_time: Date.now(),
29 | cw_name:file.name
30 | });
31 | }
32 | return res;
33 | }
34 |
35 | static async deleteOne(c_id, cw_id) {
36 | let name = await mysqlHelper.single(`select cw_name from courseware where cw_id=?`,cw_id);
37 | await fs.remove(path.join(config.rootPath,'files/course',c_id,'courseware',name));
38 | await mysqlHelper.execute(`delete from courseware where c_id=? and cw_id=?`, c_id, cw_id);
39 | }
40 |
41 | }
42 |
43 | module.exports = coursewareDao;
--------------------------------------------------------------------------------
/teachSystem/dao/gradeDao.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const mysqlHelper = require('../util/MysqlHelper.js');
3 | const stringHelper = require('../util/StringHelper.js');
4 | const ErrorHelper = require('../util/ErrorHelper.js');
5 |
6 |
7 | class gradeDao {
8 |
9 | static async getByCourseId(courseId) {
10 | let res = await mysqlHelper.row(`select g_id,content from grade where c_id=? order by content`, courseId);
11 | res.forEach(item => {
12 | item.name = item.content;
13 | delete item.content;
14 | });
15 | return res;
16 | }
17 |
18 |
19 | static async addOne(data) {
20 | let res = await mysqlHelper.execute(`insert into grade(c_id,content) value(?,?)`, data.c_id, data.content);
21 | return res.insertId;
22 | }
23 |
24 | static async deleteOneGrade(g_id) {
25 | let count = await mysqlHelper.single(`select count(*) from class where g_id=?`, g_id);
26 | if (count == 0) {
27 | await mysqlHelper.execute(`delete from grade where g_id=?`, g_id);
28 | } else {
29 | ErrorHelper.newError(`仅当该年级下无班级时可删除`, 403);
30 | }
31 | }
32 | }
33 |
34 | module.exports = gradeDao;
--------------------------------------------------------------------------------
/teachSystem/dao/knowledgePointDao.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const MysqlHelper = require('../util/MysqlHelper.js');
3 | const ErrorHelper = require('../util/ErrorHelper.js');
4 |
5 | class knowledgePointDao {
6 | static async getPontsOrderBySection(c_id) {
7 | let sections = await MysqlHelper.row(`select s_id,s_name from section where c_id=?`, c_id);
8 | for (let i = 0; i < sections.length; i++) {
9 | sections[i].children = await MysqlHelper.row(`select kp_id,content from knowledge_point where s_id=?`, sections[i].s_id);
10 | }
11 | return sections;
12 | }
13 | //某学生知识点掌握情况
14 | static async getStudentCondition(u_id, c_id) {
15 | let res = await MysqlHelper.row(`select kp_id,count,right_count from student_knowledge_view where u_id=? and c_id=?`, u_id, c_id);
16 | return res;
17 | }
18 |
19 |
20 |
21 | static async addOne(data) {
22 | let res = await MysqlHelper.execute(`insert into knowledge_point(s_id,content) value(?,?)`, data.s_id, data.content);
23 | return res.insertId;
24 | }
25 |
26 | static async deleteOne(kp_id) {
27 | await MysqlHelper.delete(`delete from knowledge_point where kp_id=?`, kp_id);
28 | }
29 | }
30 |
31 | module.exports = knowledgePointDao;
--------------------------------------------------------------------------------
/teachSystem/dao/sectionDao.js:
--------------------------------------------------------------------------------
1 | 'use strcit'
2 | const MysqlHelper = require('../util/MysqlHelper.js');
3 | const ErrorHelper = require('../util/ErrorHelper.js');
4 |
5 | class sectionDao{
6 | static async getByCourseId(c_id){
7 | let res = await MysqlHelper.row(`select s_id,s_name from section where c_id=?`,c_id);
8 | return res;
9 | }
10 |
11 | static async addOne(data){
12 | let res = await MysqlHelper.insert(`insert into section(c_id,s_name) value(?,?)`,data.c_id,data.s_name);
13 | return res;
14 | }
15 |
16 | static async deleteOne(s_id){
17 | await MysqlHelper.delete(`delete from section where s_id=?`,s_id);
18 | }
19 | }
20 |
21 | module.exports = sectionDao;
--------------------------------------------------------------------------------
/teachSystem/dao/studentClassDao.js:
--------------------------------------------------------------------------------
1 | let mysqlHelper = require('../util/MysqlHelper.js');
2 |
3 | class StudentClass {
4 | static async add(u_id,class_id) {
5 | await mysqlHelper.execute(`
6 | insert into student_class values(?,?)
7 | `, class_id, u_id);
8 | }
9 | }
10 |
11 | module.exports=StudentClass;
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_10.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_2.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_5.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_6.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_7.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_8.png
--------------------------------------------------------------------------------
/teachSystem/files/badgeIcon/badge_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/badgeIcon/badge_9.png
--------------------------------------------------------------------------------
/teachSystem/files/code/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/code/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/files/course/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/course/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/files/imgs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/imgs/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/files/temp/uploads/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/temp/uploads/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/files/tests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/tests/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/files/userIcon/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/files/userIcon/default.jpg
--------------------------------------------------------------------------------
/teachSystem/index.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const Router = require('koa-router');
3 | const RouterMW = require('./middleware/controllerEngine.js');
4 | const koaBody = require('koa-body');
5 | const cors = require('koa2-cors');
6 | const path = require('path');
7 | const fs = require('fs');
8 | const static = require('koa-static');
9 |
10 | const config = require('./config/config.js');
11 | const handleError = require('./middleware/handleError.js');
12 | const auth = require('./middleware/auth.js');
13 | const init = require('./middleware/init.js');
14 | console.log(config);
15 |
16 | const app = new Koa();
17 | // 开发阶段使用koa-static返回静态文件,部署后使用nginx代理
18 | app.use(static(config.staticPath));
19 |
20 |
21 | let router = new Router({
22 | prefix: config.url_prefix
23 | });
24 |
25 | app.use(cors());
26 |
27 | app.use(koaBody(config.bodyLimit));
28 |
29 | app.use(init);
30 |
31 | app.use(handleError);
32 |
33 | router.use(auth);
34 | app.use(RouterMW(router, path.join(config.rootPath, 'api')));
35 |
36 | app.listen(config.port);
37 | console.log('server listened ', config.port);
--------------------------------------------------------------------------------
/teachSystem/log/log.js:
--------------------------------------------------------------------------------
1 | /*
2 | 访问日志 某ip访问某请求
3 | 操作日志 某用户执行某操作
4 | 响应时间日志 某接口的执行时间(认证时间,操作时间)
5 | */
6 | const log4js = require('log4js');
7 | const config = require('../config/config.js');
8 |
9 | log4js.configure(config.log);
10 | const visitLog = log4js.getLogger('visit');
11 | const operateLog = log4js.getLogger('operate');
12 | const responseTimeLog = log4js.getLogger('responseTime');
13 |
14 | let visit = (ip, uri, method) => {
15 | visitLog.info(ip + ' ' + uri + ' ' + method);
16 | }
17 |
18 | let operate = (uid, uri, method, data) => {
19 | operateLog.info(uid + ' ' + uri + ' ' + method + ' ' + data);
20 | }
21 |
22 | //单位ms
23 | let responseTime = (uri, method, identifyTime, operateTime) => {
24 | responseTimeLog.info(uri + ' ' + method + ' ' + identifyTime + ' ' + operateTime);
25 | }
26 |
27 | module.exports = {
28 | visit,
29 | operate,
30 | responseTime
31 | }
--------------------------------------------------------------------------------
/teachSystem/log/operate/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/operate/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/log/responseTime/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/responseTime/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/log/visit/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FleyX/teach_system/41a2e6614285f0514f3ce5f810d7a320d54475f8/teachSystem/log/visit/.gitkeep
--------------------------------------------------------------------------------
/teachSystem/middleware/controllerEngine.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const koaBody = require('koa-body');
4 | const config = require('../config/config.js');
5 |
6 | function addMapping(router, filePath) {
7 | let mapping = require(filePath);
8 | for (let url in mapping) {
9 | if (url.startsWith('GET ')) {
10 | let temp = url.substring(4);
11 | router.get(temp, mapping[url]);
12 | console.log(`----GET:${temp}`);
13 | } else if (url.startsWith('POST ')) {
14 | let temp = url.substring(5);
15 | router.post(temp, mapping[url]);
16 | console.log(`----POST:${temp}`);
17 | } else if (url.startsWith('PUT ')) {
18 | let temp = url.substring(4);
19 | router.put(temp, mapping[url]);
20 | console.log(`----PUT:${temp}`)
21 | } else if (url.startsWith('DELETE ')) {
22 | let temp = url.substring(7);
23 | router.delete(temp, mapping[url]);
24 | console.log(`----DELETE: ${temp}`);
25 | } else {
26 | console.log(`xxxxx无效路径:${url}`);
27 | }
28 | }
29 | }
30 |
31 | function addControllers(router, filePath) {
32 | let files = fs.readdirSync(filePath);
33 | files.forEach(element => {
34 | let temp = path.join(filePath, element);
35 | let state = fs.statSync(temp);
36 | if (state.isDirectory()) {
37 | addControllers(router, temp);
38 | } else {
39 | if (!temp.endsWith('Helper.js')) {
40 | console.log('\n--开始处理: ' + element + "路由");
41 | addMapping(router, temp);
42 | }
43 | }
44 | });
45 | }
46 |
47 | function engine(router, folder) {
48 | addControllers(router, folder);
49 | return router.routes();
50 | }
51 |
52 | module.exports = engine;
--------------------------------------------------------------------------------
/teachSystem/middleware/handleError.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | let f = async (ctx, next) => {
3 | try {
4 | await next();
5 | } catch (error) {
6 | if (error.status != undefined) {
7 | ctx.status = error.status;
8 | } else {
9 | ctx.status = 500;
10 | }
11 | ctx.body = error.message;
12 | console.error(error);
13 | }
14 | }
15 |
16 | module.exports = f;
--------------------------------------------------------------------------------
/teachSystem/middleware/init.js:
--------------------------------------------------------------------------------
1 | const config = require("../config/config.js");
2 | const ObjectHelper = require("../util/ObjectOperate");
3 | const Log = require("../log/log.js");
4 |
5 | let doSuccess = (ctx, body) => {
6 | switch (ctx.method) {
7 | case "GET":
8 | ctx.status = body !== null ? 200 : 204;
9 | ctx.body = body;
10 | break;
11 | case "POST":
12 | ctx.status = body !== null ? 201 : 204;
13 | ctx.body = body;
14 | break;
15 | case "PUT":
16 | ctx.status = body !== null ? 200 : 204;
17 | ctx.body = body;
18 | break;
19 | case "DELETE":
20 | ctx.status = body !== null ? 200 : 204;
21 | ctx.body = body;
22 | break;
23 | }
24 | Object.assign(ctx.allParams, ctx.params);
25 | //记录用户操作
26 | if (ctx.userInfo != undefined) {
27 | Log.operate(ctx.userInfo.u_id, ctx.path, ctx.method, JSON.stringify(ctx.allParams));
28 | }
29 | };
30 |
31 | let doFail = (ctx, body) => {
32 | if (body != null && body.message == "403") {
33 | ctx.status = 403;
34 | ctx.body = "无操作权限";
35 | } else {
36 | ctx.status = 400;
37 | ctx.body = body.message;
38 | }
39 | };
40 |
41 | module.exports = async (ctx, next) => {
42 | //合并请求参数到allParams
43 | let objs = [];
44 | if (ctx.method == "POST" || ctx.method == "PUT") {
45 | if (ctx.request.body) {
46 | if (ctx.request.body.fields != undefined && ctx.request.body.files != undefined) {
47 | objs.push(ctx.request.body.fields, ctx.request.body.files);
48 | } else {
49 | objs.push(ctx.request.body);
50 | }
51 | }
52 | }
53 | objs.push(ctx.query);
54 | ctx.allParams = ObjectHelper.combineObject(objs);
55 |
56 | ctx.onSuccess = function(body = null) {
57 | doSuccess(ctx, body);
58 | };
59 | ctx.onFail = function(body = null) {
60 | doFail(ctx, body);
61 | };
62 | let ip = config.env == "development" ? ctx.ip : ctx.request.header["x-real-ip"];
63 | Log.visit(ip, ctx.URL.pathname, ctx.method);
64 | await next();
65 | };
66 |
--------------------------------------------------------------------------------
/teachSystem/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "restdemo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "mocha",
8 | "start": "node index.js",
9 | "updateSource": "node dbAuth.js"
10 | },
11 | "author": "fxb",
12 | "license": "ISC",
13 | "dependencies": {
14 | "async": "^2.6.0",
15 | "fs-extra": "^5.0.0",
16 | "iconv-lite": "^0.4.23",
17 | "js-xlsx": "^0.8.22",
18 | "jsonwebtoken": "^8.2.0",
19 | "nodemailer": "^4.6.2",
20 | "trek-captcha": "^0.4.0",
21 | "koa": "^2.4.1",
22 | "koa-body": "^2.5.0",
23 | "koa-router": "^7.3.0",
24 | "koa-static": "^4.0.2",
25 | "koa2-cors": "^2.0.5",
26 | "log4js": "^2.3.12",
27 | "mysql": "^2.15.0",
28 | "redis": "^2.8.0",
29 | "request": "^2.85.0",
30 | "uuid": "^3.1.0",
31 | "xlsx": "^0.12.6"
32 | },
33 | "devDependencies": {
34 | "mocha": "^4.0.1"
35 | }
36 | }
--------------------------------------------------------------------------------
/teachSystem/test/courseDao.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const courseDao = require('../dao/courseDao.js');
3 |
4 | describe('#addOne(data)', () => {
5 | it('should be done', async () => {
6 | let res = await courseDao.addOne({
7 | code: '1234',
8 | c_name: "c++程序设计",
9 | teacher: [{
10 | u_id: 14,
11 | u_name: 'asdf'
12 | }, {
13 | u_id: 15,
14 | u_name: 'asdf'
15 | }]
16 | });
17 | await courseDao.deleteOne(res.c_id);
18 | })
19 | })
20 |
21 | describe('#updateOne(data,c_id)', () => {
22 | it('should be done', async () => {
23 | await courseDao.updateOne({
24 | code: '12345',
25 | c_name: "c++",
26 | teacher: [{
27 | u_id: 14,
28 | u_name: 'asdf'
29 | }]
30 | }, res.c_id);
31 | })
32 | })
33 |
34 | describe('#deleteOne(c_id)',()=>{
35 | it('should be done',async()=>{
36 | await courseDao.deleteOne();
37 | })
38 | })
--------------------------------------------------------------------------------
/teachSystem/util/ErrorHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class ErrorHelper {
4 | /**
5 | * 返回一个自定义错误
6 | * @param {String} message
7 | * @param {Number} status
8 | */
9 | static newError(message, status) {
10 | return getError(message, status);
11 | }
12 |
13 | static Error403(message){
14 | return getError(message,403);
15 | }
16 | static Error406(message){
17 | return getError(message,406);
18 | }
19 | static Error400(message){
20 | return getError(message,400);
21 | }
22 |
23 | }
24 |
25 | let getError = (message, status) => {
26 | let error = new Error(message);
27 | error.status = status;
28 | return error;
29 | }
30 |
31 | module.exports = ErrorHelper;
--------------------------------------------------------------------------------
/teachSystem/util/MailHelper.js:
--------------------------------------------------------------------------------
1 | const nodeMailer = require("nodemailer");
2 | const config = require("../config/config.js");
3 |
4 | let transporter = nodeMailer.createTransport(config.mail);
5 | var mailOptions = {
6 | from: config.mail.auth.user // 发送者
7 | };
8 |
9 | let resetPass = (code, mail) => {
10 | mailOptions.text = `您正在请求重置密码,验证码为(五分钟内有效):${code}`;
11 | mailOptions.html = `
12 | 您正在请求重置密码,验证码为(五分钟内有效):
${code}
13 | `;
14 | mailOptions.subject = "验证码请求";
15 | mailOptions.to = mail;
16 | return transporter.sendMail(mailOptions);
17 | };
18 |
19 | module.exports = {
20 | resetPass
21 | };
22 |
--------------------------------------------------------------------------------
/teachSystem/util/ObjectOperate.js:
--------------------------------------------------------------------------------
1 | /*
2 | 合并node对象,对于相同的属性后面覆盖前面
3 | */
4 | class ObjectOperation {
5 | static combineObject(...objs) {
6 | if (objs.length == 1 && objs[0] instanceof Array) {
7 | objs = objs[0];
8 | }
9 | let sum = {};
10 | let length = objs.length;
11 | for (let i = 0; i < length; i++) {
12 | sum = Object.assign(sum,objs[i]);
13 | }
14 | return sum;
15 | }
16 | }
17 |
18 |
19 | module.exports = ObjectOperation;
--------------------------------------------------------------------------------
/teachSystem/util/RequestHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const request = require('request');
3 | const fs = require('fs');
4 | const iconv = require('iconv-lite');
5 |
6 | class RequestHelper {
7 | static GET(url, headers, charset='utf-8') {
8 | var options = {
9 | method: 'GET',
10 | url: url,
11 | headers: headers || null
12 | };
13 | return new Promise((resolve, reject) => {
14 | request(options)
15 | .on('error', (err) => {
16 | reject(err);
17 | })
18 | .pipe(iconv.decodeStream(charset)).collect((err, body) => {
19 | try {
20 | resolve(JSON.parse(body));
21 | } catch (error) {
22 | resolve(body);
23 | }
24 | });
25 | })
26 | }
27 |
28 | static POST(url, form, headers, charset = 'utf-8') {
29 | var options = {
30 | method: 'POST',
31 | url: url,
32 | // form: form || null,
33 | body:form,
34 | json:true,
35 | headers: headers || null
36 | };
37 | return new Promise((resolve, reject) => {
38 | request(options)
39 | .on('error', (err) => {
40 | reject(err);
41 | })
42 | .pipe(iconv.decodeStream(charset)).collect((err, body) => {
43 | try {
44 | resolve(JSON.parse(body));
45 | } catch (error) {
46 | resolve(body);
47 | }
48 | });
49 | })
50 | }
51 |
52 | static async download(url, path) {
53 | if (fs.existsSync(path))
54 | return;
55 | let options = {
56 | method: 'GET',
57 | url: url,
58 | headers: {
59 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
60 | }
61 | };
62 | let stream = fs.createWriteStream(path);
63 | return new Promise((resolve, reject) => {
64 | request(options)
65 | .on('error', (err) => {
66 | console.log(err);
67 | })
68 | .pipe(stream);
69 | stream.on('finish', () => {
70 | resolve(fs.statSync(path).size);
71 | })
72 | })
73 | }
74 | }
75 |
76 | module.exports = RequestHelper;
--------------------------------------------------------------------------------
/teachSystem/util/TextVerify.js:
--------------------------------------------------------------------------------
1 | class TextVerify {
2 | //6-20位字母数字组合
3 | static isPassword(str = null) {
4 | if (str == null) {
5 | return false;
6 | }
7 | var reg = /^[\w]{6,20}$/;
8 | return reg.test(str);
9 | }
10 |
11 | //电子邮件地址
12 | static isEmail(str = null) {
13 | if (str == null) {
14 | return false;
15 | }
16 | var reg = /^\w+([-\.]\w+)*@\w+([\.-]\w+)*\.\w{2,4}$/;
17 | return reg.test(str);
18 | }
19 |
20 | //手机号
21 | static isMobile(str = null) {
22 | if (str == null) {
23 | return false;
24 | }
25 | var reg = /^1\d{10}$/;
26 | return reg.test(str);
27 | }
28 | }
29 |
30 | module.exports = TextVerify;
31 |
--------------------------------------------------------------------------------
/teachSystem/util/TimeHelper.js:
--------------------------------------------------------------------------------
1 | class TimeHelper {
2 | static async sleep(duration) {
3 | return new Promise((resolve, reject) => {
4 | setTimeout(() => resolve(), duration);
5 | });
6 | }
7 | }
8 |
9 | module.exports = TimeHelper;
10 |
--------------------------------------------------------------------------------