fix: 第二轮残留(前端)

- 移除 config.tenantCode 死配置,注册页加租户输入
- 全局 errorHandler + unhandledrejection 兜底错误提示
- selectTenant 二次登录加 loading
- 路由守卫支持 meta.freeAccess 白名单

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-11 17:22:16 +08:00
parent e1bc973034
commit eff4f8b314
6 changed files with 25 additions and 7 deletions

2
components.d.ts vendored
View File

@ -18,6 +18,7 @@ declare module 'vue' {
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDialog: typeof import('element-plus/es')['ElDialog']
@ -77,6 +78,7 @@ declare global {
const ElButton: typeof import('element-plus/es')['ElButton']
const ElCard: typeof import('element-plus/es')['ElCard']
const ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
const ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
const ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
const ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
const ElDialog: typeof import('element-plus/es')['ElDialog']

View File

@ -18,8 +18,6 @@ export interface LoginConfig {
layout: LoginLayout
/** 背景(图 URL 或 CSS 渐变/颜色)。 */
background?: string
/** 登录提交的租户编码(页面不展示租户输入框,统一用此默认租户)。 */
tenantCode: string
/** 登录字段(JSON Schema,字段名须与后端 DTO 一致)。 */
fields: JsonSchema
/** 是否显示「记住我」。 */
@ -40,7 +38,6 @@ export const defaultLoginConfig: LoginConfig = {
subtitle: '通用系统管理脚手架',
copyright: '云南投云科技有限公司',
layout: 'split',
tenantCode: 'touyun',
fields: {
type: 'object',
properties: {

View File

@ -1,4 +1,5 @@
import { createApp } from 'vue'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/dark/css-vars.css'
import App from './App.vue'
@ -36,4 +37,13 @@ setAuthExpiredHandler(() => {
installI18n(app)
installAssets()
// 全局兜底:未捕获的组件错误 / Promise 拒绝,统一弹出错误提示(避免静默失败)
app.config.errorHandler = (err) => {
ElMessage.error(err instanceof Error ? err.message : String(err))
}
window.addEventListener('unhandledrejection', (e) => {
const reason = e.reason
ElMessage.error(reason instanceof Error ? reason.message : String(reason))
})
app.mount('#app')

View File

@ -208,11 +208,14 @@ const { loading, run: doLogin } = useRequest(
async function selectTenant(tenantCode: string): Promise<void> {
tenantDialogVisible.value = false
loading.value = true
try {
await auth.login(model.username, model.password, tenantCode)
router.push(config.homePath)
} catch (e) {
error.value = (e as Error).message
} finally {
loading.value = false
}
}

View File

@ -14,6 +14,11 @@ meta:
</div>
<el-form ref="formRef" class="auth__form" :model="model" :rules="rules" size="large" @keyup.enter="onSubmit">
<el-form-item prop="tenantCode">
<el-input v-model="model.tenantCode" placeholder="租户编码">
<template #prefix><icon type="iconify" icon="mdi:domain" /></template>
</el-input>
</el-form-item>
<el-form-item prop="username">
<el-input v-model="model.username" placeholder="用户名">
<template #prefix><icon type="iconify" icon="mdi:account" /></template>
@ -71,9 +76,10 @@ applyTheme(app.themeColor, app.isDark)
const formRef = ref<FormInstance>()
const loading = ref(false)
const model = reactive({ username: '', password: '', confirmPassword: '', email: '', phone: '' })
const model = reactive({ tenantCode: '', username: '', password: '', confirmPassword: '', email: '', phone: '' })
const rules: FormRules = {
tenantCode: [{ required: true, message: '请输入租户编码', trigger: 'blur' }],
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
@ -105,7 +111,7 @@ async function onSubmit(): Promise<void> {
loading.value = true
try {
await register({
tenantCode: config.tenantCode,
tenantCode: model.tenantCode,
username: model.username,
password: model.password,
email: model.email,

View File

@ -66,8 +66,8 @@ router.beforeEach(async (to, from, next) => {
next('/login')
return
}
// 路由级权限:自由路径放行,其余须在可访问菜单中
if (FREE_PATHS.has(to.path)) {
// 路由级权限:自由路径或 meta.freeAccess 放行,其余须在可访问菜单中
if (FREE_PATHS.has(to.path) || to.meta.freeAccess === true) {
next()
return
}