perf(style): use skill
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
31
app/theme.ts
31
app/theme.ts
@@ -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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user