主要更新uniapp相关功能

This commit is contained in:
hero920103 committed 2026-10-07 00:59:05 +08:00
1 parent aa92ca40a2
commit 5b4dbb2b46
65 files changed
+8532 -1921

No files matched your search

+110 -60
View File
@@ -30,7 +30,7 @@
@tap="chooseTenant(item)"
>
<view class="tenant-avatar">
<FaIcon name="building" color="#3973ff" :size="18" />
<FaIcon name="building" color="#2563eb" :size="18" />
</view>
<view class="tenant-info">
<text class="tenant-name">{{ item.tenant_name || `企业${item.tid}` }}</text>
@@ -45,17 +45,40 @@
</view>
</block>
<!-- ============ 账号密码登录 ============ -->
<!-- ============ 登录(小程序端微信认证优先,其次账号密码;H5/APK 仅账号密码) ============ -->
<block v-else>
<view class="panel-head">
<text class="panel-title">欢迎登录</text>
<text class="panel-sub">请输入您的账号与密码以继续</text>
<text class="panel-sub">{{ showMiniLogin ? '使用微信一键登录,或使用账号密码继续' : '请输入您的账号与密码以继续' }}</text>
</view>
<view v-if="errorMsg" class="error-box">
<text class="error-text">{{ errorMsg }}</text>
</view>
<!-- 微信小程序:微信认证登录为主入口 -->
<!-- #ifdef MP-WEIXIN -->
<block v-if="showMiniLogin">
<button
class="btn-wechat"
open-type="getPhoneNumber"
:class="{ disabled: submitting }"
@getphonenumber="onGetPhoneNumber"
>
<FaIcon name="weixin" type="brands" color="#ffffff" :size="18" />
<text>微信手机号一键登录</text>
</button>
<view class="tip-row">
<text class="tip-text">首次使用请先用账号密码登录一次,自动完成微信绑定后即可一键登录</text>
</view>
<view class="divider">
<view class="divider-line" />
<text class="divider-text">使用账号密码登录</text>
<view class="divider-line" />
</view>
</block>
<!-- #endif -->
<view class="field">
<text class="field-label">账号</text>
<view class="input-wrap">
@@ -110,31 +133,6 @@
<text class="agree-tip-text">请先阅读并勾选同意《用户协议》和《隐私政策》</text>
</view>
<!-- 微信小程序一键登录 -->
<block v-if="showMiniLogin">
<view class="divider">
<view class="divider-line" />
<text class="divider-text">其他登录方式</text>
<view class="divider-line" />
</view>
<!-- #ifdef MP-WEIXIN -->
<button
class="btn-wechat"
open-type="getPhoneNumber"
:class="{ disabled: submitting }"
@getphonenumber="onGetPhoneNumber"
>
<FaIcon name="weixin" type="brands" color="#ffffff" :size="18" />
<text>微信手机号一键登录</text>
</button>
<!-- #endif -->
<view class="tip-row">
<text class="tip-text">首次使用将自动绑定当前微信手机号对应的云泽账号</text>
</view>
</block>
<view class="back-row">
<text class="tip-text">还没有账号?</text>
<text class="link" @tap="goRegister">立即注册</text>
@@ -167,7 +165,8 @@ import {
fetchUserInfo,
getAuthVerifyConfig,
getMiniappLoginConfig,
loginByMiniapp
loginByMiniapp,
bindMiniapp
} from '@/api/auth.js'
import {
loginSuccess,
@@ -202,6 +201,18 @@ const verifyCfg = ref({ needGeetest: false, captchaId: '' })
/** 仅微信小程序端且服务端已开通时展示「一键登录」入口 */
const showMiniLogin = ref(false)
/** 运行端检测:h5 / apk(app-plus) / mp-weixin,决定登录方式优先级 */
const platform = ref('')
// #ifdef H5
platform.value = 'h5'
// #endif
// #ifdef APP-PLUS
platform.value = 'apk'
// #endif
// #ifdef MP-WEIXIN
platform.value = 'mp-weixin'
// #endif
onMounted(async () => {
if (isLoggedIn()) {
uni.reLaunch({ url: '/pages/nav/nav' })
@@ -231,25 +242,33 @@ async function loadVerifyConfig() {
}
}
/** 小程序一键登录是否已开通(未开通则隐藏入口) */
/** 小程序一键登录是否已开通(仅微信端拉取,未开通则回退账号密码登录) */
async function loadMiniLoginConfig() {
// #ifdef MP-WEIXIN
if (platform.value !== 'mp-weixin') return
try {
const data = await getMiniappLoginConfig()
showMiniLogin.value = !!data?.enabled
} catch {
showMiniLogin.value = false
}
// #endif
}
/* ==================== 登录主流程 ==================== */
async function handleLogin() {
if (submitting.value) return
console.log('[login] tap handleLogin', { submitting: submitting.value, agreed: agreed.value })
if (submitting.value) {
uni.showToast({ title: '登录处理中,请稍候', icon: 'none' })
return
}
// 未勾选协议:弹窗确认,确认后自动勾选并继续登录
if (!agreed.value) {
const ok = await confirmAgreement()
let ok = false
try {
ok = await confirmAgreement()
} catch {
ok = false
}
if (!ok) {
showAgreementTip.value = true
return
@@ -269,7 +288,8 @@ async function handleLogin() {
errorMsg.value = ''
try {
let captcha = null
if (verifyCfg.value.needGeetest) {
// 小程序端无极验 SDK:不弹验证,后端对 yz-miniapp 客户端也跳过校验
if (verifyCfg.value.needGeetest && platform.value !== 'mp-weixin') {
if (!verifyCfg.value.captchaId) {
errorMsg.value = '人机验证未配置,请联系管理员'
return
@@ -376,6 +396,9 @@ async function finishLogin(tokens, tenantList, tid, fallbackAccount = '') {
}
})
// 微信端:登录成功后静默绑定 openid,认证中/无手机号能力时一键登录靠它认人
await silentBindMiniapp()
uni.hideLoading()
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
@@ -399,20 +422,35 @@ function uniLoginCode() {
})
}
/**
* 微信端登录成功后静默把当前 openid 绑到该账号。
* 小程序认证中 / 未开通手机号能力时,一键登录拿不到手机号,
* 后端按这条绑定认人;绑定失败不阻塞登录。
*/
async function silentBindMiniapp() {
if (platform.value !== 'mp-weixin') return
try {
const code = await uniLoginCode()
await bindMiniapp({ code })
} catch {
/* 静默:不影响已完成的登录 */
}
}
// #ifdef MP-WEIXIN
/** getPhoneNumber 授权回调:动态 code + uni.login code 一起换登录态 */
async function onGetPhoneNumber(e) {
if (submitting.value) return
const detail = (e && e.detail) || {}
if (detail.errMsg && detail.errMsg.indexOf('fail') !== -1) {
if (detail.errMsg.indexOf('deny') !== -1) {
uni.showToast({ title: '你已取消手机号授权', icon: 'none' })
}
const errMsg = detail.errMsg || ''
if (errMsg.indexOf('deny') !== -1) {
uni.showToast({ title: '你已取消手机号授权', icon: 'none' })
return
}
if (!detail.code) {
uni.showToast({ title: '未获取到手机号凭证,请重试', icon: 'none' })
return
// 认证中 / 未开通手机号能力:拿不到动态 code 时降级为「按已绑定 openid 登录」
const phoneCode = detail.code && errMsg.indexOf('fail') === -1 ? detail.code : ''
if (!phoneCode) {
uni.showToast({ title: '手机号能力暂不可用,按已绑定账号登录', icon: 'none' })
}
if (!agreed.value) {
const ok = await confirmAgreement()
@@ -426,7 +464,7 @@ async function onGetPhoneNumber(e) {
errorMsg.value = ''
try {
const code = await uniLoginCode()
const data = await loginByMiniapp({ code, phoneCode: detail.code })
const data = await loginByMiniapp({ code, phoneCode })
await handleAuthResult(data)
} catch (err) {
handleMiniError(err)
@@ -441,12 +479,10 @@ function handleMiniError(err) {
if (reason === 'identity_not_found') {
uni.showModal({
title: '尚未绑定云泽账号',
content: '当前微信手机号未注册云泽账号,请先使用账号密码登录或注册后再使用一键登录。',
content: '当前微信未绑定云泽账号,且手机号能力暂不可用。请先在下方用账号密码登录一次(会自动完成微信绑定),之后即可一键登录。',
showCancel: false,
confirmText: '去注册',
success: (res) => {
if (res.confirm) goRegister()
}
// 微信小程序 showModal 的 confirmText 上限为 4 个汉字,超限会导致 API 失败、弹窗不显示
confirmText: '密码登录'
})
return
}
@@ -494,28 +530,42 @@ function showAgreement() {
/**
* 未勾选协议时的二次确认弹窗。
* 点「同意并登录」→ 自动勾选协议并返回 true,调用方继续登录;
* 点「同意登录」→ 自动勾选协议并返回 true,调用方继续登录;
* 点「再看看」→ 返回 false,停在当前页并高亮提示。
* 兜底:部分端 showModal 既不回调 success 也不回调 fail,会导致 await 永久挂起、
* 点击登录「没反应」,这里用超时保证一定会返回,绝不阻塞。
* @returns {Promise<boolean>}
*/
function confirmAgreement() {
return new Promise((resolve) => {
let settled = false
let timer = null
const finish = (val) => {
if (settled) return
settled = true
if (timer) clearTimeout(timer)
resolve(val)
}
timer = setTimeout(() => {
console.warn('[login] confirmAgreement 超时未回调,按未同意处理')
finish(false)
}, 8000)
uni.showModal({
title: '请确认用户协议',
content: '登录即表示您已阅读并同意《用户协议》与《隐私政策》,是否同意并继续登录?',
confirmText: '同意并登录',
confirmText: '同意登录',
cancelText: '再看看',
confirmColor: '#3973ff',
confirmColor: '#2563eb',
success: (res) => {
if (res.confirm) {
agreed.value = true
showAgreementTip.value = false
resolve(true)
finish(true)
} else {
resolve(false)
finish(false)
}
},
fail: () => resolve(false)
fail: () => finish(false)
})
})
}
@@ -530,7 +580,7 @@ function goForget() {
.login-page {
min-height: 100vh;
background: linear-gradient(160deg, #1b3b8f 0%, #2f6fe4 52%, #52a8ff 100%);
background: linear-gradient(160deg, #1e3a8a 0%, #2563eb 100%);
padding: 0 32rpx calc(env(safe-area-inset-bottom, 0px) + 40rpx);
box-sizing: border-box;
}
@@ -703,7 +753,7 @@ function goForget() {
.btn {
height: 92rpx;
border-radius: $radius-md;
background: linear-gradient(135deg, #3973ff 0%, #52a8ff 100%);
background: linear-gradient(135deg, #2563eb 0%, #60a5fa 100%);
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
@@ -781,7 +831,7 @@ function goForget() {
.link {
font-size: 26rpx;
color: #3973ff;
color: #2563eb;
}
/* ---------- 企业选择 ---------- */
@@ -801,7 +851,7 @@ function goForget() {
background: #fbfcff;
&:active {
border-color: #3973ff;
border-color: #2563eb;
background: #f5f9ff;
}
@@ -878,8 +928,8 @@ function goForget() {
height: 16rpx;
margin-left: -5rpx;
transform: translateY(-50%) rotate(45deg);
border-right: 3rpx solid #1b3b8f;
border-bottom: 3rpx solid #1b3b8f;
border-right: 3rpx solid #1e3a8a;
border-bottom: 3rpx solid #1e3a8a;
}
}