|
|
@@ -0,0 +1,183 @@
|
|
|
+<template>
|
|
|
+ <div class="oa-mp-bind">
|
|
|
+ <div v-if="phase === 'loading'" class="oa-mp-bind__card">
|
|
|
+ <van-loading type="spinner" color="#0a9f6d" vertical>处理中…</van-loading>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="phase === 'success'" class="oa-mp-bind__card oa-mp-bind__card--ok">
|
|
|
+ <div class="oa-mp-bind__icon" aria-hidden="true">✓</div>
|
|
|
+ <p class="oa-mp-bind__title">已开启审批通知</p>
|
|
|
+ <p class="oa-mp-bind__hint">可返回小程序继续使用</p>
|
|
|
+ <van-button type="primary" block class="oa-mp-bind__btn" @click="goBack">
|
|
|
+ 返回
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
+ <div v-else class="oa-mp-bind__card oa-mp-bind__card--err">
|
|
|
+ <p class="oa-mp-bind__title">{{ errorText || '绑定失败' }}</p>
|
|
|
+ <van-button plain block class="oa-mp-bind__btn" @click="goBack">返回</van-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { onMounted, ref } from 'vue'
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
+import { showToast } from 'vant'
|
|
|
+import { captureTokenFromUrl, getToken } from '../utils/auth.js'
|
|
|
+import { post } from '../utils/http.js'
|
|
|
+import { closeToHost } from '../oa/close-to-host.js'
|
|
|
+
|
|
|
+const route = useRoute()
|
|
|
+/** @type {import('vue').Ref<'loading' | 'success' | 'error'>} */
|
|
|
+const phase = ref('loading')
|
|
|
+const errorText = ref('')
|
|
|
+
|
|
|
+function readCode() {
|
|
|
+ const fromRoute = String(route.query.code || '').trim()
|
|
|
+ if (fromRoute) return fromRoute
|
|
|
+ try {
|
|
|
+ return String(new URLSearchParams(window.location.search || '').get('code') || '').trim()
|
|
|
+ } catch {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function stablePageUrl() {
|
|
|
+ return `${window.location.origin}/oa-h5/oa-mp-bind`
|
|
|
+}
|
|
|
+
|
|
|
+function redirectToWechatOAuth(appid) {
|
|
|
+ const redirectUri = encodeURIComponent(stablePageUrl())
|
|
|
+ const url =
|
|
|
+ `https://open.weixin.qq.com/connect/oauth2/authorize` +
|
|
|
+ `?appid=${encodeURIComponent(appid)}` +
|
|
|
+ `&redirect_uri=${redirectUri}` +
|
|
|
+ `&response_type=code&scope=snsapi_base&state=mpbind#wechat_redirect`
|
|
|
+ window.location.replace(url)
|
|
|
+}
|
|
|
+
|
|
|
+function fail(msg) {
|
|
|
+ errorText.value = msg
|
|
|
+ phase.value = 'error'
|
|
|
+}
|
|
|
+
|
|
|
+function goBack() {
|
|
|
+ closeToHost(
|
|
|
+ { action: 'oa-mp-bind-done', message: phase.value === 'success' ? '已开启审批通知' : '' },
|
|
|
+ {
|
|
|
+ showToast: (msg) => showToast(msg),
|
|
|
+ fallbackMessage: '请关闭本页返回小程序',
|
|
|
+ fallbackMiniPath: '/pages/index/index'
|
|
|
+ }
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+async function bindWithCode(code) {
|
|
|
+ try {
|
|
|
+ await post('/mini/auth/mp-bind', { code })
|
|
|
+ phase.value = 'success'
|
|
|
+ // 成功后尝试回到小程序(可选)
|
|
|
+ try {
|
|
|
+ closeToHost(
|
|
|
+ { action: 'oa-mp-bind-done', message: '已开启审批通知' },
|
|
|
+ {
|
|
|
+ showToast: (msg) => showToast(msg),
|
|
|
+ fallbackMessage: '已开启审批通知',
|
|
|
+ fallbackMiniPath: '/pages/index/index'
|
|
|
+ }
|
|
|
+ )
|
|
|
+ } catch {
|
|
|
+ /* ignore host close errors */
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ fail((e && e.message) || '绑定失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ captureTokenFromUrl()
|
|
|
+ const code = readCode()
|
|
|
+ const appid = String(import.meta.env.VITE_WX_MP_APPID || '').trim()
|
|
|
+ const token = getToken()
|
|
|
+
|
|
|
+ if (!code) {
|
|
|
+ if (!appid) {
|
|
|
+ fail('未配置服务号 AppID')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!token) {
|
|
|
+ fail('请从小程序入口打开')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ redirectToWechatOAuth(appid)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!token) {
|
|
|
+ fail('请从小程序入口打开')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ void bindWithCode(code)
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.oa-mp-bind {
|
|
|
+ min-height: 100vh;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 24px 20px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #f5f7f6;
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__card {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 360px;
|
|
|
+ padding: 32px 24px 24px;
|
|
|
+ border-radius: 16px;
|
|
|
+ background: #fff;
|
|
|
+ text-align: center;
|
|
|
+ box-shadow: 0 4px 16px rgba(10, 159, 109, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__icon {
|
|
|
+ width: 56px;
|
|
|
+ height: 56px;
|
|
|
+ margin: 0 auto 16px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #e9f9f2;
|
|
|
+ color: #0a9f6d;
|
|
|
+ font-size: 28px;
|
|
|
+ line-height: 56px;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__title {
|
|
|
+ margin: 0 0 8px;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #0f291e;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__hint {
|
|
|
+ margin: 0 0 24px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #5f7a6d;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__card--err .oa-mp-bind__title {
|
|
|
+ margin-bottom: 24px;
|
|
|
+ color: #b91c1c;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.oa-mp-bind__btn {
|
|
|
+ --van-button-primary-background: #0a9f6d;
|
|
|
+ --van-button-primary-border-color: #0a9f6d;
|
|
|
+ border-radius: 10px;
|
|
|
+}
|
|
|
+</style>
|