增加web代码

master
xiao.ming 2021-11-30 16:19:40 +08:00
parent 45e62a67f3
commit 8925945571
33 changed files with 15899 additions and 45 deletions

View File

@ -19,7 +19,7 @@ zap:
# redis configuration
redis:
db: 0
addr: '127.0.0.1:6379'
addr: '10.25.16.212:6379'
password: ''

View File

@ -1 +1 @@
log/2021-11-17.log
log/2021-11-18.log

View File

@ -1,43 +0,0 @@
[goweb-demo]2021/11/10 - 10:56:35.664 info /Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 10:56:35.667 info /Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 11:00:35.952 error /Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38 accept tcp [::]:8888: use of closed network connection
[goweb-demo]2021/11/10 - 11:00:55.329 info /Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 11:00:55.330 info /Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 11:03:58.651 info /Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 11:03:58.653 info /Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 11:07:46.066 info /Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:49 router register success
goweb-gin-demo/initialize.Routers
/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:49
goweb-gin-demo/core.RunServer
/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:22
main.main
/Users/zero/work/mygithub/goweb-gin-demo/main.go:15
runtime.main
/Users/zero/go/sdk/go1.16.9/src/runtime/proc.go:225
[goweb-demo]2021/11/10 - 11:07:46.070 info /Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
goweb-gin-demo/core.RunServer
/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31
main.main
/Users/zero/work/mygithub/goweb-gin-demo/main.go:15
runtime.main
/Users/zero/go/sdk/go1.16.9/src/runtime/proc.go:225
[goweb-demo]2021/11/10 - 16:57:19.765 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 16:57:19.767 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 17:26:39.999 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 17:26:40.001 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 17:27:01.783 error /Users/zero/work/mygithub/run_goweb-gin-demo/api/wt/wt_reports.go:112 获取失败! {"err": "Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '[ 1, 2, 5, 1 ] LIMIT ? OFFSET ?' at line 1; Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '[ 1, 2, 5, 1 ] LIMIT ? OFFSET ?' at line 1"}
[goweb-demo]2021/11/10 - 17:29:35.409 error /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:38 accept tcp [::]:8888: use of closed network connection
[goweb-demo]2021/11/10 - 17:29:47.850 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 17:29:47.852 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 17:33:16.947 error /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:38 accept tcp [::]:8888: use of closed network connection
[goweb-demo]2021/11/10 - 17:33:28.565 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 17:33:28.566 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 18:07:58.172 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 18:07:58.174 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 18:08:45.860 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 18:08:45.861 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 18:45:34.852 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 18:45:34.853 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8888"}
[goweb-demo]2021/11/10 - 18:47:31.305 error /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:38 accept tcp [::]:8888: use of closed network connection
[goweb-demo]2021/11/10 - 18:47:42.710 info /Users/zero/work/mygithub/run_goweb-gin-demo/initialize/router.go:49 router register success
[goweb-demo]2021/11/10 - 18:47:42.711 info /Users/zero/work/mygithub/run_goweb-gin-demo/core/server.go:31 server run success on {"address": ":8889"}

View File

@ -16,3 +16,11 @@
[goweb-demo]2021/11/17 - 19:32:08.254 error /Users/zero/work/mygithub/goweb-gin-demo/server/api/wt/wt_comments.go:111 获取失败! {"err": "Error 1054: Unknown column 'wt_comments.nick_name' in 'field list'"}
[goweb-demo]2021/11/17 - 19:34:29.681 info /Users/zero/work/mygithub/goweb-gin-demo/server/initialize/router.go:49 router register success
[goweb-demo]2021/11/17 - 19:34:29.682 info /Users/zero/work/mygithub/goweb-gin-demo/server/core/server.go:31 server run success on {"address": ":8981"}
[goweb-demo]2021/11/17 - 21:24:08.831 info /Users/zero/work/mygithub/goweb-gin-demo/server/initialize/router.go:49 router register success
[goweb-demo]2021/11/17 - 21:24:08.832 info /Users/zero/work/mygithub/goweb-gin-demo/server/core/server.go:31 server run success on {"address": ":8981"}
[goweb-demo]2021/11/17 - 13:25:22.345 info /usr/local/weekly_report/initialize/router.go:49 router register success
[goweb-demo]2021/11/17 - 13:25:22.362 info /usr/local/weekly_report/core/server.go:31 server run success on {"address": ":8981"}
[goweb-demo]2021/11/17 - 21:26:18.705 error /Users/zero/work/mygithub/goweb-gin-demo/server/core/server.go:38 accept tcp [::]:8981: use of closed network connection
[goweb-demo]2021/11/17 - 21:26:27.431 info /Users/zero/work/mygithub/goweb-gin-demo/server/initialize/router.go:49 router register success
[goweb-demo]2021/11/17 - 21:26:27.434 info /Users/zero/work/mygithub/goweb-gin-demo/server/core/server.go:31 server run success on {"address": ":8981"}
[goweb-demo]2021/11/17 - 21:26:59.880 error /Users/zero/work/mygithub/goweb-gin-demo/server/core/server.go:38 accept tcp [::]:8981: use of closed network connection

24
web/README.md Normal file
View File

@ -0,0 +1,24 @@
# weekly
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
web/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

12733
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

57
web/package.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "weekly",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service dist",
"lint": "vue-cli-service lint"
},
"dependencies": {
"ant-design-vue": "^1.7.8",
"axios": "^0.21.4",
"core-js": "^3.6.5",
"js-base64": "^3.7.2",
"less": "^2.7.3",
"mavon-editor": "^2.9.1",
"qs": "^6.10.1",
"quill": "^1.3.7",
"vue": "^2.6.11",
"vue-froala-wysiwyg": "^4.0.6",
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.5.3",
"vuex-persistedstate": "^4.1.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"crypto-js": "^4.1.1",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"less-loader": "^4.1.0",
"lodash.pick": "^4.4.0",
"vue-template-compiler": "^2.6.11",
"vuex": "^3.6.2"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
web/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

17
web/public/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

44
web/src/App.vue Normal file
View File

@ -0,0 +1,44 @@
<template>
<a-config-provider :locale="zh_CN">
<div id="app">
<router-view></router-view>
</div>
</a-config-provider>
</template>
<script>
import zh_CN from 'ant-design-vue/lib/locale-provider/zh_CN'
export default {
name: 'App',
data() {
return{
zh_CN
}
}
}
</script>
<style>
html, body {
height: 100%;
width: 100%;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
height: 100%;
width: 100%;
}
main {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
width: 100%;
}
main > div {
width: calc(100vw - 310px);
width: 100%;
}
</style>

BIN
web/src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,145 @@
<template>
<div class="login_div">
<div class="login_content">
<div class="login_title">周报系统</div>
<a-form :form="form" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }" @submit="handleSubmit">
<a-form-item class="login_explain">
<a-input v-decorator="['username', { rules: [{ required: true, message: '!' }] },]"
placeholder="请输入用户名" class="input_login" autocomplete="off">
<a-icon slot="prefix" type="user" class="icon_color" />
</a-input>
</a-form-item>
<a-form-item class="login_explain">
<a-input v-decorator="[ 'password', { rules: [{ required: true, message: '!' }] }, ]"
type="password"
placeholder="请输入密码" class="input_login">
<a-icon slot="prefix" type="lock" class="icon_color" />
</a-input>
</a-form-item>
<a-form-item class="login_explain captcha">
<a-input v-decorator="[ 'captcha', { rules: [{ required: true, message: '!' }] }, ]"
@keyup.enter="handleSubmit"
placeholder="请输入验证码" style="width: 220px; margin-left: 30px" autocomplete="off">
</a-input>
<img :src="captcha" class="captcha_img" title="点击刷新" @click="getCaptcha" alt="">
</a-form-item>
<a-form-item>
<a-button type="primary" class="login_button" @click="handleSubmit">
登录
</a-button>
</a-form-item>
</a-form>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "LoginComponent",
data() {
return {
formLayout: 'horizontal',
captcha: '',
captchaId: '',
form: this.$form.createForm(this, { name: 'login' }),
message: '',
};
},
mounted() {
//
this.getCaptcha();
},
methods: {
getCaptcha() {
this.$axios.post(api.GET_CAPTCHA.url).then(res => {
if (res.data.code === 0) {
this.captcha = res.data.data.picPath;
this.captchaId = res.data.data.captchaId;
} else {
this.$message.error(res.data.msg);
}
})
},
handleSubmit(e) {
e.preventDefault();
this.form.validateFields((err, values) => {
const loginData = {};
// loginData.username = this.cryptos.encrypto(values.username);
// loginData.password = this.cryptos.encrypto(values.password);
loginData.username = values.username;
loginData.password = values.password;
loginData.captchaId = this.captchaId;
loginData.captcha = values.captcha;
if (!err) {
this.$axios.defaults.withCredentials = true;
this.$axios.post(api.LOGIN.url, loginData).then(res => {
if (res.data.code === 0) {
// session
sessionStorage.setItem('login', res.data.data.token);
sessionStorage.setItem('userId', res.data.data.user.ID);
sessionStorage.setItem('userName', res.data.data.user.userName);
sessionStorage.setItem('nickName', res.data.data.user.nickName);
this.$router.push('/');
} else {
this.getCaptcha();
this.$message.error(res.data.msg);
}
})
}
});
},
},
}
</script>
<style>
.login_div {
background-color: #6495ED;
height: 100%;
width: 100%;
min-height: 100%;
display: flex;
align-items: center;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.login_content {
width: 386px;
margin-left: calc(50% - 193px);
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.4);
text-align: center;
}
.login_title {
font-size: 35px;
text-align: center;
margin-bottom: 25px;
font-weight: 600;
margin-top: 20px;
}
.login_button {
margin-left: 160px;
}
.login_explain > div > div > .ant-form-explain {
text-align: left;
margin-left: 30px;
}
.captcha > div > div > .ant-form-item-children {
display: flex;
}
.icon_color {
color: rgba(0,0,0,.25);
}
.captcha_img {
height: 32px;
margin-left: 30px;
cursor: pointer;
}
.input_login {
width: 320px;
margin-left: 30px
}
</style>

View File

@ -0,0 +1,499 @@
<template>
<div :class="mainDiv">
<div>
<a-button type="primary" style="float: right" @click="tripTo()"> </a-button>
</div>
<div class="header_write">{{infoData.header}}</div>
<!-- 模板内容-->
<div v-for="(item, index) in infoData.contents" :key="index">
<div class="title_weekly">{{item.title}}</div>
<quill-editor
:id="index"
style="margin-top: 20px"
v-model="item.content"
:ref="index"
@blur="onEditorBlur($event, index)" @focus="onEditorFocus($event, index)"
@change="onEditorChange($event)">
</quill-editor>
</div>
<div class="title_weekly">图片上传</div>
<div>
<div class="clearfix">
<a-upload
action="/week/fileUploadAndDownload/upload"
list-type="picture-card"
:file-list="fileList"
@preview="handlePreview"
@change="handleChange"
:showUploadList="showUploadList"
accept=".png,.jpeg,.jpg"
:headers="{ 'x-token': xToken, 'x-user-id': xUserId}"
:remove="removeFile"
>
<div v-if="canEdit">
<a-icon type="plus" />
<div class="ant-upload-text">
Upload
</div>
</div>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</div>
</div>
<div class="title_weekly">文件上传</div>
<div>
<div class="clearfix">
<a-upload
action="/week/fileUploadAndDownload/upload"
:file-list="fileList2"
@change="handleChange2"
:showUploadList="showUploadList"
:headers="{ 'x-token': xToken, 'x-user-id': xUserId}"
:remove="removeFile"
>
<div v-if="canEdit">
<a-button> <a-icon type="upload" /> Upload </a-button>
</div>
</a-upload>
</div>
</div>
<div class="title_weekly" style="margin: 20px 0 0 0"><span style="color: red">*</span>发送给</div>
<a-select
mode="multiple"
style="width: 100%"
placeholder="请选择人员"
@change="userChange"
v-model="sends"
:disabled="!canEdit"
:class="sendToClass"
>
<a-select-option v-for="li in userList" :key="li.ID">
{{li.nickName}}
</a-select-option>
</a-select>
<span style="color: red" v-if="sendToClass === 'cannotSubmit'"></span>
<div style="text-align: center" v-if="canEdit">
<a-button type="primary" class="commit_button" @click="editReports"> </a-button>
</div>
<div class="title_comments">评论</div>
<div v-for="(li, index) in commentsList">
<hr v-if="index !== 0"/>
<div class="title_weekly" style="font-size: 16px;">{{li.nickName}}</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{li.comment}}</div>
<div style="width: 100%; text-align: right">{{li.CreatedAt}}</div>
</div>
<div>
<a-textarea placeholder="请输入评论" v-model="comment" :rows="4" />
<div style="width: 100%; text-align: center">
<a-button type="primary" style="margin-top: 20px;" @click="addComment"> </a-button>
</div>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
import { quillEditor } from "vue-quill-editor";
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
export default {
name: "InfoComponent",
components: {quillEditor},
created() {
this.infoId = JSON.parse(this.$route.query.data);
// id
this.findData();
},
data() {
return {
mainDiv: 'top0',
infoId: '',
infoData: {},
canEdit: false,
showUploadList: {},
//
fileList: [],
previewVisible: false,
previewImage: '',
xToken: sessionStorage.getItem('login'),
xUserId: sessionStorage.getItem('userId'),
pictures: [],
//
fileList2: [],
//
template: {},
//
userList: [],
sends: [],
attachments: [],
// ()
sendTo: [],
sendToClass: 'canSubmit',
//
commentsList: [],
comment: ''
}
},
methods: {
tripTo() {
this.$router.push('viewWeeklyReport');
},
findData() {
this.$axios.get(api.FIND_REPORT_BY_ID.url + `?id=${this.infoId}`).then(res => {
if (res.data.code === 0) {
this.infoData = res.data.data.rewtReports;
if (this.infoData.userId === Number(sessionStorage.getItem('userId'))) {
this.canEdit = true;
} else {
this.showUploadList = {showRemoveIcon: false};
setTimeout(() => {
for (const li of document.getElementsByClassName('ql-toolbar')) {
li.style.display = 'none';
}
}, 80);
this.mainDiv = 'top1';
}
//
this.findComments(this.infoData.ID);
this.findUserList().then(() => {
this.setData();
});
} else {
this.$message.error(res.data.msg);
}
})
},
setData() {
//
this.template = this.infoData.contents;
const pic = [];
for (let index = 0; index < this.infoData.pictures.length; index++) {
const temp = {};
temp.uid = index;
temp.name = this.infoData.pictures[index].name;
temp.key = this.infoData.pictures[index].key;
temp.url = this.BASEURL + api.DOWNLOAD_FILE.url + '?fileName=' + this.infoData.pictures[index].key;
temp.status = 'done';
pic.push(temp);
}
this.fileList = pic;
const files = [];
for (let index = 0; index < this.infoData.attachments.length; index++) {
const temp = {};
temp.uid = index;
temp.name = this.infoData.attachments[index].name;
temp.key = this.infoData.attachments[index].key;
temp.url = this.BASEURL + api.DOWNLOAD_FILE.url + '?fileName=' + this.infoData.attachments[index].key;
temp.status = 'done';
files.push(temp);
}
this.fileList2 = files;
const user = [];
for (const li of this.infoData.sendTo) {
let flag = false;
for (const tr of this.userList) {
if (tr.ID === li.id) {
flag = true;
}
}
if (!flag) {
const da = {};
da.ID = li.id;
da.userName = li.name;
this.userList.push(da);
}
user.push(li.id);
}
this.sends = user;
this.sendTo = this.infoData.sendTo;
},
//
handleCancel() {
this.previewVisible = false;
},
async handlePreview(file) {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
this.previewImage = file.url || file.preview;
this.previewVisible = true;
},
handleChange({ fileList }) {
this.fileList = fileList;
},
//
handleChange2({ fileList }) {
this.fileList2 = fileList;
},
//
removeFile(file) {
if (this.canEdit) {
const params = {};
if (file.response) {
params.key = file.response.data.file.key;
} else {
params.key = file.key;
}
const files = [];
for (const li of this.fileList) {
if (li.response) {
if (li.response.data.file.key !== params.key) {
files.push(li);
}
} else if (li.key !== params.key) {
files.push(li);
}
}
this.fileList = files;
const files2 = [];
for (const li of this.fileList2) {
if (li.response) {
if (li.response.data.file.key !== params.key) {
files2.push(li);
}
} else if (li.key !== params.key) {
files2.push(li);
}
}
this.fileList2 = files2;
this.$axios.post(api.DELETE_FILE.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
});
}
},
//
onEditorBlur(event, id){
document.getElementById(id).children[0].style.zIndex = '1';
},
//
onEditorFocus(event, id){
if (!this.canEdit) {
event.enable(false);
} else {
event.enable(true);
}
document.getElementById(id).children[0].style.zIndex = '2';
},
//
onEditorChange(){
// console.log(this.template.contents);
},
//
async findUserList() {
const params = {};
params.page = 1;
params.pageSize = 99999;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
this.userList = res.data.data.list;
return Promise.resolve(this.userList);
} else {
this.$message.error(res.data.msg);
}
});
},
userChange(select) {
const temp = [];
if (select.length > 0) {
this.userList.forEach(li => {
for (const id of select) {
if (li.ID === id) {
const data = {};
data.id = id;
data.name = li.userName;
temp.push(data);
}
}
});
}
this.sendTo = temp;
if (this.sendTo.length > 0) {
this.sendToClass = 'canSubmit';
}
},
editReports() {
const pic = [];
if (this.fileList.length > 0) {
this.fileList.forEach(li => {
const da = {};
if (li.response) {
da.key = li.response.data.file.key;
da.name = li.response.data.file.name;
} else {
da.key = li.key;
da.name = li.name;
}
pic.push(da);
});
}
this.pictures = pic;
const atta = [];
if (this.fileList2.length > 0) {
this.fileList2.forEach(li => {
const da = {};
if (li.response) {
da.key = li.response.data.file.key;
da.name = li.response.data.file.name;
} else {
da.key = li.key;
da.name = li.name;
}
atta.push(da);
});
}
this.attachments = atta;
if (this.sendTo.length === 0) {
this.sendToClass = 'cannotSubmit';
} else {
const params = {};
params.id = this.infoData.ID;
params.userId = sessionStorage.getItem('userId');
params.userName = sessionStorage.getItem('userName');
params.sendTo = this.sendTo;
params.pictures = this.pictures;
params.attachments = this.attachments;
params.header = this.infoData.header;
params.contents = this.infoData.contents;
this.$axios.put(api.EDIT_REPORT.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
this.fileList = [];
this.previewVisible = false;
this.previewImage = '';
this.pictures = [];
this.fileList2 = [];
this.template = {};
this.userList = [];
this.attachments = [];
this.sendTo = [];
this.findData();
} else {
this.$message.error(res.data.msg);
}
})
}
},
//
findComments(reportId) {
this.$axios.get(api.FIND_COMMENT_LIST.url + `?reportId=${reportId}&page=1&pageSise=99999`).then(res => {
if (res.data.code === 0) {
this.commentsList = res.data.data.list;
} else {
this.$message.error(res.data.msg);
}
});
},
addComment() {
const params = {};
params.reportId = this.infoData.ID;
params.userName = sessionStorage.getItem('userName');
params.comment = this.comment;
this.$axios.post(api.ADD_COMMENT.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
this.findComments(this.infoData.ID);
this.comment = '';
} else {
this.$message.error(res.data.msg);
}
});
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill;
},
},
}
</script>
<style xml:lang="less">
.ql-toolbar {
position: fixed;
top: 104px;
background-color: white;
width: calc(100% - 270px);
left: 260px;
border-radius: 4px;
box-shadow: 1px 2px 1px 1px rgba(144,144,144,0.1);
}
.quill-editor {
border: 1px solid #ccc;
margin: 20px 0;
}
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
z-index: 3;
top: 33px;
}
.ql-snow .ql-tooltip {
left: 0!important;
}
.header_write {
text-align: center;
font-size: 18px;
font-weight: 600;
}
.readOnly {
-webkit-user-modify: read-only;
}
.canSubmit > div {
border-color: #d9d9d9;
}
.cannotSubmit > div {
border-color: red;
}
.top0 {
margin: 50px 0;
}
.top1 {
margin: 0;
}
.title_weekly {
font-weight: 600;
}
.commit_button {
width: 400px;
margin-top: 20px
}
.title_comments {
font-weight: 600;
margin: 20px 0;
font-size: 18px;
}
</style>

View File

@ -0,0 +1,207 @@
<template>
<div>
<div>
<span>人员</span>
<a-select
class="select_div"
placeholder="请选择人员"
v-model="selectedUserId"
:allowClear="true"
:showSearch="true"
>
<a-select-option v-for="li in userList" :key="li.ID">
{{li.nickName}}
</a-select-option>
</a-select>
<span class="span_title">起止时间</span>
<a-range-picker show-time style="margin-left: 10px" v-model="rangeTime">
<template slot="renderExtraFooter">
extra footer
</template>
</a-range-picker>
<span class="span_title">周报内容</span>
<a-input v-model="content" placeholder="请输入周报内容" class="content_input"></a-input>
<a-button type="primary" @click="findDataList" class="span_title">
查询
</a-button>
<div style="margin-top: 10px">
<a-table :columns="columns" :data-source="tableList" rowKey="ID" :pagination="pagination" @change="findDataList">
<span slot="action" slot-scope="text, li">
<a @click="openInfo(li)"></a>
</span>
<span slot="contents" slot-scope="text" class="ecllipsis" :title="toContent(text)">
{{toContent(text)}}
</span>
</a-table>
</div>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "MainComponent",
data() {
return {
userList: [],
selectedUserId: '',
rangeTime: [],
content: '',
columns: [
{
title: '姓名',
dataIndex: 'nickName',
key: 'nickName'
},
{
title: '内容',
dataIndex: 'contents',
key: 'contents',
scopedSlots: { customRender: 'contents' },
width: '500px',
},
{
title: '评论数',
dataIndex: 'commentCount',
key: 'commentCount'
},
{
title: '时间',
dataIndex: 'CreatedAt',
key: 'CreatedAt'
},
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' },
},
],
tableList: [],
pagination: {
total: 0,
defaultCurrent: 1,
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
onShowSizeChange: (current, pageSize) => {
this.pagination.defaultCurrent = current;
this.pagination.defaultPageSize = pageSize;
},
onChange: (current, pageSize) => {
this.pagination.defaultCurrent = current;
this.pagination.defaultPageSize = pageSize;
},
},
page: 1,
pageSize: 10,
}
},
created() {
this.findUserList();
this.findDataList();
},
methods: {
//
findUserList() {
const params = {};
params.page = 1;
params.pageSize = 9999;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
this.userList = res.data.data.list;
} else {
this.$message.error(res.data.msg);
}
})
},
//
findDataList() {
let startTime = '';
let endTime = '';
if (this.rangeTime.length !== 0) {
startTime = this.toDate(this.rangeTime[0]._d);
endTime = this.toDate(this.rangeTime[1]._d);
}
this.$axios.get(api.FIND_REPORT_LIST.url + `?page=${this.pagination.defaultCurrent}&pageSize=${this.pagination.defaultPageSize}&content=${this.content}&currUserId=${sessionStorage.getItem('userId')}&userId=${this.selectedUserId}&startTime=${startTime}&endTime=${endTime}`).then(res => {
if (res.data.code === 0) {
this.tableList = res.data.data.list;
this.pagination.total = res.data.data.total;
} else {
this.$message.error(res.data.msg);
this.pagination.total = 0;
}
})
},
openInfo(data) {
this.$router.push({
path:'/editWeeklyReport',
query:{data: JSON.stringify(data.ID)}
})
},
//
toDate (date) {
const y = date.getFullYear();
let m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
let d = date.getDate();
d = d < 10 ? ('0' + d) : d;
const h = date.getHours();
let minute = date.getMinutes();
minute = minute < 10 ? ('0' + minute) : minute;
return y + '-' + m + '-' + d+' '+h+':'+minute;
},
//
toContent(data) {
let str = '';
data.forEach(li => {
str += li.title + this.toChinese(li.content) + ';';
});
return str;
},
toChinese(strValue) {
if (strValue !== null && strValue !== '') {
const reg = /[\u4e00-\u9fa5]/g;
let content = '';
try {
if (strValue.match(reg) !== null) {
content = strValue.match(reg).join('');
}
} catch (e) {
console.log(e);
}
return content;
}
return '';
}
}
}
</script>
<style xml:lang="less">
.ecllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
width: 500px;
}
.select_div {
width: 200px;
margin-left: 10px;
}
.span_title {
margin-left: 20px;
}
.content_input {
width: 200px;
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,226 @@
<template>
<div>
<div class="static_ana">数据统计</div>
<div class="title_ana">周报</div>
<div class="content_ana">
<div class="left_ana">
<div>{{infoData.commitCount}}</div>
<a-popover placement="topLeft">
<template slot="content">
{{commitPeoples}}
</template>
<span>已提交</span>
</a-popover>
</div>
<div class="right_ana">
<div>{{infoData.uncommitCount}}</div>
<a-popover placement="topLeft">
<template slot="content">
{{uncommitPeoples}}
</template>
<span>未提交</span>
</a-popover>
</div>
</div>
<div class="export_ana">导出功能</div>
<div class="content_ana">
<div class="left_ana">
<span class="title_ana">人员</span>
<a-select
mode="multiple"
style="width: 400px; margin-left: 20px"
placeholder="请选择人员"
@change="userChange"
v-model="sends"
>
<a-select-option v-for="li in userList" :key="li.ID">
{{li.userName}}
</a-select-option>
</a-select>
</div>
<div class="right_ana">
<span class="time_ana">起止时间</span>
<a-range-picker @change="timeOnChange" />
<a-button type="primary" @click="exportFile" style="margin-left: 20px"> 导出 </a-button>
</div>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "StatisticalExport",
data() {
return {
infoData: {},
uncommitPeoples: '',
commitPeoples: '',
//
userList: [],
sends: [],
reporters: [],
startTime: '',
endTime: '',
}
},
created() {
this.findResult();
this.findUserList();
},
methods: {
findResult() {
this.$axios.get(api.FIND_RESULT.url + `?userId=${sessionStorage.getItem('userId')}`).then(res => {
if (res.data.code === 0) {
this.infoData = res.data.data.rewtOutput;
let un = '';
if (res.data.data.rewtOutput.uncommitPeoples) {
res.data.data.rewtOutput.uncommitPeoples.forEach(da => {
un += da.name + '';
});
}
this.uncommitPeoples = un.substring(0, un.length - 1);
let is = '';
if (res.data.data.rewtOutput.commitPeoples) {
res.data.data.rewtOutput.commitPeoples.forEach(da => {
is += da.name + '';
});
}
this.commitPeoples = is.substring(0, un.length - 1);
} else {
this.$message.error(res.data.msg);
}
})
},
findUserList() {
const params = {};
params.page = 1;
params.pageSize = 99999;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
let data = [];
const li = {};
li.ID = 0;
li.userName = '全部';
data.push(li);
data = data.concat(res.data.data.list);
this.userList = data;
const all = [];
res.data.data.list.forEach(li => {
all.push(li.ID);
});
this.allId = all;
} else {
this.$message.error(res.data.msg);
}
})
},
userChange(select) {
let flag = false;
select.forEach(id => {
if (id === 0) {
flag = true;
}
});
const temp = [];
if (flag) {
this.sends = this.allId;
this.userList.forEach(li => {
if (li.ID !== 0) {
const data = {};
data.id = li.ID;
data.name = li.userName;
temp.push(data);
}
});
} else {
if (select.length > 0) {
this.userList.forEach(li => {
select.forEach(id => {
if (li.ID === id) {
const data = {};
data.id = id;
data.name = li.userName;
temp.push(data);
}
});
});
}
}
this.reporters = temp;
},
timeOnChange(date, dateString) {
this.startTime = dateString[0];
this.endTime = dateString[1];
},
exportFile() {
const name = this.startTime + '-' + this.endTime + '周报汇总.xlsx';
this.$axios({
url: api.EXPORT_FILE.url + `?userIds=${this.sends}&startTime=${this.startTime}&endTime=${this.endTime}`,
method: 'get',
responseType: 'blob'
}).then((res) => {
let blob = new Blob([res.data]);
let url = URL.createObjectURL(blob);
this.toFile(url, name)
})
},
toFile(downUrl, fileName) {
const aLinkUrl = document.createElement('a');
aLinkUrl.href = downUrl;
aLinkUrl.download = fileName;
const clickAlink = (obj) => {
const ev = document.createEvent('MouseEvents');
ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
obj.dispatchEvent(ev)
};
clickAlink(aLinkUrl)
},
}
}
</script>
<style xml:lang="less">
.static_ana {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
}
.title_ana {
font-size: 16px;
font-weight: 600;
margin: 20px;
}
.content_ana {
display: flex;
width: 100%;
font-size: 20px;
font-weight: 600;
}
.left_ana {
width: 50%;
text-align: right;
padding-right: 50px;
border-right: 1px solid #ccc;
}
.right_ana {
width: 50%;
padding-left: 50px;
}
.export_ana {
font-size: 18px;
font-weight: 600;
margin: 20px 0;
}
.time_ana {
font-size: 16px;
font-weight: 600;
margin: 20px 20px 20px 0;
}
</style>

View File

@ -0,0 +1,169 @@
<template>
<div class="content_div">
<div>
<span class="title_tem">模板标题</span>
<a-input v-model="data.header" placeholder="请输入模板标题" class="modal_title"></a-input>
<span class="create_people">创建人</span>
<span v-if="isCreate">{{userName}}</span>
<span v-else>{{data.userName}}</span>
<a-button type="primary" style="float: right" @click="saveData"> <a-icon type="save" /> 保存 </a-button>
</div>
<div>
<div v-if="!isCreate">
<div v-for="(li, index) in data.contents" :key="index">
<div class="content1">
<div class="content2">
<a-input v-model="li.title" placeholder="请输入字段标题" class="input_temp"></a-input>
<a-icon type="delete" style="cursor: pointer" @click="deleteContent(index)"/>
</div>
<div class="content_tem">
<span>
待填写者输入
</span>
</div>
</div>
</div>
</div>
<div v-else>
<div v-for="(li, index) in addContents" :key="index">
<div class="content1">
<div class="content2">
<a-input v-model="li.title" placeholder="请输入字段标题" class="input_temp"></a-input>
<a-icon type="delete" style="cursor: pointer" @click="deleteContent(index)"/>
</div>
<div class="content_tem">
<span>
待填写者输入
</span>
</div>
</div>
</div>
</div>
<a-button type="primary" class="add_button_tem" @click="addContent"> <a-icon type="plus" /> </a-button>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "TemplateEditing",
created() {
this.findData();
},
data() {
return {
data: {},
//
isCreate: true,
userName: '',
addContents: []
}
},
methods: {
findData() {
this.$axios.get(api.GET_TEMPLATE_LIST.url).then(res => {
if (res.data.code === 0) {
if (res.data.data.total > 0) {
this.isCreate = false;
this.data = res.data.data.list[0];
} else {
this.isCreate = true;
this.data.userName = sessionStorage.getItem('userName');
this.userName = sessionStorage.getItem('userName');
}
} else {
this.$message.error(res.data.msg);
}
});
},
addContent() {
const li = {};
li.title = '';
li.content = '';
if (this.data.contents) {
this.data.contents.push(li);
this.addContents.push(li);
} else {
const con = [];
con.push(li);
this.data.contents = con;
this.addContents.push(li);
}
},
deleteContent(index) {
this.data.contents.splice(index, 1);
},
saveData() {
if (this.isCreate) {
//
this.$axios.post(api.ADD_TEMPLATE.url, this.data).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
});
} else {
//
this.$axios.put(api.EDIT_TEMPLATE.url, this.data).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
});
}
}
}
}
</script>
<style xml:lang="less">
.content_div {
height: 100%;
padding: 0 200px;
}
.content1 {
margin-top: 20px;
box-shadow: 0 2px 12px 0 #ccc;
border-radius: 8px;
padding: 20px;
}
.content2 {
border-bottom: 1px solid #ccc;
}
.input_temp {
width: calc(100% - 50px);
border: none;
font-size: 16px;
font-weight: 600;
}
.input_temp:focus {
border: none!important;
box-shadow: none!important;
}
.title_tem {
font-size: 20px;
font-weight: 600;
}
.modal_title {
width: 350px;
margin-left: 10px;
}
.create_people {
margin-left: 100px;
margin-right: 10px;
}
.content_tem {
margin: 10px 0 0 10px;
}
.add_button_tem {
width: 100%;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,335 @@
<template>
<div class="content_wri">
<div class="header_write">{{template.header}}</div>
<!-- 模板内容-->
<div v-for="(item, index) in template.contents" :key="index">
<div class="title_weight">{{item.title}}</div>
<quill-editor
:id="index"
style="margin-top: 20px"
v-model="item.content"
:ref="index"
@blur="onEditorBlur($event, index)" @focus="onEditorFocus($event, index)">
</quill-editor>
</div>
<div class="title_weight">图片上传</div>
<div>
<div class="clearfix">
<a-upload
action="/week/fileUploadAndDownload/upload"
list-type="picture-card"
:file-list="fileList"
@preview="handlePreview"
@change="handleChange"
accept=".png,.jpeg,.jpg"
:headers="{ 'x-token': xToken, 'x-user-id': xUserId}"
:remove="removeFile"
>
<div v-if="fileList.length < 8">
<a-icon type="plus" />
<div class="ant-upload-text">
Upload
</div>
</div>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</div>
</div>
<div class="title_weight">文件上传</div>
<div>
<div class="clearfix">
<a-upload
action="/week/fileUploadAndDownload/upload"
:file-list="fileList2"
@change="handleChange2"
:headers="{ 'x-token': xToken, 'x-user-id': xUserId}"
:remove="removeFile"
>
<div>
<a-button> <a-icon type="upload" /> Upload </a-button>
</div>
</a-upload>
</div>
</div>
<div style="font-weight: 600; margin: 20px 0 0 0"><span style="color: red">*</span>发送给</div>
<a-select
mode="multiple"
style="width: 100%"
placeholder="请选择人员"
@change="userChange"
v-model="sends"
:class="sendToClass"
>
<a-select-option v-for="li in userList" :key="li.ID">
{{li.nickName}}
</a-select-option>
</a-select>
<span style="color: red" v-if="sendToClass === 'cannotSubmit'"></span>
<div style="text-align: center">
<a-button type="primary" class="commit_button" @click="addReports"> </a-button>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
import { quillEditor } from "vue-quill-editor";
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
export default {
name: "WriteWeeklyReport",
components: {quillEditor},
data() {
return {
//
fileList: [],
previewVisible: false,
previewImage: '',
xToken: sessionStorage.getItem('login'),
xUserId: sessionStorage.getItem('userId'),
pictures: [],
//
fileList2: [],
//
template: {},
//
userList: [],
sends: [],
attachments: [],
sendToClass: 'canSubmit',
// ()
sendTo: [],
};
},
created() {
//
this.findTempl();
//
this.findUserList();
},
methods: {
//
handleCancel() {
this.previewVisible = false;
},
async handlePreview(file) {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
this.previewImage = file.url || file.preview;
this.previewVisible = true;
},
handleChange({ fileList }) {
this.fileList = fileList;
},
//
handleChange2({ fileList }) {
this.fileList2 = fileList;
},
//
removeFile(file) {
const params = {};
params.key = file.response.data.file.key;
const files = [];
this.fileList.forEach(li => {
if (li.response.data.file.key !== params.key) {
files.push(li);
}
});
this.fileList = files;
const files2 = [];
this.fileList2.forEach(li => {
if (li.response.data.file.key !== params.key) {
files2.push(li);
}
});
this.fileList2 = files2;
this.$axios.post(api.DELETE_FILE.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
});
},
//
findTempl() {
this.$axios.get(api.GET_TEMPLATE_LIST.url).then(res => {
if (res.data.code === 0) {
if (res.data.data.total > 0) {
this.template = res.data.data.list[0];
} else {
this.$message.error('请先创建周报模板');
}
} else {
this.$message.error(res.data.msg);
}
});
},
//
onEditorBlur(event, id){
document.getElementById(id).children[0].style.zIndex = '1';
},
//
onEditorFocus(event, id){
document.getElementById(id).children[0].style.zIndex = '2';
},
//
findUserList() {
const params = {};
params.page = 1;
params.pageSize = 99999;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
this.userList = res.data.data.list;
} else {
this.$message.error(res.data.msg);
}
})
},
userChange(select) {
const temp = [];
if (select.length > 0) {
this.userList.forEach(li => {
select.forEach(id => {
if (li.ID === id) {
const data = {};
data.id = id;
data.name = li.userName;
temp.push(data);
}
})
});
}
this.sendTo = temp;
if (this.sendTo.length > 0) {
this.sendToClass = 'canSubmit';
}
},
addReports() {
const pic = [];
if (this.fileList.length > 0) {
this.fileList.forEach(li => {
const da = {};
da.key = li.response.data.file.key;
da.name = li.response.data.file.name;
pic.push(da);
});
}
this.pictures = pic;
const atta = [];
if (this.fileList2.length > 0) {
this.fileList2.forEach(li => {
const da = {};
da.key = li.response.data.file.key;
da.name = li.response.data.file.name;
atta.push(da);
});
}
this.attachments = atta;
if (this.sendTo.length === 0) {
this.sendToClass = 'cannotSubmit';
} else {
const params = {};
params.userId = Number(sessionStorage.getItem('userId'));
params.userName = sessionStorage.getItem('userName');
params.sendTo = this.sendTo;
params.pictures = this.pictures;
params.attachments = this.attachments;
params.header = this.template.header;
params.contents = this.template.contents;
this.$axios.post(api.ADD_REPORT.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
this.findTempl();
this.findTempl();
this.fileList = [];
this.previewVisible = false;
this.previewImage = '';
this.pictures = [];
this.fileList2 = [];
this.template = {};
this.userList = [];
this.attachments = [];
this.sendTo = [];
this.sends = [];
} else {
this.$message.error(res.data.msg);
}
})
}
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill;
},
},
}
</script>
<style xml:lang="less">
.ql-toolbar {
position: fixed;
top: 104px;
background-color: white;
width: calc(100% - 270px);
left: 260px;
border-radius: 4px;
box-shadow: 1px 2px 1px 1px rgba(144,144,144,0.1);
}
.quill-editor {
border: 1px solid #ccc;
margin: 20px 0;
}
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
z-index: 3;
top: 33px;
}
.ql-snow .ql-tooltip {
left: 0!important;
}
.header_write {
text-align: center;
font-size: 18px;
font-weight: 600;
}
.canSubmit > div {
border-color: #d9d9d9;
}
.cannotSubmit > div {
border-color: red;
}
.content_wri {
margin: 50px 0;
}
.title_weight {
font-weight: 600;
}
.commit_button {
width: 400px;
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,328 @@
<template>
<a-layout class="content">
<a-layout-header class="header">
<div>
<div class="title_nav">
周报系统
</div>
<div @click="toggleCollapsed" class="control_button">
<a-icon :type="collapsed ? 'menu-unfold' : 'menu-fold'" />
</div>
</div>
<div class="tip_div">
欢迎您
<span class="tip_div_nickName">{{nickName}}</span>|
<span @click="logout" class="logout_span">注销</span>
</div>
</a-layout-header>
<a-layout>
<div class="left_div_nav">
<div :class="left_div">
<a-menu
class="menu_nav"
mode="inline"
theme="dark"
:inline-collapsed="collapsed"
style="text-align: left"
@click="menuClick"
:defaultSelectedKeys="defaultSelectedKeys"
:defaultOpenKeys="[1,6]"
>
<template v-for="item in menuList">
<a-menu-item v-if="!item.children" :key="item.ID" :title="item.name">
<a-icon type="pie-chart" />
<span>{{ item.name }}</span>
</a-menu-item>
<sub-menu v-else :key="item.ID" :menu-info="item" />
</template>
</a-menu>
</div>
<div :class="right_div" class="right_div_nav">
<div class="current_div">
<span class="current_span">当前位置</span>
<span>{{location}}</span>
</div>
<a-layout class="layout_nav">
<a-layout-content
:style="{ background: '#fff', padding: '20px', margin: 0, minHeight: '280px' }"
>
<router-view></router-view>
</a-layout-content>
</a-layout>
</div>
</div>
</a-layout>
</a-layout>
</template>
<script>
import { Menu } from 'ant-design-vue';
import api from '../../utils/api';
const SubMenu = {
template: `
<a-sub-menu :key="menuInfo.ID" v-bind="$props" v-on="$listeners">
<span slot="title" :title="menuInfo.name">
<a-icon :type="menuInfo.icon" /><span>{{ menuInfo.name }}</span>
</span>
<template v-for="item in menuInfo.children">
<a-menu-item v-if="!item.children" :key="item.ID" :title="item.name">
<a-icon :type="item.icon" />
<span>{{ item.name }}</span>
</a-menu-item>
<sub-menu v-else :key="item.ID" :menu-info="item" />
</template>
</a-sub-menu>
`,
name: 'SubMenu',
// must add isSubMenu: true
isSubMenu: true,
props: {
...Menu.SubMenu.props,
// Cannot overlap with properties within Menu.SubMenu.props
menuInfo: {
type: Object,
default: () => ({}),
},
},
};
export default {
components: {
'sub-menu': SubMenu,
},
watch: {
// vuex
'$store.state.pageRouterData.location' (newval) {
this.location = newval;
},
'$store.state.pageRouterData.defaultSelectedKeys' (newval) {
this.defaultSelectedKeys = newval;
}
},
name: "Navigation",
data() {
return {
collapsed: false,
left_div: 'left_div_open',
right_div: 'right_div_open',
location: this.$store.state.pageRouterData.location,
defaultSelectedKeys: this.$store.state.pageRouterData.defaultSelectedKeys,
menuList: [],
//
nickName: sessionStorage.getItem('nickName'),
}
},
created() {
setTimeout(this.findMenuList(), 1000);
},
methods: {
//
trip(name, path) {
this.location = name;
this.$router.push(path);
},
toggleCollapsed() {
this.collapsed = !this.collapsed;
if (this.left_div === 'left_div_open') {
this.left_div = 'left_div_close';
this.right_div = 'right_div_close';
if (document.getElementsByClassName('ql-toolbar').length > 0) {
document.getElementsByClassName('ql-toolbar').forEach(li => {
li.style.left = '90px';
li.style.width = 'calc(100% - 100px)';
});
}
} else{
this.left_div = 'left_div_open';
this.right_div = 'right_div_open';
document.getElementsByClassName('ql-toolbar').forEach(li => {
li.style.left = '260px';
li.style.width = 'calc(100% - 270px)';
});
}
},
findMenuList() {
this.$axios.defaults.headers.common['x-token'] = sessionStorage.getItem('login');
this.$axios.defaults.headers.common['x-user-id'] = sessionStorage.getItem('userId');
this.$axios.post(api.GET_MENU.url).then(res => {
if (res.data.code === 0) {
this.menuList = res.data.data.menus;
}
});
},
logout() {
sessionStorage.clear();
this.$store.commit('pageRouterData/setLocation', '查看周报');
this.$store.commit('pageRouterData/setDefaultSelectedKeys', [2]);
this.$router.push('/login');
},
// eslint-disable-next-line no-unused-vars
menuClick({ item, key }) {
this.getCheckedNodes(this.menuList, key);
const keyArray = [key];
this.$store.commit('pageRouterData/setDefaultSelectedKeys', keyArray);
},
getCheckedNodes(data, value) {
data.forEach(item => {
if (item.ID === value) {
this.$store.commit('pageRouterData/setLocation', item.name);
this.trip(item.name, item.path);
} else {
if (item.children && item.children.length > 0) {
this.getCheckedNodes(item.children, value);
}
}
});
},
}
}
</script>
<style scoped>
.content {
height: 100%;
}
section {
overflow: hidden;
background-color: rgba(255, 255, 255, 0.4);
}
.left_div_open {
width: 250px;
background-color: rgb(0, 21, 41);
height: 100%;
overflow-y: auto;
}
.left_div_close {
width: 80px;
background-color: rgb(0, 21, 41);
height: 100%;
overflow-y: auto;
}
.right_div_open {
width: calc(100% - 250px)
}
.right_div_close {
width: calc(100% - 80px)
}
.title_nav {
color: white;
font-size: 25px;
font-weight: 600;
text-align: left;
margin-left: -25px;
width: 225px;
float: left;
}
.control_button {
float: left;
color: white;
cursor: pointer;
}
</style>
<style xml:lang="less">
.ant-layout-header {
height: 64px;
padding: 0 50px;
line-height: 64px;
background: #0a42ab!important;
}
.__tabs {
display: -webkit-box;
margin-top: -1px;
width: 100%;
overflow-x: hidden;
}
.__tab-item {
white-space: nowrap;
padding: 8px;
font-size: 14px;
border: 1px solid #cccccc4d;
border-left: none;
border-bottom: 0;
line-height: 14px;
cursor: pointer;
transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
overflow: hidden;
}
.el-icon-close {
width: 12px;
margin-right: 0;
}
.__is-active {
border-bottom: 2px solid #409eff;
color: #409eff;
background-color: #f6f6f6;
}
.__is-active .el-icon-close {
width: 12px;
margin-right: 0;
margin-left: 2px;
}
.__contextmenu {
margin: 0;
border: 1px solid #e4e7ed;
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 14px;
color: #333;
box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.__contextmenu li {
margin: 0;
}
.__contextmenu li :hover {
background: #f2f2f2;
cursor: pointer;
}
.__contextmenu li button {
color: #2c3e50;
border: none;
}
.ant-menu-item {
margin: 0!important;
}
.tip_div {
float: right;
color: white;
width: 180px;
display: flex;
}
.tip_div_nickName {
margin-right: 10px
}
.logout_span {
margin-left: 10px;
cursor: pointer;
}
.left_div_nav {
display: flex;
height: 100%;
}
.right_div_nav {
background-color: rgba(231, 234, 237, 0.3);
}
.current_div {
background-color: white;
height: 30px;
text-align: left;
line-height: 30px;
display: flex;
}
.current_span {
font-weight: 600;
font-size: 14px;
padding-left: 10px;
}
.layout_nav {
margin: 10px;
height: calc(100% - 50px);
}
</style>

View File

@ -0,0 +1,283 @@
<template>
<div>
<div class="rule_div">设定规则</div>
<div>
<span>需要提交人</span>
<a-select
mode="multiple"
class="select_sta"
placeholder="请选择人员"
@change="userChange"
v-model="sends"
>
<a-select-option v-for="li in userList" :key="li.ID">
{{li.nickName}}
</a-select-option>
</a-select>
</div>
<div class="main_sta_div">
<span class="time_sta_span">起止时间</span>
<a-select v-model="startWeek" class="select_sta_time">
<a-select-option :value="1">
周一
</a-select-option>
<a-select-option :value="2">
周二
</a-select-option>
<a-select-option :value="3">
周三
</a-select-option>
<a-select-option :value="4">
周四
</a-select-option>
<a-select-option :value="5">
周五
</a-select-option>
<a-select-option :value="6">
周六
</a-select-option>
<a-select-option :value="7">
周日
</a-select-option>
</a-select>
<a-select v-model="startHour" class="select_str">
<a-select-option :key="0">
0:00
</a-select-option>
<a-select-option v-for="li in 23" :key="li">
{{li}}:00
</a-select-option>
</a-select>
<span style="margin-left: 20px">~</span>
<a-select v-model="endWeek" class="select_str">
<a-select-option :value="1">
周一
</a-select-option>
<a-select-option :value="2">
周二
</a-select-option>
<a-select-option :value="3">
周三
</a-select-option>
<a-select-option :value="4">
周四
</a-select-option>
<a-select-option :value="5">
周五
</a-select-option>
<a-select-option :value="6">
周六
</a-select-option>
<a-select-option :value="7">
周日
</a-select-option>
</a-select>
<a-select v-model="endHour" style="width: 80px; margin-left: 10px">
<a-select-option :key="0">
0:00
</a-select-option>
<a-select-option v-for="li in 23" :key="li">
{{li}}:00
</a-select-option>
</a-select>
<a-button type="primary" @click="submitData" style="margin-left: 20px"> 提交 </a-button>
</div>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "StatisticalRules",
data() {
return {
isCreate: true,
//
userList: [],
sends: [],
reporters: [],
// id
allId: [],
startWeek: '',
startHour: '',
endWeek: '',
endHour: '',
editId: '',
}
},
created() {
this.findDataList();
},
methods: {
findDataList() {
const params = {};
params.page = 1;
params.pageSize = 99999;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
let data = [];
const li = {};
li.ID = 0;
li.userName = '全部';
data.push(li);
data = data.concat(res.data.data.list);
this.userList = data;
const all = [];
res.data.data.list.forEach(li => {
all.push(li.ID);
});
this.allId = all;
this.$axios.get(api.FIND_RULE_LIST.url + `?page=1&pageSize=1&userId=${sessionStorage.getItem('userId')}`).then(resp => {
if (resp.data.code === 0) {
if (resp.data.data.list) {
this.isCreate = false;
this.editId = resp.data.data.list[0].ID;
this.startWeek = resp.data.data.list[0].startWeek;
this.startHour = resp.data.data.list[0].startHour;
this.endWeek = resp.data.data.list[0].endWeek;
this.endHour = resp.data.data.list[0].endHour;
const user = [];
const temp = [];
for (const li of resp.data.data.list[0].reporters) {
let flag = false;
for (const tr of this.userList) {
if (tr.ID === li.id) {
flag = true;
}
}
if (!flag) {
const da = {};
da.ID = li.id;
da.userName = li.name;
this.userList.push(da);
}
user.push(li.id);
const data = {};
data.id = li.id;
data.name = li.name;
temp.push(data);
}
this.sends = user;
this.reporters = temp;
}
} else {
this.$message.error(resp.data.msg);
}
})
} else {
this.$message.error(res.data.msg);
}
})
},
userChange(select) {
let flag = false;
select.forEach(li => {
if (li === 0) {
flag = true;
}
});
const temp = [];
if (flag) {
this.sends = this.allId;
this.userList.forEach(li => {
if (li.ID !== 0) {
const data = {};
data.id = li.ID;
data.name = li.userName;
temp.push(data);
}
});
} else {
if (select.length > 0) {
this.userList.forEach(li => {
select.forEach(id => {
if (li.ID === id) {
const data = {};
data.id = id;
data.name = li.userName;
temp.push(data);
}
})
});
}
}
this.reporters = temp;
},
submitData() {
if (this.isCreate) {
//
const params = {};
params.userId = Number(sessionStorage.getItem('userId'));
params.startWeek = this.startWeek;
params.startHour = this.startHour;
params.endWeek = this.endWeek;
params.endHour = this.endHour;
params.reporters = this.reporters;
this.$axios.post(api.ADD_RULE.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
})
} else {
//
const params = {};
params.ID = this.editId;
params.userId = Number(sessionStorage.getItem('userId'));
params.startWeek = this.startWeek;
params.startHour = this.startHour;
params.endWeek = this.endWeek;
params.endHour = this.endHour;
params.reporters = this.reporters;
this.$axios.put(api.UPDATE_RULE.url, params).then(res => {
if (res.data.code === 0) {
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
})
}
}
}
}
</script>
<style xml:lang="less">
.rule_div {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
}
.select_sta {
width: 500px;
margin-left: 20px;
}
.main_sta_div {
margin-top: 10px;
}
.time_sta_span {
margin-left: 14px;
}
.select_sta_time {
width: 80px;
margin-left: 20px;
}
.select_str {
width: 80px;
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,286 @@
<template>
<div>
<div style="height: 50px">
<a-button type="primary" @click="openAdd" style="float: right">
<a-icon type="plus" />
新增
</a-button>
</div>
<div>
<a-table :columns="columns" :data-source="tableList" rowKey="ID" :pagination="pagination" @change="findUserList">
<template slot="authorityId" slot-scope="text, li">
<span v-for="al in authorityList" :key="al.ID">
<span v-if="al.authorityId === li.authorityId">{{al.authorityName}}</span>
</span>
</template>
<span slot="action" slot-scope="text, li">
<a @click="openEdit(li)"></a>
<a-divider type="vertical" />
<a @click="openDelete(li)"></a>
</span>
</a-table>
</div>
<a-modal v-model="addVisible" title="新增用户" @ok="submitAdd">
<a-form :form="addForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
<a-form-item label="用户名">
<a-input v-decorator="[ 'username', { rules: [{ required: true, message: '!' }] }, ]"
type="text"
placeholder="请输入用户名" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="昵称">
<a-input v-decorator="[ 'nickname', { rules: [{ required: true, message: '!' }] }, ]"
type="text"
placeholder="请输入昵称" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="密码">
<a-input v-decorator="[ 'password', { rules: [{ required: true, message: '!' }] }, ]"
type="password"
placeholder="请输入密码" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="角色">
<a-select style="width: 320px"
placeholder="请输入角色"
v-decorator="[ 'authorityId', { rules: [{ required: true, message: '角色不能为空!' }] }, ]"
@change="addAuthorityChange">
<a-select-option v-for="li in authorityList" :key="li.authorityId">
{{ li.authorityName }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</a-modal>
<a-modal v-model="editVisible" title="编辑用户" @ok="submitEdit">
<a-form :form="editForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }">
<a-input v-decorator="['ID']"
type="text" class="input_width_use" style="display: none">
</a-input>
<a-form-item label="用户名">
<a-input v-decorator="[ 'username', { rules: [{ required: true, message: '!' }] }, ]"
type="text"
:disabled="true"
placeholder="请输入用户名" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="昵称">
<a-input v-decorator="[ 'nickname', { rules: [{ required: true, message: '!' }] }, ]"
type="text"
placeholder="请输入昵称" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="密码">
<a-input v-decorator="[ 'password', { rules: [{ required: true, message: '!' }] }, ]"
type="password"
placeholder="请输入密码" class="input_width_use">
</a-input>
</a-form-item>
<a-form-item label="角色">
<a-select style="width: 320px"
placeholder="请输入角色"
v-decorator="[ 'authorityId', { rules: [{ required: true, message: '角色不能为空!' }] }, ]"
@change="editAuthorityChange">
<a-select-option v-for="li in authorityList" :key="li.authorityId">
{{ li.authorityName }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "UserManagement",
data() {
return {
columns: [
{
title: '用户名',
dataIndex: 'userName',
key: 'userName',
},
{
title: '昵称',
dataIndex: 'nickName',
key: 'nickName',
},
{
title: '角色',
dataIndex: 'authorityId',
key: 'authorityId',
scopedSlots: { customRender: 'authorityId' }
},
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' },
},
],
authorityList: [],
tableList: [],
pagination: {
total: 0,
defaultCurrent: 1,
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'],
onShowSizeChange: (current, pageSize) => {
this.pagination.defaultCurrent = current;
this.pagination.defaultPageSize = pageSize;
},
onChange: (current, pageSize) => {
this.pagination.defaultCurrent = current;
this.pagination.defaultPageSize = pageSize;
},
},
page: 1,
pageSize: 10,
addVisible: false,
addForm: this.$form.createForm(this, { name: 'addUser' }),
addAuthorityIds: [],
editVisible: false,
editData: '',
editForm: this.$form.createForm(this, { name: 'editUser' }),
editAuthorityIds: [],
oldPassword: '',
}
},
created() {
this.findUserList();
this.findAuthorityList();
},
methods: {
findUserList() {
const params = {};
params.page = this.pagination.defaultCurrent;
params.pageSize = this.pagination.defaultPageSize;
this.$axios.post(api.GET_USER_LIST.url, params).then(res => {
if (res.data.code === 0) {
this.tableList = res.data.data.list;
this.pagination.total = res.data.data.total;
} else {
this.$message.error(res.data.msg);
this.pagination.total = 0;
}
})
},
//
findAuthorityList() {
const params = {};
params.page = 1;
params.pageSize = 9999;
this.$axios.post(api.GET_AUTHORITY_LIST.url, params).then(res => {
if (res.data.code === 0) {
this.authorityList = res.data.data.list;
} else {
this.$message.error(res.data.msg);
}
})
},
//
openAdd() {
this.addForm.resetFields();
this.addVisible = true;
},
addAuthorityChange(value) {
this.addAuthorityIds = [value];
},
submitAdd(e) {
e.preventDefault();
this.addForm.validateFields((err, values) => {
values.authorityIds = this.addAuthorityIds;
if (!err) {
this.$axios.post(api.ADD_USER.url, values).then(res => {
if (res.data.code === 0) {
this.$message.success('新增成功!');
this.addForm.resetFields();
this.addVisible = false;
this.findUserList();
} else {
this.$message.error(res.data.msg);
}
})
}
});
},
//
openEdit(data) {
this.editForm.resetFields();
this.editVisible = true;
this.editData = data;
this.oldPassword = data.password;
this.$nextTick(() => {
this.editForm.setFieldsValue({
ID: data.ID,
username: data.userName,
nickname: data.nickName,
password: data.password,
authorityId: data.authorityId
});
});
this.editAuthorityIds = [data.authorityId];
},
editAuthorityChange(value) {
this.editAuthorityIds = [value];
},
submitEdit(e) {
e.preventDefault();
this.editForm.validateFields((err, values) => {
values.ID = this.editData.ID;
values.authorityIds = this.editAuthorityIds;
if (this.oldPassword === values.password) {
values.password = null;
}
if (!err) {
this.$axios.put(api.EDIT_USER.url, values).then(res => {
if (res.data.code === 0) {
this.$message.success('编辑成功!');
this.editForm.resetFields();
this.editVisible = false;
this.findUserList();
} else {
this.$message.error(res.data.msg);
}
})
}
});
},
//
openDelete(data) {
this.$confirm({
title: '提示信息:',
content: '确定删除数据?',
onOk: () => this.submitDelete(data)
});
},
submitDelete(data) {
this.$axios.delete(api.DELETE_USER.url, {data: {ID: data.ID}}).then(res => {
if (res.data.code === 0) {
this.$message.success('删除成功!');
} else {
this.$message.error(res.data.msg);
}
this.findUserList();
})
},
}
}
</script>
<style xml:lang="less">
.input_width_use {
width: 320px;
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<div>
<a-form :form="form" class="edit_password">
<a-form-item label="当前密码">
<a-input v-decorator="['oldPassword',{rules: [{required: true,message: '',}],},]"
type="password"
required="true"
placeholder="请输入当前密码" class="input_width_pass">
</a-input>
</a-form-item>
<a-form-item label="新密码">
<a-input v-decorator="['password1',{rules: [{required: true,message: '',},{validator: validateToNextPassword}]}]"
type="password"
placeholder="请输入新密码" class="input_width_pass">
</a-input>
</a-form-item>
<a-form-item label="确认密码">
<a-input v-decorator="['password2',{rules: [{required: true,message: '',},{validator: compareToFirstPassword}]}]"
type="password"
placeholder="请输入新密码" class="input_width_pass">
</a-input>
</a-form-item>
<a-form-item label="">
<a-button type="primary" @click="submitPassword" style="margin-left: 210px">
确定
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script>
import api from '../../utils/api'
export default {
name: "UserPassword",
data() {
return {
//
editVisible: false,
form: this.$form.createForm(this, { name: 'editPassword' }),
confirmDirty: false,
}
},
methods: {
compareToFirstPassword(rule, value, callback) {
const form = this.form;
if (value && value !== form.getFieldValue('password1')) {
callback('两次密码不一致');
} else {
callback();
}
},
validateToNextPassword(rule, value, callback) {
const form = this.form;
if (value && this.confirmDirty) {
form.validateFields(['password2'], { force: true });
}
callback();
},
//
submitPassword(e) {
//
e.preventDefault();
this.form.validateFields((err, values) => {
const loginData = {};
loginData.username = sessionStorage.getItem('userName');
loginData.password = values.oldPassword;
loginData.newPassword = values.password1;
if (!err) {
this.$axios.post(api.CHANGE_PASSWORD.url, loginData).then(res => {
if (res.data.code === 0) {
this.form.resetFields();
this.$message.success(res.data.msg);
} else {
this.$message.error(res.data.msg);
}
})
}
});
},
}
}
</script>
<style xml:lang="less">
.edit_password > .ant-form-item {
display: flex;
}
.edit_password > .ant-form-item > .ant-form-item-label {
width: 100px;
}
.input_width_pass {
width: 320px;
}
</style>

55
web/src/main.js Normal file
View File

@ -0,0 +1,55 @@
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
import Antd from 'ant-design-vue'
// 原ant-design css
import 'ant-design-vue/dist/antd.css'
// 自定义全局主题样式
import './theam/ant-design-vue.js'
import cryptos from './utils/crypto'
import global_variable from "./utils/global_variable";
// 路由
import router from './router'
// vuex
import store from './store/index.js'
import Vuex from 'vuex'
Vue.config.productionTip = false;
const instance = axios.create({
baseURL: '/week'
});
// 配置过滤response
instance.interceptors.response.use((response) => {
if (response) {
if (response.data.code === 'FALSE') {
return 'FALSE';
}
return response;
}
}, error => {
return Promise.reject(error);
});
// 跨域请求时是否需要使用凭证
instance.defaults.withCredentials = true;
instance.defaults.headers.post['Content-Type'] = 'application/json';
instance.defaults.headers.get['Content-Type'] = 'application/json';
// 配置请求头
instance.defaults.headers.common['x-token'] = sessionStorage.getItem('login');
instance.defaults.headers.common['x-user-id'] = sessionStorage.getItem('userId');
Vue.prototype.$axios = instance;
Vue.use(Antd);
Vue.prototype.cryptos = cryptos;
Vue.prototype.BASEURL = global_variable.baseURL;
Vue.use(Vuex);
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');

105
web/src/router/index.js Normal file
View File

@ -0,0 +1,105 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router);
const router = new Router(
{
mode: 'history',
routes: [
{
path: '/',
redirect: '/mainComponent',
meta: { title: '第一个展示的页面' }
},
{
path: '/main',
component: () => import('../components/navigation/Navigation.vue'),
meta: { title: '侧边栏仪表板' },
children: [
{
path: '/mainComponent',
component: () => import('../components/main/MainComponent.vue'),
meta: { title: '主页面' }
},
{
path: '/viewWeeklyReport',
component: () => import('../components/main/MainComponent.vue'),
meta: { title: '查看周报' }
},
{
path: '/writeWeeklyReport',
component: () => import('../components/main/WriteWeeklyReport.vue'),
meta: { title: '写周报' }
},
{
path: '/editWeeklyReport',
component: () => import('../components/main/InfoComponent.vue'),
meta: { title: '编辑周报' }
},
{
path: '/statisticalExport',
component: () => import('../components/main/StatisticalExport.vue'),
meta: { title: '统计导出' }
},
{
path: '/templateEditing',
component: () => import('../components/main/TemplateEditing.vue'),
meta: { title: '模板编辑' }
},
{
path: '/userPassword',
component: () => import('../components/setUp/UserPassword.vue'),
meta: { title: '用户密码' }
},
{
path: '/statisticalRules',
component: () => import('../components/setUp/StatisticalRules.vue'),
meta: { title: '统计规则' }
},
{
path: '/userManagement',
component: () => import('../components/setUp/UserManagement.vue'),
meta: { title: '用户管理' }
},
{
path: '/test',
component: () => import('../components/test/TestComponent.vue'),
meta: { title: '测试页面' }
},
]
},
{
path: '/login',
component: () => import('../components/login/LoginComponent.vue'),
meta: { title: '登录页' }
},
{
path: '*',
redirect: '/404'
}
]
}
);
router.beforeEach((to, from, next) => {
// 获取session中存储的用户登录数据
let isLogin = sessionStorage.getItem('login');
if (isLogin) {
next();
} else {
if (to.path === '/login') {
next();
} else {
next('/login');
}
}
});
// 解决重复路由报红问题
const originalPush = Router.prototype.push;
Router.prototype.push = function push (location) {
return originalPush.call(this, location).catch(err => err)
};
export default router

18
web/src/store/index.js Normal file
View File

@ -0,0 +1,18 @@
import Vue from 'vue'
import Vuex from 'vuex'
import pageRouterData from './modules/pageRouterData'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
pageRouterData,
},
state: {},
mutations: {},
// vuex持久化插件
plugins: [createPersistedState(
{storage: window.sessionStorage}
)]
})

View File

@ -0,0 +1,17 @@
export default({
namespaced: true,
state: {
location: '',
defaultSelectedKeys: [2],
},
getters: {},
mutations: {
setLocation (state, data) {
state.location = data;
},
setDefaultSelectedKeys (state, data) {
state.defaultSelectedKeys = data;
},
},
actions: {}
})

View File

@ -0,0 +1,4 @@
import Vue from 'vue'
import Antd from 'ant-design-vue'
import './antd-variables.less'
Vue.use(Antd);

View File

@ -0,0 +1,45 @@
/*
Write your variables here. All available variables can be
found in https://github.com/vueComponent/ant-design-vue/blob/master/components/style/themes/default.less.
*/
@import '~ant-design-vue/dist/antd.less';
// Here are the variables to cover, such as:
@primary-color: #6495ED;
.ant-table table {
text-align: center;
}
.ant-table-body > table > thead > tr > th {
text-align: center;
}
::-webkit-scrollbar {
width: 8px !important;
height: 12px !important;
}
::-webkit-scrollbar-thumb {
border-radius: 5px !important;
-webkit-box-shadow: inset 0 0 5px rgba(121, 185, 255, 0.4) !important;
background: rgba(121, 185, 255, 0.4) !important;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px #F0F0F0 !important;
border-radius: 0 !important;
background: #F0F0F0 !important;
}
.ant-input[disabled] {
color: rgba(0, 0, 0, 0.5);
}
.ant-select-disabled {
color: rgba(0, 0, 0, 0.5);
}
.ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
background-color: rgba(121, 185, 255, 0.4) !important;
}

134
web/src/utils/api.js Normal file
View File

@ -0,0 +1,134 @@
const GET_CAPTCHA = {
url: '/base/captcha',
deacriptions: '获取验证码'
};
const LOGIN = {
url: '/base/login',
deacriptions: '登录'
};
const GET_MENU = {
url: '/menu/getMenu',
deacriptions: '获取动态路由'
};
const CHANGE_PASSWORD = {
url: '/user/changePassword',
deacriptions: '重置密码'
};
const GET_USER_LIST = {
url: '/user/getUserList',
deacriptions: '分页获取用户列表'
};
const GET_AUTHORITY_LIST = {
url: '/authority/getAuthorityList',
deacriptions: '获取角色列表'
};
const DELETE_USER = {
url: '/user/deleteUser',
deacriptions: '删除用户'
};
const ADD_USER = {
url: '/user/register',
deacriptions: '新增用户'
};
const EDIT_USER = {
url: '/user/setUserInfo',
deacriptions: '编辑用户'
};
const UPLOAD_FILE = {
url: '/fileUploadAndDownload/upload',
deacriptions: '文件上传'
};
const DELETE_FILE = {
url: '/fileUploadAndDownload/deleteFile',
deacriptions: '文件删除'
};
const DOWNLOAD_FILE = {
url: '/fileUploadAndDownload/download',
deacriptions: '文件下载'
};
const GET_TEMPLATE_LIST = {
url: '/wtTemplates/getWtTemplateList',
deacriptions: '获取模板'
};
const ADD_TEMPLATE = {
url: '/wtTemplates/createWtTemplate',
deacriptions: '新建模板'
};
const EDIT_TEMPLATE = {
url: '/wtTemplates/updateWtTemplate',
deacriptions: '编辑模板'
};
const ADD_REPORT = {
url: '/wtReports/createWtReports',
deacriptions: '创建周报'
};
const FIND_REPORT_LIST = {
url: '/wtReports/getWtReportsList',
deacriptions: '分页查询周报'
};
const EDIT_REPORT = {
url: '/wtReports/updateWtReports',
deacriptions: '更新周报'
};
const FIND_REPORT_BY_ID = {
url: '/wtReports/findWtReports',
deacriptions: '根据id查询周报'
};
const FIND_COMMENT_LIST = {
url: '/wtComment/getWtCommentList',
deacriptions: '获取周报评论'
};
const ADD_COMMENT = {
url: '/wtComment/createWtComment',
deacriptions: '创建周报评论'
};
const ADD_RULE = {
url: '/wtRule/createWtRule',
deacriptions: '创建规则'
};
const UPDATE_RULE = {
url: '/wtRule/updateWtRule',
deacriptions: '编辑规则'
};
const FIND_RULE_LIST = {
url: '/wtRule/getWtRuleList',
deacriptions: ' 查询规则'
};
const FIND_RESULT = {
url: '/wtOutput/GetStatResult',
deacriptions: ' 查询统计结果'
};
const EXPORT_FILE = {
url: '/wtOutput/ExportReportToExcel',
deacriptions: ' 导出报表'
};
//一定要注册才可以使用
export default {
GET_CAPTCHA: GET_CAPTCHA,
LOGIN: LOGIN,
GET_MENU: GET_MENU,
CHANGE_PASSWORD: CHANGE_PASSWORD,
GET_USER_LIST: GET_USER_LIST,
GET_AUTHORITY_LIST: GET_AUTHORITY_LIST,
DELETE_USER: DELETE_USER,
ADD_USER: ADD_USER,
EDIT_USER: EDIT_USER,
UPLOAD_FILE: UPLOAD_FILE,
DELETE_FILE: DELETE_FILE,
DOWNLOAD_FILE: DOWNLOAD_FILE,
GET_TEMPLATE_LIST: GET_TEMPLATE_LIST,
ADD_TEMPLATE: ADD_TEMPLATE,
EDIT_TEMPLATE: EDIT_TEMPLATE,
ADD_REPORT: ADD_REPORT,
FIND_REPORT_LIST: FIND_REPORT_LIST,
EDIT_REPORT: EDIT_REPORT,
FIND_REPORT_BY_ID: FIND_REPORT_BY_ID,
FIND_COMMENT_LIST: FIND_COMMENT_LIST,
ADD_COMMENT: ADD_COMMENT,
ADD_RULE: ADD_RULE,
UPDATE_RULE: UPDATE_RULE,
FIND_RULE_LIST: FIND_RULE_LIST,
FIND_RESULT: FIND_RESULT,
EXPORT_FILE: EXPORT_FILE,
}

27
web/src/utils/crypto.js Normal file
View File

@ -0,0 +1,27 @@
import Crypto from 'crypto-js'
// 加密
export default {
// 设置key和偏移量
key: Crypto.enc.Latin1.parse('key'),
iv: Crypto.enc.Latin1.parse('iv'),
// 加密
encrypto (password) {
const res = Crypto.AES.encrypt(password, this.key, {
iv: this.iv,
mode: Crypto.mode.CBC,
padding: Crypto.pad.ZeroPadding
}).toString();
return res;
},
// 解密
decrypto (password) {
const res = Crypto.AES.decrypt(password, this.key, {
iv: this.iv,
mode: Crypto.mode.CBC,
padding: Crypto.pad.Pkcs7
})
return Crypto.enc.Utf8.stringify(res)
}
}

View File

@ -0,0 +1,6 @@
// 配置全局变量
const baseURL = 'http://10.25.16.212:8980/week/';
export default {
baseURL,
}

24
web/vue.config.js Normal file
View File

@ -0,0 +1,24 @@
module.exports = {
outputDir: 'dist', //build输出目录
assetsDir: 'assets', //静态资源目录js, css, img
lintOnSave: false, //是否开启eslint
devServer: {
// host: "10.25.16.212",
host: "10.25.17.18",
port: '8080',
https: false,
hotOnly: false,
proxy: {
'/week': {
target: 'http://10.25.16.212:8981/',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/week': ''
}
}
},
},
runtimeCompiler: true,
};