fix(person/collection): fix card

This commit is contained in:
2026-02-05 14:53:28 +08:00
parent 885bf210c3
commit 04611e98e3
2 changed files with 444 additions and 106 deletions

File diff suppressed because one or more lines are too long

View File

@@ -23,16 +23,21 @@ import {
Badge, Badge,
Button, Button,
Collapse, Collapse,
Flex,
Group, Group,
Loader,
Pagination,
Paper, Paper,
Select, Select,
SimpleGrid, SimpleGrid,
Stack, Stack,
Text, Text,
TextInput, TextInput,
UnstyledButton,
} from "@mantine/core" } from "@mantine/core"
import { notifications } from "@mantine/notifications" import { notifications } from "@mantine/notifications"
import { import {
IconCalendar,
IconChevronDown, IconChevronDown,
IconChevronUp, IconChevronUp,
IconRefresh, IconRefresh,
@@ -41,9 +46,10 @@ import {
IconStarFilled, IconStarFilled,
} from "@tabler/icons-react" } from "@tabler/icons-react"
import dayjs from "dayjs" import dayjs from "dayjs"
import { DataTable, DataTableColumn } from "mantine-datatable" import { User2 } from "lucide-react"
import { useRouter, useSearchParams } from "next/navigation" import { useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react" import { useCallback, useEffect, useMemo, useState } from "react"
import { CardCoverIcon } from "./components/ProjectIcon"
type PageType = "all" | "collect" | "dynamic" type PageType = "all" | "collect" | "dynamic"
@@ -191,25 +197,38 @@ export default function CollectionPage() {
}) })
}, []) }, [])
const columns = useMemo(() => { const currentPage = storeParams.page_number ?? 1
const cols: DataTableColumn<Project.DataProps>[] = [ const pageSize = storeParams.page_size ?? 15
{
accessor: "id", const ProjectCard = (record: Project.DataProps) => {
title: "项目ID", return (
width: 90, <Paper
}, key={record.id}
{ withBorder
accessor: "name", radius="md"
title: "项目名称", shadow="sm"
width: 220, style={{
render: (record) => ( borderColor: "rgba(0,0,0,0.06)",
<Button height: 240,
padding: 14,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
background: "white",
}}>
<Group justify="space-between" wrap="nowrap">
<Flex flex={1}>
<CardCoverIcon width={68} height={68} />
<Stack gap={0}>
<Flex align="center">
<UnstyledButton
variant="subtle" variant="subtle"
size="xs" size="sm"
style={{ style={{
paddingLeft: 6, paddingLeft: 6,
paddingRight: 6, paddingRight: 6,
maxWidth: 220, justifyContent: "flex-start",
width: "100%",
}} }}
onClick={() => { onClick={() => {
const url = type const url = type
@@ -218,58 +237,45 @@ export default function CollectionPage() {
router.push(url) router.push(url)
}}> }}>
<Text <Text
size="sm" fw={700}
c={"brand"}
style={{ style={{
maxWidth: 200, maxWidth: "100%",
overflow: "hidden", overflow: "hidden",
textOverflow: "ellipsis", textOverflow: "ellipsis",
whiteSpace: "nowrap", whiteSpace: "nowrap",
}}> }}>
{record.name} {record.name}
</Text> </Text>
</Button> </UnstyledButton>
), <Badge
}, color={statusBadgeColor(record.status)}
{ style={{
accessor: "project_type", width: "-webkit-fill-available",
title: "所属业务", }}>
width: 140,
render: (record) => record.project_type ?? "-",
},
{
accessor: "status",
title: "状态",
width: 110,
render: (record) => (
<Badge color={statusBadgeColor(record.status)}>
{STATUS_CODE.get(record.status) ?? record.status} {STATUS_CODE.get(record.status) ?? record.status}
</Badge> </Badge>
), </Flex>
}, <Flex align="center" px={6} gap={6}>
{ <User2
accessor: "owner", color="var(--mantine-color-dimmed)"
title: "负责人", width={14}
width: 120, height={14}
render: (record) => record.owner ?? "-", />
}, <Text size="sm">{record.owner ?? "-"}</Text>
{ </Flex>
accessor: "label_schema_name", <Flex align="center" px={6} gap={6}>
title: "标注方案", <IconCalendar
width: 160, color="var(--mantine-color-dimmed)"
render: (record) => record.label_schema_name ?? "-", width={14}
}, height={14}
{ />
accessor: "update_at", <Text size="sm">
title: "更新时间", {record.create_at ? record.create_at.split(" ")[0] : "-"}
width: 160, </Text>
render: (record) => record.update_at ?? record.create_at ?? "-", </Flex>
}, </Stack>
{ </Flex>
accessor: "operation",
title: "操作",
width: 80,
textAlign: "center",
render: (record) => (
<ActionIcon <ActionIcon
variant="subtle" variant="subtle"
color={record.is_collect ? "yellow" : "gray"} color={record.is_collect ? "yellow" : "gray"}
@@ -284,7 +290,7 @@ export default function CollectionPage() {
title: "操作成功", title: "操作成功",
message: record.is_collect ? "已取消收藏" : "已收藏", message: record.is_collect ? "已取消收藏" : "已收藏",
}) })
load() setQueryTrigger((v) => v + 1)
} catch (e) { } catch (e) {
notifications.show({ notifications.show({
color: "red", color: "red",
@@ -299,11 +305,64 @@ export default function CollectionPage() {
<IconStar size={16} /> <IconStar size={16} />
)} )}
</ActionIcon> </ActionIcon>
), </Group>
},
] <Stack gap={6} style={{ flex: 1, minHeight: 0, marginTop: 8 }}>
return cols <Text size="sm" c="dimmed">
}, [load, router, type]) {record.remarks ? record.remarks : "暂无备注"}
</Text>
</Stack>
</Paper>
)
}
const PaginationBar = (props: {
currentPage: number
pageSize: number
total: number
onChangePage: (page: number) => void
onChangePageSize: (size: number) => void
}) => {
const pageCount = Math.max(
1,
Math.ceil((props.total ?? 0) / props.pageSize)
)
return (
<div
style={{
height: 36,
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: 12,
paddingLeft: 12,
paddingRight: 12,
}}>
<Select
size="xs"
value={String(props.pageSize)}
data={[15, 45, 99].map((n) => ({
label: `${n}/页`,
value: String(n),
}))}
onChange={(v) => {
const next = Number(v)
if (!Number.isFinite(next)) return
props.onChangePageSize(next)
}}
allowDeselect={false}
style={{ width: 92 }}
/>
<Pagination
size="sm"
withControls
total={pageCount}
value={Math.min(props.currentPage, pageCount)}
onChange={props.onChangePage}
/>
</div>
)
}
return ( return (
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md"> <Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
@@ -500,36 +559,70 @@ export default function CollectionPage() {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
}}> }}>
<Group gap={0} align="stretch" style={{ flex: 1, minHeight: 0 }}> <div
<DataTable<Project.DataProps> style={{
width="100%" flex: 1,
style={{ width: "100%" }} minHeight: 0,
withTableBorder overflowY: "auto",
withRowBorders paddingRight: 2,
pinFirstColumn }}>
pinLastColumn {loading ? (
scrollAreaProps={{ type: "auto" }} <div
fetching={loading} style={{
records={records} height: "100%",
columns={columns} minHeight: 220,
totalRecords={total} display: "flex",
recordsPerPage={storeParams.page_size ?? 15} alignItems: "center",
page={storeParams.page_number ?? 1} justifyContent: "center",
onPageChange={(p) => { }}>
<Loader size="sm" />
</div>
) : records.length ? (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
gap: 16,
}}>
{records.map(ProjectCard)}
</div>
) : (
<div
style={{
height: "100%",
minHeight: 220,
border: "1px solid rgba(0,0,0,0.06)",
borderRadius: 12,
background: "rgba(0,0,0,0.02)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}>
<Text size="lg" c="dimmed" fw={600}>
</Text>
</div>
)}
</div>
<div style={{ height: 36, marginTop: 16 }}>
<PaginationBar
currentPage={currentPage}
pageSize={pageSize}
total={total ?? 0}
onChangePage={(p) => {
setSearchParams({ ...storeParams, page_number: p }, type) setSearchParams({ ...storeParams, page_number: p }, type)
setQueryTrigger((v) => v + 1) setQueryTrigger((v) => v + 1)
}} }}
onRecordsPerPageChange={(s) => { onChangePageSize={(s) => {
setSearchParams( setSearchParams(
{ ...storeParams, page_number: 1, page_size: s }, { ...storeParams, page_number: 1, page_size: s },
type type
) )
setQueryTrigger((v) => v + 1) setQueryTrigger((v) => v + 1)
}} }}
recordsPerPageOptions={[10, 15, 20, 50]}
noRecordsText="暂无数据"
/> />
</Group> </div>
</Paper> </Paper>
</Stack> </Stack>
) )