|
@@ -0,0 +1,371 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="redirect-page">
|
|
|
|
|
+ <div class="bg bg-1" />
|
|
|
|
|
+ <div class="bg bg-2" />
|
|
|
|
|
+
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="brand">
|
|
|
|
|
+ <div class="icon">
|
|
|
|
|
+ <van-icon name="share-o" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="text">
|
|
|
|
|
+ <h1>正在跳转</h1>
|
|
|
|
|
+ <p>请稍候,系统正在为你打开页面</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="status-panel">
|
|
|
|
|
+ <div class="top">
|
|
|
|
|
+ <van-loading size="20px" type="spinner" />
|
|
|
|
|
+ <span class="status-text">{{ statusText }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <van-progress
|
|
|
|
|
+ :percentage="progress"
|
|
|
|
|
+ :show-pivot="false"
|
|
|
|
|
+ :stroke-width="6"
|
|
|
|
|
+ track-color="rgba(255,255,255,0.08)"
|
|
|
|
|
+ color="linear-gradient(90deg, #3b82f6 0%, #22c55e 100%)"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <div class="meta">
|
|
|
|
|
+ <span>{{ stepLabel }}</span>
|
|
|
|
|
+ <span>{{ progress }}%</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="tips">
|
|
|
|
|
+ <div class="title">处理中</div>
|
|
|
|
|
+ <div class="list">
|
|
|
|
|
+ <div class="item">
|
|
|
|
|
+ <van-icon name="passed" class="item-icon" />
|
|
|
|
|
+ <span>链接校验</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="item">
|
|
|
|
|
+ <van-icon name="shield-o" class="item-icon" />
|
|
|
|
|
+ <span>安全检测</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="item">
|
|
|
|
|
+ <van-icon name="guide-o" class="item-icon" />
|
|
|
|
|
+ <span>准备跳转</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="footer">
|
|
|
|
|
+ <p>若长时间未跳转,请稍后重试</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
|
|
+import { showToast } from 'vant'
|
|
|
|
|
+import { miniappUrlLinkApi } from '@/services/modules/wechat-redirect'
|
|
|
|
|
+
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
+
|
|
|
|
|
+const progress = ref(10)
|
|
|
|
|
+
|
|
|
|
|
+const getPushRecordId = (): string => {
|
|
|
|
|
+ const { pushRecordId } = route.query
|
|
|
|
|
+
|
|
|
|
|
+ if (typeof pushRecordId === 'string') {
|
|
|
|
|
+ return pushRecordId
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (Array.isArray(pushRecordId) && pushRecordId.length > 0) {
|
|
|
|
|
+ return pushRecordId[0] ?? ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ''
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getLoginUrl = (pushRecordId: string): string => {
|
|
|
|
|
+ const baseUrl =
|
|
|
|
|
+ import.meta.env.VITE_APP_ENV === 'prod'
|
|
|
|
|
+ ? 'https://hcp.yaoyi.net/h5/login'
|
|
|
|
|
+ : 'https://hcppre.yaoyi.net/h5/login'
|
|
|
|
|
+
|
|
|
|
|
+ return `${baseUrl}?pushRecordId=${encodeURIComponent(pushRecordId)}`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getMiniappLinkParams = (redirectUrl: string) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ path: 'pages/index/index',
|
|
|
|
|
+ query: `redirect=${encodeURIComponent(redirectUrl)}`,
|
|
|
|
|
+ envVersion: import.meta.env.VITE_APP_ENV === 'prod' ? 'release' : 'trial',
|
|
|
|
|
+ } as const
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const redirectToMiniapp = async (): Promise<void> => {
|
|
|
|
|
+ const pushRecordId = getPushRecordId()
|
|
|
|
|
+
|
|
|
|
|
+ if (!pushRecordId) {
|
|
|
|
|
+ showToast('链接不完整或参数错误')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const redirectUrl = getLoginUrl(pushRecordId)
|
|
|
|
|
+ const params = getMiniappLinkParams(redirectUrl)
|
|
|
|
|
+ const res = await miniappUrlLinkApi(params)
|
|
|
|
|
+
|
|
|
|
|
+ if (res.code !== 0 || !res.data) {
|
|
|
|
|
+ showToast(res.msg || '跳转链接生成失败')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.location.href = res.data
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('[redirectToMiniapp] failed:', error)
|
|
|
|
|
+ showToast('页面跳转失败,请稍后重试')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const statusText = computed(() => {
|
|
|
|
|
+ if (progress.value < 30) return '正在初始化页面'
|
|
|
|
|
+ if (progress.value < 70) return '正在校验链接有效性'
|
|
|
|
|
+ if (progress.value < 100) return '正在准备跳转'
|
|
|
|
|
+ return '正在跳转...'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const stepLabel = computed(() => {
|
|
|
|
|
+ if (progress.value < 30) return '初始化中'
|
|
|
|
|
+ if (progress.value < 70) return '校验中'
|
|
|
|
|
+ if (progress.value < 100) return '准备跳转'
|
|
|
|
|
+ return '跳转中'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+let timer: number | null = null
|
|
|
|
|
+
|
|
|
|
|
+const clearTimer = (): void => {
|
|
|
|
|
+ if (timer !== null) {
|
|
|
|
|
+ window.clearInterval(timer)
|
|
|
|
|
+ timer = null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const startProgress = (): void => {
|
|
|
|
|
+ timer = window.setInterval(() => {
|
|
|
|
|
+ if (progress.value >= 95) {
|
|
|
|
|
+ clearTimer()
|
|
|
|
|
+ progress.value = 100
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (progress.value < 30) {
|
|
|
|
|
+ progress.value += 8
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (progress.value < 70) {
|
|
|
|
|
+ progress.value += 5
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ progress.value += 2
|
|
|
|
|
+ }, 300)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ startProgress()
|
|
|
|
|
+ redirectToMiniapp()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onBeforeUnmount(() => {
|
|
|
|
|
+ clearTimer()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.redirect-page {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 24px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ background:
|
|
|
|
|
+ radial-gradient(circle at top left, rgba(59, 130, 246, 0.22), transparent 38%),
|
|
|
|
|
+ radial-gradient(circle at bottom right, rgba(34, 197, 94, 0.18), transparent 34%),
|
|
|
|
|
+ linear-gradient(180deg, #0b1220 0%, #111827 52%, #0f172a 100%);
|
|
|
|
|
+
|
|
|
|
|
+ .bg {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ filter: blur(48px);
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .bg-1 {
|
|
|
|
|
+ top: -80px;
|
|
|
|
|
+ left: -60px;
|
|
|
|
|
+ width: 220px;
|
|
|
|
|
+ height: 220px;
|
|
|
|
|
+ background: rgba(59, 130, 246, 0.25);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .bg-2 {
|
|
|
|
|
+ right: -70px;
|
|
|
|
|
+ bottom: -80px;
|
|
|
|
|
+ width: 240px;
|
|
|
|
|
+ height: 240px;
|
|
|
|
|
+ background: rgba(34, 197, 94, 0.18);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ max-width: 420px;
|
|
|
|
|
+ padding: 28px 22px 22px;
|
|
|
|
|
+ border-radius: 24px;
|
|
|
|
|
+ background: rgba(15, 23, 42, 0.72);
|
|
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.08);
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ 0 20px 50px rgba(0, 0, 0, 0.35),
|
|
|
|
|
+ inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
|
|
|
|
+ backdrop-filter: blur(18px);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .brand {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 14px;
|
|
|
|
|
+ margin-bottom: 24px;
|
|
|
|
|
+
|
|
|
|
|
+ .icon {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ width: 52px;
|
|
|
|
|
+ height: 52px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ background: linear-gradient(135deg, rgba(59, 130, 246, 0.95), rgba(34, 197, 94, 0.9));
|
|
|
|
|
+ box-shadow: 0 10px 24px rgba(59, 130, 246, 0.28);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .text {
|
|
|
|
|
+ h1 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ line-height: 1.25;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ p {
|
|
|
|
|
+ margin: 6px 0 0;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.68);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .status-panel {
|
|
|
|
|
+ padding: 18px 16px;
|
|
|
|
|
+ border-radius: 18px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.04);
|
|
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.06);
|
|
|
|
|
+
|
|
|
|
|
+ .top {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .status-text {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .meta {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.58);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tips {
|
|
|
|
|
+ margin-top: 18px;
|
|
|
|
|
+ padding: 18px 16px;
|
|
|
|
|
+ border-radius: 18px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.03);
|
|
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.05);
|
|
|
|
|
+
|
|
|
|
|
+ .title {
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.86);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.72);
|
|
|
|
|
+
|
|
|
|
|
+ .item-icon {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #7dd3fc;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .footer {
|
|
|
|
|
+ margin-top: 18px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+
|
|
|
|
|
+ p {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: rgba(255, 255, 255, 0.45);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (max-width: 480px) {
|
|
|
|
|
+ .redirect-page {
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+
|
|
|
|
|
+ .card {
|
|
|
|
|
+ padding: 22px 16px 18px;
|
|
|
|
|
+ border-radius: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .brand {
|
|
|
|
|
+ .icon {
|
|
|
|
|
+ width: 46px;
|
|
|
|
|
+ height: 46px;
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .text {
|
|
|
|
|
+ h1 {
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|