Files
f9-web-ai-test/client/src/views/Profile.vue
T
2026-10-08 10:43:59 +08:00

197 lines
5.0 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import api from '../api';
import { toast } from '../utils';
const router = useRouter();
const user = ref(JSON.parse(localStorage.getItem('user') || '{}'));
const editMode = ref(false);
const form = ref({ nickname: '', phone: '', building: '', bio: '' });
const pwdForm = ref({ oldPassword: '', newPassword: '', confirm: '' });
const showPwd = ref(false);
onMounted(async () => {
try {
const data = await api.get('/auth/me');
user.value = data;
localStorage.setItem('user', JSON.stringify(data));
form.value = { nickname: data.nickname, phone: data.phone, building: data.building, bio: data.bio };
} catch (e) {
toast(e.message);
}
});
async function saveProfile() {
try {
const data = await api.put('/auth/me', form.value);
user.value = data;
localStorage.setItem('user', JSON.stringify(data));
editMode.value = false;
toast('保存成功');
} catch (e) {
toast(e.message);
}
}
async function changePassword() {
const { oldPassword, newPassword, confirm } = pwdForm.value;
if (!oldPassword || !newPassword) return toast('请填写完整');
if (newPassword !== confirm) return toast('两次新密码不一致');
try {
await api.put('/auth/password', { oldPassword, newPassword });
toast('密码修改成功');
pwdForm.value = { oldPassword: '', newPassword: '', confirm: '' };
showPwd.value = false;
} catch (e) {
toast(e.message);
}
}
function logout() {
localStorage.removeItem('token');
localStorage.removeItem('user');
router.push('/login');
}
</script>
<template>
<div class="page">
<div class="card profile-card">
<div class="avatar big">{{ user.nickname?.charAt(0) }}</div>
<div class="info">
<div class="name">{{ user.nickname }}</div>
<div class="meta">@{{ user.username }}</div>
<div class="meta" v-if="user.building">📍 {{ user.building }}</div>
<div class="meta" v-if="user.bio">{{ user.bio }}</div>
</div>
<span v-if="user.role === 'admin'" class="badge">管理员</span>
</div>
<div class="card section">
<div class="section-title">
<h3>个人资料</h3>
<button class="btn btn-ghost btn-sm" @click="editMode = !editMode">
{{ editMode ? '取消' : '编辑' }}
</button>
</div>
<template v-if="editMode">
<div class="form">
<label>昵称</label>
<input v-model="form.nickname" />
<label>手机号</label>
<input v-model="form.phone" />
<label>楼栋/房号</label>
<input v-model="form.building" />
<label>个人简介</label>
<textarea v-model="form.bio" rows="3"></textarea>
<button class="btn" @click="saveProfile">保存</button>
</div>
</template>
<template v-else>
<div class="kv">
<div class="row"><span>昵称</span><span>{{ user.nickname }}</span></div>
<div class="row"><span>手机号</span><span>{{ user.phone || '未填写' }}</span></div>
<div class="row"><span>楼栋/房号</span><span>{{ user.building || '未填写' }}</span></div>
<div class="row"><span>个人简介</span><span>{{ user.bio || '未填写' }}</span></div>
</div>
</template>
</div>
<div class="card section">
<div class="section-title">
<h3>账号安全</h3>
<button class="btn btn-ghost btn-sm" @click="showPwd = !showPwd">
{{ showPwd ? '收起' : '修改密码' }}
</button>
</div>
<div v-if="showPwd" class="form">
<input v-model="pwdForm.oldPassword" type="password" placeholder="原密码" />
<input v-model="pwdForm.newPassword" type="password" placeholder="新密码(至少6位)" />
<input v-model="pwdForm.confirm" type="password" placeholder="确认新密码" />
<button class="btn" @click="changePassword">确认修改</button>
</div>
</div>
<button class="btn btn-danger logout-btn" @click="logout">退出登录</button>
</div>
</template>
<style scoped>
.profile-card {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.avatar.big {
width: 64px;
height: 64px;
font-size: 28px;
}
.info {
flex: 1;
}
.name {
font-size: 20px;
font-weight: 700;
}
.meta {
color: var(--text-light);
font-size: 13px;
margin-top: 4px;
}
.section {
margin-bottom: 16px;
}
.section-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.section-title h3 {
font-size: 16px;
}
.form {
display: flex;
flex-direction: column;
gap: 10px;
}
.form label {
font-size: 13px;
color: var(--text-light);
margin-top: 4px;
}
.kv .row {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid var(--border);
font-size: 14px;
}
.kv .row:last-child {
border-bottom: none;
}
.kv .row span:first-child {
color: var(--text-light);
}
.logout-btn {
width: 100%;
margin-top: 8px;
}
</style>