257 lines
7.5 KiB
Vue
257 lines
7.5 KiB
Vue
<template>
|
||
<!-- 通用 OA web-view:发起 + 详情共用;结构对齐 start/webview -->
|
||
<web-view v-if="src" :src="src" @message="onMessage" />
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
buildOaDetailUrl,
|
||
buildOaStartUrl,
|
||
resolveOaReturnPath,
|
||
takeOaDetailTitle,
|
||
takeOaStartTitle
|
||
} from '../../../utils/oa-h5.js'
|
||
import { navigateBackFromOaHost } from '../../../utils/oa-navigate-back.js'
|
||
import {
|
||
forceRelogin,
|
||
getToken,
|
||
silentWechatLogin
|
||
} from '../../../utils/auth.js'
|
||
import { fetchOaTemplateDetail } from '../../../utils/oa-api.js'
|
||
|
||
const CLOSE_ACTIONS = new Set([
|
||
'oa-start-close',
|
||
'oa-start-cancel',
|
||
'oa-start-draft',
|
||
'oa-start-submitted',
|
||
'oa-detail-close',
|
||
'oa-detail-done',
|
||
'oa-detail-withdraw',
|
||
'oa-detail-restart'
|
||
])
|
||
|
||
function buildQueryString(query) {
|
||
const q = query || {}
|
||
const parts = []
|
||
Object.keys(q).forEach((key) => {
|
||
const val = q[key]
|
||
if (val === undefined || val === null || val === '') return
|
||
parts.push(
|
||
encodeURIComponent(key) + '=' + encodeURIComponent(String(val))
|
||
)
|
||
})
|
||
return parts.join('&')
|
||
}
|
||
|
||
export default {
|
||
data() {
|
||
return { src: '', _closing: false, _query: {} }
|
||
},
|
||
async onLoad(query) {
|
||
this._query = query || {}
|
||
if (!getToken()) {
|
||
try {
|
||
await silentWechatLogin()
|
||
} catch (e) {
|
||
const qs = buildQueryString(this._query)
|
||
forceRelogin(
|
||
'请先登录',
|
||
'/pages/oa/webview/index' + (qs ? '?' + qs : '')
|
||
)
|
||
return
|
||
}
|
||
}
|
||
this.openFromQuery(this._query)
|
||
},
|
||
onShow() {
|
||
console.log('[oa webview] onShow closing=', this._closing)
|
||
},
|
||
onUnload() {
|
||
console.log('[oa webview] onUnload')
|
||
// #ifdef H5
|
||
if (this._onParentMessage) {
|
||
window.removeEventListener('message', this._onParentMessage)
|
||
this._onParentMessage = null
|
||
}
|
||
// #endif
|
||
},
|
||
methods: {
|
||
openFromQuery(query) {
|
||
const instanceId = String((query && query.instanceId) || '')
|
||
const taskId = String((query && query.taskId) || '')
|
||
const mode = String((query && query.mode) || '') || 'view'
|
||
const autoPrint = String((query && query.autoPrint) || '') === '1'
|
||
const appId = String((query && query.appId) || '')
|
||
const draftId = String((query && query.draftId) || '')
|
||
const fromInstanceId = String((query && query.fromInstanceId) || '')
|
||
|
||
// 传给 OA H5:真机 navigateBack 静默失败时 wx.reLaunch/redirectTo 用
|
||
const returnPath = resolveOaReturnPath(query)
|
||
|
||
let src = ''
|
||
|
||
if (instanceId) {
|
||
const cachedName = takeOaDetailTitle()
|
||
if (cachedName) {
|
||
uni.setNavigationBarTitle({ title: cachedName })
|
||
}
|
||
src = buildOaDetailUrl({
|
||
instanceId,
|
||
taskId,
|
||
mode,
|
||
autoPrint,
|
||
returnPath
|
||
})
|
||
} else if (draftId || appId || fromInstanceId) {
|
||
const cachedName = takeOaStartTitle()
|
||
if (cachedName) {
|
||
uni.setNavigationBarTitle({ title: cachedName })
|
||
} else if (appId) {
|
||
this.resolveTitle(appId)
|
||
}
|
||
src = buildOaStartUrl({ appId, draftId, fromInstanceId, returnPath })
|
||
} else {
|
||
uni.showToast({ title: '参数错误', icon: 'none' })
|
||
setTimeout(() => {
|
||
uni.navigateBack({
|
||
fail: () => {}
|
||
})
|
||
}, 400)
|
||
return
|
||
}
|
||
|
||
if (!/\btoken=/.test(src)) {
|
||
const qs = buildQueryString(query)
|
||
forceRelogin(
|
||
'请先登录',
|
||
'/pages/oa/webview/index' + (qs ? '?' + qs : '')
|
||
)
|
||
return
|
||
}
|
||
|
||
console.log('[oa webview] onLoad', {
|
||
returnPath,
|
||
mode,
|
||
instanceId,
|
||
taskId,
|
||
appId,
|
||
draftId,
|
||
fromInstanceId,
|
||
src
|
||
})
|
||
try {
|
||
const pages = getCurrentPages() || []
|
||
console.log(
|
||
'[oa webview] stack',
|
||
pages.map((p) => (p && p.route) || '')
|
||
)
|
||
} catch (e) {
|
||
console.log('[oa webview] stack err', e)
|
||
}
|
||
this.src = src
|
||
|
||
// H5:OA 页在 iframe 内,靠 postMessage 通知本页 uni.navigateBack
|
||
// (微信小程序由 OA 内 wx.miniProgram.navigateBack,勿在此再退一层)
|
||
// #ifdef H5
|
||
if (!this._onParentMessage) {
|
||
this._onParentMessage = (e) => {
|
||
const d = e && e.data
|
||
if (!d || d.source !== 'zkxw-oa-h5') return
|
||
console.log('[oa webview] iframe message', d)
|
||
this.navigateBackFromOa(d)
|
||
}
|
||
window.addEventListener('message', this._onParentMessage)
|
||
}
|
||
// #endif
|
||
},
|
||
async resolveTitle(appId) {
|
||
try {
|
||
const detail = await fetchOaTemplateDetail(appId)
|
||
const name = String(
|
||
(detail && (detail.appName || detail.name)) || ''
|
||
).trim()
|
||
if (name) uni.setNavigationBarTitle({ title: name })
|
||
} catch (_) {
|
||
/* 保留 pages.json 兜底标题 */
|
||
}
|
||
},
|
||
navigateBackFromOa(payload) {
|
||
console.log(
|
||
'[oa webview] navigateBackFromOa',
|
||
payload,
|
||
'closing=',
|
||
this._closing
|
||
)
|
||
if (this._closing) return
|
||
const action = String((payload && payload.action) || '')
|
||
if (action && !CLOSE_ACTIONS.has(action)) {
|
||
console.log('[oa webview] ignore action', action)
|
||
return
|
||
}
|
||
|
||
// 复制重开:离开详情 webview 栈时,改开发起页(同页 redirect)
|
||
if (action === 'oa-detail-restart') {
|
||
const appId = String((payload && payload.appId) || '').trim()
|
||
const fromInstanceId = String(
|
||
(payload && (payload.fromInstanceId || payload.instanceId)) || ''
|
||
).trim()
|
||
if (!appId && !fromInstanceId) return
|
||
this._closing = true
|
||
const q = []
|
||
if (appId) q.push('appId=' + encodeURIComponent(appId))
|
||
if (fromInstanceId) {
|
||
q.push('fromInstanceId=' + encodeURIComponent(fromInstanceId))
|
||
}
|
||
uni.redirectTo({
|
||
url: '/pages/oa/webview/index?' + q.join('&'),
|
||
fail: () => {
|
||
this._closing = false
|
||
uni.navigateTo({
|
||
url: '/pages/oa/webview/index?' + q.join('&'),
|
||
fail: () => {
|
||
this._closing = false
|
||
}
|
||
})
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
this._closing = true
|
||
const msg = String((payload && payload.message) || '').trim()
|
||
if (msg) {
|
||
uni.showToast({ title: msg, icon: 'none', duration: 1200 })
|
||
}
|
||
setTimeout(() => {
|
||
navigateBackFromOaHost({
|
||
onStuck: () => {
|
||
this._closing = false
|
||
}
|
||
})
|
||
}, msg ? 400 : 0)
|
||
},
|
||
onMessage(e) {
|
||
// 微信:postMessage 多在 web-view 后退/销毁时送达。
|
||
// 若 H5 侧 navigateBack 已成功,此处再退会多退一层,故默认只打日志。
|
||
// 若携带需宿主处理的 action(如复制重开),在仍停留本页时兜底处理。
|
||
console.log('[oa webview] @message raw', e && e.detail)
|
||
const batches = (e && e.detail && e.detail.data) || []
|
||
const items = Array.isArray(batches) ? batches : [batches]
|
||
for (let i = items.length - 1; i >= 0; i--) {
|
||
const raw = items[i]
|
||
const payload =
|
||
raw && typeof raw === 'object' && raw.data !== undefined
|
||
? raw.data
|
||
: raw
|
||
const action = String((payload && payload.action) || '')
|
||
console.log('[oa webview] @message item', i, action, payload)
|
||
if (action === 'oa-detail-restart') {
|
||
this.navigateBackFromOa(payload)
|
||
return
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|