diff --git a/src/editor/assets/css/index.css b/src/editor/assets/css/index.css
index 8384f8d..1036a24 100644
--- a/src/editor/assets/css/index.css
+++ b/src/editor/assets/css/index.css
@@ -223,7 +223,7 @@
}
.contextmenu-content .contextmenu-item {
- width: 180px;
+ min-width: 140px;
padding: 0 32px 0 16px;
height: 30px;
display: flex;
@@ -248,6 +248,7 @@
flex: 1;
text-align: right;
line-height: 30px;
+ margin-left: 20px;
}
.contextmenu-content .contextmenu-item i {
@@ -269,4 +270,40 @@
.contextmenu-print {
background-image: url(../../assets/images/print.svg);
+}
+
+.contextmenu-insert-row-col {
+ background-image: url(../../assets/images/insert-row-col.svg);
+}
+
+.contextmenu-insert-top-row {
+ background-image: url(../../assets/images/insert-top-row.svg);
+}
+
+.contextmenu-insert-bottom-row {
+ background-image: url(../../assets/images/insert-bottom-row.svg);
+}
+
+.contextmenu-insert-left-col {
+ background-image: url(../../assets/images/insert-left-col.svg);
+}
+
+.contextmenu-insert-right-col {
+ background-image: url(../../assets/images/insert-right-col.svg);
+}
+
+.contextmenu-delete-row-col {
+ background-image: url(../../assets/images/delete-row-col.svg);
+}
+
+.contextmenu-delete-row {
+ background-image: url(../../assets/images/delete-row.svg);
+}
+
+.contextmenu-delete-col {
+ background-image: url(../../assets/images/delete-col.svg);
+}
+
+.contextmenu-delete-table {
+ background-image: url(../../assets/images/delete-table.svg);
}
\ No newline at end of file
diff --git a/src/editor/assets/images/delete-col.svg b/src/editor/assets/images/delete-col.svg
new file mode 100644
index 0000000..5e22407
--- /dev/null
+++ b/src/editor/assets/images/delete-col.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/delete-row-col.svg b/src/editor/assets/images/delete-row-col.svg
new file mode 100644
index 0000000..1a46462
--- /dev/null
+++ b/src/editor/assets/images/delete-row-col.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/delete-row.svg b/src/editor/assets/images/delete-row.svg
new file mode 100644
index 0000000..98ffc5c
--- /dev/null
+++ b/src/editor/assets/images/delete-row.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/delete-table.svg b/src/editor/assets/images/delete-table.svg
new file mode 100644
index 0000000..73c6b76
--- /dev/null
+++ b/src/editor/assets/images/delete-table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/insert-bottom-row.svg b/src/editor/assets/images/insert-bottom-row.svg
new file mode 100644
index 0000000..f18d203
--- /dev/null
+++ b/src/editor/assets/images/insert-bottom-row.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/insert-left-col.svg b/src/editor/assets/images/insert-left-col.svg
new file mode 100644
index 0000000..492cec3
--- /dev/null
+++ b/src/editor/assets/images/insert-left-col.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/insert-right-col.svg b/src/editor/assets/images/insert-right-col.svg
new file mode 100644
index 0000000..38d8306
--- /dev/null
+++ b/src/editor/assets/images/insert-right-col.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/insert-row-col.svg b/src/editor/assets/images/insert-row-col.svg
new file mode 100644
index 0000000..cd80e5f
--- /dev/null
+++ b/src/editor/assets/images/insert-row-col.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/editor/assets/images/insert-top-row.svg b/src/editor/assets/images/insert-top-row.svg
new file mode 100644
index 0000000..1ad48b6
--- /dev/null
+++ b/src/editor/assets/images/insert-top-row.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 faded6f..97968d0 100644
--- a/src/editor/core/command/Command.ts
+++ b/src/editor/core/command/Command.ts
@@ -26,6 +26,13 @@ export class Command {
private static right: Function
private static rowMargin: Function
private static insertTable: Function
+ private static insertTableTopRow: Function
+ private static insertTableBottomRow: Function
+ private static insertTableLeftCol: Function
+ private static insertTableRightCol: Function
+ private static deleteTableRow: Function
+ private static deleteTableCol: Function
+ private static deleteTable: Function
private static image: Function
private static search: Function
private static print: Function
@@ -56,6 +63,13 @@ export class Command {
Command.right = adapt.rowFlex.bind(adapt)
Command.rowMargin = adapt.rowMargin.bind(adapt)
Command.insertTable = adapt.insertTable.bind(adapt)
+ Command.insertTableTopRow = adapt.insertTableTopRow.bind(adapt)
+ Command.insertTableBottomRow = adapt.insertTableBottomRow.bind(adapt)
+ Command.insertTableLeftCol = adapt.insertTableLeftCol.bind(adapt)
+ Command.insertTableRightCol = adapt.insertTableRightCol.bind(adapt)
+ Command.deleteTableRow = adapt.deleteTableRow.bind(adapt)
+ Command.deleteTableCol = adapt.deleteTableCol.bind(adapt)
+ Command.deleteTable = adapt.deleteTable.bind(adapt)
Command.image = adapt.image.bind(adapt)
Command.search = adapt.search.bind(adapt)
Command.print = adapt.print.bind(adapt)
@@ -156,6 +170,34 @@ export class Command {
return Command.insertTable(row, col)
}
+ public executeInsertTableTopRow() {
+ return Command.insertTableTopRow()
+ }
+
+ public executeInsertTableBottomRow() {
+ return Command.insertTableBottomRow()
+ }
+
+ public executeInsertTableLeftCol() {
+ return Command.insertTableLeftCol()
+ }
+
+ public executeInsertTableRightCol() {
+ return Command.insertTableRightCol()
+ }
+
+ public executDeleteTableRow() {
+ return Command.deleteTableRow()
+ }
+
+ public executDeleteTableCol() {
+ return Command.deleteTableCol()
+ }
+
+ public executDeleteTable() {
+ return Command.deleteTable()
+ }
+
public executeImage(payload: IDrawImagePayload) {
return Command.image(payload)
}
diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts
index 1199027..06557a8 100644
--- a/src/editor/core/command/CommandAdapt.ts
+++ b/src/editor/core/command/CommandAdapt.ts
@@ -15,6 +15,7 @@ import { getUUID } from "../../utils"
import { formatElementList } from "../../utils/element"
import { printImageBase64 } from "../../utils/print"
import { Draw } from "../draw/Draw"
+import { TableTool } from "../draw/particle/table/TableTool"
import { CanvasEvent } from "../event/CanvasEvent"
import { HistoryManager } from "../history/HistoryManager"
import { Position } from "../position/Position"
@@ -22,11 +23,14 @@ import { RangeManager } from "../range/RangeManager"
export class CommandAdapt {
+ private readonly defaultWidth: number = 40
+
private draw: Draw
private range: RangeManager
private position: Position
private historyManager: HistoryManager
private canvasEvent: CanvasEvent
+ private tableTool: TableTool
private options: Required
constructor(draw: Draw) {
@@ -35,6 +39,7 @@ export class CommandAdapt {
this.position = draw.getPosition()
this.historyManager = draw.getHistoryManager()
this.canvasEvent = draw.getCanvasEvent()
+ this.tableTool = draw.getTableTool()
this.options = draw.getOptions()
}
@@ -291,6 +296,364 @@ export class CommandAdapt {
this.draw.render({ curIndex, isSetCursor: false })
}
+ public insertTableTopRow() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, trIndex, tableId } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTr = curTrList[trIndex!]
+ // 之前跨行的增加跨行数
+ if (curTr.tdList.length < element.colgroup!.length) {
+ const curTrNo = curTr.tdList[0].rowIndex!
+ for (let t = 0; t < trIndex!; t++) {
+ const tr = curTrList[t]
+ for (let d = 0; d < tr.tdList.length; d++) {
+ const td = tr.tdList[d]
+ if (td.rowspan > 1 && td.rowIndex! + td.rowspan >= curTrNo + 1) {
+ td.rowspan += 1
+ }
+ }
+ }
+ }
+ // 增加当前行
+ const newTrId = getUUID()
+ const newTr: ITr = {
+ height: curTr.height,
+ id: newTrId,
+ tdList: []
+ }
+ for (let t = 0; t < curTr.tdList.length; t++) {
+ const curTd = curTr.tdList[t]
+ const newTdId = getUUID()
+ newTr.tdList.push({
+ id: newTdId,
+ rowspan: 1,
+ colspan: curTd.colspan,
+ value: [{
+ value: ZERO,
+ size: 16,
+ tableId,
+ trId: newTrId,
+ tdId: newTdId
+ }]
+ })
+ }
+ curTrList.splice(trIndex!, 0, newTr)
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: true,
+ index,
+ trIndex,
+ tdIndex: 0,
+ tdId: newTr.tdList[0].id,
+ trId: newTr.id,
+ tableId
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ curIndex: 0 })
+ const position = this.position.getOriginalPositionList()
+ this.tableTool.render(element, position[index!])
+ }
+
+ public insertTableBottomRow() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, trIndex, tableId } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTr = curTrList[trIndex!]
+ const anchorTr = curTrList.length - 1 === trIndex
+ ? curTr
+ : curTrList[trIndex! + 1]
+ // 之前/当前行跨行的增加跨行数
+ if (anchorTr.tdList.length < element.colgroup!.length) {
+ const curTrNo = anchorTr.tdList[0].rowIndex!
+ for (let t = 0; t < trIndex! + 1; t++) {
+ const tr = curTrList[t]
+ for (let d = 0; d < tr.tdList.length; d++) {
+ const td = tr.tdList[d]
+ if (td.rowspan > 1 && td.rowIndex! + td.rowspan >= curTrNo + 1) {
+ td.rowspan += 1
+ }
+ }
+ }
+ }
+ // 增加当前行
+ const newTrId = getUUID()
+ const newTr: ITr = {
+ height: anchorTr.height,
+ id: newTrId,
+ tdList: []
+ }
+ for (let t = 0; t < anchorTr.tdList.length; t++) {
+ const curTd = anchorTr.tdList[t]
+ const newTdId = getUUID()
+ newTr.tdList.push({
+ id: newTdId,
+ rowspan: 1,
+ colspan: curTd.colspan,
+ value: [{
+ value: ZERO,
+ size: 16,
+ tableId,
+ trId: newTrId,
+ tdId: newTdId
+ }]
+ })
+ }
+ curTrList.splice(trIndex! + 1, 0, newTr)
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: true,
+ index,
+ trIndex: trIndex! + 1,
+ tdIndex: 0,
+ tdId: newTr.tdList[0].id,
+ trId: newTr.id,
+ tableId
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ curIndex: 0 })
+ const position = this.position.getOriginalPositionList()
+ this.tableTool.render(element, position[index!])
+ }
+
+ public insertTableLeftCol() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, tdIndex, tableId } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTdIndex = tdIndex!
+ // 增加列
+ for (let t = 0; t < curTrList.length; t++) {
+ const tr = curTrList[t]
+ const tdId = getUUID()
+ tr.tdList.splice(curTdIndex, 0, {
+ id: tdId,
+ rowspan: 1,
+ colspan: 1,
+ value: [{
+ value: ZERO,
+ size: 16,
+ tableId,
+ trId: tr.id,
+ tdId
+ }]
+ })
+ }
+ // 重新计算宽度
+ const colgroup = element.colgroup!
+ colgroup.splice(curTdIndex, 0, {
+ width: this.defaultWidth
+ })
+ const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0)
+ const width = this.draw.getOriginalInnerWidth()
+ if (colgroupWidth > width) {
+ const adjustWidth = (colgroupWidth - width) / colgroup.length
+ for (let g = 0; g < colgroup.length; g++) {
+ const group = colgroup[g]
+ group.width -= adjustWidth
+ }
+ }
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: true,
+ index,
+ trIndex: 0,
+ tdIndex: curTdIndex,
+ tdId: curTrList[0].tdList[curTdIndex].id,
+ trId: curTrList[0].id,
+ tableId
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ curIndex: 0 })
+ const position = this.position.getOriginalPositionList()
+ this.tableTool.render(element, position[index!])
+ }
+
+ public insertTableRightCol() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, tdIndex, tableId } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTdIndex = tdIndex! + 1
+ // 增加列
+ for (let t = 0; t < curTrList.length; t++) {
+ const tr = curTrList[t]
+ const tdId = getUUID()
+ tr.tdList.splice(curTdIndex, 0, {
+ id: tdId,
+ rowspan: 1,
+ colspan: 1,
+ value: [{
+ value: ZERO,
+ size: 16,
+ tableId,
+ trId: tr.id,
+ tdId
+ }]
+ })
+ }
+ // 重新计算宽度
+ const colgroup = element.colgroup!
+ colgroup.splice(curTdIndex, 0, {
+ width: this.defaultWidth
+ })
+ const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0)
+ const width = this.draw.getOriginalInnerWidth()
+ if (colgroupWidth > width) {
+ const adjustWidth = (colgroupWidth - width) / colgroup.length
+ for (let g = 0; g < colgroup.length; g++) {
+ const group = colgroup[g]
+ group.width -= adjustWidth
+ }
+ }
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: true,
+ index,
+ trIndex: 0,
+ tdIndex: curTdIndex,
+ tdId: curTrList[0].tdList[curTdIndex].id,
+ trId: curTrList[0].id,
+ tableId
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ curIndex: 0 })
+ const position = this.position.getOriginalPositionList()
+ this.tableTool.render(element, position[index!])
+ }
+
+ public deleteTableRow() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, trIndex } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTr = curTrList[trIndex!]
+ // 如果是最后一行,直接删除整个表格
+ if (curTrList.length <= 1) {
+ this.deleteTable()
+ return
+ }
+ // 补跨行
+ for (let d = 0; d < curTr.tdList.length; d++) {
+ const td = curTr.tdList[d]
+ if (td.rowspan > 1) {
+ let start = trIndex! + 1
+ while (start < trIndex! + td.rowspan) {
+ const tdId = getUUID()
+ const tr = curTrList[start]
+ tr.tdList.splice(d, 0, {
+ id: tdId,
+ rowspan: 1,
+ colspan: 1,
+ value: [{
+ value: ZERO,
+ size: 16,
+ tableId: element.id,
+ trId: tr.id,
+ tdId
+ }]
+ })
+ start += 1
+ }
+ }
+ }
+ // 删除当前行
+ curTrList.splice(trIndex!, 1)
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: false
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ isSetCursor: false })
+ this.tableTool.dispose()
+ }
+
+ public deleteTableCol() {
+ const positionContext = this.position.getPositionContext()
+ if (!positionContext.isTable) return
+ const { index, tdIndex, trIndex } = positionContext
+ const originalElementList = this.draw.getOriginalElementList()
+ const element = originalElementList[index!]
+ const curTrList = element.trList!
+ const curTd = curTrList[trIndex!].tdList[tdIndex!]
+ const curColIndex = curTd.colIndex!
+ // 如果是最后一列,直接删除整个表格
+ const moreTdTr = curTrList.find(tr => tr.tdList.length > 1)
+ if (!moreTdTr) {
+ this.deleteTable()
+ return
+ }
+ // 跨列处理
+ for (let t = 0; t < curTrList.length; t++) {
+ const tr = curTrList[t]
+ for (let d = 0; d < tr.tdList.length; d++) {
+ const td = tr.tdList[d]
+ if (td.colspan > 1) {
+ const tdColIndex = td.colIndex!
+ // 交叉减去一列
+ if (tdColIndex <= curColIndex && tdColIndex + td.colspan - 1 >= curColIndex) {
+ td.colspan -= 1
+ }
+ }
+ }
+ }
+ // 删除当前列
+ for (let t = 0; t < curTrList.length; t++) {
+ const tr = curTrList[t]
+ let start = -1
+ for (let d = 0; d < tr.tdList.length; d++) {
+ const td = tr.tdList[d]
+ if (td.colIndex === curColIndex) {
+ start = d
+ }
+ }
+ if (~start) {
+ tr.tdList.splice(start, 1)
+ }
+ }
+ element.colgroup?.splice(curColIndex, 1)
+ // 重新设置上下文
+ this.position.setPositionContext({
+ isTable: false
+ })
+ this.range.setRange(0, 0)
+ // 重新渲染
+ this.draw.render({ isSetCursor: false })
+ this.tableTool.dispose()
+ }
+
+ public deleteTable() {
+ const positionContext = this.position.getPositionContext()
+ if (positionContext.isTable) {
+ const originalElementList = this.draw.getOriginalElementList()
+ originalElementList.splice(positionContext.index!, 1)
+ const curIndex = positionContext.index! - 1
+ this.position.setPositionContext({
+ isTable: false,
+ index: curIndex
+ })
+ this.range.setRange(curIndex, curIndex)
+ this.draw.render({ curIndex })
+ this.tableTool.dispose()
+ }
+ }
+
public image(payload: IDrawImagePayload) {
const { startIndex, endIndex } = this.range.getRange()
if (startIndex === 0 && endIndex === 0) return
diff --git a/src/editor/core/contextmenu/menus/tableMenus.ts b/src/editor/core/contextmenu/menus/tableMenus.ts
new file mode 100644
index 0000000..9da7bdc
--- /dev/null
+++ b/src/editor/core/contextmenu/menus/tableMenus.ts
@@ -0,0 +1,78 @@
+import { IRegisterContextMenu } from "../../../interface/contextmenu/ContextMenu"
+import { Command } from "../../command/Command"
+
+export const tableMenus: IRegisterContextMenu[] = [
+ {
+ isDivider: true
+ },
+ {
+ name: '插入行列',
+ icon: 'insert-row-col',
+ when: (payload) => {
+ return payload.isInTable
+ },
+ childMenus: [
+ {
+ name: '上方插入1行',
+ icon: 'insert-top-row',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executeInsertTableTopRow()
+ }
+ }, {
+ name: '下方插入1行',
+ icon: 'insert-bottom-row',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executeInsertTableBottomRow()
+ }
+ }, {
+ name: '左侧插入1列',
+ icon: 'insert-left-col',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executeInsertTableLeftCol()
+ }
+ }, {
+ name: '右侧插入1列',
+ icon: 'insert-right-col',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executeInsertTableRightCol()
+ }
+ }
+ ]
+ },
+ {
+ name: '删除行列',
+ icon: 'delete-row-col',
+ when: (payload) => {
+ return payload.isInTable
+ },
+ childMenus: [
+ {
+ name: '删除1行',
+ icon: 'delete-row',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executDeleteTableRow()
+ }
+ },
+ {
+ name: '删除一列',
+ icon: 'delete-col',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executDeleteTableCol()
+ }
+ }, {
+ name: '删除整个表格',
+ icon: 'delete-table',
+ when: () => true,
+ callback: (command: Command) => {
+ command.executDeleteTable()
+ }
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts
index 68e2f78..b9adc50 100644
--- a/src/editor/core/draw/Draw.ts
+++ b/src/editor/core/draw/Draw.ts
@@ -123,6 +123,11 @@ export class Draw {
return width - margins[1] - margins[3]
}
+ public getOriginalInnerWidth(): number {
+ const { width, margins } = this.options
+ return width - margins[1] - margins[3]
+ }
+
public getMargins(): number[] {
return this.options.margins.map(m => m * this.options.scale)
}
diff --git a/src/editor/core/draw/particle/table/TableParticle.ts b/src/editor/core/draw/particle/table/TableParticle.ts
index caca0cc..c1a5fd5 100644
--- a/src/editor/core/draw/particle/table/TableParticle.ts
+++ b/src/editor/core/draw/particle/table/TableParticle.ts
@@ -69,7 +69,7 @@ export class TableParticle {
let colIndex = 0
const preTd = tr.tdList[d - 1]
if (preTd) {
- colIndex = preTd.colIndex! + (offsetXIndex || 1)
+ colIndex = preTd.colIndex! + offsetXIndex + 1
if (preTd.colspan > 1) {
colIndex += preTd.colspan - 1
}
diff --git a/src/editor/index.ts b/src/editor/index.ts
index 51705a6..a225ef1 100644
--- a/src/editor/index.ts
+++ b/src/editor/index.ts
@@ -11,6 +11,7 @@ import { formatElementList } from './utils/element'
import { Register } from './core/register/Register'
import { globalMenus } from './core/contextmenu/menus/GlobalMenus'
import { ContextMenu } from './core/contextmenu/ContextMenu'
+import { tableMenus } from './core/contextmenu/menus/tableMenus'
import { IRegisterContextMenu } from './interface/contextmenu/ContextMenu'
export default class Editor {
@@ -64,6 +65,7 @@ export default class Editor {
contextMenu
})
this.register.contextMenuList(globalMenus)
+ this.register.contextMenuList(tableMenus)
}
}