fix(style): fix

This commit is contained in:
zhangheng
2026-04-03 15:12:00 +08:00
parent bbde4df329
commit 0eb18943a4
9 changed files with 642 additions and 201 deletions

View File

@@ -9,12 +9,9 @@ import {
} from "@/components/label/store/auth"
import { OwnTaskStatusOpts } from "@/components/label/utils/constants"
import {
ActionIcon,
Button,
Collapse,
Flex,
Group,
Paper,
Select,
SimpleGrid,
Stack,
@@ -28,9 +25,17 @@ import {
IconRefresh,
IconSearch,
} from "@tabler/icons-react"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react"
import {
SettingContentPanel,
SettingDataTable,
SettingFilterActions,
SettingFilterPanel,
SettingHeaderActions,
SettingListHeader,
} from "@/components/setting/PageSurface"
import TaskStatusTag from "./components/TaskStatusTag"
function createInitialFilters() {
@@ -109,7 +114,6 @@ export default function OwnTaskTableContainer(props: {
}
return next
}, [
,
form.search_id,
form.search_status,
pageNumber,
@@ -173,6 +177,24 @@ export default function OwnTaskTableContainer(props: {
}
}
const handleSearch = () => {
setPageNumber(1)
setForm({ ...filters })
setQueryTrigger((v) => v + 1)
}
const handleReset = () => {
const next = createInitialFilters()
setFilters(next)
setForm(next)
setPageNumber(1)
setQueryTrigger((v) => v + 1)
}
const handleRefresh = () => {
setQueryTrigger((v) => v + 1)
}
const columns = useMemo(() => {
const cols: DataTableColumn<Task.DataProps>[] = [
{
@@ -267,12 +289,11 @@ export default function OwnTaskTableContainer(props: {
return (
<Stack gap="sm" style={{ flex: 1, minHeight: 0 }}>
<Paper withBorder p="sm" radius="sm">
<SettingFilterPanel>
<Group justify="space-between" wrap="wrap" gap="sm">
<Group gap="xs">
<Text fw={700}></Text>
<Text fw={700}></Text>
<Button
size="xs"
variant="transparent"
rightSection={
searchExpanded ? (
@@ -285,43 +306,20 @@ export default function OwnTaskTableContainer(props: {
{searchExpanded ? "收起筛选" : "展开筛选"}
</Button>
</Group>
<Group gap="xs">
<Button
size="xs"
radius={"xs"}
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={() => {
const next = createInitialFilters()
setFilters(next)
setForm(next)
setPageNumber(1)
}}>
</Button>
<Button
size="xs"
radius={"xs"}
leftSection={<IconSearch size={16} />}
onClick={() => {
setPageNumber(1)
setForm({ ...filters })
}}>
</Button>
</Group>
</Group>
<Collapse
in={searchExpanded}
transitionDuration={250}
transitionTimingFunction="ease"
animateOpacity>
<Stack gap="sm" mt="sm">
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
<Stack gap="md" mt="md">
<SimpleGrid
cols={{ base: 1, sm: 2, md: 3, lg: 4 }}
spacing="md"
verticalSpacing="md">
<TextInput
size="xs"
radius="xs"
label="任务ID"
placeholder="请输入任务 ID"
value={filters.search_id}
onChange={(e) =>
setFilters((s) => ({
@@ -332,8 +330,6 @@ export default function OwnTaskTableContainer(props: {
style={{ width: 160 }}
/>
<Select
size="xs"
radius="xs"
label="状态"
data={Object.entries(OwnTaskStatusOpts).map(
([value, label]) => ({
@@ -346,60 +342,70 @@ export default function OwnTaskTableContainer(props: {
setFilters((s) => ({ ...s, search_status: v ?? "0" }))
}
allowDeselect={false}
style={{ width: 160 }}
/>
</SimpleGrid>
</Stack>
</Collapse>
</Paper>
<SettingFilterActions>
<Button
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={handleReset}>
</Button>
<Button
leftSection={<IconSearch size={16} />}
onClick={handleSearch}
loading={loading}>
</Button>
</SettingFilterActions>
</SettingFilterPanel>
<Paper
withBorder
p="md"
radius="sm"
<SettingContentPanel
style={{
flex: 1,
minHeight: 0,
display: "flex",
flexDirection: "column",
}}>
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
<Flex justify={"space-between"} align="center" w="100%">
<Flex gap={12}>
<Button
size="xs"
radius="xs"
variant="default"
disabled={!isLabel}
onClick={() => handleAcquireTask(1)}>
</Button>
<Button
size="xs"
radius="xs"
variant="default"
disabled={!isReview1}
onClick={() => handleAcquireTask(2)}>
</Button>
<Button
size="xs"
radius="xs"
variant="default"
disabled={!isReview2}
onClick={() => handleAcquireTask(3)}>
</Button>
</Flex>
<ActionIcon onClick={load} variant="transparent">
<IconRefresh size={16} />
</ActionIcon>
</Flex>
<DataTable<Task.DataProps>
<Stack gap="sm" align="stretch" style={{ flex: 1, minHeight: 0 }}>
<SettingListHeader
title="我的任务"
count={`${total}`}
actions={
<SettingHeaderActions>
<Button
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={handleRefresh}
loading={loading}>
</Button>
<Button
variant="default"
disabled={!isLabel}
onClick={() => handleAcquireTask(1)}>
</Button>
<Button
variant="default"
disabled={!isReview1}
onClick={() => handleAcquireTask(2)}>
</Button>
<Button
variant="default"
disabled={!isReview2}
onClick={() => handleAcquireTask(3)}>
</Button>
</SettingHeaderActions>
}
/>
<SettingDataTable<Task.DataProps>
width="100%"
style={{ width: "100%" }}
withTableBorder
withRowBorders
style={{ width: "100%", flex: 1 }}
pinFirstColumn
pinLastColumn
scrollAreaProps={{ type: "auto" }}
@@ -415,11 +421,9 @@ export default function OwnTaskTableContainer(props: {
setPageNumber(1)
}}
recordsPerPageOptions={[10, 15, 20]}
noRecordsText="暂无数据"
recordsPerPageLabel="当前页数"
/>
</Stack>
</Paper>
</SettingContentPanel>
</Stack>
)
}

View File

@@ -12,13 +12,19 @@ import {
TaskStatusOpts,
} from "@/components/label/utils/constants"
import {
ActionIcon,
SettingContentPanel,
SettingDataTable,
SettingFilterActions,
SettingFilterPanel,
SettingHeaderActions,
SettingListHeader,
} from "@/components/setting/PageSurface"
import {
Button,
Collapse,
Flex,
Group,
MultiSelect,
Paper,
Select,
SimpleGrid,
Stack,
@@ -35,7 +41,7 @@ import {
} from "@tabler/icons-react"
import dayjs from "dayjs"
import duration from "dayjs/plugin/duration"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useMemo, useState } from "react"
import * as XLSX from "xlsx-js-style"
@@ -125,7 +131,6 @@ export default function TaskTableContainer(props: {
}
}) {
const { info, userEnums } = props
false && console.log(info)
const { user_id } = usePermissionStore()
@@ -444,6 +449,24 @@ export default function TaskTableContainer(props: {
} catch {}
}
const handleSearch = () => {
setPageNumber(1)
setForm({ ...filters })
setQueryTrigger((v) => v + 1)
}
const handleReset = () => {
const next = createInitialFilters()
setFilters(next)
setForm(next)
setPageNumber(1)
setQueryTrigger((v) => v + 1)
}
const handleRefresh = () => {
setQueryTrigger((v) => v + 1)
}
const getTime = (time: number) => {
return dayjs.duration(time, "seconds").format("HH:mm:ss")
}
@@ -775,12 +798,11 @@ export default function TaskTableContainer(props: {
return (
<>
<Stack gap="sm" style={{ flex: 1, minHeight: 0 }}>
<Paper withBorder p="sm" radius="sm">
<SettingFilterPanel>
<Group justify="space-between" wrap="wrap" gap="sm">
<Group gap="xs">
<Text fw={700}></Text>
<Text fw={700}></Text>
<Button
size="xs"
variant="transparent"
rightSection={
searchExpanded ? (
@@ -793,43 +815,20 @@ export default function TaskTableContainer(props: {
{searchExpanded ? "收起筛选" : "展开筛选"}
</Button>
</Group>
<Group gap="xs">
<Button
size="xs"
radius={"xs"}
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={() => {
const next = createInitialFilters()
setFilters(next)
setForm(next)
setPageNumber(1)
}}>
</Button>
<Button
size="xs"
radius={"xs"}
leftSection={<IconSearch size={16} />}
onClick={() => {
setPageNumber(1)
setForm({ ...filters })
}}>
</Button>
</Group>
</Group>
<Collapse
in={searchExpanded}
transitionDuration={250}
transitionTimingFunction="ease"
animateOpacity>
<Stack gap="sm" mt="sm">
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
<Stack gap="md" mt="md">
<SimpleGrid
cols={{ base: 1, sm: 2, md: 3, lg: 4 }}
spacing="md"
verticalSpacing="md">
<TextInput
size="xs"
radius="xs"
label="任务ID"
placeholder="请输入任务 ID"
value={filters.search_id}
onChange={(e) =>
setFilters((s) => ({
@@ -839,8 +838,6 @@ export default function TaskTableContainer(props: {
}
/>
<Select
size="xs"
radius="xs"
label="任务状态"
data={Object.entries(TaskStatusOpts).map(
([value, label]) => ({
@@ -855,8 +852,6 @@ export default function TaskTableContainer(props: {
allowDeselect={false}
/>
<Select
size="xs"
radius="xs"
label="是否返工"
data={[
{ label: "全部", value: "" },
@@ -871,8 +866,6 @@ export default function TaskTableContainer(props: {
/>
<MultiSelect
label="当前负责人"
size="xs"
radius="xs"
data={Array.from(userEnums.allEnum.entries()).map(
([x, y]) => ({ label: y, value: x.toString() })
)}
@@ -885,8 +878,6 @@ export default function TaskTableContainer(props: {
/>
<MultiSelect
label="标注员"
size="xs"
radius="xs"
data={Array.from(userEnums.labelEnum.entries()).map(
([x, y]) => ({ label: y, value: x.toString() })
)}
@@ -897,8 +888,6 @@ export default function TaskTableContainer(props: {
/>
<MultiSelect
label="审核员"
size="xs"
radius="xs"
data={Array.from(userEnums.review1Enum.entries()).map(
([x, y]) => ({ label: y, value: x.toString() })
)}
@@ -911,8 +900,6 @@ export default function TaskTableContainer(props: {
/>
<MultiSelect
label="复审员"
size="xs"
radius="xs"
data={Array.from(userEnums.review2Enum.entries()).map(
([x, y]) => ({ label: y, value: x.toString() })
)}
@@ -926,54 +913,69 @@ export default function TaskTableContainer(props: {
</SimpleGrid>
</Stack>
</Collapse>
</Paper>
<Paper
withBorder
p="md"
radius="sm"
<SettingFilterActions>
<Button
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={handleReset}>
</Button>
<Button
leftSection={<IconSearch size={16} />}
onClick={handleSearch}
loading={loading}>
</Button>
</SettingFilterActions>
</SettingFilterPanel>
<SettingContentPanel
style={{
flex: 1,
minHeight: 0,
display: "flex",
flexDirection: "column",
}}>
<Stack gap={12} align="stretch" style={{ flex: 1, minHeight: 0 }}>
<Flex justify={"space-between"} align="center" w="100%">
<Flex gap={12}>
<Button
size="xs"
radius="xs"
variant="outline"
disabled={!selectedRecords.length}
onClick={handleExport}>
</Button>
<Button
size="xs"
radius="xs"
variant="outline"
disabled={!selectedRecords.length}
onClick={clickDispatchTask}>
</Button>
<Button
size="xs"
radius="xs"
variant="outline"
disabled={!selectedRecords.length}
onClick={clickReleaseTask}>
</Button>
</Flex>
<ActionIcon onClick={load} variant="transparent">
<IconRefresh size={16} />
</ActionIcon>
</Flex>
<DataTable<Task.DataProps>
<Stack gap="sm" align="stretch" style={{ flex: 1, minHeight: 0 }}>
<SettingListHeader
title="任务列表"
count={
selectedRecords.length
? `${total} 条 / 已选 ${selectedRecords.length}`
: `${total}`
}
actions={
<SettingHeaderActions>
<Button
variant="default"
leftSection={<IconRefresh size={16} />}
onClick={handleRefresh}
loading={loading}>
</Button>
<Button
variant="default"
disabled={!selectedRecords.length}
onClick={handleExport}>
</Button>
<Button
variant="default"
disabled={!selectedRecords.length}
onClick={clickDispatchTask}>
</Button>
<Button
variant="default"
disabled={!selectedRecords.length}
onClick={clickReleaseTask}>
</Button>
</SettingHeaderActions>
}
/>
<SettingDataTable<Task.DataProps>
width="100%"
style={{ width: "100%" }}
withTableBorder
withRowBorders
style={{ width: "100%", flex: 1 }}
pinFirstColumn
pinLastColumn
scrollAreaProps={{ type: "auto" }}
@@ -991,11 +993,9 @@ export default function TaskTableContainer(props: {
selectedRecords={selectedRecords}
onSelectedRecordsChange={setSelectedRecords}
recordsPerPageOptions={[10, 15, 20]}
noRecordsText="暂无数据"
recordsPerPageLabel="当前页数"
/>
</Stack>
</Paper>
</SettingContentPanel>
</Stack>
<WorkflowModal

View File

@@ -6,8 +6,9 @@ import {
selectModeMap,
staticsColor,
} from "@/components/label/utils/constants"
import { SettingDataTable } from "@/components/setting/PageSurface"
import { Badge, Button, Group, Modal, Paper, Stack, Text } from "@mantine/core"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { useState } from "react"
type DetailRow = ProjectDetail.SubAttributesDescribe
@@ -188,9 +189,7 @@ export default function LabelSchemeContainer(props: {
}}>
<Stack gap="sm">
<Text fw={700}></Text>
<DataTable<ProjectDetail.LabelSchemaList>
withTableBorder
withRowBorders
<SettingDataTable<ProjectDetail.LabelSchemaList>
idAccessor="category_id"
records={schemas}
columns={columns}
@@ -209,9 +208,7 @@ export default function LabelSchemeContainer(props: {
title="子属性描述"
centered
size="70%">
<DataTable<DetailRow>
withTableBorder
withRowBorders
<SettingDataTable<DetailRow>
records={detailRows}
columns={detailColumns}
noRecordsText="暂无数据"

View File

@@ -3,6 +3,7 @@
import { projectConfig } from "@/components/label/api/project"
import { ProjectDetail } from "@/components/label/api/project/typing"
import { usePermissionStore } from "@/components/label/store/auth"
import { SettingDataTable } from "@/components/setting/PageSurface"
import {
ActionIcon,
Badge,
@@ -18,7 +19,7 @@ import { modals } from "@mantine/modals"
import { notifications } from "@mantine/notifications"
import { IconPlus, IconTrash } from "@tabler/icons-react"
import dayjs from "dayjs"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { useMemo, useState } from "react"
type RulePayload = {
@@ -221,9 +222,7 @@ export default function NoteRulesTable(props: {
</Text>
</Stack>
<DataTable<RuleRow>
withTableBorder
withRowBorders
<SettingDataTable<RuleRow>
records={rows}
columns={columns}
noRecordsText="暂无规则"

View File

@@ -2,6 +2,7 @@
import { projectConfig } from "@/components/label/api/project"
import { Project, ProjectDetail } from "@/components/label/api/project/typing"
import { SettingDataTable } from "@/components/setting/PageSurface"
import {
ActionIcon,
Badge,
@@ -22,7 +23,7 @@ import {
import { modals } from "@mantine/modals"
import { notifications } from "@mantine/notifications"
import { IconEdit, IconPlus, IconX } from "@tabler/icons-react"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { ReactNode, useEffect, useMemo, useState } from "react"
import StageUserModal from "./StageUserModal"
import {
@@ -1429,9 +1430,7 @@ export default function TaskStageTabsContainer(props: {
</Button>
</Group>
<DataTable<StageUserRow>
withTableBorder
withRowBorders
<SettingDataTable<StageUserRow>
records={rows}
columns={userColumns(stage)}
noRecordsText="暂无人员"

View File

@@ -1,9 +1,10 @@
"use client"
import { getTaskWorkFlow } from "@/components/label/api/task"
import { SettingDataTable } from "@/components/setting/PageSurface"
import { Button, Group, Modal, ScrollArea, Stack, Text } from "@mantine/core"
import { notifications } from "@mantine/notifications"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { DataTableColumn } from "mantine-datatable"
import { useEffect, useMemo, useState } from "react"
import TaskStatusTag from "./TaskStatusTag"
@@ -139,15 +140,13 @@ export default function WorkflowModal(props: {
size={720}>
<Stack gap="sm">
<ScrollArea h={360} type="auto">
<DataTable<WorkflowRow>
withTableBorder
withRowBorders
<SettingDataTable<WorkflowRow>
pinLastColumn
fetching={loading}
idAccessor="__rowId"
records={rows}
width={Math.max(columns.length * 140, 640)}
columns={columns}
noRecordsText={loading ? "加载中..." : "暂无数据"}
minHeight={220}
/>
</ScrollArea>

View File

@@ -7,13 +7,16 @@ import {
Breadcrumbs,
Flex,
Group,
Paper,
Stack,
Text,
UnstyledButton,
} from "@mantine/core"
import { useRouter, useSearchParams } from "next/navigation"
import { useEffect, useMemo, useState } from "react"
import {
SettingFilterPanel,
SettingPage,
} from "@/components/setting/PageSurface"
import InfoSettingContainer from "./InfoSettingContainer"
import OwnTaskTableContainer from "./OwnTaskTableContainer"
import TaskBoardContainer from "./TaskBoardContainer"
@@ -193,15 +196,15 @@ export default function CollectionDetailPage() {
}
return (
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
<Paper withBorder p="md" radius="sm">
<SettingPage>
<SettingFilterPanel>
<Group justify="space-between" wrap="wrap" gap="sm">
<Flex gap="sm" w="100%" justify="space-between">
<Breadcrumbs separator=">">{breadcrumbsItems}</Breadcrumbs>
{DetailMenuItems()}
</Flex>
</Group>
</Paper>
</SettingFilterPanel>
<Stack style={{ flex: 1, minHeight: 0 }} gap="md">
{selectKey === "own" ? (
@@ -219,6 +222,6 @@ export default function CollectionDetailPage() {
/>
) : null}
</Stack>
</Stack>
</SettingPage>
)
}

View File

@@ -0,0 +1,128 @@
.page {
width: 100%;
height: calc(100vh - 56px);
padding: 16px;
gap: 16px;
}
.panel {
border-color: #e6ebf2;
border-radius: 12px;
background: #ffffff;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
}
.filterPanel {
position: relative;
}
.filterPanel :global(.mantine-InputWrapper-label) {
color: #1f2329;
font-size: 14px;
font-weight: 500;
line-height: 22px;
margin-bottom: 6px;
}
.filterPanel :global(.mantine-Input-input),
.filterPanel :global(.mantine-Select-input),
.filterPanel :global(.mantine-NumberInput-input),
.filterPanel :global(.mantine-PillsInput-input),
.filterPanel :global(.mantine-Textarea-input) {
min-height: 36px;
border-color: #d0d5dd;
background: #ffffff;
color: #31373d;
font-size: 14px;
box-shadow: none;
transition:
border-color 0.15s ease,
box-shadow 0.15s ease,
background-color 0.15s ease;
}
.filterPanel :global(.mantine-Textarea-input) {
min-height: 96px;
}
.filterPanel :global(.mantine-Input-input::placeholder),
.filterPanel :global(.mantine-Select-input::placeholder),
.filterPanel :global(.mantine-NumberInput-input::placeholder),
.filterPanel :global(.mantine-PillsInput-input::placeholder),
.filterPanel :global(.mantine-Textarea-input::placeholder) {
color: #98a2b3;
}
.filterPanel :global(.mantine-Input-input:focus),
.filterPanel :global(.mantine-Select-input:focus),
.filterPanel :global(.mantine-NumberInput-input:focus),
.filterPanel :global(.mantine-PillsInput-input:focus-within),
.filterPanel :global(.mantine-Textarea-input:focus) {
border-color: #69c0ff;
box-shadow: 0 0 0 3px rgba(105, 192, 255, 0.14);
}
.filterPanel :global(.mantine-Button-root[data-variant="transparent"]) {
color: #168de8;
}
.filterPanel :global(.mantine-Button-root:not([data-variant="transparent"])),
.headerActions :global(.mantine-Button-root) {
min-height: 36px;
border-radius: 8px;
padding-inline: 14px;
font-size: 14px;
font-weight: 500;
}
.filterPanel :global(.mantine-ActionIcon-root),
.headerActions :global(.mantine-ActionIcon-root) {
width: 36px;
height: 36px;
border-radius: 8px;
}
.filterPanel :global(.mantine-Pill-root) {
background: #eef7ff;
color: #168de8;
}
.contentPanel {
border-color: #e6ebf2;
}
.filterActions,
.headerActions {
gap: 12px;
}
.listHeader {
margin-bottom: 16px;
}
.sectionHeader {
padding-bottom: 12px;
border-bottom: 1px solid #eef2f6;
}
.sectionEyebrow {
color: #168de8;
font-size: 12px;
font-weight: 700;
line-height: 18px;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.sectionTitle {
color: #1f2329;
font-size: 18px;
font-weight: 700;
line-height: 28px;
}
.sectionDescription {
color: #667085;
font-size: 13px;
line-height: 20px;
}

View File

@@ -0,0 +1,312 @@
"use client"
import {
Badge,
Group,
Paper,
Stack,
Text,
type GroupProps,
type PaperProps,
type StackProps,
} from "@mantine/core"
import type { PropsWithChildren, ReactNode } from "react"
import {
DataTable,
type DataTableDefaultColumnProps,
type DataTableProps,
} from "mantine-datatable"
import classes from "./PageSurface.module.css"
function mergeClassName(...classNames: Array<string | undefined>) {
return classNames.filter(Boolean).join(" ")
}
export function SettingPage({
className,
...props
}: PropsWithChildren<StackProps>) {
return (
<Stack
w="100%"
h="calc(100vh - 56px)"
p="md"
gap="md"
className={mergeClassName(classes.page, className)}
{...props}
/>
)
}
export function SettingPanel({
className,
withBorder = true,
radius = "lg",
p = "md",
shadow = "xs",
...props
}: PropsWithChildren<PaperProps>) {
return (
<Paper
withBorder={withBorder}
radius={radius}
p={p}
shadow={shadow}
className={mergeClassName(classes.panel, className)}
{...props}
/>
)
}
export function SettingFilterPanel({
className,
...props
}: PropsWithChildren<PaperProps>) {
return (
<SettingPanel
className={mergeClassName(classes.filterPanel, className)}
{...props}
/>
)
}
export function SettingContentPanel({
className,
...props
}: PropsWithChildren<PaperProps>) {
return (
<SettingPanel
className={mergeClassName(classes.contentPanel, className)}
{...props}
/>
)
}
type SettingSectionHeaderProps = GroupProps & {
eyebrow?: string
title: ReactNode
description?: ReactNode
actions?: ReactNode
}
export function SettingSectionHeader({
eyebrow,
title,
description,
actions,
className,
...props
}: SettingSectionHeaderProps) {
return (
<Group
justify="space-between"
align="flex-start"
gap="sm"
wrap="wrap"
className={mergeClassName(classes.sectionHeader, className)}
{...props}>
<Stack gap={2}>
{eyebrow ? (
<Text className={classes.sectionEyebrow}>{eyebrow}</Text>
) : null}
<Text className={classes.sectionTitle}>{title}</Text>
{description ? (
<Text className={classes.sectionDescription}>{description}</Text>
) : null}
</Stack>
{actions}
</Group>
)
}
type SettingListHeaderProps = {
title: ReactNode
count?: ReactNode
actions?: ReactNode
className?: string
}
export function SettingListHeader({
title,
count,
actions,
className,
}: SettingListHeaderProps) {
return (
<Group
justify="space-between"
align="center"
wrap="wrap"
gap="sm"
className={mergeClassName(classes.listHeader, className)}>
<Group gap="xs">
<Text fw={700}>{title}</Text>
{count !== undefined ? (
<Badge variant="light" color="gray">
{count}
</Badge>
) : null}
</Group>
{actions}
</Group>
)
}
export function SettingHeaderActions({
className,
gap = "sm",
...props
}: PropsWithChildren<GroupProps>) {
return (
<Group
gap={gap}
wrap="wrap"
className={mergeClassName(classes.headerActions, className)}
{...props}
/>
)
}
export function SettingFilterActions({
className,
gap = "sm",
justify = "flex-end",
mt = "md",
...props
}: PropsWithChildren<GroupProps>) {
return (
<Group
justify={justify}
gap={gap}
wrap="wrap"
mt={mt}
className={mergeClassName(classes.filterActions, className)}
{...props}
/>
)
}
const settingDataTableStyles = {
header: {
backgroundColor: "#F8F9FB",
borderBottom: "1px solid #EEF2F6",
color: "#56606A",
fontSize: "14px",
},
root: {
height: "100%",
fontSize: "14px",
color: "#31373D",
borderBottom: "1px solid #E8EDF3",
borderRadius: 10,
overflow: "hidden",
backgroundColor: "#FFFFFF",
},
pagination: {
padding: "14px 16px 18px",
},
} as const
const settingDataTableDefaultColumnProps: DataTableDefaultColumnProps<
Record<string, unknown>
> = {
titleStyle: {
whiteSpace: "nowrap",
fontWeight: 600,
},
}
export function SettingDataTable<T = Record<string, unknown>>(
props: DataTableProps<T>
) {
const {
styles,
defaultColumnProps,
withTableBorder,
withRowBorders,
striped,
highlightOnHover,
loaderBackgroundBlur,
loadingText,
noRecordsText,
minHeight,
page,
onPageChange,
totalRecords,
recordsPerPage,
recordsPerPageLabel,
paginationText,
...rest
} = props
const paginationProps =
page !== undefined &&
onPageChange !== undefined &&
recordsPerPage !== undefined
? {
page,
onPageChange,
totalRecords,
recordsPerPage,
loadingText: loadingText ?? "正在加载数据...",
recordsPerPageLabel: recordsPerPageLabel ?? "每页条数",
paginationText:
paginationText ??
(({
from,
to,
totalRecords,
}: {
from: number
to: number
totalRecords: number
}) => `${from}-${to} / ${totalRecords}`),
}
: {}
const tableProps = {
withTableBorder: withTableBorder ?? true,
withRowBorders: withRowBorders ?? true,
striped: striped ?? true,
highlightOnHover: highlightOnHover ?? true,
defaultColumnProps:
defaultColumnProps ??
(settingDataTableDefaultColumnProps as DataTableDefaultColumnProps<T>),
loaderBackgroundBlur: loaderBackgroundBlur ?? 1,
noRecordsText: noRecordsText ?? "暂无数据",
minHeight: minHeight ?? 360,
styles: styles
? { ...settingDataTableStyles, ...styles }
: settingDataTableStyles,
...paginationProps,
...rest,
} as DataTableProps<T>
return <DataTable {...tableProps} />
}
export const settingTabsStyles = {
list: {
padding: 4,
backgroundColor: "#F8F9FB",
border: "1px solid #EEF2F6",
borderRadius: 10,
gap: 8,
},
tab: {
minHeight: 36,
borderRadius: 8,
color: "#56606A",
fontWeight: 600,
"&[data-active]": {
backgroundColor: "#FFFFFF",
color: "#1F2329",
boxShadow: "0 6px 18px rgba(15, 23, 42, 0.08)",
},
},
panel: {
flex: 1,
minHeight: 0,
paddingTop: 16,
},
} as const