| | |
| | | <template> |
| | | <view class="inspection-detail"> |
| | | <!-- 使用通用页面头部组件 --> |
| | | <PageHeader title="设备巡检详情" @back="goBack" /> |
| | | |
| | | <PageHeader title="设备巡检详情" |
| | | @back="goBack" /> |
| | | <!-- 设备信息卡片 --> |
| | | <view class="device-card"> |
| | | <view class="device-header"> |
| | | <view class="device-icon"> |
| | | <up-icon name="settings" size="24" color="#1890ff"></up-icon> |
| | | <up-icon name="settings" |
| | | size="24" |
| | | color="#1890ff"></up-icon> |
| | | </view> |
| | | <view class="device-info"> |
| | | <text class="device-name">{{ deviceInfo.deviceName }}</text> |
| | | <text class="device-code">{{ deviceInfo.deviceCode }}</text> |
| | | </view> |
| | | <view class="qr-scan" @click="scanDeviceQR"> |
| | | <up-icon name="scan" size="20" color="#1890ff"></up-icon> |
| | | <view class="qr-scan" |
| | | @click="scanDeviceQR"> |
| | | <up-icon name="scan" |
| | | size="20" |
| | | color="#1890ff"></up-icon> |
| | | <text class="scan-text">扫码</text> |
| | | </view> |
| | | </view> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 巡检项目清单 --> |
| | | <view class="inspection-items"> |
| | | <view class="section-title"> |
| | | <up-icon name="list" size="18" color="#333"></up-icon> |
| | | <up-icon name="list" |
| | | size="18" |
| | | color="#333"></up-icon> |
| | | <text class="title-text">巡检项目清单</text> |
| | | <text class="progress-text">({{ completedItems }}/{{ totalItems }})</text> |
| | | </view> |
| | | |
| | | <view class="items-list"> |
| | | <view |
| | | v-for="(item, index) in inspectionItems" |
| | | :key="index" |
| | | class="inspection-item" |
| | | :class="{ 'completed': item.completed, 'abnormal': item.isAbnormal }" |
| | | > |
| | | <view class="item-header" @click="toggleItem(index)"> |
| | | <view v-for="(item, index) in inspectionItems" |
| | | :key="index" |
| | | class="inspection-item" |
| | | :class="{ 'completed': item.completed, 'abnormal': item.isAbnormal }"> |
| | | <view class="item-header" |
| | | @click="toggleItem(index)"> |
| | | <view class="item-left"> |
| | | <view class="checkbox" :class="{ 'checked': item.completed }"> |
| | | <up-icon v-if="item.completed" name="checkmark" size="14" color="#ffffff"></up-icon> |
| | | <view class="checkbox" |
| | | :class="{ 'checked': item.completed }"> |
| | | <up-icon v-if="item.completed" |
| | | name="checkmark" |
| | | size="14" |
| | | color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-name">{{ item.name }}</text> |
| | | </view> |
| | | <view class="item-status"> |
| | | <u-tag v-if="item.isAbnormal" type="error" size="mini">异常</u-tag> |
| | | <u-tag v-else-if="item.completed" type="success" size="mini">正常</u-tag> |
| | | <u-tag v-else type="info" size="mini">待检</u-tag> |
| | | <u-tag v-if="item.isAbnormal" |
| | | type="error" |
| | | size="mini">异常</u-tag> |
| | | <u-tag v-else-if="item.completed" |
| | | type="success" |
| | | size="mini">正常</u-tag> |
| | | <u-tag v-else |
| | | type="info" |
| | | size="mini">待检</u-tag> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 展开的详情内容 --> |
| | | <view v-if="item.expanded" class="item-content"> |
| | | <view v-if="item.expanded" |
| | | class="item-content"> |
| | | <view class="item-description"> |
| | | <text class="desc-text">{{ item.description }}</text> |
| | | </view> |
| | | |
| | | <!-- 巡检结果选择 --> |
| | | <view class="result-section"> |
| | | <text class="section-label">巡检结果:</text> |
| | | <view class="result-options"> |
| | | <u-radio-group v-model="item.result" @change="onResultChange(index, $event)"> |
| | | <u-radio |
| | | v-for="option in resultOptions" |
| | | :key="option.value" |
| | | :label="option.value" |
| | | :name="option.label" |
| | | size="small" |
| | | > |
| | | <u-radio-group v-model="item.result" |
| | | @change="onResultChange(index, $event)"> |
| | | <u-radio v-for="option in resultOptions" |
| | | :key="option.value" |
| | | :label="option.value" |
| | | :name="option.label" |
| | | size="small"> |
| | | {{ option.label }} |
| | | </u-radio> |
| | | </u-radio-group> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 异常情况描述 --> |
| | | <view v-if="item.result === 'abnormal'" class="abnormal-section"> |
| | | <view v-if="item.result === 'abnormal'" |
| | | class="abnormal-section"> |
| | | <text class="section-label">异常描述:</text> |
| | | <up-textarea |
| | | v-model="item.abnormalDesc" |
| | | placeholder="请详细描述异常情况" |
| | | :maxlength="200" |
| | | count |
| | | height="80" |
| | | ></up-textarea> |
| | | <up-textarea v-model="item.abnormalDesc" |
| | | placeholder="请详细描述异常情况" |
| | | :maxlength="200" |
| | | count |
| | | height="80"></up-textarea> |
| | | </view> |
| | | |
| | | <!-- 图片上传 --> |
| | | <view class="upload-section"> |
| | | <text class="section-label">现场照片:</text> |
| | | <up-upload |
| | | :fileList="item.images" |
| | | @afterRead="(event) => afterRead(event, index, 'images')" |
| | | @delete="(event) => deleteFile(event, index, 'images')" |
| | | name="images" |
| | | multiple |
| | | :maxCount="5" |
| | | :previewImage="true" |
| | | > |
| | | <up-upload :fileList="item.images" |
| | | @afterRead="(event) => afterRead(event, index, 'images')" |
| | | @delete="(event) => deleteFile(event, index, 'images')" |
| | | name="images" |
| | | multiple |
| | | :maxCount="5" |
| | | :previewImage="true"> |
| | | <view class="upload-btn"> |
| | | <up-icon name="camera" size="20" color="#999"></up-icon> |
| | | <up-icon name="camera" |
| | | size="20" |
| | | color="#999"></up-icon> |
| | | <text class="upload-text">添加照片</text> |
| | | </view> |
| | | </up-upload> |
| | | </view> |
| | | |
| | | <!-- 视频上传 --> |
| | | <view class="upload-section"> |
| | | <text class="section-label">现场视频:</text> |
| | | <up-upload |
| | | :fileList="item.videos" |
| | | @afterRead="(event) => afterRead(event, index, 'videos')" |
| | | @delete="(event) => deleteFile(event, index, 'videos')" |
| | | name="videos" |
| | | :maxCount="2" |
| | | accept="video" |
| | | > |
| | | <up-upload :fileList="item.videos" |
| | | @afterRead="(event) => afterRead(event, index, 'videos')" |
| | | @delete="(event) => deleteFile(event, index, 'videos')" |
| | | name="videos" |
| | | :maxCount="2" |
| | | accept="video"> |
| | | <view class="upload-btn"> |
| | | <up-icon name="play-circle" size="20" color="#999"></up-icon> |
| | | <up-icon name="play-circle" |
| | | size="20" |
| | | color="#999"></up-icon> |
| | | <text class="upload-text">添加视频</text> |
| | | </view> |
| | | </up-upload> |
| | | </view> |
| | | |
| | | <!-- 备注 --> |
| | | <view class="remark-section"> |
| | | <text class="section-label">备注:</text> |
| | | <up-textarea |
| | | v-model="item.remark" |
| | | placeholder="请输入备注信息(可选)" |
| | | :maxlength="100" |
| | | count |
| | | height="60" |
| | | ></up-textarea> |
| | | <up-textarea v-model="item.remark" |
| | | placeholder="请输入备注信息(可选)" |
| | | :maxlength="100" |
| | | count |
| | | height="60"></up-textarea> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 底部操作按钮 --> |
| | | <view class="bottom-actions"> |
| | | <u-button |
| | | type="primary" |
| | | size="large" |
| | | :disabled="!canSubmit" |
| | | @click="submitInspection" |
| | | :loading="submitting" |
| | | > |
| | | <u-button type="primary" |
| | | size="large" |
| | | :disabled="!canSubmit" |
| | | @click="submitInspection" |
| | | :loading="submitting"> |
| | | {{ allCompleted ? '提交巡检记录' : `继续巡检 (${completedItems}/${totalItems})` }} |
| | | </u-button> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { submitInspectionRecord } from '@/api/equipmentManagement/inspection' |
| | | import dayjs from 'dayjs' |
| | | import { ref, computed, onMounted } from "vue"; |
| | | import { onShow } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import { submitInspectionRecord } from "@/api/equipmentManagement/inspection"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // 设备信息 |
| | | const deviceInfo = ref({}) |
| | | // 设备信息 |
| | | const deviceInfo = ref({}); |
| | | |
| | | // 巡检项目列表 |
| | | const inspectionItems = ref([]) |
| | | // 巡检项目列表 |
| | | const inspectionItems = ref([]); |
| | | |
| | | // 提交状态 |
| | | const submitting = ref(false) |
| | | // 提交状态 |
| | | const submitting = ref(false); |
| | | |
| | | // 巡检结果选项 |
| | | const resultOptions = [ |
| | | { label: '正常', value: 'normal' }, |
| | | { label: '异常', value: 'abnormal' } |
| | | ] |
| | | // 巡检结果选项 |
| | | const resultOptions = [ |
| | | { label: "正常", value: "normal" }, |
| | | { label: "异常", value: "abnormal" }, |
| | | ]; |
| | | |
| | | // 显示提示信息 |
| | | const showToast = (message) => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: 'none' |
| | | }) |
| | | } |
| | | // 显示提示信息 |
| | | const showToast = message => { |
| | | uni.showToast({ |
| | | title: message, |
| | | icon: "none", |
| | | }); |
| | | }; |
| | | |
| | | // 计算属性 |
| | | const totalItems = computed(() => inspectionItems.value.length) |
| | | const completedItems = computed(() => inspectionItems.value.filter(item => item.completed).length) |
| | | const allCompleted = computed(() => completedItems.value === totalItems.value && totalItems.value > 0) |
| | | const canSubmit = computed(() => completedItems.value > 0) |
| | | // 计算属性 |
| | | const totalItems = computed(() => inspectionItems.value.length); |
| | | const completedItems = computed( |
| | | () => inspectionItems.value.filter(item => item.completed).length |
| | | ); |
| | | const allCompleted = computed( |
| | | () => completedItems.value === totalItems.value && totalItems.value > 0 |
| | | ); |
| | | const canSubmit = computed(() => completedItems.value > 0); |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | if (completedItems.value > 0) { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: '当前有未保存的巡检记录,确定要离开吗?', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | uni.navigateBack() |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | if (completedItems.value > 0) { |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "当前有未保存的巡检记录,确定要离开吗?", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | uni.navigateBack(); |
| | | } |
| | | }, |
| | | }); |
| | | } else { |
| | | uni.navigateBack(); |
| | | } |
| | | }; |
| | | |
| | | // 扫描设备二维码 |
| | | const scanDeviceQR = () => { |
| | | uni.scanCode({ |
| | | success: res => { |
| | | console.log("扫码结果:", res); |
| | | if (res.result.includes(deviceInfo.value.deviceCode)) { |
| | | showToast("设备确认成功"); |
| | | // 记录扫码时间 |
| | | deviceInfo.value.scanTime = new Date().toISOString(); |
| | | } else { |
| | | showToast("设备二维码不匹配"); |
| | | } |
| | | }, |
| | | fail: err => { |
| | | console.log("扫码失败:", err); |
| | | showToast("扫码失败"); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 切换巡检项目 |
| | | const toggleItem = index => { |
| | | inspectionItems.value[index].expanded = |
| | | !inspectionItems.value[index].expanded; |
| | | }; |
| | | |
| | | // 巡检结果改变 |
| | | const onResultChange = (index, value) => { |
| | | const item = inspectionItems.value[index]; |
| | | item.result = value; |
| | | item.completed = true; |
| | | item.isAbnormal = value === "abnormal"; |
| | | |
| | | // 如果选择正常,清空异常描述 |
| | | if (value === "normal") { |
| | | item.abnormalDesc = ""; |
| | | } |
| | | }; |
| | | |
| | | // 文件上传后处理 |
| | | const afterRead = async (event, index, type) => { |
| | | const { file } = event; |
| | | const item = inspectionItems.value[index]; |
| | | |
| | | // 模拟上传过程 |
| | | uni.showLoading({ title: "上传中..." }); |
| | | |
| | | try { |
| | | // 这里应该调用实际的上传API |
| | | await new Promise(resolve => setTimeout(resolve, 1000)); |
| | | |
| | | // 添加到对应的文件列表 |
| | | if (type === "images") { |
| | | item.images = item.images || []; |
| | | item.images.push({ |
| | | url: file.url, |
| | | name: file.name, |
| | | size: file.size, |
| | | }); |
| | | } else if (type === "videos") { |
| | | item.videos = item.videos || []; |
| | | item.videos.push({ |
| | | url: file.url, |
| | | name: file.name, |
| | | size: file.size, |
| | | }); |
| | | } |
| | | }) |
| | | } else { |
| | | uni.navigateBack() |
| | | } |
| | | } |
| | | |
| | | // 扫描设备二维码 |
| | | const scanDeviceQR = () => { |
| | | uni.scanCode({ |
| | | success: (res) => { |
| | | console.log('扫码结果:', res) |
| | | if (res.result.includes(deviceInfo.value.deviceCode)) { |
| | | showToast('设备确认成功') |
| | | // 记录扫码时间 |
| | | deviceInfo.value.scanTime = new Date().toISOString() |
| | | } else { |
| | | showToast('设备二维码不匹配') |
| | | uni.hideLoading(); |
| | | showToast("上传成功"); |
| | | } catch (error) { |
| | | uni.hideLoading(); |
| | | showToast("上传失败"); |
| | | } |
| | | }; |
| | | |
| | | // 删除文件 |
| | | const deleteFile = (event, index, type) => { |
| | | const item = inspectionItems.value[index]; |
| | | if (type === "images") { |
| | | item.images.splice(event.index, 1); |
| | | } else if (type === "videos") { |
| | | item.videos.splice(event.index, 1); |
| | | } |
| | | }; |
| | | |
| | | // 提交巡检记录 |
| | | const submitInspection = async () => { |
| | | if (!canSubmit.value) { |
| | | showToast("请至少完成一项巡检"); |
| | | return; |
| | | } |
| | | |
| | | // 检查异常项目是否填写了描述 |
| | | const abnormalItems = inspectionItems.value.filter(item => item.isAbnormal); |
| | | for (const item of abnormalItems) { |
| | | if (!item.abnormalDesc || item.abnormalDesc.trim() === "") { |
| | | showToast(`请填写"${item.name}"的异常描述`); |
| | | return; |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.log('扫码失败:', err) |
| | | showToast('扫码失败') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 切换巡检项目 |
| | | const toggleItem = (index) => { |
| | | inspectionItems.value[index].expanded = !inspectionItems.value[index].expanded |
| | | } |
| | | submitting.value = true; |
| | | |
| | | // 巡检结果改变 |
| | | const onResultChange = (index, value) => { |
| | | const item = inspectionItems.value[index] |
| | | item.result = value |
| | | item.completed = true |
| | | item.isAbnormal = value === 'abnormal' |
| | | |
| | | // 如果选择正常,清空异常描述 |
| | | if (value === 'normal') { |
| | | item.abnormalDesc = '' |
| | | } |
| | | } |
| | | try { |
| | | const recordData = { |
| | | deviceId: deviceInfo.value.id, |
| | | deviceCode: deviceInfo.value.deviceCode, |
| | | inspectionDate: dayjs().format("YYYY-MM-DD"), |
| | | inspector: deviceInfo.value.inspector, |
| | | scanTime: deviceInfo.value.scanTime, |
| | | items: inspectionItems.value.map(item => ({ |
| | | name: item.name, |
| | | result: item.result, |
| | | completed: item.completed, |
| | | isAbnormal: item.isAbnormal, |
| | | abnormalDesc: item.abnormalDesc, |
| | | images: item.images || [], |
| | | videos: item.videos || [], |
| | | remark: item.remark, |
| | | })), |
| | | completedAt: new Date().toISOString(), |
| | | }; |
| | | |
| | | // 文件上传后处理 |
| | | const afterRead = async (event, index, type) => { |
| | | const { file } = event |
| | | const item = inspectionItems.value[index] |
| | | |
| | | // 模拟上传过程 |
| | | uni.showLoading({ title: '上传中...' }) |
| | | |
| | | try { |
| | | // 这里应该调用实际的上传API |
| | | await new Promise(resolve => setTimeout(resolve, 1000)) |
| | | |
| | | // 添加到对应的文件列表 |
| | | if (type === 'images') { |
| | | item.images = item.images || [] |
| | | item.images.push({ |
| | | url: file.url, |
| | | name: file.name, |
| | | size: file.size |
| | | }) |
| | | } else if (type === 'videos') { |
| | | item.videos = item.videos || [] |
| | | item.videos.push({ |
| | | url: file.url, |
| | | name: file.name, |
| | | size: file.size |
| | | }) |
| | | // 模拟API调用 |
| | | await new Promise(resolve => setTimeout(resolve, 2000)); |
| | | |
| | | // 实际API调用 |
| | | // await submitInspectionRecord(recordData) |
| | | |
| | | showToast("巡检记录提交成功"); |
| | | |
| | | // 返回列表页面 |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } catch (error) { |
| | | showToast("提交失败,请重试"); |
| | | } finally { |
| | | submitting.value = false; |
| | | } |
| | | |
| | | uni.hideLoading() |
| | | showToast('上传成功') |
| | | } catch (error) { |
| | | uni.hideLoading() |
| | | showToast('上传失败') |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 删除文件 |
| | | const deleteFile = (event, index, type) => { |
| | | const item = inspectionItems.value[index] |
| | | if (type === 'images') { |
| | | item.images.splice(event.index, 1) |
| | | } else if (type === 'videos') { |
| | | item.videos.splice(event.index, 1) |
| | | } |
| | | } |
| | | |
| | | // 提交巡检记录 |
| | | const submitInspection = async () => { |
| | | if (!canSubmit.value) { |
| | | showToast('请至少完成一项巡检') |
| | | return |
| | | } |
| | | |
| | | // 检查异常项目是否填写了描述 |
| | | const abnormalItems = inspectionItems.value.filter(item => item.isAbnormal) |
| | | for (const item of abnormalItems) { |
| | | if (!item.abnormalDesc || item.abnormalDesc.trim() === '') { |
| | | showToast(`请填写"${item.name}"的异常描述`) |
| | | return |
| | | // 初始化数据 |
| | | const initData = () => { |
| | | // 从存储中获取当前巡检信息 |
| | | const currentInspection = uni.getStorageSync("currentInspection"); |
| | | if (currentInspection) { |
| | | deviceInfo.value = currentInspection; |
| | | } |
| | | } |
| | | |
| | | submitting.value = true |
| | | |
| | | try { |
| | | const recordData = { |
| | | deviceId: deviceInfo.value.id, |
| | | deviceCode: deviceInfo.value.deviceCode, |
| | | inspectionDate: dayjs().format('YYYY-MM-DD'), |
| | | inspector: deviceInfo.value.inspector, |
| | | scanTime: deviceInfo.value.scanTime, |
| | | items: inspectionItems.value.map(item => ({ |
| | | name: item.name, |
| | | result: item.result, |
| | | completed: item.completed, |
| | | isAbnormal: item.isAbnormal, |
| | | abnormalDesc: item.abnormalDesc, |
| | | images: item.images || [], |
| | | videos: item.videos || [], |
| | | remark: item.remark |
| | | })), |
| | | completedAt: new Date().toISOString() |
| | | } |
| | | |
| | | // 模拟API调用 |
| | | await new Promise(resolve => setTimeout(resolve, 2000)) |
| | | |
| | | // 实际API调用 |
| | | // await submitInspectionRecord(recordData) |
| | | |
| | | showToast('巡检记录提交成功') |
| | | |
| | | // 返回列表页面 |
| | | setTimeout(() => { |
| | | uni.navigateBack() |
| | | }, 1500) |
| | | |
| | | } catch (error) { |
| | | showToast('提交失败,请重试') |
| | | } finally { |
| | | submitting.value = false |
| | | } |
| | | } |
| | | |
| | | // 初始化数据 |
| | | const initData = () => { |
| | | // 从存储中获取当前巡检信息 |
| | | const currentInspection = uni.getStorageSync('currentInspection') |
| | | if (currentInspection) { |
| | | deviceInfo.value = currentInspection |
| | | } |
| | | |
| | | // 模拟巡检项目数据 |
| | | inspectionItems.value = [ |
| | | { |
| | | name: '设备外观检查', |
| | | description: '检查设备外观是否有损坏、锈蚀、变形等异常情况', |
| | | completed: false, |
| | | expanded: false, |
| | | result: '', |
| | | isAbnormal: false, |
| | | abnormalDesc: '', |
| | | images: [], |
| | | videos: [], |
| | | remark: '' |
| | | }, |
| | | { |
| | | name: '运行状态检查', |
| | | description: '检查设备运行是否正常,有无异常声音、振动等', |
| | | completed: false, |
| | | expanded: false, |
| | | result: '', |
| | | isAbnormal: false, |
| | | abnormalDesc: '', |
| | | images: [], |
| | | videos: [], |
| | | remark: '' |
| | | }, |
| | | { |
| | | name: '安全装置检查', |
| | | description: '检查各类安全装置是否完好,安全标识是否清晰', |
| | | completed: false, |
| | | expanded: false, |
| | | result: '', |
| | | isAbnormal: false, |
| | | abnormalDesc: '', |
| | | images: [], |
| | | videos: [], |
| | | remark: '' |
| | | }, |
| | | { |
| | | name: '环境条件检查', |
| | | description: '检查设备周围环境是否符合要求,通风、照明等是否正常', |
| | | completed: false, |
| | | expanded: false, |
| | | result: '', |
| | | isAbnormal: false, |
| | | abnormalDesc: '', |
| | | images: [], |
| | | videos: [], |
| | | remark: '' |
| | | }, |
| | | { |
| | | name: '仪表读数记录', |
| | | description: '记录相关仪表的读数,检查是否在正常范围内', |
| | | completed: false, |
| | | expanded: false, |
| | | result: '', |
| | | isAbnormal: false, |
| | | abnormalDesc: '', |
| | | images: [], |
| | | videos: [], |
| | | remark: '' |
| | | } |
| | | ] |
| | | } |
| | | // 模拟巡检项目数据 |
| | | inspectionItems.value = [ |
| | | { |
| | | name: "设备外观检查", |
| | | description: "检查设备外观是否有损坏、锈蚀、变形等异常情况", |
| | | completed: false, |
| | | expanded: false, |
| | | result: "", |
| | | isAbnormal: false, |
| | | abnormalDesc: "", |
| | | images: [], |
| | | videos: [], |
| | | remark: "", |
| | | }, |
| | | { |
| | | name: "运行状态检查", |
| | | description: "检查设备运行是否正常,有无异常声音、振动等", |
| | | completed: false, |
| | | expanded: false, |
| | | result: "", |
| | | isAbnormal: false, |
| | | abnormalDesc: "", |
| | | images: [], |
| | | videos: [], |
| | | remark: "", |
| | | }, |
| | | { |
| | | name: "安全装置检查", |
| | | description: "检查各类安全装置是否完好,安全标识是否清晰", |
| | | completed: false, |
| | | expanded: false, |
| | | result: "", |
| | | isAbnormal: false, |
| | | abnormalDesc: "", |
| | | images: [], |
| | | videos: [], |
| | | remark: "", |
| | | }, |
| | | { |
| | | name: "环境条件检查", |
| | | description: "检查设备周围环境是否符合要求,通风、照明等是否正常", |
| | | completed: false, |
| | | expanded: false, |
| | | result: "", |
| | | isAbnormal: false, |
| | | abnormalDesc: "", |
| | | images: [], |
| | | videos: [], |
| | | remark: "", |
| | | }, |
| | | { |
| | | name: "仪表读数记录", |
| | | description: "记录相关仪表的读数,检查是否在正常范围内", |
| | | completed: false, |
| | | expanded: false, |
| | | result: "", |
| | | isAbnormal: false, |
| | | abnormalDesc: "", |
| | | images: [], |
| | | videos: [], |
| | | remark: "", |
| | | }, |
| | | ]; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | initData() |
| | | }) |
| | | onMounted(() => { |
| | | initData(); |
| | | }); |
| | | |
| | | onShow(() => { |
| | | // 页面显示时刷新数据 |
| | | }) |
| | | onShow(() => { |
| | | // 页面显示时刷新数据 |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .inspection-detail { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | min-height: 100vh; |
| | | padding-bottom: 80px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | height: 200px; |
| | | background: linear-gradient(135deg, rgba(102, 126, 234, 0.8) 0%, rgba(118, 75, 162, 0.8) 100%); |
| | | z-index: 0; |
| | | .inspection-detail { |
| | | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | | min-height: 100vh; |
| | | padding-bottom: 80px; |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | height: 200px; |
| | | background: linear-gradient( |
| | | 135deg, |
| | | rgba(102, 126, 234, 0.8) 0%, |
| | | rgba(118, 75, 162, 0.8) 100% |
| | | ); |
| | | z-index: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .device-card { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | margin: 10px 20px; |
| | | border-radius: 20px; |
| | | padding: 24px; |
| | | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | position: relative; |
| | | z-index: 1; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); |
| | | } |
| | | } |
| | | .device-card { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | margin: 10px 20px; |
| | | border-radius: 20px; |
| | | padding: 24px; |
| | | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | position: relative; |
| | | z-index: 1; |
| | | transition: all 0.3s ease; |
| | | |
| | | .device-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .device-icon { |
| | | width: 56px; |
| | | height: 56px; |
| | | background: linear-gradient(135deg, #667eea, #764ba2); |
| | | border-radius: 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: scale(1.05); |
| | | } |
| | | } |
| | | |
| | | .device-info { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .device-name { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .device-code { |
| | | font-size: 13px; |
| | | color: #8c8c8c; |
| | | font-weight: 500; |
| | | padding: 4px 12px; |
| | | background: rgba(140, 140, 140, 0.1); |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | width: fit-content; |
| | | } |
| | | |
| | | .qr-scan { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 12px 16px; |
| | | background: linear-gradient(135deg, #52c41a, #389e0d); |
| | | border-radius: 12px; |
| | | box-shadow: 0 4px 15px rgba(82, 196, 26, 0.3); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: scale(1.05); |
| | | box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4); |
| | | } |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | } |
| | | } |
| | | |
| | | .scan-text { |
| | | font-size: 13px; |
| | | color: #ffffff; |
| | | font-weight: 600; |
| | | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .device-details { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | background: rgba(248, 250, 252, 0.8); |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | backdrop-filter: blur(10px); |
| | | } |
| | | |
| | | .detail-item { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 14px; |
| | | padding: 8px 0; |
| | | transition: all 0.2s ease; |
| | | |
| | | &:hover { |
| | | background: rgba(255, 255, 255, 0.5); |
| | | margin: 0 -8px; |
| | | padding-left: 8px; |
| | | padding-right: 8px; |
| | | border-radius: 8px; |
| | | } |
| | | } |
| | | |
| | | .label { |
| | | color: #595959; |
| | | min-width: 80px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .value { |
| | | color: #262626; |
| | | flex: 1; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .inspection-items { |
| | | margin: 10px 20px; |
| | | position: relative; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .section-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 20px 0; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.2); |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | border-radius: 16px; |
| | | padding: 20px; |
| | | margin-bottom: 16px; |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .title-text { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | flex: 1; |
| | | } |
| | | |
| | | .progress-text { |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | background: linear-gradient(135deg, #667eea, #764ba2); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | background-clip: text; |
| | | } |
| | | |
| | | .items-list { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | border-radius: 20px; |
| | | overflow: hidden; |
| | | margin-top: 0; |
| | | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | .inspection-item { |
| | | border-bottom: 1px solid rgba(0, 0, 0, 0.06); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | &.completed { |
| | | background: rgba(82, 196, 26, 0.05); |
| | | border-left: 4px solid #52c41a; |
| | | } |
| | | |
| | | &.abnormal { |
| | | background: rgba(255, 77, 79, 0.05); |
| | | border-left: 4px solid #ff4d4f; |
| | | } |
| | | |
| | | &:hover { |
| | | background: rgba(102, 126, 234, 0.05); |
| | | } |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 15px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .checkbox { |
| | | width: 20px; |
| | | height: 20px; |
| | | border: 2px solid #d9d9d9; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | |
| | | &.checked { |
| | | background: #52c41a; |
| | | border-color: #52c41a; |
| | | } |
| | | } |
| | | |
| | | .item-name { |
| | | font-size: 15px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .item-status { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .item-content { |
| | | padding: 0 15px 20px; |
| | | border-top: 1px solid #f5f5f5; |
| | | } |
| | | |
| | | .item-description { |
| | | padding: 15px 0; |
| | | } |
| | | |
| | | .desc-text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .result-section, |
| | | .abnormal-section, |
| | | .upload-section, |
| | | .remark-section { |
| | | margin-top: 15px; |
| | | } |
| | | |
| | | .section-label { |
| | | display: block; |
| | | font-size: 14px; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .result-options { |
| | | display: flex; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .upload-btn { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 88px; |
| | | height: 88px; |
| | | border: 2px dashed rgba(102, 126, 234, 0.3); |
| | | border-radius: 16px; |
| | | background: rgba(102, 126, 234, 0.05); |
| | | gap: 8px; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | border-color: rgba(102, 126, 234, 0.5); |
| | | background: rgba(102, 126, 234, 0.1); |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2); |
| | | } |
| | | |
| | | &:active { |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 13px; |
| | | color: #667eea; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .bottom-actions { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(20px); |
| | | padding: 20px; |
| | | border-top: 1px solid rgba(255, 255, 255, 0.2); |
| | | box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.1); |
| | | z-index: 10; |
| | | |
| | | button { |
| | | height: 48px; |
| | | border-radius: 16px; |
| | | font-weight: 600; |
| | | font-size: 16px; |
| | | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | } |
| | | |
| | | .device-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .device-icon { |
| | | width: 56px; |
| | | height: 56px; |
| | | background: linear-gradient(135deg, #667eea, #764ba2); |
| | | border-radius: 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: scale(1.05); |
| | | } |
| | | } |
| | | |
| | | .device-info { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .device-name { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .device-code { |
| | | font-size: 13px; |
| | | color: #8c8c8c; |
| | | font-weight: 500; |
| | | padding: 4px 12px; |
| | | background: rgba(140, 140, 140, 0.1); |
| | | border-radius: 12px; |
| | | display: inline-block; |
| | | width: fit-content; |
| | | } |
| | | |
| | | .qr-scan { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 12px 16px; |
| | | background: linear-gradient(135deg, #52c41a, #389e0d); |
| | | border-radius: 12px; |
| | | box-shadow: 0 4px 15px rgba(82, 196, 26, 0.3); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: scale(1.05); |
| | | box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4); |
| | | } |
| | | |
| | | &:active { |
| | | transform: scale(0.98); |
| | | } |
| | | } |
| | | |
| | | .scan-text { |
| | | font-size: 13px; |
| | | color: #ffffff; |
| | | font-weight: 600; |
| | | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .device-details { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | background: rgba(248, 250, 252, 0.8); |
| | | border-radius: 16px; |
| | | padding: 16px; |
| | | backdrop-filter: blur(10px); |
| | | } |
| | | |
| | | .detail-item { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 14px; |
| | | padding: 8px 0; |
| | | transition: all 0.2s ease; |
| | | |
| | | &:hover { |
| | | background: rgba(255, 255, 255, 0.5); |
| | | margin: 0 -8px; |
| | | padding-left: 8px; |
| | | padding-right: 8px; |
| | | border-radius: 8px; |
| | | } |
| | | } |
| | | |
| | | .label { |
| | | color: #595959; |
| | | min-width: 80px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .value { |
| | | color: #262626; |
| | | flex: 1; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .inspection-items { |
| | | margin: 10px 20px; |
| | | position: relative; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .section-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 20px 0; |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.2); |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | border-radius: 16px; |
| | | padding: 20px; |
| | | margin-bottom: 16px; |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .title-text { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | flex: 1; |
| | | } |
| | | |
| | | .progress-text { |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | background: linear-gradient(135deg, #667eea, #764ba2); |
| | | -webkit-background-clip: text; |
| | | -webkit-text-fill-color: transparent; |
| | | background-clip: text; |
| | | } |
| | | |
| | | .items-list { |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(15px); |
| | | border-radius: 20px; |
| | | overflow: hidden; |
| | | margin-top: 0; |
| | | box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | } |
| | | |
| | | .inspection-item { |
| | | border-bottom: 1px solid rgba(0, 0, 0, 0.06); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | &.completed { |
| | | background: rgba(82, 196, 26, 0.05); |
| | | border-left: 4px solid #52c41a; |
| | | } |
| | | |
| | | &.abnormal { |
| | | background: rgba(255, 77, 79, 0.05); |
| | | border-left: 4px solid #ff4d4f; |
| | | } |
| | | |
| | | &:hover { |
| | | background: rgba(102, 126, 234, 0.05); |
| | | } |
| | | } |
| | | |
| | | .item-header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 15px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .item-left { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .checkbox { |
| | | width: 20px; |
| | | height: 20px; |
| | | border: 2px solid #d9d9d9; |
| | | border-radius: 4px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | |
| | | &.checked { |
| | | background: #52c41a; |
| | | border-color: #52c41a; |
| | | } |
| | | } |
| | | |
| | | .item-name { |
| | | font-size: 15px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .item-status { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .item-content { |
| | | padding: 0 15px 20px; |
| | | border-top: 1px solid #f5f5f5; |
| | | } |
| | | |
| | | .item-description { |
| | | padding: 15px 0; |
| | | } |
| | | |
| | | .desc-text { |
| | | font-size: 14px; |
| | | color: #666; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .result-section, |
| | | .abnormal-section, |
| | | .upload-section, |
| | | .remark-section { |
| | | margin-top: 15px; |
| | | } |
| | | |
| | | .section-label { |
| | | display: block; |
| | | font-size: 14px; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .result-options { |
| | | display: flex; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .upload-btn { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 88px; |
| | | height: 88px; |
| | | border: 2px dashed rgba(102, 126, 234, 0.3); |
| | | border-radius: 16px; |
| | | background: rgba(102, 126, 234, 0.05); |
| | | gap: 8px; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | border-color: rgba(102, 126, 234, 0.5); |
| | | background: rgba(102, 126, 234, 0.1); |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2); |
| | | } |
| | | |
| | | &:active { |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .upload-text { |
| | | font-size: 13px; |
| | | color: #667eea; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .bottom-actions { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background: rgba(255, 255, 255, 0.95); |
| | | backdrop-filter: blur(20px); |
| | | padding: 20px; |
| | | border-top: 1px solid rgba(255, 255, 255, 0.2); |
| | | box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.1); |
| | | z-index: 10; |
| | | |
| | | button { |
| | | height: 48px; |
| | | border-radius: 16px; |
| | | font-weight: 600; |
| | | font-size: 16px; |
| | | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); |
| | | |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | &:active { |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | } |
| | | </style> |