<template>
|
<view class="inspection-upload-page">
|
<!-- 页面头部 -->
|
<PageHeader title="巡检上传" @back="goBack"/>
|
|
<!-- 数据列表 -->
|
<view class="table-section">
|
<!-- 生产巡检列表 -->
|
<view class="task-list">
|
<view
|
v-for="(item, index) in taskTableData"
|
:key="index"
|
class="task-item"
|
@click="handleAdd(item)"
|
>
|
<view class="task-header">
|
<view class="task-info">
|
<text class="task-name">{{ item.taskName }}</text>
|
<text class="task-location">{{ item.inspectionLocation }}</text>
|
</view>
|
<view class="task-actions">
|
<u-button
|
type="primary"
|
size="small"
|
@click.stop="handleAdd(item)"
|
:customStyle="{
|
borderRadius: '15px',
|
height: '30px',
|
fontSize: '12px'
|
}"
|
>
|
上传
|
</u-button>
|
<u-button
|
type="info"
|
size="small"
|
@click.stop="startScanForTask(item)"
|
:customStyle="{
|
borderRadius: '15px',
|
height: '30px',
|
fontSize: '12px'
|
}"
|
>
|
扫码
|
</u-button>
|
</view>
|
</view>
|
<view class="task-details">
|
<view class="detail-item">
|
<text class="detail-label">备注</text>
|
<text class="detail-value">{{ item.remarks || '无' }}</text>
|
</view>
|
<view class="detail-item">
|
<text class="detail-label">执行人</text>
|
<text class="detail-value">{{ item.inspector }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 空状态 -->
|
<view v-if="taskTableData.length === 0" class="no-data">
|
<text>暂无数据</text>
|
</view>
|
</view>
|
|
<!-- 扫码区域 - 全局弹窗 -->
|
<view v-if="isScanning" class="qr-scan-overlay">
|
<view class="qr-scan-container">
|
<view class="scan-header">
|
<text class="scan-title">扫描二维码</text>
|
<u-button
|
type="error"
|
size="small"
|
@click.stop="stopScan"
|
:customStyle="{
|
borderRadius: '15px',
|
height: '30px',
|
fontSize: '12px'
|
}"
|
>
|
关闭
|
</u-button>
|
</view>
|
<camera
|
class="qr-camera"
|
device-position="back"
|
flash="off"
|
@scancode="handleScanCode"
|
@error="handleCameraError"
|
></camera>
|
<view class="scan-frame-wrapper">
|
<view class="scan-frame"></view>
|
<view class="scan-tip">请将二维码放入框内</view>
|
</view>
|
<u-alert
|
v-if="cameraError"
|
:title="cameraError"
|
type="error"
|
:showIcon="true"
|
:closable="true"
|
@close="cameraError = ''"
|
:customStyle="{
|
margin: '10px 0'
|
}"
|
></u-alert>
|
</view>
|
</view>
|
|
<!-- 弹窗组件 -->
|
<form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
|
</view>
|
</template>
|
|
<script setup>
|
import { onMounted, onUnmounted, ref, nextTick } from 'vue'
|
import { onShow } from '@dcloudio/uni-app'
|
import PageHeader from '@/components/PageHeader.vue'
|
import FormDia from './components/formDia.vue'
|
import { getLedgerById } from '@/api/equipmentManagement/ledger.js'
|
import {inspectionTaskList} from "@/api/inspectionManagement";
|
|
// 组件引用
|
const formDia = ref()
|
|
// 加载提示方法
|
const showLoadingToast = (message) => {
|
uni.showLoading({
|
title: message,
|
mask: true
|
})
|
}
|
const closeToast = () => {
|
uni.hideLoading()
|
}
|
|
// 表格数据
|
const taskTableData = ref([]) // 生产巡检数据
|
|
// 当前扫描的任务
|
const currentScanningTask = ref(null)
|
|
// 请求取消标志,用于取消正在进行的请求
|
let isRequestCancelled = false
|
|
// 扫码相关状态
|
const isScanning = ref(false)
|
const cameraError = ref('')
|
|
// 生命周期
|
onMounted(() => {
|
// 延迟初始化,确保DOM已渲染
|
nextTick(() => {
|
getList()
|
})
|
})
|
|
onShow(() => {
|
// 页面显示时刷新数据
|
getList()
|
})
|
|
// 组件销毁时的清理
|
onUnmounted(() => {
|
// 设置取消标志,阻止后续的异步操作
|
isRequestCancelled = true
|
|
// 停止扫码
|
if (isScanning.value) {
|
isScanning.value = false
|
}
|
})
|
|
// 返回上一页
|
const goBack = () => {
|
uni.navigateBack()
|
}
|
|
// 查询数据
|
const handleQuery = () => {
|
getList()
|
}
|
|
// 获取列表数据
|
const getList = () => {
|
// 显示加载提示
|
showLoadingToast('加载中...')
|
|
// 设置取消标志
|
isRequestCancelled = false
|
|
inspectionTaskList({}).then(res => {
|
// 检查组件是否还存在且请求未被取消
|
if (!isRequestCancelled) {
|
console.log('生产巡检API返回数据:', res);
|
|
// 处理不同的数据结构
|
let records = [];
|
if (res && res.data) {
|
// 尝试多种可能的数据结构
|
if (Array.isArray(res.data.records)) {
|
records = res.data.records;
|
} else if (Array.isArray(res.data.rows)) {
|
records = res.data.rows;
|
} else if (Array.isArray(res.data)) {
|
records = res.data;
|
} else if (Array.isArray(res.data.list)) {
|
records = res.data.list;
|
}
|
}
|
|
if (records.length > 0) {
|
taskTableData.value = records;
|
console.log('生产巡检数据设置成功,记录数:', records.length);
|
} else {
|
console.warn('生产巡检数据为空或格式不正确:', res);
|
taskTableData.value = [];
|
uni.showToast({
|
title: '暂无巡检任务数据',
|
icon: 'none'
|
});
|
}
|
}
|
// 关闭加载提示
|
closeToast()
|
}).catch(err => {
|
// 检查组件是否还存在且请求未被取消
|
if (!isRequestCancelled) {
|
console.error('获取生产巡检数据失败:', err);
|
taskTableData.value = [];
|
// 添加错误提示
|
uni.showToast({
|
title: '获取数据失败',
|
icon: 'error'
|
})
|
}
|
// 关闭加载提示
|
closeToast()
|
})
|
}
|
|
|
// 上传
|
const handleAdd = (row) => {
|
nextTick(() => {
|
// 检查组件是否还存在
|
if (formDia.value && formDia.value.openDialog) {
|
formDia.value.openDialog(row)
|
} else {
|
console.error('上传组件引用不存在')
|
uni.showToast({
|
title: '组件未准备好',
|
icon: 'error'
|
})
|
}
|
})
|
}
|
|
// 为指定任务开始扫码
|
const startScanForTask = async (task) => {
|
try {
|
// 记录当前扫描的任务
|
currentScanningTask.value = task
|
console.log('为任务开始扫码:', task.taskName)
|
|
// 显示扫描界面
|
isScanning.value = true
|
|
// 使用uniapp的扫码API
|
uni.scanCode({
|
success: (res) => {
|
console.log('扫码成功:', res)
|
handleScanSuccess(res)
|
},
|
fail: (err) => {
|
console.error('扫码失败:', err)
|
uni.showToast({
|
title: '扫码失败',
|
icon: 'error'
|
})
|
// 关闭扫描界面
|
isScanning.value = false
|
},
|
complete: () => {
|
// 扫码完成后关闭扫描界面
|
setTimeout(() => {
|
isScanning.value = false
|
}, 1000)
|
}
|
})
|
} catch (e) {
|
console.error('启动扫码失败:', e)
|
uni.showToast({
|
title: '启动扫码失败',
|
icon: 'error'
|
})
|
isScanning.value = false
|
}
|
}
|
|
// 停止扫码
|
const stopScan = () => {
|
isScanning.value = false
|
currentScanningTask.value = null
|
}
|
|
// 扫码成功处理
|
const handleScanSuccess = async (result) => {
|
try {
|
console.log('处理扫码结果:', result)
|
console.log('当前关联任务:', currentScanningTask.value?.taskName)
|
|
uni.showToast({
|
title: '识别成功',
|
icon: 'success'
|
})
|
|
// 解析二维码数据
|
let qrData
|
let deviceId = ''
|
|
try {
|
qrData = JSON.parse(result.result)
|
console.log('解析的二维码数据:', qrData)
|
deviceId = qrData.deviceId || qrData.qrCodeId
|
} catch (e) {
|
// 如果不是JSON格式,尝试从URL中提取deviceId
|
const url = result.result
|
|
if (url.includes('deviceId=')) {
|
// 从URL中提取deviceId
|
const match = url.match(/deviceId=(\d+)/)
|
if (match && match[1]) {
|
deviceId = match[1]
|
}
|
}
|
|
qrData = {
|
deviceName: deviceId ? `设备${deviceId}` : result.result,
|
location: '',
|
qrCodeId: deviceId // 使用提取的deviceId或原始结果
|
}
|
}
|
|
// 如果有设备ID,尝试从API获取真实的设备名称
|
if (deviceId) {
|
try {
|
console.log('正在查询设备信息,设备ID:', deviceId)
|
const response = await getLedgerById(deviceId)
|
console.log('设备信息查询结果:', response)
|
|
if (response.code === 200 && response.data) {
|
qrData.deviceName = response.data.deviceName || `设备${deviceId}`
|
qrData.location = response.data.storageLocation || ''
|
console.log('获取到设备名称:', qrData.deviceName)
|
} else {
|
console.warn('设备信息查询失败,使用默认名称')
|
qrData.deviceName = qrData.deviceName || `设备${deviceId}`
|
}
|
} catch (apiError) {
|
console.error('查询设备信息失败:', apiError)
|
// API调用失败时使用默认名称
|
qrData.deviceName = qrData.deviceName || `设备${deviceId}`
|
}
|
}
|
|
// 确保数据完整性
|
if (!qrData.deviceName) {
|
qrData.deviceName = result.result
|
}
|
if (!qrData.qrCodeId) {
|
qrData.qrCodeId = deviceId || result.result
|
}
|
|
// 将扫码数据与任务关联
|
if (currentScanningTask.value) {
|
// 关联任务信息
|
const taskData = {
|
...currentScanningTask.value,
|
qrCodeData: qrData
|
}
|
|
// 打开上传弹窗,传递关联后的任务数据
|
nextTick(() => {
|
if (formDia.value && formDia.value.openDialog) {
|
formDia.value.openDialog(taskData)
|
} else {
|
console.error('上传组件引用不存在')
|
uni.showToast({
|
title: '组件未准备好',
|
icon: 'error'
|
})
|
}
|
})
|
}
|
} catch (error) {
|
console.error('处理扫码结果失败:', error)
|
uni.showToast({
|
title: error.message || '数据解析失败',
|
icon: 'error'
|
})
|
} finally {
|
// 关闭扫描界面
|
isScanning.value = false
|
}
|
}
|
|
// 摄像头错误处理
|
const handleCameraError = (error) => {
|
console.error('摄像头错误:', error)
|
cameraError.value = '摄像头访问失败,请检查权限设置'
|
}
|
</script>
|
|
<style scoped lang="scss">
|
// 导入销售模块公共样式
|
@import '@/styles/sales-common.scss';
|
|
// 页面容器样式
|
.inspection-upload-page {
|
min-height: 100vh;
|
background: #f8f9fa;
|
position: relative;
|
}
|
|
// 列表容器样式
|
.table-section {
|
padding: 20px;
|
}
|
|
// 任务列表样式 - 使用销售台账的样式规范
|
.task-list {
|
.task-item {
|
background: #ffffff;
|
border-radius: 12px;
|
margin-bottom: 16px;
|
overflow: hidden;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
padding: 0 16px;
|
|
&:active {
|
transform: scale(0.98);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
}
|
}
|
}
|
|
// 项目头部样式
|
.task-header {
|
padding: 16px 0;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 0;
|
}
|
|
.task-info {
|
flex: 1;
|
}
|
|
.task-name {
|
font-size: 14px;
|
color: #333;
|
font-weight: 500;
|
margin-bottom: 0;
|
line-height: 1.4;
|
}
|
|
.task-location {
|
font-size: 12px;
|
color: #666;
|
margin-top: 4px;
|
}
|
|
// 任务操作按钮样式
|
.task-actions {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
margin-left: 0;
|
}
|
|
// 任务详情样式 - 使用销售台账的详情行样式
|
.task-details {
|
padding: 16px 0;
|
|
.detail-item {
|
display: flex;
|
align-items: flex-end;
|
justify-content: space-between;
|
margin-bottom: 8px;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.detail-label {
|
font-size: 12px;
|
color: #777777;
|
min-width: 60px;
|
flex-shrink: 0;
|
}
|
|
.detail-value {
|
font-size: 12px;
|
color: #000000;
|
text-align: right;
|
flex: 1;
|
margin-left: 16px;
|
line-height: 1.4;
|
}
|
}
|
}
|
|
// 无数据提示样式 - 使用销售台账的样式
|
.no-data {
|
padding: 40px 0;
|
text-align: center;
|
color: #999;
|
background: none;
|
margin: 0;
|
}
|
|
.no-data text {
|
font-size: 14px;
|
color: #999;
|
}
|
|
/* 扫码弹窗样式 */
|
.qr-scan-overlay {
|
position: fixed;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background-color: rgba(0, 0, 0, 0.8);
|
z-index: 9999;
|
display: flex;
|
flex-direction: column;
|
justify-content: center;
|
align-items: center;
|
padding: 20px;
|
}
|
|
.qr-scan-container {
|
width: 100%;
|
max-width: 400px;
|
background-color: #000;
|
border-radius: 12px;
|
overflow: hidden;
|
}
|
|
.scan-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 15px;
|
background-color: rgba(0, 0, 0, 0.7);
|
}
|
|
.scan-title {
|
font-size: 18px;
|
font-weight: 600;
|
color: #fff;
|
}
|
|
.qr-camera {
|
width: 100%;
|
height: 400px;
|
}
|
|
.scan-frame-wrapper {
|
position: relative;
|
width: 100%;
|
height: 300px;
|
}
|
|
.scan-frame {
|
position: absolute;
|
top: 50%;
|
left: 50%;
|
transform: translate(-50%, -50%);
|
width: 80%;
|
height: 80%;
|
border: 3px solid #1890ff;
|
border-radius: 8px;
|
box-shadow: 0 0 20px rgba(24, 144, 255, 0.3);
|
animation: pulse 2s infinite;
|
}
|
|
.scan-tip {
|
position: absolute;
|
bottom: 10px;
|
left: 50%;
|
transform: translateX(-50%);
|
color: #fff;
|
font-size: 14px;
|
text-align: center;
|
background-color: rgba(0, 0, 0, 0.6);
|
padding: 5px 15px;
|
border-radius: 20px;
|
}
|
|
@keyframes pulse {
|
0% { opacity: 0.8; }
|
50% { opacity: 0.4; }
|
100% { opacity: 0.8; }
|
}
|
|
</style>
|