加入BlogContent工具栏,完成点赞功能

This commit is contained in:
2024-08-30 16:28:25 +08:00
parent 76fa835459
commit cdee1205c2
8 changed files with 151 additions and 218 deletions

View File

@@ -8,8 +8,10 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
Agreement: typeof import('./src/components/Common/Agreement.vue')['default']
BlogContentToolBar: typeof import('./src/components/Blog/BlogContentToolBar.vue')['default']
BlogToolBar: typeof import('./src/components/Blog/BlogToolBar.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElInput: typeof import('element-plus/es')['ElInput']
ElPopover: typeof import('element-plus/es')['ElPopover']

View File

@@ -11,6 +11,7 @@
"type-check": "vue-tsc --build --force"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.8",
"highlight.js": "^11.10.0",
"marked": "^14.1.0",

4
tonecn/pnpm-lock.yaml generated
View File

@@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@element-plus/icons-vue':
specifier: ^2.3.1
version: 2.3.1(vue@3.4.27)
axios:
specifier: ^1.6.8
version: 1.6.8
@@ -99,7 +102,6 @@ packages:
vue: ^3.2.0
dependencies:
vue: 3.4.27(typescript@5.4.5)
dev: true
/@esbuild/aix-ppc64@0.20.2:
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}

View File

@@ -0,0 +1,107 @@
<script setup>
import { Star, Edit, StarFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus';
import { ref, onMounted, onUnmounted } from 'vue';
import { useRoute } from 'vue-router';
import { request } from '@/lib/request';
const route = useRoute()
const bloguuid = route.params.uuid;
const inputComment = ref('')
const inputCommentName = ref('')
const commentDialogVisible = ref(false)
const toolBarVisible = ref(true);
const isLiked = ref(false)
let lastScrollTop = 0;
const handleScrollMove = () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
if (scrollTop > lastScrollTop) {
toolBarVisible.value = false;// 下滑
} else {
toolBarVisible.value = true;// 上滑
}
lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; // For Mobile or negative scrolling
}
const likeBlog = async () => {
if (isLiked.value) {
return ElMessage.success('已经点过赞啦~')
}
try {
let likeRes = await request.post('/blogLike?bloguuid=' + bloguuid)
if (likeRes.code == 0) {
isLiked.value = true;
return ElMessage.success('点赞成功~')
} else {
throw new Error(likeRes.message);
}
} catch (error) {
console.error('点赞失败', error);
return ElMessage.warning('手速太快啦~稍后再来试试吧');
}
}
onMounted(async () => {
window.addEventListener('scroll', handleScrollMove)
});
onUnmounted(async () => {
window.removeEventListener('scroll', handleScrollMove)
})
</script>
<template>
<transition name="el-zoom-in-bottom">
<div class="tool-bar-container" v-show="toolBarVisible">
<div class="tool-bar">
<el-input v-model="inputComment" autosize type="textarea" class="input-comment" placeholder="快来留下你的评论吧~"
clearable />
<el-button type="primary" :icon="Edit" class="button" circle @click=""></el-button>
<el-button type="danger" :icon="isLiked ? StarFilled : Star" class="button" @click="likeBlog"
circle></el-button>
</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>
</transition>
</template>
<style scoped>
.tool-bar-container {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
/* height: 60px; */
background-color: #ffffff;
box-shadow: 0px 0px 9px 1px #ddd;
display: flex;
justify-content: center;
align-items: center;
}
.tool-bar {
width: 100%;
max-width: 600px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px 20px;
}
.input-comment {
margin-right: 12px;
}
@media screen and (max-width: 570px) {
.button {
background-color: none;
}
}
</style>

View File

@@ -1,201 +0,0 @@
<script setup>
import { Star, Edit, StarFilled, Watch } from '@element-plus/icons-vue'
import { ref, onMounted, onUnmounted, watch } from 'vue';
import FingerprintJS from '@fingerprintjs/fingerprintjs';
import { useRoute } from 'vue-router';
import { request } from '@/lib/request';
import { ElMessage, ElMessageBox } from 'element-plus';
let showToolBar = defineModel();
const emit = defineEmits(['loadComment'])
watch(showToolBar, (newData, oldData) => {
if (newData)
isScrollingUp.value = true;
})
const route = useRoute();
const uuid = route.params.uuid;
let input_comment = ref('')
let input_comment_name = ref('')
let isStarted = ref(false)
let fgId;
let CommentDialogVisible = ref(false)
let isScrollingUp = ref(true);
onMounted(async () => {
window.addEventListener('wheel', handleWheel);
const fp = await FingerprintJS.load();
const result = await fp.get();
fgId = result.visitorId;
});
onUnmounted(() => {
window.removeEventListener('wheel', handleWheel);
});
function handleWheel(event) {
// deltaY > 0 表示向下滚动deltaY < 0 表示向上滚动
isScrollingUp.value = event.deltaY < 0;
showToolBar.value = isScrollingUp.value;
}
let blogLike = async () => {
if (isStarted.value) {
ElMessage({
message: '你已经点过赞啦!',
type: 'success',
})
return;
}
let res = await ServerAPI.async_getRequest(`BlogLike?uuid=${uuid}&fgId=${fgId}`);
try {
if (res && res.status == 'OK') {
if (res.code == 1) {
// 点赞成功
isStarted.value = true;
ElMessage({
message: '点赞成功!',
type: 'success',
})
} else if (res.code == 0) {
// 重复点赞
isStarted.value = true;
ElMessage({
message: '你已经点过赞啦!',
type: 'success',
})
}
} else {
throw new Error('点赞失败')
}
} catch (error) {
// 网络错误等原因
console.log(error)
ElMessage({
message: '点赞失败',
type: 'error',
})
}
}
// 评论按钮触发事件
let blogComment = async () => {
if (!input_comment.value) {
ElMessage({
message: '请先填写评论内容呀~',
type: 'warning'
})
return;
}
if (input_comment.value.length >= 65535) {
ElMessage({
message: '评论内容太长啦~',
type: 'warning'
})
return;
}
CommentDialogVisible.value = true;
}
// 提交评论
let sendComment = async () => {
if (input_comment_name.value.length >= 255) {
ElMessage({
message: '昵称太长啦~',
type: 'warning'
})
return;
}
let res = await ServerAPI.async_postRequest(`PostBlogComment`, {
'uuid': uuid,
'fgId': fgId,
'comment': input_comment.value,
'comment_name': input_comment_name.value ? input_comment_name.value : '匿名',
});
try {
if (res && res.status == 'OK') {
if (res.code == 1) {
// 评论成功
ElMessage({
message: '评论成功!',
type: 'success',
})
CommentDialogVisible.value = false;
// 重新加载评论区内容
emit('loadComment');
return;
} else if (res.code == 0) {
// 评论3次机会用完
ElMessage({
message: '您的评论次数已达上限',
type: 'warning',
})
CommentDialogVisible.value = false;
return;
}
}
throw new Error('评论失败')
} catch (error) {
// 网络错误等原因
console.log(error)
ElMessage({
message: '评论失败',
type: 'error',
})
CommentDialogVisible.value = false;
}
}
</script>
<template>
<transition name="el-zoom-in-bottom">
<div class="tool-bar-container" v-show="isScrollingUp">
<div class="tool-bar">
<el-input v-model="input_comment" autosize type="textarea" class="input-comment"
placeholder="快来留下你的评论吧~" clearable />
<el-button type="primary" :icon="Edit" class="button" circle @click="blogComment"></el-button>
<el-button type="danger" :icon="isStarted ? StarFilled : Star" class="button" @click="blogLike"
circle></el-button>
</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="input_comment_name" 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>
</transition>
</template>
<style scoped>
.tool-bar-container {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
/* height: 60px; */
background-color: #ffffff;
box-shadow: 0px 0px 9px 1px #ddd;
display: flex;
justify-content: center;
align-items: center;
}
.tool-bar {
width: 100%;
max-width: 600px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px 20px;
}
.input-comment {
margin-right: 12px;
}
@media screen and (max-width: 570px) {
.button {
background-color: none;
}
}
</style>

View File

@@ -7,6 +7,7 @@ import { Marked } from 'marked';
import { markedHighlight } from "marked-highlight";
import hljs from 'highlight.js';
import "highlight.js/styles/xcode.css";
import BlogContentToolBar from '@/components/Blog/BlogContentToolBar.vue';
const loadStatus = ref(0);// 0加载中 -1加载失败 -2文章不存在或不可见 1加载成功
const route = useRoute();
@@ -48,22 +49,6 @@ onMounted(async () => {
console.error('请求博客内容发生错误 ', error);
loadStatus.value = -1;
}
// 处理iframe元素
let iframes = document.querySelectorAll('iframe');
for (let i = 0; i < iframes.length; i++) {
let iframe = iframes[i];
// 创建一个新的div元素
let wrapperDiv = document.createElement('div');
// 设置新div的类名
wrapperDiv.className = 'video-container';
// 获取iframe的父元素
let parent = iframe.parentNode;
// 将新的div插入到iframe之前
parent?.insertBefore(wrapperDiv, iframe);
// 将iframe移动到新创建的div内部
wrapperDiv.appendChild(iframe);
}
})
</script>
<template>
@@ -80,6 +65,7 @@ onMounted(async () => {
<div v-html="blogContent" id="blogContentContainer"></div>
</div>
</div>
<BlogContentToolBar />
</template>
<style scoped>
.bcc {