完善博客评论功能
This commit is contained in:
46
Server/src/APIs/BlogComment.ts
Normal file
46
Server/src/APIs/BlogComment.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { API } from "../Plugs/API/API";
|
||||||
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
|
import MySQLConnection from '../Plugs/MySQLConnection'
|
||||||
|
import MountUserAgent from "../Plugs/Middleware/mountUserAgent";
|
||||||
|
import axios from "axios";
|
||||||
|
import MountIP from "../Plugs/Middleware/mountIP";
|
||||||
|
|
||||||
|
|
||||||
|
// 提交博客评论
|
||||||
|
class BlogComment extends API {
|
||||||
|
constructor() {
|
||||||
|
super('POST', '/blogComment', MountUserAgent, MountIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onRequset(data: any, res: any) {
|
||||||
|
let { bloguuid, content, name, _userAgent, _ip } = data;
|
||||||
|
if (!bloguuid || bloguuid.length != 32 || typeof content != 'string' || typeof name != 'string'
|
||||||
|
|| content.trim() == '' || name.trim() == '') {
|
||||||
|
return res.json(ServerStdResponse.INVALID_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理数据
|
||||||
|
content = content.trim();
|
||||||
|
name = name.trim();
|
||||||
|
_ip = (_ip as string).replace('::ffff:', '');
|
||||||
|
// 获取IPAddress
|
||||||
|
let ip_address = '未知'
|
||||||
|
try {
|
||||||
|
let ipAddressRes = await axios.get(`http://ip-api.com/json/${_ip}?lang=zh-CN`);
|
||||||
|
if (ipAddressRes.data && ipAddressRes.data.status == 'success') {
|
||||||
|
ip_address = ipAddressRes.data.country == '中国' ? ipAddressRes.data.city : ipAddressRes.data.country;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.warn('获取IP属地失败', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
let blogLikeRes = await MySQLConnection.execute('INSERT INTO blog_comment (uuid, content, name, ip, ip_address, user_agent, time) VALUES (?,?,?,?,?,?,?)', [bloguuid, content.trim(), name.trim(), _ip, ip_address, _userAgent, Date.now()]);
|
||||||
|
if (!blogLikeRes || blogLikeRes.affectedRows != 1) {
|
||||||
|
this.logger.error('发布博客评论时,数据库发生错误');
|
||||||
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
|
}
|
||||||
|
return res.json(ServerStdResponse.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BlogComment;
|
||||||
11
Server/src/Plugs/Middleware/MountIP.ts
Normal file
11
Server/src/Plugs/Middleware/MountIP.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express"
|
||||||
|
import Logger from "../Logger";
|
||||||
|
const logger = new Logger('MountIP')
|
||||||
|
|
||||||
|
let MountIP = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
req.body._ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.ip;
|
||||||
|
logger.info(`[${req.method}][${req.url.split('?')[0]}] IP解析成功:${req.body._ip}`);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MountIP;
|
||||||
11
Server/src/Plugs/Middleware/MountUserAgent.ts
Normal file
11
Server/src/Plugs/Middleware/MountUserAgent.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express"
|
||||||
|
import Logger from "../Logger";
|
||||||
|
const logger = new Logger('MountUserAgent')
|
||||||
|
|
||||||
|
let MountUserAgent = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
req.body._userAgent = req.headers['user-agent'];
|
||||||
|
logger.info(`[${req.method}][${req.url.split('?')[0]}] 用户代理解析成功:${req.body._userAgent}`);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MountUserAgent;
|
||||||
@@ -11,6 +11,7 @@ import GetResourceList from "../APIs/GetResourceList";
|
|||||||
import GetBlogList from "../APIs/GetBlogList";
|
import GetBlogList from "../APIs/GetBlogList";
|
||||||
import GetBlogContent from "../APIs/GetBlogContent";
|
import GetBlogContent from "../APIs/GetBlogContent";
|
||||||
import BlogLike from "../APIs/BlogLike";
|
import BlogLike from "../APIs/BlogLike";
|
||||||
|
import BlogComment from "../APIs/BlogComment";
|
||||||
import GetCaptcha from "../APIs/GetCaptcha";
|
import GetCaptcha from "../APIs/GetCaptcha";
|
||||||
import CheckCaptcha from "../APIs/CheckCaptcha";
|
import CheckCaptcha from "../APIs/CheckCaptcha";
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ class Server {
|
|||||||
this.apiLoader.add(GetBlogList);
|
this.apiLoader.add(GetBlogList);
|
||||||
this.apiLoader.add(GetBlogContent);
|
this.apiLoader.add(GetBlogContent);
|
||||||
this.apiLoader.add(BlogLike);
|
this.apiLoader.add(BlogLike);
|
||||||
|
this.apiLoader.add(BlogComment);
|
||||||
this.apiLoader.add(GetCaptcha);
|
this.apiLoader.add(GetCaptcha);
|
||||||
this.apiLoader.add(CheckCaptcha);
|
this.apiLoader.add(CheckCaptcha);
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { ElMessage } from 'element-plus';
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { request } from '@/lib/request';
|
import { request } from '@/lib/request';
|
||||||
|
import RotationVerification from '../Common/RotationVerification.vue';
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const bloguuid = route.params.uuid;
|
const bloguuid = route.params.uuid;
|
||||||
const inputComment = ref('')
|
const inputComment = ref('')
|
||||||
const inputCommentName = ref('')
|
let inputCommentName = '';
|
||||||
const commentDialogVisible = ref(false)
|
|
||||||
const toolBarVisible = ref(true);
|
const toolBarVisible = ref(true);
|
||||||
const isLiked = ref(false)
|
const isLiked = ref(false)
|
||||||
|
const isCaptchaViewShow = ref(false)
|
||||||
let lastScrollTop = 0;
|
let lastScrollTop = 0;
|
||||||
const handleScrollMove = () => {
|
const handleScrollMove = () => {
|
||||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
@@ -45,6 +46,40 @@ onUnmounted(async () => {
|
|||||||
window.removeEventListener('scroll', handleScrollMove)
|
window.removeEventListener('scroll', handleScrollMove)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const commentHandle = () => {
|
||||||
|
if (inputComment.value == '' || inputComment.value.trim() == '') {
|
||||||
|
return ElMessage.warning('请先填写留言内容哟~')
|
||||||
|
}
|
||||||
|
ElMessageBox.prompt('请输入留言昵称(可留空)', 'Tip', {
|
||||||
|
confirmButtonText: '提交',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
}).then(({ value }) => {
|
||||||
|
inputCommentName = value ? value : '';
|
||||||
|
isCaptchaViewShow.value = true;
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.info('已取消')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const submitComment = async () => {
|
||||||
|
isCaptchaViewShow.value = false;
|
||||||
|
ElMessage.info('正在提交,请稍后')
|
||||||
|
try {
|
||||||
|
let commentRes = await request.post('blogComment', {
|
||||||
|
bloguuid: bloguuid,
|
||||||
|
content: inputComment.value.trim(),
|
||||||
|
name: inputCommentName.trim() == '' ? '匿名' : inputCommentName.trim()
|
||||||
|
})
|
||||||
|
if (commentRes.code == 0) {
|
||||||
|
return ElMessage.success('评论成功~');
|
||||||
|
} else {
|
||||||
|
throw new Error(commentRes.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('评论失败', error)
|
||||||
|
ElMessage.error('遇到了一些问题,稍后再来试试吧~')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<transition name="el-zoom-in-bottom">
|
<transition name="el-zoom-in-bottom">
|
||||||
@@ -52,25 +87,14 @@ onUnmounted(async () => {
|
|||||||
<div class="tool-bar">
|
<div class="tool-bar">
|
||||||
<el-input v-model="inputComment" autosize type="textarea" class="input-comment" placeholder="快来留下你的评论吧~"
|
<el-input v-model="inputComment" autosize type="textarea" class="input-comment" placeholder="快来留下你的评论吧~"
|
||||||
clearable />
|
clearable />
|
||||||
<el-button type="primary" :icon="Edit" class="button" circle @click=""></el-button>
|
<el-button type="primary" :icon="Edit" class="button" circle @click="commentHandle"></el-button>
|
||||||
<el-button type="danger" :icon="isLiked ? StarFilled : Star" class="button" @click="likeBlog"
|
<el-button type="danger" :icon="isLiked ? StarFilled : Star" class="button" @click="likeBlog"
|
||||||
circle></el-button>
|
circle></el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog v-model="commentDialogVisible" title="提示">
|
|
||||||
<div style="display: flex;flex-direction: column;gap: 5px;">
|
|
||||||
<div>每篇文章最多可发布3条评论,确认要现在发布吗?</div>
|
|
||||||
<div>另外,您可选择留下昵称:</div>
|
|
||||||
<el-input v-model="inputCommentName" placeholder="昵称(可选)" />
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="CommentDialogVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="sendComment">确认</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
<RotationVerification v-if="isCaptchaViewShow"
|
||||||
|
@fail="() => { isCaptchaViewShow = false; ElMessage.warning('验证失败') }" @success="submitComment" />
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tool-bar-container {
|
.tool-bar-container {
|
||||||
|
|||||||
Reference in New Issue
Block a user