272 lines
11 KiB
Vue
272 lines
11 KiB
Vue
<script setup>
|
|
import {useRoute} from "vue-router";
|
|
import {get, post} from "@/net";
|
|
import {computed, reactive, ref} from "vue";
|
|
import axios from "axios";
|
|
import {ArrowLeft, ChatSquare, CircleCheck, Delete, EditPen, Female, Male, Plus, Star} from "@element-plus/icons-vue";
|
|
import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html';
|
|
import {useStore} from "@/store";
|
|
import Card from "@/components/Card.vue";
|
|
import router from "@/router";
|
|
import InteractButton from "@/components/InteractButton.vue";
|
|
import {ElMessage} from "element-plus";
|
|
import TopicTag from "@/components/TopicTag.vue";
|
|
import TopicEditor from "@/components/TopicEditor.vue";
|
|
import TopicCommentEditor from "@/components/TopicCommentEditor.vue";
|
|
|
|
const route = useRoute()
|
|
const store = useStore()
|
|
|
|
const tid = route.params.tid
|
|
|
|
const topic = reactive({
|
|
data: null,
|
|
like: false,
|
|
collect: false,
|
|
comments: [],
|
|
page: 1
|
|
})
|
|
const edit = ref(false)
|
|
const comment = reactive({
|
|
show: false,
|
|
text: '',
|
|
quote: null
|
|
})
|
|
|
|
function init() {
|
|
get(`/api/forum/topic?tid=${tid}`, data => {
|
|
topic.data = data
|
|
topic.like = data.interact.like
|
|
topic.collect = data.interact.collect
|
|
})
|
|
loadComments(0)
|
|
}
|
|
init()
|
|
|
|
function convertToHtml(content){
|
|
const ops = JSON.parse(content).ops
|
|
const converter = new QuillDeltaToHtmlConverter(ops, {inlineStyles: true});
|
|
return converter.convert();
|
|
}
|
|
|
|
function interact(type, message) {
|
|
get(`/api/forum/interact?tid=${tid}&type=${type}&state=${!topic[type]}`, () => {
|
|
topic[type] = !topic[type]
|
|
if(topic[type])
|
|
ElMessage.success(`${message}成功!`)
|
|
else
|
|
ElMessage.success(`已取消${message}!`)
|
|
})
|
|
}
|
|
|
|
function updateTopic(editor) {
|
|
post('/api/forum/update-topic', {
|
|
id: tid,
|
|
type: editor.type,
|
|
title: editor.title,
|
|
content: editor.text
|
|
}, () => {
|
|
ElMessage.success("帖子内容更新成功!")
|
|
edit.value = false
|
|
init()
|
|
})
|
|
}
|
|
|
|
function loadComments(page){
|
|
topic.comments = null
|
|
topic.page = page + 1
|
|
get(`/api/forum/comments?tid=${tid}&page=${page}`, data => topic.comments = data)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="topic-page" v-if="topic.data">
|
|
<div class="topic-main" style="position: sticky;top: 0;z-index: 10">
|
|
<card style="display: flex;width: 100%">
|
|
<el-button type="info" :icon="ArrowLeft" size="small"
|
|
round plain @click="router.push('/index')">返回列表</el-button>
|
|
<div style="text-align: center;flex: 1;height: 25px">
|
|
<topic-tag :type="topic.data.type"/>
|
|
<span style="margin-left: 5px;font-weight: bold">{{topic.data.title}}</span>
|
|
</div>
|
|
</card>
|
|
</div>
|
|
<div class="topic-main">
|
|
<div class="topic-main-left">
|
|
<el-avatar :src="axios.defaults.baseURL + '/images' + topic.data.user.avatar"
|
|
:size="60"></el-avatar>
|
|
<div style="margin-left: 10px">
|
|
<div style="font-weight: bold;font-size: 18px">
|
|
{{topic.data.user.username}}
|
|
<span style="color: hotpink" v-if="topic.data.user.gender">
|
|
<el-icon><Female/></el-icon>
|
|
</span>
|
|
<span style="color: dodgerblue" v-if="topic.data.user.gender === false">
|
|
<el-icon><Male/></el-icon>
|
|
</span>
|
|
</div>
|
|
<div class="desc">{{topic.data.user.email || '已隐藏电子邮件'}}</div>
|
|
</div>
|
|
<el-divider style="margin: 10px 0"></el-divider>
|
|
<div style="text-align: left;margin: 0 5px">
|
|
<div class="desc">微信号: {{topic.data.user.wx}}</div>
|
|
<div class="desc">QQ号: {{topic.data.user.qq}}</div>
|
|
<div class="desc">手机号: {{topic.data.user.phone}}</div>
|
|
</div>
|
|
<el-divider style="margin: 10px 0"></el-divider>
|
|
<div class="desc" style="margin: 0 5px">{{topic.data.user.desc}}</div>
|
|
</div>
|
|
<div class="topic-main-right">
|
|
<div class="topic-content" v-html="convertToHtml(topic.data.content)"></div>
|
|
<el-divider/>
|
|
<div style="font-size: 13px;color: grey;text-align: center">
|
|
<div>发帖时间: {{new Date(topic.data.time).toLocaleString()}}</div>
|
|
</div>
|
|
<div style="text-align: right;margin-top: 30px">
|
|
<interact-button name="编辑帖子" color="dodgerblue" style="margin-right: 20px"
|
|
:check="false" @check="edit = true" v-if="store.user.id === topic.data.user.uid">
|
|
<el-icon><EditPen /></el-icon>
|
|
</interact-button>
|
|
<interact-button name="点个赞吧" check-name="已点赞" color="pink"
|
|
:check="topic.like" @check="interact('like', '点赞')">
|
|
<el-icon><CircleCheck /></el-icon>
|
|
</interact-button>
|
|
<interact-button name="收藏帖子" check-name="已收藏" color="orange"
|
|
:check="topic.collect" @check="interact('collect', '收藏')"
|
|
style="margin-left: 20px">
|
|
<el-icon><Star/></el-icon>
|
|
</interact-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<transition name="el-fade-in-linear" mode="out-in">
|
|
<div v-if="topic.comments">
|
|
<div class="topic-main" style="margin-top: 10px" v-for="item in topic.comments">
|
|
<div class="topic-main-left">
|
|
<el-avatar :src="axios.defaults.baseURL + '/images' + item.user.avatar"
|
|
:size="60"></el-avatar>
|
|
<div style="margin-left: 10px">
|
|
<div style="font-weight: bold;font-size: 18px">
|
|
{{item.user.username}}
|
|
<span style="color: hotpink" v-if="item.user.gender">
|
|
<el-icon><Female/></el-icon>
|
|
</span>
|
|
<span style="color: dodgerblue" v-if="item.user.gender === false">
|
|
<el-icon><Male/></el-icon>
|
|
</span>
|
|
</div>
|
|
<div class="desc">{{item.user.email || '已隐藏电子邮件'}}</div>
|
|
</div>
|
|
<el-divider style="margin: 10px 0"></el-divider>
|
|
<div style="text-align: left;margin: 0 5px">
|
|
<div class="desc">微信号: {{item.user.wx}}</div>
|
|
<div class="desc">QQ号: {{item.user.qq}}</div>
|
|
<div class="desc">手机号: {{item.user.phone}}</div>
|
|
</div>
|
|
</div>
|
|
<div class="topic-main-right" style="display: flex;flex-direction: column">
|
|
<div style="font-size: 13px;color: grey">
|
|
<div>评论时间: {{new Date(item.time).toLocaleString()}}</div>
|
|
</div>
|
|
<div v-if="item.quote" class="comment-quote">
|
|
回复: {{item.quote}}
|
|
</div>
|
|
<div class="topic-content" style="flex: 1" v-html="convertToHtml(item.content)"></div>
|
|
<div style="text-align: right">
|
|
<el-link :icon="ChatSquare" @click="comment.show = true;comment.quote = item"
|
|
type="info"> 回复评论</el-link>
|
|
<el-link :icon="Delete" type="danger" v-if="item.user.id === store.user.id"
|
|
style="margin-left: 20px"> 删除评论</el-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="width: fit-content;margin: 20px auto">
|
|
<el-pagination background layout="prev, pager, next"
|
|
v-model:current-page="topic.page"
|
|
@current-change="number => loadComments(number - 1)"
|
|
:total="topic.data.commentCount" :page-size="10" hide-on-single-page/>
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
<topic-editor :show="edit" @close="edit = false" v-if="topic.data"
|
|
:default-title="topic.data.title" :default-text="topic.data.content"
|
|
:default-type="topic.data.type" submit-button="更新帖子内容"
|
|
:submit="updateTopic"/>
|
|
<topic-comment-editor :show="comment.show" @close="comment.show = false"
|
|
:quote="comment.quote" :tid="tid"
|
|
@comment="comment.show = false;loadComments(Math.min(1, Math.ceil(topic.data.commentCount / 10)))"/>
|
|
<div class="add-comment" @click="comment.show = true">
|
|
<el-icon><Plus /></el-icon>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.comment-quote {
|
|
font-size: 13px;
|
|
color: grey;
|
|
background-color: rgba(94, 94, 94, 0.2);
|
|
padding: 10px;
|
|
margin-top: 10px;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.add-comment {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
width: 40px;
|
|
height: 40px;
|
|
line-height: 45px;
|
|
font-size: 18px;
|
|
color: var(--el-color-primary);
|
|
text-align: center;
|
|
border-radius: 20px;
|
|
background: var(--el-bg-color-overlay);
|
|
box-shadow: var(--el-box-shadow-lighter);
|
|
|
|
&:hover {
|
|
background: var(--el-border-color-extra-light);
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.topic-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.topic-main {
|
|
display: flex;
|
|
border-radius: 7px;
|
|
margin: 0 auto;
|
|
background-color: var(--el-bg-color);
|
|
width: 800px;
|
|
|
|
.topic-main-left {
|
|
width: 200px;
|
|
padding: 10px;
|
|
text-align: center;
|
|
border-right: solid 1px var(--el-border-color);
|
|
|
|
.desc {
|
|
font-size: 13px;
|
|
color: grey;
|
|
}
|
|
}
|
|
|
|
.topic-main-right {
|
|
width: 600px;
|
|
padding: 10px 20px;
|
|
|
|
.topic-content {
|
|
font-size: 14px;
|
|
line-height: 22px;
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
}
|
|
</style>
|