perf(style): use skill

This commit is contained in:
zhangheng
2026-04-29 16:27:07 +08:00
parent 9d9bf4ff7f
commit 8372fb4a64
8 changed files with 267 additions and 45 deletions

View File

@@ -25,3 +25,12 @@ body {
:-webkit-full-screen::backdrop { :-webkit-full-screen::backdrop {
background: var(--mantine-color-body); background: var(--mantine-color-body);
} }
.mantine-datatable-page-size-selector-active {
background: var(--mantine-primary-color-filled) !important;
color: var(--mantine-color-white) !important;
}
.mantine-datatable-page-size-selector-active .mantine-Text-root {
color: inherit !important;
}

View File

@@ -19,9 +19,19 @@ import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications" import { Notifications } from "@mantine/notifications"
import { Suspense } from "react" import { Suspense } from "react"
import "./globals.css" import "./globals.css"
import settingClasses from "@/components/setting/PageSurface.module.css"
import { theme } from "./theme" import { theme } from "./theme"
const GLOBAL_NOTICE_Z_INDEX = 4000 const GLOBAL_NOTICE_Z_INDEX = 4000
const sharedModalProps = {
centered: true,
classNames: {
content: settingClasses.modalContent,
header: settingClasses.modalHeader,
title: settingClasses.modalTitle,
body: settingClasses.modalBody,
},
}
export default function RootLayout({ export default function RootLayout({
children, children,
@@ -61,7 +71,7 @@ export default function RootLayout({
<Notifications zIndex={GLOBAL_NOTICE_Z_INDEX} withinPortal /> <Notifications zIndex={GLOBAL_NOTICE_Z_INDEX} withinPortal />
<InfoCheck /> <InfoCheck />
<OptStore /> <OptStore />
<ModalsProvider> <ModalsProvider modalProps={sharedModalProps}>
<Suspense fallback={<></>}>{children}</Suspense> <Suspense fallback={<></>}>{children}</Suspense>
</ModalsProvider> </ModalsProvider>
</MantineProvider> </MantineProvider>

View File

@@ -96,7 +96,7 @@
padding: 0; padding: 0;
background: transparent; background: transparent;
color: #1874ff; color: #1874ff;
font-size: 12px; font-size: 13px;
line-height: 20px; line-height: 20px;
cursor: pointer; cursor: pointer;
transition: transition:

View File

@@ -1,5 +1,5 @@
// theme.ts // theme.ts
import { Button, createTheme, MantineColorsTuple } from "@mantine/core" import { Button, Menu, createTheme, MantineColorsTuple } from "@mantine/core"
/* -------- 1. 自定义颜色(可删/可改) -------- */ /* -------- 1. 自定义颜色(可删/可改) -------- */
const brandColors: MantineColorsTuple = [ const brandColors: MantineColorsTuple = [
@@ -151,5 +151,34 @@ export const theme = createTheme({
} }
}, },
}), }),
Menu: Menu.extend({
defaultProps: {
radius: "sm",
shadow: "sm",
},
styles() {
return {
dropdown: {
border: "1px solid #E5E6EB",
borderRadius: "4px",
background: "#FFFFFF",
boxShadow: "0 8px 20px rgba(15, 23, 42, 0.08)",
padding: "4px",
},
item: {
minHeight: "28px",
borderRadius: "4px",
padding: "4px 8px",
fontSize: "12px",
lineHeight: "20px",
color: "#4E5969",
},
arrow: {
borderColor: "#E5E6EB",
background: "#FFFFFF",
},
}
},
}),
}, },
}) })

View File

@@ -42,9 +42,9 @@
} }
.statusWaiting { .statusWaiting {
border-color: #e7d2a7; border-color: #ffdba5;
background: #fbf7ee; background: #fff7e8;
color: #92641e; color: #d46b08;
} }
.statusActive { .statusActive {
@@ -68,9 +68,9 @@
.rejectedPill { .rejectedPill {
min-height: 20px; min-height: 20px;
padding: 0 7px; padding: 0 7px;
border-color: #e8c98a; border-color: #ffc987;
background: #fff8ed; background: #fff3e0;
color: #a46b12; color: #c65d0e;
font-size: 11px; font-size: 11px;
font-weight: 500; font-weight: 500;
line-height: 16px; line-height: 16px;

View File

@@ -57,16 +57,56 @@
padding: 8px 8px 0; padding: 8px 8px 0;
} }
.navbarIconLink {
display: flex;
width: 100%;
min-height: 40px;
align-items: center;
justify-content: center;
border-radius: 4px;
color: #4e5969;
transition:
background-color 0.2s ease,
color 0.2s ease;
}
.navbarIconLink:hover {
background: #eef4ff;
color: #1874ff;
}
.navbarIconLinkActive {
background: #e7efff;
color: #1874ff;
}
.navbarFooter { .navbarFooter {
border-top: 1px solid #e5e6eb; border-top: 1px solid #e5e6eb;
padding: 6px 0 0; padding: 4px 8px;
}
.navbarToggleIcon {
display: inline-flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
border-radius: 4px;
color: #4e5969;
transition:
background-color 0.2s ease,
color 0.2s ease;
}
.navbarToggleIcon:hover {
background: #f2f3f5;
color: #1874ff;
} }
.main { .main {
background: #f7f8fa; background: #f7f8fa;
} }
.userName { .userName {
color: #1d2129; color: #1d2129;
font-size: 14px; font-size: 14px;

View File

@@ -22,6 +22,8 @@ import Avvvatars from "avvvatars-react"
import { useDisclosure, useMediaQuery } from "@mantine/hooks" import { useDisclosure, useMediaQuery } from "@mantine/hooks"
import { import {
IconChevronDown, IconChevronDown,
IconChevronLeft,
IconChevronRight,
IconLogout, IconLogout,
IconMoon, IconMoon,
IconSun, IconSun,
@@ -372,6 +374,10 @@ export default function AppLayout({
/> />
) )
} else { } else {
const isGroupActive = pathname.startsWith(
`/${activeHeaderMenu.url}/${item.url}`
)
return ( return (
<Menu <Menu
width={"auto"} width={"auto"}
@@ -381,19 +387,25 @@ export default function AppLayout({
openDelay={100} openDelay={100}
closeDelay={200}> closeDelay={200}>
<Menu.Target> <Menu.Target>
<NavLink <Tooltip
noWrap={true} label={item.title || ""}
leftSection={ position="right"
item.icon && ( openDelay={100}
closeDelay={200}
transitionProps={{ duration: 100 }}>
<UnstyledButton
className={cx(classes.navbarIconLink, {
[classes.navbarIconLinkActive]: isGroupActive,
})}
aria-label={item.title}>
{item.icon ? (
<ClientIcon <ClientIcon
icon={item.icon} icon={item.icon}
style={{ width: 14, height: 14 }} style={{ width: 14, height: 14 }}
/> />
) ) : null}
} </UnstyledButton>
label={item.title} </Tooltip>
styles={getShellNavLinkStyles(false)}
/>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
{item.items?.map((sub) => {item.items?.map((sub) =>
@@ -436,23 +448,40 @@ export default function AppLayout({
openDelay={100} openDelay={100}
closeDelay={200} closeDelay={200}
transitionProps={{ duration: 100 }}> transitionProps={{ duration: 100 }}>
<NavLink {isOpen ? (
// href={`/${activeHeaderMenu.url}/${item.url}`} <NavLink
onClick={() => { // href={`/${activeHeaderMenu.url}/${item.url}`}
toggleMobile() onClick={() => {
router.push(`/${activeHeaderMenu.url}/${item.url}`) toggleMobile()
}} router.push(`/${activeHeaderMenu.url}/${item.url}`)
noWrap={true} }}
active={isActive} noWrap={true}
leftSection={ active={isActive}
leftSection={
<ClientIcon
icon={item.icon}
style={{ width: 14, height: 14 }}
/>
}
label={item.title}
styles={getShellNavLinkStyles(isActive)}
/>
) : (
<UnstyledButton
className={cx(classes.navbarIconLink, {
[classes.navbarIconLinkActive]: isActive,
})}
aria-label={item.title}
onClick={() => {
toggleMobile()
router.push(`/${activeHeaderMenu.url}/${item.url}`)
}}>
<ClientIcon <ClientIcon
icon={item.icon} icon={item.icon}
style={{ width: 14, height: 14 }} style={{ width: 14, height: 14 }}
/> />
} </UnstyledButton>
label={item.title} )}
styles={getShellNavLinkStyles(isActive)}
/>
</Tooltip> </Tooltip>
) )
} }
@@ -460,17 +489,31 @@ export default function AppLayout({
</ScrollArea> </ScrollArea>
</Stack> </Stack>
<Stack justify="center" gap={0} className={classes.navbarFooter}> <Stack justify="center" gap={0} className={classes.navbarFooter}>
<Flex <Flex justify={isOpen ? "flex-end" : "center"} align="center">
justify={isOpen ? "flex-end" : "center"} <Box visibleFrom="sm">
align="center" <Tooltip
mb={"sm"}> events={{
<Burger hover: !isOpen,
opened={false} focus: false,
onClick={() => updateIsOpen(!isOpen)} touch: false,
size="sm" }}
visibleFrom="sm" label={isOpen ? "收起菜单" : "展开菜单"}
mr={isOpen ? "sm" : undefined} position="right"
/> openDelay={100}
closeDelay={200}
transitionProps={{ duration: 100 }}>
<UnstyledButton
className={classes.navbarToggleIcon}
aria-label={isOpen ? "收起菜单" : "展开菜单"}
onClick={() => updateIsOpen(!isOpen)}>
{isOpen ? (
<IconChevronLeft size={18} />
) : (
<IconChevronRight size={18} />
)}
</UnstyledButton>
</Tooltip>
</Box>
</Flex> </Flex>
</Stack> </Stack>
</AppShell.Navbar> </AppShell.Navbar>

View File

@@ -13,6 +13,77 @@
box-shadow: none; box-shadow: none;
} }
.modalContent {
overflow: hidden;
border-radius: 8px;
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.08);
}
.modalHeader {
padding: 16px 20px 0;
background: #ffffff;
}
.modalTitle {
color: #1d2129;
font-size: 16px;
font-weight: 500;
line-height: 24px;
}
.modalBody {
padding: 16px 20px 20px;
background: #ffffff;
}
.modalContent :global(.mantine-Button-root) {
min-height: 32px;
border-radius: 4px;
padding-inline: 12px;
font-size: 14px;
font-weight: 500;
box-shadow: none;
transition:
background-color 0.2s ease,
border-color 0.2s ease,
color 0.2s ease;
}
.modalContent :global(.mantine-Button-root[data-variant="filled"]) {
--button-bg: #1874ff;
--button-hover: #0f6ae8;
--button-color: #ffffff;
--button-bd: transparent;
background: #1874ff !important;
color: #ffffff !important;
}
.modalContent :global(.mantine-Button-root[data-variant="filled"]:hover) {
background: #0f6ae8 !important;
}
.modalContent :global(.mantine-Button-root[data-variant="default"]) {
border-color: #e5e6eb;
background: #ffffff;
color: #4e5969;
}
.modalContent :global(.mantine-Button-root[data-disabled]),
.modalContent :global(.mantine-Button-root:disabled) {
border-color: #e5e6eb !important;
background: #f2f3f5 !important;
color: #c9cdd4 !important;
cursor: not-allowed;
opacity: 1;
}
.modalContent :global(.mantine-Button-root[data-disabled]:hover),
.modalContent :global(.mantine-Button-root:disabled:hover) {
border-color: #e5e6eb !important;
background: #f2f3f5 !important;
color: #c9cdd4 !important;
}
.filterPanel { .filterPanel {
position: relative; position: relative;
} }
@@ -121,6 +192,26 @@
color: #4e5969; color: #4e5969;
} }
.filterPanel :global(.mantine-Button-root[data-disabled]),
.filterPanel :global(.mantine-Button-root:disabled),
.headerActions :global(.mantine-Button-root[data-disabled]),
.headerActions :global(.mantine-Button-root:disabled) {
border-color: #e5e6eb !important;
background: #f2f3f5 !important;
color: #c9cdd4 !important;
cursor: not-allowed;
opacity: 1;
}
.filterPanel :global(.mantine-Button-root[data-disabled]:hover),
.filterPanel :global(.mantine-Button-root:disabled:hover),
.headerActions :global(.mantine-Button-root[data-disabled]:hover),
.headerActions :global(.mantine-Button-root:disabled:hover) {
border-color: #e5e6eb !important;
background: #f2f3f5 !important;
color: #c9cdd4 !important;
}
.filterPanel :global(.mantine-ActionIcon-root), .filterPanel :global(.mantine-ActionIcon-root),
.headerActions :global(.mantine-ActionIcon-root) { .headerActions :global(.mantine-ActionIcon-root) {
width: 32px; width: 32px;