mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-07-01 13:58:46 +08:00
feat(editor): 历史记录支持展示操作人
在历史列表分组头部和子步骤中展示 operator 信息,并补充步骤类型字段以承载操作人与扩展数据,同时收敛相关类型定义与插件方法声明,提升历史记录渲染与扩展能力。
This commit is contained in:
parent
9f2fa1a9c8
commit
1ade61d62e
@ -49,6 +49,13 @@
|
||||
>{{ sourceLabel(group.source) }}</span
|
||||
>
|
||||
|
||||
<span
|
||||
v-if="!merged && group.operator"
|
||||
class="m-editor-history-list-item-operator"
|
||||
:title="`操作人:${group.operator}`"
|
||||
>{{ group.operator }}</span
|
||||
>
|
||||
|
||||
<span
|
||||
v-if="!merged && group.time"
|
||||
class="m-editor-history-list-item-time"
|
||||
@ -103,6 +110,9 @@
|
||||
:title="`操作途径:${sourceLabel(s.source)}`"
|
||||
>{{ sourceLabel(s.source) }}</span
|
||||
>
|
||||
<span v-if="s.operator" class="m-editor-history-list-item-operator" :title="`操作人:${s.operator}`">{{
|
||||
s.operator
|
||||
}}</span>
|
||||
<span v-if="s.time" class="m-editor-history-list-item-time" :title="s.timeTitle || s.time">{{ s.time }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -25,51 +25,49 @@ export interface HistoryBucketGroup<T extends BaseStepValue = BaseStepValue> {
|
||||
steps: { index: number; applied: boolean; isCurrent?: boolean; step: T }[];
|
||||
}
|
||||
|
||||
/** GroupRow 渲染所需的单个子步视图模型(已由 {@link toRowGroup} 预先派生,组件内部不再触碰原始 step)。 */
|
||||
export interface HistoryRowStep {
|
||||
/** 该子步在所属栈中的稳定索引。 */
|
||||
index: number;
|
||||
/**
|
||||
* GroupRow 的组头部与子步共用的展示字段(均由 {@link toRowGroup} 预先派生)。
|
||||
* 抽出公共部分避免 {@link HistoryRowStep} / {@link HistoryRowGroup} 重复声明,
|
||||
* 也便于消费方用本类型收窄「组头 / 子步」通用渲染逻辑的入参。
|
||||
*/
|
||||
export interface HistoryRowDisplay {
|
||||
/** 是否已应用(false 表示已被 undo,UI 灰态)。 */
|
||||
applied: boolean;
|
||||
/** 是否为当前所在步骤。 */
|
||||
isCurrent?: boolean;
|
||||
/** 是否为最近一次保存的记录。 */
|
||||
saved?: boolean;
|
||||
/** 子步描述文案。 */
|
||||
/** 是否为当前所在步骤 / 分组。 */
|
||||
isCurrent: boolean;
|
||||
/** 描述文案。 */
|
||||
desc: string;
|
||||
/** 是否可查看差异。 */
|
||||
diffable?: boolean;
|
||||
/** 是否可回滚。 */
|
||||
revertable?: boolean;
|
||||
/** 操作途径。 */
|
||||
source?: HistoryOpSource;
|
||||
/** 操作人。 */
|
||||
operator?: string;
|
||||
/** 时间文案。 */
|
||||
time?: string;
|
||||
/** 时间的完整 title 提示。 */
|
||||
timeTitle?: string;
|
||||
}
|
||||
|
||||
/** GroupRow 渲染所需的单个子步视图模型(已由 {@link toRowGroup} 预先派生,组件内部不再触碰原始 step)。 */
|
||||
export interface HistoryRowStep extends HistoryRowDisplay {
|
||||
/** 该子步在所属栈中的稳定索引。 */
|
||||
index: number;
|
||||
/** 是否为最近一次保存的记录。 */
|
||||
saved?: boolean;
|
||||
/** 是否可查看差异。 */
|
||||
diffable?: boolean;
|
||||
/** 是否可回滚。 */
|
||||
revertable?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* GroupRow 渲染所需的整组视图模型(由 {@link toRowGroup} 统一派生)。
|
||||
* 把原先 GroupRow 上十多个扁平 props 收敛为单一对象,header 信息与子步列表一并携带。
|
||||
*/
|
||||
export interface HistoryRowGroup {
|
||||
export interface HistoryRowGroup extends HistoryRowDisplay {
|
||||
/** 分组的稳定 key,作为 toggle 事件 payload 与折叠状态的索引。 */
|
||||
key: string;
|
||||
/** 组内最后一步是否已应用。 */
|
||||
applied: boolean;
|
||||
/** 是否为当前所在分组。 */
|
||||
isCurrent: boolean;
|
||||
/** 操作类型,用于徽标颜色与文案。 */
|
||||
opType: HistoryOpType;
|
||||
/** 组整体描述文案。 */
|
||||
desc: string;
|
||||
/** 组的操作途径(取组内最近一步)。 */
|
||||
source?: HistoryOpSource;
|
||||
/** 组头部时间文案(取组内最近一步)。 */
|
||||
time?: string;
|
||||
/** 组头部时间的完整 title 提示。 */
|
||||
timeTitle?: string;
|
||||
/** 子步列表(时间正序);其长度即合并步数,length > 1 即为合并组。 */
|
||||
subSteps: HistoryRowStep[];
|
||||
}
|
||||
@ -144,6 +142,10 @@ export const sourceLabel = (source: HistoryOpSource = 'unknown'): string => {
|
||||
export const groupSource = (group: { steps: { step: { source?: HistoryOpSource } }[] }): HistoryOpSource | undefined =>
|
||||
group.steps[group.steps.length - 1]?.step.source;
|
||||
|
||||
/** 取一组历史步骤里最后一步(最近一次)的操作人,用于组头部展示。 */
|
||||
export const groupOperator = (group: { steps: { step: { operator?: string } }[] }): string | undefined =>
|
||||
group.steps[group.steps.length - 1]?.step.operator;
|
||||
|
||||
/** {@link toRowGroup} 接受的最小分组结构,PageHistoryGroup 与 HistoryBucketGroup 均满足。 */
|
||||
interface RowGroupInput<T extends BaseStepValue = BaseStepValue> {
|
||||
applied: boolean;
|
||||
@ -173,17 +175,19 @@ export const toRowGroup = <T extends BaseStepValue = BaseStepValue>(
|
||||
opType: group.opType,
|
||||
desc: describeGroup ? describeGroup(group) : describeStep(lastStep),
|
||||
source: groupSource(group),
|
||||
operator: groupOperator(group),
|
||||
time: formatHistoryTime(timestamp),
|
||||
timeTitle: formatHistoryFullTime(timestamp),
|
||||
subSteps: group.steps.map((s) => ({
|
||||
index: s.index,
|
||||
applied: s.applied,
|
||||
isCurrent: s.isCurrent,
|
||||
isCurrent: Boolean(s.isCurrent),
|
||||
saved: s.step.saved,
|
||||
desc: describeStep(s.step),
|
||||
diffable: isStepDiffable ? isStepDiffable(s.step) : false,
|
||||
revertable: s.applied && (isStepRevertable ? isStepRevertable(s.step) : true),
|
||||
source: s.step.source,
|
||||
operator: s.step.operator,
|
||||
time: formatHistoryTime(s.step.timestamp),
|
||||
timeTitle: formatHistoryFullTime(s.step.timestamp),
|
||||
})),
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import type { Writable } from 'type-fest';
|
||||
|
||||
import type { CodeBlockContent, DataSourceSchema, Id, MPage, MPageFragment } from '@tmagic/core';
|
||||
import type { ChangeRecord } from '@tmagic/form';
|
||||
@ -33,6 +34,7 @@ import type {
|
||||
PersistedHistoryState,
|
||||
StackHistoryGroup,
|
||||
StepValue,
|
||||
SyncHookPlugin,
|
||||
} from '@editor/type';
|
||||
import { getEditorConfig } from '@editor/utils/config';
|
||||
import {
|
||||
@ -51,6 +53,22 @@ import { UndoRedo } from '@editor/utils/undo-redo';
|
||||
import BaseService from './BaseService';
|
||||
import editorService from './editor';
|
||||
|
||||
const canUsePluginMethods = {
|
||||
sync: [
|
||||
'push',
|
||||
'pushCodeBlock',
|
||||
'pushDataSource',
|
||||
'undoCodeBlock',
|
||||
'redoCodeBlock',
|
||||
'undoDataSource',
|
||||
'redoDataSource',
|
||||
'undo',
|
||||
'redo',
|
||||
] as const,
|
||||
};
|
||||
|
||||
type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
|
||||
|
||||
/** 历史记录持久化快照的默认存储位置与结构版本。 */
|
||||
const DEFAULT_DB_NAME = 'tmagic-editor';
|
||||
const DEFAULT_STORE_NAME = 'history';
|
||||
@ -69,7 +87,7 @@ class History extends BaseService {
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super([]);
|
||||
super([...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false }))]);
|
||||
|
||||
this.on('change', this.setCanUndoRedo);
|
||||
}
|
||||
@ -630,6 +648,10 @@ class History extends BaseService {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public usePlugin(options: SyncHookPlugin<SyncMethodName, History>): void {
|
||||
super.usePlugin(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取出指定页面的栈;不传 pageId 时按当前活动页取。
|
||||
*
|
||||
|
||||
@ -330,6 +330,20 @@
|
||||
font-weight: 400; // 防止被合并组头部的粗体继承
|
||||
}
|
||||
|
||||
// 「操作人」徽标:浅蓝描边胶囊,弱化展示操作人,仅在 step.operator 有值时渲染。
|
||||
.m-editor-history-list-item-operator {
|
||||
flex: 0 0 auto;
|
||||
padding: 0 6px;
|
||||
border: 1px solid #c6e2ff;
|
||||
border-radius: 8px;
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
white-space: nowrap;
|
||||
font-weight: 400; // 防止被合并组头部的粗体继承
|
||||
}
|
||||
|
||||
// 「已保存」徽标:绿色实心胶囊,标记最近一次保存对应的历史记录(与 historyService.markSaved 对应)。
|
||||
.m-editor-history-list-item-saved {
|
||||
flex: 0 0 auto;
|
||||
|
||||
@ -820,6 +820,10 @@ export interface BaseStepValue<T = unknown> {
|
||||
* 避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录。
|
||||
*/
|
||||
rootStep?: boolean;
|
||||
/** 操作人 */
|
||||
operator?: string;
|
||||
/** 扩展信息 */
|
||||
extra?: Record<string, any>;
|
||||
}
|
||||
// #endregion BaseStepValue
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ const makeGroup = (overrides: Partial<HistoryRowGroup> = {}): HistoryRowGroup =>
|
||||
/** 构造单个子步,缺省值贴近真实派生结果。 */
|
||||
const makeStep = (overrides: Partial<HistoryRowStep> & Pick<HistoryRowStep, 'index'>): HistoryRowStep => ({
|
||||
applied: true,
|
||||
isCurrent: false,
|
||||
desc: '',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user