feat(editor): 数据源与代码块历史记录不再合并相邻操作

每条操作独立展示,与页面历史的合并策略区分开。
This commit is contained in:
roymondchen 2026-06-11 17:25:54 +08:00
parent 273d13dd1f
commit 89cef4e9a9
4 changed files with 25 additions and 44 deletions

View File

@ -100,8 +100,8 @@
/** /**
* 历史记录面板在顶部 NavMenu 上点击图标打开 popover分三个 tab * 历史记录面板在顶部 NavMenu 上点击图标打开 popover分三个 tab
* - 页面当前活动页面的历史栈连续修改同一节点的多步会被合并成一组 * - 页面当前活动页面的历史栈连续修改同一节点的多步会被合并成一组
* - 数据源 dataSource.id 每组内部相邻的连续 update 自动合并 * - 数据源 dataSource.id 每条操作记录独立展示
* - 代码块同上 codeBlock.id 组并合并相邻 update * - 代码块同上 codeBlock.id 每条操作记录独立展示
* *
* 数据通过 historyService 暴露的聚合 API 读取UI 仅用于只读展示 * 数据通过 historyService 暴露的聚合 API 读取UI 仅用于只读展示
* 同时支持点击任意一条记录跳转至该状态 * 同时支持点击任意一条记录跳转至该状态

View File

@ -526,11 +526,8 @@ class History extends BaseService {
} }
/** /**
* codeBlockId * codeBlockId
* opType id group * update
* - "代码块/数据源各自按 id 分栈""连续修改同目标的相邻步骤合并展示"
* - group UI changeRecords
* - applied
*/ */
public getCodeBlockHistoryGroups(): CodeBlockHistoryGroup[] { public getCodeBlockHistoryGroups(): CodeBlockHistoryGroup[] {
const groups: CodeBlockHistoryGroup[] = []; const groups: CodeBlockHistoryGroup[] = [];
@ -622,7 +619,7 @@ class History extends BaseService {
} }
/** /**
* dataSourceId * dataSourceId
*/ */
public getDataSourceHistoryGroups(): DataSourceHistoryGroup[] { public getDataSourceHistoryGroups(): DataSourceHistoryGroup[] {
const groups: DataSourceHistoryGroup[] = []; const groups: DataSourceHistoryGroup[] = [];

View File

@ -121,8 +121,7 @@ export const markStackSaved = <S extends { saved?: boolean }>(undoRedo?: UndoRed
/** /**
* id / group * id / group
* - "新增/删除" update * update
* - 'update' steps
* *
* `kind` `kind` * `kind` `kind`
*/ */
@ -139,38 +138,19 @@ export const mergeStackSteps = <S extends BaseStepValue, K extends 'code-block'
applied: boolean; applied: boolean;
isCurrent?: boolean; isCurrent?: boolean;
}[] => { }[] => {
type Group = {
kind: K;
id: Id;
opType: HistoryOpType;
steps: { step: S; index: number; applied: boolean; isCurrent?: boolean }[];
applied: boolean;
isCurrent?: boolean;
};
const groups: Group[] = [];
let current: Group | null = null;
const currentIndex = cursor - 1; const currentIndex = cursor - 1;
list.forEach((step, index) => { return list.map((step, index) => {
const { opType } = step;
const applied = index < cursor; const applied = index < cursor;
const isCurrent = index === currentIndex; const isCurrent = index === currentIndex;
if (opType === 'update' && current?.opType === 'update') { return {
current.steps.push({ step, index, applied, isCurrent }); kind,
current.applied = applied; id,
if (isCurrent) current.isCurrent = true; opType: step.opType,
} else { steps: [{ step, index, applied, isCurrent }],
current = { applied,
kind, isCurrent,
id, };
opType,
steps: [{ step, index, applied, isCurrent }],
applied,
isCurrent,
};
groups.push(current);
}
}); });
return groups;
}; };
/** /**

View File

@ -122,18 +122,20 @@ describe('markStackSaved', () => {
}); });
describe('mergeStackSteps', () => { describe('mergeStackSteps', () => {
test('连续 update 合并为一组', () => { test('连续 update 各自独立成组', () => {
const list = [ const list = [
{ opType: 'update', uuid: '1' }, { opType: 'update', uuid: '1' },
{ opType: 'update', uuid: '2' }, { opType: 'update', uuid: '2' },
] as CodeBlockStepValue[]; ] as CodeBlockStepValue[];
const groups = mergeStackSteps('code-block', 'code_1', list, 2); const groups = mergeStackSteps('code-block', 'code_1', list, 2);
expect(groups).toHaveLength(1); expect(groups).toHaveLength(2);
expect(groups[0].steps).toHaveLength(2); expect(groups[0].steps).toHaveLength(1);
expect(groups[1].steps).toHaveLength(1);
expect(groups[0].opType).toBe('update'); expect(groups[0].opType).toBe('update');
expect(groups[1].opType).toBe('update');
}); });
test('add / update 不合并', () => { test('add / update 各自独立成组', () => {
const list = [ const list = [
{ opType: 'add', uuid: '1' }, { opType: 'add', uuid: '1' },
{ opType: 'update', uuid: '2' }, { opType: 'update', uuid: '2' },
@ -148,10 +150,12 @@ describe('mergeStackSteps', () => {
{ opType: 'update', uuid: '2' }, { opType: 'update', uuid: '2' },
] as CodeBlockStepValue[]; ] as CodeBlockStepValue[];
const groups = mergeStackSteps('code-block', 'code_1', list, 1); const groups = mergeStackSteps('code-block', 'code_1', list, 1);
expect(groups[0].applied).toBe(false); expect(groups[0].applied).toBe(true);
expect(groups[0].steps[0].applied).toBe(true); expect(groups[0].steps[0].applied).toBe(true);
expect(groups[0].steps[0].isCurrent).toBe(true); expect(groups[0].steps[0].isCurrent).toBe(true);
expect(groups[0].steps[1].applied).toBe(false); expect(groups[0].isCurrent).toBe(true);
expect(groups[1].applied).toBe(false);
expect(groups[1].steps[0].applied).toBe(false);
}); });
}); });