做统一认证登录

This commit is contained in:
2026-09-19 21:44:04 +08:00
parent 1a4471e34d
commit fa281363fb
78 changed files with 8127 additions and 1442 deletions
+97 -5
View File
@@ -137,7 +137,9 @@ import {
login,
loginBySms,
sendLoginCode,
fetchVerifyConfig
fetchVerifyConfig,
loginUAC,
switchTenant
} from '@/api/auth.js'
import {
loginSuccess,
@@ -146,7 +148,11 @@ import {
getTenantName,
getRememberLogin,
saveRememberLogin,
clearRememberLogin
clearRememberLogin,
isSSOEnabled,
setAuthTokens,
setTid,
getToken
} from '@/utils/auth.js'
import { showGeetest4 } from '@/utils/geetest.js'
@@ -191,7 +197,6 @@ onMounted(async () => {
rememberMe.value = true
tenantName.value = remembered.tenantName
username.value = remembered.account
password.value = remembered.password
} else {
tenantName.value = getTenantName()
}
@@ -276,6 +281,84 @@ function navigateAfterLogin() {
}, 400)
}
/* ==================== 统一认证(UAC)登录 ====================
* APP 无浏览器回跳,走密码直连:
* 1) POST /auth/login 拿 access + refresh
* 2) 一人多企业时弹出企业列表让用户选,选完调 /auth/switch-tenant
* ============================================================ */
/** 选择要进入的企业(一人多企业场景) */
function chooseTenant(tenants) {
return new Promise((resolve, reject) => {
const names = (tenants || []).map(t => t.tenant_name || `企业${t.tid}`)
uni.showActionSheet({
itemList: names,
title: '选择要进入的企业',
success: (res) => resolve(tenants[res.tapIndex]),
fail: () => reject(new Error('已取消选择'))
})
})
}
/** 统一认证:账号密码登录 */
async function submitSSOLogin() {
const account = username.value.trim()
const pwd = password.value
if (!account) {
uni.showToast({ title: '请输入账号', icon: 'none' })
return false
}
if (!pwd) {
uni.showToast({ title: '请输入密码', icon: 'none' })
return false
}
const data = await loginUAC({ account, password: pwd })
if (!data || data.code !== 200) {
uni.showToast({ title: (data && data.msg) || '登录失败', icon: 'none' })
return false
}
// 记住账号(不含密码)
if (rememberMe.value) {
saveRememberLogin({ tenantName: tenantName.value.trim(), account })
} else {
clearRememberLogin()
}
let currentTid = 0
if (data.need_choose_tenant) {
// 多企业:先记住令牌,再让用户选择
setAuthTokens(data.tokens || { access_token: data.access_token, refresh_token: data.refresh_token, sid: data.sid })
const picked = await chooseTenant(data.tenants)
const res = await switchTenant({ tid: picked.tid })
if (res && res.code === 200 && res.data && res.data.tokens) {
setAuthTokens(res.data.tokens)
currentTid = picked.tid
} else {
uni.showToast({ title: (res && res.msg) || '进入企业失败', icon: 'none' })
return false
}
} else {
setAuthTokens(data.tokens || {})
if (data.tenants && data.tenants.length > 0) currentTid = data.tenants[0].tid
}
setTid(currentTid)
loginSuccess({
token: getToken(),
user: {
id: data.identity ? data.identity.id : 0,
account,
name: (data.identity && (data.identity.nickname || data.identity.mobile)) || account,
tid: currentTid,
tenant_name: (data.tenants || []).map(t => t.tenant_name).join('、')
}
})
navigateAfterLogin()
return true
}
async function submitAccountLogin(geetestResult = null) {
if (!username.value.trim()) {
uni.showToast({ title: '请输入账号', icon: 'none' })
@@ -311,8 +394,7 @@ async function submitAccountLogin(geetestResult = null) {
if (rememberMe.value) {
saveRememberLogin({
tenantName: tenantName.value.trim(),
account: username.value.trim(),
password: password.value
account: username.value.trim()
})
} else {
clearRememberLogin()
@@ -326,6 +408,16 @@ async function handleLogin() {
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
return
}
// 统一认证模式:账号即手机/邮箱,无需填写租户名
if (isSSOEnabled()) {
submitting.value = true
try {
await submitSSOLogin()
} finally {
submitting.value = false
}
return
}
if (!tenantName.value.trim()) {
uni.showToast({ title: '请输入租户名称', icon: 'none' })
return
-39
View File
@@ -39,45 +39,6 @@
<view v-else class="schedule-list">
<view v-for="group in groupedList" :key="group.key" class="group">
<text class="group-title">{{ group.label }}</text>
<view
v-for="item in group.items"
:key="item.id"
class="schedule-card"
:class="{ done: item.completed }"
>
<view class="schedule-main" @tap="openDetail(item.id)">
<view class="schedule-head">
<view class="status-dot" :class="item.completed ? 'done' : 'pending'" />
<text class="schedule-title">{{ getScheduleSummary(item) }}</text>
</view>
<view class="schedule-meta">
<FaIcon name="clock" color="#909399" :size="12" />
<text>{{ formatDate(item.datetime, true) }}</text>
</view>
<view v-if="item.remindChannels?.length" class="channel-row">
<text
v-for="ch in item.remindChannels"
:key="ch"
class="channel-mini"
>{{ getRemindChannelLabel(ch) }}</text>
<text v-if="item.remindMinutes" class="remind-text">{{ getRemindMinutesLabel(item.remindMinutes) }}</text>
</view>
</view>
<view class="action-row">
<view
class="action-btn"
:class="{ disabled: item.completed }"
@tap="!item.completed && onToggleComplete(item)"
>
<FaIcon name="check" :color="item.completed ? '#c0c4cc' : '#3c9cff'" :size="14" />
<text>{{ item.completed ? '已完成' : '标记完成' }}</text>
</view>
<view v-if="!item.completed" class="action-btn" @tap="openEdit(item.id)">
<FaIcon name="pen-to-square" color="#3c9cff" :size="14" />
<text>编辑</text>
</view>
</view>
</view>
</view>
</view>
<view class="bottom-space" />