可正常启用

This commit is contained in:
戚辰先生 2024-12-01 14:44:21 +08:00
parent 9975d4508e
commit 6e79e0faac
3 changed files with 352 additions and 266 deletions

View File

@ -1,166 +1,187 @@
import React, { useEffect, useState } from 'react'; import React, { useState, useEffect } from 'react';
import { Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect } from 'antd'; import { Table, Button, Modal, Form, Input, Space, InputNumber, Switch, TreeSelect, Select } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import type { DepartmentDTO } from './types'; import type { DepartmentResponse } from './types';
import { getDepartmentTree, createDepartment, updateDepartment, deleteDepartment, getNextSort } from './service.ts'; import { useTableData } from '@/hooks/useTableData';
import type { FixedType } from 'rc-table/lib/interface';
import { getDepartmentTree } from './service';
import type { UserResponse } from '@/pages/System/User/types';
import { getUsers } from './service';
const DepartmentPage: React.FC = () => { const DepartmentPage: React.FC = () => {
const [departments, setDepartments] = useState<DepartmentDTO[]>([]); const {
list: departments,
pagination,
loading,
loadData: fetchDepartments,
handleCreate,
handleUpdate,
handleDelete
} = useTableData({
service: {
baseUrl: '/api/v1/department'
},
defaultParams: {
sortField: 'createTime',
sortOrder: 'descend'
}
});
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const [editingDepartment, setEditingDepartment] = useState<DepartmentDTO | null>(null); const [editingDepartment, setEditingDepartment] = useState<DepartmentResponse | null>(null);
const [loading, setLoading] = useState(false); const [departmentTree, setDepartmentTree] = useState<DepartmentResponse[]>([]);
const [users, setUsers] = useState<UserResponse[]>([]);
const [form] = Form.useForm(); const [form] = Form.useForm();
const fetchDepartments = async () => {
try {
setLoading(true);
const data = await getDepartmentTree();
setDepartments(data || []);
console.log("部门", data);
} catch (error) {
console.error('获取部门列表失败:', error);
message.error('获取部门列表失败');
} finally {
setLoading(false);
}
};
useEffect(() => { useEffect(() => {
fetchDepartments(); Promise.all([
getDepartmentTree().then(setDepartmentTree),
getUsers().then(setUsers)
]);
}, []); }, []);
const handleAdd = () => { const handleAdd = () => {
setEditingDepartment(null); setEditingDepartment(null);
form.resetFields(); form.resetFields();
// 获取当前最大排序值
const maxSort = Math.max(0, ...departments.map(dept => dept.sort));
form.setFieldsValue({ form.setFieldsValue({
enabled: true, enabled: true,
sort: 0 sort: maxSort + 1
}); });
setModalVisible(true); setModalVisible(true);
}; };
const handleEdit = (record: DepartmentDTO) => { const handleEdit = (record: DepartmentResponse) => {
setEditingDepartment(record); setEditingDepartment(record);
form.setFieldsValue({ form.setFieldsValue(record);
...record,
parentId: record.parentId || undefined
});
setModalVisible(true); setModalVisible(true);
}; };
const handleDelete = async (id: number) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这个部门吗?',
onOk: async () => {
try {
await deleteDepartment(id);
message.success('删除成功');
fetchDepartments();
} catch (error) {
message.error('删除失败');
}
},
});
};
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
if (editingDepartment) { if (editingDepartment) {
await updateDepartment(editingDepartment.id, { const success = await handleUpdate(editingDepartment.id, {
...values, ...values,
version: editingDepartment.version version: editingDepartment.version
}); });
message.success('更新成功'); if (success) {
} else {
await createDepartment(values);
message.success('创建成功');
}
setModalVisible(false); setModalVisible(false);
fetchDepartments(); fetchDepartments();
} catch (error) { getDepartmentTree().then(setDepartmentTree);
message.error('操作失败'); }
} else {
const success = await handleCreate(values);
if (success) {
setModalVisible(false);
fetchDepartments();
getDepartmentTree().then(setDepartmentTree);
}
} }
};
const handleParentChange = async (value: number | undefined) => {
try {
const nextSort = await getNextSort(value);
form.setFieldValue('sort', nextSort);
} catch (error) { } catch (error) {
console.error('获取排序号失败:', error); console.error('操作失败:', error);
} }
}; };
const columns = [ const columns = [
{ {
title: '部门名称', title: 'ID',
dataIndex: 'name', dataIndex: 'id',
key: 'name', key: 'id',
width: '25%', width: 60,
fixed: 'left' as FixedType,
sorter: true
}, },
{ {
title: '部门编码', title: '部门编码',
dataIndex: 'code', dataIndex: 'code',
key: 'code', key: 'code',
width: '20%', width: 100,
sorter: true
},
{
title: '部门名称',
dataIndex: 'name',
key: 'name',
width: 150,
sorter: true
},
{
title: '上级部门',
dataIndex: 'parentId',
key: 'parentId',
width: 150,
render: (parentId: number) => {
const parent = departments.find(dept => dept.id === parentId);
return parent?.name || '-';
}
},
{
title: '部门描述',
dataIndex: 'description',
key: 'description',
width: 200,
ellipsis: true
}, },
{ {
title: '排序', title: '排序',
dataIndex: 'sort', dataIndex: 'sort',
key: 'sort', key: 'sort',
width: '15%', width: 80,
sorter: true
},
{
title: '负责人',
dataIndex: 'leaderName',
key: 'leaderName',
width: 100
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'enabled', dataIndex: 'enabled',
key: 'enabled', key: 'enabled',
width: '15%', width: 80,
render: (enabled: boolean) => ( render: (enabled: boolean) => (
<Switch checked={enabled} disabled /> <Switch checked={enabled} disabled size="small"/>
), )
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
width: '25%', width: 160,
render: (_: any, record: DepartmentDTO) => ( fixed: 'right' as FixedType,
<Space> render: (_: any, record: DepartmentResponse) => (
<Space size={0}>
<Button <Button
type="link" type="link"
icon={<EditOutlined />} size="small"
icon={<EditOutlined/>}
onClick={() => handleEdit(record)} onClick={() => handleEdit(record)}
> >
</Button> </Button>
<Button <Button
type="link" type="link"
size="small"
danger danger
icon={<DeleteOutlined />} icon={<DeleteOutlined/>}
onClick={() => handleDelete(record.id)} onClick={() => handleDelete(record.id)}
disabled={record.parentId === null} disabled={record.children?.length > 0}
> >
</Button> </Button>
</Space> </Space>
), )
}, }
]; ];
const getTreeSelectData = (deps: DepartmentDTO[]): any[] => {
return deps.map(dept => ({
title: dept.name,
value: dept.id,
children: dept.children && dept.children.length > 0 ? getTreeSelectData(dept.children) : undefined,
disabled: editingDepartment ? dept.id === editingDepartment.id : false
}));
};
return ( return (
<div style={{ padding: '24px' }}> <div style={{padding: '24px'}}>
<div style={{ marginBottom: 16 }}> <div style={{marginBottom: 16}}>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}> <Button type="primary" icon={<PlusOutlined/>} onClick={handleAdd}>
</Button> </Button>
</div> </div>
@ -170,8 +191,8 @@ const DepartmentPage: React.FC = () => {
columns={columns} columns={columns}
dataSource={departments} dataSource={departments}
rowKey="id" rowKey="id"
defaultExpandAllRows scroll={{x: 1500}}
pagination={false} pagination={pagination}
size="middle" size="middle"
bordered bordered
/> />
@ -188,46 +209,102 @@ const DepartmentPage: React.FC = () => {
form={form} form={form}
layout="vertical" layout="vertical"
> >
<Form.Item
name="name"
label="部门名称"
rules={[{ required: true, message: '请输入部门名称' }]}
>
<Input placeholder="请输入部门名称" />
</Form.Item>
<Form.Item <Form.Item
name="code" name="code"
label="部门编码" label="部门编码"
rules={[{ required: true, message: '请输入部门编码' }]} rules={[
{required: true, message: '请输入部门编码'},
{pattern: /^[A-Z_]+$/, message: '部门编码只能包含大写字母和下划线'}
]}
> >
<Input placeholder="请输入部门编码" /> <Input placeholder="请输入部门编码"/>
</Form.Item> </Form.Item>
<Form.Item
name="name"
label="部门名称"
rules={[{required: true, message: '请输入部门名称'}]}
>
<Input placeholder="请输入部门名称"/>
</Form.Item>
<Form.Item <Form.Item
name="parentId" name="parentId"
label="上级部门" label="上级部门"
> >
<TreeSelect <TreeSelect
treeData={getTreeSelectData(departments)} treeData={departmentTree.map(dept => ({
title: dept.name,
value: dept.id,
children: dept.children?.map(child => ({
title: child.name,
value: child.id
}))
}))}
placeholder="请选择上级部门" placeholder="请选择上级部门"
allowClear allowClear
treeDefaultExpandAll treeDefaultExpandAll
onChange={handleParentChange} disabled={editingDepartment?.children?.length > 0}
disabled={editingDepartment?.parentId === null}
/> />
</Form.Item> </Form.Item>
<Form.Item
name="description"
label="部门描述"
>
<Input.TextArea placeholder="请输入部门描述"/>
</Form.Item>
<Form.Item <Form.Item
name="sort" name="sort"
label="排序" label="显示排序"
rules={[{required: true, message: '请输入显示排序'}]}
> >
<Input type="number" placeholder="排序号将自动生成" disabled /> <InputNumber
style={{width: '100%'}}
min={0}
placeholder="请输入显示排序"
/>
</Form.Item> </Form.Item>
<Form.Item
name="leaderId"
label="负责人"
>
<Select
placeholder="请选择负责人"
allowClear
showSearch
optionFilterProp="children"
onChange={(value, option: any) => {
form.setFieldsValue({
leaderId: value,
leaderName: option?.label
});
}}
>
{users.map(user => (
<Select.Option
key={user.id}
value={user.id}
label={user.nickname || user.username}
>
{user.nickname || user.username}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item name="leaderName" hidden>
<Input />
</Form.Item>
<Form.Item <Form.Item
name="enabled" name="enabled"
label="状态" label="状态"
valuePropName="checked" valuePropName="checked"
initialValue={true}
> >
<Switch /> <Switch checkedChildren="启用" unCheckedChildren="禁用"/>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>

View File

@ -1,33 +1,30 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { Page } from '@/types/base/page';
import type { DepartmentResponse, DepartmentRequest, DepartmentQuery } from './types'; import type { DepartmentResponse, DepartmentRequest, DepartmentQuery } from './types';
export const getDepartments = async (params?: DepartmentQuery) => { const BASE_URL = '/api/v1/department';
return request.get('/api/v1/department', {
params,
errorMessage: '获取部门列表失败,请刷新重试'
});
};
export const createDepartment = async (data: DepartmentRequest) => { // 获取部门列表(分页)
return request.post('/api/v1/department', data, { export const getDepartments = (params?: DepartmentQuery) =>
errorMessage: '创建部门失败,请稍后重试' request.get<Page<DepartmentResponse>>(`${BASE_URL}/page`, { params });
});
};
export const updateDepartment = async (id: number, data: DepartmentRequest) => { // 获取部门树
return request.put(`/api/v1/department/${id}`, data, { export const getDepartmentTree = () =>
errorMessage: '更新部门失败,请稍后重试' request.get<DepartmentResponse[]>(`${BASE_URL}/tree`);
});
};
export const deleteDepartment = async (id: number) => { // 创建部门
return request.delete(`/api/v1/department/${id}`, { export const createDepartment = (data: DepartmentRequest) =>
errorMessage: '删除部门失败,请稍后重试' request.post<DepartmentResponse>(BASE_URL, data);
});
}; // 更新部门
export const updateDepartment = (id: number, data: DepartmentRequest) =>
request.put<DepartmentResponse>(`${BASE_URL}/${id}`, data);
// 删除部门
export const deleteDepartment = (id: number) =>
request.delete(`${BASE_URL}/${id}`);
// 获取用户列表(不分页)
export const getUsers = () =>
request.get<UserResponse[]>('/api/v1/user/list');
export const getDepartmentTree = async () => {
return request.get('/api/v1/department/tree', {
errorMessage: '获取部门树失败,请刷新重试'
});
};

View File

@ -1,21 +1,33 @@
export interface DepartmentDTO { import type { BaseResponse } from '@/types/base/response';
id: number;
name: string; export interface DepartmentResponse extends BaseResponse {
code: string; code: string;
name: string;
description?: string; description?: string;
parentId?: number; parentId?: number;
sort: number; sort: number;
enabled: boolean; enabled: boolean;
leaderId?: number; leaderId?: number;
leaderName?: string; leaderName?: string;
version?: number; children?: DepartmentResponse[];
children?: DepartmentDTO[];
} }
export interface TreeSelectNode { export interface DepartmentRequest {
title: string; code: string;
value: number; name: string;
key: number; description?: string;
disabled: boolean; parentId?: number;
children?: TreeSelectNode[]; sort: number;
enabled: boolean;
leaderId?: number;
leaderName?: string;
}
export interface DepartmentQuery {
code?: string;
name?: string;
parentId?: number;
enabled?: boolean;
leaderId?: number;
leaderName?: string;
} }