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

This commit is contained in:
Kunagisa 2025-07-10 17:21:40 +08:00
parent c40638eca2
commit 66efa6c310
4 changed files with 264 additions and 66 deletions

View File

@ -99,3 +99,17 @@ export const addDemandReply = async (id, replyData) => {
throw error;
}
};
/**
* 获取所有需求列表管理员
* @returns {Promise<Array<Object>>} 返回所有需求列表数据
*/
export const getAllDemandsList = async () => {
try {
const response = await axiosInstance.get('/demands/getlistall');
return response.data;
} catch (error) {
console.error('获取所有需求列表失败:', error);
throw error;
}
};

View File

@ -50,15 +50,27 @@
<td class="date" data-label="创建时间">{{ formatDate(demand.date) }}</td>
<td class="reply-count" data-label="回复数量">{{ getReplyCount(demand.sendcontent) }}</td>
<td class="status" data-label="状态">
<span :class="['tag', isDeleted(demand.content) ? 'deleted' : 'active']">
{{ isDeleted(demand.content) ? '已删除' : '正常' }}
<span :class="['tag', getStatusClass(demand)]">
{{ getStatusText(demand) }}
</span>
</td>
<td class="actions" data-label="操作">
<button class="btn-common btn-small btn-gradient" @click="showDetail(demand)">查看</button>
<button v-if="!isDeleted(demand.content)" class="btn-common btn-small btn-warning" @click="handleHide(demand)">隐藏</button>
<button v-else class="btn-common btn-small btn-restore" @click="handleRestore(demand)">恢复</button>
<button v-if="showDeletedItems" class="btn-common btn-small btn-danger" @click="handleDelete(demand)">删除</button>
<!-- 检查是否为已删除状态 -->
<template v-if="isDeleted(demand)">
<!-- 已删除的需求只有在显示已删除时才显示恢复按钮 -->
<button v-if="showDeletedItems" class="btn-common btn-small btn-restore" @click="handleRestore(demand)">恢复</button>
</template>
<template v-else>
<!-- 正常状态的需求根据格式显示对应按钮 -->
<template v-if="demand.status">
<!-- 新格式数据 -->
<button v-if="demand.status === 'NORMAL'" class="btn-common btn-small btn-warning" @click="handleHide(demand)">隐藏</button>
<button v-if="demand.status === 'NORMAL' && showDeletedItems" class="btn-common btn-small btn-danger" @click="handleDelete(demand)">删除</button>
<button v-if="demand.status === 'HIDE'" class="btn-common btn-small btn-restore" @click="handleRestore(demand)">恢复</button>
</template>
<!-- 旧格式数据不显示任何操作按钮 -->
</template>
</td>
</tr>
</tbody>
@ -99,23 +111,27 @@
</div>
<div class="detail-item">
<span class="label">状态</span>
<span :class="['tag', isDeleted(selectedDemand?.content) ? 'deleted' : 'active']">
{{ isDeleted(selectedDemand?.content) ? '已删除' : '正常' }}
<span :class="['tag', getStatusClass(selectedDemand)]">
{{ getStatusText(selectedDemand) }}
</span>
</div>
<div class="reply-section">
<h3>回复内容</h3>
<div class="reply-list">
<div v-if="selectedDemand?.sendcontent">
<div v-for="(reply, index) in selectedDemand.sendcontent.split('|')" :key="index" class="reply-content">
<div v-if="selectedDemand?.sendcontent && replyParsedTexts.length > 0">
<div v-for="(parsedReply, index) in replyParsedTexts" :key="index" class="reply-content">
<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">
<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 v-else-if="selectedDemand?.sendcontent && replyParsedTexts.length === 0">
加载中...
</div>
<div v-else class="no-reply">
暂无回复
</div>
@ -201,7 +217,9 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { getDemandsList, updateDemand, deleteDemand, addDemand } from '../../api/demands'
import { getAllDemandsList, updateDemand, deleteDemand, addDemand, getDemandsList } from '../../api/demands'
import { getUserByInfo } from '../../api/login'
import { getStoredUser } from '@/utils/jwt'
//
const demands = ref([])
@ -209,6 +227,8 @@ const loading = ref(false)
const error = ref(null)
const showModal = ref(false)
const selectedDemand = ref(null)
const replyParsedTexts = ref([])
const usernameCache = ref({})
const showDeleteConfirm = ref(false)
const showRestoreConfirm = ref(false)
const pendingDeleteId = ref(null)
@ -225,12 +245,25 @@ const addForm = ref({
const addError = ref('')
const addLoading = ref(false)
//
const isDeleted = (demand) => {
// content&DEL
if (demand.content?.startsWith('&DEL')) {
return true
}
// status
if (demand.status === 'DEL') {
return true
}
return false
}
//
const displayDemands = computed(() => {
if (showDeletedItems.value) {
return demands.value
} else {
return demands.value.filter(demand => !isDeleted(demand.content))
return demands.value.filter(demand => !isDeleted(demand))
}
})
@ -239,17 +272,77 @@ const isNoReward = (reward) => {
return !reward || reward === '无赏金'
}
//
const isDeleted = (content) => {
return content?.startsWith('&DEL')
//
const getStatusClass = (demand) => {
// content&DEL
if (demand.content?.startsWith('&DEL')) {
return 'status-deleted'
}
// 使status
if (demand.status) {
switch (demand.status) {
case 'NORMAL':
return 'status-normal'
case 'DEL':
return 'status-deleted'
case 'HIDE':
return 'status-hidden'
default:
return 'status-normal'
}
}
return 'status-normal'
}
//
//
const getStatusText = (demand) => {
// content&DEL
if (demand.content?.startsWith('&DEL')) {
return '已删除'
}
// 使status
if (demand.status) {
switch (demand.status) {
case 'NORMAL':
return '正常'
case 'DEL':
return '已删除'
case 'HIDE':
return '已隐藏'
default:
return '正常'
}
}
return '正常'
}
//
const getDisplayContent = (content) => {
if (!content) return ''
// &DEL
return content.replace(/^&DEL/, '')
}
//
const isNormalStatus = (demand) => {
// status
if (demand.status) {
return demand.status === 'NORMAL'
}
// content&DEL
return !demand.content?.startsWith('&DEL')
}
//
const isHiddenStatus = (demand) => {
// status
if (demand.status) {
return demand.status === 'HIDE'
}
// false
return false
}
//
const getReplyCount = (sendcontent) => {
if (!sendcontent) return 0
@ -278,8 +371,14 @@ const fetchDemands = async () => {
loading.value = true
error.value = null
try {
const data = await getDemandsList()
const data = await getAllDemandsList()
demands.value = data
//
const currentUser = getStoredUser()
console.log('当前用户信息:', currentUser)
console.log('用户权限:', currentUser?.privilege)
console.log('临时权限:', currentUser?.temp_privilege)
} catch (err) {
console.error('加载需求列表失败:', err)
if (err.response?.status === 403) {
@ -295,9 +394,19 @@ const fetchDemands = async () => {
}
//
const showDetail = (demand) => {
const showDetail = async (demand) => {
selectedDemand.value = demand
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
}
}
}
//
@ -316,25 +425,35 @@ const handleHide = (demand) => {
//
const confirmOperation = async () => {
try {
if (currentOperation.value === 'hide') {
const demand = demands.value.find(d => d.id === pendingDeleteId.value)
if (!demand) return
const demand = demands.value.find(d => d.id === pendingDeleteId.value)
if (!demand) return
const updatedContent = `&DEL${demand.content}`
await updateDemand(pendingDeleteId.value, {
requester: demand.requester,
qq_code: demand.qq_code,
content: updatedContent,
reward: demand.reward,
date: demand.date,
sendcontent: demand.sendcontent
})
} else if (currentOperation.value === 'delete') {
await deleteDemand(pendingDeleteId.value)
const updateData = {
requester: demand.requester,
qq_code: demand.qq_code,
content: demand.content,
reward: demand.reward,
date: demand.date,
sendcontent: demand.sendcontent
}
if (currentOperation.value === 'hide') {
updateData.status = 'HIDE'
} else if (currentOperation.value === 'delete') {
updateData.status = 'DEL'
}
console.log('管理员操作 - 更新需求的数据:', updateData)
console.log('管理员操作 - 需求ID:', pendingDeleteId.value)
const result = await updateDemand(pendingDeleteId.value, updateData)
console.log('管理员操作 - 更新结果:', result)
fetchDemands()
} catch (e) {
console.error('操作失败:', e)
console.error('管理员操作失败:', e)
console.error('错误详情:', e.response?.data)
error.value = `操作失败: ${e.response?.data?.detail || e.message}`
} finally {
showDeleteConfirm.value = false
pendingDeleteId.value = null
@ -367,18 +486,35 @@ const confirmRestore = async () => {
const demand = demands.value.find(d => d.id === pendingRestoreId.value)
if (!demand) return
const restoredContent = demand.content.replace(/^&DEL/, '')
await updateDemand(pendingRestoreId.value, {
let updateData = {
requester: demand.requester,
qq_code: demand.qq_code,
content: restoredContent,
reward: demand.reward,
date: demand.date,
sendcontent: demand.sendcontent
})
sendcontent: demand.sendcontent,
status: 'NORMAL'
}
//
if (demand.status) {
// content
updateData.content = demand.content
} else {
// content&DEL
updateData.content = demand.content.replace(/^&DEL/, '')
}
console.log('管理员恢复 - 更新需求的数据:', updateData)
console.log('管理员恢复 - 需求ID:', pendingRestoreId.value)
const result = await updateDemand(pendingRestoreId.value, updateData)
console.log('管理员恢复 - 更新结果:', result)
fetchDemands()
} catch (e) {
console.error('恢复失败:', e)
console.error('错误详情:', e.response?.data)
error.value = `恢复失败: ${e.response?.data?.detail || e.message}`
} finally {
showRestoreConfirm.value = false
pendingRestoreId.value = null
@ -391,6 +527,7 @@ const cancelRestore = () => {
pendingRestoreId.value = null
}
//
const onRefresh = () => {
fetchDemands()
@ -448,42 +585,83 @@ const submitAddForm = async () => {
}
//
function parseReplyText(str) {
// qq:
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 {
user: qqMatch[1],
content: qqMatch[2].trim()
user: username,
content: newFormatMatch[2].trim().replace(/\\n/g, ' ').replace(/\\r/g, '').replace(/\\t/g, ' ')
}
}
// QQ
const match = str.match(/^(.+?[(][1-9][0-9]{4,}[)])(.*)$/)
if (match) {
// QQ
const oldFormatMatch = str.match(/^(.+?)\((\d+)\)(.*)$/)
if (oldFormatMatch) {
const nickname = oldFormatMatch[1].trim()
const qqNumber = oldFormatMatch[2]
return {
user: match[1].trim(),
content: match[2].replace(/^|^:/, '').trim()
user: `${nickname}${qqNumber}`, //
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 {
user: '',
content: str
content: str.replace(/\\n/g, ' ').replace(/\\r/g, '').replace(/\\t/g, ' ')
}
}
// QQ
function extractQQ(str) {
// qq:
const qqMatch = str.match(/^(\d+):(.+)$/)
if (qqMatch) {
return qqMatch[1]
// qq:
const newFormatMatch = str.match(/^(\d+):(.*)$/)
if (newFormatMatch) {
return newFormatMatch[1]
}
// QQ
const match = str.match(/[(]([1-9][0-9]{4,})[)]/)
return match ? match[1] : ''
// QQ
const oldFormatMatch = str.match(/^(.+?)\((\d+)\)(.*)$/)
if (oldFormatMatch) {
return oldFormatMatch[2] // QQ
}
//
const flexibleOldMatch = str.match(/[(](\d+)[)]/);
return flexibleOldMatch ? flexibleOldMatch[1] : ''
}
//
@ -652,18 +830,24 @@ onMounted(() => {
border: 1px solid #d9d9d9;
}
.active {
.status-normal {
background: #f0f9ff;
color: #0369a1;
border: 1px solid #7dd3fc;
}
.deleted {
.status-deleted {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fca5a5;
}
.status-hidden {
background: #fefce8;
color: #a16207;
border: 1px solid #fcd34d;
}
.actions {
display: flex;
gap: 5px;

View File

@ -163,10 +163,10 @@ const validateForm = () => {
// usernameError.value = 'QQ'
// return false
// }
if (username.value.length < 4) {
usernameError.value = 'QQ号码长度不能小于4个字符'
return false
}
// if (username.value.length < 4) {
// usernameError.value = 'QQ4'
// return false
// }
//
if (!password.value) {

View File

@ -118,7 +118,7 @@
<h2>需求详情</h2>
<div style="display:flex;align-items:center;gap:16px;">
<template v-if="isOwner">
<!-- <button class="edit-btn big-action-btn" @click="openEditModal">修改</button>-->
<button class="edit-btn big-action-btn" @click="openEditModal">修改</button>
<button class="delete-btn big-action-btn" @click="handleDeleteDemand">删除</button>
</template>
<button class="close-btn" @click="closeModal">&times;</button>