Browse Source

feat:故障管理代码

master
zhaobing 9 months ago
parent
commit
81c9fa5147
  1. 78
      api/app/lib/controllers/bigScreen/error.js
  2. 17
      api/app/lib/controllers/patrolManage/patrolRecord.js
  3. 13
      api/app/lib/routes/bigScreen/error.js
  4. BIN
      web-screen/client/assets/bigScreen/shield.png
  5. 16
      web-screen/client/src/layout/components/header/index.js
  6. 2
      web-screen/client/src/layout/reducers/tab.js
  7. 85
      web-screen/client/src/sections/bigScreen/actions/error.js
  8. 3
      web-screen/client/src/sections/bigScreen/actions/index.js
  9. 429
      web-screen/client/src/sections/bigScreen/components/IssueHandleModal.js
  10. 248
      web-screen/client/src/sections/bigScreen/components/error/bottom.js
  11. 208
      web-screen/client/src/sections/bigScreen/components/error/style.less
  12. 310
      web-screen/client/src/sections/bigScreen/components/error/top.js
  13. 2
      web-screen/client/src/sections/bigScreen/components/leader/left.js
  14. 1
      web-screen/client/src/sections/bigScreen/components/run/style.less
  15. 6
      web-screen/client/src/sections/bigScreen/components/style.less
  16. 16
      web-screen/client/src/utils/fs.js
  17. 2
      web-screen/client/src/utils/webapi.js

78
api/app/lib/controllers/bigScreen/error.js

@ -0,0 +1,78 @@
'use strict';
const moment = require("moment")
async function countIssueByState(ctx, next) {
const sequelize = ctx.fs.dc.orm
const { projectId } = ctx.query
try {
const rslt = await sequelize.query(`
SELECT COUNT(CASE WHEN prih.state=4 THEN 1 END) AS waitingRepair,
COUNT(CASE WHEN prih.state=1 THEN 1 END) AS waitingPlan,
COUNT(CASE WHEN prih.state=5 THEN 1 END) AS waitingCheck,
COUNT(CASE WHEN prih.state=2 THEN 1 END) AS waitingExamine
FROM patrol_record rd
INNER JOIN patrol_record_issue_handle prih
ON rd.id = prih.patrol_record_id
WHERE rd.project_id in (${projectId})
`)
ctx.status = 200
ctx.body = rslt[0][0]
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '根据状态分类查询问题处理失败' }
}
}
//近30天的问题统计
async function countDayIssue(ctx, next) {
const sequelize = ctx.fs.dc.orm
const { projectId } = ctx.query
try {
const rslt = await sequelize.query(`
SELECT
date_sequence::DATE AS date,
COALESCE(COUNT(patrol_record.inspection_time), 0) AS count
FROM
generate_series(CURRENT_DATE - INTERVAL '29 days', CURRENT_DATE, '1 day'::interval) date_sequence
LEFT JOIN patrol_record
ON date_sequence::DATE = DATE(patrol_record.inspection_time)
AND patrol_record.project_id in (:projectId)
AND patrol_record.alarm='true'
GROUP BY date
ORDER BY date;
`, {
replacements: {
projectId: projectId,
},
type: sequelize.QueryTypes.SELECT,
})
ctx.status = 200
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '近30天的问题统计查询失败' }
}
}
module.exports = {
countIssueByState,
countDayIssue
}

17
api/app/lib/controllers/patrolManage/patrolRecord.js

@ -9,7 +9,7 @@ async function findPatrolRecord(ctx, next) {
try {
const models = ctx.fs.dc.models;
const { startTime, endTime, alarm, patrolPlanId, pointId } = ctx.params;
const { home } = ctx.query
const { home,projectId } = ctx.query
const sequelize = ctx.fs.dc.orm;
// patrolPlanId传all查所有
let generalInclude = [{
@ -20,6 +20,7 @@ async function findPatrolRecord(ctx, next) {
model: models.PatrolRecordIssueHandle
}
]
if (patrolPlanId == 'all') {
/* 如果有startTime && endTime,查询所有符合条件的数据 */
if (startTime !== 'null' && endTime !== 'null') {
@ -146,7 +147,11 @@ async function findPatrolRecord(ctx, next) {
}
} else {
rslt = []
//大屏那边的查询,根据monitorObject筛选
if(userInfo.monitorObject.length){
// rslt = rslt.filter(s => userInfo.monitorObject.find(x => x == s.points.project.id))
rslt=rslt
}
}
}
ctx.status = 200;
@ -452,7 +457,13 @@ function getSubSystemPatrolAbout(opts) {
const models = ctx.fs.dc.models;
const { STime, ETime, keywords,IsbigScreen,projectId } = ctx.query
if(IsbigScreen==='true'){
generalInclude=[{ model: models.PatrolRecordIssueHandle }, { model: models.Project, where: { id: { $in: projectId.split(',') } } }]
if(projectId){
//传结构物id就查询当前结构物对应的记录
generalInclude=[{ model: models.PatrolRecordIssueHandle }, { model: models.Project, where: { id: { $in: projectId.split(',') } } }]
}else{
//不传结构物id查询所有
generalInclude=[{ model: models.PatrolRecordIssueHandle }, { model: models.Project }]
}
}else{
generalInclude= [{ model: models.PatrolRecordIssueHandle }, { model: models.Project, where: { subType: { $like: `%${keywords}%` } } }]
}

13
api/app/lib/routes/bigScreen/error.js

@ -0,0 +1,13 @@
'use strict';
const error = require('../../controllers/bigScreen/error');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/bigScreen/patrolRecordIssue'] = { content: '', visible: false };
router.get('/bigScreen/patrolRecordIssue',error.countIssueByState)
app.fs.api.logAttr['GET/bigScreen/patrolRecordDayIssue'] = { content: '', visible: false };
router.get('/bigScreen/patrolRecordDayIssue',error.countDayIssue)
}

BIN
web-screen/client/assets/bigScreen/shield.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

16
web-screen/client/src/layout/components/header/index.js

@ -28,7 +28,7 @@ const Header = props => {
const [patrolManageVisible, setPatrolManageVisible] = useState(false)
const [deviceManageTabsVisible, setDeviceManageTabsVisible] = useState(false)
const [currentSubMenuTab, setCurrentSubMenuTab] = useState('')
const [tab, setTab] = useState('leader')
const [tab, setTab] = useState('aiot')
const [projectName, setProjectName ]= useState('')
const [weather, setWeather] = useState([])
let headerTitleStyle = {
@ -66,11 +66,11 @@ const Header = props => {
}, [])
const queryWeather = () => {
RouteRequest.get(`/query/weather/3d?location=101240101`).then(res => {
if (res?.daily?.length === 3) {
setWeather(res.daily)
}
})
// RouteRequest.get(`/query/weather/3d?location=101240101`).then(res => {
// if (res?.daily?.length === 3) {
// setWeather(res.daily)
// }
// })
}
const iconSrc = useMemo(() => {
@ -89,7 +89,7 @@ const Header = props => {
}
const onClick1 = tab => {
setTab(tab)
if (tab == 'patrolManage') {
if (tab == 'inspection') {
setPatrolManageVisible(true)
setDeviceManageTabsVisible(false)
} else if (tab == 'device') {
@ -320,7 +320,7 @@ const Header = props => {
? 'inline-block'
: 'none',
}}>
<img src={`/assets/images/bigscreen/boring_${currentSubMenuTab == tab.value ? 1 : 0}.png`} />
{/* <img src={`/assets/images/bigscreen/boring_${currentSubMenuTab == tab.value ? 1 : 0}.png`} /> */}
</span>
</div>
))}

2
web-screen/client/src/layout/reducers/tab.js

@ -3,7 +3,7 @@
import Immutable from 'immutable';
const initState = {
tab: 'leader',
tab: 'aiot',
showCG: true
};

85
web-screen/client/src/sections/bigScreen/actions/error.js

@ -0,0 +1,85 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function addPatrolRecordIssueHandle(params) {
return (dispatch) => basicAction({
type: 'post',
data: params,
dispatch,
actionType: 'ADD_PatrolRecordIssueHandle_REPORT',
url: ApiTable.addPatrolRecordIssueHandle,
msg: {
option: '维修计划新增',
},
});
}
export function modifyPatrolRecordIssueHandle(id, params, msg) {
return (dispatch) => basicAction({
type: 'put',
data: params,
dispatch,
actionType: 'MODIFY_PatrolRecordIssueHandle_REPORT',
url: ApiTable.modifyPatrolRecordIssueHandle.replace('{id}', id),
msg: {
option: msg || '维修计划审批',
},
});
}
export const GET_PATROL_RECORD_LIST = 'GET_PATROL_RECORD_LIST';
export const GET_PATROL_RECORD_LIST_SUCCESS = 'GET_PATROL_RECORD_LIST_SUCCESS';
export const GET_PATROL_RECORD_LIST_ERROR = 'GET_PATROL_RECORD_LIST_ERROR';
export function getRecords(url) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
actionType: GET_PATROL_RECORD_LIST,
url: url,
msg: { error: '获取巡检记录失败', },
reducer: { name: 'record' }
});
}
export function countIssueByState(query) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
query,
actionType: 'COUNT_ISSUE_BY_STATE',
url: ApiTable.countIssueByState,
msg: {
option: '根据问题状态统计',
},
reducer: { name: 'issues' }
});
}
export function countDayIssue(query) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
query,
actionType: 'COUNT_DAY_ISSUE',
url: ApiTable.countDayIssue,
msg: {
option: '近三十天问题统计',
},
reducer: { name: 'dayIssues' }
});
}
export default{
getRecords,
modifyPatrolRecordIssueHandle,
addPatrolRecordIssueHandle,
countIssueByState,
countDayIssue
}

3
web-screen/client/src/sections/bigScreen/actions/index.js

@ -1,7 +1,8 @@
'use strict';
import leader from './leader'
import run from './run'
import error from './error'
export default {
...leader,...run
...leader,...run,...error
}

429
web-screen/client/src/sections/bigScreen/components/IssueHandleModal.js

@ -0,0 +1,429 @@
import React, { useState, useRef } from 'react';
import { Button, Form, Row, Col, Table, Popconfirm, Input, message } from 'antd';
import {
ModalForm,
ProFormText,
ProFormSelect,
ProFormTextArea,
ProFormDatePicker,
ProFormDateRangePicker,
ProFormDependency
} from '@ant-design/pro-form';
import Uploads from '$components/Uploads';
import moment from 'moment';
const FormItem = Form.Item;
//state: 1下发未上报 2已上报待审批 3整改完成 上报结果result: status 0 已上报未审批 1 审批通过 2 审批驳回
export default (props) => {
const { title, triggerRender, editData = null, onFinish, readOnly, structsUsers, user } = props;
const users = structsUsers?.find(s => s.id == editData?.points?.project?.id)?.users?.map(v => { return { value: v.id, label: v.name, ...v } })
const formItemLayout = { labelCol: { span: 7 }, wrapperCol: { span: 16 } };
const formRef = useRef();
const initialValues = editData ? {
...editData,
...editData?.patrolRecordIssueHandles[0],
approvePerson: editData?.patrolRecordIssueHandles[0]?.approvePerson?.name || '',
approveDate: editData?.patrolRecordIssueHandles[0]?.approveDate ? moment(editData?.patrolRecordIssueHandles[0]?.approveDate).format('YYYY-MM-DD HH:mm:ss') : "",
repairPerson: users?.find(s => s.value == editData?.patrolRecordIssueHandles[0]?.repairPerson?.id) ? editData?.patrolRecordIssueHandles[0]?.repairPerson?.id : null,
checkPerson: users?.find(s => s.value == editData?.patrolRecordIssueHandles[0]?.checkPerson?.id) ? editData?.patrolRecordIssueHandles[0]?.checkPerson?.id : null,
} : {};
if (editData?.patrolRecordIssueHandles?.length > 0) {
initialValues.dateRange = [editData?.patrolRecordIssueHandles[0]?.startTime, editData?.patrolRecordIssueHandles[0]?.endTime]
}
const [approve, setApprove] = useState('')
const approveHandle = async (values) => {
onFinish && await onFinish({
msg: approve ? '计划同意' : '计划驳回',
state: approve ? 4 : 3,
approveOpinion: values?.approveOpinion,
approvePerson: user,
approveDate: moment()
}, editData)
}
const renderPlanInfo = () => {
return <>
{/* <Card title={'巡检信息'}> */}
<div className="item-title">{"巡检信息"}</div>
<Form>
<Row>
<Col span={12}>
<Form.Item label="结构物名称:" {...formItemLayout}>
<Input value={editData?.points?.project?.name} readOnly />
</Form.Item>
<Form.Item label="巡检人:" {...formItemLayout}>
<Input value={editData?.points?.user?.name} readOnly />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="巡检单位:" {...formItemLayout}>
<Input value={editData?.points?.user?.department?.name} readOnly title={222} />
</Form.Item>
<Form.Item label="巡检时间:" {...formItemLayout}>
<Input value={editData?.inspectionTime && moment(editData?.inspectionTime).format('YYYY-MM-DD HH:mm:ss')} readOnly />
</Form.Item>
</Col>
</Row>
</Form>
<div className="item-title">{"问题详情"}</div>
<Row>
<Col span={12}>
<Form.Item label="点位名称:" {...formItemLayout}>
<Input value={editData?.points?.itemData?.name} readOnly />
</Form.Item>
</Col>
</Row>
<Row>
<Col span={24}>
{
editData?.points?.inspectContent && Array.isArray(editData?.points?.inspectContent) &&
editData?.points?.inspectContent?.map(s => {
if (s?.alarm == true) {
return <>
{s?.deviceName && <Col span={12}>
<Form.Item label="设备名称:" {...formItemLayout}>
<Input value={s?.deviceName} readOnly />
</Form.Item>
</Col>}
{
s?.checkItems?.map(k => {
return <Row style={{ marginBottom: 15 }}>
<Col span={12}>
<Form.Item label="检查项:" {...formItemLayout}>
<Input value={k.name} readOnly />
</Form.Item>
<Form.Item label="异常等级:" {...formItemLayout}>
<Input value={k?.level} readOnly />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="问题描述:" {...formItemLayout}>
<Input value={k?.msgInp} readOnly title={222} />
</Form.Item>
<Form.Item label="现场图片:" {...formItemLayout}>
<Uploads
listType='picture-card'
uploadType='project'
maxFilesNum={1}
maxFileSize={10}
isQiniu={true}
disabled={true}
fileTypes={["png", "jpg"]}
defaultValue={
(() => {
let nextV = []
for (let s of (k.imgs || [])) {
if (s) {
nextV.push({
storageUrl: s
})
}
}
return nextV
})()
}
/>
</Form.Item>
</Col>
</Row>
})
}
</>
}
})
}
</Col>
</Row></>
}
const renderRepairInfo = () => {
return <>
<div className="item-title">{"维修处理"}</div>
<ProFormTextArea
name="repairDesc"
label="维修情况描述:"
disabled={true}
/>
<Form.Item label="完工图片:">
<Uploads
listType='picture-card'
uploadType='project'
maxFilesNum={1}
maxFileSize={10}
isQiniu={true}
disabled={true}
fileTypes={["png", "jpg"]}
defaultValue={
(() => {
let nextV = []
for (let s of (editData?.patrolRecordIssueHandles[0]?.repairImage || [])) {
if (s) {
nextV.push({
storageUrl: s
})
}
}
return nextV
})()
}
/>
</Form.Item>
</>
}
const renderCheckInfo = () => {
return <>
<div className="item-title">{"质检验收"}</div>
<ProFormTextArea
name="checkPerson1"
label="验收人:"
disabled={true}
value={editData?.patrolRecordIssueHandles[0]?.checkPerson?.name}
/>
<ProFormText
name="checkPerson1"
label="成本(元):"
disabled={true}
value={editData?.patrolRecordIssueHandles[0]?.cost}
/>
<Form.Item label="完工图片:">
<Uploads
listType='picture-card'
uploadType='project'
maxFilesNum={1}
maxFileSize={10}
isQiniu={true}
disabled={true}
fileTypes={["png", "jpg"]}
defaultValue={
(() => {
let nextV = []
for (let s of (editData?.patrolRecordIssueHandles[0]?.checkImage || [])) {
if (s) {
nextV.push({
storageUrl: s
})
}
}
return nextV
})()
}
/>
</Form.Item>
<ProFormTextArea
name="checkOpinion"
label="验收意见:"
disabled={true}
/>
<ProFormTextArea
name="checkState"
label="验收结果:"
disabled={true}
value={editData?.patrolRecordIssueHandles[0]?.state == 6 ? '验收通过' : '验收不通过'}
/>
</>
}
return (
<ModalForm
formRef={formRef}
title={title || ''}
initialValues={initialValues}
trigger={
triggerRender ? triggerRender : <Button type="primary" >
{title || ''}
</Button>
}
width={1300}
layout="horizontal"
// grid={true}
{...formItemLayout}
modalProps={{
destroyOnClose: true,
// onCancel: () => { },
bodyStyle: { height: 620, overflowY: 'auto' }
}}
onFinish={async (values) => {
if (editData?.patrolRecordIssueHandles[0]?.state === 2 && title == '审核') {
approveHandle(values)
return true;
} else {
values.repairUnit = values?.repairUnits;
values.startTime = values?.dateRange[0];
values.endTime = values?.dateRange[1];
values.repairPerson = { id: users?.find(s => s.value == values.repairPerson)?.value, name: users?.find(s => s.id == values.repairPerson)?.label }
values.checkPerson = { id: users?.find(s => s.value == values.checkPerson)?.value, name: users?.find(s => s.id == values.checkPerson)?.label }
onFinish && await onFinish(values, editData)
//message.success('提交成功');
return true;
}
}}
submitter={editData?.patrolRecordIssueHandles[0]?.state === 2 && title != '修改计划' && title != '查看详情' ? {
render: (props, defaultDoms) => {
return [
<Button onClick={() => {
setApprove(1)
props.submit();
}} type='primary'>确定</Button>,
<Button onClick={() => {
setApprove(0)
props.submit();
}}>驳回</Button>
];
}
} : (!readOnly)}
>
<div>
{/*问题记录信息*/}
{renderPlanInfo()}
{/*问题处理计划表单*/}
{
((editData?.patrolRecordIssueHandles[0]?.state == 1 && title != '查看详情') || editData?.patrolRecordIssueHandles[0]?.state > 1) &&
<>
<div className="item-title">{"维修计划信息"}</div>
<Row>
<Col span={12}>
{!readOnly ? <ProFormSelect
rules={[{ required: true, message: '请选择维修人' }]}
options={users || []}
disabled={readOnly}
name="repairPerson"
label="维修人"
fieldProps={{
showSearch: true
}}
/> :
<ProFormText
name="repairPerson1"
label="维修人:"
disabled={true}
value={editData?.patrolRecordIssueHandles[0]?.repairPerson?.name}
/>
}
</Col>
<Col span={12}>
<ProFormDependency name={['repairPerson']}>
{({ repairPerson }) => {
const department = users?.find(s => s.id == repairPerson)?.department?.name
return (
<ProFormText
disabled={true}
name="repairUnits"
label="维修单位"
placeholder=""
fieldProps={{
showSearch: true
}}
colProps={{
span: 12,
}}
value={department}
/>
);
}}
</ProFormDependency>
</Col>
<Col span={12}>
<ProFormDateRangePicker
disabled={readOnly}
name="dateRange"
label="计划开始时间~结束时间:"
rules={[{ required: true, message: '请选择计划时间范围' }]}
/>
</Col>
<Col span={12}>
{!readOnly ? <ProFormSelect
rules={[{ required: true, message: '请选择质检人' }]}
options={users || []}
disabled={readOnly}
name="checkPerson"
label="质检人"
fieldProps={{
showSearch: true
}}
/> : <ProFormText
name="checkPerson1"
label="质检人:"
disabled={true}
value={editData?.patrolRecordIssueHandles[0]?.checkPerson?.name}
/>}
</Col>
<Col span={12}>
<ProFormTextArea
name="repairAsk"
label="维修要求:"
disabled={readOnly}
rules={[
{
max: 200, message: '维修要求长度不能大于200个字符'
},
{
whitespace: true, message: '请勿输入空格'
}]}
placeholder=""
/></Col>
{editData?.patrolRecordIssueHandles[0]?.state && editData?.patrolRecordIssueHandles[0]?.state > 3 && <Col span={12}>
<ProFormText
name="approvePerson"
label="制定人:"
disabled={true}
/>
<ProFormText
name="approveDate"
label="制定时间:"
disabled={true}
/>
</Col>}
</Row>
</>
}
{
((editData?.patrolRecordIssueHandles[0]?.state > 2) || title == '审核') &&
<>
<div className="item-title">{"维修计划审批"}</div>
<div>
<ProFormTextArea
name="approveOpinion"
label="审批意见:"
disabled={title != '审核'}
rules={[
{ required: true, message: '请输入审批意见' },
{
max: 200, message: '审批意见长度不能大于200个字符'
},
{
whitespace: true, message: '请勿输入空格'
}]}
placeholder="请输入审批意见"
/>
</div>
</>
}
{(editData?.patrolRecordIssueHandles[0]?.state && editData?.patrolRecordIssueHandles[0]?.state > 4) && renderRepairInfo()}
{(editData?.patrolRecordIssueHandles[0]?.state && editData?.patrolRecordIssueHandles[0]?.state > 5) && renderCheckInfo()}
</div>
</ ModalForm>
);
};

248
web-screen/client/src/sections/bigScreen/components/error/bottom.js

@ -1,19 +1,225 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, message, Button, Input } from 'antd'
import { Form, Input, Select, Button, Table, Modal, DatePicker, Checkbox, Row, Col, Collapse} from 'antd'
import { connect } from 'react-redux'
import ProTable from '@ant-design/pro-table'
import moment from 'moment'
import ReactEcharts from 'echarts-for-react'
import PerfectScrollbar from 'perfect-scrollbar'
import '../style.less'
import IssueHandleModal from '../IssueHandleModal'
import './style.less'
import { useFsRequest, ApiTable, Func } from '$utils';
const { Panel } = Collapse;
const ISSUEHANDLE_STATE = [1, 2, 3, 4, 5, 6, 7]
const STATE_TEXT = { 1: '待制定计划', 2: '待审核', 3: '计划驳回', 4: '待维修', 5: '待验收', 6: '验收通过', 7: '验收不通过', }
const Bottom = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
const { dispatch, clientHeight, clientWidth, actions,user } = props
const { bigScreen,patrolManage, issueHandle } = actions
const [tableList, settableList] = useState([])
const [name, setName] = useState('');
const [curState, setCurState] = useState('全部');
const format = 'YYYY-MM-DD HH:mm:ss'
const times = [moment().subtract(70, 'years').format(format), moment().format(format)]
const [search, setSearch] = useState({ name: null, time: [times[0], times[1]], state: 'null' })
const { data: structsUsers = [] } = useFsRequest({ url: 'structures/users' });
useEffect(() => {
queryData()
}, [])
const queryData = () => {
dispatch(bigScreen.getRecords(`patrolRecord/all/${times[0]}/${times[1]}/true/null`)).then(res => {
if (res.success) {
settableList(name != null ? res.payload.data
?.filter(v => (v.points.itemData.name.indexOf(name) != -1 || v.points.project.name.indexOf(name) != -1))
.filter(x => curState == '全部' || curState == renderOptionText(x?.patrolRecordIssueHandles[0]?.state))
.map(v => ({ ...v, key: v.id })) : res.payload.data?.map(v => ({ ...v, key: v.id })))
}
})
}
const onFinish = async (values, editData) => {
const dataToSave = { ...values };
if (editData?.patrolRecordIssueHandles?.length > 0) {
let msg = '';
if (editData?.patrolRecordIssueHandles[0]?.state == 3) {
dataToSave.state = 2;
msg = '维修计划修改';
}
if (editData?.patrolRecordIssueHandles[0]?.state == 1) {
dataToSave.state = 2;
dataToSave.creator = user;
dataToSave.createTime = moment();
msg = '计划制定';
}
return dispatch(
bigScreen.modifyPatrolRecordIssueHandle(editData?.patrolRecordIssueHandles[0]?.id, dataToSave,
values?.msg || msg),
).then(() => {
queryData();
});
}
let state = ISSUEHANDLE_STATE[1];
return dispatch(bigScreen.addPatrolRecordIssueHandle({
...dataToSave,
state,
patrolRecordId: editData?.id,
creator: user,
createTime: moment()
})).then(() => {
queryData();
});
};
const renderOptionText = (currentState) => {
let text = '待制定计划'
return STATE_TEXT[currentState] || text
}
const columns = [
{
title: '结构物名称',
dataIndex: 'name',
key: 'name',
width: '10%',
showInDetail: true,
render: (text, record, index) => {
return !record.points?.project ? '' : <div>{record.points.project.name}</div>
}
},
{
title: '上报人',
dataIndex: 'type',
key: 'type',
showInDetail: true,
width: '10%',
render: (text, record, index) => {
return !record.points?.user ? '' : <div>{record.points.user.name}</div>
}
},
{
title: '上报时间',
dataIndex: 'time',
key: 'time',
showInDetail: true,
render: (text, record, index) => moment(record.inspectionTime).format('YYYY-MM-DD HH:mm:ss') || '--'
}, {
title: '点位名称',
dataIndex: 'station',
key: 'station',
showInDetail: true,
render: (text, record, index) => record?.points?.itemData?.name
},
{
title: '问题来源',
dataIndex: 'source',
key: 'source',
showInDetail: true,
render: (text, record, index) => record?.patrolPlanId == -1 ? '主动上报' : '巡检上报' //暂定巡检上报 后续会增加平台录入
},
{
title: '严重等级',
dataIndex: 'level',
key: 'level',
showInDetail: true,
render: (text, record, index) => {
const LEVELS_ = ['严重', '中度', '轻微'];
const recordLevels = []
record?.points?.inspectContent ? Object.keys(record?.points?.inspectContent).map(key => {
recordLevels.push(record?.points?.inspectContent[key]?.level)
}) : ''
if (Array.isArray(record?.points?.inspectContent)) {
record?.points?.inspectContent?.map(x => {
x.checkItems?.map(v => {
recordLevels.push(v?.level)
})
})
}
const level = LEVELS_.find(s => recordLevels.find(x => x == s))
return level==='轻微'?<div style={{width:'3rem',padding:'0 .5rem',
backgroundImage: 'linear-gradient(90deg, #4af5e200 0%, #4af5e67d 51%, #4af5e000 100%)'}}>{level}</div>:
level==='中度'?<div style={{width:'3rem',padding:'0 .5rem',
backgroundImage: 'linear-gradient(90deg, #f5c24a00 0%, #f5c24a80 51%, #f5c24a00 100%)'}}>{level}</div>:
level==='严重'?<div style={{width:'3rem',padding:'0 .5rem',
backgroundImage: 'linear-gradient(90deg, #ff535300 0%, #ff5353a1 51%, #ff535300 100%)'}}>{level}</div>:'--'
|| '-';
}
},
{
title: '当前状态',
dataIndex: 'state',
key: 'name',
width: '10%',
showInDetail: true,
render: (text, record, index) => {
return !record?.patrolRecordIssueHandles || record?.patrolRecordIssueHandles?.length == 0 ? '待制定计划' :
renderOptionText(record?.patrolRecordIssueHandles[0]?.state)
}
},
{
title: '操作',
dataIndex: 'operation',
key: 'operation',
render: (text, record, index) => {
const options = [];
if ((!record?.patrolRecordIssueHandles || record?.patrolRecordIssueHandles?.length == 0 || record?.patrolRecordIssueHandles[0]?.state == 1)) {
options.push(<IssueHandleModal
structsUsers={structsUsers}
editData={record}
readOnly={false}
key="edit"
title="制定计划"
triggerRender={<a style={{ marginRight: 8 }}>制定计划</a>}
user={user}
onFinish={onFinish} />)
}
if ( (record?.patrolRecordIssueHandles[0]?.state == 3 || record?.patrolRecordIssueHandles[0]?.state == 2)) {
options.push(<IssueHandleModal
structsUsers={structsUsers}
editData={record}
readOnly={false}
key="edit"
title="修改计划"
triggerRender={<a style={{ marginRight: 8 }}>修改计划</a>}
user={user}
onFinish={onFinish} />)
}
if ( record?.patrolRecordIssueHandles[0]?.state == 2) {
options.push(<IssueHandleModal
structsUsers={structsUsers}
editData={record}
readOnly={true}
key="edit"
title="审核"
triggerRender={<a style={{ marginRight: 8 }}>审核</a>}
user={user}
onFinish={onFinish} />)
}
options.push(<IssueHandleModal
structsUsers={structsUsers}
editData={record}
readOnly={true}
key="edit"
title="查看详情"
triggerRender={<a>查看详情</a>}
user={user}
onFinish={onFinish} />)
return options;
}
}
]
@ -23,7 +229,41 @@ const Bottom = props => {
return (
<>
<div id='patrol-record' className='global-main' style={{marginTop:'2rem',backgroundColor:'transparent'}}>
<div style={{ marginBottom: 20, display: 'flex', justifyContent: 'space-between' }}>
<div>
<Input style={{ width: 240, marginRight: 20 }}
value={name} onChange={e => { setName(e.target.value) }}
placeholder="请输入结构物名称或点位名称" allowClear />
<Select
value={curState}
onChange={e => { setCurState(e) }}
style={{ width: 140, marginRight: 20 }}
options={[
{ value: '全部', label: '全部' },
].concat(ISSUEHANDLE_STATE.map(s => {
return { value: renderOptionText(s), label: renderOptionText(s) }
}))} />
<Button type='primary' onClick={() => { queryData() }}>搜索</Button>
</div>
</div>
<Table
scroll={{y:'23rem'}}
columns={columns}
dataSource={tableList}
pagination={{
showSizeChanger: true,
pageSizeOptions: [10, 20, 50],
className: 'global-pagination',
}}
rowClassName={(record, index) => {
let className = 'global-light-row';
if (index % 2 === 1) className = 'global-dark-row';
return className;
}}
/>
</div>
</>
)
}

208
web-screen/client/src/sections/bigScreen/components/error/style.less

@ -3,19 +3,219 @@
width: 33.625rem;
height: 20.3125rem;
margin-top: 2.6875rem;
background-image: linear-gradient(180deg, #0080ff00 3%, #0080ff1a 54%, #0080ff3d 100%);
background-image: linear-gradient(-45deg, #0080ff00 3%, #0080ff1a 54%, #0080ff3d 100%);
.topHeader {
// width: 33.375rem;
// height: 2.6875rem;
background: url(/assets/bigScreen/cardHeader.png) no-repeat;
background-size: 100% 100%;
height: 2.6875rem;
width: 100%;
font-family: PangMenZhengDao;
font-size: 1.125rem;
color: #CCE6FF;
// color: #CCE6FF;
letter-spacing: .0231rem;
display: flex;
padding-left: 2.375rem;
justify-content: space-between,
justify-content: space-between;
line-height: 2.6875rem;
}
}
}
.container-container{
display: flex;
height:17.625rem ;
width: 33.625rem;
.container-container-left{
flex: 1;
}
.container-container-right{
flex: 1;
margin-top: 7rem;
margin-left: 3rem;
.container-container-right-first{
position:relative;
display: flex;
align-items: center;
}
.container-container-right-second{
position:relative;
display: flex;
align-items: center;
margin-top: 1rem;
}
.container-container-right-three{
position:relative;
display: flex;
align-items: center;
margin-top: 1rem;
}
}
}
.container-container-right-first>div:first-child{
width: .5rem;
height: .5rem;
background-color: red;
}
.container-container-right-second>div:first-child{
width: .5rem;
height: .5rem;
background-color: #7FFFFC;
}
.container-container-right-three>div:first-child{
width: .5rem;
height: .5rem;
background-color: #F5C24A;
}
.container-container-right-first>div:last-child,
.container-container-right-second>div:last-child,
.container-container-right-three>div:last-child{
margin-left: .5rem;
}
.errorContainer{
.third-card-container{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 17.625rem;
}
.secondEcharts{
height:17.625rem ;
width: 47.75rem;
padding-bottom: 1rem;
.echarts-for-react {
height: 17.5rem !important; ;
}
}
.third-card{
background: url(/assets/bigScreen/shield.png) no-repeat;
background-size: 100% 100%;
width: 32.25rem;
height:12.1875rem;
position: relative;
.third-card-item{
position: absolute;
left: 1.8rem;
top: 2rem;
div:nth-child(1){
margin-bottom: 0.5rem;
}
div:nth-child(2){
display: flex;
justify-content: end;
align-items: center;
width: 10rem;
div:nth-child(1){
flex:1
}
div:nth-child(2){
flex:1
}
}
}
}
}
.patrol-record-detail-modal {
.ant-collapse>.ant-collapse-item>.ant-collapse-header {
padding: 0
}
}
.item-title {
background-color: #4A93DF;
padding: 10px;
color: #fff;
margin-bottom: 20px;
padding-left: 20px;
}
.ant-table-thead > tr > th {
background-color: green;
}
.ant-input-affix-wrapper{
background-color: #00508D !important;
>input{
background-color: #00508D;
}
}
#patrol-record{
.ant-select{
.ant-select-selector{
background-color: #00508D !important;
}
}
}
.ant-select-dropdown{
background-color: #00508D !important;
}
#patrol-record{
.ant-table-wrapper{
.ant-table-container{
.ant-table-header{
background: url(/assets/bigScreen/cardHeader.png) no-repeat;
background-size: 100% 100%;
}
}
}
}
#patrol-record{
.ant-table{
background: transparent;
color: #fff;
}
.ant-table-wrapper{
.ant-table-container{
.ant-table-header table{
background: url(/assets/bigScreen/cardHeader.png) no-repeat;
}
}
}
.ant-table-tbody > tr > td{
border-bottom: 1px solid #010e18 ;
}
.ant-table-thead > tr > th {
background-color: transparent;
background: transparent;
color: #FFFFFF;
font-family: PangMenZhengDao;
font-size: 1.125rem;
border-bottom:2px solid transparent;
}
.ant-table-cell-scrollbar:not([rowspan]){
box-shadow: 0 1px 0 1px transparent;
}
.ant-table-tbody > tr.ant-table-row:hover > td, .ant-table-tbody > tr > td.ant-table-cell-row-hover{
background-image: linear-gradient(360deg, #0080ff4d 0%, #0080ff33 58%, #0080ff4d 100%);
}
}
#patrol-record > div.ant-table-wrapper > div > div > div > div > div.ant-table-body > table > tbody > tr{
background-color: #00508D
}
#patrol-record {
.ant-pagination-item-link{
color:#fff ;
background-color: #00508D;
}
.ant-pagination-item{
color:#fff ;
background-color: #00508D;
}
}

310
web-screen/client/src/sections/bigScreen/components/error/top.js

@ -10,29 +10,325 @@ import './style.less'
const Top = props => {
const { dispatch, clientHeight, clientWidth, actions } = props
const {bigScreen}=actions
const [pieData,setPieData]=useState([])
const [totalQuestions,setTotalQuestions]=useState(0)//总问题
const [questionData,setQusetionData]=useState({waitingRepair:0,waitingPlan:0,waitingCheck:0,waitingExamine:0})
const [issueTotal,setIssueTotal]=useState(0)//质量数据的总数
const [month,setMonth]=useState([])
const [monthData,setMonthData]=useState([])
//初始化
useEffect(()=>{
getData()
},[])
// 第一张饼图
const pieOption = {
tooltip: {
trigger: 'item'
},
// legend: {
// top: '5%',
// left: 'right'
// },
color:['#7FFFFC','#F5C24A', , 'red'],
graphic: [{
type: 'text',
left: 'center',
top: '45%',
backgroundColor: 'blue',
z: 10,
style: {
fill: '#ffffff',
text: [
totalQuestions,
'发现问题'
].join('\n\n'),//实现两文本上下换行的效果
textAlign: 'center',
font: '1.5625rem D-DIN-Italic',
color:'#FFFFFF',
fontWeight:'Italic'
// color:'red'
}
}],
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: pieData
}
]
}
//we
const lineChartsOptions = {
title: {
text: '单位次',
textStyle: {
color: '#fff',
fontSize: '1rem'
}
},
tooltip: {
trigger: 'axis'
},
// legend: {
// data: ,
// right: 0,
// },
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
// toolbox: {
// feature: {
// saveAsImage: {}
// }
// },
xAxis: {
type: 'category',
boundaryGap: false,
data: month
},
yAxis: {
type: 'value'
},
series: [
{
name: '发现问题',
type: 'line',
stack: 'Total',
data: monthData
},
]
}
const getData=()=>{
const structArr = JSON.parse(sessionStorage.getItem('user')).monitorObject
dispatch(bigScreen.getSubSystemPatrolAbout({
projectId: structArr.toString(), IsbigScreen: 'true',
IsbigScreen: 'true',
STime: moment('1970-01-01').format('YYYY-MM-DD') + ' 00:00:00',
ETime: moment('2099-12-31').format('YYYY-MM-DD') + ' 23:59:59',
})
).then(res => {
if (res.success) {
const data = res.payload.data
const maxInspectionTimeByPointId = {};
data.forEach((item) => {
const { pointId, inspectionTime } = item;
if (pointId in maxInspectionTimeByPointId) {
if (inspectionTime > maxInspectionTimeByPointId[pointId]) {
maxInspectionTimeByPointId[pointId] = inspectionTime;
}
} else {
maxInspectionTimeByPointId[pointId] = inspectionTime;
}
})
const filteredData = data.filter((item) => {
const { pointId, inspectionTime } = item;
return inspectionTime === maxInspectionTimeByPointId[pointId];
})
const deviceLevelStatistics = {};
const levelValues = { 轻微: 0, 中度: 1, 严重: 2 };
filteredData.forEach((record) => {
const points = record.points;
if (points && points.inspectContent && Array.isArray(points.inspectContent)) {
points.inspectContent.forEach((content) => {
const device = content.deviceId;
content.checkItems.forEach(checkItem => {
const level = checkItem.level;
if (!checkItem.isNormal) {
if (!deviceLevelStatistics[device]) {
// 如果设备不存在于统计对象中,初始化
deviceLevelStatistics[device] = {
deviceName: content.deviceName, // 可能需要设备名称
level: level
};
} else {
// 如果设备已存在于统计对象中,比较level并更新为最低的level
deviceLevelStatistics[device].level = levelValues[level] > levelValues[deviceLevelStatistics[device].level] ? level : deviceLevelStatistics[device].level;
}
}
})
})
}
})
const levelCounts = { 轻微: 0, 中度: 0, 严重: 0 };
for (const deviceId in deviceLevelStatistics) {
if (deviceLevelStatistics.hasOwnProperty(deviceId)) {
const deviceInfo = deviceLevelStatistics[deviceId];
const level = deviceInfo.level;
// 增加相应等级的设备数量
levelCounts[level]++;
}
}
const total = Object.values(levelCounts).reduce((sum, count) => Number(sum) + Number(count), 0);
const data1 = Object.entries(levelCounts).map(([name, value]) => ({ name, value }))
setTotalQuestions(total)
setPieData(data1)
}
})
//根据状态统计问题
dispatch(bigScreen.countIssueByState({ projectId: structArr.toString()})).then(res=>{
if(res.success){
setQusetionData(res.payload.data)
const total = Object.values(res.payload.data).reduce((sum, count) => Number(sum) + Number(count), 0);
setIssueTotal(total)
}
})
//近三十天的问题统计
dispatch(bigScreen.countDayIssue({ projectId: structArr.toString()})).then(res=>{
if(res.success){
const data=res.payload.data
const { monthData, month } = data.reduce((result, item) => {
result.monthData.push(Number(item.count));
result.month.push(item.date);
return result;
}, { monthData: [], month: [] });
setMonthData(monthData)
setMonth(month)
// setQusetionData(res.payload.data)
// const total = Object.values(res.payload.data).reduce((sum, count) => Number(sum) + Number(count), 0);
// setIssueTotal(total)
}
})
}
return (
<>
<div style={{display:'flex'}}>
<div style={{ display: 'flex' }}>
{/* 上1 */}
<div style={{ position: 'relative' }} className='errorContainer'>
<div className='topHeader'>
<div>故障等级分布</div>
<div>
<div className='topHeader'>
<div>故障等级分布</div>
</div>
<div className='container-container'>
<div className='container-container-left'>
<ReactEcharts option={pieOption}></ReactEcharts>
</div>
<div className='container-container-right'>
<div className='container-container-right-first'>
<div ></div>
<div>严重&nbsp;<span style={{ marginLeft: '.5rem' }}>{pieData.find(item=>item.name==='严重')?.value||0}</span>&nbsp;
<span style={{ marginLeft: '.5rem',color:'red' }}>{pieData.length?((pieData.find(item=>item.name==='严重')?.value/totalQuestions)*100).toFixed(0):0 }%</span></div>
</div>
<div className='container-container-right-second'>
<div ></div>
<div>轻微&nbsp;<span style={{ marginLeft: '.5rem' }}>{pieData.find(item=>item.name==='轻微')?.value||0}</span>&nbsp;
<span style={{ marginLeft: '.5rem',color:'#7FFFFC' }}>{pieData.length?((pieData.find(item=>item.name==='轻微')?.value/totalQuestions)*100).toFixed(0):0 }%</span></div>
</div>
<div className='container-container-right-three'>
<div ></div>
<div>中等&nbsp;<span style={{ marginLeft: '.5rem' }}>{pieData.find(item=>item.name==='中度')?.value||0}</span>&nbsp;
<span style={{ marginLeft: '.5rem',color:'#F5C24A' }}>{pieData.length?((pieData.find(item=>item.name==='中度')?.value/totalQuestions)*100).toFixed(0):0}%</span></div>
</div>
</div>
</div>
</div>
</div>
{/* 上2 */}
<div style={{ position: 'relative' }} className='errorContainer'>
<div className='topHeader'>
<div>问题发现趋势30</div>
<div style={{ position: 'relative', width: '47.75rem', marginLeft: '1.5rem' }} className='errorContainer'>
<div>
<div className='topHeader'>
<div>问题发现趋势30</div>
</div>
<div className='secondEcharts'> <ReactEcharts option={lineChartsOptions}></ReactEcharts></div>
</div>
</div>
{/* 上三 */}
<div style={{ position: 'relative', marginLeft: '1.5rem' }} className='errorContainer'>
<div>
<div className='topHeader'>
<div>质量数据统计</div>
</div>
<div className='third-card-container'>
<div className='third-card'>
<div className='third-card-item'>
<div style={{color:'#cce6ff',fontSize:'.75rem',}}>待维修</div>
<div>
<div style={{color:'#cce6ff',fontSize:'.75rem',}}>数量:&nbsp;&nbsp;
<span style={{ color:'#6eece9',fontSize:'1.125rem'}}>{questionData.waitingrepair}</span>&nbsp;&nbsp;</div>
<div style={{ color:'#cce6ff',fontSize:'.75rem'}}>占比:&nbsp;&nbsp;
<span style={{ color:'#7ffffc',fontSize:'1.125rem'}}>{issueTotal?((questionData.waitingrepair/issueTotal)*100).toFixed(0):0}%</span>
</div>
</div>
</div>
<div className='third-card-item' style={{left:'20.5rem'}}>
<div style={{color:'#cce6ff',fontSize:'.75rem',textAlign:'right'}}>待制定计划</div>
<div>
<div style={{color:'#cce6ff',fontSize:'.75rem',}}>数量:&nbsp;&nbsp;
<span style={{ color:'#6eece9',fontSize:'1.125rem'}}>{questionData.waitingplan}</span>&nbsp;&nbsp;</div>
<div style={{ color:'#cce6ff',fontSize:'.75rem'}}>占比:&nbsp;&nbsp;
<span style={{ color:'#7ffffc',fontSize:'1.125rem'}}>{issueTotal?((questionData.waitingplan/issueTotal)*100).toFixed(0):0}%</span>
</div>
</div>
</div>
<div className='third-card-item' style={{left:'1.8rem',top:'7.2rem'}}>
<div style={{color:'#cce6ff',fontSize:'.75rem',}}>待验收</div>
<div>
<div style={{color:'#cce6ff',fontSize:'.75rem',}}>数量:&nbsp;&nbsp;
<span style={{ color:'#6eece9',fontSize:'1.125rem'}}>{questionData.waitingcheck}</span>&nbsp;&nbsp;</div>
<div style={{ color:'#cce6ff',fontSize:'.75rem'}}>占比:&nbsp;&nbsp;
<span style={{ color:'#7ffffc',fontSize:'1.125rem'}}>{issueTotal?((questionData.waitingcheck/issueTotal)*100).toFixed(0):0}%</span>
</div>
</div>
</div>
<div className='third-card-item' style={{left:'20.5rem',top:'7.2rem'}}>
<div style={{color:'#cce6ff',fontSize:'.75rem',textAlign:'right'}}>待审核</div>
<div>
<div style={{color:'#cce6ff',fontSize:'.75rem'}}>数量:&nbsp;&nbsp;
<span style={{ color:'#6eece9',fontSize:'1.125rem'}}>{questionData.waitingexamine}</span>&nbsp;&nbsp;</div>
<div style={{ color:'#cce6ff',fontSize:'.75rem'}}>占比:&nbsp;&nbsp;
<span style={{ color:'#7ffffc',fontSize:'1.125rem'}}>{issueTotal?((questionData.waitingexamine/issueTotal)*100).toFixed(0):0}%</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

2
web-screen/client/src/sections/bigScreen/components/leader/left.js

@ -226,7 +226,7 @@ const Left = props => {
{/* 左一 */}
{/**backgroundImage: 'linear-gradient(269deg, #000080ff 0%, #1a0080ff 58%, #3d0080ff 100%)' */}
<div>
<div style={{ position: 'relative' }} className='contanier'>
<div style={{ position: 'relative' }} className='contanier1'>
<div
style={{
display: 'flex',

1
web-screen/client/src/sections/bigScreen/components/run/style.less

@ -3,6 +3,7 @@
height: 17.8125rem;
margin-top: 1.5rem;
position: relative;
background-image: linear-gradient(180deg, #0080ff3d 0%, #0080ff1a 58%, #0080ff3d 100%);
.pieChartfs{
text-align: center;
height: 17.8125rem;

6
web-screen/client/src/sections/bigScreen/components/style.less

@ -260,7 +260,7 @@
height: 18.8125rem;
margin-top: 2.6875rem;
position: relative;
background-image: linear-gradient(-45deg, #0080ff00 0%, #0080ff1a 58%, #0080ff3d 100%);
background-image: linear-gradient(180deg, #0080ff3d 0%, #0080ff1a 58%, #0080ff3d 100%);
// margin-bottom: 0.5rem
}
@ -412,9 +412,9 @@
.contanier {
width: 26.3125rem;
height: 18.8125rem;
height: 16.8125rem;
margin-top: 2.6875rem;
background-image: linear-gradient(-45deg, #0080ff00 0%, #0080ff1a 58%, #0080ff3d 100%);
background-image: linear-gradient(180deg, #0080ff3d 0%, #0080ff1a 58%, #0080ff3d 100%);
}
.angle_top {

16
web-screen/client/src/utils/fs.js

@ -21,16 +21,28 @@
// 1rem的值永远为根元素的字体大小,所以此处通过调整全局字体大小来重置rem
docEl.style.fontSize = rem + "px";
}
// function setRemUnit2() {
// var rem = docEl.clientHeight / 67.5
// console.log('docEl.clientWidth',docEl.clientHeight)
setRemUnit();
// // 1rem的值永远为根元素的字体大小,所以此处通过调整全局字体大小来重置rem
// docEl.style.fontSize = rem + "px";
// }
setRemUnit();
// setRemUnit2()
// 监听resize事件——屏幕大小发生变化时触发
window.addEventListener("resize", setRemUnit);
window.addEventListener("resize", function () {
setRemUnit();
// setRemUnit2();
});
// 监听pageshow事件——显示页面时触发
window.addEventListener("pageshow", function(e) {
// 若是浏览器中点击后退时显示页面,则重置rem
if (e.persisted) {
setRemUnit();
// setRemUnit2()
}
});

2
web-screen/client/src/utils/webapi.js

@ -183,6 +183,8 @@ export const ApiTable = {
getRecord:'bigScreen/patrol/record',
getProjects:'bigScreen/projects',
findNewestRecord:'bigScreen/newestRecord',
countIssueByState:'bigScreen/patrolRecordIssue',
countDayIssue:'bigScreen/patrolRecordDayIssue'
};
//

Loading…
Cancel
Save