|
|
|
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<Location> => {
|
|
|
|
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<void> {
|
|
|
|
// 校验锁具状态
|
|
|
|
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) =>
|
|
|
|
`<option value="${option.value}">${option.number}. ${option.label}</option>`
|
|
|
|
)
|
|
|
|
.join('')
|
|
|
|
|
|
|
|
let selectedLockId: string | null = null
|
|
|
|
|
|
|
|
try {
|
|
|
|
await ElMessageBox.confirm(
|
|
|
|
`<div>
|
|
|
|
<p style="margin-bottom: 10px;">请选择要绑定的锁具:</p>
|
|
|
|
<select id="lockSelector" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
|
|
|
|
<option value="">-- 请选择锁具 --</option>
|
|
|
|
${lockOptionsHtml}
|
|
|
|
</select>
|
|
|
|
</div>`,
|
|
|
|
'选择锁具',
|
|
|
|
{
|
|
|
|
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('用户取消选择锁具')
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
// ElMessage.error('绑定失败,请重试')
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 将 localId 转换为 File(先尝试 getLocalImgData,失败则用 Canvas 回退)
|
|
|
|
async function convertLocalIdToFile(localId: string): Promise<File> {
|
|
|
|
// 优先: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<File>((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<File> {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|