|
@@ -0,0 +1,693 @@
|
|
|
|
|
+ <template>
|
|
|
|
|
+ <div class="container">
|
|
|
|
|
+ <div class="processing-photo-task">
|
|
|
|
|
+ <div class="top">
|
|
|
|
|
+ <div class="nav-bar">
|
|
|
|
|
+ <van-nav-bar
|
|
|
|
|
+ title="加工拍照任务"
|
|
|
|
|
+ left-arrow
|
|
|
|
|
+ @click-left="goBack"
|
|
|
|
|
+ @click-right="onRefresh"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #left>
|
|
|
|
|
+ <van-icon name="arrow-left" size="25" />
|
|
|
|
|
+ <div
|
|
|
|
|
+ style="
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ height: 46px;
|
|
|
|
|
+ padding-right: 20px;
|
|
|
|
|
+ line-height: 46px;
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ 返回
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #right>
|
|
|
|
|
+ <div class="nav-right" style="color: #fff">重置</div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-nav-bar>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="context">
|
|
|
|
|
+ <!-- 筛选区域 -->
|
|
|
|
|
+ <div class="filter-section">
|
|
|
|
|
+ <van-row gutter="10">
|
|
|
|
|
+ <van-col span="8">
|
|
|
|
|
+ <van-field
|
|
|
|
|
+ v-model="filterForm.owners"
|
|
|
|
|
+ placeholder="请选择货主"
|
|
|
|
|
+ readonly
|
|
|
|
|
+ clickable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ clear-trigger="always"
|
|
|
|
|
+ @click="showOwnerDialog"
|
|
|
|
|
+ @clear="onOwnerClear"
|
|
|
|
|
+ />
|
|
|
|
|
+ </van-col>
|
|
|
|
|
+ <van-col span="8">
|
|
|
|
|
+ <van-field
|
|
|
|
|
+ v-model="filterForm.status"
|
|
|
|
|
+ placeholder="请选择状态"
|
|
|
|
|
+ readonly
|
|
|
|
|
+ clickable
|
|
|
|
|
+ @click="showStatusPicker = true"
|
|
|
|
|
+ />
|
|
|
|
|
+ </van-col>
|
|
|
|
|
+ <van-col span="8">
|
|
|
|
|
+ <van-button type="primary" size="small" block @click="onFilterSearch">
|
|
|
|
|
+ 搜索
|
|
|
|
|
+ </van-button>
|
|
|
|
|
+ </van-col>
|
|
|
|
|
+ </van-row>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <van-pull-refresh
|
|
|
|
|
+ v-model="loading"
|
|
|
|
|
+ @refresh="onRefresh"
|
|
|
|
|
+ :style="{
|
|
|
|
|
+ 'max-height': computedMaxHeight,
|
|
|
|
|
+ 'min-height': computedMaxHeight,
|
|
|
|
|
+ overflow: 'auto',
|
|
|
|
|
+ }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <table class="photo-task-table">
|
|
|
|
|
+ <thead>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th style="width: 25%">货主</th>
|
|
|
|
|
+ <th style="width: 35%">加工单号</th>
|
|
|
|
|
+ <th style="width: 15%">状态</th>
|
|
|
|
|
+ <th style="width: 12.5%">查看</th>
|
|
|
|
|
+ <th style="width: 12.5%">操作</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody>
|
|
|
|
|
+ <tr v-for="(row, rowIndex) in photoTasks" :key="rowIndex">
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <span class="text-compact">{{ row.ownerName }}</span>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <span class="text-sm code-link" @click="showNotes(row)">{{ row.code }}</span>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <span class="text-compact">{{ row.status }}</span>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <van-button type="primary" plain size="small" @click="showPhotos(row)" class="compact-button"
|
|
|
|
|
+ >查看
|
|
|
|
|
+ </van-button>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <van-button v-if="row.status !== '完成'" type="primary" size="small" @click="toPhoto(row)" class="compact-button"
|
|
|
|
|
+ >拍摄
|
|
|
|
|
+ </van-button>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 分页控件 -->
|
|
|
|
|
+ <van-pagination
|
|
|
|
|
+ v-model="paginate.page"
|
|
|
|
|
+ :page-size="paginate.size"
|
|
|
|
|
+ :total-items="paginate.total"
|
|
|
|
|
+ :page-count="paginate.pages"
|
|
|
|
|
+ :show-page-size="5"
|
|
|
|
|
+ model="simple"
|
|
|
|
|
+ forced="true"
|
|
|
|
|
+ @change="onPageChange"
|
|
|
|
|
+ class="pagination"
|
|
|
|
|
+ />
|
|
|
|
|
+ </van-pull-refresh>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 货主选择器弹窗 -->
|
|
|
|
|
+ <Owner
|
|
|
|
|
+ ref="ownerRef"
|
|
|
|
|
+ @onOwner="onOwnerSelect"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 状态选择器弹窗 -->
|
|
|
|
|
+ <van-popup v-model:show="showStatusPicker" position="bottom">
|
|
|
|
|
+ <van-picker
|
|
|
|
|
+ :columns="statusOptions"
|
|
|
|
|
+ @confirm="onStatusConfirm"
|
|
|
|
|
+ @cancel="showStatusPicker = false"
|
|
|
|
|
+ />
|
|
|
|
|
+ </van-popup>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 图片上传弹窗 -->
|
|
|
|
|
+ <van-popup v-model:show="showUpload" position="bottom" :style="{ height: '80%' }">
|
|
|
|
|
+ <div class="upload-popup">
|
|
|
|
|
+ <van-nav-bar
|
|
|
|
|
+ title="图片上传"
|
|
|
|
|
+ left-arrow
|
|
|
|
|
+ @click-left="showUpload = false"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #left>
|
|
|
|
|
+ <van-icon name="arrow-left" size="25" />
|
|
|
|
|
+ <div style="color: #fff; height: 46px; padding-right: 20px; line-height: 46px;">
|
|
|
|
|
+ 返回
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-nav-bar>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="upload-content">
|
|
|
|
|
+ <van-uploader
|
|
|
|
|
+ v-model="uploadImages"
|
|
|
|
|
+ :max-count="9"
|
|
|
|
|
+ :max-size="10 * 1024 * 1024"
|
|
|
|
|
+ :before-read="beforeReadImage"
|
|
|
|
|
+ :after-read="afterReadImage"
|
|
|
|
|
+ multiple
|
|
|
|
|
+ preview-full-image
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <div class="upload-footer">
|
|
|
|
|
+ <van-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ block
|
|
|
|
|
+ :loading="uploading"
|
|
|
|
|
+ @click="submitUpload"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ uploading ? '上传中...' : '提交上传' }}
|
|
|
|
|
+ </van-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </van-popup>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
|
|
+import { showNotify, showImagePreview, showFailToast, showLoadingToast, closeToast, showDialog } from 'vant'
|
|
|
|
|
+import {
|
|
|
|
|
+ getProcessingPhotoTask,
|
|
|
|
|
+ processingPhotoTaskItems,
|
|
|
|
|
+ uploadPhoto,status
|
|
|
|
|
+} from '@/api/processing/index'
|
|
|
|
|
+import { getHeader, goBack } from '@/utils/android'
|
|
|
|
|
+import { compressImage } from '@/utils/imageCompression'
|
|
|
|
|
+import Owner from '@/components/Owner.vue'
|
|
|
|
|
+import { useStore } from '@/store/modules/user'
|
|
|
|
|
+try {
|
|
|
|
|
+ getHeader()
|
|
|
|
|
+} catch (error) {
|
|
|
|
|
+ console.log(error)
|
|
|
|
|
+}
|
|
|
|
|
+const store = useStore()
|
|
|
|
|
+const warehouse = store.warehouse
|
|
|
|
|
+
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const photoTasks = ref([])
|
|
|
|
|
+const showUpload = ref(false)
|
|
|
|
|
+const uploadImages = ref([])
|
|
|
|
|
+const uploading = ref(false)
|
|
|
|
|
+const currentTaskId = ref(null)
|
|
|
|
|
+const ownerRef = ref(null)
|
|
|
|
|
+
|
|
|
|
|
+const paginate = ref({
|
|
|
|
|
+ page: 1,
|
|
|
|
|
+ size: 20,
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ pages: 0,
|
|
|
|
|
+})
|
|
|
|
|
+const reviewImages = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// 筛选相关数据
|
|
|
|
|
+const filterForm = ref({
|
|
|
|
|
+ owners: '',
|
|
|
|
|
+ ownerCode: '',
|
|
|
|
|
+ status: '',
|
|
|
|
|
+ statusCode: ''
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const showStatusPicker = ref(false)
|
|
|
|
|
+const statusOptions = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// 计算最大高度适配手持设备
|
|
|
|
|
+const computedMaxHeight = computed(() => {
|
|
|
|
|
+ // 减去导航栏高度(46px)和筛选区域高度(约80px)
|
|
|
|
|
+ return window.innerHeight - 46 - 80 + 'px'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 获取从其他页面传递的参数
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ // 可以从route.query或route.params中获取参数
|
|
|
|
|
+ if (route.query.id) {
|
|
|
|
|
+ // 如果有传入id参数,可以做特殊处理
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ status().then(res=>{
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ statusOptions.value = res.data.map(item=>{
|
|
|
|
|
+ return {text:item.name,value:item.code}
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 显示notes信息 - 改为弹框方式
|
|
|
|
|
+function showNotes(row) {
|
|
|
|
|
+ const notes = row.notes || '暂无备注信息'
|
|
|
|
|
+ showDialog({
|
|
|
|
|
+ title: '任务备注',
|
|
|
|
|
+ message: notes,
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ messageAlign: 'left'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function onRefresh() {
|
|
|
|
|
+ // 刷新时重置到第一页
|
|
|
|
|
+ paginate.value.page = 1
|
|
|
|
|
+ filterForm.value.owners = null
|
|
|
|
|
+ filterForm.value.ownerCode = null
|
|
|
|
|
+ filterForm.value.status = null
|
|
|
|
|
+ filterForm.value.statusCode = null
|
|
|
|
|
+ listProcessingPhoto()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function listProcessingPhoto() {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+ // 合并筛选条件和分页参数,使用statusCode而不是status
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ ...paginate.value,
|
|
|
|
|
+ warehouses: warehouse,
|
|
|
|
|
+ owners: filterForm.value.ownerCode,
|
|
|
|
|
+ status: filterForm.value.statusCode
|
|
|
|
|
+ }
|
|
|
|
|
+ getProcessingPhotoTask(params).then((res) => {
|
|
|
|
|
+ photoTasks.value = res?.data?.records || []
|
|
|
|
|
+ // 更新分页信息
|
|
|
|
|
+ if (res?.data) {
|
|
|
|
|
+ paginate.value.size = res.data.size || 0
|
|
|
|
|
+ paginate.value.total = res.data.total || 0
|
|
|
|
|
+ paginate.value.page = res.data.current || 0
|
|
|
|
|
+ paginate.value.pages = res.data.pages || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 分页变更处理
|
|
|
|
|
+function onPageChange(page) {
|
|
|
|
|
+ paginate.value.page = page
|
|
|
|
|
+ listProcessingPhoto()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function showPhotos(row) {
|
|
|
|
|
+ processingPhotoTaskItems(row.id).then((res) => {
|
|
|
|
|
+ const items = res.data
|
|
|
|
|
+ if (!items || items.length === 0) {
|
|
|
|
|
+ showNotify({ type: 'warning', message: '没有对应的拍摄' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const baseUrl = import.meta.env.VITE_APP_BASE_API + 'statics/storage/'
|
|
|
|
|
+ const paths = baseUrl.split('.')
|
|
|
|
|
+ paths[1] = 'swms'
|
|
|
|
|
+ const base = paths.join('.')
|
|
|
|
|
+ // 解构图片url
|
|
|
|
|
+ reviewImages.value = items.map(item => {
|
|
|
|
|
+ const {path,module} = item
|
|
|
|
|
+ return `${base}${module}/${path}`
|
|
|
|
|
+ }).filter(url => url)
|
|
|
|
|
+
|
|
|
|
|
+ // 预览图片
|
|
|
|
|
+ if (reviewImages.value.length > 0) {
|
|
|
|
|
+ showImagePreview({
|
|
|
|
|
+ images: reviewImages.value,
|
|
|
|
|
+ closeable: true,
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showNotify({ type: 'warning', message: '没有可预览的图片' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ showNotify({ type: 'danger', message: '获取图片失败' })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function toPhoto(row) {
|
|
|
|
|
+ currentTaskId.value = row.id
|
|
|
|
|
+ uploadImages.value = []
|
|
|
|
|
+ showUpload.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 检查图片格式和大小
|
|
|
|
|
+const beforeReadImage = (file) => {
|
|
|
|
|
+ const files = Array.isArray(file) ? file : [file]
|
|
|
|
|
+ for (const f of files) {
|
|
|
|
|
+ // 检查文件格式
|
|
|
|
|
+ if (f.name.toLowerCase().endsWith('.heic') || f.name.toLowerCase().endsWith('.heif')) {
|
|
|
|
|
+ showFailToast('不支持HEIC/HEIF格式的图片')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查文件类型
|
|
|
|
|
+ const isImage = /^image\//.test(f.type)
|
|
|
|
|
+ if (!isImage) {
|
|
|
|
|
+ showFailToast('请上传图片文件')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查文件大小 (10MB)
|
|
|
|
|
+ if (f.size > 10 * 1024 * 1024) {
|
|
|
|
|
+ showFailToast('图片大小不能超过10MB')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 图片读取完成后处理
|
|
|
|
|
+const afterReadImage = async (file) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 处理单个文件或多个文件
|
|
|
|
|
+ const files = Array.isArray(file) ? file : [file]
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = 0; i < files.length; i++) {
|
|
|
|
|
+ const fileItem = files[i]
|
|
|
|
|
+ const originalFile = fileItem.file
|
|
|
|
|
+
|
|
|
|
|
+ if (originalFile) {
|
|
|
|
|
+ // 检查文件大小,如果大于1MB则进行压缩
|
|
|
|
|
+ if (originalFile.size > 1 * 1024 * 1024) {
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'primary',
|
|
|
|
|
+ message: `正在压缩图片: ${originalFile.name} (${(originalFile.size / 1024 / 1024).toFixed(2)}MB)`
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 压缩图片到1MB以内
|
|
|
|
|
+ const compressedFile = await compressImage(originalFile, 1 * 1024 * 1024)
|
|
|
|
|
+
|
|
|
|
|
+ // 更新文件对象
|
|
|
|
|
+ fileItem.file = compressedFile
|
|
|
|
|
+ fileItem.content = URL.createObjectURL(compressedFile)
|
|
|
|
|
+
|
|
|
|
|
+ // 显示压缩结果
|
|
|
|
|
+ const compressionRatio = ((originalFile.size - compressedFile.size) / originalFile.size * 100).toFixed(1)
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'success',
|
|
|
|
|
+ message: `图片压缩完成: ${(compressedFile.size / 1024 / 1024).toFixed(2)}MB (压缩${compressionRatio}%)`
|
|
|
|
|
+ })
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('图片压缩失败:', error)
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'warning',
|
|
|
|
|
+ message: `图片压缩失败,将使用原图上传: ${error.message}`
|
|
|
|
|
+ })
|
|
|
|
|
+ // 压缩失败时保持原文件
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log(`图片大小符合要求,无需压缩: ${(originalFile.size / 1024).toFixed(2)}KB`)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('处理图片时发生错误:', error)
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'danger',
|
|
|
|
|
+ message: '处理图片时发生错误: ' + error.message
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 提交上传
|
|
|
|
|
+const submitUpload = async () => {
|
|
|
|
|
+ if (!uploadImages.value.length) {
|
|
|
|
|
+ showFailToast('请先选择图片')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!currentTaskId.value) {
|
|
|
|
|
+ showFailToast('任务ID无效')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uploading.value = true
|
|
|
|
|
+ const toast = showLoadingToast('上传中...')
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const totalImages = uploadImages.value.length
|
|
|
|
|
+ let successCount = 0
|
|
|
|
|
+ let failedImages = []
|
|
|
|
|
+
|
|
|
|
|
+ // 逐个上传图片
|
|
|
|
|
+ const failedMessages = []
|
|
|
|
|
+ for (let i = 0; i < uploadImages.value.length; i++) {
|
|
|
|
|
+ const image = uploadImages.value[i]
|
|
|
|
|
+ try {
|
|
|
|
|
+ const formData = new FormData()
|
|
|
|
|
+ formData.append('file', image.file)
|
|
|
|
|
+ // 调用上传API
|
|
|
|
|
+ await uploadPhoto(currentTaskId.value, formData)
|
|
|
|
|
+ successCount++
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ failedMessages.push(`${i+1}/${uploadImages.value.length}张图片上传失败:${err.message}`)
|
|
|
|
|
+ console.error(`第${i + 1}张图片上传失败:`, err.message)
|
|
|
|
|
+ failedImages.push({
|
|
|
|
|
+ index: i,
|
|
|
|
|
+ image: image,
|
|
|
|
|
+ error: err.message || '上传失败'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ closeToast()
|
|
|
|
|
+
|
|
|
|
|
+ if (failedMessages.length !== 0) {
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'danger',
|
|
|
|
|
+ message: failedMessages.join("\\t\\n")
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (successCount === totalImages) {
|
|
|
|
|
+ // 全部上传成功
|
|
|
|
|
+ showNotify({ type: 'success', message: '全部图片上传成功' })
|
|
|
|
|
+ // 关闭弹窗并刷新列表
|
|
|
|
|
+ showUpload.value = false
|
|
|
|
|
+ uploadImages.value = []
|
|
|
|
|
+ onRefresh()
|
|
|
|
|
+ } else if (successCount > 0) {
|
|
|
|
|
+ // 部分上传成功
|
|
|
|
|
+ showNotify({
|
|
|
|
|
+ type: 'warning',
|
|
|
|
|
+ message: `${successCount}/${totalImages}张图片上传成功,${failedImages.length}张失败`
|
|
|
|
|
+ })
|
|
|
|
|
+ // 移除上传成功的图片,保留失败的图片
|
|
|
|
|
+ uploadImages.value = failedImages.map(item => item.image)
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ closeToast()
|
|
|
|
|
+ showNotify({ type: 'danger', message: '上传过程发生异常: ' + (err.message || '未知错误') })
|
|
|
|
|
+ // 发生异常时保留所有图片,让用户可以重试
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ uploading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function onFilterSearch() {
|
|
|
|
|
+ // 搜索时重置到第一页
|
|
|
|
|
+ paginate.value.page = 1
|
|
|
|
|
+ listProcessingPhoto()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 显示货主选择弹窗
|
|
|
|
|
+function showOwnerDialog() {
|
|
|
|
|
+ if (ownerRef.value) {
|
|
|
|
|
+ ownerRef.value.show('filter')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理货主选择
|
|
|
|
|
+function onOwnerSelect(item, type) {
|
|
|
|
|
+ filterForm.value.owners = item.name
|
|
|
|
|
+ filterForm.value.ownerCode = item.code
|
|
|
|
|
+ // 选择货主后自动搜索item
|
|
|
|
|
+ onFilterSearch()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理货主清空
|
|
|
|
|
+function onOwnerClear() {
|
|
|
|
|
+ filterForm.value.owners = ''
|
|
|
|
|
+ filterForm.value.ownerCode = ''
|
|
|
|
|
+ // 清空货主后自动搜索
|
|
|
|
|
+ onFilterSearch()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function onStatusConfirm({ selectedOptions }) {
|
|
|
|
|
+ filterForm.value.status = selectedOptions[0].text
|
|
|
|
|
+ filterForm.value.statusCode = selectedOptions[0].value
|
|
|
|
|
+ showStatusPicker.value = false
|
|
|
|
|
+ // 选择状态后自动搜索
|
|
|
|
|
+ onFilterSearch()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 初始化加载数据
|
|
|
|
|
+onRefresh()
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+// 固定顶部区域样式
|
|
|
|
|
+.top {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ z-index: 1000;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); // 添加阴影效果
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.nav-bar {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1001;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.context {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 为整个容器添加上边距,避免内容被固定区域遮挡
|
|
|
|
|
+.container {
|
|
|
|
|
+ padding-top: 126px; // 导航栏46px + 筛选区域约80px
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 确保van-field的清空按钮样式正确显示
|
|
|
|
|
+:deep(.van-field__clear) {
|
|
|
|
|
+ display: flex !important;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ color: #c8c9cc;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+
|
|
|
|
|
+ &:active {
|
|
|
|
|
+ color: #969799;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 确保van-field右侧区域有足够空间显示清空按钮
|
|
|
|
|
+:deep(.van-field__right-icon) {
|
|
|
|
|
+ display: flex !important;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.photo-task-table {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ border-collapse: collapse;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ table-layout: fixed;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+
|
|
|
|
|
+ thead {
|
|
|
|
|
+ tr {
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+
|
|
|
|
|
+ th {
|
|
|
|
|
+ padding: 6px 2px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ tbody {
|
|
|
|
|
+ tr {
|
|
|
|
|
+ height: 36px;
|
|
|
|
|
+ border-bottom: 1px solid #e0e0e0;
|
|
|
|
|
+
|
|
|
|
|
+ &:last-child {
|
|
|
|
|
+ border-bottom: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ td {
|
|
|
|
|
+ padding: 3px 2px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+
|
|
|
|
|
+ .text-compact {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 1.2;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .text-sm {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ line-height: 1.2;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .compact-button {
|
|
|
|
|
+ height: 24px;
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+ padding: 0 6px;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ min-width: 36px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.pagination {
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-popup {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+
|
|
|
|
|
+ .upload-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+
|
|
|
|
|
+ .upload-footer {
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.filter-section {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-bottom: 1px solid #e0e0e0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 移除之前的padding-top,因为已经在.container中设置了
|
|
|
|
|
+
|
|
|
|
|
+:deep(.van-pull-refresh) {
|
|
|
|
|
+ // 调整滚动区域的高度计算,减去固定的导航栏和筛选区域高度
|
|
|
|
|
+ max-height: calc(100vh - 126px) !important; // 总共减去126px
|
|
|
|
|
+ min-height: calc(100vh - 126px) !important;
|
|
|
|
|
+ overflow: auto !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.code-link {
|
|
|
|
|
+ color: #1989fa;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ text-decoration: underline;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ color: #0570db;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|