119 lines
4.3 KiB
Svelte
119 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
import { REVIEW_TAGS, PRICE_LABELS, type ReviewTag, type PriceLabel, type ReviewCard } from '$lib/types/review';
|
|
|
|
let { open = false, onClose, onAdd } = $props<{ open?: boolean; onClose: () => void; onAdd: (card: Omit<ReviewCard, 'id' | 'createdAt' | 'updatedAt' | 'starred'>) => void }>();
|
|
|
|
let form = $state({
|
|
title: '',
|
|
content: '',
|
|
tags: [] as ReviewTag[],
|
|
stock: {
|
|
name: '',
|
|
code: '',
|
|
amount: 0,
|
|
priceGroups: [
|
|
{ price: 0, label: PRICE_LABELS[0] as PriceLabel }
|
|
]
|
|
}
|
|
});
|
|
|
|
function addPriceGroup() {
|
|
form.stock.priceGroups.push({ price: 0, label: PRICE_LABELS[0] });
|
|
}
|
|
function removePriceGroup(idx: number) {
|
|
if (form.stock.priceGroups.length > 1) {
|
|
form.stock.priceGroups.splice(idx, 1);
|
|
}
|
|
}
|
|
function resetForm() {
|
|
form.title = '';
|
|
form.content = '';
|
|
form.tags = [];
|
|
form.stock = {
|
|
name: '',
|
|
code: '',
|
|
amount: 0,
|
|
priceGroups: [
|
|
{ price: 0, label: PRICE_LABELS[0] }
|
|
]
|
|
};
|
|
}
|
|
function handleSubmit() {
|
|
if (!form.title.trim() || !form.content.trim()) return;
|
|
onAdd({
|
|
title: form.title,
|
|
content: form.content,
|
|
tags: [...form.tags],
|
|
stock: {
|
|
name: form.stock.name,
|
|
code: form.stock.code,
|
|
amount: form.stock.amount,
|
|
priceGroups: form.stock.priceGroups.map(pg => ({ ...pg }))
|
|
}
|
|
});
|
|
resetForm();
|
|
onClose();
|
|
}
|
|
function handleCancel() {
|
|
resetForm();
|
|
onClose();
|
|
}
|
|
</script>
|
|
|
|
{#if open}
|
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/30 backdrop-blur-sm">
|
|
<div class="bg-white dark:bg-zinc-900 rounded-xl shadow-xl p-6 w-full max-w-md relative animate-in fade-in-0 zoom-in-95">
|
|
<h2 class="text-lg font-bold mb-4">添加复盘卡片</h2>
|
|
<form on:submit|preventDefault={handleSubmit}>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium mb-1">标题</label>
|
|
<input class="input input-bordered w-full" bind:value={form.title} required />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium mb-1">内容</label>
|
|
<textarea class="input input-bordered w-full min-h-[80px]" bind:value={form.content} required />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium mb-1">标签</label>
|
|
<div class="flex flex-wrap gap-2">
|
|
{#each REVIEW_TAGS as tag}
|
|
<label class="flex items-center gap-1 cursor-pointer">
|
|
<input type="checkbox" bind:group={form.tags} value={tag} />
|
|
<span class="text-xs">{tag}</span>
|
|
</label>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium mb-1">股票信息</label>
|
|
<div class="flex gap-2 mb-2">
|
|
<input class="input input-bordered flex-1" placeholder="名称" bind:value={form.stock.name} />
|
|
<input class="input input-bordered flex-1" placeholder="代码" bind:value={form.stock.code} />
|
|
<input class="input input-bordered w-20" type="number" min="0" placeholder="数量" bind:value={form.stock.amount} />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">价格组</label>
|
|
{#each form.stock.priceGroups as pg, i}
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<select class="input input-bordered w-24" bind:value={pg.label}>
|
|
{#each PRICE_LABELS as label}
|
|
<option value={label}>{label}</option>
|
|
{/each}
|
|
</select>
|
|
<input class="input input-bordered w-24" type="number" min="0" placeholder="价格" bind:value={pg.price} />
|
|
{#if form.stock.priceGroups.length > 1}
|
|
<button type="button" class="btn btn-xs btn-error" on:click={() => removePriceGroup(i)}>移除</button>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
<button type="button" class="btn btn-xs btn-primary mt-1" on:click={addPriceGroup}>添加价格组</button>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end gap-2 mt-6">
|
|
<button type="button" class="btn btn-ghost" on:click={handleCancel}>取消</button>
|
|
<button type="submit" class="btn btn-primary">添加</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{/if} |