Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app-hooks/useSafeNavigate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const safeShowDenied = () => {
})
}

export default function useSafeNavigate() {
export default function useSafeNavigate () {
const navigate = useNavigate()
const lastDeniedRef = useRef(null)

Expand Down
11 changes: 6 additions & 5 deletions src/components/KeepAlive/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ const createKeepAliveManager = () => {
// set global options: { deactivateDelay, keepInactiveCount, limit }
setOptions: (opts = {}) => {
if (typeof opts.deactivateDelay === 'number') deactivateDelay = opts.deactivateDelay
if (typeof opts.keepInactiveCount === 'number')
if (typeof opts.keepInactiveCount === 'number') {
keepInactiveCount = Math.max(0, Math.floor(opts.keepInactiveCount))
}
if (typeof opts.limit === 'number') limit = Math.max(0, Math.floor(opts.limit))
},
register: (id, opts) => {
Expand Down Expand Up @@ -193,7 +194,7 @@ const createKeepAliveManager = () => {
instances.delete(id)
activeMap.delete(id)
keys = keys.filter((k) => k !== id)
},
}
}
}

Expand Down Expand Up @@ -302,7 +303,7 @@ const KeepAlive = ({ id, active = false, children, persistOnUnmount = false, cac
if (!active) return
scrollPos.current.set(e.target, {
left: e.target.scrollLeft,
top: e.target.scrollTop,
top: e.target.scrollTop
})
}

Expand Down Expand Up @@ -350,7 +351,7 @@ const KeepAlive = ({ id, active = false, children, persistOnUnmount = false, cac
if (container.parentNode !== placeholder) {
// 在移动DOM之前,发送自定义事件通知子组件
const event = new CustomEvent('keepalive-dom-move', {
detail: { from: container.parentNode, to: placeholder },
detail: { from: container.parentNode, to: placeholder }
})
container.dispatchEvent(event)

Expand Down Expand Up @@ -420,7 +421,7 @@ KeepAlive.propTypes = {
active: PropTypes.bool,
children: PropTypes.node,
persistOnUnmount: PropTypes.bool,
cacheLimit: PropTypes.number,
cacheLimit: PropTypes.number
}

export default KeepAlive
174 changes: 89 additions & 85 deletions src/components/stateless/NotificationDrawer/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ const initialNotifications = [
title: '新功能上线',
description: '全站菜单查询功能全新上线,支持快速定位与跳转!',
read: false,
time: '5分钟前',
time: '5分钟前'
},
{
id: 3,
title: '新功能上线',
description: 'ResponsiveTable高复用表格上线,欢迎试用!',
read: false,
time: '10分钟前',
time: '10分钟前'
},
{ id: 4, title: '新功能上线', description: '权限系统与主题切换已支持。', read: false, time: '15分钟前' },
{ id: 5, title: '文档更新', description: 'README.md 已优化,欢迎查阅。', read: true, time: '1天前' },
{ id: 5, title: '文档更新', description: 'README.md 已优化,欢迎查阅。', read: true, time: '1天前' }
]

const NotificationDropdown = ({ iconColor, variant = 'inline', buttonStyle, ghost = false }) => {
Expand Down Expand Up @@ -58,7 +58,7 @@ const NotificationDropdown = ({ iconColor, variant = 'inline', buttonStyle, ghos
}

const {
token: { colorBgContainer, colorBorder, colorText, colorTextSecondary, colorTextTertiary },
token: { colorBgContainer, colorBorder, colorText, colorTextSecondary, colorTextTertiary }
} = theme.useToken()

// content structure: header (optional) / list (scrollable) / footer (fixed)
Expand All @@ -69,63 +69,65 @@ const NotificationDropdown = ({ iconColor, variant = 'inline', buttonStyle, ghos
backgroundColor: colorBgContainer,
color: colorText,
display: 'flex',
flexDirection: 'column',
flexDirection: 'column'
}}
>
<div style={{ flex: 1, overflowY: 'auto', padding: 12 }}>
{notifications.length === 0 ? (
<div style={{ color: colorTextTertiary, textAlign: 'center', padding: '32px 0' }}>暂无通知</div>
) : (
notifications.slice(0, MAX_ITEMS).map((item) => (
<button
key={item.id}
type="button"
onClick={() => handleNotificationOpen(item.id)}
style={{
opacity: item.read ? 0.6 : 1,
padding: '8px 0',
borderBottom: `1px solid ${colorBorder}`,
display: 'flex',
flexDirection: 'column',
gap: 4,
cursor: 'pointer',
width: '100%',
textAlign: 'left',
background: 'transparent',
borderTop: 'none',
borderLeft: 'none',
borderRight: 'none',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{!item.read ? <Badge status="processing" /> : <Badge status="default" />}
<Typography.Text strong>{item.title}</Typography.Text>
</div>
<div style={{ fontSize: 13, color: colorTextSecondary, marginTop: 6 }}>{item.description}</div>
<div style={{ fontSize: 12, color: colorTextTertiary, marginTop: 4, textAlign: 'right' }}>
{item.time}
</div>
</button>
))
)}
{notifications.length === 0
? (
<div style={{ color: colorTextTertiary, textAlign: 'center', padding: '32px 0' }}>暂无通知</div>
)
: (
notifications.slice(0, MAX_ITEMS).map((item) => (
<button
key={item.id}
type='button'
onClick={() => handleNotificationOpen(item.id)}
style={{
opacity: item.read ? 0.6 : 1,
padding: '8px 0',
borderBottom: `1px solid ${colorBorder}`,
display: 'flex',
flexDirection: 'column',
gap: 4,
cursor: 'pointer',
width: '100%',
textAlign: 'left',
background: 'transparent',
borderTop: 'none',
borderLeft: 'none',
borderRight: 'none'
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{!item.read ? <Badge status='processing' /> : <Badge status='default' />}
<Typography.Text strong>{item.title}</Typography.Text>
</div>
<div style={{ fontSize: 13, color: colorTextSecondary, marginTop: 6 }}>{item.description}</div>
<div style={{ fontSize: 12, color: colorTextTertiary, marginTop: 4, textAlign: 'right' }}>
{item.time}
</div>
</button>
))
)}
</div>

<div
style={{
borderTop: `1px solid ${colorBorder}`,
padding: 8,
backgroundColor: colorBgContainer,
textAlign: 'right',
textAlign: 'right'
}}
>
<Space>
<Button size="small" onClick={markAllRead} icon={<CheckCircleOutlined />}>
<Button size='small' onClick={markAllRead} icon={<CheckCircleOutlined />}>
全部已读
</Button>
<Button size="small" onClick={clearAll} icon={<DeleteOutlined />} danger>
<Button size='small' onClick={clearAll} icon={<DeleteOutlined />} danger>
清空
</Button>
<Button size="small" type="link" onClick={() => safeNavigate('/notifications')}>
<Button size='small' type='link' onClick={() => safeNavigate('/notifications')}>
查看全部
</Button>
</Space>
Expand All @@ -136,56 +138,58 @@ const NotificationDropdown = ({ iconColor, variant = 'inline', buttonStyle, ghos
const openDrawer = () => setPopVisible(true)

const iconNode = (
<Badge count={unreadCount} size="small" overflowCount={49}>
<Badge count={unreadCount} size='small' overflowCount={49}>
<BellOutlined style={{ fontSize: 16, color: iconColor }} />
</Badge>
)

const triggerNode =
variant === 'button' ? (
<Button
type="default"
size="small"
ghost={ghost}
icon={iconNode}
onClick={openDrawer}
aria-haspopup="dialog"
aria-expanded={popVisible}
style={{
fontSize: 16,
...(buttonStyle || {}),
...(iconColor ? { color: iconColor } : {}),
}}
>
{isMobile ? '通知' : null}
</Button>
) : (
<button
type="button"
onClick={openDrawer}
aria-haspopup="dialog"
aria-expanded={popVisible}
style={{
cursor: 'pointer',
color: iconColor,
background: 'transparent',
border: 'none',
padding: 0,
display: 'inline-flex',
alignItems: 'center',
}}
>
{iconNode}
{isMobile && <span style={{ marginLeft: 4 }}>通知</span>}
</button>
)
variant === 'button'
? (
<Button
type='default'
size='small'
ghost={ghost}
icon={iconNode}
onClick={openDrawer}
aria-haspopup='dialog'
aria-expanded={popVisible}
style={{
fontSize: 16,
...(buttonStyle || {}),
...(iconColor ? { color: iconColor } : {})
}}
>
{isMobile ? '通知' : null}
</Button>
)
: (
<button
type='button'
onClick={openDrawer}
aria-haspopup='dialog'
aria-expanded={popVisible}
style={{
cursor: 'pointer',
color: iconColor,
background: 'transparent',
border: 'none',
padding: 0,
display: 'inline-flex',
alignItems: 'center'
}}
>
{iconNode}
{isMobile && <span style={{ marginLeft: 4 }}>通知</span>}
</button>
)

return (
<>
{triggerNode}
<Drawer
title="通知"
placement="right"
title='通知'
placement='right'
onClose={() => setPopVisible(false)}
open={popVisible}
size={360}
Expand All @@ -201,7 +205,7 @@ NotificationDropdown.propTypes = {
iconColor: PropTypes.string,
variant: PropTypes.oneOf(['inline', 'button']),
buttonStyle: PropTypes.object,
ghost: PropTypes.bool,
ghost: PropTypes.bool
}

export default NotificationDropdown
2 changes: 1 addition & 1 deletion src/components/stateless/PointerMove/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const PointerMove = () => {
className={styles.star}
animate={{ x: coordinates?.x ?? 0, y: coordinates?.y ?? 0, opacity: 1 }}
transition={{
type: 'spring',
type: 'spring'
}}
/>
)
Expand Down
Loading
Loading