gongxiangp2/components/admin/DepartmentManagement.tsx
chenpei 0ce59be34e 备份:组织架构设计方案讨论前的代码快照
- 包含所有当前修改和新功能
- 准备开始组织架构优化实施方案
2025-12-30 13:20:26 +08:00

161 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 部门管理组件
* 提取自 app/admin/page.tsx 的部门管理功能
*/
'use client'
import { useState } from 'react'
import type { Department } from '@/hooks/useAdminState'
interface DepartmentManagementProps {
departments: Department[]
setDepartments: React.Dispatch<React.SetStateAction<Department[]>>
showToast: (message: string, type?: 'success' | 'error') => void
fetchDepartments: () => Promise<void>
}
export default function DepartmentManagement({
departments,
setDepartments,
showToast,
fetchDepartments,
}: DepartmentManagementProps) {
const [newDepartmentName, setNewDepartmentName] = useState('')
const [addingDepartment, setAddingDepartment] = useState(false)
// 添加部门
const handleAddDepartment = async () => {
if (!newDepartmentName.trim()) {
showToast('请输入部门名称', 'error')
return
}
setAddingDepartment(true)
try {
const response = await fetch('/api/departments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newDepartmentName.trim() }),
})
if (response.ok) {
await fetchDepartments()
setNewDepartmentName('')
showToast('部门添加成功')
} else {
const error = await response.json()
showToast(error.error || '添加失败', 'error')
}
} catch (error) {
console.error('Add department error:', error)
showToast('添加失败,请稍后重试', 'error')
} finally {
setAddingDepartment(false)
}
}
// 删除部门
const handleDeleteDepartment = async (id: string, name: string) => {
if (!confirm(`确定要删除部门"${name}"吗?`)) return
try {
const response = await fetch(`/api/departments?id=${id}`, {
method: 'DELETE',
})
if (response.ok) {
setDepartments(prev => prev.filter(d => d.id !== id))
showToast('部门删除成功')
} else {
const error = await response.json()
showToast(error.error || '删除失败', 'error')
}
} catch (error) {
console.error('Delete department error:', error)
showToast('删除失败,请稍后重试', 'error')
}
}
return (
<div className="card-apple">
<div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">部门管理</h2>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">管理系统中的所有部门</p>
</div>
<div className="p-6">
{/* 添加部门 */}
<div className="flex gap-3 mb-6">
<input
type="text"
placeholder="输入新部门名称"
value={newDepartmentName}
onChange={(e) => setNewDepartmentName(e.target.value)}
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-gray-400 outline-none"
onKeyDown={(e) => {
if (e.key === 'Enter' && !addingDepartment) {
handleAddDepartment()
}
}}
/>
<button
onClick={handleAddDepartment}
disabled={addingDepartment}
className="px-6 py-2 bg-gray-900 dark:bg-gray-700 text-white rounded-lg hover:bg-gray-800 dark:hover:bg-gray-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{addingDepartment ? (
<>
<svg className="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
添加中...
</>
) : (
<>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
添加部门
</>
)}
</button>
</div>
{/* 部门列表 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{departments.map((dept) => (
<div
key={dept.id}
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-600 rounded-lg hover:shadow-md transition-shadow"
>
<div className="flex items-center gap-3">
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
<span className="font-medium text-gray-900 dark:text-white">{dept.name}</span>
</div>
<button
onClick={() => handleDeleteDepartment(dept.id, dept.name)}
className="p-2 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-lg transition-colors"
title="删除部门"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
))}
</div>
{departments.length === 0 && (
<div className="text-center py-12 text-gray-500 dark:text-gray-400">
暂无部门,请添加新部门
</div>
)}
</div>
</div>
)
}