Files
used-car-h5/pages/login/index.vue
T
2026-01-16 09:54:29 +08:00

222 lines
5.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="auth-page">
<view class="auth-card">
<!-- 这里如果你有站点 logo,可替换为自己的路径;没有也不影响 -->
<!-- <image class="logo" src="/static/logo.png" mode="heightFix"></image> -->
<view class="spinner"></view>
<view class="text-wrap">
<text class="title">{{ title }}</text>
<text class="desc">{{ desc }}<text class="dots">{{ dots }}</text></text>
</view>
<view class="progress">
<view class="bar"></view>
</view>
</view>
</view>
</template>
<script>
import { request, showToast } from '@/api/apiT.js'
export default {
data() {
return {
fsappid: '',
title: '正在登录',
desc: '加载中',
dots: '',
_dotTimer: null,
// 最短停留时间,避免“闪一下就跳”的割裂感
minStayMs: 600,
_startedAt: 0
}
},
onLoad(options) {
this.fsappid = options.appid || '';
uni.setStorageSync('fsappid', this.fsappid);
// 保存登录成功后的回跳地址(支持 redirect/r)
const redirect = options.redirect || options.r || ''
if (redirect) {
// options 里可能已被 decode,也可能没有,保险起见 try 一下
try {
uni.setStorageSync('post_login_redirect', decodeURIComponent(redirect))
} catch (e) {
uni.setStorageSync('post_login_redirect', redirect)
}
}
this._startedAt = Date.now()
this.startDots()
this.silentAuth()
},
onUnload() {
this.stopDots()
},
methods: {
startDots() {
this.stopDots()
let n = 0
this._dotTimer = setInterval(() => {
n = (n + 1) % 4
this.dots = '.'.repeat(n)
}, 350)
},
stopDots() {
if (this._dotTimer) {
clearInterval(this._dotTimer)
this._dotTimer = null
}
},
async silentAuth() {
try {
// 1) 如果本地已有 token,直接进首页(丝滑)
// const token = uni.getStorageSync('token')
// if (token) {
// this.title = '已登录'
// this.desc = '正在进入页面'
// return this.goRedirectSmooth()
// }
// 2) 获取飞书授权跳转地址(静默授权/免登)
this.title = '正在跳转飞书授权'
this.desc = '准备中'
const res = await request({
url: 'common/auth/feishu/scope',
method: 'get',
data:{app_id:this.fsappid,is_pc:0}
})
const auth_url = res?.data?.data?.auth_url
if (!auth_url) {
throw new Error('未获取到授权地址')
}
// 3) 给一点点过渡时间,让动画先跑起来再跳转
await this.sleep(180)
// 仅 H5 才能用 window.location
// #ifdef H5
window.location.replace(auth_url)
// #endif
// #ifndef H5
showToast && showToast('仅 H5 环境支持飞书静默授权')
// #endif
} catch (err) {
console.error('[feishu silentAuth] error:', err)
uni.showToast({ title: err?.message || '登录失败', icon: 'none' })
}
},
getRedirectTarget() {
return uni.getStorageSync('post_login_redirect') || '/pages/index/index'
},
goRedirectSmooth() {
const elapsed = Date.now() - this._startedAt
const wait = Math.max(0, this.minStayMs - elapsed)
setTimeout(() => {
const target = this.getRedirectTarget()
// 用完即清,避免后续误跳
uni.removeStorageSync('post_login_redirect')
uni.reLaunch({ url: target })
}, wait)
},
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
}
}
</script>
<style scoped lang="scss">
.auth-page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
/* 更“高级”的登录背景 */
background: linear-gradient(180deg, #FF972F 0%, #F4F6F8 38%, #F4F6F8 100%);
}
.auth-card {
width: 100%;
max-width: 640rpx;
background: rgba(255, 255, 255, 0.92);
border-radius: 20rpx;
padding: 44rpx 36rpx 32rpx;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
align-items: center;
backdrop-filter: blur(10px);
}
.logo {
height: 72rpx;
margin-bottom: 26rpx;
}
.spinner {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
border: 8rpx solid rgba(255,151,47, 0.18);
border-top-color: #FF972F;
animation: spin 0.9s linear infinite;
margin-bottom: 24rpx;
}
.text-wrap {
text-align: center;
}
.title {
display: block;
font-size: 34rpx;
color: #1f2937;
font-weight: 600;
margin-bottom: 8rpx;
}
.desc {
display: block;
font-size: 26rpx;
color: #6b7280;
}
.dots {
display: inline-block;
width: 40rpx; /* 固定宽度避免文字抖动 */
text-align: left;
}
.progress {
width: 100%;
height: 10rpx;
border-radius: 999rpx;
background: rgba(255, 151, 47, 0.10);
overflow: hidden;
margin-top: 26rpx;
}
.progress .bar {
height: 100%;
width: 40%;
border-radius: 999rpx;
background: rgba(255, 151, 47, 0.55);
animation: move 1.1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes move {
0% { transform: translateX(-60%); }
50% { transform: translateX(140%); }
100% { transform: translateX(-60%); }
}
</style>