feat: implement MediaFormModal for adding new media entries, update Media interface to make 'id' optional, and refactor MediaItem to use utility for star ratings
This commit is contained in:
154
src/lib/MediaFormModal.svelte
Normal file
154
src/lib/MediaFormModal.svelte
Normal file
@@ -0,0 +1,154 @@
|
||||
<script lang="ts">
|
||||
import type { Media } from './interfaces';
|
||||
import { fade, scale } from 'svelte/transition';
|
||||
|
||||
let {show, mode, submitMedia, handleClose} = $props();
|
||||
let media: Media = $state({
|
||||
title: '',
|
||||
type: '',
|
||||
status: 'plan_to_watch',
|
||||
date: '',
|
||||
rating: 0,
|
||||
platform: '',
|
||||
notes: ''
|
||||
});
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 'plan_to_watch', label: '计划中' },
|
||||
{ value: 'in_progress', label: '进行中' },
|
||||
{ value: 'completed', label: '已完成' }
|
||||
] as const;
|
||||
|
||||
function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
// 暂时留空,后续处理
|
||||
submitMedia(media);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
|
||||
transition:fade={{ duration: 200 }}
|
||||
onclick={handleClose}
|
||||
role="presentation"
|
||||
>
|
||||
<div
|
||||
class="bg-white rounded-lg p-6 w-full mx-4 max-w-[800px]"
|
||||
transition:scale={{ duration: 200 }}
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
role="presentation"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 id="modal-title" class="text-xl font-semibold text-gray-900">
|
||||
{mode === 'add' ? '添加新作品' : '编辑作品'}
|
||||
</h2>
|
||||
<button
|
||||
class="text-gray-400 hover:text-gray-500"
|
||||
onclick={handleClose}
|
||||
type="button"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onsubmit={handleSubmit} class="space-y-4">
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="title">标题</label>
|
||||
<input
|
||||
id="title"
|
||||
type="text"
|
||||
bind:value={media.title}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="请输入标题"
|
||||
required
|
||||
aria-required="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="status">状态</label>
|
||||
<select
|
||||
id="status"
|
||||
bind:value={media.status}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
required
|
||||
aria-required="true"
|
||||
>
|
||||
{#each statusOptions as option}
|
||||
<option value={option.value}>{option.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="date">日期</label>
|
||||
<input
|
||||
id="date"
|
||||
type="date"
|
||||
bind:value={media.date}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
aria-label="选择日期"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="rating">评分</label>
|
||||
<input
|
||||
id="rating"
|
||||
type="number"
|
||||
min="0"
|
||||
max="10"
|
||||
step="0.5"
|
||||
bind:value={media.rating}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
aria-label="输入评分,范围0-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="platform">平台</label>
|
||||
<input
|
||||
id="platform"
|
||||
type="text"
|
||||
bind:value={media.platform}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="例如:Steam、Netflix、Kindle等"
|
||||
aria-label="输入平台名称"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center gap-6">
|
||||
<label class="font-medium text-gray-700 whitespace-nowrap" for="notes">备注</label>
|
||||
<textarea
|
||||
id="notes"
|
||||
bind:value={media.notes}
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
rows="3"
|
||||
placeholder="添加一些备注..."
|
||||
aria-label="添加备注信息"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
|
||||
onclick={handleClose}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
{mode === 'add' ? '添加' : '保存'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user