地图查询构造器

This commit is contained in:
Kunagisa 2025-06-24 22:54:04 +08:00
parent 912d2d4a12
commit 4ac36abfe3
3 changed files with 299 additions and 30 deletions

View File

@ -92,3 +92,20 @@ export const getAllMapRating = async () => {
}
};
/**
* 依据条件搜索地图
* @param {Object} params - 查询参数对象键为参数名值为参数值
* @returns {Promise<any>} 返回一个包含地图信息的Promise对象
*/
export const searchMapsByConditions = async (params) => {
try {
// 构建查询字符串
const query = new URLSearchParams(params).toString();
const response = await axiosInstance.get(`/map/search?${query}`);
return response.data;
} catch (error) {
console.error('依据条件搜索地图失败:', error);
throw error;
}
};

221
src/utils/MapQuery.js Normal file
View File

@ -0,0 +1,221 @@
/**
* 地图查询构造器
* 提供链式API来构建 /map/search 的查询参数.
*
* 根据API要求不同字段适用不同的查询方法:
* - **直接等值查询 (无前缀)**: `id`, `name`, `user` . 请使用 `.eq()` 或专用方法如 `.user()`.
* - **带规则查询 (有前缀)**: `size`, `player_count`, `download_count`, `favourite_count`. 请使用 `.equ()`, `.lss()`, `.geq()` 等方法.
* - **多值查询**: `tags`. 请使用 `.in()` 方法.
*
* **用法示例:**
* const params = new MapQuery()
* .user('张三')
* .leq('download_count', 100)
* .like('name', '测试')
* .in('tags', ['PVP', '特殊'])
* .build();
*/
export default class MapQuery {
constructor() {
this.params = {};
}
/**
* 设置 id
* @param {number} value
* @returns {MapQuery}
*/
id(value) {
return this.eq('id', value);
}
/**
* 设置 name
* @param {string} value
* @returns {MapQuery}
*/
name(value) {
return this.eq('name', value);
}
/**
* 设置 chinese_name
* @param {string} value
* @returns {MapQuery}
*/
chineseName(value) {
return this.eq('chinese_name', value);
}
/**
* 设置 user
* @param {string} value
* @returns {MapQuery}
*/
user(value) {
return this.eq('user', value);
}
/**
* 设置 create_time
* @param {string} value
* @returns {MapQuery}
*/
createTime(value) {
return this.eq('create_time', value);
}
/**
* 设置 author
* @param {string} value
* @returns {MapQuery}
*/
author(value) {
return this.eq('author', value);
}
/**
* 设置 img_file
* @param {string} value
* @returns {MapQuery}
*/
imgFile(value) {
return this.eq('img_file', value);
}
/**
* 设置 thumbnail
* @param {string} value
* @returns {MapQuery}
*/
thumbnail(value) {
return this.eq('thumbnail', value);
}
/**
* 设置 zip_file
* @param {string} value
* @returns {MapQuery}
*/
zipFile(value) {
return this.eq('zip_file', value);
}
/**
* 设置 file_name
* @param {string} value
* @returns {MapQuery}
*/
fileName(value) {
return this.eq('file_name', value);
}
/**
* 设置 verify_status
* @param {string} value
* @returns {MapQuery}
*/
verifyStatus(value) {
return this.eq('verify_status', value);
}
/**
* 等于 (e.g., `user='张三'`)
* @param {string} key - 参数名
* @param {string|number} value - 参数值
* @returns {MapQuery}
*/
eq(key, value) {
this.params[key] = value;
return this;
}
/**
* 等于 (带EQU前缀, e.g., `player_count='EQU4'`)
* @param {string} key - 参数名
* @param {string|number} value - 参数值
* @returns {MapQuery}
*/
equ(key, value) {
this.params[key] = `EQU${value}`;
return this;
}
/**
* 不等于 (e.g., `download_count='NEQ100'`)
* @param {string} key - 参数名
* @param {string|number} value - 参数值
* @returns {MapQuery}
*/
neq(key, value) {
this.params[key] = `NEQ${value}`;
return this;
}
/**
* 小于 (Less Than)
* @param {string} key - 参数名
* @param {number} value - 参数值
* @returns {MapQuery}
*/
lss(key, value) {
this.params[key] = `LSS${value}`;
return this;
}
/**
* 小于等于 (Less Than or Equal)
* @param {string} key - 参数名
* @param {number} value - 参数值
* @returns {MapQuery}
*/
leq(key, value) {
this.params[key] = `LEQ${value}`;
return this;
}
/**
* 大于 (Greater Than)
* @param {string} key - 参数名
* @param {number} value - 参数值
* @returns {MapQuery}
*/
gtr(key, value) {
this.params[key] = `GTR${value}`;
return this;
}
/**
* 大于等于 (Greater Than or Equal)
* @param {string} key - 参数名
* @param {number} value - 参数值
* @returns {MapQuery}
*/
geq(key, value) {
this.params[key] = `GEQ${value}`;
return this;
}
/**
* 模糊匹配
* @param {string} key - 参数名
* @param {string} value - 参数值
* @returns {MapQuery}
*/
like(key, value) {
this.params[key] = `LIKE${value}`;
return this;
}
/**
* 包含于 (用于 tags)
* @param {string} key - 参数名
* @param {Array<string>} arr - 参数值数组
* @returns {MapQuery}
*/
in(key, arr) {
if (Array.isArray(arr)) {
this.params[key] = arr.join(',');
}
return this;
}
/**
* 构建最终的参数对象
* @returns {Object}
*/
build() {
return { ...this.params };
}
}

View File

@ -4,7 +4,16 @@
<span class="back-icon"></span> 返回
</button>
<div class="page-header">
<h1>{{ authorName }}</h1>
<h1>{{ authorName }} 的地图</h1>
</div>
<div class="filters-row">
<div class="filters">
<label>玩家数</label>
<select v-model="playerCountFilter" class="filter-select" @change="handleFilterChange">
<option value="">全部</option>
<option v-for="n in [2,3,4,5,6,7,8]" :key="n" :value="n">{{ n }}</option>
</select>
</div>
</div>
<div v-if="isLoading" class="loading-container">
<div class="loading-bar">
@ -92,7 +101,9 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getMaps } from '@/api/maps.js'
import { searchMapsByConditions } from '@/api/centre_maps.js'
import MapQuery from '@/utils/MapQuery.js'
import { getAllTags } from '@/api/maps.js'
import ErrorDialog from '@/components/ErrorDialog.vue'
import '../../assets/styles/common.css'
import '../../assets/styles/Maps.css'
@ -103,6 +114,9 @@ const authorMaps = ref([])
const authorName = ref('')
const isLoading = ref(true)
//
const playerCountFilter = ref('')
//
const showError = ref(false)
const errorTitle = ref('')
@ -154,6 +168,11 @@ const handleJumpPage = () => {
jumpPage.value = ''
}
const handleFilterChange = () => {
currentPage.value = 1
fetchAuthorMaps()
}
//
const goBack = () => {
router.push('/author')
@ -183,34 +202,14 @@ const fetchAuthorMaps = async () => {
}
authorName.value = author
// 使API
const cacheKey = 'allMaps'
let allMaps = []
const cached = sessionStorage.getItem(cacheKey)
if (cached) {
allMaps = JSON.parse(cached)
} else {
//
let page = 1
let hasMore = true
while (hasMore) {
const response = await getMaps({
page: page,
ordering: '-create_time'
})
if (response.results && response.results.length > 0) {
allMaps = allMaps.concat(response.results)
hasMore = !!response.next
page++
} else {
hasMore = false
const query = new MapQuery().user(author)
if (playerCountFilter.value) {
query.equ('player_count', playerCountFilter.value)
}
}
sessionStorage.setItem(cacheKey, JSON.stringify(allMaps))
}
//
authorMaps.value = allMaps.filter(map => map.user === author)
console.log('作者地图列表:', authorMaps.value)
const params = query.build()
const maps = await searchMapsByConditions(params)
authorMaps.value = maps
} catch (error) {
console.error('获取作者地图列表失败:', error)
showErrorMessage('获取作者地图列表失败,请稍后重试')
@ -441,4 +440,36 @@ onMounted(() => {
font-size: 12px;
}
}
.filters-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 0;
}
.filters {
display: flex;
align-items: center;
gap: 12px;
}
.filters label {
font-size: 15px;
color: #222;
margin-right: 6px;
font-weight: 500;
}
.filter-select {
padding: 6px 12px;
border-radius: 4px;
border: 1px solid #ddd;
font-size: 15px;
background-color: #fff;
}
.filters-row, .filters {
box-shadow: none;
}
</style>