作家写作
This commit is contained in:
1 parent
1a122eb7fd
commit
07ad87365f
7 files changed
+380
-228
No files matched your search
@@ -16,7 +16,9 @@
|
||||
.item-nav{
|
||||
width:100%;
|
||||
padding:10px 0;
|
||||
border:0;
|
||||
border-bottom:1px solid rgba(85, 166, 254, 0.2);
|
||||
background:transparent;
|
||||
font-family:'Noto Sans S Chinese', sans-serif;
|
||||
font-weight:500;
|
||||
color:rgba(255, 255, 255, 0.8);
|
||||
@@ -44,7 +46,7 @@
|
||||
|
||||
.item-nav-content{
|
||||
width: 155px;
|
||||
height: 970px;
|
||||
min-height: 970px;
|
||||
background: rgba(3, 38, 80, 0.8);
|
||||
border: 1px solid;
|
||||
border-color: rgba(43, 143, 254, 0.6);
|
||||
@@ -54,6 +56,8 @@
|
||||
left: 97px;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
padding: 14px 13px 18px 16px;
|
||||
overflow-y:auto;
|
||||
}
|
||||
|
||||
.item-nav-active{
|
||||
@@ -81,4 +85,87 @@
|
||||
height:45px;
|
||||
background: url('../assets/icon/nav-line-logo.svg') no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-flyout-list{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:2px;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.sidebar-flyout-list--level-1,
|
||||
.sidebar-flyout-list--level-2,
|
||||
.sidebar-flyout-list--level-3{
|
||||
margin-left:19px;
|
||||
padding-left:14px;
|
||||
}
|
||||
|
||||
.sidebar-flyout-node{
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.sidebar-flyout-node--has-children.sidebar-flyout-node--expanded > .sidebar-flyout-list::before{
|
||||
content:'';
|
||||
position:absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
width:1px;
|
||||
height:100%;
|
||||
background:rgba(43, 143, 254, 0.22);
|
||||
}
|
||||
|
||||
.sidebar-flyout-item{
|
||||
width:100%;
|
||||
min-height:34px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:9px;
|
||||
border:0;
|
||||
background:transparent;
|
||||
padding:3px 0;
|
||||
font-family:Noto Sans S Chinese;
|
||||
font-size:18px;
|
||||
line-height:28px;
|
||||
font-weight:500;
|
||||
letter-spacing:0;
|
||||
color:rgba(255, 255, 255, 0.8);
|
||||
text-align:left;
|
||||
cursor:pointer;
|
||||
transition:color 0.2s ease, text-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-flyout-item:hover,
|
||||
.sidebar-flyout-item--active{
|
||||
color:#48ddff;
|
||||
text-shadow:0 0 8px rgba(72, 221, 255, 0.25);
|
||||
}
|
||||
|
||||
.sidebar-flyout-item--leaf{
|
||||
justify-content:center;
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
.sidebar-flyout-list--level-1 > .sidebar-flyout-node > .sidebar-flyout-item--leaf,
|
||||
.sidebar-flyout-list--level-2 > .sidebar-flyout-node > .sidebar-flyout-item--leaf,
|
||||
.sidebar-flyout-list--level-3 > .sidebar-flyout-node > .sidebar-flyout-item--leaf{
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
.sidebar-flyout-arrow{
|
||||
width:9px;
|
||||
height:9px;
|
||||
flex:0 0 9px;
|
||||
background:url('../assets/icon/down-logo.svg') center / contain no-repeat;
|
||||
transform:rotate(-90deg);
|
||||
transition:transform 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-flyout-node--expanded > .sidebar-flyout-item > .sidebar-flyout-arrow{
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
|
||||
.sidebar-flyout-label{
|
||||
flex:1;
|
||||
white-space:nowrap;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { ElementType } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import {
|
||||
@@ -285,7 +285,10 @@ export function PageSidebarDark({
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const isAdmin = getStoredAuth()?.system_role === "admin";
|
||||
const visibleMenuItems = filterMenuItemsByRole(menuItems, !!isAdmin);
|
||||
const visibleMenuItems = useMemo(
|
||||
() => filterMenuItemsByRole(menuItems, !!isAdmin),
|
||||
[isAdmin],
|
||||
);
|
||||
const { t } = useI18n();
|
||||
const { data: unreadCount = 0 } = useUnreadCount();
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(defaultExpandedIds);
|
||||
@@ -306,6 +309,26 @@ export function PageSidebarDark({
|
||||
setSettingsOpen(true);
|
||||
};
|
||||
|
||||
const hasActiveChild = (item: MenuItem): boolean => {
|
||||
if (item.path && pathname === item.path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return item.children?.some(hasActiveChild) ?? false;
|
||||
};
|
||||
|
||||
const initialActiveTopItem = visibleMenuItems.find(hasActiveChild) ?? visibleMenuItems[0];
|
||||
const [activeTopId, setActiveTopId] = useState(initialActiveTopItem?.id ?? "qa-center");
|
||||
|
||||
useEffect(() => {
|
||||
const matchedTopItem = visibleMenuItems.find(hasActiveChild);
|
||||
if (matchedTopItem) {
|
||||
setActiveTopId(matchedTopItem.id);
|
||||
}
|
||||
}, [pathname, visibleMenuItems]);
|
||||
|
||||
const activeTopItem = visibleMenuItems.find((item) => item.id === activeTopId) ?? initialActiveTopItem;
|
||||
|
||||
const toggleExpanded = (id: string) => {
|
||||
setExpandedIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -318,6 +341,51 @@ export function PageSidebarDark({
|
||||
});
|
||||
};
|
||||
|
||||
const handleTopItemClick = (item: MenuItem) => {
|
||||
setActiveTopId(item.id);
|
||||
|
||||
if (!item.children?.length && item.path) {
|
||||
navigate(item.path);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (item: MenuItem) => {
|
||||
if (item.children?.length) {
|
||||
toggleExpanded(item.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.path) {
|
||||
navigate(item.path);
|
||||
}
|
||||
};
|
||||
|
||||
const renderFlyoutMenu = (items: MenuItem[], level = 0) => (
|
||||
<div className={`sidebar-flyout-list sidebar-flyout-list--level-${level}`}>
|
||||
{items.map((item) => {
|
||||
const hasChildren = !!item.children?.length;
|
||||
const isExpanded = expandedIds.has(item.id);
|
||||
const isSelected = item.path ? pathname === item.path : hasActiveChild(item);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
className={`sidebar-flyout-node sidebar-flyout-node--level-${level} ${hasChildren ? "sidebar-flyout-node--has-children" : ""} ${isExpanded ? "sidebar-flyout-node--expanded" : ""}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`sidebar-flyout-item ${isSelected ? "sidebar-flyout-item--active" : ""} ${hasChildren ? "sidebar-flyout-item--parent" : "sidebar-flyout-item--leaf"}`}
|
||||
onClick={() => handleMenuItemClick(item)}
|
||||
>
|
||||
{hasChildren ? <span className="sidebar-flyout-arrow" aria-hidden="true" /> : null}
|
||||
<span className="sidebar-flyout-label">{item.label}</span>
|
||||
</button>
|
||||
{hasChildren && isExpanded ? renderFlyoutMenu(item.children ?? [], level + 1) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<SettingsDialog
|
||||
@@ -332,11 +400,18 @@ export function PageSidebarDark({
|
||||
{/* TODO: 在这里编写深色主题的侧边栏 HTML 结构 */}
|
||||
<div className="sidebar-contnet">
|
||||
<div className="sidebar-contents">
|
||||
<div className="item-nav-content"></div>
|
||||
<div className="item-nav-content">
|
||||
{activeTopItem?.children?.length ? renderFlyoutMenu(activeTopItem.children) : null}
|
||||
</div>
|
||||
{visibleMenuItems.map((item) => (
|
||||
<div key={item.id} className={`item-nav ${item.id === 'qa-center' ? 'item-nav-active' : ''}`}>
|
||||
<button
|
||||
type="button"
|
||||
key={item.id}
|
||||
className={`item-nav ${item.id === activeTopId ? "item-nav-active" : ""}`}
|
||||
onClick={() => handleTopItemClick(item)}
|
||||
>
|
||||
<span className="item-nav__label">{item.label}</span>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user