feat: add account settings and router dependency
This commit is contained in:
@@ -9,6 +9,7 @@ const router=createRouter({history:createWebHistory(),routes:[
|
||||
{path:'/login',component:()=>import('./views/LoginView.vue'),meta:{public:true}},
|
||||
{path:'/register',component:()=>import('./views/RegisterView.vue'),meta:{public:true}},
|
||||
{path:'/profile',component:()=>import('./views/ProfileView.vue')},
|
||||
{path:'/settings',component:()=>import('./views/AccountSettingsView.vue')},
|
||||
{path:'/admin',component:()=>import('./views/AdminView.vue'),meta:{role:'admin'}},
|
||||
]})
|
||||
router.beforeEach(async to=>{if(to.meta.public)return true;try{role=(await getCurrentUser()).role}catch{return {path:'/login',query:{redirect:to.fullPath}}}if(to.meta.role&&role!==to.meta.role)return {path:'/profile',query:{forbidden:'1'}};return true})
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { changePassword, getCurrentUser, updateMyProfile } from '../services/auth'
|
||||
import type { SessionUser } from '../types/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const user = ref<SessionUser>()
|
||||
const profile = ref({ nickname: '', email: '' })
|
||||
const password = ref({ current_password: '', new_password: '', confirm_password: '' })
|
||||
const loading = ref(true)
|
||||
const savingProfile = ref(false)
|
||||
const savingPassword = ref(false)
|
||||
const error = ref('')
|
||||
const success = ref('')
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
user.value = await getCurrentUser()
|
||||
profile.value = { nickname: user.value.nickname ?? '', email: user.value.email ?? '' }
|
||||
} catch (cause) {
|
||||
error.value = cause instanceof Error ? cause.message : '无法读取账户资料'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
async function saveProfile() {
|
||||
error.value = ''
|
||||
success.value = ''
|
||||
if (profile.value.email && !/^\S+@\S+\.\S+$/.test(profile.value.email)) {
|
||||
error.value = '请输入有效邮箱地址。'
|
||||
return
|
||||
}
|
||||
savingProfile.value = true
|
||||
try {
|
||||
const updated = await updateMyProfile({ nickname: profile.value.nickname || null, email: profile.value.email || null })
|
||||
if (user.value) user.value = { ...user.value, ...updated }
|
||||
success.value = '基本资料已保存。'
|
||||
} catch (cause) {
|
||||
error.value = cause instanceof Error ? cause.message : '保存资料失败'
|
||||
} finally {
|
||||
savingProfile.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function savePassword() {
|
||||
error.value = ''
|
||||
success.value = ''
|
||||
if (password.value.new_password.length < 8) {
|
||||
error.value = '新密码至少需要 8 个字符。'
|
||||
return
|
||||
}
|
||||
if (password.value.new_password !== password.value.confirm_password) {
|
||||
error.value = '两次输入的新密码不一致。'
|
||||
return
|
||||
}
|
||||
savingPassword.value = true
|
||||
try {
|
||||
await changePassword({ current_password: password.value.current_password, new_password: password.value.new_password })
|
||||
password.value = { current_password: '', new_password: '', confirm_password: '' }
|
||||
router.replace('/login?changed=1')
|
||||
} catch (cause) {
|
||||
error.value = cause instanceof Error ? cause.message : '修改密码失败'
|
||||
} finally {
|
||||
savingPassword.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<RouterLink to="/profile">返回个人中心</RouterLink>
|
||||
<h1>账户设置</h1>
|
||||
<p v-if="loading">正在读取账户资料…</p>
|
||||
<template v-else>
|
||||
<p v-if="error" role="alert">{{ error }}</p>
|
||||
<p v-if="success" role="status">{{ success }}</p>
|
||||
<form @submit.prevent="saveProfile">
|
||||
<h2>基本资料</h2>
|
||||
<label>用户名<input :value="user?.username" disabled /></label>
|
||||
<label>昵称<input v-model.trim="profile.nickname" maxlength="80" autocomplete="nickname" /></label>
|
||||
<label>邮箱<input v-model.trim="profile.email" type="email" maxlength="320" autocomplete="email" /></label>
|
||||
<button :disabled="savingProfile">{{ savingProfile ? '保存中…' : '保存资料' }}</button>
|
||||
</form>
|
||||
<form @submit.prevent="savePassword">
|
||||
<h2>修改密码</h2>
|
||||
<label>当前密码<input v-model="password.current_password" type="password" required autocomplete="current-password" /></label>
|
||||
<label>新密码<input v-model="password.new_password" type="password" required minlength="8" autocomplete="new-password" /></label>
|
||||
<label>确认新密码<input v-model="password.confirm_password" type="password" required minlength="8" autocomplete="new-password" /></label>
|
||||
<p>修改密码后会退出当前会话,请使用新密码重新登录。</p>
|
||||
<button :disabled="savingPassword">{{ savingPassword ? '提交中…' : '修改密码' }}</button>
|
||||
</form>
|
||||
</template>
|
||||
</main>
|
||||
</template>
|
||||
@@ -1 +1 @@
|
||||
<script setup lang="ts">import { onMounted,ref } from 'vue';import { getCurrentUser,logout } from '../services/auth';import { useRouter } from 'vue-router';const user=ref<any>(),error=ref(''),router=useRouter();onMounted(async()=>{try{user.value=await getCurrentUser()}catch(e){error.value=e instanceof Error?e.message:'无法读取资料'}});async function signOut(){await logout();router.replace('/login')}</script><template><main><h1>个人中心</h1><p v-if="error">{{error}}</p><dl v-else-if="user"><dt>用户名</dt><dd>{{user.username}}</dd><dt>昵称</dt><dd>{{user.nickname||'未设置'}}</dd><dt>邮箱</dt><dd>{{user.email||'未设置'}}</dd><dt>角色</dt><dd>{{user.role}}</dd></dl><button @click="signOut">退出登录</button></main></template>
|
||||
<script setup lang="ts">import { onMounted,ref } from 'vue';import { getCurrentUser,logout } from '../services/auth';import { useRouter } from 'vue-router';import type { SessionUser } from '../types/auth';const user=ref<SessionUser>(),error=ref(''),router=useRouter();onMounted(async()=>{try{user.value=await getCurrentUser()}catch(e){error.value=e instanceof Error?e.message:'无法读取资料'}});async function signOut(){try{await logout()}finally{router.replace('/login')}}</script><template><main><h1>个人中心</h1><p v-if="error">{{error}}</p><dl v-else-if="user"><dt>用户名</dt><dd>{{user.username}}</dd><dt>昵称</dt><dd>{{user.nickname||'未设置'}}</dd><dt>邮箱</dt><dd>{{user.email||'未设置'}}</dd><dt>角色</dt><dd>{{user.role}}</dd><dt>注册时间</dt><dd>{{new Date(user.created_at).toLocaleString()}}</dd></dl><div><RouterLink to="/settings">账户设置</RouterLink><button @click="signOut">退出登录</button></div></main></template>
|
||||
|
||||
Reference in New Issue
Block a user