Private
Public Access
1
0
Files
u-desk/docs/05-代码审查/审查报告/code-review-report-2026-01-30.md

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 优先级的修复任务!