diff --git a/frontend/src/pages/FilesPage.vue b/frontend/src/pages/FilesPage.vue index 570edc95..4f1c4965 100644 --- a/frontend/src/pages/FilesPage.vue +++ b/frontend/src/pages/FilesPage.vue @@ -992,6 +992,8 @@ function fileRowProps(data: { item: FileEntry }) { style: { cursor: item.type === 'directory' || item.type === 'symlink' ? 'pointer' : 'default' }, onClick: (e: MouseEvent) => { if (isRowActionTarget(e.target)) return + // 只响应单击(e.detail === 1),防止双击时触发两次导航 + if (e.detail !== 1) return onRowClick(e, { item }) }, onDblclick: (e: MouseEvent) => { diff --git a/frontend/src/utils/fileMode.ts b/frontend/src/utils/fileMode.ts index 527fd111..b1bcc1bf 100644 --- a/frontend/src/utils/fileMode.ts +++ b/frontend/src/utils/fileMode.ts @@ -82,51 +82,42 @@ export function buildFilesAccessHint(params: { lastReadDeniedPath?: string | null unreadableCount?: number }): { type: 'warning' | 'error'; text: string } | null { + // root SSH 用户无需任何提示 + if (params.isRootSsh) return null + const lines: string[] = [] - if (params.lastReadDeniedPath) { - lines.push(`无法读取「${params.lastReadDeniedPath}」:当前 SSH 用户可能无读权限。`) - } const n = params.unreadableCount ?? 0 + + // 只在有实际权限问题时显示提示 + if (params.lastReadDeniedPath) { + lines.push(`无法读取「${params.lastReadDeniedPath}」:当前 SSH 用户无权限。`) + } if (n > 0 && params.sshUser) { - lines.push(`当前目录约 ${n} 个文件对 SSH 用户「${params.sshUser}」可能不可读(根据 ls 权限推断)。`) + lines.push(`当前目录约 ${n} 个文件对 SSH 用户「${params.sshUser}」不可读。`) } - if (!params.isRootSsh) { + // 确认有实际错误后,再给出配置建议 + if (lines.length > 0) { if (params.filesElevation === 'off') { - lines.push('此服务器「文件提权」已关闭,不会自动 sudo;写/chmod/读 root 属主文件可能失败。') + lines.push('此服务器「文件提权」已关闭,不会自动 sudo。') } else if (!params.canSudo) { - lines.push( - '浏览/写权限、chmod、读取 root 属主目录等可能失败。请在目标机配置免密 sudo(NOPASSWD),或改用 root SSH 用户。', - ) + lines.push('请配置免密 sudo(NOPASSWD)或使用「一键配置」,或改用 root SSH 用户。') } else if (!params.whitelistOk) { - lines.push( - '已可免密 sudo,但白名单可能不完整(须含 bash、chmod、chown、tee、cat 等);部分写/chmod 仍会失败。', - ) - } else if ( - params.lastReadDeniedPath - || n > 0 - || (params.capabilityMessage && !params.capabilityMessage.includes('无需')) - ) { - lines.push('权限不足时将自动尝试 sudo -n(需目标机已按示例配置 sudoers)。') + lines.push('已有 sudo 但白名单不完整(须含 bash、chmod、chown、tee、cat 等)。') + } else { + lines.push('系统已自动尝试 sudo -n 提权,如仍失败请检查 sudoers 白名单。') } } if (!lines.length) return null + const needsSudoersDoc = - !params.isRootSsh - && (params.filesElevation === 'off' || !params.canSudo || !params.whitelistOk) + params.filesElevation === 'off' || !params.canSudo || !params.whitelistOk const suffix = needsSudoersDoc ? ` ${filesSudoersDocHint()}` : '' const type: 'warning' | 'error' = - !params.isRootSsh && (!params.canSudo || params.filesElevation === 'off') - ? 'error' - : 'warning' + (!params.canSudo || params.filesElevation === 'off') ? 'error' : 'warning' const body = lines.join(' ') - const capMsg = params.capabilityMessage?.trim() - const text = - capMsg && needsSudoersDoc && !body.includes(capMsg) - ? `${body}(检测:${capMsg})${suffix}` - : `${body}${suffix}` - return { type, text } + return { type, text: `${body}${suffix}` } } export { SUDOERS_DOC }