Files
opencloud/src/features/profile/views/ProfileSettingsView.vue
T

218 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { NAlert, NButton, NCard, NForm, NInput, useMessage } from 'naive-ui'
import { useAuthStore } from '@/features/auth/stores/auth'
const authStore = useAuthStore()
const message = useMessage()
const usernameDraft = ref('')
const currentPassword = ref('')
const newPassword = ref('')
const confirmNewPassword = ref('')
const error = ref('')
const success = ref('')
const usernameSaving = ref(false)
const passwordSaving = ref(false)
onMounted(() => {
usernameDraft.value = authStore.profile?.username || ''
})
async function saveUsername() {
const nextUsername = usernameDraft.value.trim()
error.value = ''
success.value = ''
if (!nextUsername) {
error.value = '昵称不能为空。'
return
}
if (nextUsername.length > 16) {
error.value = '昵称不能超过 16 个字符。'
return
}
if (nextUsername === authStore.profile?.username) {
success.value = '昵称没有变化。'
return
}
usernameSaving.value = true
try {
await authStore.updateUsername(nextUsername)
success.value = '昵称已更新。'
message.success('昵称已更新')
} catch (e) {
error.value = e instanceof Error ? e.message : '昵称更新失败'
} finally {
usernameSaving.value = false
}
}
async function savePassword() {
error.value = ''
success.value = ''
if (newPassword.value.length < 8) {
error.value = '新密码至少需要 8 位。'
return
}
if (newPassword.value !== confirmNewPassword.value) {
error.value = '两次输入的新密码不一致。'
return
}
if (!currentPassword.value) {
error.value = '请输入当前密码。'
return
}
passwordSaving.value = true
try {
await authStore.updatePassword(currentPassword.value, newPassword.value)
currentPassword.value = ''
newPassword.value = ''
confirmNewPassword.value = ''
success.value = '密码已更新。其他设备上的会话已退出。'
message.success('密码已更新')
} catch (e) {
error.value = e instanceof Error ? e.message : '密码更新失败'
} finally {
passwordSaving.value = false
}
}
</script>
<template>
<div
class="min-h-[calc(100vh-4rem)] bg-[linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)] py-10"
>
<div class="oc-container oc-container--content">
<RouterLink to="/profile">
<NButton
type="default"
secondary
strong
class="oc-panel-button oc-panel-button--neutral"
>← 返回个人主页</NButton
>
</RouterLink>
<div class="mt-6">
<p class="oc-page-eyebrow">Account Settings</p>
<h1 class="oc-page-title">个人资料设置</h1>
<p class="oc-page-description">
修改公开昵称和登录密码。昵称会显示在画廊、百科和地图中,且不能与其他用户重复。
</p>
</div>
<div class="mt-8 grid gap-6">
<NAlert v-if="error" type="error" :show-icon="false" :bordered="false">
{{ error }}
</NAlert>
<NAlert v-if="success" type="success" :show-icon="false" :bordered="false">
{{ success }}
</NAlert>
<NCard class="oc-panel-card border border-slate-200 bg-white" :bordered="false">
<div class="grid gap-5 lg:grid-cols-[0.8fr_1.2fr] lg:items-start">
<div>
<h2 class="text-2xl font-bold text-slate-900">公开昵称</h2>
<p class="mt-2 text-sm leading-6 text-slate-500">
用于公开展示你的上传记录。保存前会检查是否与其他用户重复。
</p>
</div>
<div class="grid gap-3 sm:grid-cols-[minmax(0,1fr)_auto]">
<NInput
v-model:value="usernameDraft"
placeholder="输入新的昵称"
maxlength="16"
show-count
/>
<NButton
secondary
strong
type="default"
class="oc-panel-button oc-panel-button--teal"
:loading="usernameSaving"
@click="saveUsername"
>
保存昵称
</NButton>
</div>
</div>
</NCard>
<NCard class="oc-panel-card border border-slate-200 bg-white" :bordered="false">
<div class="grid gap-5 lg:grid-cols-[0.8fr_1.2fr] lg:items-start">
<div>
<h2 class="text-2xl font-bold text-slate-900">登录密码</h2>
<p class="mt-2 text-sm leading-6 text-slate-500">
修改后下次登录需要使用新密码。建议使用至少 8 位且不易猜测的密码。
</p>
</div>
<NForm class="grid gap-3" @submit.prevent="savePassword">
<input
:value="authStore.user?.email || ''"
type="text"
name="username"
autocomplete="username"
class="sr-only"
tabindex="-1"
aria-label="账户邮箱"
readonly
/>
<NInput
v-model:value="currentPassword"
type="password"
show-password-on="click"
:input-props="{
autocomplete: 'current-password',
name: 'current-password',
required: true,
}"
placeholder="当前密码"
/>
<NInput
v-model:value="newPassword"
type="password"
show-password-on="click"
:input-props="{
autocomplete: 'new-password',
name: 'new-password',
required: true,
}"
placeholder="新密码"
/>
<NInput
v-model:value="confirmNewPassword"
type="password"
show-password-on="click"
:input-props="{
autocomplete: 'new-password',
name: 'confirm-new-password',
required: true,
}"
placeholder="确认新密码"
/>
<div class="flex justify-end">
<NButton
secondary
strong
type="default"
attr-type="submit"
class="oc-panel-button oc-panel-button--teal"
:loading="passwordSaving"
>
保存密码
</NButton>
</div>
</NForm>
</div>
</NCard>
</div>
</div>
</div>
</template>