|
1 | | -<!-- 设置:服务器(添加 / 切换 / 重新登录 / 删除,地址与端口分开填)+ 播放(mpv 路径、预加载进度、个人 mpv 配置的位置)+ 关于(界面用哪块 GPU)。分组列表样式。 --> |
| 1 | +<!-- 设置:服务器(添加 / 切换 / 编辑 / 重新登录 / 删除,地址与端口分开填)+ 播放(mpv 路径、预加载进度、个人 mpv 配置的位置)+ 关于(界面用哪块 GPU)。分组列表样式。 --> |
2 | 2 | <script lang="ts"> |
3 | 3 | import { invoke } from '@tauri-apps/api/core' |
4 | | - import { Check, LogIn, Plus, Trash2 } from '@lucide/svelte' |
| 4 | + import { Check, LogIn, Pencil, Plus, Trash2 } from '@lucide/svelte' |
5 | 5 | import { errText, loadConfig, refresh, session } from '../lib/api.svelte' |
6 | 6 | import { go } from '../lib/router.svelte' |
7 | 7 |
|
8 | | - let form = $state({ url: '', port: '', user: '', password: '' }) |
| 8 | + const blank = { url: '', port: '', user: '', password: '' } |
| 9 | + let form = $state({ ...blank }) |
| 10 | + /** 正在编辑的服务器 id;和「添加」共用 form。 */ |
| 11 | + let editing = $state<string | null>(null) |
9 | 12 | let relogin = $state<{ id: string; password: string } | null>(null) |
10 | 13 | let busy = $state(false) |
11 | 14 | let msg = $state<{ text: string; ok: boolean } | null>(null) |
|
28 | 31 | } |
29 | 32 | } |
30 | 33 |
|
| 34 | + function formPort() { |
| 35 | + const port = form.port.trim() ? Number(form.port) : null |
| 36 | + if (port !== null && !(port > 0 && port < 65536)) throw { message: '端口应为 1–65535' } |
| 37 | + return port |
| 38 | + } |
31 | 39 | const add = () => |
32 | 40 | run(async () => { |
33 | | - const port = form.port.trim() ? Number(form.port) : null |
34 | | - if (port !== null && !(port > 0 && port < 65536)) throw { message: '端口应为 1–65535' } |
35 | | - await invoke('server_add', { url: form.url, port, user: form.user, password: form.password }) |
36 | | - form = { url: '', port: '', user: '', password: '' } |
| 41 | + await invoke('server_add', { url: form.url, port: formPort(), user: form.user, password: form.password }) |
| 42 | + form = { ...blank } |
37 | 43 | go('/') |
38 | 44 | }) |
| 45 | + function startEdit(s: { id: string; url: string; port: number; user: string }) { |
| 46 | + form = { url: s.url, port: String(s.port), user: s.user, password: '' } |
| 47 | + editing = s.id |
| 48 | + adding = false |
| 49 | + relogin = null |
| 50 | + } |
| 51 | + function startAdd() { |
| 52 | + form = { ...blank } |
| 53 | + editing = null |
| 54 | + adding = true |
| 55 | + } |
| 56 | + /** 只改地址时后端沿用原 token,不用填密码;改了用户名或填了密码就重新登录。 */ |
| 57 | + const save = (id: string) => |
| 58 | + run(async () => { |
| 59 | + await invoke('server_edit', { id, url: form.url, port: formPort(), user: form.user, password: form.password }) |
| 60 | + editing = null |
| 61 | + form = { ...blank } |
| 62 | + }, '已保存') |
39 | 63 | function login() { |
40 | 64 | const r = relogin |
41 | 65 | if (!r) return |
|
72 | 96 |
|
73 | 97 | <h3>服务器</h3> |
74 | 98 | <div class="group"> |
| 99 | + {#snippet serverForm(submit: () => void, label: string, pwHint: string, cancel: (() => void) | null)} |
| 100 | + <form class="row form" onsubmit={(e) => { e.preventDefault(); submit() }}> |
| 101 | + <input class="url" placeholder="地址,如 https://emby.example.com" bind:value={form.url} required spellcheck="false" /> |
| 102 | + <input class="port" placeholder="端口(默认按协议)" bind:value={form.port} inputmode="numeric" /> |
| 103 | + <input placeholder="用户名" bind:value={form.user} required autocomplete="username" spellcheck="false" /> |
| 104 | + <input type="password" placeholder={pwHint} bind:value={form.password} autocomplete="current-password" /> |
| 105 | + <div class="btns"> |
| 106 | + <button class="btn primary" disabled={busy}>{label}</button> |
| 107 | + {#if cancel}<button type="button" class="btn ghost" onclick={cancel}>取消</button>{/if} |
| 108 | + </div> |
| 109 | + </form> |
| 110 | + {/snippet} |
75 | 111 | {#each session.cfg?.servers ?? [] as s (s.id)} |
| 112 | + {#if editing === s.id} |
| 113 | + {@render serverForm(() => save(s.id), '保存', '密码(只改地址可不填)', () => (editing = null))} |
| 114 | + {:else} |
76 | 115 | <div class="row"> |
77 | 116 | <div class="grow"> |
78 | 117 | <div class="name">{s.name}{#if s.id === session.cfg?.active}<span class="tag">当前</span>{/if}</div> |
|
85 | 124 | <button class="btn ghost" onclick={() => (relogin = null)}>取消</button> |
86 | 125 | {:else} |
87 | 126 | {#if s.id !== session.cfg?.active}<button class="btn" disabled={busy} onclick={() => use(s.id)}><Check size={15} /> 使用</button>{/if} |
88 | | - <button class="btn" disabled={busy} onclick={() => (relogin = { id: s.id, password: '' })}><LogIn size={15} /> 重新登录</button> |
| 127 | + <button class="btn" disabled={busy} onclick={() => startEdit(s)}><Pencil size={15} /> 编辑</button> |
| 128 | + <button class="btn" disabled={busy} onclick={() => ((relogin = { id: s.id, password: '' }), (editing = null))}><LogIn size={15} /> 重新登录</button> |
89 | 129 | <button class="btn icon danger" title="删除" disabled={busy} onclick={() => remove(s.id, s.name)}><Trash2 size={15} /></button> |
90 | 130 | {/if} |
91 | 131 | </div> |
| 132 | + {/if} |
92 | 133 | {/each} |
93 | 134 | {#if adding} |
94 | | - <form class="row add" onsubmit={(e) => { e.preventDefault(); add() }}> |
95 | | - <input class="url" placeholder="地址,如 https://emby.example.com" bind:value={form.url} required spellcheck="false" /> |
96 | | - <input class="port" placeholder="端口(默认按协议)" bind:value={form.port} inputmode="numeric" /> |
97 | | - <input placeholder="用户名" bind:value={form.user} required autocomplete="username" spellcheck="false" /> |
98 | | - <input type="password" placeholder="密码" bind:value={form.password} autocomplete="current-password" /> |
99 | | - <div class="btns"> |
100 | | - <button class="btn primary" disabled={busy}><Plus size={15} /> 添加并登录</button> |
101 | | - {#if session.cfg?.servers.length}<button type="button" class="btn ghost" onclick={() => (adding = false)}>取消</button>{/if} |
102 | | - </div> |
103 | | - </form> |
| 135 | + {@render serverForm(add, '添加并登录', '密码', session.cfg?.servers.length ? () => (adding = false) : null)} |
104 | 136 | {:else} |
105 | | - <button class="row link" onclick={() => (adding = true)}><Plus size={16} /> 添加服务器</button> |
| 137 | + <button class="row link" onclick={startAdd}><Plus size={16} /> 添加服务器</button> |
106 | 138 | {/if} |
107 | 139 | </div> |
108 | 140 |
|
|
155 | 187 | } |
156 | 188 | input:not([type='range']):focus { background: var(--bg); box-shadow: inset 0 -2px 0 var(--accent); } |
157 | 189 | input[type='range'] { accent-color: var(--accent); } |
158 | | - .add { display: grid; grid-template-columns: 170px 1fr 1fr; gap: 10px; } |
159 | | - .add .url, .add .btns { grid-column: 1 / -1; } |
| 190 | + .form { display: grid; grid-template-columns: 170px 1fr 1fr; gap: 10px; } |
| 191 | + .form .url, .form .btns { grid-column: 1 / -1; } |
160 | 192 | .btns { display: flex; gap: 8px; } |
161 | 193 | code { font-family: ui-monospace, Consolas, monospace; font-size: 11px; padding: 0 4px; border-radius: 4px; background: var(--fill); } |
162 | 194 | .note { margin: 10px var(--gutter); font-size: 12px; color: var(--text-3); line-height: 1.8; } |
|
0 commit comments