109 lines
3.9 KiB
TypeScript
109 lines
3.9 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useState } from 'react'
|
||
|
||
interface MaintenanceInfo {
|
||
enabled: boolean
|
||
message: string | null
|
||
startTime: string | null
|
||
endTime: string | null
|
||
}
|
||
|
||
export default function MaintenanceBanner() {
|
||
const [maintenance, setMaintenance] = useState<MaintenanceInfo | null>(null)
|
||
const [dismissed, setDismissed] = useState(false)
|
||
|
||
useEffect(() => {
|
||
const fetchMaintenance = async () => {
|
||
try {
|
||
const response = await fetch('/api/system-settings')
|
||
if (response.ok) {
|
||
const data = await response.json()
|
||
if (data.maintenanceEnabled && data.maintenanceMessage) {
|
||
// 检查维护时间范围
|
||
const now = new Date()
|
||
const startTime = data.maintenanceStartTime ? new Date(data.maintenanceStartTime) : null
|
||
const endTime = data.maintenanceEndTime ? new Date(data.maintenanceEndTime) : null
|
||
|
||
// 如果设置了时间范围,检查是否在范围内
|
||
if (startTime && endTime) {
|
||
if (now < startTime || now > endTime) {
|
||
return // 不在维护时间范围内,不显示
|
||
}
|
||
} else if (startTime && now < startTime) {
|
||
return // 维护还未开始
|
||
} else if (endTime && now > endTime) {
|
||
return // 维护已结束
|
||
}
|
||
|
||
setMaintenance({
|
||
enabled: true,
|
||
message: data.maintenanceMessage,
|
||
startTime: data.maintenanceStartTime,
|
||
endTime: data.maintenanceEndTime,
|
||
})
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取维护通知失败:', error)
|
||
}
|
||
}
|
||
|
||
fetchMaintenance()
|
||
// 每5分钟检查一次
|
||
const interval = setInterval(fetchMaintenance, 5 * 60 * 1000)
|
||
return () => clearInterval(interval)
|
||
}, [])
|
||
|
||
// 检查是否已关闭
|
||
useEffect(() => {
|
||
if (maintenance?.enabled) {
|
||
const dismissedKey = `maintenance_dismissed_${maintenance.startTime || 'default'}`
|
||
const isDismissed = sessionStorage.getItem(dismissedKey) === 'true'
|
||
setDismissed(isDismissed)
|
||
}
|
||
}, [maintenance])
|
||
|
||
if (!maintenance?.enabled || !maintenance.message || dismissed) {
|
||
return null
|
||
}
|
||
|
||
const handleDismiss = () => {
|
||
const dismissedKey = `maintenance_dismissed_${maintenance.startTime || 'default'}`
|
||
sessionStorage.setItem(dismissedKey, 'true')
|
||
setDismissed(true)
|
||
}
|
||
|
||
return (
|
||
<div className="fixed top-0 left-0 right-0 z-50 bg-gradient-to-r from-orange-500 to-red-500 text-white shadow-lg">
|
||
<div className="container mx-auto px-4 py-3 max-w-7xl">
|
||
<div className="flex items-center justify-between gap-4">
|
||
<div className="flex items-center gap-3 flex-1">
|
||
<svg className="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||
</svg>
|
||
<div className="flex-1">
|
||
<p className="font-medium text-sm sm:text-base">
|
||
⚠️ 系统维护通知
|
||
</p>
|
||
<p className="text-xs sm:text-sm opacity-90 mt-0.5">
|
||
{maintenance.message}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={handleDismiss}
|
||
className="px-3 py-2 hover:bg-orange-600 rounded-lg transition-colors flex-shrink-0"
|
||
title="关闭通知"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|