feat: 初始化构建节点设备模块

develop2
donghao 1 year ago
parent bc48347bb1
commit 49bfe975bc

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-03-27 14:56:27
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-22 16:50:40
* @LastEditTime: 2024-04-26 09:33:39
* @FilePath: \general-ai-manage\config\routes.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -103,9 +103,9 @@ export const innerMenuRoutes = [
menuIcon: 'TestIcon',
},
{
name: 'business-node-setting',
path: '/business/nodeSetting',
component: './Business/NodeSetting',
name: 'business-device-group',
path: '/business/deviceGroup',
component: './Business/DeviceGroup',
access: 'canReadMenu',
key: '1002',
menuIcon: '',

@ -0,0 +1,33 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-25 15:45:17
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-28 17:15:19
* @FilePath: \general-ai-platform-web\mock\device.ts
* @Description: mock
*/
import { mockGetDeviceListByGroup } from './pools/deviceData';
import { successMockApiProps } from './typing';
import { fetchCurrPageByList } from './utils/apiMock';
export default {
// 设备列表
'GET /api/device/listByGroup': async (req: Request, res: Response) => {
const { page, pageSize } = req.query;
const resData: successMockApiProps = {
...fetchCurrPageByList({
...mockGetDeviceListByGroup,
data: { ...mockGetDeviceListByGroup.data, page, pageSize: pageSize || 10 },
}),
};
res.json(resData);
},
// // 设备分类
// 'GET /api/dict/deviceType': async (req: Request, res: Response) => {
// const resData: successMockApiProps = {
// ...fetchMockSuccessFullByOther(mockGetDeviceTypeDictData),
// };
// res.json(resData);
// },
};

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-25 16:53:15
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-25 15:50:16
* @LastEditTime: 2024-04-26 16:35:54
* @FilePath: \general-ai-platform-web\mock\deviceGroup.ts
* @Description: mock
*/
@ -12,6 +12,7 @@ import {
mockGetDeviceGroupData3,
mockGetDeviceGroupFkSelectData,
mockGetDeviceGroupListData,
mockGetDeviceGroupSettingData,
mockGetDeviceGroupTreeData,
} from './pools/deviceGroupData';
import { successMockApiProps } from './typing';
@ -29,6 +30,15 @@ export default {
res.json(resData);
},
// 设备组网点树列表
'GET /api/device_group/setting_data': async (req: Request, res: Response) => {
const resData: successMockApiProps = {
...fetchMockSuccessFullByOther(mockGetDeviceGroupSettingData),
};
res.json(resData);
},
/** 未启用 */
// 设备组网点选项列表
'GET /api/device_group/getDeviceGroupFkSelect': async (req: Request, res: Response) => {
const resData: successMockApiProps = {
@ -43,6 +53,7 @@ export default {
};
res.json(resData);
},
// 设备组详情
'GET /api/device_group/getDeviceGroupById': async (req: Request, res: Response) => {
const { id } = req.query;

@ -2,21 +2,28 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-17 14:01:39
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-17 14:01:47
* @LastEditTime: 2024-04-28 14:08:45
* @FilePath: \general-ai-manage\mock\dict.ts
* @Description: mock
*/
import { mockGetIndustryDictData } from './pools/dictData';
import { mockGetDeviceTypeDictData, mockGetIndustryDictData } from './pools/dictData';
import { successMockApiProps } from './typing';
import { fetchMockSuccessFullByOther } from './utils/apiMock';
export default {
// 实时分析告警列表分页
'GET /api/dict/industry/': async (req: Request, res: Response) => {
// 行业分类
'GET /api/dict/industry': async (req: Request, res: Response) => {
const resData: successMockApiProps = {
...fetchMockSuccessFullByOther(mockGetIndustryDictData),
};
res.json(resData);
},
// 设备分类
'GET /api/dict/deviceType': async (req: Request, res: Response) => {
const resData: successMockApiProps = {
...fetchMockSuccessFullByOther(mockGetDeviceTypeDictData),
};
res.json(resData);
},
};

@ -0,0 +1,35 @@
// 节点下设备列表数据
// 节点设备列表
export const mockGetDeviceListByGroup = {
data: {
count: 2,
results: [
{
id: '10001',
name: '海康威视环球摄像头', // 设备名称
deviceType: '摄像头', // 设备类型
isEnable: true, // 是否部署
createTime: '2023-12-17T13:37:31.758471+08:00',
updateTime: '2024-04-25T15:22:16.530494+08:00',
deviceSite: '东区左侧', // 设备位置
deviceModel: 'haikang_video_2024', // 设备型号
deviceParams: '627663_aduh237298', // 设备参数
remark: '', // 备注
},
{
id: '10002',
name: '海康威视环球摄像头',
deviceType: '控制器',
isEnable: false,
createTime: '2023-12-17T13:37:31.758471+08:00',
updateTime: '2024-04-25T15:22:16.530494+08:00',
deviceSite: '南区前侧',
deviceModel: 'haikang_video_2024',
deviceParams: '627663_aduh237298',
remark: '',
},
],
page: 1,
pageSize: 10,
},
};

@ -1,40 +1,257 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-25 15:45:31
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-29 14:44:39
* @FilePath: \general-ai-platform-web\mock\pools\deviceGroupData.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-01-25 16:32:31
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-25 15:51:57
* @LastEditTime: 2024-04-28 13:21:47
* @FilePath: \general-ai-platform-web\mock\pools\deviceGroupData.ts
* @Description:
*/
// 设备组节点选项列表
export const mockGetDeviceGroupFkSelectData = {
data: {
list: [
{
name: '南京节点',
id: 1,
},
{
name: '秦淮节点',
id: 2,
},
{
name: '江宁节点',
id: 3,
},
{
name: '安徽节点',
id: 4,
},
{
name: '合肥节点',
id: 5,
},
],
},
};
// 设备组节点树
export const mockGetDeviceGroupSettingData = {
data: [
{
name: '安徽省',
id: '340000',
groupList: [
{
name: '合肥市',
id: '340100',
groupList: [
{ name: '瑶海区', id: '340102', groupList: [] },
{ name: '庐阳区', id: '340103', groupList: [] },
{ name: '蜀山区', id: '340104', groupList: [] },
{ name: '包河区', id: '340111', groupList: [] },
{ name: '长丰县', id: '340121', groupList: [] },
{ name: '肥东县', id: '340122', groupList: [] },
{ name: '肥西县', id: '340123', groupList: [] },
{ name: '庐江县', id: '340124', groupList: [] },
{ name: '巢湖市', id: '340181', groupList: [] },
],
},
{
name: '芜湖市',
id: '340200',
groupList: [
{ name: '镜湖区', id: '340202', groupList: [] },
{ name: '弋江区', id: '340203', groupList: [] },
{ name: '鸠江区', id: '340207', groupList: [] },
{ name: '三山区', id: '340208', groupList: [] },
{ name: '芜湖县', id: '340221', groupList: [] },
{ name: '繁昌县', id: '340222', groupList: [] },
{ name: '南陵县', id: '340223', groupList: [] },
{ name: '无为县', id: '340225', groupList: [] },
],
},
{
name: '蚌埠市',
id: '340300',
groupList: [
{ name: '龙子湖区', id: '340302', groupList: [] },
{ name: '蚌山区', id: '340303', groupList: [] },
{ name: '禹会区', id: '340304', groupList: [] },
{ name: '淮上区', id: '340311', groupList: [] },
{ name: '怀远县', id: '340321', groupList: [] },
{ name: '五河县', id: '340322', groupList: [] },
{ name: '固镇县', id: '340323', groupList: [] },
],
},
{
name: '淮南市',
id: '340400',
groupList: [
{ name: '大通区', id: '340402', groupList: [] },
{ name: '田家庵区', id: '340403', groupList: [] },
{ name: '谢家集区', id: '340404', groupList: [] },
{ name: '八公山区', id: '340405', groupList: [] },
{ name: '潘集区', id: '340406', groupList: [] },
{ name: '凤台县', id: '340421', groupList: [] },
{ name: '寿县', id: '340422', groupList: [] },
],
},
{
name: '马鞍山市',
id: '340500',
groupList: [
{ name: '花山区', id: '340503', groupList: [] },
{ name: '雨山区', id: '340504', groupList: [] },
{ name: '博望区', id: '340506', groupList: [] },
{ name: '当涂县', id: '340521', groupList: [] },
{ name: '含山县', id: '340522', groupList: [] },
{ name: '和县', id: '340523', groupList: [] },
],
},
{
name: '淮北市',
id: '340600',
groupList: [
{ name: '杜集区', id: '340602', groupList: [] },
{ name: '相山区', id: '340603', groupList: [] },
{ name: '烈山区', id: '340604', groupList: [] },
{ name: '濉溪县', id: '340621', groupList: [] },
],
},
{
name: '铜陵市',
id: '340700',
groupList: [
{ name: '铜官区', id: '340705', groupList: [] },
{ name: '义安区', id: '340706', groupList: [] },
{ name: '郊区', id: '340711', groupList: [] },
{ name: '枞阳县', id: '340722', groupList: [] },
],
},
{
name: '安庆市',
id: '340800',
groupList: [
{ name: '迎江区', id: '340802', groupList: [] },
{ name: '大观区', id: '340803', groupList: [] },
{ name: '宜秀区', id: '340811', groupList: [] },
{ name: '怀宁县', id: '340822', groupList: [] },
{ name: '潜山市', id: '340882', groupList: [] },
{ name: '太湖县', id: '340825', groupList: [] },
{ name: '宿松县', id: '340826', groupList: [] },
{ name: '望江县', id: '340827', groupList: [] },
{ name: '岳西县', id: '340828', groupList: [] },
{ name: '桐城市', id: '340881', groupList: [] },
],
},
{
name: '滁州市',
id: '341100',
groupList: [
{ name: '琅琊区', id: '341102', groupList: [] },
{ name: '南谯区', id: '341103', groupList: [] },
{ name: '来安县', id: '341122', groupList: [] },
{ name: '全椒县', id: '341124', groupList: [] },
{ name: '定远县', id: '341125', groupList: [] },
{ name: '凤阳县', id: '341126', groupList: [] },
{ name: '天长市', id: '341181', groupList: [] },
{ name: '明光市', id: '341182', groupList: [] },
],
},
{
name: '阜阳市',
id: '341200',
groupList: [
{ name: '颍州区', id: '341202', groupList: [] },
{ name: '颍东区', id: '341203', groupList: [] },
{ name: '颍泉区', id: '341204', groupList: [] },
{ name: '临泉县', id: '341221', groupList: [] },
{ name: '太和县', id: '341222', groupList: [] },
{ name: '阜南县', id: '341225', groupList: [] },
{ name: '颍上县', id: '341226', groupList: [] },
{ name: '界首市', id: '341282', groupList: [] },
],
},
{
name: '宿州市',
id: '341300',
groupList: [
{ name: '埇桥区', id: '341302', groupList: [] },
{ name: '砀山县', id: '341321', groupList: [] },
{ name: '萧县', id: '341322', groupList: [] },
{ name: '灵璧县', id: '341323', groupList: [] },
{ name: '泗县', id: '341324', groupList: [] },
],
},
{
name: '六安市',
id: '341500',
groupList: [
{ name: '金安区', id: '341502', groupList: [] },
{ name: '裕安区', id: '341503', groupList: [] },
{ name: '叶集区', id: '341504', groupList: [] },
{ name: '霍邱县', id: '341522', groupList: [] },
{ name: '舒城县', id: '341523', groupList: [] },
{ name: '金寨县', id: '341524', groupList: [] },
{ name: '霍山县', id: '341525', groupList: [] },
],
},
{
name: '亳州市',
id: '341600',
groupList: [
{ name: '谯城区', id: '341602', groupList: [] },
{ name: '涡阳县', id: '341621', groupList: [] },
{ name: '蒙城县', id: '341622', groupList: [] },
{ name: '利辛县', id: '341623', groupList: [] },
],
},
{
name: '池州市',
id: '341700',
groupList: [
{ name: '贵池区', id: '341702', groupList: [] },
{ name: '东至县', id: '341721', groupList: [] },
{ name: '石台县', id: '341722', groupList: [] },
{ name: '青阳县', id: '341723', groupList: [] },
],
},
{
name: '宣城市',
id: '341800',
groupList: [
{ name: '宣州区', id: '341802', groupList: [] },
{ name: '郎溪县', id: '341821', groupList: [] },
{ name: '广德县', id: '341822', groupList: [] },
{ name: '泾县', id: '341823', groupList: [] },
{ name: '绩溪县', id: '341824', groupList: [] },
{ name: '旌德县', id: '341825', groupList: [] },
{ name: '宁国市', id: '341881', groupList: [] },
],
},
{
name: '黄山市',
id: '341000',
groupList: [
{ name: '屯溪区', id: '341002', groupList: [] },
{ name: '黄山区', id: '341003', groupList: [] },
{ name: '徽州区', id: '341004', groupList: [] },
{ name: '歙县', id: '341021', groupList: [] },
{ name: '休宁县', id: '341022', groupList: [] },
{ name: '黟县', id: '341023', groupList: [] },
{ name: '祁门县', id: '341024', groupList: [] },
],
},
],
},
{
name: '江苏省',
id: '320000',
groupList: [
{
name: '南京市',
id: '320100',
groupList: [
{ name: '玄武区', id: '320102', groupList: [] },
{ name: '秦淮区', id: '320104', groupList: [] },
{ name: '建邺区', id: '320105', groupList: [] },
{ name: '鼓楼区', id: '320106', groupList: [] },
{ name: '浦口区', id: '320111', groupList: [] },
{ name: '栖霞区', id: '320113', groupList: [] },
{ name: '雨花台区', id: '320114', groupList: [] },
{ name: '江宁区', id: '320115', groupList: [] },
{ name: '六合区', id: '320116', groupList: [] },
{ name: '溧水区', id: '320117', groupList: [] },
{ name: '高淳区', id: '320118', groupList: [] },
],
},
],
},
],
};
export const mockGetDeviceGroupTreeData = {
data: {
tree: [
@ -72,17 +289,34 @@ export const mockGetDeviceGroupTreeData = {
// 设备组列表分页
export const mockGetDeviceGroupListData = {
data: {
list: [
results: [
{
id: 1,
level: 1,
id: '0001',
createTime: '2023-10-17T10:43:31.254107+08:00',
updateTime: '2023-10-17T10:45:25.030034+08:00',
name: '上海节点',
address: '中国上海市浦东新区申迪北路',
fatherName: '无',
fatherId: '',
lon: 121.667987,
lat: 31.144417,
managerName: '刘芳',
managerPhone: '16526365632',
remark: '--',
// TODO 完成设备节点列表数据的构建
},
{
level: 1,
id: '0002',
createTime: '2023-10-17T10:43:31.254107+08:00',
updateTime: '2023-10-17T10:45:25.030034+08:00',
name: '南京节点',
code: 'DG00002',
address: '江苏省南京市南京市栖霞区紫东路南京紫东国际创意园',
address: '江苏省南京市南京市栖霞区紫东路南京紫东国际创意园', // 地址
telephone: '12345',
lon: '118.914349',
lat: '32.086019',
lon: '118.914349', // 经度
lat: '32.086019', // 纬度
managerName: '张三',
managerPhone: '111111111',
isEnable: true,
@ -90,7 +324,8 @@ export const mockGetDeviceGroupListData = {
remark: '',
children: [
{
id: 2,
level: 2,
id: '20001',
createTime: '2023-10-17T13:37:31.758471+08:00',
updateTime: '2023-10-17T13:39:31.530494+08:00',
name: '秦淮节点',
@ -105,10 +340,10 @@ export const mockGetDeviceGroupListData = {
parentFkId: 1,
remark: '',
children: null,
key: '2',
},
{
id: 3,
level: 2,
id: '20002',
createTime: '2023-10-17T13:40:28.823372+08:00',
updateTime: '2023-10-17T13:40:28.823372+08:00',
name: '江宁节点',
@ -123,52 +358,42 @@ export const mockGetDeviceGroupListData = {
parentFkId: 1,
remark: '',
children: null,
key: '3',
},
],
key: '1',
},
],
count: 2,
page: 1,
pageSize: 10,
},
};
/** 未启用 */
// 设备组节点选项列表
export const mockGetDeviceGroupFkSelectData = {
data: {
list: [
{
name: '南京节点',
id: 1,
},
{
name: '秦淮节点',
id: 2,
},
{
name: '江宁节点',
id: 3,
},
{
id: 4,
createTime: '2023-10-17T15:02:30.725705+08:00',
updateTime: '2023-10-17T15:02:30.725705+08:00',
name: '安徽节点',
code: 'DG00005',
address: '',
telephone: '',
lon: '',
lat: '',
managerName: '',
managerPhone: '',
isEnable: true,
parentFkId: 0,
remark: '',
children: [
{
id: 5,
createTime: '2023-10-17T15:05:13.542992+08:00',
updateTime: '2023-10-17T15:08:01.071315+08:00',
name: '合肥节点',
code: 'DG00006',
address: '安徽省合肥市包河区马鞍山路130号',
telephone: '',
lon: '117.309214',
lat: '31.862594',
managerName: '',
managerPhone: '',
isEnable: true,
parentFkId: 4,
remark: '',
children: null,
key: '5',
},
],
key: '4',
id: 4,
},
{
name: '合肥节点',
id: 5,
},
],
total: 0,
page: 1,
pageSize: 10,
},
};
@ -239,3 +464,107 @@ export const mockGetDeviceGroupData3 = {
},
},
};
// 节点信息
export const mockGetNodeInfo = [
{
name: '南京中山陵',
address: '中国江苏省南京市玄武区明孝陵路1号',
fatherName: '南京',
fatherId: 'NJ002',
lon: 118.835383,
lat: 32.036853,
managerName: '赵刚',
managerPhone: '025-8443 5328',
remark: '中山陵是中国江苏省南京市的一处著名纪念建筑,是国父孙中山的陵墓。',
},
{
name: '南京总统府',
address: '中国江苏省南京市玄武区长江路292号',
fatherName: '南京',
fatherId: 'NJ003',
lon: 118.796492,
lat: 32.054979,
managerName: '陈霞',
managerPhone: '025-8462 3951',
remark:
'南京总统府是中国南京市玄武区长江路292号的一座历史建筑是中华民国时期南京国民政府的办公地点。',
},
{
name: '东方明珠塔',
address: '中国上海市浦东新区陆家嘴环路1号',
fatherName: '上海',
fatherId: 'SH004',
lon: 121.506377,
lat: 31.239722,
managerName: '王静',
managerPhone: '021-5879 1888',
remark: '东方明珠塔是位于中国上海市浦东陆家嘴金融贸易区的一座电视塔,是上海的标志性建筑之一。',
},
{
name: '南京雨花台',
address: '中国江苏省南京市雨花台区雨花路78号',
fatherName: '南京',
fatherId: 'NJ004',
lon: 118.748754,
lat: 31.997532,
managerName: '张磊',
managerPhone: '025-5275 0088',
remark: '雨花台是中国江苏省南京市雨花台区的一个丘陵,也是南京市的一个知名旅游景点。',
},
{
name: '上海科技馆',
address: '中国上海市浦东新区世纪大道2000号',
fatherName: '上海',
fatherId: 'SH005',
lon: 121.537235,
lat: 31.216444,
managerName: '周洋',
managerPhone: '021-6854 6916',
remark: '上海科技馆是位于中国上海市浦东新区的一座科技展示馆,是上海市的科普教育基地之一。',
},
{
name: '南京中华门',
address: '中国江苏省南京市秦淮区中华门',
fatherName: '南京',
fatherId: 'NJ005',
lon: 118.790325,
lat: 32.046384,
managerName: '李明',
managerPhone: '025-8357 8845',
remark: '中华门是中国江苏省南京市秦淮区的一处历史古迹,是南京古城墙的门。',
},
{
name: '上海外滩观景台',
address: '中国上海市黄浦区中山东一路20号',
fatherName: '上海',
fatherId: 'SH006',
lon: 121.489562,
lat: 31.240737,
managerName: '王红',
managerPhone: '021-5879 8901',
remark: '上海外滩观景台位于上海市黄浦区外滩20号是游览上海外滩天际线的最佳地点之一。',
},
{
name: '南京中山陵石象路',
address: '中国江苏省南京市玄武区石象路',
fatherName: '南京',
fatherId: 'NJ006',
lon: 118.835168,
lat: 32.041867,
managerName: '刘军',
managerPhone: '025-8463 2211',
remark: '南京中山陵石象路是中国江苏省南京市玄武区的一条道路,沿路有许多历史建筑和景点。',
},
{
name: '上海豫园',
address: '中国上海市黄浦区安仁街137号',
fatherName: '上海',
fatherId: 'SH007',
lon: 121.492783,
lat: 31.227565,
managerName: '张艳',
managerPhone: '021-6328 8888',
remark: '上海豫园是位于上海市黄浦区的一座古典式园林,是上海市的主要旅游景点之一。',
},
];

@ -2,10 +2,12 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-17 13:58:57
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-17 14:05:50
* @LastEditTime: 2024-04-28 14:03:58
* @FilePath: \general-ai-manage\mock\pools\dictData.ts
* @Description: mock
*/
// 行业分类
export const mockGetIndustryDictData = {
data: {
results: [
@ -186,3 +188,12 @@ export const mockGetIndustryDictData = {
],
},
};
// 设备分类
export const mockGetDeviceTypeDictData = {
data: {
results: [
{ id: 1002000, name: '摄像头' },
{ id: 1002002, name: '控制器' },
],
},
};

@ -0,0 +1,4 @@
<svg t="1714117834638" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="3262" width="200" height="200">
<path d="M320 0 192 128l384 384-384 384 128 128 512-512L320 0z" p-id="3263" fill="#154DDD"></path>
</svg>

After

Width:  |  Height:  |  Size: 260 B

@ -0,0 +1 @@
<svg t="1714117834638" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3262" width="200" height="200"><path d="M320 0 192 128l384 384-384 384 128 128 512-512L320 0z" p-id="3263" fill="#999999"></path></svg>

After

Width:  |  Height:  |  Size: 250 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@ -12,6 +12,12 @@
background-color: transparent;
padding-inline: 0 !important;
}
.app_page_wrap .page_menu .ant-menu {
background-image: url(../public/home/menu_footer_bg.png);
background-repeat: no-repeat;
background-position: left bottom;
background-size: cover;
}
.app_page_wrap .page_body {
width: calc(100vw - 200px);
}
@ -19,10 +25,10 @@
position: fixed;
top: 0;
right: 0;
z-index: 999;
width: calc(100vw - 200px);
min-width: 100px;
height: 50px;
z-index: 999;
padding: 0 24px 0 20px;
background-color: #f8fafd;
}

@ -12,6 +12,17 @@
background-color: transparent;
padding-inline: 0 !important;
}
.page_menu {
// menu_footer_bg.png
.ant-menu {
// background-color: red;
background-image: url(../public/home/menu_footer_bg.png);
background-repeat: no-repeat;
background-position: left bottom;
background-size: cover;
}
}
.page_body {
width: calc(100vw - 200px);
.body_nav_bar {
@ -23,6 +34,7 @@
min-width: 100px;
height: 50px;
padding: 0 24px 0 20px;
background-color: #f8fafd;
.avatar_box {
& > img {

@ -145,7 +145,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
// if (initialState?.loading) return <PageLoading />;
return (
<div className="flex app_page_wrap">
<div style={{ width: '200px', position: 'relative' }}>
<div style={{ width: '200px', position: 'relative' }} className="page_menu">
<MenuBar
menuData={initialState?.menuData}
changeMenu={(record) => setCurrMenu(record)}

@ -39,6 +39,7 @@
text-align: left;
text-transform: none;
}
.head5 {
color: #333333;
font-weight: bold;
@ -61,6 +62,3 @@
text-align: left;
text-transform: none;
}
body {
}

@ -1,11 +1,11 @@
.menubar_wrap {
position: fixed;
left: 0;
top: 0;
height: 100vh;
display: flex;
left: 0;
z-index: 999;
display: flex;
width: 200px;
height: 100vh;
padding: 0;
background-color: #fff;
border-radius: 0px 0px 32px 0px;
@ -46,7 +46,6 @@
display: flex;
flex: 1;
width: 100%;
padding-top: 10px;
}
.menubar_wrap .menu_bottom {
width: 96px;

@ -52,7 +52,6 @@
display: flex;
flex: 1;
width: 100%;
padding-top: 10px;
}
.menu_bottom {
width: 96px;

@ -2,19 +2,20 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-03-27 16:03:20
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-23 10:48:58
* @LastEditTime: 2024-04-28 09:44:23
* @FilePath: \general-ai-manage\src\components\Header\index.tsx
* @Description: layout
*/
import { useBusinessInfo } from '@/hooks/useBusinessInfo';
import { history, useIntl } from '@umijs/max';
import { ReactComponent as BusinessLogoIcon } from '/public/home/business_logo.svg';
import type { MenuProps } from 'antd';
import { Menu } from 'antd';
import React, { useEffect, useState } from 'react';
import { ReactComponent as BusinessLogoIcon } from '/public/home/business_logo.svg';
import { ReactComponent as BusinessBigLogo } from '/public/home/business_big_logo.svg';
// import menuFooter from '/public/menuFooter.svg';
import './index.less';
@ -104,10 +105,21 @@ const MenuBar: React.FC<MenuBarProps> = ({ menuData, changeMenu }) => {
return (
<div className="flex flex-col items-center justify-between menubar_wrap">
<div className="flex items-center menu_top">
<BusinessLogoIcon></BusinessLogoIcon>
<span>{getStoreBusinessInfo()?.name}</span>
<div>
<div
className="flex items-center justify-center menu_top"
onClick={() => {
history.replace('/');
}}
>
<BusinessBigLogo></BusinessBigLogo>
</div>
<div className="flex items-center justify-center mx-[12px] mt-[16px] mb-[12px] p-[4px] bg-[#154DDD1a] text-[#154DDD] rounded-[8px] text-[14px]">
<BusinessLogoIcon></BusinessLogoIcon>
<span className="pl-[8px]">{getStoreBusinessInfo()?.name}</span>
</div>
</div>
{/* // TODO 菜单需要补充路由聚焦状态 */}
<div className="menu_center">
<Menu
@ -123,9 +135,6 @@ const MenuBar: React.FC<MenuBarProps> = ({ menuData, changeMenu }) => {
))}
</Menu>
</div>
{/* <div className="menu_bottom">
<img src={menuFooter} />
</div> */}
</div>
);
};

@ -0,0 +1,3 @@
import deviceGroupTree from './src/deviceGroupTree';
export const DeviceGroupTree = deviceGroupTree;

@ -0,0 +1,47 @@
.base_tree_wrap {
font-size: 14px;
}
.base_tree_wrap > .ant-form-item {
margin-bottom: 0;
}
.base_tree_wrap > .ant-form-item .ant-input-outlined {
border: none;
}
.base_tree_wrap > .ant-form-item .ant-input:focus,
.base_tree_wrap > .ant-form-item .ant-input-focused {
border: none;
box-shadow: none;
/* 去掉聚焦时的阴影效果 */
/* 如果需要,你也可以调整其他聚焦时的样式 */
}
.base_tree_wrap > .ant-form-item .ant-input-outlined:focus,
.base_tree_wrap > .ant-form-item .ant-input-outlined:focus-within {
box-shadow: none;
/* 去掉聚焦时的阴影效果 */
}
.base_tree_wrap .tree_node_1 {
width: 100%;
margin: 12px 0;
padding: 8px 17px;
background: #f5f5f5;
border-radius: 4px;
cursor: pointer;
}
.base_tree_wrap .tree_node_1 .ant-tree-switcher-noop {
opacity: 0;
}
.base_tree_wrap .tree_node_item .action_list {
display: none;
}
.base_tree_wrap .tree_node_1:hover,
.base_tree_wrap .ant-tree-treenode-selected {
color: #154ddd;
background: rgba(21, 77, 221, 0.1);
border-radius: 8px;
}
.base_tree_wrap .tree_node_item:hover .action_list {
display: block;
}
.base_tree_wrap .tree_node_item:hover .action_list .anticon-delete {
color: #e80d0d;
}

@ -0,0 +1,53 @@
.base_tree_wrap {
font-size: 14px;
& > .ant-form-item {
margin-bottom: 0;
.ant-input-outlined {
border: none;
}
.ant-input:focus,
.ant-input-focused {
border: none;
box-shadow: none; /* 去掉聚焦时的阴影效果 */
/* 如果需要,你也可以调整其他聚焦时的样式 */
}
.ant-input-outlined:focus,
.ant-input-outlined:focus-within {
box-shadow: none; /* 去掉聚焦时的阴影效果 */
}
}
.tree_node_1 {
width: 100%;
margin: 12px 0;
padding: 8px 17px;
background: #f5f5f5;
border-radius: 4px;
cursor: pointer;
.ant-tree-switcher-noop {
opacity: 0;
}
}
.tree_node_item {
.action_list {
display: none;
}
}
.tree_node_1:hover,
.ant-tree-treenode-selected {
color: #154ddd;
background: rgba(21, 77, 221, 0.1);
border-radius: 8px;
}
.tree_node_item:hover {
// color: #154ddd;
// background: rgba(21, 77, 221, 0.1);
// border-radius: 8px;
.action_list {
display: block;
.anticon-delete {
color: #e80d0d;
}
}
}
}

@ -0,0 +1,247 @@
import { formatTreeValByKey } from '@/utils/baseTree';
import { DeleteFilled, SearchOutlined } from '@ant-design/icons';
import { Button, Form, Input, Modal, Tree } from 'antd';
import _debounce from 'lodash/debounce';
import React, { useEffect, useState } from 'react';
import { BaseTreeProps } from '../typing';
import { ProFormText } from '@ant-design/pro-components';
import './baseTree.less';
const { TreeNode } = Tree;
const BaseTree: React.FC<BaseTreeProps> = (props) => {
/**state */
// const { token } = theme.useToken();
const { treeData, ...defaultProps } = props;
console.log(treeData, 'treeData', defaultProps);
const [currTreeData, setCurrTreeData] = useState([
// { title: '节点1', key: '1' },
// { title: '节点2', key: '2', children: [{ title: '子节点1', key: '3' }] },
]);
const [visible, setVisible] = useState(false);
const [editNodeKey, setEditNodeKey] = useState(null);
const [form] = Form.useForm();
const action_add_key = '';
/**工具方法集 */
// 柯里化-树的节点数值
function formatTreeValByNodeKey(
key: 'title' | 'key' | 'children',
node: Record<string, any>,
): any {
return formatTreeValByKey(key, node, props.fieldNames);
}
/**查询树节点 */
// 定义一个防抖处理函数500ms 后执行 handleChange 函数
const debouncedHandleChange = _debounce((value) => {
// TODO 查询所有匹配的节点
console.log('debouncedHandleChange_value:', value);
}, 500);
// 搜索节点
function handleSearch(val) {
debouncedHandleChange(val);
}
const handleAddNode = () => {
// setVisible(true);
// form.resetFields();
};
// const getNodeByKey = (key) => {
// return currTreeData.find((node) => node.key === key);
// };
// const removeNodeByKey = (data, key) => {
// return data.filter((node) => {
// if (node.key === key) {
// return false;
// }
// if (node.children) {
// node.children = removeNodeByKey(node.children, key);
// }
// return true;
// });
// };
// const handleEditNode = (key) => {
// setEditNodeKey(key);
// setVisible(true);
// form.setFieldsValue({ nodeTitle: getNodeByKey(key).title });
// };
// const handleDeleteNode = (key) => {
// const newData = removeNodeByKey(currTreeData, key);
// setCurrTreeData(newData);
// };
// const handleAddChildNode = (key) => {
// setEditNodeKey(key);
// setVisible(true);
// form.resetFields();
// };
// 组装节点数据
const handleModalOk = () => {
form.validateFields().then((values) => {
const { nodeTitle } = values;
let newData;
if (editNodeKey) {
// 编辑节点标题
newData = currTreeData.map((node) => {
if (node.key === editNodeKey) {
return { ...node, title: nodeTitle };
}
return node;
});
} else if (editNodeKey === null) {
// 新增根节点
newData = [...currTreeData, { title: nodeTitle, key: Date.now().toString() }];
} else {
// 新增子节点
newData = currTreeData.map((node) => {
if (node.key === editNodeKey) {
return {
...node,
children: [
...(node.children || []),
{ title: nodeTitle, key: Date.now().toString() },
],
};
}
return node;
});
}
console.log('handleModalOk_newData', newData);
setCurrTreeData(newData);
setVisible(false);
setEditNodeKey('');
});
};
const handleModalCancel = () => {
setVisible(false);
setEditNodeKey('');
};
// render 新增节点操作
const renderAddAction = (node?: Record<string, any>) => {
// console.log(node, 'renderAddAction_node');
return props?.addRender ? (
props?.addRender(node)
) : (
<div onClick={handleAddNode}>
<Button></Button>
</div>
);
};
// 定义每个节点内容 递归操作
const renderTreeNodes = (data) => {
if (Array.isArray(data) && data.length) {
return data.map((node) => {
return (
<TreeNode
{...node}
title={formatTreeValByNodeKey('title', node)}
key={formatTreeValByNodeKey('key', node)}
className={`tree_node_item tree_node_${node.level}`}
>
{formatTreeValByNodeKey('children', node) &&
renderTreeNodes(formatTreeValByNodeKey('children', node))}
<TreeNode key={formatTreeValByNodeKey('key', node) + action_add_key}></TreeNode>
</TreeNode>
);
});
}
return <></>;
};
// 渲染每个节点内容
const renderTreeItem = (node) => {
return (
<div className="flex w-full">
<span className="w-[140px]">{node.title}</span>
{!props?.hideInDelete ? (
<span
className="action_list"
onClick={() => {
props.handleDelete(node);
}}
>
<DeleteFilled />
</span>
) : (
<></>
)}
</div>
);
};
useEffect(() => {
setCurrTreeData(props.treeData);
console.log(props.treeData, 'props_treeData');
}, [props.treeData]);
return (
<div className="base_tree_wrap">
{!props?.hideInSearch ? (
<ProFormText
className="tree_search_item"
onChange={(e) => handleSearch(e.target.value)}
key="name"
name="name"
label=""
fieldProps={{
style: {
margin: 0,
},
prefix: <SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />,
}}
/>
) : (
<></>
)}
<Tree
defaultExpandAll
onSelect={(selectedKeys, info) => {
console.log('selected', selectedKeys);
props.selectTree(selectedKeys, info);
}}
titleRender={(node) => {
if (node.title) {
return renderTreeItem(node);
}
return renderAddAction(node);
}}
{...defaultProps}
>
{renderTreeNodes(currTreeData)}
</Tree>
<div className="tree_node_1">{renderAddAction()}</div>
{/* <div onClick={handleAddNode}>添加一级节点</div> */}
{/* TODO 使用ProForm */}
<Modal
title={editNodeKey ? '编辑节点' : '新增节点'}
visible={visible}
onOk={handleModalOk}
onCancel={handleModalCancel}
>
<Form form={form}>
<Form.Item
name="nodeTitle"
label="节点名称"
rules={[{ required: true, message: '请输入节点名称' }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default BaseTree;

@ -0,0 +1,72 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-26 11:11:05
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-29 16:26:05
* @FilePath: \general-ai-manage\src\components\Tree\src\deviceGroupTree.tsx
* @Description:
* @
* 1
* 2
* 3
* 4
*/
import { PlusCircleOutlined } from '@ant-design/icons';
import React from 'react';
import { theme } from 'antd';
import { DeviceGroupTreeProps } from '../typing';
import BaseTree from './baseTree';
const DeviceGroupTree: React.FC<DeviceGroupTreeProps> = (props) => {
const { token } = theme.useToken();
console.log('token', token);
// async function loadTree() {
// const resp = await getDeviceGroupSettingTree()
// setDeviceGroupList(resp.data)
// }
// useEffect(()=>{
// loadTree()
// },[])
// const [expandedKeys] = useState(['0-0', '0-0-0', '0-0-0-0']);
function handleAdd(node) {
// 当前节点的父节点 可以添加数据
console.log(node, 'handleAdd_node');
props.addTreeNode(node);
}
return (
<div className="deviceGroup_tree_wrap">
<BaseTree
treeData={props.dataSource}
fieldNames={{ title: 'name', key: 'id', children: 'children' }}
addRender={(node) => {
return (
<div
onClick={() => {
handleAdd(node);
}}
style={{ color: token.colorPrimary }}
key={node?.key || 'add'}
>
<PlusCircleOutlined />
<span className="pl-[10px]"> {node?.key ? '添加子节点' : '添加节点'} </span>
</div>
);
}}
handleDelete={(node) => {
// 提示删除
console.log('handleDelete_node', node);
}}
selectTree={(selectedKeys, info) => {
props.selectTree(selectedKeys, info);
}}
></BaseTree>
{/* 新增节点 修改节点 */}
</div>
);
};
export default DeviceGroupTree;

@ -0,0 +1,25 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-26 11:09:49
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-29 16:24:05
* @FilePath: \general-ai-platform-web\src\components\Tree\typing.ts
* @Description:
*/
// 增删改查基础树
export interface BaseTreeProps extends TreeProps {
hideInAdd?: boolean; // 是否隐藏新增节点
// hideInEdit?: boolean // 是否隐藏编辑节点
hideInDelete?: boolean; // 是否隐藏删除节点
hideInSearch?: boolean; // 是否隐藏筛选节点
addRender?: React.ReactNode; // 新增操作自定义
addText?: string | React.ReactNode; // 新增按钮文案自定义
deleteRender?: React.ReactNode; // 删除操作自定义
deleteText?: string | React.ReactNode; // 删除按钮文案自定义
}
// 节点树
export interface DeviceGroupTreeProps extends BaseTreeProps {
addTreeNode?: (arg1: Record<string, any>) => void;
selectTree: (arg1: any, arg2: any) => void;
}

@ -0,0 +1,23 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-28 15:30:31
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-28 15:32:43
* @FilePath: \general-ai-platform-web\src\enums\device.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 告警规则
export const deviceGroupEnums: Record<string, any>[] = [
{
label: '设备列表',
key: '1',
},
{
label: '业务模型部署',
key: '2',
},
{
label: '告警设置',
key: '3',
},
];

@ -88,6 +88,9 @@ ol {
.gn_form .ant-pro-form-group-container {
gap: 0px 12px !important;
}
.gn_modal_form {
margin-top: 16px;
}
.gn_table_query_filter {
padding: 20px 0;
}
@ -114,14 +117,12 @@ ol {
margin-right: 0;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item .ant-steps-item-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin: 8px 0;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item .ant-steps-item-tail {
display: none;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item .ant-steps-item-icon {
margin: 0 8px;
}
@ -137,6 +138,21 @@ ol {
color: #666666;
font-size: 12px;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item .ant-steps-item-tail {
position: absolute;
top: auto;
left: calc(100% - 30px);
width: 16px;
height: 16px;
margin: 0;
padding: 0;
background: url('../public/home/business_arrow_default.svg') no-repeat;
background-size: cover;
border: none;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item .ant-steps-item-tail:after {
background: transparent;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-finish {
background: #e5edff;
border: 1px solid #154ddd;
@ -153,6 +169,10 @@ ol {
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-finish .ant-steps-item-description {
color: #154ddd;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-finish .ant-steps-item-tail {
background: url('../public/home/business_arrow.svg') no-repeat;
background-size: cover;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-active {
background: #e5edff;
border: 1px solid #154ddd;
@ -163,10 +183,15 @@ ol {
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-active .ant-steps-item-description {
color: #154ddd;
}
.gn_model_steps_form .ant-modal-content .ant-modal-body .ant-steps .ant-steps-item-active .ant-steps-item-tail {
background: url('../public/home/business_arrow.svg') no-repeat;
background-size: cover;
}
/* Descriptions */
.gn_table_descriptions {
padding: 16px 16px 0;
background: #ffffff;
border: 1px solid rgba(21, 77, 221, 0.1);
border-radius: 8px;
}
/* ProTable ProList */
@ -188,6 +213,21 @@ ol {
padding: 0;
}
/* 主题样式 */
.ant-btn-primary,
.ant-btn-primary:not(:disabled):not(.ant-btn-disabled):hover {
background-color: #154ddd;
}
.ant-btn-default:not(:disabled):not(.ant-btn-disabled):hover,
.ant-btn-link {
color: #154ddd;
}
:where(.css-dev-only-do-not-override-42nv3w).ant-btn-default:not(:disabled):not(
.ant-btn-disabled
):hover {
color: #154ddd;
background: #ffffff;
border-color: #154ddd;
}
/* 单行文本溢出显示省略号 */
.single_line {
overflow: hidden;

@ -114,6 +114,9 @@ ol {
gap: 0px 12px !important;
}
}
.gn_modal_form {
margin-top: 16px;
}
// 筛选表单
.gn_table_query_filter {
@ -147,14 +150,13 @@ ol {
}
.ant-steps-item-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin: 8px 0;
}
.ant-steps-item-tail {
display: none;
}
.ant-steps-item-icon {
margin: 0 8px;
}
@ -170,6 +172,25 @@ ol {
color: #666666;
font-size: 12px;
}
.ant-steps-item-tail {
// display: none;
position: absolute;
top: auto;
left: calc(100% - 30px);
width: 16px;
height: 16px;
margin: 0;
padding: 0;
// background-color: red;
background: url('../public/home/business_arrow_default.svg') no-repeat;
background-size: cover;
border: none;
// right: -10px;
&:after {
background: transparent;
}
}
}
// 已完成
@ -188,6 +209,10 @@ ol {
.ant-steps-item-description {
color: #154ddd;
}
.ant-steps-item-tail {
background: url('../public/home/business_arrow.svg') no-repeat;
background-size: cover;
}
}
// 当前选中
.ant-steps-item-active {
@ -199,6 +224,10 @@ ol {
.ant-steps-item-description {
color: #154ddd;
}
.ant-steps-item-tail {
background: url('../public/home/business_arrow.svg') no-repeat;
background-size: cover;
}
}
}
}
@ -208,6 +237,7 @@ ol {
.gn_table_descriptions {
padding: 16px 16px 0;
background: #ffffff;
border: 1px solid rgba(21, 77, 221, 0.1);
border-radius: 8px;
}
@ -235,21 +265,21 @@ ol {
}
/* 主题样式 */
// .ant-btn-primary,
// .ant-btn-primary:not(:disabled):not(.ant-btn-disabled):hover {
// background-color: @theme_color;
// }
// .ant-btn-default:not(:disabled):not(.ant-btn-disabled):hover,
// .ant-btn-link {
// color: @theme_color;
// }
// :where(.css-dev-only-do-not-override-42nv3w).ant-btn-default:not(:disabled):not(
// .ant-btn-disabled
// ):hover {
// color: @theme_color;
// background: #ffffff;
// border-color: @theme_color;
// }
.ant-btn-primary,
.ant-btn-primary:not(:disabled):not(.ant-btn-disabled):hover {
background-color: @theme_color;
}
.ant-btn-default:not(:disabled):not(.ant-btn-disabled):hover,
.ant-btn-link {
color: @theme_color;
}
:where(.css-dev-only-do-not-override-42nv3w).ant-btn-default:not(:disabled):not(
.ant-btn-disabled
):hover {
color: @theme_color;
background: #ffffff;
border-color: @theme_color;
}
/* 单行文本溢出显示省略号 */
.single_line {

@ -1,28 +1,74 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-11-21 14:23:38
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-29 17:37:55
* @FilePath: \general-ai-platform-web\src\locales\zh-CN\device.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @Description:
*/
export const device: { [key: string]: string } = {
'device.device.table.list.id': 'ID',
'device.device.table.list.name': '设备名称',
'device.device.table.list.code': '设备代码',
'device.device.table.list.position': '位置',
'device.device.table.list.param': '设备参数',
'device.device.table.list.spec': '设备规格',
'device.device.table.list.categoryFkId': '设备类别',
'device.device.table.list.groupFkId': '设备分组',
'device.device.table.list.isEnable': '是否启用',
'device.device.table.list.remark': '备注',
'device.device.table.list.createTime': '创建时间',
'device.device.table.list.updateTime': '更新时间',
'device.device.table.rule.required.name': '设备名称为必填项',
'device.device.table.rule.required.code': '设备代码为必填项',
'device.device.table.list.add': '新建设备',
'device.device.table.list.update': '更新设备',
// 节点设置
export const device_group: { [key: string]: string } = {
'device_group.tree_node.name': '节点名称',
'device_group.tree_node.rule.required.name': '请填写节点名称',
'device_group.tree_node.fatherName': '上级节点',
'device_group.tree_node.lon': '经度',
'device_group.tree_node.lat': '纬度',
'device_group.tree_node.address': '地址',
'device_group.tree_node.lonlat': '经纬度',
'device_group.tree_node.managerName': '负责人',
'device_group.tree_node.managerPhone': '联系方式',
'device_group.tree_node.remark': '简介',
'device_group.tree_node.createForm.add': '新建节点',
// 未启用
'device_group.table.list.id': 'ID',
'device_group.table.list.name': '分组名称',
'device_group.table.list.code': '分组代码',
'device_group.table.list.address': '地址',
'device_group.table.list.telephone': '电话',
'device_group.table.list.lon': '经度',
'device_group.table.list.lat': '纬度',
'device_group.table.list.managerName': '负责人姓名',
'device_group.table.list.managerPhone': '负责人联系方式',
'device_group.table.list.isEnable': '是否启用',
'device_group.table.list.parentFkId': '父节点',
'device_group.table.list.remark': '备注',
'device_group.table.list.createTime': '创建时间',
'device_group.table.list.updateTime': '更新时间',
'device_group.table.rule.required.name': '分组名称为必填项',
'device_group.table.rule.required.code': '分组代码为必填项',
'device_group.table.rule.required.managerName': '负责人姓名为必填项',
'device_group.table.rule.required.managerPhone': '负责人联系方式为必填项',
'device_group.table.rule.required.parentFkId': '父节点为必填项',
'device_group.table.list.add': '新建设备分组',
'device_group.table.list.update': '更新设备分组',
'device_group.table.list.treeAdd': '添加根节点',
};
export const device_group_list: { [key: string]: string } = {
'device_group_list.table.list.name': '设备名称',
'device_group_list.table.list.deviceType': '设备类型',
'device_group_list.table.list.isEnable': '模型部署',
'device_group_list.table.list.deployed.isEnable': '已部署',
'device_group_list.table.list.undeployed.isEnable': '未部署',
'device_group_list.table.list.action.setModel': '基础模型配置',
'device_group_list.table.list.action.new': '新建设备',
'device_group_list.table.list.action.modelType': '设备分类',
// 未启用
'device_group_list.table.list.code': '设备代码',
'device_group_list.table.list.position': '位置',
'device_group_list.table.list.param': '设备参数',
'device_group_list.table.list.spec': '设备规格',
'device_group_list.table.list.categoryFkId': '设备类别',
'device_group_list.table.list.groupFkId': '设备分组',
'device_group_list.table.list.remark': '备注',
'device_group_list.table.list.createTime': '创建时间',
'device_group_list.table.list.updateTime': '更新时间',
'device_group_list.table.rule.required.name': '设备名称为必填项',
'device_group_list.table.rule.required.code': '设备代码为必填项',
'device_group_list.table.list.add': '新建设备',
'device_group_list.table.list.update': '更新设备',
};
export const device_category: { [key: string]: string } = {
'device.device_category.table.list.id': 'ID',
@ -36,30 +82,7 @@ export const device_category: { [key: string]: string } = {
'device.device_category.table.list.add': '新建设备类别',
'device.device_category.table.list.update': '更新设备类别',
};
export const device_group: { [key: string]: string } = {
'device.device_group.table.list.id': 'ID',
'device.device_group.table.list.name': '分组名称',
'device.device_group.table.list.code': '分组代码',
'device.device_group.table.list.address': '地址',
'device.device_group.table.list.telephone': '电话',
'device.device_group.table.list.lon': '经度',
'device.device_group.table.list.lat': '纬度',
'device.device_group.table.list.managerName': '负责人姓名',
'device.device_group.table.list.managerPhone': '负责人联系方式',
'device.device_group.table.list.isEnable': '是否启用',
'device.device_group.table.list.parentFkId': '父节点',
'device.device_group.table.list.remark': '备注',
'device.device_group.table.list.createTime': '创建时间',
'device.device_group.table.list.updateTime': '更新时间',
'device.device_group.table.rule.required.name': '分组名称为必填项',
'device.device_group.table.rule.required.code': '分组代码为必填项',
'device.device_group.table.rule.required.managerName': '负责人姓名为必填项',
'device.device_group.table.rule.required.managerPhone': '负责人联系方式为必填项',
'device.device_group.table.rule.required.parentFkId': '父节点为必填项',
'device.device_group.table.list.add': '新建设备分组',
'device.device_group.table.list.update': '更新设备分组',
'device.device_group.table.list.treeAdd': '添加根节点',
};
export const device_relation: { [key: string]: string } = {
'device.device_relation.table.list.id': 'ID',
'device.device_relation.table.list.deviceParentFkId': '设备父节点',

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-01 11:20:09
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-23 10:51:24
* @LastEditTime: 2024-04-26 09:36:38
* @FilePath: \uighur-recognition-web2\src\locales\zh-CN\menu.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -64,7 +64,7 @@ export default {
'menu.model-runtime-lib': '模型运行库',
'menu.business-info-index': '企业信息',
'menu.business-node-setting': '节点设置',
'menu.business-device-group': '节点设置',
'menu.business-model-index': '业务模型',
// 待废弃
'menu.realTime': '实时分析',

@ -0,0 +1,62 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-23 17:00:00
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-28 17:55:58
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\baseInfo.tsx
* @Description:
*
*/
import { ProDescriptions } from '@ant-design/pro-components';
import { FormattedMessage } from '@umijs/max';
type BaseInfoProps = {
info: Record<string, any>;
};
const BaseInfo: React.FC<BaseInfoProps> = ({ info }) => {
// 设备基本信息
const DeviceDetailColumns = [
{
title: <FormattedMessage id="device_group.tree_node.name" defaultMessage="节点名称" />,
dataIndex: 'name',
},
{
title: <FormattedMessage id="device_group.tree_node.fatherName" defaultMessage="上级节点" />,
dataIndex: 'fatherName',
},
{
title: <FormattedMessage id="device_group.tree_node.address" defaultMessage="地址" />,
dataIndex: 'address',
},
{
title: <FormattedMessage id="device_group.tree_node.lonlat" defaultMessage="经纬度" />,
dataIndex: 'lonlat',
render: (_, record) => {
return record?.lon + ',' + record?.lat;
},
},
{
title: <FormattedMessage id="device_group.tree_node.managerName" defaultMessage="负责人" />,
dataIndex: 'managerName',
},
{
title: (
<FormattedMessage id="device_group.tree_node.managerPhone" defaultMessage="联系方式" />
),
dataIndex: 'managerPhone',
},
{
title: <FormattedMessage id="device_group.tree_node.remark" defaultMessage="简介" />,
dataIndex: 'remark',
},
];
return (
<div className="gn_table_descriptions bg_active_1">
<ProDescriptions column={3} columns={DeviceDetailColumns} dataSource={info}></ProDescriptions>
</div>
);
};
export default BaseInfo;

@ -0,0 +1,175 @@
// import { postModelCategoryCreateModelCategory } from '@/services/resource/ModelCategory';
import { ModalForm, ProForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
} from '../../../../../config/defaultForm';
// @ts-ignore
export type CreateDeviceFormProps = {
createModalOpen: boolean;
handleModal: () => void;
reload: any;
};
const CreateDeviceForm: React.FC<CreateDeviceFormProps> = (props) => {
const intl = useIntl();
// const [isAuto, setIsAuto] = useState(true);
const [form] = Form.useForm<API.ModelCategory>();
return (
<ModalForm<API.ModelCategory>
className="gn_modal_form gn_form"
width={proFormSmallModelWidth}
title={intl.formatMessage({
id: 'device_group.tree_node.CreateDeviceForm.add',
defaultMessage: '新建',
})}
open={props.createModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
console.log(values, 'add_finish_values');
// TODO 对接新增接口
// postModelCategoryCreateModelCategory(values)
// .then(() => {
// message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// });
props.handleModal();
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="name"
label={<FormattedMessage id="device_group.tree_node.name" defaultMessage="名称" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="device_group.tree_node.rule.required.name"
defaultMessage="名称必填"
/>
),
},
]}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="fatherName"
label={
<FormattedMessage id="device_group.tree_node.fatherName" defaultMessage="上级节点" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.fatherName',
defaultMessage: '$$$',
})}`}
disabled
/>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="address"
label={<FormattedMessage id="device_group.tree_node.address" defaultMessage="地址" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.address',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="lon"
label={<FormattedMessage id="device_group.tree_node.lon" defaultMessage="经度" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.lon',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="lat"
label={<FormattedMessage id="device_group.tree_node.lat" defaultMessage="纬度" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.lat',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="managerName"
label={
<FormattedMessage id="device_group.tree_node.managerName" defaultMessage="负责人" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.managerName',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="managerPhone"
label={
<FormattedMessage id="device_group.tree_node.managerPhone" defaultMessage="联系方式" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.managerPhone',
defaultMessage: '$$$',
})}`}
/>
<ProFormTextArea
width={proFormSmallItemStyleProps.width}
name="remark"
label={<FormattedMessage id="device_group.tree_node.remark" defaultMessage="简介" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.remark',
defaultMessage: '$$$',
})}`}
/>
</ProForm.Group>
</ModalForm>
);
};
export default CreateDeviceForm;

@ -0,0 +1,176 @@
// import { postModelCategoryCreateModelCategory } from '@/services/resource/ModelCategory';
import { ModalForm, ProForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
} from '../../../../../config/defaultForm';
// @ts-ignore
export type CreateFormProps = {
createModalOpen: boolean;
handleModal: () => void;
parentInfo?: Record<string, any>;
reload: any;
};
const CreateForm: React.FC<CreateFormProps> = (props) => {
const intl = useIntl();
// const [isAuto, setIsAuto] = useState(true);
const [form] = Form.useForm<API.ModelCategory>();
return (
<ModalForm<API.ModelCategory>
className="gn_modal_form gn_form"
width={proFormSmallModelWidth}
title={intl.formatMessage({
id: 'device_group.tree_node.createForm.add',
defaultMessage: '新建',
})}
open={props.createModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
console.log(values, 'add_finish_values');
// TODO 对接新增接口
// postModelCategoryCreateModelCategory(values)
// .then(() => {
// message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// });
props.handleModal();
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="name"
label={<FormattedMessage id="device_group.tree_node.name" defaultMessage="名称" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="device_group.tree_node.rule.required.name"
defaultMessage="名称必填"
/>
),
},
]}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="fatherName"
label={
<FormattedMessage id="device_group.tree_node.fatherName" defaultMessage="上级节点" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.fatherName',
defaultMessage: '$$$',
})}`}
disabled
/>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="address"
label={<FormattedMessage id="device_group.tree_node.address" defaultMessage="地址" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.address',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="lon"
label={<FormattedMessage id="device_group.tree_node.lon" defaultMessage="经度" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.lon',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="lat"
label={<FormattedMessage id="device_group.tree_node.lat" defaultMessage="纬度" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.lat',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="managerName"
label={
<FormattedMessage id="device_group.tree_node.managerName" defaultMessage="负责人" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.managerName',
defaultMessage: '$$$',
})}`}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width}
name="managerPhone"
label={
<FormattedMessage id="device_group.tree_node.managerPhone" defaultMessage="联系方式" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.managerPhone',
defaultMessage: '$$$',
})}`}
/>
<ProFormTextArea
width={proFormSmallItemStyleProps.width}
name="remark"
label={<FormattedMessage id="device_group.tree_node.remark" defaultMessage="简介" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'device_group.tree_node.remark',
defaultMessage: '$$$',
})}`}
/>
</ProForm.Group>
</ModalForm>
);
};
export default CreateForm;

@ -0,0 +1,246 @@
import TableActionCard from '@/components/TableActionCard';
import { getDeviceListByGroup } from '@/services/testApi/device';
import { ProTable } from '@ant-design/pro-components';
import { Access, FormattedMessage, history, useAccess } from '@umijs/max';
import { Button } from 'antd';
import { useRef, useState } from 'react';
import { proTablePaginationOptions } from '../../../../../config/defaultTable';
import IsDelete from '@/components/TableActionCard/isDelete';
import CreateDeviceForm from './createDeviceForm';
type DeviceListProps = {
info: Record<string, any>;
};
const DeviceList: React.FC<DeviceListProps> = () => {
const access = useAccess();
// const intl = useIntl();
const actionRef = useRef<ActionType>();
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [categoryFkIdIds, setCategoryFkIdIds] = useState([]);
// 动态设置每页数量
const [currentPageSize, setCurrentPageSize] = useState<number>(10);
/**新增 编辑 删除 */
// 新增
const handleCreateModal = () => {
setCreateModalOpen(!createModalOpen);
};
function reloadList() {
actionRef.current?.reload();
}
const columns: ProColumns<Record<string, any>>[] = [
{
title: <FormattedMessage id="device_group_list.table.list.name" defaultMessage="设备名称" />,
dataIndex: 'name',
hideInSearch: true,
// width: 80,
// key: 'fixedName',
// fixed: 'left',
},
{
title: (
<FormattedMessage id="device_group_list.table.list.deviceType" defaultMessage="设备类型" />
),
dataIndex: 'deviceType',
hideInSearch: true,
// width: 120,
},
{
title: (
<FormattedMessage id="device_group_list.table.list.isEnable" defaultMessage="模型部署" />
),
dataIndex: 'isEnable',
hideInSearch: true,
// width: 80,
render: (dom, record) => {
return (
<div
className={`model_index_type_tag flex items-center justify-center ${
record.isEnable ? 'active1' : 'active2'
}`}
>
<span className="dot"></span>
<span>
{record.isEnable ? (
<FormattedMessage
id="device_group_list.table.list.deployed.isEnable"
defaultMessage="已部署"
/>
) : (
<FormattedMessage
id="device_group_list.table.list.undeployed.isEnable"
defaultMessage="未部署"
/>
)}
</span>
</div>
);
},
},
{
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
key: 'option',
render: (_, record) => [
<TableActionCard
key="TableActionCardRef"
renderActions={[
// TODO 设为默认交互补充
{
key: 'setModel',
renderDom: (
<Button
key="setModel"
type="link"
size="small"
onClick={() => {
// setCurrentRow(record);
// history.push('/home/model-detail');
// doToDetail(record);
// setShowDetail(true);
}}
>
<FormattedMessage
id="device_group_list.table.list.action.setModel"
defaultMessage="基础模型配置"
/>
</Button>
),
},
{
key: 'updateDetail',
renderDom: (
<Button
key="update"
type="link"
size="small"
onClick={() => {
// TODO 编辑在新增联调后实现
// setCurrentRow(record);
// history.push('/home/model-detail');
// doToDetail(record);
// setShowDetail(true);
// setUpdateModalOpen(true);
}}
>
<FormattedMessage id="pages.searchTable.update" defaultMessage="编辑" />
</Button>
),
},
{
key: 'destroy',
renderDom: (
<IsDelete
deleteApi={() => {
handleDestroy(record).then(() => {});
}}
></IsDelete>
),
},
]}
></TableActionCard>,
],
},
];
return (
<div className="dg_deviceList_wrap">
<ProTable
className="gn_pro_table"
cardProps={{
bodyStyle: { padding: 0 },
}}
// 标题栏
toolBarRender={() => [
<Access
accessible={access.canUpdate(history.location.pathname)}
key={`${history.location.pathname}-add`}
>
<div className="mb-[16px] ">
<Button
className="mr-[12px]"
type="primary"
key="add"
onClick={() => {
setCreateModalOpen(true);
}}
>
<FormattedMessage
id="device_group_list.table.list.action.new"
defaultMessage="新建"
/>
</Button>
<Button
key="modelType"
onClick={() => {
setCreateModalOpen(true);
}}
>
<FormattedMessage
id="device_group_list.table.list.action.modelType"
defaultMessage="设备分类"
/>
</Button>
</div>
</Access>,
]}
search={false}
// scroll={{ y: proTableCommonOptions.commscrollY, x: proTableCommonOptions.commscrollX }}
options={{ fullScreen: false, setting: false, density: false, reload: false }}
actionRef={actionRef}
rowKey="key"
onDataSourceChange={(data) => {
console.log(data, 'onDataSourceChange_data');
// let CategoryFkIdIds: any = data.map((v) => {
// return v.categoryFkId;
// });
// setCategoryFkIdIds(CategoryFkIdIds);
}}
pagination={{
...proTablePaginationOptions,
pageSize: currentPageSize,
onChange: (page, pageSize) => setCurrentPageSize(pageSize),
}}
columnsState={{
persistenceKey: 'algorithm_model_list',
persistenceType: 'localStorage',
}}
request={async (params = {}) => {
const { current, ...rest } = params;
const reqParams = {
page: current,
...rest,
};
let resp = await getDeviceListByGroup({ ...reqParams });
console.log(resp, 'getDeviceListByGroup_resp');
return {
data: resp.data?.results.map((v: Record<string, any>) => {
return { ...v, key: v.id };
}),
success: resp.success,
total: resp.data.count,
current: current,
pageSize: currentPageSize,
};
}}
columns={columns}
/>
<CreateDeviceForm
createModalOpen={createModalOpen}
handleModal={handleCreateModal}
reload={reloadList}
/>
</div>
);
};
export default DeviceList;

@ -0,0 +1,37 @@
.deviceGroup_page {
height: calc(100vh - 92px);
/* 节点列表 */
/* 节点信息 */
}
.deviceGroup_page .dg_content .gn_card {
height: calc(100% - 8px);
}
.deviceGroup_page .node_list > .gn_card {
box-shadow: 0px 2px 8px 0px rgba(0, 79, 178, 0.15);
}
.deviceGroup_page .node_info {
padding-left: 0;
}
.dg_deviceList_wrap .model_index_type_tag {
width: 82px;
height: 24px;
color: #52C41A;
background: #E8F7E6;
border: 1px solid #BAEEA1;
border-radius: 12px;
}
.dg_deviceList_wrap .model_index_type_tag .dot {
width: 6px;
height: 6px;
margin-right: 4px;
background: #52C41A;
border-radius: 50%;
}
.dg_deviceList_wrap .model_index_type_tag.active2 {
color: #E80D0D;
background: #FEEFEE;
border: 1px solid #F9B2AE;
}
.dg_deviceList_wrap .model_index_type_tag.active2 .dot {
background: #E80D0D;
}

@ -0,0 +1,45 @@
.deviceGroup_page {
height: calc(100vh - 92px);
.dg_content {
.gn_card {
height: calc(100% - 8px);
}
}
/* 节点列表 */
.node_list {
& > .gn_card {
box-shadow: 0px 2px 8px 0px rgba(0, 79, 178, 0.15);
}
}
/* 节点信息 */
.node_info {
padding-left: 0;
}
}
.dg_deviceList_wrap {
.model_index_type_tag {
width: 82px;
height: 24px;
color: #52c41a;
background: #e8f7e6;
border: 1px solid #baeea1;
border-radius: 12px;
.dot {
width: 6px;
height: 6px;
margin-right: 4px;
background: #52c41a;
border-radius: 50%;
}
&.active2 {
color: #e80d0d;
background: #feefee;
border: 1px solid #f9b2ae;
.dot {
background: #e80d0d;
}
}
}
}

@ -0,0 +1,136 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-29 16:50:22
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\index.tsx
* @Description: deviceGroupdg
* @
* 1
* 2
* 3
* 4
* 5
*/
import { DeviceGroupTree } from '@/components/Tree';
import { deviceGroupEnums } from '@/enums/deviceGroup';
import { getDeviceGroupList } from '@/services/testApi/deviceGroup';
import { ProCard } from '@ant-design/pro-components';
import { Button, Tabs } from 'antd';
import { useEffect, useState } from 'react';
import BaseInfo from './components/baseInfo';
import CreateForm from './components/createForm';
import DeviceList from './components/deviceList';
import './index.less';
const DeviceGroup: React.FC = () => {
/**state */
// 节点信息
const [deviceTreeList, setDeviceTreeList] = useState<Record<string, any>[]>([]);
const [nodeInfo, setNodeInfo] = useState<Record<string, any>>({}); // 当前节点信息
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false); // 创建新增窗口是否打开
// 切换模块
const [tabKey, setTabKey] = useState<string>(deviceGroupEnums[0].key);
const [tabs] = useState<any>([...deviceGroupEnums]);
const changeTabMode = (key: string) => {
setTabKey(key);
console.log(key);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// initList(key);
};
/**节点列表 */
// 设备节点树
async function loadDeviceTree() {
const resp = await getDeviceGroupList({ page: 1, pageSize: 100 });
console.log(resp.data, 'loadDeviceTree');
setDeviceTreeList(resp?.data.results);
setNodeInfo(resp?.data.results[0]);
}
// 新增
const handleCreateModal = () => {
setCreateModalOpen(!createModalOpen);
};
// 节点信息展示
// 切换
// 初始化加载
useEffect(() => {
loadDeviceTree();
}, []);
return (
<div className="flex deviceGroup_page">
{/* 节点列表 */}
<div className="dg_content gn_head_card w-[300px] node_list">
<ProCard className="gn_card pb-[16px]" title={<span className="head5"></span>}>
<div className="dg_tree_box">
<DeviceGroupTree
dataSource={deviceTreeList}
changeNode={(record) => {
setNodeInfo(record);
}}
addTreeNode={(node) => {
//TODO 判断是根节点还是子节点 调用新增接口
console.log('addTreeNode_node', node);
handleCreateModal();
}}
selectTree={(selectedKeys, info) => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
info?.node && setNodeInfo(info?.node);
// handleSelectNode(selectedKeys, info)
console.log('selectTree_selected', selectedKeys, info);
}}
></DeviceGroupTree>
</div>
</ProCard>
</div>
{/* 节点信息 */}
<div className="flex-1 dg_content gn_head_card node_info">
<ProCard
className="gn_card pb-[16px]"
title={<span className="head5"></span>}
extra={
<Button
type="primary"
onClick={() => {
//TODO 打开修改节点信息弹窗
// handleUpdateModal();
}}
>
</Button>
}
>
<div className="dg_content_box">
<BaseInfo info={nodeInfo} />
<Tabs
activeKey={tabKey}
items={tabs}
onChange={(key) => {
changeTabMode(key);
}}
></Tabs>
{tabKey === '1' && <DeviceList info={nodeInfo}></DeviceList>}
</div>
</ProCard>
</div>
{/* 弹窗 */}
<CreateForm
createModalOpen={createModalOpen}
handleModal={handleCreateModal}
parentInfo={nodeInfo}
reload={() => {
// TODO 调用获取节点列表的接口
}}
/>
</div>
);
};
export default DeviceGroup;

@ -1,14 +0,0 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-22 15:34:36
* @FilePath: \general-ai-platform-web\src\pages\Node\NodeSetting\index.tsx
* @Description:
* @
*/
const NodeSetting: React.FC = () => {
return <div className="nodeSetting_page"></div>;
};
export default NodeSetting;

@ -45,11 +45,7 @@ const ModelDetail: React.FC = () => {
/**新增 编辑 删除 */
// 新增
const handleCreateModal = () => {
if (createModalOpen) {
setCreateModalOpen(false);
} else {
setCreateModalOpen(true);
}
setCreateModalOpen(!createModalOpen);
};
function reloadList() {

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-23 17:00:00
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-24 17:14:48
* @LastEditTime: 2024-04-28 09:33:34
* @FilePath: \general-ai-platform-web\src\pages\Project\BusinessInfo\components\baseInfo.tsx
* @Description:
*
@ -45,7 +45,7 @@ const BaseInfo: React.FC<BaseInfoProps> = ({ info }) => {
},
];
return (
<div className="gn_table_descriptions bg_active_1">
<div className="= gn_table_descriptions bg_active_1">
<ProDescriptions column={4} columns={ModelDetailColumns} dataSource={info}></ProDescriptions>
</div>
);

@ -0,0 +1,31 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-25 15:39:42
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-28 17:14:06
* @FilePath: \general-ai-platform-web\src\services\testApi\device.ts
* @Description: api
*/
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** 节点下设备分页列表 */
export async function getDeviceListByGroup(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/device/listByGroup`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
},
);
}

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-25 15:39:42
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-25 15:49:38
* @LastEditTime: 2024-04-28 11:19:40
* @FilePath: \general-ai-platform-web\src\services\testApi\deviceGroup.ts
* @Description: api
*/
@ -11,26 +11,26 @@
import { request } from '@umijs/max';
/** 节点设置分页列表 */
// export async function getDeviceGroupList(
// body: Record<string, any>, //
// options?: { [key: string]: any },
// ) {
// return request<API.Response & { data?: API.PageResult; msg?: string }>(
// `/api/device_group/list`,
// {
// method: 'GET',
// headers: {
// 'Content-Type': 'application/json',
// },
// params: {
// ...body,
// },
// ...(options || {}),
// },
// );
// }
export async function getDeviceGroupList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/device_group/getDeviceGroupList`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
},
);
}
/** 获取设备组树 POST /device_group/getDeviceGroupTree */
/** 获取设备组树 GET /device_group/getDeviceGroupTree */
export async function getDeviceGroupTree(
body: Record<string, any>, //
options?: { [key: string]: any },
@ -49,3 +49,23 @@ export async function getDeviceGroupTree(
},
);
}
/** 获取节点树 GET /device_group/getDeviceGroupTree */
export async function getDeviceGroupSettingTree(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/device_group/setting_data`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
},
);
}

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-09 13:46:44
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-17 14:09:35
* @LastEditTime: 2024-04-28 14:24:40
* @FilePath: \general-ai-manage\src\services\testApi\businessProject.ts
* @Description: mock
*/
@ -10,12 +10,28 @@
/* eslint-disable */
import { request } from '@umijs/max';
/** 企业项目分页列表 */
/** 行业分类列表 */
export async function getDictIndustry(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/dict/industry/`, {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/dict/industry`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
});
}
/** 设备类型列表 */
export async function getDictDeviceType(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/dict/deviceType`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',

@ -0,0 +1,13 @@
export const formatTreeValByKey = (
key: string,
node: Record<string, any>,
fieldNames?: string,
): any => {
if (fieldNames && Object.keys(fieldNames).includes(key)) {
return node[fieldNames[key]];
}
if (Object.keys(node).includes(key)) {
return node[key];
}
return null;
};
Loading…
Cancel
Save