需求改了好多,不需要自己输用户名和qq号了

This commit is contained in:
Kunagisa 2025-07-10 16:34:35 +08:00
parent d2f8d3fad2
commit c40638eca2
2 changed files with 271 additions and 95 deletions

View File

@ -2,6 +2,7 @@ import axios from 'axios';
import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向 import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向
const API_BASE_URL = 'https://api.zybdatasupport.online'; const API_BASE_URL = 'https://api.zybdatasupport.online';
//const API_BASE_URL = 'http://hk.zybdatasupport.online:8000/';
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: API_BASE_URL, baseURL: API_BASE_URL,

View File

@ -20,6 +20,7 @@
<th>需求创建时间</th> <th>需求创建时间</th>
<th>回复数量</th> <th>回复数量</th>
<th>回复</th> <th>回复</th>
<th>状态</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -47,6 +48,11 @@
<td> <td>
<button class="btn-common btn-gradient btn-reply" @click.stop="showReply(demand)">回复</button> <button class="btn-common btn-gradient btn-reply" @click.stop="showReply(demand)">回复</button>
</td> </td>
<td class="status">
<span :class="['tag', getStatusClass(demand.status)]">
{{ getStatusText(demand.status) }}
</span>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -61,28 +67,28 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="add-modal-form" @submit.prevent="submitReply"> <form class="add-modal-form" @submit.prevent="submitReply">
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">昵称</span> <!-- <span class="label">昵称</span>-->
<input <!-- <input-->
v-model="addForm.author" <!-- v-model="addForm.author"-->
class="input" <!-- class="input"-->
placeholder="请输入您的昵称" <!-- placeholder="请输入您的昵称"-->
required <!-- required-->
/> <!-- />-->
</div> <!-- </div>-->
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">QQ号</span> <!-- <span class="label">QQ号</span>-->
<input <!-- <input-->
v-model="addForm.author_contact" <!-- v-model="addForm.author_contact"-->
class="input" <!-- class="input"-->
placeholder="请输入您的QQ号" <!-- placeholder="请输入您的QQ号"-->
type="text" <!-- type="text"-->
pattern="[0-9]*" <!-- pattern="[0-9]*"-->
inputmode="numeric" <!-- inputmode="numeric"-->
readonly <!-- readonly-->
required <!-- required-->
/> <!-- />-->
</div> <!-- </div>-->
<div class="form-row"> <div class="form-row">
<span class="label">内容</span> <span class="label">内容</span>
<textarea <textarea
@ -143,6 +149,12 @@
<span class="label">发布时间</span> <span class="label">发布时间</span>
<span class="value">{{ formatDate(selectedDemand?.date) }}</span> <span class="value">{{ formatDate(selectedDemand?.date) }}</span>
</div> </div>
<div class="detail-item">
<span class="label">状态</span>
<span :class="['tag', getStatusClass(selectedDemand?.status)]">
{{ getStatusText(selectedDemand?.status) }}
</span>
</div>
<div class="reply-section"> <div class="reply-section">
<h3>回复内容</h3> <h3>回复内容</h3>
<div class="reply-list"> <div class="reply-list">
@ -156,16 +168,20 @@
</div> </div>
</div> </div>
<div v-else class="no-reply"> <div v-else class="no-reply">
<div v-if="selectedDemand?.sendcontent"> <div v-if="selectedDemand?.sendcontent && replyParsedTexts.length > 0">
<div v-for="(reply, index) in selectedDemand.sendcontent.split('|')" :key="index" class="reply-content"> <div v-for="(parsedReply, index) in replyParsedTexts" :key="index" class="reply-content">
<div class="reply-with-avatar"> <div class="reply-with-avatar">
<img :src="`https://q1.qlogo.cn/g?b=qq&nk=${extractQQ(reply)}&s=40`" alt="User Avatar" class="reply-avatar" /> <img :src="`https://q1.qlogo.cn/g?b=qq&nk=${extractQQ(selectedDemand.sendcontent.split('|')[index])}&s=40`" alt="User Avatar" class="reply-avatar" />
<div class="reply-text"> <div class="reply-text">
<b>{{ parseReplyText(reply).user }}</b> {{ parseReplyText(reply).content }} <b>{{ parsedReply.user }}</b>
<span class="reply-content-with-newlines">{{ parsedReply.content }}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-else-if="selectedDemand?.sendcontent && replyParsedTexts.length === 0">
加载中...
</div>
<div v-else> <div v-else>
暂无回复 暂无回复
</div> </div>
@ -188,22 +204,22 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="add-modal-form" @submit.prevent="submitAddForm"> <form class="add-modal-form" @submit.prevent="submitAddForm">
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">请求者</span> <!-- <span class="label">请求者</span>-->
<input v-model="addForm.requester" class="input" placeholder="可选" /> <!-- <input v-model="addForm.requester" class="input" placeholder="可选" />-->
</div> <!-- </div>-->
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">QQ号</span> <!-- <span class="label">QQ号</span>-->
<input <!-- <input -->
v-model="addForm.qq_code" <!-- v-model="addForm.qq_code" -->
class="input" <!-- class="input" -->
placeholder="可选" <!-- placeholder="可选" -->
type="text" <!-- type="text"-->
pattern="[0-9]*" <!-- pattern="[0-9]*"-->
inputmode="numeric" <!-- inputmode="numeric"-->
readonly <!-- readonly-->
/> <!-- />-->
</div> <!-- </div>-->
<div class="form-row"> <div class="form-row">
<span class="label">需求内容</span> <span class="label">需求内容</span>
<textarea <textarea
@ -237,14 +253,14 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="add-modal-form" @submit.prevent="handleEditSubmit"> <form class="add-modal-form" @submit.prevent="handleEditSubmit">
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">请求者</span> <!-- <span class="label">请求者</span>-->
<input v-model="editForm.requester" class="input" required /> <!-- <input v-model="editForm.requester" class="input" required />-->
</div> <!-- </div>-->
<div class="form-row"> <!-- <div class="form-row">-->
<span class="label">QQ号</span> <!-- <span class="label">QQ号</span>-->
<input v-model="editForm.qq_code" class="input" required readonly /> <!-- <input v-model="editForm.qq_code" class="input" required readonly />-->
</div> <!-- </div>-->
<div class="form-row"> <div class="form-row">
<span class="label">需求内容</span> <span class="label">需求内容</span>
<textarea v-model="editForm.content" class="input" rows="3" required></textarea> <textarea v-model="editForm.content" class="input" rows="3" required></textarea>
@ -262,9 +278,9 @@
<div v-if="showDeleteConfirm" class="modal-overlay" style="z-index:2000;"> <div v-if="showDeleteConfirm" class="modal-overlay" style="z-index:2000;">
<div class="modal-content" style="max-width:350px;text-align:center;"> <div class="modal-content" style="max-width:350px;text-align:center;">
<div class="modal-header"> <div class="modal-header">
<h2 style="color:#F56C6C;">隐藏并删除</h2> <h2 style="color:#F56C6C;">删除</h2>
</div> </div>
<div class="modal-body" style="font-size:16px;">确定要隐藏并删除该需求吗此操作不可恢复</div> <div class="modal-body" style="font-size:16px;">确定要删除该需求吗此操作不可恢复</div>
<div class="delete-dialog-footer" style="display:flex;justify-content:center;gap:18px;margin:18px 0 8px 0;"> <div class="delete-dialog-footer" style="display:flex;justify-content:center;gap:18px;margin:18px 0 8px 0;">
<button class="confirm-button" @click="confirmDelete">确认</button> <button class="confirm-button" @click="confirmDelete">确认</button>
<button class="cancel-button" @click="cancelDelete">取消</button> <button class="cancel-button" @click="cancelDelete">取消</button>
@ -279,6 +295,7 @@
import { ref, onMounted, nextTick, computed } from 'vue' import { ref, onMounted, nextTick, computed } from 'vue'
import { getDemandsList, addDemand, updateDemand, deleteDemand, addDemandReply } from '../../api/demands' import { getDemandsList, addDemand, updateDemand, deleteDemand, addDemandReply } from '../../api/demands'
import { getStoredUser } from '@/utils/jwt' import { getStoredUser } from '@/utils/jwt'
import { getUserByInfo } from '../../api/login'
import ErrorDialog from '@/components/ErrorDialog.vue' import ErrorDialog from '@/components/ErrorDialog.vue'
// //
@ -289,6 +306,7 @@ const showModal = ref(false)
const reply = ref(null) const reply = ref(null)
const replyModal = ref(false) const replyModal = ref(false)
const selectedDemand = ref(null) const selectedDemand = ref(null)
const replyParsedTexts = ref([])
const showAddModal = ref(false) const showAddModal = ref(false)
const addError = ref('') const addError = ref('')
const addForm = ref({ const addForm = ref({
@ -322,9 +340,23 @@ const isNoReward = (reward) => {
return !reward || reward === '无赏金' return !reward || reward === '无赏金'
} }
// &DEL //
const filteredDemands = computed(() => { const filteredDemands = computed(() => {
return demands.value.filter(demand => !demand.content?.startsWith('&DEL')) const user = getStoredUser()
return demands.value.filter(demand => {
// DEL&DEL
if (demand.status === 'DEL' || demand.content?.startsWith('&DEL')) {
return false
}
//
if (demand.status === 'HIDE') {
return user && demand.qq_code && String(user.qq_code) === String(demand.qq_code)
}
//
return true
})
}) })
const getReplyCount = (demand) => { const getReplyCount = (demand) => {
@ -339,6 +371,34 @@ const getReplyCount = (demand) => {
return 0; return 0;
} }
//
const getStatusClass = (status) => {
switch (status) {
case 'NORMAL':
return 'status-normal'
case 'DEL':
return 'status-deleted'
case 'HIDE':
return 'status-hidden'
default:
return 'status-normal'
}
}
//
const getStatusText = (status) => {
switch (status) {
case 'NORMAL':
return '正常'
case 'DEL':
return '已删除'
case 'HIDE':
return '管理员已隐藏'
default:
return '正常'
}
}
// //
const formatDate = (dateString) => { const formatDate = (dateString) => {
if (!dateString || dateString === 'Test_date') return '日期未提供' if (!dateString || dateString === 'Test_date') return '日期未提供'
@ -377,25 +437,44 @@ const fetchDemands = async () => {
} }
// //
const showReply = (demand) => { const showReply = async (demand) => {
reply.value = demand reply.value = demand
replyModal.value = true replyModal.value = true
resetReplyForm(); await resetReplyForm();
} }
// //
const closeReplyModal = () => { const closeReplyModal = async () => {
replyModal.value = false; replyModal.value = false;
resetReplyForm(); await resetReplyForm();
} }
// //
const resetReplyForm = () => { const resetReplyForm = async () => {
// qq_code // qq_code
const user = getStoredUser() const user = getStoredUser()
let authorName = ''
//
if (user && user.qq_code) {
try {
console.log('回复表单 - 正在获取用户信息QQ号:', user.qq_code)
const userInfo = await getUserByInfo({ qq_code: user.qq_code })
console.log('回复表单 - 获取到的用户信息:', userInfo)
// authorName = userInfo.name || ''
authorName = userInfo.username || '' // 使usernamename
console.log('回复表单 - 设置的作者昵称:', authorName)
} catch (error) {
console.log('回复表单 - 获取用户昵称失败:', error)
// 使
authorName = ''
}
}
addForm.value = { addForm.value = {
sendcontent: '', sendcontent: '',
author: '', // author: '',
author: authorName, // 使
author_contact: user && user.qq_code ? user.qq_code : '' author_contact: user && user.qq_code ? user.qq_code : ''
}; };
addError.value = ''; addError.value = '';
@ -406,9 +485,19 @@ const resetReplyForm = () => {
} }
// //
const showDetail = (demand) => { const showDetail = async (demand) => {
selectedDemand.value = demand selectedDemand.value = demand
showModal.value = true showModal.value = true
// sendcontent
if (demand.sendcontent) {
replyParsedTexts.value = []
const replies = demand.sendcontent.split('|')
for (let i = 0; i < replies.length; i++) {
const parsed = await parseReplyText(replies[i])
replyParsedTexts.value[i] = parsed
}
}
} }
// //
@ -418,11 +507,30 @@ const closeModal = () => {
} }
// //
const openAddModal = () => { const openAddModal = async () => {
// //
const user = getStoredUser() const user = getStoredUser()
let requesterName = ''
//
if (user && user.qq_code) {
try {
console.log('正在获取用户信息QQ号:', user.qq_code)
const userInfo = await getUserByInfo({ qq_code: user.qq_code })
console.log('获取到的用户信息:', userInfo)
// requesterName = userInfo.name || ''
requesterName = userInfo.username || '' // 使usernamename
console.log('设置的请求者昵称:', requesterName)
} catch (error) {
console.log('获取用户昵称失败:', error)
// 使
requesterName = ''
}
}
addForm.value = { addForm.value = {
requester: '', // requester: '',
requester: requesterName, // 使
content: '', content: '',
reward: '', reward: '',
qq_code: user && user.qq_code ? user.qq_code : '', qq_code: user && user.qq_code ? user.qq_code : '',
@ -440,11 +548,11 @@ function closeAddModal() {
addError.value = '' addError.value = ''
} }
// script setup // // script setup
const validateQQ = (event) => { // const validateQQ = (event) => {
// // //
addForm.value.qq_code = event.target.value.replace(/[^\d]/g, ''); // addForm.value.qq_code = event.target.value.replace(/[^\d]/g, '');
} // }
// submitAddForm QQ // submitAddForm QQ
async function submitAddForm() { async function submitAddForm() {
@ -504,13 +612,13 @@ const submitReply = async () => {
addError.value = ''; addError.value = '';
try { try {
const replyData = { const replyData = {
reply: addForm.value.sendcontent reply: addForm.value.sendcontent.replace(/\n/g, ' ').replace(/\r/g, ' ')
}; };
console.log('提交的回复数据:', replyData); console.log('提交的回复数据:', replyData);
await addDemandReply(reply.value.id, replyData); await addDemandReply(reply.value.id, replyData);
replyModal.value = false; replyModal.value = false;
resetReplyForm(); await resetReplyForm();
fetchDemands(); // fetchDemands(); //
} catch (e) { } catch (e) {
console.error('提交回复失败:', e); console.error('提交回复失败:', e);
@ -536,40 +644,83 @@ function autoResize() {
} }
function extractQQ(str) { function extractQQ(str) {
// qq: // qq:
const qqMatch = str.match(/^(\d+):(.+)$/) const newFormatMatch = str.match(/^(\d+):(.*)$/)
if (qqMatch) { if (newFormatMatch) {
return qqMatch[1] return newFormatMatch[1]
} }
// QQ // QQ
const match = str.match(/[(]([1-9][0-9]{4,})[)]/) const oldFormatMatch = str.match(/^(.+?)\((\d+)\)(.*)$/)
return match ? match[1] : '' if (oldFormatMatch) {
return oldFormatMatch[2] // QQ
}
//
const flexibleOldMatch = str.match(/[(](\d+)[)]/);
return flexibleOldMatch ? flexibleOldMatch[1] : ''
} }
function parseReplyText(str) { //
// qq: const usernameCache = ref({})
const qqMatch = str.match(/^(\d+):(.+)$/)
if (qqMatch) { async function parseReplyText(str) {
// qq:
const newFormatMatch = str.match(/^(\d+):(.*)$/)
if (newFormatMatch) {
const qqNumber = newFormatMatch[1]
let username = qqNumber // QQ
//
if (usernameCache.value[qqNumber]) {
username = usernameCache.value[qqNumber]
} else {
// API
try {
const userInfo = await getUserByInfo({ qq_code: qqNumber })
if (userInfo && userInfo.username) {
username = `${userInfo.username}${qqNumber}`
usernameCache.value[qqNumber] = username
}
} catch (error) {
console.log('获取用户信息失败:', error)
// 使QQ
username = qqNumber
}
}
return { return {
user: qqMatch[1], user: username,
content: qqMatch[2].trim() content: newFormatMatch[2].trim().replace(/\\n/g, '\n').replace(/\\r/g, '').replace(/\\t/g, ' ')
} }
} }
// QQ // QQ
const match = str.match(/^(.+?[(][1-9][0-9]{4,}[)])(.*)$/) const oldFormatMatch = str.match(/^(.+?)\((\d+)\)(.*)$/)
if (match) { if (oldFormatMatch) {
// match[1] QQmatch[2] " xxx " const nickname = oldFormatMatch[1].trim()
const qqNumber = oldFormatMatch[2]
return { return {
user: match[1].trim(), user: `${nickname}${qqNumber}`, //
content: match[2].replace(/^|^:/, '').trim() content: oldFormatMatch[3].trim().replace(/\\n/g, ' ').replace(/\\r/g, '').replace(/\\t/g, ' ')
} }
} }
// fallback
//
const flexibleOldMatch = str.match(/^(.+?)[(](\d+)[)][:](.*)$/)
if (flexibleOldMatch) {
const nickname = flexibleOldMatch[1].trim()
const qqNumber = flexibleOldMatch[2]
return {
user: `${nickname}${qqNumber}`,
content: flexibleOldMatch[3].trim().replace(/\\n/g, ' ').replace(/\\r/g, '').replace(/\\t/g, ' ')
}
}
// fallback
return { return {
user: '', user: '',
content: str content: str.replace(/\\n/g, ' ').replace(/\\r/g, '').replace(/\\t/g, ' ')
} }
} }
@ -623,15 +774,15 @@ function handleDeleteDemand() {
async function confirmDelete() { async function confirmDelete() {
try { try {
// 使updateDemand APIcontent&DEL // DELcontent
const updatedContent = `&DEL${selectedDemand.value.content}`
await updateDemand(pendingDeleteId.value, { await updateDemand(pendingDeleteId.value, {
requester: selectedDemand.value.requester, requester: selectedDemand.value.requester,
qq_code: selectedDemand.value.qq_code, qq_code: selectedDemand.value.qq_code,
content: updatedContent, content: selectedDemand.value.content,
reward: selectedDemand.value.reward, reward: selectedDemand.value.reward,
date: selectedDemand.value.date, date: selectedDemand.value.date,
sendcontent: selectedDemand.value.sendcontent sendcontent: selectedDemand.value.sendcontent,
status: 'DEL'
}) })
fetchDemands() fetchDemands()
closeModal() closeModal()
@ -742,8 +893,27 @@ const onRefresh = () => {
border: 1px solid #b6d2ff; border: 1px solid #b6d2ff;
} }
.status-normal {
background: #f0f9ff;
color: #0369a1;
border: 1px solid #7dd3fc;
}
.status-deleted {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fca5a5;
}
.status-hidden {
background: #fefce8;
color: #a16207;
border: 1px solid #fcd34d;
}
.maps-table td.reward, .maps-table td.reward,
.maps-table td.reply-count, .maps-table td.reply-count,
.maps-table td.status,
.maps-table tr { .maps-table tr {
box-shadow: none !important; box-shadow: none !important;
filter: none !important; filter: none !important;
@ -1039,6 +1209,11 @@ const onRefresh = () => {
font-size: 15px; font-size: 15px;
} }
.reply-content-with-newlines {
white-space: pre-wrap; /* 保留换行符和空白字符 */
word-wrap: break-word; /* 长单词自动换行 */
}
.warning-tip { .warning-tip {
background: linear-gradient(90deg, #ffeaea 0%, #ffd6d6 100%); background: linear-gradient(90deg, #ffeaea 0%, #ffd6d6 100%);
color: #d32f2f; color: #d32f2f;