生产管理系统 - 员工管理、企业信息联调完毕以及一些页面上的样式修改

This commit is contained in:
2025-11-05 17:18:25 +08:00
parent c10b507cf6
commit 1fb128ede5
16 changed files with 1266 additions and 207 deletions

View File

@@ -9,10 +9,28 @@ import { EmployeeManagementFilters } from './components/EmployeeManagementFilter
import { EmployeeList } from './components/EmployeeList';
import { EmployeeFormDialog } from './components/EmployeeFormDialog';
import { EmployeeDetailDialog } from './components/EmployeeDetailDialog';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Employee, Role, EmployeeFilters, EmployeeFormData } from './types';
import {
fetchEmployees,
transformEmployeesList,
createEmployee,
updateEmployee,
activateUser,
deactivateUser,
deleteUser,
CreateEmployeeRequest,
UpdateEmployeeRequest,
PaginationState,
EmployeesQueryParams
} from './components/employeeApi';
@@ -21,6 +39,15 @@ export default function EmployeeManagementPage() {
const [employees, setEmployees] = useState<Employee[]>([]);
const [roles, setRoles] = useState<Role[]>([]);
const [loading, setLoading] = useState(false);
const [creating, setCreating] = useState(false);
const [updating, setUpdating] = useState(false);
const [toggling, setToggling] = useState<string | null>(null); // 记录正在操作的用户ID
// 确认对话框状态
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
const [userToDelete, setUserToDelete] = useState<Employee | null>(null);
const [deactivateConfirmOpen, setDeactivateConfirmOpen] = useState(false);
const [userToDeactivate, setUserToDeactivate] = useState<Employee | null>(null);
const [pagination, setPagination] = useState<PaginationState>({
page: 1,
size: 10,
@@ -35,6 +62,7 @@ export default function EmployeeManagementPage() {
});
const [showForm, setShowForm] = useState(false);
const [showDetailDialog, setShowDetailDialog] = useState(false);
const [formKey, setFormKey] = useState(0); // 添加key来强制重新渲染表单
const [editingEmployee, setEditingEmployee] = useState<Employee | null>(null);
const [selectedEmployee, setSelectedEmployee] = useState<Employee | null>(null);
const [formData, setFormData] = useState<EmployeeFormData>({
@@ -135,16 +163,36 @@ export default function EmployeeManagementPage() {
const handleAddEmployee = () => {
setEditingEmployee(null);
setFormData({
clearForm();
setFormKey(prev => prev + 1); // 增加key强制重新渲染
setShowForm(true);
};
const clearForm = () => {
// 先设置一个空的表单对象
const emptyForm = {
enterpriseId: 'ent-2',
enterpriseName: '丰收现代农业集团',
status: 'active',
auditStatus: 'pending',
status: 'active' as const,
auditStatus: 'pending' as const,
roleIds: [],
idCard: '',
address: '',
});
setShowForm(true);
username: '',
name: '',
phone: '',
email: '',
department: '',
position: '',
};
// 强制清空表单
setFormData(emptyForm);
// 使用setTimeout确保状态更新完成
setTimeout(() => {
setFormData({...emptyForm});
}, 0);
};
const handleEdit = (employee: Employee) => {
@@ -153,7 +201,7 @@ export default function EmployeeManagementPage() {
setShowForm(true);
};
const handleSave = () => {
const handleSave = async () => {
if (!formData.username || !formData.name || !formData.phone) {
toast.error('请填写必填项');
return;
@@ -170,58 +218,195 @@ export default function EmployeeManagementPage() {
const roleNames = selectedRoles.map(r => r.name);
if (editingEmployee) {
// 更新
const updated = employees.map(emp =>
emp.id === editingEmployee.id
? {
...emp,
...formData,
roles: roleNames,
updatedAt: new Date().toISOString(),
}
: emp
);
setEmployees(updated);
localStorage.setItem('smart_agriculture_employees', JSON.stringify(updated));
toast.success('员工信息更新成功');
} else {
// 新增
const newEmployee: Employee = {
id: `emp-${Date.now()}`,
...formData as Employee,
roles: roleNames,
auditStatus: 'pending',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
const updated = [...employees, newEmployee];
setEmployees(updated);
localStorage.setItem('smart_agriculture_employees', JSON.stringify(updated));
toast.success('员工添加成功');
}
// 更新 - 调用API
setUpdating(true);
try {
// 构建API请求参数
const updateRequest: UpdateEmployeeRequest = {
email: formData.email || '',
username: formData.username,
full_name: formData.name,
phone: formData.phone,
password: '', // 编辑时不传密码
tenant_id: formData.enterpriseId,
scope: 'tenant',
department_id: formData.departmentId || '',
is_superuser: formData.isSuperuser || false,
};
setShowForm(false);
// 调用API更新用户
const updatedEmployee = await updateEmployee(editingEmployee.id, updateRequest);
// 更新本地列表中的员工数据
const updated = employees.map(emp =>
emp.id === editingEmployee.id
? {
...emp,
...updatedEmployee,
roles: roleNames,
updatedAt: new Date().toISOString(),
}
: emp
);
setEmployees(updated);
toast.success('员工信息更新成功');
setShowForm(false);
clearForm();
// 刷新员工列表数据
await loadEmployees();
} catch (error) {
console.error('更新员工失败:', error);
// 处理错误,显示具体的错误消息
const errorMessage = error instanceof Error ? error.message : '员工信息更新失败';
toast.error(errorMessage);
} finally {
setUpdating(false);
}
} else {
// 新增 - 调用API
setCreating(true);
try {
// 构建API请求参数
const createRequest: CreateEmployeeRequest = {
email: formData.email || '', // 没有邮箱就传空字符串
username: formData.username,
full_name: formData.name,
phone: formData.phone,
password: '', // 传递空字符串给后端
tenant_id: formData.enterpriseId,
scope: 'tenant',
department_id: formData.departmentId || '',
is_superuser: formData.isSuperuser || false,
};
// 调用API创建用户
const newEmployee = await createEmployee(createRequest);
// 将新员工添加到列表
const updated = [newEmployee, ...employees];
setEmployees(updated);
toast.success('员工添加成功');
setShowForm(false);
clearForm();
// 刷新员工列表数据
await loadEmployees();
} catch (error) {
console.error('创建员工失败:', error);
// 处理错误,显示具体的错误消息
const errorMessage = error instanceof Error ? error.message : '员工添加失败';
toast.error(errorMessage);
} finally {
setCreating(false);
}
}
};
const handleDelete = (id: string) => {
if (!confirm('确定要删除该员工吗?')) return;
const handleDelete = (userId: string) => {
// 防止重复操作
if (toggling) {
toast.warning('操作进行中,请稍候...');
return;
}
const updated = employees.filter(emp => emp.id !== id);
setEmployees(updated);
localStorage.setItem('smart_agriculture_employees', JSON.stringify(updated));
toast.success('员工删除成功');
// 查找要删除的员工信息
const employeeToDelete = employees.find(emp => emp.id === userId);
if (!employeeToDelete) {
toast.error('未找到要删除的用户');
return;
}
// 设置要删除的用户并显示确认对话框
setUserToDelete(employeeToDelete);
setDeleteConfirmOpen(true);
};
const executeDelete = async (userId: string) => {
setToggling(userId);
try {
// 调用API删除用户
await deleteUser(userId);
// 成功后从本地列表中移除
const updated = employees.filter(emp => emp.id !== userId);
setEmployees(updated);
toast.success('用户删除成功');
// 刷新列表确保数据同步
await loadEmployees();
// 关闭确认对话框
setDeleteConfirmOpen(false);
setUserToDelete(null);
} catch (error) {
console.error('删除用户失败:', error);
// 处理错误,显示具体的错误消息
const errorMessage = error instanceof Error ? error.message : '删除失败,请稍后重试';
toast.error(errorMessage);
// 失败时不关闭确认对话框,用户可以重试
} finally {
setToggling(null);
}
};
const handleToggleStatus = (employee: Employee) => {
const newStatus = employee.status === 'active' ? 'frozen' : 'active';
const updated = employees.map(emp =>
emp.id === employee.id
? { ...emp, status: newStatus, updatedAt: new Date().toISOString() }
: emp
);
setEmployees(updated);
localStorage.setItem('smart_agriculture_employees', JSON.stringify(updated));
toast.success(newStatus === 'active' ? '账户已激活' : '账户已冻结');
if (toggling) {
toast.warning('操作进行中,请稍候...');
return;
}
if (employee.isActive) {
// 当前是激活状态,进行停用操作,需要二次确认
setUserToDeactivate(employee);
setDeactivateConfirmOpen(true);
} else {
// 当前是停用状态,进行激活操作(不需要确认)
executeToggleStatus(employee);
}
};
const executeToggleStatus = async (employee: Employee) => {
setToggling(employee.id);
try {
if (employee.isActive) {
// 当前是激活状态,进行停用操作
await deactivateUser(employee.id);
toast.success('账户已停用');
} else {
// 当前是停用状态,进行激活操作
await activateUser(employee.id);
toast.success('账户已激活');
}
// 成功后刷新列表
await loadEmployees();
// 关闭停用确认对话框
if (deactivateConfirmOpen) {
setDeactivateConfirmOpen(false);
setUserToDeactivate(null);
}
} catch (error) {
console.error('切换用户状态失败:', error);
// 处理错误,显示具体的错误消息
const errorMessage = error instanceof Error ? error.message : '操作失败,请稍后重试';
toast.error(errorMessage);
// 失败时不关闭确认对话框,用户可以重试
} finally {
setToggling(null);
}
};
const handleResetPassword = (employee: Employee) => {
@@ -301,10 +486,12 @@ export default function EmployeeManagementPage() {
onToggleStatus={handleToggleStatus}
onDelete={handleDelete}
onAudit={handleAudit}
togglingId={toggling}
/>
{/* 添加/编辑表单 */}
<EmployeeFormDialog
key={formKey} // 使用key强制重新渲染清除浏览器缓存
open={showForm}
onOpenChange={setShowForm}
editingEmployee={editingEmployee}
@@ -312,6 +499,9 @@ export default function EmployeeManagementPage() {
onFormDataChange={setFormData}
onSave={handleSave}
roles={roles}
creating={creating}
updating={updating}
onClearForm={clearForm}
/>
{/* 详情对话框 */}
@@ -320,6 +510,74 @@ export default function EmployeeManagementPage() {
onOpenChange={setShowDetailDialog}
selectedEmployee={selectedEmployee}
/>
{/* 删除确认对话框 */}
<AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
"
{userToDelete?.displayName || userToDelete?.fullName || userToDelete?.username || ''}
"
<br /><br />
<span className="text-red-600 font-semibold">
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={toggling !== null}>
</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
if (userToDelete) {
executeDelete(userToDelete.id);
}
}}
disabled={toggling !== null}
className="bg-red-600 hover:bg-red-700"
>
{toggling ? '删除中...' : '确认删除'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{/* 停用确认对话框 */}
<AlertDialog open={deactivateConfirmOpen} onOpenChange={setDeactivateConfirmOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
"
{userToDeactivate?.displayName || userToDeactivate?.fullName || userToDeactivate?.username || ''}
"
<br /><br />
<span className="text-orange-600 font-semibold">
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={toggling !== null}>
</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
if (userToDeactivate) {
executeToggleStatus(userToDeactivate);
}
}}
disabled={toggling !== null}
className="bg-orange-600 hover:bg-orange-700"
>
{toggling ? '停用中...' : '确认停用'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}