cents/apps/web/src/views/MeView.vue

103 lines
5.3 KiB
Vue

<script setup lang="ts">
import { Bell, Check, ChevronRight, CircleUserRound, Cloud, LogOut, Pencil, ShieldCheck, X } from "@lucide/vue";
import { ref } from "vue";
import { useRouter } from "vue-router";
import QuickEntryHost from "../components/QuickEntryHost.vue";
import { useAuthStore } from "../stores/auth";
import { useLedgerStore } from "../stores/ledgers";
const auth = useAuthStore();
const ledgerStore = useLedgerStore();
const router = useRouter();
const editing = ref(false);
const name = ref("");
const saving = ref(false);
const errorMessage = ref("");
const loggingOut = ref(false);
function startEditing() {
name.value = auth.user?.name ?? "";
errorMessage.value = "";
editing.value = true;
}
async function saveName() {
if (!name.value.trim() || saving.value) return;
saving.value = true;
errorMessage.value = "";
try {
await auth.updateName(name.value.trim());
await ledgerStore.loadCurrentMembers();
editing.value = false;
} catch {
errorMessage.value = "姓名保存失败";
} finally {
saving.value = false;
}
}
async function logout() {
if (loggingOut.value) return;
loggingOut.value = true;
try {
await auth.logout();
await router.replace({ name: "login" });
} finally {
loggingOut.value = false;
}
}
</script>
<template>
<main class="app-shell me-shell">
<header class="me-header"><strong>我的</strong></header>
<div class="me-scroll">
<section class="profile-band">
<span><CircleUserRound :size="32" /></span>
<form v-if="editing" @submit.prevent="saveName">
<input v-model="name" maxlength="40" aria-label="姓名" autocomplete="name" autofocus />
<button type="button" aria-label="取消修改" title="取消" @click="editing = false"><X :size="18" /></button>
<button type="submit" aria-label="保存姓名" title="保存" :disabled="saving || !name.trim()"><Check :size="18" /></button>
<small v-if="errorMessage">{{ errorMessage }}</small>
</form>
<div v-else><strong>{{ auth.user?.name }}</strong><small>成员 ID · {{ auth.user?.id.slice(0, 8) }}</small></div>
<button v-if="!editing" class="edit-name" type="button" aria-label="修改姓名" title="修改姓名" @click="startEditing"><Pencil :size="18" /></button>
</section>
<section class="me-settings">
<button type="button"><Bell :size="19" /><span>通知与提醒</span><ChevronRight :size="18" /></button>
<button type="button"><Cloud :size="19" /><span>同步状态</span><ChevronRight :size="18" /></button>
<button type="button"><ShieldCheck :size="19" /><span>隐私与安全</span><ChevronRight :size="18" /></button>
</section>
<button class="logout-action" type="button" :disabled="loggingOut" @click="logout">
<LogOut :size="19" />{{ loggingOut ? "正在退出" : "退出登录" }}
</button>
</div>
<QuickEntryHost />
</main>
</template>
<style scoped>
.me-shell { background:#f5f8f7; }
.me-header { height:70px; display:flex; align-items:end; border-bottom:1px solid #dce7e4; padding:14px 18px 12px; background:#fff; }
.me-header strong { font-size:20px; }
.me-scroll { height:calc(100% - 70px); overflow-y:auto; padding:14px 16px 104px; }
.profile-band { position:relative; min-height:94px; display:grid; grid-template-columns:58px minmax(0,1fr) 40px; align-items:center; gap:12px; border-top:1px solid #dce7e4; border-bottom:1px solid #dce7e4; padding:12px 14px; background:#fff; }
.profile-band > span { width:54px; height:54px; display:grid; place-items:center; border-radius:50%; background:#e8f7f3; color:#087f72; }
.profile-band > div { min-width:0; display:grid; gap:2px; }
.profile-band strong { overflow:hidden; font-size:16px; text-overflow:ellipsis; white-space:nowrap; }
.profile-band small { color:#7b8884; font-size:11px; }
.edit-name,.profile-band form button { width:38px; height:38px; display:grid; place-items:center; border:0; border-radius:50%; background:transparent; color:#536963; }
.profile-band form { display:grid; grid-template-columns:minmax(0,1fr) 38px 38px; align-items:center; gap:2px; }
.profile-band form input { min-width:0; height:42px; border:0; border-bottom:2px solid #087f72; outline:0; color:#26342f; font-size:16px; }
.profile-band form button:last-of-type { background:#e7f6f2; color:#087f72; }
.profile-band form button:disabled { opacity:.45; }
.profile-band form small { grid-column:1/-1; color:#cf513d; }
.me-settings { margin-top:12px; border-top:1px solid #dce7e4; border-bottom:1px solid #dce7e4; padding:0 14px; background:#fff; }
.me-settings button { width:100%; min-height:58px; display:grid; grid-template-columns:28px 1fr 20px; align-items:center; gap:8px; border:0; border-bottom:1px solid #e5edeb; padding:0; background:#fff; color:#31504a; text-align:left; }
.me-settings button:last-child { border-bottom:0; }
.me-settings span { color:#26342f; font-size:14px; }
.logout-action { width:100%; height:48px; display:flex; align-items:center; justify-content:center; gap:7px; margin-top:18px; border:1px solid #e2c4be; border-radius:8px; background:#fff; color:#bf503e; font-weight:700; }
.logout-action:disabled { opacity:.55; }
@media (max-width:360px) { .me-scroll { padding-right:12px; padding-left:12px; } .profile-band { grid-template-columns:52px minmax(0,1fr) 38px; gap:8px; padding-right:10px; padding-left:10px; } }
</style>