feat(web-admin): add mp webview shell and /m/pl/plan static routes.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
c0721786ff
commit
8fbe0679c9
8 files changed
+238
No files matched your search
@@ -0,0 +1,15 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
export const mpWebviewApi = {
|
||||
exchange: (ticket: string) =>
|
||||
request.post<{
|
||||
token: string
|
||||
refreshToken: string
|
||||
expiresIn: number
|
||||
userInfo: Api.Auth.UserInfo
|
||||
}>({
|
||||
url: '/web/auth/mp-webview-exchange',
|
||||
data: { ticket },
|
||||
showErrorMessage: false
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<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;
|
||||
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>
|
||||
@@ -129,6 +129,48 @@ export const staticRoutes: AppRouteRecordRaw[] = [
|
||||
component: () => import('@views/public/duty/index.vue'),
|
||||
meta: { title: '值班抢班', isHideTab: true, isPublicMobile: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan',
|
||||
name: 'MpPlPlanList',
|
||||
component: () => import('@views/m/pl/plan/index.vue'),
|
||||
meta: { title: '运输计划', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/new',
|
||||
name: 'MpPlPlanNew',
|
||||
component: () => import('@views/m/pl/plan/form.vue'),
|
||||
meta: { title: '新增计划', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/paste',
|
||||
name: 'MpPlPlanPaste',
|
||||
component: () => import('@views/m/pl/plan/paste.vue'),
|
||||
meta: { title: '三方人车快速创建', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/:id/edit',
|
||||
name: 'MpPlPlanEdit',
|
||||
component: () => import('@views/m/pl/plan/form.vue'),
|
||||
meta: { title: '编辑计划', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/:id/arrange',
|
||||
name: 'MpPlPlanArrange',
|
||||
component: () => import('@views/m/pl/plan/form.vue'),
|
||||
meta: { title: '安排运输', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/:id/print',
|
||||
name: 'MpPlPlanPrint',
|
||||
component: () => import('@views/m/pl/plan/print.vue'),
|
||||
meta: { title: '打印单据', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/m/pl/plan/:id',
|
||||
name: 'MpPlPlanDetail',
|
||||
component: () => import('@views/m/pl/plan/detail.vue'),
|
||||
meta: { title: '计划详情', isHideTab: true, isMpWebview: true }
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'Exception404CatchAll',
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<MpWebviewShell>
|
||||
<p class="mp-wv-placeholder">计划详情加载中…</p>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mp-wv-placeholder {
|
||||
margin: 24px 0 0;
|
||||
font-size: 14px;
|
||||
color: #6b7b74;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<MpWebviewShell>
|
||||
<p class="mp-wv-placeholder">计划表单加载中…</p>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mp-wv-placeholder {
|
||||
margin: 24px 0 0;
|
||||
font-size: 14px;
|
||||
color: #6b7b74;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<MpWebviewShell>
|
||||
<p class="mp-wv-placeholder">运输计划加载中…</p>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mp-wv-placeholder {
|
||||
margin: 24px 0 0;
|
||||
font-size: 14px;
|
||||
color: #6b7b74;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<MpWebviewShell>
|
||||
<p class="mp-wv-placeholder">三方人车快速创建加载中…</p>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mp-wv-placeholder {
|
||||
margin: 24px 0 0;
|
||||
font-size: 14px;
|
||||
color: #6b7b74;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<MpWebviewShell>
|
||||
<p class="mp-wv-placeholder">打印预览加载中…</p>
|
||||
</MpWebviewShell>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MpWebviewShell from '@/layouts/MpWebviewShell.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mp-wv-placeholder {
|
||||
margin: 24px 0 0;
|
||||
font-size: 14px;
|
||||
color: #6b7b74;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user