diff --git a/src/editor/assets/css/resizer/resizer.css b/src/editor/assets/css/resizer/resizer.css index a27f13a..f1199cb 100644 --- a/src/editor/assets/css/resizer/resizer.css +++ b/src/editor/assets/css/resizer/resizer.css @@ -5,6 +5,7 @@ .ce-resizer-selection>div { position: absolute; + z-index: 9; width: 10px; height: 10px; box-shadow: 0 1px 4px 0 rgb(0 0 0 / 30%); diff --git a/src/editor/assets/css/zone/zone.css b/src/editor/assets/css/zone/zone.css index b16d619..a5738bc 100644 --- a/src/editor/assets/css/zone/zone.css +++ b/src/editor/assets/css/zone/zone.css @@ -1,4 +1,4 @@ -.ce-header-indicator>div { +.ce-zone-indicator>div { padding: 3px 6px; color: #000000; font-size: 12px; @@ -7,27 +7,28 @@ transform-origin: 0 0; } -.ce-header-indicator-border__top, -.ce-header-indicator-border__bottom, -.ce-header-indicator-border__left, -.ce-header-indicator-border__right { +.ce-zone-indicator-border__top, +.ce-zone-indicator-border__bottom, +.ce-zone-indicator-border__left, +.ce-zone-indicator-border__right { display: block; position: absolute; + z-index: 0; } -.ce-header-indicator-border__top { +.ce-zone-indicator-border__top { border-top: 2px dashed rgb(238, 238, 238); } -.ce-header-indicator-border__bottom { +.ce-zone-indicator-border__bottom { border-top: 2px dashed rgb(238, 238, 238); width: 100%; } -.ce-header-indicator-border__left { +.ce-zone-indicator-border__left { border-left: 2px dashed rgb(238, 238, 238); } -.ce-header-indicator-border__right { +.ce-zone-indicator-border__right { border-right: 2px dashed rgb(238, 238, 238); } \ No newline at end of file diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 86f7545..1401c6e 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -51,6 +51,7 @@ import { EDITOR_COMPONENT, EDITOR_PREFIX } from '../../dataset/constant/Editor' import { I18n } from '../i18n/I18n' import { ImageObserver } from '../observer/ImageObserver' import { Zone } from '../zone/Zone' +import { Footer } from './frame/Footer' export class Draw { @@ -89,6 +90,7 @@ export class Draw { private pageNumber: PageNumber private waterMark: Watermark private header: Header + private footer: Footer private hyperlinkParticle: HyperlinkParticle private dateParticle: DateParticle private separatorParticle: SeparatorParticle @@ -152,6 +154,7 @@ export class Draw { this.pageNumber = new PageNumber(this) this.waterMark = new Watermark(this) this.header = new Header(this) + this.footer = new Footer(this) this.hyperlinkParticle = new HyperlinkParticle(this) this.dateParticle = new DateParticle(this) this.separatorParticle = new SeparatorParticle() @@ -217,9 +220,14 @@ export class Draw { public getMainHeight(): number { const pageHeight = this.getHeight() + return pageHeight - this.getMainOuterHeight() + } + + public getMainOuterHeight(): number { const margins = this.getMargins() - const extraHeight = this.header.getExtraHeight() - return pageHeight - margins[0] - margins[2] - extraHeight + const headerExtraHeight = this.header.getExtraHeight() + const footerExtraHeight = this.footer.getExtraHeight() + return margins[0] + margins[2] + headerExtraHeight + footerExtraHeight } public getCanvasWidth(pageNo = -1): number { @@ -385,9 +393,13 @@ export class Draw { public getOriginalElementList() { const zoneManager = this.getZone() - return zoneManager.isHeaderActive() - ? this.header.getElementList() - : this.elementList + if (zoneManager.isHeaderActive()) { + return this.header.getElementList() + } + if (zoneManager.isFooterActive()) { + return this.footer.getElementList() + } + return this.elementList } public getOriginalMainElementList(): IElement[] { @@ -468,6 +480,10 @@ export class Draw { return this.header } + public getFooter(): Footer { + return this.footer + } + public getHyperlinkParticle(): HyperlinkParticle { return this.hyperlinkParticle } @@ -646,7 +662,8 @@ export class Draw { // 数据 const data: IEditorData = { header: zipElementList(this.headerElementList), - main: zipElementList(this.elementList) + main: zipElementList(this.elementList), + footer: zipElementList(this.footerElementList) } return { version, @@ -803,10 +820,8 @@ export class Draw { metrics.boundingBoxDescent = elementHeight metrics.boundingBoxAscent = 0 // 表格分页处理(拆分表格) - const margins = this.getMargins() const height = this.getHeight() - const headerExtraHeight = this.header.getExtraHeight() - const marginHeight = margins[0] + margins[2] + headerExtraHeight + const marginHeight = this.getMainOuterHeight() let curPagePreHeight = marginHeight for (let r = 0; r < rowList.length; r++) { const row = rowList[r] @@ -973,9 +988,7 @@ export class Draw { const pageRowList: IRow[][] = [[]] const { pageMode } = this.options const height = this.getHeight() - const margins = this.getMargins() - const headerExtraHeight = this.header.getExtraHeight() - const marginHeight = margins[0] + margins[2] + headerExtraHeight + const marginHeight = this.getMainOuterHeight() let pageHeight = marginHeight let pageNo = 0 if (pageMode === PageMode.CONTINUITY) { @@ -1196,8 +1209,8 @@ export class Draw { const { inactiveAlpha, pageMode } = this.options const innerWidth = this.getInnerWidth() const ctx = this.ctxList[pageNo] - // 判断当前激活区域-激活页眉时主题元素透明度降低 - ctx.globalAlpha = this.zone.isHeaderActive() ? inactiveAlpha : 1 + // 判断当前激活区域-非正文区域时元素透明度降低 + ctx.globalAlpha = !this.zone.isMainActive() ? inactiveAlpha : 1 this._clearPage(pageNo) // 绘制背景 this.background.render(ctx) @@ -1218,6 +1231,8 @@ export class Draw { this.header.render(ctx, pageNo) // 绘制页码 this.pageNumber.render(ctx, pageNo) + // 绘制页脚 + this.footer.render(ctx, pageNo) // 搜索匹配绘制 if (this.search.getSearchKeyword()) { this.search.render(ctx, pageNo) @@ -1236,7 +1251,6 @@ export class Draw { entries.forEach(entry => { if (entry.isIntersecting) { const index = Number((entry.target).dataset.index) - this.header.render(this.ctxList[index], index) this._drawPage({ elementList, positionList, @@ -1278,6 +1292,8 @@ export class Draw { if (isCompute) { // 页眉信息 this.header.compute() + // 页脚信息 + this.footer.compute() // 行信息 this.rowList = this.computeRowList(innerWidth, this.elementList) // 页面信息 @@ -1338,6 +1354,7 @@ export class Draw { const self = this const oldElementList = deepClone(this.elementList) const oldHeaderElementList = deepClone(this.header.getElementList()) + const oldFooterElementList = deepClone(this.footer.getElementList()) const { startIndex, endIndex } = this.range.getRange() const pageNo = this.pageNo const oldPositionContext = deepClone(positionContext) @@ -1345,8 +1362,9 @@ export class Draw { this.historyManager.execute(function () { self.zone.setZone(zone) self.setPageNo(pageNo) - self.position.setPositionContext(oldPositionContext) - self.header.setElementList(oldHeaderElementList) + self.position.setPositionContext(deepClone(oldPositionContext)) + self.header.setElementList(deepClone(oldHeaderElementList)) + self.footer.setElementList(deepClone(oldFooterElementList)) self.elementList = deepClone(oldElementList) self.range.setRange(startIndex, endIndex) self.render({ curIndex, isSubmitHistory: false }) @@ -1359,8 +1377,8 @@ export class Draw { this.tableTool.render() } // 页眉指示器重新渲染 - if (isCompute && this.zone.isHeaderActive()) { - this.zone.drawHeaderZoneIndicator() + if (isCompute && !this.zone.isMainActive()) { + this.zone.drawZoneIndicator() } // 页面尺寸改变 if (this.listener.pageSizeChange) { diff --git a/src/editor/core/draw/frame/Footer.ts b/src/editor/core/draw/frame/Footer.ts new file mode 100644 index 0000000..b72cc46 --- /dev/null +++ b/src/editor/core/draw/frame/Footer.ts @@ -0,0 +1,134 @@ +import { maxHeightRadioMapping } from '../../../dataset/constant/Common' +import { EditorZone } from '../../../dataset/enum/Editor' +import { DeepRequired } from '../../../interface/Common' +import { IEditorOption } from '../../../interface/Editor' +import { IElement, IElementPosition } from '../../../interface/Element' +import { IRow } from '../../../interface/Row' +import { Position } from '../../position/Position' +import { Draw } from '../Draw' + +export class Footer { + + private draw: Draw + private position: Position + private options: DeepRequired + + private elementList: IElement[] + private rowList: IRow[] + private positionList: IElementPosition[] + + constructor(draw: Draw) { + this.draw = draw + this.position = draw.getPosition() + this.options = draw.getOptions() + + this.elementList = draw.getFooterElementList() + this.rowList = [] + 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() + this._computePositionList() + } + + private _recovery() { + this.rowList = [] + this.positionList = [] + } + + private _computeRowList() { + const innerWidth = this.draw.getInnerWidth() + this.rowList = this.draw.computeRowList(innerWidth, this.elementList) + } + + private _computePositionList() { + const footerBottom = this.getFooterBottom() + const innerWidth = this.draw.getInnerWidth() + const margins = this.draw.getMargins() + const startX = margins[3] + // 页面高度 - 页脚顶部距离页面底部高度 + const pageHeight = this.draw.getHeight() + const footerHeight = this.getHeight() + const startY = pageHeight - footerBottom - footerHeight + this.position.computePageRowPosition({ + positionList: this.positionList, + rowList: this.rowList, + pageNo: 0, + startIndex: 0, + startX, + startY, + innerWidth + }) + } + + public getFooterBottom(): number { + const { footer: { bottom }, scale } = this.options + return Math.floor(bottom * scale) + } + + public getMaxHeight(): number { + const { footer: { maxHeightRadio } } = this.options + const height = this.draw.getHeight() + return Math.floor(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 margins = this.draw.getMargins() + const footerHeight = this.getHeight() + const footerBottom = this.getFooterBottom() + const extraHeight = footerBottom + footerHeight - margins[2] + 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, + pageNo, + startIndex: 0, + innerWidth, + zone: EditorZone.FOOTER + }) + } + +} \ No newline at end of file diff --git a/src/editor/core/draw/frame/Header.ts b/src/editor/core/draw/frame/Header.ts index e4778fa..bfc8b45 100644 --- a/src/editor/core/draw/frame/Header.ts +++ b/src/editor/core/draw/frame/Header.ts @@ -1,4 +1,4 @@ -import { maxHeightRadioMapping } from '../../../dataset/constant/Header' +import { maxHeightRadioMapping } from '../../../dataset/constant/Common' import { EditorZone } from '../../../dataset/enum/Editor' import { DeepRequired } from '../../../interface/Common' import { IEditorOption } from '../../../interface/Editor' diff --git a/src/editor/core/draw/frame/PageNumber.ts b/src/editor/core/draw/frame/PageNumber.ts index 62384b0..fcecdc0 100644 --- a/src/editor/core/draw/frame/PageNumber.ts +++ b/src/editor/core/draw/frame/PageNumber.ts @@ -32,7 +32,7 @@ export class PageNumber { const margins = this.draw.getMargins() const { width: textWidth } = ctx.measureText(text) if (rowFlex === RowFlex.CENTER) { - x = (width + textWidth) / 2 + x = (width - textWidth) / 2 } else if (rowFlex === RowFlex.RIGHT) { x = width - textWidth - margins[1] } else { diff --git a/src/editor/core/event/handlers/keydown.ts b/src/editor/core/event/handlers/keydown.ts index b1b6f78..7c41401 100644 --- a/src/editor/core/event/handlers/keydown.ts +++ b/src/editor/core/event/handlers/keydown.ts @@ -262,9 +262,9 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) { } else if (evt.key === KeyMap.ESC) { // 退出格式刷 host.clearPainterStyle() - // 退出页眉编辑 + // 退出页眉页脚编辑 const zoneManager = draw.getZone() - if (zoneManager.isHeaderActive()) { + if (!zoneManager.isMainActive()) { zoneManager.setZone(EditorZone.MAIN) } evt.preventDefault() diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index 201695e..77ffc05 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -48,8 +48,15 @@ export class Position { public getOriginalPositionList(): IElementPosition[] { const zoneManager = this.draw.getZone() - const header = this.draw.getHeader() - return zoneManager.isHeaderActive() ? header.getPositionList() : this.positionList + if (zoneManager.isHeaderActive()) { + const header = this.draw.getHeader() + return header.getPositionList() + } + if (zoneManager.isFooterActive()) { + const footer = this.draw.getFooter() + return footer.getPositionList() + } + return this.positionList } public getOriginalMainPositionList(): IElementPosition[] { @@ -209,7 +216,8 @@ export class Position { } const zoneManager = this.draw.getZone() const curPageNo = this.draw.getPageNo() - const positionNo = zoneManager.isMainActive() ? curPageNo : 0 + const isMainActive = zoneManager.isMainActive() + const positionNo = isMainActive ? curPageNo : 0 for (let j = 0; j < positionList.length; j++) { const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = positionList[j] if (positionNo !== pageNo) continue @@ -306,15 +314,15 @@ export class Position { } } // 判断所属行是否存在元素 - 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] + const lastLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === positionNo) + for (let j = 0; j < lastLetterList.length; j++) { + const { index, pageNo, coordinate: { leftTop, leftBottom } } = lastLetterList[j] 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 === positionNo && p.rowNo === firstLetterList[j].rowNo) + const headIndex = positionList.findIndex(p => p.pageNo === positionNo && p.rowNo === lastLetterList[j].rowNo) curPositionIndex = ~headIndex ? headIndex - 1 : index } else { curPositionIndex = index @@ -324,18 +332,30 @@ export class Position { } } if (!isLastArea) { - // 判断所属位置是否属于header区域,当前位置小于第一行的上边距 - if (zoneManager.isMainActive()) { - if (y < firstLetterList[0].coordinate.leftTop[1]) { + const mainLastLetterList = isMainActive + ? lastLetterList + : this.getOriginalMainPositionList().filter(p => p.isLastLetter && p.pageNo === positionNo) + const firstPosition = mainLastLetterList[0] + const lastPosition = mainLastLetterList[mainLastLetterList.length - 1] + // 判断所属位置是否属于页眉页脚区域 + if (isMainActive) { + // 页眉:当前位置小于第一行的上边距 + if (y < firstPosition.coordinate.leftTop[1]) { return { index: -1, zone: EditorZone.HEADER } } - } - // 判断所属位置是否属于main区域,当前位置大于第一行的上边距 - if (zoneManager.isHeaderActive()) { - if (y > firstLetterList[0].coordinate.leftTop[1]) { + // 页脚:当前位置大于最后一行的下边距 + if (y > lastPosition.coordinate.leftBottom[1]) { + return { + index: -1, + zone: EditorZone.FOOTER + } + } + } else { + // main区域:当前位置大于第一行的上边距 && 小于最后一行的下边距 + if (y >= firstPosition.coordinate.leftTop[1] && y <= lastPosition.coordinate.leftBottom[1]) { return { index: -1, zone: EditorZone.MAIN @@ -344,7 +364,7 @@ export class Position { } // 当前页最后一行 return { - index: firstLetterList[firstLetterList.length - 1]?.index || positionList.length - 1, + index: lastLetterList[lastLetterList.length - 1]?.index || positionList.length - 1, } } return { diff --git a/src/editor/core/zone/Zone.ts b/src/editor/core/zone/Zone.ts index 7f9a2c0..e19d401 100644 --- a/src/editor/core/zone/Zone.ts +++ b/src/editor/core/zone/Zone.ts @@ -6,6 +6,7 @@ import { I18n } from '../i18n/I18n' export class Zone { + private readonly INDICATOR_PADDING = 2 private readonly INDICATOR_TITLE_TRANSLATE = [20, 5] private draw: Draw @@ -14,7 +15,7 @@ export class Zone { private container: HTMLDivElement private currentZone: EditorZone - private headerIndicatorContainer: HTMLDivElement | null + private indicatorContainer: HTMLDivElement | null constructor(draw: Draw) { this.draw = draw @@ -22,7 +23,7 @@ export class Zone { this.options = draw.getOptions() this.container = draw.getContainer() this.currentZone = EditorZone.MAIN - this.headerIndicatorContainer = null + this.indicatorContainer = null } public isHeaderActive(): boolean { @@ -33,6 +34,10 @@ export class Zone { return this.getZone() === EditorZone.MAIN } + public isFooterActive(): boolean { + return this.getZone() === EditorZone.FOOTER + } + public getZone(): EditorZone { return this.currentZone } @@ -46,74 +51,87 @@ export class Zone { isSetCursor: false, isCompute: false }) - // 页眉指示器 - if (this.isHeaderActive()) { - this.drawHeaderZoneIndicator() - } else { - this._clearHeaderZoneIndicator() - } + // 指示器 + this.drawZoneIndicator() } - public drawHeaderZoneIndicator() { - this._clearHeaderZoneIndicator() + public drawZoneIndicator() { + this._clearZoneIndicator() + if (!this.isHeaderActive() && !this.isFooterActive()) return const { scale } = this.options + const isHeaderActive = this.isHeaderActive() const [offsetX, offsetY] = this.INDICATOR_TITLE_TRANSLATE - this.headerIndicatorContainer = document.createElement('div') - this.headerIndicatorContainer.classList.add(`${EDITOR_PREFIX}-header-indicator`) const pageList = this.draw.getPageList() const margins = this.draw.getMargins() const innerWidth = this.draw.getInnerWidth() const pageHeight = this.draw.getHeight() const pageGap = this.draw.getPageGap() const preY = pageHeight + pageGap + // 创建指示器容器 + this.indicatorContainer = document.createElement('div') + this.indicatorContainer.classList.add(`${EDITOR_PREFIX}-zone-indicator`) + // 指示器位置 const header = this.draw.getHeader() - const headerHeight = header.getHeight() - const headerTop = header.getHeaderTop() - + const footer = this.draw.getFooter() + const indicatorHeight = isHeaderActive + ? header.getHeight() + : footer.getHeight() + const indicatorTop = isHeaderActive + ? header.getHeaderTop() + : pageHeight - footer.getFooterBottom() - indicatorHeight for (let p = 0; p < pageList.length; p++) { - const startY = preY * p + headerTop - const indicatorTitle = document.createElement('div') + const startY = preY * p + indicatorTop + const indicatorLeftX = margins[3] - this.INDICATOR_PADDING + const indicatorRightX = margins[3] + innerWidth + this.INDICATOR_PADDING + const indicatorTopY = isHeaderActive + ? startY - this.INDICATOR_PADDING + : startY + indicatorHeight + this.INDICATOR_PADDING + const indicatorBottomY = isHeaderActive + ? startY + indicatorHeight + this.INDICATOR_PADDING + : startY - this.INDICATOR_PADDING // 标题 - indicatorTitle.innerText = this.i18n.t('frame.header') - indicatorTitle.style.top = `${startY + headerHeight}px` + const indicatorTitle = document.createElement('div') + indicatorTitle.innerText = this.i18n.t(`frame.${isHeaderActive ? 'header' : 'footer'}`) + indicatorTitle.style.top = `${indicatorBottomY}px` indicatorTitle.style.transform = `translate(${offsetX * scale}px, ${offsetY * scale}px) scale(${scale})` - this.headerIndicatorContainer.append(indicatorTitle) + this.indicatorContainer.append(indicatorTitle) // 上边线 const lineTop = document.createElement('span') - lineTop.classList.add(`${EDITOR_PREFIX}-header-indicator-border__top`) - lineTop.style.top = `${startY}px` + lineTop.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__top`) + lineTop.style.top = `${indicatorTopY}px` lineTop.style.width = `${innerWidth}px` lineTop.style.marginLeft = `${margins[3]}px` - this.headerIndicatorContainer.append(lineTop) + this.indicatorContainer.append(lineTop) // 左边线 const lineLeft = document.createElement('span') - lineLeft.classList.add(`${EDITOR_PREFIX}-header-indicator-border__left`) + lineLeft.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__left`) lineLeft.style.top = `${startY}px` - lineLeft.style.height = `${headerHeight}px` - lineLeft.style.left = `${margins[3]}px` - this.headerIndicatorContainer.append(lineLeft) + lineLeft.style.height = `${indicatorHeight}px` + lineLeft.style.left = `${indicatorLeftX}px` + this.indicatorContainer.append(lineLeft) // 下边线 const lineBottom = document.createElement('span') - lineBottom.classList.add(`${EDITOR_PREFIX}-header-indicator-border__bottom`) - lineBottom.style.top = `${startY + headerHeight}px` - this.headerIndicatorContainer.append(lineBottom) + lineBottom.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__bottom`) + lineBottom.style.top = `${indicatorBottomY}px` + this.indicatorContainer.append(lineBottom) // 右边线 const lineRight = document.createElement('span') - lineRight.classList.add(`${EDITOR_PREFIX}-header-indicator-border__right`) + lineRight.classList.add(`${EDITOR_PREFIX}-zone-indicator-border__right`) lineRight.style.top = `${startY}px` - lineRight.style.height = `${headerHeight}px` - lineRight.style.left = `${margins[3] + innerWidth}px` - this.headerIndicatorContainer.append(lineRight) + lineRight.style.height = `${indicatorHeight}px` + lineRight.style.left = `${indicatorRightX}px` + this.indicatorContainer.append(lineRight) } - this.container.append(this.headerIndicatorContainer) + this.container.append(this.indicatorContainer) } - private _clearHeaderZoneIndicator() { - this.headerIndicatorContainer?.remove() - this.headerIndicatorContainer = null + private _clearZoneIndicator() { + this.indicatorContainer?.remove() + this.indicatorContainer = null } + } \ No newline at end of file diff --git a/src/editor/dataset/constant/Common.ts b/src/editor/dataset/constant/Common.ts index 470ce7b..de5bf4c 100644 --- a/src/editor/dataset/constant/Common.ts +++ b/src/editor/dataset/constant/Common.ts @@ -1,4 +1,12 @@ +import { MaxHeightRatio } from '../enum/Common' + export const ZERO = '\u200B' export const WRAP = '\n' export const HORIZON_TAB = '\t' -export const NBSP = '\u0020' \ No newline at end of file +export const NBSP = '\u0020' + +export const maxHeightRadioMapping: Record = { + [MaxHeightRatio.HALF]: 1 / 2, + [MaxHeightRatio.ONE_THIRD]: 1 / 3, + [MaxHeightRatio.QUARTER]: 1 / 4 +} \ No newline at end of file diff --git a/src/editor/dataset/constant/Footer.ts b/src/editor/dataset/constant/Footer.ts new file mode 100644 index 0000000..29f57d0 --- /dev/null +++ b/src/editor/dataset/constant/Footer.ts @@ -0,0 +1,7 @@ +import { IFooter } from '../../interface/Footer' +import { MaxHeightRatio } from '../enum/Common' + +export const defaultFooterOption: Readonly> = { + bottom: 30, + maxHeightRadio: MaxHeightRatio.HALF +} \ No newline at end of file diff --git a/src/editor/dataset/constant/Header.ts b/src/editor/dataset/constant/Header.ts index d63fb49..808d9b0 100644 --- a/src/editor/dataset/constant/Header.ts +++ b/src/editor/dataset/constant/Header.ts @@ -1,13 +1,7 @@ import { IHeader } from '../../interface/Header' -import { HeaderMaxHeightRatio } from '../enum/Header' +import { MaxHeightRatio } from '../enum/Common' export const defaultHeaderOption: Readonly> = { top: 30, - maxHeightRadio: HeaderMaxHeightRatio.HALF -} - -export const maxHeightRadioMapping: Record = { - [HeaderMaxHeightRatio.HALF]: 1 / 2, - [HeaderMaxHeightRatio.ONE_THIRD]: 1 / 3, - [HeaderMaxHeightRatio.QUARTER]: 1 / 4 + maxHeightRadio: MaxHeightRatio.HALF } \ No newline at end of file diff --git a/src/editor/dataset/enum/Header.ts b/src/editor/dataset/enum/Common.ts similarity index 64% rename from src/editor/dataset/enum/Header.ts rename to src/editor/dataset/enum/Common.ts index 71c69d1..c4c3b52 100644 --- a/src/editor/dataset/enum/Header.ts +++ b/src/editor/dataset/enum/Common.ts @@ -1,4 +1,4 @@ -export enum HeaderMaxHeightRatio { +export enum MaxHeightRatio { HALF = 'half', ONE_THIRD = 'one-third', QUARTER = 'quarter' diff --git a/src/editor/index.ts b/src/editor/index.ts index 529c9c9..8b0bbc9 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -35,6 +35,9 @@ import { IPageNumber } from './interface/PageNumber' import { defaultPageNumberOption } from './dataset/constant/PageNumber' import { VerticalAlign } from './dataset/enum/VerticalAlign' import { TableBorder } from './dataset/enum/table/Table' +import { IFooter } from './interface/Footer' +import { defaultFooterOption } from './dataset/constant/Footer' +import { MaxHeightRatio } from './dataset/enum/Common' export default class Editor { @@ -48,6 +51,10 @@ export default class Editor { ...defaultHeaderOption, ...options.header } + const footerOptions: Required = { + ...defaultFooterOption, + ...options.footer + } const pageNumberOptions: Required = { ...defaultPageNumberOption, ...options.pageNumber @@ -105,6 +112,7 @@ export default class Editor { inactiveAlpha: 0.6, ...options, header: headerOptions, + footer: footerOptions, pageNumber: pageNumberOptions, watermark: waterMarkOptions, control: controlOptions, @@ -114,18 +122,20 @@ export default class Editor { // 数据处理 let headerElementList: IElement[] = [] let mainElementList: IElement[] = [] + let footerElementList: IElement[] = [] if (Array.isArray(data)) { mainElementList = data } else { headerElementList = data.header || [] mainElementList = data.main + footerElementList = data.footer || [] } - formatElementList(headerElementList, { - editorOptions - }) - formatElementList(mainElementList, { - editorOptions - }) + [headerElementList, mainElementList, footerElementList] + .forEach(elementList => { + formatElementList(elementList, { + editorOptions + }) + }) // 监听 this.listener = new Listener() // 启动 @@ -134,7 +144,8 @@ export default class Editor { editorOptions, { header: headerElementList, - main: mainElementList + main: mainElementList, + footer: footerElementList }, this.listener ) @@ -177,7 +188,8 @@ export { KeyMap, BlockType, PaperDirection, - TableBorder + TableBorder, + MaxHeightRatio } // 对外类型 diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index 178f983..2df4da3 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -3,6 +3,7 @@ import { EditorMode, PageMode, PaperDirection } from '../dataset/enum/Editor' import { ICheckboxOption } from './Checkbox' import { IControlOption } from './Control' import { ICursorOption } from './Cursor' +import { IFooter } from './Footer' import { IHeader } from './Header' import { IMargin } from './Margin' import { IPageNumber } from './PageNumber' @@ -49,6 +50,7 @@ export interface IEditorOption { paperDirection?: PaperDirection; inactiveAlpha?: number; header?: IHeader; + footer?: IFooter; pageNumber?: IPageNumber; watermark?: IWatermark; control?: IControlOption; diff --git a/src/editor/interface/Footer.ts b/src/editor/interface/Footer.ts new file mode 100644 index 0000000..5dc896f --- /dev/null +++ b/src/editor/interface/Footer.ts @@ -0,0 +1,6 @@ +import { MaxHeightRatio } from '../dataset/enum/Common' + +export interface IFooter { + bottom?: number; + maxHeightRadio?: MaxHeightRatio; +} \ No newline at end of file diff --git a/src/editor/interface/Header.ts b/src/editor/interface/Header.ts index c188b03..5f9923e 100644 --- a/src/editor/interface/Header.ts +++ b/src/editor/interface/Header.ts @@ -1,6 +1,6 @@ -import { HeaderMaxHeightRatio } from '../dataset/enum/Header' +import { MaxHeightRatio } from '../dataset/enum/Common' export interface IHeader { top?: number; - maxHeightRadio?: HeaderMaxHeightRatio; + maxHeightRadio?: MaxHeightRatio; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 6eed966..170b99b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,7 +26,11 @@ window.onload = function () { value: '\n', type: ElementType.SEPARATOR }], - main: data + main: data, + footer: [{ + value: 'canvas-editor', + size: 12 + }] }, options )