Browse Source

数据查询模块

dev
巴林闲侠 2 years ago
parent
commit
5ca14a9838
  1. 82
      api/app/lib/controllers/alarm/dataContinuity.js
  2. 9
      api/app/lib/routes/alarm/index.js
  3. 36
      web/client/src/sections/data/actions/dataQuery.js
  4. 4
      web/client/src/sections/data/actions/index.js
  5. 61
      web/client/src/sections/data/components/dataQueryCheck.jsx
  6. 224
      web/client/src/sections/data/containers/dataQuery.jsx
  7. 78
      web/client/src/sections/data/routes.js
  8. 5
      web/client/src/utils/webapi.js

82
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,
};

9
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);
};

36
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: '获取数据详情失败' },
});
}

4
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
}

61
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 (
<SideSheet
title={`${checkData?.alarmDataContinuityType?.name} ${moment(checkData.createTime).format('YYYY-MM-DD HH:mm:ss')}`}
visible={visible}
onCancel={() => {
onCancel()
setHtmlCode('')
}}
width={clientWidth - 200}
>
<SimpleBar style={{ height: 'calc(100vh - 82px)', }} forceVisible="y" >
<Skeleton
loading={loading}
active={true}
placeholder={SkeletonScreen()}
>
<div dangerouslySetInnerHTML={{ __html: htmlCode }} />
</Skeleton>
</SimpleBar>
</SideSheet>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
clientWidth: global.clientWidth,
};
}
export default connect(mapStateToProps)(DataQueryCkeck);

224
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 (
<>
<div>
<img src="/assets/images/install/watting.png" alt="" style={{ width: 'calc(100% + 16px)', position: "relative",top: -12, left: -8, }} />
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 (
<>
<div style={{ background: '#FFFFFF', margin: '8px 12px', padding: '20px 20px 0px 20px' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #005ABD', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#101531', marginLeft: 8 }}>数据查询</div>
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>DATA QUERY</div>
</div>
<div style={{ marginRight: 20, display: 'flex', alignItems: 'center' }} className='myempush'>
<Form
layout="horizontal"
style={{ position: "relative", width: "100%", flex: 1 }}
onSubmit={(values) => {
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 })
}}
>
<Form.DatePicker
pure
field="createTimes"
label="产生时间"
style={{ width: 268 }}
type="dateRange" density="compact"
/>
<Form.Select
pure
label="数据查询类型"
field="typeNo"
placeholder="数据查询类型"
style={{ width: 260, marginLeft: 12, marginRight: 12 }}
showClear
>
{
dataContinuityType.map((item, index) => {
return (
<Form.Select.Option key={index} value={item.id}>
{item.name}
</Form.Select.Option>
)
})
}
</Form.Select>
<Button theme='solid' type="primary" htmlType="submit">
查询
</Button>
</Form>
</div>
</div>
<div style={{ marginTop: 20 }}>
<Skeleton
loading={loading}
// loading={false}
active={true}
placeholder={SkeletonScreen()}
>
<Table
rowKey="id"
columns={[{
title: '数据类型',
dataIndex: 'typeName',
render: (text, record, index) => {
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 (
<Space>
<Button theme='borderless' type="primary" onClick={() => {
dispatch(sectionData.getContinuityDetail(record.id)).then(res => {
if (res.success) {
downloadWord(res.payload?.data?.file || '', record?.alarmDataContinuityType?.name + moment(record.createTime).format('YYYY-MM-DD HH:mm:ss'))
}
setLoading(false)
})
}}>
下载
</Button>
<Button theme='borderless' type="primary" onClick={() => {
setCheckData(record);
setCheckVis(true);
}}>
查看
</Button>
</Space>
)
}
}]}
dataSource={dataContinuity?.rows}
bordered={false}
hideExpandedColumn={false}
empty="暂无数据"
pagination={false}
/>
</Skeleton>
<div
style={{
display: "flex",
justifyContent: "flex-end",
padding: "20px 20px",
}}
>
<div style={{ display: 'flex', }}>
<span style={{ lineHeight: "30px", fontSize: 13, color: 'rgba(0,90,189,0.8)' }}>
{dataContinuity?.count}条数据
</span>
<Pagination
total={dataContinuity?.count}
showSizeChanger
currentPage={query.page + 1}
pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 });
getData({ limit: pageSize, page: currentPage - 1 })
}}
/>
</div>
</div>
</div>
</>
)
<DataQueryCkeck visible={checkVis} onCancel={() => {
setCheckVis(false);
setCheckData({})
}} checkData={checkData} />
</div>
</>
)
}
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);

78
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',
}]
}]
}
}];

5
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", //新增/编辑推送配置

Loading…
Cancel
Save