93 lines
2.8 KiB
Vue
93 lines
2.8 KiB
Vue
<template>
|
|
<div class="mp-wv-root">
|
|
<p v-if="phase === 'boot'" class="mp-wv-placeholder">加载中…</p>
|
|
<p v-else-if="phase === 'need-mini'" class="mp-wv-placeholder">请从山峡化工小程序打开</p>
|
|
<p v-else-if="phase === 'expired'" class="mp-wv-placeholder">登录已失效</p>
|
|
<slot v-else />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, ref } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useUserStore } from '@/store/modules/user'
|
|
import { useDictStore } from '@/store/modules/dict'
|
|
import { mpWebviewApi } from '@/api/mp-webview'
|
|
import { redirectToMiniLogin, stripMpTicketFromUrl } from '@/utils/mp-webview'
|
|
|
|
const JWX_SRC = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const userStore = useUserStore()
|
|
const dictStore = useDictStore()
|
|
const phase = ref<'boot' | 'need-mini' | 'expired' | 'ready'>('boot')
|
|
|
|
function loadJweixin(): Promise<void> {
|
|
if (typeof window === 'undefined') return Promise.resolve()
|
|
const wxObj = (window as unknown as { wx?: { miniProgram?: unknown } }).wx
|
|
if (wxObj?.miniProgram) return Promise.resolve()
|
|
if (document.querySelector('script[data-mp-jweixin]')) return Promise.resolve()
|
|
return new Promise((resolve) => {
|
|
const script = document.createElement('script')
|
|
script.src = JWX_SRC
|
|
script.async = true
|
|
script.setAttribute('data-mp-jweixin', '1')
|
|
script.onload = () => resolve()
|
|
script.onerror = () => resolve()
|
|
document.head.appendChild(script)
|
|
})
|
|
}
|
|
|
|
async function bootstrap() {
|
|
await loadJweixin()
|
|
const ticket = String(route.query.mpTicket || '').trim()
|
|
if (ticket) {
|
|
try {
|
|
const data = await mpWebviewApi.exchange(ticket)
|
|
userStore.setToken(data.token, data.refreshToken)
|
|
userStore.setUserInfo(data.userInfo)
|
|
userStore.setLoginStatus(true)
|
|
await dictStore.loadAllDict()
|
|
const nextQuery = stripMpTicketFromUrl(route.path, { ...route.query })
|
|
await router.replace({ path: route.path, query: nextQuery as Record<string, string> })
|
|
phase.value = 'ready'
|
|
} catch {
|
|
phase.value = 'expired'
|
|
redirectToMiniLogin()
|
|
}
|
|
return
|
|
}
|
|
if (userStore.isLogin) {
|
|
phase.value = 'ready'
|
|
return
|
|
}
|
|
phase.value = 'need-mini'
|
|
}
|
|
|
|
onMounted(() => {
|
|
void bootstrap()
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.mp-wv-root {
|
|
--brand: #0a9f6d;
|
|
--el-color-primary: #0a9f6d;
|
|
box-sizing: border-box;
|
|
min-height: 100vh;
|
|
max-width: 480px;
|
|
margin: 0 auto;
|
|
padding: 12px 16px calc(24px + env(safe-area-inset-bottom));
|
|
color: #1f2a26;
|
|
background: #f5f8f7;
|
|
}
|
|
|
|
.mp-wv-placeholder {
|
|
margin: 48px 0 0;
|
|
font-size: 14px;
|
|
color: #6b7b74;
|
|
text-align: center;
|
|
}
|
|
</style>
|