2025-03-08 12:49:38 +08:00

373 lines
8.2 KiB
Vue

<script setup>
import Message from "./Message.vue";
import {onBeforeUnmount, onMounted, ref, watch} from "vue";
import api from "../../../utils/axios.js";
import store from "../../../store/index.js";
import swal from "../../../utils/sweetalert.js";
import AuthService from "../../../../services/auth.js";
import PagingController from "../../../components/PagingController.vue";
const messages = ref(store.state.demosLocal.board?.messages || []);
const amount = ref(store.state.demosLocal.board?.amount || 0);
const currentPage = ref(store.state.demosLocal.board?.currentPage || 1);
const pageLoading = ref(false);
const sendCD = ref(0);
const userInput = ref('');
let timer = null;
async function refreshBoard(page, pageSize) {
if (!page) {
page = currentPage.value;
}
if (!pageSize) {
pageSize = 10;
}
try {
await api.post(`/viewboard`, {
PAGE: page,
PAGE_SIZE: pageSize
}).then(res => {
res.data = res.data.map((msg)=>{
function stringToFloat(str) {
// 使用字符串的字符码生成一个基于字符串的唯一数值
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i); // 位运算来生成哈希
hash = hash & hash; // 强制转换为32位整数
}
// 将哈希值映射到0~1之间
return Math.abs(hash) / (Math.pow(2, 32) - 1);
}
msg.likes = Math.round(stringToFloat('random'+ msg.content)*10000);
return msg;})
messages.value = res.data;
amount.value = Math.ceil(res.amount / pageSize);
store.commit('setLocalDemoValue', {demo: 'board', value: {messages: messages.value}});
store.commit('setLocalDemoValue', {demo: 'board', value: {amount: amount.value}});
})
} catch {
return 1;
}
return 0;
}
async function goPage(page) {
const messageTemp = messages.value
const pageTemp = currentPage.value;
pageLoading.value = true;
currentPage.value = page;
if (await refreshBoard(page) === 0) {
store.commit('setLocalDemoValue', {demo: 'board', value: {currentPage: page}});
pageLoading.value = false;
return;
}
swal.tip('error', '加载留言板失败...')
messages.value = messageTemp;
currentPage.value = pageTemp;
pageLoading.value = false;
}
async function sendMessage() {
if (sendCD.value !== 0) {
return;
}
if (userInput.value.trim().length === 0) {
swal.tip('info', '不得为空')
return;
}
sendCD.value = 'wait';
try {
const response = await api.post('/postmessage', {
TOKEN: AuthService.getToken(),
CONTENT: userInput.value
});
if (response.code !== 0) {
swal.tip('error', '发送失败');
return;
}
userInput.value = '';
sendCD.value = 5;
store.commit('setLocalDemoValue', {demo: 'board', value: { sendCD: sendCD.value }});
await refreshBoard();
return;
} catch {
swal.tip('error', '网络连接错误...');
}
sendCD.value = 0;
}
async function clickRefresh() {
pageLoading.value = true;
if (await refreshBoard() !== 0) {
swal.tip('error', '刷新失败');
}
pageLoading.value = false;
}
let interval = null; // 定时器引用
function startCountdown() {
if (interval) clearInterval(interval); // 避免重复创建定时器
interval = setInterval(() => {
if (sendCD.value > 0) {
sendCD.value--;
store.commit('setLocalDemoValue', {demo: 'board', value: { sendCD: sendCD.value }});
} else {
clearInterval(interval);
interval = null;
}
}, 1000);
}
// 监听 sendCD 变量
watch(sendCD, (newValue) => {
if (newValue > 0 && !interval) {
startCountdown();
}
});
onMounted(async () => {
await refreshBoard();
timer = setInterval(refreshBoard, 7000);
});
onBeforeUnmount(() => {
if (timer) {
clearInterval(timer);
timer = null; // ✅ 避免悬空定时器
}
});
</script>
<template>
<div class="container">
<div class="board-body">
<div class="message-container" :class="{loading: pageLoading}">
<Message v-for="msg in messages" :message="msg" @refresh-board="refreshBoard"/>
<div v-if="messages?.length === 0">正在加载...</div>
</div>
<paging-controller :current-page="currentPage" :amount="amount" :go-page-func="goPage" :loading="pageLoading"/>
</div>
<div class="sender-container">
<div class="sender-tips">
<img :src="store.getters.profileImage" alt="Avatar">
<div class="text">
<div>{{ store.state.userInfo.username || '请先登录' }}</div>
<div>{{ '&lt' + store.getters.userRole + '&gt' }}</div>
</div>
<div class="refresh-btn">
<button @click="clickRefresh">刷新</button>
</div>
</div>
<div class="sender-input">
<input class="message-input" v-model="userInput" @keydown.enter="sendMessage" :disabled="!store.state.userInfo.uid">
<button class="send-button" @click="sendMessage" :disabled="sendCD !== 0 || !store.state.userInfo.uid">
{{ sendCD === 0 ? '发送' : sendCD === 'wait' ? '稍等' : `${sendCD}` }}
</button>
</div>
</div>
</div>
</template>
<style scoped>
.container {
display: flex;
align-items: center;
justify-items: flex-start;
width: 100%;
height: calc(100vh - 130px);
overflow: auto;
}
.board-body {
display: flex;
flex-direction: column;
gap: 20px;
width: calc(100% - 60px);
max-width: 1000px;
margin: 0 auto 120px;
height: auto;
padding: 20px;
border-radius: 15px;
background: rgba(128, 128, 128, 0.06);
border: cyan solid 1px;
}
.theme-light .board-body {
border: #b2b2b2 solid 1px;
}
.message-container {
display: flex;
flex-direction: column;
justify-content: center;
gap: 15px;
transition: opacity 0.2s ease;
}
.message-container.loading {
opacity: 0.4;
}
.paging-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
}
.page-btn {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
background: black;
//width: 25px;
height: 25px;
padding: 0 5px;
border-radius: 4px;
border: cyan solid 1px;
}
.theme-light .page-btn {
background-color: white;
border: #b2b2b2 solid 1px;
}
.page-btn.active {
background: cyan;
color: black;
}
.theme-light .page-btn.active {
background: #2a2a2a;
color: white;
}
.sender-container {
margin: 20px;
background-color: #212121;
border: cyan solid 1px;
border-radius: 10px;
position: fixed;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 95%;
height: auto;
overflow: hidden;
}
.theme-light .sender-container {
background-color: white;
border: #b2b2b2 solid 1px;
}
.sender-tips {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 100%;
}
.sender-tips img {
width: 50px;
height: 50px;
margin: 15px 15px 0;
border-radius: 50%;
}
.sender-tips .text {
display: flex;
flex-direction: column;
}
.refresh-btn {
flex: 1;
margin-right: 15px;
display: flex;
flex-direction: row-reverse;
}
.refresh-btn button {
background-color: #4f4f4f;
color: cyan;
width: 50px;
height: 50px;
margin: 15px 0 0 15px;
border-radius: 50%;
}
.theme-light .refresh-btn button {
background-color: white;
color: #1a1a1a;
border: #1a1a1a solid 1px;
}
.sender-input {
display: flex;
flex-direction: row;
width: 97%;
gap: 10px;
margin: 15px;
transition: all 0.5s ease;
}
/* 输入框样式 */
.message-input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
outline: none;
}
/* 发送按钮样式 */
.send-button {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
}
.theme-light .send-button {
color: #1a1a1a;
background-color: #ffb74d;
}
/* 鼠标悬停时的按钮样式 */
.send-button:hover {
background-color: #0056b3;
}
.theme-light .send-button:hover {
background-color: #ffb74d;
}
.send-button:active {
transform: scale(0.95);
}
.send-button:disabled {
background-color: #9d9d9d;
}
.theme-light .send-button:disabled {
background-color: #9d9d9d;
}
</style>