pref: rebuild topic footer
This commit is contained in:
parent
69b964be7f
commit
1a48556048
|
@ -211,8 +211,10 @@ export default {
|
||||||
publish: 'Confirm to publish?',
|
publish: 'Confirm to publish?',
|
||||||
publishSuccess: 'Publish Successfully',
|
publishSuccess: 'Publish Successfully',
|
||||||
publishCancel: 'Cancel Publish',
|
publishCancel: 'Cancel Publish',
|
||||||
upvote:
|
upvoteTopic:
|
||||||
'Are you sure you want to upvote this topic? This will cost you 17 Moe Moe Points',
|
'Are you sure you want to upvote this topic? This will cost you 17 Moe Moe Points',
|
||||||
|
upvoteReply:
|
||||||
|
'Are you sure you want to upvote this reply? This will cost you 3 Moe Moe Points',
|
||||||
rewrite: 'Confirm to Rewrite?',
|
rewrite: 'Confirm to Rewrite?',
|
||||||
rewriteSuccess: 'Rewrite Successfully',
|
rewriteSuccess: 'Rewrite Successfully',
|
||||||
rewriteCancel: 'Cancel Rewrite',
|
rewriteCancel: 'Cancel Rewrite',
|
||||||
|
|
|
@ -210,7 +210,8 @@ export default {
|
||||||
publish: '确认发布吗?',
|
publish: '确认发布吗?',
|
||||||
publishSuccess: '发布成功',
|
publishSuccess: '发布成功',
|
||||||
publishCancel: '取消发布',
|
publishCancel: '取消发布',
|
||||||
upvote: '您确定推这个话题吗,这将会消耗您 17 萌萌点',
|
upvoteTopic: '您确定推这个话题吗,这将会消耗您 17 萌萌点',
|
||||||
|
upvoteReply: '您确定推这个回复吗,这将会消耗您 3 萌萌点',
|
||||||
rewrite: '确认 Rewrite 吗?',
|
rewrite: '确认 Rewrite 吗?',
|
||||||
rewriteSuccess: 'Rewrite 成功',
|
rewriteSuccess: 'Rewrite 成功',
|
||||||
rewriteCancel: '取消 Rewrite',
|
rewriteCancel: '取消 Rewrite',
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
这是 KUNGalgame 话题展示区域楼主话题的区域,楼主的话题将会被展示在这里,位于话题展示区域最上方
|
这是 KUNGalgame 话题展示区域楼主话题的区域,楼主的话题将会被展示在这里,位于话题展示区域最上方
|
||||||
-->
|
-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 楼主话题的 Footer
|
// Footer
|
||||||
import MasterFooter from './MasterFooter.vue'
|
import Footer from './footer/Footer.vue'
|
||||||
// 楼主话题是否重新编辑
|
// 楼主话题是否重新编辑
|
||||||
import Rewrite from '../components/Rewrite.vue'
|
import Rewrite from '../components/Rewrite.vue'
|
||||||
// 楼主的信息
|
// 楼主的信息
|
||||||
|
@ -103,17 +103,22 @@ const loliStatus = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 话题的点赞数等信息,被作用人就是该话题的发布人 -->
|
<!-- 话题的点赞数等信息,被作用人就是该话题的发布人 -->
|
||||||
<MasterFooter
|
<Footer
|
||||||
:info="{
|
:info="{
|
||||||
tid,
|
tid,
|
||||||
|
rid: 0,
|
||||||
views,
|
views,
|
||||||
likes,
|
likes,
|
||||||
dislikes,
|
dislikes,
|
||||||
upvotes,
|
upvotes,
|
||||||
share,
|
|
||||||
}"
|
}"
|
||||||
:topic="{ tid, title, content, tags, category }"
|
:content="{
|
||||||
:master="user"
|
title,
|
||||||
|
content,
|
||||||
|
tags,
|
||||||
|
category,
|
||||||
|
}"
|
||||||
|
:to-uid="user.uid"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,431 +0,0 @@
|
||||||
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
|
||||||
import { Icon } from '@iconify/vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
// 全局消息组件(底部)
|
|
||||||
import { useKUNGalgameMessageStore } from '@/store/modules/message'
|
|
||||||
// 全局消息组件(顶部)
|
|
||||||
import message from '@/components/alert/Message'
|
|
||||||
// throttle 函数
|
|
||||||
import { throttle } from '@/utils/throttle'
|
|
||||||
|
|
||||||
import { TopicUserInfo } from '@/api'
|
|
||||||
|
|
||||||
// 导入编辑界面的 store
|
|
||||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
|
||||||
// 导入话题页面 store
|
|
||||||
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
|
||||||
// 导入用户的 store
|
|
||||||
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
// 使用编辑界面的 store
|
|
||||||
const { topicRewrite } = storeToRefs(useKUNGalgameEditStore())
|
|
||||||
// 使用话题页面的 store
|
|
||||||
const { isEdit, replyDraft } = storeToRefs(useKUNGalgameTopicStore())
|
|
||||||
// 使用路由
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
// 接受父组件的传值
|
|
||||||
const props = defineProps<{
|
|
||||||
info: {
|
|
||||||
tid: number
|
|
||||||
views: number
|
|
||||||
likes: number[]
|
|
||||||
dislikes: number[]
|
|
||||||
upvotes: number[]
|
|
||||||
share: number[]
|
|
||||||
}
|
|
||||||
topic: {
|
|
||||||
tid: number
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
tags: string[]
|
|
||||||
category: string[]
|
|
||||||
}
|
|
||||||
master: TopicUserInfo
|
|
||||||
}>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 这里只是简单起见,不显示重新编辑
|
|
||||||
* 实际上如果用户自己修改了 localStorage 中保存的信息,这个验证就失效了
|
|
||||||
* 但是修改了也没有用,验证逻辑位于后端
|
|
||||||
*/
|
|
||||||
// 当前登录用户的 uid
|
|
||||||
const currUserUid = useKUNGalgameUserStore().uid
|
|
||||||
// 话题发布者的 uid
|
|
||||||
const masterUid = props.master.uid
|
|
||||||
// 是否具有重新编辑的权限
|
|
||||||
const isShowRewrite = currUserUid === masterUid
|
|
||||||
|
|
||||||
// footer 左侧的动作,点赞等
|
|
||||||
const actions = reactive({
|
|
||||||
upvotes: props.info.upvotes,
|
|
||||||
likes: props.info.likes,
|
|
||||||
dislikes: props.info.dislikes,
|
|
||||||
share: props.info.share,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 是否已经点过赞,点过踩
|
|
||||||
const isActive = reactive({
|
|
||||||
isUpvote: false,
|
|
||||||
isLiked: false,
|
|
||||||
isDisliked: false,
|
|
||||||
isShared: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
// throttle 回调函数
|
|
||||||
const throttleCallback = () => {
|
|
||||||
message(
|
|
||||||
'You can only perform one operation within 1007 milliseconds',
|
|
||||||
'您在 1007 毫秒内只能进行一次操作',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// throttle 函数,1007 毫秒仅会触发一次点赞
|
|
||||||
const handleClickLikeThrottled = throttle(
|
|
||||||
async () => {
|
|
||||||
// 当前用户不可以给自己点赞
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message(
|
|
||||||
'You cannot like your own topic',
|
|
||||||
'您不可以给自己的话题点赞',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换点赞激活状态
|
|
||||||
isActive.isLiked = !isActive.isLiked
|
|
||||||
|
|
||||||
const res = await useKUNGalgameTopicStore().updateTopicLike(
|
|
||||||
props.info.tid,
|
|
||||||
masterUid,
|
|
||||||
isActive.isLiked
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新点赞数
|
|
||||||
actions.likes.length = isActive.isLiked
|
|
||||||
? actions.likes.length + 1
|
|
||||||
: actions.likes.length - 1
|
|
||||||
} else {
|
|
||||||
message('Topic like failed!', '点赞话题失败', 'error')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1007,
|
|
||||||
throttleCallback
|
|
||||||
)
|
|
||||||
|
|
||||||
// throttle 函数,1007 毫秒仅会触发一次点踩
|
|
||||||
const handleClickDislikeThrottled = throttle(
|
|
||||||
async () => {
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message(
|
|
||||||
'You cannot dislike your own topic',
|
|
||||||
'您不可以给自己的话题点踩',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isActive.isDisliked = !isActive.isDisliked
|
|
||||||
const res = await useKUNGalgameTopicStore().updateTopicDislike(
|
|
||||||
props.info.tid,
|
|
||||||
masterUid,
|
|
||||||
isActive.isDisliked
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新点赞数
|
|
||||||
actions.dislikes.length = isActive.isDisliked
|
|
||||||
? actions.dislikes.length + 1
|
|
||||||
: actions.dislikes.length - 1
|
|
||||||
} else {
|
|
||||||
message('Topic dislike failed!', '点踩话题失败', 'error')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1007,
|
|
||||||
throttleCallback
|
|
||||||
)
|
|
||||||
|
|
||||||
// 推话题
|
|
||||||
const handleClickUpvote = async () => {
|
|
||||||
// 当前用户不可以推自己的话题
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message('You cannot upvote your own topic', '您不可以推自己的话题', 'warn')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 该功能必须有错误处理
|
|
||||||
// if (useKUNGalgameUserStore().moemoepoint < 1100) {
|
|
||||||
// message(
|
|
||||||
// 'Your Moe Moe Points are less than 1100, and you cannot use the upvote feature',
|
|
||||||
// '您的萌萌点小于 1100,无法使用推功能',
|
|
||||||
// 'warn'
|
|
||||||
// )
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 调用弹窗确认
|
|
||||||
const res = await useKUNGalgameMessageStore().alert(
|
|
||||||
'AlertInfo.edit.upvote',
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
// 这里实现用户的点击确认取消逻辑
|
|
||||||
if (res) {
|
|
||||||
// 请求推话题的接口
|
|
||||||
const res = await useKUNGalgameTopicStore().updateTopicUpvote(
|
|
||||||
props.info.tid,
|
|
||||||
masterUid
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新推数
|
|
||||||
actions.upvotes.length++
|
|
||||||
|
|
||||||
message('Topic upvote successfully', '推话题成功', 'success')
|
|
||||||
} else {
|
|
||||||
message('Topic upvote failed!', '推话题失败', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点赞
|
|
||||||
const handleClickLike = () => {
|
|
||||||
handleClickLikeThrottled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点踩
|
|
||||||
const handleClickDislike = () => {
|
|
||||||
handleClickDislikeThrottled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击回复打开回复面板
|
|
||||||
const handleClickReply = async () => {
|
|
||||||
// 保存必要信息,以便发表回复
|
|
||||||
replyDraft.value.tid = props.info.tid
|
|
||||||
// 被回复人就是发表人的 uid
|
|
||||||
replyDraft.value.to_uid = masterUid
|
|
||||||
// 楼主的 floor 就是 0
|
|
||||||
replyDraft.value.to_floor = 0
|
|
||||||
// 打开回复面板
|
|
||||||
isEdit.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新编辑
|
|
||||||
const handleClickRewrite = () => {
|
|
||||||
// 保存数据
|
|
||||||
topicRewrite.value.tid = props.topic.tid
|
|
||||||
topicRewrite.value.title = props.topic.title
|
|
||||||
topicRewrite.value.content = props.topic.content
|
|
||||||
topicRewrite.value.tags = props.topic.tags
|
|
||||||
topicRewrite.value.category = props.topic.category
|
|
||||||
|
|
||||||
// 设置正在重新编辑状态为真
|
|
||||||
topicRewrite.value.isTopicRewriting = true
|
|
||||||
|
|
||||||
// 跳转到编辑界面
|
|
||||||
router.push({ name: 'Edit' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化按钮的状态,是否已点赞等
|
|
||||||
onMounted(() => {
|
|
||||||
// 已经推过
|
|
||||||
if (props.info.upvotes.includes(currUserUid)) {
|
|
||||||
isActive.isUpvote = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经点赞
|
|
||||||
if (props.info.likes.includes(currUserUid)) {
|
|
||||||
isActive.isLiked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经点踩
|
|
||||||
if (props.info.dislikes.includes(currUserUid)) {
|
|
||||||
isActive.isDisliked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经分享
|
|
||||||
if (props.info.share.includes(currUserUid)) {
|
|
||||||
isActive.isShared = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- 楼主话题底部 -->
|
|
||||||
<div class="footer">
|
|
||||||
<!-- 底部左侧部分(点赞、推话题、踩) -->
|
|
||||||
<div class="left">
|
|
||||||
<ul>
|
|
||||||
<!-- 推话题 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isUpvote ? 'active' : ''"
|
|
||||||
@click="handleClickUpvote"
|
|
||||||
>
|
|
||||||
<Icon icon="bi:rocket" />
|
|
||||||
</span>
|
|
||||||
{{ actions.upvotes.length }}
|
|
||||||
</li>
|
|
||||||
<!-- 查看数量 -->
|
|
||||||
<li>
|
|
||||||
<span class="icon"><Icon icon="ic:outline-remove-red-eye" /></span>
|
|
||||||
{{ info.views }}
|
|
||||||
</li>
|
|
||||||
<!-- 点赞 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isLiked ? 'active' : ''"
|
|
||||||
@click="handleClickLike"
|
|
||||||
>
|
|
||||||
<Icon icon="line-md:thumbs-up-twotone" />
|
|
||||||
</span>
|
|
||||||
{{ actions.likes.length }}
|
|
||||||
</li>
|
|
||||||
<!-- 踩 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isDisliked ? 'active' : ''"
|
|
||||||
@click="handleClickDislike"
|
|
||||||
>
|
|
||||||
<Icon icon="line-md:thumbs-down-twotone" />
|
|
||||||
</span>
|
|
||||||
{{ actions.dislikes.length }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部右侧部分(回复、评论、只看、编辑) -->
|
|
||||||
<div class="right">
|
|
||||||
<div @click="handleClickReply" class="reply">
|
|
||||||
{{ $tm('topic.content.reply') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 分享 -->
|
|
||||||
<span class="icon"><Icon icon="majesticons:share-line" /></span>
|
|
||||||
|
|
||||||
<!-- 只看 -->
|
|
||||||
<span class="icon"><Icon icon="ph:user-focus-duotone" /></span>
|
|
||||||
|
|
||||||
<!-- 编辑 -->
|
|
||||||
<span v-if="isShowRewrite" @click="handleClickRewrite" class="icon">
|
|
||||||
<Icon icon="line-md:pencil-twotone-alt" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 楼主话题底部 */
|
|
||||||
.footer {
|
|
||||||
padding: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
/* 底部左侧部分(点赞、推话题、踩) */
|
|
||||||
.left ul {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--kungalgame-font-color-3);
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 17px;
|
|
||||||
margin-right: 0;
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
&:nth-child(1) span {
|
|
||||||
color: var(--kungalgame-red-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图标字体的样式 */
|
|
||||||
.icon {
|
|
||||||
font-size: 24px;
|
|
||||||
color: var(--kungalgame-font-color-2);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 底部右侧部分(回复、评论、只看、编辑) */
|
|
||||||
.right {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
margin-right: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply {
|
|
||||||
position: relative;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--kungalgame-blue-4);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
color: var(--kungalgame-pink-4);
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
transition: width 0.2s, height 0.2s, border-bottom-color 0s;
|
|
||||||
transition-delay: 0.2s, 0s, 0.2s;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
border-left: 2px solid var(--kungalgame-pink-4);
|
|
||||||
border-bottom: 2px solid var(--kungalgame-pink-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
transition: width 0.2s, height 0.2s, border-right-color 0.2s;
|
|
||||||
transition-delay: 0s, 0.2s, 0.2s;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
border-top: 2px solid var(--kungalgame-pink-4);
|
|
||||||
border-right: 2px solid var(--kungalgame-pink-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 激活后的样式 */
|
|
||||||
.active {
|
|
||||||
color: var(--kungalgame-blue-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
.footer {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
147
src/views/topic/components/footer/Dislike.vue
Normal file
147
src/views/topic/components/footer/Dislike.vue
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { Icon } from '@iconify/vue'
|
||||||
|
// 全局消息组件(顶部)
|
||||||
|
import message from '@/components/alert/Message'
|
||||||
|
// throttle 函数
|
||||||
|
import { throttle } from '@/utils/throttle'
|
||||||
|
|
||||||
|
// 导入话题页面 store
|
||||||
|
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
||||||
|
|
||||||
|
// 接受父组件的传值
|
||||||
|
const props = defineProps<{
|
||||||
|
uid: number
|
||||||
|
tid: number
|
||||||
|
rid: number
|
||||||
|
dislikes: number[]
|
||||||
|
toUid: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const dislikesArray = ref(props.dislikes)
|
||||||
|
const isDisliked = ref(false)
|
||||||
|
|
||||||
|
// throttle 回调函数
|
||||||
|
const throttleCallback = () => {
|
||||||
|
message(
|
||||||
|
'You can only perform one operation within 1007 milliseconds',
|
||||||
|
'您在 1007 毫秒内只能进行一次操作',
|
||||||
|
'warn'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点踩
|
||||||
|
const dislikeOperation = async (
|
||||||
|
tid: number,
|
||||||
|
rid: number,
|
||||||
|
toUid: number,
|
||||||
|
isPush: boolean
|
||||||
|
) => {
|
||||||
|
// rid 为零表示点赞的是楼主的话题
|
||||||
|
const isMasterTopic = rid === 0
|
||||||
|
if (isMasterTopic) {
|
||||||
|
return await useKUNGalgameTopicStore().updateTopicDislike(
|
||||||
|
tid,
|
||||||
|
toUid,
|
||||||
|
isPush
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return await useKUNGalgameTopicStore().updateReplyDislike(
|
||||||
|
tid,
|
||||||
|
toUid,
|
||||||
|
rid,
|
||||||
|
isPush
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// throttle 函数,1007 毫秒仅会触发一次点踩
|
||||||
|
const handleClickDislikeThrottled = throttle(
|
||||||
|
async () => {
|
||||||
|
// 当前用户不可以给自己点赞
|
||||||
|
if (props.uid === props.toUid) {
|
||||||
|
message('You cannot dislike yourself', '您不可以给自己点踩', 'warn')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换点赞激活状态
|
||||||
|
isDisliked.value = !isDisliked.value
|
||||||
|
|
||||||
|
const res = await dislikeOperation(
|
||||||
|
props.tid,
|
||||||
|
props.rid,
|
||||||
|
props.toUid,
|
||||||
|
isDisliked.value
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 更新点踩数
|
||||||
|
dislikesArray.value.length = isDisliked.value
|
||||||
|
? dislikesArray.value.length + 1
|
||||||
|
: dislikesArray.value.length - 1
|
||||||
|
} else {
|
||||||
|
message('Dislike failed!', '点踩失败', 'error')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1007,
|
||||||
|
throttleCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
// 点踩
|
||||||
|
const handleClickDislike = () => {
|
||||||
|
handleClickDislikeThrottled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化按钮的状态
|
||||||
|
onMounted(() => {
|
||||||
|
// 已经点踩
|
||||||
|
if (props.dislikes.includes(props.uid)) {
|
||||||
|
isDisliked.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 踩 -->
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
:class="isDisliked ? 'active' : ''"
|
||||||
|
@click="handleClickDislike"
|
||||||
|
>
|
||||||
|
<Icon icon="line-md:thumbs-down-twotone" />
|
||||||
|
</span>
|
||||||
|
{{ dislikes.length }}
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 17px;
|
||||||
|
margin-right: 0;
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
&:nth-child(1) span {
|
||||||
|
color: var(--kungalgame-red-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标字体的样式 */
|
||||||
|
.icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--kungalgame-font-color-2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激活后的样式 */
|
||||||
|
.active {
|
||||||
|
color: var(--kungalgame-blue-4);
|
||||||
|
}
|
||||||
|
</style>
|
263
src/views/topic/components/footer/Footer.vue
Normal file
263
src/views/topic/components/footer/Footer.vue
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Icon } from '@iconify/vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
// 推组件
|
||||||
|
import Upvote from './Upvote.vue'
|
||||||
|
// 点赞组件
|
||||||
|
import Like from './Like.vue'
|
||||||
|
// 踩组件
|
||||||
|
import Dislike from './Dislike.vue'
|
||||||
|
|
||||||
|
// 导入编辑界面的 store
|
||||||
|
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||||
|
// 导入话题页面 store
|
||||||
|
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
||||||
|
// 导入用户的 store
|
||||||
|
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
// 使用编辑界面的 store
|
||||||
|
const { topicRewrite } = storeToRefs(useKUNGalgameEditStore())
|
||||||
|
// 使用话题页面的 store
|
||||||
|
const { isEdit, replyDraft } = storeToRefs(useKUNGalgameTopicStore())
|
||||||
|
// 使用路由
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 接受父组件的传值
|
||||||
|
const props = defineProps<{
|
||||||
|
info: {
|
||||||
|
tid: number
|
||||||
|
rid: number
|
||||||
|
views: number
|
||||||
|
likes: number[]
|
||||||
|
dislikes: number[]
|
||||||
|
upvotes: number[]
|
||||||
|
}
|
||||||
|
content: {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
tags: string[]
|
||||||
|
category: string[]
|
||||||
|
}
|
||||||
|
toUid: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这里只是简单起见,不显示重新编辑
|
||||||
|
* 实际上如果用户自己修改了 localStorage 中保存的信息,这个验证就失效了
|
||||||
|
* 但是修改了也没有用,验证逻辑位于后端
|
||||||
|
*/
|
||||||
|
// 当前登录用户的 uid
|
||||||
|
const currUserUid = useKUNGalgameUserStore().uid
|
||||||
|
// 话题 / 回复发布者的 uid
|
||||||
|
const masterUid = props.toUid
|
||||||
|
// 是否具有重新编辑的权限
|
||||||
|
const isShowRewrite = currUserUid === masterUid
|
||||||
|
|
||||||
|
// 点击回复打开回复面板
|
||||||
|
const handleClickReply = async () => {
|
||||||
|
// 保存必要信息,以便发表回复
|
||||||
|
replyDraft.value.tid = props.info.tid
|
||||||
|
// 被回复人就是发表人的 uid
|
||||||
|
replyDraft.value.to_uid = masterUid
|
||||||
|
// 楼主的 floor 就是 0
|
||||||
|
replyDraft.value.to_floor = 0
|
||||||
|
// 打开回复面板
|
||||||
|
isEdit.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新编辑
|
||||||
|
const handleClickRewrite = () => {
|
||||||
|
// 保存数据
|
||||||
|
topicRewrite.value.tid = props.info.tid
|
||||||
|
topicRewrite.value.title = props.content.title
|
||||||
|
topicRewrite.value.content = props.content.content
|
||||||
|
topicRewrite.value.tags = props.content.tags
|
||||||
|
topicRewrite.value.category = props.content.category
|
||||||
|
|
||||||
|
// 设置正在重新编辑状态为真
|
||||||
|
topicRewrite.value.isTopicRewriting = true
|
||||||
|
|
||||||
|
// 跳转到编辑界面
|
||||||
|
router.push({ name: 'Edit' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 楼主话题底部 -->
|
||||||
|
<div class="footer">
|
||||||
|
<!-- 底部左侧部分(点赞、推话题、踩) -->
|
||||||
|
<div class="left">
|
||||||
|
<ul>
|
||||||
|
<!-- 推话题 -->
|
||||||
|
<Upvote
|
||||||
|
:uid="currUserUid"
|
||||||
|
:tid="props.info.tid"
|
||||||
|
:rid="props.info.rid"
|
||||||
|
:upvotes="props.info.upvotes"
|
||||||
|
:to-uid="masterUid"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 浏览数 -->
|
||||||
|
<li>
|
||||||
|
<span class="icon"><Icon icon="ic:outline-remove-red-eye" /></span>
|
||||||
|
{{ info.views }}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- 点赞 -->
|
||||||
|
<Like
|
||||||
|
:uid="currUserUid"
|
||||||
|
:tid="props.info.tid"
|
||||||
|
:rid="props.info.rid"
|
||||||
|
:likes="props.info.likes"
|
||||||
|
:to-uid="masterUid"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 踩 -->
|
||||||
|
<Dislike
|
||||||
|
:uid="currUserUid"
|
||||||
|
:tid="props.info.tid"
|
||||||
|
:rid="props.info.rid"
|
||||||
|
:dislikes="props.info.dislikes"
|
||||||
|
:to-uid="masterUid"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部右侧部分(回复、评论、只看、编辑) -->
|
||||||
|
<div class="right">
|
||||||
|
<div @click="handleClickReply" class="reply">
|
||||||
|
{{ $tm('topic.content.reply') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分享 -->
|
||||||
|
<span class="icon"><Icon icon="majesticons:share-line" /></span>
|
||||||
|
|
||||||
|
<!-- 只看 -->
|
||||||
|
<span class="icon"><Icon icon="ph:user-focus-duotone" /></span>
|
||||||
|
|
||||||
|
<!-- 编辑 -->
|
||||||
|
<span v-if="isShowRewrite" @click="handleClickRewrite" class="icon">
|
||||||
|
<Icon icon="line-md:pencil-twotone-alt" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 回复的插槽 -->
|
||||||
|
<span>
|
||||||
|
<slot name="comment"></slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* 楼主话题底部 */
|
||||||
|
.footer {
|
||||||
|
padding: 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
/* 底部左侧部分(点赞、推话题、踩) */
|
||||||
|
.left ul {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--kungalgame-font-color-3);
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 17px;
|
||||||
|
margin-right: 0;
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
&:nth-child(1) span {
|
||||||
|
color: var(--kungalgame-red-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标字体的样式 */
|
||||||
|
.icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--kungalgame-font-color-2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部右侧部分(回复、评论、只看、编辑) */
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply {
|
||||||
|
position: relative;
|
||||||
|
width: 70px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--kungalgame-blue-4);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: var(--kungalgame-pink-4);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
transition: width 0.2s, height 0.2s, border-bottom-color 0s;
|
||||||
|
transition-delay: 0.2s, 0s, 0.2s;
|
||||||
|
width: 70px;
|
||||||
|
height: 30px;
|
||||||
|
border-left: 2px solid var(--kungalgame-pink-4);
|
||||||
|
border-bottom: 2px solid var(--kungalgame-pink-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
transition: width 0.2s, height 0.2s, border-right-color 0.2s;
|
||||||
|
transition-delay: 0s, 0.2s, 0.2s;
|
||||||
|
width: 70px;
|
||||||
|
height: 30px;
|
||||||
|
border-top: 2px solid var(--kungalgame-pink-4);
|
||||||
|
border-right: 2px solid var(--kungalgame-pink-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激活后的样式 */
|
||||||
|
.active {
|
||||||
|
color: var(--kungalgame-blue-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.footer {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
span:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
143
src/views/topic/components/footer/Like.vue
Normal file
143
src/views/topic/components/footer/Like.vue
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { Icon } from '@iconify/vue'
|
||||||
|
// 全局消息组件(顶部)
|
||||||
|
import message from '@/components/alert/Message'
|
||||||
|
// throttle 函数
|
||||||
|
import { throttle } from '@/utils/throttle'
|
||||||
|
|
||||||
|
// 导入话题页面 store
|
||||||
|
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
||||||
|
|
||||||
|
// 接受父组件的传值
|
||||||
|
const props = defineProps<{
|
||||||
|
uid: number
|
||||||
|
tid: number
|
||||||
|
rid: number
|
||||||
|
likes: number[]
|
||||||
|
toUid: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const likesArray = ref(props.likes)
|
||||||
|
const isLiked = ref(false)
|
||||||
|
|
||||||
|
// throttle 回调函数
|
||||||
|
const throttleCallback = () => {
|
||||||
|
message(
|
||||||
|
'You can only perform one operation within 1007 milliseconds',
|
||||||
|
'您在 1007 毫秒内只能进行一次操作',
|
||||||
|
'warn'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点赞
|
||||||
|
const likeOperation = async (
|
||||||
|
tid: number,
|
||||||
|
rid: number,
|
||||||
|
toUid: number,
|
||||||
|
isPush: boolean
|
||||||
|
) => {
|
||||||
|
// rid 为零表示点赞的是楼主的话题
|
||||||
|
const isMasterTopic = rid === 0
|
||||||
|
if (isMasterTopic) {
|
||||||
|
return await useKUNGalgameTopicStore().updateTopicLike(tid, toUid, isPush)
|
||||||
|
} else {
|
||||||
|
return await useKUNGalgameTopicStore().updateReplyLike(
|
||||||
|
tid,
|
||||||
|
toUid,
|
||||||
|
rid,
|
||||||
|
isPush
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// throttle 函数,1007 毫秒仅会触发一次点赞
|
||||||
|
const handleClickLikeThrottled = throttle(
|
||||||
|
async () => {
|
||||||
|
// 当前用户不可以给自己点赞
|
||||||
|
if (props.uid === props.toUid) {
|
||||||
|
message('You cannot like yourself', '您不可以给自己点赞', 'warn')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换点赞激活状态
|
||||||
|
isLiked.value = !isLiked.value
|
||||||
|
|
||||||
|
const res = await likeOperation(
|
||||||
|
props.tid,
|
||||||
|
props.rid,
|
||||||
|
props.toUid,
|
||||||
|
isLiked.value
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 更新点赞数
|
||||||
|
likesArray.value.length = isLiked.value
|
||||||
|
? likesArray.value.length + 1
|
||||||
|
: likesArray.value.length - 1
|
||||||
|
} else {
|
||||||
|
message('Like failed!', '点赞失败', 'error')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1007,
|
||||||
|
throttleCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
// 点赞
|
||||||
|
const handleClickLike = () => {
|
||||||
|
handleClickLikeThrottled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化按钮的状态,是否已点赞等
|
||||||
|
onMounted(() => {
|
||||||
|
// 已经点赞
|
||||||
|
if (props.likes.includes(props.uid)) {
|
||||||
|
isLiked.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 点赞 -->
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
:class="isLiked ? 'active' : ''"
|
||||||
|
@click="handleClickLike"
|
||||||
|
>
|
||||||
|
<Icon icon="line-md:thumbs-up-twotone" />
|
||||||
|
</span>
|
||||||
|
{{ likesArray.length }}
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 17px;
|
||||||
|
margin-right: 0;
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
&:nth-child(1) span {
|
||||||
|
color: var(--kungalgame-red-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标字体的样式 */
|
||||||
|
.icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--kungalgame-font-color-2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激活后的样式 */
|
||||||
|
.active {
|
||||||
|
color: var(--kungalgame-blue-4);
|
||||||
|
}
|
||||||
|
</style>
|
146
src/views/topic/components/footer/Upvote.vue
Normal file
146
src/views/topic/components/footer/Upvote.vue
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { Icon } from '@iconify/vue'
|
||||||
|
// 全局消息组件(底部)
|
||||||
|
import { useKUNGalgameMessageStore } from '@/store/modules/message'
|
||||||
|
// 全局消息组件(顶部)
|
||||||
|
import message from '@/components/alert/Message'
|
||||||
|
// 导入话题页面 store
|
||||||
|
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
||||||
|
|
||||||
|
// 接受父组件的传值
|
||||||
|
const props = defineProps<{
|
||||||
|
uid: number
|
||||||
|
tid: number
|
||||||
|
rid: number
|
||||||
|
upvotes: number[]
|
||||||
|
toUid: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const upvotesArray = ref(props.upvotes)
|
||||||
|
const isUpvote = ref(false)
|
||||||
|
|
||||||
|
// 推话题
|
||||||
|
const upvoteTopic = async () => {
|
||||||
|
// 调用弹窗确认
|
||||||
|
const res = await useKUNGalgameMessageStore().alert(
|
||||||
|
'AlertInfo.edit.upvoteTopic',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
// 这里实现用户的点击确认取消逻辑
|
||||||
|
if (res) {
|
||||||
|
// 请求推话题的接口
|
||||||
|
const res = await useKUNGalgameTopicStore().updateTopicUpvote(
|
||||||
|
props.tid,
|
||||||
|
props.toUid
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 更新推数
|
||||||
|
upvotesArray.value.length++
|
||||||
|
|
||||||
|
message('Topic upvote successfully', '推话题成功', 'success')
|
||||||
|
} else {
|
||||||
|
message('Topic upvote failed!', '推话题失败', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推回复
|
||||||
|
const upvoteReply = async () => {
|
||||||
|
// 调用弹窗确认
|
||||||
|
const res = await useKUNGalgameMessageStore().alert(
|
||||||
|
'AlertInfo.edit.upvoteReply',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
// 这里实现用户的点击确认取消逻辑
|
||||||
|
if (res) {
|
||||||
|
// 请求推话题的接口
|
||||||
|
const res = await useKUNGalgameTopicStore().updateReplyUpvote(
|
||||||
|
props.tid,
|
||||||
|
props.toUid,
|
||||||
|
props.rid
|
||||||
|
)
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 更新推数
|
||||||
|
upvotesArray.value.length++
|
||||||
|
|
||||||
|
message('Reply upvote successfully', '推回复成功', 'success')
|
||||||
|
} else {
|
||||||
|
message('Reply upvote failed!', '推回复失败', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推
|
||||||
|
const handleClickUpvote = async () => {
|
||||||
|
// 当前用户不可以推自己
|
||||||
|
if (props.uid === props.toUid) {
|
||||||
|
message('You cannot upvote your own topic', '您不可以推自己的话题', 'warn')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为零是楼主的话题
|
||||||
|
if (props.rid === 0) {
|
||||||
|
upvoteTopic()
|
||||||
|
} else {
|
||||||
|
upvoteReply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化按钮的状态
|
||||||
|
onMounted(() => {
|
||||||
|
// 已经推过
|
||||||
|
if (props.upvotes.includes(props.uid)) {
|
||||||
|
isUpvote.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 推话题 -->
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
:class="isUpvote ? 'active' : ''"
|
||||||
|
@click="handleClickUpvote"
|
||||||
|
>
|
||||||
|
<Icon icon="bi:rocket" />
|
||||||
|
</span>
|
||||||
|
{{ upvotes.length }}
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 17px;
|
||||||
|
margin-right: 0;
|
||||||
|
span {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
&:nth-child(1) span {
|
||||||
|
color: var(--kungalgame-red-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标字体的样式 */
|
||||||
|
.icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--kungalgame-font-color-2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 激活后的样式 */
|
||||||
|
.active {
|
||||||
|
color: var(--kungalgame-blue-4);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -9,7 +9,7 @@ import { Icon } from '@iconify/vue'
|
||||||
// 导入评论组件
|
// 导入评论组件
|
||||||
import Comments from '../comment/Comments.vue'
|
import Comments from '../comment/Comments.vue'
|
||||||
// 导入 Footer 组件
|
// 导入 Footer 组件
|
||||||
import ReplyFooter from './ReplyFooter.vue'
|
import Footer from '../footer/Footer.vue'
|
||||||
// 导入发布时间组件
|
// 导入发布时间组件
|
||||||
import Time from '../Time.vue'
|
import Time from '../Time.vue'
|
||||||
// 导入标签组件
|
// 导入标签组件
|
||||||
|
@ -97,25 +97,31 @@ const handleClickComment = (rid: number) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 其它人回复的底部 -->
|
<!-- 回复没有浏览数,所以是 0,占位 -->
|
||||||
<!-- info 代表 footer 的点赞等信息,master 表示当前的回复用户信息 -->
|
<!-- title 和 category 也没有,占位 -->
|
||||||
<!-- 这里传入了回复的插槽,因为只有回复可以被评论 -->
|
<Footer
|
||||||
<ReplyFooter
|
|
||||||
:info="{
|
:info="{
|
||||||
|
tid: reply.tid,
|
||||||
rid: reply.rid,
|
rid: reply.rid,
|
||||||
|
views: 0,
|
||||||
likes: reply.likes,
|
likes: reply.likes,
|
||||||
dislikes: reply.dislikes,
|
dislikes: reply.dislikes,
|
||||||
upvotes: reply.upvotes,
|
upvotes: reply.upvotes,
|
||||||
to_floor: reply.floor,
|
|
||||||
}"
|
}"
|
||||||
:master="reply.r_user"
|
:content="{
|
||||||
|
title: '',
|
||||||
|
content: reply.content,
|
||||||
|
tags: reply.tags,
|
||||||
|
category: [],
|
||||||
|
}"
|
||||||
|
:to-uid="reply.r_user.uid"
|
||||||
>
|
>
|
||||||
<template #comment>
|
<template #comment>
|
||||||
<span @click="handleClickComment(reply.rid)" class="icon">
|
<span @click="handleClickComment(reply.rid)" class="icon">
|
||||||
<Icon icon="fa-regular:comment-dots" />
|
<Icon icon="fa-regular:comment-dots" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</ReplyFooter>
|
</Footer>
|
||||||
|
|
||||||
<!-- 评论区域 -->
|
<!-- 评论区域 -->
|
||||||
<Comments
|
<Comments
|
||||||
|
|
|
@ -1,417 +0,0 @@
|
||||||
<!-- 话题的底部区域,推话题,回复,点赞等 -->
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { Icon } from '@iconify/vue'
|
|
||||||
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
// 全局消息组件(底部)
|
|
||||||
import { useKUNGalgameMessageStore } from '@/store/modules/message'
|
|
||||||
// 全局消息组件(顶部)
|
|
||||||
import message from '@/components/alert/Message'
|
|
||||||
// throttle 函数
|
|
||||||
import { throttle } from '@/utils/throttle'
|
|
||||||
|
|
||||||
import { TopicUserInfo } from '@/api'
|
|
||||||
|
|
||||||
// 导入编辑界面的 store
|
|
||||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
|
||||||
// 导入话题页面 store
|
|
||||||
import { useKUNGalgameTopicStore } from '@/store/modules/topic'
|
|
||||||
// 导入用户的 store
|
|
||||||
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
// 当前的话题 tid
|
|
||||||
const tid = parseInt(useRoute().params.tid as string)
|
|
||||||
|
|
||||||
// 使用话题页面的 store
|
|
||||||
const topicStore = useKUNGalgameTopicStore()
|
|
||||||
const { isEdit, replyDraft } = storeToRefs(topicStore)
|
|
||||||
|
|
||||||
// 接受父组件的传值
|
|
||||||
const props = defineProps<{
|
|
||||||
info: {
|
|
||||||
rid: number
|
|
||||||
likes: number[]
|
|
||||||
dislikes: number[]
|
|
||||||
upvotes: number[]
|
|
||||||
// 被回复人的 floor
|
|
||||||
to_floor: number
|
|
||||||
}
|
|
||||||
master: TopicUserInfo
|
|
||||||
}>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 这里只是简单起见,不显示重新编辑
|
|
||||||
* 实际上如果用户自己修改了 localStorage 中保存的信息,这个验证就失效了
|
|
||||||
* 但是修改了也没有用,验证逻辑位于后端
|
|
||||||
*/
|
|
||||||
// 当前登录用户的 uid
|
|
||||||
const currUserUid = useKUNGalgameUserStore().uid
|
|
||||||
// 话题发布者的 uid
|
|
||||||
const masterUid = props.master.uid
|
|
||||||
// 是否具有重新编辑的权限
|
|
||||||
const isShowRewrite = currUserUid === masterUid
|
|
||||||
|
|
||||||
// footer 左侧的动作,点赞等
|
|
||||||
const actions = reactive({
|
|
||||||
upvotes: props.info.upvotes,
|
|
||||||
likes: props.info.likes,
|
|
||||||
dislikes: props.info.dislikes,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 是否已经点过赞,点过踩
|
|
||||||
const isActive = reactive({
|
|
||||||
isUpvote: false,
|
|
||||||
isLiked: false,
|
|
||||||
isDisliked: false,
|
|
||||||
isShared: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
// throttle 回调函数
|
|
||||||
const throttleCallback = () => {
|
|
||||||
message(
|
|
||||||
'You can only perform one operation within 1007 milliseconds',
|
|
||||||
'您在 1007 毫秒内只能进行一次操作',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// throttle 函数,1007 毫秒仅会触发一次点赞
|
|
||||||
const handleClickLikeThrottled = throttle(
|
|
||||||
async () => {
|
|
||||||
// 当前用户不可以给自己点赞
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message(
|
|
||||||
'You cannot like your own reply',
|
|
||||||
'您不可以给自己的回复点赞',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换点赞激活状态
|
|
||||||
isActive.isLiked = !isActive.isLiked
|
|
||||||
|
|
||||||
const res = await useKUNGalgameTopicStore().updateReplyLike(
|
|
||||||
tid,
|
|
||||||
masterUid,
|
|
||||||
props.info.rid,
|
|
||||||
isActive.isLiked
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新点赞数
|
|
||||||
actions.likes.length = isActive.isLiked
|
|
||||||
? actions.likes.length + 1
|
|
||||||
: actions.likes.length - 1
|
|
||||||
} else {
|
|
||||||
message('Reply like failed!', '点赞回复失败', 'error')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1007,
|
|
||||||
throttleCallback
|
|
||||||
)
|
|
||||||
|
|
||||||
// throttle 函数,1007 毫秒仅会触发一次点踩
|
|
||||||
const handleClickDislikeThrottled = throttle(
|
|
||||||
async () => {
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message(
|
|
||||||
'You cannot dislike your own reply',
|
|
||||||
'您不可以给自己的回复点踩',
|
|
||||||
'warn'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isActive.isDisliked = !isActive.isDisliked
|
|
||||||
const res = await useKUNGalgameTopicStore().updateReplyDislike(
|
|
||||||
tid,
|
|
||||||
masterUid,
|
|
||||||
props.info.rid,
|
|
||||||
isActive.isDisliked
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新点赞数
|
|
||||||
actions.dislikes.length = isActive.isDisliked
|
|
||||||
? actions.dislikes.length + 1
|
|
||||||
: actions.dislikes.length - 1
|
|
||||||
} else {
|
|
||||||
message('Reply dislike failed!', '点踩回复失败', 'error')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1007,
|
|
||||||
throttleCallback
|
|
||||||
)
|
|
||||||
|
|
||||||
// 推话题
|
|
||||||
const handleClickUpvote = async () => {
|
|
||||||
// 当前用户不可以推自己的话题
|
|
||||||
if (currUserUid === masterUid) {
|
|
||||||
message('You cannot upvote your own reply', '您不可以推自己的回复', 'warn')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 该功能必须有错误处理
|
|
||||||
// if (useKUNGalgameUserStore().moemoepoint < 1100) {
|
|
||||||
// message(
|
|
||||||
// 'Your Moe Moe Points are less than 1100, and you cannot use the upvote feature',
|
|
||||||
// '您的萌萌点小于 1100,无法使用推功能',
|
|
||||||
// 'warn'
|
|
||||||
// )
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 调用弹窗确认
|
|
||||||
const res = await useKUNGalgameMessageStore().alert(
|
|
||||||
'AlertInfo.edit.upvote',
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
// 这里实现用户的点击确认取消逻辑
|
|
||||||
if (res) {
|
|
||||||
// 请求推话题的接口
|
|
||||||
const res = await useKUNGalgameTopicStore().updateReplyUpvote(
|
|
||||||
tid,
|
|
||||||
masterUid,
|
|
||||||
props.info.rid
|
|
||||||
)
|
|
||||||
|
|
||||||
if (res.code === 200) {
|
|
||||||
// 更新推数
|
|
||||||
actions.upvotes.length++
|
|
||||||
|
|
||||||
message('Topic upvote successfully', '推话题成功', 'success')
|
|
||||||
} else {
|
|
||||||
message('Topic upvote failed!', '推话题失败', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点赞
|
|
||||||
const handleClickLike = () => {
|
|
||||||
handleClickLikeThrottled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点踩
|
|
||||||
const handleClickDislike = () => {
|
|
||||||
handleClickDislikeThrottled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击回复打开回复面板
|
|
||||||
const handelReply = async () => {
|
|
||||||
// 保存必要信息,以便发表回复
|
|
||||||
replyDraft.value.tid = tid
|
|
||||||
replyDraft.value.replyUserName = props.master.name
|
|
||||||
replyDraft.value.to_uid = props.master.uid
|
|
||||||
replyDraft.value.to_floor = props.info.to_floor
|
|
||||||
|
|
||||||
isEdit.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新编辑
|
|
||||||
const handleClickEdit = () => {}
|
|
||||||
|
|
||||||
// 初始化按钮的状态,是否已点赞等
|
|
||||||
onMounted(() => {
|
|
||||||
// 已经推过
|
|
||||||
if (props.info.upvotes.includes(currUserUid)) {
|
|
||||||
isActive.isUpvote = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经点赞
|
|
||||||
if (props.info.likes.includes(currUserUid)) {
|
|
||||||
isActive.isLiked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已经点踩
|
|
||||||
if (props.info.dislikes.includes(currUserUid)) {
|
|
||||||
isActive.isDisliked = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- 楼主话题底部 -->
|
|
||||||
<div class="footer">
|
|
||||||
<!-- 底部左侧部分(点赞、推话题、踩) -->
|
|
||||||
<div class="left">
|
|
||||||
<ul>
|
|
||||||
<!-- 推话题 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isUpvote ? 'active' : ''"
|
|
||||||
@click="handleClickUpvote"
|
|
||||||
>
|
|
||||||
<Icon icon="bi:rocket" />
|
|
||||||
</span>
|
|
||||||
{{ actions.upvotes.length }}
|
|
||||||
</li>
|
|
||||||
<!-- 点赞 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isLiked ? 'active' : ''"
|
|
||||||
@click="handleClickLike"
|
|
||||||
>
|
|
||||||
<Icon icon="line-md:thumbs-up-twotone" />
|
|
||||||
</span>
|
|
||||||
{{ actions.likes.length }}
|
|
||||||
</li>
|
|
||||||
<!-- 踩 -->
|
|
||||||
<li>
|
|
||||||
<span
|
|
||||||
class="icon"
|
|
||||||
:class="isActive.isDisliked ? 'active' : ''"
|
|
||||||
@click="handleClickDislike"
|
|
||||||
>
|
|
||||||
<Icon icon="line-md:thumbs-down-twotone" />
|
|
||||||
</span>
|
|
||||||
{{ actions.dislikes.length }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!-- 底部右侧部分(回复、评论、只看、编辑) -->
|
|
||||||
<div class="right">
|
|
||||||
<ul>
|
|
||||||
<li @click="handelReply">
|
|
||||||
<div class="reply">
|
|
||||||
{{ $tm('topic.content.reply') }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<!-- 分享 -->
|
|
||||||
<li>
|
|
||||||
<span class="icon"><Icon icon="majesticons:share-line" /></span>
|
|
||||||
</li>
|
|
||||||
<!-- 只看 -->
|
|
||||||
<li>
|
|
||||||
<span class="icon"><Icon icon="ph:user-focus-duotone" /></span>
|
|
||||||
</li>
|
|
||||||
<!-- 编辑 -->
|
|
||||||
<li>
|
|
||||||
<span @click="handleClickEdit" class="icon">
|
|
||||||
<Icon icon="line-md:pencil-twotone-alt" />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- 回复的插槽 -->
|
|
||||||
<li>
|
|
||||||
<slot name="comment"></slot>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 楼主话题底部 */
|
|
||||||
.footer {
|
|
||||||
padding: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
/* 底部左侧部分(点赞、推话题、踩) */
|
|
||||||
.left ul {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--kungalgame-font-color-3);
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 17px;
|
|
||||||
margin-right: 0;
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
&:nth-child(1) span {
|
|
||||||
color: var(--kungalgame-red-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图标字体的样式 */
|
|
||||||
.icon {
|
|
||||||
font-size: 24px;
|
|
||||||
color: var(--kungalgame-font-color-2);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
/* 底部右侧部分(回复、评论、只看、编辑) */
|
|
||||||
.right ul {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-left: 10px;
|
|
||||||
li {
|
|
||||||
margin-right: 17px;
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply {
|
|
||||||
position: relative;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--kungalgame-blue-4);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
&::before,
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
color: var(--kungalgame-pink-4);
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
transition: width 0.2s, height 0.2s, border-bottom-color 0s;
|
|
||||||
transition-delay: 0.2s, 0s, 0.2s;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
border-left: 2px solid var(--kungalgame-pink-4);
|
|
||||||
border-bottom: 2px solid var(--kungalgame-pink-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
transition: width 0.2s, height 0.2s, border-right-color 0.2s;
|
|
||||||
transition-delay: 0s, 0.2s, 0.2s;
|
|
||||||
width: 70px;
|
|
||||||
height: 30px;
|
|
||||||
border-top: 2px solid var(--kungalgame-pink-4);
|
|
||||||
border-right: 2px solid var(--kungalgame-pink-4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 激活后的样式 */
|
|
||||||
.active {
|
|
||||||
color: var(--kungalgame-blue-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
.footer {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
Reference in a new issue