Compare commits

..

8 Commits

Author SHA1 Message Date
sulaimaannaasif6866 51304d782a feat(admin): 商品编辑页增加上一个/下一个导航
CI / Go (api) (pull_request) Successful in 9s
CI / Python (ingestion) (pull_request) Successful in 10s
CI / Migrations (postgres) (pull_request) Successful in 14s
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 05:50:20 +00:00
lixu 6e81a7eb36 Merge pull request 'feat(admin): 商品列表分页增强(每页数量+跳页)' (#14) from devin/1782012403-list-pagination into main
CI / Go (api) (push) Successful in 8s
CI / Python (ingestion) (push) Successful in 9s
CI / Migrations (postgres) (push) Successful in 14s
2026-06-21 11:27:41 +08:00
sulaimaannaasif6866 8bee570cb2 feat(admin): 商品列表底部增加每页数量与跳页控制
CI / Go (api) (pull_request) Successful in 9s
CI / Python (ingestion) (pull_request) Successful in 9s
CI / Migrations (postgres) (pull_request) Successful in 14s
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 03:26:43 +00:00
lixu ed06d269c6 Merge pull request 'style(public): 页脚内容居中' (#13) from devin/1782012124-footer-center into main
CI / Go (api) (push) Successful in 9s
CI / Python (ingestion) (push) Successful in 9s
CI / Migrations (postgres) (push) Successful in 18s
2026-06-21 11:23:02 +08:00
sulaimaannaasif6866 0214253b48 style(public): 页脚内容居中
CI / Go (api) (pull_request) Successful in 8s
CI / Python (ingestion) (pull_request) Successful in 9s
CI / Migrations (postgres) (pull_request) Successful in 14s
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 03:22:04 +00:00
lixu afced01ba6 Merge pull request 'style(public): 更新页脚免责文案' (#12) from devin/1782011674-footer-text into main
CI / Go (api) (push) Successful in 9s
CI / Python (ingestion) (push) Successful in 10s
CI / Migrations (postgres) (push) Successful in 14s
2026-06-21 11:15:29 +08:00
sulaimaannaasif6866 8812e5f5e3 style(public): 更新页脚免责文案
CI / Go (api) (pull_request) Successful in 9s
CI / Python (ingestion) (pull_request) Successful in 10s
CI / Migrations (postgres) (pull_request) Successful in 13s
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 03:14:34 +00:00
lixu 01055ded02 Merge pull request 'style(admin): 后台各页容器统一居中' (#11) from devin/1782011126-admin-center into main
CI / Go (api) (push) Successful in 8s
CI / Python (ingestion) (push) Successful in 9s
CI / Migrations (postgres) (push) Successful in 15s
2026-06-21 11:06:31 +08:00
4 changed files with 104 additions and 16 deletions
+13 -2
View File
@@ -28,6 +28,7 @@ export default function App() {
const [tab, setTab] = useState<Tab>("products");
const [pending, setPending] = useState<number | null>(null);
const [view, setView] = useState<View>({ name: "list" });
const [navIds, setNavIds] = useState<string[]>([]);
useEffect(() => {
if (!authed) return;
@@ -190,9 +191,19 @@ export default function App() {
) : tab === "submissions" ? (
<SubmissionsPage onPending={setPending} />
) : view.name === "list" ? (
<ProductList onOpen={(id) => setView({ name: "detail", id })} />
<ProductList
onOpen={(id, ids) => {
setNavIds(ids);
setView({ name: "detail", id });
}}
/>
) : (
<ProductDetail id={view.id} onBack={() => setView({ name: "list" })} />
<ProductDetail
id={view.id}
ids={navIds}
onNavigate={(id) => setView({ name: "detail", id })}
onBack={() => setView({ name: "list" })}
/>
)}
</main>
</div>
@@ -9,6 +9,8 @@ import {
} from "../types";
import {
ArrowLeft,
ChevronLeft,
ChevronRight,
Plus,
Save,
Trash2,
@@ -88,10 +90,18 @@ const inputCls =
export default function ProductDetail({
id,
onBack,
ids = [],
onNavigate,
}: {
id: string;
onBack: () => void;
ids?: string[];
onNavigate?: (id: string) => void;
}) {
const navIndex = ids.indexOf(id);
const prevId = navIndex > 0 ? ids[navIndex - 1] : null;
const nextId =
navIndex >= 0 && navIndex < ids.length - 1 ? ids[navIndex + 1] : null;
const [d, setD] = useState<Detail | null>(null);
const [brands, setBrands] = useState<Brand[]>([]);
const [categories, setCategories] = useState<Category[]>([]);
@@ -226,12 +236,35 @@ export default function ProductDetail({
return (
<div className="mx-auto max-w-5xl space-y-5">
<div className="flex items-center justify-between">
<button
onClick={onBack}
className="flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900"
>
<ArrowLeft className="h-4 w-4" />
</button>
<div className="flex items-center gap-2">
<button
onClick={onBack}
className="flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900"
>
<ArrowLeft className="h-4 w-4" />
</button>
{ids.length > 1 && navIndex >= 0 && (
<div className="ml-2 flex items-center gap-1 text-sm">
<button
onClick={() => prevId && onNavigate?.(prevId)}
disabled={!prevId}
className="flex items-center gap-1 rounded border border-gray-300 px-2 py-1 text-gray-600 hover:bg-gray-50 disabled:opacity-40"
>
<ChevronLeft className="h-4 w-4" />
</button>
<span className="text-xs text-gray-400">
{navIndex + 1} / {ids.length}
</span>
<button
onClick={() => nextId && onNavigate?.(nextId)}
disabled={!nextId}
className="flex items-center gap-1 rounded border border-gray-300 px-2 py-1 text-gray-600 hover:bg-gray-50 disabled:opacity-40"
>
<ChevronRight className="h-4 w-4" />
</button>
</div>
)}
</div>
<div className="flex items-center gap-3">
{msg && <span className="text-sm text-emerald-600">{msg}</span>}
{error && <span className="text-sm text-red-600">{error}</span>}
+50 -5
View File
@@ -27,12 +27,13 @@ function QualityBadge({ score }: { score: number }) {
export default function ProductList({
onOpen,
}: {
onOpen: (id: string) => void;
onOpen: (id: string, ids: string[]) => void;
}) {
const [q, setQ] = useState("");
const [input, setInput] = useState("");
const [page, setPage] = useState(1);
const [size] = useState(20);
const [size, setSize] = useState(20);
const [jump, setJump] = useState("");
const [rows, setRows] = useState<ProductRow[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
@@ -164,7 +165,7 @@ export default function ProductList({
onClose={() => setCreating(false)}
onCreated={(id) => {
setCreating(false);
onOpen(id);
onOpen(id, [id]);
}}
/>
)}
@@ -270,7 +271,7 @@ export default function ProductList({
rows.map((r) => (
<tr
key={r.id}
onClick={() => onOpen(r.id)}
onClick={() => onOpen(r.id, rows.map((x) => x.id))}
className={`cursor-pointer hover:bg-emerald-50/50 ${
selected.has(r.id) ? "bg-emerald-50/60" : ""
}`}
@@ -319,7 +320,25 @@ export default function ProductList({
</table>
</div>
<div className="mt-4 flex items-center justify-end gap-2 text-sm text-gray-600">
<div className="mt-4 flex flex-wrap items-center justify-end gap-2 text-sm text-gray-600">
<div className="mr-auto flex items-center gap-1">
<span></span>
<select
value={size}
onChange={(e) => {
setSize(Number(e.target.value));
setPage(1);
}}
className="rounded border border-gray-300 px-2 py-1"
>
{[20, 50, 100].map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</select>
<span> · {total} </span>
</div>
<button
disabled={page <= 1}
onClick={() => setPage((p) => p - 1)}
@@ -337,6 +356,32 @@ export default function ProductList({
>
</button>
<form
onSubmit={(e) => {
e.preventDefault();
const n = Number(jump);
if (Number.isFinite(n) && n >= 1) {
setPage(Math.min(Math.max(1, Math.trunc(n)), pages));
setJump("");
}
}}
className="flex items-center gap-1"
>
<span></span>
<input
value={jump}
onChange={(e) => setJump(e.target.value.replace(/[^0-9]/g, ""))}
placeholder={String(page)}
className="w-14 rounded border border-gray-300 px-2 py-1 text-center"
aria-label="跳转页码"
/>
<button
type="submit"
className="rounded border border-gray-300 px-3 py-1 hover:bg-gray-50"
>
</button>
</form>
</div>
</div>
);
+2 -3
View File
@@ -81,7 +81,7 @@ export default function App() {
</main>
<footer className="border-t bg-white">
<div className="max-w-5xl mx-auto px-4 py-4 text-xs text-gray-400 leading-relaxed">
<div className="max-w-5xl mx-auto px-4 py-4 text-xs text-gray-400 leading-relaxed text-center">
{qualified != null && (
<div className="mb-2 text-gray-500">
@@ -91,8 +91,7 @@ export default function App() {
</div>
)}
/
稿
<button onClick={() => setView({ name: "api" })} className="ml-1 text-emerald-600 hover:underline">
API
</button>