Some checks failed
CI Check / check (push) Has been cancelled
- 创建统一UI三层架构系统(基础层、颜色层、组合层) - 拆分Admin页面为独立Tab组件(MonitorTab、UsersTab、DepartmentsTab等) - 提取公共逻辑到Hooks(useToast、useDataFetch、useUserOperations等) - 提取工具函数(validators、formatters、api等) - 创建统一弹窗组件(Modal)和useModal Hook - 添加错误边界和统一状态组件 - 完善类型定义和可访问性支持 - 更新文档(优化记录、优化计划、更新日志)
265 lines
8.0 KiB
TypeScript
265 lines
8.0 KiB
TypeScript
/**
|
|
* 用户操作 Hook
|
|
* 统一管理用户相关的操作(更新、删除、切换状态等)
|
|
*/
|
|
|
|
'use client'
|
|
|
|
import { useCallback } from 'react'
|
|
import type { User, Department, ApiSuccessResponse } from '../_components/types'
|
|
import { parseErrorResponse } from '../_utils/api'
|
|
import type { ToastFn } from '../_components/types'
|
|
|
|
interface UseUserOperationsOptions {
|
|
users: User[]
|
|
setUsers: React.Dispatch<React.SetStateAction<User[]>>
|
|
showToast: ToastFn
|
|
fetchData: () => Promise<void>
|
|
findDepartmentManagers: (departmentName: string | null | undefined) => User[] | null
|
|
}
|
|
|
|
export function useUserOperations({
|
|
users,
|
|
setUsers,
|
|
showToast,
|
|
fetchData,
|
|
findDepartmentManagers,
|
|
}: UseUserOperationsOptions) {
|
|
const updateUser = useCallback(
|
|
async (user: User, payload: { username: string; department: string }) => {
|
|
const { username, department } = payload
|
|
const usernameChanged = username !== user.username
|
|
const departmentChanged = department !== (user.department || '')
|
|
|
|
if (!usernameChanged && !departmentChanged) {
|
|
showToast('未做任何修改', 'error')
|
|
return false
|
|
}
|
|
|
|
try {
|
|
let materialsUpdated = false
|
|
let parentIdToSet: string | null = null
|
|
|
|
if (departmentChanged && department) {
|
|
const managers = findDepartmentManagers(department) || []
|
|
if (managers.length > 0) {
|
|
const firstManager = managers.find((manager) => manager.id !== user.id) || managers[0]
|
|
parentIdToSet = firstManager.id
|
|
}
|
|
}
|
|
|
|
if (usernameChanged || departmentChanged) {
|
|
const response = await fetch('/api/admin/users/update', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
userId: user.id,
|
|
username,
|
|
department,
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorData = await parseErrorResponse(response)
|
|
showToast(errorData.error || '更新失败', 'error')
|
|
return false
|
|
}
|
|
const successData = (await response.json()) as ApiSuccessResponse<{ materialsUpdated?: boolean }>
|
|
materialsUpdated = Boolean(successData.materialsUpdated)
|
|
|
|
if (departmentChanged && parentIdToSet) {
|
|
const hierarchyResponse = await fetch('/api/admin/users/update-hierarchy', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
userId: user.id,
|
|
parentId: parentIdToSet,
|
|
}),
|
|
})
|
|
|
|
if (!hierarchyResponse.ok) {
|
|
const errorData = await parseErrorResponse(hierarchyResponse)
|
|
showToast(errorData.error || '调整上下级失败', 'error')
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
await fetchData()
|
|
showToast(materialsUpdated ? '用户信息已更新,素材信息已同步' : '用户信息已更新')
|
|
return true
|
|
} catch (error) {
|
|
console.error('Update user error:', error)
|
|
showToast('更新失败,请稍后重试', 'error')
|
|
return false
|
|
}
|
|
},
|
|
[showToast, fetchData, findDepartmentManagers]
|
|
)
|
|
|
|
const deleteUser = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
const response = await fetch(`/api/admin/users?id=${id}`, {
|
|
method: 'DELETE',
|
|
})
|
|
|
|
if (response.ok) {
|
|
setUsers((prevUsers) => prevUsers.filter((u) => u.id !== id))
|
|
showToast('删除成功')
|
|
return true
|
|
}
|
|
const errorData = await parseErrorResponse(response)
|
|
showToast(errorData.error || '删除失败', 'error')
|
|
return false
|
|
} catch (error) {
|
|
console.error('Error deleting user:', error)
|
|
showToast('删除失败', 'error')
|
|
return false
|
|
}
|
|
},
|
|
[setUsers, showToast]
|
|
)
|
|
|
|
const toggleDownload = useCallback(
|
|
async (userId: string, currentCanDownload: boolean) => {
|
|
const action = currentCanDownload ? '关闭' : '开启'
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/users/toggle-download', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ userId }),
|
|
})
|
|
|
|
if (response.ok) {
|
|
// 直接更新本地状态,避免重新加载整个列表
|
|
setUsers((prevUsers) =>
|
|
prevUsers.map((user) =>
|
|
user.id === userId ? { ...user, canDownload: !currentCanDownload } : user
|
|
)
|
|
)
|
|
showToast(`下载权限已${action}`)
|
|
} else {
|
|
const errorData = await parseErrorResponse(response)
|
|
showToast(errorData.error || '操作失败', 'error')
|
|
}
|
|
} catch (error) {
|
|
console.error('Error toggling download permission:', error)
|
|
showToast('操作失败', 'error')
|
|
}
|
|
},
|
|
[setUsers, showToast]
|
|
)
|
|
|
|
const toggleActive = useCallback(
|
|
async (userId: string, currentStatus: boolean) => {
|
|
const action = currentStatus ? '禁用' : '激活'
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/users/toggle-active', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
userId,
|
|
isActive: !currentStatus,
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
// 直接更新本地状态
|
|
setUsers((prevUsers) =>
|
|
prevUsers.map((user) => (user.id === userId ? { ...user, isActive: !currentStatus } : user))
|
|
)
|
|
showToast(`${action}成功`)
|
|
} else {
|
|
const errorData = await parseErrorResponse(response)
|
|
showToast(errorData.error || `${action}失败`, 'error')
|
|
}
|
|
} catch (error) {
|
|
console.error('Toggle active error:', error)
|
|
showToast(`${action}失败`, 'error')
|
|
}
|
|
},
|
|
[setUsers, showToast]
|
|
)
|
|
|
|
const setRole = useCallback(
|
|
async (userId: string, currentRole: string) => {
|
|
const newRole = currentRole === 'ADMIN' ? 'USER' : 'ADMIN'
|
|
const action = newRole === 'ADMIN' ? '设置为管理员' : '取消管理员权限'
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/users/set-role', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
userId,
|
|
role: newRole,
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
// 刷新数据,用户树会同步更新最新的管理员信息
|
|
await fetchData()
|
|
showToast(`${action}成功`)
|
|
} else {
|
|
const errorData = await parseErrorResponse(response)
|
|
showToast(errorData.error || `${action}失败`, 'error')
|
|
}
|
|
} catch (error) {
|
|
console.error('Set role error:', error)
|
|
showToast(`${action}失败`, 'error')
|
|
}
|
|
},
|
|
[showToast, fetchData]
|
|
)
|
|
|
|
const transferSuperAdmin = useCallback(
|
|
async (targetEmail: string, password: string) => {
|
|
try {
|
|
const res = await fetch('/api/admin/transfer-superadmin', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ targetEmail, password }),
|
|
})
|
|
if (res.ok) {
|
|
showToast('最高权限已转移')
|
|
await fetchData()
|
|
return true
|
|
}
|
|
const errorData = await parseErrorResponse(res)
|
|
showToast(errorData.error || '操作失败', 'error')
|
|
return false
|
|
} catch (err) {
|
|
console.error('transfer super admin error:', err)
|
|
showToast('操作失败,请稍后重试', 'error')
|
|
return false
|
|
}
|
|
},
|
|
[showToast, fetchData]
|
|
)
|
|
|
|
return {
|
|
updateUser,
|
|
deleteUser,
|
|
toggleDownload,
|
|
toggleActive,
|
|
setRole,
|
|
transferSuperAdmin,
|
|
}
|
|
}
|
|
|
|
export type UseUserOperationsReturn = ReturnType<typeof useUserOperations>
|
|
|