commit f64ddb9c59015ba7dc8b3d83f289df99635c5cc6
Author: yezihao <319931482@qq.com>
Date: Tue Jun 29 17:35:45 2021 +0800
v1.1.0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81337bb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,60 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
+
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+.mvn/wrapper/maven-wrapper.jar
+
+
+**/mvnw
+**/mvnw.cmd
+**/.mvn
+**/target/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8564f29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 RuoYi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d0ae09
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+## 学校实验室预约管理系统
+
+基于若依快速搭建的 学校实验室预约管理系统。
+
+简化教师预约以及管理实验室过程,基于前后端分离脚手架快速搭建,主要模块包含 实验室预约、预约记录、实验室课表、维护实验室、数据统计分析 等。
+
+## 用户权限
+
+1. 超级管理员
+2. 学校管理员
+3. 实验室管理员
+4. 教师
+
+## 功能实现
+
+- 教师预约实验室,异步发送邮件通知审批,预约成功自动生成实验室课表,可导出课表为PDF或图片;
+- 实验室管理员能维护实验室信息及审批预约申请并邮件通知,可导出预约记录为Excel;
+- 学校管理员可维护学校基本信息以及导入用户;
+- 用户可查看不同学院实验室申请率及占用情况。
+
+## 使用技术栈
+
+SpringBoot、SpringSecurity、Redis、JWT、MyBatis-Plus、MySQL、Vue
+
+## 运行
+
+1. 启动Redis
+2. 修改yml配置文件(MySQL、Redis)
+3. 修改email配置
+4. 导入sql文件到数据库
+5. 启动项目
+
+**注意**:默认关闭登陆时验证码校验,到以下方法中打开注释即可
+
+```java
+com.ruoyi.framework.web.service.SysLoginService#login
+```
+
diff --git a/lab-booking-ui/.env.development b/lab-booking-ui/.env.development
new file mode 100644
index 0000000..ed487f8
--- /dev/null
+++ b/lab-booking-ui/.env.development
@@ -0,0 +1,8 @@
+# 开发环境配置
+ENV = 'development'
+
+# 学校实验室预约管理系统/开发环境
+VUE_APP_BASE_API = '/dev-api'
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true
diff --git a/lab-booking-ui/.env.production b/lab-booking-ui/.env.production
new file mode 100644
index 0000000..d58bbf3
--- /dev/null
+++ b/lab-booking-ui/.env.production
@@ -0,0 +1,5 @@
+# 生产环境配置
+ENV = 'production'
+
+# 学校实验室预约管理系统/生产环境
+VUE_APP_BASE_API = '/prod-api'
diff --git a/lab-booking-ui/.env.staging b/lab-booking-ui/.env.staging
new file mode 100644
index 0000000..f88e561
--- /dev/null
+++ b/lab-booking-ui/.env.staging
@@ -0,0 +1,7 @@
+NODE_ENV = production
+
+# 测试环境配置
+ENV = 'staging'
+
+# 学校实验室预约管理系统/测试环境
+VUE_APP_BASE_API = '/stage-api'
diff --git a/lab-booking-ui/.eslintignore b/lab-booking-ui/.eslintignore
new file mode 100644
index 0000000..89be6f6
--- /dev/null
+++ b/lab-booking-ui/.eslintignore
@@ -0,0 +1,10 @@
+# 忽略build目录下类型为js的文件的语法检查
+build/*.js
+# 忽略src/assets目录下文件的语法检查
+src/assets
+# 忽略public目录下文件的语法检查
+public
+# 忽略当前目录下为js的文件的语法检查
+*.js
+# 忽略当前目录下为vue的文件的语法检查
+*.vue
\ No newline at end of file
diff --git a/lab-booking-ui/.eslintrc.js b/lab-booking-ui/.eslintrc.js
new file mode 100644
index 0000000..82bbdee
--- /dev/null
+++ b/lab-booking-ui/.eslintrc.js
@@ -0,0 +1,199 @@
+// ESlint 检查配置
+module.exports = {
+ root: true,
+ parserOptions: {
+ parser: 'babel-eslint',
+ sourceType: 'module'
+ },
+ env: {
+ browser: true,
+ node: true,
+ es6: true,
+ },
+ extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+ // add your custom rules here
+ //it is base on https://github.com/vuejs/eslint-config-vue
+ rules: {
+ "vue/max-attributes-per-line": [2, {
+ "singleline": 10,
+ "multiline": {
+ "max": 1,
+ "allowFirstLine": false
+ }
+ }],
+ "vue/singleline-html-element-content-newline": "off",
+ "vue/multiline-html-element-content-newline":"off",
+ "vue/name-property-casing": ["error", "PascalCase"],
+ "vue/no-v-html": "off",
+ 'accessor-pairs': 2,
+ 'arrow-spacing': [2, {
+ 'before': true,
+ 'after': true
+ }],
+ 'block-spacing': [2, 'always'],
+ 'brace-style': [2, '1tbs', {
+ 'allowSingleLine': true
+ }],
+ 'camelcase': [0, {
+ 'properties': 'always'
+ }],
+ 'comma-dangle': [2, 'never'],
+ 'comma-spacing': [2, {
+ 'before': false,
+ 'after': true
+ }],
+ 'comma-style': [2, 'last'],
+ 'constructor-super': 2,
+ 'curly': [2, 'multi-line'],
+ 'dot-location': [2, 'property'],
+ 'eol-last': 2,
+ 'eqeqeq': ["error", "always", {"null": "ignore"}],
+ 'generator-star-spacing': [2, {
+ 'before': true,
+ 'after': true
+ }],
+ 'handle-callback-err': [2, '^(err|error)$'],
+ 'indent': [2, 2, {
+ 'SwitchCase': 1
+ }],
+ 'jsx-quotes': [2, 'prefer-single'],
+ 'key-spacing': [2, {
+ 'beforeColon': false,
+ 'afterColon': true
+ }],
+ 'keyword-spacing': [2, {
+ 'before': true,
+ 'after': true
+ }],
+ 'new-cap': [2, {
+ 'newIsCap': true,
+ 'capIsNew': false
+ }],
+ 'new-parens': 2,
+ 'no-array-constructor': 2,
+ 'no-caller': 2,
+ 'no-console': 'off',
+ 'no-class-assign': 2,
+ 'no-cond-assign': 2,
+ 'no-const-assign': 2,
+ 'no-control-regex': 0,
+ 'no-delete-var': 2,
+ 'no-dupe-args': 2,
+ 'no-dupe-class-members': 2,
+ 'no-dupe-keys': 2,
+ 'no-duplicate-case': 2,
+ 'no-empty-character-class': 2,
+ 'no-empty-pattern': 2,
+ 'no-eval': 2,
+ 'no-ex-assign': 2,
+ 'no-extend-native': 2,
+ 'no-extra-bind': 2,
+ 'no-extra-boolean-cast': 2,
+ 'no-extra-parens': [2, 'functions'],
+ 'no-fallthrough': 2,
+ 'no-floating-decimal': 2,
+ 'no-func-assign': 2,
+ 'no-implied-eval': 2,
+ 'no-inner-declarations': [2, 'functions'],
+ 'no-invalid-regexp': 2,
+ 'no-irregular-whitespace': 2,
+ 'no-iterator': 2,
+ 'no-label-var': 2,
+ 'no-labels': [2, {
+ 'allowLoop': false,
+ 'allowSwitch': false
+ }],
+ 'no-lone-blocks': 2,
+ 'no-mixed-spaces-and-tabs': 2,
+ 'no-multi-spaces': 2,
+ 'no-multi-str': 2,
+ 'no-multiple-empty-lines': [2, {
+ 'max': 1
+ }],
+ 'no-native-reassign': 2,
+ 'no-negated-in-lhs': 2,
+ 'no-new-object': 2,
+ 'no-new-require': 2,
+ 'no-new-symbol': 2,
+ 'no-new-wrappers': 2,
+ 'no-obj-calls': 2,
+ 'no-octal': 2,
+ 'no-octal-escape': 2,
+ 'no-path-concat': 2,
+ 'no-proto': 2,
+ 'no-redeclare': 2,
+ 'no-regex-spaces': 2,
+ 'no-return-assign': [2, 'except-parens'],
+ 'no-self-assign': 2,
+ 'no-self-compare': 2,
+ 'no-sequences': 2,
+ 'no-shadow-restricted-names': 2,
+ 'no-spaced-func': 2,
+ 'no-sparse-arrays': 2,
+ 'no-this-before-super': 2,
+ 'no-throw-literal': 2,
+ 'no-trailing-spaces': 2,
+ 'no-undef': 2,
+ 'no-undef-init': 2,
+ 'no-unexpected-multiline': 2,
+ 'no-unmodified-loop-condition': 2,
+ 'no-unneeded-ternary': [2, {
+ 'defaultAssignment': false
+ }],
+ 'no-unreachable': 2,
+ 'no-unsafe-finally': 2,
+ 'no-unused-vars': [2, {
+ 'vars': 'all',
+ 'args': 'none'
+ }],
+ 'no-useless-call': 2,
+ 'no-useless-computed-key': 2,
+ 'no-useless-constructor': 2,
+ 'no-useless-escape': 0,
+ 'no-whitespace-before-property': 2,
+ 'no-with': 2,
+ 'one-var': [2, {
+ 'initialized': 'never'
+ }],
+ 'operator-linebreak': [2, 'after', {
+ 'overrides': {
+ '?': 'before',
+ ':': 'before'
+ }
+ }],
+ 'padded-blocks': [2, 'never'],
+ 'quotes': [2, 'single', {
+ 'avoidEscape': true,
+ 'allowTemplateLiterals': true
+ }],
+ 'semi': [2, 'never'],
+ 'semi-spacing': [2, {
+ 'before': false,
+ 'after': true
+ }],
+ 'space-before-blocks': [2, 'always'],
+ 'space-before-function-paren': [2, 'never'],
+ 'space-in-parens': [2, 'never'],
+ 'space-infix-ops': 2,
+ 'space-unary-ops': [2, {
+ 'words': true,
+ 'nonwords': false
+ }],
+ 'spaced-comment': [2, 'always', {
+ 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+ }],
+ 'template-curly-spacing': [2, 'never'],
+ 'use-isnan': 2,
+ 'valid-typeof': 2,
+ 'wrap-iife': [2, 'any'],
+ 'yield-star-spacing': [2, 'both'],
+ 'yoda': [2, 'never'],
+ 'prefer-const': 2,
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+ 'object-curly-spacing': [2, 'always', {
+ objectsInObjects: false
+ }],
+ 'array-bracket-spacing': [2, 'never']
+ }
+}
diff --git a/lab-booking-ui/.gitignore b/lab-booking-ui/.gitignore
new file mode 100644
index 0000000..78a752d
--- /dev/null
+++ b/lab-booking-ui/.gitignore
@@ -0,0 +1,23 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock
diff --git a/lab-booking-ui/LICENSE b/lab-booking-ui/LICENSE
new file mode 100644
index 0000000..8564f29
--- /dev/null
+++ b/lab-booking-ui/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 RuoYi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/lab-booking-ui/README.md b/lab-booking-ui/README.md
new file mode 100644
index 0000000..61aeffc
--- /dev/null
+++ b/lab-booking-ui/README.md
@@ -0,0 +1,37 @@
+## 开发
+
+```bash
+# 克隆项目
+git clone https://gitee.com/y_project/RuoYi-Vue
+
+# 进入项目目录
+cd lab-booking-ui
+
+# 安装依赖
+npm install
+
+# 建议不要直接使用 cnpm 安装依赖,可以通使用国内镜像解决 npm 下载速度慢的问题
+npm install --registry=https://registry.npm.taobao.org
+
+# 启动服务
+npm run dev
+```
+
+浏览器访问 http://localhost:80
+
+## 发布
+
+```bash
+# 构建测试环境
+npm run build:stage
+
+# 构建生产环境
+npm run build:prod
+```
+
+## 注意
+
+为了开发方便,关闭了验证码功能,到以下文件中打开 TODO验证码注释
+```bash
+labbooking-ui\src\views\login.vue
+```
\ No newline at end of file
diff --git a/lab-booking-ui/babel.config.js b/lab-booking-ui/babel.config.js
new file mode 100644
index 0000000..b99f001
--- /dev/null
+++ b/lab-booking-ui/babel.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ presets: [
+ // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
+ '@vue/cli-plugin-babel/preset'
+ ],
+ 'env': {
+ 'development': {
+ // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
+ // This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
+ 'plugins': ['dynamic-import-node']
+ }
+ }
+}
diff --git a/lab-booking-ui/build/index.js b/lab-booking-ui/build/index.js
new file mode 100644
index 0000000..0c57de2
--- /dev/null
+++ b/lab-booking-ui/build/index.js
@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+ const report = rawArgv.includes('--report')
+
+ run(`vue-cli-service build ${args}`)
+
+ const port = 9526
+ const publicPath = config.publicPath
+
+ var connect = require('connect')
+ var serveStatic = require('serve-static')
+ const app = connect()
+
+ app.use(
+ publicPath,
+ serveStatic('./dist', {
+ index: ['index.html', '/']
+ })
+ )
+
+ app.listen(port, function () {
+ console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
+ if (report) {
+ console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
+ }
+
+ })
+} else {
+ run(`vue-cli-service build ${args}`)
+}
diff --git a/lab-booking-ui/package.json b/lab-booking-ui/package.json
new file mode 100644
index 0000000..1526d40
--- /dev/null
+++ b/lab-booking-ui/package.json
@@ -0,0 +1,89 @@
+{
+ "name": "labbooking",
+ "version": "1.1.0",
+ "description": "学校实验室预约管理系统",
+ "author": "叶子豪",
+ "license": "MIT",
+ "scripts": {
+ "dev": "vue-cli-service serve",
+ "build:prod": "vue-cli-service build",
+ "build:stage": "vue-cli-service build --mode staging",
+ "preview": "node build/index.js --preview",
+ "lint": "eslint --ext .js,.vue src"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "src/**/*.{js,vue}": [
+ "eslint --fix",
+ "git add"
+ ]
+ },
+ "keywords": [
+ "vue",
+ "admin",
+ "dashboard",
+ "element-ui",
+ "boilerplate",
+ "admin-template",
+ "management-system"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://gitee.com/yezihao/lab-booking-vue.git"
+ },
+ "dependencies": {
+ "@riophae/vue-treeselect": "0.4.0",
+ "axios": "0.21.0",
+ "clipboard": "2.0.6",
+ "core-js": "3.8.1",
+ "echarts": "4.9.0",
+ "element-ui": "2.15.0",
+ "file-saver": "2.0.4",
+ "fuse.js": "6.4.3",
+ "highlight.js": "9.18.5",
+ "html2canvas": "^1.0.0-rc.7",
+ "js-beautify": "1.13.0",
+ "js-cookie": "2.2.1",
+ "jsencrypt": "3.0.0-rc.1",
+ "jspdf": "^2.3.1",
+ "nprogress": "0.2.0",
+ "quill": "1.3.7",
+ "screenfull": "5.0.2",
+ "sortablejs": "1.10.2",
+ "vue": "2.6.12",
+ "vue-count-to": "1.0.13",
+ "vue-cropper": "0.5.5",
+ "vue-router": "3.4.9",
+ "vuedraggable": "2.24.3",
+ "vuex": "3.6.0"
+ },
+ "devDependencies": {
+ "@vue/cli-plugin-babel": "4.4.6",
+ "@vue/cli-plugin-eslint": "4.4.6",
+ "@vue/cli-service": "4.4.6",
+ "babel-eslint": "10.1.0",
+ "chalk": "4.1.0",
+ "connect": "3.6.6",
+ "eslint": "7.15.0",
+ "eslint-plugin-vue": "7.2.0",
+ "lint-staged": "10.5.3",
+ "runjs": "4.4.2",
+ "sass": "1.32.0",
+ "sass-loader": "10.1.0",
+ "script-ext-html-webpack-plugin": "2.1.5",
+ "svg-sprite-loader": "5.1.1",
+ "vue-template-compiler": "2.6.12"
+ },
+ "engines": {
+ "node": ">=8.9",
+ "npm": ">= 3.0.0"
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions"
+ ]
+}
diff --git a/lab-booking-ui/public/favicon.ico b/lab-booking-ui/public/favicon.ico
new file mode 100644
index 0000000..e263760
Binary files /dev/null and b/lab-booking-ui/public/favicon.ico differ
diff --git a/lab-booking-ui/public/index.html b/lab-booking-ui/public/index.html
new file mode 100644
index 0000000..6d64bf9
--- /dev/null
+++ b/lab-booking-ui/public/index.html
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+ <%= webpackConfig.name %>
+
+
+
+
+
+
diff --git a/lab-booking-ui/public/robots.txt b/lab-booking-ui/public/robots.txt
new file mode 100644
index 0000000..77470cb
--- /dev/null
+++ b/lab-booking-ui/public/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
\ No newline at end of file
diff --git a/lab-booking-ui/src/App.vue b/lab-booking-ui/src/App.vue
new file mode 100644
index 0000000..e448b11
--- /dev/null
+++ b/lab-booking-ui/src/App.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/api/labbooking/course.js b/lab-booking-ui/src/api/labbooking/course.js
new file mode 100644
index 0000000..82bb023
--- /dev/null
+++ b/lab-booking-ui/src/api/labbooking/course.js
@@ -0,0 +1,61 @@
+import request from '@/utils/request'
+
+// 查询个人所有课程
+export function getMyCourse() {
+ return request({
+ url: '/labbooking/course/getCourse',
+ method: 'get'
+ })
+}
+
+// 查询课程信息列表
+export function listCourse(query) {
+ return request({
+ url: '/labbooking/course/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询课程信息详细
+export function getCourse(id) {
+ return request({
+ url: '/labbooking/course/' + id,
+ method: 'get'
+ })
+}
+
+// 新增课程信息
+export function addCourse(data) {
+ return request({
+ url: '/labbooking/course',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改课程信息
+export function updateCourse(data) {
+ return request({
+ url: '/labbooking/course',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除课程信息
+export function delCourse(id) {
+ return request({
+ url: '/labbooking/course/' + id,
+ method: 'delete'
+ })
+}
+
+// 导出个人课程信息
+export function exportCourse(query) {
+ return request({
+ url: '/labbooking/course/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/labbooking/lab.js b/lab-booking-ui/src/api/labbooking/lab.js
new file mode 100644
index 0000000..7d81b8b
--- /dev/null
+++ b/lab-booking-ui/src/api/labbooking/lab.js
@@ -0,0 +1,100 @@
+import request from '@/utils/request'
+
+// 查询实验室信息列表
+export function bookLabList(query) {
+ return request({
+ url: '/labbooking/lab/bookLabList',
+ method: 'get',
+ params: query
+ })
+}
+
+// 管理员查询实验室信息列表
+export function labList(query) {
+ return request({
+ url: '/labbooking/lab/labList',
+ method: 'get',
+ params: query
+ })
+}
+
+// 预约状态修改
+export function changeUserStatus(id, status) {
+ const data = {
+ id,
+ status
+ }
+ return request({
+ url: '/labbooking/lab/changeStatus',
+ method: 'put',
+ data: data
+ })
+}
+
+export function labUse(year, term) {
+ return request({
+ url: '/labbooking/lab/labUse',
+ method: 'get',
+ params: year, term
+ })
+}
+
+// 查询实验室信息详细
+export function getLab(id) {
+ return request({
+ url: '/labbooking/lab/' + id,
+ method: 'get'
+ })
+}
+
+// 新增实验室信息
+export function addLab(data) {
+ return request({
+ url: '/labbooking/lab',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改实验室信息
+export function updateLab(data) {
+ return request({
+ url: '/labbooking/lab',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除实验室信息
+export function delLab(ids) {
+ return request({
+ url: '/labbooking/lab/' + ids,
+ method: 'delete'
+ })
+}
+
+// 导出实验室信息
+export function bookLabExport(query) {
+ return request({
+ url: '/labbooking/lab/bookLabExport',
+ method: 'get',
+ params: query
+ })
+}
+
+// 管理员导出实验室信息
+export function labExport(query) {
+ return request({
+ url: '/labbooking/lab/labExport',
+ method: 'get',
+ params: query
+ })
+}
+
+// 根据用户查询学院
+export function getMyDept() {
+ return request({
+ url: '/labbooking/lab/getDept',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/labbooking/record.js b/lab-booking-ui/src/api/labbooking/record.js
new file mode 100644
index 0000000..d526b01
--- /dev/null
+++ b/lab-booking-ui/src/api/labbooking/record.js
@@ -0,0 +1,71 @@
+import request from '@/utils/request'
+
+// 查询教师实验室预约信息列表
+export function listRecord(query, year, term) {
+ return request({
+ url: '/labbooking/record/list',
+ method: 'get',
+ params: query, year, term
+ })
+}
+
+// 查询实验室管理员预约信息列表
+export function listManagerRecord(query, year, term) {
+ return request({
+ url: '/labbooking/record/managerList',
+ method: 'get',
+ params: query, year, term
+ })
+}
+
+export function collegeapply(year, term) {
+ return request({
+ url: '/labbooking/record/college',
+ method: 'get',
+ params: year, term
+ })
+}
+
+export function cancel(recordIdList) {
+ return request({
+ url: '/labbooking/record/cancel',
+ method: 'put',
+ data: recordIdList
+ })
+}
+
+// 新增实验室预约信息
+export function addRecordAndWeek(data) {
+ return request({
+ url: '/labbooking/record/addRecordAndWeek',
+ method: 'post',
+ data: data
+ })
+}
+
+// 导出实验室预约信息
+export function exportRecord(query, year, term) {
+ return request({
+ url: '/labbooking/record/export',
+ method: 'get',
+ params: query, year, term
+ })
+}
+
+// 管理员审批实验室预约信息
+export function approve(query) {
+ return request({
+ url: '/labbooking/record/approve',
+ method: 'put',
+ data: query
+ })
+}
+
+// 管理员取消实验室预约信息
+export function managerCancel(query) {
+ return request({
+ url: '/labbooking/record/managerCancel',
+ method: 'put',
+ data: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/labbooking/table.js b/lab-booking-ui/src/api/labbooking/table.js
new file mode 100644
index 0000000..b2f6b88
--- /dev/null
+++ b/lab-booking-ui/src/api/labbooking/table.js
@@ -0,0 +1,120 @@
+import request from '@/utils/request'
+
+import html2canvas from 'html2canvas';
+import JsPDF from 'jspdf';
+
+export default {
+ list(query) {
+ return request({
+ url: '/labbooking/table/list',
+ method: 'get',
+ params: query
+ })
+ },
+
+ getTableDetails(labId) {
+ return request({
+ url: `/labbooking/table/get/${labId}`,
+ method: 'get',
+ })
+ },
+
+ applyTableList(labId, year, term) {
+ return request({
+ url: `/labbooking/table/applyTableList/${labId}/${year}/${term}`,
+ method: 'get'
+ })
+ },
+
+ appliedTableList(labId, year, term) {
+ return request({
+ url: `/labbooking/table/appliedTableList/${labId}/${year}/${term}`,
+ method: 'get'
+ })
+ },
+
+ downloadImg(ele, imgName) {
+ html2canvas(ele, {
+ dpi: 300
+ }).then(canvas => {
+ let dataURL = canvas.toDataURL('image/png')
+ if (dataURL !== '') {
+ let alink = document.createElement('a')
+ alink.href = dataURL
+ alink.download = imgName + '.png'
+ alink.click()
+ }
+ })
+ },
+
+ downloadPDF(ele, pdfName) {
+ window.scrollTo(0, 0);
+ let eleW = ele.offsetWidth;// 获得该容器的宽
+ let eleH = ele.offsetHeight;// 获得该容器的高
+ let eleOffsetTop = ele.offsetTop; // 获得该容器到文档顶部的距离
+ let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离
+
+ var canvas = document.createElement("canvas");
+ var abs = 0;
+
+ let win_in = document.documentElement.clientWidth || document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
+ let win_out = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
+
+ if (win_out > win_in) {
+ // abs = (win_o - win_i)/2; // 获得滚动条长度的一半
+ abs = (win_out - win_in) / 2; // 获得滚动条宽度的一半
+ // console.log(a, '新abs');
+ }
+ canvas.width = eleW * 2; // 将画布宽&&高放大两倍
+ canvas.height = eleH * 2;
+
+ var context = canvas.getContext("2d");
+ context.scale(2, 2);
+ context.translate(-eleOffsetLeft - abs, -eleOffsetTop);
+ // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
+ // translate的时候,要把这个差值去掉
+
+ // html2canvas(element).then( (canvas)=>{ //报错
+ // html2canvas(element[0]).then( (canvas)=>{
+ html2canvas(ele, {
+ dpi: 300,
+ // allowTaint: true, //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
+ useCORS: true //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
+ }).then((canvas) => {
+ var contentWidth = canvas.width;
+ var contentHeight = canvas.height;
+ //一页pdf显示html页面生成的canvas高度;
+ var pageHeight = contentWidth / 595.28 * 841.89;
+ //未生成pdf的html页面高度
+ var leftHeight = contentHeight;
+ //页面偏移
+ var position = 0;
+ //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
+ var imgWidth = 595.28;
+ var imgHeight = 595.28 / contentWidth * contentHeight;
+ var pageData = canvas.toDataURL('image/jpeg', 1.0);
+ var pdf = new JsPDF('', 'pt', 'a4');
+ //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
+ //当内容未超过pdf一页显示的范围,无需分页
+ // console.log('处理完画布高度:' + contentHeight)
+ // console.log('每页高度:' + pageHeight)
+ if (leftHeight < pageHeight) {
+ //在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
+ pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
+ // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
+ } else { // 分页
+ while (leftHeight > 10) {
+ // console.log(leftHeight)
+ pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
+ leftHeight -= pageHeight;
+ position -= 841.89;
+ //避免添加空白页
+ if (leftHeight > 10) {
+ pdf.addPage();
+ }
+ }
+ }
+ pdf.save(pdfName);
+ })
+ }
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/login.js b/lab-booking-ui/src/api/login.js
new file mode 100644
index 0000000..869cd9f
--- /dev/null
+++ b/lab-booking-ui/src/api/login.js
@@ -0,0 +1,40 @@
+import request from '@/utils/request'
+
+// 登录方法
+export function login(username, password, code, uuid) {
+ const data = {
+ username,
+ password,
+ code,
+ uuid
+ }
+ return request({
+ url: '/login',
+ method: 'post',
+ data: data
+ })
+}
+
+// 获取用户详细信息
+export function getInfo() {
+ return request({
+ url: '/getInfo',
+ method: 'get'
+ })
+}
+
+// 退出方法
+export function logout() {
+ return request({
+ url: '/logout',
+ method: 'post'
+ })
+}
+
+// 获取验证码
+export function getCodeImg() {
+ return request({
+ url: '/captchaImage',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/menu.js b/lab-booking-ui/src/api/menu.js
new file mode 100644
index 0000000..faef101
--- /dev/null
+++ b/lab-booking-ui/src/api/menu.js
@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 获取路由
+export const getRouters = () => {
+ return request({
+ url: '/getRouters',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/monitor/cache.js b/lab-booking-ui/src/api/monitor/cache.js
new file mode 100644
index 0000000..59d3505
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/cache.js
@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 查询缓存详细
+export function getCache() {
+ return request({
+ url: '/monitor/cache',
+ method: 'get'
+ })
+}
diff --git a/lab-booking-ui/src/api/monitor/job.js b/lab-booking-ui/src/api/monitor/job.js
new file mode 100644
index 0000000..58c4343
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/job.js
@@ -0,0 +1,80 @@
+import request from '@/utils/request'
+
+// 查询定时任务调度列表
+export function listJob(query) {
+ return request({
+ url: '/monitor/job/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询定时任务调度详细
+export function getJob(jobId) {
+ return request({
+ url: '/monitor/job/' + jobId,
+ method: 'get'
+ })
+}
+
+// 新增定时任务调度
+export function addJob(data) {
+ return request({
+ url: '/monitor/job',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改定时任务调度
+export function updateJob(data) {
+ return request({
+ url: '/monitor/job',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除定时任务调度
+export function delJob(jobId) {
+ return request({
+ url: '/monitor/job/' + jobId,
+ method: 'delete'
+ })
+}
+
+// 导出定时任务调度
+export function exportJob(query) {
+ return request({
+ url: '/monitor/job/export',
+ method: 'get',
+ params: query
+ })
+}
+
+// 任务状态修改
+export function changeJobStatus(jobId, status) {
+ const data = {
+ jobId,
+ status
+ }
+ return request({
+ url: '/monitor/job/changeStatus',
+ method: 'put',
+ data: data
+ })
+}
+
+
+// 定时任务立即执行一次
+export function runJob(jobId, jobGroup) {
+ const data = {
+ jobId,
+ jobGroup
+ }
+ return request({
+ url: '/monitor/job/run',
+ method: 'put',
+ data: data
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/monitor/jobLog.js b/lab-booking-ui/src/api/monitor/jobLog.js
new file mode 100644
index 0000000..be1fffd
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/jobLog.js
@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// 查询调度日志列表
+export function listJobLog(query) {
+ return request({
+ url: '/monitor/jobLog/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除调度日志
+export function delJobLog(jobLogId) {
+ return request({
+ url: '/monitor/jobLog/' + jobLogId,
+ method: 'delete'
+ })
+}
+
+// 清空调度日志
+export function cleanJobLog() {
+ return request({
+ url: '/monitor/jobLog/clean',
+ method: 'delete'
+ })
+}
+
+// 导出调度日志
+export function exportJobLog(query) {
+ return request({
+ url: '/monitor/jobLog/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/monitor/logininfor.js b/lab-booking-ui/src/api/monitor/logininfor.js
new file mode 100644
index 0000000..383d61f
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/logininfor.js
@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// 查询登录日志列表
+export function list(query) {
+ return request({
+ url: '/monitor/logininfor/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除登录日志
+export function delLogininfor(infoId) {
+ return request({
+ url: '/monitor/logininfor/' + infoId,
+ method: 'delete'
+ })
+}
+
+// 清空登录日志
+export function cleanLogininfor() {
+ return request({
+ url: '/monitor/logininfor/clean',
+ method: 'delete'
+ })
+}
+
+// 导出登录日志
+export function exportLogininfor(query) {
+ return request({
+ url: '/monitor/logininfor/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/monitor/online.js b/lab-booking-ui/src/api/monitor/online.js
new file mode 100644
index 0000000..bd22137
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/online.js
@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询在线用户列表
+export function list(query) {
+ return request({
+ url: '/monitor/online/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 强退用户
+export function forceLogout(tokenId) {
+ return request({
+ url: '/monitor/online/' + tokenId,
+ method: 'delete'
+ })
+}
diff --git a/lab-booking-ui/src/api/monitor/operlog.js b/lab-booking-ui/src/api/monitor/operlog.js
new file mode 100644
index 0000000..f09b8ed
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/operlog.js
@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// 查询操作日志列表
+export function list(query) {
+ return request({
+ url: '/monitor/operlog/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除操作日志
+export function delOperlog(operId) {
+ return request({
+ url: '/monitor/operlog/' + operId,
+ method: 'delete'
+ })
+}
+
+// 清空操作日志
+export function cleanOperlog() {
+ return request({
+ url: '/monitor/operlog/clean',
+ method: 'delete'
+ })
+}
+
+// 导出操作日志
+export function exportOperlog(query) {
+ return request({
+ url: '/monitor/operlog/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/monitor/server.js b/lab-booking-ui/src/api/monitor/server.js
new file mode 100644
index 0000000..feed783
--- /dev/null
+++ b/lab-booking-ui/src/api/monitor/server.js
@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 查询服务器详细
+export function getServer() {
+ return request({
+ url: '/monitor/server',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/config.js b/lab-booking-ui/src/api/system/config.js
new file mode 100644
index 0000000..93d11fc
--- /dev/null
+++ b/lab-booking-ui/src/api/system/config.js
@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+// 查询参数列表
+export function listConfig(query) {
+ return request({
+ url: '/system/config/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询参数详细
+export function getConfig(configId) {
+ return request({
+ url: '/system/config/' + configId,
+ method: 'get'
+ })
+}
+
+// 根据参数键名查询参数值
+export function getConfigKey(configKey) {
+ return request({
+ url: '/system/config/configKey/' + configKey,
+ method: 'get'
+ })
+}
+
+// 新增参数配置
+export function addConfig(data) {
+ return request({
+ url: '/system/config',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改参数配置
+export function updateConfig(data) {
+ return request({
+ url: '/system/config',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除参数配置
+export function delConfig(configId) {
+ return request({
+ url: '/system/config/' + configId,
+ method: 'delete'
+ })
+}
+
+// 清理参数缓存
+export function clearCache() {
+ return request({
+ url: '/system/config/clearCache',
+ method: 'delete'
+ })
+}
+
+// 导出参数
+export function exportConfig(query) {
+ return request({
+ url: '/system/config/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/dept.js b/lab-booking-ui/src/api/system/dept.js
new file mode 100644
index 0000000..2804676
--- /dev/null
+++ b/lab-booking-ui/src/api/system/dept.js
@@ -0,0 +1,68 @@
+import request from '@/utils/request'
+
+// 查询部门列表
+export function listDept(query) {
+ return request({
+ url: '/system/dept/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询部门列表(排除节点)
+export function listDeptExcludeChild(deptId) {
+ return request({
+ url: '/system/dept/list/exclude/' + deptId,
+ method: 'get'
+ })
+}
+
+// 查询部门详细
+export function getDept(deptId) {
+ return request({
+ url: '/system/dept/' + deptId,
+ method: 'get'
+ })
+}
+
+// 查询部门下拉树结构
+export function treeselect() {
+ return request({
+ url: '/system/dept/treeselect',
+ method: 'get'
+ })
+}
+
+// 根据角色ID查询部门树结构
+export function roleDeptTreeselect(roleId) {
+ return request({
+ url: '/system/dept/roleDeptTreeselect/' + roleId,
+ method: 'get'
+ })
+}
+
+// 新增部门
+export function addDept(data) {
+ return request({
+ url: '/system/dept',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改部门
+export function updateDept(data) {
+ return request({
+ url: '/system/dept',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除部门
+export function delDept(deptId) {
+ return request({
+ url: '/system/dept/' + deptId,
+ method: 'delete'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/dict/data.js b/lab-booking-ui/src/api/system/dict/data.js
new file mode 100644
index 0000000..d7aca89
--- /dev/null
+++ b/lab-booking-ui/src/api/system/dict/data.js
@@ -0,0 +1,61 @@
+import request from '@/utils/request'
+
+// 查询字典数据列表
+export function listData(query) {
+ return request({
+ url: '/system/dict/data/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询字典数据详细
+export function getData(dictCode) {
+ return request({
+ url: '/system/dict/data/' + dictCode,
+ method: 'get'
+ })
+}
+
+// 根据字典类型查询字典数据信息
+export function getDicts(dictType) {
+ return request({
+ url: '/system/dict/data/type/' + dictType,
+ method: 'get'
+ })
+}
+
+// 新增字典数据
+export function addData(data) {
+ return request({
+ url: '/system/dict/data',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改字典数据
+export function updateData(data) {
+ return request({
+ url: '/system/dict/data',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除字典数据
+export function delData(dictCode) {
+ return request({
+ url: '/system/dict/data/' + dictCode,
+ method: 'delete'
+ })
+}
+
+// 导出字典数据
+export function exportData(query) {
+ return request({
+ url: '/system/dict/data/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/dict/type.js b/lab-booking-ui/src/api/system/dict/type.js
new file mode 100644
index 0000000..bc77692
--- /dev/null
+++ b/lab-booking-ui/src/api/system/dict/type.js
@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+// 查询字典类型列表
+export function listType(query) {
+ return request({
+ url: '/system/dict/type/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询字典类型详细
+export function getType(dictId) {
+ return request({
+ url: '/system/dict/type/' + dictId,
+ method: 'get'
+ })
+}
+
+// 新增字典类型
+export function addType(data) {
+ return request({
+ url: '/system/dict/type',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改字典类型
+export function updateType(data) {
+ return request({
+ url: '/system/dict/type',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除字典类型
+export function delType(dictId) {
+ return request({
+ url: '/system/dict/type/' + dictId,
+ method: 'delete'
+ })
+}
+
+// 清理参数缓存
+export function clearCache() {
+ return request({
+ url: '/system/dict/type/clearCache',
+ method: 'delete'
+ })
+}
+
+// 导出字典类型
+export function exportType(query) {
+ return request({
+ url: '/system/dict/type/export',
+ method: 'get',
+ params: query
+ })
+}
+
+// 获取字典选择框列表
+export function optionselect() {
+ return request({
+ url: '/system/dict/type/optionselect',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/menu.js b/lab-booking-ui/src/api/system/menu.js
new file mode 100644
index 0000000..f6415c6
--- /dev/null
+++ b/lab-booking-ui/src/api/system/menu.js
@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 查询菜单列表
+export function listMenu(query) {
+ return request({
+ url: '/system/menu/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询菜单详细
+export function getMenu(menuId) {
+ return request({
+ url: '/system/menu/' + menuId,
+ method: 'get'
+ })
+}
+
+// 查询菜单下拉树结构
+export function treeselect() {
+ return request({
+ url: '/system/menu/treeselect',
+ method: 'get'
+ })
+}
+
+// 根据角色ID查询菜单下拉树结构
+export function roleMenuTreeselect(roleId) {
+ return request({
+ url: '/system/menu/roleMenuTreeselect/' + roleId,
+ method: 'get'
+ })
+}
+
+// 新增菜单
+export function addMenu(data) {
+ return request({
+ url: '/system/menu',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改菜单
+export function updateMenu(data) {
+ return request({
+ url: '/system/menu',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除菜单
+export function delMenu(menuId) {
+ return request({
+ url: '/system/menu/' + menuId,
+ method: 'delete'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/notice.js b/lab-booking-ui/src/api/system/notice.js
new file mode 100644
index 0000000..c274ea5
--- /dev/null
+++ b/lab-booking-ui/src/api/system/notice.js
@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询公告列表
+export function listNotice(query) {
+ return request({
+ url: '/system/notice/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询公告详细
+export function getNotice(noticeId) {
+ return request({
+ url: '/system/notice/' + noticeId,
+ method: 'get'
+ })
+}
+
+// 新增公告
+export function addNotice(data) {
+ return request({
+ url: '/system/notice',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改公告
+export function updateNotice(data) {
+ return request({
+ url: '/system/notice',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除公告
+export function delNotice(noticeId) {
+ return request({
+ url: '/system/notice/' + noticeId,
+ method: 'delete'
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/post.js b/lab-booking-ui/src/api/system/post.js
new file mode 100644
index 0000000..434cd35
--- /dev/null
+++ b/lab-booking-ui/src/api/system/post.js
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询岗位列表
+export function listPost(query) {
+ return request({
+ url: '/system/post/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询岗位详细
+export function getPost(postId) {
+ return request({
+ url: '/system/post/' + postId,
+ method: 'get'
+ })
+}
+
+// 新增岗位
+export function addPost(data) {
+ return request({
+ url: '/system/post',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改岗位
+export function updatePost(data) {
+ return request({
+ url: '/system/post',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除岗位
+export function delPost(postId) {
+ return request({
+ url: '/system/post/' + postId,
+ method: 'delete'
+ })
+}
+
+// 导出岗位
+export function exportPost(query) {
+ return request({
+ url: '/system/post/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/role.js b/lab-booking-ui/src/api/system/role.js
new file mode 100644
index 0000000..463501c
--- /dev/null
+++ b/lab-booking-ui/src/api/system/role.js
@@ -0,0 +1,75 @@
+import request from '@/utils/request'
+
+// 查询角色列表
+export function listRole(query) {
+ return request({
+ url: '/system/role/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询角色详细
+export function getRole(roleId) {
+ return request({
+ url: '/system/role/' + roleId,
+ method: 'get'
+ })
+}
+
+// 新增角色
+export function addRole(data) {
+ return request({
+ url: '/system/role',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改角色
+export function updateRole(data) {
+ return request({
+ url: '/system/role',
+ method: 'put',
+ data: data
+ })
+}
+
+// 角色数据权限
+export function dataScope(data) {
+ return request({
+ url: '/system/role/dataScope',
+ method: 'put',
+ data: data
+ })
+}
+
+// 角色状态修改
+export function changeRoleStatus(roleId, status) {
+ const data = {
+ roleId,
+ status
+ }
+ return request({
+ url: '/system/role/changeStatus',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除角色
+export function delRole(roleId) {
+ return request({
+ url: '/system/role/' + roleId,
+ method: 'delete'
+ })
+}
+
+// 导出角色
+export function exportRole(query) {
+ return request({
+ url: '/system/role/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/api/system/user.js b/lab-booking-ui/src/api/system/user.js
new file mode 100644
index 0000000..3b9a776
--- /dev/null
+++ b/lab-booking-ui/src/api/system/user.js
@@ -0,0 +1,127 @@
+import request from '@/utils/request'
+import { praseStrEmpty } from "@/utils/ruoyi";
+
+// 查询用户列表
+export function listUser(query) {
+ return request({
+ url: '/system/user/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询用户详细
+export function getUser(userId) {
+ return request({
+ url: '/system/user/' + praseStrEmpty(userId),
+ method: 'get'
+ })
+}
+
+// 新增用户
+export function addUser(data) {
+ return request({
+ url: '/system/user',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改用户
+export function updateUser(data) {
+ return request({
+ url: '/system/user',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除用户
+export function delUser(userId) {
+ return request({
+ url: '/system/user/' + userId,
+ method: 'delete'
+ })
+}
+
+// 导出用户
+export function exportUser(query) {
+ return request({
+ url: '/system/user/export',
+ method: 'get',
+ params: query
+ })
+}
+
+// 用户密码重置
+export function resetUserPwd(userId, password) {
+ const data = {
+ userId,
+ password
+ }
+ return request({
+ url: '/system/user/resetPwd',
+ method: 'put',
+ data: data
+ })
+}
+
+// 用户状态修改
+export function changeUserStatus(userId, status) {
+ const data = {
+ userId,
+ status
+ }
+ return request({
+ url: '/system/user/changeStatus',
+ method: 'put',
+ data: data
+ })
+}
+
+// 查询用户个人信息
+export function getUserProfile() {
+ return request({
+ url: '/system/user/profile',
+ method: 'get'
+ })
+}
+
+// 修改用户个人信息
+export function updateUserProfile(data) {
+ return request({
+ url: '/system/user/profile',
+ method: 'put',
+ data: data
+ })
+}
+
+// 用户密码重置
+export function updateUserPwd(oldPassword, newPassword) {
+ const data = {
+ oldPassword,
+ newPassword
+ }
+ return request({
+ url: '/system/user/profile/updatePwd',
+ method: 'put',
+ params: data
+ })
+}
+
+// 用户头像上传
+export function uploadAvatar(data) {
+ return request({
+ url: '/system/user/profile/avatar',
+ method: 'post',
+ data: data
+ })
+}
+
+// 下载用户导入模板
+export function importTemplate() {
+ return request({
+ url: '/system/user/importTemplate',
+ method: 'get'
+ })
+}
diff --git a/lab-booking-ui/src/api/tool/gen.js b/lab-booking-ui/src/api/tool/gen.js
new file mode 100644
index 0000000..4506927
--- /dev/null
+++ b/lab-booking-ui/src/api/tool/gen.js
@@ -0,0 +1,76 @@
+import request from '@/utils/request'
+
+// 查询生成表数据
+export function listTable(query) {
+ return request({
+ url: '/tool/gen/list',
+ method: 'get',
+ params: query
+ })
+}
+// 查询db数据库列表
+export function listDbTable(query) {
+ return request({
+ url: '/tool/gen/db/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询表详细信息
+export function getGenTable(tableId) {
+ return request({
+ url: '/tool/gen/' + tableId,
+ method: 'get'
+ })
+}
+
+// 修改代码生成信息
+export function updateGenTable(data) {
+ return request({
+ url: '/tool/gen',
+ method: 'put',
+ data: data
+ })
+}
+
+// 导入表
+export function importTable(data) {
+ return request({
+ url: '/tool/gen/importTable',
+ method: 'post',
+ params: data
+ })
+}
+
+// 预览生成代码
+export function previewTable(tableId) {
+ return request({
+ url: '/tool/gen/preview/' + tableId,
+ method: 'get'
+ })
+}
+
+// 删除表数据
+export function delTable(tableId) {
+ return request({
+ url: '/tool/gen/' + tableId,
+ method: 'delete'
+ })
+}
+
+// 生成代码(自定义路径)
+export function genCode(tableName) {
+ return request({
+ url: '/tool/gen/genCode/' + tableName,
+ method: 'get'
+ })
+}
+
+// 同步数据库
+export function synchDb(tableName) {
+ return request({
+ url: '/tool/gen/synchDb/' + tableName,
+ method: 'get'
+ })
+}
diff --git a/lab-booking-ui/src/assets/401_images/401.gif b/lab-booking-ui/src/assets/401_images/401.gif
new file mode 100644
index 0000000..cd6e0d9
Binary files /dev/null and b/lab-booking-ui/src/assets/401_images/401.gif differ
diff --git a/lab-booking-ui/src/assets/404_images/404.png b/lab-booking-ui/src/assets/404_images/404.png
new file mode 100644
index 0000000..3d8e230
Binary files /dev/null and b/lab-booking-ui/src/assets/404_images/404.png differ
diff --git a/lab-booking-ui/src/assets/404_images/404_cloud.png b/lab-booking-ui/src/assets/404_images/404_cloud.png
new file mode 100644
index 0000000..c6281d0
Binary files /dev/null and b/lab-booking-ui/src/assets/404_images/404_cloud.png differ
diff --git a/lab-booking-ui/src/assets/icons/index.js b/lab-booking-ui/src/assets/icons/index.js
new file mode 100644
index 0000000..2c6b309
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/index.js
@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import SvgIcon from '@/components/SvgIcon'// svg component
+
+// register globally
+Vue.component('svg-icon', SvgIcon)
+
+const req = require.context('./svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+requireAll(req)
diff --git a/lab-booking-ui/src/assets/icons/svg/404.svg b/lab-booking-ui/src/assets/icons/svg/404.svg
new file mode 100644
index 0000000..6df5019
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/404.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/bug.svg b/lab-booking-ui/src/assets/icons/svg/bug.svg
new file mode 100644
index 0000000..05a150d
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/bug.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/build.svg b/lab-booking-ui/src/assets/icons/svg/build.svg
new file mode 100644
index 0000000..97c4688
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/build.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/button.svg b/lab-booking-ui/src/assets/icons/svg/button.svg
new file mode 100644
index 0000000..904fddc
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/button.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/cascader.svg b/lab-booking-ui/src/assets/icons/svg/cascader.svg
new file mode 100644
index 0000000..e256024
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/cascader.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/chart.svg b/lab-booking-ui/src/assets/icons/svg/chart.svg
new file mode 100644
index 0000000..27728fb
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/checkbox.svg b/lab-booking-ui/src/assets/icons/svg/checkbox.svg
new file mode 100644
index 0000000..013fd3a
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/checkbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/clipboard.svg b/lab-booking-ui/src/assets/icons/svg/clipboard.svg
new file mode 100644
index 0000000..90923ff
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/clipboard.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/code.svg b/lab-booking-ui/src/assets/icons/svg/code.svg
new file mode 100644
index 0000000..ed4d23c
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/code.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/color.svg b/lab-booking-ui/src/assets/icons/svg/color.svg
new file mode 100644
index 0000000..44a81aa
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/component.svg b/lab-booking-ui/src/assets/icons/svg/component.svg
new file mode 100644
index 0000000..29c3458
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/component.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/dashboard.svg b/lab-booking-ui/src/assets/icons/svg/dashboard.svg
new file mode 100644
index 0000000..5317d37
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/dashboard.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/date-range.svg b/lab-booking-ui/src/assets/icons/svg/date-range.svg
new file mode 100644
index 0000000..fda571e
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/date-range.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/date.svg b/lab-booking-ui/src/assets/icons/svg/date.svg
new file mode 100644
index 0000000..52dc73e
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/date.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/dict.svg b/lab-booking-ui/src/assets/icons/svg/dict.svg
new file mode 100644
index 0000000..4849377
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/dict.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/documentation.svg b/lab-booking-ui/src/assets/icons/svg/documentation.svg
new file mode 100644
index 0000000..7043122
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/documentation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/download.svg b/lab-booking-ui/src/assets/icons/svg/download.svg
new file mode 100644
index 0000000..c896951
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/download.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/drag.svg b/lab-booking-ui/src/assets/icons/svg/drag.svg
new file mode 100644
index 0000000..4185d3c
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/drag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/druid.svg b/lab-booking-ui/src/assets/icons/svg/druid.svg
new file mode 100644
index 0000000..a2b4b4e
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/druid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/edit.svg b/lab-booking-ui/src/assets/icons/svg/edit.svg
new file mode 100644
index 0000000..d26101f
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/edit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/education.svg b/lab-booking-ui/src/assets/icons/svg/education.svg
new file mode 100644
index 0000000..7bfb01d
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/education.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/email.svg b/lab-booking-ui/src/assets/icons/svg/email.svg
new file mode 100644
index 0000000..74d25e2
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/email.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/example.svg b/lab-booking-ui/src/assets/icons/svg/example.svg
new file mode 100644
index 0000000..46f42b5
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/example.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/excel.svg b/lab-booking-ui/src/assets/icons/svg/excel.svg
new file mode 100644
index 0000000..74d97b8
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/excel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/exit-fullscreen.svg b/lab-booking-ui/src/assets/icons/svg/exit-fullscreen.svg
new file mode 100644
index 0000000..485c128
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/exit-fullscreen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/eye-open.svg b/lab-booking-ui/src/assets/icons/svg/eye-open.svg
new file mode 100644
index 0000000..88dcc98
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/eye-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/eye.svg b/lab-booking-ui/src/assets/icons/svg/eye.svg
new file mode 100644
index 0000000..16ed2d8
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/eye.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/form.svg b/lab-booking-ui/src/assets/icons/svg/form.svg
new file mode 100644
index 0000000..dcbaa18
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/form.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/fullscreen.svg b/lab-booking-ui/src/assets/icons/svg/fullscreen.svg
new file mode 100644
index 0000000..0e86b6f
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/fullscreen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/github.svg b/lab-booking-ui/src/assets/icons/svg/github.svg
new file mode 100644
index 0000000..db0a0d4
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/guide.svg b/lab-booking-ui/src/assets/icons/svg/guide.svg
new file mode 100644
index 0000000..b271001
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/guide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/icon.svg b/lab-booking-ui/src/assets/icons/svg/icon.svg
new file mode 100644
index 0000000..82be8ee
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/input.svg b/lab-booking-ui/src/assets/icons/svg/input.svg
new file mode 100644
index 0000000..ab91381
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/input.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/international.svg b/lab-booking-ui/src/assets/icons/svg/international.svg
new file mode 100644
index 0000000..e9b56ee
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/international.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/job.svg b/lab-booking-ui/src/assets/icons/svg/job.svg
new file mode 100644
index 0000000..2a93a25
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/job.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/language.svg b/lab-booking-ui/src/assets/icons/svg/language.svg
new file mode 100644
index 0000000..0082b57
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/language.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/link.svg b/lab-booking-ui/src/assets/icons/svg/link.svg
new file mode 100644
index 0000000..48197ba
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/list.svg b/lab-booking-ui/src/assets/icons/svg/list.svg
new file mode 100644
index 0000000..20259ed
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/list.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/lock.svg b/lab-booking-ui/src/assets/icons/svg/lock.svg
new file mode 100644
index 0000000..74fee54
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/log.svg b/lab-booking-ui/src/assets/icons/svg/log.svg
new file mode 100644
index 0000000..d879d33
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/log.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/logininfor.svg b/lab-booking-ui/src/assets/icons/svg/logininfor.svg
new file mode 100644
index 0000000..267f844
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/logininfor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/message.svg b/lab-booking-ui/src/assets/icons/svg/message.svg
new file mode 100644
index 0000000..14ca817
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/message.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/money.svg b/lab-booking-ui/src/assets/icons/svg/money.svg
new file mode 100644
index 0000000..c1580de
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/money.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/monitor.svg b/lab-booking-ui/src/assets/icons/svg/monitor.svg
new file mode 100644
index 0000000..bc308cb
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/monitor.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/nested.svg b/lab-booking-ui/src/assets/icons/svg/nested.svg
new file mode 100644
index 0000000..06713a8
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/nested.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/number.svg b/lab-booking-ui/src/assets/icons/svg/number.svg
new file mode 100644
index 0000000..ad5ce9a
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/number.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/online.svg b/lab-booking-ui/src/assets/icons/svg/online.svg
new file mode 100644
index 0000000..330a202
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/online.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/password.svg b/lab-booking-ui/src/assets/icons/svg/password.svg
new file mode 100644
index 0000000..6c64def
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/password.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/pdf.svg b/lab-booking-ui/src/assets/icons/svg/pdf.svg
new file mode 100644
index 0000000..957aa0c
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/pdf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/people.svg b/lab-booking-ui/src/assets/icons/svg/people.svg
new file mode 100644
index 0000000..2bd54ae
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/people.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/peoples.svg b/lab-booking-ui/src/assets/icons/svg/peoples.svg
new file mode 100644
index 0000000..aab852e
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/peoples.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/phone.svg b/lab-booking-ui/src/assets/icons/svg/phone.svg
new file mode 100644
index 0000000..ab8e8c4
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/phone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/post.svg b/lab-booking-ui/src/assets/icons/svg/post.svg
new file mode 100644
index 0000000..2922c61
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/post.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/qq.svg b/lab-booking-ui/src/assets/icons/svg/qq.svg
new file mode 100644
index 0000000..ee13d4e
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/qq.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/question.svg b/lab-booking-ui/src/assets/icons/svg/question.svg
new file mode 100644
index 0000000..cf75bd4
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/question.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/radio.svg b/lab-booking-ui/src/assets/icons/svg/radio.svg
new file mode 100644
index 0000000..0cde345
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/radio.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/rate.svg b/lab-booking-ui/src/assets/icons/svg/rate.svg
new file mode 100644
index 0000000..aa3b14d
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/rate.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/redis.svg b/lab-booking-ui/src/assets/icons/svg/redis.svg
new file mode 100644
index 0000000..2f1d62d
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/redis.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/row.svg b/lab-booking-ui/src/assets/icons/svg/row.svg
new file mode 100644
index 0000000..0780992
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/row.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/search.svg b/lab-booking-ui/src/assets/icons/svg/search.svg
new file mode 100644
index 0000000..84233dd
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/select.svg b/lab-booking-ui/src/assets/icons/svg/select.svg
new file mode 100644
index 0000000..d628382
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/select.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/server.svg b/lab-booking-ui/src/assets/icons/svg/server.svg
new file mode 100644
index 0000000..ca37b00
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/server.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/shopping.svg b/lab-booking-ui/src/assets/icons/svg/shopping.svg
new file mode 100644
index 0000000..87513e7
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/shopping.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/size.svg b/lab-booking-ui/src/assets/icons/svg/size.svg
new file mode 100644
index 0000000..ddb25b8
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/size.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/skill.svg b/lab-booking-ui/src/assets/icons/svg/skill.svg
new file mode 100644
index 0000000..a3b7312
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/skill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/slider.svg b/lab-booking-ui/src/assets/icons/svg/slider.svg
new file mode 100644
index 0000000..fbe4f39
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/slider.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/star.svg b/lab-booking-ui/src/assets/icons/svg/star.svg
new file mode 100644
index 0000000..6cf86e6
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/swagger.svg b/lab-booking-ui/src/assets/icons/svg/swagger.svg
new file mode 100644
index 0000000..05d4e7b
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/swagger.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/switch.svg b/lab-booking-ui/src/assets/icons/svg/switch.svg
new file mode 100644
index 0000000..0ba61e3
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/switch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/system.svg b/lab-booking-ui/src/assets/icons/svg/system.svg
new file mode 100644
index 0000000..dba28cf
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/system.svg
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/tab.svg b/lab-booking-ui/src/assets/icons/svg/tab.svg
new file mode 100644
index 0000000..b4b48e4
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/tab.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/table.svg b/lab-booking-ui/src/assets/icons/svg/table.svg
new file mode 100644
index 0000000..0e3dc9d
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/textarea.svg b/lab-booking-ui/src/assets/icons/svg/textarea.svg
new file mode 100644
index 0000000..2709f29
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/textarea.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/theme.svg b/lab-booking-ui/src/assets/icons/svg/theme.svg
new file mode 100644
index 0000000..5982a2f
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/theme.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/time-range.svg b/lab-booking-ui/src/assets/icons/svg/time-range.svg
new file mode 100644
index 0000000..13c1202
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/time-range.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/time.svg b/lab-booking-ui/src/assets/icons/svg/time.svg
new file mode 100644
index 0000000..b376e32
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/time.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/tool.svg b/lab-booking-ui/src/assets/icons/svg/tool.svg
new file mode 100644
index 0000000..c813067
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/tool.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/tree-table.svg b/lab-booking-ui/src/assets/icons/svg/tree-table.svg
new file mode 100644
index 0000000..8aafdb8
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/tree-table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/tree.svg b/lab-booking-ui/src/assets/icons/svg/tree.svg
new file mode 100644
index 0000000..dd4b7dd
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/tree.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/upload.svg b/lab-booking-ui/src/assets/icons/svg/upload.svg
new file mode 100644
index 0000000..bae49c0
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/upload.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/user.svg b/lab-booking-ui/src/assets/icons/svg/user.svg
new file mode 100644
index 0000000..0ba0716
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/user.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/validCode.svg b/lab-booking-ui/src/assets/icons/svg/validCode.svg
new file mode 100644
index 0000000..cfb1021
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/validCode.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/wechat.svg b/lab-booking-ui/src/assets/icons/svg/wechat.svg
new file mode 100644
index 0000000..c586e55
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/wechat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svg/zip.svg b/lab-booking-ui/src/assets/icons/svg/zip.svg
new file mode 100644
index 0000000..f806fc4
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svg/zip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/icons/svgo.yml b/lab-booking-ui/src/assets/icons/svgo.yml
new file mode 100644
index 0000000..d11906a
--- /dev/null
+++ b/lab-booking-ui/src/assets/icons/svgo.yml
@@ -0,0 +1,22 @@
+# replace default config
+
+# multipass: true
+# full: true
+
+plugins:
+
+ # - name
+ #
+ # or:
+ # - name: false
+ # - name: true
+ #
+ # or:
+ # - name:
+ # param1: 1
+ # param2: 2
+
+- removeAttrs:
+ attrs:
+ - 'fill'
+ - 'fill-rule'
diff --git a/lab-booking-ui/src/assets/images/dark.svg b/lab-booking-ui/src/assets/images/dark.svg
new file mode 100644
index 0000000..f646bd7
--- /dev/null
+++ b/lab-booking-ui/src/assets/images/dark.svg
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/images/light.svg b/lab-booking-ui/src/assets/images/light.svg
new file mode 100644
index 0000000..ab7cc08
--- /dev/null
+++ b/lab-booking-ui/src/assets/images/light.svg
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/assets/images/login-background.jpg b/lab-booking-ui/src/assets/images/login-background.jpg
new file mode 100644
index 0000000..8a89eb8
Binary files /dev/null and b/lab-booking-ui/src/assets/images/login-background.jpg differ
diff --git a/lab-booking-ui/src/assets/images/profile.jpg b/lab-booking-ui/src/assets/images/profile.jpg
new file mode 100644
index 0000000..b3a940b
Binary files /dev/null and b/lab-booking-ui/src/assets/images/profile.jpg differ
diff --git a/lab-booking-ui/src/assets/logo/logo.png b/lab-booking-ui/src/assets/logo/logo.png
new file mode 100644
index 0000000..e263760
Binary files /dev/null and b/lab-booking-ui/src/assets/logo/logo.png differ
diff --git a/lab-booking-ui/src/assets/styles/btn.scss b/lab-booking-ui/src/assets/styles/btn.scss
new file mode 100644
index 0000000..e6ba1a8
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/btn.scss
@@ -0,0 +1,99 @@
+@import './variables.scss';
+
+@mixin colorBtn($color) {
+ background: $color;
+
+ &:hover {
+ color: $color;
+
+ &:before,
+ &:after {
+ background: $color;
+ }
+ }
+}
+
+.blue-btn {
+ @include colorBtn($blue)
+}
+
+.light-blue-btn {
+ @include colorBtn($light-blue)
+}
+
+.red-btn {
+ @include colorBtn($red)
+}
+
+.pink-btn {
+ @include colorBtn($pink)
+}
+
+.green-btn {
+ @include colorBtn($green)
+}
+
+.tiffany-btn {
+ @include colorBtn($tiffany)
+}
+
+.yellow-btn {
+ @include colorBtn($yellow)
+}
+
+.pan-btn {
+ font-size: 14px;
+ color: #fff;
+ padding: 14px 36px;
+ border-radius: 8px;
+ border: none;
+ outline: none;
+ transition: 600ms ease all;
+ position: relative;
+ display: inline-block;
+
+ &:hover {
+ background: #fff;
+
+ &:before,
+ &:after {
+ width: 100%;
+ transition: 600ms ease all;
+ }
+ }
+
+ &:before,
+ &:after {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 2px;
+ width: 0;
+ transition: 400ms ease all;
+ }
+
+ &::after {
+ right: inherit;
+ top: inherit;
+ left: 0;
+ bottom: 0;
+ }
+}
+
+.custom-button {
+ display: inline-block;
+ line-height: 1;
+ white-space: nowrap;
+ cursor: pointer;
+ background: #fff;
+ color: #fff;
+ -webkit-appearance: none;
+ text-align: center;
+ box-sizing: border-box;
+ outline: 0;
+ margin: 0;
+ padding: 10px 15px;
+ font-size: 14px;
+ border-radius: 4px;
+}
diff --git a/lab-booking-ui/src/assets/styles/element-ui.scss b/lab-booking-ui/src/assets/styles/element-ui.scss
new file mode 100644
index 0000000..955d3ca
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/element-ui.scss
@@ -0,0 +1,84 @@
+// cover some element-ui styles
+
+.el-breadcrumb__inner,
+.el-breadcrumb__inner a {
+ font-weight: 400 !important;
+}
+
+.el-upload {
+ input[type="file"] {
+ display: none !important;
+ }
+}
+
+.el-upload__input {
+ display: none;
+}
+
+.cell {
+ .el-tag {
+ margin-right: 0px;
+ }
+}
+
+.small-padding {
+ .cell {
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+}
+
+.fixed-width {
+ .el-button--mini {
+ padding: 7px 10px;
+ width: 60px;
+ }
+}
+
+.status-col {
+ .cell {
+ padding: 0 10px;
+ text-align: center;
+
+ .el-tag {
+ margin-right: 0px;
+ }
+ }
+}
+
+// to fixed https://github.com/ElemeFE/element/issues/2461
+.el-dialog {
+ transform: none;
+ left: 0;
+ position: relative;
+ margin: 0 auto;
+}
+
+// refine element ui upload
+.upload-container {
+ .el-upload {
+ width: 100%;
+
+ .el-upload-dragger {
+ width: 100%;
+ height: 200px;
+ }
+ }
+}
+
+// dropdown
+.el-dropdown-menu {
+ a {
+ display: block
+ }
+}
+
+// fix date-picker ui bug in filter-item
+.el-range-editor.el-input__inner {
+ display: inline-flex !important;
+}
+
+// to fix el-date-picker css style
+.el-range-separator {
+ box-sizing: content-box;
+}
diff --git a/lab-booking-ui/src/assets/styles/element-variables.scss b/lab-booking-ui/src/assets/styles/element-variables.scss
new file mode 100644
index 0000000..8b7a48e
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/element-variables.scss
@@ -0,0 +1,31 @@
+/**
+* I think element-ui's default theme color is too light for long-term use.
+* So I modified the default color and you can modify it to your liking.
+**/
+
+/* theme color */
+$--color-primary: #1890ff;
+$--color-success: #13ce66;
+$--color-warning: #ffba00;
+$--color-danger: #ff4949;
+// $--color-info: #1E1E1E;
+
+$--button-font-weight: 400;
+
+// $--color-text-regular: #1f2d3d;
+
+$--border-color-light: #dfe4ed;
+$--border-color-lighter: #e6ebf5;
+
+$--table-border:1px solid#dfe6ec;
+
+/* icon font path, required */
+$--font-path: '~element-ui/lib/theme-chalk/fonts';
+
+@import "~element-ui/packages/theme-chalk/src/index";
+
+// the :export directive is the magic sauce for webpack
+// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
+:export {
+ theme: $--color-primary;
+}
diff --git a/lab-booking-ui/src/assets/styles/index.scss b/lab-booking-ui/src/assets/styles/index.scss
new file mode 100644
index 0000000..96095ef
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/index.scss
@@ -0,0 +1,191 @@
+@import './variables.scss';
+@import './mixin.scss';
+@import './transition.scss';
+@import './element-ui.scss';
+@import './sidebar.scss';
+@import './btn.scss';
+
+body {
+ height: 100%;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+}
+
+label {
+ font-weight: 700;
+}
+
+html {
+ height: 100%;
+ box-sizing: border-box;
+}
+
+#app {
+ height: 100%;
+}
+
+*,
+*:before,
+*:after {
+ box-sizing: inherit;
+}
+
+.no-padding {
+ padding: 0px !important;
+}
+
+.padding-content {
+ padding: 4px 0;
+}
+
+a:focus,
+a:active {
+ outline: none;
+}
+
+a,
+a:focus,
+a:hover {
+ cursor: pointer;
+ color: inherit;
+ text-decoration: none;
+}
+
+div:focus {
+ outline: none;
+}
+
+.fr {
+ float: right;
+}
+
+.fl {
+ float: left;
+}
+
+.pr-5 {
+ padding-right: 5px;
+}
+
+.pl-5 {
+ padding-left: 5px;
+}
+
+.block {
+ display: block;
+}
+
+.pointer {
+ cursor: pointer;
+}
+
+.inlineBlock {
+ display: block;
+}
+
+.clearfix {
+ &:after {
+ visibility: hidden;
+ display: block;
+ font-size: 0;
+ content: " ";
+ clear: both;
+ height: 0;
+ }
+}
+
+aside {
+ background: #eef1f6;
+ padding: 8px 24px;
+ margin-bottom: 20px;
+ border-radius: 2px;
+ display: block;
+ line-height: 32px;
+ font-size: 16px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+ color: #2c3e50;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+
+ a {
+ color: #337ab7;
+ cursor: pointer;
+
+ &:hover {
+ color: rgb(32, 160, 255);
+ }
+ }
+}
+
+//main-container全局样式
+.app-container {
+ padding: 20px;
+}
+
+.components-container {
+ margin: 30px 50px;
+ position: relative;
+}
+
+.pagination-container {
+ margin-top: 30px;
+}
+
+.text-center {
+ text-align: center
+}
+
+.sub-navbar {
+ height: 50px;
+ line-height: 50px;
+ position: relative;
+ width: 100%;
+ text-align: right;
+ padding-right: 20px;
+ transition: 600ms ease position;
+ background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
+
+ .subtitle {
+ font-size: 20px;
+ color: #fff;
+ }
+
+ &.draft {
+ background: #d0d0d0;
+ }
+
+ &.deleted {
+ background: #d0d0d0;
+ }
+}
+
+.link-type,
+.link-type:focus {
+ color: #337ab7;
+ cursor: pointer;
+
+ &:hover {
+ color: rgb(32, 160, 255);
+ }
+}
+
+.filter-container {
+ padding-bottom: 10px;
+
+ .filter-item {
+ display: inline-block;
+ vertical-align: middle;
+ margin-bottom: 10px;
+ }
+}
+
+//refine vue-multiselect plugin
+.multiselect {
+ line-height: 16px;
+}
+
+.multiselect--active {
+ z-index: 1000 !important;
+}
diff --git a/lab-booking-ui/src/assets/styles/mixin.scss b/lab-booking-ui/src/assets/styles/mixin.scss
new file mode 100644
index 0000000..06fa061
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/mixin.scss
@@ -0,0 +1,66 @@
+@mixin clearfix {
+ &:after {
+ content: "";
+ display: table;
+ clear: both;
+ }
+}
+
+@mixin scrollBar {
+ &::-webkit-scrollbar-track-piece {
+ background: #d3dce6;
+ }
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #99a9bf;
+ border-radius: 20px;
+ }
+}
+
+@mixin relative {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+@mixin pct($pct) {
+ width: #{$pct};
+ position: relative;
+ margin: 0 auto;
+}
+
+@mixin triangle($width, $height, $color, $direction) {
+ $width: $width/2;
+ $color-border-style: $height solid $color;
+ $transparent-border-style: $width solid transparent;
+ height: 0;
+ width: 0;
+
+ @if $direction==up {
+ border-bottom: $color-border-style;
+ border-left: $transparent-border-style;
+ border-right: $transparent-border-style;
+ }
+
+ @else if $direction==right {
+ border-left: $color-border-style;
+ border-top: $transparent-border-style;
+ border-bottom: $transparent-border-style;
+ }
+
+ @else if $direction==down {
+ border-top: $color-border-style;
+ border-left: $transparent-border-style;
+ border-right: $transparent-border-style;
+ }
+
+ @else if $direction==left {
+ border-right: $color-border-style;
+ border-top: $transparent-border-style;
+ border-bottom: $transparent-border-style;
+ }
+}
diff --git a/lab-booking-ui/src/assets/styles/ruoyi.scss b/lab-booking-ui/src/assets/styles/ruoyi.scss
new file mode 100644
index 0000000..de1db7f
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/ruoyi.scss
@@ -0,0 +1,239 @@
+ /**
+ * 通用css样式布局处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+ /** 基础通用 **/
+.pt5 {
+ padding-top: 5px;
+}
+.pr5 {
+ padding-right: 5px;
+}
+.pb5 {
+ padding-bottom: 5px;
+}
+.mt5 {
+ margin-top: 5px;
+}
+.mr5 {
+ margin-right: 5px;
+}
+.mb5 {
+ margin-bottom: 5px;
+}
+.mb8 {
+ margin-bottom: 8px;
+}
+.ml5 {
+ margin-left: 5px;
+}
+.mt10 {
+ margin-top: 10px;
+}
+.mr10 {
+ margin-right: 10px;
+}
+.mb10 {
+ margin-bottom: 10px;
+}
+.ml0 {
+ margin-left: 10px;
+}
+.mt20 {
+ margin-top: 20px;
+}
+.mr20 {
+ margin-right: 20px;
+}
+.mb20 {
+ margin-bottom: 20px;
+}
+.m20 {
+ margin-left: 20px;
+}
+
+.el-dialog:not(.is-fullscreen){
+ margin-top: 6vh !important;
+}
+
+.el-table {
+ .el-table__header-wrapper, .el-table__fixed-header-wrapper {
+ th {
+ word-break: break-word;
+ background-color: #f8f8f9;
+ color: #515a6e;
+ height: 40px;
+ font-size: 13px;
+ }
+ }
+ .el-table__body-wrapper {
+ .el-button [class*="el-icon-"] + span {
+ margin-left: 1px;
+ }
+ }
+}
+
+/** 表单布局 **/
+.form-header {
+ font-size:15px;
+ color:#6379bb;
+ border-bottom:1px solid #ddd;
+ margin:8px 10px 25px 10px;
+ padding-bottom:5px
+}
+
+/** 表格布局 **/
+.pagination-container {
+ position: relative;
+ height: 25px;
+ margin-bottom: 10px;
+ margin-top: 15px;
+ padding: 10px 20px !important;
+}
+
+/* tree border */
+.tree-border {
+ margin-top: 5px;
+ border: 1px solid #e5e6e7;
+ background: #FFFFFF none;
+ border-radius:4px;
+}
+
+.pagination-container .el-pagination {
+ right: 0;
+ position: absolute;
+}
+
+.el-table .fixed-width .el-button--mini {
+ padding-left: 0;
+ padding-right: 0;
+ width: inherit;
+}
+
+.el-tree-node__content > .el-checkbox {
+ margin-right: 8px;
+}
+
+.list-group-striped > .list-group-item {
+ border-left: 0;
+ border-right: 0;
+ border-radius: 0;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.list-group {
+ padding-left: 0px;
+ list-style: none;
+}
+
+.list-group-item {
+ border-bottom: 1px solid #e7eaec;
+ border-top: 1px solid #e7eaec;
+ margin-bottom: -1px;
+ padding: 11px 0px;
+ font-size: 13px;
+}
+
+.pull-right {
+ float: right !important;
+}
+
+.el-card__header {
+ padding: 14px 15px 7px;
+ min-height: 40px;
+}
+
+.el-card__body {
+ padding: 15px 20px 20px 20px;
+}
+
+.card-box {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-bottom: 10px;
+}
+
+/* button color */
+.el-button--cyan.is-active,
+.el-button--cyan:active {
+ background: #20B2AA;
+ border-color: #20B2AA;
+ color: #FFFFFF;
+}
+
+.el-button--cyan:focus,
+.el-button--cyan:hover {
+ background: #48D1CC;
+ border-color: #48D1CC;
+ color: #FFFFFF;
+}
+
+.el-button--cyan {
+ background-color: #20B2AA;
+ border-color: #20B2AA;
+ color: #FFFFFF;
+}
+
+/* text color */
+.text-navy {
+ color: #1ab394;
+}
+
+.text-primary {
+ color: inherit;
+}
+
+.text-success {
+ color: #1c84c6;
+}
+
+.text-info {
+ color: #23c6c8;
+}
+
+.text-warning {
+ color: #f8ac59;
+}
+
+.text-danger {
+ color: #ed5565;
+}
+
+.text-muted {
+ color: #888888;
+}
+
+/* image */
+.img-circle {
+ border-radius: 50%;
+}
+
+.img-lg {
+ width: 120px;
+ height: 120px;
+}
+
+.avatar-upload-preview {
+ position: absolute;
+ top: 50%;
+ transform: translate(50%, -50%);
+ width: 200px;
+ height: 200px;
+ border-radius: 50%;
+ box-shadow: 0 0 4px #ccc;
+ overflow: hidden;
+}
+
+/* 拖拽列样式 */
+.sortable-ghost{
+ opacity: .8;
+ color: #fff!important;
+ background: #42b983!important;
+}
+
+.top-right-btn {
+ position: relative;
+ float: right;
+}
diff --git a/lab-booking-ui/src/assets/styles/sidebar.scss b/lab-booking-ui/src/assets/styles/sidebar.scss
new file mode 100644
index 0000000..2f368c3
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/sidebar.scss
@@ -0,0 +1,223 @@
+#app {
+
+ .main-container {
+ min-height: 100%;
+ transition: margin-left .28s;
+ margin-left: $sideBarWidth;
+ position: relative;
+ }
+
+ .sidebar-container {
+ -webkit-transition: width .28s;
+ transition: width 0.28s;
+ width: $sideBarWidth !important;
+ background-color: $menuBg;
+ height: 100%;
+ position: fixed;
+ font-size: 0px;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1001;
+ overflow: hidden;
+ -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
+ box-shadow: 2px 0 6px rgba(0,21,41,.35);
+
+ // reset element-ui css
+ .horizontal-collapse-transition {
+ transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+ }
+
+ .scrollbar-wrapper {
+ overflow-x: hidden !important;
+ }
+
+ .el-scrollbar__bar.is-vertical {
+ right: 0px;
+ }
+
+ .el-scrollbar {
+ height: 100%;
+ }
+
+ &.has-logo {
+ .el-scrollbar {
+ height: calc(100% - 50px);
+ }
+ }
+
+ .is-horizontal {
+ display: none;
+ }
+
+ a {
+ display: inline-block;
+ width: 100%;
+ overflow: hidden;
+ }
+
+ .svg-icon {
+ margin-right: 16px;
+ }
+
+ .el-menu {
+ border: none;
+ height: 100%;
+ width: 100% !important;
+ }
+
+ .el-menu-item, .el-submenu__title {
+ overflow: hidden !important;
+ text-overflow: ellipsis !important;
+ white-space: nowrap !important;
+ }
+
+ // menu hover
+ .submenu-title-noDropdown,
+ .el-submenu__title {
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.06) !important;
+ }
+ }
+
+ & .theme-dark .is-active > .el-submenu__title {
+ color: $subMenuActiveText !important;
+ }
+
+ & .nest-menu .el-submenu>.el-submenu__title,
+ & .el-submenu .el-menu-item {
+ min-width: $sideBarWidth !important;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.06) !important;
+ }
+ }
+
+ & .theme-dark .nest-menu .el-submenu>.el-submenu__title,
+ & .theme-dark .el-submenu .el-menu-item {
+ background-color: $subMenuBg !important;
+
+ &:hover {
+ background-color: $subMenuHover !important;
+ }
+ }
+ }
+
+ .hideSidebar {
+ .sidebar-container {
+ width: 54px !important;
+ }
+
+ .main-container {
+ margin-left: 54px;
+ }
+
+ .submenu-title-noDropdown {
+ padding: 0 !important;
+ position: relative;
+
+ .el-tooltip {
+ padding: 0 !important;
+
+ .svg-icon {
+ margin-left: 20px;
+ }
+ }
+ }
+
+ .el-submenu {
+ overflow: hidden;
+
+ &>.el-submenu__title {
+ padding: 0 !important;
+
+ .svg-icon {
+ margin-left: 20px;
+ }
+
+ }
+ }
+
+ .el-menu--collapse {
+ .el-submenu {
+ &>.el-submenu__title {
+ &>span {
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ visibility: hidden;
+ display: inline-block;
+ }
+ }
+ }
+ }
+ }
+
+ .el-menu--collapse .el-menu .el-submenu {
+ min-width: $sideBarWidth !important;
+ }
+
+ // mobile responsive
+ .mobile {
+ .main-container {
+ margin-left: 0px;
+ }
+
+ .sidebar-container {
+ transition: transform .28s;
+ width: $sideBarWidth !important;
+ }
+
+ &.hideSidebar {
+ .sidebar-container {
+ pointer-events: none;
+ transition-duration: 0.3s;
+ transform: translate3d(-$sideBarWidth, 0, 0);
+ }
+ }
+ }
+
+ .withoutAnimation {
+
+ .main-container,
+ .sidebar-container {
+ transition: none;
+ }
+ }
+}
+
+// when menu collapsed
+.el-menu--vertical {
+ &>.el-menu {
+ .svg-icon {
+ margin-right: 16px;
+ }
+ }
+
+ .nest-menu .el-submenu>.el-submenu__title,
+ .el-menu-item {
+ &:hover {
+ // you can use $subMenuHover
+ background-color: rgba(0, 0, 0, 0.06) !important;
+ }
+ }
+
+ // the scroll bar appears when the subMenu is too long
+ >.el-menu--popup {
+ max-height: 100vh;
+ overflow-y: auto;
+
+ &::-webkit-scrollbar-track-piece {
+ background: #d3dce6;
+ }
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #99a9bf;
+ border-radius: 20px;
+ }
+ }
+}
diff --git a/lab-booking-ui/src/assets/styles/transition.scss b/lab-booking-ui/src/assets/styles/transition.scss
new file mode 100644
index 0000000..4cb27cc
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/transition.scss
@@ -0,0 +1,48 @@
+// global transition css
+
+/* fade */
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.28s;
+}
+
+.fade-enter,
+.fade-leave-active {
+ opacity: 0;
+}
+
+/* fade-transform */
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+ transition: all .5s;
+}
+
+.fade-transform-enter {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+
+.fade-transform-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+/* breadcrumb transition */
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+ transition: all .5s;
+}
+
+.breadcrumb-enter,
+.breadcrumb-leave-active {
+ opacity: 0;
+ transform: translateX(20px);
+}
+
+.breadcrumb-move {
+ transition: all .5s;
+}
+
+.breadcrumb-leave-active {
+ position: absolute;
+}
diff --git a/lab-booking-ui/src/assets/styles/variables.scss b/lab-booking-ui/src/assets/styles/variables.scss
new file mode 100644
index 0000000..452a1ec
--- /dev/null
+++ b/lab-booking-ui/src/assets/styles/variables.scss
@@ -0,0 +1,44 @@
+// base color
+$blue:#324157;
+$light-blue:#3A71A8;
+$red:#C03639;
+$pink: #E65D6E;
+$green: #30B08F;
+$tiffany: #4AB7BD;
+$yellow:#FEC171;
+$panGreen: #30B08F;
+
+// sidebar
+$menuText:#bfcbd9;
+$menuActiveText:#409EFF;
+$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
+
+$menuBg:#304156;
+$menuHover:#263445;
+$sidebarTitle: #ffffff;
+
+$menuLightBg:#ffffff;
+$menuLightHover:#f0f1f5;
+$sidebarLightTitle: #001529;
+
+$subMenuBg:#1f2d3d;
+$subMenuHover:#001528;
+
+$sideBarWidth: 200px;
+
+// the :export directive is the magic sauce for webpack
+// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
+:export {
+ menuText: $menuText;
+ menuActiveText: $menuActiveText;
+ subMenuActiveText: $subMenuActiveText;
+ menuBg: $menuBg;
+ menuHover: $menuHover;
+ menuLightBg: $menuLightBg;
+ menuLightHover: $menuLightHover;
+ subMenuBg: $subMenuBg;
+ subMenuHover: $subMenuHover;
+ sideBarWidth: $sideBarWidth;
+ sidebarTitle: $sidebarTitle;
+ sidebarLightTitle: $sidebarLightTitle
+}
diff --git a/lab-booking-ui/src/components/Breadcrumb/index.vue b/lab-booking-ui/src/components/Breadcrumb/index.vue
new file mode 100644
index 0000000..b313fdd
--- /dev/null
+++ b/lab-booking-ui/src/components/Breadcrumb/index.vue
@@ -0,0 +1,74 @@
+
+
+
+
+ {{ item.meta.title }}
+ {{ item.meta.title }}
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/Editor/index.vue b/lab-booking-ui/src/components/Editor/index.vue
new file mode 100644
index 0000000..640049c
--- /dev/null
+++ b/lab-booking-ui/src/components/Editor/index.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/FileUpload/index.vue b/lab-booking-ui/src/components/FileUpload/index.vue
new file mode 100644
index 0000000..69942aa
--- /dev/null
+++ b/lab-booking-ui/src/components/FileUpload/index.vue
@@ -0,0 +1,179 @@
+
+
+
+
+ 选取文件
+
+
+ 请上传
+ 大小不超过 {{ fileSize }}MB
+ 格式为 {{ fileType.join("/") }}
+ 的文件
+
+
+
+
+
+
+
+ {{ getFileName(file.name) }}
+
+
+ 删除
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/components/Hamburger/index.vue b/lab-booking-ui/src/components/Hamburger/index.vue
new file mode 100644
index 0000000..368b002
--- /dev/null
+++ b/lab-booking-ui/src/components/Hamburger/index.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/HeaderSearch/index.vue b/lab-booking-ui/src/components/HeaderSearch/index.vue
new file mode 100644
index 0000000..ce9f305
--- /dev/null
+++ b/lab-booking-ui/src/components/HeaderSearch/index.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/IconSelect/index.vue b/lab-booking-ui/src/components/IconSelect/index.vue
new file mode 100644
index 0000000..b0ec9fa
--- /dev/null
+++ b/lab-booking-ui/src/components/IconSelect/index.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/IconSelect/requireIcons.js b/lab-booking-ui/src/components/IconSelect/requireIcons.js
new file mode 100644
index 0000000..99e5c54
--- /dev/null
+++ b/lab-booking-ui/src/components/IconSelect/requireIcons.js
@@ -0,0 +1,11 @@
+
+const req = require.context('../../assets/icons/svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys()
+
+const re = /\.\/(.*)\.svg/
+
+const icons = requireAll(req).map(i => {
+ return i.match(re)[1]
+})
+
+export default icons
diff --git a/lab-booking-ui/src/components/ImageUpload/index.vue b/lab-booking-ui/src/components/ImageUpload/index.vue
new file mode 100644
index 0000000..8996329
--- /dev/null
+++ b/lab-booking-ui/src/components/ImageUpload/index.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/components/Pagination/index.vue b/lab-booking-ui/src/components/Pagination/index.vue
new file mode 100644
index 0000000..c815e13
--- /dev/null
+++ b/lab-booking-ui/src/components/Pagination/index.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/PanThumb/index.vue b/lab-booking-ui/src/components/PanThumb/index.vue
new file mode 100644
index 0000000..1bcf417
--- /dev/null
+++ b/lab-booking-ui/src/components/PanThumb/index.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/ParentView/index.vue b/lab-booking-ui/src/components/ParentView/index.vue
new file mode 100644
index 0000000..7bf6148
--- /dev/null
+++ b/lab-booking-ui/src/components/ParentView/index.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/lab-booking-ui/src/components/RightPanel/index.vue b/lab-booking-ui/src/components/RightPanel/index.vue
new file mode 100644
index 0000000..fbf27eb
--- /dev/null
+++ b/lab-booking-ui/src/components/RightPanel/index.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/RightToolbar/index.vue b/lab-booking-ui/src/components/RightToolbar/index.vue
new file mode 100644
index 0000000..e3e1286
--- /dev/null
+++ b/lab-booking-ui/src/components/RightToolbar/index.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/RuoYi/Doc/index.vue b/lab-booking-ui/src/components/RuoYi/Doc/index.vue
new file mode 100644
index 0000000..a6187f3
--- /dev/null
+++ b/lab-booking-ui/src/components/RuoYi/Doc/index.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/components/RuoYi/Git/index.vue b/lab-booking-ui/src/components/RuoYi/Git/index.vue
new file mode 100644
index 0000000..1d09a77
--- /dev/null
+++ b/lab-booking-ui/src/components/RuoYi/Git/index.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/components/Screenfull/index.vue b/lab-booking-ui/src/components/Screenfull/index.vue
new file mode 100644
index 0000000..d4e539c
--- /dev/null
+++ b/lab-booking-ui/src/components/Screenfull/index.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/SizeSelect/index.vue b/lab-booking-ui/src/components/SizeSelect/index.vue
new file mode 100644
index 0000000..e88065b
--- /dev/null
+++ b/lab-booking-ui/src/components/SizeSelect/index.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+ {{
+ item.label }}
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/SvgIcon/index.vue b/lab-booking-ui/src/components/SvgIcon/index.vue
new file mode 100644
index 0000000..e4bf5ad
--- /dev/null
+++ b/lab-booking-ui/src/components/SvgIcon/index.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/ThemePicker/index.vue b/lab-booking-ui/src/components/ThemePicker/index.vue
new file mode 100644
index 0000000..b0df471
--- /dev/null
+++ b/lab-booking-ui/src/components/ThemePicker/index.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/components/TopNav/index.vue b/lab-booking-ui/src/components/TopNav/index.vue
new file mode 100644
index 0000000..d89930a
--- /dev/null
+++ b/lab-booking-ui/src/components/TopNav/index.vue
@@ -0,0 +1,182 @@
+
+
+
+
+ {{ item.meta.title }}
+
+
+
+
+ 更多菜单
+
+
+ {{ item.meta.title }}
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/directive/permission/hasPermi.js b/lab-booking-ui/src/directive/permission/hasPermi.js
new file mode 100644
index 0000000..d7107ce
--- /dev/null
+++ b/lab-booking-ui/src/directive/permission/hasPermi.js
@@ -0,0 +1,28 @@
+ /**
+ * 操作权限处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+import store from '@/store'
+
+export default {
+ inserted(el, binding, vnode) {
+ const { value } = binding
+ const all_permission = "*:*:*";
+ const permissions = store.getters && store.getters.permissions
+
+ if (value && value instanceof Array && value.length > 0) {
+ const permissionFlag = value
+
+ const hasPermissions = permissions.some(permission => {
+ return all_permission === permission || permissionFlag.includes(permission)
+ })
+
+ if (!hasPermissions) {
+ el.parentNode && el.parentNode.removeChild(el)
+ }
+ } else {
+ throw new Error(`请设置操作权限标签值`)
+ }
+ }
+}
diff --git a/lab-booking-ui/src/directive/permission/hasRole.js b/lab-booking-ui/src/directive/permission/hasRole.js
new file mode 100644
index 0000000..1303809
--- /dev/null
+++ b/lab-booking-ui/src/directive/permission/hasRole.js
@@ -0,0 +1,28 @@
+ /**
+ * 角色权限处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+import store from '@/store'
+
+export default {
+ inserted(el, binding, vnode) {
+ const { value } = binding
+ const super_admin = "admin";
+ const roles = store.getters && store.getters.roles
+
+ if (value && value instanceof Array && value.length > 0) {
+ const roleFlag = value
+
+ const hasRole = roles.some(role => {
+ return super_admin === role || roleFlag.includes(role)
+ })
+
+ if (!hasRole) {
+ el.parentNode && el.parentNode.removeChild(el)
+ }
+ } else {
+ throw new Error(`请设置角色权限标签值"`)
+ }
+ }
+}
diff --git a/lab-booking-ui/src/directive/permission/index.js b/lab-booking-ui/src/directive/permission/index.js
new file mode 100644
index 0000000..e3d76d3
--- /dev/null
+++ b/lab-booking-ui/src/directive/permission/index.js
@@ -0,0 +1,15 @@
+import hasRole from './hasRole'
+import hasPermi from './hasPermi'
+
+const install = function(Vue) {
+ Vue.directive('hasRole', hasRole)
+ Vue.directive('hasPermi', hasPermi)
+}
+
+if (window.Vue) {
+ window['hasRole'] = hasRole
+ window['hasPermi'] = hasPermi
+ Vue.use(install); // eslint-disable-line
+}
+
+export default install
diff --git a/lab-booking-ui/src/layout/components/AppMain.vue b/lab-booking-ui/src/layout/components/AppMain.vue
new file mode 100644
index 0000000..a897638
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/AppMain.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Navbar.vue b/lab-booking-ui/src/layout/components/Navbar.vue
new file mode 100644
index 0000000..9d220e9
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Navbar.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Settings/index.vue b/lab-booking-ui/src/layout/components/Settings/index.vue
new file mode 100644
index 0000000..3895199
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Settings/index.vue
@@ -0,0 +1,251 @@
+
+
+
+
+
+
主题风格设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 主题颜色
+
+
+
+
+
+
+
系统布局配置
+
+
+ 开启 TopNav
+
+
+
+
+ 开启 Tags-Views
+
+
+
+
+ 固定 Header
+
+
+
+
+ 显示 Logo
+
+
+
+
+
+
保存配置
+
重置配置
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Sidebar/FixiOSBug.js b/lab-booking-ui/src/layout/components/Sidebar/FixiOSBug.js
new file mode 100644
index 0000000..6823726
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/FixiOSBug.js
@@ -0,0 +1,25 @@
+export default {
+ computed: {
+ device() {
+ return this.$store.state.app.device
+ }
+ },
+ mounted() {
+ // In order to fix the click on menu on the ios device will trigger the mouseleave bug
+ this.fixBugIniOS()
+ },
+ methods: {
+ fixBugIniOS() {
+ const $subMenu = this.$refs.subMenu
+ if ($subMenu) {
+ const handleMouseleave = $subMenu.handleMouseleave
+ $subMenu.handleMouseleave = (e) => {
+ if (this.device === 'mobile') {
+ return
+ }
+ handleMouseleave(e)
+ }
+ }
+ }
+ }
+}
diff --git a/lab-booking-ui/src/layout/components/Sidebar/Item.vue b/lab-booking-ui/src/layout/components/Sidebar/Item.vue
new file mode 100644
index 0000000..b515f61
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/Item.vue
@@ -0,0 +1,29 @@
+
diff --git a/lab-booking-ui/src/layout/components/Sidebar/Link.vue b/lab-booking-ui/src/layout/components/Sidebar/Link.vue
new file mode 100644
index 0000000..530b3d5
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/Link.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Sidebar/Logo.vue b/lab-booking-ui/src/layout/components/Sidebar/Logo.vue
new file mode 100644
index 0000000..96b2c50
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/Logo.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Sidebar/SidebarItem.vue b/lab-booking-ui/src/layout/components/Sidebar/SidebarItem.vue
new file mode 100644
index 0000000..c4febee
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/SidebarItem.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/Sidebar/index.vue b/lab-booking-ui/src/layout/components/Sidebar/index.vue
new file mode 100644
index 0000000..9e5d2bb
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/Sidebar/index.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/TagsView/ScrollPane.vue b/lab-booking-ui/src/layout/components/TagsView/ScrollPane.vue
new file mode 100644
index 0000000..bb753a1
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/TagsView/ScrollPane.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/TagsView/index.vue b/lab-booking-ui/src/layout/components/TagsView/index.vue
new file mode 100644
index 0000000..6caf350
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/TagsView/index.vue
@@ -0,0 +1,318 @@
+
+
+
+
+ {{ tag.title }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/components/index.js b/lab-booking-ui/src/layout/components/index.js
new file mode 100644
index 0000000..104bd3a
--- /dev/null
+++ b/lab-booking-ui/src/layout/components/index.js
@@ -0,0 +1,5 @@
+export { default as AppMain } from './AppMain'
+export { default as Navbar } from './Navbar'
+export { default as Settings } from './Settings'
+export { default as Sidebar } from './Sidebar/index.vue'
+export { default as TagsView } from './TagsView/index.vue'
diff --git a/lab-booking-ui/src/layout/index.vue b/lab-booking-ui/src/layout/index.vue
new file mode 100644
index 0000000..d490771
--- /dev/null
+++ b/lab-booking-ui/src/layout/index.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/layout/mixin/ResizeHandler.js b/lab-booking-ui/src/layout/mixin/ResizeHandler.js
new file mode 100644
index 0000000..e8d0df8
--- /dev/null
+++ b/lab-booking-ui/src/layout/mixin/ResizeHandler.js
@@ -0,0 +1,45 @@
+import store from '@/store'
+
+const { body } = document
+const WIDTH = 992 // refer to Bootstrap's responsive design
+
+export default {
+ watch: {
+ $route(route) {
+ if (this.device === 'mobile' && this.sidebar.opened) {
+ store.dispatch('app/closeSideBar', { withoutAnimation: false })
+ }
+ }
+ },
+ beforeMount() {
+ window.addEventListener('resize', this.$_resizeHandler)
+ },
+ beforeDestroy() {
+ window.removeEventListener('resize', this.$_resizeHandler)
+ },
+ mounted() {
+ const isMobile = this.$_isMobile()
+ if (isMobile) {
+ store.dispatch('app/toggleDevice', 'mobile')
+ store.dispatch('app/closeSideBar', { withoutAnimation: true })
+ }
+ },
+ methods: {
+ // use $_ for mixins properties
+ // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+ $_isMobile() {
+ const rect = body.getBoundingClientRect()
+ return rect.width - 1 < WIDTH
+ },
+ $_resizeHandler() {
+ if (!document.hidden) {
+ const isMobile = this.$_isMobile()
+ store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
+
+ if (isMobile) {
+ store.dispatch('app/closeSideBar', { withoutAnimation: true })
+ }
+ }
+ }
+ }
+}
diff --git a/lab-booking-ui/src/main.js b/lab-booking-ui/src/main.js
new file mode 100644
index 0000000..5668d91
--- /dev/null
+++ b/lab-booking-ui/src/main.js
@@ -0,0 +1,73 @@
+import Vue from 'vue'
+
+import Cookies from 'js-cookie'
+
+import Element from 'element-ui'
+import './assets/styles/element-variables.scss'
+
+import '@/assets/styles/index.scss' // global css
+import '@/assets/styles/ruoyi.scss' // ruoyi css
+import App from './App'
+import store from './store'
+import router from './router'
+import permission from './directive/permission'
+
+import './assets/icons' // icon
+import './permission' // permission control
+import { getDicts } from "@/api/system/dict/data";
+import { getConfigKey } from "@/api/system/config";
+import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi";
+import Pagination from "@/components/Pagination";
+// 自定义表格工具扩展
+import RightToolbar from "@/components/RightToolbar"
+
+// 全局方法挂载
+Vue.prototype.getDicts = getDicts
+Vue.prototype.getConfigKey = getConfigKey
+Vue.prototype.parseTime = parseTime
+Vue.prototype.resetForm = resetForm
+Vue.prototype.addDateRange = addDateRange
+Vue.prototype.selectDictLabel = selectDictLabel
+Vue.prototype.selectDictLabels = selectDictLabels
+Vue.prototype.download = download
+Vue.prototype.handleTree = handleTree
+
+Vue.prototype.msgSuccess = function (msg) {
+ this.$message({ showClose: true, message: msg, type: "success" });
+}
+
+Vue.prototype.msgError = function (msg) {
+ this.$message({ showClose: true, message: msg, type: "error" });
+}
+
+Vue.prototype.msgInfo = function (msg) {
+ this.$message.info(msg);
+}
+
+// 全局组件挂载
+Vue.component('Pagination', Pagination)
+Vue.component('RightToolbar', RightToolbar)
+
+Vue.use(permission)
+
+/**
+ * If you don't want to use mock-server
+ * you want to use MockJs for mock api
+ * you can execute: mockXHR()
+ *
+ * Currently MockJs will be used in the production environment,
+ * please remove it before going online! ! !
+ */
+
+Vue.use(Element, {
+ size: Cookies.get('size') || 'medium' // set element-ui default size
+})
+
+Vue.config.productionTip = false
+
+new Vue({
+ el: '#app',
+ router,
+ store,
+ render: h => h(App)
+})
diff --git a/lab-booking-ui/src/permission.js b/lab-booking-ui/src/permission.js
new file mode 100644
index 0000000..5632e7d
--- /dev/null
+++ b/lab-booking-ui/src/permission.js
@@ -0,0 +1,52 @@
+import router from './router'
+import store from './store'
+import { Message } from 'element-ui'
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+import { getToken } from '@/utils/auth'
+
+NProgress.configure({ showSpinner: false })
+
+const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
+
+router.beforeEach((to, from, next) => {
+ NProgress.start()
+ if (getToken()) {
+ /* has token*/
+ if (to.path === '/login') {
+ next({ path: '/' })
+ NProgress.done()
+ } else {
+ if (store.getters.roles.length === 0) {
+ // 判断当前用户是否已拉取完user_info信息
+ store.dispatch('GetInfo').then(() => {
+ store.dispatch('GenerateRoutes').then(accessRoutes => {
+ // 根据roles权限生成可访问的路由表
+ router.addRoutes(accessRoutes) // 动态添加可访问路由表
+ next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
+ })
+ }).catch(err => {
+ store.dispatch('LogOut').then(() => {
+ Message.error(err)
+ next({ path: '/' })
+ })
+ })
+ } else {
+ next()
+ }
+ }
+ } else {
+ // 没有token
+ if (whiteList.indexOf(to.path) !== -1) {
+ // 在免登录白名单,直接进入
+ next()
+ } else {
+ next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
+ NProgress.done()
+ }
+ }
+})
+
+router.afterEach(() => {
+ NProgress.done()
+})
diff --git a/lab-booking-ui/src/router/index.js b/lab-booking-ui/src/router/index.js
new file mode 100644
index 0000000..ffb12e7
--- /dev/null
+++ b/lab-booking-ui/src/router/index.js
@@ -0,0 +1,128 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+
+Vue.use(Router)
+
+/* Layout */
+import Layout from '@/layout'
+import ParentView from '@/components/ParentView';
+
+/**
+ * Note: 路由配置项
+ *
+ * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
+ * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
+ * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
+ * // 若你想不管路由下面的 children 声明的个数都显示你的根路由
+ * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
+ * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
+ * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题
+ * meta : {
+ noCache: true // 如果设置为true,则不会被 缓存(默认 false)
+ title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
+ icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg
+ breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
+ }
+ */
+
+// 公共路由
+export const constantRoutes = [
+ {
+ path: '/redirect',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: '/redirect/:path(.*)',
+ component: (resolve) => require(['@/views/redirect'], resolve)
+ }
+ ]
+ },
+ {
+ path: '/login',
+ component: (resolve) => require(['@/views/login'], resolve),
+ hidden: true
+ },
+ {
+ path: '/404',
+ component: (resolve) => require(['@/views/error/404'], resolve),
+ hidden: true
+ },
+ {
+ path: '/401',
+ component: (resolve) => require(['@/views/error/401'], resolve),
+ hidden: true
+ },
+ {
+ path: '',
+ component: Layout,
+ redirect: 'index',
+ children: [
+ {
+ path: 'index',
+ component: (resolve) => require(['@/views/index'], resolve),
+ name: '首页',
+ meta: { title: '首页', icon: 'dashboard', noCache: true, affix: true }
+ }
+ ]
+ },
+ {
+ path: '/user',
+ component: Layout,
+ hidden: true,
+ redirect: 'noredirect',
+ children: [
+ {
+ path: 'profile',
+ component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
+ name: 'Profile',
+ meta: { title: '个人中心', icon: 'user' }
+ }
+ ]
+ },
+ {
+ path: '/dict',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'type/data/:dictId(\\d+)',
+ component: (resolve) => require(['@/views/system/dict/data'], resolve),
+ name: 'Data',
+ meta: { title: '字典数据', icon: '' }
+ }
+ ]
+ },
+ {
+ path: '/job',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'log',
+ component: (resolve) => require(['@/views/monitor/job/log'], resolve),
+ name: 'JobLog',
+ meta: { title: '调度日志' }
+ }
+ ]
+ },
+ {
+ path: '/gen',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'edit/:tableId(\\d+)',
+ component: (resolve) => require(['@/views/tool/gen/editTable'], resolve),
+ name: 'GenEdit',
+ meta: { title: '修改生成配置' }
+ }
+ ]
+ }
+]
+
+export default new Router({
+ mode: 'history', // 去掉url中的#
+ scrollBehavior: () => ({ y: 0 }),
+ routes: constantRoutes
+})
diff --git a/lab-booking-ui/src/settings.js b/lab-booking-ui/src/settings.js
new file mode 100644
index 0000000..f367740
--- /dev/null
+++ b/lab-booking-ui/src/settings.js
@@ -0,0 +1,41 @@
+module.exports = {
+ title: '实验室预约系统',
+
+ /**
+ * 侧边栏主题 深色主题theme-dark,浅色主题theme-light
+ */
+ sideTheme: 'theme-dark',
+
+ /**
+ * 是否系统布局配置
+ */
+ showSettings: false,
+
+ /**
+ * 是否显示顶部导航
+ */
+ topNav: false,
+
+ /**
+ * 是否显示 tagsView
+ */
+ tagsView: true,
+
+ /**
+ * 是否固定头部
+ */
+ fixedHeader: false,
+
+ /**
+ * 是否显示logo
+ */
+ sidebarLogo: true,
+
+ /**
+ * @type {string | array} 'production' | ['production', 'development']
+ * @description Need show err logs component.
+ * The default is only used in the production env
+ * If you want to also use it in dev, you can pass ['production', 'development']
+ */
+ errorLog: 'production'
+}
diff --git a/lab-booking-ui/src/store/getters.js b/lab-booking-ui/src/store/getters.js
new file mode 100644
index 0000000..f662b86
--- /dev/null
+++ b/lab-booking-ui/src/store/getters.js
@@ -0,0 +1,20 @@
+const getters = {
+ sidebar: state => state.app.sidebar,
+ size: state => state.app.size,
+ device: state => state.app.device,
+ visitedViews: state => state.tagsView.visitedViews,
+ cachedViews: state => state.tagsView.cachedViews,
+ token: state => state.user.token,
+ avatar: state => state.user.avatar,
+ name: state => state.user.name,
+ nickName: state => state.user.nickName,
+ phoneNumber: state => state.user.phoneNumber,
+ introduction: state => state.user.introduction,
+ roles: state => state.user.roles,
+ permissions: state => state.user.permissions,
+ permission_routes: state => state.permission.routes,
+ topbarRouters: state => state.permission.topbarRouters,
+ defaultRoutes: state => state.permission.defaultRoutes,
+ sidebarRouters: state => state.permission.sidebarRouters,
+}
+export default getters
diff --git a/lab-booking-ui/src/store/index.js b/lab-booking-ui/src/store/index.js
new file mode 100644
index 0000000..53b8437
--- /dev/null
+++ b/lab-booking-ui/src/store/index.js
@@ -0,0 +1,23 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import app from './modules/app'
+import user from './modules/user'
+import tagsView from './modules/tagsView'
+import permission from './modules/permission'
+import settings from './modules/settings'
+import getters from './getters'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+ modules: {
+ app,
+ user,
+ tagsView,
+ permission,
+ settings
+ },
+ getters
+})
+
+export default store
diff --git a/lab-booking-ui/src/store/modules/app.js b/lab-booking-ui/src/store/modules/app.js
new file mode 100644
index 0000000..45d89bb
--- /dev/null
+++ b/lab-booking-ui/src/store/modules/app.js
@@ -0,0 +1,56 @@
+import Cookies from 'js-cookie'
+
+const state = {
+ sidebar: {
+ opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
+ withoutAnimation: false
+ },
+ device: 'desktop',
+ size: Cookies.get('size') || 'medium'
+}
+
+const mutations = {
+ TOGGLE_SIDEBAR: state => {
+ state.sidebar.opened = !state.sidebar.opened
+ state.sidebar.withoutAnimation = false
+ if (state.sidebar.opened) {
+ Cookies.set('sidebarStatus', 1)
+ } else {
+ Cookies.set('sidebarStatus', 0)
+ }
+ },
+ CLOSE_SIDEBAR: (state, withoutAnimation) => {
+ Cookies.set('sidebarStatus', 0)
+ state.sidebar.opened = false
+ state.sidebar.withoutAnimation = withoutAnimation
+ },
+ TOGGLE_DEVICE: (state, device) => {
+ state.device = device
+ },
+ SET_SIZE: (state, size) => {
+ state.size = size
+ Cookies.set('size', size)
+ }
+}
+
+const actions = {
+ toggleSideBar({ commit }) {
+ commit('TOGGLE_SIDEBAR')
+ },
+ closeSideBar({ commit }, { withoutAnimation }) {
+ commit('CLOSE_SIDEBAR', withoutAnimation)
+ },
+ toggleDevice({ commit }, device) {
+ commit('TOGGLE_DEVICE', device)
+ },
+ setSize({ commit }, size) {
+ commit('SET_SIZE', size)
+ }
+}
+
+export default {
+ namespaced: true,
+ state,
+ mutations,
+ actions
+}
diff --git a/lab-booking-ui/src/store/modules/permission.js b/lab-booking-ui/src/store/modules/permission.js
new file mode 100644
index 0000000..aacfc8c
--- /dev/null
+++ b/lab-booking-ui/src/store/modules/permission.js
@@ -0,0 +1,110 @@
+import { constantRoutes } from '@/router'
+import { getRouters } from '@/api/menu'
+import Layout from '@/layout/index'
+import ParentView from '@/components/ParentView';
+
+const permission = {
+ state: {
+ routes: [],
+ addRoutes: [],
+ defaultRoutes: [],
+ topbarRouters: [],
+ sidebarRouters: []
+ },
+ mutations: {
+ SET_ROUTES: (state, routes) => {
+ state.addRoutes = routes
+ state.routes = constantRoutes.concat(routes)
+ },
+ SET_DEFAULT_ROUTES: (state, routes) => {
+ state.defaultRoutes = constantRoutes.concat(routes)
+ },
+ SET_TOPBAR_ROUTES: (state, routes) => {
+ // 顶部导航菜单默认添加统计报表栏指向首页
+ const index = [{
+ path: 'index',
+ meta: { title: '统计报表', icon: 'dashboard'}
+ }]
+ state.topbarRouters = routes.concat(index);
+ },
+ SET_SIDEBAR_ROUTERS: (state, routes) => {
+ state.sidebarRouters = routes
+ },
+ },
+ actions: {
+ // 生成路由
+ GenerateRoutes({ commit }) {
+ return new Promise(resolve => {
+ // 向后端请求路由数据
+ getRouters().then(res => {
+ const sdata = JSON.parse(JSON.stringify(res.data))
+ const rdata = JSON.parse(JSON.stringify(res.data))
+ const sidebarRoutes = filterAsyncRouter(sdata)
+ const rewriteRoutes = filterAsyncRouter(rdata, false, true)
+ rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
+ commit('SET_ROUTES', rewriteRoutes)
+ commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
+ commit('SET_DEFAULT_ROUTES', sidebarRoutes)
+ commit('SET_TOPBAR_ROUTES', sidebarRoutes)
+ resolve(rewriteRoutes)
+ })
+ })
+ }
+ }
+}
+
+// 遍历后台传来的路由字符串,转换为组件对象
+function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
+ return asyncRouterMap.filter(route => {
+ if (type && route.children) {
+ route.children = filterChildren(route.children)
+ }
+ if (route.component) {
+ // Layout ParentView 组件特殊处理
+ if (route.component === 'Layout') {
+ route.component = Layout
+ } else if (route.component === 'ParentView') {
+ route.component = ParentView
+ } else {
+ route.component = loadView(route.component)
+ }
+ }
+ if (route.children != null && route.children && route.children.length) {
+ route.children = filterAsyncRouter(route.children, route, type)
+ } else {
+ delete route['children']
+ delete route['redirect']
+ }
+ return true
+ })
+}
+
+function filterChildren(childrenMap, lastRouter = false) {
+ var children = []
+ childrenMap.forEach((el, index) => {
+ if (el.children && el.children.length) {
+ if (el.component === 'ParentView') {
+ el.children.forEach(c => {
+ c.path = el.path + '/' + c.path
+ if (c.children && c.children.length) {
+ children = children.concat(filterChildren(c.children, c))
+ return
+ }
+ children.push(c)
+ })
+ return
+ }
+ }
+ if (lastRouter) {
+ el.path = lastRouter.path + '/' + el.path
+ }
+ children = children.concat(el)
+ })
+ return children
+}
+
+export const loadView = (view) => { // 路由懒加载
+ return (resolve) => require([`@/views/${view}`], resolve)
+}
+
+export default permission
diff --git a/lab-booking-ui/src/store/modules/settings.js b/lab-booking-ui/src/store/modules/settings.js
new file mode 100644
index 0000000..93f7c04
--- /dev/null
+++ b/lab-booking-ui/src/store/modules/settings.js
@@ -0,0 +1,36 @@
+import variables from '@/assets/styles/element-variables.scss'
+import defaultSettings from '@/settings'
+
+const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo } = defaultSettings
+
+const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
+const state = {
+ theme: storageSetting.theme || variables.theme,
+ sideTheme: storageSetting.sideTheme || sideTheme,
+ showSettings: showSettings,
+ topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
+ tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
+ fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
+ sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo
+}
+const mutations = {
+ CHANGE_SETTING: (state, { key, value }) => {
+ if (state.hasOwnProperty(key)) {
+ state[key] = value
+ }
+ }
+}
+
+const actions = {
+ changeSetting({ commit }, data) {
+ commit('CHANGE_SETTING', data)
+ }
+}
+
+export default {
+ namespaced: true,
+ state,
+ mutations,
+ actions
+}
+
diff --git a/lab-booking-ui/src/store/modules/tagsView.js b/lab-booking-ui/src/store/modules/tagsView.js
new file mode 100644
index 0000000..a6cfdd9
--- /dev/null
+++ b/lab-booking-ui/src/store/modules/tagsView.js
@@ -0,0 +1,183 @@
+const state = {
+ visitedViews: [],
+ cachedViews: []
+}
+
+const mutations = {
+ ADD_VISITED_VIEW: (state, view) => {
+ if (state.visitedViews.some(v => v.path === view.path)) return
+ state.visitedViews.push(
+ Object.assign({}, view, {
+ title: view.meta.title || 'no-name'
+ })
+ )
+ },
+ ADD_CACHED_VIEW: (state, view) => {
+ if (state.cachedViews.includes(view.name)) return
+ if (!view.meta.noCache) {
+ state.cachedViews.push(view.name)
+ }
+ },
+
+ DEL_VISITED_VIEW: (state, view) => {
+ for (const [i, v] of state.visitedViews.entries()) {
+ if (v.path === view.path) {
+ state.visitedViews.splice(i, 1)
+ break
+ }
+ }
+ },
+ DEL_CACHED_VIEW: (state, view) => {
+ const index = state.cachedViews.indexOf(view.name)
+ index > -1 && state.cachedViews.splice(index, 1)
+ },
+
+ DEL_OTHERS_VISITED_VIEWS: (state, view) => {
+ state.visitedViews = state.visitedViews.filter(v => {
+ return v.meta.affix || v.path === view.path
+ })
+ },
+ DEL_OTHERS_CACHED_VIEWS: (state, view) => {
+ const index = state.cachedViews.indexOf(view.name)
+ if (index > -1) {
+ state.cachedViews = state.cachedViews.slice(index, index + 1)
+ } else {
+ state.cachedViews = []
+ }
+ },
+
+ DEL_ALL_VISITED_VIEWS: state => {
+ // keep affix tags
+ const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
+ state.visitedViews = affixTags
+ },
+ DEL_ALL_CACHED_VIEWS: state => {
+ state.cachedViews = []
+ },
+
+ UPDATE_VISITED_VIEW: (state, view) => {
+ for (let v of state.visitedViews) {
+ if (v.path === view.path) {
+ v = Object.assign(v, view)
+ break
+ }
+ }
+ },
+
+ DEL_RIGHT_VIEWS: (state, view) => {
+ const index = state.visitedViews.findIndex(v => v.path === view.path)
+ if (index === -1) {
+ return
+ }
+ state.visitedViews = state.visitedViews.filter((item, idx) => {
+ if (idx <= index || (item.meta && item.meta.affix)) {
+ return true
+ }
+ const i = state.cachedViews.indexOf(item.name)
+ if (i > -1) {
+ state.cachedViews.splice(i, 1)
+ }
+ return false
+ })
+ }
+}
+
+const actions = {
+ addView({ dispatch }, view) {
+ dispatch('addVisitedView', view)
+ dispatch('addCachedView', view)
+ },
+ addVisitedView({ commit }, view) {
+ commit('ADD_VISITED_VIEW', view)
+ },
+ addCachedView({ commit }, view) {
+ commit('ADD_CACHED_VIEW', view)
+ },
+
+ delView({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delVisitedView', view)
+ dispatch('delCachedView', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delVisitedView({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_VISITED_VIEW', view)
+ resolve([...state.visitedViews])
+ })
+ },
+ delCachedView({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_CACHED_VIEW', view)
+ resolve([...state.cachedViews])
+ })
+ },
+
+ delOthersViews({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delOthersVisitedViews', view)
+ dispatch('delOthersCachedViews', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delOthersVisitedViews({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_OTHERS_VISITED_VIEWS', view)
+ resolve([...state.visitedViews])
+ })
+ },
+ delOthersCachedViews({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_OTHERS_CACHED_VIEWS', view)
+ resolve([...state.cachedViews])
+ })
+ },
+
+ delAllViews({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delAllVisitedViews', view)
+ dispatch('delAllCachedViews', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delAllVisitedViews({ commit, state }) {
+ return new Promise(resolve => {
+ commit('DEL_ALL_VISITED_VIEWS')
+ resolve([...state.visitedViews])
+ })
+ },
+ delAllCachedViews({ commit, state }) {
+ return new Promise(resolve => {
+ commit('DEL_ALL_CACHED_VIEWS')
+ resolve([...state.cachedViews])
+ })
+ },
+
+ updateVisitedView({ commit }, view) {
+ commit('UPDATE_VISITED_VIEW', view)
+ },
+
+ delRightTags({ commit }, view) {
+ return new Promise(resolve => {
+ commit('DEL_RIGHT_VIEWS', view)
+ resolve([...state.visitedViews])
+ })
+ }
+}
+
+export default {
+ namespaced: true,
+ state,
+ mutations,
+ actions
+}
diff --git a/lab-booking-ui/src/store/modules/user.js b/lab-booking-ui/src/store/modules/user.js
new file mode 100644
index 0000000..6d32370
--- /dev/null
+++ b/lab-booking-ui/src/store/modules/user.js
@@ -0,0 +1,106 @@
+import { login, logout, getInfo } from '@/api/login'
+import { getToken, setToken, removeToken } from '@/utils/auth'
+
+const user = {
+ state: {
+ token: getToken(),
+ name: '',
+ nickName: '',
+ phoneNumber: '',
+ avatar: '',
+ roles: [],
+ permissions: []
+ },
+
+ mutations: {
+ SET_TOKEN: (state, token) => {
+ state.token = token
+ },
+ SET_NAME: (state, name) => {
+ state.name = name
+ },
+ SET_NICKNAME: (state, nickName) => {
+ state.nickName = nickName
+ },
+ SET_PHONENUMBER: (state, phoneNumber) => {
+ state.phoneNumber = phoneNumber
+ },
+ SET_AVATAR: (state, avatar) => {
+ state.avatar = avatar
+ },
+ SET_ROLES: (state, roles) => {
+ state.roles = roles
+ },
+ SET_PERMISSIONS: (state, permissions) => {
+ state.permissions = permissions
+ }
+ },
+
+ actions: {
+ // 登录
+ Login({ commit }, userInfo) {
+ const username = userInfo.username.trim()
+ const password = userInfo.password
+ const code = userInfo.code
+ const uuid = userInfo.uuid
+ return new Promise((resolve, reject) => {
+ login(username, password, code, uuid).then(res => {
+ setToken(res.token)
+ commit('SET_TOKEN', res.token)
+ resolve()
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+
+ // 获取用户信息
+ GetInfo({ commit, state }) {
+ return new Promise((resolve, reject) => {
+ getInfo().then(res => {
+ const user = res.user
+ const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
+ if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
+ commit('SET_ROLES', res.roles)
+ commit('SET_PERMISSIONS', res.permissions)
+ } else {
+ commit('SET_ROLES', ['ROLE_DEFAULT'])
+ }
+ commit('SET_NAME', user.userName)
+ commit('SET_NICKNAME', user.nickName)
+ commit('SET_PHONENUMBER', user.phonenumber)
+ commit('SET_AVATAR', avatar)
+ resolve(res)
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+
+ // 退出系统
+ LogOut({ commit, state }) {
+ return new Promise((resolve, reject) => {
+ logout(state.token).then(() => {
+ commit('SET_TOKEN', '')
+ commit('SET_ROLES', [])
+ commit('SET_PERMISSIONS', [])
+ removeToken()
+ resolve()
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+
+ // 前端 登出
+ FedLogOut({ commit }) {
+ return new Promise(resolve => {
+ commit('SET_TOKEN', '')
+ removeToken()
+ resolve()
+ })
+ }
+ }
+}
+
+export default user
diff --git a/lab-booking-ui/src/utils/auth.js b/lab-booking-ui/src/utils/auth.js
new file mode 100644
index 0000000..08a43d6
--- /dev/null
+++ b/lab-booking-ui/src/utils/auth.js
@@ -0,0 +1,15 @@
+import Cookies from 'js-cookie'
+
+const TokenKey = 'Admin-Token'
+
+export function getToken() {
+ return Cookies.get(TokenKey)
+}
+
+export function setToken(token) {
+ return Cookies.set(TokenKey, token)
+}
+
+export function removeToken() {
+ return Cookies.remove(TokenKey)
+}
diff --git a/lab-booking-ui/src/utils/errorCode.js b/lab-booking-ui/src/utils/errorCode.js
new file mode 100644
index 0000000..d2111ee
--- /dev/null
+++ b/lab-booking-ui/src/utils/errorCode.js
@@ -0,0 +1,6 @@
+export default {
+ '401': '认证失败,无法访问系统资源',
+ '403': '当前操作没有权限',
+ '404': '访问资源不存在',
+ 'default': '系统未知错误,请反馈给管理员'
+}
diff --git a/lab-booking-ui/src/utils/generator/config.js b/lab-booking-ui/src/utils/generator/config.js
new file mode 100644
index 0000000..7abf227
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/config.js
@@ -0,0 +1,438 @@
+export const formConf = {
+ formRef: 'elForm',
+ formModel: 'formData',
+ size: 'medium',
+ labelPosition: 'right',
+ labelWidth: 100,
+ formRules: 'rules',
+ gutter: 15,
+ disabled: false,
+ span: 24,
+ formBtns: true
+}
+
+export const inputComponents = [
+ {
+ label: '单行文本',
+ tag: 'el-input',
+ tagIcon: 'input',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': '',
+ 'suffix-icon': '',
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '多行文本',
+ tag: 'el-input',
+ tagIcon: 'textarea',
+ type: 'textarea',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ autosize: {
+ minRows: 4,
+ maxRows: 4
+ },
+ style: { width: '100%' },
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '密码',
+ tag: 'el-input',
+ tagIcon: 'password',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ 'show-password': true,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': '',
+ 'suffix-icon': '',
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '计数器',
+ tag: 'el-input-number',
+ tagIcon: 'number',
+ placeholder: '',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ min: undefined,
+ max: undefined,
+ step: undefined,
+ 'step-strictly': false,
+ precision: undefined,
+ 'controls-position': '',
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input-number'
+ }
+]
+
+export const selectComponents = [
+ {
+ label: '下拉选择',
+ tag: 'el-select',
+ tagIcon: 'select',
+ placeholder: '请选择',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ disabled: false,
+ required: true,
+ filterable: false,
+ multiple: false,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/select'
+ },
+ {
+ label: '级联选择',
+ tag: 'el-cascader',
+ tagIcon: 'cascader',
+ placeholder: '请选择',
+ defaultValue: [],
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ props: {
+ props: {
+ multiple: false
+ }
+ },
+ 'show-all-levels': true,
+ disabled: false,
+ clearable: true,
+ filterable: false,
+ required: true,
+ options: [{
+ id: 1,
+ value: 1,
+ label: '选项1',
+ children: [{
+ id: 2,
+ value: 2,
+ label: '选项1-1'
+ }]
+ }],
+ dataType: 'dynamic',
+ labelKey: 'label',
+ valueKey: 'value',
+ childrenKey: 'children',
+ separator: '/',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/cascader'
+ },
+ {
+ label: '单选框组',
+ tag: 'el-radio-group',
+ tagIcon: 'radio',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ optionType: 'default',
+ border: false,
+ size: 'medium',
+ disabled: false,
+ required: true,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/radio'
+ },
+ {
+ label: '多选框组',
+ tag: 'el-checkbox-group',
+ tagIcon: 'checkbox',
+ defaultValue: [],
+ span: 24,
+ labelWidth: null,
+ style: {},
+ optionType: 'default',
+ border: false,
+ size: 'medium',
+ disabled: false,
+ required: true,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'
+ },
+ {
+ label: '开关',
+ tag: 'el-switch',
+ tagIcon: 'switch',
+ defaultValue: false,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ disabled: false,
+ required: true,
+ 'active-text': '',
+ 'inactive-text': '',
+ 'active-color': null,
+ 'inactive-color': null,
+ 'active-value': true,
+ 'inactive-value': false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/switch'
+ },
+ {
+ label: '滑块',
+ tag: 'el-slider',
+ tagIcon: 'slider',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ disabled: false,
+ required: true,
+ min: 0,
+ max: 100,
+ step: 1,
+ 'show-stops': false,
+ range: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/slider'
+ },
+ {
+ label: '时间选择',
+ tag: 'el-time-picker',
+ tagIcon: 'time',
+ placeholder: '请选择',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ 'picker-options': {
+ selectableRange: '00:00:00-23:59:59'
+ },
+ format: 'HH:mm:ss',
+ 'value-format': 'HH:mm:ss',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
+ },
+ {
+ label: '时间范围',
+ tag: 'el-time-picker',
+ tagIcon: 'time-range',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ 'is-range': true,
+ 'range-separator': '至',
+ 'start-placeholder': '开始时间',
+ 'end-placeholder': '结束时间',
+ format: 'HH:mm:ss',
+ 'value-format': 'HH:mm:ss',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
+ },
+ {
+ label: '日期选择',
+ tag: 'el-date-picker',
+ tagIcon: 'date',
+ placeholder: '请选择',
+ defaultValue: null,
+ type: 'date',
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ format: 'yyyy-MM-dd',
+ 'value-format': 'yyyy-MM-dd',
+ readonly: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
+ },
+ {
+ label: '日期范围',
+ tag: 'el-date-picker',
+ tagIcon: 'date-range',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ type: 'daterange',
+ 'range-separator': '至',
+ 'start-placeholder': '开始日期',
+ 'end-placeholder': '结束日期',
+ disabled: false,
+ clearable: true,
+ required: true,
+ format: 'yyyy-MM-dd',
+ 'value-format': 'yyyy-MM-dd',
+ readonly: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
+ },
+ {
+ label: '评分',
+ tag: 'el-rate',
+ tagIcon: 'rate',
+ defaultValue: 0,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ max: 5,
+ 'allow-half': false,
+ 'show-text': false,
+ 'show-score': false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/rate'
+ },
+ {
+ label: '颜色选择',
+ tag: 'el-color-picker',
+ tagIcon: 'color',
+ defaultValue: null,
+ labelWidth: null,
+ 'show-alpha': false,
+ 'color-format': '',
+ disabled: false,
+ required: true,
+ size: 'medium',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'
+ },
+ {
+ label: '上传',
+ tag: 'el-upload',
+ tagIcon: 'upload',
+ action: 'https://jsonplaceholder.typicode.com/posts/',
+ defaultValue: null,
+ labelWidth: null,
+ disabled: false,
+ required: true,
+ accept: '',
+ name: 'file',
+ 'auto-upload': true,
+ showTip: false,
+ buttonText: '点击上传',
+ fileSize: 2,
+ sizeUnit: 'MB',
+ 'list-type': 'text',
+ multiple: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/upload'
+ }
+]
+
+export const layoutComponents = [
+ {
+ layout: 'rowFormItem',
+ tagIcon: 'row',
+ type: 'default',
+ justify: 'start',
+ align: 'top',
+ label: '行容器',
+ layoutTree: true,
+ children: [],
+ document: 'https://element.eleme.cn/#/zh-CN/component/layout'
+ },
+ {
+ layout: 'colFormItem',
+ label: '按钮',
+ changeTag: true,
+ labelWidth: null,
+ tag: 'el-button',
+ tagIcon: 'button',
+ span: 24,
+ default: '主要按钮',
+ type: 'primary',
+ icon: 'el-icon-search',
+ size: 'medium',
+ disabled: false,
+ document: 'https://element.eleme.cn/#/zh-CN/component/button'
+ }
+]
+
+// 组件rule的触发方式,无触发方式的组件不生成rule
+export const trigger = {
+ 'el-input': 'blur',
+ 'el-input-number': 'blur',
+ 'el-select': 'change',
+ 'el-radio-group': 'change',
+ 'el-checkbox-group': 'change',
+ 'el-cascader': 'change',
+ 'el-time-picker': 'change',
+ 'el-date-picker': 'change',
+ 'el-rate': 'change'
+}
diff --git a/lab-booking-ui/src/utils/generator/css.js b/lab-booking-ui/src/utils/generator/css.js
new file mode 100644
index 0000000..c1c62e6
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/css.js
@@ -0,0 +1,18 @@
+const styles = {
+ 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
+ 'el-upload': '.el-upload__tip{line-height: 1.2;}'
+}
+
+function addCss(cssList, el) {
+ const css = styles[el.tag]
+ css && cssList.indexOf(css) === -1 && cssList.push(css)
+ if (el.children) {
+ el.children.forEach(el2 => addCss(cssList, el2))
+ }
+}
+
+export function makeUpCss(conf) {
+ const cssList = []
+ conf.fields.forEach(el => addCss(cssList, el))
+ return cssList.join('\n')
+}
diff --git a/lab-booking-ui/src/utils/generator/drawingDefalut.js b/lab-booking-ui/src/utils/generator/drawingDefalut.js
new file mode 100644
index 0000000..09f133c
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/drawingDefalut.js
@@ -0,0 +1,29 @@
+export default [
+ {
+ layout: 'colFormItem',
+ tagIcon: 'input',
+ label: '手机号',
+ vModel: 'mobile',
+ formId: 6,
+ tag: 'el-input',
+ placeholder: '请输入手机号',
+ defaultValue: '',
+ span: 24,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': 'el-icon-mobile',
+ 'suffix-icon': '',
+ maxlength: 11,
+ 'show-word-limit': true,
+ readonly: false,
+ disabled: false,
+ required: true,
+ changeTag: true,
+ regList: [{
+ pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
+ message: '手机号格式错误'
+ }]
+ }
+]
diff --git a/lab-booking-ui/src/utils/generator/html.js b/lab-booking-ui/src/utils/generator/html.js
new file mode 100644
index 0000000..ebf628d
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/html.js
@@ -0,0 +1,359 @@
+/* eslint-disable max-len */
+import { trigger } from './config'
+
+let confGlobal
+let someSpanIsNot24
+
+export function dialogWrapper(str) {
+ return `
+ ${str}
+
+ 取消
+ 确定
+
+ `
+}
+
+export function vueTemplate(str) {
+ return `
+
+ ${str}
+
+ `
+}
+
+export function vueScript(str) {
+ return ``
+}
+
+export function cssStyle(cssStr) {
+ return ``
+}
+
+function buildFormTemplate(conf, child, type) {
+ let labelPosition = ''
+ if (conf.labelPosition !== 'right') {
+ labelPosition = `label-position="${conf.labelPosition}"`
+ }
+ const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
+ let str = `
+ ${child}
+ ${buildFromBtns(conf, type)}
+ `
+ if (someSpanIsNot24) {
+ str = `
+ ${str}
+ `
+ }
+ return str
+}
+
+function buildFromBtns(conf, type) {
+ let str = ''
+ if (conf.formBtns && type === 'file') {
+ str = `
+ 提交
+ 重置
+ `
+ if (someSpanIsNot24) {
+ str = `
+ ${str}
+ `
+ }
+ }
+ return str
+}
+
+// span不为24的用el-col包裹
+function colWrapper(element, str) {
+ if (someSpanIsNot24 || element.span !== 24) {
+ return `
+ ${str}
+ `
+ }
+ return str
+}
+
+const layouts = {
+ colFormItem(element) {
+ let labelWidth = ''
+ if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
+ labelWidth = `label-width="${element.labelWidth}px"`
+ }
+ const required = !trigger[element.tag] && element.required ? 'required' : ''
+ const tagDom = tags[element.tag] ? tags[element.tag](element) : null
+ let str = `
+ ${tagDom}
+ `
+ str = colWrapper(element, str)
+ return str
+ },
+ rowFormItem(element) {
+ const type = element.type === 'default' ? '' : `type="${element.type}"`
+ const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
+ const align = element.type === 'default' ? '' : `align="${element.align}"`
+ const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
+ const children = element.children.map(el => layouts[el.layout](el))
+ let str = `
+ ${children.join('\n')}
+ `
+ str = colWrapper(element, str)
+ return str
+ }
+}
+
+const tags = {
+ 'el-button': el => {
+ const {
+ tag, disabled
+ } = attrBuilder(el)
+ const type = el.type ? `type="${el.type}"` : ''
+ const icon = el.icon ? `icon="${el.icon}"` : ''
+ const size = el.size ? `size="${el.size}"` : ''
+ let child = buildElButtonChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}${el.tag}>`
+ },
+ 'el-input': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
+ const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
+ const readonly = el.readonly ? 'readonly' : ''
+ const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
+ const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
+ const showPassword = el['show-password'] ? 'show-password' : ''
+ const type = el.type ? `type="${el.type}"` : ''
+ const autosize = el.autosize && el.autosize.minRows
+ ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
+ : ''
+ let child = buildElInputChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}${el.tag}>`
+ },
+ 'el-input-number': el => {
+ const { disabled, vModel, placeholder } = attrBuilder(el)
+ const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
+ const min = el.min ? `:min='${el.min}'` : ''
+ const max = el.max ? `:max='${el.max}'` : ''
+ const step = el.step ? `:step='${el.step}'` : ''
+ const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
+ const precision = el.precision ? `:precision='${el.precision}'` : ''
+
+ return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>${el.tag}>`
+ },
+ 'el-select': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const filterable = el.filterable ? 'filterable' : ''
+ const multiple = el.multiple ? 'multiple' : ''
+ let child = buildElSelectChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}${el.tag}>`
+ },
+ 'el-radio-group': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ let child = buildElRadioGroupChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${size} ${disabled}>${child}${el.tag}>`
+ },
+ 'el-checkbox-group': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ const min = el.min ? `:min="${el.min}"` : ''
+ const max = el.max ? `:max="${el.max}"` : ''
+ let child = buildElCheckboxGroupChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}${el.tag}>`
+ },
+ 'el-switch': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
+ const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
+ const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
+ const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
+ const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
+ const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
+
+ return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>${el.tag}>`
+ },
+ 'el-cascader': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const options = el.options ? `:options="${el.vModel}Options"` : ''
+ const props = el.props ? `:props="${el.vModel}Props"` : ''
+ const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
+ const filterable = el.filterable ? 'filterable' : ''
+ const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
+
+ return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>${el.tag}>`
+ },
+ 'el-slider': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const min = el.min ? `:min='${el.min}'` : ''
+ const max = el.max ? `:max='${el.max}'` : ''
+ const step = el.step ? `:step='${el.step}'` : ''
+ const range = el.range ? 'range' : ''
+ const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
+
+ return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>${el.tag}>`
+ },
+ 'el-time-picker': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+ const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+ const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+ const isRange = el['is-range'] ? 'is-range' : ''
+ const format = el.format ? `format="${el.format}"` : ''
+ const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+ const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
+
+ return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>${el.tag}>`
+ },
+ 'el-date-picker': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+ const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+ const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+ const format = el.format ? `format="${el.format}"` : ''
+ const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+ const type = el.type === 'date' ? '' : `type="${el.type}"`
+ const readonly = el.readonly ? 'readonly' : ''
+
+ return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>${el.tag}>`
+ },
+ 'el-rate': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const max = el.max ? `:max='${el.max}'` : ''
+ const allowHalf = el['allow-half'] ? 'allow-half' : ''
+ const showText = el['show-text'] ? 'show-text' : ''
+ const showScore = el['show-score'] ? 'show-score' : ''
+
+ return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>${el.tag}>`
+ },
+ 'el-color-picker': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
+ const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
+
+ return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>${el.tag}>`
+ },
+ 'el-upload': el => {
+ const disabled = el.disabled ? ':disabled=\'true\'' : ''
+ const action = el.action ? `:action="${el.vModel}Action"` : ''
+ const multiple = el.multiple ? 'multiple' : ''
+ const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
+ const accept = el.accept ? `accept="${el.accept}"` : ''
+ const name = el.name !== 'file' ? `name="${el.name}"` : ''
+ const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
+ const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
+ const fileList = `:file-list="${el.vModel}fileList"`
+ const ref = `ref="${el.vModel}"`
+ let child = buildElUploadChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}${el.tag}>`
+ }
+}
+
+function attrBuilder(el) {
+ return {
+ vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
+ clearable: el.clearable ? 'clearable' : '',
+ placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
+ width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
+ disabled: el.disabled ? ':disabled=\'true\'' : ''
+ }
+}
+
+// el-buttin 子级
+function buildElButtonChild(conf) {
+ const children = []
+ if (conf.default) {
+ children.push(conf.default)
+ }
+ return children.join('\n')
+}
+
+// el-input innerHTML
+function buildElInputChild(conf) {
+ const children = []
+ if (conf.prepend) {
+ children.push(`${conf.prepend} `)
+ }
+ if (conf.append) {
+ children.push(`${conf.append} `)
+ }
+ return children.join('\n')
+}
+
+function buildElSelectChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ children.push(` `)
+ }
+ return children.join('\n')
+}
+
+function buildElRadioGroupChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
+ const border = conf.border ? 'border' : ''
+ children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}${tag}>`)
+ }
+ return children.join('\n')
+}
+
+function buildElCheckboxGroupChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
+ const border = conf.border ? 'border' : ''
+ children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}${tag}>`)
+ }
+ return children.join('\n')
+}
+
+function buildElUploadChild(conf) {
+ const list = []
+ if (conf['list-type'] === 'picture-card') list.push(' ')
+ else list.push(`${conf.buttonText} `)
+ if (conf.showTip) list.push(`只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`)
+ return list.join('\n')
+}
+
+export function makeUpHtml(conf, type) {
+ const htmlList = []
+ confGlobal = conf
+ someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
+ conf.fields.forEach(el => {
+ htmlList.push(layouts[el.layout](el))
+ })
+ const htmlStr = htmlList.join('\n')
+
+ let temp = buildFormTemplate(conf, htmlStr, type)
+ if (type === 'dialog') {
+ temp = dialogWrapper(temp)
+ }
+ confGlobal = null
+ return temp
+}
diff --git a/lab-booking-ui/src/utils/generator/icon.json b/lab-booking-ui/src/utils/generator/icon.json
new file mode 100644
index 0000000..2d9999a
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/icon.json
@@ -0,0 +1 @@
+["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"]
\ No newline at end of file
diff --git a/lab-booking-ui/src/utils/generator/js.js b/lab-booking-ui/src/utils/generator/js.js
new file mode 100644
index 0000000..c6c06a9
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/js.js
@@ -0,0 +1,236 @@
+import { isArray } from 'util'
+import { exportDefault, titleCase } from '@/utils/index'
+import { trigger } from './config'
+
+const units = {
+ KB: '1024',
+ MB: '1024 / 1024',
+ GB: '1024 / 1024 / 1024'
+}
+let confGlobal
+const inheritAttrs = {
+ file: '',
+ dialog: 'inheritAttrs: false,'
+}
+
+
+export function makeUpJs(conf, type) {
+ confGlobal = conf = JSON.parse(JSON.stringify(conf))
+ const dataList = []
+ const ruleList = []
+ const optionsList = []
+ const propsList = []
+ const methodList = mixinMethod(type)
+ const uploadVarList = []
+
+ conf.fields.forEach(el => {
+ buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+ })
+
+ const script = buildexport(
+ conf,
+ type,
+ dataList.join('\n'),
+ ruleList.join('\n'),
+ optionsList.join('\n'),
+ uploadVarList.join('\n'),
+ propsList.join('\n'),
+ methodList.join('\n')
+ )
+ confGlobal = null
+ return script
+}
+
+function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
+ buildData(el, dataList)
+ buildRules(el, ruleList)
+
+ if (el.options && el.options.length) {
+ buildOptions(el, optionsList)
+ if (el.dataType === 'dynamic') {
+ const model = `${el.vModel}Options`
+ const options = titleCase(model)
+ buildOptionMethod(`get${options}`, model, methodList)
+ }
+ }
+
+ if (el.props && el.props.props) {
+ buildProps(el, propsList)
+ }
+
+ if (el.action && el.tag === 'el-upload') {
+ uploadVarList.push(
+ `${el.vModel}Action: '${el.action}',
+ ${el.vModel}fileList: [],`
+ )
+ methodList.push(buildBeforeUpload(el))
+ if (!el['auto-upload']) {
+ methodList.push(buildSubmitUpload(el))
+ }
+ }
+
+ if (el.children) {
+ el.children.forEach(el2 => {
+ buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+ })
+ }
+}
+
+function mixinMethod(type) {
+ const list = []; const
+ minxins = {
+ file: confGlobal.formBtns ? {
+ submitForm: `submitForm() {
+ this.$refs['${confGlobal.formRef}'].validate(valid => {
+ if(!valid) return
+ // TODO 提交表单
+ })
+ },`,
+ resetForm: `resetForm() {
+ this.$refs['${confGlobal.formRef}'].resetFields()
+ },`
+ } : null,
+ dialog: {
+ onOpen: 'onOpen() {},',
+ onClose: `onClose() {
+ this.$refs['${confGlobal.formRef}'].resetFields()
+ },`,
+ close: `close() {
+ this.$emit('update:visible', false)
+ },`,
+ handelConfirm: `handelConfirm() {
+ this.$refs['${confGlobal.formRef}'].validate(valid => {
+ if(!valid) return
+ this.close()
+ })
+ },`
+ }
+ }
+
+ const methods = minxins[type]
+ if (methods) {
+ Object.keys(methods).forEach(key => {
+ list.push(methods[key])
+ })
+ }
+
+ return list
+}
+
+function buildData(conf, dataList) {
+ if (conf.vModel === undefined) return
+ let defaultValue
+ if (typeof (conf.defaultValue) === 'string' && !conf.multiple) {
+ defaultValue = `'${conf.defaultValue}'`
+ } else {
+ defaultValue = `${JSON.stringify(conf.defaultValue)}`
+ }
+ dataList.push(`${conf.vModel}: ${defaultValue},`)
+}
+
+function buildRules(conf, ruleList) {
+ if (conf.vModel === undefined) return
+ const rules = []
+ if (trigger[conf.tag]) {
+ if (conf.required) {
+ const type = isArray(conf.defaultValue) ? 'type: \'array\',' : ''
+ let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder
+ if (message === undefined) message = `${conf.label}不能为空`
+ rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`)
+ }
+ if (conf.regList && isArray(conf.regList)) {
+ conf.regList.forEach(item => {
+ if (item.pattern) {
+ rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`)
+ }
+ })
+ }
+ ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)
+ }
+}
+
+function buildOptions(conf, optionsList) {
+ if (conf.vModel === undefined) return
+ if (conf.dataType === 'dynamic') { conf.options = [] }
+ const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},`
+ optionsList.push(str)
+}
+
+function buildProps(conf, propsList) {
+ if (conf.dataType === 'dynamic') {
+ conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
+ conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
+ conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)
+ }
+ const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`
+ propsList.push(str)
+}
+
+function buildBeforeUpload(conf) {
+ const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const
+ returnList = []
+ if (conf.fileSize) {
+ rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}
+ if(!isRightSize){
+ this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')
+ }`
+ returnList.push('isRightSize')
+ }
+ if (conf.accept) {
+ acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)
+ if(!isAccept){
+ this.$message.error('应该选择${conf.accept}类型的文件')
+ }`
+ returnList.push('isAccept')
+ }
+ const str = `${conf.vModel}BeforeUpload(file) {
+ ${rightSizeCode}
+ ${acceptCode}
+ return ${returnList.join('&&')}
+ },`
+ return returnList.length ? str : ''
+}
+
+function buildSubmitUpload(conf) {
+ const str = `submitUpload() {
+ this.$refs['${conf.vModel}'].submit()
+ },`
+ return str
+}
+
+function buildOptionMethod(methodName, model, methodList) {
+ const str = `${methodName}() {
+ // TODO 发起请求获取数据
+ this.${model}
+ },`
+ methodList.push(str)
+}
+
+function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
+ const str = `${exportDefault}{
+ ${inheritAttrs[type]}
+ components: {},
+ props: [],
+ data () {
+ return {
+ ${conf.formModel}: {
+ ${data}
+ },
+ ${conf.formRules}: {
+ ${rules}
+ },
+ ${uploadVar}
+ ${selectOptions}
+ ${props}
+ }
+ },
+ computed: {},
+ watch: {},
+ created () {},
+ mounted () {},
+ methods: {
+ ${methods}
+ }
+}`
+ return str
+}
diff --git a/lab-booking-ui/src/utils/generator/render.js b/lab-booking-ui/src/utils/generator/render.js
new file mode 100644
index 0000000..e8640f0
--- /dev/null
+++ b/lab-booking-ui/src/utils/generator/render.js
@@ -0,0 +1,126 @@
+import { makeMap } from '@/utils/index'
+
+// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js
+const isAttr = makeMap(
+ 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,'
+ + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,'
+ + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,'
+ + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,'
+ + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,'
+ + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,'
+ + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,'
+ + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,'
+ + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,'
+ + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,'
+ + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,'
+ + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,'
+ + 'target,title,type,usemap,value,width,wrap'
+)
+
+function vModel(self, dataObject, defaultValue) {
+ dataObject.props.value = defaultValue
+
+ dataObject.on.input = val => {
+ self.$emit('input', val)
+ }
+}
+
+const componentChild = {
+ 'el-button': {
+ default(h, conf, key) {
+ return conf[key]
+ },
+ },
+ 'el-input': {
+ prepend(h, conf, key) {
+ return {conf[key]}
+ },
+ append(h, conf, key) {
+ return {conf[key]}
+ }
+ },
+ 'el-select': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ list.push( )
+ })
+ return list
+ }
+ },
+ 'el-radio-group': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ if (conf.optionType === 'button') list.push({item.label} )
+ else list.push({item.label} )
+ })
+ return list
+ }
+ },
+ 'el-checkbox-group': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ if (conf.optionType === 'button') {
+ list.push({item.label} )
+ } else {
+ list.push({item.label} )
+ }
+ })
+ return list
+ }
+ },
+ 'el-upload': {
+ 'list-type': (h, conf, key) => {
+ const list = []
+ if (conf['list-type'] === 'picture-card') {
+ list.push( )
+ } else {
+ list.push({conf.buttonText} )
+ }
+ if (conf.showTip) {
+ list.push(只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
)
+ }
+ return list
+ }
+ }
+}
+
+export default {
+ render(h) {
+ const dataObject = {
+ attrs: {},
+ props: {},
+ on: {},
+ style: {}
+ }
+ const confClone = JSON.parse(JSON.stringify(this.conf))
+ const children = []
+
+ const childObjs = componentChild[confClone.tag]
+ if (childObjs) {
+ Object.keys(childObjs).forEach(key => {
+ const childFunc = childObjs[key]
+ if (confClone[key]) {
+ children.push(childFunc(h, confClone, key))
+ }
+ })
+ }
+
+ Object.keys(confClone).forEach(key => {
+ const val = confClone[key]
+ if (key === 'vModel') {
+ vModel(this, dataObject, confClone.defaultValue)
+ } else if (dataObject[key]) {
+ dataObject[key] = val
+ } else if (!isAttr(key)) {
+ dataObject.props[key] = val
+ } else {
+ dataObject.attrs[key] = val
+ }
+ })
+ return h(this.conf.tag, dataObject, children)
+ },
+ props: ['conf']
+}
diff --git a/lab-booking-ui/src/utils/index.js b/lab-booking-ui/src/utils/index.js
new file mode 100644
index 0000000..918580f
--- /dev/null
+++ b/lab-booking-ui/src/utils/index.js
@@ -0,0 +1,390 @@
+import { parseTime } from './ruoyi'
+
+/**
+ * 表格时间格式化
+ */
+export function formatDate(cellValue) {
+ if (cellValue == null || cellValue == "") return "";
+ var date = new Date(cellValue)
+ var year = date.getFullYear()
+ var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
+ var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
+ var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
+ var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
+ var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
+ return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
+}
+
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+export function formatTime(time, option) {
+ if (('' + time).length === 10) {
+ time = parseInt(time) * 1000
+ } else {
+ time = +time
+ }
+ const d = new Date(time)
+ const now = Date.now()
+
+ const diff = (now - d) / 1000
+
+ if (diff < 30) {
+ return '刚刚'
+ } else if (diff < 3600) {
+ // less 1 hour
+ return Math.ceil(diff / 60) + '分钟前'
+ } else if (diff < 3600 * 24) {
+ return Math.ceil(diff / 3600) + '小时前'
+ } else if (diff < 3600 * 24 * 2) {
+ return '1天前'
+ }
+ if (option) {
+ return parseTime(time, option)
+ } else {
+ return (
+ d.getMonth() +
+ 1 +
+ '月' +
+ d.getDate() +
+ '日' +
+ d.getHours() +
+ '时' +
+ d.getMinutes() +
+ '分'
+ )
+ }
+}
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function getQueryObject(url) {
+ url = url == null ? window.location.href : url
+ const search = url.substring(url.lastIndexOf('?') + 1)
+ const obj = {}
+ const reg = /([^?&=]+)=([^?&=]*)/g
+ search.replace(reg, (rs, $1, $2) => {
+ const name = decodeURIComponent($1)
+ let val = decodeURIComponent($2)
+ val = String(val)
+ obj[name] = val
+ return rs
+ })
+ return obj
+}
+
+/**
+ * @param {string} input value
+ * @returns {number} output value
+ */
+export function byteLength(str) {
+ // returns the byte length of an utf8 string
+ let s = str.length
+ for (var i = str.length - 1; i >= 0; i--) {
+ const code = str.charCodeAt(i)
+ if (code > 0x7f && code <= 0x7ff) s++
+ else if (code > 0x7ff && code <= 0xffff) s += 2
+ if (code >= 0xDC00 && code <= 0xDFFF) i--
+ }
+ return s
+}
+
+/**
+ * @param {Array} actual
+ * @returns {Array}
+ */
+export function cleanArray(actual) {
+ const newArray = []
+ for (let i = 0; i < actual.length; i++) {
+ if (actual[i]) {
+ newArray.push(actual[i])
+ }
+ }
+ return newArray
+}
+
+/**
+ * @param {Object} json
+ * @returns {Array}
+ */
+export function param(json) {
+ if (!json) return ''
+ return cleanArray(
+ Object.keys(json).map(key => {
+ if (json[key] === undefined) return ''
+ return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
+ })
+ ).join('&')
+}
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function param2Obj(url) {
+ const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
+ if (!search) {
+ return {}
+ }
+ const obj = {}
+ const searchArr = search.split('&')
+ searchArr.forEach(v => {
+ const index = v.indexOf('=')
+ if (index !== -1) {
+ const name = v.substring(0, index)
+ const val = v.substring(index + 1, v.length)
+ obj[name] = val
+ }
+ })
+ return obj
+}
+
+/**
+ * @param {string} val
+ * @returns {string}
+ */
+export function html2Text(val) {
+ const div = document.createElement('div')
+ div.innerHTML = val
+ return div.textContent || div.innerText
+}
+
+/**
+ * Merges two objects, giving the last one precedence
+ * @param {Object} target
+ * @param {(Object|Array)} source
+ * @returns {Object}
+ */
+export function objectMerge(target, source) {
+ if (typeof target !== 'object') {
+ target = {}
+ }
+ if (Array.isArray(source)) {
+ return source.slice()
+ }
+ Object.keys(source).forEach(property => {
+ const sourceProperty = source[property]
+ if (typeof sourceProperty === 'object') {
+ target[property] = objectMerge(target[property], sourceProperty)
+ } else {
+ target[property] = sourceProperty
+ }
+ })
+ return target
+}
+
+/**
+ * @param {HTMLElement} element
+ * @param {string} className
+ */
+export function toggleClass(element, className) {
+ if (!element || !className) {
+ return
+ }
+ let classString = element.className
+ const nameIndex = classString.indexOf(className)
+ if (nameIndex === -1) {
+ classString += '' + className
+ } else {
+ classString =
+ classString.substr(0, nameIndex) +
+ classString.substr(nameIndex + className.length)
+ }
+ element.className = classString
+}
+
+/**
+ * @param {string} type
+ * @returns {Date}
+ */
+export function getTime(type) {
+ if (type === 'start') {
+ return new Date().getTime() - 3600 * 1000 * 24 * 90
+ } else {
+ return new Date(new Date().toDateString())
+ }
+}
+
+/**
+ * @param {Function} func
+ * @param {number} wait
+ * @param {boolean} immediate
+ * @return {*}
+ */
+export function debounce(func, wait, immediate) {
+ let timeout, args, context, timestamp, result
+
+ const later = function() {
+ // 据上一次触发时间间隔
+ const last = +new Date() - timestamp
+
+ // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
+ if (last < wait && last > 0) {
+ timeout = setTimeout(later, wait - last)
+ } else {
+ timeout = null
+ // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
+ if (!immediate) {
+ result = func.apply(context, args)
+ if (!timeout) context = args = null
+ }
+ }
+ }
+
+ return function(...args) {
+ context = this
+ timestamp = +new Date()
+ const callNow = immediate && !timeout
+ // 如果延时不存在,重新设定延时
+ if (!timeout) timeout = setTimeout(later, wait)
+ if (callNow) {
+ result = func.apply(context, args)
+ context = args = null
+ }
+
+ return result
+ }
+}
+
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ * @param {Object} source
+ * @returns {Object}
+ */
+export function deepClone(source) {
+ if (!source && typeof source !== 'object') {
+ throw new Error('error arguments', 'deepClone')
+ }
+ const targetObj = source.constructor === Array ? [] : {}
+ Object.keys(source).forEach(keys => {
+ if (source[keys] && typeof source[keys] === 'object') {
+ targetObj[keys] = deepClone(source[keys])
+ } else {
+ targetObj[keys] = source[keys]
+ }
+ })
+ return targetObj
+}
+
+/**
+ * @param {Array} arr
+ * @returns {Array}
+ */
+export function uniqueArr(arr) {
+ return Array.from(new Set(arr))
+}
+
+/**
+ * @returns {string}
+ */
+export function createUniqueString() {
+ const timestamp = +new Date() + ''
+ const randomNum = parseInt((1 + Math.random()) * 65536) + ''
+ return (+(randomNum + timestamp)).toString(32)
+}
+
+/**
+ * Check if an element has a class
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ * @returns {boolean}
+ */
+export function hasClass(ele, cls) {
+ return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
+}
+
+/**
+ * Add class to element
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ */
+export function addClass(ele, cls) {
+ if (!hasClass(ele, cls)) ele.className += ' ' + cls
+}
+
+/**
+ * Remove class from element
+ * @param {HTMLElement} elm
+ * @param {string} cls
+ */
+export function removeClass(ele, cls) {
+ if (hasClass(ele, cls)) {
+ const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
+ ele.className = ele.className.replace(reg, ' ')
+ }
+}
+
+export function makeMap(str, expectsLowerCase) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = 0; i < list.length; i++) {
+ map[list[i]] = true
+ }
+ return expectsLowerCase
+ ? val => map[val.toLowerCase()]
+ : val => map[val]
+}
+
+export const exportDefault = 'export default '
+
+export const beautifierConf = {
+ html: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'separate',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: false,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ },
+ js: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'normal',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: true,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ }
+}
+
+// 首字母大小
+export function titleCase(str) {
+ return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
+}
+
+// 下划转驼峰
+export function camelCase(str) {
+ return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
+}
+
+export function isNumberStr(str) {
+ return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
+}
+
diff --git a/lab-booking-ui/src/utils/jsencrypt.js b/lab-booking-ui/src/utils/jsencrypt.js
new file mode 100644
index 0000000..78d9523
--- /dev/null
+++ b/lab-booking-ui/src/utils/jsencrypt.js
@@ -0,0 +1,30 @@
+import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
+
+// 密钥对生成 http://web.chacuo.net/netrsakeypair
+
+const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
+ 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
+
+const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
+ '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
+ 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
+ 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
+ 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
+ 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
+ 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
+ 'UP8iWi1Qw0Y='
+
+// 加密
+export function encrypt(txt) {
+ const encryptor = new JSEncrypt()
+ encryptor.setPublicKey(publicKey) // 设置公钥
+ return encryptor.encrypt(txt) // 对数据进行加密
+}
+
+// 解密
+export function decrypt(txt) {
+ const encryptor = new JSEncrypt()
+ encryptor.setPrivateKey(privateKey) // 设置私钥
+ return encryptor.decrypt(txt) // 对数据进行解密
+}
+
diff --git a/lab-booking-ui/src/utils/permission.js b/lab-booking-ui/src/utils/permission.js
new file mode 100644
index 0000000..1730e33
--- /dev/null
+++ b/lab-booking-ui/src/utils/permission.js
@@ -0,0 +1,51 @@
+import store from '@/store'
+
+/**
+ * 字符权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkPermi(value) {
+ if (value && value instanceof Array && value.length > 0) {
+ const permissions = store.getters && store.getters.permissions
+ const permissionDatas = value
+ const all_permission = "*:*:*";
+
+ const hasPermission = permissions.some(permission => {
+ return all_permission === permission || permissionDatas.includes(permission)
+ })
+
+ if (!hasPermission) {
+ return false
+ }
+ return true
+ } else {
+ console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
+ return false
+ }
+}
+
+/**
+ * 角色权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkRole(value) {
+ if (value && value instanceof Array && value.length > 0) {
+ const roles = store.getters && store.getters.roles
+ const permissionRoles = value
+ const super_admin = "admin";
+
+ const hasRole = roles.some(role => {
+ return super_admin === role || permissionRoles.includes(role)
+ })
+
+ if (!hasRole) {
+ return false
+ }
+ return true
+ } else {
+ console.error(`need roles! Like checkRole="['admin','editor']"`)
+ return false
+ }
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/utils/request.js b/lab-booking-ui/src/utils/request.js
new file mode 100644
index 0000000..ae89f24
--- /dev/null
+++ b/lab-booking-ui/src/utils/request.js
@@ -0,0 +1,103 @@
+import axios from 'axios'
+import { Notification, MessageBox, Message } from 'element-ui'
+import store from '@/store'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
+// 创建axios实例
+const service = axios.create({
+ // axios中请求配置有baseURL选项,表示请求URL公共部分
+ baseURL: process.env.VUE_APP_BASE_API,
+ // 超时
+ timeout: 10000
+})
+// request拦截器
+service.interceptors.request.use(config => {
+ // 是否需要设置 token
+ const isToken = (config.headers || {}).isToken === false
+ if (getToken() && !isToken) {
+ config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+ }
+ // get请求映射params参数
+ if (config.method === 'get' && config.params) {
+ let url = config.url + '?';
+ for (const propName of Object.keys(config.params)) {
+ const value = config.params[propName];
+ var part = encodeURIComponent(propName) + "=";
+ if (value !== null && typeof(value) !== "undefined") {
+ if (typeof value === 'object') {
+ for (const key of Object.keys(value)) {
+ let params = propName + '[' + key + ']';
+ var subPart = encodeURIComponent(params) + "=";
+ url += subPart + encodeURIComponent(value[key]) + "&";
+ }
+ } else {
+ url += part + encodeURIComponent(value) + "&";
+ }
+ }
+ }
+ url = url.slice(0, -1);
+ config.params = {};
+ config.url = url;
+ }
+ return config
+}, error => {
+ console.log(error)
+ Promise.reject(error)
+})
+
+// 响应拦截器
+service.interceptors.response.use(res => {
+ // 未设置状态码则默认成功状态
+ const code = res.data.code || 200;
+ // 获取错误信息
+ const msg = errorCode[code] || res.data.msg || errorCode['default']
+ if (code === 401) {
+ MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
+ confirmButtonText: '重新登录',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }
+ ).then(() => {
+ store.dispatch('LogOut').then(() => {
+ location.href = '/index';
+ })
+ })
+ } else if (code === 500) {
+ Message({
+ message: msg,
+ type: 'error'
+ })
+ return Promise.reject(new Error(msg))
+ } else if (code !== 200) {
+ Notification.error({
+ title: msg
+ })
+ return Promise.reject('error')
+ } else {
+ return res.data
+ }
+ },
+ error => {
+ console.log('err' + error)
+ let { message } = error;
+ if (message == "Network Error") {
+ message = "后端接口连接异常";
+ }
+ else if (message.includes("timeout")) {
+ message = "系统接口请求超时";
+ }
+ else if (message.includes("Request failed with status code")) {
+ message = "系统接口" + message.substr(message.length - 3) + "异常";
+ }
+ Message({
+ message: message,
+ type: 'error',
+ duration: 5 * 1000
+ })
+ return Promise.reject(error)
+ }
+)
+
+export default service
diff --git a/lab-booking-ui/src/utils/ruoyi.js b/lab-booking-ui/src/utils/ruoyi.js
new file mode 100644
index 0000000..bf8390e
--- /dev/null
+++ b/lab-booking-ui/src/utils/ruoyi.js
@@ -0,0 +1,175 @@
+/**
+ * 通用js方法封装处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+const baseURL = process.env.VUE_APP_BASE_API
+
+// 日期格式化
+export function parseTime(time, pattern) {
+ if (arguments.length === 0 || !time) {
+ return null
+ }
+ const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+ let date
+ if (typeof time === 'object') {
+ date = time
+ } else {
+ if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+ time = parseInt(time)
+ } else if (typeof time === 'string') {
+ time = time.replace(new RegExp(/-/gm), '/');
+ }
+ if ((typeof time === 'number') && (time.toString().length === 10)) {
+ time = time * 1000
+ }
+ date = new Date(time)
+ }
+ const formatObj = {
+ y: date.getFullYear(),
+ m: date.getMonth() + 1,
+ d: date.getDate(),
+ h: date.getHours(),
+ i: date.getMinutes(),
+ s: date.getSeconds(),
+ a: date.getDay()
+ }
+ const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+ let value = formatObj[key]
+ // Note: getDay() returns 0 on Sunday
+ if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+ if (result.length > 0 && value < 10) {
+ value = '0' + value
+ }
+ return value || 0
+ })
+ return time_str
+}
+
+// 表单重置
+export function resetForm(refName) {
+ if (this.$refs[refName]) {
+ this.$refs[refName].resetFields();
+ }
+}
+
+// 添加日期范围
+export function addDateRange(params, dateRange, propName) {
+ var search = params;
+ search.params = {};
+ if (null != dateRange && '' != dateRange) {
+ if (typeof (propName) === "undefined") {
+ search.params["beginTime"] = dateRange[0];
+ search.params["endTime"] = dateRange[1];
+ } else {
+ search.params["begin" + propName] = dateRange[0];
+ search.params["end" + propName] = dateRange[1];
+ }
+ }
+ return search;
+}
+
+// 回显数据字典
+export function selectDictLabel(datas, value) {
+ var actions = [];
+ Object.keys(datas).some((key) => {
+ if (datas[key].dictValue == ('' + value)) {
+ actions.push(datas[key].dictLabel);
+ return true;
+ }
+ })
+ return actions.join('');
+}
+
+// 回显数据字典(字符串数组)
+export function selectDictLabels(datas, value, separator) {
+ var actions = [];
+ var currentSeparator = undefined === separator ? "," : separator;
+ var temp = value.split(currentSeparator);
+ Object.keys(value.split(currentSeparator)).some((val) => {
+ Object.keys(datas).some((key) => {
+ if (datas[key].dictValue == ('' + temp[val])) {
+ actions.push(datas[key].dictLabel + currentSeparator);
+ }
+ })
+ })
+ return actions.join('').substring(0, actions.join('').length - 1);
+}
+
+// 通用下载方法
+export function download(fileName) {
+ window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
+}
+
+// 字符串格式化(%s )
+export function sprintf(str) {
+ var args = arguments, flag = true, i = 1;
+ str = str.replace(/%s/g, function () {
+ var arg = args[i++];
+ if (typeof arg === 'undefined') {
+ flag = false;
+ return '';
+ }
+ return arg;
+ });
+ return flag ? str : '';
+}
+
+// 转换字符串,undefined,null等转化为""
+export function praseStrEmpty(str) {
+ if (!str || str == "undefined" || str == "null") {
+ return "";
+ }
+ return str;
+}
+
+/**
+ * 构造树型结构数据
+ * @param {*} data 数据源
+ * @param {*} id id字段 默认 'id'
+ * @param {*} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ */
+export function handleTree(data, id, parentId, children) {
+ let config = {
+ id: id || 'id',
+ parentId: parentId || 'parentId',
+ childrenList: children || 'children'
+ };
+
+ var childrenListMap = {};
+ var nodeIds = {};
+ var tree = [];
+
+ for (let d of data) {
+ let parentId = d[config.parentId];
+ if (childrenListMap[parentId] == null) {
+ childrenListMap[parentId] = [];
+ }
+ nodeIds[d[config.id]] = d;
+ childrenListMap[parentId].push(d);
+ }
+
+ for (let d of data) {
+ let parentId = d[config.parentId];
+ if (nodeIds[parentId] == null) {
+ tree.push(d);
+ }
+ }
+
+ for (let t of tree) {
+ adaptToChildrenList(t);
+ }
+
+ function adaptToChildrenList(o) {
+ if (childrenListMap[o[config.id]] !== null) {
+ o[config.childrenList] = childrenListMap[o[config.id]];
+ }
+ if (o[config.childrenList]) {
+ for (let c of o[config.childrenList]) {
+ adaptToChildrenList(c);
+ }
+ }
+ }
+ return tree;
+}
diff --git a/lab-booking-ui/src/utils/scroll-to.js b/lab-booking-ui/src/utils/scroll-to.js
new file mode 100644
index 0000000..c5d8e04
--- /dev/null
+++ b/lab-booking-ui/src/utils/scroll-to.js
@@ -0,0 +1,58 @@
+Math.easeInOutQuad = function(t, b, c, d) {
+ t /= d / 2
+ if (t < 1) {
+ return c / 2 * t * t + b
+ }
+ t--
+ return -c / 2 * (t * (t - 2) - 1) + b
+}
+
+// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
+var requestAnimFrame = (function() {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
+})()
+
+/**
+ * Because it's so fucking difficult to detect the scrolling element, just move them all
+ * @param {number} amount
+ */
+function move(amount) {
+ document.documentElement.scrollTop = amount
+ document.body.parentNode.scrollTop = amount
+ document.body.scrollTop = amount
+}
+
+function position() {
+ return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
+}
+
+/**
+ * @param {number} to
+ * @param {number} duration
+ * @param {Function} callback
+ */
+export function scrollTo(to, duration, callback) {
+ const start = position()
+ const change = to - start
+ const increment = 20
+ let currentTime = 0
+ duration = (typeof (duration) === 'undefined') ? 500 : duration
+ var animateScroll = function() {
+ // increment the time
+ currentTime += increment
+ // find the value with the quadratic in-out easing function
+ var val = Math.easeInOutQuad(currentTime, start, change, duration)
+ // move the document.body
+ move(val)
+ // do the animation unless its over
+ if (currentTime < duration) {
+ requestAnimFrame(animateScroll)
+ } else {
+ if (callback && typeof (callback) === 'function') {
+ // the animation is done so lets callback
+ callback()
+ }
+ }
+ }
+ animateScroll()
+}
diff --git a/lab-booking-ui/src/utils/validate.js b/lab-booking-ui/src/utils/validate.js
new file mode 100644
index 0000000..adfa254
--- /dev/null
+++ b/lab-booking-ui/src/utils/validate.js
@@ -0,0 +1,83 @@
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export function isExternal(path) {
+ return /^(https?:|mailto:|tel:)/.test(path)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUsername(str) {
+ const valid_map = ['admin', 'editor']
+ return valid_map.indexOf(str.trim()) >= 0
+}
+
+/**
+ * @param {string} url
+ * @returns {Boolean}
+ */
+export function validURL(url) {
+ const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
+ return reg.test(url)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validLowerCase(str) {
+ const reg = /^[a-z]+$/
+ return reg.test(str)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUpperCase(str) {
+ const reg = /^[A-Z]+$/
+ return reg.test(str)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validAlphabets(str) {
+ const reg = /^[A-Za-z]+$/
+ return reg.test(str)
+}
+
+/**
+ * @param {string} email
+ * @returns {Boolean}
+ */
+export function validEmail(email) {
+ const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
+ return reg.test(email)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function isString(str) {
+ if (typeof str === 'string' || str instanceof String) {
+ return true
+ }
+ return false
+}
+
+/**
+ * @param {Array} arg
+ * @returns {Boolean}
+ */
+export function isArray(arg) {
+ if (typeof Array.isArray === 'undefined') {
+ return Object.prototype.toString.call(arg) === '[object Array]'
+ }
+ return Array.isArray(arg)
+}
diff --git a/lab-booking-ui/src/utils/zipdownload.js b/lab-booking-ui/src/utils/zipdownload.js
new file mode 100644
index 0000000..fff4873
--- /dev/null
+++ b/lab-booking-ui/src/utils/zipdownload.js
@@ -0,0 +1,40 @@
+import axios from 'axios'
+import { getToken } from '@/utils/auth'
+
+const mimeMap = {
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ zip: 'application/zip'
+}
+
+const baseUrl = process.env.VUE_APP_BASE_API
+export function downLoadZip(str, filename) {
+ var url = baseUrl + str
+ axios({
+ method: 'get',
+ url: url,
+ responseType: 'blob',
+ headers: { 'Authorization': 'Bearer ' + getToken() }
+ }).then(res => {
+ resolveBlob(res, mimeMap.zip)
+ })
+}
+/**
+ * 解析blob响应内容并下载
+ * @param {*} res blob响应内容
+ * @param {String} mimeType MIME类型
+ */
+export function resolveBlob(res, mimeType) {
+ const aLink = document.createElement('a')
+ var blob = new Blob([res.data], { type: mimeType })
+ // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
+ var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
+ var contentDisposition = decodeURI(res.headers['content-disposition'])
+ var result = patt.exec(contentDisposition)
+ var fileName = result[1]
+ fileName = fileName.replace(/\"/g, '')
+ aLink.href = URL.createObjectURL(blob)
+ aLink.setAttribute('download', fileName) // 设置下载文件名称
+ document.body.appendChild(aLink)
+ aLink.click()
+ document.body.removeChild(aLink);
+}
diff --git a/lab-booking-ui/src/views/components/icons/element-icons.js b/lab-booking-ui/src/views/components/icons/element-icons.js
new file mode 100644
index 0000000..9ea4d63
--- /dev/null
+++ b/lab-booking-ui/src/views/components/icons/element-icons.js
@@ -0,0 +1,3 @@
+const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
+
+export default elementIcons
diff --git a/lab-booking-ui/src/views/components/icons/index.vue b/lab-booking-ui/src/views/components/icons/index.vue
new file mode 100644
index 0000000..d3c9a71
--- /dev/null
+++ b/lab-booking-ui/src/views/components/icons/index.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ {{ generateIconCode(item) }}
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+ {{ generateElementIconCode(item) }}
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/components/icons/svg-icons.js b/lab-booking-ui/src/views/components/icons/svg-icons.js
new file mode 100644
index 0000000..724cd8e
--- /dev/null
+++ b/lab-booking-ui/src/views/components/icons/svg-icons.js
@@ -0,0 +1,10 @@
+const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys()
+
+const re = /\.\/(.*)\.svg/
+
+const svgIcons = requireAll(req).map(i => {
+ return i.match(re)[1]
+})
+
+export default svgIcons
diff --git a/lab-booking-ui/src/views/components/tbCourse.vue b/lab-booking-ui/src/views/components/tbCourse.vue
new file mode 100644
index 0000000..bebe365
--- /dev/null
+++ b/lab-booking-ui/src/views/components/tbCourse.vue
@@ -0,0 +1,498 @@
+
+
+
点击下载pdf
+
点击下载图片
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.courseName }}
+ {{ item.className }}
+ {{ item.teacherName }}
+ {{ item.location }}
+ {{ item.week }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dialogData.number }}
+
+
+
+
+ {{ dialogData.requirement }}
+
+
+
+
+ {{ dialogData.capacity }}
+
+
+
+
+ {{ dialogData.labRemark }}
+
+
+
+
+ {{ dialogData.courseRemark }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/components/tbCourseDialog.vue b/lab-booking-ui/src/views/components/tbCourseDialog.vue
new file mode 100644
index 0000000..c11550b
--- /dev/null
+++ b/lab-booking-ui/src/views/components/tbCourseDialog.vue
@@ -0,0 +1,509 @@
+
+
+
+
+
+
+
+
+
+ {{ scope.row.Monday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Tuesday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Wednesday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Thursday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Friday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Saturday.week }}
+
+
+
+
+
+
+
+ {{ scope.row.Sunday.week }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确定
+ 取消选择
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/components/timeSelect.vue b/lab-booking-ui/src/views/components/timeSelect.vue
new file mode 100644
index 0000000..44cf508
--- /dev/null
+++ b/lab-booking-ui/src/views/components/timeSelect.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+ 学年
+
+
+
+
+
+
+ 学期
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/dashboard/BarChart.vue b/lab-booking-ui/src/views/dashboard/BarChart.vue
new file mode 100644
index 0000000..be0af34
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/BarChart.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/dashboard/LineChart.vue b/lab-booking-ui/src/views/dashboard/LineChart.vue
new file mode 100644
index 0000000..e654168
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/LineChart.vue
@@ -0,0 +1,135 @@
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/dashboard/PanelGroup.vue b/lab-booking-ui/src/views/dashboard/PanelGroup.vue
new file mode 100644
index 0000000..1a1081f
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/PanelGroup.vue
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/dashboard/PieChart.vue b/lab-booking-ui/src/views/dashboard/PieChart.vue
new file mode 100644
index 0000000..4d2ef32
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/PieChart.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/dashboard/RaddarChart.vue b/lab-booking-ui/src/views/dashboard/RaddarChart.vue
new file mode 100644
index 0000000..6823af3
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/RaddarChart.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/dashboard/mixins/resize.js b/lab-booking-ui/src/views/dashboard/mixins/resize.js
new file mode 100644
index 0000000..b1e76e9
--- /dev/null
+++ b/lab-booking-ui/src/views/dashboard/mixins/resize.js
@@ -0,0 +1,56 @@
+import { debounce } from '@/utils'
+
+export default {
+ data() {
+ return {
+ $_sidebarElm: null,
+ $_resizeHandler: null
+ }
+ },
+ mounted() {
+ this.initListener()
+ },
+ activated() {
+ if (!this.$_resizeHandler) {
+ // avoid duplication init
+ this.initListener()
+ }
+
+ // when keep-alive chart activated, auto resize
+ this.resize()
+ },
+ beforeDestroy() {
+ this.destroyListener()
+ },
+ deactivated() {
+ this.destroyListener()
+ },
+ methods: {
+ // use $_ for mixins properties
+ // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+ $_sidebarResizeHandler(e) {
+ if (e.propertyName === 'width') {
+ this.$_resizeHandler()
+ }
+ },
+ initListener() {
+ this.$_resizeHandler = debounce(() => {
+ this.resize()
+ }, 100)
+ window.addEventListener('resize', this.$_resizeHandler)
+
+ this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+ this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+ },
+ destroyListener() {
+ window.removeEventListener('resize', this.$_resizeHandler)
+ this.$_resizeHandler = null
+
+ this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+ },
+ resize() {
+ const { chart } = this
+ chart && chart.resize()
+ }
+ }
+}
diff --git a/lab-booking-ui/src/views/error/401.vue b/lab-booking-ui/src/views/error/401.vue
new file mode 100644
index 0000000..448b6ec
--- /dev/null
+++ b/lab-booking-ui/src/views/error/401.vue
@@ -0,0 +1,88 @@
+
+
+
+ 返回
+
+
+
+
+ 401错误!
+
+ 您没有访问权限!
+ 对不起,您没有访问权限,请不要进行非法操作!您可以返回主页面
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/error/404.vue b/lab-booking-ui/src/views/error/404.vue
new file mode 100644
index 0000000..96f075c
--- /dev/null
+++ b/lab-booking-ui/src/views/error/404.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+ 404错误!
+
+
+ {{ message }}
+
+
+ 对不起,您正在寻找的页面不存在。尝试检查URL的错误,然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。
+
+
+ 返回首页
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/index.vue b/lab-booking-ui/src/views/index.vue
new file mode 100644
index 0000000..442d2e6
--- /dev/null
+++ b/lab-booking-ui/src/views/index.vue
@@ -0,0 +1,252 @@
+
+
+
+
+
+ 你好,我的朋友
+
+
+
+
+
+
+
+ 实验室预约管理系统
+ 旨在简化预约流程
+
+ 当前版本: v{{ version }}
+
+
+ 教务处
+ 网上办事服务大厅
+
+
+
+
+
+
+ 技术选型
+
+
+
+
+ 后端技术
+
+ SpringBoot
+ Spring Security
+ Redis
+ JWT
+ MyBatis-Plus
+ MySQL
+ ...
+
+
+
+ 前端技术
+
+ Vue
+ Element-ui
+ ...
+
+
+
+
+
+
+
+
+
+
+ 联系信息
+
+
+
+ 微信:🤭
+
+
+ 电子邮箱:yezihaode@qq.com
+
+
+
+
+
+
+
+ 更新日志
+
+
+
+
+ 解决服务器时区问题,修复课表细节
+ 其他细节优化
+
+
+
+
+ 数据库重构,抽取冗余字段数据
+ 修改系统结构
+ 其他细节优化
+
+
+
+
+ 项目基本功能实现
+ 新增实验室维护模块
+ 新增实验室审批功能
+ 新增实验室取消功能
+ 用户学院相关权限完善
+ 其他细节优化
+
+
+
+
+
+ 新增数据分析模块
+ 添加实验室预约取消功能
+ 预约实验室回显权限问题
+ 其他细节优化
+
+
+
+
+
+ 完善预约模块
+ 完善预约记录模块
+ 完善实验室课表模块,添加导出课表功能
+ 完善个人信息模块
+ 完善课程信息模块
+ 其他细节优化
+
+
+
+
+
+ 实验室预约管理前后端分离系统基本框架搭建
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/index_v1.vue b/lab-booking-ui/src/views/index_v1.vue
new file mode 100644
index 0000000..d2d2ec6
--- /dev/null
+++ b/lab-booking-ui/src/views/index_v1.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/labbooking/booking/lab.vue b/lab-booking-ui/src/views/labbooking/booking/lab.vue
new file mode 100644
index 0000000..411534f
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/booking/lab.vue
@@ -0,0 +1,474 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 导出所有
+
+
+
+
+
+
+
+
+
+
+
+
+ 电话号码: {{ scope.row.phoneNumber }}
+ 电子邮箱: {{ scope.row.email }}
+
+ 办 公 室: {{ scope.row.office }}
+
+
+ {{ scope.row.userName }}
+
+
+
+
+
+
+
+
+
+ 申请
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/booking/record.vue b/lab-booking-ui/src/views/labbooking/booking/record.vue
new file mode 100644
index 0000000..3e053e8
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/booking/record.vue
@@ -0,0 +1,351 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 批量取消
+ 导出所有
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.year }}-{{ scope.row.year + 1 }}学年
+ 第{{ scope.row.term + 1 }}学期
+
+ 周{{ scope.row.day }}第{{
+ scope.row.time == 1
+ ? "1-2"
+ : scope.row.time == 2
+ ? "3-4"
+ : scope.row.time == 3
+ ? "5-6"
+ : scope.row.time == 4
+ ? "7-8"
+ : "9-10"
+ }}节
+
+ {{ scope.row.week }}
+
+
+
+
+
+ {{
+ statusFormat(scope.row)
+ }}
+ {{
+ statusFormat(scope.row)
+ }}
+ {{
+ statusFormat(scope.row)
+ }}
+
+ {{ scope.row.rejectContent }}
+ {{
+ statusFormat(scope.row)
+ }}
+
+
+
+
+
+
+ 取消
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/labbooking/data/index.vue b/lab-booking-ui/src/views/labbooking/data/index.vue
new file mode 100644
index 0000000..ea027dc
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/data/index.vue
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/lab/index.vue b/lab-booking-ui/src/views/labbooking/lab/index.vue
new file mode 100644
index 0000000..aa5b286
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/lab/index.vue
@@ -0,0 +1,603 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出所有
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ parseTime(scope.row.gmtCreate, "{y}-{m}-{d} {h}:{i}:{s}")
+ }}
+
+
+
+
+ {{
+ parseTime(scope.row.gmtModified, "{y}-{m}-{d} {h}:{i}:{s}")
+ }}
+
+
+
+
+ 使用情况
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/lab/record.vue b/lab-booking-ui/src/views/labbooking/lab/record.vue
new file mode 100644
index 0000000..d2a0f1e
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/lab/record.vue
@@ -0,0 +1,599 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+
+ 批量操作
+
+
+ 批量通过
+ 批量驳回
+ 批量取消
+
+
+
+
+
+ 导出所有
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.year }}-{{ scope.row.year + 1 }}学年
+ 第{{ scope.row.term + 1 }}学期
+ 周{{ scope.row.day }}第{{ scope.row.time }}节
+ {{ scope.row.week }}
+
+
+
+
+
+ {{
+ statusFormat(scope.row)
+ }}
+ {{
+ statusFormat(scope.row)
+ }}
+ {{
+ statusFormat(scope.row)
+ }}
+
+ {{ scope.row.rejectContent }}
+ {{
+ statusFormat(scope.row)
+ }}
+
+
+
+
+
+
+ 审批
+
+ 取消
+
+
+
+
+
+
+
+
+ 驳回理由
+
+
+
+
+
+
+
+ 是否审批通过该条实验室预约记录
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/table/index.vue b/lab-booking-ui/src/views/labbooking/table/index.vue
new file mode 100644
index 0000000..4f693d4
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/table/index.vue
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/table/utils/converter.js b/lab-booking-ui/src/views/labbooking/table/utils/converter.js
new file mode 100644
index 0000000..1655cc9
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/table/utils/converter.js
@@ -0,0 +1,60 @@
+export function arr2shortString(arr) {
+ if (arr.length === 1) return arr[0]
+ arr = [...new Set(arr)] //去重
+ arr = arr.sort(function (a, b) {
+ return a - b
+ })
+ var min = arr[0]
+ var max
+ var newArr = []
+ for (let i = 0; i < arr.length - 1; i++) {
+ max = arr[i] === arr[i + 1] - 1 ? arr[i + 1] : arr[i]
+ if (max === arr[i] || max === arr[arr.length - 1]) {
+ newArr.push(min === max ? max : min + '-' + max)
+ min = arr[i + 1]
+ if (min === arr[arr.length - 1] && min - 1 !== arr[i]) {
+ newArr.push(min)
+ }
+ }
+ }
+ return newArr.toString()
+}
+
+//分割,arr为数组,range为分割范围,要求:arr为已排序数组,range为正整数
+export function seg(arr, range) {
+ arr = [...new Set(arr)] //去重
+ var min = arr[0]
+ var max
+ var newArr = []
+ for (let i = 0; i < arr.length - 1; i++) {
+ max = arr[i] === arr[i + 1] - 1 ? arr[i + 1] : arr[i]
+ if (arr[i] - min === range - 1) { //在constrict的基础上加if
+ newArr.push(min + '-' + arr[i])
+ min = arr[i + 1]
+ } else if (max === arr[i] || max === arr[arr.length - 1]) {
+ newArr.push(min === max ? max : min + '-' + max)
+ min = arr[i + 1]
+ if (min === arr[arr.length - 1] && min - 1 !== arr[i]) {
+ newArr.push(min)
+ }
+ }
+ }
+ return newArr.toString()
+}
+
+
+// 课表items.item转可显示html
+export function item2html(items) {
+ if (!items.length) return ''
+ let spanHeader = ''
+ let spanFooter = ' '
+ let br = ' '
+ let html = ''
+ for (let item in items) {
+ html += spanHeader + items[item].course_name + ' ' + items[item].nickname + spanFooter + br
+ html += spanHeader + items[item].classes + ' ' + items[item].population + spanFooter + br
+ html += spanHeader + '第' + arr2shortString(items[item].weeks) + '周' + spanFooter + br
+ html += spanHeader + items[item].software + spanFooter + br
+ }
+ return html
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/table/utils/dialog.js b/lab-booking-ui/src/views/labbooking/table/utils/dialog.js
new file mode 100644
index 0000000..0267c0a
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/table/utils/dialog.js
@@ -0,0 +1,54 @@
+// 询问框
+export async function showDialog(content, that) {
+ return new Promise((resolve, reject) => {
+ that.$confirm(content, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ resolve()
+ }).catch(() => { });
+ })
+}
+
+//询问框带reject
+export async function showDialogWithReject(content, that) {
+ return new Promise((resolve, reject) => {
+ that.$confirm(content, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ resolve()
+ }).catch(() => { reject() });
+ })
+}
+// 显示操作成功
+export function showSuccess(content, that) {
+ that.$message({
+ type: 'success',
+ message: content
+ });
+}
+// 显示操作失败
+export function showFail(content, that) {
+ that.$message({
+ type: 'info',
+ message: content
+ });
+}
+
+// 加载
+export function showLoading(content, that) {
+ return that.$loading({
+ lock: true,
+ text: content,
+ spinner: 'el-icon-loading',
+ background: 'rgba(0, 0, 0, 0.7)'
+ })
+}
+
+// 关闭加载
+export function closeLoading(loading) {
+ loading.close()
+}
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/labbooking/user/course.vue b/lab-booking-ui/src/views/labbooking/user/course.vue
new file mode 100644
index 0000000..26ba3d2
--- /dev/null
+++ b/lab-booking-ui/src/views/labbooking/user/course.vue
@@ -0,0 +1,464 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+ 教工编号: {{ scope.row.userName }}
+ 教工名称: {{ scope.row.nickName }}
+ 电话号码: {{ scope.row.phonenumber }}
+ 电子邮箱: {{ scope.row.email }}
+
+ 办 公 室: {{ scope.row.office }}
+
+
+ {{ scope.row.nickName }}
+
+
+
+
+
+
+
+
+
+
+ {{
+ parseTime(scope.row.gmtCreate, "{y}-{m}-{d} {h}:{i}:{s}")
+ }}
+
+
+
+
+ {{
+ parseTime(scope.row.gmtModified, "{y}-{m}-{d} {h}:{i}:{s}")
+ }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/login.vue b/lab-booking-ui/src/views/login.vue
new file mode 100644
index 0000000..56429a4
--- /dev/null
+++ b/lab-booking-ui/src/views/login.vue
@@ -0,0 +1,214 @@
+
+
+
+ 测试账号
+
+
+ 系统管理员:10000 / 12346
+ 实验室管理员:10001 / 123456
+ 校级实验室管理员:10002 / 123456
+ 教师:10003 / 123456
+
+ 无需输入验证码即可登录!
+
+
+ 实验室预约管理系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 记住密码
+
+
+ 登 录
+ 登 录 中...
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/cache/index.vue b/lab-booking-ui/src/views/monitor/cache/index.vue
new file mode 100644
index 0000000..df0cb1e
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/cache/index.vue
@@ -0,0 +1,153 @@
+
+
+
+
+
+ 基本信息
+
+
+
+
+ Redis版本
+ {{ cache.info.redis_version }}
+ 运行模式
+ {{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}
+ 端口
+ {{ cache.info.tcp_port }}
+ 客户端数
+ {{ cache.info.connected_clients }}
+
+
+ 运行时间(天)
+ {{ cache.info.uptime_in_days }}
+ 使用内存
+ {{ cache.info.used_memory_human }}
+ 使用CPU
+ {{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}
+ 内存配置
+ {{ cache.info.maxmemory_human }}
+
+
+ AOF是否开启
+ {{ cache.info.aof_enabled == "0" ? "否" : "是" }}
+ RDB是否成功
+ {{ cache.info.rdb_last_bgsave_status }}
+ Key数量
+ {{ cache.dbSize }}
+ 网络入口/出口
+ {{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps
+
+
+
+
+
+
+
+
+
+ 命令统计
+
+
+
+
+
+
+
+ 内存信息
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/druid/index.vue b/lab-booking-ui/src/views/monitor/druid/index.vue
new file mode 100644
index 0000000..a1a0bd9
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/druid/index.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/job/index.vue b/lab-booking-ui/src/views/monitor/job/index.vue
new file mode 100644
index 0000000..a5dc8c7
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/job/index.vue
@@ -0,0 +1,488 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+ 日志
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 执行一次
+ 详细
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 调用方法
+
+
+ Bean调用示例:ryTask.ryParams('ry')
+ Class类调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry')
+ 参数说明:支持字符串,布尔类型,长整型,浮点型,整型
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 允许
+ 禁止
+
+
+
+
+
+
+ 立即执行
+ 执行一次
+ 放弃执行
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ form.jobId }}
+ {{ form.jobName }}
+
+
+ {{ jobGroupFormat(form) }}
+ {{ form.createTime }}
+
+
+ {{ form.cronExpression }}
+
+
+ {{ parseTime(form.nextValidTime) }}
+
+
+ {{ form.invokeTarget }}
+
+
+
+ 正常
+ 失败
+
+
+
+
+ 允许
+ 禁止
+
+
+
+
+ 默认策略
+ 立即执行
+ 执行一次
+ 放弃执行
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/monitor/job/log.vue b/lab-booking-ui/src/views/monitor/job/log.vue
new file mode 100644
index 0000000..e34b635
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/job/log.vue
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 删除
+
+
+ 清空
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 详细
+
+
+
+
+
+
+
+
+
+
+
+ {{ form.jobLogId }}
+ {{ form.jobName }}
+
+
+ {{ form.jobGroup }}
+ {{ form.createTime }}
+
+
+ {{ form.invokeTarget }}
+
+
+ {{ form.jobMessage }}
+
+
+
+ 正常
+ 失败
+
+
+
+ {{ form.exceptionInfo }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/monitor/logininfor/index.vue b/lab-booking-ui/src/views/monitor/logininfor/index.vue
new file mode 100644
index 0000000..c181267
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/logininfor/index.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 删除
+
+
+ 清空
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.loginTime) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/online/index.vue b/lab-booking-ui/src/views/monitor/online/index.vue
new file mode 100644
index 0000000..fc18a5e
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/online/index.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ {{(pageNum - 1) * pageSize + scope.$index + 1}}
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.loginTime) }}
+
+
+
+
+ 强退
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/operlog/index.vue b/lab-booking-ui/src/views/monitor/operlog/index.vue
new file mode 100644
index 0000000..472a49a
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/operlog/index.vue
@@ -0,0 +1,321 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 删除
+
+
+ 清空
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.operTime) }}
+
+
+
+
+ 详细
+
+
+
+
+
+
+
+
+
+
+
+ {{ form.title }} / {{ typeFormat(form) }}
+ {{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}
+
+
+ {{ form.operUrl }}
+ {{ form.requestMethod }}
+
+
+ {{ form.method }}
+
+
+ {{ form.operParam }}
+
+
+ {{ form.jsonResult }}
+
+
+
+ 正常
+ 失败
+
+
+
+ {{ parseTime(form.operTime) }}
+
+
+ {{ form.errorMsg }}
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/monitor/server/index.vue b/lab-booking-ui/src/views/monitor/server/index.vue
new file mode 100644
index 0000000..8ba3082
--- /dev/null
+++ b/lab-booking-ui/src/views/monitor/server/index.vue
@@ -0,0 +1,210 @@
+
+
+
+
+
+ CPU
+
+
+
+
+ 属性
+ 值
+
+
+
+
+ 核心数
+ {{ server.cpu.cpuNum }}
+
+
+ 用户使用率
+ {{ server.cpu.used }}%
+
+
+ 系统使用率
+ {{ server.cpu.sys }}%
+
+
+ 当前空闲率
+ {{ server.cpu.free }}%
+
+
+
+
+
+
+
+
+
+ 内存
+
+
+
+
+ 属性
+ 内存
+ JVM
+
+
+
+
+ 总内存
+ {{ server.mem.total }}G
+ {{ server.jvm.total }}M
+
+
+ 已用内存
+ {{ server.mem.used}}G
+ {{ server.jvm.used}}M
+
+
+ 剩余内存
+ {{ server.mem.free }}G
+ {{ server.jvm.free }}M
+
+
+ 使用率
+ {{ server.mem.usage }}%
+ {{ server.jvm.usage }}%
+
+
+
+
+
+
+
+
+
+
+ 服务器信息
+
+
+
+
+
+ 服务器名称
+ {{ server.sys.computerName }}
+ 操作系统
+ {{ server.sys.osName }}
+
+
+ 服务器IP
+ {{ server.sys.computerIp }}
+ 系统架构
+ {{ server.sys.osArch }}
+
+
+
+
+
+
+
+
+
+
+ Java虚拟机信息
+
+
+
+
+
+ Java名称
+ {{ server.jvm.name }}
+ Java版本
+ {{ server.jvm.version }}
+
+
+ 启动时间
+ {{ server.jvm.startTime }}
+ 运行时长
+ {{ server.jvm.runTime }}
+
+
+ 安装路径
+ {{ server.jvm.home }}
+
+
+ 项目路径
+ {{ server.sys.userDir }}
+
+
+
+
+
+
+
+
+
+
+ 磁盘状态
+
+
+
+
+
+ 盘符路径
+ 文件系统
+ 盘符类型
+ 总大小
+ 可用大小
+ 已用大小
+ 已用百分比
+
+
+
+
+ {{ sysFile.dirName }}
+ {{ sysFile.sysTypeName }}
+ {{ sysFile.typeName }}
+ {{ sysFile.total }}
+ {{ sysFile.free }}
+ {{ sysFile.used }}
+ {{ sysFile.usage }}%
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/redirect.vue b/lab-booking-ui/src/views/redirect.vue
new file mode 100644
index 0000000..db4c1d6
--- /dev/null
+++ b/lab-booking-ui/src/views/redirect.vue
@@ -0,0 +1,12 @@
+
diff --git a/lab-booking-ui/src/views/system/config/index.vue b/lab-booking-ui/src/views/system/config/index.vue
new file mode 100644
index 0000000..e26ed90
--- /dev/null
+++ b/lab-booking-ui/src/views/system/config/index.vue
@@ -0,0 +1,361 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+ 清理缓存
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/dept/index.vue b/lab-booking-ui/src/views/system/dept/index.vue
new file mode 100644
index 0000000..1d86425
--- /dev/null
+++ b/lab-booking-ui/src/views/system/dept/index.vue
@@ -0,0 +1,317 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/dict/data.vue b/lab-booking-ui/src/views/system/dict/data.vue
new file mode 100644
index 0000000..3101b66
--- /dev/null
+++ b/lab-booking-ui/src/views/system/dict/data.vue
@@ -0,0 +1,354 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/dict/index.vue b/lab-booking-ui/src/views/system/dict/index.vue
new file mode 100644
index 0000000..c7697e3
--- /dev/null
+++ b/lab-booking-ui/src/views/system/dict/index.vue
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+ 清理缓存
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.dictType }}
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/menu/index.vue b/lab-booking-ui/src/views/system/menu/index.vue
new file mode 100644
index 0000000..63cecc0
--- /dev/null
+++ b/lab-booking-ui/src/views/system/menu/index.vue
@@ -0,0 +1,400 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 新增
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 目录
+ 菜单
+ 按钮
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 是
+ 否
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+ 缓存
+ 不缓存
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/notice/index.vue b/lab-booking-ui/src/views/system/notice/index.vue
new file mode 100644
index 0000000..1f570a8
--- /dev/null
+++ b/lab-booking-ui/src/views/system/notice/index.vue
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/post/index.vue b/lab-booking-ui/src/views/system/post/index.vue
new file mode 100644
index 0000000..57c918c
--- /dev/null
+++ b/lab-booking-ui/src/views/system/post/index.vue
@@ -0,0 +1,326 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/role/index.vue b/lab-booking-ui/src/views/system/role/index.vue
new file mode 100644
index 0000000..3cb6df9
--- /dev/null
+++ b/lab-booking-ui/src/views/system/role/index.vue
@@ -0,0 +1,610 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 数据权限
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+ 展开/折叠
+ 全选/全不选
+ 父子联动
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 展开/折叠
+ 全选/全不选
+ 父子联动
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/user/index.vue b/lab-booking-ui/src/views/system/user/index.vue
new file mode 100644
index 0000000..6a7c43c
--- /dev/null
+++ b/lab-booking-ui/src/views/system/user/index.vue
@@ -0,0 +1,704 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导入
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+ 电话号码: {{ scope.row.phonenumber }}
+ 电子邮箱: {{ scope.row.email }}
+ 办 公 室: {{ scope.row.office }}
+
+ {{ scope.row.nickName }}
+
+
+
+
+
+
+
+ {{role.roleName}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.dictLabel}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 将文件拖到此处,或
+ 点击上传
+
+
+ 是否更新已经存在的用户数据
+ 下载模板
+
+ 提示:仅允许导入“xls”或“xlsx”格式文件!
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/user/profile/index.vue b/lab-booking-ui/src/views/system/user/profile/index.vue
new file mode 100644
index 0000000..bbad43f
--- /dev/null
+++ b/lab-booking-ui/src/views/system/user/profile/index.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+ 个人信息
+
+
+
+
+
+
+
+ 基本资料
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/system/user/profile/resetPwd.vue b/lab-booking-ui/src/views/system/user/profile/resetPwd.vue
new file mode 100644
index 0000000..ee65240
--- /dev/null
+++ b/lab-booking-ui/src/views/system/user/profile/resetPwd.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 保存
+ 关闭
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/system/user/profile/userAvatar.vue b/lab-booking-ui/src/views/system/user/profile/userAvatar.vue
new file mode 100644
index 0000000..d6ea0fc
--- /dev/null
+++ b/lab-booking-ui/src/views/system/user/profile/userAvatar.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 选择
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提 交
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/system/user/profile/userInfo.vue b/lab-booking-ui/src/views/system/user/profile/userInfo.vue
new file mode 100644
index 0000000..94bb374
--- /dev/null
+++ b/lab-booking-ui/src/views/system/user/profile/userInfo.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 男
+ 女
+
+
+
+
+
+
+ 保存
+ 关闭
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/build/CodeTypeDialog.vue b/lab-booking-ui/src/views/tool/build/CodeTypeDialog.vue
new file mode 100644
index 0000000..941ec36
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/CodeTypeDialog.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+
+ 确定
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/build/DraggableItem.vue b/lab-booking-ui/src/views/tool/build/DraggableItem.vue
new file mode 100644
index 0000000..e881778
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/DraggableItem.vue
@@ -0,0 +1,100 @@
+
diff --git a/lab-booking-ui/src/views/tool/build/IconsDialog.vue b/lab-booking-ui/src/views/tool/build/IconsDialog.vue
new file mode 100644
index 0000000..958be50
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/IconsDialog.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/build/RightPanel.vue b/lab-booking-ui/src/views/tool/build/RightPanel.vue
new file mode 100644
index 0000000..abaec43
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/RightPanel.vue
@@ -0,0 +1,944 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+ {{ activeData.componentName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 选择
+
+
+
+
+
+
+ 选择
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 默认
+
+
+ 右侧
+
+
+
+
+
+
+ 个字符
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text
+
+
+ picture
+
+
+ picture-card
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 选项
+
+
+
+
+
+ 添加选项
+
+
+
+
+
+
+ 选项
+
+
+
+ 动态数据
+
+
+ 静态数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加父级
+
+
+
+
+
+
+
+
+ 默认
+
+
+ 按钮
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 中等
+
+
+ 较小
+
+
+ 迷你
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 布局结构树
+
+
+
+
+ {{ node.label }}
+
+
+
+
+
+
+ 正则校验
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加规则
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 中等
+
+
+ 较小
+
+
+ 迷你
+
+
+
+
+
+
+ 左对齐
+
+
+ 右对齐
+
+
+ 顶部对齐
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/build/TreeNodeDialog.vue b/lab-booking-ui/src/views/tool/build/TreeNodeDialog.vue
new file mode 100644
index 0000000..c225c4c
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/TreeNodeDialog.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确定
+
+
+ 取消
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/build/index.vue b/lab-booking-ui/src/views/tool/build/index.vue
new file mode 100644
index 0000000..1f8b361
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/build/index.vue
@@ -0,0 +1,794 @@
+
+
+
+
+
+
Form Generator
+
+
+
+
+
+ 输入型组件
+
+
+
+
+
+ {{ element.label }}
+
+
+
+
+ 选择型组件
+
+
+
+
+
+ {{ element.label }}
+
+
+
+
+ 布局型组件
+
+
+
+
+
+ {{ element.label }}
+
+
+
+
+
+
+
+
+
+
+ 导出vue文件
+
+
+ 复制代码
+
+
+ 清空
+
+
+
+
+
+
+
+
+
+ 从左侧拖入或点选组件进行表单设计
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/gen/basicInfoForm.vue b/lab-booking-ui/src/views/tool/gen/basicInfoForm.vue
new file mode 100644
index 0000000..757962c
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/gen/basicInfoForm.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/gen/editTable.vue b/lab-booking-ui/src/views/tool/gen/editTable.vue
new file mode 100644
index 0000000..94d0ef3
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/gen/editTable.vue
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.dictName }}
+ {{ dict.dictType }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 提交
+ 返回
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/gen/genInfoForm.vue b/lab-booking-ui/src/views/tool/gen/genInfoForm.vue
new file mode 100644
index 0000000..27d6a22
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/gen/genInfoForm.vue
@@ -0,0 +1,300 @@
+
+
+
+
+
+ 生成模板
+
+
+
+
+
+
+
+
+
+
+
+ 生成包路径
+
+
+
+
+
+
+
+
+
+
+
+ 生成模块名
+
+
+
+
+
+
+
+
+
+
+
+ 生成业务名
+
+
+
+
+
+
+
+
+
+
+
+ 生成功能名
+
+
+
+
+
+
+
+
+
+
+
+ 上级菜单
+
+
+
+
+
+
+
+
+
+
+
+ 生成代码方式
+
+
+
+
+ zip压缩包
+ 自定义路径
+
+
+
+
+
+
+ 自定义路径
+
+
+
+
+
+
+
+ 最近路径快速选择
+
+
+
+ 恢复默认的生成基础路径
+
+
+
+
+
+
+
+
+
+
+
+
+ 树编码字段
+
+
+
+
+
+
+
+
+
+
+
+
+ 树父编码字段
+
+
+
+
+
+
+
+
+
+
+
+
+ 树名称字段
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 关联子表的表名
+
+
+
+
+
+
+
+
+
+
+
+
+ 子表关联的外键名
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/gen/importTable.vue b/lab-booking-ui/src/views/tool/gen/importTable.vue
new file mode 100644
index 0000000..56b2c0c
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/gen/importTable.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lab-booking-ui/src/views/tool/gen/index.vue b/lab-booking-ui/src/views/tool/gen/index.vue
new file mode 100644
index 0000000..8e9b513
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/gen/index.vue
@@ -0,0 +1,340 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 生成
+
+
+ 导入
+
+
+ 修改
+
+
+ 删除
+
+
+
+
+
+
+
+
+ {{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}
+
+
+
+
+
+
+
+
+
+ 预览
+ 编辑
+ 删除
+ 同步
+ 生成代码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lab-booking-ui/src/views/tool/swagger/index.vue b/lab-booking-ui/src/views/tool/swagger/index.vue
new file mode 100644
index 0000000..61cfcca
--- /dev/null
+++ b/lab-booking-ui/src/views/tool/swagger/index.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/lab-booking-ui/vue.config.js b/lab-booking-ui/vue.config.js
new file mode 100644
index 0000000..91ff7c3
--- /dev/null
+++ b/lab-booking-ui/vue.config.js
@@ -0,0 +1,118 @@
+'use strict'
+const path = require('path')
+const defaultSettings = require('./src/settings.js')
+
+function resolve(dir) {
+ return path.join(__dirname, dir)
+}
+
+const name = defaultSettings.title || '实验室预约系统' // 标题
+
+const port = process.env.port || process.env.npm_config_port || 80 // 端口
+
+// vue.config.js 配置说明
+//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
+// 这里只列一部分,具体配置参考文档
+module.exports = {
+ // 部署生产环境和开发环境下的URL。
+ // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
+ // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
+ publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
+ // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
+ outputDir: 'dist',
+ // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
+ assetsDir: 'static',
+ // 是否开启eslint保存检测,有效值:ture | false | 'error'
+ lintOnSave: process.env.NODE_ENV === 'development',
+ // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
+ productionSourceMap: false,
+ // webpack-dev-server 相关配置
+ devServer: {
+ host: '0.0.0.0',
+ port: port,
+ open: true,
+ proxy: {
+ // detail: https://cli.vuejs.org/config/#devserver-proxy
+ [process.env.VUE_APP_BASE_API]: {
+ target: `http://localhost:8080`,
+ changeOrigin: true,
+ pathRewrite: {
+ ['^' + process.env.VUE_APP_BASE_API]: ''
+ }
+ }
+ },
+ disableHostCheck: true
+ },
+ configureWebpack: {
+ name: name,
+ resolve: {
+ alias: {
+ '@': resolve('src')
+ }
+ }
+ },
+ chainWebpack(config) {
+ config.plugins.delete('preload') // TODO: need test
+ config.plugins.delete('prefetch') // TODO: need test
+
+ // set svg-sprite-loader
+ config.module
+ .rule('svg')
+ .exclude.add(resolve('src/assets/icons'))
+ .end()
+ config.module
+ .rule('icons')
+ .test(/\.svg$/)
+ .include.add(resolve('src/assets/icons'))
+ .end()
+ .use('svg-sprite-loader')
+ .loader('svg-sprite-loader')
+ .options({
+ symbolId: 'icon-[name]'
+ })
+ .end()
+
+ config
+ .when(process.env.NODE_ENV !== 'development',
+ config => {
+ config
+ .plugin('ScriptExtHtmlWebpackPlugin')
+ .after('html')
+ .use('script-ext-html-webpack-plugin', [{
+ // `runtime` must same as runtimeChunk name. default is `runtime`
+ inline: /runtime\..*\.js$/
+ }])
+ .end()
+ config
+ .optimization.splitChunks({
+ chunks: 'all',
+ cacheGroups: {
+ libs: {
+ name: 'chunk-libs',
+ test: /[\\/]node_modules[\\/]/,
+ priority: 10,
+ chunks: 'initial' // only package third parties that are initially dependent
+ },
+ elementUI: {
+ name: 'chunk-elementUI', // split elementUI into a single package
+ priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
+ test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
+ },
+ commons: {
+ name: 'chunk-commons',
+ test: resolve('src/components'), // can customize your rules
+ minChunks: 3, // minimum common number
+ priority: 5,
+ reuseExistingChunk: true
+ }
+ }
+ })
+ config.optimization.runtimeChunk('single'),
+ {
+ from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
+ to: './', //到根目录下
+ }
+ }
+ )
+ }
+}
diff --git a/lab-booking/pom.xml b/lab-booking/pom.xml
new file mode 100644
index 0000000..acaf9c5
--- /dev/null
+++ b/lab-booking/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ ruoyi
+ com.ruoyi
+ 3.4.0
+
+ 4.0.0
+
+ com.hzu
+ lab-booking
+ 1.1.0
+
+ lab-booking
+ 实验室预约管理模块
+
+
+
+
+
+ com.ruoyi
+ ruoyi-common
+
+
+
+
+ com.ruoyi
+ ruoyi-framework
+
+
+
+
+ io.springfox
+ springfox-swagger2
+
+
+
+
+ io.swagger
+ swagger-annotations
+ 1.5.21
+
+
+
+ io.swagger
+ swagger-models
+ 1.5.21
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
+
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/config/MyThreadPoolConfig.java b/lab-booking/src/main/java/com/hzu/labbooking/config/MyThreadPoolConfig.java
new file mode 100644
index 0000000..8d09f9a
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/config/MyThreadPoolConfig.java
@@ -0,0 +1,34 @@
+package com.hzu.labbooking.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+public class MyThreadPoolConfig {
+
+ @Bean
+ public ThreadPoolExecutor taskExecutor() {
+ return new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,
+ new LinkedBlockingDeque<>(100000), new ThreadPoolExecutor.AbortPolicy());
+
+// // 设置核心线程数
+// executor.setCorePoolSize(5);
+// // 设置最大线程数
+// executor.setMaxPoolSize(10);
+// // 设置队列容量
+// executor.setQueueCapacity(20);
+// // 设置线程活跃时间(秒)
+// executor.setKeepAliveSeconds(60);
+// // 设置默认线程名称
+// executor.setThreadNamePrefix("hello-");
+// // 设置拒绝策略
+// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+// // 等待所有任务结束后再关闭线程池
+// executor.setWaitForTasksToCompleteOnShutdown(true);
+// return executor;
+ }
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/constant/Constants.java b/lab-booking/src/main/java/com/hzu/labbooking/constant/Constants.java
new file mode 100644
index 0000000..440799d
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/constant/Constants.java
@@ -0,0 +1,21 @@
+package com.hzu.labbooking.constant;
+
+/**
+ * @author: skyed
+ * @since: 2021/5/3
+ */
+public class Constants {
+
+ /*下学期起始时间*/
+ public static final int NEXT_TERM_START = 3;
+ /*下学期结束时间*/
+ public static final int NEXT_TERM_END = 7;
+ /*节次个数*/
+ public static final long TIME_SIZE = 5;
+ /*星期个数*/
+ public static final long DAY_SIZE = 7;
+ /*周次个数*/
+ public static final long WEEK_SIZE = 18;
+ /*学院父级ID*/
+ public static final long DEPT_PARENT_ID = 200;
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/constant/LabStatusConstansts.java b/lab-booking/src/main/java/com/hzu/labbooking/constant/LabStatusConstansts.java
new file mode 100644
index 0000000..5282f20
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/constant/LabStatusConstansts.java
@@ -0,0 +1,16 @@
+package com.hzu.labbooking.constant;
+
+/**
+ * @author: yezihao
+ * @since: 2021/5/27
+ */
+public class LabStatusConstansts {
+
+ /*实验室预约功能:关闭*/
+ public static final int CLOSED = 0;
+ /*实验室预约功能:开启*/
+ public static final int OPEN = 1;
+ /*实验室逻辑删除*/
+ public static final int DELETED = 2;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/constant/RecordStatusConstants.java b/lab-booking/src/main/java/com/hzu/labbooking/constant/RecordStatusConstants.java
new file mode 100644
index 0000000..5e6da61
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/constant/RecordStatusConstants.java
@@ -0,0 +1,18 @@
+package com.hzu.labbooking.constant;
+
+/**
+ * @author: yezihao
+ * @since: 2021/5/27
+ */
+public class RecordStatusConstants {
+
+ /*预约状态:待审批*/
+ public static final int NOT_APPROVE = 0;
+ /*预约状态:审批通过*/
+ public static final int APPROVE = 1;
+ /*预约状态:驳回审批*/
+ public static final int REJECTED = 2;
+ /*预约状态:已取消*/
+ public static final int CANCELLED = 3;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/constant/WeekStatusConstants.java b/lab-booking/src/main/java/com/hzu/labbooking/constant/WeekStatusConstants.java
new file mode 100644
index 0000000..fd69f9f
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/constant/WeekStatusConstants.java
@@ -0,0 +1,14 @@
+package com.hzu.labbooking.constant;
+
+/**
+ * @author: yezihao
+ * @since: 2021/5/27
+ */
+public class WeekStatusConstants {
+
+ /*该时间预约:关闭*/
+ public static final int CLOSED = 0;
+ /*该时间预约:开启*/
+ public static final int OPEN = 1;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/controller/CourseController.java b/lab-booking/src/main/java/com/hzu/labbooking/controller/CourseController.java
new file mode 100644
index 0000000..474e98c
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/controller/CourseController.java
@@ -0,0 +1,103 @@
+package com.hzu.labbooking.controller;
+
+import java.util.List;
+
+import com.hzu.labbooking.domain.vo.MyCourseVO;
+import com.ruoyi.common.core.domain.R;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.hzu.labbooking.domain.Course;
+import com.hzu.labbooking.service.ICourseService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 课程信息Controller
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@RestController
+@RequestMapping("/labbooking/course")
+public class CourseController extends BaseController {
+
+ @Autowired
+ private ICourseService courseService;
+
+ /**
+ * 查询个人课程信息列表
+ */
+ @GetMapping("/list")
+ public TableDataInfo list(Course course) {
+ startPage();
+ List list = courseService.selectCourseList(course);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询个人所有课程(预约实验室模块)
+ */
+ @GetMapping("/getCourse")
+ public R> getCourse() {
+ List list = courseService.selectCourse();
+ return R.ok(list);
+ }
+
+ /**
+ * 获取课程信息详细信息
+ */
+ @GetMapping(value = "/{id}")
+ public AjaxResult getInfo(@PathVariable("id") Long id) {
+ return AjaxResult.success(courseService.selectCourseById(id));
+ }
+
+ /**
+ * 新增个人课程信息
+ */
+ @Log(title = "课程信息", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody Course course) {
+ return toAjax(courseService.insertCourse(course));
+ }
+
+ /**
+ * 修改课程信息
+ */
+ @Log(title = "课程信息", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody Course course) {
+ return toAjax(courseService.updateCourse(course));
+ }
+
+ /**
+ * 删除课程信息
+ */
+ @Log(title = "课程信息", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public AjaxResult remove(@PathVariable Long[] ids) {
+ return toAjax(courseService.deleteCourseByIds(ids));
+ }
+
+ /**
+ * 导出个人课程信息列表
+ */
+ @Log(title = "课程信息", businessType = BusinessType.EXPORT)
+ @GetMapping("/export")
+ public AjaxResult export(Course course) {
+ List list = courseService.selectCourseList(course);
+ ExcelUtil util = new ExcelUtil<>(MyCourseVO.class);
+ return util.exportExcel(list, "课程信息数据");
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/controller/LabController.java b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabController.java
new file mode 100644
index 0000000..73763ef
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabController.java
@@ -0,0 +1,183 @@
+package com.hzu.labbooking.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.hzu.labbooking.constant.LabStatusConstansts;
+import com.hzu.labbooking.domain.vo.LabUseVO;
+import com.hzu.labbooking.domain.vo.LabVO;
+import com.hzu.labbooking.domain.vo.MyLabVO;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.service.ISysDeptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.hzu.labbooking.domain.Lab;
+import com.hzu.labbooking.service.ILabService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 实验室信息Controller
+ *
+ * @author yezihao
+ * @date 2021-06-14
+ */
+@RestController
+@RequestMapping("/labbooking/lab")
+public class LabController extends BaseController {
+
+ @Autowired
+ private ILabService labService;
+ @Autowired
+ private ISysDeptService deptService;
+ @Autowired
+ private TokenService tokenService;
+
+ /**
+ * 查询实验室信息列表
+ */
+ @GetMapping("/bookLabList")
+ public TableDataInfo bookLabList(Lab lab) {
+ startPage();
+ List list = labService.selectBookLabList(lab);
+ return getDataTable(list);
+ }
+
+ /**
+ * 管理员查询实验室信息列表
+ */
+ @GetMapping("/labList")
+ public TableDataInfo labList(Lab lab) {
+ startPage();
+ List list = labService.selectLabList(lab);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询实验室信息列表
+ */
+ @GetMapping("/labUse")
+ public AjaxResult labUse(Long year, Long term) {
+ List voList = labService.getLabUseRate(year, term);
+ return AjaxResult.success(voList);
+ }
+
+ /**
+ * 导出实验室信息列表
+ */
+ @Log(title = "实验室信息", businessType = BusinessType.EXPORT)
+ @GetMapping("/bookLabExport")
+ public AjaxResult bookLabExport(Lab lab) {
+ List list = labService.selectBookLabList(lab);
+ ExcelUtil util = new ExcelUtil(LabVO.class);
+ return util.exportExcel(list, "实验室信息数据");
+ }
+
+ /**
+ * 管理员导出实验室信息列表
+ */
+ @Log(title = "实验室信息", businessType = BusinessType.EXPORT)
+ @GetMapping("/labExport")
+ public AjaxResult labExport(Lab lab) {
+ List list = labService.selectLabList(lab);
+ ExcelUtil util = new ExcelUtil<>(MyLabVO.class);
+ return util.exportExcel(list, "实验室信息数据");
+ }
+
+ /**
+ * 获取实验室信息详细信息
+ */
+ @GetMapping(value = "/{id}")
+ public AjaxResult getInfo(@PathVariable("id") Long id) {
+ return AjaxResult.success(labService.selectLabById(id));
+ }
+
+ /**
+ * 新增实验室信息
+ */
+ @Log(title = "实验室信息", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody Lab lab) {
+ int status = labService.insertLab(lab);
+ if (status == -1) {
+ return AjaxResult.error("实验室名称或者实验室位置已经存在");
+ } else {
+ return toAjax(status);
+ }
+ }
+
+ /**
+ * 修改实验室信息
+ */
+ @Log(title = "实验室信息", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody Lab lab) {
+ int status = labService.updateLab(lab);
+ if (status == -1) {
+ return AjaxResult.error("实验室位置已存在");
+ } else if (status == -2) {
+ return AjaxResult.error("实验室名称已经存在");
+ } else if (status == -3) {
+ return AjaxResult.error("实验室名称或者实验室位置已经存在");
+ } else if (status == -4) {
+ return AjaxResult.error("未知异常");
+ } else {
+ return toAjax(status);
+ }
+ }
+
+ /**
+ * 删除实验室信息
+ */
+ @Log(title = "实验室信息", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{ids}")
+ public AjaxResult remove(@PathVariable Long[] ids) {
+ Lab lab = new Lab();
+ lab.setStatus((long) LabStatusConstansts.DELETED);
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.in("id", (Object[]) ids);
+ return toAjax(labService.update(lab, wrapper));
+ }
+
+ /**
+ * 修改实验室状态
+ */
+ @Log(title = "预约状态修改", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody Lab lab) {
+ return toAjax(labService.updateLabStatus(lab));
+ }
+
+ /**
+ * 获取当前用户学院信息
+ */
+ @GetMapping("/getDept")
+ public R getDept() {
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ Long deptId = loginUser.getUser().getDeptId();
+ List list = new ArrayList<>();
+ list.add(deptId);
+ //SysDept sysDept = deptService.selectDeptById(deptId);
+ //Long parentId = sysDept.getParentId();
+ //if (parentId != 0) list.add(parentId);
+ List sysDepts = deptService.selectDeptNameByIds(list);
+ return R.ok(sysDepts);
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/controller/LabRecordController.java b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabRecordController.java
new file mode 100644
index 0000000..a1a9e5e
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabRecordController.java
@@ -0,0 +1,116 @@
+package com.hzu.labbooking.controller;
+
+import java.util.List;
+
+import com.hzu.labbooking.domain.vo.CollegeApplyVO;
+import com.hzu.labbooking.domain.vo.LabRecordVO;
+import com.hzu.labbooking.domain.vo.LabTableVO;
+import com.hzu.labbooking.domain.vo.RecordVO;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.framework.web.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
+import com.hzu.labbooking.domain.LabRecord;
+import com.hzu.labbooking.service.ILabRecordService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.page.TableDataInfo;
+
+/**
+ * 实验室预约信息Controller
+ *
+ * @author yezihao
+ * @date 2021-06-14
+ */
+@RestController
+@RequestMapping("/labbooking/record")
+public class LabRecordController extends BaseController {
+
+ @Autowired
+ private ILabRecordService labRecordService;
+ @Autowired
+ private TokenService tokenService;
+
+ /**
+ * 查询实验室预约信息列表
+ */
+ @GetMapping("/list")
+ public TableDataInfo list(LabRecord labRecord, long year, long term) {
+ // startPage(); 不分页
+ List list = labRecordService.selectLabRecordList(labRecord, year, term);
+ return getDataTable(list);
+ }
+
+ @PostMapping("/addRecordAndWeek")
+ public R addRecordAndWeek(@RequestBody LabTableVO vo) {
+ SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
+ int sz = labRecordService.addRecordAndWeek(vo, user.getUserId());
+ if (sz > 0) {
+ return R.ok(sz, "预约成功");
+ }
+ // 这里应该加状态码和msg放到Constants里
+ return R.fail("预约失败");
+ }
+
+ /**
+ * 教师取消实验室预约信息列表
+ */
+ @Log(title = "取消实验室预约", businessType = BusinessType.UPDATE)
+ @PutMapping("/cancel")
+ public AjaxResult cancelBooking(@RequestBody List recordIdList) {
+ return toAjax(labRecordService.cancelBooking(recordIdList));
+ }
+
+ /**
+ * 管理员查询实验室预约信息列表
+ */
+ @GetMapping("/managerList")
+ public TableDataInfo managerList(LabRecord labRecord, long year, long term) {
+ List list = labRecordService.selectManagerRecordList(labRecord, year, term);
+ return getDataTable(list);
+ }
+
+ /**
+ * 管理员审核实验室预约信息列表
+ */
+ @Log(title = "管理员审核实验室预约", businessType = BusinessType.UPDATE)
+ @PutMapping("/approve")
+ public AjaxResult approveBooking(@RequestBody List vos) {
+ return toAjax(labRecordService.approveBooking(vos));
+ }
+
+ /**
+ * 管理员取消实验室预约信息列表
+ */
+ @Log(title = "管理员取消实验室预约", businessType = BusinessType.UPDATE)
+ @PutMapping("/managerCancel")
+ public AjaxResult managerCancelBooking(@RequestBody List vos) {
+ return toAjax(labRecordService.managerCancelBooking(vos));
+ }
+
+ /**
+ * 每学期/每个学院实验室申请率(扇形展示echarts)
+ */
+ @GetMapping("/college")
+ public AjaxResult college(Long year, Long term) {
+ List list = labRecordService.selectCollegeApply(year, term);
+ return AjaxResult.success(list);
+ }
+
+ /**
+ * 导出实验室预约信息列表
+ */
+ @Log(title = "实验室预约信息", businessType = BusinessType.EXPORT)
+ @GetMapping("/export")
+ public AjaxResult export(LabRecord labRecord, long year, long term) {
+ List list = labRecordService.selectLabRecordList(labRecord, year, term);
+ ExcelUtil util = new ExcelUtil(LabRecordVO.class);
+ return util.exportExcel(list, "实验室预约信息数据");
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/controller/LabTableController.java b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabTableController.java
new file mode 100644
index 0000000..52c46b1
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/controller/LabTableController.java
@@ -0,0 +1,52 @@
+package com.hzu.labbooking.controller;
+
+import com.hzu.labbooking.domain.vo.LabTableDetailsVO;
+import com.hzu.labbooking.domain.vo.LabTableVO;
+import com.hzu.labbooking.service.ILabService;
+import com.ruoyi.common.core.domain.R;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author: yezihao
+ * @date: 2021-06-14
+ */
+@RestController
+@RequestMapping("/labbooking/table")
+public class LabTableController {
+
+ @Autowired
+ private ILabService labService;
+
+ @GetMapping("/list")
+ public R> list(@RequestParam Long year,
+ @RequestParam Long term) {
+ List voList = labService.getAllLabTable(year, term);
+ return R.ok(voList);
+ }
+
+ @GetMapping("/applyTableList/{labId}/{year}/{term}")
+ public R> applyTableList(@PathVariable("labId") long labId,
+ @PathVariable("year") long year,
+ @PathVariable("term") long term) {
+ List voList = labService.getAllApplyTable(labId, year, term);
+ return R.ok(voList);
+ }
+
+ @GetMapping("/appliedTableList/{labId}/{year}/{term}")
+ public R> appliedTableList(@PathVariable("labId") long labId,
+ @PathVariable("year") long year,
+ @PathVariable("term") long term) {
+ List voList = labService.getAllAppliedTable(labId, year, term);
+ return R.ok(voList);
+ }
+
+ @GetMapping("/get/{labId}")
+ public R getTableDetails(@PathVariable("labId") Long labId) {
+ LabTableDetailsVO vo = labService.getTableDetails(labId);
+ return R.ok(vo);
+ }
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/Course.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/Course.java
new file mode 100644
index 0000000..35dc132
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/Course.java
@@ -0,0 +1,101 @@
+package com.hzu.labbooking.domain;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 课程信息对象 lms_course
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_course")
+public class Course extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 课程编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ @TableField(exist = false)
+ private Long courseId; //和id的值相同,规避命名冲突用的
+
+ /**
+ * 课程名称
+ */
+ @Excel(name = "课程名称")
+ private String name;
+
+ @TableField(exist = false)
+ private String courseName; //和name的值相同,规避命名冲突用的
+
+ /**
+ * 授课教师编号
+ */
+ @Excel(name = "授课教师编号")
+ private Long userId;
+
+ /**
+ * 课程条件[软件或硬件,多个用逗号隔开]
+ */
+ @Excel(name = "课程条件")
+ private String requirement;
+
+ /**
+ * 班级名称[多个用逗号隔开]
+ */
+ @Excel(name = "班级名称")
+ private String className;
+
+ /**
+ * 课程人数
+ */
+ @Excel(name = "课程人数")
+ private Long number;
+
+ /**
+ * 课程人数
+ */
+ @Excel(name = "学年")
+ private Long year;
+
+ /**
+ * 课程人数
+ */
+ @Excel(name = "学期")
+ private Long term;
+
+ /**
+ * 课程备注
+ */
+ @Excel(name = "课程备注")
+ private String remarks;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT) //插入操作时自动更新该字段
+ private Date gmtCreate;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新操作时自动更新该字段
+ private Date gmtModified;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/Lab.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/Lab.java
new file mode 100644
index 0000000..cf69508
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/Lab.java
@@ -0,0 +1,94 @@
+package com.hzu.labbooking.domain;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 实验室信息对象 lms_lab
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_lab")
+public class Lab extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 实验室编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 所属学院编号
+ */
+ private Long deptId;
+
+ /**
+ * 实验室名称[5-101/大创实验室]
+ */
+ @Excel(name = "实验室名称")
+ private String name;
+
+ /**
+ * 实验室位置[5-101]
+ */
+ @Excel(name = "实验室位置")
+ private String location;
+
+ /**
+ * 容纳人数
+ */
+ @Excel(name = "容纳人数")
+ private Long capacity;
+
+ /**
+ * 实验条件[软件或硬件,多个用逗号隔开]
+ */
+ @Excel(name = "实验条件")
+ private String requirement;
+
+ /**
+ * 实验室管理员编号
+ */
+ @Excel(name = "实验室管理员编号")
+ private Long userId;
+
+ /**
+ * 是否开启实验室预约功能[0-关闭, 1-开启]
+ */
+ @Excel(name = "是否开启实验室预约功能")
+ private Long status;
+
+ /**
+ * 实验室备注
+ */
+ @Excel(name = "实验室备注")
+ private String remarks;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT) //插入操作时自动更新该字段
+ private Date gmtCreate;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新操作时自动更新该字段
+ private Date gmtModified;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/LabRecord.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/LabRecord.java
new file mode 100644
index 0000000..432cd9a
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/LabRecord.java
@@ -0,0 +1,107 @@
+package com.hzu.labbooking.domain;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 实验室预约信息对象 lms_lab_record
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_lab_record")
+public class LabRecord extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 实验室预约记录编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 预约用户编号
+ */
+ @Excel(name = "预约用户编号")
+ private Long userId;
+
+ /**
+ * 课程编号
+ */
+ @Excel(name = "课程编号")
+ private Long courseId;
+
+ /**
+ * 课程/班级名称
+ */
+ @TableField(exist = false)
+ private String name;
+
+ /**
+ * 实验室编号
+ */
+ @Excel(name = "实验室编号")
+ private Long labId;
+
+ /**
+ * 学年[2020(2020-2021)]
+ */
+ @Excel(name = "学年")
+ private Long year;
+
+ /**
+ * 学期[1, 2]
+ */
+ @Excel(name = "学期")
+ private Long term;
+
+ /**
+ * 星期[1-7]
+ */
+ @Excel(name = "星期")
+ private Long day;
+
+ /**
+ * 节次[1-5(1-2,3-4,...)]
+ */
+ @Excel(name = "节次")
+ private Long time;
+
+ /**
+ * 实验室预约状态[0-待审批, 1-审批通过, 2-驳回审批]
+ */
+ @Excel(name = "实验室预约状态")
+ private Long status;
+
+ /**
+ * 驳回内容
+ */
+ @Excel(name = "驳回内容")
+ private String rejectContent;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT) //插入操作时自动更新该字段
+ private Date gmtCreate;
+
+ /**
+ * 更新时间[驳回时间/通过时间]
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "更新时间[驳回时间/通过时间]", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新操作时自动更新该字段
+ private Date gmtModified;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/LabWeek.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/LabWeek.java
new file mode 100644
index 0000000..d3b3da0
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/LabWeek.java
@@ -0,0 +1,59 @@
+package com.hzu.labbooking.domain;
+
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 实验室预约周次对象 lms_lab_week
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_lab_week")
+public class LabWeek extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 实验室预约周次编号
+ */
+ @TableId(type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 周次[1-18]
+ */
+ @Excel(name = "周次")
+ private Long week;
+
+ /**
+ * 可预约时间段是否开启[0-关闭, 1-开启]
+ */
+ @Excel(name = "可预约时间段是否开启")
+ private Long status;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT) //插入操作时自动更新该字段
+ private Date gmtCreate;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新操作时自动更新该字段
+ private Date gmtModified;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/Office.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/Office.java
new file mode 100644
index 0000000..68cc638
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/Office.java
@@ -0,0 +1,32 @@
+package com.hzu.labbooking.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 教师信息对象 lms_office
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_office")
+public class Office extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户编号
+ */
+ private Long id;
+
+ /**
+ * 用户所在办公室
+ */
+ private String office;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/RecordWeek.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/RecordWeek.java
new file mode 100644
index 0000000..f3d4cd4
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/RecordWeek.java
@@ -0,0 +1,31 @@
+package com.hzu.labbooking.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+
+/**
+ * 预约信息周次映射对象 lms_record_week
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("lms_record_week")
+public class RecordWeek extends MpBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 实验室预约记录ID
+ */
+ private Long recordId;
+
+ /**
+ * 实验室预约周次ID
+ */
+ private Long weekId;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/component/MapKey.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/MapKey.java
new file mode 100644
index 0000000..33b0f92
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/MapKey.java
@@ -0,0 +1,48 @@
+package com.hzu.labbooking.domain.component;
+
+import lombok.Data;
+
+/**
+ * @author: skyed
+ * @date: 2021-4-29
+ */
+@Data
+public class MapKey {
+
+ private long day;
+ private long time;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ //if (!super.equals(o)) return false;
+
+ MapKey mapKey = (MapKey) o;
+ if (day != mapKey.day) {
+ return false;
+ }
+ return time == mapKey.time;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + (int) (day ^ (day >>> 32));
+ result = 31 * result + (int) (time ^ (time >>> 32));
+ return result;
+ }
+
+ public MapKey() {
+ }
+
+ public MapKey(long day, long time) {
+ this.day = day;
+ this.time = time;
+ }
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordKey.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordKey.java
new file mode 100644
index 0000000..cd488d0
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordKey.java
@@ -0,0 +1,62 @@
+package com.hzu.labbooking.domain.component;
+
+import lombok.Data;
+
+/**
+ * @author: yezihao
+ * @since: 2021/6/15
+ */
+@Data
+public class RecordKey {
+
+ /**
+ * 用户编号
+ */
+ private Long userId;
+ /**
+ * 课程编号
+ */
+ private Long courseId;
+ /**
+ * 预约状态
+ */
+ private Long status;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ //if (!super.equals(o)) return false;
+ RecordKey recordKey = (RecordKey) o;
+ if (!userId.equals(recordKey.userId)) {
+ return false;
+ }
+ if (!status.equals(recordKey.status)) {
+ return false;
+ }
+ return courseId.equals(recordKey.courseId);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + (int) (status ^ (status >>> 32));
+ result = 31 * result + (int) (userId ^ (userId >>> 32));
+ result = 31 * result + (int) (courseId ^ (courseId >>> 32));
+ return result;
+ }
+
+ public RecordKey() {
+ }
+
+ public RecordKey(Long userId, Long courseId, Long status) {
+ this.userId = userId;
+ this.courseId = courseId;
+ this.status = status;
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableKey.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableKey.java
new file mode 100644
index 0000000..35d6051
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableKey.java
@@ -0,0 +1,80 @@
+package com.hzu.labbooking.domain.component;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: yezihao
+ * @since: 2021/6/15
+ */
+@Data
+public class RecordTableKey {
+ /**
+ * 预约记录ID
+ */
+ private Long id;
+ /**
+ * 课程编号
+ */
+ private Long courseId;
+ /**
+ * 实验室编号
+ */
+ private Long labId;
+ /**
+ * 星期[1-7]
+ */
+ private Long day;
+ /**
+ * 节次[1-5(1-2,3-4,...)]
+ */
+ private Long time;
+
+ /**
+ * 预约用户ID
+ */
+ private Long userId;
+
+ public RecordTableKey() {
+ }
+
+ public RecordTableKey(Long id, Long courseId, Long labId, Long day, Long time, Long userId) {
+ this.id = id;
+ this.courseId = courseId;
+ this.labId = labId;
+ this.day = day;
+ this.time = time;
+ this.userId = userId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RecordTableKey recordTableKey = (RecordTableKey) o;
+
+ if (!id.equals(recordTableKey.id))
+ return false;
+ if (!courseId.equals(recordTableKey.courseId))
+ return false;
+ if (!day.equals(recordTableKey.day))
+ return false;
+ if (!time.equals(recordTableKey.time))
+ return false;
+ return labId.equals(recordTableKey.labId);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + (int) (id ^ (id >>> 32));
+ result = 31 * result + (int) (courseId ^ (courseId >>> 32));
+ result = 31 * result + (int) (labId ^ (labId >>> 32));
+ result = 31 * result + (int) (day ^ (day >>> 32));
+ result = 31 * result + (int) (time ^ (time >>> 32));
+ return result;
+ }
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableValue.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableValue.java
new file mode 100644
index 0000000..1a8b0cc
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordTableValue.java
@@ -0,0 +1,41 @@
+package com.hzu.labbooking.domain.component;
+
+import com.hzu.labbooking.domain.LabWeek;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: yezihao
+ * @since: 2021/6/15
+ */
+@Data
+public class RecordTableValue {
+
+ /**
+ * 实验室预约记录ID
+ */
+ private List recordIdList;
+
+ /**
+ * 实验室预约状态[0-待审批, 1-审批通过, 2-驳回审批]
+ */
+ private Long status;
+
+ /**
+ * 驳回内容
+ */
+ private String rejectContent;
+
+ /**
+ * 更新时间[驳回时间/通过时间]
+ */
+ private Date gmtModified;
+
+ /**
+ * 预约周次
+ */
+ private List weekList;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordValue.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordValue.java
new file mode 100644
index 0000000..4bbadf7
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/component/RecordValue.java
@@ -0,0 +1,31 @@
+package com.hzu.labbooking.domain.component;
+
+import com.hzu.labbooking.domain.LabWeek;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author: yezihao
+ * @since: 2021/6/15
+ */
+@Data
+public class RecordValue {
+ /**
+ * 预约记录编号
+ */
+ private Long recordId;
+ /**
+ * 周次列表
+ */
+ private List labWeekList;
+
+ public RecordValue() {
+ }
+
+ public RecordValue(Long recordId, List labWeekList) {
+ this.recordId = recordId;
+ this.labWeekList = labWeekList;
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/CollegeApplyVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/CollegeApplyVO.java
new file mode 100644
index 0000000..b8f4114
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/CollegeApplyVO.java
@@ -0,0 +1,9 @@
+package com.hzu.labbooking.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class CollegeApplyVO {
+ private Integer value;
+ private String name;
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabRecordVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabRecordVO.java
new file mode 100644
index 0000000..c0cbe1b
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabRecordVO.java
@@ -0,0 +1,67 @@
+package com.hzu.labbooking.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: skyed
+ * @since: 2021/5/6
+ */
+@Data
+public class LabRecordVO {
+
+ private Long userId;
+
+ private Long labId;
+
+ @Excel(name = "用户名称")
+ private String nickName;
+
+ @Excel(name = "实验室位置")
+ private String location;
+
+ @Excel(name = "实验室名称")
+ private String labName;
+
+ @Excel(name = "班级名称")
+ private String className;
+
+ @Excel(name = "班级人数")
+ private Long number;
+
+ @Excel(name = "课程名称")
+ private String courseName;
+
+ @Excel(name = "学年")
+ private Long year;
+
+ @Excel(name = "学期")
+ private Long term;
+
+ @Excel(name = "星期")
+ private Long day;
+
+ @Excel(name = "节次")
+ private Long time;
+
+ private List weekList;
+
+ @Excel(name = "周次")
+ private String week;
+
+ @Excel(name = "预约状态")
+ private Long status;
+
+ @Excel(name = "驳回内容")
+ private String rejectContent;
+
+ @Excel(name = "更新时间")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date gmtModified;
+
+ private List recordIdList;
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDataVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDataVO.java
new file mode 100644
index 0000000..128bd8d
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDataVO.java
@@ -0,0 +1,62 @@
+package com.hzu.labbooking.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author: skyed
+ * @since: 2021/5/5
+ */
+
+/**
+ * 课表中一个格子可能有多个课程,这个类是课程元数据
+ */
+@Data
+public class LabTableDataVO {
+
+ private Long labId;
+
+ private Long courseId;
+
+ /**
+ * 周次[1-18]
+ */
+ private String week;
+
+ /**
+ * 周次列表
+ */
+ private List weekList;
+
+ /**
+ * 星期[1-7]
+ */
+ private Long day;
+
+ /**
+ * 节次[1-5(1-2,3-4,...)]
+ */
+ private Long time;
+
+ /**
+ * 用户昵称
+ */
+ private String teacherName;
+
+ /**
+ * 课程名称
+ */
+ private String courseName;
+
+ /**
+ * 班级名称[多个用逗号隔开]
+ */
+ private String className;
+
+ /**
+ * 实验室位置[5-101]
+ */
+ private String location;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDetailsVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDetailsVO.java
new file mode 100644
index 0000000..6399b5c
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableDetailsVO.java
@@ -0,0 +1,33 @@
+package com.hzu.labbooking.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author: skyed
+ * @date: 2021-4-29
+ */
+@Data
+public class LabTableDetailsVO {
+
+ /** 课程人数 */
+ @ApiModelProperty(value = "课程人数")
+ private Long number;
+
+ /** 实验条件[软件或硬件,多个用逗号隔开] */
+ @ApiModelProperty(value = "实验条件[软件或硬件,多个用逗号隔开]")
+ private String requirement;
+
+ /** 容纳人数 */
+ @ApiModelProperty(value = "容纳人数")
+ private Long capacity;
+
+ /** 实验室备注 */
+ @ApiModelProperty(value = "实验室备注")
+ private String labRemark;
+
+ /** 课程备注 */
+ @ApiModelProperty(value = "课程备注")
+ private String courseRemark;
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableVO.java
new file mode 100644
index 0000000..16bea30
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabTableVO.java
@@ -0,0 +1,91 @@
+package com.hzu.labbooking.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author: skyed
+ * @date: 2021-4-29
+ */
+@Data
+public class LabTableVO {
+
+ /**
+ * 实验室id
+ */
+ private Long id;
+
+ /**
+ * 实验室id,规避命名冲突
+ */
+ private Long labId;
+
+ /**
+ * 实验室管理员邮箱
+ */
+ private String email;
+
+ /**
+ * 课程id
+ */
+ private Long courseId;
+
+ /**
+ * 预约的周次列表
+ */
+ private List checkedValue;
+
+ /**
+ * 年份
+ */
+ private Long year;
+
+ /**
+ * 学期
+ */
+ private Long term;
+
+ private List dataList;
+
+ /**
+ * 周次[1-18]
+ */
+ private String week;
+
+ /**
+ * 周次列表
+ */
+ private List weekList;
+
+ /**
+ * 星期[1-7]
+ */
+ private Long day;
+
+ /**
+ * 节次[1-5(1-2,3-4,...)]
+ */
+ private Long time;
+
+ /**
+ * 用户昵称
+ */
+ private String teacherName;
+
+ /**
+ * 课程名称
+ */
+ private String courseName;
+
+ /**
+ * 班级名称[多个用逗号隔开]
+ */
+ private String className;
+
+ /**
+ * 实验室位置[5-101]
+ */
+ private String location;
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabUseVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabUseVO.java
new file mode 100644
index 0000000..a94755a
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabUseVO.java
@@ -0,0 +1,75 @@
+package com.hzu.labbooking.domain.vo;
+
+import java.math.BigDecimal;
+
+public class LabUseVO {
+ /**
+ * 实验室名称
+ */
+ private String labName;
+ /**
+ * 使用学时
+ */
+ private Integer use;
+ /**
+ * 总共学时
+ */
+ private Integer all;
+ /**
+ * 实验室利用率
+ */
+ private BigDecimal rate;
+
+ public LabUseVO() {
+ }
+
+ public String getLabName() {
+ return labName;
+ }
+
+ public void setLabName(String labName) {
+ this.labName = labName;
+ }
+
+ public Integer getUse() {
+ return use;
+ }
+
+ public void setUse(Integer use) {
+ this.use = use * 2;
+ }
+
+ public Integer getAll() {
+ return all;
+ }
+
+ public void setAll(Integer all) {
+ this.all = all;
+ }
+
+ public BigDecimal getRate() {
+ return rate;
+ }
+
+ public void setRate(BigDecimal rate) {
+ this.rate = rate;
+ }
+
+ public void setAll() {
+ this.all = 18 * 7 * 5 * 2;
+ }
+
+ public void setRate() {
+ this.rate = new BigDecimal(this.use).divide(new BigDecimal(this.all), 3, BigDecimal.ROUND_HALF_UP);
+ }
+
+ @Override
+ public String toString() {
+ return "LabUseVO{" +
+ "labName='" + labName + '\'' +
+ ", use=" + use +
+ ", all=" + all +
+ ", rate=" + rate +
+ '}';
+ }
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabVO.java
new file mode 100644
index 0000000..247fa86
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/LabVO.java
@@ -0,0 +1,63 @@
+package com.hzu.labbooking.domain.vo;
+
+import com.ruoyi.common.annotation.Excel;
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+
+/**
+ * @author: skyed
+ * @since: 2021/5/1
+ */
+@Data
+public class LabVO {
+
+
+ private Long id;
+
+ @Excel(name = "实验室名称")
+ private String labName;
+
+ private Long deptId;
+
+ @Excel(name = "所属学院名称")
+ private String deptName;
+
+ @Excel(name = "实验室位置")
+ private String location;
+
+ @Excel(name = "容纳人数")
+ private Long capacity;
+
+ @Excel(name = "实验条件")
+ private String requirement;
+
+ private Long userId;
+
+ @Excel(name = "管理员名称")
+ private String userName;
+
+ @Excel(name = "电子邮箱")
+ private String email;
+
+ @Excel(name = "电话号码")
+ private String phoneNumber;
+
+ private String office;
+
+ @Excel(name = "实验室备注")
+ private String remarks;
+
+ // id: 1,
+ // labName: '实验室名称',
+ // location: '位置',
+ // capacity: 50,
+ // requirement: '软件',
+ // userId: 1,
+ // userName: '叶子豪',
+ // email: '497875761@qq.com',
+ // phoneNumber: '138415312',
+ // office: '办公1楼',
+ // remarks: '备注备注备注备注备注备注备注备注备注'
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyCourseVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyCourseVO.java
new file mode 100644
index 0000000..f878449
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyCourseVO.java
@@ -0,0 +1,80 @@
+package com.hzu.labbooking.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.MpBaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 个人课程信息VO
+ *
+ * @author yezihao
+ * @date 2021-04-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class MyCourseVO extends MpBaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 课程编号 */
+ @Excel(name = "课程编号")
+ private Long id;
+
+ /** 课程名称 */
+ @Excel(name = "课程名称")
+ private String name;
+
+ /** 授课教师编号 */
+ private Long userId;
+
+ /** 教师编号 */
+ @Excel(name = "教师编号")
+ private String userName;
+
+ /** 教师名称 */
+ @Excel(name = "教师名称")
+ private String nickName;
+
+ /** 电子邮箱 */
+ @Excel(name = "电子邮箱")
+ private String email;
+
+ /** 手机号码 */
+ @Excel(name = "手机号码")
+ private String phonenumber;
+
+ /** 用户所在办公室 */
+ @Excel(name = "办公室")
+ private String office;
+
+ /** 课程条件 */
+ @Excel(name = "课程条件")
+ private String requirement;
+
+ /** 班级名称[多个用逗号隔开] */
+ @Excel(name = "班级名称")
+ private String className;
+
+ /** 课程人数 */
+ @Excel(name = "课程人数")
+ private Long number;
+
+ /** 课程备注 */
+ @Excel(name = "课程备注")
+ private String remarks;
+
+ /** 创建时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date gmtCreate;
+
+ /** 更新时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date gmtModified;
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyLabVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyLabVO.java
new file mode 100644
index 0000000..490222a
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/MyLabVO.java
@@ -0,0 +1,72 @@
+package com.hzu.labbooking.domain.vo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @Auther: cbwei
+ * @create: 2021/5/22 17:14
+ */
+
+@Data
+public class MyLabVO {
+
+
+ private Long id;
+
+ @Excel(name = "实验室名称")
+ private String labName;
+
+ /** 学院id **/
+ private Long deptId;
+
+ @Excel(name = "所属学院名称")
+ private String deptName;
+
+ @Excel(name = "实验室位置")
+ private String location;
+
+ @Excel(name = "容纳人数")
+ private Long capacity;
+
+ @Excel(name = "实验条件")
+ private String requirement;
+
+ private Long userId;
+
+ @Excel(name = "管理员名称")
+ private String userName;
+
+ @Excel(name = "电子邮箱")
+ private String email;
+
+ @Excel(name = "电话")
+ private String phoneNumber;
+
+ @Excel(name = "办公室")
+ private String office;
+
+ @Excel(name = "实验室备注")
+ private String remarks;
+
+ /** 是否开启实验室预约功能[0-关闭, 1-开启] */
+ @Excel(name = "实验室是否可用[0-关闭, 1-开启]")
+ private Long status;
+
+ /** 创建时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date gmtCreate;
+
+ /** 更新时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date gmtModified;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/RecordVO.java b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/RecordVO.java
new file mode 100644
index 0000000..65a8be2
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/domain/vo/RecordVO.java
@@ -0,0 +1,44 @@
+package com.hzu.labbooking.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Auther: cbwei
+ * @create: 2021/5/24 9:40
+ */
+@Data
+public class RecordVO {
+
+ private Long userId;
+ /**
+ * 实验室ID
+ */
+ private Long labId;
+ /**
+ * 预约状态
+ **/
+ private Long status;
+
+ /**
+ * 实验室位置
+ **/
+ private String location;
+
+ /**
+ * 实验室名称
+ **/
+ private String labName;
+
+ /**
+ * 驳回内容
+ **/
+ private String rejectContent;
+
+ /**
+ * 预约ID列表
+ */
+ private List recordIdList;
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/CourseMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/CourseMapper.java
new file mode 100644
index 0000000..8a4f78e
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/CourseMapper.java
@@ -0,0 +1,56 @@
+package com.hzu.labbooking.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.Course;
+
+/**
+ * 课程信息Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface CourseMapper extends BaseMapper {
+
+ /**
+ * 查询课程信息
+ *
+ * @param id 课程信息ID
+ * @return 课程信息
+ */
+ public Course selectCourseById(Long id);
+
+ /**
+ * 查询课程信息列表
+ *
+ * @param course 课程信息
+ * @return 课程信息集合
+ */
+ public List selectCourseList(Course course);
+
+ /**
+ * 新增课程信息
+ *
+ * @param course 课程信息
+ * @return 结果
+ */
+ public int insertCourse(Course course);
+
+ /**
+ * 修改课程信息
+ *
+ * @param course 课程信息
+ * @return 结果
+ */
+ public int updateCourse(Course course);
+
+ /**
+ * 批量删除课程信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteCourseByIds(Long[] ids);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabMapper.java
new file mode 100644
index 0000000..27346a9
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabMapper.java
@@ -0,0 +1,66 @@
+package com.hzu.labbooking.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.Lab;
+import com.hzu.labbooking.domain.vo.LabVO;
+import com.hzu.labbooking.domain.vo.MyLabVO;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 实验室信息Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface LabMapper extends BaseMapper {
+
+ /**
+ * 查询实验室信息
+ *
+ * @param id 实验室信息ID
+ * @return 实验室信息
+ */
+ public Lab selectLabById(Long id);
+
+ /**
+ * 查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @param deptId
+ * @return 实验室信息集合
+ */
+ public List selectBookLabList(@Param("lab") Lab lab,
+ @Param("deptId") Long deptId,
+ @Param("parentDeptId") Long parentDeptId);
+
+ /**
+ * 查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @param deptId
+ * @return 实验室信息集合
+ */
+ public List selectLabList(@Param("lab") Lab lab,
+ @Param("deptId") Long deptId,
+ @Param("parentDeptId") Long parentDeptId,
+ @Param("userId") Long userId);
+
+ /**
+ * 新增实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ public int insertLab(Lab lab);
+
+ /**
+ * 修改实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ public int updateLab(Lab lab);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabRecordMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabRecordMapper.java
new file mode 100644
index 0000000..f5f712f
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabRecordMapper.java
@@ -0,0 +1,32 @@
+package com.hzu.labbooking.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.LabRecord;
+
+/**
+ * 实验室预约信息Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface LabRecordMapper extends BaseMapper {
+
+ /**
+ * 查询实验室预约信息列表
+ *
+ * @param labRecord 实验室预约信息
+ * @return 实验室预约信息集合
+ */
+ public List selectLabRecordList(LabRecord labRecord);
+
+ /**
+ * 新增实验室预约信息
+ *
+ * @param labRecord 实验室预约信息
+ * @return 结果
+ */
+ public int insertLabRecord(LabRecord labRecord);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabWeekMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabWeekMapper.java
new file mode 100644
index 0000000..bbb27fb
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/LabWeekMapper.java
@@ -0,0 +1,16 @@
+package com.hzu.labbooking.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.LabWeek;
+
+/**
+ * 实验室预约时间段Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface LabWeekMapper extends BaseMapper {
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/OfficeMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/OfficeMapper.java
new file mode 100644
index 0000000..53d25c4
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/OfficeMapper.java
@@ -0,0 +1,46 @@
+package com.hzu.labbooking.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.Office;
+
+/**
+ * 教师信息Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface OfficeMapper extends BaseMapper {
+
+ /**
+ * 查询教师信息
+ *
+ * @param id 教师信息ID
+ * @return 教师信息
+ */
+ public Office selectOfficeById(Long id);
+
+ /**
+ * 新增教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ public int insertOffice(Office office);
+
+ /**
+ * 修改教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ public int updateOffice(Office office);
+
+ /**
+ * 批量删除教师信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteOfficeByIds(Long[] ids);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/mapper/RecordWeekMapper.java b/lab-booking/src/main/java/com/hzu/labbooking/mapper/RecordWeekMapper.java
new file mode 100644
index 0000000..f04cce3
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/mapper/RecordWeekMapper.java
@@ -0,0 +1,16 @@
+package com.hzu.labbooking.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hzu.labbooking.domain.RecordWeek;
+
+/**
+ * 预约信息周次映射Mapper接口
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+public interface RecordWeekMapper extends BaseMapper {
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/ICourseService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/ICourseService.java
new file mode 100644
index 0000000..3e40d6b
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/ICourseService.java
@@ -0,0 +1,64 @@
+package com.hzu.labbooking.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.Course;
+import com.hzu.labbooking.domain.vo.MyCourseVO;
+
+/**
+ * 课程信息Service接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface ICourseService extends IService {
+
+ /**
+ * 查询个人封装课程信息
+ *
+ * @param id 课程信息ID
+ * @return 课程信息
+ */
+ public MyCourseVO selectCourseById(Long id);
+
+ /**
+ * 查询个人课程信息列表
+ *
+ * @param course 个人课程信息
+ * @return 个人课程信息集合
+ */
+ public List selectCourseList(Course course);
+
+ /**
+ * 新增个人课程信息
+ *
+ * @param course 个人课程信息
+ * @return 结果
+ */
+ public int insertCourse(Course course);
+
+ /**
+ * 修改课程信息
+ *
+ * @param course 课程信息
+ * @return 结果
+ */
+ public int updateCourse(Course course);
+
+ /**
+ * 批量删除课程信息
+ *
+ * @param ids 需要删除的课程信息ID
+ * @return 结果
+ */
+ public int deleteCourseByIds(Long[] ids);
+
+ /**
+ * 查询个人课程列表
+ *
+ * @return 个人课程信息集合
+ */
+ public List selectCourse();
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/ILabRecordService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabRecordService.java
new file mode 100644
index 0000000..8d3d60e
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabRecordService.java
@@ -0,0 +1,88 @@
+package com.hzu.labbooking.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.LabRecord;
+import com.hzu.labbooking.domain.vo.CollegeApplyVO;
+import com.hzu.labbooking.domain.vo.LabRecordVO;
+import com.hzu.labbooking.domain.vo.LabTableVO;
+import com.hzu.labbooking.domain.vo.RecordVO;
+
+/**
+ * 实验室预约信息Service接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface ILabRecordService extends IService {
+
+ /**
+ * 查询实验室预约信息列表
+ *
+ * @param labRecord 实验室预约信息
+ * @return 实验室预约信息集合
+ */
+ public List selectLabRecordList(LabRecord labRecord, long year, long term);
+
+ /**
+ * 添加预约到记录及周次
+ *
+ * @param vo 添加预约记录VO
+ * @param userId 申请人ID
+ * @return 结果
+ */
+ public int addRecordAndWeek(LabTableVO vo, Long userId);
+
+ /**
+ * 管理员审核实验室预约列表
+ *
+ * @param vos 审批预约VO列表
+ * @return 结果
+ */
+ public Boolean approveBooking(List vos);
+
+ /**
+ * 取消预约记录
+ *
+ * @param recordIdList 预约记录id
+ * @return 实验室预约信息集合
+ */
+ public Boolean cancelBooking(List recordIdList);
+
+ /**
+ * 管理员查询实验室预约列表
+ *
+ * @param labRecord 预约记录
+ * @param year 学年
+ * @param term 学期
+ * @return 结果
+ */
+ public List selectManagerRecordList(LabRecord labRecord, long year, long term);
+
+ /**
+ * 管理员取消预约记录
+ *
+ * @param vos 预约记录VO列表
+ * @return 结果
+ */
+ public Boolean managerCancelBooking(List vos);
+
+ /**
+ * 查询实验室预约信息列表
+ *
+ * @param year 学年
+ * @param term 学期
+ * @return 查询实验室预约信息列表
+ */
+ public List selectCollegeApply(Long year, Long term);
+
+ /**
+ * 新增实验室预约信息
+ *
+ * @param labRecord 实验室预约信息
+ * @return 结果
+ */
+ public int insertLabRecord(LabRecord labRecord);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/ILabService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabService.java
new file mode 100644
index 0000000..a61e326
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabService.java
@@ -0,0 +1,109 @@
+package com.hzu.labbooking.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.Lab;
+import com.hzu.labbooking.domain.vo.*;
+
+/**
+ * 实验室信息Service接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface ILabService extends IService {
+
+ /**
+ * 查询实验室信息
+ *
+ * @param id 实验室信息ID
+ * @return 实验室信息
+ */
+ public Lab selectLabById(Long id);
+
+ /**
+ * 查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @return 实验室信息集合
+ */
+ public List selectBookLabList(Lab lab);
+
+ /**
+ * 管理员查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @return 实验室信息集合
+ */
+ public List selectLabList(Lab lab);
+
+ /**
+ * 新增实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ public int insertLab(Lab lab);
+
+ /**
+ * 修改实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ public int updateLab(Lab lab);
+
+ /**
+ * 获取所有实验室课表
+ *
+ * @param year 年
+ * @param term 学期
+ * @return 结果
+ */
+ public List getAllLabTable(Long year, Long term);
+
+ /**
+ * 获取课表详细信息
+ *
+ * @param labId 实验室ID
+ * @return 结果
+ */
+ public LabTableDetailsVO getTableDetails(Long labId);
+
+ /**
+ * 获取实验室使用率
+ *
+ * @return 结果
+ */
+ public List getLabUseRate(Long year, Long term);
+
+ /**
+ * 获取某个实验室所有可预约课表数据
+ *
+ * @param labId 实验室ID
+ * @param year 学年
+ * @param term 学期
+ * @return 结果
+ */
+ public List getAllApplyTable(long labId, long year, long term);
+
+ /**
+ * 获取某个实验室所有预约情况数据
+ *
+ * @param labId 实验室ID
+ * @param year 学年
+ * @param term 学期
+ * @return 结果
+ */
+ public List getAllAppliedTable(long labId, long year, long term);
+
+ /**
+ * 更新实验室状态
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ public int updateLabStatus(Lab lab);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/ILabWeekService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabWeekService.java
new file mode 100644
index 0000000..2d70a17
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/ILabWeekService.java
@@ -0,0 +1,17 @@
+package com.hzu.labbooking.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.LabWeek;
+
+/**
+ * 实验室预约时间段Service接口
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+public interface ILabWeekService extends IService {
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/IOfficeService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/IOfficeService.java
new file mode 100644
index 0000000..327a196
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/IOfficeService.java
@@ -0,0 +1,46 @@
+package com.hzu.labbooking.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.Office;
+
+/**
+ * 教师信息Service接口
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+public interface IOfficeService extends IService {
+
+ /**
+ * 查询教师信息
+ *
+ * @param id 教师信息ID
+ * @return 教师信息
+ */
+ public Office selectOfficeById(Long id);
+
+ /**
+ * 新增教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ public int insertOffice(Office office);
+
+ /**
+ * 修改教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ public int updateOffice(Office office);
+
+ /**
+ * 批量删除教师信息
+ *
+ * @param ids 需要删除的教师信息ID
+ * @return 结果
+ */
+ public int deleteOfficeByIds(Long[] ids);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/IRecordWeekService.java b/lab-booking/src/main/java/com/hzu/labbooking/service/IRecordWeekService.java
new file mode 100644
index 0000000..13770cb
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/IRecordWeekService.java
@@ -0,0 +1,32 @@
+package com.hzu.labbooking.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hzu.labbooking.domain.RecordWeek;
+
+/**
+ * 预约信息周次映射Service接口
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+public interface IRecordWeekService extends IService {
+
+ /**
+ * 通过recordID查询对应weekID
+ *
+ * @param recordId 预约记录ID列表
+ * @return 结果
+ */
+ List selectWeekList(Long recordId);
+
+ /**
+ * 通过recordID查询所有对应weekID
+ *
+ * @param recordIdList 预约记录ID列表
+ * @return 结果
+ */
+ List selectAllWeekList(List recordIdList);
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/CourseServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/CourseServiceImpl.java
new file mode 100644
index 0000000..3fbbd67
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/CourseServiceImpl.java
@@ -0,0 +1,128 @@
+package com.hzu.labbooking.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hzu.labbooking.domain.vo.MyCourseVO;
+import com.hzu.labbooking.service.IOfficeService;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.ruoyi.framework.web.service.TokenService;
+import org.springframework.stereotype.Service;
+import com.hzu.labbooking.mapper.CourseMapper;
+import com.hzu.labbooking.domain.Course;
+import com.hzu.labbooking.service.ICourseService;
+
+/**
+ * 课程信息Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+@Service
+public class CourseServiceImpl extends ServiceImpl implements ICourseService {
+
+ @Autowired
+ private CourseMapper courseMapper;
+
+ @Autowired
+ private TokenService tokenService;
+
+ @Autowired
+ private IOfficeService officeService;
+
+ @Autowired
+ private ISysUserService userService;
+
+ /**
+ * 修改课程信息
+ *
+ * @param course 课程信息
+ * @return 结果
+ */
+ @Override
+ public int updateCourse(Course course) {
+ return courseMapper.updateCourse(course);
+ }
+
+ /**
+ * 批量删除课程信息
+ *
+ * @param ids 需要删除的课程信息ID
+ * @return 结果
+ */
+ @Override
+ public int deleteCourseByIds(Long[] ids) {
+ return courseMapper.deleteCourseByIds(ids);
+ }
+
+ @Override
+ public List selectCourse() {
+ SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq("user_id", user.getUserId());
+ List list = baseMapper.selectList(wrapper);
+ for (Course course : list) {
+ course.setCourseName(course.getName());
+ course.setCourseId(course.getId());
+ }
+ return list;
+ }
+
+ @Override
+ public List selectCourseList(Course course) {
+ SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
+ String office = officeService.selectOfficeById(user.getUserId()).getOffice();
+ // 拼接检索条件
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq("user_id", user.getUserId()).eq("year", course.getYear()).eq("term", course.getTerm())
+ .like(StringUtils.isNotEmpty(course.getName()), "name", course.getName())
+ .like(StringUtils.isNotEmpty(course.getRequirement()), "requirement", course.getRequirement())
+ .like(StringUtils.isNotEmpty(course.getClassName()), "class_name", course.getClassName())
+ .eq(StringUtils.isNotNull(course.getNumber()), "number", course.getNumber());
+
+ List courseList = baseMapper.selectList(wrapper);
+ // 封装课程VO
+ List list = new ArrayList<>();
+ for (Course myCourse : courseList) {
+ MyCourseVO myCourseVO = new MyCourseVO();
+ BeanUtils.copyProperties(myCourse, myCourseVO);
+ myCourseVO.setNickName(user.getNickName());
+ myCourseVO.setEmail(user.getEmail());
+ myCourseVO.setPhonenumber(user.getPhonenumber());
+ myCourseVO.setUserName(user.getUserName());
+ myCourseVO.setOffice(office);
+ list.add(myCourseVO);
+ }
+ return list;
+ }
+
+ @Override
+ public MyCourseVO selectCourseById(Long id) {
+ Course course = courseMapper.selectCourseById(id);
+ SysUser user = userService.selectUserById(course.getUserId());
+
+ MyCourseVO myCourseVO = new MyCourseVO();
+ BeanUtils.copyProperties(course, myCourseVO);
+ myCourseVO.setNickName(user.getNickName());
+ myCourseVO.setUserName(user.getUserName());
+ myCourseVO.setEmail(user.getEmail());
+ myCourseVO.setPhonenumber(user.getPhonenumber());
+ myCourseVO.setOffice(officeService.selectOfficeById(user.getUserId()).getOffice());
+
+ return myCourseVO;
+ }
+
+ @Override
+ public int insertCourse(Course course) {
+ course.setUserId(tokenService.getLoginUser(ServletUtils.getRequest()).getUser().getUserId());
+ return baseMapper.insert(course);
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabRecordServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabRecordServiceImpl.java
new file mode 100644
index 0000000..3903527
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabRecordServiceImpl.java
@@ -0,0 +1,874 @@
+package com.hzu.labbooking.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hzu.labbooking.constant.LabStatusConstansts;
+import com.hzu.labbooking.constant.RecordStatusConstants;
+import com.hzu.labbooking.constant.WeekStatusConstants;
+import com.hzu.labbooking.domain.*;
+import com.hzu.labbooking.domain.component.*;
+import com.hzu.labbooking.domain.vo.CollegeApplyVO;
+import com.hzu.labbooking.domain.vo.LabRecordVO;
+import com.hzu.labbooking.domain.vo.LabTableVO;
+import com.hzu.labbooking.domain.vo.RecordVO;
+import com.hzu.labbooking.mapper.LabRecordMapper;
+import com.hzu.labbooking.mapper.LabWeekMapper;
+import com.hzu.labbooking.service.*;
+import com.hzu.labbooking.utils.SendMailUtils;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.exception.BaseException;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.service.ISysDeptService;
+import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 实验室预约信息Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-06-14
+ */
+@Service
+public class LabRecordServiceImpl extends ServiceImpl implements ILabRecordService {
+
+ @Autowired
+ private LabRecordMapper labRecordMapper;
+ @Autowired
+ private ILabWeekService weekService;
+ @Autowired
+ private ILabService labService;
+ @Autowired
+ private SendMailUtils sendMailUtils;
+ @Autowired
+ private ThreadPoolExecutor executor;
+ @Autowired
+ private TokenService tokenService;
+ @Autowired
+ private ICourseService courseService;
+ @Autowired
+ private LabServiceImpl labServiceImpl;
+ @Autowired
+ private ISysUserService userService;
+ @Autowired
+ private IRecordWeekService recordWeekService;
+ @Autowired
+ private LabWeekMapper weekMapper;
+ @Autowired
+ private ISysDeptService deptService;
+
+ /**
+ * 查询实验室预约信息列表
+ *
+ * @param labRecord 实验室预约信息
+ * @return 实验室预约信息
+ */
+ @Override
+ public List selectLabRecordList(LabRecord labRecord, long year, long term) {
+ /*
+ 1.根据user_id查出当前用户所有预约记录
+ 2.模糊检索:根据course_id检索courseName和className,通过查出的预约记录来筛选
+ 3.根据预约记录id获取对应的weekIdList,并封装聚合课程
+ 4.遍历预约记录map,封装week
+ */
+ // 1.根据user_id查出当前用户所有预约记录
+ SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
+
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper.eq("user_id", user.getUserId());
+ recordQueryWrapper.eq("year", year);
+ recordQueryWrapper.eq("term", term);
+
+ if (labRecord.getStatus() != null) {
+ recordQueryWrapper.eq("status", labRecord.getStatus());
+ }
+
+ recordQueryWrapper.orderByDesc("gmt_create");
+ List tmpRecordList = this.list(recordQueryWrapper);
+
+ if (tmpRecordList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // 2.模糊检索:根据course_id检索courseName和className,通过查出的预约记录来筛选
+ String name = labRecord.getName();
+
+ Set courseIdList = new HashSet<>();
+ for (LabRecord record : tmpRecordList) {
+ courseIdList.add(record.getCourseId());
+ }
+
+ QueryWrapper courseQueryWrapper = new QueryWrapper<>();
+ courseQueryWrapper.select("id");
+ courseQueryWrapper.in("id", courseIdList);
+ if (StringUtils.isNotEmpty(name)) {
+ courseQueryWrapper.like("name", name);
+ courseQueryWrapper.or();
+ courseQueryWrapper.like("class_name", name);
+ }
+ List courseList = courseService.list(courseQueryWrapper);
+
+ Set courseIdSet = new HashSet<>();
+ for (Course course : courseList) {
+ courseIdSet.add(course.getId());
+ }
+
+ List recordList = new ArrayList<>(); // 存放模糊检索后的结果
+ for (LabRecord record : tmpRecordList) {
+ if (courseIdSet.contains(record.getCourseId())) {
+ recordList.add(record);
+ }
+ }
+
+ // 3.根据预约记录id获取对应的weekIdList,并封装聚合课程
+ Map recordWeekMap = new HashMap<>();
+ for (LabRecord record : recordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ //weekQueryWrapper.ne("status", WeekStatusConstants.OPEN);
+
+ // 获取当前预约所有周次
+ List labWeekList = weekMapper.selectList(weekQueryWrapper);
+
+ if (labWeekList.isEmpty()) {
+ continue;
+ }
+
+ RecordTableKey recordTableKey = new RecordTableKey();
+ RecordTableValue recordTableValue = new RecordTableValue();
+ BeanUtils.copyProperties(record, recordTableKey);
+ BeanUtils.copyProperties(record, recordTableValue);
+
+ List recordIdList = new ArrayList<>();
+ recordIdList.add(record.getId());
+ recordTableValue.setRecordIdList(recordIdList);
+
+ // 若遇到相同预约,不同周次,将其添加到周次中
+ if (!recordWeekMap.containsKey(recordTableKey)) {
+ recordTableValue.setWeekList(labWeekList);
+ recordWeekMap.put(recordTableKey, recordTableValue);
+ } else {
+ RecordTableValue value = recordWeekMap.get(recordTableKey);
+ value.getRecordIdList().add(record.getId());
+ value.getWeekList().addAll(labWeekList);
+ }
+ }
+
+ // 4.遍历预约记录map,封装week
+ List voList = new ArrayList<>();
+ for (RecordTableKey key : recordWeekMap.keySet()) {
+ RecordTableValue value = recordWeekMap.get(key);
+
+ List weekList = new ArrayList<>();
+ for (LabWeek week : value.getWeekList()) {
+ weekList.add(week.getWeek());
+ }
+
+ Course course = courseService.getById(key.getCourseId());
+ Lab lab = labService.getById(key.getLabId());
+
+ LabRecordVO vo = new LabRecordVO();
+ vo.setYear(year);
+ vo.setTerm(term);
+ vo.setUserId(user.getUserId());
+ vo.setNickName(user.getNickName());
+ vo.setClassName(course.getClassName());
+ vo.setCourseName(course.getName());
+ vo.setNumber(course.getNumber());
+ vo.setDay(key.getDay());
+ vo.setTime(key.getTime());
+ vo.setRejectContent(value.getRejectContent());
+ vo.setLocation(lab.getLocation());
+ vo.setLabName(lab.getName());
+ vo.setWeekList(weekList);
+ vo.setWeek(labServiceImpl.convert(weekList));
+ vo.setStatus(value.getStatus());
+ vo.setGmtModified(value.getGmtModified());
+ vo.setRecordIdList(value.getRecordIdList());
+ voList.add(vo);
+ }
+
+ // 结果更新时间降序显示(最新在前)
+ //voList.sort(new Comparator() {
+ // @Override
+ // public int compare(LabRecordVO o1, LabRecordVO o2) {
+ // return (int) -(o1.getGmtModified().getTime() - o2.getGmtModified().getTime());
+ // }
+ //});
+
+ return voList;
+ }
+
+ @Transactional
+ @Override
+ public Boolean cancelBooking(List recordIdList) {
+ /*
+ 1.修改预约记录状态status=3,表示已取消
+ 2.获取该预约所有weekId
+ 3.修改week状态status=1,表示可预约
+ */
+
+ if (recordIdList.isEmpty()) {
+ return false;
+ }
+
+ // 1.修改预约记录状态status=3,表示已取消
+ UpdateWrapper updateWrapper = new UpdateWrapper<>();
+ updateWrapper.in("id", recordIdList);
+ updateWrapper.set("status", RecordStatusConstants.CANCELLED);
+ updateWrapper.ne("status", RecordStatusConstants.REJECTED);
+ updateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean update = this.update(updateWrapper);
+ if (!update) {
+ throw new BaseException("取消失败");
+ }
+
+ // 2.通过recordIdList获取该预约所有weekId
+ List weekIdList = recordWeekService.selectAllWeekList(recordIdList);
+
+ // 3.修改week状态status=1,表示可预约
+ UpdateWrapper weekUpdateWrapper = new UpdateWrapper<>();
+ weekUpdateWrapper.set("status", WeekStatusConstants.OPEN);
+ weekUpdateWrapper.in("id", weekIdList);
+ weekUpdateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean update1 = weekService.update(weekUpdateWrapper);
+ if (!update1) {
+ throw new BaseException("取消失败");
+ }
+
+ return true;
+ }
+
+ /**
+ * 每学期/每个学院实验室申请率(扇形展示eCharts)
+ * 统计每个lab_id个数,通过lab_id查出实验室所属学院名称,封装返回
+ * 1.查出指定学年学期所有预约记录
+ * 2.通过lab_id查出实验室所属学院名称(多个重复,记录个数)
+ * 3.通过lab_id检索出实验室所属学院,然后再检索出学院名称
+ */
+ @Override
+ public List selectCollegeApply(Long year, Long term) {
+
+ // 1.查出指定学年学期所有预约记录
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper.eq("year", year).eq("term", term);
+ List tmpRecordList = this.list(recordQueryWrapper);
+
+ if (tmpRecordList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // 2.通过lab_id查出实验室所属学院名称(多个重复,记录个数)
+ Map labIdCountMap = new HashMap<>();
+ for (LabRecord record : tmpRecordList) {
+ labIdCountMap.put(record.getLabId(), labIdCountMap.getOrDefault(record.getLabId(), 0) + 1);
+ }
+
+ // 3.通过lab_id检索出实验室所属学院,然后再检索出学院名称
+ List voList = new ArrayList<>();
+ for (Long labId : labIdCountMap.keySet()) {
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.select("dept_id").eq("id", labId);
+ Long deptId = labService.getOne(wrapper).getDeptId();
+
+ String deptName = deptService.selectDeptById(deptId).getDeptName();
+ CollegeApplyVO vo = new CollegeApplyVO();
+ vo.setName(deptName);
+ vo.setValue(labIdCountMap.get(labId));
+ voList.add(vo);
+ }
+
+ return voList;
+ }
+
+ /**
+ * 添加预约记录和时间
+ */
+ @Transactional
+ @Override
+ public int addRecordAndWeek(LabTableVO vo, Long userId) {
+ List weekList = vo.getCheckedValue(); // 选择的周次
+ if (weekList.isEmpty()) {
+ throw new BaseException("没有插入任何预约记录");
+ }
+
+ // 1.校验容量,capacity和number
+ Lab lab = labService.getById(vo.getLabId());
+ Course course = courseService.getById(vo.getCourseId());
+ if (lab.getCapacity() < course.getNumber()) {
+ throw new BaseException("该实验室无法容纳!");
+ }
+
+ // 2.首先校验时间段是否已存在预约;存在是否审批,week是否被占用;
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper
+ .eq("lab_id", vo.getLabId())
+ .eq("year", vo.getYear())
+ .eq("term", vo.getTerm())
+ .eq("day", vo.getDay())
+ .eq("time", vo.getTime())
+ .ne("status", RecordStatusConstants.REJECTED)
+ .ne("status", RecordStatusConstants.CANCELLED);
+ List tmpRecordList = this.list(recordQueryWrapper);
+
+ Map recordMap = new HashMap<>();
+
+ if (!tmpRecordList.isEmpty()) {
+ // 之前预约过,聚合之前预约过的记录,只聚合待审批状态
+ Map> mp = new HashMap<>();
+ for (LabRecord record : tmpRecordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ List labWeekList = weekService.list(weekQueryWrapper);
+ // 封装day,time对应预约周次
+ MapKey key = new MapKey(record.getDay(), record.getTime());
+
+ List labWeeks = new ArrayList<>(labWeekList);
+ RecordKey recordKey =
+ new RecordKey(record.getUserId(), record.getCourseId(), record.getStatus());
+ RecordValue recordValue = new RecordValue(record.getId(), labWeeks);
+ recordMap.put(recordKey, recordValue);
+
+ if (!mp.containsKey(key)) {
+ mp.put(key, labWeekList);
+ } else {
+ List value = mp.get(key);
+ value.addAll(labWeekList);
+ }
+ }
+
+ for (MapKey key : mp.keySet()) {
+ List value = mp.get(key);
+ // 判断时间段是否被占用,关闭状态表示不可预约
+ for (LabWeek week : value) {
+ if (weekList.contains(week.getWeek()) &&
+ week.getStatus() == WeekStatusConstants.CLOSED) {
+ throw new BaseException("该时间段已被占用");
+ }
+ }
+ }
+ }
+
+ // 3.判断当前用户是否在该时间段已有预约,若为自己先前预约,修改record,添加week与record_week
+ // 否则新增record,添加week与record_week
+ // 注意:存在记录,但week状态为1可预约状态,则修改,修改week时将status置为0是幂等的;
+ int recordCnt = 0; // 统计record插入数据个数
+
+ RecordKey key = new RecordKey();
+ key.setUserId(userId);
+ key.setCourseId(vo.getCourseId());
+ key.setStatus((long) RecordStatusConstants.NOT_APPROVE);
+
+ // 不存在相同预约记录
+ if (!recordMap.containsKey(key)) {
+ // 封装record
+ LabRecord newRecord = new LabRecord();
+ newRecord.setUserId(userId);
+ newRecord.setCourseId(vo.getCourseId());
+ newRecord.setLabId(vo.getLabId());
+ newRecord.setYear(vo.getYear());
+ newRecord.setTerm(vo.getTerm());
+ newRecord.setDay(vo.getDay());
+ newRecord.setTime(vo.getTime());
+ newRecord.setStatus((long) RecordStatusConstants.NOT_APPROVE);
+
+ int r = this.insertLabRecord(newRecord);
+ if (r <= 0) {
+ throw new BaseException("预约失败");
+ }
+ // 封装week
+ List labWeekList = new ArrayList<>();
+ for (Long week : weekList) {
+ LabWeek labWeek = new LabWeek();
+ labWeek.setWeek(week);
+ labWeek.setStatus((long) WeekStatusConstants.CLOSED);
+ labWeekList.add(labWeek);
+ }
+
+ boolean w = weekService.saveBatch(labWeekList);
+ if (!w) {
+ throw new BaseException("预约失败");
+ }
+
+ // 封装record_week
+ List recordWeekList = new ArrayList<>();
+ for (LabWeek labWeek : labWeekList) {
+ RecordWeek recordWeek = new RecordWeek();
+ recordWeek.setRecordId(newRecord.getId());
+ recordWeek.setWeekId(labWeek.getId());
+ recordWeekList.add(recordWeek);
+ }
+
+ boolean rw = recordWeekService.saveBatch(recordWeekList);
+ if (!rw) {
+ throw new BaseException("预约失败");
+ }
+ ++recordCnt;
+ } else {
+ // 先前预约过,且状态为待审批0,修改record状态/时间,直接插入week、record_week
+ // 若checkedValue对应week状态为1打开状态,则修改,否则新增记录
+ RecordValue value = recordMap.get(key);
+
+ // 1.修改预约记录状态
+ UpdateWrapper wrapper = new UpdateWrapper<>();
+ wrapper.set("status", RecordStatusConstants.NOT_APPROVE);
+ wrapper.eq("id", value.getRecordId());
+ wrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean r = this.update(wrapper);
+ if (!r) {
+ throw new BaseException("预约失败");
+ }
+ // 2.找出需要修改的week的id,修改其状态为0关闭状态
+ List existsWeek = value.getLabWeekList();
+ List updateIds = new ArrayList<>();
+ for (LabWeek week : existsWeek) {
+ if (weekList.contains(week.getWeek())) {
+ updateIds.add(week.getId());
+ }
+ }
+ if (!updateIds.isEmpty()) {
+ UpdateWrapper updateWrapper = new UpdateWrapper<>();
+ updateWrapper.set("status", WeekStatusConstants.CLOSED);
+ updateWrapper.in("id", updateIds);
+ updateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean uw = weekService.update(updateWrapper);
+ if (!uw) {
+ throw new BaseException("预约失败");
+ }
+
+ // 3.求剩下week的差集添加到week/record_week
+ weekList.removeAll(updateIds);
+ }
+
+ // 封装week
+ List labWeekList = new ArrayList<>();
+ for (Long week : weekList) {
+ LabWeek labWeek = new LabWeek();
+ labWeek.setWeek(week);
+ labWeek.setStatus((long) WeekStatusConstants.CLOSED);
+ labWeekList.add(labWeek);
+ }
+
+ boolean w = weekService.saveBatch(labWeekList);
+ if (!w) {
+ throw new BaseException("预约失败");
+ }
+ // 封装record_week
+ List recordWeekList = new ArrayList<>();
+ for (LabWeek labWeek : labWeekList) {
+ RecordWeek recordWeek = new RecordWeek();
+ recordWeek.setRecordId(value.getRecordId());
+ recordWeek.setWeekId(labWeek.getId());
+ recordWeekList.add(recordWeek);
+ }
+
+ boolean rw = recordWeekService.saveBatch(recordWeekList);
+ if (!rw) {
+ throw new BaseException("预约失败");
+ }
+ ++recordCnt;
+ }
+
+ // 4.邮件提醒
+ CompletableFuture.runAsync(() -> {
+ sendEmail("实验室预约审核提醒", vo.getEmail(),
+ "实验室预约系统中有用户进行了实验室预约,请前往审核");
+ }, executor);
+
+ return recordCnt;
+ }
+
+ @Transactional
+ @Override
+ public Boolean approveBooking(List vos) {
+ if (vos.isEmpty()) {
+ return false;
+ }
+ for (RecordVO vo : vos) {
+ Boolean approve = approve(vo);
+ if (!approve) {
+ throw new BaseException("审批失败");
+ }
+ }
+ return true;
+ }
+
+ private Boolean approve(RecordVO vo) {
+ List recordIdList = vo.getRecordIdList();
+ if (recordIdList.isEmpty()) {
+ return false;
+ }
+
+ Lab lab = labService.selectLabById(vo.getLabId());
+ if (lab.getStatus() == LabStatusConstansts.DELETED) {
+ throw new BaseException("实验室已删除,审批失败");
+ }
+
+ Long status = vo.getStatus();
+ String labName = vo.getLabName();
+ String location = vo.getLocation();
+ String rejectContent = vo.getRejectContent();
+ // 获取预约用户信息
+ SysUser user = userService.selectUserById(vo.getUserId());
+ String email = user.getEmail();
+ String nickName = user.getNickName();
+
+
+ // 修改record状态status为指定状态,同时修改week状态
+ //通过预约状态判断是否通过或驳回
+ if (status == RecordStatusConstants.APPROVE) {
+
+ //审批通过,修改预约记录状态为通过
+ UpdateWrapper updateWrapper = new UpdateWrapper<>();
+ updateWrapper.in("id", recordIdList);
+ updateWrapper.set("status", RecordStatusConstants.APPROVE);
+ updateWrapper.ne("status", RecordStatusConstants.REJECTED);
+ updateWrapper.ne("status", RecordStatusConstants.APPROVE);
+ updateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean u = this.update(updateWrapper);
+ if (!u) {
+ throw new BaseException("审批通过失败");
+ }
+ // 预约提交时已经确定week关闭状态,这里不需要修改
+
+ // 发送邮件提示成功
+ CompletableFuture.runAsync(() -> {
+ sendEmail("实验室审批通过提醒", email,
+ nickName + " 老师,你预约的 " + labName + " 已审批通过,请到 " + location + " 上课");
+ }, executor);
+
+ } else if (status == RecordStatusConstants.REJECTED) {
+
+ //审批失败,修改预约记录状态为通过
+ UpdateWrapper updateWrapper = new UpdateWrapper<>();
+ updateWrapper.in("id", recordIdList);
+ updateWrapper.set("status", RecordStatusConstants.REJECTED);
+ updateWrapper.set("reject_content", rejectContent);
+ updateWrapper.ne("status", RecordStatusConstants.APPROVE);
+ updateWrapper.ne("status", RecordStatusConstants.REJECTED);
+ updateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean update = this.update(updateWrapper);
+ if (!update) {
+ throw new BaseException("审批驳回失败");
+ }
+
+ // 根据record_id获取week_id,然后修改week状态为可开启状态
+ List weekIdList = recordWeekService.selectAllWeekList(recordIdList);
+
+ UpdateWrapper weeUpdateWrapper = new UpdateWrapper<>();
+ weeUpdateWrapper.set("status", WeekStatusConstants.OPEN);
+ weeUpdateWrapper.in("id", weekIdList);
+ weeUpdateWrapper.set("gmt_modified", LocalDateTime.now());
+
+ boolean u1 = weekService.update(weeUpdateWrapper);
+ if (!u1) {
+ throw new BaseException("审批驳回失败");
+ }
+
+ //发送邮件给老师
+ CompletableFuture.runAsync(() -> {
+ sendEmail("实验室审批驳回提醒", email,
+ nickName + " 老师,你预约的 " + labName + " 审批驳回,驳回理由为:"
+ + rejectContent + "。如有需要,请重新预约。");
+ }, executor);
+
+ } else {
+ throw new BaseException("实验室列表预约状态异常");
+ }
+
+ return true;
+ }
+
+ /**
+ * 管理员取消实验室预约
+ */
+ @Transactional
+ @Override
+ public Boolean managerCancelBooking(List vos) {
+ if (vos.isEmpty()) {
+ return false;
+ }
+ for (RecordVO vo : vos) {
+ Boolean cancel = managerCancel(vo);
+ if (!cancel) {
+ throw new BaseException("取消失败");
+ }
+ }
+ return true;
+ }
+
+ private Boolean managerCancel(RecordVO vo) {
+ /*
+ 1.修改预约记录状态status=3,表示已取消
+ 2.获取该预约所有weekId
+ 3.修改week状态status=1,表示可预约
+ */
+ List recordIdList = vo.getRecordIdList();
+ if (recordIdList.isEmpty()) {
+ return false;
+ }
+ Long status = vo.getStatus();
+ if (status == RecordStatusConstants.NOT_APPROVE ||
+ status == RecordStatusConstants.REJECTED ||
+ status == RecordStatusConstants.CANCELLED) {
+ return false;
+ }
+ // 1.修改预约记录状态status=3,表示已取消
+ UpdateWrapper updateWrapper = new UpdateWrapper<>();
+ updateWrapper.in("id", recordIdList);
+ updateWrapper.set("status", RecordStatusConstants.CANCELLED);
+ updateWrapper.set("gmt_modified", LocalDateTime.now());
+ boolean update = this.update(updateWrapper);
+ if (!update) {
+ return false;
+ }
+
+ // 2.通过recordIdList获取该预约所有weekId
+ List weekIdList = recordWeekService.selectAllWeekList(recordIdList);
+
+ // 3.修改week状态status=1,表示可预约
+ UpdateWrapper weekUpdateWrapper = new UpdateWrapper<>();
+ weekUpdateWrapper.set("status", WeekStatusConstants.OPEN);
+ weekUpdateWrapper.in("id", weekIdList);
+ weekUpdateWrapper.set("gmt_modified", LocalDateTime.now());
+ boolean update1 = weekService.update(weekUpdateWrapper);
+ if (!update1) {
+ return false;
+ }
+
+ // 发送邮件给教师
+ SysUser user = userService.selectUserById(vo.getUserId());
+
+ CompletableFuture.runAsync(() -> {
+ sendEmail("实验室审批取消提醒", user.getEmail(),
+ user.getNickName() + " 老师,你预约的 " + vo.getLabName() +
+ " 已被取消,很抱歉给你带来困扰,如有需要,请重新预约。");
+ }, executor);
+
+ return true;
+ }
+
+ /**
+ * 管理员查询实验室预约记录
+ *
+ * @param labRecord 查询VO
+ * @param year 学年
+ * @param term 学期
+ * @return 结果
+ */
+ @Override
+ public List selectManagerRecordList(LabRecord labRecord, long year, long term) {
+ /*
+ 1.根据user_id查出当前用户所有预约记录
+ 2.模糊检索:根据course_id检索courseName和className,通过查出的预约记录来筛选
+ 3.根据预约记录id获取对应的weekIdList,并封装聚合课程
+ 4.遍历预约记录map,封装week
+ */
+ // 1.根据管理员user_id查出拥有的lab_id,根据lab_id查出关联实验室所有预约记录
+ SysUser user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
+
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.select("id").eq("user_id", user.getUserId());
+ List labList = labService.list(queryWrapper);
+ if (labList.isEmpty()) {
+ return new ArrayList<>();
+ }
+ List labIdList = new ArrayList<>();
+ for (Lab lab : labList) {
+ labIdList.add(lab.getId());
+ }
+
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper.in("lab_id", labIdList);
+ recordQueryWrapper.eq("year", year);
+ recordQueryWrapper.eq("term", term);
+
+ if (labRecord.getStatus() != null) {
+ recordQueryWrapper.eq("status", labRecord.getStatus());
+ }
+
+ recordQueryWrapper.orderByDesc("gmt_create");
+ List tmpRecordList = this.list(recordQueryWrapper);
+
+ if (tmpRecordList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // 2.模糊检索:根据course_id检索courseName和className,通过查出的预约记录来筛选
+ String name = labRecord.getName();
+
+ Set courseIdList = new HashSet<>();
+ for (LabRecord record : tmpRecordList) {
+ courseIdList.add(record.getCourseId());
+ }
+
+ QueryWrapper courseQueryWrapper = new QueryWrapper<>();
+ courseQueryWrapper.select("id");
+ courseQueryWrapper.in("id", courseIdList);
+ if (StringUtils.isNotEmpty(name)) {
+ courseQueryWrapper.like("name", name);
+ courseQueryWrapper.or();
+ courseQueryWrapper.like("class_name", name);
+ }
+ List courseList = courseService.list(courseQueryWrapper);
+
+ Set courseIdSet = new HashSet<>();
+ for (Course course : courseList) {
+ courseIdSet.add(course.getId());
+ }
+
+ List recordList = new ArrayList<>(); // 存放模糊检索后的结果
+ for (LabRecord record : tmpRecordList) {
+ if (courseIdSet.contains(record.getCourseId())) {
+ recordList.add(record);
+ }
+ }
+
+ // 3.根据预约记录id获取对应的weekIdList,并封装聚合课程
+ Map recordWeekMap = new HashMap<>();
+ for (LabRecord record : recordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ //weekQueryWrapper.ne("status", WeekStatusConstants.OPEN);
+
+ // 获取当前预约所有周次
+ List labWeekList = weekMapper.selectList(weekQueryWrapper);
+
+ if (labWeekList.isEmpty()) {
+ continue;
+ }
+
+ RecordTableKey recordTableKey = new RecordTableKey();
+ RecordTableValue recordTableValue = new RecordTableValue();
+ BeanUtils.copyProperties(record, recordTableKey);
+ BeanUtils.copyProperties(record, recordTableValue);
+
+ List recordIdList = new ArrayList<>();
+ recordIdList.add(record.getId());
+ recordTableValue.setRecordIdList(recordIdList);
+
+ // 若遇到相同预约,不同周次,将其添加到周次中
+ if (!recordWeekMap.containsKey(recordTableKey)) {
+ recordTableValue.setWeekList(labWeekList);
+ recordWeekMap.put(recordTableKey, recordTableValue);
+ } else {
+ RecordTableValue value = recordWeekMap.get(recordTableKey);
+ value.getRecordIdList().add(record.getId());
+ value.getWeekList().addAll(labWeekList);
+ }
+ }
+
+ // 4.遍历预约记录map,封装week
+ List voList = new ArrayList<>();
+ for (RecordTableKey key : recordWeekMap.keySet()) {
+ RecordTableValue value = recordWeekMap.get(key);
+
+ List weekList = new ArrayList<>();
+ for (LabWeek week : value.getWeekList()) {
+ weekList.add(week.getWeek());
+ }
+
+ Course course = courseService.getById(key.getCourseId());
+ Lab lab = labService.getById(key.getLabId());
+
+ LabRecordVO vo = new LabRecordVO();
+ vo.setYear(year);
+ vo.setTerm(term);
+ vo.setLabId(key.getLabId());
+ vo.setUserId(key.getUserId());
+ vo.setNickName(user.getNickName());
+ vo.setClassName(course.getClassName());
+ vo.setCourseName(course.getName());
+ vo.setNumber(course.getNumber());
+ vo.setDay(key.getDay());
+ vo.setTime(key.getTime());
+ vo.setRejectContent(value.getRejectContent());
+ vo.setLocation(lab.getLocation());
+ vo.setLabName(lab.getName());
+ vo.setWeekList(weekList);
+ vo.setWeek(labServiceImpl.convert(weekList));
+ vo.setStatus(value.getStatus());
+ vo.setGmtModified(value.getGmtModified());
+ vo.setRecordIdList(value.getRecordIdList());
+ voList.add(vo);
+ }
+
+ // 结果更新时间降序显示(最新在前)
+ //voList.sort(new Comparator() {
+ // @Override
+ // public int compare(LabRecordVO o1, LabRecordVO o2) {
+ // return (int) -(o1.getGmtModified().getTime() - o2.getGmtModified().getTime());
+ // }
+ //});
+
+ return voList;
+ }
+
+ /**
+ * 新增实验室预约信息
+ *
+ * @param labRecord 实验室预约信息
+ * @return 结果
+ */
+ @Override
+ public int insertLabRecord(LabRecord labRecord) {
+ return labRecordMapper.insertLabRecord(labRecord);
+ }
+
+ /**
+ * 发送邮件,异步执行
+ */
+ public void sendEmail(String subject, String receiver, String text) {
+ String content =
+ "\r\n" +
+ " \r\n" +
+ " \r\n" +
+ "
\r\n" +
+ "\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "\r\n" +
+ "
\r\n" +
+ "
您好!
\r\n" +
+ "
" + text + "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ "
\r\n" +
+ " \r\n" +
+ "";
+ //发邮箱
+ sendMailUtils.sendEmail(subject, receiver, content);
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabServiceImpl.java
new file mode 100644
index 0000000..032438c
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabServiceImpl.java
@@ -0,0 +1,743 @@
+package com.hzu.labbooking.service.impl;
+
+import java.util.*;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hzu.labbooking.constant.Constants;
+import com.hzu.labbooking.constant.RecordStatusConstants;
+import com.hzu.labbooking.constant.WeekStatusConstants;
+import com.hzu.labbooking.domain.*;
+import com.hzu.labbooking.domain.component.MapKey;
+import com.hzu.labbooking.domain.component.RecordTableKey;
+import com.hzu.labbooking.domain.vo.*;
+import com.hzu.labbooking.mapper.LabWeekMapper;
+import com.hzu.labbooking.service.*;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.service.ISysDeptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.hzu.labbooking.mapper.LabMapper;
+
+/**
+ * 实验室信息Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Service
+public class LabServiceImpl extends ServiceImpl implements ILabService {
+
+ @Autowired
+ private LabMapper labMapper;
+ @Autowired
+ private ILabRecordService recordService;
+ @Autowired
+ private ILabWeekService weekService;
+ @Autowired
+ private ICourseService courseService;
+ @Autowired
+ private TokenService tokenService;
+ @Autowired
+ private ISysDeptService deptService;
+ @Autowired
+ private IRecordWeekService recordWeekService;
+ @Autowired
+ private LabWeekMapper weekMapper;
+
+ /**
+ * 查询实验室信息
+ *
+ * @param id 实验室信息ID
+ * @return 实验室信息
+ */
+ @Override
+ public Lab selectLabById(Long id) {
+ return labMapper.selectLabById(id);
+ }
+
+ /**
+ * 查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @return 实验室信息
+ */
+ @Override
+ public List selectBookLabList(Lab lab) {
+
+ // 1.获取当前用户所属学院及公共学院的所有实验室信息
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ Long deptId = loginUser.getUser().getDeptId();
+ Long parentDeptId = deptService.getParentDeptId(deptId);
+ List voList = labMapper.selectBookLabList(lab, deptId, parentDeptId);
+
+ // 2.封装学院名称到list中
+ List deptIdList = new ArrayList<>();
+ //遍历voList,获取学院id集合
+ for (LabVO labVO : voList) {
+ deptIdList.add(labVO.getDeptId());
+ }
+
+ if (voList.size() > 0) {
+ //通过学院id集合获取学院列表
+ List deptList = deptService.selectDeptNameByIds(deptIdList);
+ //创建Map,通过遍历deptList获取以学院id和学院名字为键值对的集合
+ Map map = new HashMap<>();
+ for (SysDept sysDept : deptList) {
+ map.put(sysDept.getDeptId(), sysDept.getDeptName());
+ }
+ //遍历voList,通过map,添加学院名称
+ for (LabVO labVO : voList) {
+ labVO.setDeptName(map.get(labVO.getDeptId()));
+ }
+ }
+
+ return voList;
+ }
+
+ /**
+ * 管理员查询实验室信息列表
+ *
+ * @param lab 实验室信息
+ * @return 实验室信息
+ */
+ @Override
+ public List selectLabList(Lab lab) {
+ // 1.获取当前用户所属学院及公共学院的所有实验室信息
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ Long userId = loginUser.getUser().getUserId();
+ Long deptId = loginUser.getUser().getDeptId();
+
+ Long parentDeptId = deptService.getParentDeptId(deptId);
+ // 通过lab、学院id和用户名查询实验室列表
+ List voList = labMapper.selectLabList(lab, deptId, parentDeptId, userId);
+ // 2.封装学院名称到list中
+ List deptIdList = new ArrayList<>();
+ // 遍历voList,获取学院id集合
+ for (MyLabVO myLabVO : voList) {
+ deptIdList.add(myLabVO.getDeptId());
+ }
+
+ if (voList.size() > 0) {
+ // 通过学院id集合获取学院列表
+ List deptList = deptService.selectDeptNameByIds(deptIdList);
+ // 创建Map,通过遍历deptList获取以学院id和学院名字为键值对的集合
+ Map map = new HashMap<>();
+ for (SysDept sysDept : deptList) {
+ map.put(sysDept.getDeptId(), sysDept.getDeptName());
+ }
+ // 遍历voList,通过map,添加学院名称
+ for (MyLabVO myLabVO : voList) {
+ myLabVO.setDeptName(map.get(myLabVO.getDeptId()));
+ }
+ }
+
+ return voList;
+ }
+
+ /**
+ * 新增实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ @Override
+ public int insertLab(Lab lab) {
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ //获取登录用户的用户名
+ Long userId = loginUser.getUser().getUserId();
+ lab.setUserId(userId);
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq("name", lab.getName()).or().eq("location", lab.getLocation());
+ List labs = labMapper.selectList(wrapper);
+ if (StringUtils.isNotEmpty(labs)) {
+ return -1;
+ } else {
+ return labMapper.insert(lab);
+ }
+ }
+
+ /**
+ * 修改实验室信息
+ *
+ * @param lab 实验室信息
+ * @return 结果
+ */
+ @Override
+ public int updateLab(Lab lab) {
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ //获取登录用户的用户名
+ Long userId = loginUser.getUser().getUserId();
+
+ //表单校验
+ //1. 实验室名称和位置相同,直接修改
+ QueryWrapper wrapper1 = new QueryWrapper<>();
+ wrapper1.eq("user_id", userId).eq("name", lab.getName())
+ .eq("location", lab.getLocation()).eq("id", lab.getId());
+ List labs1 = labMapper.selectList(wrapper1);
+ if (StringUtils.isNotEmpty(labs1)) {
+ return labMapper.updateLab(lab);
+ }
+
+ //2. 实验室名称相同,位置不同,对位置进行校验,不重复时修改
+ QueryWrapper wrapper2 = new QueryWrapper<>();
+ wrapper2.eq("user_id", userId).eq("name", lab.getName())
+ .ne("location", lab.getLocation()).eq("id", lab.getId());
+ List labs2 = labMapper.selectList(wrapper2);
+ if (StringUtils.isNotEmpty(labs2)) {
+ List list = labMapper.selectList(new QueryWrapper().eq("location", lab.getLocation()).ne("id", lab.getId()));
+ if (StringUtils.isNotEmpty(list)) {
+ return -1;
+ //throw new BaseException("实验室名字相同~~~~~~~~~~~~~~~~~~");
+ } else {
+ return labMapper.updateLab(lab);
+ }
+ }
+
+ //3. 实验室名称不同,位置相同,对名称进行校验,不重复时修改
+ QueryWrapper wrapper3 = new QueryWrapper<>();
+ wrapper2.eq("user_id", userId).ne("name", lab.getName())
+ .eq("location", lab.getLocation()).eq("id", lab.getId());
+ List labs3 = labMapper.selectList(wrapper3);
+ if (StringUtils.isNotEmpty(labs3)) {
+ List list = labMapper.selectList(new QueryWrapper().eq("name", lab.getName()).ne("id", lab.getId()));
+ if (StringUtils.isNotEmpty(list)) {
+ return -2;
+ } else {
+ return labMapper.updateLab(lab);
+ }
+ }
+
+ //4. 实验室名称不同,位置不同,对名称或位置进行校验,不重复时修改
+ QueryWrapper wrapper4 = new QueryWrapper<>();
+ wrapper2.eq("user_id", userId).ne("name", lab.getName())
+ .ne("location", lab.getLocation()).eq("id", lab.getId());
+ List labs4 = labMapper.selectList(wrapper4);
+ if (StringUtils.isNotEmpty(labs4)) {
+ List list = labMapper.selectList(new QueryWrapper().eq("name", lab.getName()).or().eq("location", lab.getLocation()));
+ if (StringUtils.isNotEmpty(list)) {
+ return -3;
+ } else {
+ return labMapper.updateLab(lab);
+ }
+ }
+ return -4;
+ }
+
+ /**
+ * 获取当前用户的课表
+ */
+ @Override
+ public List getAllLabTable(Long year, Long term) {
+ // 1.获取当前用户信息
+ LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+ Long userId = loginUser.getUser().getUserId();
+ String nickName = loginUser.getUser().getNickName();
+
+ // 2.根据user_id在record查出该条预约记录
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper
+ .eq("user_id", userId)
+ .eq("year", year).eq("term", term)
+ .ne("status", RecordStatusConstants.REJECTED)
+ .ne("status", RecordStatusConstants.CANCELLED);
+ List recordList = recordService.list(recordQueryWrapper); // 当前学年学期所有预约记录
+
+ if (recordList == null || recordList.size() == 0) {
+ return null; // 课表为空
+ }
+
+ // 3.根据预约记录id获取对应的weekIdList,并封装聚合课程
+ Map> recordWeekMap = new HashMap<>();
+ for (LabRecord record : recordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ weekQueryWrapper.eq("status", WeekStatusConstants.CLOSED);
+
+ // 获取当前预约所有周次
+ List labWeekList = weekMapper.selectList(weekQueryWrapper);
+
+ if (labWeekList.isEmpty()) {
+ continue;
+ }
+
+ RecordTableKey recordTableKey = new RecordTableKey();
+ recordTableKey.setCourseId(record.getCourseId());
+ recordTableKey.setLabId(record.getLabId());
+ recordTableKey.setDay(record.getDay());
+ recordTableKey.setTime(record.getTime());
+ recordTableKey.setUserId(record.getUserId());
+ recordTableKey.setId(0L);
+ //BeanUtils.copyProperties(record, recordTableKey);
+
+ // 若遇到相同预约,不同周次,将其添加到周次中
+ if (!recordWeekMap.containsKey(recordTableKey)) {
+ List weeks = new ArrayList<>(labWeekList);
+ recordWeekMap.put(recordTableKey, weeks);
+ } else {
+ recordWeekMap.get(recordTableKey).addAll(labWeekList);
+ }
+ }
+
+ // 封装每个daytime中课程/dataList数据
+ Map> mp = new HashMap<>();
+ for (Map.Entry> recordWeek : recordWeekMap.entrySet()) {
+ RecordTableKey key = recordWeek.getKey();
+
+ Lab lab = this.getById(key.getLabId());
+ //判断实验室是否关闭
+ //if (lab.getStatus()==0) {
+ // continue;
+ //}
+ Course course = courseService.getById(key.getCourseId());
+
+ // 获取到该预约记录预约周次
+ List weekList = new ArrayList<>();
+ for (LabWeek week : recordWeek.getValue()) {
+ weekList.add(week.getWeek());
+ }
+
+ LabTableDataVO dataVO = new LabTableDataVO();
+ dataVO.setCourseId(key.getCourseId());
+ dataVO.setClassName(course.getClassName());
+ dataVO.setCourseName(course.getName());
+ dataVO.setTeacherName(nickName);
+ dataVO.setLabId(lab.getId());
+ dataVO.setLocation(lab.getLocation());
+ dataVO.setDay(key.getDay());
+ dataVO.setTime(key.getTime());
+ dataVO.setWeekList(weekList);
+ dataVO.setWeek(convert(weekList));
+
+ List dataList = new ArrayList<>();
+
+ MapKey mapKey = new MapKey();
+ mapKey.setDay(key.getDay());
+ mapKey.setTime(key.getTime());
+
+ // 如果存在(day,time)相同,则加到其的list中,否则new list加入
+ if (!mp.containsKey(mapKey)) {
+ dataList.add(dataVO);
+ mp.put(mapKey, dataList);
+ } else {
+ mp.get(mapKey).add(dataVO);
+ }
+ }
+
+ // 封装结果
+ List voList = new ArrayList<>();
+ for (MapKey key : mp.keySet()) {
+ List dataList = mp.get(key);
+
+ LabTableVO vo = new LabTableVO();
+ vo.setDay(key.getDay());
+ vo.setTime(key.getTime());
+ vo.setDataList(dataList);
+
+ voList.add(vo);
+ }
+
+ return voList;
+ }
+
+ @Override
+ public LabTableDetailsVO getTableDetails(Long labId) {
+ LabTableDetailsVO vo = new LabTableDetailsVO();
+ Lab lab = this.getById(labId);
+ LabRecord record = recordService.getById(labId);
+ Course course = courseService.getById(record.getCourseId());
+ vo.setRequirement(lab.getRequirement());
+ vo.setCapacity(lab.getCapacity());
+ vo.setLabRemark(lab.getRemarks());
+ vo.setNumber(course.getNumber());
+ vo.setCourseRemark(course.getRemarks());
+ return vo;
+ }
+
+ /**
+ * 该实验室这个学年学期,被预约具体周的个数,1周相当于1节也就是2课时
+ * 获取这个学期所有预约记录,找到对应的week,并且状态必须为0,表示已占用;
+ * 使用率=不可用时间段/总实验室提供学时(18周*7天*5节*2学时=1260学时/学期),1条week=2学时
+ * 1.查出指定学年学期所有预约记录
+ * 2.通过lab_id查出week个数,且其状态为0表示已被占用;封装 resultMap(labId=week个数*2)
+ * 3.封装数据并排序
+ * 最后统计出占用学时:不可用时间段useTime=useCount*2
+ */
+ @Override
+ public List getLabUseRate(Long year, Long term) {
+ // 1.查出指定学年学期所有预约记录
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper
+ .eq("year", year).eq("term", term)
+ .ne("status", RecordStatusConstants.REJECTED)
+ .ne("status", RecordStatusConstants.CANCELLED);
+ List tmpRecordList = recordService.list(recordQueryWrapper);
+
+ if (tmpRecordList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // 2.通过lab_id查出week个数,且其状态为0表示已被占用;封装 resultMap(labId=week个数*2)
+ Map resultMap = new HashMap<>();
+ for (LabRecord record : tmpRecordList) {
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.select("week_id").eq("record_id", record.getId());
+ List weekList = recordWeekService.list(wrapper);
+
+ List weekIdList = new ArrayList<>();
+ for (RecordWeek weekId : weekList) {
+ weekIdList.add(weekId.getWeekId());
+ }
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ weekQueryWrapper.eq("status", WeekStatusConstants.CLOSED);
+ int count = weekService.count(weekQueryWrapper);
+ resultMap.put(record.getLabId(), resultMap.getOrDefault(record.getLabId(), 0) + count);
+ }
+
+ List vos = new ArrayList<>();
+ for (Long labId : resultMap.keySet()) {
+ QueryWrapper w = new QueryWrapper<>();
+ w.select("name").eq("id", labId);
+ Lab lab = this.getOne(w);
+
+ LabUseVO labUseVO = new LabUseVO();
+ labUseVO.setLabName(lab.getName()); //每个实验室名称
+ labUseVO.setUse(resultMap.get(labId)); //每个实验室被预约使用时间 status=0
+ labUseVO.setAll();
+ labUseVO.setRate();
+ vos.add(labUseVO);
+ }
+
+ vos.sort(new Comparator() {
+ @Override
+ public int compare(LabUseVO o1, LabUseVO o2) {
+ return -(o1.getUse() - o2.getUse());
+ }
+ });
+
+ return vos;
+ }
+
+ /**
+ * 获取某个实验室的可预约课表
+ * 1. 根据lab_id, year, term, status=0在lab_time查出所有记录(lab_id, week, day, time)
+ * 这个查出来的记录表示已占用的,我们之后对无法预约的时间求绝对补集得到所有可预约时间
+ * 2. 维护一个HashMap,key是(day,time),value是list
+ * 3. 双重遍历day和time,从HashMap取出list,然后取这个list的补集,再转化成String放到LabWeekVO
+ */
+ @Override
+ public List getAllApplyTable(long labId, long year, long term) {
+ /*
+ 获取某个实验室的可预约课表
+ 1.找出所有不可预约的时间,之后求补集
+ 2.通过record_id获取week_id,封装key(day,time)=value(week)
+ 3.双重遍历day和time,从HashMap取出weekList,然后取这个list的补集,再转化成String放到 LabTableVO
+ */
+
+ // 1.找出所有不可预约的时间,之后求补集
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper
+ .eq("lab_id", labId)
+ .eq("year", year)
+ .eq("term", term)
+ .ne("status", RecordStatusConstants.REJECTED)
+ .ne("status", RecordStatusConstants.CANCELLED);
+ List recordList = recordService.list(recordQueryWrapper);
+
+ // 2.通过record_id获取week_id,封装key(day,time)=value(week)
+ Map> mp = new HashMap<>();
+ for (LabRecord record : recordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ weekQueryWrapper.eq("status", WeekStatusConstants.CLOSED);
+
+ // 获取当前预约所有周次
+ List labWeekList = weekMapper.selectList(weekQueryWrapper);
+
+ if (labWeekList.isEmpty()) {
+ continue;
+ }
+
+ List weekList = new ArrayList<>();
+ for (LabWeek week : labWeekList) {
+ weekList.add(week.getWeek());
+ }
+
+ MapKey key = new MapKey(record.getDay(), record.getTime());
+
+ // 若遇到相同预约,不同周次,将其添加到周次中
+ if (!mp.containsKey(key)) {
+ mp.put(key, weekList);
+ } else {
+ mp.get(key).addAll(weekList);
+ }
+ }
+
+ // 3.双重遍历day和time,从HashMap取出weekList,然后取这个list的补集,再转化成String放到 LabTableVO
+ List voList = new ArrayList<>();
+ for (long day = 1; day <= Constants.DAY_SIZE; day++) {
+ for (long time = 1; time <= Constants.TIME_SIZE; time++) {
+ LabTableVO vo = new LabTableVO();
+
+ List weekList = new ArrayList<>();
+ MapKey key = new MapKey(day, time);
+ if (!mp.containsKey(key)) {
+ for (long i = 1; i <= Constants.WEEK_SIZE; i++) {
+ weekList.add(i);
+ }
+ } else {
+ List tmpList = mp.get(key);
+ for (long i = 1; i <= Constants.WEEK_SIZE; i++) {
+ boolean f = true;
+ for (Long tmp : tmpList) {
+ if (i == tmp) {
+ f = false;
+ break;
+ }
+ }
+ if (f) {
+ weekList.add(i);
+ }
+ }
+ }
+ vo.setLabId(labId);
+ vo.setDay(day);
+ vo.setTime(time);
+ vo.setWeekList(weekList);
+ if (weekList.isEmpty()) {
+ vo.setWeek("已满");
+ } else {
+ vo.setWeek(convert(weekList) + " 可预约");
+ }
+ voList.add(vo);
+ }
+ }
+
+ return voList;
+ }
+
+ @Override
+ public List getAllAppliedTable(long labId, long year, long term) {
+ // 1.找出所有不可预约的时间,之后求补集
+ QueryWrapper recordQueryWrapper = new QueryWrapper<>();
+ recordQueryWrapper
+ .eq("lab_id", labId)
+ .eq("year", year)
+ .eq("term", term)
+ .ne("status", RecordStatusConstants.REJECTED)
+ .ne("status", RecordStatusConstants.CANCELLED);
+ List recordList = recordService.list(recordQueryWrapper);
+
+ // 2.通过record_id获取week_id,封装key(day,time)=value(week)
+ Map> mp = new HashMap<>();
+ for (LabRecord record : recordList) {
+ List weekIdList = recordWeekService.selectWeekList(record.getId());
+
+ QueryWrapper weekQueryWrapper = new QueryWrapper<>();
+ weekQueryWrapper.in("id", weekIdList);
+ weekQueryWrapper.eq("status", WeekStatusConstants.CLOSED);
+
+ // 获取当前预约所有周次
+ List labWeekList = weekMapper.selectList(weekQueryWrapper);
+
+ if (labWeekList.isEmpty()) {
+ continue;
+ }
+
+ List weekList = new ArrayList<>();
+ for (LabWeek week : labWeekList) {
+ weekList.add(week.getWeek());
+ }
+
+ MapKey key = new MapKey(record.getDay(), record.getTime());
+
+ // 若遇到相同预约,不同周次,将其添加到周次中
+ if (!mp.containsKey(key)) {
+ mp.put(key, weekList);
+ } else {
+ mp.get(key).addAll(weekList);
+ }
+ }
+
+ // 3.双重遍历day和time,从HashMap取出weekList,然后取这个list的补集,再转化成String放到 LabTableVO
+ List voList = new ArrayList<>();
+ for (long day = 1; day <= Constants.DAY_SIZE; day++) {
+ for (long time = 1; time <= Constants.TIME_SIZE; time++) {
+ LabTableVO vo = new LabTableVO();
+
+ List weekList = new ArrayList<>();
+ MapKey key = new MapKey(day, time);
+ if (mp.containsKey(key)) {
+ List tmpList = mp.get(key);
+ for (long i = 1; i <= Constants.WEEK_SIZE; i++) {
+ boolean f = false;
+ for (Long tmp : tmpList) {
+ if (i == tmp) {
+ f = true;
+ break;
+ }
+ }
+ if (f) {
+ weekList.add(i);
+ }
+ }
+ }
+ vo.setLabId(labId);
+ vo.setDay(day);
+ vo.setTime(time);
+ vo.setWeekList(weekList);
+ if (weekList.isEmpty()) {
+ vo.setWeek("无");
+ } else {
+ vo.setWeek(convert(weekList) + " 已预约");
+ }
+ voList.add(vo);
+ }
+ }
+
+ return voList;
+ }
+
+ @Override
+ public int updateLabStatus(Lab lab) {
+ return baseMapper.updateLab(lab);
+ }
+
+ /* 将week数组转化为String显示,连续的数字用'-'连接 */
+ // [1,2,3,4,5,7] --> [1-5,7]
+ // [1,2,3,5,6,8,16] --> [1-3,5-6,8,16]
+ // [2,4,6,8,10] --> [2,4,6,8,10]
+ // [2,4,6,8,10,12] --> [1-12]双
+ public String convert(List week) {
+
+ Collections.sort(week);
+ StringBuilder ans = new StringBuilder();
+ if (week == null || week.isEmpty()) {
+ return "";
+ }
+
+ int sz = week.size();
+ if (sz >= 6) {
+ boolean f = false;
+ for (int i = 1; i < sz; i++) {
+ if (week.get(i) - week.get(i - 1) != 2) {
+ f = true;// true表示不连续
+ break;
+ }
+ }
+ if (!f) {
+ Long s = week.get(0);
+ Long e = week.get(sz - 1);
+ if (s == 2) {
+ s--;
+ }
+ if (e == 15 || e == 17) {
+ e++;
+ }
+ ans.append("[").append(s);
+ ans.append("-");
+ ans.append(e).append("周]");
+ if (week.get(0) % 2 == 0) {
+ ans.append("双");
+ } else {
+ ans.append("单");
+ }
+ return ans.toString();
+ }
+ }
+
+ int len = 1; // len表示连续的个数,要求>=2才处理
+ int idx = 1; // 周次索引
+ long s = 0; // 连续的起点
+
+ ans.append("[");
+
+ while (idx < week.size()) {
+ if (week.get(idx) == week.get(idx - 1) + 1) { // 连续
+ len++;
+ if (len == 2) {
+ s = week.get(idx - 1); //len==2,开始连续,s记录起点
+ }
+ } else { //不连续
+ if (len >= 2) {
+ ans.append(s);
+ ans.append("-");
+ ans.append(week.get(idx - 1));
+ } else { //len < 2
+ ans.append(week.get(idx - 1));
+ }
+ ans.append(",");
+ len = 1;
+ }
+ idx++;
+ }
+
+ //最后剩余的一个数字
+ if (len >= 2) {
+ ans.append(s).append("-");
+ }
+ ans.append(week.get(idx - 1)).append("周]");
+
+ return ans.toString();
+ }
+
+ private String convert3day(List week) {
+
+ Collections.sort(week);
+ StringBuilder ans = new StringBuilder();
+ if (week == null || week.isEmpty()) return "";
+
+ int len = 1; //len表示连续的个数,要求>=3才处理
+ int idx = 1;
+ long s = 0; //连续的起点
+
+ ans.append("[");
+
+ while (idx < week.size()) {
+ if (week.get(idx) == week.get(idx - 1) + 1) {
+ len++;
+ if (len == 3) {
+ s = week.get(idx - 2); //len==3,开始连续,s先记录一下起点
+ }
+ } else {
+ if (len >= 3) {
+ ans.append(s);
+ ans.append("-");
+ ans.append(week.get(idx - 1));
+ } else if (len == 2) {
+ ans.append(week.get(idx - 2));
+ ans.append(", ");
+ ans.append(week.get(idx - 1));
+ } else {
+ ans.append(week.get(idx - 1));
+ }
+ ans.append(", ");
+ len = 1;
+ }
+ idx++;
+ }
+ //最后处理一下
+ if (len >= 3) {
+ ans.append(s);
+ ans.append("-");
+ } else if (len == 2) {
+ ans.append(week.get(idx - 2));
+ ans.append(", ");
+ }
+ ans.append(week.get(idx - 1));
+ ans.append("周]");
+
+ return ans.toString();
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabWeekServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabWeekServiceImpl.java
new file mode 100644
index 0000000..86fad31
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/LabWeekServiceImpl.java
@@ -0,0 +1,18 @@
+package com.hzu.labbooking.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import com.hzu.labbooking.mapper.LabWeekMapper;
+import com.hzu.labbooking.domain.LabWeek;
+import com.hzu.labbooking.service.ILabWeekService;
+
+/**
+ * 实验室预约时间段Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Service
+public class LabWeekServiceImpl extends ServiceImpl implements ILabWeekService {
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/OfficeServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/OfficeServiceImpl.java
new file mode 100644
index 0000000..01d692a
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/OfficeServiceImpl.java
@@ -0,0 +1,66 @@
+package com.hzu.labbooking.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.hzu.labbooking.mapper.OfficeMapper;
+import com.hzu.labbooking.domain.Office;
+import com.hzu.labbooking.service.IOfficeService;
+
+/**
+ * 教师信息Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-04-29
+ */
+@Service
+public class OfficeServiceImpl extends ServiceImpl implements IOfficeService {
+
+ @Autowired
+ private OfficeMapper officeMapper;
+
+ /**
+ * 查询教师信息
+ *
+ * @param id 教师信息ID
+ * @return 教师信息
+ */
+ @Override
+ public Office selectOfficeById(Long id) {
+ return officeMapper.selectOfficeById(id);
+ }
+
+ /**
+ * 新增教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ @Override
+ public int insertOffice(Office office) {
+ return officeMapper.insertOffice(office);
+ }
+
+ /**
+ * 修改教师信息
+ *
+ * @param office 教师信息
+ * @return 结果
+ */
+ @Override
+ public int updateOffice(Office office) {
+ return officeMapper.updateOffice(office);
+ }
+
+ /**
+ * 批量删除教师信息
+ *
+ * @param ids 需要删除的教师信息ID
+ * @return 结果
+ */
+ @Override
+ public int deleteOfficeByIds(Long[] ids) {
+ return officeMapper.deleteOfficeByIds(ids);
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/service/impl/RecordWeekServiceImpl.java b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/RecordWeekServiceImpl.java
new file mode 100644
index 0000000..451dd49
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/service/impl/RecordWeekServiceImpl.java
@@ -0,0 +1,46 @@
+package com.hzu.labbooking.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hzu.labbooking.domain.RecordWeek;
+import com.hzu.labbooking.mapper.RecordWeekMapper;
+import com.hzu.labbooking.service.IRecordWeekService;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 预约信息周次映射Service业务层处理
+ *
+ * @author yezihao
+ * @date 2021-06-13
+ */
+@Service
+public class RecordWeekServiceImpl extends ServiceImpl implements IRecordWeekService {
+
+ @Override
+ public List selectWeekList(Long recordId) {
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.select("week_id").eq("record_id", recordId);
+ List weekIdList = this.list(wrapper);
+ List res = new ArrayList<>();
+ for (RecordWeek week : weekIdList) {
+ res.add(week.getWeekId());
+ }
+ return res;
+ }
+
+ @Override
+ public List selectAllWeekList(List recordIdList) {
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.select("week_id").in("record_id", recordIdList);
+ List weekIdList = this.list(wrapper);
+ List res = new ArrayList<>();
+ for (RecordWeek week : weekIdList) {
+ res.add(week.getWeekId());
+ }
+ return res;
+ }
+
+}
diff --git a/lab-booking/src/main/java/com/hzu/labbooking/utils/SendMailUtils.java b/lab-booking/src/main/java/com/hzu/labbooking/utils/SendMailUtils.java
new file mode 100644
index 0000000..df6c2e9
--- /dev/null
+++ b/lab-booking/src/main/java/com/hzu/labbooking/utils/SendMailUtils.java
@@ -0,0 +1,66 @@
+package com.hzu.labbooking.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * 邮件发送工具类
+ *
+ * @author: skyed
+ * @date: 2021-5-5
+ */
+@Slf4j
+@Component
+public class SendMailUtils {
+
+ @Value(value = "${spring.mail.username}")
+ public String SENDER;
+
+ @Resource
+ private JavaMailSenderImpl mailSender;
+
+ /**
+ * 发送邮件
+ *
+ * @param subject 主题
+ * @param receiver 接收者
+ * @param text 文本
+ */
+ public void sendEmail(String subject, String receiver, String text) {
+ try {
+ //创建一个复杂的消息邮件
+ MimeMessage mimeMessage = mailSender.createMimeMessage();
+
+ //multipart:true
+ MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
+
+ helper.setSubject(subject);
+
+ helper.setText(text, true);
+ //邮件接收人
+ helper.setTo(receiver);
+
+ //邮件发送者
+ helper.setFrom(SENDER);
+
+ mailSender.send(mimeMessage);
+
+ log.info("邮件发送成功");
+ /*
+ //添加邮件附件
+ String path = ""; //文件路径
+ String fileName = ""; //文件名
+ helper.addAttachment(fileName, new File(path));
+ */
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/lab-booking/src/main/resources/mapper/labbooking/CourseMapper.xml b/lab-booking/src/main/resources/mapper/labbooking/CourseMapper.xml
new file mode 100644
index 0000000..5e40c38
--- /dev/null
+++ b/lab-booking/src/main/resources/mapper/labbooking/CourseMapper.xml
@@ -0,0 +1,86 @@
+
+
+