141 lines
3.4 KiB
Vue
141 lines
3.4 KiB
Vue
|
|
<template>
|
||
|
|
<div class="profile-view">
|
||
|
|
<div class="page-header">
|
||
|
|
<h1><UserOutlined /> 个人信息</h1>
|
||
|
|
<p>查看和修改您的账户信息</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<a-spin :spinning="loading">
|
||
|
|
<div class="profile-content" v-if="profile">
|
||
|
|
<div class="profile-card">
|
||
|
|
<div class="avatar-section">
|
||
|
|
<a-avatar :size="96" class="profile-avatar">
|
||
|
|
<img v-if="avatarUrl" :src="avatarUrl" alt="头像" />
|
||
|
|
<UserOutlined v-else />
|
||
|
|
</a-avatar>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="info-section">
|
||
|
|
<a-descriptions :column="1" bordered size="small">
|
||
|
|
<a-descriptions-item label="姓名">
|
||
|
|
{{ profile.name || '-' }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
<a-descriptions-item label="账号">
|
||
|
|
{{ profile.username || '-' }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
<a-descriptions-item label="角色">
|
||
|
|
{{ roleLabel }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
<a-descriptions-item label="手机号">
|
||
|
|
{{ profile.phone || '-' }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
<a-descriptions-item label="邮箱">
|
||
|
|
{{ profile.email || '-' }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
<a-descriptions-item label="所属机构" v-if="profile.tenantId">
|
||
|
|
{{ profile.tenantName || `租户ID: ${profile.tenantId}` }}
|
||
|
|
</a-descriptions-item>
|
||
|
|
</a-descriptions>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<a-empty v-else-if="!loading" description="加载失败,请刷新重试" />
|
||
|
|
</a-spin>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup lang="ts">
|
||
|
|
import { ref, computed, onMounted } from 'vue';
|
||
|
|
import { UserOutlined } from '@ant-design/icons-vue';
|
||
|
|
import { getProfile, type UserProfile } from '@/api/auth';
|
||
|
|
|
||
|
|
const loading = ref(false);
|
||
|
|
const profile = ref<UserProfile | null>(null);
|
||
|
|
|
||
|
|
const avatarUrl = computed(() => {
|
||
|
|
const p = profile.value;
|
||
|
|
if (!p) return '';
|
||
|
|
return (p.avatarUrl || p.avatar || '') as string;
|
||
|
|
});
|
||
|
|
|
||
|
|
const roleLabel = computed(() => {
|
||
|
|
const role = profile.value?.role;
|
||
|
|
const map: Record<string, string> = {
|
||
|
|
admin: '超管',
|
||
|
|
school: '学校管理员',
|
||
|
|
teacher: '教师',
|
||
|
|
parent: '家长',
|
||
|
|
};
|
||
|
|
return role ? map[role] || role : '-';
|
||
|
|
});
|
||
|
|
|
||
|
|
const loadProfile = async () => {
|
||
|
|
loading.value = true;
|
||
|
|
try {
|
||
|
|
const data = await getProfile();
|
||
|
|
profile.value = data as UserProfile;
|
||
|
|
} catch (error: any) {
|
||
|
|
console.error('获取个人信息失败', error);
|
||
|
|
} finally {
|
||
|
|
loading.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
loadProfile();
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.profile-view {
|
||
|
|
padding: 24px;
|
||
|
|
min-height: 100vh;
|
||
|
|
background: linear-gradient(180deg, #FFF8F0 0%, #FFFFFF 100%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-header {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-header h1 {
|
||
|
|
font-size: 24px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #333;
|
||
|
|
margin: 0 0 8px 0;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-header p {
|
||
|
|
font-size: 14px;
|
||
|
|
color: #666;
|
||
|
|
margin: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.profile-card {
|
||
|
|
background: white;
|
||
|
|
border-radius: 16px;
|
||
|
|
padding: 32px;
|
||
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||
|
|
max-width: 560px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.avatar-section {
|
||
|
|
text-align: center;
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.profile-avatar {
|
||
|
|
background: linear-gradient(135deg, #FF8C42 0%, #FFB347 100%);
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-section :deep(.ant-descriptions-item-label) {
|
||
|
|
width: 100px;
|
||
|
|
font-weight: 500;
|
||
|
|
color: #666;
|
||
|
|
}
|
||
|
|
</style>
|