@ -1,18 +1,25 @@ |
|||||
pipeline { |
podTemplate { |
||||
agent { |
node('pod-templ-jenkins-slave-common') { |
||||
node{ |
|
||||
label 'jnlp-slave' |
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 { |
container('image-builder') { |
||||
stage('巡检 api ......') { |
sh''' |
||||
steps { |
pwd |
||||
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
ls -al |
||||
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|
||||
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./api' |
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./api/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
||||
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' |
|
||||
|
''' |
||||
} |
} |
||||
|
|
||||
|
buildName "${IMAGE_NAME_SHORT}:${IMAGE_VERSION}" |
||||
|
buildDescription "${IMAGE_NAME}:${IMAGE_VERSION}" |
||||
} |
} |
||||
} |
} |
||||
} |
} |
@ -1,18 +1,25 @@ |
|||||
pipeline { |
podTemplate { |
||||
agent { |
node('pod-templ-jenkins-slave-common') { |
||||
node{ |
|
||||
label 'jnlp-slave' |
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 { |
container('image-builder') { |
||||
stage('巡检 web ......') { |
sh''' |
||||
steps { |
pwd |
||||
buildName "#${BUILD_NUMBER} ~/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
ls -al |
||||
buildDescription "harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}" |
|
||||
sh 'nerdctl build -t harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION} ./web' |
/kaniko/executor --context=${BUILD_WORKSPACE} --dockerfile=./web/Dockerfile --destination=${IMAGE_NAME}:${IMAGE_VERSION} --cache=false --cleanup |
||||
sh 'nerdctl push harbor.anxinyun.cn/smartcity/${JOB_NAME}:${IMAGE_VERSION}' |
|
||||
|
''' |
||||
} |
} |
||||
|
|
||||
|
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", |
"navigationBarTextStyle": "white", |
||||
"navigationBarTitleText": "故障日历", |
"navigationBarTitleText": "故障风险管理", |
||||
"enablePullDownRefresh": false, |
"enablePullDownRefresh": false, |
||||
"usingComponents": { |
"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--> |
<!--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 */ |
/* 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, |
||||
|
|
||||
|
} |
||||
|
}]; |