Forráskód Böngészése

🐛 fix(scene): 新增场景管理和场景历史记录管理

shylock 11 órája
szülő
commit
07a4cecb05

+ 6 - 0
config/routes.ts

@@ -220,6 +220,12 @@
     icon: 'Appstore',
     component: './DeviceOnlineManagement/index',
   },
+  {
+    path: '/sceneManagement',
+    name: '场景管理',
+    icon: 'Appstore',
+    component: './SceneManagement/index',
+  },
   // 房间列表
   {
     path: '/roomList',

+ 267 - 0
src/pages/SceneManagement/index.tsx

@@ -0,0 +1,267 @@
+import React, { useEffect, useState } from 'react';
+import { PageContainer } from '@ant-design/pro-components';
+import { Card, Space, Table } from 'antd';
+import type { ColumnsType } from 'antd/es/table';
+import { queryScenes } from '@/services/scene';
+import SceneHistory from '@/pages/SceneManagement/sceneHistory';
+
+interface DataType {
+  name: string;
+  home_name: number;
+  type_name: string;
+  creator_name: string;
+  description_expr: string;
+  record_id: string;
+}
+
+/**
+ * 场景管理
+ * @constructor
+ */
+const SceneManagement: React.FC = () => {
+  const [loading, setLoading] = useState(false);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList] = useState([]);
+  const [visible, setVisible] = useState(false);
+  const [recordId, setRecordId] = useState('');
+  const weekArray = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
+
+  // 获取列表数据
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+    };
+    queryScenes(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    setLoading(true);
+    getList();
+  }, []);
+
+  // 分页切换
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+    };
+    queryScenes(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  // 跳转到历史记录弹框中
+  const toHistory = (record: any) => {
+    setVisible(true);
+    setRecordId(record.record_id);
+  };
+
+  // 历史弹框回调
+  const onHistoryCallback = () => {
+    setVisible(false);
+  };
+
+  // 转换成星期  例: 0,1,1,1,1,1,1 => 周一,周二,周三,周四,周五,周六
+  const convertToWeekdays = (binaryStr: string) => {
+    // 判断 binaryStr 是否存在
+    if (!binaryStr || typeof binaryStr !== 'string') {
+      return '';
+    }
+
+    const days = binaryStr.split(',');
+
+    // 判断是否全为 0(仅此一次)
+    if (days.every((day) => parseInt(day) === 0)) {
+      return '仅此一次';
+    }
+
+    // 判断是否全为 1(每天)
+    if (days.every((day) => parseInt(day) === 1)) {
+      return '每天';
+    }
+
+    // 其他情况按原逻辑处理
+    return days
+      .map((day, index) => (parseInt(day) === 1 ? weekArray[index] : ''))
+      .filter((day) => day !== '')
+      .join(',');
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      width: 80,
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '房间名称',
+      dataIndex: 'home_name',
+      key: 'home_name',
+      width: 250,
+    },
+    {
+      title: '场景名称',
+      dataIndex: 'name',
+      key: 'name',
+      width: 250,
+    },
+    {
+      title: '场景类型',
+      dataIndex: 'type_name',
+      key: 'type_name',
+      width: 250,
+    },
+    {
+      title: '关联设备数',
+      dataIndex: 'device_infos',
+      key: 'device_infos',
+      width: 250,
+      render: (v) => {
+        if (v) {
+          return `${v.length}个`;
+        } else {
+          return `0个`;
+        }
+      },
+    },
+    {
+      title: '动作',
+      dataIndex: 'action_desc',
+      key: 'action_desc',
+      width: 250,
+      render: (v: string) => {
+        if (!v || typeof v !== 'string') {
+          return <div style={{ color: 'gray' }}>暂无动作数据</div>;
+        }
+
+        return v.split(';').map((item: string) => {
+          return <div key={item}>{item}</div>;
+        });
+      },
+    },
+    {
+      title: '条件',
+      dataIndex: 'conditions',
+      key: 'conditions',
+      width: 350,
+      render: (v: string) => {
+        if (!v || !Array.isArray(v) || v.length === 0) {
+          return <div style={{ color: 'gray' }}>暂无条件数据</div>;
+        }
+
+        return v.map((item: any, index: number) => {
+          const key = item.record_id || `condition-${index}`;
+
+          switch (item.entity_type) {
+            case 'timer':
+              const time = item.expr?.time || '';
+              const loops = item.expr?.loops || '';
+              return <div key={key}>{`定时:${time} | ${convertToWeekdays(loops)};`}</div>;
+            case 'weather':
+              const weatherName = item.weather_name || '';
+              const weatherValueName = item.weather_value_name || '';
+              const location = item.expr?.location || '';
+              return (
+                <div key={key}>{`${weatherName}: ${weatherValueName} | 地址:${location};`}</div>
+              );
+            case 'device_status':
+              const statusFuncName = item.status_func_name || '';
+              const statusFuncValueName = item.status_func_value_name || '';
+              const deviceName = item.device_name || '';
+              return (
+                <div key={key}>{`${statusFuncName}: ${statusFuncValueName} | ${deviceName};`}</div>
+              );
+            default:
+              return (
+                <div key={key} style={{ color: 'gray' }}>
+                  暂无条件数据
+                </div>
+              );
+          }
+        });
+      },
+    },
+    {
+      title: '执行条件',
+      dataIndex: 'description_expr',
+      key: 'description_expr',
+      width: 250,
+      render: (v: string) => {
+        return <span>{{ or: '满足任一条件', and: '满足所有条件' }[v]}</span>;
+      },
+    },
+    {
+      title: '创建者',
+      dataIndex: 'creator_name',
+      key: 'creator_name',
+      width: 250,
+    },
+    {
+      title: '操作',
+      key: 'action',
+      width: 150,
+      render: (_, record) => (
+        <Space size="middle">
+          <a
+            onClick={() => {
+              toHistory(record);
+            }}
+          >
+            历史记录
+          </a>
+        </Space>
+      ),
+    },
+  ];
+  const paginationProps = {
+    showSizeChanger: true,
+    showQuickJumper: true,
+    showTotal: (total: number) => {
+      return <span> 共 {total}条 </span>;
+    },
+    ...pagination,
+  };
+  return (
+    <PageContainer>
+      <div>
+        <Card>
+          <Table
+            columns={columns}
+            dataSource={dataList}
+            rowKey={(record) => record.record_id}
+            pagination={paginationProps}
+            loading={loading}
+            onChange={tableChange}
+          />
+
+          {visible && (
+            <SceneHistory
+              visible={visible}
+              editCallback={() => {
+                onHistoryCallback();
+              }}
+              recordId={recordId}
+            />
+          )}
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default SceneManagement;

+ 128 - 0
src/pages/SceneManagement/sceneHistory.tsx

@@ -0,0 +1,128 @@
+import { Modal, Table } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { queryScenesHis } from '@/services/scene';
+import type { ColumnsType } from 'antd/es/table';
+import moment from 'moment';
+
+interface editPros {
+  visible: boolean;
+  editCallback: () => void;
+  recordId: any;
+}
+
+interface DataType {
+  name: string;
+  home_name: number;
+  type_name: string;
+  creator_name: string;
+  description_expr: string;
+  record_id: string;
+}
+
+const SceneHistory: React.FC<editPros> = (props) => {
+  const { visible, editCallback, recordId } = props;
+  const [dataList, setDataList] = useState([]);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [loading, setLoading] = useState(false);
+
+  const getHistoryList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+      scene_id: recordId,
+    };
+    queryScenesHis(params).then((res: any) => {
+      if (res && res.code === 0) {
+        const list = res.data.list || [];
+        setDataList(list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    setLoading(true);
+    getHistoryList();
+  }, []);
+
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+      scene_id: recordId,
+    };
+    queryScenesHis(params).then((res: any) => {
+      if (res && res.code === 0) {
+        const list = res.data.list || [];
+        setDataList(list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  const onOk = () => {};
+
+  const onCancel = () => {
+    editCallback();
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '执行时间',
+      dataIndex: 'created_at',
+      key: 'created_at',
+      render: (v) => {
+        return v && moment(v).format('YYYY-MM-DD HH:mm');
+      },
+    },
+    {
+      title: '触发条件',
+      dataIndex: 'trigger_condition',
+      key: 'trigger_condition',
+    },
+    {
+      title: '执行动作',
+      dataIndex: 'action',
+      key: 'action',
+    },
+    {
+      title: '执行结果',
+      dataIndex: 'result',
+      key: 'result',
+    },
+  ];
+
+  const paginationProps = {
+    showSizeChanger: true,
+    showQuickJumper: true,
+    showTotal: (total: number) => {
+      return <span> 共 {total}条 </span>;
+    },
+    ...pagination,
+  };
+
+  return (
+    <Modal title="历史记录" open={visible} onOk={onOk} onCancel={onCancel} width={1000}>
+      <Table
+        columns={columns}
+        dataSource={dataList}
+        rowKey={(record) => record.record_id}
+        pagination={paginationProps}
+        loading={loading}
+        onChange={tableChange}
+      />
+    </Modal>
+  );
+};
+export default SceneHistory;

+ 18 - 0
src/services/scene.ts

@@ -0,0 +1,18 @@
+import { request } from '@@/plugin-request/request';
+import { stringify } from 'qs';
+
+/**
+ * 查询场景列表
+ * @param param
+ */
+export async function queryScenes(param: object) {
+  return request(`/web/v1/scenes?${stringify(param)}`);
+}
+
+/**
+ * 查询场景历史列表
+ * @param param
+ */
+export async function queryScenesHis(param: object) {
+  return request(`/web/v1/scene_his?${stringify(param)}`);
+}