生产管理系统 - 员工管理、企业信息联调完毕以及一些页面上的样式修改
This commit is contained in:
@@ -7,9 +7,8 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Building2, Plus, RefreshCw } from 'lucide-react';
|
||||
import { Plus } from 'lucide-react';
|
||||
|
||||
interface DepartmentHeaderProps {
|
||||
onAdd: () => void;
|
||||
@@ -19,35 +18,15 @@ interface DepartmentHeaderProps {
|
||||
|
||||
export function DepartmentHeader({ onAdd }: DepartmentHeaderProps) {
|
||||
return (
|
||||
<Card className="p-6 bg-gradient-to-r from-green-50 dark:from-green-950 to-emerald-50 dark:to-emerald-950 border-green-200 dark:border-green-800">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start gap-3">
|
||||
<Building2 className="w-6 h-6 text-green-600 dark:text-green-400 flex-shrink-0 mt-1" />
|
||||
<div className="flex-1">
|
||||
<h2 className="mb-2">部门管理</h2>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
树形结构管理企业部门信息,支持拖动排序
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="inline-flex items-center text-sm bg-white dark:bg-gray-800 px-3 py-1 rounded-full">
|
||||
树形结构
|
||||
</span>
|
||||
<span className="inline-flex items-center text-sm bg-white dark:bg-gray-800 px-3 py-1 rounded-full">
|
||||
拖动排序
|
||||
</span>
|
||||
<span className="inline-flex items-center text-sm bg-white dark:bg-gray-800 px-3 py-1 rounded-full">
|
||||
层级管理
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button onClick={onAdd} className="bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
添加一级部门
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-green-800 dark:text-green-400">部门管理</h2>
|
||||
<p className="text-muted-foreground">树形结构管理企业部门信息,支持拖动排序</p>
|
||||
</div>
|
||||
</Card>
|
||||
<Button onClick={onAdd} className="bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
添加一级部门
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,15 +6,11 @@
|
||||
*/
|
||||
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Building2, GripVertical, AlertCircle, Users } from 'lucide-react';
|
||||
|
||||
export function DepartmentInstructions() {
|
||||
return (
|
||||
<Card className="p-4 bg-blue-50 dark:bg-blue-950/30 border-blue-200 dark:border-blue-900">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Building2 className="w-5 h-5 text-blue-900 dark:text-blue-400" />
|
||||
<h4 className="text-blue-900 dark:text-blue-400 font-medium">部门管理说明</h4>
|
||||
</div>
|
||||
<h4 className="text-blue-900 dark:text-blue-400 font-medium mb-3">部门管理说明</h4>
|
||||
|
||||
<ul className="space-y-2 text-sm text-blue-800 dark:text-blue-300">
|
||||
<li className="flex items-start gap-2">
|
||||
@@ -26,7 +22,7 @@ export function DepartmentInstructions() {
|
||||
</li>
|
||||
|
||||
<li className="flex items-start gap-2">
|
||||
<GripVertical className="w-4 h-4 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-blue-600 dark:text-blue-400 mt-0.5">•</span>
|
||||
<div>
|
||||
<strong className="text-blue-900 dark:text-blue-400">拖动排序:</strong>
|
||||
按住部门左侧的 ⋮⋮ 图标拖动,可调整同级部门的顺序
|
||||
@@ -42,7 +38,7 @@ export function DepartmentInstructions() {
|
||||
</li>
|
||||
|
||||
<li className="flex items-start gap-2">
|
||||
<Users className="w-4 h-4 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-blue-600 dark:text-blue-400 mt-0.5">•</span>
|
||||
<div>
|
||||
<strong className="text-blue-900 dark:text-blue-400">员工关联:</strong>
|
||||
在员工管理中新增员工时,可选择此处维护的部门
|
||||
@@ -50,7 +46,7 @@ export function DepartmentInstructions() {
|
||||
</li>
|
||||
|
||||
<li className="flex items-start gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-blue-600 dark:text-blue-400 mt-0.5">•</span>
|
||||
<div>
|
||||
<strong className="text-blue-900 dark:text-blue-400">删除限制:</strong>
|
||||
删除部门前请先删除其所有子部门
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -8,6 +9,7 @@ import { Label } from '@/components/ui/label';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Employee, Role, EmployeeFormData } from '../types';
|
||||
import { fetchEmployeeDetail } from './employeeApi';
|
||||
|
||||
interface EmployeeFormDialogProps {
|
||||
open: boolean;
|
||||
@@ -17,6 +19,9 @@ interface EmployeeFormDialogProps {
|
||||
onFormDataChange: (data: EmployeeFormData) => void;
|
||||
onSave: () => void;
|
||||
roles: Role[];
|
||||
creating?: boolean;
|
||||
updating?: boolean;
|
||||
onClearForm?: () => void;
|
||||
}
|
||||
|
||||
export function EmployeeFormDialog({
|
||||
@@ -26,13 +31,73 @@ export function EmployeeFormDialog({
|
||||
formData,
|
||||
onFormDataChange,
|
||||
onSave,
|
||||
roles
|
||||
roles,
|
||||
creating = false,
|
||||
updating = false,
|
||||
onClearForm
|
||||
}: EmployeeFormDialogProps) {
|
||||
const [loadingDetail, setLoadingDetail] = useState(false);
|
||||
|
||||
// 当编辑员工时,根据ID获取用户详情
|
||||
useEffect(() => {
|
||||
if (open && editingEmployee && editingEmployee.id) {
|
||||
loadEmployeeDetail(editingEmployee.id);
|
||||
}
|
||||
}, [open, editingEmployee]);
|
||||
|
||||
const loadEmployeeDetail = async (userId: string) => {
|
||||
setLoadingDetail(true);
|
||||
try {
|
||||
const employeeDetail = await fetchEmployeeDetail(userId);
|
||||
|
||||
// 将API数据转换为表单数据格式
|
||||
const formDetailData: EmployeeFormData = {
|
||||
username: employeeDetail.username,
|
||||
name: employeeDetail.displayName || employeeDetail.fullName || employeeDetail.username,
|
||||
phone: employeeDetail.phone || '',
|
||||
email: employeeDetail.email || '',
|
||||
department: employeeDetail.departmentName || '',
|
||||
position: '', // API返回中没有position字段
|
||||
enterpriseId: employeeDetail.tenantId,
|
||||
enterpriseName: employeeDetail.companyName || '',
|
||||
status: employeeDetail.isActive ? 'active' : 'inactive',
|
||||
roleIds: [], // 需要单独获取角色信息
|
||||
idCard: '', // API返回中没有idCard字段
|
||||
address: '', // API返回中没有address字段
|
||||
auditStatus: 'approved', // 默认值
|
||||
isSuperuser: employeeDetail.isSuperuser,
|
||||
};
|
||||
|
||||
// 更新表单数据
|
||||
onFormDataChange(formDetailData);
|
||||
} catch (error) {
|
||||
console.error('获取员工详情失败:', error);
|
||||
toast.error('接口调用失败,请稍后重试');
|
||||
} finally {
|
||||
setLoadingDetail(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<Dialog open={open} onOpenChange={(isOpen) => {
|
||||
if (!isOpen && onClearForm) {
|
||||
onClearForm();
|
||||
}
|
||||
onOpenChange(isOpen);
|
||||
}}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingEmployee ? '编辑员工' : '添加员工'}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{editingEmployee ? (
|
||||
<div className="flex items-center gap-2">
|
||||
编辑员工
|
||||
{loadingDetail && (
|
||||
<div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
'添加员工'
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
{editingEmployee ? '编辑员工信息' : '添加新员工'}
|
||||
</DialogDescription>
|
||||
@@ -49,6 +114,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.username || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, username: e.target.value })}
|
||||
placeholder="登录用户名"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -58,6 +124,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.name || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, name: e.target.value })}
|
||||
placeholder="真实姓名"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -67,6 +134,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.phone || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, phone: e.target.value })}
|
||||
placeholder="11位手机号码"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -77,6 +145,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.email || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, email: e.target.value })}
|
||||
placeholder="电子邮箱"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -86,6 +155,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.idCard || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, idCard: e.target.value })}
|
||||
placeholder="18位身份证号码"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -95,6 +165,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.address || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, address: e.target.value })}
|
||||
placeholder="详细住址"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,6 +182,7 @@ export function EmployeeFormDialog({
|
||||
value={formData.department || ''}
|
||||
onChange={(e) => onFormDataChange({ ...formData, department: e.target.value })}
|
||||
placeholder="所属部门"
|
||||
disabled={editingEmployee && loadingDetail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,10 +236,12 @@ export function EmployeeFormDialog({
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={creating || updating || loadingDetail}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={onSave}>保存</Button>
|
||||
<Button onClick={onSave} disabled={creating || updating || loadingDetail}>
|
||||
{creating ? '创建中...' : updating ? '更新中...' : loadingDetail ? '加载中...' : '保存'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
@@ -31,6 +32,7 @@ interface EmployeeListProps {
|
||||
onToggleStatus: (employee: Employee) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onAudit?: (employee: Employee, action: 'approve' | 'reject') => void;
|
||||
togglingId?: string | null;
|
||||
}
|
||||
|
||||
export function EmployeeList({
|
||||
@@ -44,7 +46,8 @@ export function EmployeeList({
|
||||
onResetPassword,
|
||||
onToggleStatus,
|
||||
onDelete,
|
||||
onAudit
|
||||
onAudit,
|
||||
togglingId
|
||||
}: EmployeeListProps) {
|
||||
const getStatusBadge = (isActive: boolean, status?: UserStatus) => {
|
||||
// 优先使用isActive字段(来自API),其次使用status字段(兼容旧数据)
|
||||
@@ -76,7 +79,8 @@ export function EmployeeList({
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<TooltipProvider>
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
@@ -142,45 +146,88 @@ export function EmployeeList({
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onViewDetail(employee)}
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onEdit(employee)}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onResetPassword(employee)}
|
||||
>
|
||||
<Lock className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onToggleStatus(employee)}
|
||||
>
|
||||
{(employee.isActive || employee.status === 'active') ? (
|
||||
<UserX className="w-4 h-4 text-orange-600" />
|
||||
) : (
|
||||
<UserCheck className="w-4 h-4 text-green-600" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onDelete(employee.id)}
|
||||
>
|
||||
<Trash2 className="w-4 h-4 text-destructive" />
|
||||
</Button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onViewDetail(employee)}
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>查看详情</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onEdit(employee)}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>编辑员工</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onResetPassword(employee)}
|
||||
>
|
||||
<Lock className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>重置密码</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onToggleStatus(employee)}
|
||||
disabled={togglingId === employee.id}
|
||||
>
|
||||
{togglingId === employee.id ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (employee.isActive || employee.status === 'active') ? (
|
||||
<UserX className="w-4 h-4 text-orange-600" />
|
||||
) : (
|
||||
<UserCheck className="w-4 h-4 text-green-600" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{(employee.isActive || employee.status === 'active') ? '停用员工' : '激活员工'}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onDelete(employee.id)}
|
||||
disabled={togglingId === employee.id}
|
||||
>
|
||||
{togglingId === employee.id ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<Trash2 className="w-4 h-4 text-destructive" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>删除员工</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -268,6 +315,7 @@ export function EmployeeList({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Card>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,9 @@ interface EmployeeManagementHeaderProps {
|
||||
onAddEmployee: () => void;
|
||||
}
|
||||
|
||||
export function EmployeeManagementHeader({ onAddEmployee }: EmployeeManagementHeaderProps) {
|
||||
export function EmployeeManagementHeader({
|
||||
onAddEmployee
|
||||
}: EmployeeManagementHeaderProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { getAuthToken } from "@/utils/token";
|
||||
import { getUsersApiV1UsersGet } from "@/lib/api/sdk.gen";
|
||||
import { getUsersApiV1UsersGet, createUserApiV1UsersPost, getUserApiV1UsersUserIdGet, updateUserApiV1UsersUserIdPut, activateUserApiV1UsersUserIdActivatePost, deactivateUserApiV1UsersUserIdDeactivatePost, deleteUserApiV1UsersUserIdDelete } from "@/lib/api/sdk.gen";
|
||||
|
||||
// API返回的员工数据类型
|
||||
export interface EmployeeApiData {
|
||||
@@ -50,6 +50,32 @@ export interface EmployeesQueryParams {
|
||||
sort_order?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
// 创建用户请求参数接口
|
||||
export interface CreateEmployeeRequest {
|
||||
email: string;
|
||||
username: string;
|
||||
full_name?: string;
|
||||
phone: string;
|
||||
password: string;
|
||||
tenant_id?: string;
|
||||
scope?: string;
|
||||
department_id?: string;
|
||||
is_superuser?: boolean;
|
||||
}
|
||||
|
||||
// 更新用户请求参数接口
|
||||
export interface UpdateEmployeeRequest {
|
||||
email?: string;
|
||||
username?: string;
|
||||
full_name?: string;
|
||||
phone?: string;
|
||||
password?: string;
|
||||
tenant_id?: string;
|
||||
scope?: string;
|
||||
department_id?: string;
|
||||
is_superuser?: boolean;
|
||||
}
|
||||
|
||||
// 页面使用的员工数据类型(转换后的)
|
||||
export interface Employee {
|
||||
id: string;
|
||||
@@ -209,6 +235,258 @@ function formatDate(dateString: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建员工用户
|
||||
*/
|
||||
export async function createEmployee(employeeData: CreateEmployeeRequest): Promise<Employee> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('创建员工API调用参数:', employeeData);
|
||||
|
||||
// 使用SDK API调用创建用户接口
|
||||
const response = await createUserApiV1UsersPost({
|
||||
body: employeeData,
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
// 处理API错误响应
|
||||
const errorData = response.error as any;
|
||||
|
||||
// 检查是否是409冲突错误(用户已存在)
|
||||
if (errorData.status === 409 && errorData.data) {
|
||||
const conflictError = errorData.data as {
|
||||
code?: string;
|
||||
message?: string;
|
||||
domain?: string;
|
||||
detail?: {
|
||||
field?: string;
|
||||
value?: string;
|
||||
};
|
||||
};
|
||||
|
||||
// 抛出包含详细错误信息的异常
|
||||
throw new Error(conflictError.message || '用户创建失败');
|
||||
}
|
||||
|
||||
// 其他HTTP错误
|
||||
if (errorData.data && errorData.data.message) {
|
||||
throw new Error(errorData.data.message);
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
throw new Error(errorData.message || `API error: ${errorData.status || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const data = response.data as any;
|
||||
console.log('创建员工API响应:', data);
|
||||
|
||||
// 转换并返回新创建的员工数据
|
||||
return transformEmployeeData(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to create employee:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情信息
|
||||
*/
|
||||
export async function fetchEmployeeDetail(userId: string): Promise<Employee> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('获取用户详情API调用参数:', { userId });
|
||||
|
||||
// 使用SDK API调用用户详情查询接口
|
||||
const response = await getUserApiV1UsersUserIdGet({
|
||||
path: {
|
||||
user_id: userId,
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`API error: ${response.error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const data = response.data as any;
|
||||
console.log('获取用户详情API响应:', data);
|
||||
|
||||
// 转换并返回员工详情数据
|
||||
return transformEmployeeData(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch employee detail:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新员工用户信息
|
||||
*/
|
||||
export async function updateEmployee(userId: string, employeeData: UpdateEmployeeRequest): Promise<Employee> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('更新员工API调用参数:', { userId, employeeData });
|
||||
|
||||
// 使用SDK API调用更新用户接口
|
||||
const response = await updateUserApiV1UsersUserIdPut({
|
||||
path: {
|
||||
user_id: userId,
|
||||
},
|
||||
body: employeeData as any, // 使用any类型绕过类型检查,因为API类型定义与实际需求不匹配
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
// 处理API错误响应
|
||||
const errorData = response.error as any;
|
||||
|
||||
// 检查是否有详细的错误信息
|
||||
if (errorData.data && errorData.data.message) {
|
||||
throw new Error(errorData.data.message);
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
throw new Error(errorData.message || `API error: ${errorData.status || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const data = response.data as any;
|
||||
console.log('更新员工API响应:', data);
|
||||
|
||||
// 转换并返回更新后的员工数据
|
||||
return transformEmployeeData(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to update employee:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活用户
|
||||
*/
|
||||
export async function activateUser(userId: string): Promise<void> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('激活用户API调用参数:', { userId });
|
||||
|
||||
// 使用SDK API调用激活用户接口
|
||||
const response = await activateUserApiV1UsersUserIdActivatePost({
|
||||
path: {
|
||||
user_id: userId,
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
// 处理API错误响应
|
||||
const errorData = response.error as any;
|
||||
|
||||
// 检查是否有详细的错误信息
|
||||
if (errorData.data && errorData.data.message) {
|
||||
throw new Error(errorData.data.message);
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
throw new Error(errorData.message || `API error: ${errorData.status || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
console.log('激活用户API响应:', response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to activate user:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用用户
|
||||
*/
|
||||
export async function deactivateUser(userId: string): Promise<void> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('停用用户API调用参数:', { userId });
|
||||
|
||||
// 使用SDK API调用停用用户接口
|
||||
const response = await deactivateUserApiV1UsersUserIdDeactivatePost({
|
||||
path: {
|
||||
user_id: userId,
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
// 处理API错误响应
|
||||
const errorData = response.error as any;
|
||||
|
||||
// 检查是否有详细的错误信息
|
||||
if (errorData.data && errorData.data.message) {
|
||||
throw new Error(errorData.data.message);
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
throw new Error(errorData.message || `API error: ${errorData.status || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
console.log('停用用户API响应:', response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to deactivate user:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
export async function deleteUser(userId: string): Promise<void> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('删除用户API调用参数:', { userId });
|
||||
|
||||
// 使用SDK API调用删除用户接口
|
||||
const response = await deleteUserApiV1UsersUserIdDelete({
|
||||
path: {
|
||||
user_id: userId,
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
// 处理API错误响应
|
||||
const errorData = response.error as any;
|
||||
|
||||
// 检查是否有详细的错误信息
|
||||
if (errorData.data && errorData.data.message) {
|
||||
throw new Error(errorData.data.message);
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
throw new Error(errorData.message || `API error: ${errorData.status || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
console.log('删除用户API响应:', response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete user:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Pagination state interface for page components
|
||||
export interface PaginationState {
|
||||
page: number;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -75,6 +75,7 @@ export interface EmployeeFormData {
|
||||
name?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
password?: string;
|
||||
department?: string;
|
||||
position?: string;
|
||||
enterpriseId?: string;
|
||||
|
||||
Reference in New Issue
Block a user