feat(frontend): 跨服务器传输源机 A 展示可点击站点链接

与服务器列表同源 resolveServerSiteUrl;选 A 后显示域名链接。
This commit is contained in:
Nexus Agent
2026-06-21 13:19:34 +08:00
parent 71f0f53e6a
commit 45d21026b7
5 changed files with 87 additions and 0 deletions
@@ -0,0 +1,32 @@
# 审计 — 2026-06-21 跨服务器传输 UI 补强
**Changelog**: `docs/changelog/2026-06-21-server-transfer-dest-browser.md``docs/changelog/2026-06-21-server-transfer-source-site-link.md`
## Step 3 规则扫描
| 文件 | 规则 | 结论 |
|------|------|------|
| `useServerFileTransfer.ts` | B 机 browse 同步 destPath;站点 URL 计算 | PASS |
| `ServerTransferPage.vue` | 双端文件表;A 机站点外链 `rel=noopener` | PASS |
| `useServerList.ts` | ServerBrief 扩展 extra_attrs 供站点解析 | PASS |
## 文件清单
- `frontend/src/composables/useServerFileTransfer.ts`
- `frontend/src/composables/useServerList.ts`
- `frontend/src/pages/ServerTransferPage.vue`
- `docs/changelog/2026-06-21-server-transfer-dest-browser.md`
- `docs/changelog/2026-06-21-server-transfer-source-site-link.md`
## Closure
| 项 | 状态 |
|----|------|
| B 机目录浏览 | DONE |
| A 机站点链接 | DONE |
## DoD
- [x] B 机文件列表与路径同步
- [x] A 机下方可点击站点 URL
- [x] `vite build` 通过
@@ -0,0 +1,17 @@
# 跨服务器传输:源机 A 下方展示可点击网址
**日期**2026-06-21
## 摘要
选择源服务器 A 后,在服务器选择框下方显示站点域名链接(`resolveServerSiteUrl`),新标签页打开。
## 涉及文件
- `frontend/src/composables/useServerList.ts``ServerBrief` 增加 `description` / `extra_attrs`
- `frontend/src/composables/useServerFileTransfer.ts``sourceSiteUrl` / `sourceSiteHost`
- `frontend/src/pages/ServerTransferPage.vue` — 链接展示
## 说明
未 push Gitea;需上线时由用户指示再传输。
@@ -14,6 +14,7 @@ import {
normalizeRemotePath,
parentRemotePath,
} from '@/utils/remotePath'
import { resolveServerSiteHost, resolveServerSiteUrl } from '@/utils/serverSiteUrl'
export type ServerTransferMode = 'relay' | 'deliver'
@@ -83,6 +84,20 @@ export function useServerFileTransfer() {
filterServerList(destServers.value, destServerSearchQuery.value, destServerId.value),
)
const selectedSourceServer = computed(() =>
sourceServerId.value != null
? serverList.value.find((s) => s.id === sourceServerId.value)
: undefined,
)
const sourceSiteUrl = computed(() =>
selectedSourceServer.value ? resolveServerSiteUrl(selectedSourceServer.value) : null,
)
const sourceSiteHost = computed(() =>
selectedSourceServer.value ? resolveServerSiteHost(selectedSourceServer.value) : null,
)
const selectedItems = computed(() => {
const set = new Set(selectedPaths.value)
return files.value.filter((f) => set.has(joinRemotePath(currentPath.value, f.name)))
@@ -456,6 +471,8 @@ export function useServerFileTransfer() {
destServerSearchQuery,
filteredSourceServerList,
filteredDestServerList,
sourceSiteUrl,
sourceSiteHost,
sourceServerId,
destServerId,
destServers,
@@ -18,6 +18,8 @@ export interface ServerBrief {
category?: string | null
is_online?: boolean
status?: string
description?: string | null
extra_attrs?: Record<string, unknown>
}
export function useServerList() {
+19
View File
@@ -58,6 +58,20 @@
</template>
</v-autocomplete>
<div v-if="sourceSiteUrl && sourceSiteHost" class="mb-3 d-flex align-center ga-1">
<v-icon size="small" color="primary">mdi-web</v-icon>
<a
:href="sourceSiteUrl"
target="_blank"
rel="noopener noreferrer"
class="text-body-2 text-primary text-decoration-none transfer-site-link"
:title="`打开 ${sourceSiteHost}`"
>
{{ sourceSiteHost }}
<v-icon size="x-small" class="ml-1">mdi-open-in-new</v-icon>
</a>
</div>
<div class="d-flex ga-2 mb-3 flex-wrap align-center">
<v-text-field
:model-value="currentPath"
@@ -380,6 +394,8 @@ const {
destServerSearchQuery,
filteredSourceServerList,
filteredDestServerList,
sourceSiteUrl,
sourceSiteHost,
sourceServerId,
destServerId,
currentPath,
@@ -445,4 +461,7 @@ function formatSize(bytes: number): string {
.cursor-pointer {
cursor: pointer;
}
.transfer-site-link:hover {
text-decoration: underline;
}
</style>