From d0d091b368490ed2f540ddc7a4d829b206a7dc05 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 09 三月 2026 11:29:29 +0800
Subject: [PATCH] 衡阳鹏创电子 1.部署app修改
---
src/pages/inspectionUpload/index.vue | 2670 ++++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 2,109 insertions(+), 561 deletions(-)
diff --git a/src/pages/inspectionUpload/index.vue b/src/pages/inspectionUpload/index.vue
index aed4e6e..c86f6bc 100644
--- a/src/pages/inspectionUpload/index.vue
+++ b/src/pages/inspectionUpload/index.vue
@@ -1,643 +1,2191 @@
<template>
<view class="inspection-upload-page">
<!-- 椤甸潰澶撮儴 -->
- <PageHeader title="宸℃涓婁紶" />
-
- <!-- 鏍囩椤� -->
- <view class="tabs-container">
- <view class="custom-tabs">
- <view
- v-for="(tab, index) in tabs"
- :key="index"
- class="tab-item"
- :class="{ 'tab-active': currentTabIndex === index }"
- @click="handleTabChange(index)"
- >
- {{ tab.name }}
- </view>
- <view class="tab-line" :style="{ left: currentTabIndex * 50 + '%' }"></view>
- </view>
- </view>
-
- <!-- 鎵爜妯″潡 -->
- <view v-if="activeTab === 'qrCode'" class="scan-section">
- <view class="scan-controls">
- <u-button
- :type="isScanning ? 'error' : 'primary'"
- :loading="scanLoading"
- @click="toggleScan"
- >
- {{ scanButtonText }}
- </u-button>
- </view>
-
- <!-- 鎵爜鍖哄煙 -->
- <view v-show="isScanning" class="qr-scan-container">
- <camera
- class="qr-camera"
- device-position="back"
- flash="off"
- @scancode="handleScanCode"
- @error="handleCameraError"
- ></camera>
- <view class="scan-overlay">
- <view class="scan-frame"></view>
- <view class="scan-tip">璇峰皢浜岀淮鐮佹斁鍏ユ鍐�</view>
- </view>
- </view>
-
- <!-- 鐘舵�佹彁绀� -->
- <view class="status-info">
- <u-alert
- v-if="cameraError"
- :title="cameraError"
- type="error"
- :showIcon="true"
- :closable="true"
- @close="cameraError = ''"
- ></u-alert>
- <view v-if="isScanning" class="scanning-text">
- <u-loading-icon mode="circle" color="#1890ff" size="20"></u-loading-icon>
- <text class="scanning-label">姝e湪鎵弿浜岀淮鐮�...</text>
- </view>
- </view>
- </view>
-
+ <PageHeader title="璁惧宸℃"
+ @back="goBack" />
<!-- 鏁版嵁鍒楄〃 -->
<view class="table-section">
<!-- 鐢熶骇宸℃鍒楄〃 -->
- <view v-if="activeTab === 'task'" class="task-list">
- <view
- v-for="(item, index) in tableData"
- :key="index"
- class="task-item"
- @click="handleAdd(item)"
- >
+ <view class="task-list">
+ <view v-for="(item, index) in taskTableData"
+ :key="index"
+ class="task-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 type="primary"
+ size="small"
+ @click.stop="startScanForTask(item)"
+ :customStyle="{
+ borderRadius: '15px',
+ height: '30px',
+ fontSize: '12px',
+ marginRight: '8px'
+ }">
+ 鎵爜涓婁紶
+ </u-button>
+ <u-button type="success"
+ size="small"
+ @click.stop="viewAttachments(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-label">浠诲姟ID</text>
+ <text class="detail-value">{{ item.taskId || item.id }}</text>
+ </view>
+ <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-label">鎵ц浜�</text>
<text class="detail-value">{{ item.inspector }}</text>
</view>
- </view>
- </view>
- </view>
-
- <!-- 鐜板満宸℃鍒楄〃 -->
- <view v-if="activeTab === 'qrCode'" class="qr-list">
- <view
- v-for="(item, index) in tableData"
- :key="index"
- class="qr-item"
- @click="viewFile(item)"
- >
- <view class="qr-header">
- <view class="qr-info">
- <text class="device-name">{{ item.qrCode?.deviceName }}</text>
- <text class="device-location">{{ item.qrCode?.location }}</text>
- </view>
- <view class="qr-actions">
- <u-button
- type="primary"
- size="small"
- @click.stop="viewFile(item)"
- :customStyle="{
- borderRadius: '15px',
- height: '30px',
- fontSize: '12px'
- }"
- >
- 鏌ョ湅闄勪欢
- </u-button>
- </view>
- </view>
- <view class="qr-details">
<view class="detail-item">
- <text class="detail-label">宸℃浜猴細</text>
- <text class="detail-value">{{ item.scanner }}</text>
+ <text class="detail-label">浠诲姟涓嬪彂鏃ユ湡</text>
+ <text class="detail-value">{{ item.dateStr }}</text>
</view>
<view class="detail-item">
- <text class="detail-label">宸℃鏃堕棿锛�</text>
- <text class="detail-value">{{ item.scanTime }}</text>
+ <text class="detail-label">宸℃鐘舵��</text>
+ <view class="detail-value">
+ <uni-tag v-if="item.fileStatus==2"
+ text="宸插畬鎴�"
+ size="small"
+ type="success"
+ inverted></uni-tag>
+ <uni-tag v-else-if="item.fileStatus==1"
+ text="宸℃涓�"
+ size="small"
+ type="primary"
+ inverted></uni-tag>
+ <uni-tag v-else=""
+ text="鏈贰妫�"
+ size="small"
+ type="warning"
+ inverted></uni-tag>
+ </view>
</view>
</view>
</view>
+ <uni-load-more :status="loadMoreStatus"></uni-load-more>
</view>
-
<!-- 绌虹姸鎬� -->
- <view v-if="tableData.length === 0 && !tableLoading" class="empty-state">
- <u-empty
- mode="data"
- text="鏆傛棤鏁版嵁"
- :iconSize="80"
- ></u-empty>
- </view>
-
- <!-- 鍔犺浇鐘舵�� -->
- <view v-if="tableLoading" class="loading-state">
- <u-loading-icon mode="circle" color="#1890ff" size="40"></u-loading-icon>
- <text class="loading-text">鍔犺浇涓�...</text>
+ <view v-if="taskTableData?.length === 0"
+ class="no-data">
+ <text>鏆傛棤鏁版嵁</text>
</view>
</view>
-
- <!-- 鍒嗛〉 -->
- <view v-if="total > 0" class="pagination-container">
- <u-pagination
- :total="total"
- :current="pageNum"
- :pageSize="pageSize"
- @change="handlePageChange"
- :showTotal="true"
- :showSizer="false"
- :showJumper="false"
- ></u-pagination>
+ <!-- 鍥剧墖涓婁紶寮圭獥 - 鍘熺敓瀹炵幇 -->
+ <view v-if="showUploadDialog"
+ class="custom-modal-overlay"
+ @click="closeUploadDialog">
+ <view class="custom-modal-container"
+ @click.stop>
+ <view class="upload-popup-content">
+ <view class="upload-popup-header">
+ <text class="upload-popup-title">涓婁紶宸℃璁板綍</text>
+ </view>
+ <view class="upload-popup-body">
+ <!-- 鍒嗙被鏍囩椤� -->
+ <view class="upload-tabs">
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'before' }"
+ @click="switchUploadType('before')">
+ 鐢熶骇鍓�
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'after' }"
+ @click="switchUploadType('after')">
+ 鐢熶骇涓�
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentUploadType === 'issue' }"
+ @click="switchUploadType('issue')">
+ 鐢熶骇鍚�
+ </view>
+ </view>
+ <!-- 寮傚父鐘舵�侀�夋嫨 -->
+ <view class="exception-section">
+ <text class="section-title">鏄惁瀛樺湪寮傚父锛�</text>
+ <view class="exception-options">
+ <view class="exception-option"
+ :class="{ active: hasException === false }"
+ @click="setExceptionStatus(false)">
+ <u-icon name="checkmark-circle"
+ size="20"
+ color="#52c41a"></u-icon>
+ <text>姝e父</text>
+ </view>
+ <view class="exception-option"
+ :class="{ active: hasException === true }"
+ @click="setExceptionStatus(true)">
+ <u-icon name="close-circle"
+ size="20"
+ color="#ff4d4f"></u-icon>
+ <text>瀛樺湪寮傚父</text>
+ </view>
+ </view>
+ </view>
+ <!-- 褰撳墠鍒嗙被鐨勪笂浼犲尯鍩� -->
+ <view class="simple-upload-area">
+ <view class="upload-buttons">
+ <u-button type="primary"
+ @click="chooseMedia('image')"
+ :loading="uploading"
+ :disabled="getCurrentFiles().length >= uploadConfig.limit"
+ :customStyle="{ marginRight: '10px', flex: 1 }">
+ <u-icon name="camera"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px;"></u-icon>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶇収' }}
+ </u-button>
+ <u-button type="success"
+ @click="chooseMedia('video')"
+ :loading="uploading"
+ :disabled="getCurrentFiles().length >= uploadConfig.limit"
+ :customStyle="{ flex: 1 }">
+ <uni-icons type="videocam"
+ name="videocam"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px;"></uni-icons>
+ {{ uploading ? '涓婁紶涓�...' : '鎷嶈棰�' }}
+ </u-button>
+ </view>
+ <!-- 涓婁紶杩涘害 -->
+ <view v-if="uploading"
+ class="upload-progress">
+ <u-line-progress :percentage="uploadProgress"
+ :showText="true"
+ activeColor="#409eff"></u-line-progress>
+ </view>
+ <!-- 褰撳墠鍒嗙被鐨勬枃浠跺垪琛� -->
+ <view v-if="getCurrentFiles().length > 0"
+ class="file-list">
+ <view v-for="(file, index) in getCurrentFiles()"
+ :key="index"
+ class="file-item">
+ <view class="file-preview-container">
+ <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)"
+ :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
+ class="file-preview"
+ mode="aspectFill" />
+ <view v-else-if="file.type === 'video'"
+ class="video-preview">
+ <uni-icons type="videocam"
+ name="videocam"
+ size="18"
+ color="#fff"
+ style="margin-right: 5px;"></uni-icons>
+ <text class="video-text">瑙嗛</text>
+ </view>
+ <!-- 鍒犻櫎鎸夐挳 -->
+ <view class="delete-btn"
+ @click="removeFile(index)">
+ <u-icon name="close"
+ size="12"
+ color="#fff"></u-icon>
+ </view>
+ </view>
+ <view class="file-info">
+ <text class="file-name">{{ file.bucketFilename || file.name || (file.type === 'image' ? '鍥剧墖' : '瑙嗛')
+ }}</text>
+ <text class="file-size">{{ formatFileSize(file.size) }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-if="getCurrentFiles().length === 0"
+ class="empty-state">
+ <text>璇烽�夋嫨瑕佷笂浼犵殑{{ getUploadTypeText() }}鍥剧墖鎴栬棰�</text>
+ </view>
+ <!-- 缁熻淇℃伅 -->
+ <view class="upload-summary">
+ <text class="summary-text">
+ 鐢熶骇鍓�: {{ beforeModelValue.length }}涓枃浠� |
+ 鐢熶骇涓�: {{ afterModelValue.length }}涓枃浠� |
+ 鐢熶骇鍚�: {{ issueModelValue.length }}涓枃浠�
+ </text>
+ </view>
+ </view>
+ </view>
+ <view class="upload-popup-footer">
+ <u-button @click="closeUploadDialog"
+ :customStyle="{ marginRight: '10px' }">鍙栨秷</u-button>
+ <u-button v-if="hasException === true"
+ type="warning"
+ @click="goToRepair"
+ :customStyle="{ marginRight: '10px' }">
+ 鏂板鎶ヤ慨
+ </u-button>
+ <u-button type="primary"
+ @click="submitUpload">鎻愪氦</u-button>
+ </view>
+ </view>
+ </view>
</view>
-
- <!-- 寮圭獥缁勪欢 -->
- <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
- <qr-code-form-dia ref="qrCodeFormDia" @closeDia="handleQuery"></qr-code-form-dia>
+ <!-- 鏌ョ湅闄勪欢寮圭獥 -->
+ <view v-if="showAttachmentDialog"
+ class="custom-modal-overlay"
+ @click="closeAttachmentDialog">
+ <view class="custom-modal-container"
+ @click.stop>
+ <view class="attachment-popup-content">
+ <view class="attachment-popup-header">
+ <text class="attachment-popup-title">鏌ョ湅闄勪欢 - {{ currentViewTask?.taskName }}</text>
+ <view class="close-btn-attachment"
+ @click="closeAttachmentDialog">
+ <u-icon name="close"
+ size="16"
+ color="#666"></u-icon>
+ </view>
+ </view>
+ <view class="attachment-popup-body">
+ <!-- 鍒嗙被鏍囩椤� -->
+ <view class="attachment-tabs">
+ <view class="tab-item"
+ :class="{ active: currentViewType === 'before' }"
+ @click="switchViewType('before')">
+ 鐢熶骇鍓� ({{ getAttachmentsByType(0).length }})
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentViewType === 'after' }"
+ @click="switchViewType('after')">
+ 鐢熶骇涓� ({{ getAttachmentsByType(1).length }})
+ </view>
+ <view class="tab-item"
+ :class="{ active: currentViewType === 'issue' }"
+ @click="switchViewType('issue')">
+ 鐢熶骇鍚� ({{ getAttachmentsByType(2).length }})
+ </view>
+ </view>
+ <!-- 褰撳墠鍒嗙被鐨勯檮浠跺垪琛� -->
+ <view class="attachment-content">
+ <view v-if="getCurrentViewAttachments().length > 0"
+ class="attachment-list">
+ <view v-for="(file, index) in getCurrentViewAttachments()"
+ :key="index"
+ class="attachment-item"
+ @click="previewAttachment(file)">
+ <view class="attachment-preview-container">
+ <image v-if="file.type === 'image' || isImageFile(file)"
+ :src="file.url || file.downloadUrl"
+ class="attachment-preview"
+ mode="aspectFill" />
+ <view v-else
+ class="attachment-video-preview">
+ <u-icon name="video"
+ size="24"
+ color="#409eff"></u-icon>
+ <text class="video-text">瑙嗛</text>
+ </view>
+ </view>
+ <view class="attachment-info">
+ <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || '闄勪欢'
+ }}</text>
+ <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="attachment-empty">
+ <text>璇ュ垎绫绘殏鏃犻檮浠�</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+ <!-- 瑙嗛棰勮寮圭獥 -->
+ <view v-if="showVideoDialog"
+ class="video-modal-overlay"
+ @click="closeVideoPreview">
+ <view class="video-modal-container"
+ @click.stop>
+ <view class="video-modal-header">
+ <text class="video-modal-title">{{ currentVideoFile?.originalFilename || '瑙嗛棰勮' }}</text>
+ <view class="close-btn-video"
+ @click="closeVideoPreview">
+ <u-icon name="close"
+ size="16"
+ color="#fff"></u-icon>
+ </view>
+ </view>
+ <view class="video-modal-body">
+ <video v-if="currentVideoFile"
+ :src="currentVideoFile.url || currentVideoFile.downloadUrl"
+ class="video-player"
+ controls
+ autoplay
+ @error="handleVideoError"></video>
+ </view>
+ </view>
+ </view>
</view>
</template>
<script setup>
-import { onMounted, ref, reactive, computed, nextTick } from 'vue'
-import { onShow } from '@dcloudio/uni-app'
-import PageHeader from '@/components/PageHeader.vue'
-import FormDia from './components/formDia.vue'
-import QrCodeFormDia from './components/qrCodeFormDia.vue'
-import { qrCodeScanRecordList } from '@/api/inspectionUpload/index.js'
-import { getInspectionTaskList } from '@/api/equipmentManagement/inspection.js'
-import {inspectionTaskList} from "@/api/inspectionManagement";
-// import ViewQrCodeFiles from '@/pages/inspectionManagement/components/viewQrCodeFiles.vue'
+ import { onMounted, onUnmounted, ref, nextTick, computed, reactive } from "vue";
+ import { onShow, onReachBottom, onPullDownRefresh } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { getLedgerById } from "@/api/equipmentManagement/ledger.js";
+ import {
+ inspectionTaskList,
+ uploadInspectionTask,
+ } from "@/api/inspectionManagement";
+ import { getToken } from "@/utils/auth";
+ import config from "@/config";
-// 缁勪欢寮曠敤
-const formDia = ref()
-const qrCodeFormDia = ref()
+ // 缁勪欢寮曠敤宸茬Щ闄�
-// 褰撳墠鏍囩
-const activeTab = ref('task')
+ // 鍔犺浇鎻愮ず鏂规硶
+ const showLoadingToast = message => {
+ uni.showLoading({
+ title: message,
+ mask: true,
+ });
+ };
+ const closeToast = () => {
+ uni.hideLoading();
+ };
-const tabName = ref('task')
-const currentTabIndex = ref(0)
+ // 琛ㄦ牸鏁版嵁
+ const taskTableData = ref([]); // 鐢熶骇宸℃鏁版嵁
-// 鏍囩椤垫暟鎹�
-const tabs = reactive([
- { name: '鐢熶骇宸℃' },
- { name: '鐜板満宸℃' }
-])
+ // 褰撳墠鎵弿鐨勪换鍔�
+ const currentScanningTask = ref(null);
+ const infoData = ref(null);
-// 琛ㄦ牸鏁版嵁
-const tableData = ref([])
-const tableLoading = ref(false)
-const total = ref(0)
-const pageNum = ref(1)
-const pageSize = ref(10)
+ // 涓婁紶鐩稿叧鐘舵��
+ const showUploadDialog = ref(false);
+ const uploadFiles = ref([]); // 淇濈暀鐢ㄤ簬鍏煎鎬�
+ const uploadStatusType = ref(0);
+ const uploading = ref(false);
+ const uploadProgress = ref(0);
+ const number = ref(0);
+ const uploadList = ref([]);
-// 鎵爜鐩稿叧鐘舵��
-const isScanning = ref(false)
-const scanLoading = ref(false)
-const cameraError = ref('')
+ // 涓変釜鍒嗙被鐨勪笂浼犵姸鎬�
+ const beforeModelValue = ref([]); // 鐢熶骇鍓�
+ const afterModelValue = ref([]); // 鐢熶骇涓�
+ const issueModelValue = ref([]); // 鐢熶骇鍚�
-// 璁$畻灞炴��
-const scanButtonText = computed(() => {
- if (scanLoading.value) return '姝e湪鍒濆鍖�...'
- return isScanning.value ? '鍋滄鎵爜' : '寮�濮嬫壂鐮�'
-})
+ // 褰撳墠婵�娲荤殑涓婁紶绫诲瀷
+ const currentUploadType = ref("before"); // 'before', 'after', 'issue'
-// 鐢熷懡鍛ㄦ湡
-onMounted(() => {
- // 寤惰繜鍒濆鍖栵紝纭繚DOM宸叉覆鏌�
- nextTick(() => {
- handleTabClick({ props: { name: 'task' } })
- })
-})
+ // 鏌ョ湅闄勪欢鐩稿叧鐘舵��
+ const showAttachmentDialog = ref(false);
+ const currentViewTask = ref(null);
+ const currentViewType = ref("before"); // 'before', 'after', 'issue'
+ const attachmentList = ref([]); // 褰撳墠鏌ョ湅浠诲姟鐨勯檮浠跺垪琛�
-onShow(() => {
- // 椤甸潰鏄剧ず鏃跺埛鏂版暟鎹�
- getList()
-})
+ // 瑙嗛棰勮鐩稿叧鐘舵��
+ const showVideoDialog = ref(false);
+ const currentVideoFile = ref(null);
-// 鏍囩椤靛垏鎹�
-const handleTabChange = (index) => {
- currentTabIndex.value = index
- const tabNames = ['task', 'qrCode']
- activeTab.value = tabNames[index]
- tabName.value = tabNames[index]
- tableData.value = []
- pageNum.value = 1
- getList()
-}
+ // 寮傚父鐘舵��
+ const hasException = ref(null); // null: 鏈�夋嫨, true: 瀛樺湪寮傚父, false: 姝e父
-// 鏍囩椤电偣鍑伙紙鍏煎鏃ф柟娉曪級
-const handleTabClick = (tab) => {
- tabName.value = tab.props.name
- activeTab.value = tab.props.name
- tableData.value = []
- getList()
-}
+ // 涓婁紶閰嶇疆
+ const uploadConfig = {
+ action: "/file/upload",
+ limit: 10,
+ fileSize: 50, // MB
+ fileType: ["jpg", "jpeg", "png", "mp4", "mov"],
+ maxVideoDuration: 60, // 绉�
+ };
-// 鏌ヨ鏁版嵁
-const handleQuery = () => {
- pageNum.value = 1
- pageSize.value = 10
- getList()
-}
+ // 璁$畻涓婁紶URL
+ const uploadFileUrl = computed(() => {
+ const baseUrl = "http://1.15.17.182:9002";
-// 鑾峰彇鍒楄〃鏁版嵁
-const getList = () => {
- tableLoading.value = true
- if (tabName.value === "task") {
- inspectionTaskList({size: pageSize.value, current: pageNum.value}).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- total.value = res.data.total;
- })
- } else {
- qrCodeScanRecordList({size: pageSize.value, current: pageNum.value}).then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- total.value = res.data.total;
- })
- }
-}
+ return baseUrl + uploadConfig.action;
+ });
-// 鍒嗛〉鍙樺寲
-const handlePageChange = (page) => {
- pageNum.value = page
- getList()
-}
+ // 璁$畻璇锋眰澶�
+ const headers = computed(() => {
+ const token = getToken();
+ return token ? { Authorization: "Bearer " + token } : {};
+ });
-// 涓婁紶
-const handleAdd = (row) => {
- nextTick(() => {
- formDia.value?.openDialog(row)
- })
-}
+ // 璇锋眰鍙栨秷鏍囧織锛岀敤浜庡彇娑堟鍦ㄨ繘琛岀殑璇锋眰
+ let isRequestCancelled = false;
-// 鏌ョ湅闄勪欢
-const viewFile = (row) => {
- console.log('鏌ョ湅闄勪欢:', row)
- uni.showToast({
- title: '鏌ョ湅闄勪欢鍔熻兘寮�鍙戜腑',
- icon: 'none'
- })
-}
+ const pagesPames = reactive({
+ size: 10,
+ current: 1,
+ });
-// 鎵爜鐩稿叧鏂规硶
-const toggleScan = async () => {
- if (isScanning.value) {
- await stopScan()
- } else {
- await startScan()
- }
-}
+ const loadMoreStatus = computed(() => {
+ if (loading.value) {
+ return "loading";
+ }
+ if (noMore.value) {
+ return "noMore";
+ }
+ return "more";
+ });
+ const totalSize = ref(0);
+ const noMore = computed(() => {
+ return taskTableData.value.length >= totalSize.value;
+ });
+ const loading = ref(false);
-const startScan = async () => {
- try {
- scanLoading.value = true
- // 浣跨敤uniapp鐨勬壂鐮丄PI
- uni.scanCode({
- success: (res) => {
- console.log('鎵爜鎴愬姛:', res)
- handleScanSuccess(res)
- },
- fail: (err) => {
- console.error('鎵爜澶辫触:', err)
+ const reloadPage = () => {
+ pagesPames.current = 1;
+ taskTableData.value = [];
+ getList();
+ };
+ const loadPage = () => {
+ if (noMore.value || loading.value) return;
+ pagesPames.current += 1;
+ getList();
+ };
+
+ // 鐢熷懡鍛ㄦ湡
+ onMounted(() => {
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM宸叉覆鏌�
+ // nextTick(() => {
+ // getList()
+ // })
+ });
+
+ onReachBottom(() => {
+ loadPage();
+ });
+ onPullDownRefresh(() => {
+ reloadPage();
+ uni.stopPullDownRefresh();
+ });
+
+ onShow(() => {
+ // 椤甸潰鏄剧ず鏃跺埛鏂版暟鎹�
+ reloadPage();
+ });
+
+ // 缁勪欢閿�姣佹椂鐨勬竻鐞�
+ onUnmounted(() => {
+ // 璁剧疆鍙栨秷鏍囧織锛岄樆姝㈠悗缁殑寮傛鎿嶄綔
+ isRequestCancelled = true;
+
+ // 鍏抽棴涓婁紶寮圭獥
+ if (showUploadDialog.value) {
+ showUploadDialog.value = false;
+ }
+ });
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = () => {
+ // 鏄剧ず鍔犺浇鎻愮ず
+ // showLoadingToast('鍔犺浇涓�...')
+
+ // 璁剧疆鍙栨秷鏍囧織
+ isRequestCancelled = false;
+ loading.value = true;
+ inspectionTaskList({ ...pagesPames })
+ .then(res => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪涓旇姹傛湭琚彇娑�
+ if (!isRequestCancelled) {
+ // 澶勭悊涓嶅悓鐨勬暟鎹粨鏋�
+ let records = [];
+ if (res && res.data) {
+ // 灏濊瘯澶氱鍙兘鐨勬暟鎹粨鏋�
+ totalSize.value = res.data.total;
+ 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 = [
+ ...taskTableData.value,
+ ...records.map(record => {
+ record.fileStatus = getFileStatus(record);
+ return record;
+ }),
+ ];
+ } else {
+ taskTableData.value = [];
+ uni.showToast({
+ title: "鏆傛棤宸℃浠诲姟鏁版嵁",
+ icon: "none",
+ });
+ }
+ }
+ loading.value = false;
+ // 鍏抽棴鍔犺浇鎻愮ず
+ // closeToast()
+ })
+ .catch(err => {
+ // 妫�鏌ョ粍浠舵槸鍚﹁繕瀛樺湪涓旇姹傛湭琚彇娑�
+ if (!isRequestCancelled) {
+ taskTableData.value = [];
+ // 娣诲姞閿欒鎻愮ず
+ uni.showToast({
+ title: "鑾峰彇鏁版嵁澶辫触",
+ icon: "error",
+ });
+ }
+ loading.value = false;
+ // 鍏抽棴鍔犺浇鎻愮ず
+ // closeToast()
+ });
+ };
+
+ const getFileStatus = record => {
+ let _beforeProduction =
+ record.beforeProduction && record.beforeProduction.length;
+ let _afterProduction =
+ record.afterProduction && record.afterProduction.length;
+ let _productionIssues =
+ record.productionIssues && record.productionIssues.length;
+ if (_beforeProduction && _afterProduction && _productionIssues) {
+ return 2;
+ } else if (_beforeProduction || _afterProduction || _productionIssues) {
+ return 1;
+ } else {
+ return 0;
+ }
+ };
+
+ // 涓烘寚瀹氫换鍔″紑濮嬫壂鐮侊紙鐪熸満锛�
+ const startScanForTask = async task => {
+ try {
+ currentScanningTask.value = task;
+ uni.scanCode({
+ success: res => {
+ handleScanSuccess(res);
+ },
+ fail: err => {
+ console.error("鎵爜澶辫触:", err);
+ uni.showToast({
+ title: "鎵爜澶辫触",
+ icon: "error",
+ });
+ },
+ });
+ } catch (e) {
+ console.error("鍚姩鎵爜澶辫触:", e);
+ uni.showToast({
+ title: "鍚姩鎵爜澶辫触",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鎵爜鎴愬姛澶勭悊锛氭牎楠屽悗鎵撳紑涓婁紶寮圭獥
+ const handleScanSuccess = result => {
+ try {
+ // 瑙f瀽浜岀淮鐮佹暟鎹紝鎻愬彇deviceId
+ let deviceId = "";
+
+ if (result?.result && typeof result.result === "string") {
+ if (result.result.includes("deviceId=")) {
+ const match = result.result.match(/deviceId=(\d+)/);
+ if (match && match[1]) deviceId = match[1];
+ } else {
+ try {
+ const qrData = JSON.parse(result.result);
+ deviceId = qrData.deviceId || qrData.qrCodeId || "";
+ } catch (e) {
+ deviceId = result.result;
+ }
+ }
+ }
+
+ if (!deviceId) {
+ uni.showToast({ title: "鏈瘑鍒埌璁惧ID", icon: "error" });
+ return;
+ }
+
+ const currentTaskId =
+ currentScanningTask.value?.taskId || currentScanningTask.value?.id;
+ if (!currentTaskId) {
+ uni.showToast({ title: "浠诲姟淇℃伅缂哄け", icon: "error" });
+ return;
+ }
+
+ if (deviceId === currentTaskId.toString()) {
+ uni.showToast({ title: "璇嗗埆鎴愬姛", icon: "success" });
+ openUploadDialog(currentScanningTask.value);
+ } else {
+ uni.showToast({ title: "璇锋壂鎻忔纭殑璁惧", icon: "error" });
+ }
+ } catch (error) {
+ console.error("鎵爜缁撴灉澶勭悊澶辫触:", error);
+ uni.showToast({
+ title: error?.message || "鏁版嵁瑙f瀽澶辫触",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鎵撳紑涓婁紶寮圭獥
+ const openUploadDialog = task => {
+ // 璁剧疆浠诲姟淇℃伅鍒癷nfoData
+ if (task) {
+ infoData.value = {
+ ...task,
+ taskId: task.taskId || task.id,
+ storageBlobDTO: [], // 鍒濆鍖栨枃浠跺垪琛�
+ };
+ }
+
+ // 璁剧疆涓婁紶鐘舵�佺被鍨嬶紙鍙互鏍规嵁浠诲姟绫诲瀷璁剧疆涓嶅悓鐨勭姸鎬侊級
+ uploadStatusType.value = 0; // 榛樿鐘舵��
+
+ // 娓呯┖涔嬪墠鐨勬枃浠�
+ uploadFiles.value = [];
+
+ // 鏄剧ず涓婁紶寮圭獥
+ showUploadDialog.value = true;
+ };
+
+ // 鍏抽棴涓婁紶寮圭獥
+ const closeUploadDialog = () => {
+ showUploadDialog.value = false;
+ uploadFiles.value = [];
+ // 娓呯悊涓変釜鍒嗙被鐨勬暟鎹�
+ beforeModelValue.value = [];
+ afterModelValue.value = [];
+ issueModelValue.value = [];
+ currentUploadType.value = "before";
+ hasException.value = null; // 閲嶇疆寮傚父鐘舵��
+ infoData.value = null; // 娓呯悊浠诲姟鏁版嵁
+ };
+
+ // 鍒囨崲涓婁紶绫诲瀷
+ const switchUploadType = type => {
+ currentUploadType.value = type;
+ };
+
+ // 鑾峰彇褰撳墠鍒嗙被鐨勬枃浠跺垪琛�
+ const getCurrentFiles = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return beforeModelValue.value || [];
+ case "after":
+ return afterModelValue.value || [];
+ case "issue":
+ return issueModelValue.value || [];
+ default:
+ return [];
+ }
+ };
+
+ // 鑾峰彇涓婁紶绫诲瀷鏂囨湰
+ const getUploadTypeText = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return "鐢熶骇鍓�";
+ case "after":
+ return "鐢熶骇涓�";
+ case "issue":
+ return "鐢熶骇鍚�";
+ default:
+ return "";
+ }
+ };
+
+ // 澶勭悊涓婁紶鏂囦欢鏇存柊
+ const handleUploadUpdate = files => {
+ uploadFiles.value = files;
+ };
+
+ // 璁剧疆寮傚父鐘舵��
+ const setExceptionStatus = status => {
+ hasException.value = status;
+ };
+
+ // 璺宠浆鍒版柊澧炴姤淇〉闈�
+ const goToRepair = () => {
+ try {
+ // 瀛樺偍褰撳墠浠诲姟淇℃伅鍒版湰鍦板瓨鍌紝渚涙姤淇〉闈娇鐢�
+ const taskInfo = {
+ taskId: infoData.value?.taskId || infoData.value?.id,
+ taskName: infoData.value?.taskName,
+ inspectionLocation: infoData.value?.inspectionLocation,
+ inspector: infoData.value?.inspector,
+ // 浼犻�掑綋鍓嶄笂浼犵殑鏂囦欢淇℃伅
+ uploadedFiles: {
+ before: beforeModelValue.value,
+ after: afterModelValue.value,
+ issue: issueModelValue.value,
+ },
+ };
+
+ uni.setStorageSync("repairTaskInfo", JSON.stringify(taskInfo));
+
+ // 璺宠浆鍒版柊澧炴姤淇〉闈�
+ uni.navigateTo({
+ url: "/pages/equipmentManagement/repair/add",
+ });
+
+ // 鍏抽棴涓婁紶寮圭獥
+ closeUploadDialog();
+ } catch (error) {
+ console.error("璺宠浆鎶ヤ慨椤甸潰澶辫触:", error);
+ uni.showToast({
+ title: "璺宠浆澶辫触锛岃閲嶈瘯",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鎻愪氦涓婁紶
+ const submitUpload = async () => {
+ try {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗗紓甯哥姸鎬�
+ if (hasException.value === null) {
uni.showToast({
- title: '鎵爜澶辫触',
- icon: 'error'
- })
+ title: "璇烽�夋嫨鏄惁瀛樺湪寮傚父",
+ icon: "none",
+ });
+ return;
+ }
+
+ // 妫�鏌ユ槸鍚︽湁浠讳綍鏂囦欢涓婁紶
+ const totalFiles =
+ beforeModelValue.value.length +
+ afterModelValue.value.length +
+ issueModelValue.value.length;
+ if (totalFiles === 0) {
+ uni.showToast({
+ title: "璇峰厛涓婁紶鏂囦欢",
+ icon: "none",
+ });
+ return;
+ }
+
+ // 鏄剧ず鎻愪氦涓殑鍔犺浇鎻愮ず
+ showLoadingToast("鎻愪氦涓�...");
+
+ // 鎸夌収鎮ㄧ殑閫昏緫鍚堝苟鎵�鏈夊垎绫荤殑鏂囦欢
+ let arr = [];
+ if (beforeModelValue.value.length > 0) {
+ arr.push(...beforeModelValue.value);
+ }
+ if (afterModelValue.value.length > 0) {
+ arr.push(...afterModelValue.value);
+ }
+ if (issueModelValue.value.length > 0) {
+ arr.push(...issueModelValue.value);
+ }
+
+ // 浼犵粰鍚庣鐨勪复鏃舵枃浠禝D鍒楄〃锛坱empFileIds锛�
+ // 鍏煎锛氭湁浜涙帴鍙e彲鑳借繑鍥� tempId / tempFileId / id
+ let tempFileIds = [];
+ if (arr !== null && arr.length > 0) {
+ tempFileIds = arr
+ .map(item => item?.tempId ?? item?.tempFileId ?? item?.id)
+ .filter(v => v !== undefined && v !== null && v !== "");
+ }
+
+ // 鎻愪氦鏁版嵁
+ infoData.value.storageBlobDTO = arr;
+ // 娣诲姞寮傚父鐘舵�佷俊鎭�
+ infoData.value.hasException = hasException.value;
+ infoData.value.tempFileIds = tempFileIds;
+ const result = await uploadInspectionTask({ ...infoData.value });
+
+ // 妫�鏌ユ彁浜ょ粨鏋�
+ if (result && (result.code === 200 || result.success)) {
+ // 鎻愪氦鎴愬姛
+ closeToast(); // 鍏抽棴鍔犺浇鎻愮ず
+
+ uni.showToast({
+ title: "鎻愪氦鎴愬姛",
+ icon: "success",
+ });
+
+ // 鍏抽棴寮圭獥
+ closeUploadDialog();
+
+ // 鍒锋柊鍒楄〃
+ setTimeout(() => {
+ reloadPage();
+ }, 500);
+ } else {
+ // 鎻愪氦澶辫触
+ closeToast();
+ uni.showToast({
+ title: result?.msg || result?.message || "鎻愪氦澶辫触",
+ icon: "error",
+ });
+ }
+ } catch (error) {
+ console.error("鎻愪氦涓婁紶澶辫触:", error);
+ closeToast(); // 鍏抽棴鍔犺浇鎻愮ず
+
+ let errorMessage = "鎻愪氦澶辫触";
+ if (error.message) {
+ errorMessage = error.message;
+ } else if (error.msg) {
+ errorMessage = error.msg;
+ } else if (typeof error === "string") {
+ errorMessage = error;
+ }
+
+ uni.showToast({
+ title: errorMessage,
+ icon: "error",
+ });
+ }
+ };
+
+ // 鏌ョ湅闄勪欢
+ const viewAttachments = async task => {
+ try {
+ currentViewTask.value = task;
+ currentViewType.value = "before";
+
+ // 瑙f瀽鏂扮殑鏁版嵁缁撴瀯
+ attachmentList.value = [];
+
+ // 鍚庣鍙嶆樉瀛楁锛堜綘鎻愪緵鐨勬暟鎹粨鏋勶級锛�
+ // - commonFileListBefore锛氱敓浜у墠锛堥�氬父 type=10锛�
+ // - commonFileListAfter锛氱敓浜т腑锛堥�氬父 type=11锛�
+ // - commonFileList锛氬彲鑳芥槸鍏ㄩ儴/鍏滃簳锛堣嫢鍖呭惈鐢熶骇鍚庯紝涓�鑸� type=12锛�
+ const allList = Array.isArray(task?.commonFileList)
+ ? task.commonFileList
+ : [];
+ const beforeList = Array.isArray(task?.commonFileListBefore)
+ ? task.commonFileListBefore
+ : allList.filter(f => f?.type === 10);
+ const afterList = Array.isArray(task?.commonFileListAfter)
+ ? task.commonFileListAfter
+ : allList.filter(f => f?.type === 11);
+ // 濡傛灉鍚庣鍚庣画琛ヤ簡 commonFileListIssue锛屽垯浼樺厛鐢紱鍚﹀垯浠� commonFileList 閲屾寜 type=12 鍏滃簳
+ const issueList = Array.isArray(task?.commonFileListIssue)
+ ? task.commonFileListIssue
+ : allList.filter(f => f?.type === 12);
+
+ const mapToViewFile = (file, viewType) => {
+ const u = normalizeFileUrl(file?.url || file?.downloadUrl || "");
+ return {
+ ...file,
+ // 鐢ㄤ簬涓夋爣绛鹃〉鍒嗙粍锛�0=鐢熶骇鍓� 1=鐢熶骇涓� 2=鐢熶骇鍚�
+ type: viewType,
+ name: file?.name || file?.originalFilename || file?.bucketFilename,
+ bucketFilename: file?.bucketFilename || file?.name,
+ originalFilename: file?.originalFilename || file?.name,
+ url: u,
+ downloadUrl: u,
+ size: file?.size || file?.byteSize,
+ };
+ };
+
+ attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0)));
+ attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1)));
+ attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2)));
+
+ showAttachmentDialog.value = true;
+ } catch (error) {
+ uni.showToast({
+ title: "鑾峰彇闄勪欢澶辫触",
+ icon: "error",
+ });
+ }
+ };
+
+ // 鍏抽棴闄勪欢鏌ョ湅寮圭獥
+ const closeAttachmentDialog = () => {
+ showAttachmentDialog.value = false;
+ currentViewTask.value = null;
+ attachmentList.value = [];
+ currentViewType.value = "before";
+ };
+
+ // 鍒囨崲鏌ョ湅绫诲瀷
+ const switchViewType = type => {
+ currentViewType.value = type;
+ };
+
+ // 鏍规嵁type鑾峰彇瀵瑰簲鍒嗙被鐨勯檮浠�
+ const getAttachmentsByType = typeValue => {
+ return attachmentList.value.filter(file => file.type === typeValue) || [];
+ };
+ // 鑾峰彇type鍊�
+ const getTabType = () => {
+ switch (currentUploadType.value) {
+ case "before":
+ return 10;
+ case "after":
+ return 11;
+ case "issue":
+ return 12;
+ default:
+ return 10;
+ }
+ };
+ // 鑾峰彇褰撳墠鏌ョ湅绫诲瀷鐨勯檮浠�
+ const getCurrentViewAttachments = () => {
+ switch (currentViewType.value) {
+ case "before":
+ return getAttachmentsByType(0);
+ case "after":
+ return getAttachmentsByType(1);
+ case "issue":
+ return getAttachmentsByType(2);
+ default:
+ return [];
+ }
+ };
+
+ // 鍒ゆ柇鏄惁涓哄浘鐗囨枃浠�
+ const isImageFile = file => {
+ // 妫�鏌ontentType瀛楁
+ if (file.contentType && file.contentType.startsWith("image/")) {
+ return true;
+ }
+
+ // 妫�鏌ュ師鏈夌殑type瀛楁
+ if (file.type === "image") return true;
+
+ // 妫�鏌ユ枃浠舵墿灞曞悕
+ const name = file.bucketFilename || file.originalFilename || file.name || "";
+ const ext = name.split(".").pop()?.toLowerCase();
+ return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext);
+ };
+
+ // 鏂囦欢璁块棶鍩虹鍩燂紙鍚庣瑕佹眰鍓嶇紑锛�
+ const filePreviewBase = "http://114.132.189.42:9098";
+
+ // 灏嗗悗绔繑鍥炵殑鏂囦欢鍦板潃瑙勮寖鎴愬彲璁块棶URL
+ // 鍏煎鍦烘櫙锛�
+ // - 宸茬粡鏄� http/https锛氱洿鎺ヨ繑鍥�
+ // - 浠� / 寮�澶达細鎷兼帴 filePreviewBase
+ // - Windows 鏈湴璺緞锛堝 D:\ruoyi\prod\uploads...\xx.jpg锛夛細灏濊瘯鎴彇 prod 涔嬪悗鐨勭浉瀵硅矾寰勫苟鎷兼帴 filePreviewBase
+ const normalizeFileUrl = rawUrl => {
+ try {
+ if (!rawUrl || typeof rawUrl !== "string") return "";
+ const url = rawUrl.trim();
+ if (!url) return "";
+ if (/^https?:\/\//i.test(url)) return url;
+ if (url.startsWith("/")) return `${filePreviewBase}${url}`;
+
+ // Windows path -> web path
+ if (/^[a-zA-Z]:\\/.test(url)) {
+ const normalized = url.replace(/\\/g, "/");
+ const idx = normalized.indexOf("/prod/");
+ if (idx >= 0) {
+ const relative = normalized.slice(idx + "/prod/".length);
+ return `${filePreviewBase}/${relative}`;
+ }
+ // 鍏滃簳锛氭棤娉曟帹鏂槧灏勮鍒欐椂锛岃嚦灏戞妸鍙嶆枩鏉犲彉鎴愭鏂滄潬
+ return normalized;
+ }
+
+ // 鍏朵粬鐩稿璺緞锛氱洿鎺ョ敤 baseUrl 鎷间竴涓�
+ return `${filePreviewBase}/${url.replace(/^\//, "")}`;
+ } catch (e) {
+ return rawUrl || "";
+ }
+ };
+
+ // 棰勮闄勪欢
+ const previewAttachment = file => {
+ if (isImageFile(file)) {
+ // 棰勮鍥剧墖
+ const imageUrls = getCurrentViewAttachments()
+ .filter(f => isImageFile(f))
+ .map(f => f.url || f.downloadUrl);
+
+ uni.previewImage({
+ urls: imageUrls,
+ current: file.url || file.downloadUrl,
+ });
+ } else {
+ // 棰勮瑙嗛 - 鏄剧ず瑙嗛鎾斁寮圭獥
+ showVideoPreview(file);
+ }
+ };
+
+ // 鏄剧ず瑙嗛棰勮
+ const showVideoPreview = file => {
+ currentVideoFile.value = file;
+ showVideoDialog.value = true;
+ };
+
+ // 鍏抽棴瑙嗛棰勮
+ const closeVideoPreview = () => {
+ showVideoDialog.value = false;
+ currentVideoFile.value = null;
+ };
+
+ // 瑙嗛鎾斁閿欒澶勭悊
+ const handleVideoError = error => {
+ uni.showToast({
+ title: "瑙嗛鎾斁澶辫触",
+ icon: "error",
+ });
+ };
+
+ // 鎷嶇収/鎷嶈棰戯紙鐪熸満浼樺厛鐢� chooseMedia锛涗笉鏀寔鍒欓檷绾э級
+ const chooseMedia = type => {
+ if (getCurrentFiles().length >= uploadConfig.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶,
+ icon: "none",
+ });
+ return;
+ }
+
+ const remaining = uploadConfig.limit - getCurrentFiles().length;
+
+ // 浼樺厛锛歝hooseMedia锛堟敮鎸� image/video锛�
+ if (typeof uni.chooseMedia === "function") {
+ uni.chooseMedia({
+ count: Math.min(remaining, 1),
+ mediaType: [type || "image"],
+ sizeType: ["compressed", "original"],
+ sourceType: ["camera"],
+ success: res => {
+ try {
+ const files = res?.tempFiles || [];
+ if (!files.length) throw new Error("鏈幏鍙栧埌鏂囦欢");
+
+ files.forEach((tf, idx) => {
+ const filePath = tf.tempFilePath || tf.path || "";
+ const fileType = tf.fileType || type || "image";
+ const ext = fileType === "video" ? "mp4" : "jpg";
+ const file = {
+ tempFilePath: filePath,
+ path: filePath,
+ type: fileType,
+ name: `${fileType}_${Date.now()}_${idx}.${ext}`,
+ size: tf.size || 0,
+ duration: tf.duration || 0,
+ createTime: Date.now(),
+ uid: Date.now() + Math.random() + idx,
+ };
+ handleBeforeUpload(file);
+ });
+ } catch (e) {
+ console.error("澶勭悊鎷嶆憚缁撴灉澶辫触:", e);
+ uni.showToast({ title: "澶勭悊鏂囦欢澶辫触", icon: "error" });
+ }
+ },
+ fail: err => {
+ console.error("鎷嶆憚澶辫触:", err);
+ uni.showToast({ title: "鎷嶆憚澶辫触", icon: "error" });
+ },
+ });
+ return;
+ }
+
+ // 闄嶇骇锛歝hooseImage / chooseVideo
+ if (type === "video") {
+ chooseVideo();
+ } else {
+ uni.chooseImage({
+ count: 1,
+ sizeType: ["compressed", "original"],
+ sourceType: ["camera"],
+ success: res => {
+ const tempFilePath = res?.tempFilePaths?.[0];
+ const tempFile = res?.tempFiles?.[0] || {};
+ if (!tempFilePath) return;
+ handleBeforeUpload({
+ tempFilePath,
+ path: tempFilePath,
+ type: "image",
+ name: `photo_${Date.now()}.jpg`,
+ size: tempFile.size || 0,
+ createTime: Date.now(),
+ uid: Date.now() + Math.random(),
+ });
+ },
+ });
+ }
+ };
+
+ // 鎷嶇収
+ const chooseImage = () => {
+ if (uploadFiles.value.length >= uploadConfig.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳芥媿鎽�${uploadConfig.limit}涓枃浠禶,
+ icon: "none",
+ });
+ return;
+ }
+
+ uni.chooseMedia({
+ count: 1,
+ mediaType: ["image", "video"],
+ sizeType: ["compressed", "original"],
+ sourceType: ["camera"],
+ success: res => {
+ try {
+ if (!res.tempFiles || res.tempFiles.length === 0) {
+ throw new Error("鏈幏鍙栧埌鍥剧墖鏂囦欢");
+ }
+
+ const tempFilePath = res.tempFiles[0];
+ const tempFile =
+ res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {};
+
+ const file = {
+ tempFilePath: tempFilePath,
+ path: tempFilePath, // 淇濇寔鍏煎鎬�
+ type: "image",
+ name: `photo_${Date.now()}.jpg`,
+ size: tempFile.size || 0,
+ createTime: new Date().getTime(),
+ uid: Date.now() + Math.random(),
+ };
+
+ handleBeforeUpload(file);
+ } catch (error) {
+ console.error("澶勭悊鎷嶇収缁撴灉澶辫触:", error);
+ uni.showToast({
+ title: "澶勭悊鍥剧墖澶辫触",
+ icon: "error",
+ });
+ }
+ },
+ fail: err => {
+ console.error("鎷嶇収澶辫触:", err);
+ uni.showToast({
+ title: "鎷嶇収澶辫触: " + (err.errMsg || "鏈煡閿欒"),
+ icon: "error",
+ });
+ },
+ });
+ };
+
+ // 鎷嶈棰�
+ const chooseVideo = () => {
+ if (uploadFiles.value.length >= uploadConfig.limit) {
+ uni.showToast({
+ title: `鏈�澶氬彧鑳芥媿鎽�${uploadConfig.limit}涓枃浠禶,
+ icon: "none",
+ });
+ return;
+ }
+
+ uni.chooseVideo({
+ sourceType: ["camera"],
+ maxDuration: uploadConfig.maxVideoDuration,
+ camera: "back",
+ success: res => {
+ try {
+ if (!res.tempFilePath) {
+ throw new Error("鏈幏鍙栧埌瑙嗛鏂囦欢");
+ }
+
+ const file = {
+ tempFilePath: res.tempFilePath,
+ path: res.tempFilePath, // 淇濇寔鍏煎鎬�
+ type: "video",
+ name: `video_${Date.now()}.mp4`,
+ size: res.size || 0,
+ duration: res.duration || 0,
+ createTime: new Date().getTime(),
+ uid: Date.now() + Math.random(),
+ };
+
+ handleBeforeUpload(file);
+ } catch (error) {
+ console.error("澶勭悊鎷嶈棰戠粨鏋滃け璐�:", error);
+ uni.showToast({
+ title: "澶勭悊瑙嗛澶辫触",
+ icon: "error",
+ });
+ }
+ },
+ fail: err => {
+ console.error("鎷嶈棰戝け璐�:", err);
+ uni.showToast({
+ title: "鎷嶈棰戝け璐�: " + (err.errMsg || "鏈煡閿欒"),
+ icon: "error",
+ });
+ },
+ });
+ };
+
+ // 鍒犻櫎鏂囦欢
+ const removeFile = index => {
+ uni.showModal({
+ title: "纭鍒犻櫎",
+ content: "纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�",
+ success: res => {
+ if (res.confirm) {
+ // 鏍规嵁褰撳墠涓婁紶绫诲瀷鍒犻櫎瀵瑰簲鍒嗙被鐨勬枃浠�
+ switch (currentUploadType.value) {
+ case "before":
+ beforeModelValue.value.splice(index, 1);
+ break;
+ case "after":
+ afterModelValue.value.splice(index, 1);
+ break;
+ case "issue":
+ issueModelValue.value.splice(index, 1);
+ break;
+ }
+ uni.showToast({
+ title: "鍒犻櫎鎴愬姛",
+ icon: "success",
+ });
+ }
+ },
+ });
+ };
+
+ // 妫�鏌ョ綉缁滆繛鎺�
+ const checkNetworkConnection = () => {
+ return new Promise(resolve => {
+ uni.getNetworkType({
+ success: res => {
+ if (res.networkType === "none") {
+ resolve(false);
+ } else {
+ resolve(true);
+ }
+ },
+ fail: () => {
+ resolve(false);
+ },
+ });
+ });
+ };
+
+ // 涓婁紶鍓嶆牎楠�
+ const handleBeforeUpload = async file => {
+ // 鏍¢獙鏂囦欢绫诲瀷
+ if (
+ uploadConfig.fileType &&
+ Array.isArray(uploadConfig.fileType) &&
+ uploadConfig.fileType.length > 0
+ ) {
+ const fileName = file.name || "";
+ const fileExtension = fileName
+ ? fileName.split(".").pop().toLowerCase()
+ : "";
+
+ // 鏍规嵁鏂囦欢绫诲瀷纭畾鏈熸湜鐨勬墿灞曞悕
+ let expectedTypes = [];
+ if (file.type === "image") {
+ expectedTypes = ["jpg", "jpeg", "png", "gif", "webp"];
+ } else if (file.type === "video") {
+ expectedTypes = ["mp4", "mov", "avi", "wmv"];
+ }
+
+ // 妫�鏌ユ枃浠舵墿灞曞悕鏄惁鍦ㄥ厑璁哥殑绫诲瀷涓�
+ if (fileExtension && expectedTypes.length > 0) {
+ const isAllowed = expectedTypes.some(
+ type => uploadConfig.fileType.includes(type) && type === fileExtension
+ );
+
+ if (!isAllowed) {
+ uni.showToast({
+ title: `鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇锋媿鎽� ${expectedTypes.join("/")} 鏍煎紡鐨勬枃浠禶,
+ icon: "none",
+ });
+ return false;
+ }
+ }
+ }
+
+ // 鏍¢獙閫氳繃锛屽紑濮嬩笂浼�
+ uploadFile(file);
+ return true;
+ };
+
+ // 鏂囦欢涓婁紶澶勭悊锛堢湡鏈鸿蛋 uni.uploadFile锛�
+ const uploadFile = async file => {
+ uploading.value = true;
+ uploadProgress.value = 0;
+ number.value++; // 澧炲姞涓婁紶璁℃暟
+
+ // 纭繚token瀛樺湪
+ const token = getToken();
+ if (!token) {
+ handleUploadError("鐢ㄦ埛鏈櫥褰�");
+ return;
+ }
+
+ const typeValue = getTabType(); // 鐢熶骇鍓�:10, 鐢熶骇涓�:11, 鐢熶骇鍚�:12
+
+ uploadWithUniUploadFile(
+ file,
+ file.tempFilePath || file.path || "",
+ typeValue,
+ token
+ );
+ };
+
+ // 浣跨敤uni.uploadFile涓婁紶锛堥潪H5鐜鎴朒5鍥為��鏂规锛�
+ const uploadWithUniUploadFile = (file, filePath, typeValue, token) => {
+ if (!filePath) {
+ handleUploadError("鏂囦欢璺緞涓嶅瓨鍦�");
+ return;
+ }
+
+ const uploadTask = uni.uploadFile({
+ url: uploadFileUrl.value,
+ filePath: filePath,
+ name: "file",
+ formData: {
+ type: typeValue,
+ },
+ header: {
+ Authorization: `Bearer ${token}`,
+ },
+ success: res => {
+ try {
+ if (res.statusCode === 200) {
+ const response = JSON.parse(res.data);
+ if (response.code === 200) {
+ handleUploadSuccess(response, file);
+ uni.showToast({
+ title: "涓婁紶鎴愬姛",
+ icon: "success",
+ });
+ } else {
+ handleUploadError(response.msg || "鏈嶅姟鍣ㄨ繑鍥為敊璇�");
+ }
+ } else {
+ handleUploadError(`鏈嶅姟鍣ㄩ敊璇紝鐘舵�佺爜: ${res.statusCode}`);
+ }
+ } catch (e) {
+ console.error("瑙f瀽鍝嶅簲澶辫触:", e);
+ console.error("鍘熷鍝嶅簲鏁版嵁:", res.data);
+ handleUploadError("鍝嶅簲鏁版嵁瑙f瀽澶辫触: " + e.message);
+ }
+ },
+ fail: err => {
+ console.error("涓婁紶澶辫触:", err.errMsg || err);
+ number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
+
+ let errorMessage = "涓婁紶澶辫触";
+ if (err.errMsg) {
+ if (err.errMsg.includes("statusCode: null")) {
+ errorMessage = "缃戠粶杩炴帴澶辫触锛岃妫�鏌ョ綉缁滆缃�";
+ } else if (err.errMsg.includes("timeout")) {
+ errorMessage = "涓婁紶瓒呮椂锛岃閲嶈瘯";
+ } else if (err.errMsg.includes("fail")) {
+ errorMessage = "涓婁紶澶辫触锛岃妫�鏌ョ綉缁滆繛鎺�";
+ } else {
+ errorMessage = err.errMsg;
+ }
+ }
+
+ handleUploadError(errorMessage);
},
complete: () => {
- scanLoading.value = false
- }
- })
- } catch (e) {
- console.error('鍚姩鎵爜澶辫触:', e)
- scanLoading.value = false
- uni.showToast({
- title: '鍚姩鎵爜澶辫触',
- icon: 'error'
- })
- }
-}
+ uploading.value = false;
+ uploadProgress.value = 0;
+ },
+ });
-const stopScan = async () => {
- isScanning.value = false
-}
-
-// 鎵爜鎴愬姛澶勭悊
-const handleScanSuccess = async (result) => {
- try {
- console.log('澶勭悊鎵爜缁撴灉:', result)
-
- uni.showToast({
- title: '璇嗗埆鎴愬姛',
- icon: 'success'
- })
-
- // 瑙f瀽浜岀淮鐮佹暟鎹�
- let qrData
- try {
- qrData = JSON.parse(result.result)
- console.log('瑙f瀽鐨勪簩缁寸爜鏁版嵁:', qrData)
- } catch (e) {
- // 濡傛灉涓嶆槸JSON鏍煎紡锛岀洿鎺ヤ娇鐢ㄦ壂鐮佺粨鏋滀綔涓鸿澶囧悕绉�
- qrData = {
- deviceName: result.result,
- location: '',
- qrCodeId: result.result // 娣诲姞浜岀淮鐮両D
- }
- console.log('浣跨敤榛樿鏁版嵁鏍煎紡:', qrData)
+ // 鐩戝惉涓婁紶杩涘害
+ if (uploadTask && uploadTask.onProgressUpdate) {
+ uploadTask.onProgressUpdate(res => {
+ uploadProgress.value = res.progress;
+ });
}
-
- // 纭繚鏁版嵁瀹屾暣鎬�
- if (!qrData.deviceName) {
- qrData.deviceName = result.result
+ };
+
+ // 涓婁紶澶辫触澶勭悊
+ const handleUploadError = (message = "涓婁紶鏂囦欢澶辫触", showRetry = false) => {
+ uploading.value = false;
+ uploadProgress.value = 0;
+
+ if (showRetry) {
+ uni.showModal({
+ title: "涓婁紶澶辫触",
+ content: message + "锛屾槸鍚﹂噸璇曪紵",
+ success: res => {
+ if (res.confirm) {
+ // 鐢ㄦ埛閫夋嫨閲嶈瘯锛岃繖閲屽彲浠ラ噸鏂拌Е鍙戜笂浼�
+ }
+ },
+ });
+ } else {
+ uni.showToast({
+ title: message,
+ icon: "error",
+ });
}
- if (!qrData.qrCodeId) {
- qrData.qrCodeId = result.result
+ };
+
+ // 涓婁紶鎴愬姛鍥炶皟
+ const handleUploadSuccess = (res, file) => {
+ console.log("涓婁紶鎴愬姛鍝嶅簲:", res);
+
+ // 澶勭悊涓嶅悓鐨勬暟鎹粨鏋勶細鍙兘鏄暟缁勶紝涔熷彲鑳芥槸鍗曚釜瀵硅薄
+ let uploadedFile = null;
+ uploadedFile = res.data;
+
+ if (!uploadedFile) {
+ console.error("鏃犳硶瑙f瀽涓婁紶鍝嶅簲鏁版嵁:", res);
+ number.value--; // 涓婁紶澶辫触鏃跺噺灏戣鏁�
+ handleUploadError("涓婁紶鍝嶅簲鏁版嵁鏍煎紡閿欒", false);
+ return;
}
-
- callBackendAPI(qrData)
- } catch (error) {
- console.error('澶勭悊鎵爜缁撴灉澶辫触:', error)
- uni.showToast({
- title: error.message || '鏁版嵁瑙f瀽澶辫触',
- icon: 'error'
- })
- }
-}
-const callBackendAPI = (result) => {
- console.log('鍑嗗鎵撳紑寮规锛屾暟鎹�:', result)
- console.log('寮规缁勪欢寮曠敤:', qrCodeFormDia.value)
-
- // 纭繚缁勪欢寮曠敤瀛樺湪
- if (qrCodeFormDia.value) {
- console.log('鐩存帴璋冪敤寮规openDialog鏂规硶')
- qrCodeFormDia.value.openDialog(result)
- } else {
- // 濡傛灉缁勪欢寮曠敤涓嶅瓨鍦紝绛夊緟涓嬩竴涓猼ick
- console.log('缁勪欢寮曠敤涓嶅瓨鍦紝绛夊緟nextTick')
- nextTick(() => {
- console.log('nextTick鍚庡脊妗嗙粍浠跺紩鐢�:', qrCodeFormDia.value)
- if (qrCodeFormDia.value) {
- console.log('nextTick鍚庤皟鐢ㄥ脊妗唎penDialog鏂规硶')
- qrCodeFormDia.value.openDialog(result)
- } else {
- console.error('寮规缁勪欢寮曠敤涓嶅瓨鍦�')
- uni.showToast({
- title: '寮规缁勪欢鏈噯澶囧ソ',
- icon: 'error'
- })
- }
- })
- }
-}
+ // 鏍规嵁褰撳墠涓婁紶绫诲瀷璁剧疆type瀛楁
+ let typeValue = 0; // 榛樿涓虹敓浜у墠
+ switch (currentUploadType.value) {
+ case "before":
+ typeValue = 0;
+ break;
+ case "after":
+ typeValue = 1;
+ break;
+ case "issue":
+ typeValue = 2;
+ break;
+ }
+ // 纭繚涓婁紶鐨勬枃浠舵暟鎹畬鏁达紝鍖呭惈id鍜宼ype
+ const fileData = {
+ ...file,
+ id: uploadedFile.id, // 娣诲姞鏈嶅姟鍣ㄨ繑鍥炵殑id
+ tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id,
+ url:
+ uploadedFile.url ||
+ uploadedFile.downloadUrl ||
+ file.tempFilePath ||
+ file.path,
+ bucketFilename:
+ uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name,
+ downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
+ size: uploadedFile.size || uploadedFile.byteSize || file.size,
+ createTime: uploadedFile.createTime || new Date().getTime(),
+ type: typeValue, // 娣诲姞绫诲瀷瀛楁锛�0=鐢熶骇鍓�, 1=鐢熶骇涓�, 2=鐢熶骇鍚�
+ };
-// 鎵爜澶勭悊
-const handleScanCode = (result) => {
- console.log('鎵爜缁撴灉:', result)
- handleScanSuccess(result)
-}
+ uploadList.value.push(fileData);
-// 鎽勫儚澶撮敊璇鐞�
-const handleCameraError = (error) => {
- console.error('鎽勫儚澶撮敊璇�:', error)
- cameraError.value = '鎽勫儚澶磋闂け璐ワ紝璇锋鏌ユ潈闄愯缃�'
-}
+ // 绔嬪嵆娣诲姞鍒板搴旂殑鍒嗙被锛屼笉绛夊緟鎵�鏈夋枃浠朵笂浼犲畬鎴�
+ switch (currentUploadType.value) {
+ case "before":
+ beforeModelValue.value.push(fileData);
+ break;
+ case "after":
+ afterModelValue.value.push(fileData);
+ break;
+ case "issue":
+ issueModelValue.value.push(fileData);
+ break;
+ }
+
+ // 閲嶇疆涓婁紶鍒楄〃锛堝洜涓哄凡缁忔坊鍔犲埌瀵瑰簲鍒嗙被浜嗭級
+ uploadList.value = [];
+ number.value = 0;
+ };
+
+ // 涓婁紶缁撴潫澶勭悊锛堝凡搴熷純锛岀幇鍦ㄥ湪handleUploadSuccess涓洿鎺ュ鐞嗭級
+ const uploadedSuccessfully = () => {
+ // 姝ゅ嚱鏁板凡涓嶅啀浣跨敤锛屾枃浠朵笂浼犳垚鍔熷悗绔嬪嵆娣诲姞鍒板搴斿垎绫�
+ };
+
+ // 鏍煎紡鍖栨枃浠跺ぇ灏�
+ const formatFileSize = size => {
+ if (!size) return "";
+ if (size < 1024) return size + "B";
+ if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB";
+ return (size / (1024 * 1024)).toFixed(1) + "MB";
+ };
</script>
-<style scoped lang="scss">
-.inspection-upload-page {
- min-height: 100vh;
- background-color: #f5f5f5;
-}
-
-.tabs-container {
- background-color: #fff;
- margin: 0;
- border-bottom: 1px solid #e8e8e8;
-}
-
-.custom-tabs {
- display: flex;
- position: relative;
- background-color: #fff;
- width: 100%;
-}
-
-.tab-item {
- flex: 1;
- text-align: center;
- padding: 20px 0;
- font-size: 16px;
- font-weight: 500;
- color: #606266;
- transition: all 0.3s ease;
- cursor: pointer;
- position: relative;
- z-index: 2;
-}
-
-.tab-item.tab-active {
- color: #1890ff;
- font-weight: 600;
-}
-
-.tab-line {
- position: absolute;
- bottom: 0;
- width: 50%;
- height: 3px;
- background-color: #1890ff;
- transition: left 0.3s ease;
-}
-
-.scan-section {
- background-color: #fff;
- padding: 10px;
-}
-
-.scan-controls {
- display: flex;
- justify-content: center;
-}
-
-.qr-scan-container {
- position: relative;
- width: 100%;
- max-width: 500px;
- margin: 0 auto;
- background: #000;
- border-radius: 8px;
- overflow: hidden;
-}
-
-.qr-camera {
- width: 100%;
- height: 300px;
-}
-
-.scan-overlay {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 70%;
- height: 70%;
- border: 3px solid #1890ff;
- border-radius: 8px;
- box-shadow: 0 0 20px rgba(24, 144, 255, 0.3);
- animation: pulse 2s infinite;
-}
-
-.scan-frame {
- width: 100%;
- height: 100%;
- border: 2px solid #fff;
- border-radius: 4px;
-}
-
-.scan-tip {
- position: absolute;
- bottom: -30px;
- left: 50%;
- transform: translateX(-50%);
- color: #fff;
- font-size: 14px;
- text-align: center;
-}
-
-@keyframes pulse {
- 0% { opacity: 0.8; }
- 50% { opacity: 0.4; }
- 100% { opacity: 0.8; }
-}
-
-.status-info {
- margin-top: 16px;
- text-align: center;
-}
-
-.scanning-text {
- display: flex;
- align-items: center;
- justify-content: center;
- color: #1890ff;
- margin-top: 8px;
-}
-
-.scanning-label {
- margin-left: 8px;
- font-size: 14px;
-}
-
-.table-section {
- padding: 0 15px;
-}
-
-.task-list, .qr-list {
- .task-item, .qr-item {
- background-color: #fff;
- border-radius: 8px;
- margin-bottom: 10px;
- padding: 15px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+<style scoped>
+ .inspection-upload-page {
+ min-height: 100vh;
+ background-color: #f5f5f5;
}
-}
-.task-header, .qr-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 10px;
-}
+ .table-section {
+ padding: 15px;
+ }
-.task-info, .qr-info {
- flex: 1;
-}
+ .task-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
-.task-name, .device-name {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin-bottom: 4px;
-}
+ .task-item {
+ background: #fff;
+ border-radius: 12px;
+ padding: 15px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+ }
-.task-location, .device-location {
- font-size: 14px;
- color: #666;
-}
+ .task-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 12px;
+ }
-.task-actions, .qr-actions {
- margin-left: 10px;
-}
+ .task-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
-.task-details, .qr-details {
+ .task-name {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .task-location {
+ font-size: 14px;
+ color: #666;
+ }
+
+ .task-actions {
+ display: flex;
+ gap: 8px;
+ }
+
+ .task-details {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ }
+
.detail-item {
display: flex;
- margin-bottom: 6px;
-
- .detail-label {
- font-size: 14px;
- color: #666;
- min-width: 60px;
- }
-
- .detail-value {
- font-size: 14px;
- color: #333;
- flex: 1;
- }
+ justify-content: space-between;
+ align-items: center;
}
-}
-.empty-state, .loading-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 20px;
- background-color: #fff;
- border-radius: 8px;
- margin: 10px 15px;
-}
+ .detail-label {
+ font-size: 12px;
+ color: #999;
+ }
-.loading-text {
- margin-top: 10px;
- font-size: 14px;
- color: #666;
-}
+ .detail-value {
+ font-size: 12px;
+ color: #666;
+ font-weight: 500;
+ }
-.pagination-container {
- padding: 20px 15px;
- background-color: #fff;
- margin-top: 10px;
-}
+ .no-data {
+ text-align: center;
+ padding: 40px 20px;
+ color: #999;
+ font-size: 14px;
+ }
+
+ /* 鎵爜寮圭獥鏍峰紡 */
+ .qr-scan-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 9999;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .qr-scan-container {
+ width: 90vw;
+ max-width: 400px;
+ background: #fff;
+ border-radius: 12px;
+ padding: 20px;
+ position: relative;
+ }
+
+ .scan-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ }
+
+ .scan-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .qr-camera {
+ width: 100%;
+ height: 300px;
+ border-radius: 8px;
+ overflow: hidden;
+ margin-bottom: 15px;
+ }
+
+ .scan-frame-wrapper {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .scan-frame {
+ width: 200px;
+ height: 200px;
+ border: 2px solid #409eff;
+ border-radius: 8px;
+ position: relative;
+ }
+
+ .scan-frame::before,
+ .scan-frame::after {
+ content: "";
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ border: 2px solid #409eff;
+ }
+
+ .scan-frame::before {
+ top: -2px;
+ left: -2px;
+ border-right: none;
+ border-bottom: none;
+ }
+
+ .scan-frame::after {
+ bottom: -2px;
+ right: -2px;
+ border-left: none;
+ border-top: none;
+ }
+
+ .scan-tip {
+ margin-top: 10px;
+ font-size: 14px;
+ color: #666;
+ text-align: center;
+ }
+
+ /* 鑷畾涔夋ā鎬佹鏍峰紡 */
+ .custom-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 10000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ }
+
+ .custom-modal-container {
+ width: 100%;
+ max-width: 500px;
+ max-height: 80vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ /* 涓婁紶寮圭獥鏍峰紡 */
+ .upload-popup-content {
+ background: #fff;
+ border-radius: 12px;
+ width: 100%;
+ min-height: 300px;
+ max-height: 70vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+ }
+
+ .upload-popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ border-bottom: 1px solid #eee;
+ }
+
+ .upload-popup-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .upload-popup-body {
+ flex: 1;
+ padding: 20px;
+ overflow-y: auto;
+ }
+
+ .upload-popup-footer {
+ display: flex;
+ justify-content: flex-end;
+ padding: 15px 20px;
+ border-top: 1px solid #eee;
+ gap: 10px;
+ }
+
+ /* 绠�鍖栦笂浼犵粍浠舵牱寮� */
+ .simple-upload-area {
+ padding: 15px;
+ }
+
+ .upload-buttons {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 15px;
+ }
+
+ .file-list {
+ margin-top: 15px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ }
+
+ .file-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: #fff;
+ border-radius: 12px;
+ padding: 8px;
+ border: 1px solid #e9ecef;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: all 0.3s ease;
+ width: calc(50% - 6px);
+ min-width: 120px;
+ }
+
+ .file-item:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+ }
+
+ .file-preview-container {
+ position: relative;
+ margin-bottom: 8px;
+ }
+
+ .file-preview {
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ object-fit: cover;
+ border: 2px solid #f0f0f0;
+ }
+
+ .video-preview {
+ width: 80px;
+ height: 80px;
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid #f0f0f0;
+ }
+
+ .video-text {
+ font-size: 12px;
+ color: #666;
+ margin-top: 4px;
+ }
+
+ .delete-btn {
+ position: absolute;
+ top: -6px;
+ right: -6px;
+ width: 20px;
+ height: 20px;
+ background: #ff4757;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(255, 71, 87, 0.3);
+ transition: all 0.3s ease;
+ }
+
+ .delete-btn:hover {
+ background: #ff3742;
+ transform: scale(1.1);
+ }
+
+ .file-info {
+ text-align: center;
+ width: 100%;
+ }
+
+ .file-name {
+ font-size: 12px;
+ color: #333;
+ font-weight: 500;
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100px;
+ }
+
+ .file-size {
+ font-size: 10px;
+ color: #999;
+ margin-top: 2px;
+ display: block;
+ }
+
+ .empty-state {
+ text-align: center;
+ padding: 40px 20px;
+ color: #999;
+ font-size: 14px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ border: 2px dashed #ddd;
+ }
+
+ .upload-progress {
+ margin: 15px 0;
+ padding: 0 10px;
+ }
+
+ /* 涓婁紶鏍囩椤垫牱寮� */
+ .upload-tabs {
+ display: flex;
+ background: #f8f9fa;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ padding: 4px;
+ }
+
+ .tab-item {
+ flex: 1;
+ text-align: center;
+ padding: 8px 12px;
+ font-size: 14px;
+ color: #666;
+ border-radius: 6px;
+ transition: all 0.3s ease;
+ cursor: pointer;
+ }
+
+ .tab-item.active {
+ background: #409eff;
+ color: #fff;
+ font-weight: 500;
+ }
+
+ .tab-item:hover:not(.active) {
+ background: #e9ecef;
+ color: #333;
+ }
+
+ /* 寮傚父鐘舵�侀�夋嫨鏍峰紡 */
+ .exception-section {
+ margin-bottom: 20px;
+ padding: 15px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ border: 1px solid #e9ecef;
+ }
+
+ .section-title {
+ display: block;
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 12px;
+ }
+
+ .exception-options {
+ display: flex;
+ gap: 12px;
+ }
+
+ .exception-option {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 12px 16px;
+ background: #fff;
+ border: 2px solid #e9ecef;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-size: 14px;
+ color: #666;
+ }
+
+ .exception-option.active {
+ border-color: #409eff;
+ background: #f0f8ff;
+ color: #409eff;
+ font-weight: 500;
+ }
+
+ .exception-option:hover:not(.active) {
+ border-color: #d9d9d9;
+ background: #fafafa;
+ }
+
+ /* 缁熻淇℃伅鏍峰紡 */
+ .upload-summary {
+ margin-top: 15px;
+ padding: 10px;
+ background: #f8f9fa;
+ border-radius: 6px;
+ border-left: 3px solid #409eff;
+ }
+
+ .summary-text {
+ font-size: 12px;
+ color: #666;
+ line-height: 1.4;
+ }
+
+ /* 鏌ョ湅闄勪欢寮圭獥鏍峰紡 */
+ .attachment-popup-content {
+ background: #fff;
+ border-radius: 12px;
+ width: 100%;
+ min-height: 400px;
+ max-height: 70vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+ }
+
+ .attachment-popup-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ border-bottom: 1px solid #eee;
+ background: #f8f9fa;
+ }
+
+ .attachment-popup-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .close-btn-attachment {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: #f5f5f5;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .close-btn-attachment:hover {
+ background: #e9ecef;
+ transform: scale(1.1);
+ }
+
+ .attachment-popup-body {
+ flex: 1;
+ padding: 15px 20px;
+ overflow-y: auto;
+ }
+
+ .attachment-tabs {
+ display: flex;
+ background: #f8f9fa;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ padding: 4px;
+ }
+
+ .attachment-content {
+ min-height: 200px;
+ }
+
+ .attachment-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ }
+
+ .attachment-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: #fff;
+ border-radius: 12px;
+ padding: 8px;
+ border: 1px solid #e9ecef;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: all 0.3s ease;
+ width: calc(33.33% - 8px);
+ min-width: 100px;
+ cursor: pointer;
+ }
+
+ .attachment-item:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+ }
+
+ .attachment-preview-container {
+ margin-bottom: 8px;
+ }
+
+ .attachment-preview {
+ width: 80px;
+ height: 80px;
+ border-radius: 8px;
+ object-fit: cover;
+ border: 2px solid #f0f0f0;
+ }
+
+ .attachment-video-preview {
+ width: 80px;
+ height: 80px;
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid #f0f0f0;
+ }
+
+ .attachment-info {
+ text-align: center;
+ width: 100%;
+ }
+
+ .attachment-name {
+ font-size: 12px;
+ color: #333;
+ font-weight: 500;
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 80px;
+ }
+
+ .attachment-size {
+ font-size: 10px;
+ color: #999;
+ margin-top: 2px;
+ display: block;
+ }
+
+ .attachment-empty {
+ text-align: center;
+ padding: 60px 20px;
+ color: #999;
+ font-size: 14px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ border: 2px dashed #ddd;
+ }
+
+ /* 瑙嗛棰勮寮圭獥鏍峰紡 */
+ .video-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 10001;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ }
+
+ .video-modal-container {
+ width: 90%;
+ max-width: 800px;
+ max-height: 80vh;
+ background: #000;
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+ }
+
+ .video-modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 20px;
+ background: rgba(0, 0, 0, 0.7);
+ color: #fff;
+ }
+
+ .video-modal-title {
+ font-size: 16px;
+ font-weight: 500;
+ color: #fff;
+ }
+
+ .close-btn-video {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .close-btn-video:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: scale(1.1);
+ }
+
+ .video-modal-body {
+ position: relative;
+ background: #000;
+ }
+
+ .video-player {
+ width: 100%;
+ height: auto;
+ max-height: 60vh;
+ display: block;
+ }
</style>
\ No newline at end of file
--
Gitblit v1.9.3