生产管理系统 部门全页面开发完毕
This commit is contained in:
@@ -35,57 +35,21 @@ export function DepartmentDeleteDialog({
|
|||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-md">
|
<DialogContent className="max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<div className="flex items-center gap-3">
|
<DialogTitle>确认删除部门</DialogTitle>
|
||||||
<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>
|
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<DialogDescription className="text-left">
|
<DialogDescription className="text-left">
|
||||||
{deletingDepartment && (
|
{deletingDepartment ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-3">
|
||||||
{/* 部门信息 */}
|
<p>
|
||||||
<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> 吗?
|
确定要删除部门 <strong>{deletingDepartment.name}</strong> 吗?
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
此操作不可撤销,请谨慎操作。
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<p>确定要删除选中的部门吗?</p>
|
||||||
)}
|
)}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|
||||||
@@ -97,7 +61,7 @@ export function DepartmentDeleteDialog({
|
|||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={handleConfirm}
|
onClick={handleConfirm}
|
||||||
>
|
>
|
||||||
确认删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -17,9 +17,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
|||||||
import { Department, CreateDepartmentForm } from '../types';
|
import { Department, CreateDepartmentForm } from '../types';
|
||||||
import {
|
import {
|
||||||
createDepartment,
|
createDepartment,
|
||||||
|
updateDepartment,
|
||||||
transformCreateDepartmentData,
|
transformCreateDepartmentData,
|
||||||
debounce,
|
debounce,
|
||||||
generateRandomOrderIndex,
|
getDefaultOrderIndex,
|
||||||
} from './departmentCreateApi';
|
} from './departmentCreateApi';
|
||||||
|
|
||||||
interface DepartmentFormDialogProps {
|
interface DepartmentFormDialogProps {
|
||||||
@@ -47,29 +48,33 @@ export function DepartmentFormDialog({
|
|||||||
email: '',
|
email: '',
|
||||||
description: '',
|
description: '',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
sort: generateRandomOrderIndex(),
|
sort: 0,
|
||||||
parentId: parentDepartment?.id || '',
|
parentId: parentDepartment?.id || '',
|
||||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
// 当编辑部门或父部门变化时,重置表单数据
|
// 当弹窗打开状态变化时,处理表单数据
|
||||||
useState(() => {
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
// 弹窗打开时,根据编辑状态初始化表单
|
||||||
if (editingDepartment) {
|
if (editingDepartment) {
|
||||||
|
// 编辑模式:带入所有部门数据
|
||||||
setFormData({
|
setFormData({
|
||||||
name: editingDepartment.name,
|
name: editingDepartment.name || '',
|
||||||
code: editingDepartment.code,
|
code: editingDepartment.code || '',
|
||||||
manager: editingDepartment.manager || '',
|
manager: editingDepartment.manager || '',
|
||||||
phone: editingDepartment.phone || '',
|
phone: editingDepartment.phone || '',
|
||||||
email: editingDepartment.email || '',
|
email: editingDepartment.email || '',
|
||||||
description: editingDepartment.description || '',
|
description: editingDepartment.description || '',
|
||||||
status: editingDepartment.status,
|
status: editingDepartment.status || 'active',
|
||||||
sort: editingDepartment.sort,
|
sort: editingDepartment.sort || 0,
|
||||||
parentId: editingDepartment.parentId || '',
|
parentId: editingDepartment.parentId || '',
|
||||||
level: editingDepartment.level,
|
level: editingDepartment.level || 1,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 新增模式:清空表单,使用默认值
|
||||||
setFormData({
|
setFormData({
|
||||||
name: '',
|
name: '',
|
||||||
code: '',
|
code: '',
|
||||||
@@ -78,12 +83,13 @@ export function DepartmentFormDialog({
|
|||||||
email: '',
|
email: '',
|
||||||
description: '',
|
description: '',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
sort: generateRandomOrderIndex(),
|
sort: 0,
|
||||||
parentId: parentDepartment?.id || '',
|
parentId: parentDepartment?.id || '',
|
||||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}, [open, editingDepartment, parentDepartment]);
|
||||||
|
|
||||||
const handleInputChange = (field: keyof CreateDepartmentForm, value: string | number) => {
|
const handleInputChange = (field: keyof CreateDepartmentForm, value: string | number) => {
|
||||||
setFormData(prev => ({ ...prev, [field]: value }));
|
setFormData(prev => ({ ...prev, [field]: value }));
|
||||||
@@ -126,20 +132,6 @@ export function DepartmentFormDialog({
|
|||||||
refreshDepartmentTree();
|
refreshDepartmentTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
setFormData({
|
|
||||||
name: '',
|
|
||||||
code: '',
|
|
||||||
manager: '',
|
|
||||||
phone: '',
|
|
||||||
email: '',
|
|
||||||
description: '',
|
|
||||||
status: 'active',
|
|
||||||
sort: generateRandomOrderIndex(),
|
|
||||||
parentId: parentDepartment?.id || '',
|
|
||||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 失败处理 - 不关闭页面
|
// 失败处理 - 不关闭页面
|
||||||
console.error('创建部门失败:', error);
|
console.error('创建部门失败:', error);
|
||||||
@@ -158,19 +150,27 @@ export function DepartmentFormDialog({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是编辑模式,使用原有的onSave逻辑
|
// 如果是编辑模式,使用API调用
|
||||||
if (editingDepartment) {
|
if (editingDepartment) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await onSave(formData);
|
// 调用更新部门API
|
||||||
|
await updateDepartment(editingDepartment.id, formData);
|
||||||
|
|
||||||
|
// 成功处理
|
||||||
toast.success('部门更新成功');
|
toast.success('部门更新成功');
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
|
||||||
|
// 刷新部门树
|
||||||
if (refreshDepartmentTree) {
|
if (refreshDepartmentTree) {
|
||||||
refreshDepartmentTree();
|
refreshDepartmentTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} 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 {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -183,19 +183,6 @@ export function DepartmentFormDialog({
|
|||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
// 重置表单
|
|
||||||
setFormData({
|
|
||||||
name: '',
|
|
||||||
code: '',
|
|
||||||
manager: '',
|
|
||||||
phone: '',
|
|
||||||
email: '',
|
|
||||||
description: '',
|
|
||||||
status: 'active',
|
|
||||||
sort: generateRandomOrderIndex(),
|
|
||||||
parentId: parentDepartment?.id || '',
|
|
||||||
level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function DepartmentTree({
|
|||||||
|
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 */}
|
||||||
<div className="flex items-center gap-1 ml-4">
|
<div className="flex items-center gap-1 ml-4">
|
||||||
{level < 1 && (
|
{/* 所有节点都可以添加子部门 */}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -158,7 +158,6 @@ export function DepartmentTree({
|
|||||||
>
|
>
|
||||||
<Plus className="w-4 h-4 text-green-600 dark:text-green-400" />
|
<Plus className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -8,12 +8,17 @@
|
|||||||
import { getAuthToken } from "@/utils/token";
|
import { getAuthToken } from "@/utils/token";
|
||||||
import {
|
import {
|
||||||
createDepartmentApiV1DepartmentsPost,
|
createDepartmentApiV1DepartmentsPost,
|
||||||
|
updateDepartmentApiV1DepartmentsDepartmentIdPut,
|
||||||
|
deleteDepartmentApiV1DepartmentsDepartmentIdDelete,
|
||||||
} from "@/lib/api/sdk.gen";
|
} from "@/lib/api/sdk.gen";
|
||||||
import {
|
import {
|
||||||
Department,
|
Department,
|
||||||
CreateDepartmentForm,
|
CreateDepartmentForm,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
|
// 导出deleteDepartment供其他模块使用
|
||||||
|
export { deleteDepartment };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API请求创建部门的数据结构(对应Python字段)
|
* API请求创建部门的数据结构(对应Python字段)
|
||||||
*/
|
*/
|
||||||
@@ -109,10 +114,82 @@ export async function createDepartment(formData: CreateDepartmentForm): Promise<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成随机排序索引(0-10000)
|
* 更新部门API调用
|
||||||
*/
|
*/
|
||||||
export function generateRandomOrderIndex(): number {
|
export async function updateDepartment(departmentId: string, formData: CreateDepartmentForm): Promise<CreateDepartmentApiResponse> {
|
||||||
return Math.floor(Math.random() * 10001);
|
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,
|
flattenDepartments,
|
||||||
type DepartmentTreeState
|
type DepartmentTreeState
|
||||||
} from './components/departmentApi';
|
} from './components/departmentApi';
|
||||||
|
import { deleteDepartment } from './components/departmentCreateApi';
|
||||||
|
|
||||||
// 部门管理状态管理
|
// 部门管理状态管理
|
||||||
interface DepartmentManagementState {
|
interface DepartmentManagementState {
|
||||||
@@ -135,8 +136,8 @@ export default function DepartmentManagementPage() {
|
|||||||
// 转换API数据为页面所需的格式
|
// 转换API数据为页面所需的格式
|
||||||
const departments = transformDepartmentList(response.data);
|
const departments = transformDepartmentList(response.data);
|
||||||
|
|
||||||
// 转换为与现有页面兼容的数据格式
|
// 递归转换部门数据为与现有页面兼容的数据格式
|
||||||
const compatibleDepartments: Department[] = departments.map(dept => ({
|
const transformDepartmentRecursive = (dept: any): Department => ({
|
||||||
id: dept.id,
|
id: dept.id,
|
||||||
name: dept.name,
|
name: dept.name,
|
||||||
code: dept.code,
|
code: dept.code,
|
||||||
@@ -150,22 +151,10 @@ export default function DepartmentManagementPage() {
|
|||||||
parentId: dept.parentId || undefined,
|
parentId: dept.parentId || undefined,
|
||||||
createdAt: dept.createdAt,
|
createdAt: dept.createdAt,
|
||||||
updatedAt: dept.updatedAt,
|
updatedAt: dept.updatedAt,
|
||||||
children: dept.children.map(child => ({
|
children: dept.children ? dept.children.map(transformDepartmentRecursive) : [],
|
||||||
id: child.id,
|
});
|
||||||
name: child.name,
|
|
||||||
code: child.code,
|
const compatibleDepartments: Department[] = departments.map(transformDepartmentRecursive);
|
||||||
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,
|
|
||||||
})),
|
|
||||||
}));
|
|
||||||
|
|
||||||
dispatch({ type: 'SET_DEPARTMENTS', payload: compatibleDepartments });
|
dispatch({ type: 'SET_DEPARTMENTS', payload: compatibleDepartments });
|
||||||
// 默认展开所有一级部门
|
// 默认展开所有一级部门
|
||||||
@@ -334,29 +323,26 @@ export default function DepartmentManagementPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 确认删除
|
// 确认删除
|
||||||
const confirmDelete = () => {
|
const confirmDelete = async () => {
|
||||||
if (!state.deletingDepartment) return;
|
if (!state.deletingDepartment) return;
|
||||||
|
|
||||||
const deleteFromTree = (items: Department[]): Department[] => {
|
try {
|
||||||
return items
|
// 调用删除API
|
||||||
.filter(item => item.id !== state.deletingDepartment!.id)
|
await deleteDepartment(state.deletingDepartment.id);
|
||||||
.map(item => {
|
|
||||||
if (item.children) {
|
// 删除成功后,刷新部门列表
|
||||||
return {
|
await loadDepartments();
|
||||||
...item,
|
|
||||||
children: deleteFromTree(item.children),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const updated = deleteFromTree(state.departments);
|
|
||||||
dispatch({ type: 'SET_DEPARTMENTS', payload: updated });
|
|
||||||
toast.success('部门删除成功');
|
toast.success('部门删除成功');
|
||||||
|
|
||||||
|
// 关闭删除对话框
|
||||||
dispatch({ type: 'TOGGLE_DELETE_DIALOG', payload: false });
|
dispatch({ type: 'TOGGLE_DELETE_DIALOG', payload: false });
|
||||||
dispatch({ type: 'SET_DELETING_DEPARTMENT', payload: null });
|
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