fix(daily): fix style
This commit is contained in:
128
components/setting/PageSurface.module.css
Normal file
128
components/setting/PageSurface.module.css
Normal 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;
|
||||
}
|
||||
316
components/setting/PageSurface.tsx
Normal file
316
components/setting/PageSurface.tsx
Normal file
@@ -0,0 +1,316 @@
|
||||
"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%",
|
||||
maxWidth: "100%",
|
||||
minWidth: 0,
|
||||
fontSize: "14px",
|
||||
color: "#31373D",
|
||||
borderBottom: "1px solid #E8EDF3",
|
||||
borderRadius: 10,
|
||||
overflow: "hidden",
|
||||
boxSizing: "border-box",
|
||||
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,
|
||||
minWidth: 0,
|
||||
paddingTop: 16,
|
||||
},
|
||||
} as const
|
||||
Reference in New Issue
Block a user