From 6ef08e4404a6ecb26e33952face19ef412d453b5 Mon Sep 17 00:00:00 2001 From: shaot Date: Thu, 7 Aug 2025 18:15:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=89=8D=E7=AB=AF):=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=92=8C=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/components/customTree/index.tsx | 1 + web-fe/src/pages/terminal/contast.ts | 20 -- web-fe/src/pages/terminal/index.tsx | 154 ++++++++---- .../mod/ImageSelectedTable/index.less | 17 ++ .../terminal/mod/ImageSelectedTable/index.tsx | 181 ++++++++++++++ .../terminal/mod/ImageSelectedTable/table.tsx | 102 ++++++++ .../pages/terminal/mod/bindImage/index.less | 4 + .../pages/terminal/mod/bindImage/index.tsx | 95 +++++++ .../pages/terminal/mod/bindUser/index.less | 4 + .../src/pages/terminal/mod/bindUser/index.tsx | 98 ++++++++ .../pages/terminal/mod/eidtDevice/index.tsx | 10 +- web-fe/src/pages/terminal/mod/group/index.tsx | 7 +- .../terminal/mod/selectConponents/index.less | 17 ++ .../terminal/mod/selectConponents/index.tsx | 113 +++++++++ .../terminal/mod/selectedTable/index.tsx | 232 ++++++++++++++++++ .../terminal/mod/selectedTable/table.tsx | 80 ++++++ web-fe/src/pages/userList/contast.ts | 20 -- web-fe/src/pages/userList/index.tsx | 64 ++--- .../src/pages/userList/mod/eidtUser/index.tsx | 8 +- web-fe/src/pages/userList/mod/group/index.tsx | 6 +- web-fe/src/services/userList/index.ts | 57 ++--- web-fe/src/services/userList/typings.d.ts | 68 ----- web-fe/src/types/terminal.d.ts | 74 ++++++ web-fe/src/types/user.d.ts | 58 +++++ 24 files changed, 1233 insertions(+), 257 deletions(-) delete mode 100644 web-fe/src/pages/terminal/contast.ts create mode 100644 web-fe/src/pages/terminal/mod/ImageSelectedTable/index.less create mode 100644 web-fe/src/pages/terminal/mod/ImageSelectedTable/index.tsx create mode 100644 web-fe/src/pages/terminal/mod/ImageSelectedTable/table.tsx create mode 100644 web-fe/src/pages/terminal/mod/bindImage/index.less create mode 100644 web-fe/src/pages/terminal/mod/bindImage/index.tsx create mode 100644 web-fe/src/pages/terminal/mod/bindUser/index.less create mode 100644 web-fe/src/pages/terminal/mod/bindUser/index.tsx create mode 100644 web-fe/src/pages/terminal/mod/selectConponents/index.less create mode 100644 web-fe/src/pages/terminal/mod/selectConponents/index.tsx create mode 100644 web-fe/src/pages/terminal/mod/selectedTable/index.tsx create mode 100644 web-fe/src/pages/terminal/mod/selectedTable/table.tsx delete mode 100644 web-fe/src/pages/userList/contast.ts delete mode 100644 web-fe/src/services/userList/typings.d.ts create mode 100644 web-fe/src/types/terminal.d.ts create mode 100644 web-fe/src/types/user.d.ts diff --git a/web-fe/src/pages/components/customTree/index.tsx b/web-fe/src/pages/components/customTree/index.tsx index 1c6911e..050dceb 100644 --- a/web-fe/src/pages/components/customTree/index.tsx +++ b/web-fe/src/pages/components/customTree/index.tsx @@ -4,6 +4,7 @@ import { Tree } from 'antd'; import type { DataNode, TreeProps } from 'antd/es/tree'; interface CustomTreeProps extends Omit { + // treeData: User.OrganizationNode[]; treeData: any[]; titleField: string; keyField: string; diff --git a/web-fe/src/pages/terminal/contast.ts b/web-fe/src/pages/terminal/contast.ts deleted file mode 100644 index af00509..0000000 --- a/web-fe/src/pages/terminal/contast.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface User { - id: string; - user_id:string, - username: string; - loginName: string; - userGroup: string; - userType: string; -} - -export interface OrganizationNode { - id: string; - name: string; - children?: OrganizationNode[]; -} - -export interface ModalBaseNode { - visible:boolean; - recordData: any; - selectedOrg:string; -} diff --git a/web-fe/src/pages/terminal/index.tsx b/web-fe/src/pages/terminal/index.tsx index 10926fd..0bafe94 100644 --- a/web-fe/src/pages/terminal/index.tsx +++ b/web-fe/src/pages/terminal/index.tsx @@ -7,35 +7,50 @@ import { PlusOutlined, TeamOutlined, } from '@ant-design/icons'; -import { Button, Input, message, Popconfirm, Popover, Table } from 'antd'; +import { + Button, + Input, + message, + Popconfirm, + Popover, + Table, + Tooltip, +} from 'antd'; import type { ColumnsType } from 'antd/es/table'; import React, { useEffect, useState } from 'react'; -import { OrganizationNode, Device } from './contast'; import styles from './index.less'; +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([]); - const [selectedOrg, setSelectedOrg] = useState(''); + const [orgTreeData, setOrgTreeData] = useState([]); + const [selectedOrg, setSelectedOrg] = useState(); // 用户列表 - const [dataSource, setDataSource] = useState([]); + const [dataSource, setDataSource] = useState([]); const [loading, setLoading] = useState(false); const [searchText, setSearchText] = useState(''); const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]); // 分页参数 const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [total, setTotal] = useState(0); + // 添加分组 + const [visible, setVisible] = useState(false); // 编辑用户 const [currentUserInfo, setCurrentUserInfo] = useState({ visible: false, }); + const [bindUserDta, setBindUserData] = useState({ + visible: false, + }); - // 添加分组 - const [visible, setVisible] = useState(false); + const [bindImageDta, setBindImageDta] = useState({ + visible: false, + }); // 获取用户分组组织树 useEffect(() => { @@ -48,73 +63,64 @@ const UserListPage: React.FC = () => { }, [selectedOrg, currentPage, pageSize]); const getGroupList = () => { - const mockOrgData: OrganizationNode[] = [ + const mockOrgData: User.OrganizationNode[] = [ { - name: 'Headquartershdy', - id: '1', + name: 'Headquarters', + id: 1, children: [ { name: 'HR Department', - id: '2', + id: 2, }, { name: 'IT Department', - id: '3', + id: 3, children: [ { name: 'Frontend Team', - id: '4', + id: 4, }, { name: 'Backend Team', - id: '5', + id: 5, }, ], }, { name: 'Finance Department', - id: '6', + id: 6, }, ], }, ]; - setSelectedOrg(mockOrgData[0].id as string); + setSelectedOrg(mockOrgData[0].id as number); setOrgTreeData(mockOrgData); }; const getDataSource = () => { setLoading(true); setTimeout(() => { - const startIndex = (currentPage - 1) * pageSize; - const mockUsers: Device[] = Array.from( + const mockUsers: Termial.TermialItem[] = Array.from( { length: pageSize }, (_, index) => ({ - id: `${startIndex + index + 1}`, - username: `User ${startIndex + index + 1}`, - loginName: `login${startIndex + index + 1}`, - userGroup: - index % 3 === 0 ? 'Admin' : index % 3 === 1 ? 'Manager' : 'User', - userType: - index % 4 === 0 - ? 'Full-time' - : index % 4 === 1 - ? 'Part-time' - : index % 4 === 2 - ? 'Contractor' - : 'Intern', + 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?: Device) => { - const { user_id } = device || {}; + const onDelete = (device?: Termial.TermialItem) => { + const { id } = device || {}; const payload = { - ids: user_id ? [user_id] : selectedRowKeys, + ids: id ? [id] : selectedRowKeys, }; deleteUser(payload as any).then((res) => { console.log('res=====', res); @@ -127,14 +133,14 @@ const UserListPage: React.FC = () => { }); }; - const handleEditUserInfo = (device: Device) => { + const handleEditUserInfo = (device: Termial.TermialItem) => { setCurrentUserInfo({ recordData: { ...device }, visible: true, }); }; - const columns: ColumnsType = [ + const columns: ColumnsType = [ { title: '显示名称', dataIndex: 'username', @@ -142,18 +148,18 @@ const UserListPage: React.FC = () => { }, { title: '终端名称', - dataIndex: 'loginName', - key: 'loginName', + dataIndex: 'device_name', + key: 'device_name', }, { title: '终端分组', - dataIndex: 'userGroup', - key: 'userGroup', + dataIndex: 'device_group_name', + key: 'device_group_name', }, { title: 'IP地址', - dataIndex: 'sex', - key: 'sex', + dataIndex: 'ip_addr', + key: 'ip_addr', }, { title: '终端标识', @@ -164,12 +170,22 @@ const UserListPage: React.FC = () => { title: '终端类型', dataIndex: 'device_type', key: 'device_type', + render: (text) => { + return ( +
+ + {text} + +
+ ); + }, }, { title: '操作', key: 'actions', align: 'center', width: 150, + fixed: 'right', render: (_, record) => (
-
-
@@ -214,7 +243,7 @@ const UserListPage: React.FC = () => { const onOrgSelect = (selectedKeys: React.Key[]) => { if (selectedKeys.length > 0) { - setSelectedOrg(selectedKeys[0] as string); + setSelectedOrg(selectedKeys[0] as number); setCurrentPage(1); } }; @@ -254,7 +283,7 @@ const UserListPage: React.FC = () => { // onCancel={cancel} okText="删除" cancelText="取消" - disabled={selectedOrg === ''} + disabled={!selectedOrg} >
@@ -296,7 +325,7 @@ const UserListPage: React.FC = () => { title="" description="删除操作不可逆,请确认是否删除?" onConfirm={() => onDelete()} - // onCancel={cancel} + placement="bottom" okText="删除" cancelText="取消" disabled={selectedRowKeys.length === 0} @@ -375,6 +404,31 @@ const UserListPage: React.FC = () => { }} onOk={() => {}} /> + {bindUserDta.visible && ( + { + setBindUserData({ + recordData: {}, + visible: false, + }); + }} + onOk={() => {}} + /> + )} + {bindImageDta.visible && ( + { + setBindImageDta({ + recordData: {}, + visible: false, + }); + }} + onOk={() => {}} + /> + )} ); }; diff --git a/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.less b/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.less new file mode 100644 index 0000000..d4b3322 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.less @@ -0,0 +1,17 @@ +.content_wrap { + width: 100%; + height: 100%; + .search_wrap { + display: flex; + justify-content: flex-end; + margin-bottom: 5px; + padding-bottom: 5px; + } + :global { + :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination + .ant-pagination-total-text { + position: absolute; + left: 5px; + } + } +} diff --git a/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.tsx b/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.tsx new file mode 100644 index 0000000..51a9995 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/ImageSelectedTable/index.tsx @@ -0,0 +1,181 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import type { PopoverProps } from 'antd'; +import { Alert, Button, Popconfirm, Popover, Space } from 'antd'; +import React, { useEffect, useState } from 'react'; +import CustomTable from '../selectedTable/table'; +import SelectConponents from './table'; + +interface SelectedTableProps { + placement?: PopoverProps['placement']; + selectedOrg?: number; + onCancel?: () => void; + onOk?: (values: any) => void; + orgTreeData?: User.OrganizationNode[]; + value?: any; + onChange?: (values: any) => void; +} + +const SelectedTable: React.FC = (props) => { + const { + placement = 'bottomLeft', + orgTreeData, + value: formValue, + onChange: onBindUserChange, + } = props; + const [open, setOpen] = useState(false); + const [tableData, setTableData] = useState([]); + //镜像选择 + const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]); + const [selectedRows, setSelectedRows] = useState([]); + + // 已绑定用户列表选择 + const [bindTableKeys, setBindTableKeys] = useState([]); + + useEffect(() => { + console.log('formValue==========', formValue); + const userList: any = []; + const userId: any = []; + if (formValue && formValue.length > 0) { + formValue.forEach((item: any) => { + userList.push(item); + userId.push(item.id); + }); + setSelectedRowKeys(userId); + setSelectedRows(userList); + } + }, [formValue]); + + const onHnadleCancel = () => { + setOpen(false); + setSelectedRowKeys([]); + setSelectedRows([]); + }; + + const onHandleOk = () => { + const list: any[] = []; + // 用户 + (selectedRows || []).forEach((item) => { + list.push({ + id: item.id, + name: item.name, + type: 1, + }); + }); + + setTableData(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 || {}; + const newData = tableData.filter((item) => item.id !== id); + setTableData(newData); + } else { + // 批量删除 + const newData = tableData.filter( + (item) => !bindTableKeys.includes(item.id), + ); + setTableData(newData); + setSelectedRowKeys([]); + setSelectedRows([]); + } + }; + + const accessPopover = ( +
+ {/* */} + +
+ + +
+
+ ); + + return ( + <> + + +
+ + handleDelete()} + disabled={bindTableKeys.length === 0} + okText="删除" + cancelText="取消" + > + + +
+
+
+ + + ); +}; + +export default SelectedTable; diff --git a/web-fe/src/pages/terminal/mod/ImageSelectedTable/table.tsx b/web-fe/src/pages/terminal/mod/ImageSelectedTable/table.tsx new file mode 100644 index 0000000..fb7be1b --- /dev/null +++ b/web-fe/src/pages/terminal/mod/ImageSelectedTable/table.tsx @@ -0,0 +1,102 @@ +import { Input, Table } from 'antd'; +import React, { useEffect, useState } from 'react'; +import styles from './index.less'; + +interface TableProps { + treeData?: User.OrganizationNode[]; + selectedRowKeys?: any[]; + onUserTableSelect?: (keys: any[], row: any[]) => void; +} + +interface DataType { + key: React.Key; + name: string; + age: number; + address: string; +} + +const TablePage: React.FC = ({ + onUserTableSelect, + selectedRowKeys, +}) => { + const [data, setData] = useState([]); + const [searchText, setSearchText] = useState(''); + 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); + }, []); + + const columns: TableProps['columns'] = [ + { + title: 'Name', + dataIndex: 'name', + sorter: (a, b) => a.name.localeCompare(b.name), + }, + ]; + + const handleSearch = (value: string) => { + setSearchText(value); + setCurrentPage(1); + }; + + const handlePageChange = (page: number, size: number) => { + setCurrentPage(page); + setPageSize(size); + }; + + const handlePageSizeChange = (current: number, size: number) => { + setCurrentPage(1); + setPageSize(size); + }; + + return ( +
+
+ +
+ + + + ); +}; + +export default TablePage; diff --git a/web-fe/src/pages/terminal/mod/bindImage/index.less b/web-fe/src/pages/terminal/mod/bindImage/index.less new file mode 100644 index 0000000..9b95d2c --- /dev/null +++ b/web-fe/src/pages/terminal/mod/bindImage/index.less @@ -0,0 +1,4 @@ +.model_content { + width: 100%; + height: 650px; +} diff --git a/web-fe/src/pages/terminal/mod/bindImage/index.tsx b/web-fe/src/pages/terminal/mod/bindImage/index.tsx new file mode 100644 index 0000000..5f7bc40 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/bindImage/index.tsx @@ -0,0 +1,95 @@ +// index.tsx +import { Button, Form, Input, message, Modal } from 'antd'; +import React, { useEffect } from 'react'; +import SelectedTable from '../ImageSelectedTable/index'; +import styles from './index.less'; + +interface UserEditModalProps { + onCancel: () => void; + onOk: (values: any) => void; + confirmLoading?: boolean; + dataDetial?: Termial.ModalBaseNode; + selectedOrg?: number; +} + +const BindUserModal: React.FC = ({ + onCancel, + onOk, + confirmLoading = false, + dataDetial, +}) => { + const { recordData, visible, selectedOrg } = dataDetial || {}; + const [form] = Form.useForm(); + + useEffect(() => { + // const initialValues = { user_group_id: [selectedOrg], status: 1 }; + // form.setFieldsValue(initialValues); + }, [visible, form, recordData, selectedOrg]); + + const handleOk = async () => { + try { + const values = await form.validateFields(); + console.log('values=====', values); + onOk(values); + } catch (error) { + message.error('请检查表单字段'); + } + }; + + return ( + + + + + } + > +
+
+ + + + + + + +
+
+ ); +}; + +export default BindUserModal; diff --git a/web-fe/src/pages/terminal/mod/bindUser/index.less b/web-fe/src/pages/terminal/mod/bindUser/index.less new file mode 100644 index 0000000..9b95d2c --- /dev/null +++ b/web-fe/src/pages/terminal/mod/bindUser/index.less @@ -0,0 +1,4 @@ +.model_content { + width: 100%; + height: 650px; +} diff --git a/web-fe/src/pages/terminal/mod/bindUser/index.tsx b/web-fe/src/pages/terminal/mod/bindUser/index.tsx new file mode 100644 index 0000000..119b4b3 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/bindUser/index.tsx @@ -0,0 +1,98 @@ +// index.tsx +import { Button, Form, Input, message, Modal } from 'antd'; +import React, { useEffect } from 'react'; +import SelectedTable from '../selectedTable'; +import styles from './index.less'; + +interface UserEditModalProps { + // visible: boolean; + orgTreeData?: User.OrganizationNode[]; + onCancel: () => void; + onOk: (values: any) => void; + confirmLoading?: boolean; + dataDetial?: Termial.ModalBaseNode; + selectedOrg?: number; +} + +const BindUserModal: React.FC = ({ + orgTreeData, + onCancel, + onOk, + confirmLoading = false, + dataDetial, +}) => { + const { recordData, visible, selectedOrg } = dataDetial || {}; + const [form] = Form.useForm(); + + useEffect(() => { + // const initialValues = { user_group_id: [selectedOrg], status: 1 }; + // form.setFieldsValue(initialValues); + }, [visible, form, recordData, selectedOrg]); + + const handleOk = async () => { + try { + const values = await form.validateFields(); + console.log("values=====",values) + onOk(values); + } catch (error) { + message.error('请检查表单字段'); + } + }; + + return ( + + + + + } + > +
+
+ + + + + + + +
+
+ ); +}; + +export default BindUserModal; diff --git a/web-fe/src/pages/terminal/mod/eidtDevice/index.tsx b/web-fe/src/pages/terminal/mod/eidtDevice/index.tsx index d2696a2..dbc3bcf 100644 --- a/web-fe/src/pages/terminal/mod/eidtDevice/index.tsx +++ b/web-fe/src/pages/terminal/mod/eidtDevice/index.tsx @@ -6,22 +6,18 @@ import { message, Modal, Radio, - Select, TreeSelect, } from 'antd'; import React, { useEffect } from 'react'; -import { ModalBaseNode, OrganizationNode } from '../../contast'; - -const { Option } = Select; interface UserEditModalProps { // visible: boolean; - orgTreeData: OrganizationNode[]; + orgTreeData: User.OrganizationNode[]; onCancel: () => void; onOk: (values: any) => void; confirmLoading?: boolean; - currentUserInfo?: ModalBaseNode; - selectedOrg?: string; + currentUserInfo?: Termial.ModalBaseNode; + selectedOrg?: number; } const UserEditModal: React.FC = ({ diff --git a/web-fe/src/pages/terminal/mod/group/index.tsx b/web-fe/src/pages/terminal/mod/group/index.tsx index 8de23d9..cdf81d3 100644 --- a/web-fe/src/pages/terminal/mod/group/index.tsx +++ b/web-fe/src/pages/terminal/mod/group/index.tsx @@ -1,15 +1,12 @@ -// 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 type { DataNode } from 'antd/es/tree'; -import { OrganizationNode } from '../../contast'; interface CreatGroupProps { visible: boolean; - selectedOrg: string; + selectedOrg?: number; onCancel: () => void; onOk: (values: any) => void; - orgTreeData: OrganizationNode[]; + orgTreeData: User.OrganizationNode[]; } const CreatGroup: React.FC = (props) => { diff --git a/web-fe/src/pages/terminal/mod/selectConponents/index.less b/web-fe/src/pages/terminal/mod/selectConponents/index.less new file mode 100644 index 0000000..cf3c776 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/selectConponents/index.less @@ -0,0 +1,17 @@ +.content_wrap { + width: 100%; + height: 100%; + .search_wrap{ + display: flex; + justify-content: flex-end; + margin-bottom: 5px; + padding-bottom: 5px; + } + :global { + :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination + .ant-pagination-total-text { + position: absolute; + left: 5px; + } + } +} diff --git a/web-fe/src/pages/terminal/mod/selectConponents/index.tsx b/web-fe/src/pages/terminal/mod/selectConponents/index.tsx new file mode 100644 index 0000000..42631c3 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/selectConponents/index.tsx @@ -0,0 +1,113 @@ +import { Input, Table, TreeSelect } from 'antd'; +import React, { useEffect, useState } from 'react'; +import styles from './index.less'; + +interface TableProps { + treeData?: User.OrganizationNode[]; + selectedRowKeys?: any[]; + onUserTableSelect?: (keys: any[], row: any[]) => void; +} + +interface DataType { + key: React.Key; + name: string; + age: number; + address: string; +} + +const TablePage: React.FC = ({ + treeData, + onUserTableSelect, + selectedRowKeys, +}) => { + const [data, setData] = useState([]); + // const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [searchText, setSearchText] = useState(''); + 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); + }, []); + + const columns: TableProps['columns'] = [ + { + title: 'Name', + dataIndex: 'name', + sorter: (a, b) => a.name.localeCompare(b.name), + }, + ]; + + const handleSearch = (value: string) => { + setSearchText(value); + setCurrentPage(1); + }; + + const handlePageChange = (page: number, size: number) => { + setCurrentPage(page); + setPageSize(size); + }; + + const handlePageSizeChange = (current: number, size: number) => { + setCurrentPage(1); + setPageSize(size); + }; + + return ( +
+
+ + +
+ +
+ + ); +}; + +export default TablePage; diff --git a/web-fe/src/pages/terminal/mod/selectedTable/index.tsx b/web-fe/src/pages/terminal/mod/selectedTable/index.tsx new file mode 100644 index 0000000..678662f --- /dev/null +++ b/web-fe/src/pages/terminal/mod/selectedTable/index.tsx @@ -0,0 +1,232 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ +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 React, { useEffect, useState } from 'react'; +import SelectConponents from '../selectConponents'; +import CustomTable from './table'; + +interface SelectedTableProps { + placement?: PopoverProps['placement']; + selectedOrg?: number; + onCancel?: () => void; + onOk?: (values: any) => void; + orgTreeData?: User.OrganizationNode[]; + value?: any; + onChange?: (values: any) => void; +} + +const SelectedTable: React.FC = (props) => { + const { + placement = 'bottomLeft', + orgTreeData, + value: formValue, + onChange: onBindUserChange, + } = props; + const [open, setOpen] = useState(false); + const [tableData, setTableData] = useState([]); + //用户选择列表 + const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]); + const [selectedRows, setSelectedRows] = useState([]); + // 用户组 + const [checkedKeys, setCheckedKeys] = useState([]); + const [checkedRow, setCheckedRow] = useState([]); + + // 已绑定用户列表选择 + const [bindTableKeys, setBindTableKeys] = useState([]); + + useEffect(() => { + console.log('formValue==========', formValue); + const userList: any = []; + const groupList: any = []; + const userId: any = []; + const groupId: any = []; + if (formValue && formValue.length > 0) { + formValue.forEach((item: any) => { + if (item.type === 1) { + userList.push(item); + userId.push(item.id); + } else { + groupList.push(item); + groupId.push(item.id); + } + }); + setSelectedRowKeys(userId); + setSelectedRows(userList); + setCheckedKeys(groupId); + setCheckedRow(groupList); + } + }, [formValue]); + + // const onOrgSelect = (selectedKeys: React.Key[], info: any) => {}; + + const onCheckChange = (keys: any[], info: any) => { + const { checkedNodes } = info; + console.log('info=========', info); + setCheckedKeys(keys); + setCheckedRow(checkedNodes); + }; + + const onHnadleCancel = () => { + setOpen(false); + setSelectedRowKeys([]); + setSelectedRows([]); + setCheckedKeys([]); + setCheckedRow([]); + }; + + const onHandleOk = () => { + const list: any[] = []; + // 用户 + (selectedRows || []).forEach((item) => { + list.push({ + id: item.id, + name: item.name, + type: 1, + }); + }); + // 用户组 + checkedRow.forEach((item) => { + list.push({ + id: item.id, + name: item.name, + type: 2, + }); + }); + setTableData(list); + onBindUserChange(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 || {}; + const newData = tableData.filter((item) => item.id !== id); + setTableData(newData); + } else { + // 批量删除 + const newData = tableData.filter( + (item) => !bindTableKeys.includes(item.id), + ); + setTableData(newData); + setSelectedRowKeys([]); + setSelectedRows([]); + } + }; + + const accessPopover = ( +
+ + + + } + /> + + + + + +
+ + +
+
+ ); + + return ( + <> + + +
+ + handleDelete()} + disabled={bindTableKeys.length === 0} + okText="删除" + cancelText="取消" + > + + +
+
+
+ + + ); +}; + +export default SelectedTable; diff --git a/web-fe/src/pages/terminal/mod/selectedTable/table.tsx b/web-fe/src/pages/terminal/mod/selectedTable/table.tsx new file mode 100644 index 0000000..5f0dd18 --- /dev/null +++ b/web-fe/src/pages/terminal/mod/selectedTable/table.tsx @@ -0,0 +1,80 @@ +import { Button, Table } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import React from 'react'; + +interface DeletableTableProps { + dataSource: any[]; + isSerial?: boolean; + isAction?: boolean; + scrollY: number; + onDelete?: (recod: any) => void; + rowKey?: string; + rowSelection?: any; + pagination?:any; +} + +const DeletableTable: React.FC = (props) => { + const { + dataSource, + onDelete, + isSerial = true, + isAction = true, + scrollY = 300, + ...restProps + } = props; + console.log("datasource=====",dataSource); + const getColumns = (): ColumnsType => { + const columns: ColumnsType = [ + { + title: '名称', + dataIndex: 'name', + key: 'name', + }, + { + title: '类型', + dataIndex: 'type', + key: 'type', + render: (text) => ( + {text == 1 ? '用户' : text == 2 ? '用户组' : ''} + ), + }, + ]; + + if (isSerial) { + columns.unshift({ + title: '序号', + dataIndex: 'order', + key: 'order', + width: 80, + render: (_, record, index) => {index + 1}, + }); + } + + if (isAction) { + columns.push({ + title: '操作', + key: 'action', + render: (_, record) => ( + + ), + }); + } + + return columns; + }; + + return ( +
+
+ + ); +}; + +export default DeletableTable; diff --git a/web-fe/src/pages/userList/contast.ts b/web-fe/src/pages/userList/contast.ts deleted file mode 100644 index af00509..0000000 --- a/web-fe/src/pages/userList/contast.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface User { - id: string; - user_id:string, - username: string; - loginName: string; - userGroup: string; - userType: string; -} - -export interface OrganizationNode { - id: string; - name: string; - children?: OrganizationNode[]; -} - -export interface ModalBaseNode { - visible:boolean; - recordData: any; - selectedOrg:string; -} diff --git a/web-fe/src/pages/userList/index.tsx b/web-fe/src/pages/userList/index.tsx index 2cbc853..e6c4455 100644 --- a/web-fe/src/pages/userList/index.tsx +++ b/web-fe/src/pages/userList/index.tsx @@ -5,18 +5,17 @@ import { DeleteOutlined, PlusOutlined, TeamOutlined } from '@ant-design/icons'; import { Button, Input, Popconfirm, Table, message } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import React, { useEffect, useState } from 'react'; -import { OrganizationNode, User } from './contast'; import styles from './index.less'; import UserEditModal from './mod/eidtUser'; import CreatGroup from './mod/group'; import PasswordResetModal from './mod/passwordEdit'; const UserListPage: React.FC = () => { - const [orgTreeData, setOrgTreeData] = useState([]); - const [selectedOrg, setSelectedOrg] = useState(''); + const [orgTreeData, setOrgTreeData] = useState([]); + const [selectedOrg, setSelectedOrg] = useState(); // 用户列表 - const [users, setUsers] = useState([]); + const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); const [searchText, setSearchText] = useState(''); const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]); @@ -48,37 +47,37 @@ const UserListPage: React.FC = () => { }, [selectedOrg, currentPage, pageSize]); const getUserGroupList = () => { - const mockOrgData: OrganizationNode[] = [ + const mockOrgData: User.OrganizationNode[] = [ { name: 'Headquarters', - id: '1', + id: 1, children: [ { name: 'HR Department', - id: '2', + id: 2, }, { name: 'IT Department', - id: '3', + id: 3, children: [ { name: 'Frontend Team', - id: '4', + id: 4, }, { name: 'Backend Team', - id: '5', + id: 5, }, ], }, { name: 'Finance Department', - id: '6', + id: 6, }, ], }, ]; - setSelectedOrg(mockOrgData[0].id as string); + setSelectedOrg(mockOrgData[0].id as number); setOrgTreeData(mockOrgData); }; @@ -86,22 +85,11 @@ const UserListPage: React.FC = () => { setLoading(true); setTimeout(() => { const startIndex = (currentPage - 1) * pageSize; - const mockUsers: User[] = Array.from( + const mockUsers: User.UserItem[] = Array.from( { length: pageSize }, (_, index) => ({ - id: `${startIndex + index + 1}`, - username: `User ${startIndex + index + 1}`, - loginName: `login${startIndex + index + 1}`, - userGroup: - index % 3 === 0 ? 'Admin' : index % 3 === 1 ? 'Manager' : 'User', - userType: - index % 4 === 0 - ? 'Full-time' - : index % 4 === 1 - ? 'Part-time' - : index % 4 === 2 - ? 'Contractor' - : 'Intern', + user_id: startIndex + index + 1, + user_name: 'string', }), ); @@ -111,7 +99,7 @@ const UserListPage: React.FC = () => { }, 300); }; - const onDelete = (user?: User) => { + const onDelete = (user?: User.UserItem) => { const { user_id } = user || {}; const payload = { ids: user_id ? [user_id] : selectedRowKeys, @@ -127,7 +115,7 @@ const UserListPage: React.FC = () => { }); }; - const handlePassword = (user: User) => { + const handlePassword = (user: User.UserItem) => { setEidtPassword({ recordData: { ...user }, visible: true, @@ -140,23 +128,23 @@ const UserListPage: React.FC = () => { }); }; - const handleEditUserInfo = (user: User) => { + const handleEditUserInfo = (user: User.UserItem) => { setCurrentUserInfo({ recordData: { ...user }, visible: true, }); }; - const columns: ColumnsType = [ + const columns: ColumnsType = [ { title: '登录名', - dataIndex: 'username', - key: 'username', + dataIndex: 'user_id', + key: 'user_id', }, { title: '用户姓名', - dataIndex: 'loginName', - key: 'loginName', + dataIndex: 'user_name', + key: 'user_name', }, { title: '用户分组', @@ -182,6 +170,8 @@ const UserListPage: React.FC = () => { title: '操作', key: 'actions', align: 'center', + width: 150, + fixed: 'right', render: (_, record) => (
diff --git a/web-fe/src/pages/userList/mod/eidtUser/index.tsx b/web-fe/src/pages/userList/mod/eidtUser/index.tsx index 9136453..18266f0 100644 --- a/web-fe/src/pages/userList/mod/eidtUser/index.tsx +++ b/web-fe/src/pages/userList/mod/eidtUser/index.tsx @@ -10,18 +10,16 @@ import { TreeSelect, } from 'antd'; import React, { useEffect } from 'react'; -import { ModalBaseNode, OrganizationNode } from '../../contast'; - const { Option } = Select; interface UserEditModalProps { // visible: boolean; - orgTreeData: OrganizationNode[]; + orgTreeData: User.OrganizationNode[]; onCancel: () => void; onOk: (values: any) => void; confirmLoading?: boolean; - currentUserInfo?: ModalBaseNode; - selectedOrg?: string; + currentUserInfo?: User.UserModalBaseNode; + selectedOrg?: number; } const UserEditModal: React.FC = ({ diff --git a/web-fe/src/pages/userList/mod/group/index.tsx b/web-fe/src/pages/userList/mod/group/index.tsx index b7a81ca..7ede3f4 100644 --- a/web-fe/src/pages/userList/mod/group/index.tsx +++ b/web-fe/src/pages/userList/mod/group/index.tsx @@ -1,15 +1,13 @@ // 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 type { DataNode } from 'antd/es/tree'; -import { OrganizationNode } from '../../contast'; interface CreatGroupProps { visible: boolean; - selectedOrg: string; + selectedOrg?: number; onCancel: () => void; onOk: (values: any) => void; - orgTreeData: OrganizationNode[]; + orgTreeData: User.OrganizationNode[]; } const CreatGroup: React.FC = (props) => { diff --git a/web-fe/src/services/userList/index.ts b/web-fe/src/services/userList/index.ts index fd5558d..1eb3376 100644 --- a/web-fe/src/services/userList/index.ts +++ b/web-fe/src/services/userList/index.ts @@ -1,89 +1,64 @@ import { request } from '@umijs/max'; // 新建用户分组 -export async function addUserGroup(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function addUserGroup(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } // 获取用户分组 -export async function getUserGroup(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function getUserGroup(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } // 删除用户分组 -export async function deleteUserGroup(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function deleteUserGroup(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } // 查询用户分组 -export async function getUserList(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function getUserList(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } // 新建用户 -export async function addUser(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function addUser(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } // 编辑用户 -export async function editUser(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function editUser(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } //获取某个用户信息 -export async function getUserById(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function getUserById(body?: any) { + return request('/api/v1/user', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } //删除用户 -export async function deleteUser(body?: APIS.UserInfoVO) { - return request('/api/v1/user', { +export async function deleteUser(body?: any) { + return request('/api/v1/user', { method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, data: body, }); } - diff --git a/web-fe/src/services/userList/typings.d.ts b/web-fe/src/services/userList/typings.d.ts deleted file mode 100644 index 2c8ac02..0000000 --- a/web-fe/src/services/userList/typings.d.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* eslint-disable */ -// 该文件由 OneAPI 自动生成,请勿手动修改! - -declare namespace APIS { - interface PageInfo { - /** -1 */ - current?: number; - pageSize?: number; - total?: number; - list?: Array>; - } - - interface PageInfo_UserInfo_ { - /** -1 */ - current?: number; - pageSize?: number; - total?: number; - list?: Array; - } - - interface Result { - success?: boolean; - errorMessage?: string; - data?: Record; - } - - interface Result_PageInfo_UserInfo__ { - success?: boolean; - errorMessage?: string; - data?: PageInfo_UserInfo_; - } - - interface Result_UserInfo_ { - success?: boolean; - errorMessage?: string; - data?: UserInfo; - } - - interface Result_string_ { - success?: boolean; - errorMessage?: string; - data?: string; - } - - type UserGenderEnum = 'MALE' | 'FEMALE'; - - interface UserInfo { - id?: string; - name?: string; - /** nick */ - nickName?: string; - /** email */ - email?: string; - gender?: UserGenderEnum; - } - - interface UserInfoVO { - name?: string; - /** nick */ - nickName?: string; - /** email */ - email?: string; - } - - type definitions_0 = null; -} diff --git a/web-fe/src/types/terminal.d.ts b/web-fe/src/types/terminal.d.ts new file mode 100644 index 0000000..6acfc62 --- /dev/null +++ b/web-fe/src/types/terminal.d.ts @@ -0,0 +1,74 @@ +declare namespace Termial { + interface TermialGroup { + device_group_id: number; + device_group_name: string; + parent_device_group_id?: number; + children?: TermialGroup[]; + } + + interface TermialItem { + id: number; + device_id: string; + device_name: string; + device_group_id: number; + device_group_name: string; + device_type: number; + ip_addr?: string; + mac_addr?: string; + model?: string; + description?: string; + } + + interface TermialUser { + id: number; + device_id: string; + device_group_id: number; + user_id: number; + restore_type?: number; + description?: string; + } + + interface ModalBaseNode { + visible: boolean; + recordData: any; + selectedOrg: number; + } + + interface TermialImage { + id: number; + device_id: string; + device_group_id: number; + image_id: number; + restore_type?: number; + description?: string; + } + + interface TableParams { + pagination: { + current: number; + pageSize: number; + }; + filters?: Record; + sortOrder?: 'ascend' | 'descend' | null; + sortField?: string | number | null; + keywords?: string; // 添加关键词字段 + } + + // interface DeviceParams{ + // device_id: string; + // } + + interface Termial_ListInfo { + error_code: string; + message: string; + data: { + paging: { + total: number; + total_num?: number; + page_num: number; + page_size: number; + }; + data: TermialItem[]; + }; + } +} diff --git a/web-fe/src/types/user.d.ts b/web-fe/src/types/user.d.ts new file mode 100644 index 0000000..384e721 --- /dev/null +++ b/web-fe/src/types/user.d.ts @@ -0,0 +1,58 @@ +declare namespace User { + + interface UserModalBaseNode { + visible: boolean; + recordData: any; + selectedOrg: number; + } + + interface OrganizationNode { + id: number; + name: string; + parent_id?: number; + parent_name?: string; + children?: OrganizationNode[]; + } + + interface UserItem { + user_id?: number; + user_group_id?: number; + user_group_name?: number; + user_name?: string; + password?: string; + birthday?: string; + cell_phone?: string; + email?: string; + gender?: number; + identity_no?: string; + priority?: number; + user_type?: number; + status?: number; + description?: string; + } + + interface TableParams { + pagination: { + current: number; + pageSize: number; + }; + filters?: Record; + sortOrder?: 'ascend' | 'descend' | null; + sortField?: string | number | null; + keywords?: string; // 添加关键词字段 + } + + interface User_ListInfo { + error_code: string; + message: string; + data: { + paging: { + total: number; + total_num?: number; + page_num: number; + page_size: number; + }; + data: UserItem[]; + }; + } +}