diff --git a/api/app/lib/controllers/alarm/dataContinuity.js b/api/app/lib/controllers/alarm/dataContinuity.js index 905fafa..856ce58 100644 --- a/api/app/lib/controllers/alarm/dataContinuity.js +++ b/api/app/lib/controllers/alarm/dataContinuity.js @@ -156,10 +156,90 @@ async function st (ctx) { } } +async function dataContinuityTypeList (ctx) { + try { + const { models } = ctx.fs.dc; + + const typeListRes = await models.AlarmDataContinuityType.findAll({ + order: [['id', 'asc']] + }) + + ctx.status = 200; + ctx.body = typeListRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function dataContinuityList (ctx) { + try { + const { models } = ctx.fs.dc; + const { limit, page, createTimes, typeNo } = ctx.query + + let findOption = { + order: [['createTime', 'desc']], + where: {}, + attributes: ['id', 'type', 'createTime'], + include: [{ + model: models.AlarmDataContinuityType, + }] + } + + if (limit) { + findOption.limit = limit + } + if (limit && page) { + findOption.offset = page * limit + } + if (createTimes && createTimes.length == 2) { + findOption.where.createTime = { + $between: [moment(createTimes[0]).format(), moment(createTimes[1]).format()] + } + } + if (typeNo) { + findOption.where.type = typeNo + } + + const dataRes = await models.AlarmDataContinuity.findAndCountAll(findOption) + + ctx.status = 200; + ctx.body = dataRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: error`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} +async function dataContinuityDetail (ctx) { + try { + const { models } = ctx.fs.dc; + const { continuityId } = ctx.params + const detailRes = await models.AlarmDataContinuity.findOne({ + where: { id: continuityId }, + }) -module.exports = { + ctx.status = 200; + ctx.body = detailRes + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} +module.exports = { postDataContinuity, st, + dataContinuityTypeList, + dataContinuityList, + dataContinuityDetail, }; \ No newline at end of file diff --git a/api/app/lib/routes/alarm/index.js b/api/app/lib/routes/alarm/index.js index 85a68a2..fd085b0 100644 --- a/api/app/lib/routes/alarm/index.js +++ b/api/app/lib/routes/alarm/index.js @@ -84,4 +84,13 @@ module.exports = function (app, router, opts) { // app.fs.api.logAttr['GET/alarm/service/api'] = { content: '查询服务异常信息', visible: true }; // router.get('/alarm/service/api', service.serviceErrorList); + + app.fs.api.logAttr['GET/data/continuity/type_list'] = { content: '获取数据连续性监控信息类型', visible: true }; + router.get('/data/continuity/type_list', dataContinuity.dataContinuityTypeList); + + app.fs.api.logAttr['GET/data/continuity'] = { content: '获取数据连续性监控信息', visible: true } + router.get('/data/continuity', dataContinuity.dataContinuityList); + + app.fs.api.logAttr['GET/data/continuity/:continuityId/detail'] = { content: '获取数据连续性监控信息详细', visible: true } + router.get('/data/continuity/:continuityId/detail', dataContinuity.dataContinuityDetail); }; diff --git a/web/client/src/sections/data/actions/dataQuery.js b/web/client/src/sections/data/actions/dataQuery.js new file mode 100644 index 0000000..97495e3 --- /dev/null +++ b/web/client/src/sections/data/actions/dataQuery.js @@ -0,0 +1,36 @@ +'use strict'; + +import { ApiTable, basicAction } from '$utils' + +export function getContinuityType () { + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + actionType: 'GET_DATA_CONTINUITY_TYPE', + url: `${ApiTable.getDataContinuityType}`, + msg: { error: '获取数据类型失败' }, + reducer: { name: 'dataContinuityType' } + }); +} + +export function getContinuityList (query) { + return dispatch => basicAction({ + type: 'get', + query: query, + dispatch: dispatch, + actionType: 'GET_DATA_CONTINUITY', + url: `${ApiTable.getDataContinuity}`, + msg: { error: '获取数据列表失败' }, + reducer: { name: 'dataContinuity' } + }); +} + +export function getContinuityDetail (continuityId) { + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + actionType: 'GET_DATA_CONTINUITY_DETAIL', + url: `${ApiTable.getDataContinuityDetail.replace('{continuityId}', continuityId)}`, + msg: { error: '获取数据详情失败' }, + }); +} \ No newline at end of file diff --git a/web/client/src/sections/data/actions/index.js b/web/client/src/sections/data/actions/index.js index 33cdd8b..66a9a99 100644 --- a/web/client/src/sections/data/actions/index.js +++ b/web/client/src/sections/data/actions/index.js @@ -1,7 +1,7 @@ 'use strict'; -import * as console from './console' +import * as dataQuery from './dataQuery' export default { - ...console + ...dataQuery } \ No newline at end of file diff --git a/web/client/src/sections/data/components/dataQueryCheck.jsx b/web/client/src/sections/data/components/dataQueryCheck.jsx new file mode 100644 index 0000000..6c0630f --- /dev/null +++ b/web/client/src/sections/data/components/dataQueryCheck.jsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { connect } from 'react-redux'; +import moment from 'moment'; +import SimpleBar from 'simplebar-react'; +import { SkeletonScreen, } from "$components"; +import { IconSearch } from '@douyinfe/semi-icons'; +import { Form, Button, Skeleton, Table, Pagination, SideSheet } from '@douyinfe/semi-ui'; +import '../style.less' + +function DataQueryCkeck (props) { + const { clientWidth, visible, onCancel, checkData, actions, dispatch } = props + const { data: sectionData } = actions + const [loading, setLoading] = useState(false); + const [htmlCode, setHtmlCode] = useState(''); + + useEffect(() => { + if (checkData.id) { + setLoading(true) + setHtmlCode('') + dispatch(sectionData.getContinuityDetail(checkData.id)).then(res => { + if (res.success) { + setHtmlCode(res.payload?.data?.file || '') + } + setLoading(false) + }) + } + }, [checkData]) + + return ( + { + onCancel() + setHtmlCode('') + }} + width={clientWidth - 200} + > + + +
+ + + + ) +} + +function mapStateToProps (state) { + const { auth, global } = state; + return { + user: auth.user, + actions: global.actions, + clientWidth: global.clientWidth, + }; +} + +export default connect(mapStateToProps)(DataQueryCkeck); \ No newline at end of file diff --git a/web/client/src/sections/data/containers/dataQuery.jsx b/web/client/src/sections/data/containers/dataQuery.jsx index 003eb46..d96766d 100644 --- a/web/client/src/sections/data/containers/dataQuery.jsx +++ b/web/client/src/sections/data/containers/dataQuery.jsx @@ -1,49 +1,197 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { connect } from 'react-redux'; -import { Spin, Card } from '@douyinfe/semi-ui'; +import moment from 'moment'; + +import { exportWord } from 'mhtml-to-word' +import { saveAs } from 'file-saver'; + +import { SkeletonScreen, } from "$components"; +import { IconSearch } from '@douyinfe/semi-icons'; +import DataQueryCkeck from '../components/dataQueryCheck' +import { Form, Button, Skeleton, Table, Pagination, Space } from '@douyinfe/semi-ui'; import '../style.less' -const { Meta } = Card; const Console = (props) => { - const { dispatch, actions, user, loading, socket } = props - - useEffect(() => { - // ACTION 示例 - // dispatch(actions.example.getMembers(user.orgId)) - }, []) - - // websocket 使用测试 - // useEffect(() => { - // console.log(socket) - // if (socket) { - // socket.on('TEST', function (msg) { - // console.info(msg); - // }); - // return () => { - // socket.off("TEST"); - // } - // } - - // }, [socket]) - - return ( - <> -
- + const { dispatch, actions, user, dataContinuityType, dataContinuity } = props + const { data: sectionData } = actions + const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 + const [limits, setLimits] = useState(0)//每页实际条数 + const [loading, setLoading] = useState(true); + const [params, setParams] = useState({}) + const [checkVis, setCheckVis] = useState(false) + const [checkData, setCheckData] = useState({}) + + useEffect(() => { + dispatch(sectionData.getContinuityType()) + getData() + }, []) + + const getData = (queryParams = {}) => { + setLoading(true); + dispatch(sectionData.getContinuityList({ ...params, ...query, ...queryParams, })).then(res => [ + setLoading(false) + ]) + } + + const downloadWord = (html = '', name = '数据查询 ' + moment().format('YYYY-MM-DD HH:mm:ss')) => { + exportWord({ + mhtml: html, + filename: name, + }) + // let blob = new Blob([html], { type: "application/msword;charset=utf-8" }); + // saveAs(blob, name + '.doc'); + } + + return ( + <> +
+
+
+
+
数据查询
+
DATA QUERY
+
+
+
{ + console.log(values); + if (values?.createTimes?.length) { + values.createTimes = [moment(values?.createTimes[0]).format('YYYY-MM-DD HH:mm:ss'), moment(values?.createTimes[1]).format('YYYY-MM-DD HH:mm:ss')] + } + setParams({ ...values }) + getData({ createTimes: '', typeNo: '', ...values }) + }} + > + + + { + dataContinuityType.map((item, index) => { + return ( + + {item.name} + + ) + }) + } + + + +
+
+ +
+ + { + console.log(record); + return record?.alarmDataContinuityType?.name || '' + } + }, { + title: '产生时间', + dataIndex: 'createTime', + render: (text, record, index) => { + return record?.createTime ? moment(record.createTime).format('YYYY-MM-DD HH:mm:ss') : '' + } + }, { + title: '操作', + dataIndex: 'option', + render: (text, record, index) => { + return ( + + + + + ) + } + }]} + dataSource={dataContinuity?.rows} + bordered={false} + hideExpandedColumn={false} + empty="暂无数据" + pagination={false} + /> + +
+
+ + 共{dataContinuity?.count}条数据 + + { + setQuery({ limit: pageSize, page: currentPage - 1 }); + getData({ limit: pageSize, page: currentPage - 1 }) + }} + /> +
+
- - ) + { + setCheckVis(false); + setCheckData({}) + }} checkData={checkData} /> + + + ) } function mapStateToProps (state) { - const { auth, global, members, webSocket } = state; - return { - // loading: members.isRequesting, - // user: auth.user, - // actions: global.actions, - // members: members.data, - // socket: webSocket.socket - }; + const { auth, global, dataContinuityType, dataContinuity } = state; + return { + user: auth.user, + actions: global.actions, + dataContinuityType: dataContinuityType.data || [], + dataContinuity: dataContinuity.data || [] + }; } export default connect(mapStateToProps)(Console); diff --git a/web/client/src/sections/data/routes.js b/web/client/src/sections/data/routes.js index d3e4627..b497cdd 100644 --- a/web/client/src/sections/data/routes.js +++ b/web/client/src/sections/data/routes.js @@ -1,42 +1,42 @@ -import { DataQuery, DataComparison, DataAssociation,Notebook } from './containers'; +import { DataQuery, DataComparison, DataAssociation, Notebook } from './containers'; export default [{ - type: 'inner', - route: { - path: '/data', - key: 'data', - breadcrumb: '数据', - // 不设置 component 则面包屑禁止跳转 - childRoutes: [{ - path: '/dataMonitoring', - key: 'dataMonitoring', - breadcrumb: '数据监控', - childRoutes: [{ - path: '/dataQuery', - key: 'dataQuery', - component: DataQuery, - breadcrumb: '数据查询', - }] - }, { - path: '/dataAnalysis', - key: 'dataAnalysis', - breadcrumb: '数据分析', - childRoutes: [{ - path: '/dataComparison', - key: 'dataComparison', - component: DataComparison, - breadcrumb: '数据对比', - },{ - path: '/dataAssociation', - key: 'dataAssociation', - component: DataAssociation, - breadcrumb: '数据关联', - },{ - path: '/notebook', - key: 'notebook', - component: Notebook, - breadcrumb: 'notebook', - }] - }] - } + type: 'inner', + route: { + path: '/data', + key: 'data', + breadcrumb: '数据', + // 不设置 component 则面包屑禁止跳转 + childRoutes: [{ + path: '/dataMonitoring', + key: 'dataMonitoring', + breadcrumb: '数据监控', + childRoutes: [{ + path: '/dataQuery', + key: 'dataQuery', + component: DataQuery, + breadcrumb: '数据查询', + }] + }, { + path: '/dataAnalysis', + key: 'dataAnalysis', + breadcrumb: '数据分析', + childRoutes: [{ + path: '/dataComparison', + key: 'dataComparison', + component: DataComparison, + breadcrumb: '数据对比', + }, { + path: '/dataAssociation', + key: 'dataAssociation', + component: DataAssociation, + breadcrumb: '数据关联', + }, { + path: '/notebook', + key: 'notebook', + component: Notebook, + breadcrumb: 'notebook', + }] + }] + } }]; \ No newline at end of file diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index 1d3ebc0..bb6fda1 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -51,6 +51,11 @@ export const ApiTable = { getVcmpAuth: 'vcmp/auth', // 获取视频平台应用鉴权token getAlarmVideoExceptionType: 'alarm/video/exceptionType', //查询视频设备类型 + // 数据查询 + getDataContinuityType: 'data/continuity/type_list', + getDataContinuity: 'data/continuity', + getDataContinuityDetail: 'data/continuity/{continuityId}/detail', + //服务-信鸽服务 getPush: "push", //获取推送配置列表 postPush: "push", //新增/编辑推送配置