feat(前端):用户、终端组织树

master
shaot 2025-08-08 18:09:16 +08:00
parent b92552f653
commit c3d3ab36e3
17 changed files with 981 additions and 381 deletions

0
node_modules/.cache/logger/umi.log generated vendored 100644
View File

View File

@ -43,5 +43,11 @@ export default defineConfig({
},
],
npmClient: 'pnpm',
proxy: {
'/api/nex/v1/': {
target: 'http://10.100.51.85:8112',
// changeOrigin: true,
},
},
});

View File

@ -0,0 +1,120 @@
export default {
'POST /api/v1/terminal/query/devicelist': (req: any, res: any) => {
const { page_size, page_num } = req.body;
const data = [];
for (let i = 1; i <= page_size; i++) {
data.push({
id: i,
device_id: i,
device_name: `终端${(page_num - 1) * page_size + i}`,
device_group_id: 1,
device_group_name: '分组名称',
device_type: 1,
ip_addr: '10.10.10.10',
mac_addr: 'fd:12:12:12:12:12',
model: 'model',
description: '描述',
});
}
const result = {
error_code: '0000000000',
message: '操作成功',
data: {
paging: {
total: 520,
total_num: 520,
page_num: page_num,
page_size: page_size,
},
data: data,
},
};
setTimeout(() => {
res.send(result);
}, 500);
},
'POST /api/v1/tree/query': (req: any, res: any) => {
const data = [
{
name: '总分组',
id: 1,
children: [
{
name: '组别1',
id: 2,
},
{
name: '组别2',
id: 3,
children: [
{
name: '子分组1',
id: 4,
},
{
name: '子分组1',
id: 5,
},
],
},
{
name: '分组3',
id: 6,
},
],
},
];
const result = {
error_code: '0000000000',
message: '操作成功',
data: {
data: data,
},
};
setTimeout(() => {
res.send(result);
}, 500);
},
'POST /api/v1/user/query/list': (req: any, res: any) => {
const { page_size, page_num } = req.body;
const data = [];
for (let i = 1; i <= page_size; i++) {
data.push({
user_id: i,
user_group_id: i,
user_group_name: '分组',
user_name: `用户${(page_num - 1) * page_size + i}`,
password: '1111111',
birthday: '2022-01-01',
cell_phone: '12345678901',
email: '1111111111@emial.com',
gender: 1,
identity_no: '111111184736524352622',
priority: 1,
user_type: 1,
status: 1,
description: 'stringNumber',
});
}
const result = {
error_code: '0000000000',
message: '操作成功',
data: {
paging: {
total: 520,
total_num: 520,
page_num: page_num,
page_size: page_size,
},
data: data,
},
};
setTimeout(() => {
res.send(result);
}, 500);
},
};

View File

@ -0,0 +1,44 @@
export const ERROR_CODE = '200';
export const DEVICE_TYPE_MAP = {
1: 'VDI',
3: 'VOI',
} as const;
export const RESTORE_TYPE_MAP = {
1: '全盘还原',
2: '数据盘还原',
3: '定时还原',
0: '不还原',
} as const;
export const USER_TYPE_MAP = {
1: '域用户',
0: '本地用户',
} as const;
export const GENDER_MAP = {
1: '女',
2: '男',
} as const;
// priority
export const PRIORITY_MAP = {
1: '低',
2: '中',
3: '高',
} as const;
export const USER_TYPE_OPTIONS = [
{ value: 1, label: '域用户' },
{ value: 0, label: '本地用户' },
];
export const DEVICE_TYPE_OPTIONS = [
{ value: 1, label: 'VDI' },
{ value: 3, label: 'VOI' },
];
export const GENDER_OPTIONS = [
{ value: 1, label: '女' },
{ value: 2, label: '男' },
];

View File

@ -1,14 +1,61 @@
// // custom-tree.tsx
// import React from 'react';
// import { Tree } from 'antd';
// import type { DataNode, TreeProps } from 'antd/es/tree';
// import type { Key } from 'react';
// interface CustomTreeProps extends Omit<TreeProps, 'treeData' | 'onCheck'> {
// treeData: any[];
// titleField: string;
// keyField: string;
// childrenField?: string;
// onCheck?: (checkedKeys?: Key[], rows?: any[], info?: any) => void;
// }
// const CustomTree: React.FC<CustomTreeProps> = ({
// treeData,
// titleField,
// keyField,
// childrenField = 'children',
// ...restProps
// }) => {
// // Transform the tree data to match Ant Design's expected structure
// const transformTreeData = (data: any[]): DataNode[] => {
// return data.map(item => {
// const node: DataNode = {
// title: item[titleField],
// key: item[keyField],
// ...item
// };
// // Handle children if they exist
// if (item[childrenField] && Array.isArray(item[childrenField])) {
// node.children = transformTreeData(item[childrenField]);
// }
// return node;
// });
// };
// const transformedData = transformTreeData(treeData);
// return <Tree treeData={transformedData} {...restProps} />;
// };
// export default CustomTree;
// custom-tree.tsx
import React from 'react';
import { Tree } from 'antd';
import type { DataNode, TreeProps } from 'antd/es/tree';
import type { Key } from 'react';
interface CustomTreeProps extends Omit<TreeProps, 'treeData'> {
// treeData: User.OrganizationNode[];
interface CustomTreeProps extends Omit<TreeProps, 'treeData' | 'onCheck'> {
treeData: any[];
titleField: string;
keyField: string;
childrenField?: string;
onCheck?: (checkedKeys?: Key[], rows?: any[], info?: any) => void;
}
const CustomTree: React.FC<CustomTreeProps> = ({
@ -16,6 +63,7 @@ const CustomTree: React.FC<CustomTreeProps> = ({
titleField,
keyField,
childrenField = 'children',
onCheck,
...restProps
}) => {
// Transform the tree data to match Ant Design's expected structure
@ -38,7 +86,17 @@ const CustomTree: React.FC<CustomTreeProps> = ({
const transformedData = transformTreeData(treeData);
return <Tree treeData={transformedData} {...restProps} />;
const handleCheck: TreeProps['onCheck'] = (checked, info) => {
let checkedKeys: Key[] = [];
if (Array.isArray(checked)) {
checkedKeys = checked;
} else {
checkedKeys = checked.checked;
}
onCheck?.(checkedKeys, info.checkedNodes, info);
};
return <Tree treeData={transformedData} onCheck={handleCheck} {...restProps} />;
};
export default CustomTree;

View File

@ -1,6 +1,9 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import { DEVICE_TYPE_MAP, ERROR_CODE } from '@/constants/constants';
import CustomTree from '@/pages/components/customTree';
import { deleteUser } from '@/services/userList';
import CreatGroup from '@/pages/userList/mod/group';
import { deleteDevice, getTerminalList } from '@/services/terminal';
import { deleteUserGroup, getGroupTree } from '@/services/userList';
import {
DeleteOutlined,
DownOutlined,
@ -19,20 +22,18 @@ import {
import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react';
import styles from './index.less';
import BindImage from './mod/bindImage';
import BindUserModal from './mod/bindUser';
import EditModal from './mod/eidtDevice';
import CreatGroup from './mod/group';
import BindImage from './mod/bindImage';
const UserListPage: React.FC = () => {
const [orgTreeData, setOrgTreeData] = useState<User.OrganizationNode[]>([]);
const [selectedOrg, setSelectedOrg] = useState<number>();
// 用户列表
const [dataSource, setDataSource] = useState<Termial.TermialItem[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [searchText, setSearchText] = useState<string>('');
const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]);
const [orgSearchText, setOrgSearchText] = useState<string>('');
// 分页参数
const [currentPage, setCurrentPage] = useState<number>(1);
@ -54,83 +55,108 @@ const UserListPage: React.FC = () => {
// 获取用户分组组织树
useEffect(() => {
getGroupList();
// getGroupList();
}, []);
const filterTreeData = (
data: User.OrganizationNode[],
searchTerm: string,
): User.OrganizationNode[] => {
if (!searchTerm) return data;
return data.reduce((acc: User.OrganizationNode[], node) => {
const isMatch = node.name
?.toLowerCase()
.includes(searchTerm.toLowerCase());
if (isMatch) {
// If current node matches, include it
acc.push({ ...node });
} else if (node.children && node.children.length > 0) {
// If current node doesn't match, check children
const filteredChildren = filterTreeData(node.children, searchTerm);
if (filteredChildren.length > 0) {
// If any children match, include current node with filtered children
acc.push({
...node,
children: filteredChildren,
});
}
}
return acc;
}, []);
};
const filteredOrgTreeData = filterTreeData(orgTreeData, orgSearchText);
// 获取用户列表数据
useEffect(() => {
getDataSource();
}, [selectedOrg, currentPage, pageSize]);
// if (selectedOrg) {
// getDataSource();
// }
}, [searchText, selectedOrg, currentPage, pageSize]);
const getGroupList = () => {
const mockOrgData: User.OrganizationNode[] = [
{
name: 'Headquarters',
id: 1,
children: [
{
name: 'HR Department',
id: 2,
},
{
name: 'IT Department',
id: 3,
children: [
{
name: 'Frontend Team',
id: 4,
},
{
name: 'Backend Team',
id: 5,
},
],
},
{
name: 'Finance Department',
id: 6,
},
],
},
];
setSelectedOrg(mockOrgData[0].id as number);
setOrgTreeData(mockOrgData);
};
const getDataSource = () => {
setLoading(true);
setTimeout(() => {
const mockUsers: Termial.TermialItem[] = Array.from(
{ length: pageSize },
(_, index) => ({
id: index + 1,
device_id: 'string',
device_name: 'string',
device_group_id: 1,
device_group_name: 'string',
device_type: 1,
}),
);
setDataSource(mockUsers);
setTotal(100);
setLoading(false);
}, 300);
};
const onDelete = (device?: Termial.TermialItem) => {
const { id } = device || {};
const payload = {
ids: id ? [id] : selectedRowKeys,
const getGroupList = async () => {
const params = {
type: 2,
};
deleteUser(payload as any).then((res) => {
console.log('res=====', res);
const { success } = res || {};
if (success) {
message.success('删除成功');
try {
const result = await getGroupTree(params);
if (result.error_code === ERROR_CODE) {
setOrgTreeData(result.data.data || []);
if (result.data.data.length > 0) {
setSelectedOrg(result.data.data[0].id as number);
}
} else {
message.error(result.message || '获取终端分组失败');
}
} catch (err) {
message.error('获取终端分组失败');
setLoading(false);
}
};
const getDataSource = async () => {
const params: any = {
page_size: pageSize,
page_num: currentPage,
};
if (searchText) {
params.keywords = searchText;
}
setLoading(true);
try {
const result = await getTerminalList(params);
if (result.error_code === ERROR_CODE) {
setDataSource(result.data.data || []);
setTotal(result.data.paging.total || 0);
setLoading(false);
} else {
message.error(result.message || '获取终端列表失败');
setLoading(false);
}
} catch (err) {
message.error('获取终端列表失败');
setLoading(false);
}
};
const onDelete = async (device?: Termial.TermialItem) => {
try {
const { id } = device || {};
const payload = {
ids: id ? [id] : selectedRowKeys,
};
const res = await deleteDevice(payload);
const { error_code } = res || {};
if (error_code === ERROR_CODE) {
message.success('终端删除成功');
setSelectedRowKeys([]);
getDataSource();
}
});
} catch (error) {
message.error('终端删除失败');
}
};
const handleEditUserInfo = (device: Termial.TermialItem) => {
@ -142,42 +168,62 @@ const UserListPage: React.FC = () => {
const columns: ColumnsType<Termial.TermialItem> = [
{
title: '显示名称',
dataIndex: 'username',
key: 'username',
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
align: 'center',
render: (_: any, record: any, index: number) => <span>{index + 1}</span>,
},
{
title: '终端名称',
dataIndex: 'device_name',
key: 'device_name',
width: 220,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '终端分组',
dataIndex: 'device_group_name',
key: 'device_group_name',
width: 220,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: 'IP地址',
dataIndex: 'ip_addr',
key: 'ip_addr',
width: 150,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '终端标识',
dataIndex: 'mac_addr',
key: 'mac_addr',
ellipsis: true,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '终端类型',
dataIndex: 'device_type',
key: 'device_type',
render: (text) => {
return (
<div>
<Tooltip placement="top" title={text}>
{text}
</Tooltip>
</div>
);
width: 150,
align: 'center',
render: (text: number) => {
const key = text as keyof typeof DEVICE_TYPE_MAP;
return <Tooltip>{DEVICE_TYPE_MAP[key] || '--'}</Tooltip>;
},
},
{
@ -262,8 +308,23 @@ const UserListPage: React.FC = () => {
console.log('selectedRowKeys changed: ', newSelectedRowKeys);
setSelectedRowKeys(newSelectedRowKeys as any);
};
const onDeleteGroup = () => {};
const onDeleteGroup = async () => {
if (selectedOrg) {
try {
const params = {
id: selectedOrg,
};
const res = await deleteUserGroup(params);
const { error_code } = res || {};
if (error_code === ERROR_CODE) {
message.success('分组删除成功');
getGroupList();
}
} catch (error) {
message.error('分组删除失败');
}
}
};
return (
<div className={styles.user_content}>
@ -293,18 +354,19 @@ const UserListPage: React.FC = () => {
</Popconfirm>
</div>
<Input.Search
placeholder="请输入"
placeholder="请输入分组名"
style={{ marginBottom: 6 }}
onSearch={(value) => console.log('Search org:', value)}
onSearch={(value) => setOrgSearchText(value)}
onChange={(e) => setOrgSearchText(e.target.value)}
/>
</div>
<div className={styles.tree_box}>
<CustomTree
treeData={orgTreeData}
treeData={filteredOrgTreeData}
titleField="name"
keyField="id"
childrenField="children"
defaultExpandAll
defaultExpandAll={true}
onSelect={onOrgSelect}
selectedKeys={selectedOrg ? [selectedOrg] : []}
icon={<TeamOutlined style={{ fontSize: '15px' }} />}
@ -344,7 +406,7 @@ const UserListPage: React.FC = () => {
<div>
<div>
<Input.Search
placeholder="Search users"
placeholder="终端名称"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
style={{ width: 300 }}
@ -374,6 +436,7 @@ const UserListPage: React.FC = () => {
onShowSizeChange: handlePageSizeChange,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total) => {
return `${total}条数据`;
},
@ -385,6 +448,8 @@ const UserListPage: React.FC = () => {
</div>
<CreatGroup
visible={visible}
type={2}
title="新增终端分组"
onCancel={() => {
setVisible(false);
}}

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import type { PopoverProps } from 'antd';
import { Alert, Button, Popconfirm, Popover, Space } from 'antd';
import { Button, Popconfirm, Popover, Space } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react';
import CustomTable from '../selectedTable/table';
import SelectConponents from './table';
@ -12,7 +13,7 @@ interface SelectedTableProps {
onOk?: (values: any) => void;
orgTreeData?: User.OrganizationNode[];
value?: any;
onChange?: (values: any) => void;
onChange: (values: any) => void;
}
const SelectedTable: React.FC<SelectedTableProps> = (props) => {
@ -20,7 +21,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
placement = 'bottomLeft',
orgTreeData,
value: formValue,
onChange: onBindUserChange,
onChange: onBindImageChange,
} = props;
const [open, setOpen] = useState<boolean>(false);
const [tableData, setTableData] = useState<any[]>([]);
@ -32,16 +33,15 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
const [bindTableKeys, setBindTableKeys] = useState<any[]>([]);
useEffect(() => {
console.log('formValue==========', formValue);
const userList: any = [];
const userId: any = [];
const list: any = [];
const idList: any = [];
if (formValue && formValue.length > 0) {
formValue.forEach((item: any) => {
userList.push(item);
userId.push(item.id);
list.push(item);
idList.push(item.id);
});
setSelectedRowKeys(userId);
setSelectedRows(userList);
setSelectedRowKeys(idList);
setSelectedRows(list);
}
}, [formValue]);
@ -56,30 +56,26 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
// 用户
(selectedRows || []).forEach((item) => {
list.push({
id: item.id,
name: item.name,
type: 1,
id: item.image_id,
name: item.image_name,
});
});
setTableData(list);
onBindImageChange(list);
onHnadleCancel();
};
const onUserTableSelect = (keys: any[], row: any[]) => {
console.log('selectedRowKeys changed: ', keys);
console.log('selectedRowKeys rows: ', row);
setSelectedRowKeys(keys as any);
setSelectedRows(row);
};
const onSelectChange = (keys: any[]) => {
console.log('selectedRowKeys changed: ', keys);
setBindTableKeys(keys as any);
};
const handleDelete = (record?: any) => {
console.log('record=====handleDelete', record);
if (record) {
// 单个删除
const { id } = record || {};
@ -98,12 +94,6 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
const accessPopover = (
<div style={{ width: '600px' }}>
{/* <Alert
message="可切换选择多个不同类型进行绑定"
type="info"
showIcon
closeIcon
/> */}
<SelectConponents
treeData={orgTreeData}
onUserTableSelect={onUserTableSelect}
@ -130,6 +120,33 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
</div>
);
const getColumns = (): ColumnsType<any> => {
const columns: ColumnsType<any> = [
{
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
render: (_, record, index) => <span>{index + 1}</span>,
},
{
title: '镜像名称',
dataIndex: 'image_name',
key: 'image_name',
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Button type="link" danger onClick={() => handleDelete(record)}>
</Button>
),
},
];
return columns;
};
return (
<>
<Space style={{ marginBottom: '10px' }}>
@ -166,6 +183,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
<CustomTable
dataSource={tableData}
onDelete={handleDelete}
columns={getColumns()}
scrollY={400}
rowKey="id"
pagination={false}

View File

@ -1,4 +1,8 @@
import { Input, Table } from 'antd';
/* eslint-disable @typescript-eslint/no-use-before-define */
import { ERROR_CODE,IMAGES_TYPE_MAP } from '@/constants/images.constants';
import { getImagesList } from '@/services/images';
import { Input, Table, message,Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react';
import styles from './index.less';
@ -8,42 +12,69 @@ interface TableProps {
onUserTableSelect?: (keys: any[], row: any[]) => void;
}
interface DataType {
key: React.Key;
name: string;
age: number;
address: string;
}
const TablePage: React.FC<TableProps> = ({
onUserTableSelect,
selectedRowKeys,
}) => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<any[]>([]);
const [searchText, setSearchText] = useState('');
const [searchText, setSearchText] = useState<any>();
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
const [total, setTotal] = useState(0);
// Mock data generation
useEffect(() => {
const mockData: DataType[] = [];
for (let i = 0; i < 100; i++) {
mockData.push({
id: i + 99,
name: `User ${i}`,
});
}
setData(mockData);
setTotal(mockData.length);
}, []);
loadImages();
}, [searchText, currentPage, pageSize]);
const columns: TableProps<DataType>['columns'] = [
const loadImages = async () => {
const params: any = {
page_size: pageSize,
page_num: currentPage,
};
if (searchText) {
params.keywords = searchText;
}
setLoading(true);
try {
const imagesRes = await getImagesList(params);
if (imagesRes.error_code === ERROR_CODE) {
setData(imagesRes.data.data || []);
setTotal(imagesRes.data.paging.total || 0);
setLoading(false);
} else {
message.error(imagesRes.message || '获取镜像列表失败');
setLoading(false);
}
} catch (err) {
message.error('获取镜像列表失败');
setLoading(false);
}
};
const columns: ColumnsType<any> = [
{
title: 'Name',
dataIndex: 'name',
sorter: (a, b) => a.name.localeCompare(b.name),
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
render: (_: any, record: any, index: number) => <span>{index + 1}</span>,
},
{
title: '镜像名称',
dataIndex: 'image_name',
render: (text: any) => <span>{text || ''}</span>,
},
{
title: '桌面类型',
dataIndex: 'image_type',
key: 'image_type',
width: 200,
render: (text: number) => {
const key = text as keyof typeof IMAGES_TYPE_MAP;
return <Tooltip>{IMAGES_TYPE_MAP[key] || '--'}</Tooltip>;
},
},
];
const handleSearch = (value: string) => {
@ -74,10 +105,10 @@ const TablePage: React.FC<TableProps> = ({
</div>
<Table
// rowSelection={rowSelection}
columns={columns}
dataSource={data}
rowKey="id"
loading={loading}
rowSelection={{
selectedRowKeys,
preserveSelectedRowKeys: true,
@ -92,6 +123,7 @@ const TablePage: React.FC<TableProps> = ({
onShowSizeChange: handlePageSizeChange,
showSizeChanger: false,
showQuickJumper: false,
pageSizeOptions: ['10', '20', '50', '100'],
}}
scroll={{ x: 'max-content', y: 300 }}
/>

View File

@ -1,17 +1,11 @@
// index.tsx
import {
Button,
Form,
Input,
message,
Modal,
Radio,
TreeSelect,
} from 'antd';
import { DEVICE_TYPE_OPTIONS } from '@/constants/constants';
import { ERROR_CODE } from '@/constants/images.constants';
import { saveDevice } from '@/services/terminal';
import { Button, Form, Input, message, Modal, Select, TreeSelect } from 'antd';
import React, { useEffect } from 'react';
interface UserEditModalProps {
// visible: boolean;
orgTreeData: User.OrganizationNode[];
onCancel: () => void;
onOk: (values: any) => void;
@ -39,9 +33,15 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
const handleOk = async () => {
try {
const values = await form.validateFields();
onOk(values);
const params = { ...values, user_id };
const res = await saveDevice(params);
const { error_code } = res || {};
if (error_code === ERROR_CODE) {
message.success('保存成功');
}
} catch (error) {
message.error('请检查表单字段');
message.error('保存失败');
}
};
@ -50,7 +50,6 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
types: {
email: '${label} is not a valid email!',
number: '${label} is not a valid number!',
cell_phone: '${label} is not a valid cell phone number!',
},
number: {
range: '${label} must be between ${min} and ${max}',
@ -95,8 +94,8 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
validateMessages={validateMessages}
>
<Form.Item
name="loginName"
label="显示名称"
name="device_name"
label="终端名称"
rules={[{ required: true, message: '请输入显示名称' }]}
>
<Input />
@ -111,7 +110,7 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
</Form.Item>
<Form.Item
name="user_group_id"
name="device_group_id"
label="终端分组"
rules={[{ required: true, message: '请选择终端分组' }]}
>
@ -124,8 +123,15 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
fieldNames={{ label: 'name', value: 'id', children: 'children' }}
/>
</Form.Item>
<Form.Item
name="device_typ"
label="终端类型"
rules={[{ required: false, message: '请输入终端型号' }]}
>
<Select options={DEVICE_TYPE_OPTIONS} />
</Form.Item>
{/* <Form.Item
name="status"
label="认证方式"
rules={[{ required: false, message: '请选择认证方式' }]}
@ -134,7 +140,7 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
<Radio value={1}></Radio>
<Radio value={2}></Radio>
</Radio.Group>
</Form.Item>
</Form.Item> */}
<Form.Item
name="ip_addr"
@ -157,13 +163,7 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
>
<Input />
</Form.Item> */}
<Form.Item
name="model"
label="终端型号"
rules={[{ required: false, message: '请输入终端型号' }]}
>
<Input />
</Form.Item>
<Form.Item
name="description"
label="描述"

View File

@ -1,36 +1,28 @@
import { Button, Form, Input, Modal, TreeSelect } from 'antd';
import React, { useEffect } from 'react';
import { ERROR_CODE } from '@/constants/constants';
import { addUserGroup } from '@/services/userList';
import { Button, Form, Input, Modal, TreeSelect, message } from 'antd';
import React, { useEffect, useState } from 'react';
interface CreatGroupProps {
visible: boolean;
selectedOrg?: number;
onCancel: () => void;
onOk: (values: any) => void;
onOk: (values?: any) => void;
orgTreeData: User.OrganizationNode[];
}
const CreatGroup: React.FC<CreatGroupProps> = (props) => {
const { visible, onCancel, onOk, orgTreeData, selectedOrg } = props;
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
useEffect(() => {
const initialValues = { parent_device_group_id: [selectedOrg] };
form.setFieldsValue(initialValues);
if (selectedOrg) {
const initialValues = { parent_device_group_id: [selectedOrg] };
form.setFieldsValue(initialValues);
}
}, [visible, form, selectedOrg]);
// Flatten tree data for parent group selection
// const flattenTreeData = (data: OrganizationNode[], result: { value: string; label: string }[] = []) => {
// data.forEach(item => {
// result.push({ value: item.id as string, label: item.name as string });
// if (item.children) {
// flattenTreeData(item.children, result);
// }
// });
// return result;
// };
// const parentGroupOptions = flattenTreeData(orgTreeData);
const handleOk = () => {
form.submit();
};
@ -40,14 +32,34 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
onCancel();
};
const handleFinish = (values: any) => {
onOk(values);
form.resetFields();
const handleFinish = async () => {
const values: any = await form.validateFields();
setLoading(true);
try {
console.log('values=====', values);
const { name, parent_id } = values || {};
const params: any = { name, type: 2 };
if (parent_id) {
params.parent_id = parent_id;
}
const res = await addUserGroup(params);
setLoading(false);
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('添加成功');
form.resetFields();
onCancel();
onOk();
}
} catch (error) {
setLoading(false);
message.error('创建用户分组失败');
}
};
return (
<Modal
title="添加用户分组"
title="添加终端分组"
open={visible}
onOk={handleOk}
onCancel={handleCancel}
@ -55,12 +67,14 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
cancelText="取消"
centered={true}
destroyOnHidden={true}
width={600}
footer={
<div style={{ display: 'flex', justifyContent: 'right' }}>
<Button
type="primary"
onClick={handleOk}
style={{ marginRight: '20px' }}
loading={loading}
>
</Button>
@ -80,17 +94,17 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
style={{ paddingTop: '20px', paddingBottom: '20px' }}
>
<Form.Item
name="device_group_name:"
name="name:"
label="分组名"
rules={[{ required: true, message: '请输入分组名!' }]}
>
<Input placeholder="请输入分组名" />
</Form.Item>
<Form.Item name="parent_device_group_id" label="父分组名">
<Form.Item name="parent_id" label="父分组名">
<TreeSelect
showSearch
allowClear={false}
allowClear={true}
treeDefaultExpandAll
placeholder="请选择用户分组"
treeData={orgTreeData}

View File

@ -3,6 +3,7 @@ import CustomTree from '@/pages/components/customTree';
import { TeamOutlined } from '@ant-design/icons';
import type { PopoverProps } from 'antd';
import { Alert, Button, Popconfirm, Popover, Space, Tabs } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react';
import SelectConponents from '../selectConponents';
import CustomTable from './table';
@ -129,6 +130,41 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
}
};
const getColumns = (): ColumnsType<any> => {
const columns: ColumnsType<any> = [
{
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
render: (_, record, index) => <span>{index + 1}</span>,
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
render: (text) => (
<span>{text == 1 ? '用户' : text == 2 ? '用户组' : ''}</span>
),
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Button type="link" danger onClick={() => handleDelete(record)}>
</Button>
),
}
];
return columns;
};
const accessPopover = (
<div style={{ width: '600px' }}>
<Alert
@ -220,6 +256,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
scrollY={400}
rowKey="id"
pagination={false}
columns={getColumns()}
rowSelection={{
bindTableKeys,
onChange: onSelectChange,

View File

@ -11,6 +11,7 @@ interface DeletableTableProps {
rowKey?: string;
rowSelection?: any;
pagination?:any;
columns: ColumnsType<any>;
}
const DeletableTable: React.FC<DeletableTableProps> = (props) => {
@ -23,52 +24,52 @@ const DeletableTable: React.FC<DeletableTableProps> = (props) => {
...restProps
} = props;
console.log("datasource=====",dataSource);
const getColumns = (): ColumnsType<any> => {
const columns: ColumnsType<any> = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
render: (text) => (
<span>{text == 1 ? '用户' : text == 2 ? '用户组' : ''}</span>
),
},
];
// const getColumns = (): ColumnsType<any> => {
// const columns: ColumnsType<any> = [
// {
// title: '名称',
// dataIndex: 'name',
// key: 'name',
// },
// {
// title: '类型',
// dataIndex: 'type',
// key: 'type',
// render: (text) => (
// <span>{text == 1 ? '用户' : text == 2 ? '用户组' : ''}</span>
// ),
// },
// ];
if (isSerial) {
columns.unshift({
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
render: (_, record, index) => <span>{index + 1}</span>,
});
}
// if (isSerial) {
// columns.unshift({
// title: '序号',
// dataIndex: 'order',
// key: 'order',
// width: 80,
// render: (_, record, index) => <span>{index + 1}</span>,
// });
// }
if (isAction) {
columns.push({
title: '操作',
key: 'action',
render: (_, record) => (
<Button type="link" danger onClick={() => onDelete(record)}>
</Button>
),
});
}
// if (isAction) {
// columns.push({
// title: '操作',
// key: 'action',
// render: (_, record) => (
// <Button type="link" danger onClick={() => onDelete(record)}>
// 删除
// </Button>
// ),
// });
// }
return columns;
};
// return columns;
// };
return (
<div>
<Table
columns={getColumns()}
// columns={getColumns()}
dataSource={dataSource}
scroll={{ y: scrollY }}
{...restProps}

View File

@ -1,8 +1,24 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import {
ERROR_CODE,
GENDER_MAP,
PRIORITY_MAP,
USER_TYPE_MAP,
} from '@/constants/constants';
import CustomTree from '@/pages/components/customTree';
import { deleteUser } from '@/services/userList';
import { DeleteOutlined, PlusOutlined, TeamOutlined } from '@ant-design/icons';
import { Button, Input, Popconfirm, Table, message } from 'antd';
import {
deleteUser,
deleteUserGroup,
getGroupTree,
getUserList,
} from '@/services/userList';
import {
DeleteOutlined,
PlusOutlined,
RedoOutlined,
TeamOutlined,
} from '@ant-design/icons';
import { Button, Input, message, Popconfirm, Spin, Table, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react';
import styles from './index.less';
@ -12,18 +28,20 @@ import PasswordResetModal from './mod/passwordEdit';
const UserListPage: React.FC = () => {
const [orgTreeData, setOrgTreeData] = useState<User.OrganizationNode[]>([]);
const [selectedOrg, setSelectedOrg] = useState<number>();
const [selectedOrg, setSelectedOrg] = useState<any>();
// 用户列表
const [users, setUsers] = useState<User.UserItem[]>([]);
const [dataSource, setDataSource] = useState<User.UserItem[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [spinning, setSpinning] = useState<boolean>(false);
const [searchText, setSearchText] = useState<string>('');
const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]);
const [orgSearchText, setOrgSearchText] = useState<string>('');
// 分页参数
const [currentPage, setCurrentPage] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10);
const [totalUsers, setTotalUsers] = useState<number>(0);
const [total, setTotal] = useState<number>(0);
// 编辑用户
const [currentUserInfo, setCurrentUserInfo] = useState<any>({
visible: false,
@ -43,60 +61,96 @@ const UserListPage: React.FC = () => {
// 获取用户列表数据
useEffect(() => {
getUserListInit();
}, [selectedOrg, currentPage, pageSize]);
getDataSource();
}, [searchText,selectedOrg, currentPage, pageSize]);
const getUserGroupList = () => {
const mockOrgData: User.OrganizationNode[] = [
{
name: 'Headquarters',
id: 1,
children: [
{
name: 'HR Department',
id: 2,
},
{
name: 'IT Department',
id: 3,
children: [
{
name: 'Frontend Team',
id: 4,
},
{
name: 'Backend Team',
id: 5,
},
],
},
{
name: 'Finance Department',
id: 6,
},
],
},
];
setSelectedOrg(mockOrgData[0].id as number);
setOrgTreeData(mockOrgData);
const getUserGroupList = async () => {
const params = {
type: 1,
};
setSpinning(true);
try {
const result = await getGroupTree(params);
console.log('result=====', result);
const { code, data = [] } = result || {};
setSpinning(false);
if (code === ERROR_CODE) {
if (data.length > 0) {
const { children = [] } = data[0] || {};
setOrgTreeData(children);
// if (children.length > 0) {
// setSelectedOrg(children[0].id);
// }
}
} else {
message.error(result.message || '获取用户分组失败');
}
} catch (err) {
message.error('获取用户分组失败');
setSpinning(false);
}
};
const getUserListInit = () => {
setLoading(true);
setTimeout(() => {
const startIndex = (currentPage - 1) * pageSize;
const mockUsers: User.UserItem[] = Array.from(
{ length: pageSize },
(_, index) => ({
user_id: startIndex + index + 1,
user_name: 'string',
}),
);
const filterTreeData = (
data: User.OrganizationNode[],
searchTerm: string,
): User.OrganizationNode[] => {
if (!searchTerm) return data;
setUsers(mockUsers);
setTotalUsers(100);
return data.reduce((acc: User.OrganizationNode[], node) => {
const isMatch = node.name
?.toLowerCase()
.includes(searchTerm.toLowerCase());
if (isMatch) {
// If current node matches, include it
acc.push({ ...node });
} else if (node.children && node.children.length > 0) {
// If current node doesn't match, check children
const filteredChildren = filterTreeData(node.children, searchTerm);
if (filteredChildren.length > 0) {
// If any children match, include current node with filtered children
acc.push({
...node,
children: filteredChildren,
});
}
}
return acc;
}, []);
};
const filteredOrgTreeData = filterTreeData(orgTreeData, orgSearchText);
const getDataSource = async () => {
const params: any = {
page_size: pageSize,
page_num: currentPage,
};
if (selectedOrg) {
params.user_group_id = selectedOrg;
}
if (searchText) {
params.user_name = searchText;
}
setLoading(true);
try {
const result = await getUserList(params);
console.log('res======', result);
const { code, data = {} } = result || {};
const { data: list, total = 0 } = data || {};
if (code === ERROR_CODE) {
setDataSource(list || []);
setTotal(total);
setLoading(false);
} else {
message.error(result.message || '获取终端列表失败');
setLoading(false);
}
} catch (err) {
message.error('获取终端列表失败');
setLoading(false);
}, 300);
}
};
const onDelete = (user?: User.UserItem) => {
@ -110,7 +164,7 @@ const UserListPage: React.FC = () => {
if (success) {
message.success('删除成功');
setSelectedRowKeys([]);
getUserListInit();
getDataSource();
}
});
};
@ -137,34 +191,105 @@ const UserListPage: React.FC = () => {
const columns: ColumnsType<User.UserItem> = [
{
title: '登录名',
dataIndex: 'user_id',
key: 'user_id',
title: '序号',
dataIndex: 'order',
key: 'order',
width: 80,
align: 'center',
render: (_: any, record: any, index: number) => <span>{index + 1}</span>,
},
{
title: '用户姓名',
title: '用户名',
dataIndex: 'user_name',
key: 'user_name',
width: 150,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '用户分组',
dataIndex: 'userGroup',
key: 'userGroup',
},
{
title: '性别',
dataIndex: 'sex',
key: 'sex',
dataIndex: 'user_group_name',
key: 'user_group_name',
width: 150,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 150,
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '用户类别',
dataIndex: 'userType',
key: 'userType',
dataIndex: 'user_type',
key: 'user_type',
width: 150,
align: 'center',
render: (text: number) => {
const key = text as keyof typeof USER_TYPE_MAP;
return <Tooltip>{USER_TYPE_MAP[key] || '--'}</Tooltip>;
},
},
{
title: '优先级',
dataIndex: 'priority',
key: 'priority',
width: 150,
align: 'center',
render: (text: number) => {
const key = text as keyof typeof PRIORITY_MAP;
return <Tooltip>{PRIORITY_MAP[key] || '--'}</Tooltip>;
},
},
{
title: '性别',
dataIndex: 'gender',
key: 'gender',
width: 150,
align: 'center',
render: (text: number) => {
const key = text as keyof typeof GENDER_MAP;
return <Tooltip>{GENDER_MAP[key] || '--'}</Tooltip>;
},
},
{
title: '电话',
dataIndex: 'cell_phone',
key: 'cell_phone',
width: 150,
align: 'center',
render: (text:any) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '出生日期',
dataIndex: 'birthday',
key: 'birthday',
width: 150,
align: 'center',
render: (text:any) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '身份证号',
dataIndex: 'identity_no',
key: 'identity_no',
width: 150,
align: 'center',
render: (text:any) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
},
{
title: '操作',
@ -197,7 +322,7 @@ const UserListPage: React.FC = () => {
const onOrgSelect = (selectedKeys: React.Key[]) => {
if (selectedKeys.length > 0) {
setSelectedOrg(selectedKeys[0] as number);
setSelectedOrg(selectedKeys[0]);
setCurrentPage(1);
}
};
@ -217,18 +342,47 @@ const UserListPage: React.FC = () => {
setSelectedRowKeys(newSelectedRowKeys as any);
};
const onDeleteGroup = () => {};
const onSaveGroup = () => {
getUserGroupList();
};
const onDeleteGroup = async () => {
if (selectedOrg) {
try {
const params = {
id: selectedOrg,
};
const res = await deleteUserGroup(params);
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('分组删除成功');
setSelectedOrg(null);
getUserGroupList();
}
} catch (error) {
message.error('分组删除失败');
}
}
};
return (
<div className={styles.user_content}>
<div className={styles.left_content}>
<div className={styles.search}>
<div style={{ paddingBottom: '5px' }}>
<Button
type="text"
style={{ marginRight: '8px', fontSize: '16px' }}
icon={<RedoOutlined />}
onClick={() => getUserGroupList()}
title="刷新"
/>
<Button
type="text"
style={{ marginRight: '8px', fontSize: '16px' }}
icon={<PlusOutlined />}
onClick={() => setVisible(true)}
title="新增"
/>
<Popconfirm
title=""
@ -243,26 +397,31 @@ const UserListPage: React.FC = () => {
type="text"
style={{ fontSize: '16px' }}
icon={<DeleteOutlined />}
title="删除"
disabled={!selectedOrg}
/>
</Popconfirm>
</div>
<Input.Search
placeholder="请输入名称"
style={{ marginBottom: 6 }}
onSearch={(value) => console.log('Search org:', value)}
onSearch={(value) => setOrgSearchText(value)}
onChange={(e) => setOrgSearchText(e.target.value)}
/>
</div>
<div className={styles.tree_box}>
<CustomTree
treeData={orgTreeData}
titleField="name"
keyField="id"
childrenField="children"
defaultExpandAll
onSelect={onOrgSelect}
selectedKeys={selectedOrg ? [selectedOrg] : []}
icon={<TeamOutlined style={{ fontSize: '15px' }} />}
/>
<Spin spinning={spinning} delay={100}>
<CustomTree
treeData={filteredOrgTreeData}
titleField="name"
keyField="id"
childrenField="children"
defaultExpandAll
onSelect={onOrgSelect}
selectedKeys={selectedOrg ? [selectedOrg] : []}
icon={<TeamOutlined style={{ fontSize: '15px' }} />}
/>
</Spin>
</div>
</div>
<div className={styles.right_content}>
@ -297,14 +456,14 @@ const UserListPage: React.FC = () => {
</Button>
</Popconfirm>
<Button style={{ marginRight: '8px' }} onClick={getUserListInit}>
<Button style={{ marginRight: '8px' }} onClick={getDataSource}>
</Button>
</div>
<div>
<div>
<Input.Search
placeholder="Search users"
placeholder="用户名"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
style={{ width: 300 }}
@ -319,17 +478,18 @@ const UserListPage: React.FC = () => {
<div className={styles.teble_box}>
<Table
columns={columns}
dataSource={users}
dataSource={dataSource}
loading={loading}
rowKey="id"
pagination={{
current: currentPage,
pageSize: pageSize,
total: totalUsers,
total: total,
onChange: handlePageChange,
onShowSizeChange: handlePageSizeChange,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '50', '100'],
showTotal: (total) => {
return `${total}条数据`;
},
@ -345,11 +505,13 @@ const UserListPage: React.FC = () => {
</div>
<CreatGroup
visible={visible}
type={1}
title="新增用户分组"
selectedOrg={selectedOrg}
onCancel={() => {
setVisible(false);
}}
onOk={() => {}}
onOk={onSaveGroup}
orgTreeData={orgTreeData}
/>
<UserEditModal

View File

@ -1,4 +1,6 @@
// index.tsx
import { GENDER_OPTIONS, USER_TYPE_OPTIONS } from '@/constants/constants';
import { idIDIntl } from '@ant-design/pro-components';
import {
Button,
Form,
@ -10,7 +12,6 @@ import {
TreeSelect,
} from 'antd';
import React, { useEffect } from 'react';
const { Option } = Select;
interface UserEditModalProps {
// visible: boolean;
@ -30,13 +31,13 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
currentUserInfo,
}) => {
const { recordData, visible, selectedOrg } = currentUserInfo || {};
const { user_id } = recordData || {};
const { id } = recordData || {};
const [form] = Form.useForm();
useEffect(() => {
const initialValues = { user_group_id: [selectedOrg], status: 1 };
form.setFieldsValue(initialValues);
}, [visible, form,recordData, selectedOrg]);
}, [visible, form, recordData, selectedOrg]);
const handleOk = async () => {
try {
@ -61,7 +62,7 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
return (
<Modal
title={user_id ? '编辑用户信息' : '新增用户'}
title={id ? '编辑用户信息' : '新增用户'}
open={visible}
onCancel={onCancel}
onOk={handleOk}
@ -142,22 +143,24 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
</Form.Item>
<Form.Item
name="userType"
name="user_type"
label="用户类别"
rules={[{ required: true, message: '请选择用户类别' }]}
rules={[{ required: false, message: '请选择用户类别' }]}
>
<Select placeholder="请选择用户类别">
<Option value="internal"></Option>
<Option value="external"></Option>
<Option value="partner"></Option>
<Option value="temporary"></Option>
</Select>
<Select placeholder="请选择用户类别" options={USER_TYPE_OPTIONS} />
</Form.Item>
<Form.Item
name="idCard"
name="gender"
label="性别"
rules={[{ required: false, message: '请选择性别' }]}
>
<Select placeholder="请选择性别" options={GENDER_OPTIONS} />
</Form.Item>
<Form.Item
name="identity_no"
label="身份证号"
rules={[
{ required: true, message: '请输入身份证号' },
{ required: false, message: '请输入身份证号' },
{
pattern:
/^[1-9]\d{5}(18|19|20|21|22|23|24|25|26|27|28|29|30|31)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20)$/,
@ -168,18 +171,6 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
<Input placeholder="请输入身份证号" />
</Form.Item>
<Form.Item
name="gender"
label="性别"
rules={[{ required: false, message: '请选择性别' }]}
>
<Select placeholder="请选择性别">
<Option value="male"></Option>
<Option value="female"></Option>
<Option value="other"></Option>
<Option value="prefer-not-to-say"></Option>
</Select>
</Form.Item>
{/* 电话号码 */}
<Form.Item
name="cell_phone"

View File

@ -1,37 +1,39 @@
// d:\VDI\vdi\web-fe\src\pages\userList\mod\group\index.tsx
import { Button, Form, Input, Modal, TreeSelect } from 'antd';
import React, { useEffect } from 'react';
import { ERROR_CODE } from '@/constants/constants';
import { addUserGroup } from '@/services/userList';
import { Button, Form, Input, Modal, TreeSelect, message } from 'antd';
import React, { useEffect, useState } from 'react';
interface CreatGroupProps {
title: string;
type:number;
visible: boolean;
selectedOrg?: number;
onCancel: () => void;
onOk: (values: any) => void;
onOk: (values?: any) => void;
orgTreeData: User.OrganizationNode[];
}
const CreatGroup: React.FC<CreatGroupProps> = (props) => {
const { visible, onCancel, onOk, orgTreeData, selectedOrg } = props;
const {
title = '新增分组',
visible,
onCancel,
onOk,
orgTreeData,
selectedOrg,
type
} = props;
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
useEffect(() => {
const initialValues = { parent_id: [selectedOrg] };
form.setFieldsValue(initialValues);
if (selectedOrg) {
const initialValues = { parent_id: [selectedOrg] };
form.setFieldsValue(initialValues);
}
}, [visible, form, selectedOrg]);
// Flatten tree data for parent group selection
// const flattenTreeData = (data: OrganizationNode[], result: { value: string; label: string }[] = []) => {
// data.forEach(item => {
// result.push({ value: item.id as string, label: item.name as string });
// if (item.children) {
// flattenTreeData(item.children, result);
// }
// });
// return result;
// };
// const parentGroupOptions = flattenTreeData(orgTreeData);
const handleOk = () => {
form.submit();
};
@ -41,14 +43,34 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
onCancel();
};
const handleFinish = (values: any) => {
onOk(values);
form.resetFields();
const handleFinish = async () => {
const values: any = await form.validateFields();
setLoading(true);
try {
console.log('values=====', values);
const { name, parent_id } = values || {};
const params: any = { name, type: type };
if (parent_id) {
params.parent_id = parent_id;
}
const res = await addUserGroup(params);
setLoading(false);
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('添加成功');
form.resetFields();
onCancel();
onOk();
}
} catch (error) {
setLoading(false);
message.error('创建用户分组失败');
}
};
return (
<Modal
title="添加用户分组"
title={title}
open={visible}
onOk={handleOk}
onCancel={handleCancel}
@ -56,12 +78,14 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
cancelText="取消"
centered={true}
destroyOnHidden={true}
width={600}
footer={
<div style={{ display: 'flex', justifyContent: 'right' }}>
<Button
type="primary"
onClick={handleOk}
style={{ marginRight: '20px' }}
loading={loading}
>
</Button>
@ -81,7 +105,7 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
style={{ paddingTop: '20px', paddingBottom: '20px' }}
>
<Form.Item
name="groupName"
name="name"
label="分组名"
rules={[{ required: true, message: '请输入分组名!' }]}
>
@ -91,7 +115,7 @@ const CreatGroup: React.FC<CreatGroupProps> = (props) => {
<Form.Item name="parent_id" label="父分组名">
<TreeSelect
showSearch
allowClear={false}
allowClear={true}
treeDefaultExpandAll
placeholder="请选择父分组名"
treeData={orgTreeData}

View File

@ -0,0 +1,28 @@
import { request } from '@umijs/max';
const BASE_URL = '/api/v1/terminal';
// 根据终端序列号查询镜像列表
export async function getTerminalList(params:any) {
// console.log('终端列表 params', params);
return request<Termial.Termial_ListInfo>(`${BASE_URL}/query/devicelist`, {
method: 'POST',
data: params,
});
}
export async function deleteDevice(params:any) {
// console.log('终端列表 params', params);
return request<Termial.Termial_ListInfo>(`${BASE_URL}/delete/device`, {
method: 'POST',
data: params,
});
}
export async function saveDevice(params:any) {
// console.log('终端列表 params', params);
return request<Termial.Termial_ListInfo>(`${BASE_URL}/save/device`, {
method: 'POST',
data: params,
});
}

View File

@ -1,15 +1,15 @@
import { request } from '@umijs/max';
// 新建用户分组
const BASE_URL = '/api/nex/v1';
export async function addUserGroup(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/device/group/add`, {
method: 'POST',
data: body,
});
}
// 获取用户分组
export async function getUserGroup(body?: any) {
return request<any>('/api/v1/user', {
export async function getGroupTree(body?: any) {
return request<any>(`${BASE_URL}/user/device/group/query`, {
method: 'POST',
data: body,
});
@ -17,15 +17,15 @@ export async function getUserGroup(body?: any) {
// 删除用户分组
export async function deleteUserGroup(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/device/group/delete`, {
method: 'POST',
data: body,
});
}
// 查询用户分组
// 查询用户
export async function getUserList(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/select/page`, {
method: 'POST',
data: body,
});
@ -33,7 +33,7 @@ export async function getUserList(body?: any) {
// 新建用户
export async function addUser(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/add`, {
method: 'POST',
data: body,
});
@ -41,7 +41,7 @@ export async function addUser(body?: any) {
// 编辑用户
export async function editUser(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/update`, {
method: 'POST',
data: body,
});
@ -49,7 +49,7 @@ export async function editUser(body?: any) {
//获取某个用户信息
export async function getUserById(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/query`, {
method: 'POST',
data: body,
});
@ -57,7 +57,7 @@ export async function getUserById(body?: any) {
//删除用户
export async function deleteUser(body?: any) {
return request<any>('/api/v1/user', {
return request<any>(`${BASE_URL}/user/delete`, {
method: 'DELETE',
data: body,
});