fix: 项目表格列优化 — 描述列可见、标题简化、操作列自适应宽度
This commit is contained in:
@@ -46,7 +46,7 @@ bun run dev config.yaml
|
|||||||
- **Admin(管理台)**:全局管理视角,包含总览和项目管理。默认入口,访问 `/` 即可进入。
|
- **Admin(管理台)**:全局管理视角,包含总览和项目管理。默认入口,访问 `/` 即可进入。
|
||||||
- **Workbench(工作台)**:项目维度视角,通过 `/workbench/:projectId` 进入指定项目的工作台。URL 可保存为浏览器书签,下次直接进入。仅 active 状态的项目可进入工作台,archived 项目不可访问。
|
- **Workbench(工作台)**:项目维度视角,通过 `/workbench/:projectId` 进入指定项目的工作台。URL 可保存为浏览器书签,下次直接进入。仅 active 状态的项目可进入工作台,archived 项目不可访问。
|
||||||
|
|
||||||
从项目管理页面的 active 项目行可点击"进入工作台"跳转到对应项目的工作台。
|
从项目管理页面的 active 项目行可点击"工作台"跳转到对应项目的工作台。
|
||||||
|
|
||||||
## 模型管理
|
## 模型管理
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DeleteOutlined, EditOutlined, InboxOutlined, LoginOutlined, RedoOutline
|
|||||||
import { App as AntApp, Button, Popconfirm, Space, Table, Tag } from "antd";
|
import { App as AntApp, Button, Popconfirm, Space, Table, Tag } from "antd";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
import type { Project, ProjectListResponse } from "../../../../shared/api";
|
import type { Project, ProjectListResponse, ProjectStatus } from "../../../../shared/api";
|
||||||
|
|
||||||
interface ProjectTableProps {
|
interface ProjectTableProps {
|
||||||
data: ProjectListResponse | undefined;
|
data: ProjectListResponse | undefined;
|
||||||
@@ -16,11 +16,12 @@ interface ProjectTableProps {
|
|||||||
onRestore: (id: string) => Promise<unknown>;
|
onRestore: (id: string) => Promise<unknown>;
|
||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
status: ProjectStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
const COLUMNS: ColumnsType<Project> = [
|
const COLUMNS: ColumnsType<Project> = [
|
||||||
{ dataIndex: "name", ellipsis: true, title: "项目名称", width: 160 },
|
{ dataIndex: "name", ellipsis: true, title: "名称", width: 140 },
|
||||||
{ dataIndex: "description", ellipsis: true, title: "项目描述" },
|
{ dataIndex: "description", ellipsis: true, title: "描述" },
|
||||||
{
|
{
|
||||||
align: "center",
|
align: "center",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
@@ -31,21 +32,21 @@ const COLUMNS: ColumnsType<Project> = [
|
|||||||
return <Tag color="blue">进行中</Tag>;
|
return <Tag color="blue">进行中</Tag>;
|
||||||
},
|
},
|
||||||
title: "状态",
|
title: "状态",
|
||||||
width: 100,
|
width: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: "center",
|
align: "center",
|
||||||
dataIndex: "createdAt",
|
dataIndex: "createdAt",
|
||||||
render: (_value, record: Project) => formatDatetime(record.createdAt),
|
render: (_value, record: Project) => formatDatetime(record.createdAt),
|
||||||
title: "创建时间",
|
title: "创建时间",
|
||||||
width: 185,
|
width: 180,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: "center",
|
align: "center",
|
||||||
dataIndex: "updatedAt",
|
dataIndex: "updatedAt",
|
||||||
render: (_value, record: Project) => formatDatetime(record.updatedAt),
|
render: (_value, record: Project) => formatDatetime(record.updatedAt),
|
||||||
title: "更新时间",
|
title: "更新时间",
|
||||||
width: 185,
|
width: 180,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ export function ProjectTable({
|
|||||||
onRestore,
|
onRestore,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
status,
|
||||||
}: ProjectTableProps) {
|
}: ProjectTableProps) {
|
||||||
const { message } = AntApp.useApp();
|
const { message } = AntApp.useApp();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -92,7 +94,6 @@ export function ProjectTable({
|
|||||||
|
|
||||||
const operationColumn: ColumnsType<Project>[number] = {
|
const operationColumn: ColumnsType<Project>[number] = {
|
||||||
dataIndex: "op",
|
dataIndex: "op",
|
||||||
fixed: "right",
|
|
||||||
render: (_value, record: Project) => {
|
render: (_value, record: Project) => {
|
||||||
if (record.status === "active") {
|
if (record.status === "active") {
|
||||||
return (
|
return (
|
||||||
@@ -103,7 +104,7 @@ export function ProjectTable({
|
|||||||
size="small"
|
size="small"
|
||||||
type="link"
|
type="link"
|
||||||
>
|
>
|
||||||
进入工作台
|
工作台
|
||||||
</Button>
|
</Button>
|
||||||
<Button icon={<EditOutlined />} onClick={() => onEdit(record)} size="small" type="link">
|
<Button icon={<EditOutlined />} onClick={() => onEdit(record)} size="small" type="link">
|
||||||
编辑
|
编辑
|
||||||
@@ -140,7 +141,7 @@ export function ProjectTable({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 280,
|
width: status === "active" ? 260 : 160,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -157,7 +158,6 @@ export function ProjectTable({
|
|||||||
total: data?.total ?? 0,
|
total: data?.total ?? 0,
|
||||||
}}
|
}}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
scroll={{ x: 900 }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function ProjectToolbar({
|
|||||||
onSearchClear();
|
onSearchClear();
|
||||||
}}
|
}}
|
||||||
onSearch={(value) => onSearch(value)}
|
onSearch={(value) => onSearch(value)}
|
||||||
placeholder="搜索项目名称或描述"
|
placeholder="搜索名称或描述"
|
||||||
value={draftKeyword}
|
value={draftKeyword}
|
||||||
/>
|
/>
|
||||||
{activeTab === "active" && (
|
{activeTab === "active" && (
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export function ProjectsPage() {
|
|||||||
onRestore={(id) => restoreMutation.mutateAsync(id)}
|
onRestore={(id) => restoreMutation.mutateAsync(id)}
|
||||||
page={page}
|
page={page}
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
|
status={tabValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProjectFormModal
|
<ProjectFormModal
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ describe("ProjectsPage", () => {
|
|||||||
|
|
||||||
expect(screen.getByText("已归档")).not.toBeNull();
|
expect(screen.getByText("已归档")).not.toBeNull();
|
||||||
expect(screen.getByRole("button", { name: /新建项目/ })).not.toBeNull();
|
expect(screen.getByRole("button", { name: /新建项目/ })).not.toBeNull();
|
||||||
expect(screen.getByPlaceholderText("搜索项目名称或描述")).not.toBeNull();
|
expect(screen.getByPlaceholderText("搜索名称或描述")).not.toBeNull();
|
||||||
expect(calls.some((call) => call.url.includes("status=active"))).toBe(true);
|
expect(calls.some((call) => call.url.includes("status=active"))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ describe("ProjectsPage", () => {
|
|||||||
renderWithProviders(createElement(App), { initialRoute: "/projects" });
|
renderWithProviders(createElement(App), { initialRoute: "/projects" });
|
||||||
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
||||||
|
|
||||||
fireEvent.change(screen.getByPlaceholderText("搜索项目名称或描述"), { target: { value: "归档" } });
|
fireEvent.change(screen.getByPlaceholderText("搜索名称或描述"), { target: { value: "归档" } });
|
||||||
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
||||||
await waitFor(() => expect(calls.some((call) => call.url.includes("keyword=%E5%BD%92%E6%A1%A3"))).toBe(true));
|
await waitFor(() => expect(calls.some((call) => call.url.includes("keyword=%E5%BD%92%E6%A1%A3"))).toBe(true));
|
||||||
fireEvent.click(screen.getByText("已归档"));
|
fireEvent.click(screen.getByText("已归档"));
|
||||||
@@ -152,11 +152,11 @@ describe("ProjectsPage", () => {
|
|||||||
renderWithProviders(createElement(App), { initialRoute: "/projects" });
|
renderWithProviders(createElement(App), { initialRoute: "/projects" });
|
||||||
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
||||||
|
|
||||||
fireEvent.change(screen.getByPlaceholderText("搜索项目名称或描述"), { target: { value: "归档" } });
|
fireEvent.change(screen.getByPlaceholderText("搜索名称或描述"), { target: { value: "归档" } });
|
||||||
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
||||||
await waitFor(() => expect(calls.some((call) => call.url.includes("keyword=%E5%BD%92%E6%A1%A3"))).toBe(true));
|
await waitFor(() => expect(calls.some((call) => call.url.includes("keyword=%E5%BD%92%E6%A1%A3"))).toBe(true));
|
||||||
|
|
||||||
fireEvent.change(screen.getByPlaceholderText("搜索项目名称或描述"), { target: { value: "" } });
|
fireEvent.change(screen.getByPlaceholderText("搜索名称或描述"), { target: { value: "" } });
|
||||||
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
fireEvent.click(screen.getByRole("button", { name: /搜\s*索/ }));
|
||||||
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
await waitFor(() => expect(screen.getByText("活跃项目")).not.toBeNull());
|
||||||
});
|
});
|
||||||
@@ -255,11 +255,12 @@ describe("ProjectsPage", () => {
|
|||||||
onRestore,
|
onRestore,
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
|
status: "active",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: /进入工作台/ }));
|
fireEvent.click(screen.getByRole("button", { name: /工作台/ }));
|
||||||
expect(screen.getByText("当前路径:/workbench/p1")).not.toBeNull();
|
expect(screen.getByText("当前路径:/workbench/p1")).not.toBeNull();
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: /归档/ }));
|
fireEvent.click(screen.getByRole("button", { name: /归档/ }));
|
||||||
|
|||||||
Reference in New Issue
Block a user