Add image upload to cases with cleanup

This commit is contained in:
2026-09-12 10:06:16 +08:00
parent 6e550c0d5b
commit 370ec568aa
+22 -10
View File
@@ -1,15 +1,18 @@
--- ---
import { and, asc, desc, eq, gt, lt, max } from "drizzle-orm"; import { and, asc, desc, eq, gt, lt, max } from "drizzle-orm";
import AdminLayout from "../../layouts/AdminLayout.astro"; import AdminLayout from "../../layouts/AdminLayout.astro";
import ImageField from "../../components/admin/ImageField.astro";
import { cases } from "../../db/schema"; import { cases } from "../../db/schema";
import { getDb } from "../../lib/db"; import { getDb } from "../../lib/db";
import { getEnv } from "../../lib/env"; import { getEnv } from "../../lib/env";
import { parseForm } from "../../lib/form"; import { parseForm } from "../../lib/form";
import { deleteMedia } from "../../lib/media";
import { caseInput } from "../../schemas"; import { caseInput } from "../../schemas";
export const prerender = false; export const prerender = false;
const db = getDb(getEnv().DB); const env = getEnv();
const db = getDb(env.DB);
let errors: Record<string, string> = {}; let errors: Record<string, string> = {};
let failedId = ""; let failedId = "";
@@ -44,6 +47,11 @@ if (Astro.request.method === "POST") {
}); });
} else { } else {
const id = str(form, "id"); const id = str(form, "id");
const newImage = parsed.data.imageUrl || null;
const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1);
if (current && current.imageUrl !== newImage) {
await deleteMedia(env, current.imageUrl);
}
await db await db
.update(cases) .update(cases)
.set({ .set({
@@ -51,14 +59,17 @@ if (Astro.request.method === "POST") {
location: parsed.data.location || null, location: parsed.data.location || null,
completedAt: parsed.data.completedAt || null, completedAt: parsed.data.completedAt || null,
description: parsed.data.description || null, description: parsed.data.description || null,
imageUrl: parsed.data.imageUrl || null, imageUrl: newImage,
status: parsed.data.status, status: parsed.data.status,
updatedAt: new Date(), updatedAt: new Date(),
}) })
.where(eq(cases.id, id)); .where(eq(cases.id, id));
} }
} else if (action === "delete") { } else if (action === "delete") {
await db.delete(cases).where(eq(cases.id, str(form, "id"))); const id = str(form, "id");
const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1);
await deleteMedia(env, current?.imageUrl);
await db.delete(cases).where(eq(cases.id, id));
} else if (action === "up" || action === "down") { } else if (action === "up" || action === "down") {
const id = str(form, "id"); const id = str(form, "id");
const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1); const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1);
@@ -109,8 +120,7 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
<input id="a-date" name="completedAt" type="text" placeholder="例:2026年2月" /> <input id="a-date" name="completedAt" type="text" placeholder="例:2026年2月" />
</div> </div>
<div> <div>
<label for="a-img">圖片 URL</label> <ImageField name="imageUrl" label="圖片" scope="cases" id="a-img" />
<input id="a-img" name="imageUrl" type="text" placeholder="https://..." />
</div> </div>
<div style="grid-column:1/-1"> <div style="grid-column:1/-1">
<label for="a-desc">描述</label> <label for="a-desc">描述</label>
@@ -144,8 +154,13 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
<input name="completedAt" type="text" value={item.completedAt ?? ""} /> <input name="completedAt" type="text" value={item.completedAt ?? ""} />
</div> </div>
<div> <div>
<label>圖片 URL</label> <ImageField
<input name="imageUrl" type="text" value={item.imageUrl ?? ""} /> name="imageUrl"
label="圖片"
scope="cases"
value={item.imageUrl}
id={`img-${item.id}`}
/>
</div> </div>
<div style="grid-column:1/-1"> <div style="grid-column:1/-1">
<label>描述</label> <label>描述</label>
@@ -159,9 +174,6 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
</select> </select>
</div> </div>
</div> </div>
{item.imageUrl && (
<img src={item.imageUrl} alt={item.title} style="margin-top:14px;max-width:220px;border-radius:10px;" />
)}
<div class="actions"> <div class="actions">
<button type="submit" name="action" value="save">儲存</button> <button type="submit" name="action" value="save">儲存</button>
<button type="submit" name="action" value="up" class="secondary mini" disabled={i === 0}>↑ 上移</button> <button type="submit" name="action" value="up" class="secondary mini" disabled={i === 0}>↑ 上移</button>