Browse Source

feat:地灾和aiot总览代码

master
zhaobing’ 11 months ago
parent
commit
cf7d70b881
  1. 11
      api/.vscode/launch.json
  2. 178
      api/app/lib/controllers/AIOTOverview/AIOTOverview.js
  3. 32
      api/app/lib/controllers/groundDisasterInspection/groundDisasterInspection.js
  4. 141
      api/app/lib/controllers/projectBind/projectBind.js
  5. 4
      api/app/lib/index.js
  6. 43
      api/app/lib/models/project_bind.js
  7. 19
      api/app/lib/routes/AIOTOverview/AIOTOverview.js
  8. 8
      api/app/lib/routes/groundDisasterInspection/groundDisasterInspection.js
  9. 19
      api/app/lib/routes/projectBind/index.js
  10. 82
      api/config.js
  11. 2
      api/sequelize-automate.config.js
  12. 6
      weapp/app.json
  13. 163
      weapp/package/AIOTOverview/AIOTOverview.js
  14. 7
      weapp/package/AIOTOverview/AIOTOverview.json
  15. 62
      weapp/package/AIOTOverview/AIOTOverview.wxml
  16. 52
      weapp/package/AIOTOverview/AIOTOverview.wxss
  17. 147
      weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.js
  18. 14
      weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.json
  19. 46
      weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxml
  20. 64
      weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxss
  21. 117
      weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.js
  22. 11
      weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.json
  23. 44
      weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.wxml
  24. 70
      weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.wxss
  25. 530
      weapp/package/groundDisasterInspection/groundDisasterInspection.js
  26. 25
      weapp/package/groundDisasterInspection/groundDisasterInspection.json
  27. 95
      weapp/package/groundDisasterInspection/groundDisasterInspection.wxml
  28. 132
      weapp/package/groundDisasterInspection/groundDisasterInspection.wxss
  29. 10
      weapp/package/inspectionReport/inspectionReport.js
  30. 6
      weapp/package/polling/polling.js
  31. 5
      weapp/package/polling/polling.wxml
  32. 5
      weapp/pages/workbench/workbench.js
  33. 2
      weapp/project.config.json
  34. 34
      weapp/utils/getApiUrl.js
  35. 4
      web/client/src/app.js
  36. 2
      web/client/src/sections/patrolManage/components/addReportRulesModal.js
  37. 19
      web/client/src/sections/patrolManage/containers/patrolRecord.js
  38. 6
      web/client/src/sections/projectBinding/actions/index.js
  39. 59
      web/client/src/sections/projectBinding/actions/projectBinding.js
  40. 88
      web/client/src/sections/projectBinding/components/relationModal.js
  41. 5
      web/client/src/sections/projectBinding/containers/index.js
  42. 304
      web/client/src/sections/projectBinding/containers/projectBinding.js
  43. 0
      web/client/src/sections/projectBinding/containers/style.less
  44. 15
      web/client/src/sections/projectBinding/index.js
  45. 13
      web/client/src/sections/projectBinding/nav-item.js
  46. 5
      web/client/src/sections/projectBinding/reducers/index.js
  47. 13
      web/client/src/sections/projectBinding/routes.js
  48. 9
      web/client/src/utils/webapi.js

11
api/.vscode/launch.json

@ -33,6 +33,17 @@
"--wxDomain https://api.weixin.qq.com",
"--wxAppId wxdd82ae635b22ccdb",
"--wxAppSecret 08e3d4ea9484cd7837d171e7af7c7db8",
"--apiAnxinyunUrl https://openapi.anxinyun.cn/api/v1",
"--axyProject 1a271f12-52f2-4d16-8dad-ec0c92d3e0cc/03bzzdh/123456",
"--iotaProxy https://iotaproxy.anxinyun.cn", //
"--apiCrawUrl http://218.3.126.49:30555/v1", //
//clickHouse
"--clickHouseUrl http://218.3.126.49",
"--clickHousePort 18123",
"--clickHouseUser default",
"--clickHousePassword Wbo@hi1I",
"--clickHouseDataAlarm alarm",
"--clickHouseIot iota",
]
},
{

178
api/app/lib/controllers/AIOTOverview/AIOTOverview.js

@ -0,0 +1,178 @@
'use strict';
async function getThingsDeploy (ctx) {
let rslt = {instances:{}}, errStatus = null;
let error = { name: 'FindError', message: '获取设备部署信息失败' };
let structList=[]
try {
const models = ctx.fs.dc.models
const res=await models.ProjectBind.findAll()
if(res.length){
res.map(item=>{
structList.push({axyId:item.dataValues.axyProjectId,id:item.dataValues.id})
})
console.log('structList[id]',structList['gruop'])
for(let id in structList){
let iotaResponse = await ctx.app.fs.iotRequest.get(`things/${structList[id].axyId}/deploys`)
if (JSON.parse(iotaResponse)) {
for (const ids in JSON.parse(iotaResponse).instances) {
const instances = JSON.parse(iotaResponse).instances[ids];
if (instances.type === 's.d') {
instances.instance.structId = structList[id].axyId;
instances.instance.xjId= structList[id].id
rslt.instances = Object.assign(rslt.instances, { [ids]: instances })
}
}
error = null;
}
}
}else{
throw '没有绑定的安心云结构物的数据!!!'
}
} catch (err) {
errStatus = err.status
ctx.fs.logger.error(`path: ${ctx.path}, error: ${err}`)
}
if (error) {
if (errStatus == 404) {
ctx.status = 200;
ctx.body = {
'instances': null
};
} else {
ctx.status = errStatus;
ctx.body = error;
}
} else {
ctx.status = 200;
ctx.body = rslt
}
}
async function getThingStatus (ctx) {
let structList = []
let result=[]
try {
const models = ctx.fs.dc.models
const rslt=await models.ProjectBind.findAll()
if(rslt.length){
rslt.map(item=>{
structList.push(item.dataValues.axyProjectId)
})
for(let id in structList){
if(id!='group'){
let iotaResponse = await ctx.app.fs.craw.get('thing/status', { query: { thingId:structList[id] } })
result = [...result,...JSON.parse(iotaResponse)]
}
}
}else{
throw '没有绑定的安心云结构物的数据!!!'
}
ctx.status = 200
if (result) {
ctx.body = result
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 200
ctx.body = []
}
}
//查询物联网卡相关信息
async function getCardInfo (ctx) {
try{
let rlst = []
const { clickHouse } = ctx.app.fs
const { database: iota } = clickHouse.iot.opts.config
const {structIds}=ctx.request.body
if (structIds && structIds.length) {
const id = `(${structIds.map(item => `'${item.id}'`).join(',')})`
rlst = await clickHouse.dataAlarm.query(`
with tmp as ( SELECT cs.DeviceId as deviceId,
cs.CardNo as cardNo,
cs.PType as pType,
cs.Status as status,
cs.Total as total,
cs.Allowance as allowance,
cs.Time as time
FROM alarm.CardStatus cs
WHERE cs.DeviceId in ${id}
ORDER BY cs.Time DESC
LIMIT 1)
SELECT t.*,d.thingId,d.name FROM tmp t
LEFT JOIN ${iota}.Device d
ON Device.id = t.deviceId` ).toPromise()
ctx.status = 200
ctx.body = rlst
}else{
ctx.status = 200
ctx.body = rlst
}
}catch(error){
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
"message": "查询物联网卡相关信息失败"
}
}
}
async function createInvoke (ctx, next) {
let error = { name: 'CreateError', message: '命令下发失败' };
const {searchId,data} = ctx.request.body
let rslt=[]
try {
if(searchId&&searchId.length&&Array.isArray(searchId)){
for(let id in searchId){
if(id!='group'){
let dataToIota = {
deviceId:searchId[id].deviceId,
dimCapId:searchId[id].sid,
thingId:searchId[id].structId,
timeout: 300000
}
let iotaResponse = await ctx.app.fs.iotInvoke.post(`capabilities/invoke`, {data:dataToIota})
rslt.push({...searchId[id],...iotaResponse})
error = null
}
}
}else{
let iotaResponse = await ctx.app.fs.iotInvoke.post(`capabilities/invoke`, {data:searchId})
rslt = iotaResponse
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;
}
};
module.exports = {
getThingsDeploy,
getThingStatus,
getCardInfo,
createInvoke
}

32
api/app/lib/controllers/groundDisasterInspection/groundDisasterInspection.js

@ -0,0 +1,32 @@
'use strict'
async function findDetailBySNCard(ctx, next) {
try {
const { card } = ctx.params
const models = ctx.fs.dc.models
let option = {
attributes: ['id', 'name', 'equipment_no'],
where: { equipmentNo: {$like: `%${card}%`} },
include: [
{
required: true, //inner join
model: models.Project,
attributes: ['id', 'name'],
},
],
}
const rlst = await models.Point.findAll(option)
ctx.body = rlst
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
message: '根据设备sn号查询相关信息失败',
}
}
}
module.exports = {
findDetailBySNCard,
}

141
api/app/lib/controllers/projectBind/projectBind.js

@ -0,0 +1,141 @@
'use strict'
const moment = require('moment')
let axyTokenCache = {
token: null,
orgId: null,
expireTime: null, //过期时间
}
async function getAnxinyunToken(ctx) {
try {
if (!axyTokenCache.token || moment() > moment(axyTokenCache.expireTime)) {
if (ctx.app.fs.opts.axyProject.split('/').length === 3) {
const dataToAxy = {
p: ctx.app.fs.opts.axyProject.split('/')[0],
username: ctx.app.fs.opts.axyProject.split('/')[1],
password: ctx.app.fs.opts.axyProject.split('/')[2],
}
const axyResponse = await ctx.app.fs.anxinyun.post('project/login', { data: dataToAxy })
if (axyResponse.authorized) {
axyTokenCache.token = axyResponse.token //放进缓存
axyTokenCache.orgId = axyResponse.orgId //放进缓存
axyTokenCache.expireTime = moment().add(1, 'hour').format('YYYY-MM-DD HH:mm:ss')
}
}
}
return axyTokenCache
} catch (error) {
ctx.fs.logger.error(`sechedule: laborAttendance, error: ${error}`)
}
}
//调用安心云结构物接口
async function findAnxinyunProject(ctx, next) {
try {
let { type, url, params = {} } = ctx.request.body
let data = await getAnxinyunToken(ctx)
if (url && url.indexOf('{orgId}') != -1) {
url = url.replace(`{orgId}`, data.orgId)
}
const res = await ctx.app.fs.anxinyun[type](`${url}?token=${data.token}`, {
data: params.data || {},
query: params.query || {},
})
ctx.status = 200
ctx.body = res
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
message: '查询安心云项目失败',
}
}
}
async function addorEditRelation(ctx, next) {
let err=''
const { axyProjectId, structrueId, id } = ctx.request.body
try {
const models = ctx.fs.dc.models
//编辑
if (id) {
const res=await models.ProjectBind.findOne({ where: { id,axyProjectId,structureId:structrueId } })
if(res){
err='所选安心云结构物和巡检结构物重复!!!'
throw '所选安心云结构物和巡检结构物重复!!!'
}
await models.ProjectBind.update({ axyProjectId, structrueId }, { where: { id } })
ctx.status = 204
} else {
//新增
const res= await models.ProjectBind.findOne({ where: { axyProjectId,structureId:structrueId } })
if(res){
err='所选安心云结构物和巡检结构物重复!!!'
throw '所选安心云结构物和巡检结构物重复!!!'
}
await models.ProjectBind.create({ axyProjectId, structureId:structrueId })
ctx.status = 204
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
message: err?err:id?'编辑失败':'新增失败',
}
}
}
async function getRelationList(ctx, next) {
try {
const models = ctx.fs.dc.models
const { limit, page, startTime, endTime } = ctx.query
let options = {
where: {},
}
if (limit) {
options.limit = Number(limit)
}
if (page && limit) {
options.offset = Number(page) * Number(limit)
}
if (startTime && endTime) {
options.where.inspectTm = { $between: [startTime, endTime] }
}
const res = await models.ProjectBind.findAndCountAll(options)
ctx.body = res
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
message: '查询关联关系列表失败',
}
}
}
async function deleteRelation(ctx, next) {
const {id} = ctx.params
try {
const models = ctx.fs.dc.models
const res = await models.ProjectBind.findOne({ where: { id:Number(id) } })
if (!res) {
throw 'id不存在'
}
await models.ProjectBind.destroy({ where: { id:Number(id) } })
ctx.status = 200
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`)
ctx.status = 400
ctx.body = {
message: '关联关系列表失败',
}
}
}
module.exports = {
findAnxinyunProject,
addorEditRelation,
getRelationList,
deleteRelation,
}

4
api/app/lib/index.js

@ -8,7 +8,7 @@ const routes = require('./routes');
const socketConect = require('./service/socket')
const paasRequest = require('./service/paasRequest');
const authenticator = require('./middlewares/authenticator');
//const clickHouseClient = require('./service/clickHouseClient')
const clickHouseClient = require('./service/clickHouseClient')
const schedule = require('./schedule')
// const apiLog = require('./middlewares/api-log');
@ -29,7 +29,7 @@ module.exports.entry = function (app, router, opts) {
paasRequest(app, opts)
// clickHouse 数据库 client
//clickHouseClient(app, opts)
clickHouseClient(app, opts)
// 工具类函数
utils(app, opts)

43
api/app/lib/models/project_bind.js

@ -0,0 +1,43 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ProjectBind = sequelize.define("project_bind", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: null,
comment: null,
primaryKey: true,
field: "id",
autoIncrement: true,
},
axyProjectId: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: '安心云项目id',
primaryKey: false,
field: "axy_project_id",
autoIncrement: false
},
structureId: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
comment: '结构物id',
primaryKey: false,
field: "structure_id",
autoIncrement: false
},
}, {
tableName: "project_bind",
comment: "",
indexes: []
});
dc.models.ProjectBind = ProjectBind;
return ProjectBind;
};

19
api/app/lib/routes/AIOTOverview/AIOTOverview.js

@ -0,0 +1,19 @@
'use strict';
const AIOTOverview = require('../../controllers/AIOTOverview/AIOTOverview');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/things/deploy'] = { content: '获取智能断路器', visible: true };
router.get('/things/deploy', AIOTOverview.getThingsDeploy);
app.fs.api.logAttr['GET/things/status'] = { content: '获取设备在离线', visible: true };
router.get('/things/status', AIOTOverview.getThingStatus);
app.fs.api.logAttr['POST/things/card'] = { content: '获取物联网卡相关信息', visible: true };
router.post('/things/card', AIOTOverview.getCardInfo);
//空开设备查询状态和开关设备
app.fs.api.logAttr['POST/capabilities/invoke'] = { content: '及时采集', visible: true };
router.post('/capabilities/invoke', AIOTOverview.createInvoke);
};

8
api/app/lib/routes/groundDisasterInspection/groundDisasterInspection.js

@ -0,0 +1,8 @@
'use strict'
const groundDisasterInspection = require('../../controllers/groundDisasterInspection/groundDisasterInspection')
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/card/detail/:card'] = { content: '根据sn号查询相应的点位信息和结构物信息', visible: true }
router.get('card/detail/:card', groundDisasterInspection.findDetailBySNCard)
}

19
api/app/lib/routes/projectBind/index.js

@ -0,0 +1,19 @@
'use strict';
const projectBind = require('../../controllers/projectBind/projectBind');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/anxinyun/project/list'] = { content: '获取安心云项目列表', visible: false }
router.post('/anxinyun/project/list', projectBind.findAnxinyunProject)
app.fs.api.logAttr['POST/anxinyun/project/relation'] = { content: '新增或编辑项目映射关系', visible: false }
router.post('/anxinyun/project/relation', projectBind.addorEditRelation)
app.fs.api.logAttr['GET/anxinyun/project/relation/list'] = { content: '获取项目映射关系列表', visible: false }
router.get('/anxinyun/project/relation/list', projectBind.getRelationList)
app.fs.api.logAttr['DELETE/anxinyun/project/relation/:id'] = { content: '删除项目映射关系', visible: false }
router.delete('/anxinyun/project/relation/:id', projectBind.deleteRelation)
}

82
api/config.js

@ -22,10 +22,37 @@ args.option('wxDomain', '微信API Domain');
args.option('wxAppId', '微信小程序appid');
args.option('wxAppSecret', '微信小程序AppSecret');
//以太代理
args.option('iotaProxy', '以太代理')
//设备升级
args.option('apiCrawUrl', '设备升级')
args.option('apiAnxinyunUrl', "安心云api");
args.option('axyProject', '安心云泵站项目信息');
// clickHouse
args.option('clickHouseUrl', 'clickHouse Url');
args.option('clickHousePort', 'clickHouse Port');
args.option('clickHouseUser', 'clickHouse user');
args.option('clickHousePassword', 'clickHouse password');
// args.option('clickHouseAnxincloud', 'clickHouse 安心云数据库名称');
// args.option('clickHousePepEmis', 'clickHouse 项企数据库名称');
// args.option('clickHouseProjectManage', 'clickHouse 项目管理数据库名称');
// args.option('clickHouseVcmp', 'clickHouse 视频平台数据库名称');
args.option('clickHouseDataAlarm', 'clickHouse 视频平台数据告警库名称');
// args.option('clickHouseDataAlarmLocal', 'clickHouse 本地化告警相关数据');
args.option('clickHouseIot', 'clickHouse IOT平台设备信息库名称');
// args.option('clickHouseCamworkflow', 'clickHouse 工作流数据库名称');
const flags = args.parse(process.argv);
const XUNJIAN_DB = process.env.XUNJIAN_DB || flags.pg;
// 以太代理
const IOT_PROXY = process.env.IOT_PROXY || flags.iotaProxy;
// 七牛云存储参数
const QINIU_DOMAIN_QNDMN_RESOURCE = process.env.ANXINCLOUD_QINIU_DOMAIN_QNDMN_RESOURCE || flags.qndmn;
const QINIU_BUCKET_RESOURCE = process.env.ANXINCLOUD_QINIU_BUCKET_RESOURCE || flags.qnbkt;
@ -38,8 +65,23 @@ const WX_APP_ID = process.env.WX_APP_ID || flags.wxAppId;
const WX_APP_SECRET = process.env.WX_APP_SECRET || flags.wxAppSecret;
//报表
const API_REPOR_GENERATE_URL = process.env.API_REPOR_GENERATE_URL || 'http://10.8.30.95:31825'
//安心云接口
const API_ANXINYUN_URL = process.env.API_ANXINYUN_URL || flags.apiAnxinyunUrl
const AXY_BZ_PROJECT = process.env.AXY_BZ_PROJECT || flags.axyProject
//设备升级
const API_CRAW_URL = process.env.API_CRAW_URL || flags.apiCrawUrl;
//clickHouse
const CLICKHOUST_URL = process.env.CLICKHOUST_URL || flags.clickHouseUrl
const CLICKHOUST_PORT = process.env.CLICKHOUST_PORT || flags.clickHousePort
const CLICKHOUST_USER = process.env.CLICKHOUST_USER || flags.clickHouseUser
const CLICKHOUST_PASSWORD = process.env.CLICKHOUST_PASSWORD || flags.clickHousePassword
const CLICKHOUST_DATA_ALARM = process.env.CLICKHOUST_DATA_ALARM || flags.clickHouseDataAlarm
const CLICKHOUST_IOT = process.env.CLICKHOUST_IOT || flags.clickHouseIot
if (!XUNJIAN_DB || !QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK || !WX_DOMAIN || !WX_APP_ID || !WX_APP_SECRET) {
if (!XUNJIAN_DB || !QINIU_DOMAIN_QNDMN_RESOURCE || !QINIU_BUCKET_RESOURCE || !QINIU_AK || !QINIU_SK || !WX_DOMAIN || !WX_APP_ID || !WX_APP_SECRET || !API_ANXINYUN_URL|| !AXY_BZ_PROJECT
||!IOT_PROXY||!API_CRAW_URL||!CLICKHOUST_URL||!CLICKHOUST_PORT||!CLICKHOUST_USER||!CLICKHOUST_PASSWORD||!CLICKHOUST_IOT
) {
console.log('缺少启动参数,异常退出');
args.showHelp();
process.exit(-1);
@ -79,6 +121,7 @@ const product = {
exclude: [
// "*"
], // 不做认证的路由,也可以使用 exclude: ["*"] 跳过所有路由
axyProject: AXY_BZ_PROJECT,
qiniu: {
domain: QINIU_DOMAIN_QNDMN_RESOURCE,
bucket: QINIU_BUCKET_RESOURCE,
@ -90,12 +133,45 @@ const product = {
accessKey: 'LTAI5tAFdjz7j38aNF2C9Qe8',
accessSecret: '1trYkmiqfBtvZL6BxkNH2uQcQQPs0S'
},
pssaRequest:[ { name: 'reportGenerate',
pssaRequest:[
{ name: 'reportGenerate',
root: API_REPOR_GENERATE_URL,
dataWord: 'text'
}, {
name: 'iotRequest',
root: IOT_PROXY + '/_iota_api',
dataWord: 'text'
}, {
name: 'iotInvoke',
root: IOT_PROXY + '/_iota_api',
},
}
{
name: 'anxinyun',
root: API_ANXINYUN_URL
},
{
name: 'craw',
root: API_CRAW_URL,
dataWord: 'text'
},
],
clickHouse: {
url: CLICKHOUST_URL,
port: CLICKHOUST_PORT,
user: CLICKHOUST_USER,
password: CLICKHOUST_PASSWORD,
db: [
{
name: 'dataAlarm',
db: CLICKHOUST_DATA_ALARM
},
{
name: 'iot',
db: CLICKHOUST_IOT
},
]
},
email: {
enabled: true,

2
api/sequelize-automate.config.js

@ -26,7 +26,7 @@ module.exports = {
dir: './app/lib/models', // 指定输出 models 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir`
tables: ['user', 'project'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
tables: ['project_bind'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: ['t_',], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面

6
weapp/app.json

@ -29,7 +29,11 @@
"deviceBigdataGraph/statusDetail/statusDetail",
"report/report",
"deviceBigdataGraph/lifeWarning/lifeWarning",
"expertSystem/expertSystem"
"expertSystem/expertSystem",
"groundDisasterInspection/groundDisasterInspection",
"AIOTOverview/AIOTOverview",
"AIOTOverview/flowMonitoring/flowMonitoring",
"AIOTOverview/electricityMonitoring/electricityMonitoring"
]
}
],

163
weapp/package/AIOTOverview/AIOTOverview.js

@ -0,0 +1,163 @@
// package/AIOTOverview/AIOTOverview.js
import { getThingsDeploy, getThingsStatus, getCardInfo,getRelationList} from "../../utils/getApiUrl";
import { Request } from "../../common";
const moment = require("../../utils/moment");
Page({
/**
* 页面的初始数据
*/
data: {
data: [],//用电设备数据
normal: 0,//正常
abnormal: 0,//异常
unknown: 0,//未知
iotCardNormal: 0,//物联网卡正常
iotCardNonactivated: 0,//物联网卡欠费
iotCardHalt: 0,//物联网卡停机
iotaCardData: [],//物联网卡数据
searchId:[],
},
//跳转流量监控明细
navigatorToFlow() {
const jsonData = JSON.stringify(this.data.iotaCardData)
wx.navigateTo({
url: `/package/AIOTOverview/flowMonitoring/flowMonitoring?data=` + encodeURIComponent(jsonData)
})
},
//跳转用电监控明细
navigatorToEle() {
const jsonData2 = JSON.stringify(this.data.searchId)
wx.navigateTo({
url: `/package/AIOTOverview/electricityMonitoring/electricityMonitoring?data2=${encodeURIComponent(jsonData2)}`
})
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
wx.showLoading({ title: '加载中...' });
const that = this
const devices = await Request.get(getThingsDeploy())
const status = await Request.get(getThingsStatus())
const rslt = await Request.get(getRelationList())
let deviceId = [] //空开电源设备的id
let deviceAll = []
let searchId=[]//查询e9c设备状态的id
let controlId=[]
if (devices) {
const dataList = devices
for (const id in dataList.instances) {
const instances = dataList?.instances[id]
if (instances.type == 's.d' && instances?.instance?.properties) {
deviceAll.push({ id, name: instances.instance?.properties?.name, structId: instances.instance?.structId })
if (instances.type == 's.d' && instances.instance.properties.deviceType == 'sensor') {
if (instances.instance.properties.model == 'E9C') {
for (const key in instances?.instance?.interfaces) {
const d=Object.values(instances?.instance?.interfaces[key]?.capabilities)
if(d&&d.length){
searchId.push({deviceId:id,cid:d[1]?.dimension?.id,sid:d[0]?.dimension?.id,name: instances.instance?.properties?.name, structId: instances.instance?.structId })
}
}
}
}
if (instances?.instance?.properties?.model == 'JG-RTU-S270') {
deviceId.push({ id, name: instances.instance?.properties?.name, structId: instances.instance?.structId,xjId:instances.instance?.xjId })
}
}
}
}
if (status && status.length) {
const data = deviceId.map(item => {
return {
deviceId: item.id,
name: item.name,
status: status.find(q => q.deviceId == item.id)?.status,
structId: item?.structId,
xjId:item.xjId
}
})
deviceAll.push({ id: '012ccc98-c99d-445e-8397-6da1b4567533', name: '设备二', structId: 'df48d7c5-d902-47b1-b671-d88d546ba3e4' })
if (deviceAll && deviceAll.length) {
Request.post(getCardInfo(), { structIds: deviceAll }).then(res => {
if (res) {
if(rslt.rows.length){
controlId=res.map(item=>{
return {
...item,
xjId:rslt.rows.find(q=>q.axyProjectId==item.thingId)?.id
}
})
}
that.setData({
iotaCardData: controlId,
iotCardNormal: res?.filter(item => item.status == 0)?.length,
iotCardNonactivated: res?.filter(item => item.status == 1)?.length,
iotCardHalt: res?.filter(item => item.status == 2)?.length,
searchId:searchId
})
}
})
}
that.setData({
data,
normal: data?.filter(item => item.status == 1)?.length,
unknown: data?.filter(item => item.status == -1)?.length,
abnormal: data?.filter(item => item.status == 0)?.length
})
}
wx.hideLoading()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

7
weapp/package/AIOTOverview/AIOTOverview.json

@ -0,0 +1,7 @@
{
"navigationBarTitleText": "AIOT总览",
"usingComponents": {
"van-button": "@vant/weapp/button/index"
}
}

62
weapp/package/AIOTOverview/AIOTOverview.wxml

@ -0,0 +1,62 @@
<!--package/AIOTOverview/AIOTOverview.wxml-->
<view>
<!--用电设备状态总览-->
<view class="card">
<view class="top">
<view style="display: flex; align-items: center;">
<text class="fontStyle">用电设备状态总览</text>
</view>
<view class="detailStyle">
<van-button type="info" round size="small" bindtap="navigatorToEle">查看详情</van-button>
</view>
</view>
<view class="card-content">
<view class="row flex flex-between detail">
<view class="content-left" style="font-weight: bold;">设备数量</view>
<view class="content-right" style="font-weight: bold;">{{data.length}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">正常设备</view>
<view class="content-right">{{normal+'个'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">异常设备</view>
<view class="content-right" style="color:red">{{abnormal+'个'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">未知</view>
<view class="content-right">{{unknown+'个'}}</view>
</view>
</view>
</view>
<!--物联网卡状态总览-->
<view class="card">
<view class="top">
<view style="display: flex; align-items: center;">
<text class="fontStyle">物联网卡状态总览</text>
</view>
<view class="detailStyle">
<van-button type="info" round size="small" bindtap="navigatorToFlow">查看详情</van-button>
</view>
</view>
<view class="card-content">
<view class="row flex flex-between detail">
<view class="content-left" style="font-weight: bold;">设备数量</view>
<view class="content-right" style="font-weight: bold;">{{iotaCardData.length+'个'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">正常</view>
<view class="content-right">{{iotCardNormal+'个'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">未激活</view>
<view class="content-right">{{iotCardNonactivated+'个'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">停机</view>
<view class="content-right" style="color:red">{{iotCardHalt+'个'}}</view>
</view>
</view>
</view>
</view>

52
weapp/package/AIOTOverview/AIOTOverview.wxss

@ -0,0 +1,52 @@
/* package/AIOTOverview/AIOTOverview.wxss */
.card {
position: relative;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
/* padding: 10px; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
margin: 12px 12px;
}
.top {
display: flex;
justify-content: space-between;
padding: 10px;
/* background-position: bottom; */
}
.card-content {
padding: 0 10px;
}
.card-left {
margin-left: 23px;
margin-bottom: 10px;
font-weight: 500;
font-size: 16px;
color: #000000d9;
}
.detail{
margin:10px 0
}
.card-right {
margin-right: 18px;
margin-bottom: 10px;
color: #1684FF;
}
.fontStyle {
font-family: PingFangSC-Medium;
font-weight: bold;
}
.content-left {
font-size: 12px;
}
.content-right {
font-size: 14px;
text-align: right;
}

147
weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.js

@ -0,0 +1,147 @@
// package/AIOTOverview/electricityMonitoring/electricityMonitoring.js
import { getProjectList,getRelationList,createInvoke} from "../../../utils/getApiUrl"
import {Request} from "../../../common"
Page({
/**
* 页面的初始数据
*/
data: {
structList:[ ],
curStruId: 'all', // 选中结构物id
list:[],//断路器设备list
listCopy:[],
},
onClose(e) {
// 获取报告
// this.getPatrolReport();
},
switch1Change(e){
wx.showLoading({ title: '加载中...' });
const item=e.currentTarget.dataset.item
let searchId={
deviceId:item.deviceId,
dimCapId:item.cid,
thingId:item.structId,
timeout: 300000,
param:e.detail.value?'开':'关'
}
Request.post(createInvoke(),{data:searchId}).then(res=>{
if(res){
wx.hideLoading()
}else{
wx.hideLoading()
}
})
},
onStruChange(e) {
if (e.detail) {
let data = []
if (e.detail === 'all') {
data = this.data.listCopy
} else {
data = this.data.listCopy.filter(item => item.xjId === e.detail)
}
this.setData({
curStruId: e.detail,
list: data
})
}
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
wx.showLoading({ title: '加载中...' });
const searchId= JSON.parse(decodeURIComponent(options.data2))
let data={
searchId
}
let listt=[]
//智能断路器的设备的状态
const res1 =await Request.post(createInvoke(),data)
//获取结构物列表(巡检)
// console.log('getProjectList',getProjectList)
const res=await Request.get(getProjectList())
const rslt=await Request.get(getRelationList())
if(res.rows.length&&rslt.rows.length){
const data=res.rows.map(item=>{
return {
value: rslt.rows.find(q => item.id === q.structureId)?.structureId,
text:item.name
}
})
if(res1&&res1.length){
listt=res1.map(item=>{
const c=rslt.rows?.find(q => q.axyProjectId == item.structId)?.structureId
return {
...item,
structName: res.rows.find(p=>p.id==c)?.name,
xjId: rslt.rows?.find(q => q.axyProjectId == item.structId)?.structureId
}
})
}
data.unshift({text: '全部', value: 'all' })
this.setData({
structList:data,
list:listt,
listCopy:listt
})
}
wx.hideLoading()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

14
weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.json

@ -0,0 +1,14 @@
{
"navigationBarTitleText": "用电监控",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-dropdown-menu": "@vant/weapp/dropdown-menu/index",
"van-dropdown-item": "@vant/weapp/dropdown-item/index",
"van-empty": "@vant/weapp/empty/index",
"van-switch": "@vant/weapp/switch/index"
}
}

46
weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxml

@ -0,0 +1,46 @@
<!--package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxml-->
<view>
<!--结构物选择器-->
<view class="select">
<van-dropdown-menu active-color="#1989fa">
<van-dropdown-item title="{{ '结构物' }}" bind:close="onClose" bind:change="onStruChange" value="{{ curStruId }}" options="{{ structList }}" />
</van-dropdown-menu>
</view>
<!--渲染列表-->
<view wx:if="{{list.length}}">
<view class="card" wx:for="{{list}}" wx:key='index'>
<!--头部-->
<view class="top">
<view style="display: flex; align-items: center;">
<text class="fontStyle">{{item.structName}}</text>
</view>
<view>
<switch disabled="{{!item.data.data||item.data.data.info&&item.data.data.info.includes('离线')}}" bindchange="switch1Change" data-item="{{item}}"/>
</view>
</view>
<!--内容部分-->
<view class="card-content">
<view class="row flex flex-between detail">
<view class="content-left" style="font-weight: bold;">{{item.name}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">运行状态</view>
<view class="content-right" wx:if="{{!item.data.data}}">{{'离线'}}</view>
<view class="content-right" wx:if="{{item.data.data.info&&item.data.data.info.includes('离线')}}">{{'离线'}}</view>
<view class="content-right" wx:if="{{item.data.data.info&&item.data.data.info.includes('正常')}}">{{'正常'}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">电源状态</view>
<view class="content-right" wx:if="{{!item.data.data}}">{{'离线'}}</view>
<view class="content-right" wx:if="{{item.data.data.info&&item.data.data.info.includes('离线')}}">{{'离线'}}</view>
<view class="content-right" wx:if="{{item.data.data.info&&item.data.data.info.includes('正常')}}">{{'正常'}}</view>
</view>
</view>
</view>
</view>
<view wx:else>
<van-empty description="暂无数据" />
</view>
</view>

64
weapp/package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxss

@ -0,0 +1,64 @@
/* package/AIOTOverview/electricityMonitoring/electricityMonitoring.wxss */
.select {
width: 50%;
}
.select .van-dropdown-menu {
box-shadow: none
}
/* package/AIOTOverview/AIOTOverview.wxss */
.card {
position: relative;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
/* padding: 10px; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
margin: 12px 12px;
}
.top {
display: flex;
justify-content: space-between;
padding: 10px;
/* background-position: bottom; */
}
.card-content {
padding: 0 10px;
}
.card-left {
margin-left: 23px;
margin-bottom: 10px;
font-weight: 500;
font-size: 16px;
color: #000000d9;
}
.detail {
margin: 10px 0
}
.card-right {
margin-right: 18px;
margin-bottom: 10px;
color: #1684FF;
}
.fontStyle {
font-family: PingFangSC-Medium;
font-weight: bold;
}
.content-left {
font-size: 12px;
}
.content-right {
font-size: 14px;
text-align: right;
}

117
weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.js

@ -0,0 +1,117 @@
// package/AIOTOverview/flowMonitoring/flowMonitoring.js
import { getProjectList, getRelationList } from "../../../utils/getApiUrl";
import { Request } from "../../../common";
const moment = require("../../../utils/moment");
Page({
/**
* 页面的初始数据
*/
data: {
structList: [],
curStruId: 'all', // 选中结构物id
cardData: [],
cardDataCopy: []
},
onClose(e) {
// 获取报告
// this.getPatrolReport();
},
onStruChange(e) {
if (e.detail) {
let data = []
if (e.detail === 'all') {
data = this.data.cardDataCopy
} else {
data = this.data.cardDataCopy.filter(item => item.xjId === e.detail)
}
this.setData({
curStruId: e.detail,
cardData: data
})
}
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
let dd = []
const complexArray = JSON.parse(decodeURIComponent(options.data))
//获取结构物列表(巡检)
const res = await Request.get(getProjectList())
const rslt = await Request.get(getRelationList())
if (res.rows.length && rslt.rows.length) {
const data = res.rows.map(item => {
return {
value: rslt.rows.find(q => item.id === q.structureId)?.id,
text: item.name
}
})
data.unshift({ text: '全部', value: 'all' })
if (complexArray && complexArray.length) {
dd = complexArray.map(item => {
return {
...item,
structName: data?.find(q => q.value == item.xjId)?.text
}
})
}
this.setData({
structList: data,
cardData:dd ,
cardDataCopy:dd
})
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

11
weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.json

@ -0,0 +1,11 @@
{
"navigationBarTitleText": "流量监控",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-dropdown-menu": "@vant/weapp/dropdown-menu/index",
"van-dropdown-item": "@vant/weapp/dropdown-item/index",
"van-empty": "@vant/weapp/empty/index"
}
}

44
weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.wxml

@ -0,0 +1,44 @@
<!--package/AIOTOverview/flowMonitoring/flowMonitoring.wxml-->
<view>
<!--结构物选择器-->
<view class="select">
<van-dropdown-menu active-color="#1989fa">
<van-dropdown-item title="{{ '结构物' }}" bind:close="onClose" bind:change="onStruChange" value="{{ curStruId }}" options="{{ structList }}" />
</van-dropdown-menu>
</view>
<!--渲染列表-->
<view wx:if="{{cardData.length}}">
<view class="card" wx:for="{{cardData}}" wx:key='index'>
<view>
<!--头部-->
<view class="top">
<view style="display: flex; align-items: center;">
<text class="fontStyle">{{item.structName}}</text>
</view>
</view>
<!--内容部分-->
<view class="card-content">
<view class="row flex flex-between detail">
<view class="content-left" style="font-weight: bold;">{{item.name}}</view>
</view>
<view class="row flex flex-between detail">
<view class="content-left">物联卡号:{{item.cardNo}}</view>
<view class="content-right">套餐类型:{{item.pType}}</view>
</view>
<view class="row flex flex-between detail content">
<view wx:if="{{item.status==0}}" class="content-left">卡状态:正常</view>
<view wx:if="{{item.status==1}}" class="content-left">卡状态:未激活</view>
<view wx:if="{{item.status==2}}" class="content-left">卡状态:停机</view>
<view class="content-right">套餐总量:{{item.total}}</view>
</view>
<view class="row flex flex-between detail content">
<view class="content-left">卡余额:{{item.allowance}}</view>
</view>
</view>
</view>
</view>
</view>
<view wx:else>
<van-empty description="暂无数据" />
</view>
</view>

70
weapp/package/AIOTOverview/flowMonitoring/flowMonitoring.wxss

@ -0,0 +1,70 @@
/* package/AIOTOverview/flowMonitoring/flowMonitoring.wxss */
.select {
width: 50%;
}
.select .van-dropdown-menu {
box-shadow: none
}
/* package/AIOTOverview/AIOTOverview.wxss */
.card {
position: relative;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
/* padding: 10px; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
margin: 12px 12px;
}
.top {
display: flex;
justify-content: space-between;
padding: 10px;
/* background-position: bottom; */
}
.card-content {
padding: 0 10px;
}
.card-left {
margin-left: 23px;
margin-bottom: 10px;
font-weight: 500;
font-size: 16px;
color: #000000d9;
}
.detail {
margin: 10px 0
}
.card-right {
margin-right: 18px;
margin-bottom: 10px;
color: #1684FF;
}
.fontStyle {
font-family: PingFangSC-Medium;
font-weight: bold;
}
.content-left {
font-size: 12px;
width: 60%;
}
.content-right {
font-size: 12px;
text-align: left;
width: 40%;
}

530
weapp/package/groundDisasterInspection/groundDisasterInspection.js

@ -0,0 +1,530 @@
// package/report/report.js
import { getPointList,getPatrolTemplates,getPatrolTemplate,getTemplates,reportQuest,getPatrolPlan,getStructuresList,getDetail } from "../../utils/getApiUrl";
import {Request} from "../../common";
const moment = require("../../utils/moment");
Page({
data: {
sms:'',//设备号
sList:[],//根据sn号查询的结构物列表
structListIndex: undefined,//结构物id
struct:'',//结构物
pointList:[],//点位列表
pointIndex:undefined,//点位索引
list:[],//初始数据
pointVis:false,//点位输入框的可见性
formVis:false,//表单项可见性
handleObj:'',//处理对象
handleGoal:'',//处理目的
installLocation:'',//安装位置
rtuNo:'',//RTU编号
isPlanState: false,
structList: [],//结构物列表
data:[],//巡检计划的数据(包括点位,设备等等)
patrolTemplate:[],//巡检模板
templateData:[],//巡检模板总数居
patrolTemplateIndex:undefined,//巡检模板索引
itemData: '', // 点位
address: '', // 当前位置
imgUrl: getApp().globalData.imgUrl,
checkItems: [], // 检查项
inspectContentArr: [], // 巡检内容
isCommitting: false,
},
//卡号接收
onInputChange(e){
this.setData({
sms:e.detail,
sList:[]
})
},
//根据sn号搜索
searchDetail(){
const id=this.data.sms
if(id){
Request.get(getDetail(id)).then(res => {
if(res){
//结构物
if(!res.length){
wx.showToast({
title: '未查询到相关数据',
icon: 'none',
duration: 1500
})
return;
}
const uniqueResults = res.reduce((acc, item) => {
const existingItem = acc.find(project => project.id === item.project.id);
if (!existingItem) {
acc.push({
id: item.project.id,
name: item.project.name
});
}
return acc
}, [])
this.setData({
sList:uniqueResults,
list:res
})
}
})
}
},
//点位改变函数
pointChange(e){
const that = this
// that.getPatrolTemplate()
Request.get(getPatrolTemplates()).then(res=>{
if(res){
const rlst=res.rows?.map(item=>{return {
id:item.id,
name:item.name
}})
that.setData({patrolTemplate:rlst,templateData:res.rows,
pointIndex:e.detail.value,
formVis:true,
inspectContentArr:[],
patrolTemplateIndex:null
})
}
})
},
//整理设备和检查项
getPatrolTemplate(templateId,pointDevices=[]) {
const that=this
Request.get(getPatrolTemplate(templateId)).then(res => {
const checkItems = res.rows[0].checkItems;
let inspectContentArr = [];
// 有绑定设备的点位,每个设备都要检查各个检查项
if (pointDevices.length) {
pointDevices.forEach(device => {
inspectContentArr.push({
deviceName: device.name,
deviceId: device.id,
checkItems: checkItems.map(c => ({
id: `${device.id}-${c.id}`,
name: c.name,
isNormal: true,
msgInp: null,
level: null,
imgs: [],
}))
})
});
} else {
inspectContentArr.push({
checkItems: checkItems.map(c => ({
id: c.id,
name: c.name,
isNormal: true,
msgInp: null,
level: null,
imgs: [],
}))
})
}
this.setData({
checkItems,
inspectContentArr: inspectContentArr,
})
})
},
//收集输入框的值
input1Change(e){
const that = this
that.setData({handleObj:e.detail})
},
input2Change(e){
const that = this
that.setData({handleGoal:e.detail})
},
input3Change(e){
const that = this
that.setData({installLocation:e.detail})
},
input4Change(e){
const that = this
that.setData({rtuNo:e.detail})
},
// 预览图片
previewImg: function (e) {
const { deviceidx, itemidx, index } = e.currentTarget.dataset;
// 所有图片
const imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
const newImgs = imgs.map(i => this.data.imgUrl + i);
wx.previewImage({
// 当前显示图片
current: newImgs[index],
// 所有图片
urls: newImgs
})
},
//结构物改变函数
structChange(event) {
const that = this
const projectId=that.data.sList.find(item=>item.id==that.data?.sList[Number(event.detail.value)].id)?.id
const query={projectId}
Request.get(getPointList(query)).then(res => {
if(res){
that.setData({data:res})
}
})
that.setData({
pointVis:false,
pointList:[],//选择结构物后先置空先前的点位列表
formVis:false,
pointIndex:null,
inspectContentArr:[]
})
const rlst=that.data.list?.filter(item=>item.project.id==projectId)?.map(item=>{
return {
id: item.id,
name: item.name
}
})
that.setData({
structListIndex:event.detail.value,
pointList:rlst,
pointVis:true
})
},
//选择异常或者正常
handleChangeTwo(e) {
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].isNormal = e.detail;
if (e.detail) { // 清除异常数据
nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = null;
nextInspectContentArr[deviceidx].checkItems[itemidx].level = null;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = [];
}
this.setData({ inspectContentArr: nextInspectContentArr })
},
//返回前一页
bindCancel() {
wx.navigateBack();
},
// 开始巡检录入
addPatrolRecord: function () {
const that = this;
if (that.data.isCommitting) { return }
let {
rtuNo,
installLocation,
handleGoal,
handleObj,
patrolTemplate,
patrolTemplateIndex,
structListIndex,
pointIndex,
inspectContentArr,
pointList,
sList
} = that.data;
let alarm = false;
if (!patrolTemplateIndex) {
wx.showToast({
title: '请选择模板',
icon: 'none',
duration: 1500
})
return;
}
if (!pointIndex) {
wx.showToast({
title: '请选择点位',
icon: 'none',
duration: 1500
})
return;
}
let reportArr = inspectContentArr.map(d => ({ ...d, alarm:d.checkItems.some(q=>q.isNormal==false)?true:false }))
for (const d of reportArr[0]?.checkItems) {
if (d.isNormal === null) {
wx.showToast({
title: '请填写完整',
icon: 'none',
duration: 1500
})
return
}
if ((!d.isNormal) && (!d.level || !d.msgInp)) {
wx.showToast({
title: '异常项必须输入巡查详情和选择严重等级',
icon: 'none',
duration: 2000
})
return
}
if (d.isNormal === false) {
alarm = true; // 巡检记录异常
}
}
const { id, name, departmentId, deptName } = wx.getStorageSync('userInfo');
const newData = that.data.data.map((item) => {
// let pointDevices=[]
// item.devices.map(child=>{
// const {pointDevice,...newItem } = child;
// pointDevices.push({device:newItem,deviceId:pointDevice.deviceId})
// })
// 使用对象的解构赋值去掉 project 和 devices 属性
const { project, devices, ...newItem } = item;
// return {...newItem,pointDevices}
return {...newItem}
});
const nextItemData=newData.find(item=>item.id===pointList[pointIndex].id)
let datas = {
patrolPlanId: -1,
pointId: sList[pointIndex].id,
inspectionTime: moment().format('YYYY-MM-DD HH:mm:ss'),
points: {
user: { id, name, department: { id: departmentId, name: deptName } },
project: sList[structListIndex],
itemData:nextItemData,
inspectContent: reportArr,
aboutDisaster:{ rtuNo,
installLocation,
handleGoal,
handleObj}
},
alarm,
projectId: sList[structListIndex].id
}
wx.showLoading({ title: '提交中...' });
that.setData({ isCommitting: true });
Request.post(reportQuest(), datas).then(res => {
wx.hideLoading();
that.setData({ isCommitting: false });
wx.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
that.bindCancel();
}, 1500)
})
},
//多张图片上传
uploadImg: function (data, deviceidx, itemidx) {
wx.showLoading({
title: '上传中...',
mask: true,
})
let that = this,
i = data.i ? data.i : 0,
success = data.success ? data.success : 0,
fail = data.fail ? data.fail : 0;
let imgs = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
wx.uploadFile({
url: data.url,
filePath: data.path[i],
name: 'file',
success: (resp) => {
wx.hideLoading();
success++;
let str = JSON.parse(resp.data) // 返回的结果,可能不同项目结果不一样
str = str.uploaded
if (imgs.length >= 20) {
let nextInspectContentArr = that.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ inspectContentArr: nextInspectContentArr });
return false;
} else {
imgs.push(str);
let nextInspectContentArr = that.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
that.setData({ inspectContentArr: nextInspectContentArr });
}
},
fail: (res) => {
fail++;
console.log('fail:' + i + "fail:" + fail);
},
complete: () => {
i++;
if (i == data.path.length) { // 当图片传完时,停止调用
console.log('执行完毕');
console.log('成功:' + success + " 失败:" + fail);
} else { // 若图片还没有传完,则继续调用函数
data.i = i;
data.success = success;
data.fail = fail;
that.uploadImg(data, deviceidx, itemidx); // 递归,回调自己
}
}
});
},
// 上传图片
chooseImg: function (e) { // 这里是选取图片的方法
const { deviceidx, itemidx } = e.currentTarget.dataset;
const that = this;
let pics = [];
const detailPics = that.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
if (detailPics.length >= 20) {
wx.showToast({
title: '最多选择20张图片上传',
icon: 'none'
});
return;
}
wx.chooseMedia({
count: 20, // 基础库2.25.0前,最多可支持9个文件,2.25.0及以后最多可支持20个文件
mediaType: ['image'], // 文件类型
sizeType: ['original', 'compressed'], // original 原图,compressed 压缩图,默认二者都有
sourceType: ['album', 'camera'], // album 从相册选图,camera 使用相机,默认二者都有
success: function (res) {
const imgs = res.tempFiles;
for (let i = 0; i < imgs.length; i++) {
if (res.tempFiles[i].size > 15728640) {
wx.showToast({ title: '图片大于15M,不可上传', icon: 'none' });
return;
}
const fileNameArr = res.tempFiles[i].tempFilePath.split('.');
const extension = res.tempFiles[i].tempFilePath.split('.')[fileNameArr.length - 1];
if (extension !== 'jpg' && extension !== 'png' && extension !== 'jpeg') {
wx.showToast({ title: '只能上传jpg、jpeg、png格式的图片', icon: 'none' });
return;
}
pics.push(imgs[i].tempFilePath)
}
that.uploadImg({
url: getApp().globalData.webUrl + '_upload/attachments/project', // 图片上传的接口
path: pics, // 选取的图片的地址数组
}, deviceidx, itemidx);
},
})
},
// 删除图片
deleteImg: function (e) {
const { deviceidx, itemidx, index } = e.currentTarget.dataset;
let imgs = this.data.inspectContentArr[deviceidx].checkItems[itemidx].imgs;
imgs.splice(index, 1);
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].imgs = imgs;
this.setData({ inspectContentArr: nextInspectContentArr })
},
// 巡查详情
bindInput: function (e) {
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].msgInp = e.detail.value;
this.setData({ inspectContentArr: nextInspectContentArr })
},
handleChangeThree(e) {
const { deviceidx, itemidx } = e.currentTarget.dataset;
let nextInspectContentArr = this.data.inspectContentArr;
nextInspectContentArr[deviceidx].checkItems[itemidx].level = e.detail;
this.setData({ inspectContentArr: nextInspectContentArr })
},
//巡检模板改变
patrolTemplateChange(e){
const that=this
that.getPatrolTemplate(that.data.patrolTemplate[e.detail.value].id)
that.setData({
patrolTemplateIndex:e.detail.value
})
},
bindShowMsg() {
this.setData({
select: !this.data.select
})
},
mySelect(e) {
var name = e.currentTarget.dataset.name
this.setData({
tihuoWay: name,
select: false
})
},
/**
* 页面的初始数据
*/
// data: {
// },
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const that=this
wx.setNavigationBarTitle({
title: options.key,
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

25
weapp/package/groundDisasterInspection/groundDisasterInspection.json

@ -0,0 +1,25 @@
{
"navigationBarBackgroundColor": "#1979ff",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "地灾巡检",
"enablePullDownRefresh": false,
"componentFramework": "glass-easel",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-field": "@vant/weapp/field/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-picker": "@vant/weapp/picker/index",
"van-popup": "@vant/weapp/popup/index",
"van-icon": "@vant/weapp/icon/index",
"van-collapse": "@vant/weapp/collapse/index",
"van-collapse-item": "@vant/weapp/collapse-item/index",
"van-divider": "@vant/weapp/divider/index",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-picker": "tdesign-miniprogram/picker/picker",
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item",
"van-radio": "@vant/weapp/radio/index",
"van-radio-group": "@vant/weapp/radio-group/index"
}
}

95
weapp/package/groundDisasterInspection/groundDisasterInspection.wxml

@ -0,0 +1,95 @@
<view class="popBox">
<view>
<van-cell-group class="mission-card">
<van-cell-group>
<van-field value="{{ sms }}" bind:input="onInputChange" center clearable label="设备SN号" placeholder="设备SN号" border="{{ false }}" use-button-slot>
<van-button slot="button" size="small" type="info" bindtap="searchDetail">
搜索
</van-button>
</van-field>
</van-cell-group>
<van-cell wx:if="{{sList.length}}">
<view style="display:flex">
<view class="fs-cell-title" style="">结构物:</view>
<picker style="width:100%;text-align:left" bindchange="structChange" data-type='jiegouwu' value="{{0}}" range="{{sList}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{structListIndex||structListIndex==0?sList[structListIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
<van-cell wx:if="{{pointVis}}">
<view style="display:flex">
<view class="fs-cell-title" style="">当前点位:</view>
<picker style="width:100%;text-align:left" bindchange="pointChange" data-type='point' value="{{0}}" range="{{pointList}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{pointIndex||pointIndex==0?pointList[pointIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
<!--表单项渲染-->
<view wx:if="{{formVis}}">
<van-cell-group >
<van-field value="{{handleObj}}" bind:change="input1Change" type="textarea" autosize label="排查及处理对象:" placeholder="排查及处理对象" accordion title-width="110px" />
<van-field value="{{ handleGoal }}" bind:change="input2Change" type="textarea" autosize label="排查及处理目的:" placeholder="排查及处理目的" accordion title-width="110px"/>
<van-field value="{{ installLocation }}" bind:change="input3Change" type="textarea" autosize label="安装位置:" placeholder="安装位置" accordion />
<van-field value="{{ rtuNo }}" type="textarea" bind:change="input4Change" autosize label="RTU编号:" placeholder="RTU编号" accordion />
</van-cell-group>
<van-cell >
<view style="display:flex">
<view class="fs-cell-title">巡检模板:</view>
<picker style="width:100%;text-align:left" bindchange="patrolTemplateChange" data-type='template' value="{{0}}" range="{{patrolTemplate}}" range-key="name">
<view class="fs-cell-content" style="width:100%">
{{patrolTemplateIndex||patrolTemplateIndex==0?patrolTemplate[patrolTemplateIndex].name:'请选择'}}
<van-icon name="arrow" style="float:right;position:relative; top:4px" />
</view>
</picker>
</view>
</van-cell>
</view>
</van-cell-group>
</view>
<!-- 渲染巡检内容 -->
<view wx:for="{{inspectContentArr}}" wx:key="id" wx:for-item="device" wx:for-index="deviceidx">
<view wx:if="{{device.deviceName}}" class="flex flex-start" style="height: 40px">{{device.deviceName}}</view>
<view wx:for="{{device.checkItems}}" wx:key="id" wx:for-index="itemidx">
<view class="flex-between">
<view class="item-name">{{item.name}}:</view>
<van-radio-group style="padding:10px 15px;" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindchange="handleChangeTwo" value="{{item.isNormal}}">
<van-radio style="margin-right: 20px;" class="radio-text" color="#1979ff" name="{{true}}">正常</van-radio>
<van-radio class="radio-text" checked-color="#CC0000" name="{{false}}">异常</van-radio>
</van-radio-group>
</view>
<view class="divider" />
<van-radio-group class="flex-end" style="padding:10px 15px;" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindchange="handleChangeThree" wx:if="{{item.isNormal === false}}">
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF9900" name="轻微">轻微</van-radio>
<van-radio style="margin-right: 20px;" class="radio-text" checked-color="#FF3300" name="中度">中度</van-radio>
<van-radio class="radio-text" checked-color="#990000" name="严重">严重</van-radio>
</van-radio-group>
<textarea class="textarea" placeholder="请输入巡查详情" maxlength="-1" wx:if="{{item.isNormal === false}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindinput="bindInput" />
<view class="weui-uploader" style="padding: 20rpx 30rpx;overflow-y:scroll;" wx:if="{{item.isNormal === false}}">
<view class="img-v weui-uploader__bd" style="overflow:hidden;">
<view class="pic" wx:for="{{item.imgs}}" wx:for-item="img" wx:key="*this">
<image class="weui-uploader__img showImg" src="{{imgUrl + img}}" data-index="{{index}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" mode="aspectFill" bindtap="previewImg">
<icon type="cancel" class="delete-btn" data-index="{{index}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" catchtap="deleteImg" />
</image>
</view>
<!-- 用来提示用户上传图片 -->
<view class="weui-uploader__input-box pic" data-item="{{item.name}}" data-deviceidx="{{deviceidx}}" data-itemidx="{{itemidx}}" bindtap="chooseImg">
<image class="upload" src="/images/upload.png" />
</view>
</view>
</view>
<view class="divider" />
</view>
</view>
<view class="btnBox">
<view class="cancel" bindtap="bindCancel">取消</view>
<view class="submit" bindtap="addPatrolRecord">提交</view>
</view>
</view>

132
weapp/package/groundDisasterInspection/groundDisasterInspection.wxss

@ -0,0 +1,132 @@
.divider {
width: 100%;
height: 0px;
border-top: 1px solid #F5F5F5;
}
.flex-between {
display: flex;
justify-content: space-between;
}
.flex-end {
display: flex;
justify-content: flex-end;
}
.popBox {
position: absolute;
left: 50%;
z-index: 1000;
background: #fff;
width: 95%;
margin-left: -356rpx;
padding: 20rpx 0;
}
.item-name {
margin: 20rpx 0 0 30rpx;
}
.btnBox {
padding: 50px 30rpx;
overflow: hidden;
font-size: 30rpx;
display: flex;
justify-content: space-between;
}
.cancel {
width: 38vw;
height: 42px;
line-height: 42px;
text-align: center;
background: #fff;
border: 1px solid #006BE3;
border-radius: 24px;
font-weight: 600;
font-size: 16px;
color: #1684FF;
}
.submit {
width: 38vw;
height: 42px;
line-height: 42px;
text-align: center;
background: #1684FF;
border: 1px solid #006BE3;
border-radius: 24px;
font-weight: 600;
font-size: 16px;
color: #FFFFFF;
}
.pic {
float: left;
position: relative;
margin-right: 8px;
margin-bottom: 8px;
}
.showImg {
width: 160rpx;
height: 160rpx;
}
.delete-btn {
position: absolute;
top: 0;
right: 0;
}
.upload {
width: 63px;
height: 63px;
}
.block {
display: block;
}
.icon {
width: 18px;
height: 18px;
margin-right: 5px;
}
.radio-text {
font-size: 14px;
color: #323233;
}
.van-radio-group {
display: flex;
}
.textarea {
width: 84%;
margin: 0 auto;
padding: 20rpx;
height: 120rpx;
border: 1px solid #61616166;
}
.mission-card-title {
background-color: #fff;
overflow: auto;
padding: 24rpx 16px;
display: flex;
align-items: center
}
.fs-cell-title {
max-width: 6.2em;
min-width: 6.2em;
margin-right: 12px;
text-align: left;
color: var(--field-label-color, #646566)
}
.fs-cell-content {
color: var(--field-input-text-color, #323233)
}

10
weapp/package/inspectionReport/inspectionReport.js

@ -52,14 +52,20 @@ Page({
url: getApp().globalData.imgUrl + url,
success(downloadRes) {
wx.hideLoading();
console.log('downloadRes',downloadRes)
if (downloadRes.statusCode === 200) {
const filePath = downloadRes.tempFilePath;
wx.openDocument({
filePath: filePath,
filePath:getApp().globalData.imgUrl + url,
showMenu: true,
success: function (res) {
console.log('打开文档成功');
}
},
fail: function (error) {
wx.hideLoading();
console.error('打开文档失败', error);
}
})
}
}

6
weapp/package/polling/polling.js

@ -54,6 +54,12 @@ Page({
url: `/package/report/report?key=${key}`,
})
},
jumpToGroundReport (options) {
// const key='主动上报'
wx.navigateTo({
url: `/package/groundDisasterInspection/groundDisasterInspection`
})
},
// 顶部tab切换
onChange(event) {
this.setData({

5
weapp/package/polling/polling.wxml

@ -80,8 +80,9 @@
</view>
</van-tab>
<van-tab title='主动上报' >
<view style="display: flex; justify-content: center; align-items: center; height: 100vh;">
<van-button type="info" round bindtap="jumpToReport">主动上报</van-button>
<view style="display: flex; flex-direction:column; align-items: center; height: 100vh;">
<van-button type="info" round bindtap="jumpToReport" style="margin:10vh 0">主动上报</van-button>
<van-button type="info" round bindtap="jumpToGroundReport">地灾巡检</van-button>
</view>
</van-tab>
</van-tabs>

5
weapp/pages/workbench/workbench.js

@ -36,6 +36,11 @@ Page({
text: '发现问题',
page: '/package/report/report'
},
{
iconPath: '/images/workbench/expert_systems.png',
text: 'AIOT总览',
page: '/package/AIOTOverview/AIOTOverview'
}
]
},

2
weapp/project.config.json

@ -48,7 +48,7 @@
},
"compileType": "miniprogram",
"libVersion": "2.19.4",
"appid": "wx79ff58f03d17f24d",
"appid": "wxdd82ae635b22ccdb",
"projectname": "miniprogram-92",
"condition": {},
"editorSetting": {

34
weapp/utils/getApiUrl.js

@ -70,6 +70,11 @@ exports.getdPointCurPatrolRecord = (pointId) => {
exports.getPatrolTemplate = (id) => {
return `/patrolTemplate?id=${id}`
}
// 获取巡检模板(所有的)
exports.getPatrolTemplates = () => {
return `/patrolTemplate`
}
//根据结构物获取巡检模板
exports.getTemplates = (query) => {
const {projectId } = query;
@ -111,4 +116,31 @@ exports.getPatrolRecordStatistic = (structures) => {
// 查询子系统每日巡检
exports.getSubSystemPatrol = (day, subType) => {
return `/subSystemPatrol/day?day=${day}&subType=${subType}`
}
}
//根据sn号获取结构物以及点位
exports.getDetail = (id) => {
return `/card/detail/`+id
}
//获取智能断路器
exports.getThingsDeploy = () => {
return `/things/deploy`
}
//查询设备在离线
exports.getThingsStatus= () => {
return `/things/status`
}
//查询物联网卡
exports.getCardInfo= () => {
return `/things/card`
}
//获取结构物绑定相关信息
exports.getRelationList= () => {
return `/anxinyun/project/relation/list`
}
//查询空开设备的在离线以及开关
exports.createInvoke= () => {
return `/capabilities/invoke`
}

4
web/client/src/app.js

@ -10,7 +10,7 @@ import PatrolManage from './sections/patrolManage';
import IssueHandle from './sections/issueHandle'
import Shouye from './sections/shouye';
import DeviceManage from './sections/deviceManage';
import ProjectBinding from './sections/projectBinding'
import { Func } from '$utils';
const App = props => {
const { projectName } = props
@ -22,7 +22,7 @@ const App = props => {
return (
<Layout
title={projectName}
sections={[Auth, Shouye, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle, DeviceManage]}
sections={[Auth, Shouye, ProjectRegime, Safetymanage, Organization, PatrolManage, IssueHandle, DeviceManage,ProjectBinding]}
/>
)

2
web/client/src/sections/patrolManage/components/addReportRulesModal.js

@ -147,7 +147,7 @@ const AddReportRulesModal = props => {
}
console.log('reportpic',modalData?.reportpic)
return (
<>
<Modal

19
web/client/src/sections/patrolManage/containers/patrolRecord.js

@ -22,7 +22,7 @@ const PatrolRecord = (props) => {
const format = 'YYYY-MM-DD HH:mm:ss'
const [search, setSearch] = useState({ name: null, time: [moment().add(-7, 'd').format(format), moment().format(format)], state: 'null' })
const [exportLoading, setExportLoading] = useState(false)
const aboutDisaster={handleObj:'排查及处理对象',installLocation:'安装位置',handleGoal:'排查及处理目的',rtuNo:'RTU编号'}
useEffect(() => {
record(search)
}, [])
@ -235,7 +235,22 @@ const PatrolRecord = (props) => {
)
}
})
if (modelData && modelData.points && modelData.points.inspectContent) {
//地灾相关详情数据渲染
if(modelData.points.aboutDisaster){
for (let v in modelData.points.aboutDisaster) {
dataArr.push( <>
<Row>
<Col span={5} style={{
// textAlign: 'justify', textAlignLast: 'justify'
}}>{aboutDisaster[v]} </Col>
<Col span={19}>{modelData.points.aboutDisaster[v]}</Col>
</Row>
</>)
}
}
if (modelData?.points?.itemData?.pointDevices?.length > 0) {
let inspectContent = modelData.points.inspectContent
inspectContent?.map(s => {
@ -348,7 +363,9 @@ const PatrolRecord = (props) => {
)
})
}
}
}

6
web/client/src/sections/projectBinding/actions/index.js

@ -0,0 +1,6 @@
'use strict';
import projectBinding from './projectBinding'
export default {
...projectBinding
}

59
web/client/src/sections/projectBinding/actions/projectBinding.js

@ -0,0 +1,59 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getanxinyunProject(data) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
data,
actionType: 'GET_ANXINYUN_PROJECT',
url: `${ApiTable.getanxinyunProject}`,
msg: { error: '获取安心云结构物失败' },
reducer: { name: 'anxinyunProject'}
});
}
export function addorEditRelation(data) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
data,
actionType: 'ADD_OR_EDIT_RELATION',
url: `${ApiTable.addorEditRelation}`,
msg: { option:data?.id? '编辑绑定关系':'新增绑定关系' },
// reducer: { name: 'anxinyunProject'}
});
}
export function getRelation(query) {
return dispatch => basicAction({
type: 'get',
query,
dispatch: dispatch,
actionType: 'GET_RELATION',
url: `${ApiTable.getRelation}`,
msg: { error:'查询项目映射关系失败' },
reducer: { name: 'relation'}
});
}
export function delRelation(id) {
return dispatch => basicAction({
type: 'delete',
dispatch: dispatch,
actionType: 'DEL_RELATION',
url: ApiTable.delRelation.replace('{id}', id),
msg: { option:'删除项目映射关系' },
// reducer: { name: 'anxinyunProject'}
});
}
export default{
getanxinyunProject,
getRelation,
addorEditRelation,
delRelation
}

88
web/client/src/sections/projectBinding/components/relationModal.js

@ -0,0 +1,88 @@
import React, { useRef,useState,useEffect } from 'react'
import { Button, Form } from 'antd'
import { connect } from 'react-redux';
import { InfoCircleOutlined } from '@ant-design/icons'
import { ModalForm, ProFormSelect, ProFormText, ProFormDatePicker } from '@ant-design/pro-form'
import moment from 'moment'
function RelationModal(props) {
const { title, triggerRender, editData = null, onFinish, devices,actions,dispatch,proejctListOpt,structureListOpt } = props
const{projectBinding}=actions
const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 } }
const initialValues = editData? { ...editData,} : {}
const [form] = Form.useForm()
const formRef = useRef()
return (
<ModalForm
formRef={formRef}
title={title || ''}
initialValues={initialValues}
trigger={triggerRender ? triggerRender : <Button type='primary'>{title || ''}</Button>}
layout='horizontal'
grid={true}
{...formItemLayout}
modalProps={{
destroyOnClose: true,
onCancel: () => {},
}}
onFinish={async values => {
// console.log('values1',editData)
let value={
anxinyunProject:values?.anxinyunProject,
structName:values?.structName,
id:initialValues?initialValues.key:null
}
return onFinish && (await onFinish(value))
// return true;
}}
width={500}>
<ProFormSelect
rules={[{ required: true, message: '安心云结构物' }]}
showSearch
options={proejctListOpt?.map(s => {
return { label: s.label, value: s.value }
})}
name='anxinyunProject'
label='安心云结构物'
/>
<ProFormSelect
rules={[{ required: true, message: '关联结构物' }]}
showSearch
options={structureListOpt?.map(s => {
return { label: s.label, value: s.value }
})}
name='structName'
label='关联结构物'
/>
</ModalForm>
)
}
function mapStateToProps(state) {
const {auth, global, device } = state;
return {
loading: device.isRequesting,
clientHeight: global.clientHeight,
actions: global.actions,
};
}
export const DEVICE_TYPES = [
'安防系统',
'厨房系统',
'电梯',
'供电系统',
'空调',
'排水系统',
'水系统',
'通道门禁',
'通风系统',
'通信系统',
'显示视频',
'消防系统',
'照明系统',
]
export default connect(mapStateToProps)(RelationModal)

5
web/client/src/sections/projectBinding/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import ProjectBinding from './projectBinding'
export { ProjectBinding };

304
web/client/src/sections/projectBinding/containers/projectBinding.js

@ -0,0 +1,304 @@
import React, { useEffect, useState, useRef, useMemo } from 'react'
import { Spin, Popconfirm, message, Button, Input, Select } from 'antd'
import { connect } from 'react-redux'
import ProTable from '@ant-design/pro-table'
import moment from 'moment'
import RelationModal from '../components/relationModal'
function ProjectBinding(props) {
const {loading, clientHeight, actions, dispatch, devices,anxinyunProjectLoading,relationLoading } = props
const { projectBinding, projectRegime } = actions
const tableRef = useRef()
const proTableFormRef = useRef()
const [proejctListOpt, setProjectListOpt] = useState([])//安心云结构物
const [structureListOpt, setStructureListOpt] = useState([])//巡检结构物
const [dataSource, setDataSource] = useState([])
// const [dataCopy,setDataCopy]=useState([])
const [tableParams, setTableParams] = useState({})
// const fetchData = async () => {
// let data={
// url:"users/{orgId}/projects",
// type:"get"
// }
// dispatch(projectBinding.getanxinyunProject(data)).then(async res=>{
// if(res.success){
// const d=res.payload.data?.map(item=>{
// return {
// label:item?.projects[0]?.name,
// value:item?.projects[0]?.id
// }
// })
// setProjectListOpt(d)
// const r2= await dispatch(projectRegime.getProjectList())
// const dp=r2.pauseStatemap(item=>{
// return {
// label:item?.name,
// value:item?.id
// }
// })
// setStructureListOpt(dp)
// const r= await dispatch(projectBinding.getRelation())
// const list = r?.payload?.data?.rows?.map(item=>{
// return {
// key: item.id,
// anxinyunProject:d?.find(q=>q.value===item.axyProjectId)?.label,
// structName:dp?.find(q=>q.value===item.structureId)?.label
// }
// })
// setDataSource(list)
// }
// })
// }
//初始化
useEffect(() => {
let data = {
url:'organizations/{orgId}/structures',
// url: 'users/{orgId}/projects',
type: 'get',
}
dispatch(projectBinding.getanxinyunProject(data)).then(res => {
if (res.success) {
const d = res.payload.data?.map(item => {
return {
label: item?.name,
value: item?.iotaThingId,
}
})
setProjectListOpt(d)
}
})
dispatch(projectRegime.getProjectList()).then(res => {
if (res.success) {
const dp = res.payload.data?.rows?.map(item => {
return {
label: item?.name,
value: item?.id,
}
})
setStructureListOpt(dp)
}
})
}, [])
const queryData = () => {
dispatch(projectBinding.getRelation()).then(res => {
if (res.success) {
const list = res?.payload?.data?.rows?.map(item => {
return {
key: item.id,
anxinyunProject: proejctListOpt?.find(q => q.value === item.axyProjectId)?.value,
structName: structureListOpt?.find(q => q.value === item.structureId)?.value,
}
})
setDataSource(list)
// setDataCopy(list)
}
})
}
useEffect(() => {
if (proejctListOpt && structureListOpt && proejctListOpt.length && structureListOpt.length) {
queryData()
}
}, [proejctListOpt, structureListOpt])
const onFinish = async values => {
const dataToSave = { axyProjectId: values?.anxinyunProject, structrueId: values?.structName, id: values?.id }
return dispatch(projectBinding.addorEditRelation(dataToSave)).then(res => {
if (res.success) {
queryData()
// tableRef.current.reload()
return true
} else {
return false
}
})
}
//删除按钮
const handleDelete = id => {
dispatch(projectBinding.delRelation(id)).then(res => {
if (res.success) {
queryData()
}
})
}
const filterOption = (input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
const columns = [
{
title: '安心云结构物',
dataIndex: 'anxinyunProject',
ellipsis: true,
valueType: 'select',
fieldProps: {
options: proejctListOpt,
},
renderFormItem: () => (
<Select
showSearch
// style={{paddingLeft:10}}
allowClear
placeholder='请选择'
options={proejctListOpt?.map(item => ({ label: item.label, value: item.value }))}
filterOption={filterOption}
optionFilterProp='children'></Select>
),
// fieldProps: {
// showSearch: true,
// defaultValue: '',
// },
// onFilter: (value, record) => record.name.includes(value),
// search:false
},
{
title: '结构物名称',
dataIndex: 'structName',
ellipsis: true,
valueType: 'select',
fieldProps: {
options: structureListOpt,
},
renderFormItem: () => (
<Select
showSearch
// style={{paddingLeft:10}}
allowClear
placeholder='请选择'
options={structureListOpt?.map(item => ({ label: item.label, value: item.value }))}
filterOption={filterOption}
optionFilterProp='children'></Select>
),
// fieldProps: {
// showSearch: true,
// defaultValue: '',
// },
// search:false
valueType: 'select',
fieldProps: {
options: structureListOpt,
},
},
{
title: '操作',
width: 160,
key: 'option',
valueType: 'option',
render: (text, record) => {
const options = []
options.push(
<RelationModal
proejctListOpt={proejctListOpt}
triggerRender={<a>修改映射关系</a>}
editData={record}
structureListOpt={structureListOpt}
title='修改映射关系'
onFinish={onFinish}
key='editModel'
/>
)
options.push(
<Popconfirm
key='del'
placement='top'
title='是否确认删除映射关系?'
onConfirm={() => handleDelete(record.key)}
okText='是'
cancelText='否'>
<a>删除</a>
</Popconfirm>
)
return options
},
},
]
// useMemo(()=>{
// })
const tableDatas = useMemo(() => {
const { anxinyunProject, structName } = tableParams
let rslt = dataSource
rslt = rslt
.filter(s => (anxinyunProject ? (s.anxinyunProject ? s.anxinyunProject === anxinyunProject : false) : true))
?.filter(s => (structName ? (s.structName ? s.structName === structName : false) : true))
return rslt
})
// const searchHandler=()=>{
// const anxinyunProject=proTableFormRef.current.getFieldsValue()?.anxinyunProject
// const structName=proTableFormRef.current.getFieldsValue()?.structName
// setDataSource(
// dataCopy.filter(f =>
// (!anxinyunProject || f.anxinyunProject?.includes(anxinyunProject)) &&
// (!structName || f?.structName === structName)
// )
// )
// }
return (
<Spin spinning={anxinyunProjectLoading||relationLoading}>
<div id='patrol-record' className='global-main'>
{/* <Spin spinning={loading}> */}
<div style={{ marginBottom: 19 }}>
<div className='top' style={{ marginBottom: 19 }}>
<div className='title'>
<span className='line'></span>
<span className='cn'>项目绑定</span>
<span className='en'>&nbsp;PROJECT</span>
</div>
<div></div>
</div>
</div>
<RelationModal
structureListOpt={structureListOpt}
proejctListOpt={proejctListOpt}
triggerRender={<Button type='primary'>新增</Button>}
title='新增映射关系'
onFinish={onFinish}
key='addModel'
/>
<ProTable
formRef={proTableFormRef}
rowKey='id'
options={false}
request={async params => {
setTableParams(params)
return {
data: [],
success: true,
}
}}
actionRef={tableRef}
columns={columns}
pagination={{ pageSize: 10, size: 'default', className: 'global-pagination' }}
dataSource={tableDatas || []}
search={{
labelWidth: 100,
}}
// search={{
// optionRender: ({searchText, resetText}, {form}, dom) => [
// <Button type="primary" onClick={searchHandler}>查询</Button>,
// ]
// }}
></ProTable>
{/* </Spin> */}
</div>
</Spin>
)
}
function mapStateToProps(state) {
const { auth, global, device,anxinyunProject, relation} = state
return {
loading: device.isRequesting,
clientHeight: global.clientHeight,
actions: global.actions,
relationLoading:relation.isRequesting,
anxinyunProjectLoading:anxinyunProject.isRequesting,
}
}
export default connect(mapStateToProps)(ProjectBinding)

0
web/client/src/sections/projectBinding/containers/style.less

15
web/client/src/sections/projectBinding/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: 'projectBinding',
name: '项目绑定',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

13
web/client/src/sections/projectBinding/nav-item.js

@ -0,0 +1,13 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { Func } from '$utils';
export function getNavItem(user, dispatch) {
return (
<Menu.Item icon={<img src='/assets/images/menu/device.png' style={{ width: 24, height: 24 }} />}
key="projectBinding">
<Link to="/projectBinding">项目绑定</Link>
</Menu.Item>
);
}

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

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

13
web/client/src/sections/projectBinding/routes.js

@ -0,0 +1,13 @@
'use strict';
import { ProjectBinding } from './containers';
export default [{
type: 'inner',
route: {
path: '/projectBinding',
key: 'projectBinding',
breadcrumb: '项目绑定',
component: ProjectBinding,
}
}];

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

@ -151,6 +151,15 @@ export const ApiTable = {
generateReport: "generate/report",//报表生成
//项目映射相关
getanxinyunProject:'anxinyun/project/list',
addorEditRelation:'anxinyun/project/relation',
delRelation:'anxinyun/project/relation/{id}',
getRelation:'anxinyun/project/relation/list',
};
export const RouteTable = {

Loading…
Cancel
Save