From 8f7eb3e67f7bc73a3efea8505dbff386e55b31f4 Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Sun, 27 Nov 2022 20:11:29 +0800 Subject: [PATCH 1/8] feat:content block core --- src/editor/assets/css/block/block.css | 7 +++ src/editor/assets/css/date/datePicker.css | 1 + src/editor/assets/css/index.css | 1 + src/editor/core/draw/Draw.ts | 12 +++++ .../core/draw/particle/block/BlockParticle.ts | 47 +++++++++++++++++ .../draw/particle/block/modules/BaseBlock.ts | 51 +++++++++++++++++++ .../particle/block/modules/IFrameBlock.ts | 28 ++++++++++ src/editor/dataset/constant/Element.ts | 3 +- src/editor/dataset/enum/Block.ts | 3 ++ src/editor/dataset/enum/Element.ts | 3 +- src/editor/index.ts | 4 +- src/editor/interface/Block.ts | 10 ++++ src/editor/interface/Element.ts | 6 +++ src/editor/utils/element.ts | 2 +- src/mock.ts | 16 +++++- 15 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 src/editor/assets/css/block/block.css create mode 100644 src/editor/core/draw/particle/block/BlockParticle.ts create mode 100644 src/editor/core/draw/particle/block/modules/BaseBlock.ts create mode 100644 src/editor/core/draw/particle/block/modules/IFrameBlock.ts create mode 100644 src/editor/dataset/enum/Block.ts create mode 100644 src/editor/interface/Block.ts diff --git a/src/editor/assets/css/block/block.css b/src/editor/assets/css/block/block.css new file mode 100644 index 0000000..6f6e177 --- /dev/null +++ b/src/editor/assets/css/block/block.css @@ -0,0 +1,7 @@ +.block-item { + position: absolute; + z-index: 0; + overflow: hidden; + border-radius: 8px; + border: 1px solid rgb(235 236 240); +} \ No newline at end of file diff --git a/src/editor/assets/css/date/datePicker.css b/src/editor/assets/css/date/datePicker.css index 6cd740d..be44cc4 100644 --- a/src/editor/assets/css/date/datePicker.css +++ b/src/editor/assets/css/date/datePicker.css @@ -5,6 +5,7 @@ left: 0; right: 0; position: absolute; + z-index: 1; color: #606266; background: #ffffff; border-radius: 4px; diff --git a/src/editor/assets/css/index.css b/src/editor/assets/css/index.css index 161fa99..1189064 100644 --- a/src/editor/assets/css/index.css +++ b/src/editor/assets/css/index.css @@ -1,5 +1,6 @@ @import './control/select.css'; @import './date/datePicker.css'; +@import './block/block.css'; .inputarea { width: 0; diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index c6adc84..7faa74d 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -46,6 +46,7 @@ import { WorkerManager } from '../worker/WorkerManager' import { Previewer } from './particle/previewer/Previewer' import { DateParticle } from './particle/date/DateParticle' import { IMargin } from '../../interface/Margin' +import { BlockParticle } from './particle/block/BlockParticle' export class Draw { @@ -86,6 +87,7 @@ export class Draw { private superscriptParticle: SuperscriptParticle private subscriptParticle: SubscriptParticle private checkboxParticle: CheckboxParticle + private blockParticle: BlockParticle private control: Control private workerManager: WorkerManager @@ -138,6 +140,7 @@ export class Draw { this.superscriptParticle = new SuperscriptParticle() this.subscriptParticle = new SubscriptParticle() this.checkboxParticle = new CheckboxParticle(this) + this.blockParticle = new BlockParticle(this) this.control = new Control(this) new ScrollObserver(this) @@ -707,6 +710,11 @@ export class Draw { metrics.height = defaultSize * scale metrics.boundingBoxDescent = 0 metrics.boundingBoxAscent = metrics.height + } else if (element.type === ElementType.BLOCK) { + metrics.width = element.width! * scale + metrics.height = element.height! * scale + metrics.boundingBoxDescent = metrics.height + metrics.boundingBoxAscent = 0 } else { // 设置上下标真实字体尺寸 const size = element.size || this.options.defaultSize @@ -739,6 +747,7 @@ export class Draw { const preElement = elementList[i - 1] if ( preElement?.type === ElementType.TABLE + || preElement?.type === ElementType.BLOCK || preElement?.imgDisplay === ImageDisplay.INLINE || element.imgDisplay === ImageDisplay.INLINE || curRow.width + metrics.width > innerWidth @@ -880,6 +889,9 @@ export class Draw { // 如果是两端对齐,因canvas目前不支持letterSpacing需单独绘制文本 this.textParticle.record(ctx, element, x, y + offsetY) this._drawRichText(ctx) + } else if (element.type === ElementType.BLOCK) { + this._drawRichText(ctx) + this.blockParticle.render(pageNo, element, x, y) } else { this.textParticle.record(ctx, element, x, y + offsetY) } diff --git a/src/editor/core/draw/particle/block/BlockParticle.ts b/src/editor/core/draw/particle/block/BlockParticle.ts new file mode 100644 index 0000000..74b1d09 --- /dev/null +++ b/src/editor/core/draw/particle/block/BlockParticle.ts @@ -0,0 +1,47 @@ +import { IRowElement } from '../../../../interface/Row' +import { Draw } from '../../Draw' +import { BaseBlock } from './modules/BaseBlock' + +export class BlockParticle { + + private draw: Draw + private container: HTMLDivElement + private blockContainer: HTMLDivElement + private blockMap: Map + + constructor(draw: Draw) { + this.draw = draw + this.container = draw.getContainer() + this.blockMap = new Map() + this.blockContainer = this._createBlockContainer() + this.container.append(this.blockContainer) + } + + private _createBlockContainer(): HTMLDivElement { + const blockContainer = document.createElement('div') + blockContainer.classList.add('block-container') + return blockContainer + } + + public getDraw(): Draw { + return this.draw + } + + public getBlockContainer(): HTMLDivElement { + return this.blockContainer + } + + public render(pageNo: number, element: IRowElement, x: number, y: number) { + const id = element.id! + const cacheBlock = this.blockMap.get(id) + if (cacheBlock) { + cacheBlock.setClientRects(pageNo, x, y) + } else { + const newBlock = new BaseBlock(this, element) + newBlock.render() + newBlock.setClientRects(pageNo, x, y) + this.blockMap.set(id, newBlock) + } + } + +} \ No newline at end of file diff --git a/src/editor/core/draw/particle/block/modules/BaseBlock.ts b/src/editor/core/draw/particle/block/modules/BaseBlock.ts new file mode 100644 index 0000000..f9e87cc --- /dev/null +++ b/src/editor/core/draw/particle/block/modules/BaseBlock.ts @@ -0,0 +1,51 @@ +import { BlockType } from '../../../../../dataset/enum/Block' +import { IRowElement } from '../../../../../interface/Row' +import { Draw } from '../../../Draw' +import { BlockParticle } from '../BlockParticle' +import { IFrameBlock } from './IFrameBlock' + +export class BaseBlock { + + private draw: Draw + private element: IRowElement + private block: IFrameBlock | null + private blockContainer: HTMLDivElement + private blockItem: HTMLDivElement + + constructor(blockParticle: BlockParticle, element: IRowElement) { + this.draw = blockParticle.getDraw() + this.blockContainer = blockParticle.getBlockContainer() + this.element = element + this.block = null + this.blockItem = this._createBlockItem() + this.blockContainer.append(this.blockItem) + } + + private _createBlockItem(): HTMLDivElement { + const blockItem = document.createElement('div') + blockItem.classList.add('block-item') + return blockItem + } + + public render() { + const block = this.element.block! + if (block.type === BlockType.IFRAME) { + this.block = new IFrameBlock(this.element) + this.block.render(this.blockItem) + } + } + + public setClientRects(pageNo: number, x: number, y: number) { + const scale = this.draw.getOptions().scale + const height = this.draw.getHeight() + const pageGap = this.draw.getPageGap() + const preY = pageNo * (height + pageGap) + // 尺寸 + this.blockItem.style.width = `${this.element.width! * scale}px` + this.blockItem.style.height = `${this.element.height! * scale}px` + // 位置 + this.blockItem.style.left = `${x}px` + this.blockItem.style.top = `${preY + y}px` + } + +} \ No newline at end of file diff --git a/src/editor/core/draw/particle/block/modules/IFrameBlock.ts b/src/editor/core/draw/particle/block/modules/IFrameBlock.ts new file mode 100644 index 0000000..b1cc2f8 --- /dev/null +++ b/src/editor/core/draw/particle/block/modules/IFrameBlock.ts @@ -0,0 +1,28 @@ +import { IRowElement } from '../../../../../interface/Row' + +export class IFrameBlock { + + private static readonly sandbox = [ + 'allow-forms', + 'allow-scripts', + 'allow-same-origin', + 'allow-popups' + ] + private element: IRowElement + + constructor(element: IRowElement) { + this.element = element + } + + public render(blockItemContainer: HTMLDivElement) { + const block = this.element.block! + const iframe = document.createElement('iframe') + iframe.sandbox.add(...IFrameBlock.sandbox) + iframe.style.border = 'none' + iframe.style.width = '100%' + iframe.style.height = '100%' + iframe.src = block.iframeBlock?.src || '' + blockItemContainer.append(iframe) + } + +} \ No newline at end of file diff --git a/src/editor/dataset/constant/Element.ts b/src/editor/dataset/constant/Element.ts index 4725e30..1c9a17a 100644 --- a/src/editor/dataset/constant/Element.ts +++ b/src/editor/dataset/constant/Element.ts @@ -50,7 +50,8 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array = [ 'valueList', 'control', 'checkbox', - 'dateFormat' + 'dateFormat', + 'block' ] export const TEXTLIKE_ELEMENT_TYPE: ElementType[] = [ diff --git a/src/editor/dataset/enum/Block.ts b/src/editor/dataset/enum/Block.ts new file mode 100644 index 0000000..c89ecbf --- /dev/null +++ b/src/editor/dataset/enum/Block.ts @@ -0,0 +1,3 @@ +export enum BlockType { + IFRAME = 'iframe' +} \ No newline at end of file diff --git a/src/editor/dataset/enum/Element.ts b/src/editor/dataset/enum/Element.ts index 972654f..a248245 100644 --- a/src/editor/dataset/enum/Element.ts +++ b/src/editor/dataset/enum/Element.ts @@ -11,5 +11,6 @@ export enum ElementType { CHECKBOX = 'checkbox', LATEX = 'latex', TAB = 'tab', - DATE = 'date' + DATE = 'date', + BLOCK = 'block' } \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index 2339e0a..49079ef 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -26,6 +26,7 @@ import { DeepRequired } from './interface/Common' import { INavigateInfo } from './core/draw/interactive/Search' import { Shortcut } from './core/shortcut/Shortcut' import { KeyMap } from './dataset/enum/KeyMap' +import { BlockType } from './dataset/enum/Block' export default class Editor { @@ -125,7 +126,8 @@ export { PageMode, ImageDisplay, Command, - KeyMap + KeyMap, + BlockType } // 对外类型 diff --git a/src/editor/interface/Block.ts b/src/editor/interface/Block.ts new file mode 100644 index 0000000..c6f7064 --- /dev/null +++ b/src/editor/interface/Block.ts @@ -0,0 +1,10 @@ +import { BlockType } from '../dataset/enum/Block' + +export interface IIFrameBlock { + src: string; +} + +export interface IBlock { + type: BlockType; + iframeBlock?: IIFrameBlock; +} \ No newline at end of file diff --git a/src/editor/interface/Element.ts b/src/editor/interface/Element.ts index 66c34ff..bdd3ead 100644 --- a/src/editor/interface/Element.ts +++ b/src/editor/interface/Element.ts @@ -1,6 +1,7 @@ import { ControlComponent, ImageDisplay } from '../dataset/enum/Control' import { ElementType } from '../dataset/enum/Element' import { RowFlex } from '../dataset/enum/Row' +import { IBlock } from './Block' import { ICheckbox } from './Checkbox' import { IControl } from './Control' import { IColgroup } from './table/Colgroup' @@ -78,6 +79,10 @@ export interface IImageElement { imgDisplay?: ImageDisplay } +export interface IBlockElement { + block?: IBlock; +} + export type IElement = IElementBasic & IElementStyle & ITable @@ -89,6 +94,7 @@ export type IElement = IElementBasic & ILaTexElement & IDateElement & IImageElement + & IBlockElement export interface IElementMetrics { width: number; diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index 65f771c..061a3ea 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -237,7 +237,7 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme if (el.value === '\n') { el.value = ZERO } - if (el.type === ElementType.IMAGE) { + if (el.type === ElementType.IMAGE || el.type === ElementType.BLOCK) { el.id = getUUID() } if (el.type === ElementType.LATEX) { diff --git a/src/mock.ts b/src/mock.ts index 0924fcc..d3d0bcc 100644 --- a/src/mock.ts +++ b/src/mock.ts @@ -1,4 +1,4 @@ -import { ControlType, ElementType, IEditorOption, IElement, RowFlex } from './editor' +import { BlockType, ControlType, ElementType, IEditorOption, IElement, RowFlex } from './editor' const text = `人民医院门诊病历\n主诉:\n发热三天,咳嗽五天。\n现病史:\n患者于三天前无明显诱因,感冒后发现面部水肿,无皮疹,尿量减少,出现乏力,在外治疗无好转,现来我院就诊。\n既往史:\n有糖尿病10年,有高血压2年,有传染性疾病1年。报告其他既往疾病。\n流行病史:\n否认14天内接触过确诊患者、疑似患者、无症状感染者及其密切接触者;否认14天内去过以下场所:水产、肉类批发市场,农贸市场,集市,大型超市,夜市;否认14天内与以下场所工作人员密切接触:水产、肉类批发市场,农贸市场,集市,大型超市;否认14天内周围(如家庭、办公室)有2例以上聚集性发病;否认14天内接触过有发热或呼吸道症状的人员;否认14天内自身有发热或呼吸道症状;否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形;陪同家属无以上情况。\n体格检查:\nT:39.5℃,P:80bpm,R:20次/分,BP:120/80mmHg;\n辅助检查:\n2020年6月10日,普放:血细胞比容36.50%(偏低)40~50;单核细胞绝对值0.75*10/L(偏高)参考值:0.1~0.6;\n门诊诊断:\n1.高血压\n2.糖尿病\n3.病毒性感冒\n4.过敏性鼻炎\n5.过敏性鼻息肉\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】\n其他记录:` @@ -289,6 +289,20 @@ elementList.push(...[{ value: `2022-08-10 17:30:01` }], type: ElementType.DATE +}]) + +// 内容快 +elementList.push(...[{ + value: '', + type: ElementType.BLOCK, + width: 520, + height: 400, + block: { + type: BlockType.IFRAME, + iframeBlock: { + src: 'https://hufe.club' + } + } }, { value: '\n' }]) From 3543e3983ed53541e2a6428af46525fcdf1c3d8d Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Sun, 27 Nov 2022 21:01:22 +0800 Subject: [PATCH 2/8] feat:add content block menu --- index.html | 3 +++ src/assets/images/block.svg | 1 + src/main.ts | 47 ++++++++++++++++++++++++++++++++++++- src/mock.ts | 16 +------------ src/style.css | 4 ++++ 5 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 src/assets/images/block.svg diff --git a/index.html b/index.html index fc8d3cb..ea30d9b 100644 --- a/index.html +++ b/index.html @@ -188,6 +188,9 @@ +