|
| 1 | +/** |
| 2 | + * 路径截断工具函数 |
| 3 | + * |
| 4 | + * 用于解决前端提示框中长路径导致内容溢出的问题。 |
| 5 | + * 当错误消息包含过长的文件路径时,会导致提示框显示异常。 |
| 6 | + * |
| 7 | + * 使用场景: |
| 8 | + * - Toast 消息中包含文件路径 |
| 9 | + * - 错误提示中包含配置文件路径 |
| 10 | + * - 任何可能因路径过长导致 UI 溢出的场景 |
| 11 | + * |
| 12 | + * 兼容性: |
| 13 | + * - Windows 路径:D:\folder\subfolder\file (使用 \ 作为分隔符) |
| 14 | + * - Linux/Unix 路径:/home/user/folder/file (使用 / 作为分隔符) |
| 15 | + * |
| 16 | + * 示例: |
| 17 | + * - Windows: D:\NapCat.Shell-1\NapCat.Shell-2\...\data → D:\NapCat.Shell-1\...\napcat-plugin-builtin\data |
| 18 | + * - Linux: /home/user/projects/napcat/plugins/data → /home/user/...\plugins/data |
| 19 | + */ |
| 20 | + |
| 21 | +/** |
| 22 | + * 截断长路径,保留开头和结尾部分 |
| 23 | + * |
| 24 | + * @param path - 需要截断的路径(支持 Windows 和 Linux 路径格式) |
| 25 | + * @param maxLength - 最大允许长度,默认 60 字符 |
| 26 | + * @returns 截断后的路径,中间用 ... 替代 |
| 27 | + * |
| 28 | + * @example |
| 29 | + * // Windows 路径 |
| 30 | + * truncatePath('D:\\folder1\\folder2\\folder3\\file.txt', 30) |
| 31 | + * // 返回: 'D:\\...\\folder3\\file.txt' |
| 32 | + * |
| 33 | + * @example |
| 34 | + * // Linux 路径 |
| 35 | + * truncatePath('/home/user/projects/deep/nested/file.txt', 30) |
| 36 | + * // 返回: '/home/user/.../nested/file.txt' |
| 37 | + */ |
| 38 | +export function truncatePath (path: string, maxLength: number = 60): string { |
| 39 | + if (path.length <= maxLength) { |
| 40 | + return path; |
| 41 | + } |
| 42 | + |
| 43 | + // 自动检测路径分隔符,兼容 Windows (\) 和 Linux/Unix (/) |
| 44 | + const separator = path.includes('\\') ? '\\' : '/'; |
| 45 | + const parts = path.split(separator); |
| 46 | + |
| 47 | + if (parts.length <= 3) { |
| 48 | + // 如果路径段太少(如 D:\folder\file),直接尾部截断 |
| 49 | + return path.substring(0, maxLength - 3) + '...'; |
| 50 | + } |
| 51 | + |
| 52 | + // 保留第一段(Windows 驱动器号如 D: 或 Linux 根目录)和最后两段(父目录+文件名) |
| 53 | + const firstPart = parts[0]; |
| 54 | + const lastParts = parts.slice(-2).join(separator); |
| 55 | + |
| 56 | + const truncated = `${firstPart}${separator}...${separator}${lastParts}`; |
| 57 | + |
| 58 | + // 如果截断后仍然超长,回退到简单的尾部截断 |
| 59 | + if (truncated.length > maxLength) { |
| 60 | + return path.substring(0, maxLength - 3) + '...'; |
| 61 | + } |
| 62 | + |
| 63 | + return truncated; |
| 64 | +} |
| 65 | + |
| 66 | +/** |
| 67 | + * 智能截断消息文本,特别处理包含路径的错误消息 |
| 68 | + * |
| 69 | + * 此函数会自动检测消息中的文件路径(Windows 和 Linux 格式)并截断过长的路径, |
| 70 | + * 以防止 UI 组件(如 Toast、Alert)因内容过长而溢出。 |
| 71 | + * |
| 72 | + * @param message - 需要处理的消息文本 |
| 73 | + * @param maxLength - 最终消息的最大长度,默认 100 字符 |
| 74 | + * @returns 处理后的消息,路径被截断,整体长度受限 |
| 75 | + * |
| 76 | + * @example |
| 77 | + * // 处理包含 Windows 路径的错误消息 |
| 78 | + * truncateErrorMessage("Save failed: Error updating config: EPERM: operation not permitted, open 'D:\\very\\long\\path\\config.json'") |
| 79 | + * // 返回: "Save failed: Error updating config: EPERM: operation not permitted, open 'D:\\...\\path\\config.json'" |
| 80 | + * |
| 81 | + * @example |
| 82 | + * // 处理包含 Linux 路径的错误消息 |
| 83 | + * truncateErrorMessage("Failed to read /home/user/projects/napcat/very/deep/nested/config.json") |
| 84 | + * // 返回: "Failed to read /home/user/.../nested/config.json" |
| 85 | + */ |
| 86 | +export function truncateErrorMessage (message: string, maxLength: number = 100): string { |
| 87 | + if (message.length <= maxLength) { |
| 88 | + return message; |
| 89 | + } |
| 90 | + |
| 91 | + // Windows 路径正则:匹配 盘符:\路径 格式,如 D:\folder\file.txt |
| 92 | + // 排除空白字符和引号,避免匹配到路径外的内容 |
| 93 | + const windowsPathRegex = /[A-Za-z]:\\[^\s'"]+/g; |
| 94 | + |
| 95 | + // Linux/Unix 路径正则:匹配 /开头的多级路径,如 /home/user/file |
| 96 | + // 要求至少有两级目录,避免匹配单独的 / |
| 97 | + const unixPathRegex = /\/[^\s'"]+(?:\/[^\s'"]+)+/g; |
| 98 | + |
| 99 | + let result = message; |
| 100 | + |
| 101 | + // 处理 Windows 路径 |
| 102 | + const windowsPaths = message.match(windowsPathRegex); |
| 103 | + if (windowsPaths) { |
| 104 | + for (const path of windowsPaths) { |
| 105 | + if (path.length > 40) { |
| 106 | + result = result.replace(path, truncatePath(path, 40)); |
| 107 | + } |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + // 处理 Unix 路径 |
| 112 | + const unixPaths = message.match(unixPathRegex); |
| 113 | + if (unixPaths) { |
| 114 | + for (const path of unixPaths) { |
| 115 | + if (path.length > 40) { |
| 116 | + result = result.replace(path, truncatePath(path, 40)); |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + // 如果处理路径后消息仍然超长,直接尾部截断 |
| 122 | + if (result.length > maxLength) { |
| 123 | + return result.substring(0, maxLength - 3) + '...'; |
| 124 | + } |
| 125 | + |
| 126 | + return result; |
| 127 | +} |
| 128 | + |
| 129 | +/** |
| 130 | + * 截断普通文本(简单截断,不做路径检测) |
| 131 | + * |
| 132 | + * @param text - 需要截断的文本 |
| 133 | + * @param maxLength - 最大长度,默认 50 字符 |
| 134 | + * @returns 截断后的文本,超长部分用 ... 替代 |
| 135 | + */ |
| 136 | +export function truncateText (text: string, maxLength: number = 50): string { |
| 137 | + if (text.length <= maxLength) { |
| 138 | + return text; |
| 139 | + } |
| 140 | + return text.substring(0, maxLength - 3) + '...'; |
| 141 | +} |
0 commit comments