import { useAppStore } from '@/store/modules/app' import { useUserStore } from '@/store/modules/user' import { useElLockStore } from '@/store/modules/elLock' const elLockStore = useElLockStore() const appStore = useAppStore() import ww from '@/utils/ww' import { LockApi } from '@/api/electron/lock' import { LockWorkRecordApi } from '@/api/electron/lockworkcord' import { bindLock as bindLockApi } from '@/api/lock' import { ElMessage, ElMessageBox } from 'element-plus' import download from '@/utils/download' import { updateFile } from '@/api/infra/file' type Location = { latitude: number longitude: number accuracy: number } export const getCurrentLocation = async (): Promise => { if (appStore.isWorkWechat) { const res = await ww.getLocation() return { latitude: res.latitude, longitude: res.longitude, accuracy: res.accuracy } } else if (window.navigator.geolocation) { return new Promise((resolve, reject) => { window.navigator.geolocation.getCurrentPosition((position) => { resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy }) }, (error) => { ElMessage.error('获取位置失败') resolve({ latitude: 0, longitude: 0, accuracy: 0 }) }) }) } else { return Promise.resolve({ latitude: 0, longitude: 0, accuracy: 0 }) } } // 统一执行绑定逻辑:校验锁状态 -> 获取位置 -> 记录工单 -> 批量更新状态 async function performBind(detail: any, lockId: number, operatorId: number): Promise { // 校验锁具状态 const lock = await LockApi.getLock(lockId) if (!lock) { ElMessage.error('锁具不存在') return } if (lock.lockStatus != 2) { ElMessage.error('锁具状态异常 无法绑定') return } // 获取当前位置 let location: Location try { location = await getCurrentLocation() } catch (_) { ElMessage.error('获取位置失败,无法绑定') return } // // 批量更新业务状态 // await Promise.all([ // PointApi.updatePoint({ // id: detail.isolationPointId, // status: 1 // 已锁定 // }), // LockApi.updateLock({ // id: lockId, // lockStatus: 7 // 已绑定 // }), // PlanItemDetailApi.updatePlanItemDetail({ // id: detail.id, // lockId: lockId, // lockStatus: 1 // 未上锁 // }) // ]) await bindLockApi({ planItemDetailId: detail.id, lockId }) // 创建工单记录 await LockWorkRecordApi.createLockWorkRecord({ operatorId: Number(operatorId), lockId: lockId, isolationPlanItemDetailId: detail.id, recordType: 2, // 未绑定 gpsCoordinates: `${location.latitude},${location.longitude}` }) ElMessage.success('绑定成功') } export const bindLock = async (detail: any) => { try { if (appStore.isWorkWechat) { const currentUserId = useUserStore().getUser.id // 扫描二维码获取锁具ID const scanRes = await (ww as any).scanQRCode({ needResult: true, scanType: ['qrCode'] }) const lockId = Number((scanRes && scanRes.resultStr) || NaN) if (!lockId || Number.isNaN(lockId)) { ElMessage.error('二维码内容无效') return } await performBind(detail, lockId, currentUserId) } else { const lockOptions = elLockStore.locks.filter(i => i.lockEnableStatus == 1 && i.lockStatus == 2).map((lock) => ({ label: lock.lockName, number: lock.lockNumber, value: lock.id })) // 检查是否有可用的锁具 if (lockOptions.length === 0) { ElMessage.error('暂无可用的锁具') return } // 如果只有一个锁具,直接使用 if (lockOptions.length === 1) { const lockId = lockOptions[0]?.value if (lockId) { await performBind(detail, lockId, useUserStore().getUser.id) } return } // 有多个锁具时,让用户选择 const lockOptionsHtml = lockOptions.map((option, index) => `` ).join('') let selectedLockId: string | null = null try { await ElMessageBox.confirm( `

请选择要绑定的锁具:

`, '选择锁具', { confirmButtonText: '确定', cancelButtonText: '取消', dangerouslyUseHTMLString: true, beforeClose: (action, _instance, done) => { if (action === 'confirm') { const selector = document.getElementById('lockSelector') as HTMLSelectElement const selectedValue = selector?.value if (!selectedValue) { ElMessage.warning('请选择一个锁具') return false } selectedLockId = selectedValue } done() } } ) // 用户点击确定后执行绑定 if (selectedLockId) { await performBind(detail, Number(selectedLockId), useUserStore().getUser.id) } } catch (error) { // 用户取消选择,不做任何操作 console.log('用户取消选择锁具') } } } catch (error) { ElMessage.error('绑定失败,请重试') throw error } } // 将 localId 转换为 File(先尝试 getLocalImgData,失败则用 Canvas 回退) async function convertLocalIdToFile(localId: string): Promise { // 优先:getLocalImgData(iOS 常见) if ((ww as any).getLocalImgData) { try { const localDataRes = await (ww as any).getLocalImgData({ localId }) const base64 = String((localDataRes && localDataRes.localData) || '') const dataUrl = base64.startsWith('data:') ? base64 : `data:image/jpeg;base64,${base64}` return download.base64ToFile(dataUrl, 'photo') } catch (_) { /* fallback to canvas */ } } // 回退:使用 Canvas 生成 base64 再转 File(Android 常见) return await new Promise((resolve, reject) => { const img = new Image() img.crossOrigin = 'anonymous' img.onload = () => { try { const canvas = document.createElement('canvas') canvas.width = img.width canvas.height = img.height const ctx = canvas.getContext('2d') if (!ctx) throw new Error('canvas context 获取失败') ctx.drawImage(img, 0, 0, img.width, img.height) const dataUrl = canvas.toDataURL('image/jpeg') resolve(download.base64ToFile(dataUrl, 'photo')) } catch (err) { reject(err) } } img.onerror = (err) => reject(err) img.src = localId }) } // 创建文件选择器(非企业微信环境使用) async function selectFile(): Promise { return new Promise((resolve, reject) => { const input = document.createElement('input') input.type = 'file' input.accept = 'image/*' input.style.display = 'none' input.onchange = (event) => { const target = event.target as HTMLInputElement const file = target.files?.[0] document.body.removeChild(input) if (file) { resolve(file) } else { reject(new Error('未选择文件')) } } input.oncancel = () => { document.body.removeChild(input) reject(new Error('用户取消选择')) } document.body.appendChild(input) input.click() }) } export const getPhoto = async () => { try { if (appStore.isWorkWechat) { // 放宽企业微信JSSDK类型限制,兼容不同终端返回与参数 const chooseRes = await (ww as any).chooseImage({ count: 1, sizeType: ['original'], sourceType: ['camera'], defaultCameraMode: 'normal', isSaveToAlbum: false } as any) as { localIds?: string[] localId?: string tempFiles?: Array<{ file?: File }> } // 企业微信返回可能是 tempFiles 或 localIds/localId const directFile: File | undefined = (chooseRes as any)?.tempFiles?.[0]?.file const localId: string | undefined = chooseRes?.localIds?.[0] || (chooseRes as any)?.localId const file: File | null = directFile ? directFile : (localId ? await convertLocalIdToFile(localId) : null) if (!file) throw new Error('无法获取拍照文件') const uploadRes = await updateFile({ file }) return uploadRes && uploadRes.data } else { // 非企业微信环境:选择文件并上传 const file = await selectFile() const uploadRes = await updateFile({ file }) return uploadRes && uploadRes.data } } catch (err) { ElMessage.error('文件上传失败') throw err } }