Files
YG_FT/docs/superpowers/plans/2026-07-10-source-upload-file-list.md
2026-07-27 09:12:47 +08:00

7.3 KiB
Raw Blame History

源数据上传紧凑文件列表 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将数据处理创建页的已上传文件从大卡片改为固定高度、可滚动且可逐项操作的紧凑列表。

Architecture: 只修改 TaskSetupStep 的模板和局部样式,继续消费现有的 uploadedFiles 属性并派发既有 remove-file 事件。使用同一组件内的标题栏和 滚动容器管理信息密度,不改变上传、格式限制或父组件数据流。

Tech Stack: Vue 3 <script setup>、TypeScript、Element Plus、SCSS、Node.js 回归脚本。

Global Constraints

  • 不新增依赖、组件、数据字段或父子组件事件。
  • 上传区、格式限制、校验语义与逐项删除行为必须保持不变。
  • 空状态保留大尺寸拖拽上传区与格式提示;存在至少一个文件时,改为列表标题右侧 的小型“继续上传”入口,且复用相同上传属性与文件变更事件。
  • 列表默认显示最多 5 行;额外文件只能在列表内部纵向滚动。
  • 文件名允许省略,但必须通过原生 title 保留完整文本。
  • 校验状态必须同时显示图标和文字;删除按钮必须可见且可键盘操作。
  • 当前工作区不是 Git 仓库,本计划不包含提交操作。

Task 1: 紧凑上传文件列表

Files:

  • Modify: frontend/scripts/regression-data-process-wizard.mjs
  • Modify: frontend/src/views/data-process/create/TaskSetupStep.vue:157-164,314-351

Interfaces:

  • Consumes: uploadedFiles: { uid: string | number; name: string; size: number; count: number }[]remove-file(uid) 事件。

  • Produces: .uploaded-file-list-header.uploaded-file-items 和每条 .uploaded-file 紧凑行;不新增对外 TypeScript 接口。

  • Step 1: 添加会失败的页面结构回归断言

frontend/scripts/regression-data-process-wizard.mjs 的第二步组件检查之后读取 TaskSetupStep.vue,并追加以下断言:

const taskSetupPath = path.join(createDir, 'TaskSetupStep.vue')
const taskSetupSource = await readFile(taskSetupPath, 'utf8')

for (const marker of [
  'uploaded-file-list-header',
  'uploaded-file-items',
  '已添加 {{ uploadedFiles.length }} 个文件',
  ':title="file.name"',
  'aria-label="已上传文件列表"',
  '继续上传',
  'v-if="uploadedFiles.length === 0"',
]) {
  assert.ok(taskSetupSource.includes(marker), `源数据文件列表缺少:${marker}`)
}
assert.match(taskSetupSource, /\.uploaded-file-items\s*\{[\s\S]*max-height:\s*240px/, '文件列表没有固定可见高度')
assert.match(taskSetupSource, /\.uploaded-file-items\s*\{[\s\S]*overflow-y:\s*auto/, '超出文件没有在列表内滚动')
assert.match(taskSetupSource, /compact-upload[\s\S]*:accept="uploadAccept"/, '继续上传没有复用格式限制')
assert.match(taskSetupSource, /compact-upload[\s\S]*on-change/, '继续上传没有复用文件变更事件')
  • Step 2: 运行回归脚本,确认新增断言失败

Run: npm --prefix frontend run test:data-process-wizard

Expected: FAIL错误信息包含新增文件列表、列表可访问性或继续上传状态切换的缺失标记。

  • Step 3: 更新模板为带标题与滚动区域的紧凑列表

TaskSetupStep.vue 中,将当前上传区改为两种互斥状态:空状态保留大尺寸拖拽 区和格式提示;存在文件时显示含继续上传入口的紧凑列表。两个上传入口均保留 multiple:accept="uploadAccept":auto-upload="false" 和相同 on-change 事件。 文件图标、元信息、成功状态和原有删除事件必须保留:

<el-upload
  v-if="uploadedFiles.length === 0"
  class="upload-empty-state"
  drag
  multiple
  :accept="uploadAccept"
  :auto-upload="false"
  :show-file-list="false"
  :on-change="(file: UploadFile) => emit('file-change', file)"
>
  <!-- 保留现有大尺寸上传引导和格式提示 -->
</el-upload>

<div v-else class="uploaded-file-list" aria-label="已上传文件列表">
  <div class="uploaded-file-list-header">
    <span>已添加 {{ uploadedFiles.length }} 个文件</span>
    <el-upload
      class="compact-upload"
      multiple
      :accept="uploadAccept"
      :auto-upload="false"
      :show-file-list="false"
      :on-change="(file: UploadFile) => emit('file-change', file)"
    >
      <el-button size="small" type="primary">继续上传</el-button>
    </el-upload>
  </div>
  <div class="uploaded-file-items">
    <div v-for="file in uploadedFiles" :key="file.uid" class="uploaded-file">
      <span class="file-icon"><i class="fa fa-file-text-o" /></span>
      <div class="file-main">
        <strong :title="file.name">{{ file.name }}</strong>
        <span>{{ formatSize(file.size) }}<template v-if="file.count"> · {{ file.count.toLocaleString() }} 条</template></span>
      </div>
      <span class="file-status"><i class="fa fa-check-circle" /> 校验通过</span>
      <el-button link type="danger" @click="emit('remove-file', file.uid)">删除</el-button>
    </div>
  </div>
</div>
  • Step 4: 将现有大卡片样式改为紧凑行和内部滚动

TaskSetupStep.vue 中替换现有 .uploaded-file-list.uploaded-file 相关样式, 使容器、标题、滚动区和行高满足以下实现:

.uploaded-file-list {
  margin-top: 20px;
  overflow: hidden;
  background: #fff;
  border: 1px solid #dfe3ea;
  border-radius: 8px;
}

.uploaded-file-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  color: #5f6878;
  font-size: 12px;
  background: #fbfcfe;
  border-bottom: 1px solid #edf0f5;
}

.uploaded-file-items {
  max-height: 240px;
  overflow-y: auto;
}

.compact-upload {
  flex: 0 0 auto;
}

.uploaded-file {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 8px 14px;
  border-bottom: 1px solid #edf0f5;

  &:last-child {
    border-bottom: 0;
  }
}

.file-icon {
  width: 28px;
  height: 28px;
  font-size: 14px;
  border-radius: 7px;
}

.file-main strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

保留 .file-main 的弹性填充与 .file-status 的成功色;为窄屏媒体查询加入更小的 行间距和可换行的状态文本,保证删除按钮不被文件名挤出。

  • Step 5: 运行回归脚本并执行类型检查

Run: npm --prefix frontend run test:data-process-wizard && npm --prefix frontend run type-check

Expected: 两个命令均以退出码 0 完成,前者输出 数据处理四步向导回归检查通过

  • Step 6: 构建生产包,检查样式与模板编译

Run: npm --prefix frontend run build

Expected: 退出码 0Vite 输出生产构建产物信息,无 Vue 模板或 SCSS 编译错误。

  • Step 7: 检查变更范围

Run: git diff -- frontend/src/views/data-process/create/TaskSetupStep.vue frontend/scripts/regression-data-process-wizard.mjs

Expected: 当前目录不是 Git 仓库时,该命令会报告仓库缺失;改用 diff -u <(git show HEAD:...) ... 不可用,因此使用 sed 复查两个文件的目标区段, 确认没有修改上传、格式限制或父组件事件。