活跃作者推荐

This commit is contained in:
Kunagisa 2025-06-12 22:16:46 +08:00
parent a949996907
commit 56b1d23b8a
5 changed files with 300 additions and 39 deletions

11
src/api/centre_maps.js Normal file
View File

@ -0,0 +1,11 @@
import axiosInstance from './axiosConfig';
export const getMapEditors = async () => {
try {
const response = await axiosInstance.get('/map/editors');
return response.data;
} catch (error) {
throw error;
}
};

View File

@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router'
import { hasValidToken, getUserInfo, logoutUser } from '../utils/jwt';
import { justLoggedIn } from '../utils/authSessionState';
import EditorsMaps from '@/views/index/EditorsMaps.vue'
const routes = [
{
@ -70,6 +71,11 @@ const routes = [
name: 'CompetitionSignUp',
component: () => import('@/views/index/CompetitionSignUp.vue'),
meta: { requiresAuth: true }
},
{
path: 'editors-maps',
name: 'EditorsMaps',
component: () => import('@/views/index/EditorsMaps.vue')
}
]
},

View File

@ -1,61 +1,166 @@
<template>
<div class="weekly-recommend">
<div class="maps">
<div class="page-header">
<h1>活跃作者推荐</h1>
<div class="header-subtitle">
<span class="date-range">{{ currentWeekRange }}</span>
</div>
</div>
<div class="table-container">
<table class="maps-table">
<thead>
<tr>
<th>序号</th>
<th>预览图</th>
<th>地图名称</th>
<th>作者</th>
<th>下载次数</th>
<th>收藏次数</th>
<th>玩家数量</th>
<th>创建时间</th>
<th>标签</th>
</tr>
<tr>
<th class="rank-column">排名</th>
<th>作者名称</th>
<th>积分</th>
<th>最近三个月活跃</th>
<th>最近一个月活跃</th>
</tr>
</thead>
<tbody>
<tr v-for="(map, index) in recommendedMaps" :key="map.id" @click="goToMapDetail(map.id)" class="table-row">
<td class="rank-number">{{ index + 1 }}</td>
<td class="preview-cell">
<img :src="map.thumbnail" :alt="map.chinese_name">
</td>
<td class="map-name">{{ map.chinese_name }}</td>
<td>{{ map.user }}</td>
<td>{{ map.download_count }}</td>
<td>{{ map.favourite_count }}</td>
<td>{{ map.player_count }}</td>
<td>{{ formatDate(map.create_time) }}</td>
<td>
<div class="tags">
<span v-for="tag in map.tags" :key="tag" class="tag">{{ tag }}</span>
</div>
</td>
</tr>
<tr v-for="(editor, index) in displayedEditors" :key="editor.update_editor" class="table-row" @click="goToAuthorMaps(editor.update_editor)">
<td class="rank-column">{{ index + 1 }}</td>
<td class="author-name">{{ editor.update_editor }}</td>
<td>{{ editor.credits }}</td>
<td>
<span :class="['status-badge', editor.three_month_live ? 'active' : 'inactive']">
{{ editor.three_month_live ? '是' : '否' }}
</span>
</td>
<td>
<span :class="['status-badge', editor.one_month_live ? 'active' : 'inactive']">
{{ editor.one_month_live ? '是' : '否' }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="expand-button" @click="toggleExpand">
{{ isExpanded ? '收起' : '展开更多' }}
</div>
</div>
</template>
<script setup>
import {ref, computed, onMounted} from 'vue'
import { useRouter } from 'vue-router'
import { getMapEditors } from '@/api/centre_maps.js'
import '../../assets/styles/common.css'
import '../../assets/styles/Maps.css'
const router = useRouter()
const sortedEditors = ref([])
const isExpanded = ref(false)
//
const displayedEditors = computed(() => {
return isExpanded.value ? sortedEditors.value : sortedEditors.value.slice(0, 10)
})
//
const goToAuthorMaps = (authorName) => {
router.push({
name: 'EditorsMaps',
query: {
author: authorName
}
})
}
const editorsList = async () => {
try {
//
const editorsData = await getMapEditors();
sortedEditors.value = editorsData.sort((a, b) => b.credits - a.credits);
console.log('排序后的作者列表:', sortedEditors.value);
} catch (error) {
console.error('获取列表失败:', error);
}
}
//
const toggleExpand = () => {
isExpanded.value = !isExpanded.value
}
onMounted(() => {
editorsList()
})
</script>
<style scoped>
.rank-number {
.maps-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.maps-table th,
.maps-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.maps-table th {
background-color: #f5f5f5;
font-weight: bold;
}
.rank-column {
width: 80px;
text-align: center;
font-weight: bold;
color: #1a237e;
text-align: center;
width: 50px;
font-size: 1.1em;
}
.table-row {
transition: background-color 0.2s;
}
.table-row:hover {
background-color: #f5f5f5;
cursor: pointer;
}
.author-name {
font-weight: bold;
color: #1a237e;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.9em;
font-weight: 500;
}
.status-badge.active {
background-color: #e8f5e9;
color: #2e7d32;
}
.status-badge.inactive {
background-color: #ffebee;
color: #c62828;
}
.expand-button {
display: flex;
justify-content: center;
align-items: center;
margin: 20px 0;
padding: 10px 20px;
background-color: #1a237e;
color: white;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
width: fit-content;
margin-left: auto;
margin-right: auto;
}
.expand-button:hover {
background-color: #283593;
}
</style>

View File

@ -113,6 +113,23 @@
<span class="label">发布时间</span>
<span class="value">{{ formatDate(selectedDemand?.date) }}</span>
</div>
<div class="reply-section">
<h3>回复内容</h3>
<div class="reply-list">
<div v-if="selectedDemand?.replies && selectedDemand.replies.length > 0">
<div v-for="reply in selectedDemand.replies" :key="reply.id" class="reply-item">
<div class="reply-header">
<span class="reply-author">{{ reply.author || '匿名用户' }}</span>
<span class="reply-time">{{ formatDate(reply.time) }}</span>
</div>
<div class="reply-content">{{ reply.content }}</div>
</div>
</div>
<div v-else class="no-reply">
暂无回复
</div>
</div>
</div>
</div>
</div>
</div>
@ -427,7 +444,7 @@ function autoResize() {
background: white;
border-radius: 8px;
width: 90%;
max-width: 600px;
max-width: 800px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
@ -588,4 +605,57 @@ function autoResize() {
line-height: 1.6;
overflow-y: auto;
}
.reply-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.reply-section h3 {
color: #1a237e;
margin-bottom: 15px;
font-size: 1.2rem;
}
.reply-list {
max-height: 300px;
overflow-y: auto;
}
.reply-item {
background: #f7faff;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
}
.reply-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.reply-author {
font-weight: 600;
color: #1a237e;
}
.reply-time {
color: #666;
font-size: 0.9em;
}
.reply-content {
color: #333;
line-height: 1.5;
}
.no-reply {
text-align: center;
color: #666;
padding: 20px;
background: #f7faff;
border-radius: 8px;
}
</style>

View File

@ -256,5 +256,74 @@ onMounted(() => {
</script>
<style scoped>
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin: 30px 0 0 0;
}
.page-btn {
padding: 6px 16px;
border: none;
background: #e3f0ff;
color: #2563eb;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.page-btn:disabled {
background: #f5f5f5;
color: #aaa;
cursor: not-allowed;
}
.page-numbers {
display: flex;
gap: 4px;
}
.page-number {
padding: 6px 12px;
border: none;
background: #f5f5f5;
color: #2563eb;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.page-number.active {
background: #2563eb;
color: #fff;
}
.page-ellipsis {
padding: 6px 8px;
color: #aaa;
font-size: 14px;
}
.page-jump {
display: flex;
align-items: center;
gap: 4px;
}
.jump-input {
width: 50px;
padding: 4px 6px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.jump-btn {
padding: 4px 10px;
background: #2563eb;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.jump-btn:hover {
background: #1a237e;
}
</style>