Files
gamegroup2/frontend/src/components/team/GameSelectDialog.vue
T
congsh 4b97c99e56 feat: add game library CRUD/import/export/favorites/comments, fix team creation
- Game library: add/delete games per group, JSON/CSV import/export, favorites, star ratings & comments
- Fix team session creation: add creator to members array, handle null currentGroup
- Fix image loading: rename SVG files from .png to .svg extensions
- Add PocketBase migrations for game_comments and game_favorites collections
- Remove seed data script

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 21:03:20 +08:00

181 lines
3.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { searchGames } from '@/api/games'
import type { Game } from '@/types'
const props = defineProps<{
modelValue: boolean
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
'select': [gameName: string]
}>()
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
const searchQuery = ref('')
const loading = ref(false)
const games = ref<Game[]>([])
const customGameName = ref('')
watch(() => props.modelValue, (show) => {
if (show) {
searchQuery.value = ''
customGameName.value = ''
games.value = []
}
})
async function handleSearch() {
if (!searchQuery.value.trim()) {
games.value = []
return
}
loading.value = true
try {
games.value = await searchGames(searchQuery.value)
} catch (error) {
console.error('搜索游戏失败:', error)
} finally {
loading.value = false
}
}
function selectGame(game: Game) {
emit('select', game.name)
visible.value = false
}
function confirmCustomGame() {
if (customGameName.value.trim()) {
emit('select', customGameName.value.trim())
visible.value = false
}
}
</script>
<template>
<el-dialog
v-model="visible"
title="选择游戏"
width="480px"
>
<div class="game-select">
<el-input
v-model="searchQuery"
placeholder="搜索游戏..."
clearable
@input="handleSearch"
/>
<div v-if="loading" class="loading">搜索中...</div>
<div v-else-if="games.length > 0" class="game-list">
<div
v-for="game in games"
:key="game.id"
class="game-item"
@click="selectGame(game)"
>
<img
:src="game.cover || '/game-placeholder.svg'"
:alt="game.name"
class="game-cover"
/>
<div class="game-info">
<span class="game-name">{{ game.name }}</span>
<span class="game-platform">{{ game.platform }}</span>
</div>
</div>
</div>
<div v-else-if="searchQuery" class="no-results">
未找到匹配的游戏手动输入
</div>
<div class="custom-input">
<el-input
v-model="customGameName"
placeholder="手动输入游戏名称"
@keyup.enter="confirmCustomGame"
/>
<el-button
type="primary"
:disabled="!customGameName.trim()"
@click="confirmCustomGame"
>
确认
</el-button>
</div>
</div>
</el-dialog>
</template>
<style scoped>
.game-select {
display: flex;
flex-direction: column;
gap: 16px;
}
.loading, .no-results {
text-align: center;
color: var(--el-text-color-secondary);
padding: 24px;
}
.game-list {
max-height: 320px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 8px;
}
.game-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
}
.game-item:hover {
background: var(--el-fill-color-light);
}
.game-cover {
width: 40px;
height: 40px;
border-radius: 6px;
object-fit: cover;
}
.game-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.game-name {
font-size: 14px;
font-weight: 500;
}
.game-platform {
font-size: 12px;
color: var(--el-text-color-secondary);
}
.custom-input {
display: flex;
gap: 8px;
}
</style>