加入了node_modules
添加了新的功能项
This commit is contained in:
71
src/App.vue
71
src/App.vue
@@ -8,23 +8,21 @@
|
||||
<div class="nav-container">
|
||||
<div class="nav-left">
|
||||
<div class="nav-brand">红色警戒3数据分析中心</div>
|
||||
<router-link to="/" class="nav-link">热门下载地图</router-link>
|
||||
<<<<<<< HEAD
|
||||
|
||||
<router-link to="/weekly" class="nav-link">每周推荐</router-link>
|
||||
<router-link to="/" class="nav-link">最近上传地图</router-link>
|
||||
<router-link to="/weekly" class="nav-link">热门下载地图</router-link>
|
||||
<router-link to="/weapon-match" class="nav-link">Weapon 匹配</router-link>
|
||||
<router-link to="/demands" class="nav-link">办事大厅</router-link>
|
||||
<router-link to="/weapon-match" class="nav-link">Weapon 匹配</router-link>
|
||||
|
||||
=======
|
||||
<router-link to="/weekly" class="nav-link">每周推荐</router-link>
|
||||
<router-link to="/weapon-match" class="nav-link">Weapon 匹配</router-link>
|
||||
>>>>>>> 062550d1260d3f66fd69a2d954f426abb5531511
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="main-content">
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<div class="footer-bottom">
|
||||
<p>Byz解忧杂货铺</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -105,7 +103,7 @@ body {
|
||||
margin-top: 60px;
|
||||
padding: 20px;
|
||||
flex: 1;
|
||||
max-width: 1200px;
|
||||
max-width: 1400px;
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@@ -133,4 +131,55 @@ body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: linear-gradient(135deg, #416bdf 0%, #71eaeb 100%);
|
||||
color: white;
|
||||
padding: 40px 0 20px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.footer-section h3 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer-section p {
|
||||
margin: 8px 0;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.footer-bottom p {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 30px 0 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -81,4 +81,45 @@
|
||||
margin: 0 -20px;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 渐变按钮基础样式 */
|
||||
.gradient-btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: linear-gradient(135deg, #71eaeb 0%, #416bdf 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gradient-btn:hover {
|
||||
background: linear-gradient(135deg, #416bdf 0%, #71eaeb 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.gradient-btn:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 按钮变体 */
|
||||
.gradient-btn.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gradient-btn.left {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.gradient-btn.center {
|
||||
margin: 20px auto;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './assets/styles/common.css'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
|
||||
@@ -2,14 +2,11 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
path: '/demands',
|
||||
name: 'DemandList',
|
||||
component: () => import('../views/DemandList.vue')
|
||||
},
|
||||
{
|
||||
=======
|
||||
>>>>>>> 062550d1260d3f66fd69a2d954f426abb5531511
|
||||
path: '/',
|
||||
name: 'Maps',
|
||||
component: () => import('../views/Maps.vue')
|
||||
|
||||
@@ -1,221 +1,531 @@
|
||||
<template>
|
||||
<div class="demand-hall">
|
||||
<header>
|
||||
<h1>办事大厅</h1>
|
||||
</header>
|
||||
|
||||
<div class="app-content">
|
||||
<div v-if="loading" class="loading">加载中...</div>
|
||||
<div v-if="error" class="error">{{ error }}</div>
|
||||
|
||||
<div class="demand-list">
|
||||
<div v-for="demand in demands" :key="demand.id || demand.requester + demand.content" class="demand-card">
|
||||
<div class="demand-header">
|
||||
<span class="requester">{{ demand.requester || '匿名' }}</span>
|
||||
<span class="reward" :class="{ 'no-reward': isNoReward(demand.reward) }">
|
||||
{{ demand.reward || '无赏金' }}
|
||||
<div class="demand-hall">
|
||||
<div class="page-header">
|
||||
<h1>需求列表</h1>
|
||||
</div>
|
||||
<button class="btn-common btn-gradient btn-margin-right" @click="openAddModal">添加需求</button>
|
||||
<button class="btn-common btn-light" @click="fetchDemands">刷新</button>
|
||||
<div class="table-container">
|
||||
<table class="maps-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>请求者</th>
|
||||
<th>QQ号</th>
|
||||
<th>请求内容</th>
|
||||
<th>悬赏金额</th>
|
||||
<th>需求创建时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(demand, index) in demands" :key="demand.id" class="table-row" @click="showDetail(demand)">
|
||||
<td class="id">{{ index + 1 }}</td>
|
||||
<td class="requester">
|
||||
<span v-if="!demand.requester" class="tag no-reward">匿名</span>
|
||||
<span v-else>{{ demand.requester }}</span></td>
|
||||
<td class="name">
|
||||
<span v-if="!demand.qq_code" class="tag no-reward">匿名</span>
|
||||
<span v-else>{{ demand.qq_code }}</span>
|
||||
</td>
|
||||
<td class="content">{{ demand.content }}</td>
|
||||
<td class="reward">
|
||||
<span :class="['tag', isNoReward(demand.reward) ? 'no-reward' : 'has-reward']">
|
||||
{{ isNoReward(demand.reward) ? '无赏金' : demand.reward }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="content">{{ demand.content || '无具体内容' }}</div>
|
||||
<div class="date">{{ formatDate(demand.date) }}</div>
|
||||
</td>
|
||||
<td class="date">{{ formatDate(demand.date) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<div v-if="showModal" class="modal-overlay" @click="closeModal">
|
||||
<div class="modal-content" @click.stop>
|
||||
<div class="modal-header">
|
||||
<h2>需求详情</h2>
|
||||
<button class="close-btn" @click="closeModal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="detail-item">
|
||||
<span class="label">QQ号:</span>
|
||||
<span v-if="!selectedDemand?.qq_code" class="tag no-reward">匿名</span>
|
||||
<span v-else>{{ selectedDemand?.qq_code }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">请求者:</span>
|
||||
<span v-if="!selectedDemand?.requester" class="tag no-reward">匿名</span>
|
||||
<span v-else>{{ selectedDemand?.requester }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">需求内容:</span>
|
||||
<span class="value">{{ selectedDemand?.content }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">赏金:</span>
|
||||
<span :class="['tag', isNoReward(selectedDemand?.reward) ? 'no-reward' : 'has-reward']">
|
||||
{{ isNoReward(selectedDemand?.reward) ? '无赏金' : selectedDemand?.reward }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">发布时间:</span>
|
||||
<span class="value">{{ formatDate(selectedDemand?.date) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="refresh-btn" @click="fetchDemands">刷新列表</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const demands = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
// 判断是否为无赏金
|
||||
const isNoReward = (reward) => {
|
||||
return !reward || reward === '无赏金'
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString || dateString === 'Test_date') return '日期未提供'
|
||||
|
||||
try {
|
||||
const date = new Date(dateString)
|
||||
if (isNaN(date.getTime())) {
|
||||
return dateString
|
||||
}
|
||||
|
||||
const pad = num => num.toString().padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
} catch (e) {
|
||||
|
||||
<!-- 添加需求弹窗 -->
|
||||
<div v-if="showAddModal" class="modal-overlay" @click="closeAddModal">
|
||||
<div class="modal-content" @click.stop>
|
||||
<div class="modal-header">
|
||||
<h2>添加需求</h2>
|
||||
<button class="close-btn" @click="closeAddModal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="add-modal-form" @submit.prevent="submitAddForm">
|
||||
<div class="form-row">
|
||||
<span class="label">请求者:</span>
|
||||
<input v-model="addForm.requester" class="input" placeholder="可选" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<span class="label">QQ号:</span>
|
||||
<input v-model="addForm.qq_code" class="input" placeholder="可选" />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<span class="label">需求内容:</span>
|
||||
<textarea
|
||||
v-model="addForm.sendcontent"
|
||||
class="input"
|
||||
placeholder="请输入需求内容"
|
||||
rows="3"
|
||||
ref="autoTextarea"
|
||||
@input="autoResize"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<span class="label">赏金:</span>
|
||||
<input v-model="addForm.reward" class="input" placeholder="可选" />
|
||||
</div>
|
||||
<div v-if="addError" class="error">{{ addError }}</div>
|
||||
<button class="btn-common btn-gradient submit-btn" :disabled="addLoading">
|
||||
{{ addLoading ? '提交中...' : '提交' }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const demands = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const showModal = ref(false)
|
||||
const selectedDemand = ref(null)
|
||||
const showAddModal = ref(false)
|
||||
const addError = ref('')
|
||||
const addForm = ref({
|
||||
requester: '',
|
||||
sendcontent: '',
|
||||
content: '',
|
||||
reward: '',
|
||||
qq_code: ''
|
||||
})
|
||||
const addLoading = ref(false)
|
||||
const autoTextarea = ref(null)
|
||||
|
||||
// 判断是否为无赏金
|
||||
const isNoReward = (reward) => {
|
||||
return !reward || reward === '无赏金'
|
||||
}
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString || dateString === 'Test_date') return '日期未提供'
|
||||
|
||||
try {
|
||||
const date = new Date(dateString)
|
||||
if (isNaN(date.getTime())) {
|
||||
return dateString
|
||||
}
|
||||
|
||||
const pad = num => num.toString().padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
} catch (e) {
|
||||
return dateString
|
||||
}
|
||||
|
||||
// 获取需求列表
|
||||
const fetchDemands = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
// 使用代理解决跨域问题
|
||||
const targetUrl = 'http://49.232.8.189:8000/demands/getlist'
|
||||
|
||||
const response = await fetch(targetUrl, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败: ${response.status}`)
|
||||
}
|
||||
|
||||
// 获取需求列表
|
||||
const fetchDemands = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await fetch('/api/demands/getlist', {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
demands.value = data
|
||||
} catch (err) {
|
||||
error.value = `加载失败: ${err.message}`
|
||||
console.error('加载需求列表失败:', err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
demands.value = data
|
||||
} catch (err) {
|
||||
error.value = `加载失败: ${err.message}`
|
||||
console.error('加载需求列表失败:', err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 组件挂载时自动加载数据
|
||||
onMounted(() => {
|
||||
fetchDemands()
|
||||
}
|
||||
|
||||
// 显示详情弹窗
|
||||
const showDetail = (demand) => {
|
||||
selectedDemand.value = demand
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
const closeModal = () => {
|
||||
showModal.value = false
|
||||
selectedDemand.value = null
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
function openAddModal() {
|
||||
addForm.value = {
|
||||
requester: '',
|
||||
sendcontent: '',
|
||||
content: '',
|
||||
reward: '',
|
||||
qq_code: ''
|
||||
}
|
||||
addError.value = ''
|
||||
showAddModal.value = true
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function closeAddModal() {
|
||||
showAddModal.value = false
|
||||
addError.value = ''
|
||||
}
|
||||
|
||||
//提交表单
|
||||
async function submitAddForm() {
|
||||
if (!addForm.value.sendcontent.trim()) {
|
||||
addError.value = '需求内容不能为空';
|
||||
return;
|
||||
}
|
||||
addLoading.value = true;
|
||||
addError.value = '';
|
||||
try {
|
||||
const now = new Date();
|
||||
const dateStr = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2,'0')}-${now.getDate().toString().padStart(2,'0')} ${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}:${now.getSeconds().toString().padStart(2,'0')}`;
|
||||
const payload = {
|
||||
requester: addForm.value.requester , // 如果requester为空,则使用qq_code
|
||||
sendcontent: addForm.value.sendcontent,
|
||||
content: addForm.value.sendcontent,
|
||||
reward: addForm.value.reward,
|
||||
date: dateStr,
|
||||
qq_code: addForm.value.qq_code
|
||||
};
|
||||
const response = await fetch('/api/demands/add', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
if (!response.ok) throw new Error('提交失败')
|
||||
showAddModal.value = false
|
||||
fetchDemands() // 刷新列表
|
||||
} catch (e) {
|
||||
addError.value = '提交失败,请稍后重试'
|
||||
} finally {
|
||||
addLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时自动加载数据
|
||||
onMounted(() => {
|
||||
fetchDemands()
|
||||
})
|
||||
|
||||
function autoResize() {
|
||||
nextTick(() => {
|
||||
const el = autoTextarea.value
|
||||
if (el) {
|
||||
el.style.height = 'auto'
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.demand-hall {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
border-radius: 0 0 5px 5px;
|
||||
}
|
||||
|
||||
.demand-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.demand-card {
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.demand-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.demand-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.requester {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.reward {
|
||||
background-color: #fadb14;
|
||||
color: #333;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.reward.no-reward {
|
||||
background-color: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.content {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.error {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #f5222d;
|
||||
background-color: #fff1f0;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
padding: 10px 20px;
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
background-color: #40a9ff;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.demand-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.demand-hall {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn-common {
|
||||
display: inline-block;
|
||||
padding: 8px 22px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #b6d2ff;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, color 0.2s, border 0.2s;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.btn-gradient {
|
||||
background: linear-gradient(90deg, #71eaeb 0%, #416bdf 100%);
|
||||
color: #fff;
|
||||
border: 1px solid #71eaeb;
|
||||
}
|
||||
.btn-gradient:hover {
|
||||
background: linear-gradient(90deg, #416bdf 0%, #71eaeb 100%);
|
||||
color: #fff;
|
||||
border: 1.5px solid #416bdf;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
background: linear-gradient(90deg, #e3f0ff 0%, #f7fbff 100%);
|
||||
color: #2563eb;
|
||||
border: 1px solid #b6d2ff;
|
||||
}
|
||||
.btn-light:hover {
|
||||
background: linear-gradient(90deg, #d0e7ff 0%, #eaf4ff 100%);
|
||||
color: #174ea6;
|
||||
border: 1.5px solid #2563eb;
|
||||
}
|
||||
|
||||
/* 按钮间距 */
|
||||
.btn-margin-right {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 300px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 彻底重写 tag 样式,保证无阴影、紧凑、圆角 */
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 2px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.has-reward {
|
||||
background: #fff7e6;
|
||||
color: #d46b08;
|
||||
border: 1px solid #ffd591;
|
||||
}
|
||||
|
||||
.no-reward {
|
||||
background: #f5f5f5;
|
||||
color: #8c8c8c;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.maps-table td.reward,
|
||||
.maps-table tr {
|
||||
box-shadow: none !important;
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 80px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
color: #1a237e;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: #1a237e;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.detail-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 600;
|
||||
color: #1a237e;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.maps-table tr {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.maps-table th,
|
||||
.maps-table td {
|
||||
text-align: center;
|
||||
vertical-align: middle !important;
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
.maps-table td.reward {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 70%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 0 0 2px #e3f0ff;
|
||||
}
|
||||
.error {
|
||||
color: #f5222d;
|
||||
background: #fff1f0;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
margin: 10px 0 0 0;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-modal-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.add-modal-form .form-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.add-modal-form .label {
|
||||
min-width: 70px;
|
||||
font-weight: 600;
|
||||
color: #1a237e;
|
||||
margin-right: 0;
|
||||
text-align: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.add-modal-form .input,
|
||||
.add-modal-form textarea {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 5px;
|
||||
font-size: 15px;
|
||||
background: #f7faff;
|
||||
transition: border 0.2s, box-shadow 0.2s;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.add-modal-form .input:focus,
|
||||
.add-modal-form textarea:focus {
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 0 0 2px #e3f0ff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.add-modal-form .error {
|
||||
color: #f5222d;
|
||||
background: #fff1f0;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.add-modal-form .submit-btn {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.add-modal-form textarea {
|
||||
min-height: 60px;
|
||||
max-height: 200px;
|
||||
resize: vertical;
|
||||
line-height: 1.6;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="maps">
|
||||
<div class="page-header">
|
||||
<h1>热门下载地图</h1>
|
||||
<h1>最近上传地图</h1>
|
||||
</div>
|
||||
<div class="filters">
|
||||
<div class="search-box">
|
||||
@@ -115,7 +115,6 @@ import '../assets/styles/common.css'
|
||||
import '../assets/styles/Maps.css'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const maps = ref([])
|
||||
const currentPage = ref(1)
|
||||
const totalPages = ref(1)
|
||||
@@ -124,14 +123,14 @@ const jumpPage = ref('')
|
||||
|
||||
// 排序和筛选相关
|
||||
const orderOptions = [
|
||||
{ label: '创建时间降序', value: '-create_time' },
|
||||
{ label: '创建时间升序', value: 'create_time' },
|
||||
{ label: '下载量降序', value: '-download_count' },
|
||||
{ label: '下载量升序', value: 'download_count' },
|
||||
{ label: '收藏数降序', value: '-favourite_count' },
|
||||
{ label: '收藏数升序', value: 'favourite_count' },
|
||||
{ label: '创建时间降序', value: '-create_time' },
|
||||
{ label: '创建时间升序', value: 'create_time' }
|
||||
{ label: '收藏数升序', value: 'favourite_count' }
|
||||
]
|
||||
const selectedOrder = ref('-download_count')
|
||||
const selectedOrder = ref('-create_time')
|
||||
const searchValue = ref('')
|
||||
const playerCountFilter = ref('')
|
||||
const tagFilter = ref('')
|
||||
@@ -170,9 +169,40 @@ const fetchMaps = async (page = 1) => {
|
||||
tags: tagFilter.value,
|
||||
ordering: selectedOrder.value
|
||||
}
|
||||
console.log('正在获取地图数据,参数:', params)
|
||||
const data = await getMaps(params)
|
||||
maps.value = data.results
|
||||
|
||||
// 只在创建时间排序时进行日期分组和下载量排序
|
||||
if (selectedOrder.value === '-create_time' || selectedOrder.value === 'create_time') {
|
||||
// 按日期分组并排序
|
||||
const groupedMaps = data.results.reduce((groups, map) => {
|
||||
const date = new Date(map.create_time).toLocaleDateString('zh-CN')
|
||||
if (!groups[date]) {
|
||||
groups[date] = []
|
||||
}
|
||||
groups[date].push(map)
|
||||
return groups
|
||||
}, {})
|
||||
|
||||
// 对每个日期组内的地图按下载量降序排序
|
||||
Object.keys(groupedMaps).forEach(date => {
|
||||
groupedMaps[date].sort((a, b) => b.download_count - a.download_count)
|
||||
})
|
||||
|
||||
// 获取所有日期并排序
|
||||
const sortedDates = Object.keys(groupedMaps).sort((a, b) => {
|
||||
return selectedOrder.value === '-create_time'
|
||||
? new Date(b) - new Date(a) // 降序
|
||||
: new Date(a) - new Date(b) // 升序
|
||||
})
|
||||
|
||||
// 按日期顺序拼接所有地图
|
||||
maps.value = sortedDates.reduce((acc, date) => {
|
||||
return acc.concat(groupedMaps[date])
|
||||
}, [])
|
||||
} else {
|
||||
maps.value = data.results
|
||||
}
|
||||
|
||||
hasNextPage.value = !!data.next
|
||||
totalPages.value = Math.ceil(data.count / 20)
|
||||
currentPage.value = page
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="weekly-recommend">
|
||||
<div class="page-header">
|
||||
<h1>每周地图推荐</h1>
|
||||
<h1>热门下载地图</h1>
|
||||
<div class="header-subtitle">
|
||||
<span class="date-range">{{ currentWeekRange }}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user