summaryrefslogtreecommitdiff
path: root/frontend/src/pages/AccountPage.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/pages/AccountPage.svelte')
-rw-r--r--frontend/src/pages/AccountPage.svelte112
1 files changed, 59 insertions, 53 deletions
diff --git a/frontend/src/pages/AccountPage.svelte b/frontend/src/pages/AccountPage.svelte
index ef6d1a3..9e90e78 100644
--- a/frontend/src/pages/AccountPage.svelte
+++ b/frontend/src/pages/AccountPage.svelte
@@ -6,6 +6,12 @@
import { extractStatusMessage } from '../lib/state';
import { fmtDateTime } from '../lib/format';
import type { SessionInfo } from '../lib/types';
+ import { Button } from '$lib/components/ui/button';
+ import { Input } from '$lib/components/ui/input';
+ import { Label } from '$lib/components/ui/label';
+ import * as Card from '$lib/components/ui/card';
+ import { Badge } from '$lib/components/ui/badge';
+ import * as Table from '$lib/components/ui/table';
let nameInput = $state(session.me?.name ?? '');
let profileBusy = $state(false);
@@ -99,91 +105,91 @@
<Page title="账户与安全" subtitle="资料、密码与个人会话管理">
<div class="stack">
- <section class="card stack">
- <h2>个人信息</h2>
+ <Card.Root size="sm" >
+ <Card.Header><Card.Title>个人信息</Card.Title></Card.Header>
<form onsubmit={saveProfile} class="row">
- <label style="flex:1 1 200px">
- 姓名
- <input bind:value={nameInput} autocomplete="name" />
- </label>
+ <div style="flex:1 1 200px">
+ <Label for="acc-name">姓名</Label>
+ <Input id="acc-name" bind:value={nameInput} autocomplete="name" />
+ </div>
<div>
邮箱
- <div class="fixed">{session.me?.email ?? '—'}</div>
+ <div class="text-muted-foreground">{session.me?.email ?? "—"}</div>
</div>
<div>
角色
<div>{session.me?.role === 'admin' ? '管理员' : '成员'}{session.me?.active === false ? ' · 已停用' : ''}</div>
</div>
- <button class="btn primary" disabled={profileBusy || nameInput.trim().length === 0}>
+ <Button type="submit" disabled={profileBusy || nameInput.trim().length === 0}>
{profileBusy ? '保存中…' : '保存资料'}
- </button>
+ </Button>
</form>
<Status busy={false} error={profileErr} empty={profileOk} />
- </section>
+ </Card.Root>
- <section class="card stack">
- <h2>修改密码</h2>
+ <Card.Root size="sm" >
+ <Card.Header><Card.Title>修改密码</Card.Title></Card.Header>
<form onsubmit={changePassword} class="stack">
<div class="grid3 wrap-3">
- <label>
- 当前密码
- <input type="password" bind:value={curPw} required autocomplete="current-password" />
- </label>
- <label>
- 新密码
- <input type="password" bind:value={newPw} required autocomplete="new-password" minlength={8} />
- </label>
- <label>
- 确认新密码
- <input type="password" bind:value={newPw2} required autocomplete="new-password" minlength={8} />
- </label>
+ <div>
+ <Label for="pw-cur">当前密码</Label>
+ <Input id="pw-cur" type="password" bind:value={curPw} required autocomplete="current-password" />
+ </div>
+ <div>
+ <Label for="pw-new">新密码</Label>
+ <Input id="pw-new" type="password" bind:value={newPw} required autocomplete="new-password" minlength={8} />
+ </div>
+ <div>
+ <Label for="pw-new2">确认新密码</Label>
+ <Input id="pw-new2" type="password" bind:value={newPw2} required autocomplete="new-password" minlength={8} />
+ </div>
</div>
<div class="row">
- <button class="btn primary" disabled={pwBusy}>
+ <Button type="submit" disabled={pwBusy}>
{pwBusy ? '更新中…' : '更新密码'}
- </button>
+ </Button>
</div>
<Status busy={false} error={pwErr} empty={pwOk} />
</form>
- </section>
+ </Card.Root>
- <section class="card stack">
- <h2>登录会话</h2>
+ <Card.Root size="sm" >
+ <Card.Header><Card.Title>登录会话</Card.Title></Card.Header>
<Status busy={sessBusy} error={sessErr} busyText="载入会话…" empty={null} />
{#if !sessBusy && !sessErr && sessions.length === 0}
<Status busy={false} empty="没有其他会话记录" />
{/if}
{#if sessions.length > 0}
- <table class="data">
- <thead>
- <tr>
- <th>创建</th>
- <th>最近活跃</th>
- <th>设备/UA</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
+ <Table.Root>
+ <Table.Header>
+ <Table.Row>
+ <Table.Head>创建</Table.Head>
+ <Table.Head>最近活跃</Table.Head>
+ <Table.Head>设备/UA</Table.Head>
+ <Table.Head></Table.Head>
+ </Table.Row>
+ </Table.Header>
+ <Table.Body>
{#each sessions as s (s.id)}
- <tr>
- <td>{fmtDateTime(s.created_at)}</td>
- <td>{fmtDateTime(s.last_seen_at)}</td>
- <td class="hint">{s.user_agent?.slice(0, 60) ?? '—'}</td>
- <td>
+ <Table.Row>
+ <Table.Cell>{fmtDateTime(s.created_at)}</Table.Cell>
+ <Table.Cell>{fmtDateTime(s.last_seen_at)}</Table.Cell>
+ <Table.Cell class="hint">{s.user_agent?.slice(0, 60) ?? '—'}</Table.Cell>
+ <Table.Cell>
<div class="row">
- {#if s.current}<span class="badge teal">当前会话</span>{/if}
- <button class="btn small danger" disabled={s.current && sessions.length === 1} onclick={() => void revoke(s)}>
+ {#if s.current}<Badge variant="secondary">当前会话</Badge>{/if}
+ <Button variant="outline" size="sm" class="text-destructive" disabled={s.current && sessions.length === 1} onclick={() => void revoke(s)}>
{s.current ? '注销当前' : '撤销'}
- </button>
+ </Button>
</div>
- </td>
- </tr>
+ </Table.Cell>
+ </Table.Row>
{/each}
- </tbody>
- </table>
+ </Table.Body>
+ </Table.Root>
{/if}
<Status busy={false} error={null} empty={sessMsg} />
<p class="hint">更改密码或被管理员停用时,其他会话将自动失效。</p>
- </section>
+ </Card.Root>
</div>
-</Page>
+</Page> \ No newline at end of file