fix: 修复课堂监控无法截屏问题
This commit is contained in:
parent
9c91889597
commit
a6fde98a35
BIN
.pnpm-store/v11/index.db
Normal file
BIN
.pnpm-store/v11/index.db
Normal file
Binary file not shown.
@ -2,7 +2,7 @@ import html2canvas from 'html2canvas'
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import globalApi from '@/api/global'
|
import globalApi from '@/api/global'
|
||||||
import { userStore } from '@/stores'
|
import { userStore } from '@/stores'
|
||||||
import { $XXL, sys_type, sys_type_study_machine } from '@/utils'
|
import { $XXL, getNativeApi } from '@/utils'
|
||||||
import sass from '@/utils/sass'
|
import sass from '@/utils/sass'
|
||||||
import { useTips } from './useTips'
|
import { useTips } from './useTips'
|
||||||
|
|
||||||
@ -13,20 +13,78 @@ let monitorState: 0 | 1 = 0
|
|||||||
|
|
||||||
const MONITOR_SNAPSHOT_INTERVAL = 10000
|
const MONITOR_SNAPSHOT_INTERVAL = 10000
|
||||||
const MONITOR_MSG_TYPE = 'monitorStart'
|
const MONITOR_MSG_TYPE = 'monitorStart'
|
||||||
|
const MONITOR_STOP_MSG_TYPE = 'monitorStop'
|
||||||
|
const MONITOR_LOG_PREFIX = '[class-monitor]'
|
||||||
|
|
||||||
|
const monitorLog = (...args: unknown[]) => console.log(MONITOR_LOG_PREFIX, ...args)
|
||||||
|
const monitorWarn = (...args: unknown[]) => console.warn(MONITOR_LOG_PREFIX, ...args)
|
||||||
|
const monitorError = (...args: unknown[]) => console.error(MONITOR_LOG_PREFIX, ...args)
|
||||||
|
|
||||||
|
const getServerOrigin = () => {
|
||||||
|
try {
|
||||||
|
return new URL(import.meta.env.VITE_SERVER_URL).origin
|
||||||
|
} catch {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLocalFilePath = (path: string) => {
|
||||||
|
return /^(file:\/\/|\/storage\/|\/sdcard\/|content:\/\/)/i.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
const normalizeUploadPath = (res: any) => {
|
const normalizeUploadPath = (res: any) => {
|
||||||
return String(
|
const rawPath = String(
|
||||||
res?.data?.filePath || res?.data?.url || res?.filePath || res?.url || res?.data?.path || '',
|
res?.data?.url ||
|
||||||
|
res?.url ||
|
||||||
|
res?.data?.filePath ||
|
||||||
|
res?.filePath ||
|
||||||
|
res?.data?.path ||
|
||||||
|
res?.path ||
|
||||||
|
'',
|
||||||
)
|
)
|
||||||
.replace(/[`]/g, '')
|
.replace(/[`]/g, '')
|
||||||
.replace(/http:\/\//gi, 'https://')
|
.replace(/http:\/\//gi, 'https://')
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
|
if (!rawPath) return ''
|
||||||
|
if (isLocalFilePath(rawPath)) {
|
||||||
|
monitorWarn('upload result is local path, ignore it', rawPath)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (/^https?:\/\//i.test(rawPath)) return rawPath
|
||||||
|
|
||||||
|
const origin = getServerOrigin()
|
||||||
|
if (origin && rawPath.startsWith('/')) return `${origin}${rawPath}`
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseNativeScreenshotPath = (raw: unknown): string => {
|
||||||
|
if (typeof raw === 'string' && raw.trim()) return raw.trim()
|
||||||
|
if (!raw || typeof raw !== 'object') return ''
|
||||||
|
const r = raw as Record<string, unknown>
|
||||||
|
const directPath = r.filePath ?? r.path ?? r.localPath ?? r.url
|
||||||
|
if (typeof directPath === 'string' && directPath.trim()) return directPath.trim()
|
||||||
|
const d = r.data
|
||||||
|
if (typeof d === 'string' && d.trim()) return d.trim()
|
||||||
|
if (d && typeof d === 'object') {
|
||||||
|
const o = d as Record<string, unknown>
|
||||||
|
const p = o.filePath ?? o.path ?? o.localPath
|
||||||
|
if (typeof p === 'string' && p.trim()) return p.trim()
|
||||||
|
}
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadNativeScreenshot = async () => {
|
const uploadNativeScreenshot = async () => {
|
||||||
|
try {
|
||||||
const { token } = storeToRefs(userStore())
|
const { token } = storeToRefs(userStore())
|
||||||
const { data: filePath } = $XXL.getScreenshot() || {}
|
const raw = $XXL.getScreenshot() || {}
|
||||||
if (!filePath) return ''
|
monitorLog('native getScreenshot result', raw)
|
||||||
|
const filePath = parseNativeScreenshotPath(raw)
|
||||||
|
if (!filePath) {
|
||||||
|
monitorWarn('native screenshot path empty')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
monitorLog('native screenshot path', filePath)
|
||||||
|
|
||||||
const res = await $XXL.uploadFile({
|
const res = await $XXL.uploadFile({
|
||||||
url: `${import.meta.env.VITE_SERVER_URL}/sysFileInfo/tenUploadAll`,
|
url: `${import.meta.env.VITE_SERVER_URL}/sysFileInfo/tenUploadAll`,
|
||||||
@ -37,70 +95,220 @@ const uploadNativeScreenshot = async () => {
|
|||||||
filePath,
|
filePath,
|
||||||
fileKey: 'file',
|
fileKey: 'file',
|
||||||
})
|
})
|
||||||
return normalizeUploadPath(res)
|
const url = normalizeUploadPath(res)
|
||||||
|
monitorLog('native upload result', { raw: res, url })
|
||||||
|
return url
|
||||||
|
} catch (err) {
|
||||||
|
monitorWarn('native screenshot upload failed', err)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createWebScreenshotFile = async () => {
|
/** App WebView 无原生截图时仅能用 DOM 截图;尽量贴合「当前视口」并降低高 DPR 白屏概率 */
|
||||||
const canvas = await html2canvas(document.body, {
|
const getMonitorCaptureElement = (): HTMLElement => {
|
||||||
useCORS: true,
|
const app = document.getElementById('app')
|
||||||
backgroundColor: null,
|
if (app && (app.offsetWidth > 0 || app.offsetHeight > 0)) return app
|
||||||
x: window.scrollX,
|
const scrollRoot = document.scrollingElement
|
||||||
y: window.scrollY,
|
if (scrollRoot instanceof HTMLElement) return scrollRoot
|
||||||
width: window.innerWidth,
|
return document.documentElement
|
||||||
height: window.innerHeight,
|
}
|
||||||
windowWidth: window.innerWidth,
|
|
||||||
windowHeight: window.innerHeight,
|
|
||||||
scale: window.devicePixelRatio || 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
return await new Promise<File | null>(resolve => {
|
const prepareMonitorDomForCapture = async () => {
|
||||||
|
try {
|
||||||
|
await document.fonts?.ready
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
await new Promise<void>(resolve => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => resolve())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvasToPngFile = (canvas: HTMLCanvasElement) =>
|
||||||
|
new Promise<File | null>(resolve => {
|
||||||
canvas.toBlob(blob => {
|
canvas.toBlob(blob => {
|
||||||
if (!blob) return resolve(null)
|
if (!blob) return resolve(null)
|
||||||
resolve(new File([blob], `monitor-${Date.now()}.png`, { type: 'image/png' }))
|
resolve(new File([blob], `monitor-${Date.now()}.png`, { type: 'image/png' }))
|
||||||
}, 'image/png')
|
}, 'image/png')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const createWebScreenshotFile = async (): Promise<File | null> => {
|
||||||
|
await prepareMonitorDomForCapture()
|
||||||
|
|
||||||
|
const vw = window.innerWidth
|
||||||
|
const vh = window.innerHeight
|
||||||
|
const scale = Math.min(window.devicePixelRatio || 1, 2)
|
||||||
|
monitorLog('web screenshot start', {
|
||||||
|
href: window.location.href,
|
||||||
|
viewport: `${vw}x${vh}`,
|
||||||
|
dpr: window.devicePixelRatio,
|
||||||
|
scale,
|
||||||
|
scrollX: window.scrollX,
|
||||||
|
scrollY: window.scrollY,
|
||||||
|
})
|
||||||
|
const base = {
|
||||||
|
useCORS: true,
|
||||||
|
allowTaint: false,
|
||||||
|
backgroundColor: '#ffffff' as const,
|
||||||
|
scale,
|
||||||
|
imageTimeout: 15000,
|
||||||
|
logging: import.meta.env.DEV,
|
||||||
|
}
|
||||||
|
const win = {
|
||||||
|
scrollX: -window.scrollX,
|
||||||
|
scrollY: -window.scrollY,
|
||||||
|
windowWidth: Math.max(document.documentElement.clientWidth, vw),
|
||||||
|
windowHeight: Math.max(document.documentElement.clientHeight, vh),
|
||||||
|
}
|
||||||
|
|
||||||
|
const attempts: Array<() => Promise<HTMLCanvasElement>> = [
|
||||||
|
// 视口截图常用写法(对 WebView 比单纯裁 x/y 更稳)
|
||||||
|
() =>
|
||||||
|
html2canvas(document.body, {
|
||||||
|
...base,
|
||||||
|
foreignObjectRendering: false,
|
||||||
|
...win,
|
||||||
|
}),
|
||||||
|
() =>
|
||||||
|
html2canvas(document.body, {
|
||||||
|
...base,
|
||||||
|
foreignObjectRendering: true,
|
||||||
|
...win,
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
const el = getMonitorCaptureElement()
|
||||||
|
const sx = window.scrollX
|
||||||
|
const sy = window.scrollY
|
||||||
|
return html2canvas(el, {
|
||||||
|
...base,
|
||||||
|
foreignObjectRendering: false,
|
||||||
|
x: sx,
|
||||||
|
y: sy,
|
||||||
|
width: vw,
|
||||||
|
height: vh,
|
||||||
|
windowWidth: vw,
|
||||||
|
windowHeight: vh,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const [index, run] of attempts.entries()) {
|
||||||
|
try {
|
||||||
|
monitorLog('web screenshot attempt start', index + 1)
|
||||||
|
const canvas = await run()
|
||||||
|
monitorLog('web screenshot canvas', {
|
||||||
|
attempt: index + 1,
|
||||||
|
width: canvas.width,
|
||||||
|
height: canvas.height,
|
||||||
|
})
|
||||||
|
if (!canvas.width || !canvas.height) {
|
||||||
|
monitorWarn('web screenshot canvas empty', index + 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const file = await canvasToPngFile(canvas)
|
||||||
|
if (file) {
|
||||||
|
monitorLog('web screenshot file ready', {
|
||||||
|
attempt: index + 1,
|
||||||
|
name: file.name,
|
||||||
|
size: file.size,
|
||||||
|
type: file.type,
|
||||||
|
})
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
monitorWarn('web screenshot toBlob returned empty', index + 1)
|
||||||
|
} catch (err) {
|
||||||
|
monitorWarn('web screenshot attempt failed', index + 1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
monitorWarn('web screenshot all attempts failed')
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadWebScreenshot = async () => {
|
const uploadWebScreenshot = async () => {
|
||||||
|
monitorLog('web upload start')
|
||||||
const file = await createWebScreenshotFile()
|
const file = await createWebScreenshotFile()
|
||||||
if (!file) return ''
|
if (!file) {
|
||||||
|
monitorWarn('web upload skipped because screenshot file is empty')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
const { data: res } = await globalApi.uploadFile(formData)
|
const { data: res } = await globalApi.uploadFile(formData)
|
||||||
return normalizeUploadPath(res)
|
const url = normalizeUploadPath(res)
|
||||||
|
monitorLog('web upload result', { raw: res, url })
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadMonitorScreenshot = async () => {
|
const uploadMonitorScreenshot = async () => {
|
||||||
if (sys_type === sys_type_study_machine) {
|
// $xxl(学习机)与 $xxl_universal(通用 App)都可能提供 getScreenshot,不能只用 sys_type 区分
|
||||||
return await uploadNativeScreenshot()
|
const nativeApi = getNativeApi()
|
||||||
|
monitorLog('choose screenshot mode', {
|
||||||
|
hasNativeApi: !!nativeApi,
|
||||||
|
hasGetScreenshot: typeof nativeApi?.getScreenshot === 'function',
|
||||||
|
hasUploadFile: typeof nativeApi?.uploadFile === 'function',
|
||||||
|
})
|
||||||
|
if (nativeApi && typeof nativeApi.getScreenshot === 'function') {
|
||||||
|
const url = await uploadNativeScreenshot()
|
||||||
|
if (url) return url
|
||||||
|
monitorWarn('native screenshot empty, fallback to web screenshot')
|
||||||
}
|
}
|
||||||
return await uploadWebScreenshot()
|
return await uploadWebScreenshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
const reportMonitorSnapshot = async (sessionId: string) => {
|
const reportMonitorSnapshot = async (sessionId: string) => {
|
||||||
const { userInfo } = storeToRefs(userStore())
|
const { userInfo } = storeToRefs(userStore())
|
||||||
if (isUploadingMonitorSnapshot || monitorSessionId !== sessionId) return
|
if (isUploadingMonitorSnapshot || monitorSessionId !== sessionId) {
|
||||||
|
monitorLog('snapshot skipped', {
|
||||||
|
isUploadingMonitorSnapshot,
|
||||||
|
monitorSessionId,
|
||||||
|
sessionId,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isUploadingMonitorSnapshot = true
|
isUploadingMonitorSnapshot = true
|
||||||
|
monitorLog('snapshot report start', { sessionId, userId: userInfo.value.userId })
|
||||||
try {
|
try {
|
||||||
const url = await uploadMonitorScreenshot()
|
const url = await uploadMonitorScreenshot()
|
||||||
if (!url || monitorSessionId !== sessionId) return
|
if (!url || monitorSessionId !== sessionId) {
|
||||||
|
monitorWarn('snapshot report skipped after screenshot', {
|
||||||
|
url,
|
||||||
|
monitorSessionId,
|
||||||
|
sessionId,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monitorLog('snapshot final image url', url)
|
||||||
|
|
||||||
await globalApi.reportSnapshot({
|
await globalApi.reportSnapshot({
|
||||||
sessionId,
|
sessionId,
|
||||||
url,
|
url,
|
||||||
userId: userInfo.value.userId,
|
userId: userInfo.value.userId,
|
||||||
})
|
})
|
||||||
|
monitorLog('snapshot report success', { sessionId, url })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
monitorError('snapshot report failed', err)
|
||||||
} finally {
|
} finally {
|
||||||
isUploadingMonitorSnapshot = false
|
isUploadingMonitorSnapshot = false
|
||||||
|
monitorLog('snapshot report end', { sessionId })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 与老师讲解 WS 等场景兼容:外层 type 可能是 push/classMessage,监控类型在 msg.type */
|
||||||
|
export const resolveMonitorWsType = (data: anyObj): string | undefined => {
|
||||||
|
const nested = data?.msg?.type
|
||||||
|
if (nested === MONITOR_STOP_MSG_TYPE || nested === MONITOR_MSG_TYPE) return nested
|
||||||
|
const top = data?.type
|
||||||
|
if (top === MONITOR_STOP_MSG_TYPE || top === MONITOR_MSG_TYPE) return top
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export const stopClassMonitor = (showTip = false) => {
|
export const stopClassMonitor = (showTip = false) => {
|
||||||
const isMonitoring = !!monitorTimer || !!monitorSessionId || monitorState === 1
|
const isMonitoring = !!monitorTimer || !!monitorSessionId || monitorState === 1
|
||||||
|
monitorLog('stop monitor', { showTip, isMonitoring, monitorSessionId, monitorState })
|
||||||
if (monitorTimer) {
|
if (monitorTimer) {
|
||||||
clearInterval(monitorTimer)
|
clearInterval(monitorTimer)
|
||||||
monitorTimer = null
|
monitorTimer = null
|
||||||
@ -117,8 +325,15 @@ export const stopClassMonitor = (showTip = false) => {
|
|||||||
|
|
||||||
const startClassMonitor = (msg: anyObj) => {
|
const startClassMonitor = (msg: anyObj) => {
|
||||||
const sessionId = msg?.sessionId
|
const sessionId = msg?.sessionId
|
||||||
if (!sessionId) return
|
monitorLog('start monitor message', msg)
|
||||||
if (monitorTimer && monitorSessionId === sessionId) return
|
if (!sessionId) {
|
||||||
|
monitorWarn('start monitor ignored because sessionId is empty')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (monitorTimer && monitorSessionId === sessionId) {
|
||||||
|
monitorLog('start monitor ignored because session is already running', sessionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
stopClassMonitor()
|
stopClassMonitor()
|
||||||
monitorSessionId = sessionId
|
monitorSessionId = sessionId
|
||||||
@ -132,10 +347,18 @@ const startClassMonitor = (msg: anyObj) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const handleClassMonitorMessage = (data: anyObj) => {
|
export const handleClassMonitorMessage = (data: anyObj) => {
|
||||||
const messageType = data?.type || data?.msg?.type
|
const monitorType = resolveMonitorWsType(data)
|
||||||
if (messageType !== MONITOR_MSG_TYPE) return false
|
monitorLog('handle monitor ws message', { monitorType, data })
|
||||||
|
if (monitorType === MONITOR_STOP_MSG_TYPE) {
|
||||||
|
stopClassMonitor(true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (monitorType !== MONITOR_MSG_TYPE) return false
|
||||||
|
|
||||||
const msg = data?.msg || data
|
const msg: anyObj = {
|
||||||
|
...(data && typeof data === 'object' ? data : {}),
|
||||||
|
...(data?.msg && typeof data.msg === 'object' && !Array.isArray(data.msg) ? data.msg : {}),
|
||||||
|
}
|
||||||
const monitoring = msg?.monitoring
|
const monitoring = msg?.monitoring
|
||||||
if (monitoring === true || monitoring === 'true') {
|
if (monitoring === true || monitoring === 'true') {
|
||||||
startClassMonitor(msg)
|
startClassMonitor(msg)
|
||||||
|
|||||||
@ -5,7 +5,11 @@ import { getSN, $XXL, isNotStudyMachine } from '@/utils'
|
|||||||
import { useLog, LogType, useTips } from '@/hooks'
|
import { useLog, LogType, useTips } from '@/hooks'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { handleClassMonitorMessage, stopClassMonitor } from './useClassMonitor'
|
import {
|
||||||
|
handleClassMonitorMessage,
|
||||||
|
resolveMonitorWsType,
|
||||||
|
stopClassMonitor,
|
||||||
|
} from './useClassMonitor'
|
||||||
|
|
||||||
let SOCKET: UseWebSocketReturn<any>
|
let SOCKET: UseWebSocketReturn<any>
|
||||||
|
|
||||||
@ -19,6 +23,7 @@ enum SOCKET_MSG_TYPE {
|
|||||||
systemMessage = 'systemMessage',
|
systemMessage = 'systemMessage',
|
||||||
messageReceive = 'messageReceive',
|
messageReceive = 'messageReceive',
|
||||||
monitorStart = 'monitorStart',
|
monitorStart = 'monitorStart',
|
||||||
|
monitorStop = 'monitorStop',
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspectorEnd = ref<anyObj>()
|
const inspectorEnd = ref<anyObj>()
|
||||||
@ -101,7 +106,9 @@ export const useSocket = () => {
|
|||||||
|
|
||||||
const onMessage = (ws: WebSocket, event: MessageEvent) => {
|
const onMessage = (ws: WebSocket, event: MessageEvent) => {
|
||||||
const data = JSON.parse(event.data)
|
const data = JSON.parse(event.data)
|
||||||
const messageType = data.type || data.msg?.type
|
const monitorEnvelope = resolveMonitorWsType(data)
|
||||||
|
const messageType = monitorEnvelope ?? data.type ?? data.msg?.type
|
||||||
|
const isPayloadMonitor = Boolean(monitorEnvelope)
|
||||||
|
|
||||||
// 确认接收
|
// 确认接收
|
||||||
sendMsg({
|
sendMsg({
|
||||||
@ -120,6 +127,7 @@ export const useSocket = () => {
|
|||||||
|
|
||||||
// if (isWeb) return;
|
// if (isWeb) return;
|
||||||
if (
|
if (
|
||||||
|
!isPayloadMonitor &&
|
||||||
messageType !== SOCKET_MSG_TYPE.lockScreen &&
|
messageType !== SOCKET_MSG_TYPE.lockScreen &&
|
||||||
messageType !== SOCKET_MSG_TYPE.unLockScreen &&
|
messageType !== SOCKET_MSG_TYPE.unLockScreen &&
|
||||||
typeof data.msg?.locked !== 'undefined'
|
typeof data.msg?.locked !== 'undefined'
|
||||||
@ -142,6 +150,7 @@ export const useSocket = () => {
|
|||||||
onDesktopPreview()
|
onDesktopPreview()
|
||||||
break
|
break
|
||||||
case SOCKET_MSG_TYPE.monitorStart:
|
case SOCKET_MSG_TYPE.monitorStart:
|
||||||
|
case SOCKET_MSG_TYPE.monitorStop:
|
||||||
handleClassMonitorMessage(data)
|
handleClassMonitorMessage(data)
|
||||||
break
|
break
|
||||||
case SOCKET_MSG_TYPE.inspectorStart:
|
case SOCKET_MSG_TYPE.inspectorStart:
|
||||||
|
|||||||
@ -7,8 +7,9 @@ export const sys_type_study_machine = 2
|
|||||||
// 普通移动端
|
// 普通移动端
|
||||||
export const sys_type_native_universal = 3
|
export const sys_type_native_universal = 3
|
||||||
|
|
||||||
// 原生api的调用对象
|
// 原生api的调用对象。App WebView 里桥可能晚于业务脚本注入,调用时需要重新读取。
|
||||||
export const native_api = window.$xxl || window.$xxl_universal
|
export const getNativeApi = () => window.$xxl || window.$xxl_universal
|
||||||
|
export const native_api = getNativeApi()
|
||||||
|
|
||||||
// 系统类型
|
// 系统类型
|
||||||
export const sys_type = window.$xxl
|
export const sys_type = window.$xxl
|
||||||
@ -102,7 +103,8 @@ export const $XXL = {
|
|||||||
|
|
||||||
const pack = (method: string, config: anyObj = {}, cb?: anyFuc): any => {
|
const pack = (method: string, config: anyObj = {}, cb?: anyFuc): any => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (isWebEvn) {
|
const api = getNativeApi()
|
||||||
|
if (!api) {
|
||||||
reject(new Error('暂不支持'))
|
reject(new Error('暂不支持'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -128,14 +130,15 @@ const pack = (method: string, config: anyObj = {}, cb?: anyFuc): any => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
native_api[method]?.(JSON.stringify({ ...config, cb: `xxl_cb.${cbName}` }))
|
api[method]?.(JSON.stringify({ ...config, cb: `xxl_cb.${cbName}` }))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const packSync = (method: string, ...args: any[]) => {
|
const packSync = (method: string, ...args: any[]) => {
|
||||||
try {
|
try {
|
||||||
if (!native_api) return
|
const api = getNativeApi()
|
||||||
const res = native_api[method]?.(...args)
|
if (!api) return
|
||||||
|
const res = api[method]?.(...args)
|
||||||
return res && JSON.parse(res)
|
return res && JSON.parse(res)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user