@bireturn/word-editor 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Editor/useDocRenderer.d.ts +15 -0
- package/lib/components/VirtualTable/index.d.ts +19 -0
- package/lib/components/VirtualTable/src/context-menu.d.ts +16 -0
- package/lib/components/VirtualTable/src/tool.d.ts +15 -0
- package/lib/composables/useDocumentData.d.ts +10 -0
- package/lib/composables/useEditor.d.ts +5 -0
- package/lib/core/command/CommandManager.d.ts +16 -0
- package/lib/core/command/commands/DeleteCommand.d.ts +14 -0
- package/lib/core/command/commands/FormatCommand.d.ts +30 -0
- package/lib/core/command/commands/InsertParagraphCommand.d.ts +12 -0
- package/lib/core/command/commands/InsertTextCommand.d.ts +18 -0
- package/lib/core/editor/Editor.d.ts +46 -0
- package/lib/core/editor/EditorOptions.d.ts +1 -0
- package/lib/core/history/HistoryManager.d.ts +15 -0
- package/lib/core/input/InputHandler.d.ts +20 -0
- package/lib/core/model/DocumentModel.d.ts +38 -0
- package/lib/core/selection/SelectionManager.d.ts +26 -0
- package/lib/index.d.ts +10 -0
- package/lib/types/command.d.ts +57 -0
- package/lib/types/document.d.ts +111 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/plugin.d.ts +12 -0
- package/lib/types/selection.d.ts +17 -0
- package/lib/utils/catalog.d.ts +29 -0
- package/lib/utils/nanoid.d.ts +1 -0
- package/lib/utils/titleIndex.d.ts +11 -0
- package/lib/word-editor.css +1 -0
- package/lib/word-editor.es.js +2978 -0
- package/lib/word-editor.umd.js +2 -0
- package/package.json +2 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const DEFAULT_FONT_SIZE = 14;
|
|
2
|
+
export declare const PAGE_CONTENT_HEIGHT = 1123;
|
|
3
|
+
export declare const PAGE_CONTENT_WIDTH = 760;
|
|
4
|
+
export declare const TABLE_USABLE_WIDTH = 756;
|
|
5
|
+
export declare const ROW_FLEX: Record<string, string>;
|
|
6
|
+
export declare function flattenAreas(els: any[]): any[];
|
|
7
|
+
export declare function groupBlocks(rawEls: any[]): any[];
|
|
8
|
+
export declare function estimateParaHeight(elements: any[]): number;
|
|
9
|
+
export declare function splitParagraphRuns(elements: any[]): any[][];
|
|
10
|
+
export declare function buildRowGroups(trList: any[]): any[][];
|
|
11
|
+
export declare function getTableScale(tableEl: any): number;
|
|
12
|
+
export declare function getParaStyle(block: any): Record<string, string>;
|
|
13
|
+
export declare function getCellVAlign(td: any): string;
|
|
14
|
+
export declare function getInlineStyle(el: any): Record<string, string>;
|
|
15
|
+
export declare function calcBlocksHeight(blocks: any[], isHeaderFooter?: boolean): number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as VirtualTable } from './src/VirtualTable.vue';
|
|
2
|
+
import { default as VirtualTableColumn } from './src/VirtualTableColumn.vue';
|
|
3
|
+
export { VirtualTable, VirtualTableColumn };
|
|
4
|
+
declare const _default: {
|
|
5
|
+
VirtualTable: any;
|
|
6
|
+
VirtualTableColumn: any;
|
|
7
|
+
};
|
|
8
|
+
export default _default;
|
|
9
|
+
export interface VirtualTableInstance {
|
|
10
|
+
recalculateAllHeights: () => void;
|
|
11
|
+
forceItemUpdate: (item: any) => void;
|
|
12
|
+
isEditByRow: (row: any) => boolean;
|
|
13
|
+
getParentRow: (row: any) => any | null;
|
|
14
|
+
getRowById: (id: string | number) => any | null;
|
|
15
|
+
scrollToRow: (row: any) => void;
|
|
16
|
+
setCurrentRow: (row: any) => void;
|
|
17
|
+
setAllTreeExpand: (expand: boolean) => void;
|
|
18
|
+
setTreeExpand: (row: any, expand: boolean) => void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建组件实例对象
|
|
3
|
+
* 返回的实例和调用 getCurrentComponent() 返回的一致
|
|
4
|
+
* @param {*} Component
|
|
5
|
+
*/
|
|
6
|
+
export declare function createComponent(Component: any, props: any, children: any): import('vue').ComponentInternalInstance | null;
|
|
7
|
+
/**
|
|
8
|
+
* 销毁组件实例对象
|
|
9
|
+
* @param {*} ComponnetInstance 通过createComponent方法得到的组件实例对象
|
|
10
|
+
*/
|
|
11
|
+
export declare function unmountComponent(ComponnetInstance: any): void;
|
|
12
|
+
export declare const ContextMenu: (options: any) => any;
|
|
13
|
+
declare const _default: {
|
|
14
|
+
ContextMenu: (options: any) => any;
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将树形结构数据按主子关系排序
|
|
3
|
+
* @param {Array} list - 原始数据列表,每个元素应包含 node_id 和 parent_id
|
|
4
|
+
* @param {string|number|null} [rootParentId] - 可选参数,指定根节点的 parent_id 值
|
|
5
|
+
* @return {Array} - 排序后的数组,保持树形结构顺序
|
|
6
|
+
*/
|
|
7
|
+
export declare function sortTreeList(list: any, rootParentId: any, treeConfig?: any): Array<any>;
|
|
8
|
+
/**
|
|
9
|
+
* 获取节点的父节点,直到找到具有className的节点,在container容器内查找
|
|
10
|
+
* @param target
|
|
11
|
+
* @param className
|
|
12
|
+
* @param container
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export declare function getParentNode(target: any, className: any, container: any): any;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function useDocumentData(): {
|
|
2
|
+
docData: import('..').DocumentModel;
|
|
3
|
+
flatParagraphs: import('vue').ComputedRef<{
|
|
4
|
+
id: string;
|
|
5
|
+
paragraph: import('..').Paragraph;
|
|
6
|
+
sectionIndex: number;
|
|
7
|
+
paragraphIndex: number;
|
|
8
|
+
}[]>;
|
|
9
|
+
sections: import('..').Section[];
|
|
10
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { InjectionKey } from 'vue';
|
|
2
|
+
import { Editor, EditorOptions } from '../core/editor/Editor';
|
|
3
|
+
export declare const EditorKey: InjectionKey<Editor>;
|
|
4
|
+
export declare function createEditor(options?: EditorOptions): Editor;
|
|
5
|
+
export declare function useEditor(): Editor;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CommandName, CommandPayloadMap, ICommand } from '../../types/command';
|
|
2
|
+
import { DocumentModel } from '../model/DocumentModel';
|
|
3
|
+
import { SelectionManager } from '../selection/SelectionManager';
|
|
4
|
+
import { HistoryManager } from '../history/HistoryManager';
|
|
5
|
+
type CommandFactory<N extends CommandName> = (payload: CommandPayloadMap[N]) => ICommand | null;
|
|
6
|
+
export declare class CommandManager {
|
|
7
|
+
private docModel;
|
|
8
|
+
private selectionManager;
|
|
9
|
+
private historyManager;
|
|
10
|
+
private _factories;
|
|
11
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager, historyManager: HistoryManager);
|
|
12
|
+
private _registerBuiltinCommands;
|
|
13
|
+
register<N extends CommandName>(name: N, factory: CommandFactory<N>): void;
|
|
14
|
+
exec<N extends CommandName>(name: N, payload?: CommandPayloadMap[N]): void;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ICommand } from '../../../types/command';
|
|
2
|
+
import { DocumentModel } from '../../model/DocumentModel';
|
|
3
|
+
import { SelectionManager } from '../../selection/SelectionManager';
|
|
4
|
+
type DeleteDirection = 'backward' | 'forward';
|
|
5
|
+
export declare class DeleteCommand implements ICommand {
|
|
6
|
+
private docModel;
|
|
7
|
+
private selectionManager;
|
|
8
|
+
readonly name: 'deleteBackward' | 'deleteForward';
|
|
9
|
+
private _deletedText;
|
|
10
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager, direction: DeleteDirection);
|
|
11
|
+
execute(): void;
|
|
12
|
+
undo(): void;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ICommand, CommandName } from '../../../types/command';
|
|
2
|
+
import { DocumentModel } from '../../model/DocumentModel';
|
|
3
|
+
import { SelectionManager } from '../../selection/SelectionManager';
|
|
4
|
+
import { ParagraphFormat } from '../../../types/document';
|
|
5
|
+
type FormatCommandName = Extract<CommandName, 'bold' | 'italic' | 'underline' | 'strikethrough' | 'superscript' | 'subscript' | 'fontSize' | 'fontFamily' | 'color' | 'backgroundColor' | 'textAlign' | 'lineHeight' | 'heading'>;
|
|
6
|
+
export declare class FormatCommand implements ICommand {
|
|
7
|
+
private docModel;
|
|
8
|
+
private selectionManager;
|
|
9
|
+
private _value?;
|
|
10
|
+
readonly name: FormatCommandName;
|
|
11
|
+
private _prevFormat;
|
|
12
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager, name: FormatCommandName, _value?: unknown | undefined);
|
|
13
|
+
execute(): void;
|
|
14
|
+
undo(): void;
|
|
15
|
+
private _isToggle;
|
|
16
|
+
private _getFormatKey;
|
|
17
|
+
private _getCurrentValue;
|
|
18
|
+
}
|
|
19
|
+
/** 段落格式命令 */
|
|
20
|
+
export declare class ParagraphFormatCommand implements ICommand {
|
|
21
|
+
private docModel;
|
|
22
|
+
private selectionManager;
|
|
23
|
+
private _formatKey;
|
|
24
|
+
private _value;
|
|
25
|
+
readonly name: CommandName;
|
|
26
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager, _formatKey: keyof ParagraphFormat, _value: unknown);
|
|
27
|
+
execute(): void;
|
|
28
|
+
undo(): void;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ICommand } from '../../../types/command';
|
|
2
|
+
import { DocumentModel } from '../../model/DocumentModel';
|
|
3
|
+
import { SelectionManager } from '../../selection/SelectionManager';
|
|
4
|
+
export declare class InsertParagraphCommand implements ICommand {
|
|
5
|
+
private docModel;
|
|
6
|
+
private selectionManager;
|
|
7
|
+
readonly name: "insertParagraph";
|
|
8
|
+
private _insertedParagraphId;
|
|
9
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager);
|
|
10
|
+
execute(): void;
|
|
11
|
+
undo(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ICommand } from '../../../types/command';
|
|
2
|
+
import { DocumentModel } from '../../model/DocumentModel';
|
|
3
|
+
import { SelectionManager } from '../../selection/SelectionManager';
|
|
4
|
+
import { ModelPoint } from '../../../types/selection';
|
|
5
|
+
import { ElementFormat } from '../../../types/document';
|
|
6
|
+
export declare class InsertTextCommand implements ICommand {
|
|
7
|
+
private docModel;
|
|
8
|
+
private selectionManager;
|
|
9
|
+
readonly name: "insertText";
|
|
10
|
+
private _point;
|
|
11
|
+
private _text;
|
|
12
|
+
private _format;
|
|
13
|
+
constructor(docModel: DocumentModel, selectionManager: SelectionManager, point: ModelPoint, text: string, format?: ElementFormat);
|
|
14
|
+
execute(): void;
|
|
15
|
+
undo(): void;
|
|
16
|
+
canMergeWith(other: ICommand): boolean;
|
|
17
|
+
merge(other: ICommand): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DocumentModel } from '../model/DocumentModel';
|
|
2
|
+
import { SelectionManager } from '../selection/SelectionManager';
|
|
3
|
+
import { HistoryManager } from '../history/HistoryManager';
|
|
4
|
+
import { CommandManager } from '../command/CommandManager';
|
|
5
|
+
import { InputHandler } from '../input/InputHandler';
|
|
6
|
+
import { CommandName, CommandPayloadMap } from '../../types/command';
|
|
7
|
+
import { IPlugin } from '../../types/plugin';
|
|
8
|
+
import { DocumentModel as IDocumentModel } from '../../types/document';
|
|
9
|
+
export interface EditorOptions {
|
|
10
|
+
/** 初始文档数据 */
|
|
11
|
+
initialDocument?: Partial<IDocumentModel>;
|
|
12
|
+
/** 是否只读 */
|
|
13
|
+
readonly?: boolean;
|
|
14
|
+
/** 插件列表 */
|
|
15
|
+
plugins?: IPlugin[];
|
|
16
|
+
}
|
|
17
|
+
export declare class Editor {
|
|
18
|
+
readonly docModel: DocumentModel;
|
|
19
|
+
readonly selection: SelectionManager;
|
|
20
|
+
readonly history: HistoryManager;
|
|
21
|
+
readonly commands: CommandManager;
|
|
22
|
+
readonly input: InputHandler;
|
|
23
|
+
private _readonly;
|
|
24
|
+
private _plugins;
|
|
25
|
+
private _inputEl;
|
|
26
|
+
constructor(options?: EditorOptions);
|
|
27
|
+
/**
|
|
28
|
+
* 执行命令(外部调用入口)
|
|
29
|
+
* @example editor.exec('bold')
|
|
30
|
+
* @example editor.exec('fontSize', 18)
|
|
31
|
+
* @example editor.exec('insertText', 'hello')
|
|
32
|
+
*/
|
|
33
|
+
exec<N extends CommandName>(name: N, payload?: CommandPayloadMap[N]): void;
|
|
34
|
+
/** 挂载输入层到指定 DOM 元素 */
|
|
35
|
+
mount(el: HTMLElement): void;
|
|
36
|
+
/** 卸载输入层 */
|
|
37
|
+
unmount(): void;
|
|
38
|
+
/** 安装插件 */
|
|
39
|
+
use(plugin: IPlugin): this;
|
|
40
|
+
/** 获取序列化后的文档 JSON */
|
|
41
|
+
getJSON(): string;
|
|
42
|
+
/** 从 JSON 加载文档(清空历史) */
|
|
43
|
+
loadJSON(json: string): void;
|
|
44
|
+
get isReadonly(): boolean;
|
|
45
|
+
private _isReadCommand;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { DocumentModel as IDocumentModel } from '../../types/document';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ICommand } from '../../types/command';
|
|
2
|
+
export declare class HistoryManager {
|
|
3
|
+
private _undoStack;
|
|
4
|
+
private _redoStack;
|
|
5
|
+
get canUndo(): boolean;
|
|
6
|
+
get canRedo(): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* 推入命令(执行后调用)
|
|
9
|
+
* 支持合并连续同类命令(如连续输入文字)
|
|
10
|
+
*/
|
|
11
|
+
push(command: ICommand): void;
|
|
12
|
+
undo(): boolean;
|
|
13
|
+
redo(): boolean;
|
|
14
|
+
clear(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommandManager } from '../command/CommandManager';
|
|
2
|
+
import { SelectionManager as ISelectionManager } from '../selection/SelectionManager';
|
|
3
|
+
export declare class InputHandler {
|
|
4
|
+
private _el;
|
|
5
|
+
private _commandManager;
|
|
6
|
+
private _selectionManager;
|
|
7
|
+
private _composing;
|
|
8
|
+
private _boundKeydown;
|
|
9
|
+
private _boundBeforeInput;
|
|
10
|
+
private _boundCompositionStart;
|
|
11
|
+
private _boundCompositionEnd;
|
|
12
|
+
private _boundSelectionChange;
|
|
13
|
+
constructor(commandManager: CommandManager, selectionManager: ISelectionManager);
|
|
14
|
+
attach(el: HTMLElement): void;
|
|
15
|
+
detach(): void;
|
|
16
|
+
private _onKeydown;
|
|
17
|
+
private _onBeforeInput;
|
|
18
|
+
private _onCompositionEnd;
|
|
19
|
+
private _onSelectionChange;
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DocumentModel as IDocumentModel, Paragraph, Section, IElement, TextElement, ElementFormat, ParagraphFormat } from '../../types';
|
|
2
|
+
import { ModelPoint } from '../../types/selection';
|
|
3
|
+
export declare class DocumentModel {
|
|
4
|
+
readonly data: IDocumentModel;
|
|
5
|
+
constructor(initialData?: Partial<IDocumentModel>);
|
|
6
|
+
createEmptySection(): Section;
|
|
7
|
+
createEmptyParagraph(format?: ParagraphFormat): Paragraph;
|
|
8
|
+
createTextElement(value: string, format?: ElementFormat): TextElement;
|
|
9
|
+
getSectionCount(): number;
|
|
10
|
+
getParagraphCount(sectionIndex?: number): number;
|
|
11
|
+
/** 获取所有段落(展开所有 section) */
|
|
12
|
+
getAllParagraphs(): Array<{
|
|
13
|
+
paragraph: Paragraph;
|
|
14
|
+
sectionIndex: number;
|
|
15
|
+
paragraphIndex: number;
|
|
16
|
+
}>;
|
|
17
|
+
getParagraphAt(sectionIndex: number, paragraphIndex: number): Paragraph | null;
|
|
18
|
+
getElementAt(point: ModelPoint): IElement | null;
|
|
19
|
+
/** 在指定位置插入文本(拆分/合并 text element) */
|
|
20
|
+
insertText(point: ModelPoint, text: string, format?: ElementFormat): void;
|
|
21
|
+
/** 删除指定范围内容 */
|
|
22
|
+
deleteAt(point: ModelPoint, length: number): void;
|
|
23
|
+
/** 在段落后插入新段落 */
|
|
24
|
+
insertParagraph(sectionIndex: number, afterIndex: number, format?: ParagraphFormat): Paragraph;
|
|
25
|
+
/** 合并两个相邻段落 */
|
|
26
|
+
mergeParagraphs(sectionIndex: number, upperIndex: number): void;
|
|
27
|
+
/** 更新段落格式 */
|
|
28
|
+
updateParagraphFormat(sectionIndex: number, paragraphIndex: number, format: Partial<ParagraphFormat>): void;
|
|
29
|
+
/** 更新元素格式 */
|
|
30
|
+
updateElementFormat(point: ModelPoint, format: Partial<ElementFormat>): void;
|
|
31
|
+
/** 批量更新选区内元素格式 */
|
|
32
|
+
updateRangeFormat(from: ModelPoint, to: ModelPoint, format: Partial<ElementFormat>): void;
|
|
33
|
+
private bumpVersion;
|
|
34
|
+
/** 序列化为 JSON */
|
|
35
|
+
toJSON(): string;
|
|
36
|
+
/** 从 JSON 恢复 */
|
|
37
|
+
static fromJSON(json: string): DocumentModel;
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ModelPoint, ModelSelection } from '../../types/selection';
|
|
2
|
+
import { DocumentModel } from '../model/DocumentModel';
|
|
3
|
+
export declare class SelectionManager {
|
|
4
|
+
private _selection;
|
|
5
|
+
private _docModel;
|
|
6
|
+
constructor(docModel: DocumentModel);
|
|
7
|
+
get selection(): ModelSelection;
|
|
8
|
+
/** 是否有选区(非光标) */
|
|
9
|
+
readonly hasSelection: import('vue').ComputedRef<boolean>;
|
|
10
|
+
setCursor(point: ModelPoint): void;
|
|
11
|
+
setSelection(anchor: ModelPoint, focus: ModelPoint): void;
|
|
12
|
+
collapse(toFocus?: boolean): void;
|
|
13
|
+
selectAll(): void;
|
|
14
|
+
/** 光标向前移动一个字符 */
|
|
15
|
+
moveForward(extend?: boolean): void;
|
|
16
|
+
/** 光标向后移动一个字符 */
|
|
17
|
+
moveBackward(extend?: boolean): void;
|
|
18
|
+
private _applyMove;
|
|
19
|
+
private _getNextParagraphPoint;
|
|
20
|
+
private _getPrevParagraphPoint;
|
|
21
|
+
/**
|
|
22
|
+
* 从 DOM element 的 data-* 属性解析模型坐标
|
|
23
|
+
* 渲染时每个元素需附带 data-si/data-pi/data-ei 属性
|
|
24
|
+
*/
|
|
25
|
+
static domToModelPoint(domNode: Node, domOffset: number): ModelPoint | null;
|
|
26
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { App, DefineComponent } from 'vue';
|
|
2
|
+
export { Editor } from './core/editor/Editor';
|
|
3
|
+
export type { EditorOptions } from './core/editor/Editor';
|
|
4
|
+
export { createEditor, useEditor, EditorKey } from './composables/useEditor';
|
|
5
|
+
export declare const EditorContainer: DefineComponent<Record<string, unknown>>;
|
|
6
|
+
export type * from './types';
|
|
7
|
+
declare const _default: {
|
|
8
|
+
install(app: App): void;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ElementFormat, ParagraphFormat } from './document';
|
|
2
|
+
/** 所有可执行命令名称 */
|
|
3
|
+
export type CommandName = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'superscript' | 'subscript' | 'fontSize' | 'fontFamily' | 'color' | 'backgroundColor' | 'textAlign' | 'lineHeight' | 'indent' | 'outdent' | 'heading' | 'bulletList' | 'orderedList' | 'insertText' | 'insertParagraph' | 'insertImage' | 'insertTable' | 'insertSeparator' | 'deleteContent' | 'deleteForward' | 'deleteBackward' | 'undo' | 'redo' | 'selectAll' | 'setSelection';
|
|
4
|
+
/** 命令参数映射 */
|
|
5
|
+
export interface CommandPayloadMap {
|
|
6
|
+
bold: undefined;
|
|
7
|
+
italic: undefined;
|
|
8
|
+
underline: undefined;
|
|
9
|
+
strikethrough: undefined;
|
|
10
|
+
superscript: undefined;
|
|
11
|
+
subscript: undefined;
|
|
12
|
+
fontSize: number;
|
|
13
|
+
fontFamily: string;
|
|
14
|
+
color: string;
|
|
15
|
+
backgroundColor: string;
|
|
16
|
+
textAlign: ElementFormat['color'];
|
|
17
|
+
lineHeight: number;
|
|
18
|
+
indent: undefined;
|
|
19
|
+
outdent: undefined;
|
|
20
|
+
heading: ParagraphFormat['heading'];
|
|
21
|
+
bulletList: undefined;
|
|
22
|
+
orderedList: undefined;
|
|
23
|
+
insertText: string;
|
|
24
|
+
insertParagraph: undefined;
|
|
25
|
+
insertImage: {
|
|
26
|
+
url: string;
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
alt?: string;
|
|
30
|
+
};
|
|
31
|
+
insertTable: {
|
|
32
|
+
rows: number;
|
|
33
|
+
cols: number;
|
|
34
|
+
};
|
|
35
|
+
insertSeparator: undefined;
|
|
36
|
+
deleteContent: undefined;
|
|
37
|
+
deleteForward: undefined;
|
|
38
|
+
deleteBackward: undefined;
|
|
39
|
+
undo: undefined;
|
|
40
|
+
redo: undefined;
|
|
41
|
+
selectAll: undefined;
|
|
42
|
+
setSelection: import('./selection').ModelSelection;
|
|
43
|
+
}
|
|
44
|
+
/** 命令执行结果 */
|
|
45
|
+
export interface CommandResult {
|
|
46
|
+
success: boolean;
|
|
47
|
+
error?: string;
|
|
48
|
+
}
|
|
49
|
+
/** 命令接口(Command Pattern) */
|
|
50
|
+
export interface ICommand {
|
|
51
|
+
name: CommandName;
|
|
52
|
+
execute(): void;
|
|
53
|
+
undo(): void;
|
|
54
|
+
/** 是否可以与相邻同类命令合并(用于连续输入合并) */
|
|
55
|
+
canMergeWith?(other: ICommand): boolean;
|
|
56
|
+
merge?(other: ICommand): void;
|
|
57
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export type ElementType = 'text' | 'image' | 'table' | 'hyperlink' | 'separator';
|
|
2
|
+
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
3
|
+
export type VerticalAlign = 'top' | 'middle' | 'bottom';
|
|
4
|
+
export type ListType = 'bullet' | 'ordered' | 'none';
|
|
5
|
+
/** 行内元素格式属性 */
|
|
6
|
+
export interface ElementFormat {
|
|
7
|
+
bold?: boolean;
|
|
8
|
+
italic?: boolean;
|
|
9
|
+
underline?: boolean;
|
|
10
|
+
strikethrough?: boolean;
|
|
11
|
+
fontSize?: number;
|
|
12
|
+
fontFamily?: string;
|
|
13
|
+
color?: string;
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
superscript?: boolean;
|
|
16
|
+
subscript?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** 基础元素接口 */
|
|
19
|
+
export interface BaseElement {
|
|
20
|
+
id: string;
|
|
21
|
+
type: ElementType;
|
|
22
|
+
format?: ElementFormat;
|
|
23
|
+
}
|
|
24
|
+
/** 文本元素 */
|
|
25
|
+
export interface TextElement extends BaseElement {
|
|
26
|
+
type: 'text';
|
|
27
|
+
value: string;
|
|
28
|
+
}
|
|
29
|
+
/** 图片元素 */
|
|
30
|
+
export interface ImageElement extends BaseElement {
|
|
31
|
+
type: 'image';
|
|
32
|
+
url: string;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
alt?: string;
|
|
36
|
+
}
|
|
37
|
+
/** 超链接元素 */
|
|
38
|
+
export interface HyperlinkElement extends BaseElement {
|
|
39
|
+
type: 'hyperlink';
|
|
40
|
+
url: string;
|
|
41
|
+
children: TextElement[];
|
|
42
|
+
}
|
|
43
|
+
/** 分隔线元素 */
|
|
44
|
+
export interface SeparatorElement extends BaseElement {
|
|
45
|
+
type: 'separator';
|
|
46
|
+
}
|
|
47
|
+
/** 表格单元格 */
|
|
48
|
+
export interface TableCell {
|
|
49
|
+
id: string;
|
|
50
|
+
rowspan?: number;
|
|
51
|
+
colspan?: number;
|
|
52
|
+
children: Paragraph[];
|
|
53
|
+
}
|
|
54
|
+
/** 表格行 */
|
|
55
|
+
export interface TableRow {
|
|
56
|
+
id: string;
|
|
57
|
+
height?: number;
|
|
58
|
+
cells: TableCell[];
|
|
59
|
+
}
|
|
60
|
+
/** 表格元素 */
|
|
61
|
+
export interface TableElement extends BaseElement {
|
|
62
|
+
type: 'table';
|
|
63
|
+
rows: TableRow[];
|
|
64
|
+
colWidths: number[];
|
|
65
|
+
}
|
|
66
|
+
export type IElement = TextElement | ImageElement | HyperlinkElement | SeparatorElement | TableElement;
|
|
67
|
+
/** 段落格式 */
|
|
68
|
+
export interface ParagraphFormat {
|
|
69
|
+
textAlign?: TextAlign;
|
|
70
|
+
lineHeight?: number;
|
|
71
|
+
marginTop?: number;
|
|
72
|
+
marginBottom?: number;
|
|
73
|
+
paddingLeft?: number;
|
|
74
|
+
paddingRight?: number;
|
|
75
|
+
listType?: ListType;
|
|
76
|
+
listLevel?: number;
|
|
77
|
+
listIndex?: number;
|
|
78
|
+
heading?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | null;
|
|
79
|
+
}
|
|
80
|
+
/** 段落(块级容器) */
|
|
81
|
+
export interface Paragraph {
|
|
82
|
+
id: string;
|
|
83
|
+
type: 'paragraph';
|
|
84
|
+
format?: ParagraphFormat;
|
|
85
|
+
children: IElement[];
|
|
86
|
+
}
|
|
87
|
+
/** 节(用于分页/分栏)*/
|
|
88
|
+
export interface Section {
|
|
89
|
+
id: string;
|
|
90
|
+
paragraphs: Paragraph[];
|
|
91
|
+
pageBreakBefore?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/** 文档根节点 */
|
|
94
|
+
export interface DocumentModel {
|
|
95
|
+
id: string;
|
|
96
|
+
version: number;
|
|
97
|
+
sections: Section[];
|
|
98
|
+
defaultFormat?: ElementFormat;
|
|
99
|
+
pageWidth?: number;
|
|
100
|
+
pageHeight?: number;
|
|
101
|
+
marginTop?: number;
|
|
102
|
+
marginBottom?: number;
|
|
103
|
+
marginLeft?: number;
|
|
104
|
+
marginRight?: number;
|
|
105
|
+
}
|
|
106
|
+
/** 编辑器顶层数据结构 */
|
|
107
|
+
export interface IEditorData {
|
|
108
|
+
header?: any[];
|
|
109
|
+
main: any[];
|
|
110
|
+
footer?: any[];
|
|
111
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Editor } from '../core/editor/Editor';
|
|
2
|
+
export interface IPlugin {
|
|
3
|
+
name: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
/** 插件安装时调用,注册命令/事件/渲染扩展 */
|
|
6
|
+
install(editor: Editor): void;
|
|
7
|
+
/** 插件卸载时调用,清理副作用 */
|
|
8
|
+
uninstall?(editor: Editor): void;
|
|
9
|
+
}
|
|
10
|
+
export interface PluginOptions {
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** 模型坐标中的点 */
|
|
2
|
+
export interface ModelPoint {
|
|
3
|
+
sectionIndex: number;
|
|
4
|
+
paragraphIndex: number;
|
|
5
|
+
elementIndex: number;
|
|
6
|
+
/** 在文本节点内的字符偏移量 */
|
|
7
|
+
offset: number;
|
|
8
|
+
}
|
|
9
|
+
/** 选区(start <= end,逻辑上) */
|
|
10
|
+
export interface ModelSelection {
|
|
11
|
+
anchor: ModelPoint;
|
|
12
|
+
focus: ModelPoint;
|
|
13
|
+
/** 是否折叠(光标,非选区) */
|
|
14
|
+
isCollapsed: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** 光标样式 */
|
|
17
|
+
export type CursorStyle = 'text' | 'crosshair' | 'default';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface CatalogItem {
|
|
2
|
+
/** 标题 id(复用渲染行 node_id,可直接用于 VirtualTable.scrollToRow 定位) */
|
|
3
|
+
id: string;
|
|
4
|
+
nodeId: string;
|
|
5
|
+
/** 标题文本 */
|
|
6
|
+
name: string;
|
|
7
|
+
/** 标题级别 1~6,数值越小级别越高 */
|
|
8
|
+
level: number;
|
|
9
|
+
/** 子目录 */
|
|
10
|
+
children: CatalogItem[];
|
|
11
|
+
}
|
|
12
|
+
interface RowLike {
|
|
13
|
+
node_id: string;
|
|
14
|
+
blockType?: string;
|
|
15
|
+
level?: string;
|
|
16
|
+
content?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 判定某渲染行是否为标题并返回其级别(0 表示非标题)。
|
|
20
|
+
* 与 canvas-editor 一致,仅依据标题元数据(type:'title' + level),不做启发式猜测。
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectTitleLevel(row: RowLike): number;
|
|
23
|
+
/**
|
|
24
|
+
* 从渲染行列表构建嵌套目录树。
|
|
25
|
+
* 嵌套规则与 canvas-editor 一致:级别更深的标题递归挂到「上一个标题」的子目录末端,
|
|
26
|
+
* 否则作为同级标题追加。
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildCatalog(rows: RowLike[]): CatalogItem[];
|
|
29
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function nanoid(size?: number): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TitleIndexer {
|
|
2
|
+
/**
|
|
3
|
+
* 推进到下一个标题并返回其序号文本(含末尾空格,如 "A. ")。
|
|
4
|
+
* 无论标题自身是否已带序号都需调用,以保证计数器连续;
|
|
5
|
+
* 调用方可在标题已自带序号时丢弃返回值、仅用于计数。
|
|
6
|
+
* @param level 标题层级(first~eighth)
|
|
7
|
+
* @param startIndex 指定起始序号时直接使用(对应源码 extension.startIndex)
|
|
8
|
+
*/
|
|
9
|
+
next(level: string, startIndex?: string | number): string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createTitleIndexer(): TitleIndexer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.vue-recycle-scroller__resize-observer[data-v-08cc04ab]{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none;z-index:-1}.vue-recycle-scroller{position:relative}.vue-recycle-scroller.direction-vertical:not(.page-mode){overflow-y:auto}.vue-recycle-scroller.direction-horizontal:not(.page-mode){overflow-x:auto}.vue-recycle-scroller.grid-mode:not(.page-mode){overflow:auto}.vue-recycle-scroller.direction-horizontal{display:flex}.vue-recycle-scroller__slot{flex:auto 0 0}.vue-recycle-scroller__item-wrapper{flex:1;box-sizing:border-box;overflow:hidden;position:relative}.vue-recycle-scroller.ready .vue-recycle-scroller__item-view{position:absolute;top:0;left:0;will-change:transform}.vue-recycle-scroller.direction-vertical .vue-recycle-scroller__item-wrapper{width:100%}.vue-recycle-scroller.direction-horizontal .vue-recycle-scroller__item-wrapper{height:100%}.vue-recycle-scroller.ready.direction-vertical .vue-recycle-scroller__item-view{width:100%}.vue-recycle-scroller.ready.direction-horizontal .vue-recycle-scroller__item-view{height:100%}.we-vt-table--context-menu-clild-wrapper,.we-vt-table--context-menu-wrapper{box-shadow:0 4px 12px #00000014,0 0 0 1px #0000000a!important;border:none!important;border-radius:var(--modern-menu-radius)!important}.we-vt-context-menu--option-wrapper li.link--active,.we-vt-table--context-menu-clild-wrapper li.link--active{background-color:transparent!important;border-color:transparent!important}.we-vt-table--context-menu-wrapper .we-vt-context-menu--option-wrapper .modern-menu-item:first-child .modern-menu-link:hover{background:var(--modern-menu-item-hover-bg)!important;color:var(--modern-menu-item-hover-color)!important}.virtual-table-menu.modern-virtual-menu{z-index:9999;position:fixed}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper{background:var(--modern-menu-bg);border:1px solid var(--modern-menu-border);border-radius:var(--modern-menu-radius);box-shadow:var(--modern-menu-shadow);padding:4px;min-width:160px;list-style:none;margin:0;transition:all var(--modern-menu-animation-duration) var(--modern-menu-animation-easing);animation:menuFadeIn .15s ease-out}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item{position:relative;padding:0;margin-bottom:0}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item:last-child{margin-bottom:0}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:var(--modern-menu-item-radius);color:var(--modern-menu-item-color);text-decoration:none;font-size:13px;font-weight:400;cursor:pointer;line-height:1.4;transition:all var(--modern-menu-animation-duration) var(--modern-menu-animation-easing)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link .menu-icon{flex-shrink:0;color:var(--modern-menu-icon-color);transition:color var(--modern-menu-animation-duration) var(--modern-menu-animation-easing)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link .menu-label{flex:1;white-space:nowrap}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link .submenu-arrow{flex-shrink:0;color:var(--modern-menu-icon-color);transition:color var(--modern-menu-animation-duration) var(--modern-menu-animation-easing)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link:hover{background:var(--modern-menu-item-hover-bg);color:var(--modern-menu-item-hover-color)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link:hover .menu-icon,.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item .modern-menu-link:hover .submenu-arrow{color:var(--modern-menu-icon-hover-color)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item.link--disabled .modern-menu-link{color:var(--modern-menu-item-disabled-color);cursor:not-allowed}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item.link--disabled .modern-menu-link:hover{background:transparent;color:var(--modern-menu-item-disabled-color)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item.link--disabled .modern-menu-link:hover .menu-icon,.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item.link--disabled .modern-menu-link:hover .submenu-arrow{color:var(--modern-menu-icon-disabled-color)}.virtual-table-menu.modern-virtual-menu .modern-menu-wrapper .modern-menu-item.has-children{position:relative}.virtual-table-menu.modern-virtual-menu .modern-submenu{position:absolute;left:calc(100% + 4px);top:0;min-width:140px;background:var(--modern-submenu-bg);border:1px solid var(--modern-menu-border);border-radius:var(--modern-submenu-radius);box-shadow:var(--modern-submenu-shadow);padding:4px;list-style:none;z-index:10000;margin:0;animation:submenuFadeIn .12s ease-out}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item{padding:0;margin-bottom:0}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item:last-child{margin-bottom:0}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item .modern-submenu-link{display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:var(--modern-submenu-item-radius);color:var(--modern-menu-item-color);text-decoration:none;font-size:12px;font-weight:400;cursor:pointer;line-height:1.4;transition:all var(--modern-menu-animation-duration) var(--modern-menu-animation-easing)}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item .modern-submenu-link .submenu-icon{flex-shrink:0;color:var(--modern-menu-icon-color);transition:color var(--modern-menu-animation-duration) var(--modern-menu-animation-easing)}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item .modern-submenu-link .submenu-label{flex:1;white-space:nowrap}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item .modern-submenu-link:hover{background:var(--modern-menu-item-hover-bg);color:var(--modern-menu-item-hover-color)}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item .modern-submenu-link:hover .submenu-icon{color:var(--modern-menu-icon-hover-color)}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item.link--disabled .modern-submenu-link{color:var(--modern-menu-item-disabled-color);cursor:not-allowed}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item.link--disabled .modern-submenu-link:hover{background:transparent;color:var(--modern-menu-item-disabled-color)}.virtual-table-menu.modern-virtual-menu .modern-submenu .modern-submenu-item.link--disabled .modern-submenu-link:hover .submenu-icon{color:var(--modern-menu-icon-disabled-color)}@keyframes menuFadeIn{0%{opacity:0;transform:scale(.98) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes submenuFadeIn{0%{opacity:0;transform:scale(.98) translate(-4px)}to{opacity:1;transform:scale(1) translate(0)}}.virtual-table[data-v-3756d9b0]{width:100%;height:100%;background-color:var(--we-vt-table-body-background-color, #fff);display:flex;flex-direction:column}.virtual-table .virtual-table-header[data-v-3756d9b0]{display:flex}.virtual-table .virtual-table-wrapper[data-v-3756d9b0]{flex:1;font-family:"FangSong"}.virtual-table .virtual-table-row[data-v-3756d9b0]{display:flex;min-height:20px}.virtual-table .virtual-table-row.current-row[data-v-3756d9b0]{background-color:var(--el-table-row-hover-bg-color);color:var(--el-color-primary)}.virtual-table .virtual-table-cell[data-v-3756d9b0]{position:relative}.virtual-table .content-wrapper[data-v-3756d9b0]{flex:1;min-height:20px;color:#606266;font-size:14px;font-weight:400}.virtual-table .virtual-table-inline-editing-trigger[data-v-3756d9b0]{white-space:pre-line;word-break:break-all;border:1px transparent dotted}.virtual-table .virtual-table-inline-editing-trigger[data-v-3756d9b0]:hover{border-color:var(--el-color-primary)}.a4-pages-container[data-v-3756d9b0]{width:100%;height:100%;overflow:auto;background-color:#f0f0f0;padding:20px;display:flex;flex-direction:column;align-items:center;font-family:"FangSong"}.a4-page[data-v-3756d9b0]{background:#fff;box-shadow:0 2px 8px #00000026;position:relative;margin:0 auto;box-sizing:border-box;display:block;flex-shrink:0}.a4-table-header[data-v-3756d9b0]{background:#fff;z-index:10;border-bottom:1px solid #e8e8e8;margin-bottom:10px;padding-bottom:10px;overflow-x:auto;min-width:0;margin-top:10px}.a4-table-header[data-v-3756d9b0]::-webkit-scrollbar{height:4px}.a4-table-header[data-v-3756d9b0]::-webkit-scrollbar-track{background:transparent}.a4-table-header[data-v-3756d9b0]::-webkit-scrollbar-thumb{background:#ddd;border-radius:2px}.a4-page-content[data-v-3756d9b0]{min-width:0;position:relative}.a4-page-content[data-v-3756d9b0]::-webkit-scrollbar{height:6px}.a4-page-content[data-v-3756d9b0]::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.a4-page-content[data-v-3756d9b0]::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.a4-page-content[data-v-3756d9b0]::-webkit-scrollbar-thumb:hover{background:#555}.a4-page-header[data-v-3756d9b0]{width:100%;overflow:hidden}.a4-page-footer[data-v-3756d9b0]{position:absolute;bottom:5px;left:0;width:100%;text-align:center;color:#666;font-size:12px;background:#fff}.page-number[data-v-3756d9b0]{display:inline-block}.a4-page .virtual-table-row[data-v-3756d9b0]{display:flex;transition:background-color .2s;width:100%}.a4-page .virtual-table-cell[data-v-3756d9b0]{overflow:hidden;text-overflow:ellipsis;box-sizing:border-box}.a4-page .virtual-table-header[data-v-3756d9b0]{min-width:fit-content}.a4-page .we-vt-header--column[data-v-3756d9b0]{flex-shrink:0;padding:8px;box-sizing:border-box}.a4-page .virtual-table-row[data-v-3756d9b0]:hover{background-color:#fafafa}.a4-page .virtual-table-row.row--current[data-v-3756d9b0]{background-color:var(--el-table-row-hover-bg-color);color:var(--el-color-primary)}.virtual-table-row.row--highlight[data-v-3756d9b0]{animation:rowHighlight-3756d9b0 1.5s ease-in-out}@keyframes rowHighlight-3756d9b0{0%{background-color:transparent}25%{background-color:gold;transform:scale(1.01)}50%{background-color:#ffeb3b}to{background-color:transparent;transform:scale(1)}}.a4-page-placeholder[data-v-3756d9b0]{background:#fff;box-shadow:0 2px 8px #00000026;position:relative;margin:0 auto;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex-shrink:0}.a4-page-loading[data-v-3756d9b0]{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:400px;width:100%}.loading-spinner[data-v-3756d9b0]{width:40px;height:40px;border:3px solid #f3f3f3;border-top:3px solid var(--el-color-primary);border-radius:50%;animation:spin-3756d9b0 1s linear infinite}@keyframes spin-3756d9b0{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-text[data-v-3756d9b0]{margin-top:20px;color:#666;font-size:14px}@media print{.a4-pages-container[data-v-3756d9b0]{background:none;padding:0}.a4-page[data-v-3756d9b0]{box-shadow:none;margin:0;page-break-after:always}.a4-page[data-v-3756d9b0]:last-child{page-break-after:auto}.a4-page-footer[data-v-3756d9b0]{position:fixed;bottom:0}}.catalog-node__label[data-v-ac882917]{display:flex;align-items:center;gap:4px;height:30px;padding-right:10px;border-radius:6px;cursor:pointer;color:#3f4654;font-size:13px;transition:background-color .15s ease,color .15s ease}.catalog-node__label[data-v-ac882917]:hover{background-color:#f1f3f6;color:#1f2937}.catalog-node__label.is-active[data-v-ac882917]{background-color:#e8f0fe;color:#2563eb;font-weight:500}.catalog-node__toggle[data-v-ac882917]{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;color:#9aa3b2}.catalog-node__toggle[data-v-ac882917]:hover{color:#2563eb}.catalog-node__text[data-v-ac882917]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.catalog[data-v-8becafa1]{display:flex;flex-direction:column;width:268px;height:100%;flex-shrink:0;background-color:#fafbfc;border-right:1px solid #e7e9ee}.catalog--collapsed[data-v-8becafa1]{width:44px;align-items:center;padding-top:10px}.catalog__header[data-v-8becafa1]{display:flex;align-items:center;justify-content:space-between;height:48px;padding:0 12px 0 14px;border-bottom:1px solid #e7e9ee}.catalog__title[data-v-8becafa1]{display:inline-flex;align-items:center;gap:6px;font-size:14px;font-weight:600;color:#1f2937}.catalog__icon-btn[data-v-8becafa1]{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:none;border-radius:6px;background:transparent;color:#6b7280;cursor:pointer;transition:background-color .15s ease,color .15s ease}.catalog__icon-btn[data-v-8becafa1]:hover{background-color:#eef0f4;color:#2563eb}.catalog__body[data-v-8becafa1]{flex:1;overflow-y:auto;padding:8px}.catalog__empty[data-v-8becafa1]{padding:24px 12px;text-align:center;font-size:13px;color:#9aa3b2}.doc-para[data-v-27e51e18]{margin:0;min-height:20px;line-height:1.43;font-size:14px;color:#000;word-break:break-word}.doc-title[data-v-27e51e18]{margin:0;line-height:1.4;font-size:14px;color:#000;word-break:break-word}.doc-link[data-v-27e51e18]{color:#00f;text-decoration:underline}.doc-image[data-v-27e51e18]{max-width:100%;vertical-align:middle}.ctrl-input[data-v-27e51e18]{display:inline-block;border-bottom:1px solid #000;min-width:60px;vertical-align:bottom;padding:0 2px;line-height:1.4}.ctrl-options[data-v-27e51e18]{display:inline-flex;flex-wrap:wrap;gap:8px;align-items:center;vertical-align:middle}.ctrl-options--vertical[data-v-27e51e18]{display:flex;flex-direction:column;align-items:flex-start;gap:2px}.ctrl-option[data-v-27e51e18]{display:inline-flex;align-items:center;gap:3px;padding-left:6px;cursor:default;-webkit-user-select:none;user-select:none}.el-check[data-v-27e51e18]{vertical-align:middle;margin:0 2px}.doc-tab[data-v-27e51e18]{display:inline-block}.doc-table-row-wrap[data-v-27e51e18]{overflow-x:auto}.doc-table-row-wrap:not(.is-first):not(.is-page-break) .doc-table tr:first-child td[data-v-27e51e18]{border-top:none}.doc-table[data-v-27e51e18]{border-collapse:collapse;table-layout:fixed}.doc-table td[data-v-27e51e18]{border:1px solid #000;padding:4px 6px;font-size:12px;color:#000;word-break:break-word;overflow-wrap:break-word}.cell-para[data-v-27e51e18]{margin:0;line-height:1.5;min-height:1em}.doc-table--no-border td[data-v-27e51e18]{border:none}.doc-list[data-v-27e51e18]{margin:0;padding-left:24px;font-size:12px;color:#000}.doc-list-item[data-v-27e51e18]{line-height:1.5;word-break:break-word;min-height:1.5em}.empty-line[data-v-27e51e18]{display:inline-block;width:1px}.editor-layout[data-v-ea9b9852]{display:flex;height:100%;overflow:hidden}.editor-container[data-v-ea9b9852]{flex:1;min-width:0;height:100%;overflow:hidden}.editor-loading[data-v-ea9b9852]{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#666}.doc-header[data-v-ea9b9852]{display:flex;flex-direction:column;align-items:center;width:100%;overflow:hidden;border-bottom:1px solid #e5e7eb;padding-bottom:4px;margin-bottom:8px}.doc-footer[data-v-ea9b9852]{display:flex;flex-direction:column;align-items:center;width:100%;overflow:hidden;border-top:1px solid #e5e7eb;padding-top:4px;margin-top:8px}.doc-page-number[data-v-ea9b9852]{text-align:center;font-size:12px;color:#666;margin-top:4px}.word-editor-scope{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif;font-size:14px;line-height:1.43;color:#1f2937;text-align:left;-webkit-font-smoothing:antialiased}.word-editor-scope *,.word-editor-scope *:before,.word-editor-scope *:after{box-sizing:border-box}:where(.word-editor-scope) :where(p,h1,h2,h3,h4,h5,h6,ul,ol,li,figure,blockquote,dl,dd){margin:0;padding:0}.word-editor-scope table{margin:0;border-collapse:collapse;border-spacing:0}.word-editor-scope img{max-width:none}.word-editor-scope .virtual-table-cell{padding:0}
|