Jelajahi Sumber

Merge branch 'zengjun/计件/20250925加工拍照任务' into testing

# Conflicts:
#	src/api/processing/index.ts
#	src/hooks/basic/menu.js
#	src/router/index.ts
#	src/views/outbound/check/activity/index.vue
zengjun 6 bulan lalu
induk
melakukan
f233f1d766

+ 3 - 1
src/api/processing/index.ts

@@ -1,5 +1,6 @@
 // @ts-ignore
 import request from '@/utils/request'
+
 /**
  * 加工-获取加工类型
  * @param params
@@ -10,11 +11,12 @@ export function getProcessingTypeList() {
     method: 'get',
   })
 }
+
 /**
  * 加工-创建加工单
  * @param params
  */
-export function createProcessing(data:any) {
+export function createProcessing(data: any) {
   return request({
     url: 'api/device/check/processing/reference',
     method: 'post',

+ 5 - 1
src/hooks/basic/menu.js

@@ -124,7 +124,11 @@ export default function() {
               icon: 'newspaper-o',
               path: 'container-operation',
             },
-
+            {
+              title: '加工拍摄任务',
+              icon: 'newspaper-o',
+              path: 'processing-photo-task',
+            },
           ],
         },
       ],

+ 7 - 0
src/router/index.ts

@@ -145,6 +145,13 @@ const routes: RouteRecordRaw[] = [
     meta:{title:'移库'},
     component: () => import('@/views/inventory/transfer/index.vue')
   },
+
+  {
+    path: '/processing-photo-task',
+    name: 'ProcessingPhotoTask',
+    meta:{title:'加工拍摄任务'},
+    component: () => import('@/views/processing/photoTask/index.vue')
+  },
 ];
 
 // 创建路由实例

+ 4 - 0
src/views/outbound/check/activity/index.vue

@@ -6,6 +6,9 @@
         <van-icon name="arrow-left" size="25" />
         <div style="color: #fff">返回</div>
       </template>
+      <!--      <template #right>-->
+      <!--        <div class="nav-right" @click="onClickRight">提交任务</div>-->
+      <!--      </template>-->
     </van-nav-bar>
     <div class="activity">
       <div class="wave-title">
@@ -24,6 +27,7 @@
             <div>KG</div>
           </template>
         </van-field>
+<!--        :class="[totalWeight>0?'success-input-barcode':'error-input-barcode']"-->
       </div>
       <div class="order-detail">
         <div class="picking-no">

+ 427 - 0
src/views/processing/photoTask/index.vue

@@ -0,0 +1,427 @@
+<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">
+          <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>
+
+      <!-- 图片上传弹窗 -->
+      <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="15 * 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
+} from '@/api/processing/index'
+import { goBack } from '@/utils/android'
+
+
+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 paginate = ref({
+  page: 1,
+  size: 20,
+  total: 0,
+  pages: 0,
+})
+const reviewImages = ref([])
+
+// 计算最大高度适配手持设备
+const computedMaxHeight = computed(() => {
+  return window.innerHeight - 46 - 20 + 'px'
+})
+
+// 获取从其他页面传递的参数
+onMounted(() => {
+  // 可以从route.query或route.params中获取参数
+  if (route.query.id) {
+    // 如果有传入id参数,可以做特殊处理
+    console.log('接收到参数id:', route.query.id)
+  }
+})
+
+// 显示notes信息 - 改为弹框方式
+function showNotes(row) {
+  const notes = row.notes || '暂无备注信息'
+  showDialog({
+    title: '任务备注',
+    message: notes,
+    confirmButtonText: '确定',
+    messageAlign: 'left'
+  })
+}
+
+function onRefresh() {
+  // 刷新时重置到第一页
+  paginate.value.page = 1
+  listProcessingPhoto()
+}
+
+function listProcessingPhoto() {
+  loading.value = true
+  getProcessingPhotoTask(paginate.value).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
+    console.log(res)
+    if (!items || items.length === 0) {
+      showNotify({ type: 'warning', message: '没有对应的拍摄' })
+      return
+    }
+    console.log(import.meta.env.VITE_APP_BASE_API)
+    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)
+
+    console.log(reviewImages.value)
+    // 预览图片
+    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
+    }
+
+    // 检查文件大小 (15MB)
+    if (f.size > 15 * 1024 * 1024) {
+      showFailToast('图片大小不能超过15MB')
+      return false
+    }
+  }
+  return true
+}
+
+// 图片读取完成后处理
+const afterReadImage = (file) => {
+  // file 是上传的文件信息,可以在这里进行额外处理
+  console.log('文件读取完成:', file)
+}
+
+// 提交上传
+const submitUpload = async () => {
+  if (!uploadImages.value.length) {
+    showFailToast('请先选择图片')
+    return
+  }
+
+  if (!currentTaskId.value) {
+    showFailToast('任务ID无效')
+    return
+  }
+
+  uploading.value = true
+  const toast = showLoadingToast('上传中...')
+
+  try {
+    // 逐个上传图片
+    for (const image of uploadImages.value) {
+      const formData = new FormData()
+      formData.append('file', image.file)
+      // 调用上传API
+      await uploadPhoto(currentTaskId.value, formData)
+    }
+
+    closeToast()
+    showNotify({ type: 'success', message: '上传成功' })
+
+    // 关闭弹窗并刷新列表
+    showUpload.value = false
+    uploadImages.value = []
+    onRefresh()
+  } catch (err) {
+    closeToast()
+    showNotify({ type: 'danger', message: '上传失败: ' + (err.message || '未知错误') })
+  } finally {
+    uploading.value = false
+  }
+}
+
+// 初始化加载数据
+onRefresh()
+</script>
+
+<style scoped lang="scss">
+.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;
+    }
+  }
+}
+
+.code-link {
+  color: #1989fa;
+  cursor: pointer;
+  text-decoration: underline;
+  
+  &:hover {
+    color: #0570db;
+  }
+}
+</style>