103 lines
3.5 KiB
Markdown
103 lines
3.5 KiB
Markdown
# 实现方案 — 2026-04-16-17-07-04
|
||
|
||
## 技术思路
|
||
在 `ReportEditor.tsx` 中为关键帧卡片新增一个 **"插入"** 按钮,点击后自动将当前帧图片填充到编辑器中第一个空置的 `.image-placeholder` 中。
|
||
|
||
为保持代码整洁,将现有 `handleDrop` 中的占位符填充逻辑抽取为可复用的 `fillPlaceholder` 函数,供拖拽放下和按钮点击共同调用。
|
||
|
||
---
|
||
|
||
## 修改文件清单
|
||
|
||
| 文件 | 变更类型 | 说明 |
|
||
|------|----------|------|
|
||
| `src/pages/ReportEditor.tsx` | 修改 | 新增 `fillPlaceholder` 函数、新增 `insertFrameToPlaceholder` 函数、修改关键帧卡片 JSX |
|
||
|
||
---
|
||
|
||
## 关键代码变更说明
|
||
|
||
### 1. 抽取公共填充函数 `fillPlaceholder`
|
||
|
||
将 `handleDrop` 中的图片填充逻辑抽离:
|
||
|
||
```tsx
|
||
const fillPlaceholder = (placeholder: HTMLElement, frame: CapturedFrame) => {
|
||
placeholder.innerHTML = `
|
||
<span class="delete-btn" contenteditable="false">×</span>
|
||
<img src="${frame.dataUrl}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" draggable="false">
|
||
`;
|
||
placeholder.classList.add('has-image');
|
||
if (editorRef.current) contentRef.current = editorRef.current.innerHTML;
|
||
saveDraftToStorage();
|
||
};
|
||
```
|
||
|
||
并同步简化 `handleDrop`:
|
||
|
||
```tsx
|
||
const handleDrop = (e: React.DragEvent, placeholder: HTMLElement) => {
|
||
e.preventDefault();
|
||
const frameId = e.dataTransfer.getData('frameId');
|
||
const frame = capturedFrames.find(f => f.id.toString() === frameId);
|
||
if (frame) {
|
||
fillPlaceholder(placeholder, frame);
|
||
}
|
||
};
|
||
```
|
||
|
||
### 2. 新增一键插入函数 `insertFrameToPlaceholder`
|
||
|
||
```tsx
|
||
const insertFrameToPlaceholder = (frame: CapturedFrame) => {
|
||
if (!editorRef.current) {
|
||
alert('编辑器未准备好');
|
||
return;
|
||
}
|
||
const emptyPlaceholder = editorRef.current.querySelector('.image-placeholder:not(.has-image)') as HTMLElement | null;
|
||
if (!emptyPlaceholder) {
|
||
alert('没有可插入图片的空位');
|
||
return;
|
||
}
|
||
fillPlaceholder(emptyPlaceholder, frame);
|
||
};
|
||
```
|
||
|
||
### 3. 在关键帧卡片底部添加 "插入" 按钮
|
||
|
||
修改现有 JSX(约第 1303 行附近),在 `timeFormatted` 与 "可拖拽" 之间插入按钮:
|
||
|
||
```tsx
|
||
<div className="text-[9px] font-bold text-text-muted mt-1.5 px-1 flex justify-between items-center">
|
||
<span>{frame.timeFormatted}</span>
|
||
<div className="flex items-center gap-2">
|
||
<button
|
||
onClick={(e) => { e.stopPropagation(); insertFrameToPlaceholder(frame); }}
|
||
className="text-accent opacity-0 group-hover:opacity-100 transition-opacity hover:underline"
|
||
>
|
||
插入
|
||
</button>
|
||
<span className="text-accent opacity-0 group-hover:opacity-100 transition-opacity">可拖拽</span>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
按钮使用 `opacity-0 group-hover:opacity-100 transition-opacity`,与 "可拖拽" 显隐行为完全一致。`e.stopPropagation()` 避免触发卡片的 `onClick`(即跳转到视频位置)。
|
||
|
||
---
|
||
|
||
## 风险点及应对策略
|
||
|
||
| 风险 | 影响 | 应对策略 |
|
||
|------|------|----------|
|
||
| `editorRef.current` 为空时点击插入 | JS 报错 | 函数开头增加判空并 alert 提示 |
|
||
| 没有空占位符时点击插入 | 用户困惑 | 未找到 `.image-placeholder:not(.has-image)` 时弹出友好提示 |
|
||
| 按钮点击触发卡片 onClick | 视频意外跳转 | 使用 `e.stopPropagation()` 阻止冒泡 |
|
||
| `handleDrop` 抽离后功能回退 | 拖拽失效 | 保持 `handleDrop` 调用 `fillPlaceholder`,逻辑与原来一致 |
|
||
|
||
---
|
||
|
||
## 改动范围总结
|
||
- 仅修改 `src/pages/ReportEditor.tsx`,不触及其他文件。
|
||
- 不引入新依赖。
|