import { ref, onMounted, onBeforeUnmount, watchEffect, computed } from 'vue' import icpList from '@/utils/ipc' import client from "@/stores/modules/client"; import socket from "@/stores/modules/socket"; import { ElMessage, ElMessageBox } from 'element-plus' import { getFilePath, getRouterUrl } from '@/utils/appfun' import { useRouter, useRoute } from "vue-router"; import checkInfo from "@/stores/modules/check"; import generate from '@/utils/menus/generate' import { clickLog, setLogInfo } from '@/utils/log' import { useUuidStore } from '@/stores/modules/uuid' import useUserInfo from "@/stores/modules/user"; import configInfo from '@/stores/modules/config'; import tokenInfo from "@/stores/modules/token"; export default function usePhotography() { const loading = ref(false) const runLoading = ref(false) const takePictureLoading = ref(false) // pagination const pageSize = ref(1) const currentPage = ref(1) const totalPages = ref(1) const goodsList = ref([]) const goods_art_no_tpl = ref('') const goods_art_no = ref('') const runAction = ref({ "action": "", "goods_art_no": "" }) const lastPhoto = ref({}) const showlastPhoto = ref(false) const isDelGoodsGetList = ref(false) const reNosObj = ref({ goods_art_no: null, action: null, }) const goodsArtNo = ref() let smartShooterTimeout: ReturnType | null = null // 初始化 WebSocket 状态管理 const socketStore = socket() const uuidStore = useUuidStore(); const clientStore = client(); const Router = useRouter() const route = useRoute(); const useUserInfoStore = useUserInfo(); const configInfoStore = configInfo(); const tokenInfoStore = tokenInfo(); const checkInfoStore = checkInfo() // 抠图请求去重与延迟队列(key: goods_art_no, value: timeoutId) const segmentQueue = new Map>() function isGoodsStillInList(goodsArtNo: string): boolean { return goodsList.value?.some((g: any) => g.goods_art_no === goodsArtNo) || false } function scheduleSegment(goodsArtNo: string) { if (!goodsArtNo) return // 若已存在,则重置计时(重新插入) if (segmentQueue.has(goodsArtNo)) { const t = segmentQueue.get(goodsArtNo) if (t) clearTimeout(t) segmentQueue.delete(goodsArtNo) } const timeoutId = setTimeout(async () => { segmentQueue.delete(goodsArtNo) if (!isGoodsStillInList(goodsArtNo)) return try { await socketStore.connectSocket(); socketStore.sendMessage({ type: 'segment_progress', data: { token: tokenInfoStore.getToken, uuid: uuidStore?.getUuid || '', goods_art_no: [goodsArtNo], } }) } catch (e) { // 忽略发送异常,避免打断主流程 } }, 20000) segmentQueue.set(goodsArtNo, timeoutId) } /** * 保存货号模板到货号变量中。 */ function saveGoodsArtNo() { if (goods_art_no_tpl.value) { goods_art_no.value = goods_art_no_tpl.value ElMessage.success('商品货号' + goods_art_no.value + '获取成功,请在遥控器上按下左或右脚按键,启动拍摄') } } /** * 获取拍照记录。 * @param params - 可选参数,用于分页或其他筛选条件。 */ async function getPhotoRecords(params?: { page?: number; size?: number } ) { if (loading.value) return; loading.value = true; const page = params?.page ?? currentPage.value ?? 1 const size = params?.size ?? pageSize.value ?? 5 console.log('params' , { ...params, page, size, }) clientStore.ipc.send(icpList.takePhoto.getPhotoRecords, { ...params, page, size, }); clientStore.ipc.on(icpList.takePhoto.getPhotoRecords, (event, result) => { loading.value = false; if (result.code === 0) { clientStore.ipc.removeAllListeners(icpList.takePhoto.getPhotoRecords); //console.log('getPhotoRecords print_time:' + new Date().toLocaleString()) // console.log('getPhotoRecords print_time:' + JSON.stringify(result.data.list)) console.log(result.data) goodsList.value = result.data.list // update pagination info if provided by API if (result.data) { currentPage.value = result.data.current_page || page totalPages.value = result.data.total_pages || 1 } if (isDelGoodsGetList.value) { isDelGoodsGetList.value = false; return; } getLastPhotoRecord() } else if (result.msg) { ElMessage.error(result.msg) } }); } /** * 执行拍照操作。 * @param data - 包含拍摄所需的数据对象。 */ async function runGoods(data) { if (runLoading.value || takePictureLoading.value) { ElMessage.error('拍摄程序正在运行,请稍候') return } await socketStore.connectSocket(); socketStore.sendMessage({ type: 'run_mcu', data, }) runLoading.value = true; runAction.value.action = data.action runAction.value.goods_art_no = data.goods_art_no // goods_art_no.value = '' goods_art_no_tpl.value = '' reNosObj.value.goods_art_no = null; reNosObj.value.action = null; clientStore.ipc.on(icpList.socket.message + '_run_mcu', (event, result) => { clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu'); console.log('_run_mcu'); console.log(result); if (result.code !== 0 && result.msg) { ElMessage.error(result.msg) runLoading.value = false return; } else { ElMessage.success('开始拍摄,请稍后') } }) } /** * 格式化时间字符串。 * @param time - 原始时间字符串。 * @returns 格式化后的时间字符串,若输入为空则返回 null。 */ const getTime = function (time) { if (!time) return null return time.replace('T', ' ').substr(5, 11) } /** * 删除所有商品货号的历史记录。 */ async function delAll() { let params = goodsList.value.map(item => item.goods_art_no) try { await ElMessageBox.confirm('确定要删除当下的历史记录吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', }) await clickLog({ describe: { action: '点击确认一键删除', goods_art_nos: params } }, route) del({ goods_art_nos: params }) } catch (e) { await clickLog({ describe: { action: '点击取消一键删除' } }, route) } } /** * 删除指定的商品货号。 * @param params - 包含需要删除的货号列表的对象。 */ const delGoods = async function (params) { try { await ElMessageBox.confirm('确定要删除货号:' + params.goods_art_nos[0] + '的拍摄数据吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', }) await clickLog({ describe: { action: '点击确认删除货号', goods_art_no: params.goods_art_nos?.[0] } }, route) del(params) } catch (e) { await clickLog({ describe: { action: '点击取消删除货号', goods_art_no: params.goods_art_nos?.[0] } }, route) } } /** * 删除指定的商品货号。 * @param params - 包含需要删除的货号列表的对象。 */ const del = async function (params) { console.log(icpList.takePhoto.delectGoodsArts, params); clientStore.ipc.removeAllListeners(icpList.takePhoto.delectGoodsArts); clientStore.ipc.send(icpList.takePhoto.delectGoodsArts, params); clientStore.ipc.on(icpList.takePhoto.delectGoodsArts, (event, result) => { clientStore.ipc.removeAllListeners(icpList.takePhoto.delectGoodsArts); console.log("icpList.takePhoto.delectGoodsArts", params); if (result.code === 0) { isDelGoodsGetList.value = true ElMessage.info('货号删除成功') getPhotoRecords() if (reNosObj.value.goods_art_no) { runGoods( { "action": reNosObj.value.action, "goods_art_no": reNosObj.value.goods_art_no }) } } else if (result.msg) { ElMessage.error(result.msg) } }); } //单个重拍 const reTakePicture = async (img) => { if (!img.id) return; if (img.image_path) { try { await ElMessageBox.confirm('此操作会先删除此数据,需要继续吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', }) await clickLog({ describe: { action: '点击确认单张重拍', goods_art_no: img.goods_art_no, action_name: img.action_name } }, route) } catch (e) { await clickLog({ describe: { action: '点击取消单张重拍', goods_art_no: img.goods_art_no, action_name: img.action_name } }, route) return } } runLoading.value = true; reNosObj.value.goods_art_no = img.goods_art_no reNosObj.value.action = 're_take_picture' let params = { id: img.action_id } clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail); clientStore.ipc.send(icpList.setting.getDeviceConfigDetail, params); clientStore.ipc.on(icpList.setting.getDeviceConfigDetail, (event, result) => { console.log('getDeviceConfigDetail') console.log(result) if (result.code == 0 && result.data) { clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail); this_run_mcu_single(result.data) } else if (result.msg) { runLoading.value = false; reNosObj.value.goods_art_no = '' reNosObj.value.action = '' ElMessage.error(result.msg) } }); function this_run_mcu_single(data) { clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_single'); socketStore.sendMessage({ type: 'run_mcu_single', data: { camera_height: Number(data.camera_height), camera_angle: Number(data.camera_angle), led_switch: data.led_switch, id: 0, mode_type: data.mode_type, turntable_position: Number(data.turntable_position), action_name: data.action_name || '测试', turntable_angle: Number(data.turntable_angle), shoe_upturn: Number(data.shoe_upturn), action_index: 1, number_focus: 0, take_picture: false, pre_delay: 0, after_delay: 0, } }); clientStore.ipc.on(icpList.socket.message + '_run_mcu_single', async (event, result) => { console.log('_run_mcu_single_row') clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_single'); this_re_take_picture() }) } async function this_re_take_picture() { await ElMessageBox.alert('已复位到该视图下,请把鞋子摆放完毕之后,点击按钮开始重拍', '提示', { confirmButtonText: "开始重拍", showClose: false, closeOnClickModal: false, closeOnPressEscape: false }) await clickLog({ describe: { action: '点击开始重拍', goods_art_no: img.goods_art_no } }, route) socketStore.sendMessage({ type: 'smart_shooter_photo_take', "data": { "id": img.id, "goods_art_no": img.goods_art_no }, }) } } const resetStatus = () => { runLoading.value = false; reNosObj.value.goods_art_no = '' reNosObj.value.action = '' runAction.value.goods_art_no = ''; runAction.value.action = ''; } //货号重拍 const reTakePictureNos = async (goods_art_no, item) => { try { await ElMessageBox.confirm('此操作会先删除删除货号:' + goods_art_no + '的拍摄数据吗,需要继续吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', }) await clickLog({ describe: { action: '点击确认重拍货号', goods_art_no } }, route) } catch (e) { await clickLog({ describe: { action: '点击取消重拍货号', goods_art_no } }, route) return } reNosObj.value.goods_art_no = goods_art_no reNosObj.value.action = '执行左脚程序' console.log(item); if (item.items && typeof item.items === 'object' && item.items[0].PhotoRecord.image_deal_mode) { reNosObj.value.action = '执行右脚程序' } del({ goods_art_nos: [goods_art_no] }) } /** * 检查是否可以进入下一步操作。 */ const next = async function () { if (runLoading.value) { ElMessage.error('正在拍摄中,请稍候') return; } if (goodsList.length) { ElMessage.error('请先拍摄商品。') return; } } const oneClickStop = () => { if (!(runLoading.value || takePictureLoading.value)) { ElMessage.error('拍摄程序已结束,不需要单独停止!') return } else { socketStore.sendMessage({ type: 'stop_action', }) } } /** * 打开最近一张拍摄图 */ const getLastPhotoRecord = async () => { return; if (goodsList.value && goodsList.value.length === 0) return; clientStore.ipc.removeAllListeners(icpList.takePhoto.getLastPhotoRecord); clientStore.ipc.send(icpList.takePhoto.getLastPhotoRecord,); clientStore.ipc.on(icpList.takePhoto.getLastPhotoRecord, (event, result) => { console.log('getLastPhotoRecord'); console.log(result.data?.goods_art_no); clientStore.ipc.removeAllListeners(icpList.takePhoto.getLastPhotoRecord); if (result.code === 0) { if (lastPhoto.value?.photo_file_name) { // if( lastPhoto.value?.image_path == result.data?.image_path) return; if (runAction.value.goods_art_no === result.data?.goods_art_no) { showlastPhoto.value = true } } lastPhoto.value = result.data } else if (result.msg) { ElMessage.error(result.msg) } }); } /** * 打开主图详情页面。 */ function openPhotographyDetail() { // 埋点:开始生成 clickLog({ describe: { action: '开始生成', goods_count: goodsList.value.length, goods_art_nos: goodsList.value.map(item => item.goods_art_no) } }, route); if (runLoading.value || takePictureLoading.value) { ElMessage.error('正在拍摄中,请稍候') return; } const { href } = Router.resolve({ name: 'PhotographyDetail', query: { goods_art_nos: goodsList.value.map(item => item.goods_art_no), } }) clientStore.ipc.removeAllListeners(icpList.utils.openMain); let params = { title: '主图与详情生成', width: 3840, height: 2160, frame: true, id: "PhotographyDetail", url: getRouterUrl(href) } clientStore.ipc.send(icpList.utils.openMain, params); } /*高级生成*/ const onGenerateCLick = (menu, item) => { if (menu.name === '历史记录') { menu.click() return } const firstWithImagePath = item.items.find( (image) => image.PhotoRecord.image_path ); if (firstWithImagePath) { menu.click({ query: { image_path: firstWithImagePath.PhotoRecord.image_path } }) } else { menu.click() } } // 打开输出目录:appConfig.appPath + '/build/extraResources/py/output' const openOutputDir = () => { try { const appPath = configInfoStore?.appConfig?.appPath || '' if (!appPath) { ElMessage.error('未获取到应用目录 appPath') return } const fullPath = `${appPath}\\output` clientStore.ipc.removeAllListeners(icpList.utils.shellFun); clientStore.ipc.send(icpList.utils.shellFun, { action: 'openMkPath', params: fullPath.replace(/\//g, '\\') }); } catch (e) { console.error(e) ElMessage.error('打开目录失败') } } const menu = computed(() => { if (configInfoStore.appModel === 2) { return [ { type: 'setting' }, { name: '切换模式', click() { configInfoStore.updateAppModel(1) Router.push({ name: 'PhotographyCheck' }) } }, { name: '生成图目录', click() { openOutputDir() } }, { ...generate } ] } if (useUserInfoStore.userInfo.brand_company_code === '1300' || configInfoStore.appConfig.debug) { return [ { type: 'setting' }, { type: 'developer' }, { name: '生成图目录', click() { openOutputDir() } }, { ...generate, } ] } return [ { type: 'setting' }, { name: '生成图目录', click() { openOutputDir() } }, { ...generate } ] }) const onRemoteControl = (type) => { if (type == 'take_picture') { // 埋点:手动拍照 clickLog({ describe: { action: '点击遥控器拍照按钮' } }, route); if (runLoading.value || takePictureLoading.value) { ElMessage.error('拍摄程序正在运行,请稍候') return } ElMessage.success('正在拍摄中,请稍候') socketStore.sendMessage({ type: 'handler_take_picture', }) return; } if (!goods_art_no.value) { ElMessage.error('请在左侧第一步中,先扫描货号或者手动输入货号!') goodsArtNo.value?.focus() // 聚焦输入框 return; } let action = '执行左脚程序' if (type === 'right') action = '执行右脚程序' // 埋点:遥控器启动拍摄 clickLog({ describe: { action: `点击遥控器${type === 'left' ? '左脚' : '右脚'}按钮`, goods_art_no: goods_art_no.value } }, route); runGoods({ "action": action, "goods_art_no": goods_art_no.value, }) } // 初始化事件监听 const initEventListeners = () => { // 监听蓝牙扫描事件 clientStore.ipc.on(icpList.socket.message + '_blue_tooth_scan', (event, result) => { console.log('_blue_tooth_scan') if (result.code === 0 && result.data?.data) { console.log(goods_art_no.value); if (!goods_art_no.value) { ElMessage.error('请在左侧第一步中,先扫描货号或者手动输入货号!') goodsArtNo.value?.focus() // 聚焦输入框 return; } runGoods({ ...result.data?.data, goods_art_no: goods_art_no.value }) } }); // 监听图片处理完成事件 clientStore.ipc.on(icpList.socket.message + '_image_process', (event, result) => { console.log('_image_process') console.log(result) getPhotoRecords() // 延迟两秒再获取一遍数据 setTimeout(() => { getPhotoRecords() }, 3000) }) // 监听拍照完成事件 clientStore.ipc.on(icpList.socket.message + '_photo_take', (event, result) => { console.log('_photo_take') console.log(result) if (result.status === 2 && result.msg.includes('执行完成')) { getPhotoRecords() // 延迟两秒再获取一遍数据 setTimeout(() => { getPhotoRecords() }, 3000) takePictureLoading.value = false; return; } if (result.code !== 0 && result.msg) { ElMessage.error(result.msg) takePictureLoading.value = false; } }) // 监听一键停止 clientStore.ipc.on(icpList.socket.message + '_stop_action', (event, result) => { console.log('_stop_action') console.log(result) oneClickStop() }) // 监听一键停止结束 clientStore.ipc.on(icpList.socket.message + '_run_mcu_stop', (event, result) => { console.log('_run_mcu_stop') resetStatus() }) // 监听拍照完成后的最终状态事件 clientStore.ipc.on(icpList.socket.message + '_photo_take_finish', (event, result) => { console.log('_photo_take_finish') console.log(result) if (result.code === 0) { setLogInfo(route, { action: '全部拍摄完成', goods_art_no: runAction.value.goods_art_no }); // 全部拍摄完成后,触发抠图队列 if (runAction.value.goods_art_no) { scheduleSegment(runAction.value.goods_art_no) } runLoading.value = false; runAction.value.goods_art_no = ''; runAction.value.action = ''; setTimeout(() => { showlastPhoto.value = false }, 3000) } }) // 监听手动触发拍照事件 clientStore.ipc.on(icpList.socket.message + '_handler_take_picture', async (event, result) => { console.log('_handler_take_picture') console.log(result) if (result.code === 0) { if (runLoading.value || takePictureLoading.value) { ElMessage.error('拍摄程序正在运行,请稍候') return } ElMessage.success('正在拍摄中,请稍候') takePictureLoading.value = true; await socketStore.connectSocket(); socketStore.sendMessage(result.data) getPhotoRecords() // 延迟两秒再获取一遍数据 setTimeout(() => { getPhotoRecords() }, 3000) } else if (result.msg) { ElMessage.error(result.msg) } }) //拍照成功 SmartShooter clientStore.ipc.on(icpList.socket.message + '_smart_shooter_photo_take', async (event, result) => { console.log('_smart_shooter_photo_take'); console.log(result); if (result.code === 0) { if (!result.data.goods_art_no) return; setLogInfo(route, { action: '单张拍摄完成', goods_art_no: result.data.goods_art_no }); if (reNosObj.value?.goods_art_no === result.data.goods_art_no) { runLoading.value = false; } // 单张重拍完成且存在重拍货号时,触发抠图队列 scheduleSegment(result.data.goods_art_no) if (smartShooterTimeout) { clearTimeout(smartShooterTimeout); } setTimeout(() => { showlastPhoto.value = true; lastPhoto.value = { file_path: result.data.photo_file_name }; setTimeout(() => { if (!runAction.value.goods_art_no) { showlastPhoto.value = false; } }, 3000) }, 100); smartShooterTimeout = setTimeout(() => { getPhotoRecords(); if (!runAction.value.goods_art_no) { showlastPhoto.value = false; } }, 2000); } else if (result.msg) { runLoading.value = false; reNosObj.value.goods_art_no = '' reNosObj.value.action = '' ElMessage.error(result.msg) } }) // 监听拍照完成后的最终状态事件 clientStore.ipc.on(icpList.socket.message + '_run_mcu_update', (event, result) => { console.log('run_mcu_updat print_time:' + new Date().toLocaleString()) console.log('run_mcu_update print_time:' + JSON.stringify(result)) if (result.code === 0) { if (result.data?.file_path) { if (lastPhoto.value?.file_path == result.data?.file_path) return; let goods_art_no = runAction.value.goods_art_no || reNosObj.value.goods_art_no if (goods_art_no === result.data?.goods_art_no) { showlastPhoto.value = true goodsList.value.map(item => { if (item.goods_art_no === result.data?.goods_art_no) { item.items[result.data.image_index].PhotoRecord.image_path = result.data?.file_path result.data.action_name = item.items[result.data.image_index].action_name setTimeout(() => { item.items[result.data.image_index].PhotoRecord.image_path = result.data?.file_path }, 1000) setTimeout(() => { showlastPhoto.value = false }, 3000) } }) setTimeout(() => { getPhotoRecords() }, 2000) } lastPhoto.value = result.data } } else if (result.msg) { ElMessage.error(result.msg) } if (reNosObj.value.goods_art_no) { resetStatus() } }) } // 清理事件监听 const cleanupEventListeners = () => { clientStore.ipc.removeAllListeners(icpList.socket.message + '_blue_tooth_scan'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_image_process'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_photo_take'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_photo_take_finish'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_update'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_stop_action'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_smart_shooter_photo_take'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_stop'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_digicam_take_picture'); clientStore.ipc.removeAllListeners(icpList.socket.message + '_segment_progress'); // 清理抠图队列的定时器 try { segmentQueue.forEach((t) => { if (t) clearTimeout(t) }) segmentQueue.clear() } catch (e) { } } // 监听蓝牙扫描 checkInfoStore.set_blue_tooth_scan_NO('') watchEffect(async () => { if (checkInfoStore.blue_tooth_scan_NO) { ElMessage.success('商品货号' + checkInfoStore.blue_tooth_scan_NO + '获取成功,请在遥控器上按下左或右脚按键,启动拍摄') goods_art_no.value = checkInfoStore.blue_tooth_scan_NO checkInfoStore.set_blue_tooth_scan_NO('') } }) return { loading, runLoading, takePictureLoading, goodsList, pageSize, currentPage, totalPages, goods_art_no_tpl, goods_art_no, runAction, lastPhoto, showlastPhoto, goodsArtNo, menu, configInfoStore, getTime, getFilePath, getPhotoRecords, delAll, delGoods, del, reTakePicture, reTakePictureNos, oneClickStop, onRemoteControl, openPhotographyDetail, onGenerateCLick, initEventListeners, cleanupEventListeners, } }