feat: 收集箱右侧区域布局优化——上下两段式,操作栏固定底部始终显示
This commit is contained in:
@@ -1,22 +1,14 @@
|
||||
import { Card, Descriptions, Tag, Typography } from "antd";
|
||||
|
||||
import type { Material, MaterialStatus, MaterialType } from "../types";
|
||||
import type { Material, MaterialType } from "../types";
|
||||
|
||||
import { formatRelativeTime } from "../../../shared/utils/time";
|
||||
import { STATUS_MAP } from "./constants";
|
||||
|
||||
interface MaterialContentProps {
|
||||
material: Material;
|
||||
}
|
||||
|
||||
const STATUS_MAP: Record<MaterialStatus, { color: string; label: string }> = {
|
||||
approved: { color: "green", label: "已通过" },
|
||||
discarded: { color: "red", label: "已放弃" },
|
||||
failed: { color: "magenta", label: "失败" },
|
||||
pending: { color: "gold", label: "待处理" },
|
||||
processing: { color: "blue", label: "处理中" },
|
||||
review: { color: "orange", label: "待审核" },
|
||||
};
|
||||
|
||||
const MATERIAL_TYPE_LABELS: Record<MaterialType, string> = {
|
||||
general: "通用",
|
||||
meeting: "会议",
|
||||
@@ -27,7 +19,7 @@ export function MaterialContent({ material }: MaterialContentProps) {
|
||||
const typeLabel = MATERIAL_TYPE_LABELS[material.materialType] ?? material.materialType;
|
||||
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<>
|
||||
<Typography.Title level={4}>素材详情</Typography.Title>
|
||||
<Card>
|
||||
<Typography.Paragraph>{material.description}</Typography.Paragraph>
|
||||
@@ -52,6 +44,6 @@ export function MaterialContent({ material }: MaterialContentProps) {
|
||||
<Descriptions.Item label="创建时间">{formatRelativeTime(material.createdAt)}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { CheckOutlined, CloseOutlined, RedoOutlined } from "@ant-design/icons";
|
||||
import { App as AntApp, Button, Empty, Result, Space, Spin } from "antd";
|
||||
|
||||
import type { Material } from "../types";
|
||||
import { App as AntApp, Button, Empty, Result, Space, Spin, Tag } from "antd";
|
||||
|
||||
import { useMaterial } from "../../../shared/hooks/use-materials";
|
||||
import { STATUS_MAP } from "./constants";
|
||||
import { MaterialContent } from "./MaterialContent";
|
||||
|
||||
interface MaterialDetailPanelProps {
|
||||
@@ -23,8 +22,10 @@ export function MaterialDetailPanel({
|
||||
}: MaterialDetailPanelProps) {
|
||||
if (!materialId) {
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<Empty description="请在左侧选择素材" />
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<Empty description="请在左侧选择素材" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -46,24 +47,30 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<Spin />
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<Spin />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<Result subTitle="加载素材详情失败" />
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<Result subTitle="加载素材详情失败" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data || !materialId) {
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<Empty description="请在左侧选择素材" />
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<Empty description="请在左侧选择素材" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -97,44 +104,32 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
}
|
||||
};
|
||||
|
||||
const statusInfo = STATUS_MAP[data.status] ?? { color: "default", label: data.status };
|
||||
|
||||
return (
|
||||
<div className="app-inbox-content">
|
||||
<MaterialContent material={data} />
|
||||
<ActionButtons material={data} onApprove={handleApprove} onDiscard={handleDiscard} onRetry={handleRetry} />
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<MaterialContent material={data} />
|
||||
</div>
|
||||
<div className="app-inbox-action-bar">
|
||||
<Tag color={statusInfo.color}>{statusInfo.label}</Tag>
|
||||
{data.status === "review" ? (
|
||||
<Space>
|
||||
<Button icon={<CheckOutlined />} onClick={() => void handleApprove()} type="primary">
|
||||
通过
|
||||
</Button>
|
||||
<Button danger icon={<CloseOutlined />} onClick={() => void handleDiscard()}>
|
||||
放弃
|
||||
</Button>
|
||||
</Space>
|
||||
) : data.status === "failed" ? (
|
||||
<Space>
|
||||
<Button icon={<RedoOutlined />} onClick={() => void handleRetry()}>
|
||||
重试
|
||||
</Button>
|
||||
</Space>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ActionButtonsProps {
|
||||
material: Material;
|
||||
onApprove: () => Promise<void>;
|
||||
onDiscard: () => Promise<void>;
|
||||
onRetry: () => Promise<void>;
|
||||
}
|
||||
|
||||
function ActionButtons({ material, onApprove, onDiscard, onRetry }: ActionButtonsProps) {
|
||||
if (material.status === "review") {
|
||||
return (
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Button icon={<CheckOutlined />} onClick={() => void onApprove()} type="primary">
|
||||
通过
|
||||
</Button>
|
||||
<Button danger icon={<CloseOutlined />} onClick={() => void onDiscard()}>
|
||||
放弃
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
if (material.status === "failed") {
|
||||
return (
|
||||
<Space style={{ marginTop: 16 }}>
|
||||
<Button icon={<RedoOutlined />} onClick={() => void onRetry()}>
|
||||
重试
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
10
src/web/features/inbox/components/constants.ts
Normal file
10
src/web/features/inbox/components/constants.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { MaterialStatus } from "../types";
|
||||
|
||||
export const STATUS_MAP: Record<MaterialStatus, { color: string; label: string }> = {
|
||||
approved: { color: "green", label: "已通过" },
|
||||
discarded: { color: "red", label: "已放弃" },
|
||||
failed: { color: "magenta", label: "失败" },
|
||||
pending: { color: "gold", label: "待处理" },
|
||||
processing: { color: "blue", label: "处理中" },
|
||||
review: { color: "orange", label: "待审核" },
|
||||
};
|
||||
@@ -338,16 +338,31 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-inbox-content {
|
||||
.app-inbox-panel {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.app-inbox-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: var(--ant-padding-xl);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.app-inbox-action-bar {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--ant-padding-sm) var(--ant-padding-xl);
|
||||
border-top: 1px solid var(--ant-color-border-secondary);
|
||||
background: var(--ant-color-bg-container);
|
||||
}
|
||||
|
||||
.app-inbox-datepicker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user