323 lines
6.6 KiB
TypeScript
323 lines
6.6 KiB
TypeScript
"use client"
|
|
|
|
import {
|
|
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 - 60px)"
|
|
p="md"
|
|
gap="md"
|
|
className={mergeClassName(classes.page, className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export function SettingPanel({
|
|
className,
|
|
withBorder = true,
|
|
radius = "sm",
|
|
p = "md",
|
|
shadow,
|
|
...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 className={classes.listHeaderTitle}>{title}</Text>
|
|
{count !== undefined ? (
|
|
<Text className={classes.listHeaderCount}>{count}</Text>
|
|
) : 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: "#F7F8FA",
|
|
borderBottom: "1px solid #E5E6EB",
|
|
color: "#4E5969",
|
|
fontSize: "14px",
|
|
fontWeight: 500,
|
|
},
|
|
root: {
|
|
height: "100%",
|
|
maxWidth: "100%",
|
|
minWidth: 0,
|
|
fontSize: "14px",
|
|
color: "#1D2129",
|
|
borderBottom: "1px solid #E5E6EB",
|
|
borderRadius: 4,
|
|
overflow: "hidden",
|
|
boxSizing: "border-box",
|
|
backgroundColor: "#FFFFFF",
|
|
},
|
|
table: {
|
|
backgroundColor: "#FFFFFF",
|
|
},
|
|
footer: {
|
|
backgroundColor: "#FFFFFF",
|
|
},
|
|
pagination: {
|
|
padding: "12px 16px",
|
|
borderTop: "1px solid #E5E6EB",
|
|
},
|
|
} as const
|
|
|
|
const settingDataTableDefaultColumnProps: DataTableDefaultColumnProps<
|
|
Record<string, unknown>
|
|
> = {
|
|
titleStyle: {
|
|
whiteSpace: "nowrap",
|
|
fontWeight: 500,
|
|
},
|
|
}
|
|
|
|
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 ?? false,
|
|
highlightOnHover: highlightOnHover ?? false,
|
|
defaultColumnProps:
|
|
defaultColumnProps ??
|
|
(settingDataTableDefaultColumnProps as DataTableDefaultColumnProps<T>),
|
|
loaderBackgroundBlur: loaderBackgroundBlur ?? 0,
|
|
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,
|
|
},
|
|
panel: {
|
|
flex: 1,
|
|
minHeight: 0,
|
|
minWidth: 0,
|
|
paddingTop: 16,
|
|
},
|
|
} as const
|
|
|
|
export const settingTabsClassNames = {
|
|
list: classes.settingTabsList,
|
|
tab: classes.settingTabsTab,
|
|
panel: classes.settingTabsPanel,
|
|
} as const
|