zhangjf 8e4afcd1a5 feat: TenantAwareLoadBalancer 整合 TenantRoutingProperties 配置
问题:TenantRoutingProperties 定义了配置但未被使用

解决方案:
1. TenantAwareLoadBalancer 注入 TenantRoutingProperties
   - 使用配置的 tenantHeader 名称
   - 使用配置的 buildTenantGroup 方法
   - 使用配置的 isSharedService 判断
   - 使用配置的 isFallbackToShared 策略

2. 新增功能
   - 支持 enabled=false 禁用租户路由
   - 共享服务跳过租户过滤
   - 可配置是否回退到共享实例

3. 更新测试适配新构造函数
2026-02-19 21:02:25 +08:00

313 lines
8.8 KiB
Vue

<template>
<div class="page-container">
<el-row :gutter="20">
<!-- 左侧个人信息卡片 -->
<el-col :span="8">
<el-card>
<template #header>
<span>个人信息</span>
</template>
<div class="user-profile">
<div class="avatar-section">
<el-avatar :size="100" :src="userInfo.avatar || defaultAvatar">
<el-icon :size="50"><UserFilled /></el-icon>
</el-avatar>
<el-upload
class="avatar-upload"
action="#"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
>
<el-button size="small" type="primary">更换头像</el-button>
</el-upload>
</div>
<div class="user-info">
<h2>{{ userInfo.realName }}</h2>
<p class="username">@{{ userInfo.username }}</p>
<p v-if="userInfo.deptName">
<el-icon><OfficeBuilding /></el-icon>
{{ userInfo.deptName }}
</p>
</div>
</div>
</el-card>
</el-col>
<!-- 右侧信息编辑 -->
<el-col :span="16">
<el-card>
<template #header>
<span>基本资料</span>
</template>
<el-tabs v-model="activeTab">
<el-tab-pane label="基本资料" name="profile">
<el-form
ref="profileFormRef"
:model="profileForm"
:rules="profileRules"
label-width="100px"
style="max-width: 500px;"
>
<el-form-item label="用户名">
<el-input v-model="userInfo.username" disabled />
</el-form-item>
<el-form-item label="姓名" prop="realName">
<el-input v-model="profileForm.realName" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="profileForm.phone" placeholder="请输入手机号" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="profileForm.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="部门">
<el-input v-model="userInfo.deptName" disabled />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleUpdateProfile" :loading="saving">
保存修改
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="修改密码" name="password">
<el-form
ref="passwordFormRef"
:model="passwordForm"
:rules="passwordRules"
label-width="100px"
style="max-width: 500px;"
>
<el-form-item label="旧密码" prop="oldPassword">
<el-input
v-model="passwordForm.oldPassword"
type="password"
placeholder="请输入旧密码"
show-password
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="passwordForm.newPassword"
type="password"
placeholder="请输入新密码"
show-password
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="passwordForm.confirmPassword"
type="password"
placeholder="请再次输入新密码"
show-password
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleUpdatePassword" :loading="saving">
修改密码
</el-button>
<el-button @click="resetPasswordForm">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, FormInstance, FormRules, UploadProps } from 'element-plus'
import { UserFilled, OfficeBuilding } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
import { updateProfile, updatePassword } from '@/api/user'
const userStore = useUserStore()
const defaultAvatar = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
const activeTab = ref('profile')
const saving = ref(false)
const userInfo = ref({
id: 0,
username: '',
realName: '',
phone: '',
email: '',
deptName: '',
avatar: ''
})
const profileFormRef = ref<FormInstance>()
const profileForm = reactive({
realName: '',
phone: '',
email: ''
})
const profileRules: FormRules = {
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
phone: [{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号',
trigger: 'blur'
}],
email: [{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: 'blur'
}]
}
const passwordFormRef = ref<FormInstance>()
const passwordForm = reactive({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const validateConfirmPassword = (rule: any, value: string, callback: any) => {
if (value !== passwordForm.newPassword) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
}
const passwordRules: FormRules = {
oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度为6-20个字符', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
{ validator: validateConfirmPassword, trigger: 'blur' }
]
}
const loadUserInfo = async () => {
try {
const info = userStore.userInfo
if (info) {
userInfo.value = { ...info }
profileForm.realName = info.realName || ''
profileForm.phone = info.phone || ''
profileForm.email = info.email || ''
}
} catch (error) {
console.error('加载用户信息失败', error)
}
}
const handleUpdateProfile = async () => {
if (!profileFormRef.value) return
await profileFormRef.value.validate(async (valid) => {
if (valid) {
saving.value = true
try {
await updateProfile(profileForm)
ElMessage.success('保存成功')
// 更新本地存储
userStore.userInfo = {
...userStore.userInfo,
...profileForm
}
} catch (error: any) {
ElMessage.error(error.message || '保存失败')
} finally {
saving.value = false
}
}
})
}
const handleUpdatePassword = async () => {
if (!passwordFormRef.value) return
await passwordFormRef.value.validate(async (valid) => {
if (valid) {
saving.value = true
try {
await updatePassword(passwordForm)
ElMessage.success('密码修改成功')
resetPasswordForm()
} catch (error: any) {
ElMessage.error(error.message || '密码修改失败')
} finally {
saving.value = false
}
}
})
}
const resetPasswordForm = () => {
passwordForm.oldPassword = ''
passwordForm.newPassword = ''
passwordForm.confirmPassword = ''
passwordFormRef.value?.resetFields()
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
ElMessage.error('头像只能是 JPG/PNG 格式!')
return false
}
if (rawFile.size / 1024 / 1024 > 2) {
ElMessage.error('头像大小不能超过 2MB!')
return false
}
// TODO: 实现头像上传
ElMessage.info('头像上传功能开发中...')
return false
}
onMounted(() => {
loadUserInfo()
})
</script>
<style scoped>
.page-container {
padding: 20px;
}
.user-profile {
text-align: center;
}
.avatar-section {
margin-bottom: 20px;
}
.avatar-upload {
margin-top: 15px;
}
.user-info h2 {
margin: 0 0 10px 0;
color: #303133;
}
.user-info .username {
color: #909399;
margin-bottom: 10px;
}
.user-info p {
color: #606266;
margin: 5px 0;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
}
</style>