You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.1 KiB
137 lines
4.1 KiB
<script setup lang="ts">
|
|
import { request, unwrapApiBody, type ApiResponse } from '../../../utils/http/factory'
|
|
|
|
definePageMeta({ title: '资料' })
|
|
|
|
type ProfileGet = {
|
|
profile: {
|
|
nickname: string | null
|
|
avatar: string | null
|
|
avatarVisibility: string
|
|
bioMarkdown: string | null
|
|
bioVisibility: string
|
|
socialLinks: { label: string; url: string; visibility: string }[]
|
|
publicSlug: string | null
|
|
}
|
|
}
|
|
|
|
const state = reactive({
|
|
nickname: '',
|
|
avatar: '',
|
|
avatarVisibility: 'private',
|
|
bioMarkdown: '',
|
|
bioVisibility: 'private',
|
|
publicSlug: '',
|
|
linksJson: '[]',
|
|
})
|
|
|
|
const loading = ref(true)
|
|
const saving = ref(false)
|
|
const message = ref('')
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
try {
|
|
const res = await request<ApiResponse<ProfileGet>>('/api/me/profile')
|
|
const p = unwrapApiBody(res).profile
|
|
state.nickname = p.nickname ?? ''
|
|
state.avatar = p.avatar ?? ''
|
|
state.avatarVisibility = p.avatarVisibility
|
|
state.bioMarkdown = p.bioMarkdown ?? ''
|
|
state.bioVisibility = p.bioVisibility
|
|
state.publicSlug = p.publicSlug ?? ''
|
|
state.linksJson = JSON.stringify(p.socialLinks ?? [], null, 2)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(load)
|
|
|
|
async function save() {
|
|
saving.value = true
|
|
message.value = ''
|
|
try {
|
|
let links: { label: string; url: string; visibility: string }[] = []
|
|
try {
|
|
links = JSON.parse(state.linksJson) as { label: string; url: string; visibility: string }[]
|
|
} catch {
|
|
message.value = '社交链接 JSON 无效'
|
|
return
|
|
}
|
|
await request('/api/me/profile', {
|
|
method: 'PUT',
|
|
body: {
|
|
nickname: state.nickname || null,
|
|
avatar: state.avatar || null,
|
|
avatarVisibility: state.avatarVisibility,
|
|
bioMarkdown: state.bioMarkdown || null,
|
|
bioVisibility: state.bioVisibility,
|
|
publicSlug: state.publicSlug || null,
|
|
socialLinks: links,
|
|
},
|
|
})
|
|
message.value = '已保存'
|
|
await load()
|
|
} catch (e: unknown) {
|
|
message.value =
|
|
typeof e === 'object' && e !== null && 'statusMessage' in e
|
|
? String((e as { statusMessage: string }).statusMessage)
|
|
: '保存失败'
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UContainer class="py-8 max-w-2xl space-y-6">
|
|
<h1 class="text-2xl font-semibold">
|
|
个人资料
|
|
</h1>
|
|
<div v-if="loading" class="text-muted">
|
|
加载中…
|
|
</div>
|
|
<UForm v-else :state="state" class="space-y-4" @submit.prevent="save">
|
|
<UFormField label="公开主页 slug(/@slug)" name="publicSlug">
|
|
<UInput v-model="state.publicSlug" placeholder="例如 my-id" />
|
|
</UFormField>
|
|
<UFormField label="昵称" name="nickname">
|
|
<UInput v-model="state.nickname" />
|
|
</UFormField>
|
|
<UFormField label="头像 URL" name="avatar">
|
|
<UInput v-model="state.avatar" />
|
|
</UFormField>
|
|
<UFormField label="头像可见性" name="avatarVisibility">
|
|
<USelect
|
|
v-model="state.avatarVisibility"
|
|
:items="[
|
|
{ label: '私密', value: 'private' },
|
|
{ label: '公开', value: 'public' },
|
|
{ label: '仅链接', value: 'unlisted' },
|
|
]"
|
|
/>
|
|
</UFormField>
|
|
<UFormField label="生平 / 简介(Markdown)" name="bioMarkdown">
|
|
<UTextarea v-model="state.bioMarkdown" autoresize :rows="6" class="w-full" />
|
|
</UFormField>
|
|
<UFormField label="简介可见性" name="bioVisibility">
|
|
<USelect
|
|
v-model="state.bioVisibility"
|
|
:items="[
|
|
{ label: '私密', value: 'private' },
|
|
{ label: '公开', value: 'public' },
|
|
{ label: '仅链接', value: 'unlisted' },
|
|
]"
|
|
/>
|
|
</UFormField>
|
|
<UFormField label="社交链接(JSON 数组)" name="linksJson">
|
|
<UTextarea v-model="state.linksJson" :rows="8" class="w-full font-mono text-sm" />
|
|
</UFormField>
|
|
<UButton type="submit" :loading="saving">
|
|
保存
|
|
</UButton>
|
|
</UForm>
|
|
<UAlert v-if="message" :title="message" />
|
|
</UContainer>
|
|
</template>
|
|
|