支持坐席切换在线状态,转接列表展示状态

侧栏可切换在线/忙碌/离线;转接弹窗显示状态点与标签,列表含在线与忙碌坐席。
This commit is contained in:
yml2213
2026-07-19 00:29:33 +08:00
parent cbc76f3258
commit ada7533ffa
7 changed files with 336 additions and 41 deletions
+78 -17
View File
@@ -1,53 +1,114 @@
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'
import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react'
import { setToken } from '@/services/request'
import { login as loginApi, type LoginResult } from '@/services/api'
import {
getMe,
login as loginApi,
updateMyStatus,
type AgentPresence,
type LoginResult,
} from '@/services/api'
interface AuthState {
user: LoginResult | null
loading: boolean
login: (username: string, password: string) => Promise<LoginResult>
user: LoginResult | null
loading: boolean
login: (username: string, password: string) => Promise<LoginResult>
logout: () => void
/** 切换本人在线 / 忙碌 / 离线 */
setPresence: (status: AgentPresence) => Promise<void>
}
const AuthContext = createContext<AuthState>({
user: null, loading: false,
login: async () => { throw new Error('认证上下文未初始化') }, logout: () => {},
user: null,
loading: false,
login: async () => { throw new Error('认证上下文未初始化') },
logout: () => {},
setPresence: async () => {},
})
function persistUser(u: LoginResult | null) {
if (!u) {
localStorage.removeItem('auth_user')
return
}
localStorage.setItem('auth_user', JSON.stringify(u))
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<LoginResult | null>(() => {
const saved = localStorage.getItem('auth_user')
if (saved) {
const u = JSON.parse(saved) as LoginResult
setToken(u.token)
return u
try {
const u = JSON.parse(saved) as LoginResult
setToken(u.token)
return u
} catch {
return null
}
}
return null
})
const [loading, setLoading] = useState(false)
// 刷新后同步服务端真实状态(含 status)
useEffect(() => {
if (!user?.token) return
let cancelled = false
getMe()
.then(res => {
if (cancelled || !res.data) return
setUser(prev => {
if (!prev) return prev
const next = {
...prev,
nickname: res.data.nickname || prev.nickname,
role: res.data.role || prev.role,
status: res.data.status || prev.status || 'offline',
}
persistUser(next)
return next
})
})
.catch(() => { /* 忽略:token 失效会在后续请求里处理 */ })
return () => { cancelled = true }
// 仅挂载时拉一次
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user?.token])
const login = useCallback(async (username: string, password: string) => {
setLoading(true)
try {
const res = await loginApi({ username, password })
const u = res.data
setToken(u.token)
setUser(u)
localStorage.setItem('auth_user', JSON.stringify(u))
return u
const u: LoginResult = { ...res.data, status: res.data.status || 'online' }
setToken(u.token)
setUser(u)
persistUser(u)
return u
} finally {
setLoading(false)
}
}, [])
const setPresence = useCallback(async (status: AgentPresence) => {
const res = await updateMyStatus(status)
const nextStatus = (res.data?.status || status) as string
setUser(prev => {
if (!prev) return prev
const next = { ...prev, status: nextStatus }
persistUser(next)
return next
})
}, [])
const logout = useCallback(() => {
// 尽力把状态置为离线,不阻塞退出
void updateMyStatus('offline').catch(() => {})
setToken('')
setUser(null)
localStorage.removeItem('auth_user')
persistUser(null)
}, [])
return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
<AuthContext.Provider value={{ user, loading, login, logout, setPresence }}>
{children}
</AuthContext.Provider>
)