feat(前端):终端绑定镜像、用户

master
shaot 2025-08-12 15:07:41 +08:00
parent a1dbc6a736
commit 7e9be02d61
7 changed files with 198 additions and 131 deletions

View File

@ -23,7 +23,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [tableData, setTableData] = useState<any[]>([]); const [tableData, setTableData] = useState<any[]>([]);
//镜像选择 //镜像选择
const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<any[]>([]);
const [selectedRows, setSelectedRows] = useState<any[]>([]); const [selectedRows, setSelectedRows] = useState<any[]>([]);
// 已绑定用户列表选择 // 已绑定用户列表选择
@ -37,6 +37,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
list.push(item); list.push(item);
idList.push(item.id); idList.push(item.id);
}); });
setTableData(formValue);
setSelectedRowKeys(idList); setSelectedRowKeys(idList);
setSelectedRows(list); setSelectedRows(list);
} }
@ -53,7 +54,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
// 镜像 // 镜像
(selectedRows || []).forEach((item) => { (selectedRows || []).forEach((item) => {
list.push({ list.push({
record_id: item.id, id: item.id,
image_name: item.image_name, image_name: item.image_name,
}); });
}); });
@ -74,17 +75,27 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
const handleDelete = (record?: any) => { const handleDelete = (record?: any) => {
if (record) { if (record) {
// 单个删除 // 单个删除
const { record_id } = record || {}; const { id } = record || {};
const newData = tableData.filter((item) => item.record_id !== record_id); const newData = tableData.filter((item) => item.id !== id);
const ids: any[] = [];
newData.map((item) => ids.push(item.id));
setTableData(newData); setTableData(newData);
setSelectedRowKeys(ids);
setSelectedRows(newData);
onBindImageChange(newData);
setBindTableKeys([]);
} else { } else {
// 批量删除 // 批量删除
const newData = tableData.filter( const newData = tableData.filter(
(item) => !bindTableKeys.includes(item.record_id), (item) => !bindTableKeys.includes(item.id),
); );
const ids: any[] = [];
newData.map((item) => ids.push(item.id));
setTableData(newData); setTableData(newData);
setSelectedRowKeys([]); setBindTableKeys([]);
setSelectedRows([]); setSelectedRowKeys(ids);
setSelectedRows(newData);
onBindImageChange(newData);
} }
}; };
@ -136,10 +147,20 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
title: '操作', title: '操作',
key: 'action', key: 'action',
width: 150, width: 150,
align: 'center',
render: (_, record) => ( render: (_, record) => (
<Button type="link" onClick={() => handleDelete(record)}> <Popconfirm
title=""
</Button> placement="bottom"
description="请确认是否删除?"
onConfirm={() => handleDelete(record)}
okText="删除"
cancelText="取消"
>
<Button type="link">
</Button>
</Popconfirm>
), ),
}, },
]; ];
@ -154,37 +175,37 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
title="" title=""
trigger="click" trigger="click"
open={open} open={open}
// onOpenChange={setOpen} onOpenChange={setOpen}
placement={placement} placement={placement}
> >
<div> <Button
<Button // onClick={() => {
onClick={() => { // setOpen(true);
setOpen(true); // }}
}} // style={{ marginRight: '15px' }}
style={{ marginRight: '15px' }} >
>
</Button>
</Button>
<Popconfirm
title=""
description="删除操作不可逆,请确认是否删除?"
onConfirm={() => handleDelete()}
disabled={bindTableKeys.length === 0}
okText="删除"
cancelText="取消"
>
<Button disabled={bindTableKeys.length === 0}></Button>
</Popconfirm>
</div>
</Popover> </Popover>
<Popconfirm
title=""
placement="bottomRight"
description="请确认是否删除?"
onConfirm={() => handleDelete()}
disabled={bindTableKeys.length === 0}
okText="删除"
cancelText="取消"
>
<Button disabled={bindTableKeys.length === 0}></Button>
</Popconfirm>
</Space> </Space>
<CustomTable <CustomTable
dataSource={tableData} dataSource={tableData}
onDelete={handleDelete} onDelete={handleDelete}
columns={getColumns()} columns={getColumns()}
scrollY={400} scrollY={400}
rowKey="record_id" rowKey="id"
pagination={false} pagination={false}
rowSelection={{ rowSelection={{
bindTableKeys, bindTableKeys,

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable @typescript-eslint/no-use-before-define */
import { ERROR_CODE } from '@/constants/constants'; import { ERROR_CODE } from '@/constants/constants';
import { getImageList } from '@/services/terminal'; import { getImageList } from '@/services/terminal';
import { Input, message, Table } from 'antd'; import { Input, message, Table, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import styles from './index.less'; import styles from './index.less';
@ -65,7 +65,9 @@ const TablePage: React.FC<TableProps> = ({
{ {
title: '镜像名称', title: '镜像名称',
dataIndex: 'image_name', dataIndex: 'image_name',
render: (text: any) => <span>{text || ''}</span>, render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
}, },
]; ];
@ -92,7 +94,7 @@ const TablePage: React.FC<TableProps> = ({
onSearch={handleSearch} onSearch={handleSearch}
enterButton enterButton
allowClear allowClear
style={{ width: '200px', marginLeft: '10px' }} style={{ width: '300px', marginLeft: '10px' }}
/> />
</div> </div>
@ -102,7 +104,7 @@ const TablePage: React.FC<TableProps> = ({
rowKey="id" rowKey="id"
loading={loading} loading={loading}
rowSelection={{ rowSelection={{
selectedRowKeys, selectedRowKeys:selectedRowKeys,
preserveSelectedRowKeys: true, preserveSelectedRowKeys: true,
onChange: onUserTableSelect, onChange: onUserTableSelect,
}} }}

View File

@ -2,16 +2,15 @@
import { ERROR_CODE } from '@/constants/constants'; import { ERROR_CODE } from '@/constants/constants';
import { getBindImageAdd, getBindImageList } from '@/services/terminal'; import { getBindImageAdd, getBindImageList } from '@/services/terminal';
import { Button, Form, message, Modal } from 'antd'; import { Button, Form, message, Modal } from 'antd';
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import SelectedTable from '../ImageSelectedTable/index'; import SelectedTable from '../ImageSelectedTable/index';
import styles from './index.less'; import styles from './index.less';
interface UserEditModalProps { interface UserEditModalProps {
onCancel: () => void; onCancel: () => void;
onOk: (values: any) => void; onOk: (values?: any) => void;
confirmLoading?: boolean; confirmLoading?: boolean;
dataDetial?: Termial.ModalBaseNode; dataDetial?: Termial.ModalBaseNode;
selectedOrg?: number;
} }
const BindUserModal: React.FC<UserEditModalProps> = ({ const BindUserModal: React.FC<UserEditModalProps> = ({
@ -20,9 +19,10 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
confirmLoading = false, confirmLoading = false,
dataDetial, dataDetial,
}) => { }) => {
const { recordData, visible, selectedOrg } = dataDetial || {}; const { recordData, visible } = dataDetial || {};
const { device_id } = recordData || {}; const { device_id } = recordData || {};
const [form] = Form.useForm(); const [form] = Form.useForm();
const [dataSource, setDataSource] = useState<any[]>([]);
useEffect(() => { useEffect(() => {
const params = { device_id: device_id }; const params = { device_id: device_id };
@ -30,44 +30,61 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
const { code, data = [] } = res || {}; const { code, data = [] } = res || {};
if (code === ERROR_CODE) { if (code === ERROR_CODE) {
if (data && data.length) { if (data && data.length) {
const initialValues = { image_list: data }; const imageList: any[] = [];
data.forEach((item: any) => {
imageList.push({
id: item.image_id,
image_name: item.image_name,
// current_id: item.id,
});
});
setDataSource(data);
const initialValues = { image_list: imageList };
form.setFieldsValue(initialValues); form.setFieldsValue(initialValues);
} }
} }
}); });
}, [visible, form, recordData, selectedOrg]); }, [visible, form, recordData]);
const onBind = (payload: any) => {
getBindImageAdd(payload).then((res: any) => {
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('绑定成功');
if (onOk) onOk();
} else {
message.error('绑定失败');
}
});
};
const handleOk = async () => { const handleOk = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
const { image_list } = values || {}; const { image_list } = values || {};
console.log('image_list=====', image_list);
if (image_list && image_list.length > 0) { if (image_list && image_list.length > 0) {
const list: any[] = []; const list: any[] = [];
image_list.forEach((item: any) => { image_list.forEach((item: any) => {
list.push({ const obj: any = {
id: item.id,
device_id: device_id, device_id: device_id,
image_id: item.record_id, image_id: item.id,
}); };
const newData = dataSource.filter(
(record) => record.image_id === item.id,
);
if (newData && newData.length === 1) {
obj.id = newData[0].id;
}
list.push({ ...obj });
}); });
const payload: any = { const payload: any = {
data: list, data: list,
}; };
onBind(payload);
getBindImageAdd(payload).then((res: any) => {
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('绑定成功');
onOk();
} else {
message.error('绑定失败');
}
});
} else { } else {
message.info('请先选择绑定的镜像'); message.info('请先选择绑定的镜像');
} }
console.log('values=====', values);
// onOk(values);
} catch (error) { } catch (error) {
message.error('请检查表单字段'); message.error('请检查表单字段');
} }

View File

@ -24,8 +24,10 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
dataDetial, dataDetial,
}) => { }) => {
const { recordData, visible } = dataDetial || {}; const { recordData, visible } = dataDetial || {};
const { device_id,device_group_id } = recordData || {}; const { device_id, device_group_id } = recordData || {};
const [orgTreeData, setOrgTreeData] = useState<User.OrganizationNode[]>([]); const [orgTreeData, setOrgTreeData] = useState<User.OrganizationNode[]>([]);
const [userDataSource, setUserDataSource] = useState<any[]>([]);
// const [groupDataSource, setGroupDataSource] = useState<any[]>([]);
const [form] = Form.useForm(); const [form] = Form.useForm();
const getGroupList = async () => { const getGroupList = async () => {
@ -57,7 +59,6 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
if (!device_id) return; if (!device_id) return;
const payload = { device_id: device_id }; const payload = { device_id: device_id };
getBindUserList(payload).then((res: any) => { getBindUserList(payload).then((res: any) => {
console.log('res========', res);
const { code, data = [] } = res || {}; const { code, data = [] } = res || {};
if (code === ERROR_CODE) { if (code === ERROR_CODE) {
if (data && data.length > 0) { if (data && data.length > 0) {
@ -65,23 +66,47 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
data.map((item: any) => { data.map((item: any) => {
const { type, user_id, user_name, user_group_id, user_group_name } = const { type, user_id, user_name, user_group_id, user_group_name } =
item || {}; item || {};
const obj: any = { ...item };
delete obj.id;
if (type === 1) { if (type === 1) {
list.push({ record_id: user_id, name: user_name, ...item }); list.push({
table_id: `user_${user_id}`,
id: user_id,
name: user_name,
user_name: user_name,
type: type,
});
} else { } else {
list.push({ list.push({
record_id: user_group_id, table_id: `group_${user_group_id}`,
id: user_group_id,
name: user_group_name, name: user_group_name,
...item, type: type,
}); });
} }
}); });
const initialValues = { user_list: list, status: 1 }; setUserDataSource(data);
const initialValues = { user_list: list };
form.setFieldsValue(initialValues); form.setFieldsValue(initialValues);
} }
} }
}); });
}, [visible, form, recordData]); }, [visible, form, recordData]);
const onBind = (payload: any) => {
bindUserAdd(payload)
.then((res: any) => {
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('绑定成功');
onOk();
} else {
message.error('绑定失败');
}
})
.catch(() => {});
};
const handleOk = async () => { const handleOk = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
@ -89,41 +114,43 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
const list: any[] = []; const list: any[] = [];
if (user_list && user_list.length > 0) { if (user_list && user_list.length > 0) {
user_list.forEach((item: any) => { user_list.forEach((item: any) => {
const { record_id, type, id } = item || {}; const { type, id } = item || {};
if (type === 1) { if (type === 1) {
// 用户 // 用户
list.push({ const obj: any = {
user_id: record_id,
id: id,
device_id, device_id,
device_group_id, device_group_id,
type: type, type: type,
}); user_id: id,
};
const newData = userDataSource.filter(
(record) => record.user_id === item.id && record.type === 1,
);
if (newData && newData.length === 1) {
obj.id = newData[0].id;
}
list.push(obj);
} else { } else {
//用户分组 //用户分组
list.push({ const obj: any = {
user_group_id: record_id, device_id,
id: id,
device_id: device_id,
device_group_id, device_group_id,
type: type, type: type,
}); user_group_id: id,
};
const newData = userDataSource.filter(
(record) => record.user_group_id === item.id && record.type === 2,
);
if (newData && newData.length === 1) {
obj.id = newData[0].id;
}
list.push(obj);
} }
}); });
const payload = { const payload = {
data: list, data: list,
}; };
bindUserAdd(payload) onBind(payload);
.then((res: any) => {
const { code } = res || {};
if (code === ERROR_CODE) {
message.success('绑定成功');
onOk();
} else {
message.error('绑定失败');
}
})
.catch(() => {});
} }
} catch (error) { } catch (error) {
message.error('请检查表单字段'); message.error('请检查表单字段');

View File

@ -134,7 +134,7 @@ const UserEditModal: React.FC<UserEditModalProps> = ({
<Form.Item <Form.Item
name="device_type" name="device_type"
label="终端类型" label="终端类型"
rules={[{ required: true, message: '请输入终端型号' }]} rules={[{ required: false, message: '请输入终端型号' }]}
> >
<Select options={DEVICE_TYPE_OPTIONS} /> <Select options={DEVICE_TYPE_OPTIONS} />
</Form.Item> </Form.Item>

View File

@ -2,7 +2,7 @@
import CustomTree from '@/pages/components/customTree'; import CustomTree from '@/pages/components/customTree';
import { TeamOutlined } from '@ant-design/icons'; import { TeamOutlined } from '@ant-design/icons';
import type { PopoverProps } from 'antd'; import type { PopoverProps } from 'antd';
import { Alert, Button, Popconfirm, Popover, Space, Tabs } from 'antd'; import { Alert, Button, Popconfirm, Popover, Space, Tabs,Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import SelectConponents from '../selectConponents'; import SelectConponents from '../selectConponents';
@ -38,7 +38,6 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
const [bindTableKeys, setBindTableKeys] = useState<any[]>([]); const [bindTableKeys, setBindTableKeys] = useState<any[]>([]);
useEffect(() => { useEffect(() => {
console.log('formValue==========', formValue);
const userList: any = []; const userList: any = [];
const groupList: any = []; const groupList: any = [];
const userId: any = []; const userId: any = [];
@ -47,10 +46,10 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
formValue.forEach((item: any) => { formValue.forEach((item: any) => {
if (item.type === 1) { if (item.type === 1) {
userList.push(item); userList.push(item);
userId.push(item.record_id); userId.push(item.id);
} else { } else {
groupList.push(item); groupList.push(item);
groupId.push(item.record_id); groupId.push(item.id);
} }
}); });
setTableData(formValue); setTableData(formValue);
@ -61,11 +60,7 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
} }
}, [formValue]); }, [formValue]);
// const onOrgSelect = (selectedKeys: React.Key[], info: any) => {};
const onCheckChange = (keys: any[], info: any) => { const onCheckChange = (keys: any[], info: any) => {
// const { checkedNodes } = info;
console.log('info=========', info);
setCheckedKeys(keys); setCheckedKeys(keys);
setCheckedRow(info); setCheckedRow(info);
}; };
@ -83,17 +78,19 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
// 用户 // 用户
(selectedRows || []).forEach((item) => { (selectedRows || []).forEach((item) => {
list.push({ list.push({
record_id: item.id, id: item.id,
name: item.user_name, name: item.user_name,
type: 1, type: 1,
table_id: `user_${item.id}`,
}); });
}); });
// 用户组 // 用户组
(checkedRow || []).forEach((item) => { (checkedRow || []).forEach((item) => {
list.push({ list.push({
record_id: item.id, id: item.id,
name: item.name, name: item.name,
type: 2, type: 2,
table_id: `group_${item.id}`,
}); });
}); });
setTableData(list); setTableData(list);
@ -102,34 +99,30 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
}; };
const onUserTableSelect = (keys: any[], row: any[]) => { const onUserTableSelect = (keys: any[], row: any[]) => {
console.log('selectedRowKeys changed: ', keys);
console.log('selectedRowKeys rows: ', row);
setSelectedRowKeys(keys as any); setSelectedRowKeys(keys as any);
setSelectedRows(row); setSelectedRows(row);
}; };
const onSelectChange = (keys: any[]) => { const onSelectChange = (keys: any[]) => {
console.log('selectedRowKeys changed: ', keys);
setBindTableKeys(keys as any); setBindTableKeys(keys as any);
}; };
const handleDelete = (record?: any) => { const handleDelete = (record?: any) => {
console.log('record=====handleDelete', record);
if (record) { if (record) {
// 单个删除 // 单个删除
const { record_id } = record || {}; const { table_id } = record || {};
const newData = tableData.filter((item) => item.record_id !== record_id); const newData = tableData.filter((item) => item.table_id !== table_id);
setTableData(newData); setTableData(newData);
setBindTableKeys([]);
if (onBindUserChange) onBindUserChange(newData); if (onBindUserChange) onBindUserChange(newData);
} else { } else {
// 批量删除 // 批量删除
const newData = tableData.filter( const newData = tableData.filter(
(item) => !bindTableKeys.includes(item.record_id), (item) => !bindTableKeys.includes(item.table_id),
); );
setTableData(newData); setTableData(newData);
setBindTableKeys([]);
if (onBindUserChange) onBindUserChange(newData); if (onBindUserChange) onBindUserChange(newData);
setSelectedRowKeys([]);
setSelectedRows([]);
} }
}; };
@ -140,17 +133,24 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
dataIndex: 'order', dataIndex: 'order',
key: 'order', key: 'order',
width: 80, width: 80,
align: 'center',
render: (_, record, index) => <span>{index + 1}</span>, render: (_, record, index) => <span>{index + 1}</span>,
}, },
{ {
title: '名称', title: '名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
align: 'center',
render: (text) => {
return <Tooltip>{text || '--'}</Tooltip>;
},
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
align: 'center',
width: 220,
render: (text) => ( render: (text) => (
<span>{text == 1 ? '用户' : text == 2 ? '用户组' : ''}</span> <span>{text == 1 ? '用户' : text == 2 ? '用户组' : ''}</span>
), ),
@ -158,10 +158,19 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
width: 180,
align: 'center',
render: (_, record) => ( render: (_, record) => (
<Button type="link" onClick={() => handleDelete(record)}> <Popconfirm
title=""
</Button> placement="bottom"
description="请确认是否删除?"
onConfirm={() => handleDelete(record)}
okText="删除"
cancelText="取消"
>
<Button type="link"></Button>
</Popconfirm>
), ),
}, },
]; ];
@ -229,36 +238,28 @@ const SelectedTable: React.FC<SelectedTableProps> = (props) => {
title="" title=""
trigger="click" trigger="click"
open={open} open={open}
// onOpenChange={setOpen} onOpenChange={setOpen}
placement={placement} placement={placement}
> >
<div> <Button></Button>
<Button
onClick={() => {
setOpen(true);
}}
style={{ marginRight: '15px' }}
>
</Button>
<Popconfirm
title=""
description="删除操作不可逆,请确认是否删除?"
onConfirm={() => handleDelete()}
disabled={bindTableKeys.length === 0}
okText="删除"
cancelText="取消"
>
<Button disabled={bindTableKeys.length === 0}></Button>
</Popconfirm>
</div>
</Popover> </Popover>
<Popconfirm
title=""
placement="bottomRight"
description="请确认是否删除?"
onConfirm={() => handleDelete()}
disabled={bindTableKeys.length === 0}
okText="删除"
cancelText="取消"
>
<Button disabled={bindTableKeys.length === 0}></Button>
</Popconfirm>
</Space> </Space>
<CustomTable <CustomTable
dataSource={tableData} dataSource={tableData}
onDelete={handleDelete} onDelete={handleDelete}
scrollY={400} scrollY={400}
rowKey="record_id" rowKey="table_id"
pagination={false} pagination={false}
columns={getColumns()} columns={getColumns()}
rowSelection={{ rowSelection={{

View File

@ -23,7 +23,6 @@ const DeletableTable: React.FC<DeletableTableProps> = (props) => {
scrollY = 400, scrollY = 400,
...restProps ...restProps
} = props; } = props;
console.log("datasource=====",dataSource);
// const getColumns = (): ColumnsType<any> => { // const getColumns = (): ColumnsType<any> => {
// const columns: ColumnsType<any> = [ // const columns: ColumnsType<any> = [
// { // {