巴林闲侠 2 years ago
parent
commit
5c93148ae3
  1. 2
      api/app/lib/controllers/organization/user.js
  2. 64
      api/app/lib/controllers/patrolManage/yujingguanli.js
  3. 209
      api/app/lib/controllers/pointDeploy/index.js
  4. 44
      api/app/lib/models/project_graph.js
  5. 53
      api/app/lib/models/project_points_deploy.js
  6. 11
      api/app/lib/routes/patrolManage/yujingguanli.js
  7. 23
      api/app/lib/routes/pointDeploy/index.js
  8. 2
      api/config.js
  9. 1
      script/1.0.4/schema/4.updata_patrol_record_issue__handle.sql
  10. 33
      script/1.0.5/schema/4.project_graph_point_deploy.sql
  11. 3
      web/client/src/app.js
  12. 1
      web/client/src/sections/auth/containers/login.js
  13. 2
      web/client/src/sections/patrolManage/actions/index.js
  14. 13
      web/client/src/sections/patrolManage/actions/yujingguanli.js
  15. 47
      web/client/src/sections/patrolManage/components/xiafagaojin.js
  16. 46
      web/client/src/sections/patrolManage/containers/yujingguanli.js
  17. 4
      web/client/src/sections/patrolManage/nav-item.js
  18. 72
      web/client/src/sections/projectRegime/actions/graph.js
  19. 3
      web/client/src/sections/projectRegime/actions/index.js
  20. 1
      web/client/src/sections/projectRegime/actions/projectSituation.js
  21. 57
      web/client/src/sections/projectRegime/components/pointDeploy/heatmap.js
  22. 114
      web/client/src/sections/projectRegime/components/pointDeploy/station-spot.js
  23. 91
      web/client/src/sections/projectRegime/components/pointDeploy/style.css
  24. 4
      web/client/src/sections/projectRegime/components/pointModel.js
  25. 4
      web/client/src/sections/projectRegime/containers/index.js
  26. 9
      web/client/src/sections/projectRegime/containers/information.js
  27. 384
      web/client/src/sections/projectRegime/containers/pointDeploy/default.js
  28. 114
      web/client/src/sections/projectRegime/containers/pointDeploy/deploy-style.less
  29. 94
      web/client/src/sections/projectRegime/containers/pointDeploy/upload-img-modal.js
  30. 22
      web/client/src/sections/projectRegime/routes.js
  31. 9
      web/client/src/sections/shouye/actions/index.js
  32. 9
      web/client/src/sections/shouye/containers/index.js
  33. 62
      web/client/src/sections/shouye/containers/shouye.js
  34. 15
      web/client/src/sections/shouye/index.js
  35. 21
      web/client/src/sections/shouye/nav-item.js
  36. 5
      web/client/src/sections/shouye/reducers/index.js
  37. 32
      web/client/src/sections/shouye/routes.js
  38. 20
      web/client/src/sections/shouye/style.less
  39. 8
      web/client/src/utils/webapi.js
  40. 2
      web/package.json

2
api/app/lib/controllers/organization/user.js

@ -172,7 +172,7 @@ async function getUser (ctx, next) {
const models = ctx.fs.dc.models;
const { depId } = ctx.params;
let userRes = null;
if (depId !== 'null') {
if (depId !== 'undefined') {
userRes = await models.User.findAll({
where: {
departmentId: parseInt(depId),

64
api/app/lib/controllers/patrolManage/yujingguanli.js

@ -0,0 +1,64 @@
'use strict';
async function varfiyCode(ctx) {
try {
const { models } = ctx.fs.dc;
const { pushBySms, pushByEmail } = ctx.app.fs.utils
const { phone, type ,email} = ctx.request.body
// 伪造的请求可能由相同的sig参数组成
// const checkSigUsed = await models.PhoneValidateCode.findOne({
// where: { sig: sig }
// });
// if (checkSigUsed) {
// throw '参数错误!'
// }
// // 验证sig正确性
// const checkSig = Hex.stringify(SHA1(phone + r));
// if (!r || !sig || sig != checkSig) {
// throw '参数错误!'
// }
let varifyCode = ''
for (let i = 0; i < 6; i++) {
varifyCode += Math.floor(Math.random() * 10)
}
if(type.includes(1)){
await pushBySms({
phone: phone,
templateCode: 'SMS_261950020',
templateParam: {
code: varifyCode
},
})
}
if(type.includes(2)){
await pushByEmail({
email: email,
title: '测试',
text:'你知道吗'
})
}
// await models.PhoneValidateCode.create({
// phone: phone,
// code: varifyCode,
// sig: sig,
// expired: moment().add(10, 'minutes').format('YYYY-MM-DD HH:mm:ss')
// })
ctx.status = 204;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : '获取验证码失败'
}
}
}
module.exports = {
varfiyCode,
// pushByEmail
}

209
api/app/lib/controllers/pointDeploy/index.js

@ -0,0 +1,209 @@
'use strict';
async function findSingleGraph(ctx, next) {
let error = { name: 'FindSingleError', message: '查询单一数据失败' };
let rslt = null;
const { projectId } = ctx.params;
try {
rslt = await ctx.fs.dc.models.ProjectGraph.findOne({
where: { projectId: projectId }
});
error = null;
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
}
if (error) {
ctx.status = 400;
ctx.body = error;
} else {
ctx.status = 200;
ctx.body = rslt;
}
}
async function createGraph(ctx, next) {
let error = { name: 'AddError', message: '添加数据失败' };
let graphId = null;
try {
const data = ctx.request.body;
let dataToSave = {
projectId: data.projectId,
graph: data.graph,
}
const t = await ctx.fs.dc.orm.transaction();
try {
let planarGraph = await ctx.fs.dc.models.ProjectGraph.create(dataToSave);
graphId = planarGraph.id;
await t.commit();
} catch (e) {
await t.rollback();
throw e;
}
error = null;
// 日志信息
ctx.fs.api = ctx.fs.api || {};
ctx.fs.api.actionParameter = JSON.stringify(data);
ctx.fs.api.actionParameterShow = `新增graphId:${graphId}`;
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
}
if (error) {
ctx.status = 400;
ctx.body = error;
} else {
ctx.status = 200;
ctx.body = { id: graphId };
}
}
async function updateGraph(ctx, next) {
let error = { name: 'UpdateError', message: '修改数据失败' };
const { id } = ctx.params;
const data = ctx.request.body;
if (id) {
if (data && Object.keys(data).length) {
try {
const models = ctx.fs.dc.models;
let planarGraph = await models.ProjectGraph.findOne({ where: { id: id } });
const dataToSave = {};
if (planarGraph) {
const { projectId, graph } = data;
if (projectId && !(projectId == planarGraph.projectId))
dataToSave.projectId = projectId;
if (graph && !(graph == planarGraph.graph))
dataToSave.graph = graph;
}
dataToSave.id = planarGraph.id;
if (Object.keys(dataToSave).length) {
await models.ProjectGraph.update(dataToSave, { where: { id: planarGraph.id } });
}
error = null;
// 日志信息
ctx.fs.api = ctx.fs.api || {};
ctx.fs.api.actionParameter = JSON.stringify(data);
ctx.fs.api.actionParameterShow = `结构物平面图id:${data.id}`;
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
}
}
} else {
error = { name: 'UpdateError', message: `不存在{id=${id}}的结构物平面图` };
}
if (error) {
ctx.status = 400;
ctx.body = error;
} else {
ctx.status = 200;
ctx.body = { message: "结构物平面图修改成功" };
}
}
async function delProjectGraph(ctx) {
try {
const { id } = ctx.params;
const models = ctx.fs.dc.models;
let info = await models.ProjectGraph.findOne({ where: { id: id } });
if (info) {
await models.ProjectPointsDeploy.destroy({ where: { graphId: id } });
await models.ProjectGraph.destroy({ where: { id } })
ctx.status = 204;
} else {
ctx.status = 400;
ctx.body = { message: '数据不存在' }
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
"message": "删除结构物布设图失败"
}
}
}
async function getDeployPoints(ctx) {
try {
const pictureId = ctx.params.pictureId;
const models = ctx.fs.dc.models;
const heatmap = await models.ProjectGraph.findOne({ where: { id: pictureId } })
if (heatmap) {
let allPoints = await models.Point.findAll({
attributes: ['id', 'name'],
where: { projectId: heatmap.dataValues.projectId }
})
let setedPoints = await models.ProjectPointsDeploy.findAll({
where: { graphId: pictureId }
})
ctx.status = 200;
ctx.body = {
allPoints,
setedPoints
};
} else {
throw new Error('pictureId not found');
}
} catch (err) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`);
ctx.status = 400;
ctx.body = {
name: 'FindError',
message: '获取结构物平面图测点布设失败'
}
}
}
async function setDeployPoints(ctx) {
const pictureId = ctx.params.pictureId;
const req = ctx.request.body;
const models = ctx.fs.dc.models;
const orm = ctx.fs.dc.orm;
try {
ctx.fs.api = ctx.fs.api || {};
ctx.fs.api.actionParameter = req;
ctx.fs.api.actionParameterShow = '结构物平面图测点部署: ' + JSON.stringify(req);
const t = await orm.transaction();
try {
await models.ProjectPointsDeploy.destroy({ where: { graphId: pictureId }, transaction: t });
const layout = req.spots.map((hotspot, index) => {
let msg = {
graphId: pictureId,
pointId: hotspot.pointId,
position: JSON.stringify(hotspot.position)
}
return msg;
});
await models.ProjectPointsDeploy.bulkCreate(layout, { transaction: t });
await t.commit();
} catch (e) {
await t.rollback();
throw e;
}
ctx.status = 200;
ctx.body = {
name: "CreateSuccess",
message: "结构物平面图测点部署成功"
};
} catch (err) {
ctx.fs.logger.error(err);
ctx.status = 400;
ctx.body = {
name: 'CreateError',
message: "结构物平面图测点部署失败"
}
}
}
module.exports = {
findSingleGraph,
createGraph,
updateGraph,
delProjectGraph,
getDeployPoints,
setDeployPoints
};

44
api/app/lib/models/project_graph.js

@ -0,0 +1,44 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ProjectGraph = sequelize.define("projectGraph", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "project_graph_id_uindex"
},
projectId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "project_id",
autoIncrement: false
},
graph: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "graph",
autoIncrement: false
}
}, {
tableName: "project_graph",
comment: "",
indexes: []
});
dc.models.ProjectGraph = ProjectGraph;
return ProjectGraph;
};

53
api/app/lib/models/project_points_deploy.js

@ -0,0 +1,53 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ProjectPointsDeploy = sequelize.define("projectPointsDeploy", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
unique: "project_points_deploy_id_uindex"
},
pointId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "point_id",
autoIncrement: false
},
graphId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "graph_id",
autoIncrement: false
},
position: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: false,
field: "position",
autoIncrement: false
}
}, {
tableName: "project_points_deploy",
comment: "",
indexes: []
});
dc.models.ProjectPointsDeploy = ProjectPointsDeploy;
return ProjectPointsDeploy;
};

11
api/app/lib/routes/patrolManage/yujingguanli.js

@ -0,0 +1,11 @@
'use strict';
const yujingguanli = require('../../controllers/patrolManage/yujingguanli');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/yujingguanli'] = { content: '下发预警邮件', visible: true };
router.post('/yujingguanli', yujingguanli.varfiyCode);
};

23
api/app/lib/routes/pointDeploy/index.js

@ -0,0 +1,23 @@
'use strict';
const pointDeploy = require('../../controllers/pointDeploy');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/project/:projectId/planarGraph'] = { content: '获取结构物平面图数据', visible: false };
router.get('/project/:projectId/planarGraph', pointDeploy.findSingleGraph);
app.fs.api.logAttr['POST/planarGraph/add'] = { content: '新增结构物平面图', visible: true };
router.post('/planarGraph/add', pointDeploy.createGraph);
app.fs.api.logAttr['PUT/planarGraph/:id/modify'] = { content: '修改结构物平面图', visible: true };
router.post('/planarGraph/:id/modify', pointDeploy.updateGraph);
app.fs.api.logAttr['DEL/project/graph/:id'] = { content: '删除结构物布设图', visible: false };
router.del('/project/graph/:id', pointDeploy.delProjectGraph);
app.fs.api.logAttr['GET/picture/:pictureId/deploy/points'] = { content: '获取点位布设信息', visible: false };
router.get('/picture/:pictureId/deploy/points', pointDeploy.getDeployPoints);
app.fs.api.logAttr['POST/set/picture/:pictureId/deploy/points'] = { content: '点位布设', visible: true };
router.post('/set/picture/:pictureId/deploy/points', pointDeploy.setDeployPoints);
};

2
api/config.js

@ -94,7 +94,7 @@ const product = {
host: 'smtp.exmail.qq.com',
port: 465,
sender: {
name: '中鼎服务',
name: '运维服务',
address: 'fsiot@free-sun.com.cn',
password: 'Fs2689'
}

1
script/1.0.4/schema/4.updata_patrol_record_issue__handle.sql

@ -1,4 +1,5 @@
ALTER TABLE patrol_record_issue_handle ADD yanshoushijian timestamp(6);
ALTER TABLE patrol_record_issue_handle ADD yanshoucishu integer;
ALTER TABLE patrol_record_issue_handle ADD yujingshijian timestamp(6);
ALTER TABLE patrol_record_issue_handle ADD yujingafchishu integer;
ALTER TABLE patrol_record_issue_handle ADD isgaojing bool;

33
script/1.0.5/schema/4.project_graph_point_deploy.sql

@ -0,0 +1,33 @@
/*结构物布设图表*/
create table project_graph
(
id serial not null,
project_id int not null,
graph varchar(255) not null
);
create unique index project_graph_id_uindex
on project_graph (id);
alter table project_graph
add constraint project_graph_pk
primary key (id);
/*点位布设表*/
create table project_points_deploy
(
id serial not null,
point_id int not null,
graph_id int not null,
position varchar(1000) not null
);
create unique index project_points_deploy_id_uindex
on project_points_deploy (id);
alter table project_points_deploy
add constraint project_points_deploy_pk
primary key (id);

3
web/client/src/app.js

@ -8,6 +8,7 @@ import ProjectRegime from './sections/projectRegime';
import Organization from './sections/organization';
import PatrolManage from './sections/patrolManage';
import IssueHandle from './sections/issueHandle'
import Shouye from './sections/shouye';
import { Func } from '$utils';
const App = props => {
const { projectName } = props
@ -19,7 +20,7 @@ const App = props => {
return (
<Layout
title={projectName}
sections={[Auth, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle]}
sections={[Auth,Shouye, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle]}
/>
)

1
web/client/src/sections/auth/containers/login.js

@ -31,6 +31,7 @@ const Login = props => {
const [form] = Form.useForm();
const tourl = () => {
return '/shouye'
if (Func.isAuthorized("STRU_INFO_CONFIG")) {
return '/projectRegime/information'
}

2
web/client/src/sections/patrolManage/actions/index.js

@ -5,6 +5,7 @@ import * as record from './record'
import * as report from './report'
import * as template from './template'
import * as checkItems from './checkItems'
import * as yujingguanli from './yujingguanli'
export default {
...plan,
@ -12,4 +13,5 @@ export default {
...report,
...template,
...checkItems,
...yujingguanli
}

13
web/client/src/sections/patrolManage/actions/yujingguanli.js

@ -0,0 +1,13 @@
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function putxinxi (data) {
return dispatch => basicAction({
type: 'post',
data,
dispatch: dispatch,
actionType: 'PUT_XINXI',
url: ApiTable.yujingguanli,
msg: { option: '发送信息' },
});
}

47
web/client/src/sections/patrolManage/components/xiafagaojin.js

@ -2,15 +2,17 @@ import { Button, Form, Input, Modal, Select, DatePicker,Checkbox } from 'antd';
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { createPatrolTemplate, delPatrolTemplate, updatePatrolTemplate, getPatrolTemplate } from '../actions/template';
import {putxinxi} from '../actions/yujingguanli'
import moment from 'moment';
const { RangePicker } = DatePicker;
const { TextArea } = Input;
const PlanModal = ({ visible, onCancel, dispatch, type, curRecord, tableRef, checkItemsGroup }) => {
const PlanModal = ({ visible, onCancel, dispatch, type, curRecord, tableRef, checkItemsGroup,userlist }) => {
const [form] = Form.useForm();
const shigutypes = [{value:1,label: '邮件告警'},
{value:2,label:'短信告警'}]
console.log(userlist,'userlist')
return (
<Modal
visible={visible}
@ -25,31 +27,16 @@ const PlanModal = ({ visible, onCancel, dispatch, type, curRecord, tableRef, che
form
.validateFields()
.then((values) => {
const params = {
...values,
}
if (type === 'create') {
dispatch(createPatrolTemplate(params)).then(res => {
if (res.success) {
tableRef.current.reload();
form.resetFields();
onCancel();
}
})
} else {
dispatch(updatePatrolTemplate({
...params,
id: curRecord.id
})).then(res => {
if (res.success) {
tableRef.current.reload();
form.resetFields();
onCancel();
}
})
}
console.log('user,',userlist)
let usedata = userlist.filter(i=>i?.username===values.name)
console.log(usedata,'usedata')
dispatch(putxinxi({phone:[params.name],email:[usedata[0]?.email],type:params.type})).then(res=>{
console.log(res,'res')
})
console.log(params,'params')
})
.catch((info) => {
console.log('Validate Failed:', info);
@ -60,22 +47,20 @@ const PlanModal = ({ visible, onCancel, dispatch, type, curRecord, tableRef, che
form={form}
// layout="vertical"
name="form_in_modal"
initialValues={{
...curRecord,
checkItems: curRecord?.checkItems?.map(c => c.id)
}}
labelCol={{ span: 5 }} wrapperCol={{ span: 19 }} offe
labelCol={{ span: 5 }} wrapperCol={{ span: 19 }}
>
<Form.Item
name="name"
label="告警接收人"
rules={[
{ required: true, message: '请输入告警接收人' },
{ required: true, message: '请输入用户账号' },
]}
>
<Input />
<Select options={userlist?.map(i=>({value:i?.username,label:i?.name}))}></Select>
</Form.Item>
<Form.Item label="告警方式">
<Form.Item label="告警方式"
name="type">
<Checkbox.Group options={shigutypes}/>
</Form.Item>
</Form>

46
web/client/src/sections/patrolManage/containers/yujingguanli.js

@ -3,11 +3,12 @@ import { connect } from 'react-redux';
import { Button, Popconfirm, Tag,Tabs } from 'antd';
import ProTable from '@ant-design/pro-table';
import Xiafagaojin from '../components/xiafagaojin';
import {getDepUser} from '../../organization/actions/user'
import { createPatrolTemplate, delPatrolTemplate, updatePatrolTemplate, getPatrolTemplate } from '../actions/template';
import { getCheckItemsGroup } from '../actions/checkItems';
import moment from 'moment';
function YujingGuanli (props) {
const { dispatch, user,actions } = props;
const { dispatch, user,actions,depUser} = props;
const tableRef = useRef();
const format = 'YYYY-MM-DD HH:mm:ss'
const { patrolManage } = actions
@ -35,29 +36,30 @@ function YujingGuanli (props) {
if (res.success) {
console.log(res,'水平')
let obj = {}
res?.payload?.data?.map(i=>{
if(obj[i?.points?.project?.id]){
// if(obj[i?.points?.project?.id.toString()][i.pointId.toString()]){
// obj[i?.points?.project?.id.toString()][i.pointId.toString()].ponintname=i.points?.itemData?.name
// obj[i?.points?.project?.id.toString()][i.pointId.toString()].num=obj[i?.points?.project?.id.toString()][i.pointId.toString()].num+i?.patrolRecordIssueHandles[0]?.state==6?1:0
// res?.payload?.data?.map(i=>{
// if(obj[i?.points?.project?.id]){
// // if(obj[i?.points?.project?.id.toString()][i.pointId.toString()]){
// // obj[i?.points?.project?.id.toString()][i.pointId.toString()].ponintname=i.points?.itemData?.name
// // obj[i?.points?.project?.id.toString()][i.pointId.toString()].num=obj[i?.points?.project?.id.toString()][i.pointId.toString()].num+i?.patrolRecordIssueHandles[0]?.state==6?1:0
// }else{
// obj[i?.points?.project?.id.toString()][i.pointId.toString()].ponintname=i.points?.itemData?.name
// obj[i?.points?.project?.id.toString()][i.pointId.toString()].num=i?.patrolRecordIssueHandles[0]?.state==6?1:0
// }
obj[i?.points?.project?.id].push({pointId:i.pointId,pointname:i.points.itemData.name})
// i?patrolRecordIssueHandles[0]?.state==6
obj[i?.points?.project?.id].num= obj[i?.points?.project?.id].num + i?.patrolRecordIssueHandles[0]?.state==6 ?1:0
}else{
obj[i?.points?.project?.id]={name:i?.points?.project?.name,num:i?.patrolRecordIssueHandles[0]?.state==6 ?1:0 }
// // }else{
// // obj[i?.points?.project?.id.toString()][i.pointId.toString()].ponintname=i.points?.itemData?.name
// // obj[i?.points?.project?.id.toString()][i.pointId.toString()].num=i?.patrolRecordIssueHandles[0]?.state==6?1:0
// // }
// obj[i?.points?.project?.id]?.push({pointId:i.pointId,pointname:i.points.itemData.name})
// // i?patrolRecordIssueHandles[0]?.state==6
// obj[i?.points?.project?.id].num= obj[i?.points?.project?.id].num + i?.patrolRecordIssueHandles[0]?.state==6 ?1:0
// }else{
// obj[i?.points?.project?.id]={name:i?.points?.project?.name,num:i?.patrolRecordIssueHandles[0]?.state==6 ?1:0 }
}
console.log(obj,'obj')
})
// }
// console.log(obj,'obj')
// })
}
})
}
console.log(depUser,'depUser')
useEffect(() => {
// dispatch(patrolManage.records(`patrolRecord/all/null/null/true/null`)).then(res=>{
// let obj = {}
@ -81,7 +83,7 @@ function YujingGuanli (props) {
// console.log(obj,'obj')
// console.log(res,'res')
// })
dispatch(getDepUser())
queryData()
dispatch(getCheckItemsGroup())
}, [])
@ -187,11 +189,12 @@ console.log(tableList,'tablist')
]}
/>
{
visible ?
visible &&depUser.filter(i=>i.username&&i.email).length!==0 ?
<Xiafagaojin
visible={visible}
type={type}
curRecord={curRecord}
userlist={depUser.filter(i=>i.username&&i.email)}
onCancel={() => {
setVisible(false);
setCurRecord({})
@ -204,10 +207,11 @@ console.log(tableList,'tablist')
}
function mapStateToProps (state) {
const { auth, global } = state
const { auth, global ,depUser} = state
return {
user: auth.user,
actions: global.actions,
depUser: depUser.data || [],
}
}
export default connect(mapStateToProps)(YujingGuanli);

4
web/client/src/sections/patrolManage/nav-item.js

@ -26,9 +26,9 @@ export function getNavItem (user, dispatch) {
{Func.isAuthorized('CHECKMOULD') && <Menu.Item key="patrolTemplate">
<Link to="/patrolManage/patrolTemplate">巡检模板</Link>
</Menu.Item>}
{/* { <Menu.Item key="yujingguanli">
{ <Menu.Item key="yujingguanli">
<Link to="/patrolManage/yujingguanli">预警管理</Link>
</Menu.Item>} */}
</Menu.Item>}
</SubMenu>
);
}

72
web/client/src/sections/projectRegime/actions/graph.js

@ -0,0 +1,72 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getProjectGraph(projectId) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
actionType: 'GET_PROJECT_PLANAR_GRAPH',
url: ApiTable.getProjectGraph.replace('{projectId}', projectId),
msg: { option: '获取结构物平面图' },
reducer: { name: 'projectGraph' }
});
}
export function createGraph(data) {
return (dispatch) => basicAction({
type: 'post',
data,
dispatch,
actionType: 'ADD_PROJECT_PLANAR_GRAPH',
url: ApiTable.createGraph,
msg: { option: '新增结构物平面图' },
});
}
export function updateGraph(id, data) {
return (dispatch) => basicAction({
type: 'post',
data,
dispatch,
actionType: 'UPDATE_PROJECT_PLANAR_GRAPH',
url: ApiTable.updateGraph.replace('{id}', id),
msg: { option: '修改结构物平面图' },
});
}
export function deleteGraph(id) {
return (dispatch) => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_PROJECT_GRAPH',
url: ApiTable.deleteGraph.replace('{id}', id),
msg: {
option: '删除结构物布设图',
},
});
}
export function getDeployPoints(pictureId) {
return (dispatch) => basicAction({
type: 'get',
dispatch,
actionType: 'GET_PROJECT_DEPLOY_POINTS',
url: ApiTable.getDeployPoints.replace('{pictureId}', pictureId),
msg: { option: '获取结构物平面图测点分布' },
reducer: { name: 'projectDeployPoints' }
});
}
export function setDeployPoints(pictureId, data) {
return (dispatch) => basicAction({
type: 'post',
data,
dispatch,
actionType: 'SET_PROJECT_DEPLOY_POINTS',
url: ApiTable.setDeployPoints.replace('{pictureId}', pictureId),
msg: { option: '结构物平面图点位布设' },
});
}

3
web/client/src/sections/projectRegime/actions/index.js

@ -2,8 +2,9 @@
import * as projectSituation from './projectSituation'
import * as projectGraph from './graph'
export default {
...projectSituation,
...projectGraph
}

1
web/client/src/sections/projectRegime/actions/projectSituation.js

@ -60,6 +60,7 @@ export function positionList (query) {
actionType: 'POSITION_LIST',
url: ApiTable.position,
msg: { error: '获取点位列表失败', },
reducer: { name: 'projectPoints' }
});
}

57
web/client/src/sections/projectRegime/components/pointDeploy/heatmap.js

@ -0,0 +1,57 @@
'use strict'
import React, { Component, PropTypes } from 'react';
import { DropTarget } from 'react-dnd';
import StationSpot from './station-spot';
const heatmapTarget = {
drop(props, monitor) {
//get item from station-spot.js
//item:{deployed, rect, spotInlist, info}
const item = monitor.getItem();
const move = monitor.getDifferenceFromInitialOffset();
props.onDeploySpot({ ...item, move });
}
};
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
};
}
class Heatmap extends React.Component {
componentDidMount() {
}
renderSpots() {
const { width, height, spots, onRemoveSpot } = this.props;
return spots.map(s => <StationSpot key={s.sensorId} info={s}
size={{ "width": width, "height": height }}
onRemoveSpot={onRemoveSpot} />);
}
render() {
const { connectDropTarget, height, width, image } = this.props;
let targetStyle = {
position: 'relative',
width: width,
// overflow:'hidden',
height: height,
background: `url("/_file-server/${image}") no-repeat`,
backgroundSize: '100% 100%',
};
return connectDropTarget(
<div id="dragTarget" style={targetStyle}>
{this.renderSpots()}
</div>
)
}
}
export default DropTarget('stationSpot', heatmapTarget, collect)(Heatmap);

114
web/client/src/sections/projectRegime/components/pointDeploy/station-spot.js

@ -0,0 +1,114 @@
/**
* Created by yuanfenghua on 2018/6/18.
*/
'use strict'
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import { connect } from 'react-redux';
import { DragSource } from 'react-dnd';
import { Tooltip } from 'antd';
import { MinusOutlined } from '@ant-design/icons';
import Style from './style.css';
const stationSource = {
beginDrag(props, monitor, component) {
const dom = findDOMNode(component);
const rect = {
x: dom.offsetLeft - dom.offsetParent.scrollLeft,
y: dom.offsetTop - dom.offsetParent.scrollTop
};
const spotInlist = {
x: dom.getBoundingClientRect().left,
y: dom.getBoundingClientRect().top
};
return {
info: props.info,
rect: rect,
spotInlist: spotInlist,
deployed: props.info.deployed
};
},
endDrag(props, monitor) {
if (!monitor.didDrop() && props.onRemoveSpot) {
props.onRemoveSpot(monitor.getItem().info);
}
},
canDrag(props) {
if (props.size) {
//热点图上的热点可拖拽
return true;
} else {
//测点树未布设的叶结点可拖拽
return !props.children && props.info.deployed == false
}
},
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
class StationSpot extends React.Component {
constructor(props) {
super(props);
}
renderTreeTitle = () => {
const { isDragging, info } = this.props;
const { spotId, location, deployed } = info;
const opacity = isDragging ? 0.4 : 1;
return (
<span style={{ lineHeight: '15px', opacity }} className={Style['icon']}>
<span key={spotId}>
<Tooltip title={location}>
<span className={deployed == false ? Style['station-tree-node-normal'] : null}>{location.length >= 12 ? location.substring(0, 12) + "..." : location}</span>
</Tooltip>
{deployed ? <MinusOutlined type="minus-circle-o" className={Style['tip']} onClick={this.onRemoveSpot} /> : null}
</span>
</span>
);
};
onRemoveSpot = () => {
const { onRemoveSpot, info } = this.props;
if (onRemoveSpot) {
onRemoveSpot(info);
}
};
renderHotspot = () => {
const { info, size } = this.props;
const { key, location, x, y, screenWidth, screenHeight } = info;
const { width, height } = size;
let style = {
position: 'absolute',
left: width * x / screenWidth,
top: height * y / screenHeight,
cursor: 'move'
};
return <span style={style}>
<Tooltip title={location}>
<div style={{ height: 24, width: 24, borderRadius: '100%', backgroundColor: 'rgba(16,142,233,0.2)', padding: '5px' }}>
<div style={{ height: 14, width: 14, borderRadius: '100%', backgroundColor: '#108ee9', boxShadow: '0 0 10px #108ee9' }}></div>
</div>
</Tooltip>
</span>
};
render() {
const { connectDragSource, size } = this.props;
return connectDragSource(
size ? this.renderHotspot() : this.renderTreeTitle()
);
}
}
export default connect()(DragSource('stationSpot', stationSource, collect)(StationSpot));

91
web/client/src/sections/projectRegime/components/pointDeploy/style.css

@ -0,0 +1,91 @@
.station-tree-node-normal{
margin-right: 6px;
padding: 0 4px;
height: 15px;
line-height: 15px;
background: #108ee9;
color: #fff;
font-size: 12px;
text-align: center;
}
.icon .tip{
margin-left:10px;
-webkit-transition:opacity 0.1s 0.2s;
opacity:0;
pointer-events:none;
}
.icon:hover .tip{
-webkit-transition:opacity 0.2s;
opacity:1;
pointer-events:auto;
}
.icon .tip:hover{
-webkit-transition:none;
}
:global(.no-card-border>.ant-card-head){
border: none;
}
.cardCoverBox{
position: absolute;
top: 50px;
left: 0;
right: 0;
bottom: 0;
}
.cardCover{
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-color: rgba(240 , 240, 240, 0.9);
z-index: 2;
}
.cardCover .btnCell{
text-align: center;
margin: 25px;
}
.cardFootCover{
display: flex;
justify-content: space-around;
align-items: center;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.9);
z-index: 1;
opacity: 0;
}
.cardFootCover:hover{
opacity: 1;
}
.deleteBox{
margin: 0 40px;
}
.deleteBox h3{
text-align: center;
margin-bottom: 5px;
}
.deleteBox .btnRow{
display: flex;
justify-content: space-around;
margin-top: 10px;
width: 100%;
}
.loadingBox{
width: 100%;
margin-bottom: 50px;
text-align: center;
padding: 50px 0;
}

4
web/client/src/sections/projectRegime/components/pointModel.js

@ -164,14 +164,14 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, q
rules={[{ required: true, message: '请输入描述内容', },]}>
<TextArea />
</Form.Item>
{/* <Form.Item label='' name="equipmentNo" style={{}}
<Form.Item label='设备编号' name="equipmentNo" style={{}}
initialValue={modelData?.equipmentNo}>
<Input placeholder="请输入设备编号" allowClear />
</Form.Item>
<Form.Item label='设备型号' name="equipmentModel" style={{}}
initialValue={modelData?.equipmentModel}>
<Input placeholder="请输入设备型号" allowClear />
</Form.Item> */}
</Form.Item>
<Form.Item
label="点位图片"
name='img'

4
web/client/src/sections/projectRegime/containers/index.js

@ -4,6 +4,6 @@
import QrCode from './qrCode'
import Information from './information'
import Point from './point'
import PointDeploy from './pointDeploy/default'
export { QrCode, Information, Point };
export { QrCode, Information, Point, PointDeploy };

9
web/client/src/sections/projectRegime/containers/information.js

@ -95,7 +95,7 @@ const Information = (props) => {
key: 'operation',
render: (text, record, index) => {
return (
<div style={{ width: 190 }}>
<div>
<Button type="link" onClick={() => {
setAddModel(true)
setModelData(record)
@ -123,8 +123,11 @@ const Information = (props) => {
</Popconfirm>
{/* <Button type="link" danger >二维码生成</Button> */}
<Button type="link" onClick={() => {
dispatch(push(`/projectRegime/information/${record.id}`));
dispatch(push(`/projectRegime/information/${record.id}/point`));
}} >点位</Button>
<Button type="link" onClick={() => {
dispatch(push(`/projectRegime/information/${record.id}/deploy`));
}} >布设</Button>
</div>
)
}
@ -228,7 +231,7 @@ const Information = (props) => {
)
}
function mapStateToProps (state) {
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,

384
web/client/src/sections/projectRegime/containers/pointDeploy/default.js

@ -0,0 +1,384 @@
'use strict'
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { findDOMNode } from 'react-dom';
import Heatmap from '../../components/pointDeploy/heatmap';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { Layout, Tree, Button, Input, Popconfirm, message, Spin } from 'antd'
const { Content, Sider } = Layout;
const Search = Input.Search;
const TreeNode = Tree.TreeNode;
import StationSpot from '../../components/pointDeploy/station-spot';
import './deploy-style.less';
import { getProjectGraph, deleteGraph, getDeployPoints, setDeployPoints } from '../../actions/graph';
import UploadImgModal from './upload-img-modal';
import PerfectScrollbar from 'perfect-scrollbar';
class ConfigPlanarGraph extends Component {
constructor(props) {
super(props);
this.ps = null;
this.state = {
uploadImgModal: '',
searchValue: '',
deployState: -1, //0未布,1已布,-1全部
filteredSpots: [],
spots: [],
dataHasChanged: false,
}
this.projectId = props?.match?.params?.id
}
componentDidUpdate() {
let ele = document.getElementById('security-spots-scroller');
if (ele) {
this.ps = new PerfectScrollbar(ele);
}
}
componentDidMount() {
this.getData();
}
getData = () => {
this.props.dispatch(getProjectGraph(this.projectId)).then(_ => {
if (_.success) {
let graph = _.payload.data;
if (graph) {//有图片
this.props.dispatch(getDeployPoints(graph.id));//获取平面图点位分布
} else {
this.setSpotsState([]);
}
}
});
}
componentWillReceiveProps(nextProps) {
const { projectDeployPoints } = nextProps;
if (projectDeployPoints && projectDeployPoints != this.props.projectDeployPoints) {
this.setSpotsState(projectDeployPoints);
}
}
setSpotsState = (projectDeployPoints) => {
let deployedSpotsMap = new Map();
projectDeployPoints.setedPoints?.forEach(s => {
deployedSpotsMap.set(s.pointId, s);
});
let tempData = [];
projectDeployPoints.allPoints?.map(m => {
let x = null, y = null, screenH = null, screenW = null;
let deployed = false;
let station = deployedSpotsMap.get(m.id);
if (station) {
let p = JSON.parse(station.position);
x = p.x;
y = p.y;
screenH = p.screenHeight;
screenW = p.screenWidth;
deployed = true;
}
tempData.push({
groupId: 1,
groupName: '全部',
pointId: m.id,
location: m.name,
x: x,
y: y,
screenHeight: screenH,
screenWidth: screenW,
deployed: deployed,
})
});
this.setState({
spots: tempData,
filteredSpots: tempData,
});
}
onAddImgClick = () => {
this.openPcrModal(null)
};
editHandler = (heatmap) => {
this.openPcrModal(heatmap)
};
openPcrModal = (heatmap) => {
const { dispatch } = this.props;
this.setState({
uploadImgModal: <UploadImgModal
dispatch={dispatch}
projectId={this.projectId}
pictureInfo={heatmap}
onCancel={this.closeUploadPointsImgModal}
getData={this.getData}
/>,
})
}
closeUploadPointsImgModal = () => {
this.setState({ uploadImgModal: '' })
};
onDeploySpot = (spot) => {
const { pictureInfo, clientHeight, clientWidth } = this.props;
const { spots, deployState, searchValue, partsSpots } = this.state;
const that = this;
let h = clientHeight / 1.3;
let w = clientWidth / 1.3;
function dealPosition(spot, item) {
if (spot.deployed) {
item.x = spot.rect.x + spot.move.x;
item.y = spot.rect.y + spot.move.y;
item.screenHeight = h;
item.screenWidth = w;
} else {
const boundingClientRect = findDOMNode(that.refs.heatmapComponent).getBoundingClientRect();
item.x = spot.spotInlist.x + spot.move.x - boundingClientRect.left;
item.y = spot.spotInlist.y + spot.move.y - boundingClientRect.top;
item.screenHeight = h;
item.screenWidth = w;
item.deployed = true;
}
}
if (pictureInfo) {
let tempSpots = Object.assign([], spots);
if (spot.info.pointId) {
tempSpots.forEach(item => {
if (item.pointId == spot.info.pointId) {
dealPosition(spot, item)
}
});
}
this.setState({ spots: tempSpots });
this.filterSpots(deployState, searchValue);
this.setState({ changedTreeNodeKey: spot.info.key, dataHasChanged: true });
}
};
filterSpots = (deployState, searchValue) => {
let deploySpots = this.state.spots;
if (deployState != -1) {
deploySpots = this.state.spots.filter(s => s.deployed == (deployState == 1 ? true : false));
}
let searchSpots = deploySpots;
if (searchValue.trim().length > 0) {
searchSpots = deploySpots.filter(s => s.location.indexOf(searchValue) >= 0);
}
this.setState({
searchValue,
deployState,
filteredSpots: searchSpots
});
};
onSearch = (searchValue) => {
this.filterSpots(this.state.deployState, searchValue);
};
handleStateChange = (deployState) => {
this.filterSpots(deployState, this.state.searchValue);
};
onRemoveSpot = (spot) => {
const { pictureInfo } = this.props;
const { spots, deployState, searchValue, partsSpots } = this.state;
if (pictureInfo) {
let tempSpots;
if (spot.pointId) {
tempSpots = Object.assign([], spots);
tempSpots.forEach(item => {
if (item.pointId == spot.pointId) {
item.x = null;
item.y = null;
item.screenWidth = null;
item.screenHeight = null;
item.deployed = false;
}
});
tempSpots = tempSpots.concat(partsSpots)
} else {
tempSpots = Object.assign([], partsSpots);
tempSpots.forEach(item => {
if (item.partId == spot.partId) {
item.x = null;
item.y = null;
item.screenWidth = null;
item.screenHeight = null;
item.deployed = false;
}
});
tempSpots = tempSpots.concat(spots)
}
this.filterSpots(deployState, searchValue);
this.setState({ changedTreeNodeKey: spot.key, dataHasChanged: true });
}
};
loop = (data) => {
if (!data || data.length == 0) return;
const treeNodes = [];
data.forEach((item) => {
let title = <StationSpot info={item} children={item.children} />;
if (item.children) {
treeNodes.push(
<TreeNode key={item.key} title={title}>
{this.loop(item.children)}
</TreeNode>
);
} else {
let titleProps = {
info: item,
children: item.children,
onRemoveSpot: this.onRemoveSpot,
};
//性能优化,减少组件渲染
if (this.state.changedTreeNodeKey == item.key) titleProps.key = Math.random();
let nodeTitle = <StationSpot {...titleProps} />;
treeNodes.push(<TreeNode key={item.key} title={nodeTitle} />);
}
});
return treeNodes;
};
formatTreeSource = (data) => {
if (!data || data.length == 0) return;
let tempGroups = new Map();
data.map(item => {
if (tempGroups.has(item.groupId)) {
let groupChildren = tempGroups.get(item.groupId).children;
item.key = `0-${item.groupId}-${item.pointId}`;
groupChildren.set(item.pointId, item);
} else {
tempGroups.set(item.groupId, {
'key': `0-${item.groupId}`,
'location': item.groupName,
'groupId': item.groupId,
'children': new Map(),
});
let groupChildren = tempGroups.get(item.groupId).children;
item.key = `0-${item.groupId}-${item.pointId}`;
groupChildren.set(item.pointId, item);
}
});
return tempGroups;
};
onSaveClick = () => {
this.saveHotspots(this.state.spots);
};
saveHotspots = (data) => {
const { pictureInfo } = this.props;
let postData = data.filter(s => s.x != null && s.y != null).map(item => {
const { x, y, screenWidth, screenHeight } = item;
let relativeX = parseFloat((x / screenWidth).toFixed(4));
let relativeY = parseFloat((y / screenHeight).toFixed(4));
if (item.pointId) {
return ({
pointId: item.pointId,
position: { x, y, screenWidth, screenHeight, relativeX, relativeY }
})
}
});
this.props.dispatch(setDeployPoints(pictureInfo.id, { "spots": postData })).then(res => {
message.success(res.payload.message);
this.setState({ dataHasChanged: false, deployState: -1 }, () => {
this.props.dispatch(getDeployPoints(pictureInfo.id));//获取平面图点位分布
});
});
};
render() {
const { pictureInfo, clientHeight, clientWidth } = this.props;
const { deployState, spots, filteredSpots, dataHasChanged } = this.state;
const treeDataSource = this.formatTreeSource(filteredSpots);
let h = clientHeight / 1.3;
let w = clientWidth / 1.3;
return (<div className='patrolLayout'>
<Layout>
<Sider width={230} style={{ background: 'transparent', height: 400 }}>
<div className='search-panel'>
<Button className={deployState == -1 ? 'btn-spots-filter' : 'btn-default'} style={{ borderRadius: 0 }} onClick={() => this.handleStateChange(-1)}>全部</Button>
<Button className={deployState == 1 ? 'btn-spots-filter' : 'btn-default'} style={{ borderRadius: 0 }} onClick={() => this.handleStateChange(1)}>已布</Button>
<Button className={deployState == 0 ? 'btn-spots-filter' : 'btn-default'} style={{ borderRadius: 0 }} onClick={() => this.handleStateChange(0)}>未布</Button>
<div style={{ marginTop: -1 }}>
<Search
placeholder={"请输入名称关键字搜索"}
style={{ borderRadius: 0 }}
onChange={e => this.onSearch(e.target.value)}
/>
</div>
</div>
{
treeDataSource ?
<div id='security-spots-scroller' style={{ position: 'relative', height: 600 }}>
<Tree className='equip-tree' showLine defaultExpandAll={true}>
{this.loop(treeDataSource)}
</Tree> </div> : <div style={{ textAlign: 'center' }}></div>
}
</Sider>
<Layout>
<Content className='spots-opr-content' style={{ width: w }}>
{spots && pictureInfo?.graph ?
<Heatmap key={Math.random()}
ref="heatmapComponent"
height={h}
width={w}
image={pictureInfo?.graph}
spots={spots.filter(s => s.deployed == true)}
onRemoveSpot={this.onRemoveSpot}
onDeploySpot={this.onDeploySpot}
/>
: <div style={{ border: '1px dashed #999', width: w, height: h, paddingTop: clientHeight * 0.4, textAlign: 'center' }}>
暂无热点图
</div>
}
<div className='opr-tip-row'>
<div>说明拖拽点位进行布设拖出画布移除点位</div>
{
pictureInfo ?
<div className='opr-button'>
<Popconfirm
title='确认删除该结构物布设图?(已布点位将同步删除)'
position='top'
onConfirm={() => {
this.props.dispatch(deleteGraph(pictureInfo.id)).then(_ => {
this.getData();
})
}}>
<Button className='graph-cfg-btn'>删除图片</Button>
</Popconfirm>
<Button className='graph-cfg-btn' onClick={() => this.editHandler(pictureInfo)} style={{ marginLeft: 15 }}>修改图片</Button>
<Button className='graph-cfg-btn' type='primary' onClick={this.onSaveClick} disabled={!dataHasChanged} style={{ marginLeft: 15 }}>完成</Button>
</div>
:
<div className='opr-button'>
<Button className='graph-cfg-btn' type='primary' onClick={this.onAddImgClick}>添加布设图</Button>
</div>
}
</div>
</Content>
</Layout>
</Layout>
{this.state.uploadImgModal}
</div>)
}
}
function mapStateToProps(state) {
const { global, projectGraph, projectDeployPoints } = state;
return {
pictureInfo: projectGraph.data,
projectDeployPoints: projectDeployPoints.data,
clientHeight: global.clientHeight,
clientWidth: global.clientWidth
};
}
export default connect(mapStateToProps)(DragDropContext(HTML5Backend)(ConfigPlanarGraph));

114
web/client/src/sections/projectRegime/containers/pointDeploy/deploy-style.less

@ -0,0 +1,114 @@
.search-panel {
text-align: center;
}
.search-panel input {
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.spots-opr-content {
padding: 0;
margin: 0;
position: relative;
}
.opr-tip-row {
display: flex;
margin-top: 10px;
justify-content: space-between;
}
.opr-button {
.ant-btn-disabled,
.ant-btn.disabled,
.ant-btn[disabled] {
//按钮 禁用
background-color: #3198F7;
}
}
.graph-cfg-btn {
color: #fff;
background-color: #3198F7;
border-color: #3198F7;
}
//蓝色按钮 鼠标悬浮
.graph-cfg-btn:hover,
.graph-cfg-btn:focus {
//color: #fff;
background-color: #3198F7;
border-color: #3198F7;
}
.patrolLayout {
margin-top: 20px;
.ant-layout {
background-color: transparent;
}
.ant-input:hover,
.ant-input:focus {
border-color: #fff;
}
}
.equip-tree {
//测点 树结构
.ant-tree.ant-tree-show-line li span.ant-tree-switcher {
//展开, 关闭按钮
color: #fff;
}
.ant-tree li .ant-tree-node-content-wrapper {
//文字白色
color: #fff;
}
.ant-tree.ant-tree-show-line li:not(:last-child)::before {
//不显示竖线
width: 0px;
border: 0px;
}
.ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
background-color: #98e5f381;
}
.ant-tree-node-content-wrapper:hover,
.ant-tree-node-content-wrapper:focus {
//文字鼠标悬浮 背景色 蓝色
background-color: #3198F7 !important;
}
}
//查询按钮
.ant-input-group-addon {
background-color: transparent;
.ant-btn {
background-color: transparent !important;
}
.ant-btn-primary {
float: right;
width: 90px;
height: 45px;
margin: 5px 0px 0px 10px;
background-color: #3198f7;
border-color: #3198f7;
}
.ant-btn-primary:hover {
border-color: #3198f7;
}
.ant-btn-primary:focus {
border-color: #3198f7;
}
}

94
web/client/src/sections/projectRegime/containers/pointDeploy/upload-img-modal.js

@ -0,0 +1,94 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import Uploads from '$components/Uploads';
import { Input, Modal, Form, Button, message, Select } from 'antd';
import { getProjectGraph, createGraph, updateGraph } from '../../actions/graph';
const DisclosureModal = (props) => {
const { dispatch, onCancel, projectId, pictureInfo, getData } = props;
let files = pictureInfo ? [{ storageUrl: pictureInfo.graph }] : []
const [form] = Form.useForm();
const [editUrl, setEditUrl] = useState(files);
//初始化表单数据
const getinitialValues = () => {
if (pictureInfo) {
return { files: 1 };
}
return {}
};
useEffect(() => {
}, []);
const handleOk = () => {
form.validateFields().then(values => {
let data = {
projectId: projectId,
graph: editUrl[0]?.storageUrl,
}
if (pictureInfo) {//更新
dispatch(updateGraph(pictureInfo.id, data)).then(_ => {
getData()
});
} else {//新增
dispatch(createGraph(data)).then(_ => {
getData();
});
}
onCancel()
})
}
const vsjunct = (params) => {
if (params.length) {
let appendix = []
for (let p of params) {
appendix.push({
fName: p.name,
size: p.size,
fileSize: p.size,
storageUrl: p.storageUrl,//必须有storageUrl
})
}
setEditUrl(appendix)
} else {
setEditUrl([])
}
}
return (
<Modal title='添加布设图' visible={true} destroyOnClose
onCancel={onCancel} onOk={handleOk}>
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 17 }} initialValues={getinitialValues()}>
<Form.Item label="布设图" name='files'
rules={[{ required: true, message: '请上传布设图' }]}>
<Uploads
className='upload'
listType='card'
uploadType='project'
maxFilesNum={1}
maxFileSize={10}
isQiniu={true}
onChange={vsjunct}
fileTypes={["png", "jpeg", "jpg"]}
value={editUrl}
defaultValue={editUrl}
/>
</Form.Item>
<Form.Item style={{ paddingLeft: '17%' }}>
<div style={{ color: '#999', width: 460 }}>说明附件格式为pngjpegjpg大小不超过10MB</div>
</Form.Item>
</Form>
</Modal>
)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
}
}
export default connect(mapStateToProps)(DisclosureModal);

22
web/client/src/sections/projectRegime/routes.js

@ -1,5 +1,5 @@
'use strict';
import { Information, QrCode, Point } from './containers';
import { Information, QrCode, Point, PointDeploy } from './containers';
export default [{
type: 'inner',
@ -13,13 +13,23 @@ export default [{
key: 'information',
breadcrumb: '结构物基础信息管理',
component: Information,
childRoutes: [ {
path: '/:id',
key: ':id',
childRoutes: [{
path: '/:id',
key: ':id',
//component: null,
breadcrumb: '结构物',
childRoutes: [{
path: '/point',
key: 'point',
component: Point,
breadcrumb: '点位',
},
]
}, {
path: '/deploy',
key: 'deploy',
component: PointDeploy,
breadcrumb: '布设',
}]
}]
}, {
path: '/qrCode',
key: 'qrCode',

9
web/client/src/sections/shouye/actions/index.js

@ -0,0 +1,9 @@
'use strict';
export default {
}

9
web/client/src/sections/shouye/containers/index.js

@ -0,0 +1,9 @@
'use strict';
import Shouye from './shouye'
export { Shouye };

62
web/client/src/sections/shouye/containers/shouye.js

@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Form, Input, Select, Button, Table, Modal, Popconfirm, Tooltip } from 'antd';
import moment from "moment";
import '../style.less';
import { push } from 'react-router-redux';
import { Model } from 'echarts';
const Information = (props) => {
const { dispatch, actions, user, loading } = props
const topdata =[]
return (
<>
<div className='shouyetop'>
<div className='shouyetopitem'>
<div className='shouyetopitem-left' >
<div>今日巡检</div>
<div>0</div>
</div>
<div className='shouyetopitem-right'>
<div>完成巡检2</div>
<div>巡检上报2</div>
</div>
</div>
<div className='shouyetopitem'>
<div className='shouyetopitem-left' >
<div>今日巡检</div>
<div>0</div>
</div>
<div className='shouyetopitem-right'>
<div>完成巡检2</div>
<div>巡检上报2</div>
</div>
</div>
<div className='shouyetopitem'>
<div className='shouyetopitem-left' >
<div>今日巡检</div>
<div>0</div>
</div>
<div className='shouyetopitem-right'>
<div>完成巡检2</div>
<div>巡检上报2</div>
</div>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Information);

15
web/client/src/sections/shouye/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'shouye',
name: '首页',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

21
web/client/src/sections/shouye/nav-item.js

@ -0,0 +1,21 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
import { Func } from '$utils';
const SubMenu = Menu.SubMenu;
export function getNavItem (user, dispatch) {
// return <SubMenu key="shouye" icon={<SettingOutlined />} title={'首页'}>
// {/* {Func.isAuthorized('STRU_INFO_CONFIG') &&<Menu.Item key="information">
// <Link to="/projectRegime/information">结构物基础信息管理</Link>
// </Menu.Item>}
// {Func.isAuthorized('QR_CODE_CONFIG') &&<Menu.Item key="qrCode">
// <Link to="/projectRegime/qrCode">二维码管理</Link>
// </Menu.Item>} */}
// </SubMenu>
return <Menu.Item key="shouye">
<Link to="/shouye">首页</Link>
</Menu.Item>
}

5
web/client/src/sections/shouye/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

32
web/client/src/sections/shouye/routes.js

@ -0,0 +1,32 @@
'use strict';
import { Shouye } from './containers';
export default [{
type: 'inner',
route: {
path: '/shouye',
key: 'shouye',
breadcrumb: '首页',
component: Shouye,
// 不设置 component 则面包屑禁止跳转
// childRoutes: [{
// path: '/information',
// key: 'information',
// breadcrumb: '结构物基础信息管理',
// component: Information,
// childRoutes: [ {
// path: '/:id',
// key: ':id',
// component: Point,
// breadcrumb: '点位',
// },
// ]
// }, {
// path: '/qrCode',
// key: 'qrCode',
// component: QrCode,
// breadcrumb: '二维码管理',
// },
// ]
}
}];

20
web/client/src/sections/shouye/style.less

@ -0,0 +1,20 @@
.shouyetop{
display: flex;
justify-content: space-between;
.shouyetopitem{
width: 25%;
display: flex;
justify-content: space-between;
box-shadow: 0 0 10px #F0F2F5;
border:1px solid #F0F2F5;
color: rgba(0, 0, 0, 0.45);
font-size: 1.875rem;
height: 7.125rem;
.shouyetopitem-left{
width: 50%;
}
.shouyetopitem-right{
width: 50%;
}
}
}

8
web/client/src/utils/webapi.js

@ -64,6 +64,7 @@ export const ApiTable = {
delCheckTask: '/delcheckTask/:id',
addPatrolRecordIssueHandle: 'patrolRecord/issue/handle',
modifyPatrolRecordIssueHandle: 'patrolRecord/issue/handle/{id}',
yujingguanli:'/yujingguanli',
//协调申请
getCoordinateList: 'risk/coordinate',
@ -129,6 +130,13 @@ export const ApiTable = {
//项目状态配置
editProjectStatus: 'project/status',
//工地平面图
getProjectGraph: 'project/{projectId}/planarGraph',
createGraph: 'planarGraph/add',
updateGraph: 'planarGraph/{id}/modify',
deleteGraph: 'project/graph/{id}',
getDeployPoints: 'picture/{pictureId}/deploy/points',
setDeployPoints: 'set/picture/{pictureId}/deploy/points',
};
export const RouteTable = {

2
web/package.json

@ -95,6 +95,8 @@
"npm": "^7.20.6",
"qrcode": "^1.5.1",
"qs": "^6.10.1",
"react-dnd": "^7",
"react-dnd-html5-backend": "^7",
"react-color": "^2.19.3",
"react-router-breadcrumbs-hoc": "^4.0.1",
"react-sortable-hoc": "^2.0.0",

Loading…
Cancel
Save