生产管理系统 部门全页面开发完毕
This commit is contained in:
@@ -35,57 +35,21 @@ export function DepartmentDeleteDialog({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-full bg-red-100 dark:bg-red-900">
|
||||
<AlertTriangle className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<div>
|
||||
<DialogTitle>确认删除</DialogTitle>
|
||||
</div>
|
||||
</div>
|
||||
<DialogTitle>确认删除部门</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogDescription className="text-left">
|
||||
{deletingDepartment && (
|
||||
<div className="space-y-4">
|
||||
{/* 部门信息 */}
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<span className="text-2xl">🏢</span>
|
||||
<div>
|
||||
<div className="font-medium">{deletingDepartment.name}</div>
|
||||
<div className="text-sm text-muted-foreground">部门编码:{deletingDepartment.code}</div>
|
||||
{deletingDepartment.manager && (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
负责人:{deletingDepartment.manager}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 删除影响说明 */}
|
||||
<div className="p-4 bg-orange-50 dark:bg-orange-950 border border-orange-200 dark:border-orange-800 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className="w-5 h-5 mt-0.5 flex-shrink-0 text-orange-600 dark:text-orange-400" />
|
||||
<div>
|
||||
<div className="font-medium mb-2 text-orange-800 dark:text-orange-200">
|
||||
删除影响:
|
||||
</div>
|
||||
<ul className="text-sm space-y-1 text-orange-700 dark:text-orange-300">
|
||||
<li>• 部门将永久删除,此操作不可恢复</li>
|
||||
<li>• 部门下的所有相关信息将被清除</li>
|
||||
<li>• 如有员工归属于此部门,需要重新分配</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 确认提示 */}
|
||||
<div className="text-center">
|
||||
<p className="text-muted-foreground">
|
||||
确定要删除部门 <strong>{deletingDepartment.name}</strong> 吗?
|
||||
</p>
|
||||
</div>
|
||||
{deletingDepartment ? (
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
确定要删除部门 <strong>{deletingDepartment.name}</strong> 吗?
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
此操作不可撤销,请谨慎操作。
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<p>确定要删除选中的部门吗?</p>
|
||||
)}
|
||||
</DialogDescription>
|
||||
|
||||
@@ -97,7 +61,7 @@ export function DepartmentDeleteDialog({
|
||||
variant="destructive"
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
确认删除
|
||||
删除
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -17,9 +17,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
||||
import { Department, CreateDepartmentForm } from '../types';
|
||||
import {
|
||||
createDepartment,
|
||||
updateDepartment,
|
||||
transformCreateDepartmentData,
|
||||
debounce,
|
||||
generateRandomOrderIndex,
|
||||
getDefaultOrderIndex,
|
||||
} from './departmentCreateApi';
|
||||
|
||||
interface DepartmentFormDialogProps {
|
||||
@@ -47,43 +48,48 @@ export function DepartmentFormDialog({
|
||||
email: '',
|
||||
description: '',
|
||||
status: 'active',
|
||||
sort: generateRandomOrderIndex(),
|
||||
sort: 0,
|
||||
parentId: parentDepartment?.id || '',
|
||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 当编辑部门或父部门变化时,重置表单数据
|
||||
useState(() => {
|
||||
if (editingDepartment) {
|
||||
setFormData({
|
||||
name: editingDepartment.name,
|
||||
code: editingDepartment.code,
|
||||
manager: editingDepartment.manager || '',
|
||||
phone: editingDepartment.phone || '',
|
||||
email: editingDepartment.email || '',
|
||||
description: editingDepartment.description || '',
|
||||
status: editingDepartment.status,
|
||||
sort: editingDepartment.sort,
|
||||
parentId: editingDepartment.parentId || '',
|
||||
level: editingDepartment.level,
|
||||
});
|
||||
} else {
|
||||
setFormData({
|
||||
name: '',
|
||||
code: '',
|
||||
manager: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
status: 'active',
|
||||
sort: generateRandomOrderIndex(),
|
||||
parentId: parentDepartment?.id || '',
|
||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||
});
|
||||
// 当弹窗打开状态变化时,处理表单数据
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// 弹窗打开时,根据编辑状态初始化表单
|
||||
if (editingDepartment) {
|
||||
// 编辑模式:带入所有部门数据
|
||||
setFormData({
|
||||
name: editingDepartment.name || '',
|
||||
code: editingDepartment.code || '',
|
||||
manager: editingDepartment.manager || '',
|
||||
phone: editingDepartment.phone || '',
|
||||
email: editingDepartment.email || '',
|
||||
description: editingDepartment.description || '',
|
||||
status: editingDepartment.status || 'active',
|
||||
sort: editingDepartment.sort || 0,
|
||||
parentId: editingDepartment.parentId || '',
|
||||
level: editingDepartment.level || 1,
|
||||
});
|
||||
} else {
|
||||
// 新增模式:清空表单,使用默认值
|
||||
setFormData({
|
||||
name: '',
|
||||
code: '',
|
||||
manager: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
status: 'active',
|
||||
sort: 0,
|
||||
parentId: parentDepartment?.id || '',
|
||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [open, editingDepartment, parentDepartment]);
|
||||
|
||||
const handleInputChange = (field: keyof CreateDepartmentForm, value: string | number) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
@@ -126,20 +132,6 @@ export function DepartmentFormDialog({
|
||||
refreshDepartmentTree();
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
setFormData({
|
||||
name: '',
|
||||
code: '',
|
||||
manager: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
status: 'active',
|
||||
sort: generateRandomOrderIndex(),
|
||||
parentId: parentDepartment?.id || '',
|
||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// 失败处理 - 不关闭页面
|
||||
console.error('创建部门失败:', error);
|
||||
@@ -158,19 +150,27 @@ export function DepartmentFormDialog({
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是编辑模式,使用原有的onSave逻辑
|
||||
// 如果是编辑模式,使用API调用
|
||||
if (editingDepartment) {
|
||||
setLoading(true);
|
||||
try {
|
||||
await onSave(formData);
|
||||
// 调用更新部门API
|
||||
await updateDepartment(editingDepartment.id, formData);
|
||||
|
||||
// 成功处理
|
||||
toast.success('部门更新成功');
|
||||
onOpenChange(false);
|
||||
|
||||
// 刷新部门树
|
||||
if (refreshDepartmentTree) {
|
||||
refreshDepartmentTree();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to update department:', error);
|
||||
toast.error('部门更新失败');
|
||||
// 失败处理 - 不关闭页面
|
||||
console.error('更新部门失败:', error);
|
||||
const errorMessage = error instanceof Error ? error.message : '部门更新失败';
|
||||
toast.error(errorMessage);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -183,19 +183,6 @@ export function DepartmentFormDialog({
|
||||
const handleClose = () => {
|
||||
if (!loading) {
|
||||
onOpenChange(false);
|
||||
// 重置表单
|
||||
setFormData({
|
||||
name: '',
|
||||
code: '',
|
||||
manager: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
status: 'active',
|
||||
sort: generateRandomOrderIndex(),
|
||||
parentId: parentDepartment?.id || '',
|
||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -149,16 +149,15 @@ export function DepartmentTree({
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex items-center gap-1 ml-4">
|
||||
{level < 1 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onAdd(dept)}
|
||||
title="添加子部门"
|
||||
>
|
||||
<Plus className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||
</Button>
|
||||
)}
|
||||
{/* 所有节点都可以添加子部门 */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onAdd(dept)}
|
||||
title="添加子部门"
|
||||
>
|
||||
<Plus className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
||||
@@ -8,12 +8,17 @@
|
||||
import { getAuthToken } from "@/utils/token";
|
||||
import {
|
||||
createDepartmentApiV1DepartmentsPost,
|
||||
updateDepartmentApiV1DepartmentsDepartmentIdPut,
|
||||
deleteDepartmentApiV1DepartmentsDepartmentIdDelete,
|
||||
} from "@/lib/api/sdk.gen";
|
||||
import {
|
||||
Department,
|
||||
CreateDepartmentForm,
|
||||
} from '../types';
|
||||
|
||||
// 导出deleteDepartment供其他模块使用
|
||||
export { deleteDepartment };
|
||||
|
||||
/**
|
||||
* API请求创建部门的数据结构(对应Python字段)
|
||||
*/
|
||||
@@ -109,10 +114,82 @@ export async function createDepartment(formData: CreateDepartmentForm): Promise<
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机排序索引(0-10000)
|
||||
* 更新部门API调用
|
||||
*/
|
||||
export function generateRandomOrderIndex(): number {
|
||||
return Math.floor(Math.random() * 10001);
|
||||
export async function updateDepartment(departmentId: string, formData: CreateDepartmentForm): Promise<CreateDepartmentApiResponse> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('更新部门API调用参数:', departmentId, formData);
|
||||
|
||||
// 转换表单数据为API请求格式
|
||||
const apiRequestData = transformCreateDepartmentData(formData);
|
||||
|
||||
// 邮箱格式验证
|
||||
if (apiRequestData.manager_email && !isValidEmail(apiRequestData.manager_email)) {
|
||||
throw new Error('邮箱格式不正确');
|
||||
}
|
||||
|
||||
// 使用真正的SDK API调用
|
||||
const response = await updateDepartmentApiV1DepartmentsDepartmentIdPut({
|
||||
path: {
|
||||
department_id: departmentId,
|
||||
},
|
||||
body: apiRequestData,
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`API error: ${response.error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const data = response.data as CreateDepartmentApiResponse;
|
||||
console.log('更新部门API响应:', data);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Failed to update department:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认排序索引
|
||||
*/
|
||||
export function getDefaultOrderIndex(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门API调用
|
||||
*/
|
||||
export async function deleteDepartment(departmentId: string): Promise<void> {
|
||||
try {
|
||||
// 获取认证token
|
||||
const token = getAuthToken();
|
||||
console.log('删除部门API调用参数:', departmentId);
|
||||
|
||||
// 使用真正的SDK API调用
|
||||
const response = await deleteDepartmentApiV1DepartmentsDepartmentIdDelete({
|
||||
path: {
|
||||
department_id: departmentId,
|
||||
},
|
||||
headers: token ? {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
} : undefined,
|
||||
});
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`API error: ${response.error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
console.log('删除部门API响应: 成功');
|
||||
} catch (error) {
|
||||
console.error('Failed to delete department:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
flattenDepartments,
|
||||
type DepartmentTreeState
|
||||
} from './components/departmentApi';
|
||||
import { deleteDepartment } from './components/departmentCreateApi';
|
||||
|
||||
// 部门管理状态管理
|
||||
interface DepartmentManagementState {
|
||||
@@ -135,8 +136,8 @@ export default function DepartmentManagementPage() {
|
||||
// 转换API数据为页面所需的格式
|
||||
const departments = transformDepartmentList(response.data);
|
||||
|
||||
// 转换为与现有页面兼容的数据格式
|
||||
const compatibleDepartments: Department[] = departments.map(dept => ({
|
||||
// 递归转换部门数据为与现有页面兼容的数据格式
|
||||
const transformDepartmentRecursive = (dept: any): Department => ({
|
||||
id: dept.id,
|
||||
name: dept.name,
|
||||
code: dept.code,
|
||||
@@ -150,22 +151,10 @@ export default function DepartmentManagementPage() {
|
||||
parentId: dept.parentId || undefined,
|
||||
createdAt: dept.createdAt,
|
||||
updatedAt: dept.updatedAt,
|
||||
children: dept.children.map(child => ({
|
||||
id: child.id,
|
||||
name: child.name,
|
||||
code: child.code,
|
||||
level: child.level + 1,
|
||||
manager: child.manager, // 从API的manager_name字段获取
|
||||
phone: child.phone, // 从API的manager_phone字段获取
|
||||
email: child.email, // 从API的manager_email字段获取
|
||||
description: child.description,
|
||||
sort: child.sortOrder,
|
||||
status: child.status as 'active' | 'inactive',
|
||||
parentId: child.parentId || undefined,
|
||||
createdAt: child.createdAt,
|
||||
updatedAt: child.updatedAt,
|
||||
})),
|
||||
}));
|
||||
children: dept.children ? dept.children.map(transformDepartmentRecursive) : [],
|
||||
});
|
||||
|
||||
const compatibleDepartments: Department[] = departments.map(transformDepartmentRecursive);
|
||||
|
||||
dispatch({ type: 'SET_DEPARTMENTS', payload: compatibleDepartments });
|
||||
// 默认展开所有一级部门
|
||||
@@ -334,29 +323,26 @@ export default function DepartmentManagementPage() {
|
||||
};
|
||||
|
||||
// 确认删除
|
||||
const confirmDelete = () => {
|
||||
const confirmDelete = async () => {
|
||||
if (!state.deletingDepartment) return;
|
||||
|
||||
const deleteFromTree = (items: Department[]): Department[] => {
|
||||
return items
|
||||
.filter(item => item.id !== state.deletingDepartment!.id)
|
||||
.map(item => {
|
||||
if (item.children) {
|
||||
return {
|
||||
...item,
|
||||
children: deleteFromTree(item.children),
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
try {
|
||||
// 调用删除API
|
||||
await deleteDepartment(state.deletingDepartment.id);
|
||||
|
||||
const updated = deleteFromTree(state.departments);
|
||||
dispatch({ type: 'SET_DEPARTMENTS', payload: updated });
|
||||
toast.success('部门删除成功');
|
||||
// 删除成功后,刷新部门列表
|
||||
await loadDepartments();
|
||||
|
||||
dispatch({ type: 'TOGGLE_DELETE_DIALOG', payload: false });
|
||||
dispatch({ type: 'SET_DELETING_DEPARTMENT', payload: null });
|
||||
toast.success('部门删除成功');
|
||||
|
||||
// 关闭删除对话框
|
||||
dispatch({ type: 'TOGGLE_DELETE_DIALOG', payload: false });
|
||||
dispatch({ type: 'SET_DELETING_DEPARTMENT', payload: null });
|
||||
} catch (error) {
|
||||
console.error('Failed to delete department:', error);
|
||||
const errorMessage = error instanceof Error ? error.message : '删除部门失败';
|
||||
toast.error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
// 拖拽功能
|
||||
|
||||
Reference in New Issue
Block a user