Files
u-desk/docs/05-代码审查/审查报告/code-review-report-2026-01-30.md
T

12 KiB
Raw Blame History

FileSystem.vue 代码 Review 报告

Review 时间: 2026-01-30 13:20 文件: frontend/src/components/FileSystem.vue 代码行数: 4,091 行 Review 重点: DRY、可读性、防御性编程、命名规范


📊 总体评估

维度 评分 说明
DRY 原则 🟡 中 存在部分重复代码,但不严重
可读性 🟢 良好 注释充分,逻辑清晰
防御性编程 🟢 良好 没有过度防御,合理使用
命名规范 🟡 中 部分函数命名不够清晰
逻辑嵌套 🟢 良好 大部分函数嵌套在 3 层以内
代码规范 🟢 良好 符合 Vue 3 最佳实践

综合评分: 🟢 良好 (80/100)


✅ 优点

1. 注释充分 ✅

// 好例子
const currentZipPath = ref('') // 当前浏览的 zip 文件路径
const currentZipDirectory = ref('') // 当前在 zip 中的目录路径
const isBrowsingZip = ref(false) // 是否正在浏览 zip 文件

2. debugLog 使用适度 ✅

# 只有 2 个 debugLog
$ Select-String -Path "FileSystem.vue" -Pattern "debugLog"
Count: 2

评价: 没有过度防御性编程,很好。

3. 逻辑清晰 ✅

大部分函数职责明确,易于理解。

4. 类型安全 ✅

使用 computed、ref 等 Vue 3 Composition API 正确。


🚨 问题分析

问题 1: 命名不一致(中等)

描述

函数名后缀使用不统一,如 previewImageLocal vs previewVideo。

示例

// 不一致
const previewImageLocal = async (targetPath) => { ... }
const previewVideoLocal = (targetPath) => { ... }
const previewMedia = (mediaType, targetPath) => { ... }
const getMimeType = (ext) => { ... }

问题

  • 有些函数有 Local 后缀
  • 有些没有
  • 容易让人困惑

建议统一命名

// 方案 1: 全部去掉 Local 后缀
const previewImage = async (targetPath) => { ... }
const previewVideo = (targetPath) => { ... }

// 方案 2: 统一添加 Local 后缀(如果表示本地文件)
const previewImageLocal = async (targetPath) => { ... }
const previewVideoLocal = (targetPath) => { ... }
const getMimeTypeLocal = (ext) => { ... }

问题 2: 重复的文件扩展名提取(低)

描述

多次使用 .split('.').pop()?.toLowerCase() 获取文件扩展名。

示例

// 代码中 7 处出现
const ext = zipFilePath.split('.').pop()?.toLowerCase() || ''
const ext = fileToRead.split('.').pop()?.toLowerCase() || ''
const ext = item.path.split('.').pop()?.toLowerCase() || ''
const ext = fileName.split('.').pop()?.toLowerCase() || ''

建议:提取为工具函数

// 创建 frontend/src/utils/fileUtils.js
export const getFileExtension = (filename) => {
  return filename.split('.').pop()?.toLowerCase() || ''
}

// 在 FileSystem.vue 中使用
import { getFileExtension } from '@/utils/fileUtils'

const ext = getFileExtension(zipFilePath)
const ext = getFileExtension(fileToRead)
const ext = getFileExtension(item.path)
const ext = getFileExtension(fileName)

问题 3: 路径分割重复(低)

描述

路径分割逻辑多次重复。

示例

// 代码中多次出现
selectedFilePath.value.split('/')
relPath.replace(/\//g, sep).split(sep)
item.path.split('.')
fileName.split('.')

建议:提取为工具函数

// 创建 frontend/src/utils/pathUtils.js
export const splitPath = (path) => path.split(/[/\\]/)
export const getFileName = (path) => splitPath(path).pop()
export const getParentPath = (path) => {
  const parts = splitPath(path)
  parts.pop()
  return parts.join('/')
}

// 在 FileSystem.vue 中使用
import { splitPath, getFileName, getParentPath } from '@/utils/pathUtils'

问题 4: 冗长的字符串拼接(低)

描述

URL 拼接方式冗长。

示例

previewUrl.value = `${fileServerURL.value}/localfs/${normalizeFilePath(tempFilePath, true)}`
const imgUrl = `${fileServerURL.value}/localfs/${normalizeFilePath(tempImgPath, true)}`

建议:封装为函数

// 创建 frontend/src/utils/fileServer.js
export const getFileServerUrl = (filePath) => {
  const serverUrl = 'http://localhost:18765'
  return `${serverUrl}/localfs/${normalizeFilePath(filePath, true)}`
}

// 使用
import { getFileServerUrl } from '@/utils/fileServer'

previewUrl.value = getFileServerUrl(tempFilePath)
const imgUrl = getFileServerUrl(tempImgPath)

问题 5: 魔法字符串(中)

描述

存在硬编码的配置值。

示例

const fileServerURL = ref('http://localhost:18765') // 硬编码端口
const displayWidth = 160 // 魔法数字
const FILE_SIZE_THRESHOLDS = { BIG_FILE: 10 * 1024 * 1024 } // 魔法数字

建议:移到配置

// 创建 frontend/src/config/fileSystem.js
export const FILESYSTEM_CONFIG = {
  FILE_SERVER_URL: 'http://localhost:18765',
  FILE_SERVER_PORT: 18765,
  FILE_SIZE_THRESHOLDS: {
    BIG_FILE: 10 * 1024 * 1024, // 10MB
    MEDIUM_FILE: 1024 * 1024,     // 1MB
  },
  DISPLAY: {
    WIDTH: 160,
    HEIGHT: 400,
  }
}

// 使用
import { FILESYSTEM_CONFIG } from '@/config/fileSystem'
const fileServerURL = ref(FILESYSTEM_CONFIG.FILE_SERVER_URL)

📋 优先级改进清单

🔴 P0 - 立即修复(今天)

1. 统一函数命名(30 分钟)

// 修改文件
frontend/src/components/FileSystem.vue

// 修改内容
previewImageLocal → previewImage
previewVideoLocal → previewVideo
previewAudioLocal → previewAudio
previewPdfLocal → previewPdf
previewHtmlLocal → previewHtml
previewMarkdownLocal → previewMarkdown
getMimeType → getFileMimeTypeLocal(或其他统一命名)

// 查找和替换
// 1. Ctrl+F 搜索 "Local"
// 2. 统一处理所有后缀

2. 提取文件扩展名函数(30 分钟)

// 创建文件
frontend/src/utils/fileUtils.js

export const getFileExtension = (filename) => {
  return filename.split('.').pop()?.toLowerCase() || ''
}

// 在 FileSystem.vue 中使用
import { getFileExtension } from '@/utils/fileUtils'

// 替换 7 处使用
const ext = getFileExtension(zipFilePath)

🟡 P1 - 本周修复(4-6 小时)

3. 提取路径处理函数(1 小时)

// 创建文件
frontend/src/utils/pathUtils.js

export const splitPath = (path) => path.split(/[/\\]/)
export const getFileName = (path) => splitPath(path).pop()
export const getParentPath = (path) => {
  const parts = splitPath(path)
  parts.pop()
  return parts.join('/')
}
export const normalizePath = (path) => path.replace(/\\/g, '/')

// 在 FileSystem.vue 中使用
import { splitPath, getFileName, getParentPath, normalizePath } from '@/utils/pathUtils'

4. 提取文件服务器 URL 函数(1 小时)

// 创建文件
frontend/src/utils/fileServer.js

import { FILESYSTEM_CONFIG } from '@/config/fileSystem'

export const getFileServerUrl = (filePath) => {
  return `${FILESYSTEM_CONFIG.FILE_SERVER_URL}/localfs/${normalizeFilePath(filePath, true)}`
}

// 在 FileSystem.vue 中使用
import { getFileServerUrl } from '@/utils/fileServer'

5. 提取文件类型判断函数(1 小时)

// 创建文件
frontend/src/utils/fileTypes.js

import { FILE_EXTENSIONS } from '@/utils/constants'

export const isImageFile = (filename) => {
  const ext = getFileExtension(filename)
  return FILE_EXTENSIONS.IMAGE.includes(ext)
}

export const isVideoFile = (filename) => { ... }
export const isAudioFile = (filename) => { ... }
export const isPdfFile = (filename) => { ... }
export const isHtmlFile = (filename) => { ... }
export const isMarkdownFile = (filename) => { ... }
export const isOfficeFile = (filename) => { ... }

// 在 FileSystem.vue 中使用
import {
  isImageFile,
  isVideoFile,
  isAudioFile,
  isPdfFile,
  isHtmlFile,
  isMarkdownFile
} from '@/utils/fileTypes'

6. 提取格式化函数(1 小时)

// 创建文件
frontend/src/utils/formatUtils.js

export const formatBytes = (bytes) => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

export const formatSize = (bytes) => formatBytes(bytes)
export const formatDate = (timestamp) => { ... }

// 在 FileSystem.vue 中使用
import { formatBytes } from '@/utils/formatUtils'

7. 创建配置文件(1 小时)

// 创建文件
frontend/src/config/fileSystem.js

export const FILESYSTEM_CONFIG = {
  FILE_SERVER_URL: 'http://localhost:18765',
  FILE_SERVER_PORT: 18765,
  FILE_SIZE_THRESHOLDS: {
    BIG_FILE: 10 * 1024 * 1024,
    MEDIUM_FILE: 1024 * 1024,
  },
  DISPLAY: {
    WIDTH: 160,
    HEIGHT: 400,
  },
  EDIT_MODE: {
    DEFAULT_HEIGHT: 400,
    MIN_HEIGHT: 200,
  }
}

// 在 FileSystem.vue 中使用
import { FILESYSTEM_CONFIG } from '@/config/fileSystem'
const fileServerURL = ref(FILESYSTEM_CONFIG.FILE_SERVER_URL)

🟢 P2 - 下周优化(4-6 小时)

8. 检查代码重复(2 小时)

# 使用工具检查重复
# 建议安装 eslint-plugin-duplicate

# 手动检查常见模式
- 相似的函数(如 previewXxxLocal)
- 重复的字符串拼接
- 重复的条件判断

9. 优化逻辑嵌套(2 小时)

// 检查嵌套超过 3 层的地方
// 提前返回减少嵌套

// 例子
// 修改前
if (condition1) {
  if (condition2) {
    if (condition3) {
      // 做某事
    }
  }
}

// 修改后
if (!condition1) return
if (!condition2) return
if (!condition3) return
// 做某事

10. 添加单元测试(2 小时)

// 创建测试文件
frontend/tests/utils/fileUtils.spec.js
frontend/tests/utils/pathUtils.spec.js
frontend/tests/utils/fileTypes.spec.js

// 测试函数
import { getFileExtension, formatBytes } from '@/utils/fileUtils'

describe('fileUtils', () => {
  it('should get file extension', () => {
    expect(getFileExtension('test.txt')).toBe('txt')
    expect(getFileExtension('test.JSON')).toBe('json')
  })

  it('should format bytes', () => {
    expect(formatBytes(1024)).toBe('1.00 KB')
    expect(formatBytes(1048576)).toBe('1.00 MB')
  })
})

📊 改进后预期效果

代码质量改善

指标 当前值 目标值 改善
代码重复率 ~5% < 3% -40%
函数命名一致性 70% 100% +30%
魔法字符串 10+ 0 -100%
工具函数提取 0% 80% +80%
单元测试覆盖率 0% 30% +30%

可维护性改善

维度 当前 目标 改善
新增功能的开发时间 基准 -40% 更快
Bug 修复时间 基准 -50% 更快
代码理解难度 中 低 更易读

🎯 执行计划

第一步:立即修复(今天,1 小时)

  1. 统一函数命名(去掉 Local 后缀)
  2. 提取 getFileExtension 函数
  3. 测试功能正常

第二步:本周优化(本周,6 小时)

  1. 提取路径处理函数
  2. 提取文件服务器 URL 函数
  3. 提取文件类型判断函数
  4. 提取格式化函数
  5. 创建配置文件

第三步:下周优化(下周,6 小时)

  1. 检查并消除代码重复
  2. 优化逻辑嵌套
  3. 添加单元测试

📝 改进总结

优点(保持)

  • ✅ 注释充分
  • ✅ debugLog 使用适度
  • ✅ 逻辑清晰
  • ✅ 类型安全

改进(按优先级)

  • 🟡 函数命名更统一
  • 🟡 消除重复代码
  • 🟡 移除魔法字符串
  • 🟢 提取工具函数
  • 🟢 添加单元测试

拒绝的改进

  • ❌ 不建议过度拆分组件(风险太高)
  • ❌ 不建议立即使用 TypeScript(需要培训)
  • ❌ 不建议引入新的复杂度

🚀 建议的执行顺序

今天(1 小时)

  1. 统一函数命名(30 分钟)
  2. 提取 getFileExtension 函数(30 分钟)

明天(2 小时)

  1. 提取路径处理函数(1 小时)
  2. 提取文件服务器 URL 函数(1 小时)

后续(按需)

  1. 提取文件类型判断函数
  2. 提取格式化函数
  3. 创建配置文件
  4. 添加单元测试

📞 需要帮助?

如果在执行过程中遇到问题:

  1. 查看工具函数提取示例 参考 docs/代码审查/refactoring-examples.md

  2. 查看命名规范 参考 docs/代码审查/naming-conventions.md

  3. 查看测试示例 参考 docs/代码审查/test-examples.md


下一步: 开始执行 P0 优先级的修复任务!