pull/1/head
parent
5f5d3d4f22
commit
672b660b69
@ -0,0 +1 @@
|
||||
registry=https://registry.npmmirror.com
|
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
// presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "prosonal-health-web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^1.0.2",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.6.5",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^4.8.0",
|
||||
"element-china-area-data": "^6.1.0",
|
||||
"element-ui": "^2.15.14",
|
||||
"fingerprintjs2": "^2.1.4",
|
||||
"js-md5": "^0.8.3",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"sm4util": "^1.0.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-sweetalert2": "^5.0.10",
|
||||
"wangeditor": "^4.7.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"prettier": "^1.19.1",
|
||||
"sass": "^1.32.0",
|
||||
"sass-loader": "^10.2.0",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended",
|
||||
"@vue/prettier"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<!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="/logo.png">
|
||||
<title>图书管理系统 - 借书 | 还书 | 订阅书 </title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</html>
|
After Width: | Height: | Size: 6.9 KiB |
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#app{
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,23 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon-r-edit:before {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
.icon-r-no:before {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
.icon-r-yes:before {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
.icon-r-delete:before {
|
||||
content: "" !important;
|
||||
}
|
||||
|
||||
.icon-r-add:before {
|
||||
content: "" !important;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// 编辑按钮
|
||||
.edit-button{
|
||||
background-color: rgb(56, 183, 129);
|
||||
font-size: 12px;
|
||||
padding: 2px 15px;
|
||||
border-radius: 3px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.edit-button:hover{
|
||||
background-color: rgb(25, 136, 88);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
.channel-button{
|
||||
background-color: rgb(245, 245, 245);
|
||||
font-size: 12px;
|
||||
padding: 2px 15px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
color: #1c1c1c;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.channel-button:hover{
|
||||
color: #304a99;
|
||||
}
|
||||
|
||||
// 文字按钮
|
||||
.text-button{
|
||||
margin-right: 10px;
|
||||
color: rgb(43, 121, 203);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// 删除按钮
|
||||
.delete-button{
|
||||
background-color: rgb(241, 241, 241);
|
||||
font-size: 12px;
|
||||
padding: 2px 15px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
color: #1c1c1c;
|
||||
}
|
||||
|
||||
.delete-button:hover{
|
||||
background-color: rgb(167, 83, 90);
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// 标签
|
||||
.theme-tag {
|
||||
color: #1c1c1c;
|
||||
background-color: rgb(237, 235, 235);
|
||||
padding: 3px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
// 弹窗标题
|
||||
.dialog-title {
|
||||
font-size: 16px;
|
||||
padding: 15px 20px;
|
||||
user-select: none;
|
||||
color: rgb(107, 106, 106);
|
||||
}
|
||||
|
||||
.dialog-avatar {
|
||||
width: 98px;
|
||||
height: 98px;
|
||||
}
|
||||
|
||||
.dialog-input {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
border: 5px;
|
||||
padding: 4px 5px;
|
||||
border: none;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
}
|
||||
.dialog-hover{
|
||||
display: inline-block;
|
||||
padding: 10px 6px;
|
||||
font-size: 12px;
|
||||
color: rgb(107, 106, 106);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
// 富文本下的样式显示
|
||||
.html-content {
|
||||
|
||||
/* table 样式 */
|
||||
table {
|
||||
border-top: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
table td,
|
||||
table th {
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
table th {
|
||||
border-bottom: 2px solid #ccc;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* blockquote 样式 */
|
||||
blockquote {
|
||||
display: block;
|
||||
border-left: 8px solid #d0e5f2;
|
||||
padding: 5px 10px;
|
||||
margin: 10px 0;
|
||||
line-height: 1.4;
|
||||
font-size: 100%;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* code 样式 */
|
||||
code {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
background-color: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
padding: 3px 5px;
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ul ol 样式 */
|
||||
ul,
|
||||
ol {
|
||||
margin: 10px 0 10px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.w-e-textarea-video-container {
|
||||
background-image: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
// 视频
|
||||
video {
|
||||
width: 100% !important;
|
||||
height: 300px !important;
|
||||
}
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
// ElementUI 自定义覆盖样式
|
||||
|
||||
.dialog-footer {
|
||||
/* 使按钮水平居中 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-slider__bar {
|
||||
height: 16px;
|
||||
background-color: #c1cc57;
|
||||
}
|
||||
|
||||
.el-slider__button-wrapper {
|
||||
height: 66px;
|
||||
width: 66px;
|
||||
top: -25px;
|
||||
}
|
||||
|
||||
|
||||
.el-switch.is-checked .el-switch__core{
|
||||
border-color: rgb(43, 121, 203) !important;
|
||||
background-color: rgb(43, 121, 203) !important;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 标签栏 */
|
||||
.el-tabs__active-bar {
|
||||
height: 2px !important;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* 标签栏 */
|
||||
.el-tabs__item {
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 88px;
|
||||
height: 88px !important;
|
||||
line-height: 88px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-timeline {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-timeline-item__tail {
|
||||
border-left: 2px solid #155599 !important;
|
||||
}
|
||||
|
||||
.el-timeline-item__node {
|
||||
background-color: #155599 !important;
|
||||
}
|
||||
|
||||
/* 表格表头 */
|
||||
.el-table thead tr th {
|
||||
color: #656464 !important;
|
||||
font-weight: 400;
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
||||
//background-color: rgb(246, 246, 246) !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-badge__content.is-fixed {
|
||||
top: 4px !important;
|
||||
right: 6px !important;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* 多选框偏移 */
|
||||
.el-table th>.cell {
|
||||
padding-left: 15px !important;
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
||||
}
|
||||
|
||||
/* 表格单元格 */
|
||||
.el-table__row .cell {
|
||||
font-size: 14px !important;
|
||||
color: #1c1c1c;
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
|
||||
/* 表格多选框颜色 */
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner,
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||
background-color: #a7535a !important;
|
||||
border-color: #a7535a !important;
|
||||
}
|
||||
|
||||
/* 分页显示色 */
|
||||
.el-pager li.active {
|
||||
color: #565555 !important;
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgb(240, 241, 242);
|
||||
background-color: rgb(240, 241, 242);
|
||||
}
|
||||
|
||||
.el-radio-button__orig-radio:checked+.el-radio-button__inner {
|
||||
color: #ffffff !important;
|
||||
background-color: #64d134 !important;
|
||||
border-color: #64d134 !important;
|
||||
box-shadow: -1px 0 0 0 #64d134 !important;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.w-e-text-container [data-slate-editor] pre>code {
|
||||
text-shadow: none !important;
|
||||
background-color: #2d2d2d !important;
|
||||
color: aliceblue !important;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
/* 代码高亮插入后,会出现 “=” 背景色,这里去掉 */
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
#w-e-text-12 span {
|
||||
background-color: #268611 !important;
|
||||
}
|
||||
|
||||
/* 气泡确认框 */
|
||||
.el-popconfirm__main {
|
||||
padding-bottom: 21px !important;
|
||||
}
|
||||
|
||||
|
||||
/* 标签 */
|
||||
.el-tag+.el-tag {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
|
||||
.el-dropdown-menu {
|
||||
padding: 20px !important;
|
||||
user-select: none !important;
|
||||
color: #1c1c1c;
|
||||
|
||||
.el-dropdown-item:hover {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
// 输入框
|
||||
.input-title {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 5px 10px;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.input-child {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 5px 10px;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div>
|
||||
<Toolbar style="border-bottom: 1px solid #eae8e8;" :editor="editor" :defaultConfig="toolbarConfig"
|
||||
:mode="mode" />
|
||||
<Editor :style="{ height: height, overflowY: 'hidden' }" v-model="content" :defaultConfig="editorConfig"
|
||||
:mode="mode" @onCreated="onCreated" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
export default Vue.extend({
|
||||
components: { Editor, Toolbar },
|
||||
props: {
|
||||
receiveContent: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: 'calc(100vh - 100px)'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
content: '<p>创作内容</p>',
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
placeholder: '请输入题目...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
server: '/api/book-manage-sys-api/v1.0/file/upload',
|
||||
fieldName: 'file',
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxNumberOfFiles: 10,
|
||||
// allowedFileTypes: ['image/*'],
|
||||
metaWithUrl: false,
|
||||
withCredentials: true,
|
||||
timeout: 10 * 1000,
|
||||
headers: {
|
||||
'token': sessionStorage.getItem('token')
|
||||
},
|
||||
customInsert(res, insertFn) {
|
||||
insertFn(res.data, res.data, res.data);
|
||||
},
|
||||
},
|
||||
uploadVideo: {
|
||||
server: '/api/book-manage-sys-api/v1.0/file/upload',
|
||||
fieldName: 'file',
|
||||
maxFileSize: 100 * 1024 * 1024,
|
||||
headers: {
|
||||
'token': sessionStorage.getItem('token')
|
||||
},
|
||||
timeout: 60 * 1000,
|
||||
customInsert(res, insertFn) {
|
||||
insertFn(res.data, res.data);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
mode: 'default',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCreated(editor) {
|
||||
this.editor = Object.seal(editor);
|
||||
this.toolbarConfig.excludeKeys = ['group-video','insertLink','fullScreen','emotion','insertTable'];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
receiveContent: {
|
||||
handler(v1, v2) {
|
||||
this.content = v1;
|
||||
},
|
||||
deep: true, // 启用深度监听
|
||||
immediate: true
|
||||
},
|
||||
content(newVal, oldVal) {
|
||||
this.$emit('on-receive', newVal);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
const editor = this.editor;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style src="@wangeditor/editor/dist/css/style.css"></style>
|
||||
<style scoped>
|
||||
.line-number {
|
||||
display: block;
|
||||
margin-right: 10px;
|
||||
/* 以下样式确保行号不被选中或复制 */
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
color: #999;
|
||||
/* 行号颜色,可自定义 */
|
||||
text-align: right;
|
||||
/* 行号对齐方式 */
|
||||
}
|
||||
</style>
|
@ -0,0 +1,615 @@
|
||||
<template>
|
||||
<div style="width: 100%;">
|
||||
<el-row style="margin: 5px 0;">
|
||||
<h2 class="commentHeader">评论 {{ evaluationsCount }}</h2>
|
||||
<el-row style="margin: 15px 0;">
|
||||
<el-col :span="2">
|
||||
<el-avatar :src="userData.userAvatar"></el-avatar>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<div class="parent-comment"
|
||||
:style="{ backgroundColor: bgColor, height: isFocused ? '120px' : '70px', borderColor: isFocused ? '#007bff' : 'transparent' }">
|
||||
<textarea class="comment-parent-input" v-model="content" placeholder="请友好交流" @focus="onFocus"
|
||||
@blur="onBlur"></textarea>
|
||||
<div>
|
||||
<span class="comment-input-number">{{ content.length }} / 300</span>
|
||||
<el-button
|
||||
:style="{ backgroundColor: isFocused ? '#007bff' : '#666', borderColor: isFocused ? '#007bff' : '#666' }"
|
||||
@click="commentClick" class="comment-clike" size="mini" type="primary">评论</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-row style="margin: 5px 0px;">
|
||||
<el-row v-for="(comment, index) in commentList " :key="index" style="padding: 10px 0;">
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
<el-avatar size="large" :src="comment.userAvatar"></el-avatar>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<span style="height: 40px;line-height: 40px;font-size: 16px;color: #515767;">{{
|
||||
comment.userName }}</span>
|
||||
<span v-if="comment.userId == userId" class="my-body-tag">我自己</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="padding: 8px 0;">
|
||||
<el-col :span="22" :offset="2">
|
||||
<span style="font-size: 16px;color: #252933;">{{ comment.content }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="padding: 8px 0;">
|
||||
<el-col :span="22" :offset="2">
|
||||
<span style="font-size: 14px;color: #8A919F;">{{ comment.time }}</span>
|
||||
<el-popconfirm confirm-button-text='好的' cancel-button-text='不删了' icon="el-icon-info"
|
||||
icon-color="red" title="删除该条评论?" v-if="comment.userId == userId"
|
||||
@confirm="deleteComment(comment)">
|
||||
<span slot="reference"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</span>
|
||||
</el-popconfirm>
|
||||
<span @click="toggleReplyInput(comment)"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-chat-dot-round"></i>
|
||||
回复<span v-if="comment.childTotal != 0">({{ comment.childTotal }})</span>
|
||||
</span>
|
||||
<span @click="upvote(comment)"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-discount" v-if="!comment.upvoteFlag">点赞</i>
|
||||
<i class="el-icon-discount" v-else style="color: #1E80FF;"> {{ comment.upvoteCount
|
||||
}}</i>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 父级评论的回复按钮和输入框 -->
|
||||
<el-row v-if="comment.showReplyInput" style="padding: 10px 0;">
|
||||
<el-col :span="22" :offset="2">
|
||||
<div class="parent-comment"
|
||||
:style="{ backgroundColor: bgColor, height: '110px', borderColor: '#007bff' }">
|
||||
<textarea class="comment-parent-input" v-model="replyContent"
|
||||
:placeholder="replyText"></textarea>
|
||||
<div>
|
||||
<span class="comment-input-number">{{ replyContent.length }} / 300</span>
|
||||
<el-button style="background-color: #007bff;user-select: none;"
|
||||
@click="submitReply(comment)" class="comment-clike" size="mini"
|
||||
type="primary">评论</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 子级评论 -->
|
||||
<el-row v-for="(commentChild, index) in comment.commentChildVOS " :key="index"
|
||||
style="padding: 10px 15px;font-size: 16px;">
|
||||
<el-row>
|
||||
<el-col :span="22" :offset="2">
|
||||
<el-row style="display: flex; align-items: center; flex-wrap: wrap;">
|
||||
<el-avatar size="small" :src="commentChild.userAvatar"
|
||||
style="margin-right: 5px;"></el-avatar>
|
||||
<span style="color: #515767; padding: 0 5px;">{{ commentChild.userName }}</span>
|
||||
<span v-if="commentChild.userId == userId" class="my-body-tag">我自己</span>
|
||||
<span v-if="commentChild.replierName != null"
|
||||
style="margin:0 15px;color: #1c1c1c;user-select: none;font-size: 12px;">
|
||||
回复
|
||||
</span>
|
||||
<el-avatar v-if="commentChild.replierName != null" size="small"
|
||||
:src="commentChild.replierAvatar" style="margin-right: 5px;"></el-avatar>
|
||||
<span v-if="commentChild.replierName != null" style="color: #515767;padding: 0 5px;">{{
|
||||
commentChild.replierName }}</span>
|
||||
<span v-if="commentChild.replierId == userId" class="my-body-tag">我自己</span>
|
||||
<span
|
||||
style="letter-spacing: 1px;font-size: 16px; color: #252933; white-space: normal; margin-left: 5px;padding: 6px 0;">
|
||||
: {{ commentChild.content }}
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row style="padding: 10px 0;">
|
||||
<span style="font-size: 14px;color: #8A919F;">{{ commentChild.time }}</span>
|
||||
<el-popconfirm confirm-button-text='好的' cancel-button-text='不删了' icon="el-icon-info"
|
||||
icon-color="red" title="删除该条评论?" v-if="commentChild.userId == userId"
|
||||
@confirm="deleteComment(commentChild)">
|
||||
<span slot="reference"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</span>
|
||||
</el-popconfirm>
|
||||
<span @click="toggleReplyInput1(commentChild)"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-chat-dot-round"></i>
|
||||
回复
|
||||
</span>
|
||||
<span @click="upvote(commentChild)"
|
||||
style="cursor: pointer;margin-left: 15px;font-size: 14px;color: #8A919F;user-select: none;">
|
||||
<i class="el-icon-discount" v-if="!commentChild.upvoteFlag">点赞</i>
|
||||
<i class="el-icon-discount" v-else style="color: #1E80FF;"> {{
|
||||
commentChild.upvoteCount }}</i>
|
||||
</span>
|
||||
</el-row>
|
||||
<!-- 子级评论的回复按钮和输入框 -->
|
||||
<el-row v-if="commentChild.replyInputStatus" style="padding: 10px 0;">
|
||||
<el-col :span="24">
|
||||
<div class="parent-comment"
|
||||
:style="{ backgroundColor: bgColor, height: '110px', borderColor: '#007bff' }">
|
||||
<textarea class="comment-parent-input" v-model="replyChildContent"
|
||||
:placeholder="replyText"></textarea>
|
||||
<div>
|
||||
<span class="comment-input-number">{{ replyChildContent.length }} /
|
||||
300</span>
|
||||
<el-button style="background-color: #007bff;"
|
||||
@click="submitReply1(commentChild)" class="comment-clike" size="mini"
|
||||
type="primary">评论</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<!-- 举报反馈对话框 -->
|
||||
<el-dialog style="user-select: none;border-radius: 5px;" title="我要举报" :visible.sync="dialogVisibleReport"
|
||||
width="30%">
|
||||
<el-row v-for="(item, index) in reports" :key="index" style="margin-top: 10px;">
|
||||
<el-row style="padding-bottom: 10px;user-select: none;">*{{ item.name }}</el-row>
|
||||
<el-row>
|
||||
<span v-for="(itemChild, indexChild) in item.list" :key="indexChild"
|
||||
@click="reportItemClick(itemChild)">
|
||||
<button :style="{ border: itemChild.isSelected ? '1px solid #4b87bc' : '1px solid #f4f4f4' }"
|
||||
class="reportItem">
|
||||
{{ itemChild.name }}
|
||||
</button>
|
||||
</span>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<button class="cannel-btn" @click="dialogVisibleReport = false">取消</button>
|
||||
<button class="yes-btn" @click="operationReport">确定举报</button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { timeAgo } from '@/utils/data'
|
||||
export default {
|
||||
props: {
|
||||
contentId: {
|
||||
type: Number,
|
||||
default: ''
|
||||
},
|
||||
contentType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userData: {},
|
||||
noteData: {},
|
||||
commentContent: '',
|
||||
content: '',
|
||||
commentList: [],
|
||||
replyContent: '',
|
||||
id: null,
|
||||
isFocused: false,
|
||||
bgColor: 'rgb(245 245 245)',
|
||||
strLength: '0/300',
|
||||
replyText: '',
|
||||
userId: '',
|
||||
dialogVisibleReport: false,
|
||||
reports: [],
|
||||
selectdStatus: false,
|
||||
evaluationsCount: 0,
|
||||
comment: {},
|
||||
replyChildContent: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
contentId(newVal,oldVal){
|
||||
if(newVal !== oldVal){
|
||||
this.loadCommentList();
|
||||
}
|
||||
},
|
||||
content() {
|
||||
if (this.content === '') {
|
||||
this.isFocused = false;
|
||||
return;
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getUserInfo();
|
||||
this.loadCommentList();
|
||||
},
|
||||
methods: {
|
||||
getUserInfo() {
|
||||
const userInfo = sessionStorage.getItem("userInfo");
|
||||
this.userData = JSON.parse(userInfo);
|
||||
this.userId = this.userData.id;
|
||||
},
|
||||
// 点赞 或 取消点赞
|
||||
upvote(comment) {
|
||||
let upvoteList = comment.upvoteList ? comment.upvoteList.split(',') : [];
|
||||
if (upvoteList.length) {
|
||||
// 界面反映
|
||||
if (comment.upvoteFlag) {
|
||||
// 取消点赞
|
||||
let index = upvoteList.indexOf(this.userData.id.toString());
|
||||
if (index !== -1) {
|
||||
upvoteList.splice(index, 1); // 移除用户ID
|
||||
}
|
||||
} else {
|
||||
// 点赞
|
||||
if (!upvoteList.includes(this.userData.userId.toString())) {
|
||||
upvoteList.push(this.userData.userId.toString()); // 添加用户ID
|
||||
}
|
||||
}
|
||||
}
|
||||
let evalustions = {
|
||||
id: comment.id,
|
||||
upvoteList: upvoteList.length ? upvoteList.join(',') : this.userData.id
|
||||
}
|
||||
this.$axios.put(`evaluations/update`, evalustions).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
comment.upvoteList = upvoteList.join(','); // 更新upvoteList字符串
|
||||
comment.upvoteFlag = !comment.upvoteFlag; // 切换点赞状态标志
|
||||
comment.upvoteCount += 1;
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`点赞状态设置异常 -> `, err);
|
||||
})
|
||||
},
|
||||
// 确定举报
|
||||
operationReport() {
|
||||
let reportItem = [];
|
||||
this.reports.forEach(entity => {
|
||||
let entityReport = entity.list.filter(child => child.isSelected);
|
||||
if (entityReport.length != 0) {
|
||||
reportItem = entityReport;
|
||||
}
|
||||
});
|
||||
if (!reportItem.length) {
|
||||
this.$message(`请选中举报项`);
|
||||
return;
|
||||
}
|
||||
this.$axios.get(`evaluations-reports/report/${this.comment.id}/${reportItem[0].name}`).then(res => {
|
||||
this.dialogVisibleReport = false;
|
||||
if (res.data.code == 200) {
|
||||
this.$swal.fire({
|
||||
title: '举报操作',
|
||||
text: '举报成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
} else {
|
||||
this.$swal.fire({
|
||||
title: '举报操作',
|
||||
text: res.data.msg,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论举报异常 -> `, err);
|
||||
})
|
||||
},
|
||||
// 选中举报项
|
||||
reportItemClick(itemChild) {
|
||||
this.reports.forEach(entity => {
|
||||
entity.list.forEach(child => {
|
||||
child.isSelected = false;
|
||||
})
|
||||
})
|
||||
itemChild.isSelected = true;
|
||||
},
|
||||
reportList() {
|
||||
this.$axios.get(`evaluations/reportList`).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.reports = [];
|
||||
res.data.data.forEach(entity => {
|
||||
let report = { name: entity.name };
|
||||
let resportList = [];
|
||||
entity.list.forEach(listItem => {
|
||||
let reportChild = {};
|
||||
reportChild.name = listItem;
|
||||
reportChild.isSelected = false;
|
||||
resportList.push(reportChild);
|
||||
})
|
||||
report.list = resportList;
|
||||
this.reports.push(report);
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`举报项加载失败 -> `, err);
|
||||
})
|
||||
},
|
||||
reportComment(comment) {
|
||||
this.reportList();
|
||||
this.dialogVisibleReport = true;
|
||||
this.comment = comment;
|
||||
},
|
||||
deleteComment(comment) { // 删除评论
|
||||
this.$axios.delete(`evaluations/delete/${comment.id}`).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.loadCommentList();
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论异常 -> `, err);
|
||||
})
|
||||
},
|
||||
onFocus() {
|
||||
this.isFocused = true;
|
||||
},
|
||||
// 输入框失去焦点
|
||||
onBlur() {
|
||||
if (this.content === '') {
|
||||
this.isFocused = false;
|
||||
return;
|
||||
}
|
||||
this.isFocused = true;
|
||||
},
|
||||
commentClick() {
|
||||
if (this.content == '') {
|
||||
this.$swal.fire({
|
||||
title: '内容提示',
|
||||
text: '评论内容为空',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 800
|
||||
});
|
||||
return;
|
||||
}
|
||||
const evaluations = {
|
||||
contentType: this.contentType,
|
||||
content: this.content,
|
||||
contentId: this.contentId,
|
||||
}
|
||||
this.$axios.post(`evaluations/insert`, evaluations).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.content = '';
|
||||
this.$swal.fire({
|
||||
title: '评论操作',
|
||||
text: '评论成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.loadCommentList()
|
||||
}, 1100)
|
||||
}else{
|
||||
this.$swal.fire({
|
||||
title: '评论异常',
|
||||
text: res.data.msg,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论异常 -> `, err);
|
||||
})
|
||||
},
|
||||
// 父级评论回复点击
|
||||
toggleReplyInput(comment) {
|
||||
this.replyText = `回复${comment.userName}...`;
|
||||
if (comment.showReplyInput == null) {
|
||||
comment.showReplyInput = false;
|
||||
}
|
||||
comment.showReplyInput = !comment.showReplyInput;
|
||||
},
|
||||
// 子级评论回复点击
|
||||
toggleReplyInput1(comment) {
|
||||
if (comment.replyInputStatus == null) {
|
||||
comment.replyInputStatus = false;
|
||||
}
|
||||
comment.replyInputStatus = !comment.replyInputStatus;
|
||||
},
|
||||
// 父级评论回复提交
|
||||
submitReply(comment) {
|
||||
if (this.replyContent == '') {
|
||||
this.$message(`评论内容不能为空`);
|
||||
return;
|
||||
}
|
||||
const evaluationsDTO = {
|
||||
contentType: this.contentType,
|
||||
content: this.replyContent,
|
||||
contentId: this.contentId,
|
||||
parentId: comment.id
|
||||
}
|
||||
this.$axios.post(`evaluations/insert`, evaluationsDTO).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.replyContent = '';
|
||||
comment.showReplyInput = false;
|
||||
this.$swal.fire({
|
||||
title: '回复操作',
|
||||
text: '回复成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1300
|
||||
});
|
||||
setTimeout(() => {
|
||||
// 重新加载评论列表
|
||||
this.loadCommentList();
|
||||
}, 1300)
|
||||
}else{
|
||||
this.$swal.fire({
|
||||
title: '评论异常',
|
||||
text: res.data.msg,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论异常 -> `, err);
|
||||
})
|
||||
},
|
||||
// 子级评论回复提交
|
||||
submitReply1(comment) {
|
||||
if (this.replyChildContent == '') {
|
||||
this.$message(`评论内容不能为空`);
|
||||
return;
|
||||
}
|
||||
const evaluationsDTO = {
|
||||
replierId: comment.userId,
|
||||
contentType: this.contentType,
|
||||
content: this.replyChildContent,
|
||||
contentId: this.contentId,
|
||||
parentId: comment.parentId
|
||||
}
|
||||
this.$axios.post(`evaluations/insert`, evaluationsDTO).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.content = '';
|
||||
comment.replyInputStatus = false;
|
||||
this.$swal.fire({
|
||||
title: '回复操作',
|
||||
text: '回复成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1300
|
||||
});
|
||||
setTimeout(() => {
|
||||
// 重新加载评论列表
|
||||
this.loadCommentList();
|
||||
}, 1300)
|
||||
}else{
|
||||
this.$swal.fire({
|
||||
title: '评论异常',
|
||||
text: res.data.msg,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 1100
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论异常 -> `, err);
|
||||
})
|
||||
},
|
||||
goBack() {
|
||||
// 返回上一级
|
||||
this.$router.go(-1);
|
||||
},
|
||||
// 加载评论列表
|
||||
loadCommentList() {
|
||||
this.$axios.get(`evaluations/list/${this.contentId}/${this.contentType}`).then(res => {
|
||||
if (res.data.code == 200) {
|
||||
this.commentList = res.data.data.data;
|
||||
this.evaluationsCount = res.data.data.evaluationsCount;
|
||||
// 父级评论
|
||||
this.commentList.forEach(entity => {
|
||||
// 时间转换
|
||||
entity.time = timeAgo(entity.createTime);
|
||||
// 子级评论
|
||||
entity.commentChildVOS.forEach(entity => entity.time = timeAgo(entity.createTime));
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(`评论查询异常异常 -> `, err);
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.cannel-btn,
|
||||
.yes-btn {
|
||||
padding: 0px 15px 5px 15px;
|
||||
font-size: 14px !important;
|
||||
margin: 0 10px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cannel-btn {
|
||||
color: #1c1c1c;
|
||||
}
|
||||
|
||||
.yes-btn {
|
||||
background-color: #4b87bc;
|
||||
color: #EAF2FF;
|
||||
}
|
||||
|
||||
.cannel-btn:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.yes-btn:hover {
|
||||
background-color: #66a8e1;
|
||||
}
|
||||
|
||||
.commentHeader {
|
||||
color: #252933;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.comment-parent-input {
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: rgb(245 245 245);
|
||||
font-size: 16px;
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
min-height: 60px;
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
user-select: none;
|
||||
margin: 0 0 20px 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.parent-comment {
|
||||
padding: 6px 12px;
|
||||
border-radius: 3px;
|
||||
transition: height 0.3s ease, border-color 0.3s ease;
|
||||
border: 1px solid transparent;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.comment-input-number {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 5px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.comment-clike {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.my-body-tag {
|
||||
font-size: 12px;
|
||||
padding: 3px 4px;
|
||||
color: #1E80FF;
|
||||
background-color: #EAF2FF;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.reportItem {
|
||||
display: inline-block;
|
||||
padding: 8px 22px;
|
||||
background-color: #f4f4f4;
|
||||
border: 1px solid #f4f4f4;
|
||||
margin: 5px 3px 5px 0;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.reportItem:hover {
|
||||
border: 1px solid #4b87bc;
|
||||
color: #4b87bc;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="line-main">
|
||||
<div>
|
||||
<span class="tag">{{ tag }}</span>
|
||||
<span class="time-show">
|
||||
<span class="top-bar" style="font-size: 12px;">时间选择</span>
|
||||
<el-select size="mini" style="width: 90px;" v-model="selectedValue" placeholder="期限">
|
||||
<el-option v-for="item in options" :key="item.num" :label="item.name" :value="item.num">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</span>
|
||||
</div>
|
||||
<div ref="chart" :style="{ width: '100%', height: height }"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// 折线图组件
|
||||
import * as echarts from 'echarts';
|
||||
export default {
|
||||
name: 'DialogLine',
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: '折线图'
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '220px'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedValue(v1, v2) {
|
||||
this.$emit('on-selected', v1);
|
||||
},
|
||||
values(v1, v2) {
|
||||
this.initChart();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
options: [{ num: 7, name: '7天内' }, { num: 30, name: '30天内' }, { num: 60, name: '60天内' }],
|
||||
selectedValue: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 图表初始化
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$refs.chart);
|
||||
let option = {
|
||||
grid: {
|
||||
left: 30,
|
||||
right: 10,
|
||||
top: 50,
|
||||
borderWidth: 0,
|
||||
},
|
||||
title: {
|
||||
text: '',
|
||||
color: '#ffffff',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: '{b}{c}',
|
||||
},
|
||||
legend: {
|
||||
data: ['']
|
||||
},
|
||||
xAxis: {
|
||||
data: this.date,
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgb(102, 102, 102)',
|
||||
fontSize: '12'
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgb(102, 102, 102)',
|
||||
fontSize: '12',
|
||||
formatter: '{value}'
|
||||
},
|
||||
},
|
||||
series: [{
|
||||
name: '',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: this.values,
|
||||
areaStyle: {
|
||||
color: 'rgba(144, 191, 237, 0.3)'
|
||||
},
|
||||
lineStyle: {
|
||||
color: '#5B8FF9'
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#5B8FF9',
|
||||
borderColor: '#5B8FF9',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
color: 'rgb(102, 102, 102)',
|
||||
},
|
||||
}]
|
||||
};
|
||||
this.chart.setOption(option);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
this.chart.dispose();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.line-main {
|
||||
margin-bottom: 5px;
|
||||
border-radius: 3px;
|
||||
|
||||
.tag {
|
||||
font-size: 16px;
|
||||
padding: 15px 6px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time-show {
|
||||
padding: 15px 6px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<span class="logo">
|
||||
<el-image style="width: 30px; height: 30px" src="/logo.png" fit="fill"></el-image>
|
||||
<div v-if="!flag">
|
||||
<span :style="{ color: bag, display: 'block' }">{{ name }}</span>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "Logo",
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
props: {
|
||||
name:{
|
||||
type: String,
|
||||
default: '系统'
|
||||
},
|
||||
flag: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
bag: {
|
||||
type: String,
|
||||
default: '#1c1c1c'
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.logo {
|
||||
color: rgb(8, 24, 16) !important;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
margin-left: 8px;
|
||||
color: #666;
|
||||
font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="line-main" :style="{ backgroundColor: bag }">
|
||||
<div>
|
||||
<span class="tag" :style="{ color: fontColor }">
|
||||
{{ tag }}</span>
|
||||
</div>
|
||||
<div ref="chart" :style="{ width: width, height: height }"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
export default {
|
||||
name: 'PieChart',
|
||||
props: {
|
||||
types: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
tag: {
|
||||
type: String,
|
||||
default: '饼状图'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '243px'
|
||||
},
|
||||
bag: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
fontColor:{
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
types(v1, v2) {
|
||||
this.initChart();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart();
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$refs.chart)
|
||||
let option = {
|
||||
title: {
|
||||
text: '',
|
||||
subtext: '',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
show: false,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '',
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false,
|
||||
fontSize: '24',
|
||||
fontWeight: '600'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'outer',
|
||||
formatter: '{d}%'
|
||||
},
|
||||
data: this.values.map((value, index) => ({
|
||||
name: this.types[index],
|
||||
value: value,
|
||||
})),
|
||||
itemStyle: {
|
||||
color: function (params) {
|
||||
const colorList = [
|
||||
'#409EFF',
|
||||
'#67C23A',
|
||||
'#E6A23C',
|
||||
'#F56C6C',
|
||||
'#909399',
|
||||
'#E4E7ED',
|
||||
'#F2F6FC',
|
||||
];
|
||||
return colorList[params.dataIndex % colorList.length];
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
this.chart.setOption(option)
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.chart) {
|
||||
this.chart.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.line-main {
|
||||
padding-top: 10px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 3px;
|
||||
background-color: #000000;
|
||||
|
||||
.tag {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding: 15px 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<el-menu :collapse-transition="false" :collapse="flag" style="padding: 5px 20px;max-width: 253px;"
|
||||
:default-active="activeIndex" :background-color="bag" text-color="#666" @select="handleSelect">
|
||||
<el-menu-item v-for="(item, index) in routes" :key="index" style="width: 100%;"
|
||||
v-if="!item.isHidden" :index="item.path"
|
||||
:class="{ 'is-active': activeIndex === item.path }">
|
||||
<i :class="item.icon" style="font-size: 20px;"></i>
|
||||
<span slot="title" style="font-size: 14px;">{{ item.name }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminMenu',
|
||||
data() {
|
||||
return {
|
||||
activeIndex: "1",
|
||||
isCollapse: true,
|
||||
selectedMenuItem: '',
|
||||
}
|
||||
},
|
||||
props: {
|
||||
routes: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
flag: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
bag: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
}
|
||||
},
|
||||
created(){
|
||||
// 上次选中路径
|
||||
const saveLastPath = sessionStorage.getItem('activeMenuItem');
|
||||
if(saveLastPath === null){
|
||||
// 加载首页
|
||||
this.handleSelect('/adminLayout');
|
||||
}else{
|
||||
this.handleSelect(saveLastPath);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(index) {
|
||||
this.activeIndex = index;
|
||||
this.$emit('select', this.activeIndex);
|
||||
sessionStorage.setItem('activeMenuItem', this.activeIndex);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.is-active {
|
||||
background-color: rgb(235, 237, 245) !important;
|
||||
color: #1c1c1c !important;
|
||||
font-weight: bold;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
height: 45px !important;
|
||||
line-height: 45px !important;
|
||||
user-select: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.el-menu-item:focus,
|
||||
.el-menu-item:hover {
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
background-color: rgb(235, 237, 245) !important;
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
height: 45px !important;
|
||||
line-height: 45px !important;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
</style>
|
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div style="box-sizing: border-box;">
|
||||
<el-tabs v-model="activeName" style="min-height: 500px;">
|
||||
<el-tab-pane label="题目原题" name="first">
|
||||
<Editor height="calc(100vh - 400px)" :receiveContent="operationData.askItem"
|
||||
@on-receive="onReceiveContent" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// 单选题组件
|
||||
import Editor from "@/components/Editor"
|
||||
export default {
|
||||
components: { Editor },
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler(newValue, oldValue) {
|
||||
this.operationData = newValue;
|
||||
this.$emit('on-listenner', newValue);
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operationData: {},
|
||||
askItem: '',
|
||||
questionNumber: 1,
|
||||
answerNumber: 1,
|
||||
activeName: 'first',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onReceiveContent(html) {
|
||||
this.operationData.askItem = html;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.options-item {
|
||||
padding: 25px 0;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.index-item {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
font-size: 16px;
|
||||
color: rgb(113, 114, 114);
|
||||
}
|
||||
|
||||
.check-item {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
padding: 3px;
|
||||
box-sizing: border-box;
|
||||
border: 3px solid rgb(232, 234, 243);
|
||||
margin-right: 10px;
|
||||
transition: .8s;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 5px 10px;
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,30 @@
|
||||
import Vue from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import 'element-ui/lib/theme-chalk/index.css';
|
||||
import { provinceAndCityData, regionData } from 'element-china-area-data';
|
||||
import VueSweetalert2 from 'vue-sweetalert2';
|
||||
import 'sweetalert2/dist/sweetalert2.min.css';
|
||||
import './assets/css/editor.scss'
|
||||
import './assets/css/button.scss'
|
||||
import './assets/css/elementui-cover.scss'
|
||||
import './assets/css/basic.scss'
|
||||
import './assets/css/dialog.scss'
|
||||
import './assets/css/input.scss'
|
||||
import request from '@/utils/request'
|
||||
import md5 from 'js-md5';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(VueSweetalert2);
|
||||
Vue.prototype.$md5 = md5;
|
||||
Vue.prototype.$axios = request;
|
||||
import swalPlugin from '@/utils/swalPlugin';
|
||||
Vue.use(swalPlugin);
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
regionData,
|
||||
provinceAndCityData,
|
||||
VueSweetalert2,
|
||||
render: h => h(App)
|
||||
}).$mount("#app");
|
@ -0,0 +1,84 @@
|
||||
import Vue from "vue";
|
||||
import VueRouter from "vue-router";
|
||||
import ElementUI from 'element-ui';
|
||||
import 'element-ui/lib/theme-chalk/index.css';
|
||||
import { getToken } from "@/utils/storage.js";
|
||||
import echarts from 'echarts';
|
||||
Vue.prototype.$echarts = echarts;
|
||||
Vue.use(ElementUI);
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const routes = [
|
||||
{ path: "/", component: () => import(`@/views/login/Login.vue`) },
|
||||
{ path: "/login", component: () => import(`@/views/login/Login.vue`) },
|
||||
{ path: "/register", component: () => import(`@/views/register/Register.vue`) },
|
||||
{ path: "/createNotice", component: () => import(`@/views/admin/CreateNotice.vue`), meta: { requireAuth: true } },
|
||||
{
|
||||
path: "/admin",
|
||||
component: () => import(`@/views/admin/Home.vue`),
|
||||
meta: { requireAuth: true },
|
||||
children: [
|
||||
{ path: "/adminLayout", name: '数据总览', icon: 'el-icon-data-analysis', component: () => import(`@/views/admin/Main.vue`), meta: { requireAuth: true } },
|
||||
{ path: "/userManage", name: '用户管理', icon: 'el-icon-user-solid', component: () => import(`@/views/admin/UserManage.vue`), meta: { requireAuth: true } },
|
||||
{ path: "/noticeManage", name: '公告管理', icon: 'el-icon-edit-outline', component: () => import(`@/views/admin/NoticeManage.vue`), meta: { requireAuth: true } }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/user",
|
||||
component: () => import(`@/views/user/Home.vue`),
|
||||
meta: { requireAuth: true },
|
||||
children: [
|
||||
{ name: '留言板', path: "/main", icon: 'el-icon-question', component: () => import(`@/views/user/Main.vue`), meta: { requireAuth: true } },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
routes,
|
||||
mode: 'history'
|
||||
});
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 放行登录页和注册页
|
||||
if (to.path === '/login' || to.path === '/register') {
|
||||
return next();
|
||||
}
|
||||
|
||||
// 检查需要认证的路由
|
||||
if (to.matched.some(record => record.meta.requireAuth)) {
|
||||
const token = getToken();
|
||||
|
||||
// 未登录情况处理
|
||||
if (!token) {
|
||||
return next({
|
||||
path: '/login',
|
||||
query: { redirect: to.fullPath } // 记录目标路由
|
||||
});
|
||||
}
|
||||
|
||||
// 已登录时的权限检查
|
||||
try {
|
||||
const role = parseInt(sessionStorage.getItem('role'));
|
||||
|
||||
// 管理员路径检查
|
||||
if (to.matched[0].path === '/admin' && role !== 1) {
|
||||
clearToken();
|
||||
return next("/login"); //返回登录页
|
||||
}
|
||||
|
||||
// 用户路径检查
|
||||
if (to.matched[0].path === '/user' && role !== 2) {
|
||||
clearToken();
|
||||
return next("/login"); //返回登录页
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
console.error('权限检查失败:', error);
|
||||
return next('/login');
|
||||
}
|
||||
}
|
||||
|
||||
// 普通页面直接放行
|
||||
next();
|
||||
});
|
||||
export default router;
|
@ -0,0 +1,19 @@
|
||||
export function timeAgo(dateString) {
|
||||
const now = new Date();
|
||||
const date = new Date(dateString);
|
||||
const secondsPast = (now.getTime() - date.getTime()) / 1000;
|
||||
if (secondsPast < 60) {
|
||||
return `${Math.floor(secondsPast)} 秒前`;
|
||||
} else if (secondsPast < 3600) {
|
||||
return `${Math.floor(secondsPast / 60)} 分钟前`;
|
||||
} else if (secondsPast <= 86400) {
|
||||
return `${Math.floor(secondsPast / 3600)} 小时前`;
|
||||
} else {
|
||||
const daysPast = Math.floor(secondsPast / 86400);
|
||||
if (daysPast === 1) {
|
||||
return '1 天前';
|
||||
} else {
|
||||
return `${daysPast} 天前`;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import axios from "axios"
|
||||
import { getToken } from "@/utils/storage.js";
|
||||
const URL_API = 'http://localhost:21090/api/book-manage-sys-api/v1.0'
|
||||
const request = axios.create({
|
||||
baseURL: URL_API,
|
||||
timeout: 8000
|
||||
});
|
||||
request.interceptors.request.use(config => {
|
||||
const token = getToken();
|
||||
if (token !== null) {
|
||||
config.headers["token"] = token;
|
||||
}
|
||||
return config;
|
||||
}, error => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
export default request;
|
@ -0,0 +1,29 @@
|
||||
const TOKEN_KEY="token"
|
||||
const INFO = "health-info";
|
||||
const ACTIVE_PATH="active_key"
|
||||
export function getToken(){
|
||||
return sessionStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function setToken(token){
|
||||
sessionStorage.setItem(TOKEN_KEY,token);
|
||||
}
|
||||
|
||||
export function getHealthInfo(){
|
||||
return sessionStorage.getItem(INFO);
|
||||
}
|
||||
|
||||
export function setHealthInfo(obj){
|
||||
sessionStorage.setItem(INFO,obj);
|
||||
}
|
||||
|
||||
export function clearToken(){
|
||||
sessionStorage.clear();
|
||||
}
|
||||
export function getActivePath(){
|
||||
return sessionStorage.getItem(ACTIVE_PATH);
|
||||
}
|
||||
|
||||
export function setActivePath(path){
|
||||
sessionStorage.setItem(ACTIVE_PATH, path);
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div style="background-color: rgb(248, 249, 250);">
|
||||
<div style="width: 800px;margin: 0 auto;padding: 30px 0;box-sizing: border-box;min-height: 100vh;">
|
||||
<p style="font-size: 22px;padding: 20px 0;">{{ noticeOperation === 'save' ? '公告新增' : '公告修改' }}</p>
|
||||
<div>
|
||||
<div style="margin: 20px 0;">
|
||||
<input type="text" placeholder="标题" v-model="notice.name">
|
||||
</div>
|
||||
<div>
|
||||
<Editor height="calc(100vh - 500px)" :receiveContent="notice.content" @on-receive="receiveData" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin: 20px 0;text-align: center;">
|
||||
<span class="operation-btn" @click="operation">
|
||||
{{ noticeOperation === 'save' ? '立即新增' : '立即修改' }}
|
||||
<i class="el-icon-right"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Editor from "@/components/Editor"
|
||||
export default {
|
||||
components: { Editor },
|
||||
data() {
|
||||
return {
|
||||
notice: {},
|
||||
saveApi: '/notice/save',
|
||||
updateApi: '/notice/update',
|
||||
noticeOperation: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadOperation();
|
||||
},
|
||||
methods: {
|
||||
operation() {
|
||||
if (this.noticeOperation === 'save') {
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
loadOperation() {
|
||||
const operation = sessionStorage.getItem('noticeOperation');
|
||||
console.log(operation);
|
||||
if (operation === 'update') {
|
||||
const notice = sessionStorage.getItem('noticeInfo');
|
||||
this.notice = JSON.parse(notice);
|
||||
}
|
||||
this.noticeOperation = sessionStorage.getItem('noticeOperation');
|
||||
},
|
||||
receiveData(html) {
|
||||
this.notice.content = html;
|
||||
},
|
||||
update() {
|
||||
this.$axios.put(this.updateApi, this.notice).then(response => {
|
||||
if (response.data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: '公告修改',
|
||||
text: '修改成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
// 返回上一级
|
||||
this.$router.go(-1);
|
||||
}
|
||||
});
|
||||
},
|
||||
save() {
|
||||
this.$axios.post(this.saveApi, this.notice).then(response => {
|
||||
if (response.data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: '公告新增',
|
||||
text: '新增成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
// 返回上一级
|
||||
this.$router.go(-1);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
input {
|
||||
outline: none;
|
||||
border: none;
|
||||
width: 100%;
|
||||
padding: 18px 12px;
|
||||
font-size: 24px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.operation-btn {
|
||||
padding: 14px 40px;
|
||||
background-color: rgb(235, 237, 245);
|
||||
color: rgb(43, 121, 203);
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="menu-container">
|
||||
<div class="menu-side" :class="{ 'menu-side-narrow': flag }">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<Logo name="图书管理" style="padding: 0 40px;margin: 15px 0;" :flag="flag" :bag="colorLogo" />
|
||||
</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<AdminMenu :flag="flag" :routes="adminRoutes" :bag="bagMenu" @select="handleRouteSelect" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="header-section">
|
||||
<LevelHeader @eventListener="eventListener" @selectOperation="selectOperation" :tag="tag"
|
||||
:userInfo="userInfo" />
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 个人中心 -->
|
||||
<el-dialog :show-close="false" :visible.sync="dialogOperaion" width="26%">
|
||||
<div slot="title" style="padding: 25px 0 0 20px;">
|
||||
<span style="font-size: 18px;font-weight: 800;">个人中心</span>
|
||||
</div>
|
||||
<el-row style="padding: 10px 20px 20px 20px;">
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;margin-bottom: 10px;">
|
||||
<span class="modelName">*头像</span>
|
||||
</p>
|
||||
<el-upload class="avatar-uploader" action="/api/book-manage-sys-api/v1.0/file/upload"
|
||||
:show-file-list="false" :on-success="handleAvatarSuccess">
|
||||
<img v-if="userInfo.url" :src="userInfo.url" style="width: 80px;height: 80px;">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;">
|
||||
<span class="modelName">*用户名</span>
|
||||
</p>
|
||||
<input class="input-title" v-model="userInfo.name" placeholder="用户名">
|
||||
</el-row>
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;">
|
||||
<span class="modelName">*用户邮箱</span>
|
||||
</p>
|
||||
<input class="input-title" v-model="userInfo.email" placeholder="用户邮箱">
|
||||
</el-row>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button class="customer" size="small" style="background-color: rgb(241, 241, 241);border: none;"
|
||||
@click="dialogOperaion = false">取 消</el-button>
|
||||
<el-button size="small" style="background-color: #15559a;border: none;" class="customer" type="info"
|
||||
@click="updateUserInfo">修改</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import request from "@/utils/request.js";
|
||||
import router from "@/router/index";
|
||||
import { clearToken } from "@/utils/storage"
|
||||
import AdminMenu from '@/components/VerticalMenu.vue';
|
||||
import Logo from '@/components/Logo.vue';
|
||||
import LevelHeader from '@/components/LevelHeader.vue';
|
||||
export default {
|
||||
name: "Admin",
|
||||
components: {
|
||||
Logo,
|
||||
LevelHeader,
|
||||
AdminMenu
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
adminRoutes: [],
|
||||
activeIndex: '',
|
||||
userInfo: {
|
||||
id: null,
|
||||
url: '',
|
||||
name: '',
|
||||
role: null,
|
||||
email: ''
|
||||
},
|
||||
flag: false,
|
||||
tag: '可视化',
|
||||
bag: 'rgb(250, 250, 250)',
|
||||
colorLogo: '#1c1c1c',
|
||||
bagMenu: 'rgb(250, 250, 250)',
|
||||
dialogOperaion: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
let menus = router.options.routes.filter(route => route.path == '/admin')[0];
|
||||
this.adminRoutes = menus.children;
|
||||
this.tokenCheckLoad();
|
||||
this.menuOperationHistory();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async updateUserInfo() {
|
||||
try {
|
||||
const userUpdateDTO = {
|
||||
userAvatar: this.userInfo.url,
|
||||
userName: this.userInfo.name,
|
||||
userEmail: this.userInfo.email
|
||||
}
|
||||
const resposne = await this.$axios.put(`/user/update`, userUpdateDTO);
|
||||
const { data } = resposne;
|
||||
if (data.code === 200) {
|
||||
this.dialogOperaion = false;
|
||||
this.tokenCheckLoad();
|
||||
this.$swal.fire({
|
||||
title: '修改个人信息',
|
||||
text: data.msg,
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this.dialogOperaion = false;
|
||||
this.$swal.fire({
|
||||
title: '修改个人信息异常',
|
||||
text: e,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
console.error(`修改个人信息异常:${e}`);
|
||||
}
|
||||
},
|
||||
handleAvatarSuccess(res, file) {
|
||||
if (res.code !== 200) {
|
||||
this.$message.error(`头像上传异常`);
|
||||
return;
|
||||
}
|
||||
this.$message.success(`头像上传成功`);
|
||||
this.userInfo.url = res.data;
|
||||
},
|
||||
eventListener(event) {
|
||||
// 个人中心
|
||||
if (event === 'center') {
|
||||
this.dialogOperaion = true;
|
||||
}
|
||||
// 退出登录
|
||||
if (event === 'loginOut') {
|
||||
this.loginOut();
|
||||
}
|
||||
},
|
||||
async loginOut() {
|
||||
const confirmed = await this.$swalConfirm({
|
||||
title: '退出登录?',
|
||||
text: `推出后需重新登录?`,
|
||||
icon: 'warning',
|
||||
});
|
||||
if (confirmed) {
|
||||
this.$swal.fire({
|
||||
title: '退出登录成功',
|
||||
text: '1s 后返回登录页面',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
clearToken();
|
||||
this.$router.push("/login");
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
menuOperationHistory() {
|
||||
this.flag = sessionStorage.getItem('flag') === 'true';
|
||||
},
|
||||
selectOperation(flag) {
|
||||
this.flag = flag;
|
||||
},
|
||||
handleRouteSelect(index) {
|
||||
let ary = this.adminRoutes.filter(entity => entity.path == index);
|
||||
this.tag = ary[0].name;
|
||||
if (this.$router.currentRoute.fullPath == index) {
|
||||
return;
|
||||
}
|
||||
this.$router.push(index);
|
||||
},
|
||||
// Token检验
|
||||
async tokenCheckLoad() {
|
||||
try {
|
||||
const res = await request.get('user/auth');
|
||||
// 错误处理
|
||||
if (res.data.code === 400) {
|
||||
this.$message.error(res.data.msg);
|
||||
this.$router.push('/login');
|
||||
return;
|
||||
}
|
||||
// 用户信息赋值
|
||||
const { id, userAvatar: url, userName: name, userRole: role, userEmail: email } = res.data.data;
|
||||
this.userInfo = { id, url, name, role, email };
|
||||
// 根据角色解析路由
|
||||
const rolePath = role === 1 ? '/admin' : '/user';
|
||||
const targetMenu = router.options.routes.find(route => route.path === rolePath);
|
||||
if (targetMenu) {
|
||||
this.routers = targetMenu.children;
|
||||
} else {
|
||||
console.warn(`未找到与角色对应的路由:${rolePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户认证信息时发生错误:', error);
|
||||
this.$message.error('认证信息加载失败,请重试!');
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.menu-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.menu-side {
|
||||
width: 253px;
|
||||
min-width: 95px;
|
||||
height: 100vh;
|
||||
padding-top: 10px;
|
||||
box-sizing: border-box;
|
||||
transition: width 0.3s ease;
|
||||
background-color: rgb(250, 250, 250);
|
||||
}
|
||||
|
||||
.menu-side-narrow {
|
||||
width: 115px;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-grow: 1;
|
||||
overflow-x: hidden;
|
||||
|
||||
.header-section {
|
||||
max-width: 100%;
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
overflow-x: hidden;
|
||||
flex-grow: 1;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
</style>
|
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<el-row style="background-color: #FFFFFF;padding: 10px 0;border-radius: 5px;">
|
||||
<el-row style="padding: 10px;">
|
||||
<el-row>
|
||||
<span class="top-bar">公告标题</span>
|
||||
<el-input size="small" style="width: 188px;" v-model="noticeQueryDto.name" placeholder="输入处" clearable
|
||||
@clear="handleFilterClear">
|
||||
</el-input>
|
||||
<span class="top-bar">发布时间</span>
|
||||
<el-date-picker size="small" style="margin-left: 10px;width: 220px;" v-model="searchTime"
|
||||
type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间">
|
||||
</el-date-picker>
|
||||
<el-button size="small" class="customer"
|
||||
style="margin-left: 10px;background-color: rgb(235, 237, 245);color: rgb(43, 121, 203);border: none;" type="primary"
|
||||
@click="handleFilter">立即查询</el-button>
|
||||
<el-button size="small"
|
||||
style="background-color: rgb(235, 237, 245);color: rgb(43, 121, 203);border: none;" class="customer"
|
||||
type="info" @click="addNotice">新增公告</el-button>
|
||||
<el-button size="small" class="customer reset"
|
||||
style="background-color: #f1f1f1;border: none;color: #909399;border: 1px solid #f1f1f1;" type="info"
|
||||
@click="resetQueryCondition">条件重置</el-button>
|
||||
<el-button size="small" class="customer"
|
||||
:style="{ marginLeft: '10px', backgroundColor: selectedRows.length ? '#a7535a' : '#F1F1F1', border: 'none', color: selectedRows.length ? '#FFFFFF' : '#909399' }"
|
||||
type="danger" @click="batchDelete()">批量删除</el-button>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-row style="margin: 10px;">
|
||||
<el-table row-key="id" @selection-change="handleSelectionChange" :data="tableData" style="width: 100%">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column prop="name" width="508" label="公告"></el-table-column>
|
||||
<el-table-column prop="createTime" width="188" label="发布时间"></el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template slot-scope="scope">
|
||||
<span class="text-button" @click="handleEdit(scope.row)">修改</span>
|
||||
<span class="text-button" @click="handleDelete(scope.row)">删除</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination style="margin: 20px 0;float: right;" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[8, 20]"
|
||||
:page-size="pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="totalItems"></el-pagination>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
filterText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 8,
|
||||
totalItems: 0,
|
||||
tableData: [],
|
||||
searchTime: [],
|
||||
selectedRows: [],
|
||||
noticeQueryDto: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchFreshData();
|
||||
},
|
||||
methods: {
|
||||
// 公告新增
|
||||
addNotice() {
|
||||
sessionStorage.setItem('noticeOperation', 'save');
|
||||
this.$router.push('/createNotice');
|
||||
},
|
||||
// 多选框选中
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
// 批量删除数据
|
||||
async batchDelete() {
|
||||
if (!this.selectedRows.length) {
|
||||
this.$message(`未选中任何数据`);
|
||||
return;
|
||||
}
|
||||
const confirmed = await this.$swalConfirm({
|
||||
title: '删除公告数据',
|
||||
text: `删除后不可恢复,是否继续?`,
|
||||
icon: 'warning',
|
||||
});
|
||||
if (confirmed) {
|
||||
try {
|
||||
let ids = this.selectedRows.map(entity => entity.id);
|
||||
const response = await this.$axios.post(`notice/batchDelete`, ids);
|
||||
if (response.data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: '删除提示',
|
||||
text: response.data.msg,
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
this.fetchFreshData();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.$swal.fire({
|
||||
title: '错误提示',
|
||||
text: e,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
console.error(`公告信息删除异常:`, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
resetQueryCondition() {
|
||||
this.noticeQueryDto = {};
|
||||
this.searchTime = [];
|
||||
this.fetchFreshData();
|
||||
},
|
||||
clearFormData() {
|
||||
this.data = {};
|
||||
},
|
||||
async fetchFreshData() {
|
||||
try {
|
||||
this.tableData = [];
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (this.searchTime != null && this.searchTime.length === 2) {
|
||||
const [startDate, endDate] = await Promise.all(this.searchTime.map(date => date.toISOString()));
|
||||
startTime = `${startDate.split('T')[0]}T00:00:00`;
|
||||
endTime = `${endDate.split('T')[0]}T23:59:59`;
|
||||
}
|
||||
// 请求参数
|
||||
const params = {
|
||||
current: this.currentPage,
|
||||
size: this.pageSize,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
...this.noticeQueryDto
|
||||
};
|
||||
const response = await this.$axios.post('notice/query', params);
|
||||
const { data } = response;
|
||||
this.tableData = data.data;
|
||||
this.totalItems = data.total;
|
||||
} catch (error) {
|
||||
console.error('查询公告信息异常:', error);
|
||||
}
|
||||
},
|
||||
handleFilter() {
|
||||
this.currentPage = 1;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
handleFilterClear() {
|
||||
this.filterText = '';
|
||||
this.handleFilter();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val;
|
||||
this.currentPage = 1;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
handleEdit(row) {
|
||||
sessionStorage.setItem('noticeInfo', JSON.stringify(row));
|
||||
sessionStorage.setItem('noticeOperation', 'update');
|
||||
this.$router.push('/createNotice');
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.selectedRows.push(row);
|
||||
this.batchDelete();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<el-row style="background-color: #FFFFFF;padding: 20px 0;border-radius: 5px;">
|
||||
<el-row style="padding: 10px;margin: 0 10px;">
|
||||
<el-row>
|
||||
<span class="top-bar">用户名</span>
|
||||
<el-input size="small" style="width: 188px;margin-right: 10px;" v-model="userQueryDto.userName" placeholder="用户名" clearable
|
||||
@clear="handleFilterClear">
|
||||
</el-input>
|
||||
<span class="top-bar">注册时间</span>
|
||||
<el-date-picker size="small" style="width: 220px;" v-model="searchTime"
|
||||
type="daterange" range-separator="至" start-placeholder="起始时间" end-placeholder="结束时间">
|
||||
</el-date-picker>
|
||||
<el-button size="small" class="customer" style="background-color: rgb(235, 237, 245);color: rgb(43, 121, 203);border: none;"
|
||||
type="primary" @click="handleFilter">立即查询</el-button>
|
||||
<el-button size="small" style="background-color: rgb(235, 237, 245);color: rgb(43, 121, 203);border: none;" class="customer"
|
||||
type="info" @click="add()">新增用户</el-button>
|
||||
<el-button size="small" class="customer"
|
||||
:style="{ marginLeft: '10px', backgroundColor: selectedRows.length ? '#a7535a' : '#F1F1F1', border: 'none', color: selectedRows.length ? '#FFFFFF' : '#909399' }"
|
||||
type="danger" @click="batchDelete()">批量删除</el-button>
|
||||
<el-button size="small" class="customer reset"
|
||||
style="background-color: #f1f1f1;border: none;color: #909399;border: 1px solid #f1f1f1;" type="info"
|
||||
@click="resetQueryCondition">条件重置</el-button>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-row style="margin: 10px 20px;">
|
||||
<el-table row-key="id" @selection-change="handleSelectionChange" :data="tableData" style="width: 100%">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column prop="userAvatar" width="68" label="头像">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :size="30" :src="scope.row.userAvatar" style="margin-top: 10px;"></el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userName" width="148" label="名称"></el-table-column>
|
||||
<el-table-column prop="userAccount" width="128" label="账号"></el-table-column>
|
||||
<el-table-column prop="userEmail" width="168" label="用户邮箱"></el-table-column>
|
||||
<el-table-column prop="userRole" width="68" label="角色">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.userRole === 1 ? '管理员' : '用户' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isLogin" width="108" label="封号">
|
||||
<template slot-scope="scope">
|
||||
<el-switch @change="handleSwitchChange(scope.row.id, scope.row.isLogin, true)"
|
||||
style="user-select: none;" v-model="scope.row.isLogin" active-color="#13ce66"
|
||||
inactive-color="rgb(226, 226, 226)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :sortable="true" prop="createTime" width="168" label="注册于"></el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template slot-scope="scope">
|
||||
<span class="text-button" @click="handleEdit(scope.row)">编辑</span>
|
||||
<span class="text-button" @click="handleDelete(scope.row)">删除</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination style="margin: 20px 0;float: right;" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[5, 7]"
|
||||
:page-size="pageSize" layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="totalItems"></el-pagination>
|
||||
</el-row>
|
||||
<!-- 操作面板 -->
|
||||
<el-dialog :show-close="false" :visible.sync="dialogUserOperaion" width="28%">
|
||||
<div slot="title">
|
||||
<p class="dialog-title">{{ !isOperation ? '新增新用户' : '编辑用户信息' }}</p>
|
||||
</div>
|
||||
<div style="padding:0 20px;">
|
||||
<el-row>
|
||||
<el-upload class="avatar-uploader" action="/api/book-manage-sys-api/v1.0/file/upload"
|
||||
:show-file-list="false" :on-success="handleAvatarSuccess">
|
||||
<img v-if="data.userAvatar" :src="data.userAvatar" class="dialog-avatar">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<span class="dialog-hover">用户名</span>
|
||||
<input class="dialog-input" v-model="data.userName" placeholder="用户名" />
|
||||
<span class="dialog-hover">账号</span>
|
||||
<input class="dialog-input" v-model="data.userAccount" placeholder="账号" />
|
||||
<span class="dialog-hover">邮箱</span>
|
||||
<input class="dialog-input" v-model="data.userEmail" placeholder="邮箱" />
|
||||
<span class="dialog-hover">密码</span>
|
||||
<input class="dialog-input" v-model="userPwd" type="password" placeholder="密码" />
|
||||
</el-row>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button size="small" v-if="!isOperation" style="background-color: rgb(43, 121, 203);border: none;"
|
||||
class="customer" type="info" @click="addOperation">新增</el-button>
|
||||
<el-button size="small" v-else style="background-color: rgb(43, 121, 203);border: none;"
|
||||
class="customer" type="info" @click="updateOperation">修改</el-button>
|
||||
<el-button class="customer" size="small" style="background-color: rgb(241, 241, 241);border: none;"
|
||||
@click="dialogUserOperaion = false">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<!-- 消息推送 -->
|
||||
<el-dialog :show-title="false" :show-close="false" :visible.sync="dialogMessageOperation" width="24%">
|
||||
<p style="padding: 20px 0 0 20px;">消息推送</p>
|
||||
<div style="padding:0 20px;">
|
||||
<el-row>
|
||||
<span class="dialog-hover">消息内容</span>
|
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" placeholder="消息内容"
|
||||
v-model="data.content">
|
||||
</el-input>
|
||||
</el-row>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button size="small" style="background-color: rgb(43, 121, 203);border: none;" class="customer"
|
||||
type="info" @click="messagePushOperation">确定推送</el-button>
|
||||
<el-button class="customer" size="small" style="background-color: rgb(241, 241, 241);border: none;"
|
||||
@click="dialogMessageOperation = false">取消</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userPwd: '',
|
||||
data: { userAvatar: '' },
|
||||
filterText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 7,
|
||||
totalItems: 0,
|
||||
dialogMessageOperation: false,
|
||||
dialogUserOperaion: false, // 开关
|
||||
isOperation: false, // 开关-标识新增或修改
|
||||
tableData: [],
|
||||
searchTime: [],
|
||||
selectedRows: [],
|
||||
status: null,
|
||||
userQueryDto: {}, // 搜索条件
|
||||
messsageContent: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
dialogUserOperaion(v1, v2) {
|
||||
if (!v1) {
|
||||
this.isOperation = !this.isOperation;
|
||||
}
|
||||
if (!v1 && v2) {
|
||||
this.data = {};
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchFreshData();
|
||||
},
|
||||
methods: {
|
||||
// 处理消息的推送
|
||||
messagePushOperation() {
|
||||
const messages = []
|
||||
const message = {
|
||||
receiverId: this.data.id,
|
||||
content: this.data.content
|
||||
}
|
||||
messages.push(message);
|
||||
this.$axios.post('/message/systemInfoSave', messages).then(response => {
|
||||
const { data } = response;
|
||||
if (data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: '消息推送',
|
||||
text: '推送成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
this.dialogMessageOperation = false;
|
||||
this.data = {};
|
||||
}
|
||||
})
|
||||
},
|
||||
handleAvatarSuccess(res, file) {
|
||||
if (res.code !== 200) {
|
||||
this.$message.error(`用户头像上传异常`);
|
||||
return;
|
||||
}
|
||||
this.$message.success(`用户头像上传成功`);
|
||||
this.data.userAvatar = res.data;
|
||||
console.log(this.data);
|
||||
},
|
||||
switchChange() {
|
||||
this.fetchFreshData();
|
||||
},
|
||||
async handleSwitchChange(id, status, operation) {
|
||||
try {
|
||||
let param = { id: id }
|
||||
// 登录状态
|
||||
if (operation) {
|
||||
param.isLogin = status;
|
||||
} else { // 评论状态
|
||||
param.isWord = status;
|
||||
}
|
||||
const response = await this.$axios.put(`/user/backUpdate`, param);
|
||||
if (response.data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: operation ? '登录状态' : '评论状态',
|
||||
text: operation ? '登录状态操作成功' : '评论状态操作成功',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`更新用户状态异常:${e}`);
|
||||
}
|
||||
},
|
||||
// 多选框选中
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
// 批量删除数据
|
||||
async batchDelete() {
|
||||
if (!this.selectedRows.length) {
|
||||
this.$message(`未选中任何数据`);
|
||||
return;
|
||||
}
|
||||
const confirmed = await this.$swalConfirm({
|
||||
title: '删除用户数据',
|
||||
text: `删除后不可恢复,是否继续?`,
|
||||
icon: 'warning',
|
||||
});
|
||||
if (confirmed) {
|
||||
try {
|
||||
let ids = this.selectedRows.map(entity => entity.id);
|
||||
const response = await this.$axios.post(`/user/batchDelete`, ids);
|
||||
if (response.data.code === 200) {
|
||||
this.$swal.fire({
|
||||
title: '删除提示',
|
||||
text: response.data.msg,
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
this.fetchFreshData();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.$swal.fire({
|
||||
title: '错误提示',
|
||||
text: e,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
console.error(`用户信息删除异常:`, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
resetQueryCondition() {
|
||||
this.userQueryDto = {};
|
||||
this.searchTime = [];
|
||||
this.fetchFreshData();
|
||||
},
|
||||
// 修改信息
|
||||
async updateOperation() {
|
||||
if (this.userPwd !== '') {
|
||||
const pwd = this.$md5(this.$md5(this.userPwd));
|
||||
this.data.userPwd = pwd;
|
||||
} else {
|
||||
this.data.userPwd = null;
|
||||
}
|
||||
try {
|
||||
const response = await this.$axios.put('/user/backUpdate', this.data);
|
||||
this.$swal.fire({
|
||||
title: '用户信息修改',
|
||||
text: response.data.msg,
|
||||
icon: response.data.code === 200 ? 'success' : 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
if (response.data.code === 200) {
|
||||
this.closeDialog();
|
||||
this.fetchFreshData();
|
||||
this.clearFormData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交表单时出错:', error);
|
||||
this.$message.error('提交失败,请稍后再试!');
|
||||
}
|
||||
},
|
||||
// 信息新增
|
||||
async addOperation() {
|
||||
if (this.userPwd !== '') {
|
||||
this.data.userPwd = this.$md5(this.$md5(this.userPwd));
|
||||
} else {
|
||||
this.data.userPwd = null;
|
||||
}
|
||||
try {
|
||||
const response = await this.$axios.post('/user/insert', this.data);
|
||||
this.$message[response.data.code === 200 ? 'success' : 'error'](response.data.msg);
|
||||
if (response.data.code === 200) {
|
||||
this.closeDialog();
|
||||
this.fetchFreshData();
|
||||
this.clearFormData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交表单时出错:', error);
|
||||
this.$message.error('提交失败,请稍后再试!');
|
||||
}
|
||||
},
|
||||
closeDialog() {
|
||||
this.dialogUserOperaion = false;
|
||||
},
|
||||
clearFormData() {
|
||||
this.data = {};
|
||||
},
|
||||
async fetchFreshData() {
|
||||
try {
|
||||
this.tableData = [];
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (this.searchTime != null && this.searchTime.length === 2) {
|
||||
const [startDate, endDate] = await Promise.all(this.searchTime.map(date => date.toISOString()));
|
||||
startTime = `${startDate.split('T')[0]}T00:00:00`;
|
||||
endTime = `${endDate.split('T')[0]}T23:59:59`;
|
||||
}
|
||||
// 请求参数
|
||||
const params = {
|
||||
current: this.currentPage,
|
||||
size: this.pageSize,
|
||||
key: this.filterText,
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
...this.userQueryDto
|
||||
};
|
||||
const response = await this.$axios.post('/user/query', params);
|
||||
const { data } = response;
|
||||
this.tableData = data.data;
|
||||
this.totalItems = data.total;
|
||||
} catch (error) {
|
||||
console.error('查询用户信息异常:', error);
|
||||
}
|
||||
},
|
||||
add() {
|
||||
this.dialogUserOperaion = true;
|
||||
},
|
||||
handleFilter() {
|
||||
this.currentPage = 1;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
handleFilterClear() {
|
||||
this.filterText = '';
|
||||
this.handleFilter();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val;
|
||||
this.currentPage = 1;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val;
|
||||
this.fetchFreshData();
|
||||
},
|
||||
messagePush(row) {
|
||||
this.dialogMessageOperation = true;
|
||||
this.data = { ...row };
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.dialogUserOperaion = true;
|
||||
this.isOperation = true;
|
||||
row.userPwd = null;
|
||||
this.data = { ...row }
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.selectedRows.push(row);
|
||||
this.batchDelete();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.tag-tip {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
background-color: rgb(245, 245, 245);
|
||||
color: rgb(104, 118, 130);
|
||||
}
|
||||
|
||||
.input-def {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
color: rgb(102, 102, 102);
|
||||
font-weight: 900;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
/* 使按钮水平居中 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 如果需要调整按钮之间的间距 */
|
||||
.customer {
|
||||
margin: 0 8px;
|
||||
/* 根据需要调整间距 */
|
||||
}
|
||||
</style>
|
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="menu-container">
|
||||
<div class="menu-side" :class="{ 'menu-side-narrow': flag }">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<Logo name="图书管理" style="padding: 0 40px;margin: 15px 0;" :flag="flag" :bag="colorLogo" />
|
||||
</div>
|
||||
<div style="margin-top: 12px;">
|
||||
<AdminMenu :flag="flag" :routes="adminRoutes" :bag="bagMenu" @select="handleRouteSelect" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="header-section">
|
||||
<LevelHeader @eventListener="eventListener" @selectOperation="selectOperation" :tag="tag"
|
||||
:userInfo="userInfo" />
|
||||
</div>
|
||||
<div class="content-section">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 个人中心 -->
|
||||
<el-dialog :show-close="false" :visible.sync="dialogOperaion" width="26%">
|
||||
<div slot="title" style="padding: 25px 0 0 20px;">
|
||||
<span style="font-size: 18px;font-weight: 800;">个人中心</span>
|
||||
</div>
|
||||
<el-row style="padding: 10px 20px 20px 20px;">
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;margin-bottom: 10px;">
|
||||
<span class="modelName">*头像</span>
|
||||
</p>
|
||||
<el-upload class="avatar-uploader" action="/api/book-manage-sys-api/v1.0/file/upload"
|
||||
:show-file-list="false" :on-success="handleAvatarSuccess">
|
||||
<img v-if="userInfo.url" :src="userInfo.url" style="width: 80px;height: 80px;">
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;">
|
||||
<span class="modelName">*用户名</span>
|
||||
</p>
|
||||
<input class="input-title" v-model="userInfo.name" placeholder="用户名">
|
||||
</el-row>
|
||||
<el-row>
|
||||
<p style="font-size: 12px;padding: 3px 0;">
|
||||
<span class="modelName">*用户邮箱</span>
|
||||
</p>
|
||||
<input class="input-title" v-model="userInfo.email" placeholder="用户邮箱">
|
||||
</el-row>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button class="customer" size="small" style="background-color: rgb(241, 241, 241);border: none;"
|
||||
@click="dialogOperaion = false">取 消</el-button>
|
||||
<el-button size="small" style="background-color: #15559a;border: none;" class="customer" type="info"
|
||||
@click="updateUserInfo">修改</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import request from "@/utils/request.js";
|
||||
import router from "@/router/index";
|
||||
import { clearToken } from "@/utils/storage"
|
||||
import AdminMenu from '@/components/VerticalMenu.vue';
|
||||
import Logo from '@/components/Logo.vue';
|
||||
import LevelHeader from '@/components/LevelHeader.vue';
|
||||
export default {
|
||||
name: "Admin",
|
||||
components: {
|
||||
Logo,
|
||||
LevelHeader,
|
||||
AdminMenu
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
adminRoutes: [],
|
||||
activeIndex: '',
|
||||
userInfo: {
|
||||
id: null,
|
||||
url: '',
|
||||
name: '',
|
||||
role: null,
|
||||
email: ''
|
||||
},
|
||||
flag: false,
|
||||
tag: '可视化',
|
||||
bag: 'rgb(250, 250, 250)',
|
||||
colorLogo: '#1c1c1c',
|
||||
bagMenu: 'rgb(250, 250, 250)',
|
||||
dialogOperaion: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
let menus = router.options.routes.filter(route => route.path == '/user')[0];
|
||||
this.adminRoutes = menus.children;
|
||||
this.tokenCheckLoad();
|
||||
this.menuOperationHistory();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async updateUserInfo() {
|
||||
try {
|
||||
const userUpdateDTO = {
|
||||
userAvatar: this.userInfo.url,
|
||||
userName: this.userInfo.name,
|
||||
userEmail: this.userInfo.email
|
||||
}
|
||||
const resposne = await this.$axios.put(`/user/update`, userUpdateDTO);
|
||||
const { data } = resposne;
|
||||
if (data.code === 200) {
|
||||
this.dialogOperaion = false;
|
||||
this.tokenCheckLoad();
|
||||
this.$swal.fire({
|
||||
title: '修改个人信息',
|
||||
text: data.msg,
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this.dialogOperaion = false;
|
||||
this.$swal.fire({
|
||||
title: '修改个人信息异常',
|
||||
text: e,
|
||||
icon: 'error',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
});
|
||||
console.error(`修改个人信息异常:${e}`);
|
||||
}
|
||||
},
|
||||
handleAvatarSuccess(res, file) {
|
||||
if (res.code !== 200) {
|
||||
this.$message.error(`头像上传异常`);
|
||||
return;
|
||||
}
|
||||
this.$message.success(`头像上传成功`);
|
||||
this.userInfo.url = res.data;
|
||||
},
|
||||
eventListener(event) {
|
||||
// 个人中心
|
||||
if (event === 'center') {
|
||||
this.dialogOperaion = true;
|
||||
}
|
||||
// 退出登录
|
||||
if (event === 'loginOut') {
|
||||
this.loginOut();
|
||||
}
|
||||
},
|
||||
async loginOut() {
|
||||
const confirmed = await this.$swalConfirm({
|
||||
title: '退出登录?',
|
||||
text: `推出后需重新登录?`,
|
||||
icon: 'warning',
|
||||
});
|
||||
if (confirmed) {
|
||||
this.$swal.fire({
|
||||
title: '退出登录成功',
|
||||
text: '1s 后返回登录页面',
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
timer: 1000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
clearToken();
|
||||
this.$router.push("/login");
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
menuOperationHistory() {
|
||||
this.flag = sessionStorage.getItem('flag') === 'true';
|
||||
},
|
||||
selectOperation(flag) {
|
||||
this.flag = flag;
|
||||
},
|
||||
handleRouteSelect(index) {
|
||||
let ary = this.adminRoutes.filter(entity => entity.path == index);
|
||||
this.tag = ary[0].name;
|
||||
if (this.$router.currentRoute.fullPath == index) {
|
||||
return;
|
||||
}
|
||||
this.$router.push(index);
|
||||
},
|
||||
// Token检验
|
||||
async tokenCheckLoad() {
|
||||
try {
|
||||
const res = await request.get('user/auth');
|
||||
// 错误处理
|
||||
if (res.data.code === 400) {
|
||||
this.$message.error(res.data.msg);
|
||||
this.$router.push('/login');
|
||||
return;
|
||||
}
|
||||
// 用户信息赋值
|
||||
const { id, userAvatar: url, userName: name, userRole: role, userEmail: email } = res.data.data;
|
||||
this.userInfo = { id, url, name, role, email };
|
||||
// 根据角色解析路由
|
||||
const rolePath = role === 1 ? '/admin' : '/user';
|
||||
const targetMenu = router.options.routes.find(route => route.path === rolePath);
|
||||
if (targetMenu) {
|
||||
this.routers = targetMenu.children;
|
||||
} else {
|
||||
console.warn(`未找到与角色对应的路由:${rolePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户认证信息时发生错误:', error);
|
||||
this.$message.error('认证信息加载失败,请重试!');
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.menu-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.menu-side {
|
||||
width: 253px;
|
||||
min-width: 95px;
|
||||
height: 100vh;
|
||||
padding-top: 10px;
|
||||
box-sizing: border-box;
|
||||
transition: width 0.3s ease;
|
||||
background-color: rgb(250, 250, 250);
|
||||
}
|
||||
|
||||
.menu-side-narrow {
|
||||
width: 115px;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-grow: 1;
|
||||
overflow-x: hidden;
|
||||
|
||||
.header-section {
|
||||
max-width: 100%;
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
overflow-x: hidden;
|
||||
flex-grow: 1;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
</style>
|
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div>首页</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
lintOnSave: false,
|
||||
devServer: {
|
||||
host: "localhost",
|
||||
port: 21091,
|
||||
https: false,
|
||||
proxy: "http://localhost:21090",
|
||||
overlay: {
|
||||
warning: false,
|
||||
errors: false
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/../../../../../../:\Desktop\project\hospital_manager\HospitalManagerApi - idea\.idea/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="seniors-nursing" />
|
||||
<module name="book-manage-sys-api" />
|
||||
<module name="health-api" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="class-net" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="book-manage-sys-api" options="-parameters" />
|
||||
<module name="class-net" options="-parameters" />
|
||||
<module name="health-api" options="-parameters" />
|
||||
<module name="seniors-nursing" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="IncorrectHttpHeaderInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="customHeaders">
|
||||
<set>
|
||||
<option value="fileName" />
|
||||
</set>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="public" />
|
||||
<option name="name" value="aliyun nexus" />
|
||||
<option name="url" value="https://maven.aliyun.com/repository/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/book-manage-sys-api.iml" filepath="$PROJECT_DIR$/book-manage-sys-api.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration />
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 2.2 MiB |
@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.kmbeast</groupId>
|
||||
<artifactId>book-manage-sys-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<name>book-manage-sys-api</name>
|
||||
<!--编译配置-->
|
||||
<properties>
|
||||
<news.build.sourceEncoding>UTF-8</news.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<!--springboot父亲依赖-->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.4.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<!--依赖-->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<!--SpringBoot 启动包-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!--SpringBoot Web-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--SpringBoot Aop-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<!--Lombok 简化实体类开发-->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.14.3</version> <!-- 请检查是否有更新的版本 -->
|
||||
</dependency>
|
||||
<!--数据库链接-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- mybatis依赖-->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>2.1.2</version>
|
||||
</dependency>
|
||||
<!--excel处理-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.2.1</version>
|
||||
</dependency>
|
||||
<!--excel处理-->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
<!-- JWT相关 -->
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
<!-- JWT相关 -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<!-- JWT相关 -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext-asian</artifactId>
|
||||
<version>5.2.0</version>
|
||||
</dependency>
|
||||
<!-- JSON解析 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.33</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<defaultGoal>compile</defaultGoal>
|
||||
<!--定义资源路径-->
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>*/*.*</include>
|
||||
<include>*.*</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>https://maven.aliyun.com/repository/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>public</id>
|
||||
<name>aliyun nexus</name>
|
||||
<url>https://maven.aliyun.com/repository/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
@ -0,0 +1,16 @@
|
||||
package cn.kmbeast;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 项目启动类
|
||||
*/
|
||||
@MapperScan("cn.kmbeast.mapper")
|
||||
@SpringBootApplication
|
||||
public class BookManageSysApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BookManageSysApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.kmbeast.config;
|
||||
|
||||
import cn.kmbeast.Interceptor.JwtInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* API拦截器配置
|
||||
*/
|
||||
@Configuration
|
||||
public class InterceptorConfig implements WebMvcConfigurer {
|
||||
|
||||
@Value("${my-server.api-context-path}")
|
||||
private String API;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 拦截器注册
|
||||
registry.addInterceptor(new JwtInterceptor())
|
||||
.addPathPatterns("/**")
|
||||
// 放行登录、注册请求
|
||||
.excludePathPatterns(
|
||||
API + "/user/login",
|
||||
API + "/user/register",
|
||||
API + "/file/upload",
|
||||
API + "/file/getFile"
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.kmbeast.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedHeaders("*")
|
||||
.allowedMethods("*")
|
||||
.maxAge(5000);
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package cn.kmbeast.controller;
|
||||
|
||||
import cn.kmbeast.utils.IdFactoryUtil;
|
||||
import cn.kmbeast.utils.PathUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文件前端控制器
|
||||
*
|
||||
* @since 2024-03-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
public class FileController {
|
||||
|
||||
@Value("${my-server.api-context-path}")
|
||||
private String API;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param multipartFile 文件流
|
||||
* @return 响应
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public Map<String, Object> uploadFile(@RequestParam("file") MultipartFile multipartFile) {
|
||||
String uuid = IdFactoryUtil.getFileId();
|
||||
String fileName = uuid + multipartFile.getOriginalFilename();
|
||||
Map<String, Object> rep = new HashMap<>();
|
||||
try {
|
||||
if (uploadFile(multipartFile, fileName)) {
|
||||
rep.put("code", 200);
|
||||
rep.put("data", API+ "/file/getFile?fileName=" + fileName);
|
||||
return rep;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
rep.put("code", 400);
|
||||
rep.put("msg", "文件上传异常");
|
||||
return rep;
|
||||
}
|
||||
rep.put("code", 400);
|
||||
rep.put("msg", "文件上传异常");
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频上传
|
||||
*
|
||||
* @param multipartFile 文件流
|
||||
* @return 响应
|
||||
*/
|
||||
@PostMapping("/video/upload")
|
||||
public Map<String, Object> videoUpload(@RequestParam("file") MultipartFile multipartFile) {
|
||||
String uuid = IdFactoryUtil.getFileId();
|
||||
String fileName = uuid + multipartFile.getOriginalFilename();
|
||||
Map<String, Object> rep = new HashMap<>();
|
||||
|
||||
try {
|
||||
if (uploadFile(multipartFile, fileName)) {
|
||||
rep.put("code", 200);
|
||||
rep.put("data", API+ "/file/getFile?fileName=" + fileName);
|
||||
return rep;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
rep.put("code", 400);
|
||||
rep.put("msg", "文件上传异常");
|
||||
return rep;
|
||||
}
|
||||
rep.put("code", 400);
|
||||
rep.put("msg", "文件上传异常");
|
||||
return rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param multipartFile 文件流
|
||||
* @param fileName 文件名
|
||||
* @return boolean
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public boolean uploadFile(MultipartFile multipartFile, String fileName) throws IOException {
|
||||
return fileName(multipartFile, fileName);
|
||||
}
|
||||
|
||||
public static boolean fileName(MultipartFile multipartFile, String fileName) throws IOException {
|
||||
File fileDir = new File(PathUtils.getClassLoadRootPath() + "/pic");
|
||||
if (!fileDir.exists()) {
|
||||
if (!fileDir.mkdirs()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File file = new File(fileDir.getAbsolutePath() + "/" + fileName);
|
||||
if (file.exists()) {
|
||||
if (!file.delete()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (file.createNewFile()) {
|
||||
multipartFile.transferTo(file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看图片资源
|
||||
*
|
||||
* @param imageName 文件名
|
||||
* @param response 响应
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@GetMapping("/getFile")
|
||||
public void getImage(@RequestParam("fileName") String imageName,
|
||||
HttpServletResponse response) throws IOException {
|
||||
File fileDir = new File(PathUtils.getClassLoadRootPath() + "/pic");
|
||||
File image = new File(fileDir.getAbsolutePath() + "/" + imageName);
|
||||
if (image.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(image);
|
||||
byte[] bytes = new byte[fileInputStream.available()];
|
||||
if (fileInputStream.read(bytes) > 0) {
|
||||
OutputStream outputStream = response.getOutputStream();
|
||||
outputStream.write(bytes);
|
||||
outputStream.close();
|
||||
}
|
||||
fileInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package cn.kmbeast.controller;
|
||||
|
||||
import cn.kmbeast.aop.Pager;
|
||||
import cn.kmbeast.pojo.api.Result;
|
||||
import cn.kmbeast.pojo.dto.query.extend.NoticeQueryDto;
|
||||
import cn.kmbeast.pojo.entity.Notice;
|
||||
import cn.kmbeast.service.NoticeService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 公告的 Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/notice")
|
||||
public class NoticeController {
|
||||
|
||||
@Resource
|
||||
private NoticeService noticeService;
|
||||
|
||||
/**
|
||||
* 公告新增
|
||||
*
|
||||
* @param notice 新增数据
|
||||
* @return Result<Void> 通用响应体
|
||||
*/
|
||||
@PostMapping(value = "/save")
|
||||
public Result<Void> save(@RequestBody Notice notice) {
|
||||
return noticeService.save(notice);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公告删除
|
||||
*
|
||||
* @param ids 要删除的公告ID列表
|
||||
* @return Result<Void> 通用响应体
|
||||
*/
|
||||
@PostMapping(value = "/batchDelete")
|
||||
public Result<Void> batchDelete(@RequestBody List<Integer> ids) {
|
||||
return noticeService.batchDelete(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公告修改
|
||||
*
|
||||
* @param notice 参数
|
||||
* @return Result<Void> 响应
|
||||
*/
|
||||
@PutMapping(value = "/update")
|
||||
public Result<Void> update(@RequestBody Notice notice) {
|
||||
return noticeService.update(notice);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公告查询
|
||||
*
|
||||
* @param noticeQueryDto 查询参数
|
||||
* @return Result<List < Notice>> 通用响应
|
||||
*/
|
||||
@Pager
|
||||
@PostMapping(value = "/query")
|
||||
public Result<List<Notice>> query(@RequestBody NoticeQueryDto noticeQueryDto) {
|
||||
return noticeService.query(noticeQueryDto);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
package cn.kmbeast.controller;
|
||||
|
||||
import cn.kmbeast.aop.Pager;
|
||||
import cn.kmbeast.aop.Protector;
|
||||
import cn.kmbeast.pojo.api.Result;
|
||||
import cn.kmbeast.pojo.dto.query.extend.UserQueryDto;
|
||||
import cn.kmbeast.pojo.dto.update.UserLoginDTO;
|
||||
import cn.kmbeast.pojo.dto.update.UserRegisterDTO;
|
||||
import cn.kmbeast.pojo.dto.update.UserUpdateDTO;
|
||||
import cn.kmbeast.pojo.entity.User;
|
||||
import cn.kmbeast.pojo.vo.ChartVO;
|
||||
import cn.kmbeast.pojo.vo.UserVO;
|
||||
import cn.kmbeast.service.UserService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param userLoginDTO 登录入参
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@PostMapping(value = "/login")
|
||||
@ResponseBody
|
||||
public Result<Object> login(@RequestBody UserLoginDTO userLoginDTO) {
|
||||
return userService.login(userLoginDTO);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* token校验
|
||||
*/
|
||||
@Protector
|
||||
@GetMapping(value = "/auth")
|
||||
@ResponseBody
|
||||
public Result<UserVO> auth() {
|
||||
return userService.auth();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过ID查询用户信息
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return Result<UserVO>
|
||||
*/
|
||||
@Protector
|
||||
@GetMapping(value = "/getById/{id}")
|
||||
@ResponseBody
|
||||
public Result<UserVO> getById(@PathVariable Integer id) {
|
||||
return userService.getById(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param userRegisterDTO 注册入参
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@PostMapping(value = "/register")
|
||||
@ResponseBody
|
||||
public Result<String> register(@RequestBody UserRegisterDTO userRegisterDTO) {
|
||||
return userService.register(userRegisterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台新增用户
|
||||
*
|
||||
* @param userRegisterDTO 注册入参
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@Protector(role = "管理员")
|
||||
@PostMapping(value = "/insert")
|
||||
@ResponseBody
|
||||
public Result<String> insert(@RequestBody UserRegisterDTO userRegisterDTO) {
|
||||
return userService.insert(userRegisterDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息修改
|
||||
*
|
||||
* @param userUpdateDTO 修改信息入参
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@Protector
|
||||
@PutMapping(value = "/update")
|
||||
@ResponseBody
|
||||
public Result<String> update(@RequestBody UserUpdateDTO userUpdateDTO) {
|
||||
return userService.update(userUpdateDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台用户信息修改
|
||||
*
|
||||
* @param user 信息实体
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@Protector(role = "管理员")
|
||||
@PutMapping(value = "/backUpdate")
|
||||
@ResponseBody
|
||||
public Result<String> backUpdate(@RequestBody User user) {
|
||||
return userService.backUpdate(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户修改密码
|
||||
*
|
||||
* @param map 修改信息入参
|
||||
* @return Result<String> 响应结果
|
||||
*/
|
||||
@PutMapping(value = "/updatePwd")
|
||||
@ResponseBody
|
||||
public Result<String> updatePwd(@RequestBody Map<String, String> map) {
|
||||
return userService.updatePwd(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除用户信息
|
||||
*/
|
||||
@Protector(role = "管理员")
|
||||
@PostMapping(value = "/batchDelete")
|
||||
@ResponseBody
|
||||
public Result<String> batchDelete(@RequestBody List<Integer> ids) {
|
||||
return userService.batchDelete(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户数据
|
||||
*
|
||||
* @param userQueryDto 查询参数
|
||||
* @return Result<List < User>> 响应结果
|
||||
*/
|
||||
@Pager
|
||||
@Protector(role = "管理员")
|
||||
@PostMapping(value = "/query")
|
||||
@ResponseBody
|
||||
public Result<List<User>> query(@RequestBody UserQueryDto userQueryDto) {
|
||||
return userService.query(userQueryDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计用户存量数据
|
||||
*
|
||||
* @return Result<List < ChartVO>> 响应结果
|
||||
*/
|
||||
@GetMapping(value = "/daysQuery/{day}")
|
||||
@ResponseBody
|
||||
public Result<List<ChartVO>> query(@PathVariable Integer day) {
|
||||
return userService.daysQuery(day);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package cn.kmbeast.controller;
|
||||
|
||||
import cn.kmbeast.pojo.api.Result;
|
||||
import cn.kmbeast.pojo.vo.ChartVO;
|
||||
import cn.kmbeast.service.ViewsService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 可视化接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/views")
|
||||
public class ViewsController {
|
||||
|
||||
@Resource
|
||||
private ViewsService viewsService;
|
||||
|
||||
/**
|
||||
* 统计一些系统的基础数据
|
||||
*
|
||||
* @return Result<List < ChartVO>>
|
||||
*/
|
||||
@GetMapping("/staticControls")
|
||||
public Result<List<ChartVO>> staticControls() {
|
||||
return viewsService.staticControls();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.kmbeast.mapper;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.extend.NoticeQueryDto;
|
||||
import cn.kmbeast.pojo.dto.query.extend.TestHistoryQueryDto;
|
||||
import cn.kmbeast.pojo.entity.Notice;
|
||||
import cn.kmbeast.pojo.entity.TestHistory;
|
||||
import cn.kmbeast.pojo.vo.TestHistoryVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 公告持久化接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface NoticeMapper {
|
||||
|
||||
void save(Notice notice);
|
||||
|
||||
void update(Notice notice);
|
||||
|
||||
void batchDelete(@Param(value = "ids") List<Integer> ids);
|
||||
|
||||
List<Notice> query(NoticeQueryDto noticeQueryDto);
|
||||
|
||||
Integer queryCount(NoticeQueryDto noticeQueryDto);
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cn.kmbeast.mapper;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.extend.UserQueryDto;
|
||||
import cn.kmbeast.pojo.entity.User;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户持久化接口
|
||||
*/
|
||||
public interface UserMapper {
|
||||
|
||||
|
||||
/**
|
||||
* 用户信息新增
|
||||
*
|
||||
* @param userInsert 用户信息
|
||||
* @return int 受影响行数
|
||||
*/
|
||||
int insert(User userInsert);
|
||||
|
||||
/**
|
||||
* 分页查询用户信息
|
||||
*
|
||||
* @param userQueryDto 分页查询参数
|
||||
* @return List<User>
|
||||
*/
|
||||
List<User> query(UserQueryDto userQueryDto);
|
||||
|
||||
/**
|
||||
* 查询满足分页查询的记录总数
|
||||
*
|
||||
* @param userQueryDto 分页查询参数
|
||||
* @return int 数据总数
|
||||
*/
|
||||
int queryCount(UserQueryDto userQueryDto);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param user 用户信息
|
||||
* @return int 受影响行数
|
||||
*/
|
||||
int update(User user);
|
||||
|
||||
/**
|
||||
* 批量删除用户信息
|
||||
*
|
||||
* @param ids 用户ID集合
|
||||
*/
|
||||
void batchDelete(@Param(value = "ids") List<Integer> ids);
|
||||
|
||||
/**
|
||||
* 根据不为空的查询信息查找用户
|
||||
*
|
||||
* @param user 参数
|
||||
* @return User
|
||||
*/
|
||||
User getByActive(User user);
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package cn.kmbeast.pojo.api;
|
||||
|
||||
/**
|
||||
* 响应基类
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class Result<T> {
|
||||
/**
|
||||
* 响应状态码
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result{" +
|
||||
"code=" + code +
|
||||
", msg='" + msg + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(Integer code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package cn.kmbeast.pojo.api;
|
||||
|
||||
/**
|
||||
* 请求响应码
|
||||
*/
|
||||
public enum ResultCode {
|
||||
/**
|
||||
* 请求成功码
|
||||
*/
|
||||
REQUEST_SUCCESS(200),
|
||||
/**
|
||||
* 请求失败码
|
||||
*/
|
||||
REQUEST_ERROR(400);
|
||||
|
||||
private Integer code;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultCode{" +
|
||||
"code=" + code +
|
||||
'}';
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
ResultCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 自动组卷配置接收类
|
||||
*/
|
||||
@Data
|
||||
public class AutoCreatePaper {
|
||||
/**
|
||||
* 生成的单选题
|
||||
*/
|
||||
private Integer oneSelected;
|
||||
/**
|
||||
* 生成的多选题
|
||||
*/
|
||||
private Integer doubleSelected;
|
||||
/**
|
||||
* 生成的填空题
|
||||
*/
|
||||
private Integer putWord;
|
||||
/**
|
||||
* 生成的判断题
|
||||
*/
|
||||
private Integer judgeSelected;
|
||||
/**
|
||||
* 科目ID
|
||||
*/
|
||||
private Integer projectId;
|
||||
/**
|
||||
* 试卷ID
|
||||
*/
|
||||
private Integer paperId;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 评论查询参数Dto
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class EvaluationsQueryDto extends QueryDto {
|
||||
|
||||
/**
|
||||
* 内容类型
|
||||
*/
|
||||
private String contentType;
|
||||
/**
|
||||
* 评论的内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class NoticeQueryDto extends QueryDto {
|
||||
private String name;
|
||||
private String content;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PaperPracticeQueryDto extends QueryDto {
|
||||
private Integer id;
|
||||
private Integer paperId;
|
||||
private Integer practiceId;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PaperQueryDto extends QueryDto {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Integer projectId;
|
||||
private Integer userId;
|
||||
private String detail;
|
||||
private Long limitTime;
|
||||
private Integer totalScore;
|
||||
private Boolean isShow;
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PracticeQueryDto extends QueryDto {
|
||||
private Integer id;
|
||||
private String askItem;
|
||||
private Integer projectId;
|
||||
private Integer practiceType;
|
||||
private Integer userId;
|
||||
private String detail;
|
||||
private Integer score;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ProjectTypeQueryDto extends QueryDto {
|
||||
private String name;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class TestHistoryQueryDto extends QueryDto {
|
||||
private Integer id;
|
||||
private String answer;
|
||||
private Integer practiceId;
|
||||
private Integer paperId;
|
||||
private Integer userId;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.kmbeast.pojo.dto.query.extend;
|
||||
|
||||
import cn.kmbeast.pojo.dto.query.base.QueryDto;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 用户查询DTO参数
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class UserQueryDto extends QueryDto {
|
||||
/**
|
||||
* 用户的帐号
|
||||
*/
|
||||
private String userAccount;
|
||||
/**
|
||||
* 用户的名称
|
||||
*/
|
||||
private String userName;
|
||||
/**
|
||||
* 用户的邮箱
|
||||
*/
|
||||
private String userEmail;
|
||||
/**
|
||||
* 用户的角色
|
||||
*/
|
||||
private Boolean role;
|
||||
/**
|
||||
* 是否可以登录
|
||||
*/
|
||||
private Boolean isLogin;
|
||||
/**
|
||||
* 是否被禁言
|
||||
*/
|
||||
private Boolean isWord;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.kmbeast.pojo.dto.save;
|
||||
|
||||
import cn.kmbeast.pojo.entity.TestHistory;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 考试参数接收类
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class TestHistoryDto extends TestHistory {
|
||||
|
||||
/**
|
||||
* 考试开始时间
|
||||
*/
|
||||
private Long startTime;
|
||||
|
||||
/**
|
||||
* 考试结束时间
|
||||
*/
|
||||
private Long endTime;
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.kmbeast.pojo.dto.update;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserLoginDTO {
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String userAccount;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String userPwd;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.kmbeast.pojo.dto.update;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserRegisterDTO {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String userName;
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String userAccount;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String userPwd;
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
private String userEmail;
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.kmbeast.pojo.dto.update;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserUpdateDTO {
|
||||
/**
|
||||
* 用户账号
|
||||
*/
|
||||
private String userAccount;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
private String userPwd;
|
||||
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String userAvatar;
|
||||
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
private String userEmail;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AuditStatusEnum {
|
||||
|
||||
NO_AUDIT(1, "未审核"),
|
||||
HAVE_AUDIT(2, "已经审核");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态解释
|
||||
*/
|
||||
private final String detail;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum IsReadEnum {
|
||||
|
||||
READ_OK(true, "已读"),
|
||||
READ_NO(false, "未读");
|
||||
|
||||
/**
|
||||
* 是否已读
|
||||
*/
|
||||
private final Boolean status;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String detail;
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 登录状态枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginStatusEnum {
|
||||
|
||||
USE(false, "可登录"),
|
||||
BANK_USE(true, "登录状态异常");
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private final Boolean flag;
|
||||
/**
|
||||
* 名成
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum MessageType {
|
||||
|
||||
// 互动消息(评论被被人回复、评论被别人点赞)、指标消息、系统通知类
|
||||
EVALUATIONS_BY_REPLY(1,"评论"),
|
||||
EVALUATIONS_BY_UPVOTE(2,"点赞"),
|
||||
DATA_MESSAGE(3,"指标提醒"),
|
||||
SYSTEM_INFO(4,"系统通知");
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
/**
|
||||
* 消息类型的备注
|
||||
*/
|
||||
private final String detail;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 题型枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PracticeTypeEnum {
|
||||
|
||||
ONE_SELECTED(1, "单选题"),
|
||||
DOUBLE_SELECTED(2, "多选题"),
|
||||
WORD_PUT(3, "填空题"),
|
||||
JUDGEMENT(4, "判断题");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String detail;
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户角色枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RoleEnum {
|
||||
|
||||
ADMIN(1, "管理员"),
|
||||
USER(2, "用户");
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
private final Integer role;
|
||||
/**
|
||||
* 角色名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 由角色编码获取角色名
|
||||
*
|
||||
* @param role 角色编码
|
||||
* @return String 角色名
|
||||
*/
|
||||
public static String ROLE(Integer role) {
|
||||
for (RoleEnum value : RoleEnum.values()) {
|
||||
if (value.getRole().equals(role)) {
|
||||
return value.name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.kmbeast.pojo.em;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 禁言状态枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum WordStatusEnum {
|
||||
|
||||
USE(false, "可用"),
|
||||
BANK_USE(true, "禁言状态");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Boolean flag;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cn.kmbeast.pojo.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 评论实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class Evaluations {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 父级评论ID
|
||||
*/
|
||||
private Integer parentId;
|
||||
|
||||
/**
|
||||
* 评论者ID
|
||||
*/
|
||||
private Integer commenterId;
|
||||
|
||||
/**
|
||||
* 回复者ID
|
||||
*/
|
||||
private Integer replierId;
|
||||
|
||||
/**
|
||||
* 内容类型
|
||||
*/
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 内容ID
|
||||
*/
|
||||
private Integer contentId;
|
||||
|
||||
/**
|
||||
* 点赞信息列表(以","分割)
|
||||
*/
|
||||
private String upvoteList;
|
||||
|
||||
/**
|
||||
* 评论时间
|
||||
*/
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.kmbeast.pojo.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 公告
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Notice {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String content;
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue