diff --git a/src/editor/assets/css/contextmenu/contextmenu.css b/src/editor/assets/css/contextmenu/contextmenu.css index 8ad16bc..dc6a7af 100644 --- a/src/editor/assets/css/contextmenu/contextmenu.css +++ b/src/editor/assets/css/contextmenu/contextmenu.css @@ -145,4 +145,16 @@ .ce-contextmenu-vertical-align-bottom { background-image: url(../../../assets/images/vertical-align-bottom.svg); +} + +.ce-contextmenu-border-all { + background-image: url(../../../assets/images/table-border-all.svg); +} + +.ce-contextmenu-border-empty { + background-image: url(../../../assets/images/table-border-empty.svg); +} + +.ce-contextmenu-border-external { + background-image: url(../../../assets/images/table-border-external.svg); } \ No newline at end of file diff --git a/src/editor/assets/images/table-border-all.svg b/src/editor/assets/images/table-border-all.svg new file mode 100644 index 0000000..338ae14 --- /dev/null +++ b/src/editor/assets/images/table-border-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/assets/images/table-border-empty.svg b/src/editor/assets/images/table-border-empty.svg new file mode 100644 index 0000000..4a6baa6 --- /dev/null +++ b/src/editor/assets/images/table-border-empty.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/assets/images/table-border-external.svg b/src/editor/assets/images/table-border-external.svg new file mode 100644 index 0000000..2ad564f --- /dev/null +++ b/src/editor/assets/images/table-border-external.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/core/command/Command.ts b/src/editor/core/command/Command.ts index c5a3085..7803eb2 100644 --- a/src/editor/core/command/Command.ts +++ b/src/editor/core/command/Command.ts @@ -1,4 +1,4 @@ -import { IElement, ImageDisplay, INavigateInfo, VerticalAlign } from '../..' +import { IElement, ImageDisplay, INavigateInfo, TableBorder, VerticalAlign } from '../..' import { EditorMode, PageMode, PaperDirection } from '../../dataset/enum/Editor' import { RowFlex } from '../../dataset/enum/Row' import { IDrawImagePayload, IPainterOptions } from '../../interface/Draw' @@ -49,6 +49,7 @@ export class Command { private static mergeTableCell: CommandAdapt['mergeTableCell'] private static cancelMergeTableCell: CommandAdapt['cancelMergeTableCell'] private static tableTdVerticalAlign: CommandAdapt['tableTdVerticalAlign'] + private static tableBorderType: CommandAdapt['tableBorderType'] private static image: CommandAdapt['image'] private static hyperlink: CommandAdapt['hyperlink'] private static deleteHyperlink: CommandAdapt['deleteHyperlink'] @@ -124,6 +125,7 @@ export class Command { Command.mergeTableCell = adapt.mergeTableCell.bind(adapt) Command.cancelMergeTableCell = adapt.cancelMergeTableCell.bind(adapt) Command.tableTdVerticalAlign = adapt.tableTdVerticalAlign.bind(adapt) + Command.tableBorderType = adapt.tableBorderType.bind(adapt) Command.image = adapt.image.bind(adapt) Command.hyperlink = adapt.hyperlink.bind(adapt) Command.deleteHyperlink = adapt.deleteHyperlink.bind(adapt) @@ -323,6 +325,10 @@ export class Command { return Command.tableTdVerticalAlign(payload) } + public executeTableBorderType(payload: TableBorder) { + return Command.tableBorderType(payload) + } + public executeHyperlink(payload: IElement) { return Command.hyperlink(payload) } diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index e17161c..7e9d33d 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -6,6 +6,7 @@ import { EditorContext, EditorMode, PageMode, PaperDirection } from '../../datas import { ElementType } from '../../dataset/enum/Element' import { ElementStyleKey } from '../../dataset/enum/ElementStyle' import { RowFlex } from '../../dataset/enum/Row' +import { TableBorder } from '../../dataset/enum/table/Table' import { VerticalAlign } from '../../dataset/enum/VerticalAlign' import { IDrawImagePayload, IPainterOptions } from '../../interface/Draw' import { IEditorOption, IEditorResult } from '../../interface/Editor' @@ -1032,9 +1033,9 @@ export class CommandAdapt { const element = originalElementList[index!] const curTd = element?.trList?.[trIndex!]?.tdList?.[tdIndex!] if ( - !curTd - || curTd.verticalAlign === payload - || (!curTd.verticalAlign && payload === VerticalAlign.TOP) + !curTd || + curTd.verticalAlign === payload || + (!curTd.verticalAlign && payload === VerticalAlign.TOP) ) { return } @@ -1046,6 +1047,27 @@ export class CommandAdapt { }) } + public tableBorderType(payload: TableBorder) { + const isReadonly = this.draw.isReadonly() + if (isReadonly) return + const positionContext = this.position.getPositionContext() + if (!positionContext.isTable) return + const { index } = positionContext + const originalElementList = this.draw.getOriginalElementList() + const element = originalElementList[index!] + if ( + (!element.borderType && payload === TableBorder.ALL) || + element.borderType === payload + ) { + return + } + element.borderType = payload + const { endIndex } = this.range.getRange() + this.draw.render({ + curIndex: endIndex + }) + } + public hyperlink(payload: IElement) { const isReadonly = this.draw.isReadonly() if (isReadonly) return diff --git a/src/editor/core/contextmenu/menus/tableMenus.ts b/src/editor/core/contextmenu/menus/tableMenus.ts index 0aa15f8..73a7934 100644 --- a/src/editor/core/contextmenu/menus/tableMenus.ts +++ b/src/editor/core/contextmenu/menus/tableMenus.ts @@ -1,4 +1,5 @@ import { VerticalAlign } from '../../../dataset/enum/VerticalAlign' +import { TableBorder } from '../../../dataset/enum/table/Table' import { IRegisterContextMenu } from '../../../interface/contextmenu/ContextMenu' import { Command } from '../../command/Command' @@ -6,6 +7,39 @@ export const tableMenus: IRegisterContextMenu[] = [ { isDivider: true }, + { + i18nPath: 'contextmenu.table.border', + icon: 'border-all', + when: (payload) => { + return !payload.isReadonly && payload.isInTable + }, + childMenus: [ + { + i18nPath: 'contextmenu.table.borderAll', + icon: 'border-all', + when: () => true, + callback: (command: Command) => { + command.executeTableBorderType(TableBorder.ALL) + } + }, + { + i18nPath: 'contextmenu.table.borderEmpty', + icon: 'border-empty', + when: () => true, + callback: (command: Command) => { + command.executeTableBorderType(TableBorder.EMPTY) + } + }, + { + i18nPath: 'contextmenu.table.borderExternal', + icon: 'border-external', + when: () => true, + callback: (command: Command) => { + command.executeTableBorderType(TableBorder.EXTERNAL) + } + } + ] + }, { i18nPath: 'contextmenu.table.verticalAlign', icon: 'vertical-align', diff --git a/src/editor/core/draw/particle/table/TableParticle.ts b/src/editor/core/draw/particle/table/TableParticle.ts index 9444b75..180ab45 100644 --- a/src/editor/core/draw/particle/table/TableParticle.ts +++ b/src/editor/core/draw/particle/table/TableParticle.ts @@ -1,8 +1,17 @@ -import { ElementType, IElement } from '../../../..' +import { ElementType, IElement, TableBorder } from '../../../..' import { IEditorOption } from '../../../../interface/Editor' import { RangeManager } from '../../../range/RangeManager' import { Draw } from '../../Draw' +interface IDrawTableBorderOption { + ctx: CanvasRenderingContext2D; + startX: number; + startY: number; + width: number; + height: number; + isDrawFullBorder?: boolean; +} + export class TableParticle { private range: RangeManager @@ -13,14 +22,19 @@ export class TableParticle { this.options = draw.getOptions() } - private _drawBorder(ctx: CanvasRenderingContext2D, startX: number, startY: number, width: number, height: number) { + private _drawBorder(payload: IDrawTableBorderOption) { + const { ctx, startX, startY, width, height, isDrawFullBorder } = payload ctx.beginPath() const x = Math.round(startX) const y = Math.round(startY) ctx.translate(0.5, 0.5) - ctx.moveTo(x, y + height) - ctx.lineTo(x, y) - ctx.lineTo(x + width, y) + if (isDrawFullBorder) { + ctx.rect(x, y, width, height) + } else { + ctx.moveTo(x, y + height) + ctx.lineTo(x, y) + ctx.lineTo(x + width, y) + } ctx.stroke() ctx.translate(-0.5, -0.5) } @@ -159,31 +173,41 @@ export class TableParticle { } public render(ctx: CanvasRenderingContext2D, element: IElement, startX: number, startY: number) { - const { colgroup, trList } = element - if (!colgroup || !trList) return + const { colgroup, trList, borderType } = element + if (!colgroup || !trList || borderType === TableBorder.EMPTY) return const { scale } = this.options const tableWidth = element.width! * scale const tableHeight = element.height! * scale + const isExternalBorderType = borderType === TableBorder.EXTERNAL ctx.save() // 渲染边框 - this._drawBorder(ctx, startX, startY, tableWidth, tableHeight) - // 渲染表格 - for (let t = 0; t < trList.length; t++) { - const tr = trList[t] - for (let d = 0; d < tr.tdList.length; d++) { - const td = tr.tdList[d] - const width = td.width! * scale - const height = td.height! * scale - const x = Math.round(td.x! * scale + startX + width) - const y = Math.round(td.y! * scale + startY) - ctx.translate(0.5, 0.5) - // 绘制线条 - ctx.beginPath() - ctx.moveTo(x, y) - ctx.lineTo(x, y + height) - ctx.lineTo(x - width, y + height) - ctx.stroke() - ctx.translate(-0.5, -0.5) + this._drawBorder({ + ctx, + startX, + startY, + width: tableWidth, + height: tableHeight, + isDrawFullBorder: isExternalBorderType + }) + if (!isExternalBorderType) { + // 渲染表格 + for (let t = 0; t < trList.length; t++) { + const tr = trList[t] + for (let d = 0; d < tr.tdList.length; d++) { + const td = tr.tdList[d] + const width = td.width! * scale + const height = td.height! * scale + const x = Math.round(td.x! * scale + startX + width) + const y = Math.round(td.y! * scale + startY) + ctx.translate(0.5, 0.5) + // 绘制线条 + ctx.beginPath() + ctx.moveTo(x, y) + ctx.lineTo(x, y + height) + ctx.lineTo(x - width, y + height) + ctx.stroke() + ctx.translate(-0.5, -0.5) + } } } ctx.restore() diff --git a/src/editor/core/i18n/lang/en.json b/src/editor/core/i18n/lang/en.json index e3c4f90..7c03137 100644 --- a/src/editor/core/i18n/lang/en.json +++ b/src/editor/core/i18n/lang/en.json @@ -39,7 +39,11 @@ "verticalAlign": "Vertical align", "verticalAlignTop": "Top", "verticalAlignMiddle": "Middle", - "verticalAlignBottom": "Bottom" + "verticalAlignBottom": "Bottom", + "border": "Table border", + "borderAll": "All", + "borderEmpty": "Empty", + "borderExternal": "External" } }, "datePicker": { diff --git a/src/editor/core/i18n/lang/zh-CN.json b/src/editor/core/i18n/lang/zh-CN.json index c81bc3d..7c69215 100644 --- a/src/editor/core/i18n/lang/zh-CN.json +++ b/src/editor/core/i18n/lang/zh-CN.json @@ -39,7 +39,11 @@ "verticalAlign": "垂直对齐", "verticalAlignTop": "顶端对齐", "verticalAlignMiddle": "垂直居中", - "verticalAlignBottom": "底端对齐" + "verticalAlignBottom": "底端对齐", + "border": "表格边框", + "borderAll": "所有框线", + "borderEmpty": "无框线", + "borderExternal": "外侧框线" } }, "datePicker": { diff --git a/src/editor/dataset/constant/Element.ts b/src/editor/dataset/constant/Element.ts index da40daf..f3d4946 100644 --- a/src/editor/dataset/constant/Element.ts +++ b/src/editor/dataset/constant/Element.ts @@ -43,6 +43,7 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array = [ 'rowMargin', 'dashArray', 'trList', + 'borderType', 'width', 'height', 'url', diff --git a/src/editor/dataset/enum/table/Table.ts b/src/editor/dataset/enum/table/Table.ts new file mode 100644 index 0000000..a1dc22d --- /dev/null +++ b/src/editor/dataset/enum/table/Table.ts @@ -0,0 +1,5 @@ +export enum TableBorder { + ALL = 'all', + EMPTY = 'empty', + EXTERNAL = 'external' +} \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index f7d7041..529c9c9 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -34,6 +34,7 @@ import { defaultCursorOption } from './dataset/constant/Cursor' import { IPageNumber } from './interface/PageNumber' import { defaultPageNumberOption } from './dataset/constant/PageNumber' import { VerticalAlign } from './dataset/enum/VerticalAlign' +import { TableBorder } from './dataset/enum/table/Table' export default class Editor { @@ -175,7 +176,8 @@ export { Command, KeyMap, BlockType, - PaperDirection + PaperDirection, + TableBorder } // 对外类型 diff --git a/src/editor/interface/Element.ts b/src/editor/interface/Element.ts index bdd3ead..5dba458 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 { TableBorder } from '../dataset/enum/table/Table' import { IBlock } from './Block' import { ICheckbox } from './Checkbox' import { IControl } from './Control' @@ -32,6 +33,7 @@ export interface IElementStyle { export interface ITableAttr { colgroup?: IColgroup[]; trList?: ITr[]; + borderType?: TableBorder; } export interface ITableElement { @@ -40,14 +42,14 @@ export interface ITableElement { tableId?: string; } +export type ITable = ITableAttr & ITableElement + export interface IHyperlinkElement { valueList?: IElement[]; url?: string; hyperlinkId?: string; } -export type ITable = ITableAttr & ITableElement - export interface ISuperscriptSubscript { actualSize?: number; } diff --git a/src/main.ts b/src/main.ts index c6e0bf7..6eed966 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1182,4 +1182,4 @@ window.onload = function () { } ]) -} +} \ No newline at end of file