增加巡检内容

This commit is contained in:
xuli
2025-11-20 17:45:41 +08:00
parent a6cc5ff885
commit 003d2d0797
42 changed files with 5957 additions and 1405 deletions

View File

@@ -0,0 +1,603 @@
<template>
<view class="con-body">
<view class="con-bg">
<!-- 头部 -->
<customHeader ref="customHeaderRef" title="巡检详情"
:leftFlag="true" :rightFlag="true"
>
<template #right>
<view class="head-right" @click="handleQuestion">
<view class="btn-yellow">
<img :src="'static/images/polling/icon-repair.png'" class="img-repair" />新建问题上报
</view>
</view>
</template>
</customHeader>
<!-- 高度来避免头部遮挡 -->
<view class="top-height"></view>
<!-- 1 单选 2多选 3判断 4 问答 5 读卡 6 扫码 7 拍照 8 视频 9 定位暂时去掉了 -->
<view class="white-bg">
<view class="blue-title">{{optionObj.optionName}}</view>
<view class="blue-sub-title" style="margin-bottom:20rpx;">
巡检日期<text>{{parseTime(optionObj.planTime,'{y}-{m}-{d} 星期{a}')}}</text>
</view>
<view class="report-list" v-for="(item, index) in optionObj.list" :key="index">
<!-- 单选 or 判断 -->
<block v-if="item.pointType==1||item.pointType==3">
<view class="r-list">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<view class="r-right">
<radio-group @change="radioChange" style="transform:scale(0.9)">
<radio value="1" color="#02C74C" style="margin-right:30rpx;" ></radio>
<radio value="2" color="#02C74C"></radio>
</radio-group>
</view>
</view>
</block>
<!-- 多选 -->
<block v-else-if="item.pointType==2">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<multipleSelect :multiple="true" :value="chooseList" downInner
:options="checkList" @change="changeCheck"
:slabel="'text'"
>
</multipleSelect>
</block>
<!-- 问答 -->
<block v-else-if="item.pointType==4">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<view class="r-input">
<!-- <input class="uni-input" placeholder="请输入" placeholder-class="place-input" /> -->
<textarea class="textarea" v-model="item.result" auto-height placeholder="请输入" placeholder-class="place-input"></textarea>
</view>
</block>
<!-- 读卡 -->
<block v-else-if="item.pointType==5">
<view class="r-list">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<!-- #ifdef APP-PLUS -->
<view class="r-right" @click="initNFC">
<img :src="'static/images/polling/icon-NFCcode.png'" class="img-nfc" /> 开始识别
</view>
<!-- #endif -->
</view>
</block>
<!-- 扫码 -->
<block v-else-if="item.pointType==6">
<view class="r-list">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<!-- #ifdef APP-PLUS -->
<view class="r-right r-active" v-if="scanStr" @click="handleScan">
<img :src="'static/images/polling/icon-QRcode-b.png'" class="img-nfc" />
<view>扫码成功<view class="r-font">点击再次扫码</view></view>
</view>
<view class="r-right" v-else @click="handleScan">
<img :src="'static/images/polling/icon-QRcode.png'" class="img-nfc" /> 开始扫码
</view>
<!-- #endif -->
</view>
</block>
<!-- 拍照 -->
<block v-else-if="item.pointType==7">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<view class="img-flex">
<view class="img-show" v-for="(item,index) in imgArr" :key="index">
<img :src="item" />
</view>
<view class="img-con" @click="chooseImage">
<img :src="'static/images/polling/icon-AddPic.png'" class="img-pic" />
<view>添加照片</view>
</view>
</view>
</block>
<!-- 视频 -->
<block v-else-if="item.pointType==8">
<view class="r-left">
<view>{{String(index+1).padStart(2, '0')+'.'}}</view>
<view>{{ item.pointName }}</view>
</view>
<view class="img-flex">
<!-- v-for="(item,index) in videoArr" :key="index" -->
<view class="img-show" v-for="(item,index) in videoArr" :key="index">
<!-- @error="videoErrorCallback"
:danmu-list="danmuList" -->
<!-- <video id="myVideo" :src="videoArr"
enable-danmu danmu-btn controls
></video> -->
<video :src="item" controls></video>
</view>
<view class="img-con" @click="chooseVideo">
<img :src="'static/images/polling/icon-AddVideo.png'" class="img-pic" />
<view>添加视频</view>
</view>
</view>
</block>
<view class="report-border" :style="{borderColor:index<optionObj.list.length-1?'#E7E7E7':'#fff'}"></view>
</view>
<view class="btn-submit">
<button type="primary" @click="handleSubmit">提交</button>
</view>
</view>
</view>
<!-- 弹窗提示 -->
<pollingShowModal :visible="visible"
:title="title"
:content="content"
:subContent="subContent"
:btnFlag2="btnFlag2"
:btnTxt="btnTxt"
:isGreen="isGreen"
:subTime="subTime"
@close="handleClose"
@confirm="handleConfirm"
ref="showModel"
></pollingShowModal>
</view>
</template>
<script setup>
import { ref,onMounted,onUnmounted,nextTick,computed,reactive } from 'vue'
import { onLoad,onHide} from '@dcloudio/uni-app';
import customHeader from '@/components/customHeader.vue';
import MescrollUni from 'mescroll-uni/mescroll-uni.vue';
import multipleSelect from "@/components/multipleSelect.vue";
import pollingShowModal from "@/components/pollingShowModal.vue";
import { parseTime } from '@/utils/datetime.js';
import { formatTaskStatus } from '@/utils/status.js';
import { noticeList } from '@/api/notice.js'
import {uploadFileMinio} from '@/utils/minio.js'
let workId = ref(undefined);
let checkList=[
{ value: 0, text: "答案1" },
{ value: 1, text: "答案2" },
{ value: 2, text: "答案3" },
{ value: 3, text: "答案4" },
{ value: 4, text: "答案5" },
{ value: 4, text: "答案6" }
];
onLoad(option => {
// console.log(option)
workId.value = option.id;
getList();
})
// 查询列表
let list = ref([]);
let optionObj = ref({})
// 获取数据列表
const getList = () => {
// let res = await noticeList();
// 1 单选 2多选 3判断 4问答 5 读卡 6 扫码 7 拍照 8 视频 9 定位
let res = {
"code": 200,
"msg": "操作成功",
"data": {
optionName:'配电箱箱体和内部线路检查',
planTime:new Date().getTime(),
list:[
{ pointName:'技术中心机房总电源', optionId:202512297899, pointType:1, },
{ pointName:'监控室消防设备阀门正常开启闭合', optionId:202512297899, pointType:2, },
{ pointName:'监控室10组灭火器压力指针处于绿色区域', optionId:202512297899, pointType:3,},
{ pointName:'库房灭火器压力指针处于绿色区', optionId:202512297899,pointType:4,},
{ pointName:'技术中心机房总电源', optionId:202512297899,pointType:5,},
{ pointName:'监控室消防设备阀门正常监控室消防设备阀门正常监控室消防设备阀门正常', optionId:202512297899,pointType:6,},
{ pointName:'库房灭火器压力指针处于绿色区', optionId:202512297899,pointType:7,},
{ pointName:'技术中心机房总电源', optionId:202512297899,pointType:8,},
],
}
}
optionObj.value = res.data||{};
}
// 跳转问题上报页面
const handleQuestion=()=>{
uni.navigateTo({
url: '/pages/business/polling/problemReport?id='+workId.value
});
}
// radio 单选选择
const radioChange=(e)=>{
console.log(e)
}
// 多选处理
let chooseList = reactive([0]);
const changeCheck = (item, value) => {
chooseList = item;
}
// 照片及拍照处理
const imgArr = ref([]);
const imgArr2=ref([]);
const chooseImage = () => {
imgArr.value = [];
uni.chooseImage({
// count: 1, // 默认是9这里设置为1次只选1张
sizeType: ['compressed'], // 可以指定是原图还是压缩图,可选 'original' 或 'compressed'
sourceType: ['album', 'camera'], // 指定来源是相册还是相机,默认二者都有
success: async (res) => {
// 选择成功,返回本地文件路径列表
let files = res.tempFilePaths;
// console.log('图片临时路径:', files, res.tempFiles);
// 显示本地图片
imgArr.value.push(...res.tempFilePaths)
// 执行上传
uploadFileMinio(res.tempFiles,'/polling',files).then(res=>{
console.log("上传成功后图片路径====>",res)
imgArr2.value = res;
})
},
fail: (err) => {
console.log('选择图片失败', err);
}
});
};
// 视频处理
const videoSrc = ref('');
const videoArr = ref([]);
const chooseVideo = () => {
uni.chooseVideo({
sourceType: ['album', 'camera'], // 来源:相册和相机
maxDuration: 60, // 最大时长(秒)
camera: 'back', // 使用后置摄像头
compressed: true, // 压缩视频
success: (res) => {
videoSrc.value=res.tempFilePath;
videoArr.value.push(res.tempFilePath)
console.log('视频路径:', res.tempFilePath);
console.log('视频时长:', res.duration);
console.log('视频大小:', res.size);
},
fail: (err) => {
console.error('选择视频失败:', err);
}
});
};
// nfc 处理
let discoveryCallback = null
const initNFC = () => {
// 确保在 App 环境
if (typeof plus === 'undefined') {
showModel('此功能仅支持 App 端')
return
}
// 检查设备是否支持NFC
if (!plus.nfc) {
showModel('设备不支持 NFC')
return;
}
if (!plus.nfc.isSupportNFC()) {
showModel('设备不支持 NFC')
return;
}
// 检查NFC是否开启
plus.nfc.isNFCEnabled((enabled) => {
if (enabled) {
startDiscovery()
} else {
showModel('请先开启设备NFC功能')
}
},(error) => {
console.error('检查NFC状态失败:'+error.message);
showModel('检查NFC状态失败')
})
}
// 执行NFC扫描
const startDiscovery = () => {
// 开始监听NFC标签
plus.nfc.startDiscovery((res) => {
console.log('NFC监听启动成功')
// 监听NFC标签被发现的事件
discoveryCallback = (result) => {
try {
console.log("NFC监听返回=>",result)
// 尝试读取标签ID或NDEF数据
const tag = result.tag
if (tag && tag.id) {
// 将标签ID字节数组转换为十六进制字符串
const tagId = tag.id.map(b => b.toString(16).padStart(2, '0')).join(':')
console.log(`检测到标签ID: ${tagId}`)
// 此处可以处理tag.ndefMessage等数据
} else {
console.log( `检测到标签,但无法解析数据`)
}
} catch (error) {
console.log( '处理NFC数据时出错' + error.message)
}
}
plus.nfc.addEventListener('discovered', discoveryCallback, false)
},(err) => {
console.log( 'NFC监听启动失败' + err.message)
})
}
// 扫二维码
const scanStr = ref(undefined);
const handleScan = () => {
// 调用扫码API
uni.scanCode({
success: (res) => {
scanStr.value = res.result;
console.log('扫码结果:', res.result); // 二维码内容
console.log('码类型:', res.scanType); // 码类型,如 QR_CODE
// 处理扫码结果,例如跳转页面
// 如果是URL可以跳转
// if (res.result.startsWith('http')) {
// uni.navigateTo({
// url: '/pages/webview/webview?url=' + encodeURIComponent(res.result)
// });
// } else {
// // 其他内容,如文本,可以展示或处理
// uni.showModal({
// content: `扫描到内容:${res.result}`,
// showCancel: false
// });
// }
},
fail: (err) => {
console.error('扫码失败:', err);
}
});
}
// 提交
const handleSubmit=()=>{
showModel('有未完成的巡检项');
// showModel2('此巡检项已完成');
}
// 自定义弹窗
let title = ref('');
let content = ref('');
let visible = ref(false);
let subContent= ref('');
let subTime= ref(undefined);
let btnFlag2=ref(true)
let btnTxt=ref('')
let isGreen =ref(false);
const showModel=(str)=>{
visible.value = true;
content.value = str;
subContent.value='是否确认提交?';
btnTxt.value='提交'
btnFlag2.value=true;
isGreen.value = false;
subTime.value=undefined;
}
const showModel2=(str)=>{
visible.value = true;
content.value = str;
subContent.value='提交时间';
btnTxt.value='确定';
btnFlag2.value=false;
isGreen.value = true;
subTime.value = new Date().getTime();
}
//关闭
const handleClose=()=>{
visible.value = false;
}
// 执行提交
const handleConfirm=()=>{
if(isGreen.value){
visible.value = false;
}
}
// 组件卸载时停止监听
onUnmounted(() => {
// #ifdef APP-PLUS
if (discoveryCallback) {
plus.nfc.removeEventListener('discovered', discoveryCallback)
}
plus.nfc.stopDiscovery?.()
// #endif
})
</script>
<style scoped>
.head-right{}
.head-right .btn-yellow{
background-color: #FCC123;
color: #4C2D11;
font-size: 28rpx;
font-weight: bold;
border-radius: 10rpx;
padding:10rpx 25rpx;
display: flex;
align-items: center;
}
.head-right .btn-yellow .img-repair{
width: 30rpx;
height: 30rpx;
}
.week{
padding:0 40rpx;
color: #fff;
font-size:32rpx;
margin-bottom:20rpx;
}
.white-bg{
width: 680rpx;
padding: 15rpx 30rpx 40rpx 40rpx;
margin-bottom: 0;
border-radius: 8px 8px 0 0;
}
.white-bg2{
/* padding:0 40rpx; */
margin-top:-30rpx;
}
.report-list{
position: relative;
}
.report-border{
border-bottom:1px solid #E7E7E7;
width:710rpx;
height: 1px;
margin:20rpx 0;
}
.report-list .r-list{
padding: 5rpx 0;
}
.report-list .r-left{
display: flex;
/* align-items: center; */
width:70%;
font-size:28rpx;
}
.report-list .r-left view:first-child{
margin-right: 10rpx;
}
.report-list .r-right{
display: flex;
font-size:30rpx;
align-items: center;
justify-content: center;
}
.report-list .r-right.r-active{
color:#05A3F4
}
.report-list .r-active .r-font{
color:#919191;
font-size:18rpx;
text-align: center;
}
.report-list .r-input{
border:1px solid #E7E7E7;
border-radius: 10rpx;
width:620rpx;
padding:15rpx 25rpx;
margin:20rpx 0 30rpx;
}
.report-list .place-input{
color:#BFBFBF;
font-size:28rpx;
}
.report-list .textarea{
min-height: 50rpx;
}
.r-right :deep(.uni-radio-input){
width:36rpx;
height: 36rpx;
}
.r-right .uni-input{
width:100%;
}
.report-list :deep(.uni-select-dc){
margin:20rpx 0 30rpx;
}
.report-list :deep(.uni-select-dc .uni-select-dc-select){
color:#BFBFBF;
font-size:28rpx;
}
.img-nfc{
width:50rpx;
height: 40rpx;
margin-right:10rpx;
}
.report-list .img-flex{
display: flex;
flex-flow:row wrap;
margin-bottom:30rpx;
}
.report-list .img-show img,
.report-list .img-show video{
width:210rpx;
height: 140rpx;
}
.report-list .img-show :deep(.uni-video-cover-play-button){
width:64rpx;
height: 64rpx;
line-height: 64rpx;
font-size:60rpx;
}
.report-list .img-con{
background-color: #EEEEEE;
width:210rpx;
height: 140rpx;
text-align: center;
color:#919191;
font-size: 24rpx;
}
.report-list .img-con .img-pic{
width:70rpx;
height: 50rpx;
margin-top:30rpx;
}
.report-list .img-show,
.report-list .img-con{
width: calc(100% / 3 - 10px); /* 减去一些间隙以避免溢出 */
margin:20rpx 20rpx 0 0;
}
.bg-border{
width:750rpx;
height:20rpx;
background-color: #F5F5F5;
margin-left:-40rpx;
margin-top:40rpx;
}
.report-list .r-list .r-name{
width:525rpx
}
.r-left .r-l-left{
width:280rpx;
}
.r-left .r-l-right{
}
.no-data .no-pic{
display: block;
width:440rpx;
height:210rpx;
margin:40rpx auto;
}
:deep(.mescroll-upwarp){
display: none !important;
}
.btn-submit{
width:400rpx;
margin:0 auto;
}
.btn-submit uni-button[type='primary']{
background-color: #05A3F4;
border-radius: 40rpx;
line-height: 2.2;
}
.btn-submit uni-button:after{
border-color:#05A3F4;
}
</style>