230 lines
6.3 KiB
TypeScript
230 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
import { MetricChip } from "@/components/ui/metric-chip";
|
|
import { PageHeader } from "@/components/ui/page-header";
|
|
import type { OrderSummaryVM } from "@/lib/types/view-models";
|
|
import {
|
|
OrdersToolbar,
|
|
type OrderFilterServiceMode,
|
|
type OrderFilterStatus,
|
|
} from "@/features/orders/components/orders-toolbar";
|
|
import { OrdersTable } from "@/features/orders/components/orders-table";
|
|
|
|
type PaginationData = {
|
|
page: number;
|
|
limit: number;
|
|
total: number;
|
|
totalPages: number;
|
|
};
|
|
|
|
type OrdersHomeProps = {
|
|
currentPage?: number;
|
|
isLoadingRecent?: boolean;
|
|
message?: string;
|
|
onOpenOrder?: (orderId: string) => void;
|
|
onOpenWorkflow?: (orderId: string) => void;
|
|
onPageChange?: (page: number) => void;
|
|
onQuerySubmit?: (query: string) => void;
|
|
onStatusChange?: (status: OrderFilterStatus) => void;
|
|
recentOrders: OrderSummaryVM[];
|
|
selectedQuery?: string;
|
|
selectedStatus?: OrderFilterStatus;
|
|
totalPages?: number;
|
|
};
|
|
|
|
type OrdersOverviewEnvelope = {
|
|
data?: {
|
|
limit?: number;
|
|
items?: OrderSummaryVM[];
|
|
page?: number;
|
|
total?: number;
|
|
totalPages?: number;
|
|
};
|
|
message?: string;
|
|
};
|
|
|
|
const TITLE_MESSAGE = "最近订单已接入真实后端接口";
|
|
const DEFAULT_MESSAGE = "当前页面直接展示真实订单列表,支持关键词、状态和分页操作。";
|
|
const DEFAULT_PAGINATION: PaginationData = {
|
|
page: 1,
|
|
limit: 6,
|
|
total: 0,
|
|
totalPages: 0,
|
|
};
|
|
|
|
export function OrdersHome({
|
|
currentPage = 1,
|
|
isLoadingRecent = false,
|
|
message = DEFAULT_MESSAGE,
|
|
onOpenOrder,
|
|
onOpenWorkflow,
|
|
onPageChange,
|
|
onQuerySubmit,
|
|
onStatusChange,
|
|
recentOrders,
|
|
selectedQuery = "",
|
|
selectedStatus = "all",
|
|
totalPages = 0,
|
|
}: OrdersHomeProps) {
|
|
const [queryValue, setQueryValue] = useState(selectedQuery);
|
|
const [serviceModeFilter, setServiceModeFilter] =
|
|
useState<OrderFilterServiceMode>("all");
|
|
|
|
useEffect(() => {
|
|
setQueryValue(selectedQuery);
|
|
}, [selectedQuery]);
|
|
|
|
const visibleOrders =
|
|
serviceModeFilter === "all"
|
|
? recentOrders
|
|
: recentOrders.filter((order) => order.serviceMode === serviceModeFilter);
|
|
|
|
return (
|
|
<section className="space-y-6">
|
|
<PageHeader
|
|
eyebrow="Orders home"
|
|
title="订单总览"
|
|
description="订单页直接承担扫描、筛选和跳转职责,不再把首屏浪费在入口说明和大卡片上。"
|
|
meta="真实列表页"
|
|
/>
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
<MetricChip label="mode" value="真实订单列表" />
|
|
<MetricChip label="rows" value={visibleOrders.length} />
|
|
<MetricChip label="message" value={TITLE_MESSAGE} />
|
|
</div>
|
|
|
|
<p className="text-sm text-[var(--ink-muted)]">{message}</p>
|
|
|
|
<OrdersToolbar
|
|
currentPage={currentPage}
|
|
query={queryValue}
|
|
serviceMode={serviceModeFilter}
|
|
status={selectedStatus}
|
|
totalPages={totalPages}
|
|
onPageChange={onPageChange}
|
|
onQueryChange={setQueryValue}
|
|
onQuerySubmit={onQuerySubmit}
|
|
onServiceModeChange={setServiceModeFilter}
|
|
onStatusChange={onStatusChange}
|
|
/>
|
|
|
|
<OrdersTable
|
|
isLoading={isLoadingRecent}
|
|
items={visibleOrders}
|
|
onOpenOrder={onOpenOrder}
|
|
onOpenWorkflow={onOpenWorkflow}
|
|
/>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function OrdersHomeScreen() {
|
|
const router = useRouter();
|
|
const [recentOrders, setRecentOrders] = useState<OrderSummaryVM[]>([]);
|
|
const [message, setMessage] = useState(DEFAULT_MESSAGE);
|
|
const [isLoadingRecent, setIsLoadingRecent] = useState(true);
|
|
const [selectedQuery, setSelectedQuery] = useState("");
|
|
const [selectedStatus, setSelectedStatus] =
|
|
useState<OrderFilterStatus>("all");
|
|
const [pagination, setPagination] = useState<PaginationData>(DEFAULT_PAGINATION);
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
|
|
async function loadRecentOrders() {
|
|
setIsLoadingRecent(true);
|
|
|
|
try {
|
|
const params = new URLSearchParams({
|
|
page: String(pagination.page),
|
|
limit: String(pagination.limit),
|
|
});
|
|
|
|
if (selectedStatus !== "all") {
|
|
params.set("status", selectedStatus);
|
|
}
|
|
|
|
if (selectedQuery.length > 0) {
|
|
params.set("query", selectedQuery);
|
|
}
|
|
|
|
const response = await fetch(`/api/dashboard/orders-overview?${params.toString()}`);
|
|
const payload = (await response.json()) as OrdersOverviewEnvelope;
|
|
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
setRecentOrders(payload.data?.items ?? []);
|
|
setPagination((current) => ({
|
|
page: payload.data?.page ?? current.page,
|
|
limit: payload.data?.limit ?? current.limit,
|
|
total: payload.data?.total ?? current.total,
|
|
totalPages: payload.data?.totalPages ?? current.totalPages,
|
|
}));
|
|
setMessage(payload.message ?? DEFAULT_MESSAGE);
|
|
} catch {
|
|
if (!active) {
|
|
return;
|
|
}
|
|
|
|
setRecentOrders([]);
|
|
setPagination((current) => ({
|
|
...current,
|
|
total: 0,
|
|
totalPages: 0,
|
|
}));
|
|
setMessage("最近访问记录加载失败,请稍后重试。");
|
|
} finally {
|
|
if (active) {
|
|
setIsLoadingRecent(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void loadRecentOrders();
|
|
|
|
return () => {
|
|
active = false;
|
|
};
|
|
}, [pagination.limit, pagination.page, selectedQuery, selectedStatus]);
|
|
|
|
return (
|
|
<OrdersHome
|
|
currentPage={pagination.page}
|
|
isLoadingRecent={isLoadingRecent}
|
|
message={message}
|
|
onPageChange={(page) =>
|
|
setPagination((current) => ({
|
|
...current,
|
|
page,
|
|
}))
|
|
}
|
|
onQuerySubmit={(query) => {
|
|
setSelectedQuery(query);
|
|
setPagination((current) => ({
|
|
...current,
|
|
page: 1,
|
|
}));
|
|
}}
|
|
recentOrders={recentOrders}
|
|
onStatusChange={(status) => {
|
|
setSelectedStatus(status);
|
|
setPagination((current) => ({
|
|
...current,
|
|
page: 1,
|
|
}));
|
|
}}
|
|
onOpenOrder={(orderId) => router.push(`/orders/${orderId}`)}
|
|
onOpenWorkflow={(orderId) => router.push(`/workflows/${orderId}`)}
|
|
selectedQuery={selectedQuery}
|
|
selectedStatus={selectedStatus}
|
|
totalPages={pagination.totalPages}
|
|
/>
|
|
);
|
|
}
|