|
9 | 9 | isTauri: false, |
10 | 10 | serverRunning: false, |
11 | 11 | shareInfo: null, |
| 12 | + selectedAddressUrl: '', |
12 | 13 | statusTimer: null, |
13 | 14 | adminFileSharing: false, |
14 | 15 | adminDragDropBound: false, |
|
47 | 48 |
|
48 | 49 | function getDownloadUrl(item) { |
49 | 50 | const path = `/api/items/${item.id}/download`; |
50 | | - if (state.shareInfo?.url) { |
51 | | - return new URL(path, new URL(state.shareInfo.url).origin).toString(); |
| 51 | + const selectedUrl = state.role === 'admin' ? currentShareUrl() : window.location.origin; |
| 52 | + if (selectedUrl) { |
| 53 | + return new URL(path, new URL(selectedUrl).origin).toString(); |
52 | 54 | } |
53 | 55 | return new URL(path, window.location.origin).toString(); |
54 | 56 | } |
55 | 57 |
|
| 58 | + function shareAddresses(info = state.shareInfo) { |
| 59 | + if (!info) return []; |
| 60 | + const addresses = Array.isArray(info.addresses) ? info.addresses : []; |
| 61 | + if (addresses.length) return addresses; |
| 62 | + return info.url ? [{ ip: info.ip || new URL(info.url).hostname, url: info.url, qr: info.qr }] : []; |
| 63 | + } |
| 64 | + |
| 65 | + function currentShareAddress() { |
| 66 | + const addresses = shareAddresses(); |
| 67 | + if (!addresses.length) return null; |
| 68 | + return addresses.find((item) => item.url === state.selectedAddressUrl) || addresses[0]; |
| 69 | + } |
| 70 | + |
| 71 | + function currentShareUrl() { |
| 72 | + return currentShareAddress()?.url || state.shareInfo?.url || ''; |
| 73 | + } |
| 74 | + |
| 75 | + function clientShareAddress() { |
| 76 | + const currentUrl = window.location.origin; |
| 77 | + const match = shareAddresses().find((item) => { |
| 78 | + try { |
| 79 | + return new URL(item.url).origin === currentUrl; |
| 80 | + } catch (_) { |
| 81 | + return false; |
| 82 | + } |
| 83 | + }); |
| 84 | + return { url: currentUrl, qr: match?.qr || state.shareInfo?.qr }; |
| 85 | + } |
| 86 | + |
56 | 87 | async function request(url, options = {}) { |
57 | 88 | const response = await fetch(`${state.apiBase}${url}`, options); |
58 | 89 | if (!response.ok) { |
|
185 | 216 | const qr = $('clientQr'); |
186 | 217 | const text = $('clientUrlText'); |
187 | 218 | const copy = $('copyClientUrl'); |
188 | | - if (qr) { |
189 | | - qr.innerHTML = info.qr; |
190 | | - } |
191 | | - if (text) { |
192 | | - text.textContent = info.url; |
193 | | - text.title = info.url; |
| 219 | + const select = $('clientAddressSelect'); |
| 220 | + const addressPicker = select?.closest('.address-picker'); |
| 221 | + const addresses = shareAddresses(info); |
| 222 | + if (state.role === 'admin' && select) { |
| 223 | + const previousUrl = state.selectedAddressUrl; |
| 224 | + const selected = addresses.find((item) => item.url === previousUrl) |
| 225 | + || addresses[0] |
| 226 | + || null; |
| 227 | + state.selectedAddressUrl = selected?.url || ''; |
| 228 | + select.innerHTML = addresses.map((item) => { |
| 229 | + const label = item.ip || new URL(item.url).hostname; |
| 230 | + return `<option value="${escapeHtml(item.url)}"${item.url === state.selectedAddressUrl ? ' selected' : ''}>${escapeHtml(label)}</option>`; |
| 231 | + }).join(''); |
| 232 | + if (addressPicker) addressPicker.hidden = addresses.length <= 1; |
| 233 | + select.onchange = () => { |
| 234 | + state.selectedAddressUrl = select.value; |
| 235 | + renderShareAddress(); |
| 236 | + render(); |
| 237 | + }; |
| 238 | + } else { |
| 239 | + state.selectedAddressUrl = ''; |
194 | 240 | } |
| 241 | + renderShareAddress(); |
195 | 242 | if (copy) { |
196 | 243 | copy.onclick = async () => { |
197 | | - const copied = await copyText(info.url); |
| 244 | + const copied = await copyText(state.role === 'admin' ? currentShareUrl() : window.location.origin); |
198 | 245 | copy.textContent = copied ? '已复制' : '复制失败'; |
199 | 246 | setTimeout(() => { copy.textContent = '复制链接'; }, 1200); |
200 | 247 | }; |
201 | 248 | } |
202 | 249 | } |
203 | 250 |
|
| 251 | + function renderShareAddress() { |
| 252 | + const address = state.role === 'admin' |
| 253 | + ? currentShareAddress() |
| 254 | + : clientShareAddress(); |
| 255 | + const qr = $('clientQr'); |
| 256 | + const text = $('clientUrlText'); |
| 257 | + if (!address) return; |
| 258 | + if (qr) { |
| 259 | + qr.innerHTML = address.qr; |
| 260 | + } |
| 261 | + if (text) { |
| 262 | + text.textContent = address.url; |
| 263 | + text.title = address.url; |
| 264 | + } |
| 265 | + } |
| 266 | + |
204 | 267 | function resetServerUi() { |
205 | 268 | state.serverRunning = false; |
206 | 269 | state.apiBase = ''; |
|
219 | 282 | document.body.classList.add('server-stopped'); |
220 | 283 | $('clientQr') && ($('clientQr').innerHTML = ''); |
221 | 284 | $('clientUrlText') && ($('clientUrlText').textContent = '服务未启动'); |
| 285 | + const select = $('clientAddressSelect'); |
| 286 | + if (select) { |
| 287 | + select.innerHTML = ''; |
| 288 | + const addressPicker = select.closest('.address-picker'); |
| 289 | + if (addressPicker) addressPicker.hidden = true; |
| 290 | + } |
222 | 291 | $('status') && ($('status').textContent = '未启动'); |
223 | 292 | state.shareInfo = null; |
| 293 | + state.selectedAddressUrl = ''; |
224 | 294 | setServerControlsStopped(); |
225 | 295 | } |
226 | 296 |
|
|
543 | 613 | if (!button || !dialog || !preview || !urlText || !copyButton) return; |
544 | 614 |
|
545 | 615 | button.addEventListener('click', () => { |
546 | | - if (!state.shareInfo?.qr) return; |
547 | | - preview.innerHTML = state.shareInfo.qr; |
548 | | - urlText.textContent = state.shareInfo.url; |
549 | | - urlText.title = state.shareInfo.url; |
| 616 | + const address = state.role === 'admin' |
| 617 | + ? currentShareAddress() |
| 618 | + : clientShareAddress(); |
| 619 | + if (!address?.qr) return; |
| 620 | + preview.innerHTML = address.qr; |
| 621 | + urlText.textContent = address.url; |
| 622 | + urlText.title = address.url; |
550 | 623 | dialog.showModal(); |
551 | 624 | }); |
552 | 625 |
|
553 | 626 | copyButton.addEventListener('click', async () => { |
554 | | - if (!state.shareInfo?.url) return; |
555 | | - const copied = await copyText(state.shareInfo.url); |
| 627 | + const url = state.role === 'admin' ? currentShareUrl() : window.location.origin; |
| 628 | + if (!url) return; |
| 629 | + const copied = await copyText(url); |
556 | 630 | copyButton.textContent = copied ? '已复制' : '复制失败'; |
557 | 631 | setTimeout(() => { copyButton.textContent = '复制链接'; }, 1200); |
558 | 632 | }); |
|
0 commit comments