203 lines
6.2 KiB
Vue
203 lines
6.2 KiB
Vue
<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 '@/stores/auth'
|
||
|
||
const authStore = useAuthStore()
|
||
const message = useMessage()
|
||
|
||
const usernameDraft = 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 < 2) {
|
||
error.value = '昵称至少需要 2 个字符。'
|
||
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
|
||
}
|
||
|
||
passwordSaving.value = true
|
||
try {
|
||
await authStore.updatePassword(newPassword.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 shadow-sm" :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="32"
|
||
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 shadow-sm" :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="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>
|