diff --git a/src/features/orders/components/orders-table.tsx b/src/features/orders/components/orders-table.tsx
new file mode 100644
index 0000000..5b751d7
--- /dev/null
+++ b/src/features/orders/components/orders-table.tsx
@@ -0,0 +1,118 @@
+import { Button } from "@/components/ui/button";
+import { EmptyState } from "@/components/ui/empty-state";
+import { StatusBadge } from "@/components/ui/status-badge";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import type { OrderSummaryVM } from "@/lib/types/view-models";
+
+type OrdersTableProps = {
+ isLoading: boolean;
+ items: OrderSummaryVM[];
+ onOpenOrder?: (orderId: string) => void;
+ onOpenWorkflow?: (orderId: string) => void;
+};
+
+function formatTimestamp(timestamp: string) {
+ return timestamp.replace("T", " ").replace("Z", " UTC");
+}
+
+export function OrdersTable({
+ isLoading,
+ items,
+ onOpenOrder,
+ onOpenWorkflow,
+}: OrdersTableProps) {
+ if (isLoading) {
+ return (
+
+ 正在加载订单列表…
+
+ );
+ }
+
+ if (!items.length) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+ 订单号
+ workflowId
+ 客户等级
+ 服务模式
+ 状态
+ 当前步骤
+ 修订数
+ 更新时间
+ 操作
+
+
+
+ {items.map((order) => (
+
+ #{order.orderId}
+ {order.workflowId ?? "未关联"}
+ {order.customerLevel}
+ {order.serviceMode}
+
+
+
+
+
+
+
+ {order.currentStepLabel}
+
+
+
+
+
+
+ {order.revisionCount}
+
+ {order.pendingManualConfirm ? (
+
+ 待确认
+
+ ) : null}
+
+
+
+ {formatTimestamp(order.updatedAt)}
+
+
+
+
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/features/orders/components/orders-toolbar.tsx b/src/features/orders/components/orders-toolbar.tsx
new file mode 100644
index 0000000..a464587
--- /dev/null
+++ b/src/features/orders/components/orders-toolbar.tsx
@@ -0,0 +1,97 @@
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { PageToolbar } from "@/components/ui/page-toolbar";
+import { Select } from "@/components/ui/select";
+import { ORDER_STATUS_META } from "@/lib/types/status";
+import type { OrderStatus, ServiceMode } from "@/lib/types/backend";
+
+export type OrderFilterStatus = OrderStatus | "all";
+export type OrderFilterServiceMode = ServiceMode | "all";
+
+type OrdersToolbarProps = {
+ currentPage: number;
+ query: string;
+ serviceMode: OrderFilterServiceMode;
+ status: OrderFilterStatus;
+ totalPages: number;
+ onPageChange?: (page: number) => void;
+ onQueryChange: (value: string) => void;
+ onQuerySubmit?: (query: string) => void;
+ onServiceModeChange: (value: OrderFilterServiceMode) => void;
+ onStatusChange?: (value: OrderFilterStatus) => void;
+};
+
+export function OrdersToolbar({
+ currentPage,
+ query,
+ serviceMode,
+ status,
+ totalPages,
+ onPageChange,
+ onQueryChange,
+ onQuerySubmit,
+ onServiceModeChange,
+ onStatusChange,
+}: OrdersToolbarProps) {
+ const safeTotalPages = Math.max(totalPages, 1);
+
+ return (
+
+
+ onQueryChange(event.target.value)}
+ />
+
+
+
+
+
+
+
+ 第 {Math.min(currentPage, safeTotalPages)} / {safeTotalPages} 页
+
+
+
+
+
+ );
+}
diff --git a/src/features/orders/orders-home.tsx b/src/features/orders/orders-home.tsx
index fa4f49c..57077f7 100644
--- a/src/features/orders/orders-home.tsx
+++ b/src/features/orders/orders-home.tsx
@@ -3,16 +3,16 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
-import { Button } from "@/components/ui/button";
-import { Card, CardContent, CardDescription, CardEyebrow, CardHeader, CardTitle } from "@/components/ui/card";
-import { EmptyState } from "@/components/ui/empty-state";
+import { MetricChip } from "@/components/ui/metric-chip";
import { PageHeader } from "@/components/ui/page-header";
-import { StatusBadge } from "@/components/ui/status-badge";
-import { ORDER_STATUS_META } from "@/lib/types/status";
-import type { OrderStatus } from "@/lib/types/backend";
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 FilterStatus = OrderStatus | "all";
type PaginationData = {
page: number;
limit: number;
@@ -28,10 +28,10 @@ type OrdersHomeProps = {
onOpenWorkflow?: (orderId: string) => void;
onPageChange?: (page: number) => void;
onQuerySubmit?: (query: string) => void;
- onStatusChange?: (status: FilterStatus) => void;
+ onStatusChange?: (status: OrderFilterStatus) => void;
recentOrders: OrderSummaryVM[];
selectedQuery?: string;
- selectedStatus?: FilterStatus;
+ selectedStatus?: OrderFilterStatus;
totalPages?: number;
};
@@ -47,27 +47,13 @@ type OrdersOverviewEnvelope = {
};
const TITLE_MESSAGE = "最近订单已接入真实后端接口";
-const DEFAULT_MESSAGE = "当前首页展示真实最近订单,同时保留订单号直达入口,方便快速跳转详情和流程。";
+const DEFAULT_MESSAGE = "当前页面直接展示真实订单列表,支持关键词、状态和分页操作。";
const DEFAULT_PAGINATION: PaginationData = {
page: 1,
limit: 6,
total: 0,
totalPages: 0,
};
-const ORDER_STATUS_FILTER_OPTIONS: Array<{
- label: string;
- value: FilterStatus;
-}> = [
- { value: "all", label: "全部状态" },
- ...Object.entries(ORDER_STATUS_META).map(([value, meta]) => ({
- value: value as OrderStatus,
- label: meta.label,
- })),
-];
-
-function formatTimestamp(timestamp: string) {
- return timestamp.replace("T", " ").replace("Z", " UTC");
-}
export function OrdersHome({
currentPage = 1,
@@ -83,190 +69,55 @@ export function OrdersHome({
selectedStatus = "all",
totalPages = 0,
}: OrdersHomeProps) {
- const [lookupValue, setLookupValue] = useState("");
const [queryValue, setQueryValue] = useState(selectedQuery);
- const normalizedLookup = lookupValue.trim();
- const canLookup = /^\d+$/.test(normalizedLookup);
- const effectiveTotalPages = Math.max(totalPages, 1);
+ const [serviceModeFilter, setServiceModeFilter] =
+ useState("all");
useEffect(() => {
setQueryValue(selectedQuery);
}, [selectedQuery]);
+ const visibleOrders =
+ serviceModeFilter === "all"
+ ? recentOrders
+ : recentOrders.filter((order) => order.serviceMode === serviceModeFilter);
+
return (
-
+
-
-
- {TITLE_MESSAGE}
-
-
- {message}
-
+
+
+
+
-
-
-
-
-
Direct lookup
-
- 订单号直达
-
- 保留订单号和流程号的直接入口,适合在列表之外快速跳转到指定订单。
-
-
-
-
-
-
-
-
-
-
-
-
+
{message}
-
-
-
-
-
Recent visits
-
- 最近访问
-
- 这里已经接入真实后端最近订单列表,页面结构继续沿用首版设计。
-
-
-
-
-
-
-
-
-
-
- {isLoadingRecent ? (
-
- 正在加载最近访问记录…
-
- ) : null}
+
- {!isLoadingRecent && recentOrders.length ? (
- recentOrders.map((order) => (
-
-
-
-
- 订单 #{order.orderId}
-
-
- 工作流 {order.workflowId ?? "未关联"}
-
-
-
-
-
-
- {order.currentStepLabel}
- {formatTimestamp(order.updatedAt)}
-
-
- ))
- ) : null}
-
- {!isLoadingRecent && !recentOrders.length ? (
-
- ) : null}
-
-
-
- 第 {Math.min(currentPage, effectiveTotalPages)} / {effectiveTotalPages} 页
-
-
-
-
-
-
-
-
-
+
);
}
@@ -277,7 +128,8 @@ export function OrdersHomeScreen() {
const [message, setMessage] = useState(DEFAULT_MESSAGE);
const [isLoadingRecent, setIsLoadingRecent] = useState(true);
const [selectedQuery, setSelectedQuery] = useState("");
- const [selectedStatus, setSelectedStatus] = useState
("all");
+ const [selectedStatus, setSelectedStatus] =
+ useState("all");
const [pagination, setPagination] = useState(DEFAULT_PAGINATION);
useEffect(() => {
diff --git a/src/lib/adapters/orders.ts b/src/lib/adapters/orders.ts
index 3bcee45..24359e9 100644
--- a/src/lib/adapters/orders.ts
+++ b/src/lib/adapters/orders.ts
@@ -62,16 +62,30 @@ export function adaptAsset(asset: AssetDto): AssetViewModel {
export function adaptOrderSummary(
order: Pick<
OrderDetailResponseDto | OrderListItemDto,
- "order_id" | "workflow_id" | "status" | "current_step" | "updated_at"
+ | "order_id"
+ | "workflow_id"
+ | "customer_level"
+ | "service_mode"
+ | "status"
+ | "current_step"
+ | "review_task_status"
+ | "revision_count"
+ | "pending_manual_confirm"
+ | "updated_at"
>,
): OrderSummaryVM {
return {
orderId: order.order_id,
workflowId: order.workflow_id,
+ customerLevel: order.customer_level,
+ serviceMode: order.service_mode,
status: order.status,
statusMeta: getOrderStatusMeta(order.status),
currentStep: order.current_step,
currentStepLabel: getWorkflowStepMeta(order.current_step).label,
+ reviewTaskStatus: order.review_task_status,
+ revisionCount: order.revision_count,
+ pendingManualConfirm: order.pending_manual_confirm,
updatedAt: order.updated_at,
};
}
diff --git a/src/lib/types/view-models.ts b/src/lib/types/view-models.ts
index 04f4af8..8de3de6 100644
--- a/src/lib/types/view-models.ts
+++ b/src/lib/types/view-models.ts
@@ -43,10 +43,15 @@ export type AssetViewModel = {
export type OrderSummaryVM = {
orderId: number;
workflowId: string | null;
+ customerLevel: CustomerLevel;
+ serviceMode: ServiceMode;
status: OrderStatus;
statusMeta: StatusMeta;
currentStep: WorkflowStepName | null;
currentStepLabel: string;
+ reviewTaskStatus: ReviewTaskStatus | null;
+ revisionCount: number;
+ pendingManualConfirm: boolean;
updatedAt: string;
};
diff --git a/tests/app/api/orders-overview.route.test.ts b/tests/app/api/orders-overview.route.test.ts
index 96fd019..2a7060a 100644
--- a/tests/app/api/orders-overview.route.test.ts
+++ b/tests/app/api/orders-overview.route.test.ts
@@ -63,6 +63,8 @@ test("proxies recent orders overview from the backend list api", async () => {
{
orderId: 3,
workflowId: "order-3",
+ customerLevel: "mid",
+ serviceMode: "semi_pro",
status: "waiting_review",
statusMeta: {
label: "待审核",
@@ -70,6 +72,9 @@ test("proxies recent orders overview from the backend list api", async () => {
},
currentStep: "review",
currentStepLabel: "人工审核",
+ reviewTaskStatus: "revision_uploaded",
+ revisionCount: 1,
+ pendingManualConfirm: true,
updatedAt: "2026-03-27T14:00:03Z",
},
],
diff --git a/tests/features/orders/orders-home.test.tsx b/tests/features/orders/orders-home.test.tsx
index 137d67d..3ef475d 100644
--- a/tests/features/orders/orders-home.test.tsx
+++ b/tests/features/orders/orders-home.test.tsx
@@ -8,6 +8,8 @@ const RECENT_ORDERS: OrderSummaryVM[] = [
{
orderId: 4201,
workflowId: "wf-4201",
+ customerLevel: "mid",
+ serviceMode: "semi_pro",
status: "waiting_review",
statusMeta: {
label: "待审核",
@@ -15,15 +17,20 @@ const RECENT_ORDERS: OrderSummaryVM[] = [
},
currentStep: "review",
currentStepLabel: "人工审核",
+ reviewTaskStatus: "revision_uploaded",
+ revisionCount: 1,
+ pendingManualConfirm: true,
updatedAt: "2026-03-27T09:15:00Z",
},
];
-test("shows the real recent-orders entry state", () => {
+test("renders orders as a high-density table with shared toolbar controls", () => {
render();
- expect(screen.getByText("最近订单已接入真实后端接口")).toBeInTheDocument();
- expect(screen.getByText("订单 #4201")).toBeInTheDocument();
+ expect(screen.getByRole("columnheader", { name: "订单号" })).toBeInTheDocument();
+ expect(screen.getByRole("columnheader", { name: "服务模式" })).toBeInTheDocument();
+ expect(screen.getByLabelText("订单状态筛选")).toBeInTheDocument();
+ expect(screen.getByText("#4201")).toBeInTheDocument();
});
test("supports status filtering and pagination actions", () => {
diff --git a/tests/lib/adapters/orders.test.ts b/tests/lib/adapters/orders.test.ts
index 65b03d8..a5619f5 100644
--- a/tests/lib/adapters/orders.test.ts
+++ b/tests/lib/adapters/orders.test.ts
@@ -68,9 +68,13 @@ test("maps order summary status metadata and current step labels", () => {
expect(viewModel).toMatchObject({
orderId: 101,
workflowId: "wf-101",
+ customerLevel: "mid",
+ serviceMode: "semi_pro",
status: "waiting_review",
currentStep: "review",
currentStepLabel: "人工审核",
+ revisionCount: 0,
+ pendingManualConfirm: false,
statusMeta: {
label: "待审核",
tone: "warning",