@ -1,18 +1,25 @@ |
|||
pipeline { |
|||
agent { |
|||
node{ |
|||
label 'jnlp-slave' |
|||
} |
|||
} |
|||
podTemplate { |
|||
node('pod-templ-jenkins-slave-common') { |
|||
|
|||
env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${IOT}/${JOB_NAME}" |
|||
env.IMAGE_NAME_SHORT = "${IOT}/${JOB_NAME}" |
|||
env.CODE_ADDR = "${GIT_ADDRESS}/free-sun/Inspection.git" |
|||
|
|||
stage('Run shell') { |
|||
git branch: 'master', credentialsId: 'gitea-builder', url: "${CODE_ADDR}" |
|||
|
|||
stages { |
|||
stage('巡检 api ......') { |
|||
steps { |
|||
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|||
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|||
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./api' |
|||
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' |
|||
container('image-builder') { |
|||
sh''' |
|||
pwd |
|||
ls -al |
|||
|
|||
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./api/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
|||
|
|||
''' |
|||
} |
|||
|
|||
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" |
|||
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" |
|||
} |
|||
} |
|||
} |
@ -1,18 +1,25 @@ |
|||
pipeline { |
|||
agent { |
|||
node{ |
|||
label 'jnlp-slave' |
|||
} |
|||
} |
|||
podTemplate { |
|||
node('pod-templ-jenkins-slave-common') { |
|||
|
|||
env.IMAGE_NAME = "${IOT_IMAGES_REGISTRY}/${IOT}/${JOB_NAME}" |
|||
env.IMAGE_NAME_SHORT = "${IOT}/${JOB_NAME}" |
|||
env.CODE_ADDR = "${GIT_ADDRESS}/free-sun/Inspection.git" |
|||
|
|||
stage('Run shell') { |
|||
git branch: 'master', credentialsId: 'gitea-builder', url: "${CODE_ADDR}" |
|||
|
|||
stages { |
|||
stage('巡检 web ......') { |
|||
steps { |
|||
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|||
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|||
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./web' |
|||
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' |
|||
container('image-builder') { |
|||
sh''' |
|||
pwd |
|||
ls -al |
|||
|
|||
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./web/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
|||
|
|||
''' |
|||
} |
|||
|
|||
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" |
|||
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" |
|||
} |
|||
} |
|||
} |
After Width: | Height: | Size: 895 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 808 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 942 B |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 808 B |
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,168 @@ |
|||
// package/riskManagement/riskCalendar/riskCalendar.js
|
|||
import * as echarts from '../components/ec-canvas/echarts'; |
|||
|
|||
Page({ |
|||
initECharts(option) { |
|||
this.ecComponent.init((canvas, width, height, dpr) => { |
|||
const chart = echarts.init(canvas, null, { |
|||
width: width, |
|||
height: height, |
|||
devicePixelRatio: dpr, |
|||
}); |
|||
chart.setOption(option); |
|||
this.chart = chart; |
|||
return chart; |
|||
}); |
|||
}, |
|||
initDeviceECharts(option) { |
|||
this.ecDeviceComponent.init((canvas, width, height, dpr) => { |
|||
const chart = echarts.init(canvas, null, { |
|||
width: width, |
|||
height: height, |
|||
devicePixelRatio: dpr, |
|||
}); |
|||
chart.setOption(option); |
|||
this.chart = chart; |
|||
return chart; |
|||
}); |
|||
}, |
|||
/** |
|||
* 页面的初始数据 |
|||
*/ |
|||
data: { |
|||
ec:{} |
|||
}, |
|||
navigator(e) { |
|||
wx.navigateTo({ |
|||
url: '/package/deviceBigdataGraph/statusDetail/statusDetail', |
|||
}) |
|||
console.log('xxxxxx',e) |
|||
}, |
|||
/** |
|||
* 生命周期函数--监听页面加载 |
|||
*/ |
|||
onLoad(options) { |
|||
const that = this |
|||
that.ecComponent = that.selectComponent('#mychart-dom-pie'); |
|||
that.ecDeviceComponent=that.selectComponent('#mychart-device-pie'); |
|||
var option = { |
|||
backgroundColor: "#ffffff", |
|||
legend: { |
|||
bottom: 10, |
|||
left: 'center', |
|||
}, |
|||
series: [{ |
|||
label: { |
|||
normal: { |
|||
fontSize: 14 |
|||
} |
|||
}, |
|||
type: 'pie', |
|||
center: ['50%', '50%'], |
|||
radius: ['20%', '40%'], |
|||
data: [{ |
|||
name: '类型一', |
|||
value: 1 |
|||
}, |
|||
{ |
|||
name: '类型二', |
|||
value: 2 |
|||
}, |
|||
{ |
|||
name: '类型三', |
|||
value: 3 |
|||
}, |
|||
{ |
|||
name: '类型四', |
|||
value: 4 |
|||
} |
|||
] |
|||
|
|||
}] |
|||
}; |
|||
var optionDevice = { |
|||
backgroundColor: "#ffffff", |
|||
legend: { |
|||
bottom: 10, |
|||
left: 'center', |
|||
}, |
|||
series: [{ |
|||
label: { |
|||
normal: { |
|||
fontSize: 14 |
|||
} |
|||
}, |
|||
type: 'pie', |
|||
center: ['50%', '50%'], |
|||
radius: ['20%', '40%'], |
|||
|
|||
data: [{ |
|||
name: '正常', |
|||
value: 1 |
|||
}, |
|||
{ |
|||
name: '未知', |
|||
value: 2 |
|||
}, |
|||
{ |
|||
name: '异常', |
|||
value: 3 |
|||
}, |
|||
|
|||
] |
|||
|
|||
}] |
|||
}; |
|||
that.initECharts(option); |
|||
that.initDeviceECharts(optionDevice); |
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面初次渲染完成 |
|||
*/ |
|||
onReady() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面显示 |
|||
*/ |
|||
onShow() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面隐藏 |
|||
*/ |
|||
onHide() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面卸载 |
|||
*/ |
|||
onUnload() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面相关事件处理函数--监听用户下拉动作 |
|||
*/ |
|||
onPullDownRefresh() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面上拉触底事件的处理函数 |
|||
*/ |
|||
onReachBottom() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 用户点击右上角分享 |
|||
*/ |
|||
onShareAppMessage() { |
|||
|
|||
} |
|||
}) |
@ -0,0 +1,11 @@ |
|||
{ |
|||
"navigationBarBackgroundColor": "#1979ff", |
|||
"navigationBarTextStyle": "white", |
|||
"navigationBarTitleText": "设备大数据图谱", |
|||
"enablePullDownRefresh": false, |
|||
"usingComponents": { |
|||
"ec-canvas": "../components/ec-canvas/ec-canvas", |
|||
"van-button": "@vant/weapp/button/index", |
|||
"van-progress": "@vant/weapp/progress/index" |
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
<!--package/riskManagement/riskCalendar/riskCalendar.wxml--> |
|||
<view class="container"> |
|||
<!--质保图谱--> |
|||
<view class="card"> |
|||
<image src="/images/shape3.png" class="imgStyle"></image> |
|||
<view class="top"> |
|||
<view style="display: flex; align-items: center;"> |
|||
<image style="width: 30px; height: 30px;" src="/images/edit.png" /> |
|||
<text class="fontStyle">质保图谱</text> |
|||
</view> |
|||
<view class="detailStyle"> |
|||
<van-button type="info" round size="small">查看详情</van-button> |
|||
</view> |
|||
</view> |
|||
<view class="progress-container"> |
|||
<text class="label">过保比率:</text> |
|||
<view class="progress-wrapper"> |
|||
<progress class="progress" percent="50" color="#4E87FF"></progress> |
|||
<text>50%</text> |
|||
</view> |
|||
</view> |
|||
<view class="progress-container"> |
|||
<text class="label">质保期比例:</text> |
|||
<view class="progress-wrapper"> |
|||
<progress class="progress" percent="50"></progress> |
|||
<text>50%</text> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
<!--设备状态--> |
|||
<view class="card"> |
|||
<image src="/images/shape1.png" class="imgStyle"></image> |
|||
<view class="top"> |
|||
<view style="display: flex; align-items: center;"> |
|||
<image style="width: 30px; height: 30px;" src="/images/device.png" /> |
|||
<text class="fontStyle">设备状态</text> |
|||
</view> |
|||
<view class="detailStyle"> |
|||
<van-button type="info" round size="small" bindtap="navigator">查看详情</van-button> |
|||
</view> |
|||
</view> |
|||
<view style="height: 250px;"> |
|||
<ec-canvas id="mychart-device-pie" canvas-id="mychart-device-pie" ec="{{ ec }}"></ec-canvas> |
|||
</view> |
|||
</view> |
|||
<!--设备类型--> |
|||
<view class="card"> |
|||
<image src="/images/shape1.png" class="imgStyle"></image> |
|||
<view class="top"> |
|||
<view style="display: flex; align-items: center;"> |
|||
<image style="width: 30px; height: 30px;" src="/images/device.png" /> |
|||
<text class="fontStyle">设备类型</text> |
|||
</view> |
|||
<view class="countStyle"> |
|||
总数: |
|||
</view> |
|||
</view> |
|||
<view style="height: 250px;"> |
|||
<ec-canvas id="mychart-dom-pie" canvas-id="mychart-pie" ec="{{ ec }}"></ec-canvas> |
|||
</view> |
|||
</view> |
|||
</view> |
@ -0,0 +1,91 @@ |
|||
/* package/riskManagement/riskCalendar/riskCalendar.wxss */ |
|||
.container { |
|||
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%); |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.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-top: 12px; |
|||
width: 100%; |
|||
background-image: linear-gradient(0deg, #F3F7FF 84%, #DBE6FF 100%); |
|||
|
|||
} |
|||
|
|||
.top { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
padding: 10px; |
|||
|
|||
/* background-position: bottom; */ |
|||
} |
|||
|
|||
.fontStyle { |
|||
width: 64px; |
|||
height: 22px; |
|||
font-family: PingFangSC-Medium; |
|||
font-weight: 500; |
|||
font-size: 16px; |
|||
color: #000000d9; |
|||
letter-spacing: 0; |
|||
margin-left: 5px; |
|||
} |
|||
|
|||
.imgStyle { |
|||
position: absolute; |
|||
width: 115px; |
|||
height: 67px; |
|||
right: 0; |
|||
|
|||
} |
|||
|
|||
.detailStyle { |
|||
z-index: 1; |
|||
} |
|||
|
|||
.progress-container { |
|||
display: flex; |
|||
align-items: center; |
|||
width: 100%; |
|||
margin: 0px 0px 10px 10px; |
|||
} |
|||
|
|||
.label { |
|||
margin-right: 10px; |
|||
width: 50%; |
|||
height: 20px; |
|||
font-family: PingFangSC-Regular; |
|||
font-weight: 400; |
|||
font-size: 14px; |
|||
color: #383A3B; |
|||
} |
|||
|
|||
.progress-wrapper { |
|||
display: flex; |
|||
align-items: center; |
|||
width: 100%; |
|||
|
|||
} |
|||
|
|||
.percentage { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
.progress { |
|||
width: 75%; |
|||
} |
|||
|
|||
.countStyle { |
|||
width: 89px; |
|||
height: 24px; |
|||
font-family: PingFangSC-Medium; |
|||
font-weight: 500; |
|||
font-size: 17px; |
|||
color: #1684FF; |
|||
letter-spacing: 0; |
|||
} |
@ -0,0 +1,120 @@ |
|||
// package/deviceBigdataGraph/detail/detail.js
|
|||
import * as echarts from '../../components/ec-canvas/echarts'; |
|||
function setOption(chart, data) { |
|||
const option = { |
|||
grid: { |
|||
top: '5%', |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '3%', |
|||
containLabel: true |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
|||
}, |
|||
yAxis: { |
|||
type: 'value' |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: data, |
|||
type: 'line' |
|||
} |
|||
] |
|||
}; |
|||
chart.setOption(option); |
|||
} |
|||
|
|||
Page({ |
|||
|
|||
/** |
|||
* 页面的初始数据 |
|||
*/ |
|||
data: { |
|||
ec: { |
|||
// onInit: initChart,
|
|||
lazyLoad: true, // 将 lazyLoad 设为 true 后,需要手动初始化图表
|
|||
}, |
|||
isLoaded: false, |
|||
list: [1,2,3] |
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面加载 |
|||
*/ |
|||
onLoad(options) { |
|||
setTimeout(() => { |
|||
this.initChart([250, 300, 100, 147, 260, 123, 311]) |
|||
}, 1000) |
|||
}, |
|||
initChart: function (data) { |
|||
this.ecComponent.init((canvas, width, height, dpr) => { |
|||
const chart = echarts.init(canvas, null, { |
|||
width: width, |
|||
height: height, |
|||
devicePixelRatio: dpr // new
|
|||
}); |
|||
setOption(chart, data); |
|||
|
|||
// 将图表实例绑定到 this 上,可以在其他成员函数中访问
|
|||
this.chart = chart; |
|||
|
|||
this.setData({ |
|||
isLoaded: true, |
|||
}); |
|||
|
|||
// 注意这里一定要返回 chart 实例,否则会影响事件处理等
|
|||
return chart; |
|||
}); |
|||
}, |
|||
/** |
|||
* 生命周期函数--监听页面初次渲染完成 |
|||
*/ |
|||
onReady() { |
|||
this.ecComponent = this.selectComponent('#device-status-chart'); |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面显示 |
|||
*/ |
|||
onShow() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面隐藏 |
|||
*/ |
|||
onHide() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面卸载 |
|||
*/ |
|||
onUnload() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面相关事件处理函数--监听用户下拉动作 |
|||
*/ |
|||
onPullDownRefresh() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面上拉触底事件的处理函数 |
|||
*/ |
|||
onReachBottom() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 用户点击右上角分享 |
|||
*/ |
|||
onShareAppMessage() { |
|||
|
|||
} |
|||
}) |
@ -0,0 +1,10 @@ |
|||
{ |
|||
"navigationBarBackgroundColor": "#1979ff", |
|||
"navigationBarTextStyle": "white", |
|||
"navigationBarTitleText": "设备状态", |
|||
"enablePullDownRefresh": false, |
|||
"usingComponents": { |
|||
"ec-canvas": "../../components/ec-canvas/ec-canvas" |
|||
|
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
<!--package/deviceBigdataGraph/detail/detail.wxml--> |
|||
<view class="status-detail"> |
|||
<view class="icon"><text class="icon-text">设备总数</text></view> |
|||
<view>300</view> |
|||
<view class="flex flex-around"> |
|||
<view class="title-item flex flex-col"> |
|||
<view>设备故障率</view> |
|||
<view><text class="title-num">{{86}}%</text></view> |
|||
</view> |
|||
<view class="title-item flex flex-col"> |
|||
<view>完好率</view> |
|||
<view><text class="title-num">{{300}}%</text></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card"> |
|||
<view class="flex flex-start"> |
|||
<!-- <image src="" class="card-img" /> --> |
|||
<view class="card-img" style="background: blue;"></view> |
|||
<view class="card-title">历史风险趋势</view> |
|||
</view> |
|||
<view class="chart"> |
|||
<ec-canvas id="device-status-chart" canvas-id="device-status-chart" ec="{{ ec }}"></ec-canvas> |
|||
</view> |
|||
</view> |
|||
</view> |
@ -0,0 +1,76 @@ |
|||
/* package/deviceBigdataGraph/detail/detail.wxss */ |
|||
.status-detail { |
|||
height: 100vh; |
|||
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%); |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.icon { |
|||
width: 61px; |
|||
height: 31.86px; |
|||
background-image: linear-gradient(0deg, #EAF2FF 5%, #2578F0 100%); |
|||
box-shadow: 0 3px 4px 1px #bfdbfa4f; |
|||
} |
|||
|
|||
.icon-text { |
|||
width: 48px; |
|||
height: 17px; |
|||
font-family: PingFangSC-Medium; |
|||
font-weight: 500; |
|||
font-size: 12px; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
.title-item { |
|||
width: 150px; |
|||
color: #ffffffd9; |
|||
} |
|||
|
|||
.title-num { |
|||
font-size: 20px; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
.title-unit { |
|||
font-size: 10px; |
|||
color: #FFFFFE; |
|||
margin-left: 10px; |
|||
} |
|||
|
|||
.card { |
|||
background: #FFFFFF; |
|||
box-shadow: 2px 2px 11px 0 #00000008, 0 0 4px 0 #00000012; |
|||
border-radius: 4px; |
|||
padding: 12px; |
|||
margin-top: 12px; |
|||
} |
|||
|
|||
.card-img { |
|||
width: 18px; |
|||
height: 18px; |
|||
margin-right: 13px; |
|||
} |
|||
|
|||
.card-title { |
|||
font-weight: 500; |
|||
font-size: 16px; |
|||
color: #383A3B; |
|||
} |
|||
|
|||
.card-link { |
|||
font-weight: 500; |
|||
font-size: 14px; |
|||
color: #1684FF; |
|||
} |
|||
|
|||
.chart { |
|||
width: 100%; |
|||
height: 195px; |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
.list { |
|||
margin-top: 10px; |
|||
padding: 10px 7px; |
|||
background-color: #F1F7FF; |
|||
} |
@ -0,0 +1,543 @@ |
|||
// package/report/report.js
|
|||
|
|||
import { getPointList,getPatrolTemplate,getTemplates,reportQuest,getPatrolPlan } from "../../utils/getApiUrl"; |
|||
import {Request} from "../../common"; |
|||
const moment = require("../../utils/moment"); |
|||
|
|||
Page({ |
|||
data: { |
|||
isPlanState: false, |
|||
structList: [{ |
|||
id: 0, |
|||
name: '指挥中心' |
|||
}, { |
|||
id: 1, |
|||
name: '管廊' |
|||
}, |
|||
{ |
|||
id: 2, |
|||
name: '电梯系统' |
|||
}, { |
|||
id: 3, |
|||
name: '供配电系统' |
|||
}, { |
|||
id: 4, |
|||
name: '燃气仓' |
|||
}, |
|||
{ |
|||
id: 5, |
|||
name: '给水仓' |
|||
}, { |
|||
id: 6, |
|||
name: '防雷与接地系统' |
|||
}, { |
|||
id: 7, |
|||
name: '电气仓' |
|||
}, |
|||
{ |
|||
id: 8, |
|||
name: '高压电力仓' |
|||
}, { |
|||
id: 9, |
|||
name: '安防系统' |
|||
} |
|||
], |
|||
data:[],//巡检计划的数据(包括点位,设备等等)
|
|||
structListIndex: undefined,//结构物id
|
|||
pointList:[],//点位列表
|
|||
pointIndex:undefined,//点位索引
|
|||
devicesList:[],//设备列表
|
|||
dataList: '', // 当前巡检计划
|
|||
patrolTemplate:[],//巡检模板
|
|||
templateData:[],//巡检模板总数居
|
|||
// curPlanTemplateId:0,//当前巡检计划的模板id
|
|||
patrolTemplateIndex:undefined,//巡检模板索引
|
|||
itemData: '', // 点位
|
|||
address: '', // 当前位置
|
|||
imgUrl: getApp().globalData.imgUrl, |
|||
checkItems: [], // 检查项
|
|||
inspectContentArr: [], // 巡检内容
|
|||
isCommitting: false, |
|||
planList: null, // 巡检计划列表
|
|||
structListVisible: true, |
|||
scenePointId: null, // 当前点位id
|
|||
}, |
|||
//巡检计划
|
|||
getPatrolPlan: function (scenePointId) { |
|||
let that = this; |
|||
wx.showLoading({ |
|||
title: '加载中', |
|||
}) |
|||
Request.get(getPatrolPlan()).then(res => { |
|||
wx.hideLoading(); |
|||
let pointPlan = res.rows.filter(plan => { |
|||
for (const point of plan.points) { |
|||
if (point.id == scenePointId) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
}).map(p => ({ |
|||
label: p.name, |
|||
value: p.name, |
|||
...p |
|||
})) |
|||
that.setData({ |
|||
planList: pointPlan |
|||
}) |
|||
}) |
|||
}, |
|||
//点位改变函数
|
|||
pointChange(e){ |
|||
const that = this |
|||
that.getPatrolPlan(that.data.data[e.detail.value].id) |
|||
that.setData({ |
|||
inspectContentArr:[], |
|||
pointIndex:e.detail.value, |
|||
devicesList:that.data.data[e.detail.value].devices, |
|||
scenePointId:that.data.data[e.detail.value].id |
|||
}) |
|||
}, |
|||
// 预览图片
|
|||
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 |
|||
that.setData({ |
|||
structListIndex: event.detail.value, |
|||
// isPlanState: true,
|
|||
pointList:[],//选择结构物后先置空先前的点位列表
|
|||
}) |
|||
|
|||
const keywords=that.data?.structList[event.detail.value]?.name |
|||
const query={keywords} |
|||
|
|||
Request.get(getTemplates(query)).then(res=>{ |
|||
if(res){ |
|||
const rlst=res.map(item=>item.patrolTemplate) |
|||
that.setData({patrolTemplate:rlst,templateData:res}) |
|||
}else{ |
|||
|
|||
} |
|||
}) |
|||
Request.get(getPointList(query)).then(res => { |
|||
if(res){ |
|||
const pointList=res.map(item=>{ |
|||
return { |
|||
id:item.id, |
|||
name:item.name |
|||
} |
|||
}) |
|||
that.setData({pointList:pointList,data:res}) |
|||
}else { |
|||
wx.hideLoading(); |
|||
} |
|||
}) |
|||
|
|||
}, |
|||
//整理设备和检查项
|
|||
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: null, |
|||
msgInp: null, |
|||
level: null, |
|||
imgs: [], |
|||
})) |
|||
}) |
|||
}); |
|||
} else { |
|||
inspectContentArr.push({ |
|||
checkItems: checkItems.map(c => ({ |
|||
id: c.id, |
|||
name: c.name, |
|||
isNormal: null, |
|||
msgInp: null, |
|||
level: null, |
|||
imgs: [], |
|||
})) |
|||
}) |
|||
} |
|||
this.setData({ |
|||
checkItems, |
|||
inspectContentArr: inspectContentArr, |
|||
}) |
|||
}) |
|||
}, |
|||
//选择异常或者正常
|
|||
handleChangeTwo(e) { |
|||
const isNormal = e.detail === 'normal'; |
|||
const { deviceidx, itemidx } = e.currentTarget.dataset; |
|||
let nextInspectContentArr = this.data.inspectContentArr; |
|||
|
|||
nextInspectContentArr[deviceidx].checkItems[itemidx].isNormal = isNormal; |
|||
if (isNormal) { // 清除异常数据
|
|||
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 { |
|||
patrolTemplate, |
|||
patrolTemplateIndex, |
|||
structListIndex, |
|||
pointIndex, |
|||
pointList, |
|||
inspectContentArr, |
|||
dataList, |
|||
address, |
|||
data, |
|||
templateData |
|||
} = that.data; |
|||
let alarm = false; |
|||
if (!address) { |
|||
wx.showToast({ |
|||
title: '请获取当前位置', |
|||
icon: 'none', |
|||
duration: 1500 |
|||
}) |
|||
return; |
|||
} |
|||
if (!structListIndex) { |
|||
wx.showToast({ |
|||
title: '请选择结构物', |
|||
icon: 'none', |
|||
duration: 1500 |
|||
}) |
|||
return; |
|||
} |
|||
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: false })); |
|||
for (const [index, device] of inspectContentArr.entries()) { |
|||
for (const item of device.checkItems) { |
|||
if (item.isNormal === null) { |
|||
wx.showToast({ |
|||
title: '请填写完整', |
|||
icon: 'none', |
|||
duration: 1500 |
|||
}) |
|||
return; |
|||
} |
|||
if ((!item.isNormal) && (!item.level || !item.msgInp)) { |
|||
wx.showToast({ |
|||
title: '异常项必须输入巡查详情和选择严重等级', |
|||
icon: 'none', |
|||
duration: 2000 |
|||
}) |
|||
return; |
|||
} |
|||
if (item.isNormal === false) { |
|||
alarm = true; // 巡检记录异常
|
|||
reportArr[index].alarm = true; // 设备异常
|
|||
} |
|||
} |
|||
} |
|||
const { id, name, departmentId, deptName } = wx.getStorageSync('userInfo'); |
|||
const curPlan = that.data.planList.find(item=>item.id=patrolTemplate[patrolTemplateIndex].id) |
|||
const nextItemData = curPlan.points.find(p => p.id == this.data.scenePointId) |
|||
const aboutSend=templateData.find(item=>item.patrolTemplate.id===patrolTemplate[patrolTemplateIndex].id) |
|||
let datas = { |
|||
patrolPlanId: -1, |
|||
pointId: pointList[pointIndex].id, |
|||
inspectionTime: moment().format('YYYY-MM-DD HH:mm:ss'), |
|||
points: { |
|||
user: { id, name, department: { id: departmentId, name: deptName } }, |
|||
project: aboutSend.project, |
|||
frequency: aboutSend.frequency, |
|||
itemData:nextItemData, |
|||
inspectContent: reportArr, |
|||
address: address |
|||
}, |
|||
alarm, |
|||
projectId: aboutSend.project.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); |
|||
}, |
|||
}) |
|||
}, |
|||
// 巡查详情
|
|||
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.data.devicesList) |
|||
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, |
|||
}); |
|||
|
|||
}, |
|||
onStructListPicker() { |
|||
this.setData({ |
|||
structListVisible: true |
|||
}); |
|||
}, |
|||
// 获取当前位置
|
|||
selfLocation() { |
|||
const that = this |
|||
wx.showLoading({ |
|||
title: '定位中', |
|||
mask: true, |
|||
}); |
|||
wx.getLocation({ |
|||
type: 'wgs84', |
|||
success: (res) => { |
|||
wx.request({ |
|||
url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${res.latitude},${res.longitude}&key=${getApp().globalData.key}`, |
|||
success: function (res) { |
|||
wx.hideLoading(); |
|||
// 根据自己项目需求获取res内容
|
|||
that.setData({ |
|||
address: res.data.result.address |
|||
}) |
|||
} |
|||
}) |
|||
}, |
|||
fail: (res) => { |
|||
wx.hideLoading(); |
|||
wx.showToast({ |
|||
title: res.errMsg, |
|||
icon: 'none', |
|||
duration: 1000 |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
/** |
|||
* 生命周期函数--监听页面初次渲染完成 |
|||
*/ |
|||
onReady() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面显示 |
|||
*/ |
|||
onShow() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面隐藏 |
|||
*/ |
|||
onHide() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面卸载 |
|||
*/ |
|||
onUnload() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面相关事件处理函数--监听用户下拉动作 |
|||
*/ |
|||
onPullDownRefresh() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 页面上拉触底事件的处理函数 |
|||
*/ |
|||
onReachBottom() { |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 用户点击右上角分享 |
|||
*/ |
|||
onShareAppMessage() { |
|||
|
|||
} |
|||
}) |
@ -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" |
|||
} |
|||
} |
@ -0,0 +1,96 @@ |
|||
<view class="popBox"> |
|||
<view> |
|||
<van-cell-group class="mission-card"> |
|||
<van-cell> |
|||
<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="{{structList}}" range-key="name"> |
|||
<view class="fs-cell-content" style="width:100%"> |
|||
{{structListIndex||structListIndex==0?structList[structListIndex].name:'请选择'}} |
|||
<van-icon name="arrow" style="float:right;position:relative; top:4px" /> |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
</van-cell> |
|||
<van-cell> |
|||
<view style="display:flex"> |
|||
<view class="fs-cell-title" style="">当前点位:</view> |
|||
<picker style="width:100%;text-align:left" bindchange="pointChange" data-type='point' 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> |
|||
<van-cell> |
|||
<view style="overflow: hidden;"> |
|||
<view style="float: left;" class="fs-cell-title">当前位置:</view> |
|||
<view style="float:right;" class="fs-cell-content" wx:if="{{address}}"> |
|||
{{address}} |
|||
</view> |
|||
<view style="float:right;"> |
|||
<image wx:if="{{!address}}" class="icon" src="../../images/landmark.svg" /> |
|||
<view style="display: inline-block;" bindtap="selfLocation" wx:if="{{!address}}"> |
|||
点击获取 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</van-cell> |
|||
<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> |
|||
|
|||
|
|||
|
|||
</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"> |
|||
<van-radio style="margin-right: 20px;" class="radio-text" color="#1979ff" name="normal">正常</van-radio> |
|||
<van-radio class="radio-text" checked-color="#CC0000" name="abnormal">异常</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="bindInput"> |
|||
<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> |
@ -0,0 +1,131 @@ |
|||
.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) |
|||
} |
@ -1,9 +1,11 @@ |
|||
{ |
|||
"navigationBarBackgroundColor": "#1979ff", |
|||
"navigationBarBackgroundColor": "#006BE3", |
|||
"navigationBarTextStyle": "white", |
|||
"navigationBarTitleText": "故障日历", |
|||
"navigationBarTitleText": "故障风险管理", |
|||
"enablePullDownRefresh": false, |
|||
"usingComponents": { |
|||
"ec-canvas": "../../components/ec-canvas/ec-canvas" |
|||
"van-calendar": "@vant/weapp/calendar/index", |
|||
"van-picker": "@vant/weapp/picker/index", |
|||
"van-popup": "@vant/weapp/popup/index" |
|||
} |
|||
} |
@ -1,2 +1,78 @@ |
|||
<!--package/riskManagement/riskCalendar/riskCalendar.wxml--> |
|||
<text>package/riskManagement/riskCalendar/riskCalendar.wxml</text> |
|||
<view class="risk-calendar"> |
|||
<view class="card"> |
|||
<view class="card-top flex flex-between"> |
|||
<view class="card-left flex"> |
|||
<image class="card-icon" src="/images/calendar_icon.png" /> |
|||
<view class="title">巡检日历</view> |
|||
</view> |
|||
<view class="card-right">{{curDate}}</view> |
|||
<image src="/images/calendar_card_bg.png" class="card-bg" /> |
|||
</view> |
|||
<view class="calendar-box"> |
|||
<van-calendar |
|||
show-mark="{{ false }}" |
|||
show-title="{{ false }}" |
|||
show-subtitle="{{ false }}" |
|||
poppable="{{ false }}" |
|||
show-confirm="{{ false }}" |
|||
min-date="{{ minDate }}" |
|||
max-date="{{ maxDate }}" |
|||
formatter="{{ formatter }}" |
|||
bind:select="onDateSelect" |
|||
row-height="48" |
|||
color="#1684FF" |
|||
class="calendar" |
|||
/> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="title-box flex flex-between "> |
|||
<view class="title">异常等级</view> |
|||
<view class="flex" bind:tap="showPopup"> |
|||
<view class="picker-text">{{curLevel}}</view> |
|||
<image class="arrow" src="/images/arrow_right_blue.svg" /> |
|||
</view> |
|||
</view> |
|||
<van-popup show="{{ show }}" round position="bottom" close-on-click-overlay="{{false}}"> |
|||
<van-picker |
|||
show-toolbar |
|||
columns="{{ level }}" |
|||
bind:cancel="onCancel" |
|||
bind:confirm="onConfirm" |
|||
/> |
|||
</van-popup> |
|||
|
|||
<view wx:for="{{showList}}" class="card" style="margin-bottom: 10px"> |
|||
<view class="card-top flex flex-between"> |
|||
<view class="card-left flex"> |
|||
<image class="card-icon" src="/images/right_icon.png" /> |
|||
<view class="title">{{item.points.project.name}}</view> |
|||
</view> |
|||
<view class="card-right-btn flex" data-item="{{item}}" bindtap="toDetail">查看详情</view> |
|||
<image src="/images/right_card_bg.png" class="card-bg" /> |
|||
</view> |
|||
<view class="card-content"> |
|||
<view class="row flex flex-between"> |
|||
<view class="content-left">本次巡检日期</view> |
|||
<view class="content-right">{{item.inspectionTime}}</view> |
|||
</view> |
|||
<view class="row flex flex-between"> |
|||
<view class="content-left">点位</view> |
|||
<view class="content-right">{{item.points.itemData.name}}</view> |
|||
</view> |
|||
<view class="row flex flex-between"> |
|||
<view class="content-left">巡检人</view> |
|||
<view class="content-right">{{item.points.user.name}}</view> |
|||
</view> |
|||
<view class="row flex flex-between"> |
|||
<view class="content-left">巡检结果</view> |
|||
<view class="content-right" style="color: red;">异常</view> |
|||
</view> |
|||
<view class="row flex flex-between"> |
|||
<view class="content-left">异常等级</view> |
|||
<view class="content-right" style="color: red;">{{item.level}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
@ -1 +1,105 @@ |
|||
/* package/riskManagement/riskCalendar/riskCalendar.wxss */ |
|||
.risk-calendar { |
|||
height: 100%; |
|||
background-image: linear-gradient(179deg, #006BE3 0%, #4E87FF 16%, #4e87ff00 93%); |
|||
padding: 0 15px; |
|||
} |
|||
|
|||
.card { |
|||
box-sizing: border-box; |
|||
background: #FFFFFF; |
|||
box-shadow: 2px 2px 11px 0 #00000008, 0 0 4px 0 #00000012; |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.card-top { |
|||
height: 68px; |
|||
background-image: linear-gradient(0deg, #F3F7FF 84%, #DBE6FF 100%); |
|||
border-radius: 4px; |
|||
position: relative; |
|||
} |
|||
|
|||
.card-bg { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 11px; |
|||
width: 115px; |
|||
height: 67px; |
|||
} |
|||
|
|||
.card-left { |
|||
margin-left: 23px; |
|||
font-weight: 500; |
|||
font-size: 16px; |
|||
color: #000000d9; |
|||
} |
|||
|
|||
.card-right { |
|||
margin-right: 18px; |
|||
color: #1684FF; |
|||
} |
|||
|
|||
.card-right-btn { |
|||
width: 76px; |
|||
height: 26px; |
|||
margin-right: 18px; |
|||
background: #1684FF; |
|||
border-radius: 16px; |
|||
color: #fff; |
|||
font-size: 13px; |
|||
z-index: 99; |
|||
} |
|||
|
|||
.card-icon { |
|||
width: 30px; |
|||
height: 30px; |
|||
margin-right: 8px; |
|||
} |
|||
|
|||
.calendar-box { |
|||
padding-bottom: 12px; |
|||
} |
|||
|
|||
.calendar { |
|||
--calendar-height: 300px; |
|||
} |
|||
|
|||
.title-box { |
|||
margin: 10px 0; |
|||
} |
|||
|
|||
.title { |
|||
font-weight: 600; |
|||
font-size: 16px; |
|||
color: #383A3B; |
|||
} |
|||
|
|||
.picker-text { |
|||
font-weight: 600; |
|||
color: #1684FF; |
|||
text-align: right; |
|||
} |
|||
|
|||
.arrow { |
|||
width: 12px; |
|||
height: 12px; |
|||
transform: rotate(90deg); |
|||
margin-left: 9px; |
|||
} |
|||
|
|||
.card-content { |
|||
padding-bottom: 10px; |
|||
} |
|||
|
|||
.row { |
|||
margin: 4px 26px; |
|||
color: #31373E; |
|||
} |
|||
|
|||
.content-left { |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.content-right { |
|||
font-size: 14px; |
|||
} |
After Width: | Height: | Size: 577 B |
@ -0,0 +1,56 @@ |
|||
'use strict'; |
|||
|
|||
import { basicAction } from '@peace/utils' |
|||
import { ApiTable } from '$utils' |
|||
|
|||
export function getDeviceList(query) { |
|||
return dispatch => basicAction({ |
|||
type: 'get', |
|||
dispatch: dispatch, |
|||
query: query || {}, |
|||
actionType: 'GET_Device_REPORT', |
|||
url: `${ApiTable.getDeviceList}`, |
|||
msg: { error: '获取设备列表失败' }, |
|||
reducer: { name: 'device' } |
|||
}); |
|||
} |
|||
|
|||
|
|||
export function addDevice(params) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'post', |
|||
data: params, |
|||
dispatch, |
|||
actionType: 'ADD_Device_REPORT', |
|||
url: ApiTable.addDevice, |
|||
msg: { |
|||
option: '设备新增', |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
export function deleteDevice(id) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'del', |
|||
dispatch, |
|||
actionType: 'DELETE_Device_REPORT', |
|||
url: ApiTable.modifyDevice.replace('{id}', id), |
|||
msg: { |
|||
option: '设备删除', |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
export function modifyDevice(id, params, msg) { |
|||
return (dispatch) => basicAction({ |
|||
type: 'put', |
|||
data: params, |
|||
dispatch, |
|||
actionType: 'MODIFY_Device_REPORT', |
|||
url: ApiTable.modifyDevice.replace('{id}', id), |
|||
msg: { |
|||
option: msg || '设备编辑', |
|||
}, |
|||
}); |
|||
} |
|||
|
@ -0,0 +1,7 @@ |
|||
'use strict'; |
|||
|
|||
import * as device from './device' |
|||
|
|||
export default { |
|||
...device |
|||
} |
@ -0,0 +1,213 @@ |
|||
'use strict'; |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import moment from 'moment'; |
|||
import { Modal, Form, Button, Upload, message } from 'antd'; |
|||
import { DownloadOutlined } from '@ant-design/icons'; |
|||
import XLSX from 'xlsx'; |
|||
import { DEVICE_TYPES } from './modelModal'; |
|||
// import porvince from './province.json'
|
|||
const workerKeys = { |
|||
name: '设备名称', |
|||
type: '设备类型', |
|||
specification: '规格型号', |
|||
dateProduced: '生产日期', |
|||
dateInstall: '安装时间', |
|||
dateGuarantee: '质保期', |
|||
dateMainten: '维保期', |
|||
} |
|||
//下载模板和上传文件读取
|
|||
const ImportDeviceModal = props => { |
|||
const { dispatch, actions, onCancel, onOk, devices } = props; |
|||
const { deviceManage } = actions; |
|||
const [msg, setMsg] = useState(''); |
|||
const [loading, setLoading] = useState(false); |
|||
const [postData, setPostData] = useState([]); |
|||
|
|||
//初始化
|
|||
useEffect(() => { |
|||
|
|||
}, []); |
|||
|
|||
const confirm = () => { |
|||
if (postData.length) { |
|||
setLoading(true) |
|||
//导入明细接口通用
|
|||
dispatch(deviceManage?.addDevice(postData)).then(res => { |
|||
if (res.success) { |
|||
onOk() |
|||
} |
|||
setLoading(false) |
|||
}) |
|||
} else { |
|||
message.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 }) |
|||
} |
|||
} |
|||
|
|||
const dldCsvMb = () => { |
|||
//表头
|
|||
let head = []; |
|||
Object.keys(workerKeys).map(key => { |
|||
head.push(workerKeys[key]); |
|||
}) |
|||
head = head.join(',') + "\n"; |
|||
//数据
|
|||
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5
|
|||
let templateCsv = "data:text/xls;charset=utf-8,\ufeff" + head; |
|||
//创建一个a标签
|
|||
let link = document.createElement("a"); |
|||
//为a标签设置属性
|
|||
link.setAttribute("href", templateCsv); |
|||
link.setAttribute("download", `设备导入模板.xls`); |
|||
//点击a标签
|
|||
link.click(); |
|||
} |
|||
const download = () => { |
|||
dldCsvMb(); |
|||
} |
|||
|
|||
const judgeTimeValid = (v) => { |
|||
let valid = true; |
|||
if (v.split('/').length !== 3) { |
|||
valid = false; |
|||
} else { |
|||
let time = new Date(v); |
|||
// if (!time) {
|
|||
// return valid;//可以不填
|
|||
// }
|
|||
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//年月日
|
|||
if (time instanceof Date) { |
|||
let timeStr = moment(time).format('YYYY/MM/DD'); |
|||
if (!ymd.test(timeStr)) { |
|||
valid = false; |
|||
} |
|||
} else { |
|||
valid = false; |
|||
} |
|||
} |
|||
return valid; |
|||
} |
|||
|
|||
return (<Modal |
|||
title={"导入设备信息"} visible={true} |
|||
onOk={confirm} width={520} |
|||
confirmLoading={loading} |
|||
onCancel={() => { |
|||
setMsg('') |
|||
setLoading(false) |
|||
setPostData([]) |
|||
onCancel() |
|||
}} |
|||
> |
|||
<Form> |
|||
<Upload |
|||
action={'/'} accept={'.xls,.xlsx'} |
|||
maxCount={1} |
|||
onRemove={(currentFile, fileList, fileItem) => { |
|||
setMsg(''); |
|||
setPostData([]); |
|||
}} |
|||
customRequest={async (data) => { |
|||
const { file, onSuccess, onError } = data |
|||
const error = (msg) => { |
|||
setMsg(msg) |
|||
onError({ message: msg }) |
|||
} |
|||
let isLt = file.size / 1024 / 1024 < 200 |
|||
if (!isLt) { |
|||
error(`文件最大不超过200M`); |
|||
return; |
|||
} |
|||
const reader = new FileReader(); |
|||
reader.onload = function (e) { |
|||
const data = e.target.result; |
|||
const workbook = XLSX.read(data, { type: 'binary', codepage: 936 }); |
|||
const firstSheetName = workbook.SheetNames[0]; |
|||
const worksheet = workbook.Sheets[firstSheetName]; |
|||
|
|||
//XLSX.utils.sheet_to_json解析 ------把身份证号和手机号变成 字符串------
|
|||
Object.keys(worksheet).forEach(k => { |
|||
worksheet[k].w ? worksheet[k].v = worksheet[k].w : '' |
|||
}) |
|||
//------------------end------------------
|
|||
|
|||
const res = XLSX.utils.sheet_to_json(worksheet); |
|||
|
|||
if (res.length > 1000) { |
|||
error('一次性上传数据行数应小于1000行,请分批上传') |
|||
return |
|||
} |
|||
if (!res.length) { |
|||
error('请填写至少一行数据') |
|||
return |
|||
} |
|||
let postData = [] |
|||
|
|||
for (let i = 0; i < res.length; i++) { |
|||
let d = res[i] |
|||
let obj = {}; |
|||
Object.keys(workerKeys).map(key => { |
|||
obj[key] = d[workerKeys[key]] || null; |
|||
}) |
|||
//必填项
|
|||
let notNullKeys = ['name', 'type', 'specification', 'dateProduced', 'dateGuarantee', 'dateMainten', 'dateInstall'] |
|||
for (let k = 0; k < notNullKeys.length; k++) { |
|||
let key = notNullKeys[k]; |
|||
if (!obj[key]) { |
|||
error(`第${i + 2}行【${workerKeys[key]}】不能为空`) |
|||
return |
|||
} |
|||
} |
|||
|
|||
//判断设备类型
|
|||
let ext = DEVICE_TYPES.find(m => m == obj.type); |
|||
if (!ext) { |
|||
error(`第${i + 2}行的【设备类型】错误,请填写【${DEVICE_TYPES.toString()}】中的一种`) |
|||
return |
|||
} |
|||
|
|||
let tValid = judgeTimeValid(obj.dateProduced) && judgeTimeValid(obj.dateGuarantee) |
|||
&& judgeTimeValid(obj.dateMainten) && judgeTimeValid(obj.dateInstall); |
|||
if (!tValid) { |
|||
error(`第${i + 2}行【日期格式】错误,请填写yyyy/mm/dd格式`) |
|||
return; |
|||
} |
|||
|
|||
let dateInstallValid = moment(obj.dateInstall).valueOf() > moment().startOf('d').add(1, 'd').valueOf(); |
|||
if (dateInstallValid) { |
|||
error(`第${i + 2}行【安装日期】不能填写今天之后的时间`) |
|||
return; |
|||
} |
|||
|
|||
|
|||
|
|||
postData.push(obj) |
|||
} |
|||
setPostData(postData) |
|||
let msg = '文件解析完成,点击确定按钮上传保存!' |
|||
setMsg(msg) |
|||
onSuccess({ message: msg }) |
|||
} |
|||
reader.readAsBinaryString(file); |
|||
}}> |
|||
<Button icon={<DownloadOutlined />} theme="light"> |
|||
请选择文件 |
|||
</Button> |
|||
</Upload> |
|||
<span>{msg}</span> |
|||
<div style={{ color: '#ccc', marginTop: 20 }}>最大不超过200M,导入文件需与 |
|||
<span onClick={() => download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板</span> |
|||
一致</div> |
|||
</Form> |
|||
</Modal>) |
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
actions: global.actions, |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(ImportDeviceModal); |
@ -0,0 +1,111 @@ |
|||
import React, { useRef } from 'react'; |
|||
import { Button, Form } from 'antd'; |
|||
import { InfoCircleOutlined } from '@ant-design/icons'; |
|||
import { |
|||
ModalForm, |
|||
ProFormSelect, |
|||
ProFormText, |
|||
ProFormDatePicker |
|||
} from '@ant-design/pro-form'; |
|||
import moment from 'moment'; |
|||
export default (props) => { |
|||
const { title, triggerRender, editData = null, onFinish, devices } = props; |
|||
const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 } }; |
|||
const initialValues = editData ? { |
|||
...editData, |
|||
} : {}; |
|||
const [form] = Form.useForm(); |
|||
const formRef = useRef(); |
|||
const disabledDate = (value) => { |
|||
return value.valueOf() > moment().startOf('d').add(1, 'd').valueOf(); |
|||
} |
|||
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) => { |
|||
return onFinish && await onFinish(values, editData, form) |
|||
// return true;
|
|||
}} |
|||
width={500} |
|||
> |
|||
|
|||
|
|||
<ProFormText |
|||
rules={[ |
|||
{ required: true, message: '请输入设备名称' }, |
|||
{ max: 255, message: '设备名称长度不能大于255个字符' }, |
|||
]} |
|||
name="name" |
|||
label="设备名称" |
|||
/> |
|||
|
|||
<ProFormSelect |
|||
rules={[{ required: true, message: '请选择设备类型' }]} |
|||
options={ |
|||
DEVICE_TYPES |
|||
.map(s => { |
|||
return { label: s, value: s } |
|||
}) |
|||
} |
|||
name="type" |
|||
label="设备类型" |
|||
/> |
|||
|
|||
<ProFormText |
|||
rules={[ |
|||
{ required: true, message: '请输入规格型号' }, |
|||
{ max: 20, message: '规格型号长度不能大于20个字符' }, |
|||
]} |
|||
name="specification" |
|||
label="规格型号" |
|||
/> |
|||
|
|||
<ProFormDatePicker |
|||
rules={[{ required: true, message: '请输入生产日期' }]} |
|||
name="dateProduced" |
|||
label="生产日期" |
|||
fieldProps={ |
|||
{ disabledDate: disabledDate } |
|||
} |
|||
|
|||
/> |
|||
<ProFormDatePicker |
|||
rules={[{ required: true, message: '请输入安装日期' }]} |
|||
name="dateInstall" |
|||
label="安装日期" |
|||
fieldProps={ |
|||
{ disabledDate: disabledDate } |
|||
} |
|||
|
|||
/> |
|||
<ProFormDatePicker |
|||
rules={[{ required: true, message: '请输入质保期' }]} |
|||
name="dateGuarantee" |
|||
label="质保期" |
|||
/> |
|||
<ProFormDatePicker |
|||
rules={[{ required: true, message: '请输入维保期' }]} |
|||
name="dateMainten" |
|||
label="维保期" |
|||
/> |
|||
|
|||
</ModalForm> |
|||
); |
|||
}; |
|||
|
|||
export const DEVICE_TYPES = ['安防系统', '厨房系统', '电梯', '供电系统', '空调', '排水系统', '水系统', '通道门禁', |
|||
'通风系统', '通信系统', '显示视频', '消防系统', '照明系统'] |
@ -0,0 +1,254 @@ |
|||
import React, { useEffect, useState } from 'react' |
|||
import { Spin, Popconfirm, message, Button, Input } from 'antd'; |
|||
import { connect } from 'react-redux'; |
|||
import ProTable from '@ant-design/pro-table'; |
|||
import DeviceModal from '../components/modelModal' |
|||
import moment from 'moment'; |
|||
import ImportDeviceModal from '../components/importDevicesModal' |
|||
function DeviceManagement(props) { |
|||
const { loading, clientHeight, actions, dispatch, devices } = props; |
|||
const [pageSize, setPageSize] = useState(10); |
|||
const [currentPage, setCurrentPage] = useState(1); |
|||
const [rowSelected, setRowSelected] = useState([]) |
|||
const [showImportModal, setShowImportModal] = useState(false); |
|||
const [name, setName] = useState(); |
|||
|
|||
const queryData = (search) => { |
|||
const query = { |
|||
limit: search ? 10 : pageSize || 10, |
|||
page: search ? 1 : currentPage || 1, |
|||
name: name |
|||
} |
|||
dispatch(actions.deviceManage.getDeviceList(query)); |
|||
} |
|||
|
|||
useEffect(() => { |
|||
queryData(); |
|||
}, [pageSize, currentPage]); |
|||
|
|||
const handleDelete = (id) => { |
|||
dispatch(actions.deviceManage.deleteDevice(id)).then(() => { |
|||
queryData(); |
|||
setRowSelected([]) |
|||
}); |
|||
}; |
|||
|
|||
const onFinish = async (values, editData) => { |
|||
if (editData) { |
|||
const dataToSave = { ...values } |
|||
return dispatch( |
|||
actions.deviceManage.modifyDevice(editData.id, dataToSave, values?.msg || ''), |
|||
).then((res) => { |
|||
if (res.success) { |
|||
queryData(); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
return dispatch(actions.deviceManage.addDevice({ |
|||
...values, |
|||
})).then(res => { |
|||
if (res.success) { |
|||
queryData(); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const columns = [ |
|||
{ |
|||
title: '设备名称', |
|||
dataIndex: 'name', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '设备类型', |
|||
dataIndex: 'type', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '规格型号', |
|||
dataIndex: 'specification', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '生产日期', |
|||
dataIndex: 'dateProduced', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '安装时间', |
|||
dataIndex: 'dateInstall', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '质保期', |
|||
dataIndex: 'dateGuarantee', |
|||
ellipsis: true, |
|||
search: false, |
|||
}, |
|||
{ |
|||
title: '维保期', |
|||
dataIndex: 'dateMainten', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '设备投入使用时长', |
|||
dataIndex: 'length', |
|||
ellipsis: true, |
|||
render: (text, record) => { |
|||
const start = moment(record?.dateInstall); |
|||
const end = moment(); |
|||
const days = end.diff(start, 'days'); |
|||
|
|||
return days + '天' |
|||
} |
|||
|
|||
}, |
|||
{ |
|||
title: '操作', |
|||
width: 160, |
|||
key: 'option', |
|||
valueType: 'option', |
|||
render: (text, record) => { |
|||
const options = []; |
|||
options.push(<DeviceModal |
|||
triggerRender={<a>编辑</a>} |
|||
editData={record} |
|||
title="编辑设备" |
|||
onFinish={onFinish} |
|||
key="editModel" |
|||
/>) |
|||
|
|||
options.push( |
|||
<Popconfirm |
|||
key="del" |
|||
placement="top" |
|||
title="是否确认删除该设备?" |
|||
onConfirm={() => handleDelete(record.id)} |
|||
okText="是" |
|||
cancelText="否" |
|||
> |
|||
<a>删除</a> |
|||
</Popconfirm>) |
|||
|
|||
return options; |
|||
|
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
return <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'> DEVICE</span> |
|||
</div> |
|||
<div> |
|||
<DeviceModal |
|||
triggerRender={<Button type='primary'>新建</Button>} |
|||
title="新建设备" |
|||
onFinish={onFinish} |
|||
key="addModel" |
|||
/> |
|||
<Button type="primary" style={{ marginRight: 10, marginLeft: 10 }} onClick={() => { setShowImportModal(true) }}>批量新增</Button> |
|||
<Popconfirm title="确认删除?" onConfirm={() => { |
|||
rowSelected?.length > 0 ? handleDelete(rowSelected?.toString()) : message.warning('请先选择要删除的设备') |
|||
}}> |
|||
<Button>批量删除</Button> |
|||
</Popconfirm> |
|||
|
|||
<Input onChange={e => setName(e?.target?.value)} style={{ width: '13vw', marginLeft: 20, marginRight: 10 }} /> |
|||
<Button type="primary" onClick={() => { |
|||
setPageSize(10) |
|||
setCurrentPage(1) |
|||
queryData(true) |
|||
}}>查询</Button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<ProTable |
|||
columns={columns} |
|||
rowKey="id" |
|||
|
|||
dateFormatter="string" |
|||
scroll={ |
|||
{ |
|||
scrollToFirstRowOnChange: true, |
|||
y: clientHeight - 260 |
|||
} |
|||
} |
|||
pagination={{ |
|||
size: 'large', |
|||
total: devices?.count, |
|||
showSizeChanger: true, |
|||
// showQuickJumper: true,
|
|||
current: currentPage, |
|||
pageSize: pageSize || 10, |
|||
pageSizeOptions: [10, 20, 50], |
|||
showTotal: (total) => { |
|||
return <span style={{ fontSize: 15 }}>{`共${Math.ceil(total / pageSize)}页,${total}项`}</span> |
|||
}, |
|||
onShowSizeChange: (currentPage, pageSize) => { |
|||
setCurrentPage(currentPage); |
|||
setPageSize(pageSize); |
|||
}, |
|||
onChange: (page, pageSize) => { |
|||
setCurrentPage(page); |
|||
setPageSize(pageSize); |
|||
} |
|||
}} |
|||
dataSource={devices?.rows || []} |
|||
rowSelection={{ |
|||
selectedRowKeys: rowSelected, |
|||
onChange: (selectedRowKeys) => { |
|||
setRowSelected(selectedRowKeys); |
|||
}, |
|||
getCheckboxProps: (record) => { |
|||
return { |
|||
disabled: record.username === 'SuperAdmin', |
|||
} |
|||
} |
|||
}} |
|||
options={false} |
|||
search={false} |
|||
/> |
|||
{showImportModal && <ImportDeviceModal |
|||
devices={devices?.rows || []} |
|||
onCancel={() => { |
|||
setShowImportModal(false) |
|||
}} |
|||
onOk={() => { |
|||
setShowImportModal(false) |
|||
queryData() |
|||
}} |
|||
/>} |
|||
</Spin> |
|||
</div> |
|||
|
|||
|
|||
} |
|||
|
|||
function mapStateToProps(state) { |
|||
const { |
|||
auth, global, device |
|||
} = state; |
|||
return { |
|||
loading: device.isRequesting, |
|||
clientHeight: global.clientHeight, |
|||
actions: global.actions, |
|||
devices: device?.data || {} |
|||
}; |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(DeviceManagement); |
|||
|
|||
|
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
import DeviceManage from './deviceManage' |
|||
|
|||
export { DeviceManage }; |
@ -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: 'deviceManage', |
|||
name: '设备管理', |
|||
reducers: reducers, |
|||
routes: routes, |
|||
actions: actions, |
|||
getNavItem: getNavItem |
|||
}; |
@ -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="deviceManage"> |
|||
<Link to="/deviceManage">设备管理</Link> |
|||
</Menu.Item> |
|||
|
|||
); |
|||
} |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
export default { |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
'use strict'; |
|||
import { DeviceManage } from './containers'; |
|||
|
|||
export default [{ |
|||
type: 'inner', |
|||
route: { |
|||
path: '/deviceManage', |
|||
key: 'deviceManage', |
|||
breadcrumb: '设备管理', |
|||
component: DeviceManage, |
|||
|
|||
} |
|||
}]; |