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

@@ -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