diff --git a/backend/pb_migrations/1776938195_updated_games_cover_to_file.js b/backend/pb_migrations/1776938195_updated_games_cover_to_file.js new file mode 100644 index 0000000..a7276c1 --- /dev/null +++ b/backend/pb_migrations/1776938195_updated_games_cover_to_file.js @@ -0,0 +1,51 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("x5adjlc0txf16r8") + + // remove old url cover field + collection.schema.removeField("cover_field") + + // add new file cover field + collection.schema.addField(new SchemaField({ + "system": false, + "id": "cover_file", + "name": "cover", + "type": "file", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "maxSize": 5242880, + "mimeTypes": ["image/jpeg","image/png","image/gif","image/webp","image/svg+xml"], + "thumbs": null, + "protected": false + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("x5adjlc0txf16r8") + + // remove file cover field + collection.schema.removeField("cover_file") + + // restore url cover field + collection.schema.addField(new SchemaField({ + "system": false, + "id": "cover_field", + "name": "cover", + "type": "url", + "required": false, + "presentable": false, + "unique": false, + "options": { + "exceptDomains": null, + "onlyDomains": null + } + })) + + return dao.saveCollection(collection) +}) diff --git a/frontend/src/api/games.ts b/frontend/src/api/games.ts index 8f49f30..9997810 100644 --- a/frontend/src/api/games.ts +++ b/frontend/src/api/games.ts @@ -1,6 +1,12 @@ import pb from './pocketbase' import type { Game, GamePlatform, GameComment } from '@/types' +// 生成游戏封面 URL +export function getGameCoverUrl(game: Game, thumb?: string): string { + if (!game.cover) return '' + return pb.files.getUrl(game, game.cover, thumb ? { thumb } : undefined) as string +} + // 获取群组的游戏列表 export async function getGroupGames(groupId: string, options?: { page?: number @@ -26,15 +32,19 @@ export async function addGame(groupId: string, data: { name: string platform?: GamePlatform tags?: string[] - cover?: string + coverFile?: File }) { const user = pb.authStore.model - return pb.collection('games').create({ - ...data, - group: groupId, - addedBy: user?.id, - popularCount: 0 - }, { $autoCancel: false }) + const formData = new FormData() + formData.append('name', data.name) + formData.append('group', groupId) + formData.append('addedBy', user?.id || '') + formData.append('popularCount', '0') + if (data.platform) formData.append('platform', data.platform) + if (data.tags && data.tags.length > 0) formData.append('tags', JSON.stringify(data.tags)) + if (data.coverFile) formData.append('cover', data.coverFile) + + return pb.collection('games').create(formData, { $autoCancel: false }) } // 删除游戏 @@ -42,12 +52,11 @@ export async function deleteGame(gameId: string) { return pb.collection('games').delete(gameId, { $autoCancel: false }) } -// 导入游戏(批量添加) +// 导入游戏(批量添加,无封面文件) export async function importGames(groupId: string, games: Array<{ name: string platform?: GamePlatform tags?: string[] - cover?: string }>) { const results = [] for (const game of games) { diff --git a/frontend/src/components/game/AddGameDialog.vue b/frontend/src/components/game/AddGameDialog.vue index de98e91..4b3588c 100644 --- a/frontend/src/components/game/AddGameDialog.vue +++ b/frontend/src/components/game/AddGameDialog.vue @@ -4,6 +4,8 @@ import { addGame } from '@/api/games' import type { GamePlatform } from '@/types' import { ElMessage } from 'element-plus' import { getAllPlatforms } from '@/api/games' +import { Plus } from '@element-plus/icons-vue' +import type { UploadFile } from 'element-plus' const props = defineProps<{ modelValue: boolean @@ -23,10 +25,23 @@ const visible = computed({ const name = ref('') const platform = ref('') const tagsInput = ref('') -const cover = ref('') +const coverFile = ref(null) +const coverPreview = ref('') const loading = ref(false) const platforms = getAllPlatforms() +function handleFileChange(uploadFile: UploadFile) { + if (uploadFile.raw) { + coverFile.value = uploadFile.raw + coverPreview.value = URL.createObjectURL(uploadFile.raw) + } +} + +function handleFileRemove() { + coverFile.value = null + coverPreview.value = '' +} + async function handleSubmit() { if (!name.value.trim()) { ElMessage.warning('请输入游戏名称') @@ -39,13 +54,14 @@ async function handleSubmit() { name: name.value.trim(), platform: platform.value || undefined, tags: tags.length > 0 ? tags : undefined, - cover: cover.value.trim() || undefined + coverFile: coverFile.value || undefined }) ElMessage.success('添加成功') name.value = '' platform.value = '' tagsInput.value = '' - cover.value = '' + coverFile.value = null + coverPreview.value = '' emit('created') visible.value = false } catch (error: any) { @@ -74,8 +90,23 @@ async function handleSubmit() {
- - + + +
+ 封面预览 +
+
+ + 点击或拖拽上传封面 +
+