| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- <template>
- <headerBar :title="$t('router.photoShot')" showUser :menu="menu" />
- <hardware-check/>
- <div class="photography-page flex-col">
- <div class="main-container">
- <div class="content-wrapper flex-col">
- <div class="step-section">
- <div class="step-number flex-col"><span class="text_22">1</span></div>
- <div class="step-one flex-col">
- <div class="step-header flex-row">
- <span class="step-title">{{ $t('photoShot.step1') }}</span>
- </div>
- <div class="step-content">
- <div class="input-container flex-row">
- <el-input class="input-item" ref="goodsArtNo" clearable v-model="goods_art_no" :placeholder="$t('photoShot.inputGoodsPlaceholder')"> </el-input>
- </div>
- <div class="auto-method flex-row justify-between">
- <img class="step-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/step1-icon.png" />
- <div class="text-method-tag flex-col mar-left-10"><span class="text_4">{{ $t('photoShot.autoGet') }}</span></div>
- <span class="method-description mar-left-10">{{ $t('photoShot.scanTip') }}</span>
- </div>
- </div>
- </div>
- </div>
- <div class="step-section">
- <div class="step-number flex-col"><span class="text_22">2</span></div>
- <div class="step-two flex-col justify-between">
- <span class="step-title">{{ $t('photoShot.step2') }}</span>
- <div class="shooting-container flex-col">
- <div class="shooting-tips flex-row justify-between">
- <img class="info-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/info-icon.png" />
- <span class="tips-text">{{ $t('photoShot.remoteTip') }}</span>
- </div>
- <div class="wifi mar-top-20">
- <img referrerpolicy="no-referrer" src="@/assets/images/Photography/wifi.png" style="width: 60px" />
- </div>
- <div class="remote-control-wrap">
- <RemoteControl
- @onRemoteControl="onRemoteControl"
- :canStop="runLoading || takePictureLoading"
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="last-photo" v-show="showlastPhoto && (lastPhoto as any)?.file_path" v-key="(lastPhoto as any)?.file_path">
- <el-button class="close-btn" type="danger" icon="Close" circle @click="closeLastPhoto" />
- <el-image :src="getFilePath((lastPhoto as any)?.file_path || '')" fit="contain" ></el-image>
- </div>
- <div class="history-section flex-col koutu-section">
- <div class="search-bar">
- <el-input
- v-model="searchGoodsArtNo"
- :placeholder="$t('photoShot.searchPlaceholder')"
- clearable
- style="width: 300px"
- @keyup.enter="handleSearch"
- >
- <template #append>
- <el-button @click="handleSearch">{{ $t('common.search') }}</el-button>
- </template>
- </el-input>
- </div>
- <div class="history-warp" ref="containerRef">
- <div v-if="!goodsList.length" class="fs-14 c-666 mar-top-50">
- {{ loading ? $t('photoShot.loading') : $t('photoShot.noData')}}
- </div>
- <div v-else class="history-item" v-for="item in goodsList" :key="item.goods_art_no" >
- <div class="history-item-header">
- <div class="history-item-left">
- <el-checkbox
- :model-value="selectedGoods.has(item.goods_art_no)"
- @change="toggleGoods(item.goods_art_no)"
- class="goods-checkbox"
- />
- <span class="goods-art-no">{{ item.goods_art_no }}</span>
- <div class="history-item-meta ">
- <span class="action-time flex left">
- <img src="@/assets/images/processImage.vue/riq.png" />
- {{ getTime(item.action_time) }}</span>
- <span class="image-count mar-left-10 flex left">
- <img src="@/assets/images/processImage.vue/tup.png" />
- {{ item.items?.length || 0 }} {{ $t('photoShot.imageCount') }}</span>
- <span v-if="!item.syncConfig" class="mar-left-10">{{ $t('message.cannotReadConfig') }}</span>
- </div>
- </div>
- <div class="history-item-right">
- <el-dropdown :disabled="runLoading || takePictureLoading" trigger="click">
- <el-button :disabled="runLoading || takePictureLoading" size="small" plain>{{ $t('photoShot.advancedGenerate') }}</el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item
- v-for="menuItem in generate.children"
- @click.native="onGenerateCLick(menuItem, item)">{{ menuItem.name }}</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-button size="small" v-if="item.syncConfig" class="mar-left-10" :disabled="runLoading || takePictureLoading" type="primary" @click="reTakePictureNos(item.goods_art_no,item)" plain v-log="{ describe: { action: 'retake goods', goods_art_no: item.goods_art_no } }">{{ $t('photoShot.retake') }}</el-button>
- <el-button style="color: #FF4C00" size="small" class="mar-left-10" :disabled="runLoading || takePictureLoading" @click="delGoods({goods_art_nos:[item.goods_art_no]})" v-log="{ describe: { action: 'delete goods', goods_art_no: item.goods_art_no } }">{{ $t('photoShot.delete') }}</el-button>
- </div>
- </div>
- <div class="history-item-images" >
- <div
- v-for="(image, index) in item.items"
- :key="image.action_id || image.action_name"
- class="history-item_image"
- v-loading="!image.PhotoRecord.image_path && runAction.goods_art_no == item.goods_art_no"
- >
- <span class="tag" v-if="!image.PhotoRecord.image_path">{{ image.action_name }}</span>
- <el-image
- v-if="image.PhotoRecord.image_path"
- :src="thumbnailMap[image.PhotoRecord.image_path] || getFilePath(image.PhotoRecord.image_path)"
- :preview-src-list="getPreviewImageList(item)"
- hide-on-click-modal
- :initial-index="getPreviewIndex(item, index)"
- class="preview-image"
- fit="contain"
- :preview-teleported="true"
- lazy
- >
- <template #placeholder>
- <span class="tag">{{ image.action_name }}</span>
- </template>
- <template #error>
- <div class="image-slot">
- <span class="tag">{{ image.action_name }}</span>
- </div>
- </template>
- </el-image>
- <div v-else class="image-placeholder">
- <span class="tag">{{ image.action_name }}</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted, onBeforeUnmount } from 'vue'
- import { useI18n } from 'vue-i18n'
- import { ElMessageBox } from 'element-plus'
- import RemoteControl from '@/views/RemoteControl/index.vue'
- import headerBar from '@/components/header-bar/index.vue'
- import hardwareCheck from '@/components/check/index.vue'
- import usePhotography from './mixin/usePhotography'
- const { t } = useI18n()
- const {
- loading, runLoading, takePictureLoading, goodsList, pageSize, currentPage,
- totalPages, goods_art_no_tpl, goods_art_no, runAction, lastPhoto,
- showlastPhoto, goodsArtNo, searchGoodsArtNo, menu, generate,
- getTime, getFilePath, getPhotoRecords, delGoods, importDirs, deleteAllGoods,
- reTakePictureNos, onRemoteControl, initEventListeners, cleanupEventListeners,
- } = usePhotography()
- const containerRef = ref<HTMLElement | null>(null)
- const selectedGoods = ref<Set<string>>(new Set())
- const thumbnailMap = ref<Record<string, string>>({})
- const toggleGoods = (goodsArtNo: string) => {
- if (selectedGoods.value.has(goodsArtNo)) {
- selectedGoods.value.delete(goodsArtNo)
- } else {
- selectedGoods.value.add(goodsArtNo)
- }
- selectedGoods.value = new Set(selectedGoods.value)
- }
- const handleSearch = () => {
- getPhotoRecords({ goods_art_no: searchGoodsArtNo.value })
- }
- const closeLastPhoto = () => {
- showlastPhoto.value = false
- }
- const handleDeleteAll = async () => {
- try {
- await ElMessageBox.confirm(t('message.confirmDeleteAll'), t('common.tips'), {
- confirmButtonText: t('common.confirm'),
- cancelButtonText: t('common.cancel'),
- })
- deleteAllGoods()
- } catch (e) {}
- }
- const getPreviewImageList = (item: any) => {
- if (!item || !item.items) return []
- return item.items.filter((img: any) => img.PhotoRecord?.image_path).map((img: any) => getFilePath(img.PhotoRecord.image_path))
- }
- const getPreviewIndex = (item: any, currentIndex: number) => {
- if (!item || !item.items) return 0
- let previewIndex = 0
- for (let i = 0; i <= currentIndex; i++) {
- if (item.items[i]?.PhotoRecord?.image_path) {
- if (i === currentIndex) break
- previewIndex++
- }
- }
- return previewIndex
- }
- onMounted(async () => {
- initEventListeners()
- await getPhotoRecords()
- })
- onBeforeUnmount(() => {
- cleanupEventListeners()
- })
- </script>
- <style lang="scss">
- .koutu-image-popper {
- width: calc(100vw - 470px) !important;
- right: 70px !important;
- top: 100px !important;
- height: calc(100vh - 170px) !important;
- transform: translate(0px, 0px) !important;
- .el-image { width: 100%; height: 100%; display: block; .el-image__inner { width: 100%; height: 100%; display: block; } }
- }
- </style>
- <style scoped lang="scss">
- .photography-page { position: relative; }
- .main-container { position: relative; display: flex; }
- .history-section { width: 100%; min-height: calc(100vh - 30px); display: flex; flex-direction: column; padding: 20px; }
- .history-warp { flex: 1; }
- .history-item { background: #FFFFFF; box-shadow: 0px 2px 4px 0px rgba(23,33,71,0.1); border-radius: 10px; border: 1px solid #D9DEE6; margin-bottom: 20px; }
- .history-item-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid #f0f0f0; }
- .history-item-left { display: flex; align-items: center; gap: 12px; }
- .goods-art-no { font-weight: 600; font-size: 16px; color: #333; }
- .history-item-meta { display: flex; align-items: center; gap: 4px; font-size: 12px; color: #8C92A7; }
- .history-item-right { display: flex; align-items: center; gap: 8px; }
- .history-item-images { display: flex; flex-wrap: wrap; padding: 12px 16px; gap: 12px; }
- .history-item_image { width: 120px; height: 120px; border-radius: 8px; overflow: hidden; background: #f5f5f5; display: flex; align-items: center; justify-content: center; position: relative; }
- .preview-image { width: 120px; height: 120px; }
- .image-placeholder { width: 120px; height: 120px; display: flex; align-items: center; justify-content: center; background: #f0f0f0; }
- .tag { position: absolute; bottom: 4px; left: 4px; background: rgba(0,0,0,0.5); color: #fff; font-size: 10px; padding: 2px 6px; border-radius: 4px; }
- .image-count { display: flex; align-items: center; gap: 4px; }
- .last-photo { position: fixed; top: 50px; right: 20px; z-index: 9999; width: 300px; background: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); padding: 8px; }
- .close-btn { position: absolute; top: 8px; right: 8px; z-index: 1; }
- ::v-deep .el-checkbox__input { transform: scale(1.4); }
- .search-bar { margin-bottom: 15px; display: flex; justify-content: flex-start; }
- </style>
|