Files
tuanwei-uniapp/pages/login/index.vue
T
weicw1996 7175fb3070 小程序登录页新增统一身份认证入口
登录方式由两种扩为三种(密码 / 微信授权 / 统一身份认证),
新增 loginByGxmuSso 与 fetchGxmuSsoAvailable 两个接口封装。
2026-09-18 23:31:05 +08:00

363 lines
7.9 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="page">
<view class="hero-card">
<view class="hero-title">进入智慧团委小程序</view>
<view class="hero-desc">支持手机号密码登录微信手机号授权登录也支持学校统一身份认证登录</view>
</view>
<view class="login-card">
<view class="tab-row">
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'password' }"
@tap="loginMode = 'password'"
>
手机号密码登录
</view>
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'wechat' }"
@tap="loginMode = 'wechat'"
>
微信授权登录
</view>
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'gxmu' }"
@tap="loginMode = 'gxmu'"
>
统一身份认证
</view>
</view>
<view v-if="loginMode === 'password'" class="form-section">
<uv-input
v-model.trim="form.username"
class="field-input"
type="text"
placeholder="请输入手机号或账号"
:maxlength="-1"
border="none"
/>
<uv-input
v-model="form.password"
class="field-input"
type="password"
placeholder="请输入密码"
:maxlength="-1"
border="none"
password
/>
<view class="submit-btn" :class="{ 'submit-btn--loading': loading }" @tap="submitPasswordLogin">
{{ loading ? '登录中...' : '立即登录' }}
</view>
</view>
<view v-else-if="loginMode === 'wechat'" class="form-section">
<view class="wechat-intro">授权后将使用微信手机号完成登录</view>
<button
class="submit-btn submit-btn--wechat"
:disabled="loading"
open-type="getPhoneNumber"
@getphonenumber="handleWxPhoneLogin"
>
{{ loading ? '登录中...' : '微信授权登录' }}
</button>
</view>
<view v-else class="form-section">
<view class="wechat-intro">使用学校统一身份认证账号学号和密码登录</view>
<uv-input
v-model.trim="ssoForm.username"
class="field-input"
type="text"
placeholder="请输入学号"
:maxlength="-1"
border="none"
/>
<uv-input
v-model="ssoForm.password"
class="field-input"
type="password"
placeholder="请输入统一身份认证密码"
:maxlength="-1"
border="none"
password
/>
<view class="submit-btn" :class="{ 'submit-btn--loading': loading }" @tap="submitGxmuLogin">
{{ loading ? '登录中...' : '统一身份认证登录' }}
</view>
</view>
</view>
</view>
</template>
<script>
import { loginByGxmuSso, loginByMpWxPhone, loginByPassword } from './service'
import { hasToken, setAuthInfo } from '../../utils/request'
export default {
data() {
return {
loginMode: 'password',
loading: false,
redirect: '',
wxCode: '',
form: {
username: '',
password: ''
},
ssoForm: {
username: '',
password: ''
}
}
},
onLoad(options) {
this.redirect = decodeURIComponent((options && options.redirect) || '')
},
onShow() {
if (hasToken()) {
this.navigateAfterLogin()
return
}
this.refreshWxCode()
},
methods: {
showToast(title) {
uni.showToast({
title,
icon: 'none'
})
},
refreshWxCode() {
uni.login({
success: (res) => {
this.wxCode = (res && res.code) || ''
}
})
},
navigateAfterLogin() {
const url = this.redirect || '/pages/index/index'
uni.reLaunch({
url
})
},
async submitPasswordLogin() {
if (this.loading) {
return
}
if (!this.form.username) {
this.showToast('请输入手机号或账号')
return
}
if (!this.form.password) {
this.showToast('请输入密码')
return
}
this.loading = true
try {
const result = await loginByPassword({
username: this.form.username,
password: this.form.password
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '登录失败')
} finally {
this.loading = false
}
},
async submitGxmuLogin() {
if (this.loading) {
return
}
if (!this.ssoForm.username) {
this.showToast('请输入学号')
return
}
if (!this.ssoForm.password) {
this.showToast('请输入统一身份认证密码')
return
}
this.loading = true
try {
const result = await loginByGxmuSso({
username: this.ssoForm.username,
password: this.ssoForm.password
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '统一身份认证登录失败')
} finally {
this.loading = false
}
},
async handleWxPhoneLogin(event) {
if (this.loading) {
return
}
const detail = (event && event.detail) || {}
if (!detail.code) {
this.showToast(detail.errMsg || '未获取到微信手机号凭证')
return
}
this.loading = true
try {
if (!this.wxCode) {
await new Promise((resolve) => {
uni.login({
success: (res) => {
this.wxCode = (res && res.code) || ''
resolve()
},
fail: () => resolve()
})
})
}
const result = await loginByMpWxPhone({
phoneCode: detail.code,
code: this.wxCode || undefined
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '微信登录失败')
this.refreshWxCode()
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
padding: 28rpx 24rpx 40rpx;
background:
radial-gradient(circle at top left, rgba(20, 150, 242, 0.14), transparent 30%),
linear-gradient(180deg, #f5fbff 0%, #eef7ff 100%);
}
.hero-card,
.login-card {
border-radius: 32rpx;
box-shadow: 0 18rpx 40rpx rgba(20, 118, 194, 0.12);
}
.hero-card {
padding: 34rpx 30rpx;
background: linear-gradient(145deg, #0d6fba 0%, #1496f2 58%, #5bc2ff 100%);
color: #fff;
}
.hero-badge {
display: inline-flex;
padding: 10rpx 18rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.16);
font-size: 22rpx;
}
.hero-title {
margin-top: 22rpx;
font-size: 42rpx;
font-weight: 700;
}
.hero-desc {
margin-top: 14rpx;
font-size: 24rpx;
line-height: 1.7;
color: rgba(255, 255, 255, 0.86);
}
.login-card {
margin-top: 24rpx;
padding: 28rpx 24rpx;
background: rgba(255, 255, 255, 0.94);
}
.tab-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14rpx;
}
.tab-item {
height: 82rpx;
border-radius: 22rpx;
background: #edf5ff;
color: #1496f2;
font-size: 22rpx;
font-weight: 600;
line-height: 82rpx;
text-align: center;
}
.tab-item--active {
background: linear-gradient(135deg, #1496f2, #0d6fba);
color: #fff;
}
.form-section {
margin-top: 22rpx;
}
:deep(.field-input.uv-input) {
width: 100%;
height: 88rpx;
margin-top: 16rpx;
padding: 0 24rpx !important;
border-radius: 24rpx;
background: #f8fbff !important;
border: 1rpx solid rgba(20, 150, 242, 0.12) !important;
}
:deep(.field-input.uv-input .uv-input__content__field-wrapper__field) {
font-size: 24rpx;
color: #1f2329;
}
.wechat-intro {
font-size: 24rpx;
line-height: 1.7;
color: #667085;
}
.submit-btn {
width: 100%;
height: 88rpx;
margin-top: 22rpx;
border: none;
border-radius: 24rpx;
background: linear-gradient(135deg, #1496f2, #0d6fba);
color: #fff;
font-size: 26rpx;
font-weight: 700;
line-height: 88rpx;
text-align: center;
}
.submit-btn--wechat {
background: linear-gradient(135deg, #1dbf73, #12a150);
}
.submit-btn--loading {
opacity: 0.72;
}
</style>