diff --git a/src/editor/core/cursor/Cursor.ts b/src/editor/core/cursor/Cursor.ts index cc5d043..de23d40 100644 --- a/src/editor/core/cursor/Cursor.ts +++ b/src/editor/core/cursor/Cursor.ts @@ -62,8 +62,9 @@ export class Cursor { // 设置光标代理 const height = this.draw.getHeight() const pageGap = this.draw.getPageGap() - const { metrics, coordinate: { leftTop, rightTop }, ascent, pageNo } = cursorPosition - const preY = pageNo * (height + pageGap) + const { metrics, coordinate: { leftTop, rightTop }, ascent } = cursorPosition + const curPageNo = this.draw.getPageNo() + const preY = curPageNo * (height + pageGap) // 增加1/4字体大小 const offsetHeight = metrics.height / 4 const cursorHeight = metrics.height + offsetHeight * 2 diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index d17655b..1c44f89 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -1,9 +1,9 @@ import { version } from '../../../../package.json' import { ZERO } from '../../dataset/constant/Common' import { RowFlex } from '../../dataset/enum/Row' -import { IDrawOption, IDrawRowPayload, IPainterOptions } from '../../interface/Draw' -import { IEditorDrawData, IEditorOption, IEditorResult } from '../../interface/Editor' -import { IElement, IElementMetrics, IElementPosition, IElementFillRect, IElementStyle } from '../../interface/Element' +import { IDrawOption, IDrawPagePayload, IDrawRowPayload, IPainterOptions } from '../../interface/Draw' +import { IEditorData, IEditorOption, IEditorResult } from '../../interface/Editor' +import { IElement, IElementMetrics, IElementFillRect, IElementStyle } from '../../interface/Element' import { IRow, IRowElement } from '../../interface/Row' import { deepClone, getUUID, nextTick } from '../../utils' import { Cursor } from '../cursor/Cursor' @@ -35,7 +35,7 @@ import { SubscriptParticle } from './particle/Subscript' import { SeparatorParticle } from './particle/Separator' import { PageBreakParticle } from './particle/PageBreak' import { Watermark } from './frame/Watermark' -import { EditorComponent, EditorMode, PageMode, PaperDirection } from '../../dataset/enum/Editor' +import { EditorComponent, EditorMode, EditorZone, PageMode, PaperDirection } from '../../dataset/enum/Editor' import { Control } from './control/Control' import { zipElementList } from '../../utils/element' import { CheckboxParticle } from './particle/CheckboxParticle' @@ -50,6 +50,7 @@ import { BlockParticle } from './particle/block/BlockParticle' import { EDITOR_COMPONENT, EDITOR_PREFIX } from '../../dataset/constant/Editor' import { I18n } from '../i18n/I18n' import { ImageObserver } from '../observer/ImageObserver' +import { Zone } from '../zone/Zone' export class Draw { @@ -61,6 +62,7 @@ export class Draw { private mode: EditorMode private options: DeepRequired private position: Position + private zone: Zone private headerElementList: IElement[] private elementList: IElement[] private footerElementList: IElement[] @@ -112,7 +114,7 @@ export class Draw { constructor( rootContainer: HTMLElement, options: DeepRequired, - data: IEditorDrawData, + data: IEditorData, listener: Listener ) { this.container = this._wrapContainer(rootContainer) @@ -133,6 +135,7 @@ export class Draw { this.i18n = new I18n() this.historyManager = new HistoryManager() this.position = new Position(this) + this.zone = new Zone() this.range = new RangeManager(this) this.margin = new Margin(this) this.background = new Background(this) @@ -212,6 +215,12 @@ export class Draw { return Math.floor(this.getOriginalHeight() * this.options.scale) } + public getOriginalMainHeight(): number { + const mainHeight = this.getOriginalHeight() + const extraHeight = this.header.getExtraHeight() + return mainHeight - extraHeight + } + public getCanvasWidth(pageNo = -1): number { const page = this.getPage(pageNo) return page.width @@ -343,6 +352,10 @@ export class Draw { return this.position } + public getZone(): Zone { + return this.zone + } + public getRange(): RangeManager { return this.range } @@ -351,12 +364,35 @@ export class Draw { return this.headerElementList } + public getTableElementList(sourceElementList: IElement[]): IElement[] { + const positionContext = this.position.getPositionContext() + const { index, trIndex, tdIndex } = positionContext + return sourceElementList[index!].trList![trIndex!].tdList[tdIndex!].value + } + public getElementList(): IElement[] { const positionContext = this.position.getPositionContext() - if (positionContext.isTable) { - const { index, trIndex, tdIndex } = positionContext - return this.elementList[index!].trList![trIndex!].tdList[tdIndex!].value - } + const elementList = this.getOriginalElementList() + return positionContext.isTable + ? this.getTableElementList(elementList) + : elementList + } + + public getMainElementList(): IElement[] { + const positionContext = this.position.getPositionContext() + return positionContext.isTable + ? this.getTableElementList(this.elementList) + : this.elementList + } + + public getOriginalElementList() { + const zoneManager = this.getZone() + return zoneManager.isHeaderActive() + ? this.header.getElementList() + : this.elementList + } + + public getOriginalMainElementList(): IElement[] { return this.elementList } @@ -406,10 +442,6 @@ export class Draw { } } - public getOriginalElementList() { - return this.elementList - } - public getCanvasEvent(): CanvasEvent { return this.canvasEvent } @@ -434,6 +466,10 @@ export class Draw { return this.tableTool } + public getHeader(): Header { + return this.header + } + public getHyperlinkParticle(): HyperlinkParticle { return this.hyperlinkParticle } @@ -610,7 +646,7 @@ export class Draw { // 配置 const { width, height, margins, watermark } = this.options // 数据 - const data: IEditorDrawData = { + const data: IEditorData = { header: zipElementList(this.headerElementList), main: zipElementList(this.elementList) } @@ -768,7 +804,7 @@ export class Draw { metrics.boundingBoxAscent = 0 // 表格分页处理(拆分表格) const margins = this.getMargins() - const height = this.getHeight() + const height = this.getOriginalMainHeight() const marginHeight = margins[0] + margins[2] let curPagePreHeight = marginHeight for (let r = 0; r < rowList.length; r++) { @@ -826,13 +862,13 @@ export class Draw { } else if (element.type === ElementType.SEPARATOR) { element.width = innerWidth metrics.width = innerWidth - metrics.height = this.options.defaultSize + metrics.height = defaultSize metrics.boundingBoxAscent = -rowMargin metrics.boundingBoxDescent = -rowMargin } else if (element.type === ElementType.PAGE_BREAK) { element.width = innerWidth metrics.width = innerWidth - metrics.height = this.options.defaultSize + metrics.height = defaultSize } else if ( element.type === ElementType.CHECKBOX || element.controlComponent === ControlComponent.CHECKBOX @@ -860,7 +896,7 @@ export class Draw { metrics.boundingBoxAscent = 0 } else { // 设置上下标真实字体尺寸 - const size = element.size || this.options.defaultSize + const size = element.size || defaultSize if (element.type === ElementType.SUPERSCRIPT || element.type === ElementType.SUBSCRIPT) { element.actualSize = Math.ceil(size * 0.6) } @@ -934,7 +970,7 @@ export class Draw { private _computePageList(): IRow[][] { const pageRowList: IRow[][] = [[]] const { pageMode } = this.options - const height = this.getHeight() + const height = this.getOriginalMainHeight() const margins = this.getMargins() const marginHeight = margins[0] + margins[2] let pageHeight = marginHeight @@ -979,7 +1015,7 @@ export class Draw { } public drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload) { - const { rowList, pageNo, positionList, startIndex } = payload + const { rowList, pageNo, elementList, positionList, startIndex, zone } = payload const { scale, tdPadding } = this.options const { isCrossRowCol, tableId } = this.range.getRange() let index = startIndex @@ -1077,11 +1113,11 @@ export class Draw { this.highlight.render(ctx) } // 选区记录 - const { startIndex, endIndex } = this.range.getRange() - if (startIndex !== endIndex && startIndex <= index && index <= endIndex) { + const { zone: currentZone, startIndex, endIndex } = this.range.getRange() + if (currentZone === zone && startIndex !== endIndex && startIndex <= index && index <= endIndex) { // 从行尾开始-绘制最小宽度 if (startIndex === index) { - const nextElement = this.elementList[startIndex + 1] + const nextElement = elementList[startIndex + 1] if (nextElement && nextElement.value === ZERO) { rangeRecord.x = x + metrics.width rangeRecord.y = y @@ -1119,11 +1155,13 @@ export class Draw { for (let d = 0; d < tr.tdList!.length; d++) { const td = tr.tdList[d] this.drawRow(ctx, { + elementList: td.value, positionList: td.positionList!, rowList: td.rowList!, pageNo, startIndex: 0, - innerWidth: (td.width! - tdGap) * scale + innerWidth: (td.width! - tdGap) * scale, + zone }) } } @@ -1150,10 +1188,13 @@ export class Draw { this.blockParticle.clear() } - private _drawPage(positionList: IElementPosition[], rowList: IRow[], pageNo: number) { - const { pageMode } = this.options + private _drawPage(payload: IDrawPagePayload) { + const { elementList, positionList, rowList, pageNo } = payload + const { inactiveAlpha, pageMode } = this.options const innerWidth = this.getInnerWidth() const ctx = this.ctxList[pageNo] + // 判断当前激活区域-激活页眉时主题元素透明度降低 + ctx.globalAlpha = this.zone.isHeaderActive() ? inactiveAlpha : 1 this._clearPage(pageNo) // 绘制背景 this.background.render(ctx) @@ -1162,14 +1203,16 @@ export class Draw { // 渲染元素 const index = rowList[0].startIndex this.drawRow(ctx, { + elementList, positionList, rowList, pageNo, startIndex: index, - innerWidth + innerWidth, + zone: EditorZone.MAIN }) // 绘制页眉 - this.header.render(ctx) + this.header.render(ctx, pageNo) // 绘制页码 this.pageNumber.render(ctx, pageNo) // 搜索匹配绘制 @@ -1183,14 +1226,20 @@ export class Draw { } private _lazyRender() { - const positionList = this.position.getOriginalPositionList() + const positionList = this.position.getOriginalMainPositionList() + const elementList = this.getOriginalMainElementList() this.lazyRenderIntersectionObserver?.disconnect() this.lazyRenderIntersectionObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const index = Number((entry.target).dataset.index) - this.header.render(this.ctxList[index]) - this._drawPage(positionList, this.pageRowList[index], index) + this.header.render(this.ctxList[index], index) + this._drawPage({ + elementList, + positionList, + rowList: this.pageRowList[index], + pageNo: index + }) } }) }) @@ -1200,9 +1249,15 @@ export class Draw { } private _immediateRender() { - const positionList = this.position.getOriginalPositionList() + const positionList = this.position.getOriginalMainPositionList() + const elementList = this.getOriginalMainElementList() for (let i = 0; i < this.pageRowList.length; i++) { - this._drawPage(positionList, this.pageRowList[i], i) + this._drawPage({ + elementList, + positionList, + rowList: this.pageRowList[i], + pageNo: i + }) } } @@ -1236,7 +1291,6 @@ export class Draw { this.imageObserver.clearAll() this.cursor.recoveryCursor() // 创建纸张 - const positionList = this.position.getOriginalPositionList() for (let i = 0; i < this.pageRowList.length; i++) { if (!this.pageList[i]) { this._createPage(i) @@ -1260,20 +1314,19 @@ export class Draw { } // 光标重绘 if (isSetCursor) { + const positionList = this.position.getPositionList() const positionContext = this.position.getPositionContext() if (positionContext.isTable) { const { index, trIndex, tdIndex } = positionContext - const tablePositionList = this.elementList[index!].trList?.[trIndex!].tdList[tdIndex!].positionList + const elementList = this.getElementList() + const tablePositionList = elementList[index!].trList?.[trIndex!].tdList[tdIndex!].positionList if (curIndex === undefined && tablePositionList) { curIndex = tablePositionList.length - 1 } const tablePosition = tablePositionList?.[curIndex!] this.position.setCursorPosition(tablePosition || null) } else { - if (curIndex === undefined) { - curIndex = positionList.length - 1 - } - this.position.setCursorPosition(positionList[curIndex!] || null) + this.position.setCursorPosition(curIndex !== undefined ? positionList[curIndex] : null) } this.cursor.drawCursor() } @@ -1281,12 +1334,16 @@ export class Draw { if (isSubmitHistory) { const self = this const oldElementList = deepClone(this.elementList) + const oldHeaderElementList = deepClone(this.header.getElementList()) const { startIndex, endIndex } = this.range.getRange() const pageNo = this.pageNo const oldPositionContext = deepClone(this.position.getPositionContext()) + const zone = this.zone.getZone() this.historyManager.execute(function () { + self.zone.setZone(zone) self.setPageNo(pageNo) self.position.setPositionContext(oldPositionContext) + self.header.setElementList(oldHeaderElementList) self.elementList = deepClone(oldElementList) self.range.setRange(startIndex, endIndex) self.render({ curIndex, isSubmitHistory: false }) diff --git a/src/editor/core/draw/frame/Header.ts b/src/editor/core/draw/frame/Header.ts index e4b6970..06cbb0f 100644 --- a/src/editor/core/draw/frame/Header.ts +++ b/src/editor/core/draw/frame/Header.ts @@ -1,3 +1,5 @@ +import { maxHeightRadioMapping } from '../../../dataset/constant/Header' +import { EditorZone } from '../../../dataset/enum/Editor' import { DeepRequired } from '../../../interface/Common' import { IEditorOption } from '../../../interface/Editor' import { IElement, IElementPosition } from '../../../interface/Element' @@ -25,6 +27,18 @@ export class Header { this.positionList = [] } + public setElementList(elementList: IElement[]) { + this.elementList = elementList + } + + public getElementList(): IElement[] { + return this.elementList + } + + public getPositionList(): IElementPosition[] { + return this.positionList + } + public compute() { this._recovery() this._computeRowList() @@ -46,7 +60,7 @@ export class Header { const innerWidth = this.draw.getInnerWidth() const margins = this.draw.getMargins() const startX = margins[3] - const startY = margins[0] + top + const startY = top this.position.computePageRowPosition({ positionList: this.positionList, rowList: this.rowList, @@ -58,14 +72,53 @@ export class Header { }) } - public render(ctx: CanvasRenderingContext2D) { + public getMaxHeight(): number { + const { header: { maxHeightRadio }, height } = this.options + return height * maxHeightRadioMapping[maxHeightRadio] + } + + public getHeight(): number { + const maxHeight = this.getMaxHeight() + const rowHeight = this.getRowHeight() + return rowHeight > maxHeight ? maxHeight : rowHeight + } + + public getRowHeight(): number { + return this.rowList.reduce((pre, cur) => pre + cur.height, 0) + } + + public getExtraHeight(): number { + const { header: { top: headerTop } } = this.options + // 页眉上边距 + 实际高 - 页面上边距 + const rowHeight = this.getRowHeight() + const margins = this.draw.getOriginalMargins() + const extraHeight = headerTop + rowHeight - margins[0] + return extraHeight <= 0 ? 0 : extraHeight + } + + public render(ctx: CanvasRenderingContext2D, pageNo: number) { + ctx.globalAlpha = 1 const innerWidth = this.draw.getInnerWidth() + const maxHeight = this.getMaxHeight() + // 超出最大高度不渲染 + const rowList: IRow[] = [] + let curRowHeight = 0 + for (let r = 0; r < this.rowList.length; r++) { + const row = this.rowList[r] + if (curRowHeight + row.height > maxHeight) { + break + } + rowList.push(row) + curRowHeight += row.height + } this.draw.drawRow(ctx, { + elementList: this.elementList, positionList: this.positionList, - rowList: this.rowList, - pageNo: 0, + rowList, + pageNo, startIndex: 0, - innerWidth + innerWidth, + zone: EditorZone.HEADER }) } diff --git a/src/editor/core/event/CanvasEvent.ts b/src/editor/core/event/CanvasEvent.ts index 7f4da3d..20b83be 100644 --- a/src/editor/core/event/CanvasEvent.ts +++ b/src/editor/core/event/CanvasEvent.ts @@ -147,8 +147,8 @@ export class CanvasEvent { keydown(evt, this) } - public dblclick() { - click.dblclick(this) + public dblclick(evt: MouseEvent) { + click.dblclick(this, evt) } public threeClick() { diff --git a/src/editor/core/event/handlers/click.ts b/src/editor/core/event/handlers/click.ts index 5aa61bd..a8dc877 100644 --- a/src/editor/core/event/handlers/click.ts +++ b/src/editor/core/event/handlers/click.ts @@ -2,9 +2,25 @@ import { ZERO } from '../../../dataset/constant/Common' import { LETTER_REG, NUMBER_LIKE_REG } from '../../../dataset/constant/Regular' import { CanvasEvent } from '../CanvasEvent' -function dblclick(host: CanvasEvent) { +function dblclick(host: CanvasEvent, evt: MouseEvent) { + // 切换区域 const draw = host.getDraw() const position = draw.getPosition() + const positionContext = position.getPositionByXY({ + x: evt.offsetX, + y: evt.offsetY + }) + if (!~positionContext.index && positionContext.zone) { + const zoneManager = draw.getZone() + zoneManager.setZone(positionContext.zone) + draw.render({ + isSubmitHistory: false, + isSetCursor: false, + isCompute: false + }) + return + } + // 自动扩选文字 const cursorPosition = position.getCursorPosition() if (!cursorPosition) return const { value, index } = cursorPosition diff --git a/src/editor/core/event/handlers/drag.ts b/src/editor/core/event/handlers/drag.ts index 8cd937b..83225b9 100644 --- a/src/editor/core/event/handlers/drag.ts +++ b/src/editor/core/event/handlers/drag.ts @@ -21,10 +21,12 @@ function dragover(evt: DragEvent | MouseEvent, host: CanvasEvent) { draw.setPageNo(Number(pageIndex)) } const position = draw.getPosition() - const { isTable, tdValueIndex, index } = position.adjustPositionContext({ + const positionContext = position.adjustPositionContext({ x: evt.offsetX, y: evt.offsetY }) + if (!positionContext) return + const { isTable, tdValueIndex, index } = positionContext // 设置选区及光标位置 const positionList = position.getPositionList() const curIndex = isTable ? tdValueIndex! : index diff --git a/src/editor/core/event/handlers/mousedown.ts b/src/editor/core/event/handlers/mousedown.ts index 252ea18..fb4d196 100644 --- a/src/editor/core/event/handlers/mousedown.ts +++ b/src/editor/core/event/handlers/mousedown.ts @@ -36,6 +36,7 @@ export function mousedown(evt: MouseEvent, host: CanvasEvent) { x: evt.offsetX, y: evt.offsetY }) + if (!positionResult) return const { index, isDirectHit, diff --git a/src/editor/core/event/handlers/mousemove.ts b/src/editor/core/event/handlers/mousemove.ts index 3286e48..6643e30 100644 --- a/src/editor/core/event/handlers/mousemove.ts +++ b/src/editor/core/event/handlers/mousemove.ts @@ -32,6 +32,7 @@ export function mousemove(evt: MouseEvent, host: CanvasEvent) { x: evt.offsetX, y: evt.offsetY }) + if (!~positionResult.index) return const { index, isTable, diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index 38770a7..4534d02 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -3,9 +3,10 @@ import { ZERO } from '../../dataset/constant/Common' import { ControlComponent, ImageDisplay } from '../../dataset/enum/Control' import { IComputePageRowPositionPayload, IComputePageRowPositionResult } from '../../interface/Position' import { IEditorOption } from '../../interface/Editor' -import { IElementPosition } from '../../interface/Element' +import { IElement, IElementPosition } from '../../interface/Element' import { ICurrentPosition, IGetPositionByXYPayload, IPositionContext } from '../../interface/Position' import { Draw } from '../draw/Draw' +import { EditorZone } from '../../dataset/enum/Editor' export class Position { @@ -28,17 +29,32 @@ export class Position { this.options = draw.getOptions() } - public getOriginalPositionList(): IElementPosition[] { - return this.positionList + public getTablePositionList(sourceElementList: IElement[]): IElementPosition[] { + const { index, trIndex, tdIndex } = this.positionContext + return sourceElementList[index!].trList![trIndex!].tdList[tdIndex!].positionList || [] } public getPositionList(): IElementPosition[] { - const { isTable } = this.positionContext - if (isTable) { - const { index, trIndex, tdIndex } = this.positionContext - const elementList = this.draw.getOriginalElementList() - return elementList[index!].trList![trIndex!].tdList[tdIndex!].positionList || [] - } + const elementList = this.draw.getElementList() + return this.positionContext.isTable + ? this.getTablePositionList(elementList) + : this.getOriginalPositionList() + } + + public getMainPositionList(): IElementPosition[] { + const elementList = this.draw.getMainElementList() + return this.positionContext.isTable + ? this.getTablePositionList(elementList) + : this.positionList + } + + public getOriginalPositionList(): IElementPosition[] { + const zoneManager = this.draw.getZone() + const header = this.draw.getHeader() + return zoneManager.isHeaderActive() ? header.getPositionList() : this.positionList + } + + public getOriginalMainPositionList(): IElementPosition[] { return this.positionList } @@ -130,7 +146,10 @@ export class Position { const pageRowList = this.draw.getPageRowList() const margins = this.draw.getMargins() const startX = margins[3] - const startY = margins[0] + // 起始位置受页眉影响 + const header = this.draw.getHeader() + const extraHeight = header.getExtraHeight() + const startY = margins[0] + extraHeight for (let i = 0; i < pageRowList.length; i++) { const rowList = pageRowList[i] const startIndex = rowList[0].startIndex @@ -169,12 +188,14 @@ export class Position { elementList = this.draw.getOriginalElementList() } if (!positionList) { - positionList = this.positionList + positionList = this.getOriginalPositionList() } + const zoneManager = this.draw.getZone() const curPageNo = this.draw.getPageNo() + const positionNo = zoneManager.isMainActive() ? curPageNo : 0 for (let j = 0; j < positionList.length; j++) { const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = positionList[j] - if (curPageNo !== pageNo) continue + if (positionNo !== pageNo) continue // 命中元素 if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) { let curPositionIndex = j @@ -267,15 +288,15 @@ export class Position { } } // 判断所属行是否存在元素 - const firstLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === curPageNo) + const firstLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === positionNo) for (let j = 0; j < firstLetterList.length; j++) { const { index, pageNo, coordinate: { leftTop, leftBottom } } = firstLetterList[j] - if (curPageNo !== pageNo) continue + if (positionNo !== pageNo) continue if (y > leftTop[1] && y <= leftBottom[1]) { const isHead = x < this.options.margins[3] // 是否在头部 if (isHead) { - const headIndex = positionList.findIndex(p => p.pageNo === curPageNo && p.rowNo === firstLetterList[j].rowNo) + const headIndex = positionList.findIndex(p => p.pageNo === positionNo && p.rowNo === firstLetterList[j].rowNo) curPositionIndex = ~headIndex ? headIndex - 1 : index } else { curPositionIndex = index @@ -285,8 +306,28 @@ export class Position { } } if (!isLastArea) { + // 判断所属位置是否属于header区域,当前位置小于第一行的上边距 + if (zoneManager.isMainActive()) { + if (y < firstLetterList[0].coordinate.leftTop[1]) { + return { + index: -1, + zone: EditorZone.HEADER + } + } + } + // 判断所属位置是否属于main区域,当前位置大于第一行的上边距 + if (zoneManager.isHeaderActive()) { + if (y > firstLetterList[0].coordinate.leftTop[1]) { + return { + index: -1, + zone: EditorZone.MAIN + } + } + } // 当前页最后一行 - return { index: firstLetterList[firstLetterList.length - 1]?.index || positionList.length - 1 } + return { + index: firstLetterList[firstLetterList.length - 1]?.index || positionList.length - 1, + } } return { index: curPositionIndex, @@ -294,13 +335,10 @@ export class Position { } } - public adjustPositionContext(payload: Pick): ICurrentPosition { + public adjustPositionContext(payload: IGetPositionByXYPayload): ICurrentPosition | null { const isReadonly = this.draw.isReadonly() - const { x, y } = payload - const positionResult = this.getPositionByXY({ - x, - y - }) + const positionResult = this.getPositionByXY(payload) + if (!~positionResult.index) return null // 移动控件内光标 if (positionResult.isControl && !isReadonly) { const { diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts index 43acc6e..3808e29 100644 --- a/src/editor/core/range/RangeManager.ts +++ b/src/editor/core/range/RangeManager.ts @@ -90,6 +90,7 @@ export class RangeManager { this.range.startTrIndex = startTrIndex this.range.endTrIndex = endTrIndex this.range.isCrossRowCol = !!(startTdIndex || endTdIndex || startTrIndex || endTrIndex) + this.range.zone = this.draw.getZone().getZone() // 激活控件 const control = this.draw.getControl() if (~startIndex && ~endIndex) { diff --git a/src/editor/core/zone/Zone.ts b/src/editor/core/zone/Zone.ts new file mode 100644 index 0000000..21f0897 --- /dev/null +++ b/src/editor/core/zone/Zone.ts @@ -0,0 +1,27 @@ +import { EditorZone } from '../../dataset/enum/Editor' + +export class Zone { + + private currentZone: EditorZone + + constructor() { + this.currentZone = EditorZone.MAIN + } + + public isHeaderActive(): boolean { + return this.getZone() === EditorZone.HEADER + } + + public isMainActive(): boolean { + return this.getZone() === EditorZone.MAIN + } + + public getZone(): EditorZone { + return this.currentZone + } + + public setZone(payload: EditorZone) { + this.currentZone = payload + } + +} \ No newline at end of file diff --git a/src/editor/dataset/constant/Header.ts b/src/editor/dataset/constant/Header.ts index 4fbc78b..d63fb49 100644 --- a/src/editor/dataset/constant/Header.ts +++ b/src/editor/dataset/constant/Header.ts @@ -2,6 +2,12 @@ import { IHeader } from '../../interface/Header' import { HeaderMaxHeightRatio } from '../enum/Header' export const defaultHeaderOption: Readonly> = { - top: -50, + top: 30, maxHeightRadio: HeaderMaxHeightRatio.HALF +} + +export const maxHeightRadioMapping: Record = { + [HeaderMaxHeightRatio.HALF]: 1 / 2, + [HeaderMaxHeightRatio.ONE_THIRD]: 1 / 3, + [HeaderMaxHeightRatio.QUARTER]: 1 / 4 } \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index 1c421c3..d43e2a4 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -39,7 +39,7 @@ export default class Editor { public register: Register public destroy: Function - constructor(container: HTMLDivElement, data: IEditorData, options: IEditorOption = {}) { + constructor(container: HTMLDivElement, data: IEditorData | IElement[], options: IEditorOption = {}) { const headerOptions: Required = { ...defaultHeaderOption, ...options.header @@ -96,6 +96,7 @@ export default class Editor { defaultHyperlinkColor: '#0000FF', headerTop: 50, paperDirection: PaperDirection.VERTICAL, + inactiveAlpha: 0.6, ...options, header: headerOptions, watermark: waterMarkOptions, diff --git a/src/editor/interface/Draw.ts b/src/editor/interface/Draw.ts index 2642751..6fb96c0 100644 --- a/src/editor/interface/Draw.ts +++ b/src/editor/interface/Draw.ts @@ -1,4 +1,5 @@ -import { IElementPosition } from './Element' +import { EditorZone } from '../dataset/enum/Editor' +import { IElement, IElementPosition } from './Element' import { IRow } from './Row' export interface IDrawOption { @@ -16,11 +17,20 @@ export interface IDrawImagePayload { } export interface IDrawRowPayload { + elementList: IElement[]; positionList: IElementPosition[]; rowList: IRow[]; pageNo: number; startIndex: number; innerWidth: number; + zone: EditorZone; +} + +export interface IDrawPagePayload { + elementList: IElement[]; + positionList: IElementPosition[]; + rowList: IRow[]; + pageNo: number; } export interface IPainterOptions { diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index db0fb39..d5c1bb6 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -7,14 +7,12 @@ import { IHeader } from './Header' import { IMargin } from './Margin' import { IWatermark } from './Watermark' -export interface IEditorDrawData { +export interface IEditorData { header?: IElement[]; main: IElement[]; footer?: IElement[]; } -export type IEditorData = IEditorDrawData | IElement[] - export interface IEditorOption { mode?: EditorMode; defaultType?: string; @@ -50,6 +48,7 @@ export interface IEditorOption { defaultHyperlinkColor?: string; headerTop?: number; paperDirection?: PaperDirection; + inactiveAlpha?: number; header?: IHeader; watermark?: IWatermark; control?: IControlOption; diff --git a/src/editor/interface/Position.ts b/src/editor/interface/Position.ts index 9170383..ca59b02 100644 --- a/src/editor/interface/Position.ts +++ b/src/editor/interface/Position.ts @@ -1,4 +1,5 @@ import { IElement } from '..' +import { EditorZone } from '../dataset/enum/Editor' import { IElementPosition } from './Element' import { IRow } from './Row' import { ITd } from './table/Td' @@ -16,6 +17,7 @@ export interface ICurrentPosition { tdId?: string; trId?: string; tableId?: string; + zone?: EditorZone; } export interface IGetPositionByXYPayload { diff --git a/src/editor/interface/Range.ts b/src/editor/interface/Range.ts index f4ee996..3c57127 100644 --- a/src/editor/interface/Range.ts +++ b/src/editor/interface/Range.ts @@ -1,3 +1,5 @@ +import { EditorZone } from '../dataset/enum/Editor' + export interface IRange { startIndex: number; endIndex: number; @@ -7,6 +9,7 @@ export interface IRange { endTdIndex?: number; startTrIndex?: number; endTrIndex?: number; + zone?: EditorZone; } export type RangeRowMap = Map> diff --git a/src/main.ts b/src/main.ts index 11fe326..5f9e2fb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,10 +15,16 @@ window.onload = function () { container, { header: [{ - value: '人民医院门诊', - size: 14, - color: '#AAAAAA', + value: '第一人民医院', + size: 32, rowFlex: RowFlex.CENTER + }, { + value: '\n门诊病历', + size: 18, + rowFlex: RowFlex.CENTER + }, { + value: '\n', + type: ElementType.SEPARATOR }], main: data }, diff --git a/src/mock.ts b/src/mock.ts index c7da542..16722b4 100644 --- a/src/mock.ts +++ b/src/mock.ts @@ -1,13 +1,6 @@ -import { ControlType, ElementType, IEditorOption, IElement, RowFlex } from './editor' +import { ControlType, ElementType, IEditorOption, IElement } 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其他记录:` - -// 模拟行居中 -const centerText = ['人民医院门诊病历'] -const centerIndex: number[] = centerText.map(c => { - const i = text.indexOf(c) - return ~i ? Array(c.length).fill(i).map((_, j) => i + j) : [] -}).flat() +const text = `主诉:\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其他记录:` // 模拟加粗字 const boldText = ['主诉:', '现病史:', '既往史:', '流行病史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:', '其他记录:'] @@ -32,13 +25,6 @@ const highlightIndex: number[] = highlightText.map(b => { // 组合纯文本数据 const elementList: IElement[] = text.split('').map((value, index) => { - if (centerIndex.includes(index)) { - return { - value, - size: 32, - rowFlex: RowFlex.CENTER - } - } if (boldIndex.includes(index)) { return { value, @@ -65,14 +51,8 @@ const elementList: IElement[] = text.split('').map((value, index) => { } }) -// 模拟分隔符 -elementList.splice(8, 0, { - value: '\n', - type: ElementType.SEPARATOR -}) - // 模拟文本控件 -elementList.splice(24, 0, { +elementList.splice(14, 0, { type: ElementType.CONTROL, value: '', control: { @@ -85,7 +65,7 @@ elementList.splice(24, 0, { }) // 模拟下拉控件 -elementList.splice(112, 0, { +elementList.splice(102, 0, { type: ElementType.CONTROL, value: '', control: { @@ -109,7 +89,7 @@ elementList.splice(112, 0, { }) // 模拟超链接 -elementList.splice(138, 0, { +elementList.splice(128, 0, { type: ElementType.HYPERLINK, value: '', valueList: [{ @@ -129,20 +109,20 @@ elementList.splice(138, 0, { }) // 模拟下标 -elementList.splice(371, 0, { +elementList.splice(361, 0, { value: '∆', color: '#FF0000', type: ElementType.SUBSCRIPT }) // 模拟上标 -elementList.splice(459, 0, { +elementList.splice(449, 0, { value: '9', type: ElementType.SUPERSCRIPT }) // 模拟图片 -elementList.splice(585, 0, { +elementList.splice(575, 0, { value: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAAAgCAYAAAB5JtSmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQ0SURBVGhD7dhrUSNBFAVgvKACEVjAAhJQgAIUYAABGEAABvgfAdn6UnWou01PppOZhIXNj1P9vo9zH5PK1Waz2V5wWlxIPgMuJJ8Bi0h+fn7eXl9fb29ubrYPDw/dO/8DHh8fu/vB4kym4Orqaofb29vund8OSSbhemewSrugBMnG3vlvw9vb265yn56edmtz/t/f33+5C8MkixQSZSsl9UzLOHUmcwTYAN/Rpl5eXnY+pnIB0Xd3d7s5m3rvDsrkCGszNiQ7r/tr4v39fSc/uipOqRcqufTHBiO78GGdzG5xcLtIFmVde7L9NsvXRo9s84+Pj+79pUAwn5GcD1wIz5r+fYGeJdnjGiF9hwL7iWAcfX19/evtKVHJXrtN8Rf4A3TVczqhrut5i1mSZQgnIriSWtdzP2N+EvIhi3/GWqHWtWXuy2IYbheiKarJZIZknkxyrryc2Utrgal+9S8iScUXIx/3kcxfe/jotcuDezLFlIbARDrzHpytXdKnQr4xyc74Vu9YV5Ih2Q/tT7mDSEYw5ZU4wu3nJx64k/1z9umlUG0hah/JSbC6Jzi5exDJWoTHERoBxu8uf/pT1j3HDkUIJitjbRfRA/iwVzlgy1RCfSF5ili9xj7BUWKs9wJZ3MpditYu+lsc+/PRx53cVF9Pdg/syE9Hb6cS75PkmhUEUFofmTvLGEXKimHueJP9Y3swWQwGLUiA9xEbHKuvgs4pPe1+1myTAKlw81buJ8kigjAXKauXPLQPhEYgJSEYsgdTUR0BmTVgc6C359wcvKGnBrGO8dO5VlD1ZZ519nrBHvrwKVMCas9hgL0YUI2wV98fC4FqCWizzXyqF44A0ZKLHkilgvPs1zbiTuZIdZ414KvqGCKZYx4zple+MSrrJVncAyL02/TOqncJwVMglx5zI4QDZ5WPvBGEcNP+7TlEcqJIAQFGsIdQjmZt7MlYA5yiI3pOQTCQXUm2TuVmXgmewxDJQDgl6deJJoU5y7p9uwZagmu1mCvbNoOOBfkhOf6lRZjzPb8qRjBMMiUhM9GNMZQq5/oRXBP7Mlj/i12A7EMIaJGqDcl8I79+/N1xTvdINQ2TDAQSvI9Md479vdqCHKSFQKAfEmgBqCTDkjaSgOZXQkg2jy1ti0xApnBQJo/0obQRipeQXbN3CmxKGQch5xgki4Efghl/kFqzPD//2DnXIodIRpaoETaXxcmwGNO7N4I2Oyuc6b+xK/tL9IH3kY/E+r1JdST4yM+7VUiuJbuPZHBeHZcNvXtziMMV9mRuvUOX8Vg9IFjRx9dUYM3s2oJyNx9ahFfSWwyRHKHG3nmL2q/mojyFVAWnEdi2Hg7OBXwUCCKr1QEtoe0+/9jI3xqIiuF2QRD0zqcwpfQnge9TVSI4tWrNe79shj98F0xDC0N4bTUVF5LPgAvJJ8dm+wcP2iJuZNdC5QAAAABJRU5ErkJggg==`, width: 89, height: 32,