Files
threeonecheck_web/pages/Inspectionresult/Inspectionresult.vue
2026-06-30 09:47:10 +08:00

517 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page padding">
<view class="progress-bar" v-if="checkData">
<view class="progress-text">
<text class="current-index"> {{ checkData.currentIndex || 1 }} 个问题</text>
<text class="total-count"> / {{ checkData.totalCount || 1 }} </text>
</view>
<view class="progress-line">
<view class="progress-inner" :style="{ width: progressPercent + '%' }"></view>
</view>
</view>
<view class="padding bg-white radius">
<view class="text-bold">{{ checkData?.name || '加载中...' }}</view>
<view class="margin-top">
<rich-text :nodes="checkData?.point || ''"></rich-text>
</view>
<view class="margin-top">
<u-radio-group
v-model="radiovalue1"
placement="row"
@change="groupChange"
>
<u-radio
:customStyle="{marginBottom: '8px'}"
v-for="(item, index) in radiolist1"
:key="index"
:label="item.name"
:name="item.name"
@change="radioChange"
>
</u-radio>
</u-radio-group>
</view>
<view v-if="radiovalue1 === '异常'" class="hazard-section margin-top">
<view class="hazard-tip">
<text class="cuIcon-warn text-yellow margin-right-xs"></text>
<text class="text-orange">检查结果为异常需填写隐患信息</text>
</view>
<view v-if="!hasHazardData" class="hazard-btn" @click="openHazardPopup">
<text class="text-blue">填写隐患信息</text>
</view>
<view v-else class="hazard-card">
<view class="card-header">
<view class="text-bold text-black">{{ hazardPayload.formData.title }}</view>
<view class="level-tag" :class="{
'level-minor': hazardPayload.formData.level === 0,
'level-normal': hazardPayload.formData.level === 1,
'level-major': hazardPayload.formData.level === 2
}">{{ LEVEL_OPTIONS[hazardPayload.formData.level]?.title }}</view>
</view>
<view class="card-body">
<view class="info-row">
<text class="text-gray">检查形式</text>
<text>{{ SOURCE_OPTIONS[hazardPayload.formData.source]?.title || '-' }}</text>
</view>
<view class="info-row">
<text class="text-gray">隐患来源</text>
<text>{{ hazardPayload.formData.hazardSourceName || '-' }}</text>
</view>
<view class="info-row">
<text class="text-gray">隐患位置</text>
<text>{{ hazardPayload.address || '-' }}</text>
</view>
<view class="info-row">
<text class="text-gray">隐患描述</text>
<text class="description-text">{{ hazardPayload.formData.description || '-' }}</text>
</view>
<view class="info-row" v-if="hazardPayload.fileList.length > 0">
<text class="text-gray">附件</text>
<text>{{ hazardPayload.fileList.length }}个文件</text>
</view>
</view>
<view class="card-footer">
<button class="btn-edit" @click="editHazard">修改</button>
<button class="btn-clear" @click="clearHazard">清除</button>
</view>
</view>
</view>
<view class="margin-top">
<up-textarea v-model="value1" placeholder="请输入备注信息" ></up-textarea>
</view>
</view>
<button class="bg-blue round margin-top-xl" @click="handleSubmit">提交</button>
<HazardFormPopup
ref="hazardFormRef"
v-model:show="showHazardPopup"
v-model="hazardPayload"
title="填写隐患信息"
mode="collect"
body-height="60vh"
:show-draft-banner="showRestoreBanner"
@confirm="onHazardConfirm"
@clear-draft="clearDraft(true)"
/>
</view>
</template>
<script setup>
import { ref, reactive, computed, nextTick } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { enterCheckPlan, submitCheckResult, addHiddenDanger } from '@/request/api.js';
import HazardFormPopup from '@/components/hazard/HazardFormPopup.vue';
import { buildDraftKey, DRAFT_NS } from '@/utils/draftCache.js';
import { useDraftCache } from '@/utils/useDraftCache.js';
import {
LEVEL_OPTIONS,
SOURCE_OPTIONS,
createEmptyHazardPayload,
buildHiddenDangerParams,
hasValidHazardPayload
} from '@/components/hazard';
const oneTableId = ref('');
const checkData = ref(null);
const hazardFormRef = ref(null);
const showHazardPopup = ref(false);
const hazardPayload = ref(createEmptyHazardPayload());
const radiolist1 = reactive([
{ name: '正常', value: 1, disabled: false },
{ name: '异常', value: 2, disabled: false },
{ name: '不涉及', value: 3, disabled: false }
]);
const radiovalue1 = ref('');
const value1 = ref('');
const normalizeInspectionDraft = (data) => {
if (!data) return { radiovalue1: '', value1: '', hazardPayload: createEmptyHazardPayload() };
if (!data.hazardPayload && data.hazardFormData) {
data.hazardPayload = {
formData: data.hazardFormData,
address: data.hazardAddress || '',
lng: data.hazardLng || 0,
lat: data.hazardLat || 0,
areaId: '',
areaName: '',
fileList: data.hazardFileList || []
};
}
return {
radiovalue1: data.radiovalue1 || '',
value1: data.value1 || '',
hazardPayload: data.hazardPayload || createEmptyHazardPayload()
};
};
const inspectionDraftHasContent = (data) => {
const normalized = normalizeInspectionDraft(data);
const payload = normalized.hazardPayload;
return !!(
normalized.radiovalue1 ||
normalized.value1 ||
payload.formData?.title ||
payload.formData?.description ||
(payload.fileList && payload.fileList.length > 0)
);
};
const {
showRestoreBanner,
clear: clearDraft,
restore: restoreDraft,
bindAutoSave
} = useDraftCache({
getKey: () => buildDraftKey(
DRAFT_NS.INSPECTION_RESULT,
oneTableId.value,
checkData.value?.currentIndex
),
getPayload: () => ({
radiovalue1: radiovalue1.value,
value1: value1.value,
hazardPayload: JSON.parse(JSON.stringify(hazardPayload.value))
}),
hasContent: inspectionDraftHasContent,
applyPayload: (data) => {
const normalized = normalizeInspectionDraft(data);
radiovalue1.value = normalized.radiovalue1;
value1.value = normalized.value1;
hazardPayload.value = normalized.hazardPayload;
nextTick(() => {
hazardFormRef.value?.applyPayload(normalized.hazardPayload);
});
},
clearForm: () => {
radiovalue1.value = '';
value1.value = '';
clearHazard();
},
canSave: () => !!oneTableId.value,
requireInitialized: true
});
bindAutoSave(() => [radiovalue1.value, value1.value, hazardPayload.value]);
const groupChange = (n) => {
if (n !== '异常') {
clearHazard();
}
};
const radioChange = (n) => {
console.log('radioChange', n);
};
const hasHazardData = computed(() => hasValidHazardPayload(hazardPayload.value));
const progressPercent = computed(() => {
if (!checkData.value) return 0;
const current = checkData.value.currentIndex || 1;
const total = checkData.value.totalCount || 1;
return Math.round((current / total) * 100);
});
const openHazardPopup = () => {
showHazardPopup.value = true;
};
const editHazard = () => {
showHazardPopup.value = true;
};
const onHazardConfirm = (payload) => {
if (payload) {
hazardPayload.value = JSON.parse(JSON.stringify(payload));
}
};
const clearHazard = () => {
hazardPayload.value = createEmptyHazardPayload();
hazardFormRef.value?.resetForm();
};
const handleSubmit = async () => {
if (!radiovalue1.value) {
uni.showToast({ title: '请选择检查结果', icon: 'none' });
return;
}
const selectedItem = radiolist1.find(item => item.name === radiovalue1.value);
const resultValue = selectedItem ? selectedItem.value : null;
if (!resultValue) {
uni.showToast({ title: '请选择检查结果', icon: 'none' });
return;
}
if (radiovalue1.value === '异常' && !hasHazardData.value) {
uni.showToast({ title: '请填写隐患信息', icon: 'none' });
return;
}
try {
uni.showLoading({ title: '提交中...' });
if (radiovalue1.value === '异常' && hasHazardData.value) {
const payload = hazardFormRef.value?.getPayload() || hazardPayload.value;
const hazardParams = buildHiddenDangerParams(payload, {
taskId: checkData.value?.taskId,
checkPointId: checkData.value?.checkPointId
});
const hazardRes = await addHiddenDanger(hazardParams);
if (hazardRes.code !== 0) {
uni.hideLoading();
uni.showToast({ title: hazardRes.msg || '新增隐患失败', icon: 'none' });
return;
}
}
const submitParams = {
taskId: checkData.value?.taskId,
result: resultValue,
remark: value1.value
};
const res = await submitCheckResult(submitParams);
uni.hideLoading();
if (res.code === 0) {
clearDraft(false);
if (res.data && res.data.allFinished === true) {
uni.showToast({ title: '全部检查已完成', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else if (res.data && res.data.nextTask) {
uni.showToast({ title: '提交成功,进入下一题', icon: 'success' });
resetFormState();
checkData.value = res.data.nextTask;
} else {
uni.showToast({ title: '提交成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
} else {
uni.showToast({ title: res.msg || '提交失败', icon: 'none' });
}
} catch (error) {
uni.hideLoading();
console.error('提交失败:', error);
uni.showToast({ title: '提交失败', icon: 'none' });
}
};
const resetFormState = () => {
radiovalue1.value = '';
value1.value = '';
clearHazard();
};
const getCheckData = async () => {
try {
const res = await enterCheckPlan(oneTableId.value);
if (res.code === 0) {
checkData.value = res.data;
restoreDraft();
}
} catch (error) {
console.error(error);
}
};
onLoad((options) => {
if (options.id) {
oneTableId.value = options.id;
getCheckData();
}
});
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #EBF2FC;
}
.progress-bar {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.progress-text {
display: flex;
align-items: baseline;
margin-bottom: 16rpx;
.current-index {
font-size: 32rpx;
font-weight: bold;
color: #2667E9;
}
.total-count {
font-size: 28rpx;
color: #999;
}
}
.progress-line {
height: 12rpx;
background: #E5E5E5;
border-radius: 6rpx;
overflow: hidden;
}
.progress-inner {
height: 100%;
background: linear-gradient(90deg, #2667E9, #5B9BFF);
border-radius: 6rpx;
transition: width 0.3s ease;
}
.hazard-section {
border-top: 1rpx solid #eee;
padding-top: 20rpx;
}
.hazard-tip {
display: flex;
align-items: center;
padding: 16rpx 20rpx;
background: #FFF7E6;
border: 1rpx solid #FFE7BA;
border-radius: 8rpx;
margin-bottom: 20rpx;
.text-orange {
color: #FA8C16;
font-size: 26rpx;
}
}
.hazard-btn {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
border: 2rpx dashed #2667E9;
border-radius: 12rpx;
background: #F5F9FF;
.text-blue {
color: #2667E9;
font-size: 28rpx;
}
}
.hazard-card {
background: #F5F9FF;
border: 1rpx solid #D6E4FF;
border-radius: 12rpx;
overflow: hidden;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #E8E8E8;
background: #fff;
}
.card-body {
padding: 20rpx;
.info-row {
display: flex;
margin-bottom: 12rpx;
font-size: 26rpx;
&:last-child {
margin-bottom: 0;
}
.text-gray {
flex-shrink: 0;
color: #999;
}
.description-text {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.card-footer {
display: flex;
border-top: 1rpx solid #E8E8E8;
background: #fff;
button {
flex: 1;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
border-radius: 0;
&::after {
border: none;
}
}
.btn-edit {
background: #fff;
color: #2667E9;
border-right: 1rpx solid #E8E8E8;
}
.btn-clear {
background: #fff;
color: #F56C6C;
}
}
}
.level-tag {
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
}
.level-minor {
background: #F6FFED;
border: 2rpx solid #B7EB8F;
color: #52C41A;
}
.level-normal {
background: #FFF7E6;
border: 2rpx solid #FFD591;
color: #FA8C16;
}
.level-major {
background: #FFF1F0;
border: 2rpx solid #FFA39E;
color: #F5222D;
}
</style>