实现完美的浮动通知系统
- 添加自定义通知组件替代阻塞式alert - 浮动定位不影响页面布局,宽度自适应内容 - 支持成功/错误两种样式,渐变背景+阴影效果 - 实现完整的淡入淡出动画,原地显示隐藏 - 重启提示显示4秒,普通操作反馈2-3秒 - 智能定时器管理,支持动画完成后清理 用户体验:切换供应商后优雅提示"请重启Claude Code终端以生效"
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { Provider } from '../shared/types'
|
||||
import ProviderList from './components/ProviderList'
|
||||
import AddProviderModal from './components/AddProviderModal'
|
||||
@@ -11,6 +11,31 @@ function App() {
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
|
||||
const [configPath, setConfigPath] = useState<string>('')
|
||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(null)
|
||||
const [notification, setNotification] = useState<{ message: string; type: 'success' | 'error' } | null>(null)
|
||||
const [isNotificationVisible, setIsNotificationVisible] = useState(false)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
// 设置通知的辅助函数
|
||||
const showNotification = (message: string, type: 'success' | 'error', duration = 3000) => {
|
||||
// 清除之前的定时器
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
|
||||
// 立即显示通知
|
||||
setNotification({ message, type })
|
||||
setIsNotificationVisible(true)
|
||||
|
||||
// 设置淡出定时器
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
setIsNotificationVisible(false)
|
||||
// 等待淡出动画完成后清除通知
|
||||
setTimeout(() => {
|
||||
setNotification(null)
|
||||
timeoutRef.current = null
|
||||
}, 300) // 与CSS动画时间匹配
|
||||
}, duration)
|
||||
}
|
||||
|
||||
// 加载供应商列表
|
||||
useEffect(() => {
|
||||
@@ -18,6 +43,15 @@ function App() {
|
||||
loadConfigPath()
|
||||
}, [])
|
||||
|
||||
// 清理定时器
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
const loadProviders = async () => {
|
||||
const loadedProviders = await window.electronAPI.getProviders()
|
||||
@@ -58,10 +92,10 @@ function App() {
|
||||
const success = await window.electronAPI.switchProvider(id)
|
||||
if (success) {
|
||||
setCurrentProviderId(id)
|
||||
// 移除阻塞式alert
|
||||
console.log('供应商切换成功')
|
||||
// 显示重启提示,时间更长
|
||||
showNotification('切换成功!请重启 Claude Code 终端以生效', 'success', 4000)
|
||||
} else {
|
||||
console.error('切换失败,请检查配置')
|
||||
showNotification('切换失败,请检查配置', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,17 +104,12 @@ function App() {
|
||||
await window.electronAPI.updateProvider(provider)
|
||||
await loadProviders()
|
||||
setEditingProviderId(null)
|
||||
// 移除阻塞式alert,避免焦点管理问题
|
||||
setTimeout(() => {
|
||||
console.log('供应商更新成功')
|
||||
}, 100)
|
||||
// 显示编辑成功提示,时间较短
|
||||
showNotification('供应商配置已保存', 'success', 2000)
|
||||
} catch (error) {
|
||||
console.error('更新供应商失败:', error)
|
||||
setEditingProviderId(null)
|
||||
// 错误情况下也避免alert
|
||||
setTimeout(() => {
|
||||
console.error('保存失败,请重试')
|
||||
}, 100)
|
||||
showNotification('保存失败,请重试', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +135,22 @@ function App() {
|
||||
</header>
|
||||
|
||||
<main className="app-main">
|
||||
<ProviderList
|
||||
<div className="provider-section">
|
||||
{/* 浮动通知组件 */}
|
||||
{notification && (
|
||||
<div className={`notification-floating ${notification.type === 'error' ? 'notification-error' : 'notification-success'} ${isNotificationVisible ? 'fade-in' : 'fade-out'}`}>
|
||||
{notification.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProviderList
|
||||
providers={providers}
|
||||
currentProviderId={currentProviderId}
|
||||
onSwitch={handleSwitchProvider}
|
||||
onDelete={handleDeleteProvider}
|
||||
onEdit={setEditingProviderId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{configPath && (
|
||||
<div className="config-path">
|
||||
|
||||
Reference in New Issue
Block a user