diff options
Diffstat (limited to 'frontend/src/pages/AccountPage.svelte')
| -rw-r--r-- | frontend/src/pages/AccountPage.svelte | 112 |
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 |
