feat:add table component
This commit is contained in:
+14
@@ -96,10 +96,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="menu-divider"></div>
|
<div class="menu-divider"></div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
|
<div class="menu-item__table">
|
||||||
|
<i></i>
|
||||||
|
</div>
|
||||||
|
<div class="menu-item__table__collapse">
|
||||||
|
<div class="table-close">×</div>
|
||||||
|
<div class="table-title">
|
||||||
|
<span class="table-select">插入</span>
|
||||||
|
<span>表格</span>
|
||||||
|
</div>
|
||||||
|
<div class="table-panel"></div>
|
||||||
|
</div>
|
||||||
<div class="menu-item__image">
|
<div class="menu-item__image">
|
||||||
<i></i>
|
<i></i>
|
||||||
<input type="file" id="image" accept=".png, .jpg, .jpeg">
|
<input type="file" id="image" accept=".png, .jpg, .jpeg">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="menu-divider"></div>
|
||||||
|
<div class="menu-item">
|
||||||
<div class="menu-item__search">
|
<div class="menu-item__search">
|
||||||
<i></i>
|
<i></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect x=".5" y=".5" width="12" height="12" rx="1" transform="translate(1 1)" stroke="#3D4757"/><path fill="#3D4757" fill-rule="nonzero" d="M2 9h12v1H2zm0-4h12v1H2z"/><path fill="#3D4757" fill-rule="nonzero" d="M5 1h1v13H5zm4 0h1v13H9z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 345 B |
@@ -21,6 +21,7 @@ export class Command {
|
|||||||
private static center: Function
|
private static center: Function
|
||||||
private static right: Function
|
private static right: Function
|
||||||
private static rowMargin: Function
|
private static rowMargin: Function
|
||||||
|
private static insertTable: Function
|
||||||
private static image: Function
|
private static image: Function
|
||||||
private static search: Function
|
private static search: Function
|
||||||
private static print: Function
|
private static print: Function
|
||||||
@@ -46,6 +47,7 @@ export class Command {
|
|||||||
Command.center = adapt.rowFlex.bind(adapt)
|
Command.center = adapt.rowFlex.bind(adapt)
|
||||||
Command.right = adapt.rowFlex.bind(adapt)
|
Command.right = adapt.rowFlex.bind(adapt)
|
||||||
Command.rowMargin = adapt.rowMargin.bind(adapt)
|
Command.rowMargin = adapt.rowMargin.bind(adapt)
|
||||||
|
Command.insertTable = adapt.insertTable.bind(adapt)
|
||||||
Command.image = adapt.image.bind(adapt)
|
Command.image = adapt.image.bind(adapt)
|
||||||
Command.search = adapt.search.bind(adapt)
|
Command.search = adapt.search.bind(adapt)
|
||||||
Command.print = adapt.print.bind(adapt)
|
Command.print = adapt.print.bind(adapt)
|
||||||
@@ -124,7 +126,11 @@ export class Command {
|
|||||||
return Command.rowMargin(payload)
|
return Command.rowMargin(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片上传、搜索、打印
|
// 表格、图片上传、搜索、打印
|
||||||
|
public executeInsertTable(row: number, col: number) {
|
||||||
|
return Command.insertTable(row, col)
|
||||||
|
}
|
||||||
|
|
||||||
public executeImage(payload: IDrawImagePayload) {
|
public executeImage(payload: IDrawImagePayload) {
|
||||||
return Command.image(payload)
|
return Command.image(payload)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import { RowFlex } from "../../dataset/enum/Row"
|
|||||||
import { IDrawImagePayload } from "../../interface/Draw"
|
import { IDrawImagePayload } from "../../interface/Draw"
|
||||||
import { IEditorOption } from "../../interface/Editor"
|
import { IEditorOption } from "../../interface/Editor"
|
||||||
import { IElement, IElementStyle } from "../../interface/Element"
|
import { IElement, IElementStyle } from "../../interface/Element"
|
||||||
|
import { IColgroup } from "../../interface/table/Colgroup"
|
||||||
|
import { ITd } from "../../interface/table/Td"
|
||||||
|
import { ITr } from "../../interface/table/Tr"
|
||||||
import { getUUID } from "../../utils"
|
import { getUUID } from "../../utils"
|
||||||
|
import { formatElementList } from "../../utils/element"
|
||||||
import { printImageBase64 } from "../../utils/print"
|
import { printImageBase64 } from "../../utils/print"
|
||||||
import { Draw } from "../draw/Draw"
|
import { Draw } from "../draw/Draw"
|
||||||
import { HistoryManager } from "../history/HistoryManager"
|
import { HistoryManager } from "../history/HistoryManager"
|
||||||
@@ -214,6 +218,55 @@ export class CommandAdapt {
|
|||||||
this.draw.render({ curIndex, isSetCursor })
|
this.draw.render({ curIndex, isSetCursor })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public insertTable(row: number, col: number) {
|
||||||
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
|
if (startIndex === 0 && endIndex === 0) return
|
||||||
|
const elementList = this.draw.getElementList()
|
||||||
|
const { width, margins } = this.options
|
||||||
|
const innerWidth = width - margins[1] - margins[3]
|
||||||
|
// colgroup
|
||||||
|
const colgroup: IColgroup[] = []
|
||||||
|
const colWidth = innerWidth / col
|
||||||
|
for (let c = 0; c < col; c++) {
|
||||||
|
colgroup.push({
|
||||||
|
width: colWidth
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// trlist
|
||||||
|
const trList: ITr[] = []
|
||||||
|
for (let r = 0; r < row; r++) {
|
||||||
|
const tdList: ITd[] = []
|
||||||
|
const tr: ITr = {
|
||||||
|
height: 40,
|
||||||
|
tdList
|
||||||
|
}
|
||||||
|
for (let c = 0; c < col; c++) {
|
||||||
|
tdList.push({
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [{ value: ZERO, size: 16 }]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
trList.push(tr)
|
||||||
|
}
|
||||||
|
const element: IElement = {
|
||||||
|
type: ElementType.TABLE,
|
||||||
|
value: ZERO,
|
||||||
|
colgroup,
|
||||||
|
trList
|
||||||
|
}
|
||||||
|
// 格式化element
|
||||||
|
formatElementList([element])
|
||||||
|
const curIndex = startIndex + 1
|
||||||
|
if (startIndex === endIndex) {
|
||||||
|
elementList.splice(curIndex, 0, element)
|
||||||
|
} else {
|
||||||
|
elementList.splice(curIndex, endIndex - startIndex, element)
|
||||||
|
}
|
||||||
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex, isSetCursor: false })
|
||||||
|
}
|
||||||
|
|
||||||
public image(payload: IDrawImagePayload) {
|
public image(payload: IDrawImagePayload) {
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
if (startIndex === 0 && endIndex === 0) return
|
if (startIndex === 0 && endIndex === 0) return
|
||||||
|
|||||||
+160
-37
@@ -1,6 +1,6 @@
|
|||||||
import { ZERO } from "../../dataset/constant/Common"
|
import { ZERO } from "../../dataset/constant/Common"
|
||||||
import { RowFlex } from "../../dataset/enum/Row"
|
import { RowFlex } from "../../dataset/enum/Row"
|
||||||
import { IDrawOption } from "../../interface/Draw"
|
import { IDrawOption, IDrawRowPayload, IDrawRowResult } from "../../interface/Draw"
|
||||||
import { IEditorOption } from "../../interface/Editor"
|
import { IEditorOption } from "../../interface/Editor"
|
||||||
import { IElement, IElementMetrics, IElementPosition, IElementStyle } from "../../interface/Element"
|
import { IElement, IElementMetrics, IElementPosition, IElementStyle } from "../../interface/Element"
|
||||||
import { IRow, IRowElement } from "../../interface/Row"
|
import { IRow, IRowElement } from "../../interface/Row"
|
||||||
@@ -23,6 +23,7 @@ import { ImageParticle } from "./particle/ImageParticle"
|
|||||||
import { TextParticle } from "./particle/TextParticle"
|
import { TextParticle } from "./particle/TextParticle"
|
||||||
import { PageNumber } from "./frame/PageNumber"
|
import { PageNumber } from "./frame/PageNumber"
|
||||||
import { GlobalObserver } from "../observer/GlobalObserver"
|
import { GlobalObserver } from "../observer/GlobalObserver"
|
||||||
|
import { TableParticle } from "./particle/table/TableParticle"
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ export class Draw {
|
|||||||
private historyManager: HistoryManager
|
private historyManager: HistoryManager
|
||||||
private imageParticle: ImageParticle
|
private imageParticle: ImageParticle
|
||||||
private textParticle: TextParticle
|
private textParticle: TextParticle
|
||||||
|
private tableParticle: TableParticle
|
||||||
private pageNumber: PageNumber
|
private pageNumber: PageNumber
|
||||||
|
|
||||||
private rowList: IRow[]
|
private rowList: IRow[]
|
||||||
@@ -83,6 +85,7 @@ export class Draw {
|
|||||||
this.highlight = new Highlight(this)
|
this.highlight = new Highlight(this)
|
||||||
this.imageParticle = new ImageParticle(this)
|
this.imageParticle = new ImageParticle(this)
|
||||||
this.textParticle = new TextParticle(this)
|
this.textParticle = new TextParticle(this)
|
||||||
|
this.tableParticle = new TableParticle(this)
|
||||||
this.pageNumber = new PageNumber(this)
|
this.pageNumber = new PageNumber(this)
|
||||||
new GlobalObserver(this)
|
new GlobalObserver(this)
|
||||||
|
|
||||||
@@ -135,6 +138,10 @@ export class Draw {
|
|||||||
return this.options.defaultBasicRowMarginHeight * this.options.scale
|
return this.options.defaultBasicRowMarginHeight * this.options.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTdPadding(): number {
|
||||||
|
return this.options.tdPadding * this.options.scale
|
||||||
|
}
|
||||||
|
|
||||||
public getContainer(): HTMLDivElement {
|
public getContainer(): HTMLDivElement {
|
||||||
return this.container
|
return this.container
|
||||||
}
|
}
|
||||||
@@ -202,6 +209,15 @@ export class Draw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getElementList(): IElement[] {
|
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
|
||||||
|
}
|
||||||
|
return this.elementList
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOriginalElementList() {
|
||||||
return this.elementList
|
return this.elementList
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,25 +322,25 @@ export class Draw {
|
|||||||
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${(el.size || defaultSize) * scale}px ${el.font || defaultFont}`
|
return `${el.italic ? 'italic ' : ''}${el.bold ? 'bold ' : ''}${(el.size || defaultSize) * scale}px ${el.font || defaultFont}`
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeRowList() {
|
private _computeRowList(innerWidth: number, elementList: IElement[]) {
|
||||||
const { defaultSize, defaultRowMargin, scale } = this.options
|
const { defaultSize, defaultRowMargin, scale, tdPadding } = this.options
|
||||||
const innerWidth = this.getInnerWidth()
|
|
||||||
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
|
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
|
||||||
|
const tdGap = tdPadding * 2
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
|
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
|
||||||
const rowList: IRow[] = []
|
const rowList: IRow[] = []
|
||||||
if (this.elementList.length) {
|
if (elementList.length) {
|
||||||
rowList.push({
|
rowList.push({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
ascent: 0,
|
ascent: 0,
|
||||||
elementList: [],
|
elementList: [],
|
||||||
rowFlex: this.elementList?.[1]?.rowFlex
|
rowFlex: elementList?.[1]?.rowFlex
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this.elementList.length; i++) {
|
for (let i = 0; i < elementList.length; i++) {
|
||||||
const curRow: IRow = rowList[rowList.length - 1]
|
const curRow: IRow = rowList[rowList.length - 1]
|
||||||
const element = this.elementList[i]
|
const element = elementList[i]
|
||||||
const rowMargin = defaultBasicRowMarginHeight * (element.rowMargin || defaultRowMargin)
|
const rowMargin = defaultBasicRowMarginHeight * (element.rowMargin || defaultRowMargin)
|
||||||
let metrics: IElementMetrics = {
|
let metrics: IElementMetrics = {
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -350,6 +366,40 @@ export class Draw {
|
|||||||
metrics.boundingBoxDescent = elementHeight
|
metrics.boundingBoxDescent = elementHeight
|
||||||
}
|
}
|
||||||
metrics.boundingBoxAscent = 0
|
metrics.boundingBoxAscent = 0
|
||||||
|
} else if (element.type === ElementType.TABLE) {
|
||||||
|
// 计算表格行列
|
||||||
|
this.tableParticle.computeRowColInfo(element)
|
||||||
|
// 计算表格内元素信息
|
||||||
|
const trList = element.trList!
|
||||||
|
for (let t = 0; t < trList.length; t++) {
|
||||||
|
const tr = trList[t]
|
||||||
|
let maxTrHeight = 0
|
||||||
|
for (let d = 0; d < tr.tdList.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
const rowList = this._computeRowList((td.width! - tdGap) * scale, td.value)
|
||||||
|
const rowHeight = rowList.reduce((pre, cur) => pre + cur.height, 0)
|
||||||
|
td.rowList = rowList
|
||||||
|
// 移除缩放导致的行高变化-渲染时会进行缩放调整
|
||||||
|
const curTrHeight = (rowHeight + tdGap) / scale
|
||||||
|
if (maxTrHeight < curTrHeight) {
|
||||||
|
maxTrHeight = curTrHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.height = maxTrHeight
|
||||||
|
}
|
||||||
|
// 需要重新计算表格内值
|
||||||
|
this.tableParticle.computeRowColInfo(element)
|
||||||
|
// 计算出表格高度
|
||||||
|
const tableHeight = trList.reduce((pre, cur) => pre + cur.height, 0)
|
||||||
|
const tableWidth = element.colgroup!.reduce((pre, cur) => pre + cur.width, 0)
|
||||||
|
element.width = tableWidth
|
||||||
|
element.height = tableHeight
|
||||||
|
const elementWidth = tableWidth * scale
|
||||||
|
const elementHeight = tableHeight * scale
|
||||||
|
metrics.width = elementWidth
|
||||||
|
metrics.height = elementHeight
|
||||||
|
metrics.boundingBoxDescent = elementHeight
|
||||||
|
metrics.boundingBoxAscent = 0
|
||||||
} else {
|
} else {
|
||||||
metrics.height = (element.size || this.options.defaultSize) * scale
|
metrics.height = (element.size || this.options.defaultSize) * scale
|
||||||
ctx.font = this._getFont(element)
|
ctx.font = this._getFont(element)
|
||||||
@@ -367,7 +417,12 @@ export class Draw {
|
|||||||
style: this._getFont(element, scale)
|
style: this._getFont(element, scale)
|
||||||
}
|
}
|
||||||
// 超过限定宽度
|
// 超过限定宽度
|
||||||
if (curRow.width + metrics.width > innerWidth || (i !== 0 && element.value === ZERO)) {
|
const preElement = elementList[i - 1]
|
||||||
|
if (
|
||||||
|
(preElement && preElement.type === ElementType.TABLE)
|
||||||
|
|| curRow.width + metrics.width > innerWidth
|
||||||
|
|| (i !== 0 && element.value === ZERO)
|
||||||
|
) {
|
||||||
rowList.push({
|
rowList.push({
|
||||||
width: metrics.width,
|
width: metrics.width,
|
||||||
height,
|
height,
|
||||||
@@ -388,35 +443,29 @@ export class Draw {
|
|||||||
curRow.elementList.push(rowElement)
|
curRow.elementList.push(rowElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.rowList = rowList
|
return rowList
|
||||||
}
|
}
|
||||||
|
|
||||||
private _drawElement(positionList: IElementPosition[], rowList: IRow[], pageNo: number) {
|
private _drawRow(ctx: CanvasRenderingContext2D, payload: IDrawRowPayload): IDrawRowResult {
|
||||||
const width = this.getWidth()
|
const { positionList, rowList, pageNo, startX, startY, startIndex, innerWidth } = payload
|
||||||
const height = this.getHeight()
|
const { scale, tdPadding } = this.options
|
||||||
const margins = this.getMargins()
|
const tdGap = tdPadding * 2
|
||||||
const ctx = this.ctxList[pageNo]
|
let x = startX
|
||||||
ctx.clearRect(0, 0, width, height)
|
let y = startY
|
||||||
// 绘制背景
|
let index = startIndex
|
||||||
this.background.render(ctx)
|
|
||||||
// 绘制页边距
|
|
||||||
const leftTopPoint: [number, number] = [margins[3], margins[0]]
|
|
||||||
this.margin.render(ctx)
|
|
||||||
// 渲染元素
|
|
||||||
let x = leftTopPoint[0]
|
|
||||||
let y = leftTopPoint[1]
|
|
||||||
let index = positionList.length
|
|
||||||
for (let i = 0; i < rowList.length; i++) {
|
for (let i = 0; i < rowList.length; i++) {
|
||||||
const curRow = rowList[i]
|
const curRow = rowList[i]
|
||||||
// 计算行偏移量(行居左、居中、居右)
|
// 计算行偏移量(行居左、居中、居右)
|
||||||
if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) {
|
if (curRow.rowFlex && curRow.rowFlex !== RowFlex.LEFT) {
|
||||||
const canvasInnerWidth = width - margins[1] - margins[3]
|
|
||||||
if (curRow.rowFlex === RowFlex.CENTER) {
|
if (curRow.rowFlex === RowFlex.CENTER) {
|
||||||
x += (canvasInnerWidth - curRow.width) / 2
|
x += (innerWidth - curRow.width) / 2
|
||||||
} else {
|
} else {
|
||||||
x += canvasInnerWidth - curRow.width
|
x += innerWidth - curRow.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 当前td所在位置
|
||||||
|
let tablePreX = x
|
||||||
|
let tablePreY = y
|
||||||
for (let j = 0; j < curRow.elementList.length; j++) {
|
for (let j = 0; j < curRow.elementList.length; j++) {
|
||||||
const element = curRow.elementList[j]
|
const element = curRow.elementList[j]
|
||||||
const metrics = element.metrics
|
const metrics = element.metrics
|
||||||
@@ -456,25 +505,89 @@ export class Draw {
|
|||||||
if (element.type === ElementType.IMAGE) {
|
if (element.type === ElementType.IMAGE) {
|
||||||
this.textParticle.complete()
|
this.textParticle.complete()
|
||||||
this.imageParticle.render(ctx, element, x, y + offsetY)
|
this.imageParticle.render(ctx, element, x, y + offsetY)
|
||||||
|
} else if (element.type === ElementType.TABLE) {
|
||||||
|
this.tableParticle.render(ctx, element, x, y)
|
||||||
} else {
|
} else {
|
||||||
this.textParticle.record(ctx, element, x, y + offsetY)
|
this.textParticle.record(ctx, element, x, y + offsetY)
|
||||||
}
|
}
|
||||||
// 选区绘制
|
// 选区绘制
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
if (startIndex !== endIndex && startIndex < index && index <= endIndex) {
|
if (startIndex !== endIndex && startIndex < index && index <= endIndex) {
|
||||||
let rangeWidth = metrics.width
|
const positionContext = this.position.getPositionContext()
|
||||||
if (rangeWidth === 0 && curRow.elementList.length === 1) {
|
// 表格需限定上下文
|
||||||
rangeWidth = this.options.rangeMinWidth
|
if (
|
||||||
|
(!positionContext.isTable && !element.tdId)
|
||||||
|
|| positionContext.tdId === element.tdId
|
||||||
|
) {
|
||||||
|
let rangeWidth = metrics.width
|
||||||
|
if (rangeWidth === 0 && curRow.elementList.length === 1) {
|
||||||
|
rangeWidth = this.options.rangeMinWidth
|
||||||
|
}
|
||||||
|
this.range.render(ctx, x, y, rangeWidth, curRow.height)
|
||||||
}
|
}
|
||||||
this.range.render(ctx, x, y, rangeWidth, curRow.height)
|
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
x += metrics.width
|
x += metrics.width
|
||||||
|
// 绘制表格内元素
|
||||||
|
if (element.type === ElementType.TABLE) {
|
||||||
|
for (let t = 0; t < element.trList!.length; t++) {
|
||||||
|
const tr = element.trList![t]
|
||||||
|
for (let d = 0; d < tr.tdList!.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
td.positionList = []
|
||||||
|
const drawRowResult = this._drawRow(ctx, {
|
||||||
|
positionList: td.positionList,
|
||||||
|
rowList: td.rowList!,
|
||||||
|
pageNo,
|
||||||
|
startIndex: 0,
|
||||||
|
startX: (td.x! + tdPadding) * scale + tablePreX,
|
||||||
|
startY: td.y! * scale + tablePreY,
|
||||||
|
innerWidth: (td.width! - tdGap) * scale
|
||||||
|
})
|
||||||
|
x = drawRowResult.x
|
||||||
|
y = drawRowResult.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 恢复初始x、y
|
||||||
|
x = tablePreX
|
||||||
|
y = tablePreY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.textParticle.complete()
|
this.textParticle.complete()
|
||||||
x = leftTopPoint[0]
|
x = startX
|
||||||
y += curRow.height
|
y += curRow.height
|
||||||
}
|
}
|
||||||
|
return { x, y, index }
|
||||||
|
}
|
||||||
|
|
||||||
|
private _drawPage(positionList: IElementPosition[], rowList: IRow[], pageNo: number) {
|
||||||
|
const width = this.getWidth()
|
||||||
|
const height = this.getHeight()
|
||||||
|
const margins = this.getMargins()
|
||||||
|
const innerWidth = this.getInnerWidth()
|
||||||
|
const ctx = this.ctxList[pageNo]
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
// 绘制背景
|
||||||
|
this.background.render(ctx)
|
||||||
|
// 绘制页边距
|
||||||
|
const leftTopPoint: [number, number] = [margins[3], margins[0]]
|
||||||
|
this.margin.render(ctx)
|
||||||
|
// 渲染元素
|
||||||
|
let x = leftTopPoint[0]
|
||||||
|
let y = leftTopPoint[1]
|
||||||
|
let index = positionList.length
|
||||||
|
const drawRowResult = this._drawRow(ctx, {
|
||||||
|
positionList,
|
||||||
|
rowList,
|
||||||
|
pageNo,
|
||||||
|
startIndex: index,
|
||||||
|
startX: x,
|
||||||
|
startY: y,
|
||||||
|
innerWidth
|
||||||
|
})
|
||||||
|
x = drawRowResult.x
|
||||||
|
y = drawRowResult.y
|
||||||
|
index = drawRowResult.index
|
||||||
// 绘制页码
|
// 绘制页码
|
||||||
this.pageNumber.render(ctx, pageNo)
|
this.pageNumber.render(ctx, pageNo)
|
||||||
// 搜索匹配绘制
|
// 搜索匹配绘制
|
||||||
@@ -491,14 +604,15 @@ export class Draw {
|
|||||||
isComputeRowList = true
|
isComputeRowList = true
|
||||||
} = payload || {}
|
} = payload || {}
|
||||||
const height = this.getHeight()
|
const height = this.getHeight()
|
||||||
|
const innerWidth = this.getInnerWidth()
|
||||||
// 计算行信息
|
// 计算行信息
|
||||||
if (isComputeRowList) {
|
if (isComputeRowList) {
|
||||||
this._computeRowList()
|
this.rowList = this._computeRowList(innerWidth, this.elementList)
|
||||||
}
|
}
|
||||||
// 清除光标等副作用
|
// 清除光标等副作用
|
||||||
this.cursor.recoveryCursor()
|
this.cursor.recoveryCursor()
|
||||||
this.position.setPositionList([])
|
this.position.setPositionList([])
|
||||||
const positionList = this.position.getPositionList()
|
const positionList = this.position.getOriginalPositionList()
|
||||||
// 按页渲染
|
// 按页渲染
|
||||||
const margins = this.getMargins()
|
const margins = this.getMargins()
|
||||||
const marginHeight = margins[0] + margins[2]
|
const marginHeight = margins[0] + margins[2]
|
||||||
@@ -522,7 +636,7 @@ export class Draw {
|
|||||||
this._createPage(i)
|
this._createPage(i)
|
||||||
}
|
}
|
||||||
const rowList = pageRowList[i]
|
const rowList = pageRowList[i]
|
||||||
this._drawElement(positionList, rowList, i)
|
this._drawPage(positionList, rowList, i)
|
||||||
}
|
}
|
||||||
// 移除多余页
|
// 移除多余页
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -540,7 +654,14 @@ export class Draw {
|
|||||||
if (curIndex === undefined) {
|
if (curIndex === undefined) {
|
||||||
curIndex = positionList.length - 1
|
curIndex = positionList.length - 1
|
||||||
}
|
}
|
||||||
this.position.setCursorPosition(positionList[curIndex!] || null)
|
const positionContext = this.position.getPositionContext()
|
||||||
|
if (positionContext.isTable) {
|
||||||
|
const { index, trIndex, tdIndex } = positionContext
|
||||||
|
const tablePosition = this.elementList[index!].trList?.[trIndex!].tdList[tdIndex!].positionList?.[curIndex!]
|
||||||
|
this.position.setCursorPosition(tablePosition || null)
|
||||||
|
} else {
|
||||||
|
this.position.setCursorPosition(positionList[curIndex!] || null)
|
||||||
|
}
|
||||||
this.cursor.drawCursor()
|
this.cursor.drawCursor()
|
||||||
}
|
}
|
||||||
// 历史记录用于undo、redo
|
// 历史记录用于undo、redo
|
||||||
@@ -549,8 +670,10 @@ export class Draw {
|
|||||||
const oldElementList = deepClone(this.elementList)
|
const oldElementList = deepClone(this.elementList)
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
const pageNo = this.pageNo
|
const pageNo = this.pageNo
|
||||||
|
const oldPositionContext = deepClone(this.position.getPositionContext())
|
||||||
this.historyManager.execute(function () {
|
this.historyManager.execute(function () {
|
||||||
self.setPageNo(pageNo)
|
self.setPageNo(pageNo)
|
||||||
|
self.position.setPositionContext(oldPositionContext)
|
||||||
self.range.setRange(startIndex, endIndex)
|
self.range.setRange(startIndex, endIndex)
|
||||||
self.elementList = deepClone(oldElementList)
|
self.elementList = deepClone(oldElementList)
|
||||||
self.render({ curIndex, isSubmitHistory: false })
|
self.render({ curIndex, isSubmitHistory: false })
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class Search {
|
|||||||
const searchMatch = this.draw.getSearchMatch()
|
const searchMatch = this.draw.getSearchMatch()
|
||||||
if (!searchMatch || !searchMatch.length) return
|
if (!searchMatch || !searchMatch.length) return
|
||||||
const searchMatchList = searchMatch.flat()
|
const searchMatchList = searchMatch.flat()
|
||||||
const positionList = this.position.getPositionList()
|
const positionList = this.position.getOriginalPositionList()
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.globalAlpha = this.options.searchMatchAlpha
|
ctx.globalAlpha = this.options.searchMatchAlpha
|
||||||
ctx.fillStyle = this.options.searchMatchColor
|
ctx.fillStyle = this.options.searchMatchColor
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import { IElement } from "../../../.."
|
||||||
|
import { IEditorOption } from "../../../../interface/Editor"
|
||||||
|
import { Draw } from "../../Draw"
|
||||||
|
|
||||||
|
export class TableParticle {
|
||||||
|
|
||||||
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
|
constructor(draw: Draw) {
|
||||||
|
this.options = draw.getOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private _drawBorder(ctx: CanvasRenderingContext2D, startX: number, startY: number, width: number, height: number) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(startX, startY)
|
||||||
|
ctx.lineTo(startX + width, startY)
|
||||||
|
ctx.lineTo(startX + width, startY + height)
|
||||||
|
ctx.lineTo(startX, startY + height)
|
||||||
|
ctx.closePath()
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
private _drawRange(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number) {
|
||||||
|
const { rangeAlpha, rangeColor } = this.options
|
||||||
|
ctx.save()
|
||||||
|
ctx.globalAlpha = rangeAlpha
|
||||||
|
ctx.fillStyle = rangeColor
|
||||||
|
ctx.fillRect(x, y, width, height)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
public computeRowColInfo(element: IElement) {
|
||||||
|
const { colgroup, trList } = element
|
||||||
|
if (!colgroup || !trList) return
|
||||||
|
let x = 0
|
||||||
|
let y = 0
|
||||||
|
for (let t = 0; t < trList.length; t++) {
|
||||||
|
const tr = trList[t]
|
||||||
|
// 表格最后一行
|
||||||
|
const isLastTr = trList.length - 1 === t
|
||||||
|
// 当前行最小高度
|
||||||
|
let rowMinHeight = 0
|
||||||
|
for (let d = 0; d < tr.tdList.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
// 计算之前行x轴偏移量
|
||||||
|
let offsetXIndex = 0
|
||||||
|
if (trList.length > 1 && t !== 0) {
|
||||||
|
for (let pT = 0; pT < t; pT++) {
|
||||||
|
const pTr = trList[pT]
|
||||||
|
// 相同x轴是否存在跨行
|
||||||
|
for (let pD = 0; pD < pTr.tdList.length; pD++) {
|
||||||
|
const pTd = pTr.tdList[pD]
|
||||||
|
const pTdX = pTd.x!
|
||||||
|
const pTdY = pTd.y!
|
||||||
|
const pTdWidth = pTd.width!
|
||||||
|
const pTdHeight = pTd.height!
|
||||||
|
// 小于
|
||||||
|
if (pTdX < x) continue
|
||||||
|
if (pTdX > x) break
|
||||||
|
if (pTd.x === x && pTdY + pTdHeight > y) {
|
||||||
|
x += pTdWidth
|
||||||
|
offsetXIndex += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 计算格列数
|
||||||
|
let colIndex = 0
|
||||||
|
const preTd = tr.tdList[d - 1]
|
||||||
|
if (preTd) {
|
||||||
|
colIndex = preTd.colIndex! + (offsetXIndex || 1)
|
||||||
|
if (preTd.colspan > 1) {
|
||||||
|
colIndex += preTd.colspan - 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
colIndex += offsetXIndex
|
||||||
|
}
|
||||||
|
// 计算格宽高
|
||||||
|
let width = 0
|
||||||
|
for (let col = 0; col < td.colspan; col++) {
|
||||||
|
width += colgroup[col + colIndex].width
|
||||||
|
}
|
||||||
|
let height = 0
|
||||||
|
for (let row = 0; row < td.rowspan; row++) {
|
||||||
|
height += trList[row + t].height
|
||||||
|
}
|
||||||
|
// y偏移量
|
||||||
|
if (rowMinHeight === 0 || rowMinHeight > height) {
|
||||||
|
rowMinHeight = height
|
||||||
|
}
|
||||||
|
// 当前行最后一个td
|
||||||
|
const isLastRowTd = tr.tdList.length - 1 === d
|
||||||
|
// 当前列最后一个td
|
||||||
|
let isLastColTd = isLastTr
|
||||||
|
if (!isLastColTd) {
|
||||||
|
if (td.rowspan > 1) {
|
||||||
|
const nextTrLength = trList.length - 1 - t
|
||||||
|
isLastColTd = td.rowspan - 1 === nextTrLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 当前表格最后一个td
|
||||||
|
const isLastTd = isLastTr && isLastRowTd
|
||||||
|
td.isLastRowTd = isLastRowTd
|
||||||
|
td.isLastColTd = isLastColTd
|
||||||
|
td.isLastTd = isLastTd
|
||||||
|
// 修改当前格clientBox
|
||||||
|
td.x = x
|
||||||
|
td.y = y
|
||||||
|
td.width = width
|
||||||
|
td.height = height
|
||||||
|
td.rowIndex = t
|
||||||
|
td.colIndex = colIndex
|
||||||
|
// 当前列x轴累加
|
||||||
|
x += width
|
||||||
|
// 一行中的最后td
|
||||||
|
if (isLastRowTd && !isLastTd) {
|
||||||
|
x = 0
|
||||||
|
y += rowMinHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(ctx: CanvasRenderingContext2D, element: IElement, startX: number, startY: number) {
|
||||||
|
const { colgroup, trList } = element
|
||||||
|
if (!colgroup || !trList) return
|
||||||
|
const { scale } = this.options
|
||||||
|
const tableWidth = element.width! * scale
|
||||||
|
const tableHeight = element.height! * scale
|
||||||
|
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 { isLastRowTd, isLastColTd, isLastTd } = td
|
||||||
|
const width = td.width! * scale
|
||||||
|
const height = td.height! * scale
|
||||||
|
const x = td.x! * scale + startX + width
|
||||||
|
const y = td.y! * scale + startY
|
||||||
|
// 绘制线条
|
||||||
|
ctx.beginPath()
|
||||||
|
if (isLastRowTd && !isLastTd) {
|
||||||
|
// 是否跨行到底
|
||||||
|
if (y + height < startY + tableHeight) {
|
||||||
|
ctx.moveTo(x, y + height)
|
||||||
|
ctx.lineTo(x - width, y + height)
|
||||||
|
}
|
||||||
|
} else if (isLastColTd && !isLastTd) {
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.lineTo(x, y + height)
|
||||||
|
} else if (!isLastRowTd && !isLastColTd && !isLastTd) {
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.lineTo(x, y + height)
|
||||||
|
ctx.lineTo(x - width, y + height)
|
||||||
|
} else if (isLastTd) {
|
||||||
|
// 是否跨列到最右
|
||||||
|
if (x + width === startX + tableWidth) {
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.lineTo(x, y + height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -80,7 +80,12 @@ export class CanvasEvent {
|
|||||||
this.draw.setPageNo(Number(pageIndex))
|
this.draw.setPageNo(Number(pageIndex))
|
||||||
}
|
}
|
||||||
// 结束位置
|
// 结束位置
|
||||||
const { index: endIndex } = this.position.getPositionByXY(evt.offsetX, evt.offsetY)
|
const positionResult = this.position.getPositionByXY({
|
||||||
|
x: evt.offsetX,
|
||||||
|
y: evt.offsetY
|
||||||
|
})
|
||||||
|
const { index, isTable, tdValueIndex } = positionResult
|
||||||
|
let endIndex = isTable ? tdValueIndex! : index
|
||||||
let end = ~endIndex ? endIndex : 0
|
let end = ~endIndex ? endIndex : 0
|
||||||
// 开始位置
|
// 开始位置
|
||||||
let start = this.mouseDownStartIndex
|
let start = this.mouseDownStartIndex
|
||||||
@@ -105,15 +110,46 @@ export class CanvasEvent {
|
|||||||
this.draw.setPageNo(Number(pageIndex))
|
this.draw.setPageNo(Number(pageIndex))
|
||||||
}
|
}
|
||||||
this.isAllowDrag = true
|
this.isAllowDrag = true
|
||||||
const { index, isDirectHit, isImage } = this.position.getPositionByXY(evt.offsetX, evt.offsetY)
|
const positionResult = this.position.getPositionByXY({
|
||||||
|
x: evt.offsetX,
|
||||||
|
y: evt.offsetY
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
index,
|
||||||
|
isDirectHit,
|
||||||
|
isImage,
|
||||||
|
isTable,
|
||||||
|
trIndex,
|
||||||
|
tdIndex,
|
||||||
|
tdValueIndex,
|
||||||
|
tdId,
|
||||||
|
trId,
|
||||||
|
tableId
|
||||||
|
} = positionResult
|
||||||
|
// 设置位置上下文
|
||||||
|
this.position.setPositionContext({
|
||||||
|
isTable: isTable || false,
|
||||||
|
index,
|
||||||
|
trIndex,
|
||||||
|
tdIndex,
|
||||||
|
tdId,
|
||||||
|
trId,
|
||||||
|
tableId
|
||||||
|
})
|
||||||
// 记录选区开始位置
|
// 记录选区开始位置
|
||||||
this.mouseDownStartIndex = index
|
this.mouseDownStartIndex = isTable ? tdValueIndex! : index
|
||||||
// 绘制
|
// 绘制
|
||||||
const isDirectHitImage = isDirectHit && isImage
|
const isDirectHitImage = isDirectHit && isImage
|
||||||
if (~index) {
|
if (~index) {
|
||||||
this.range.setRange(index, index)
|
let curIndex = index
|
||||||
|
if (isTable) {
|
||||||
|
this.range.setRange(tdValueIndex!, tdValueIndex!)
|
||||||
|
curIndex = tdValueIndex!
|
||||||
|
} else {
|
||||||
|
this.range.setRange(index, index)
|
||||||
|
}
|
||||||
this.draw.render({
|
this.draw.render({
|
||||||
curIndex: index,
|
curIndex,
|
||||||
isSubmitHistory: false,
|
isSubmitHistory: false,
|
||||||
isSetCursor: !isDirectHitImage,
|
isSetCursor: !isDirectHitImage,
|
||||||
isComputeRowList: false
|
isComputeRowList: false
|
||||||
@@ -124,7 +160,8 @@ export class CanvasEvent {
|
|||||||
if (isDirectHitImage) {
|
if (isDirectHitImage) {
|
||||||
const elementList = this.draw.getElementList()
|
const elementList = this.draw.getElementList()
|
||||||
const positionList = this.position.getPositionList()
|
const positionList = this.position.getPositionList()
|
||||||
this.imageParticle.drawResizer(elementList[index], positionList[index])
|
const curIndex = isTable ? tdValueIndex! : index
|
||||||
|
this.imageParticle.drawResizer(elementList[curIndex], positionList[curIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +204,16 @@ export class CanvasEvent {
|
|||||||
this.range.setRange(curIndex, curIndex)
|
this.range.setRange(curIndex, curIndex)
|
||||||
this.draw.render({ curIndex })
|
this.draw.render({ curIndex })
|
||||||
} else if (evt.key === KeyMap.Enter) {
|
} else if (evt.key === KeyMap.Enter) {
|
||||||
|
// 表格需要上下文信息
|
||||||
|
const positionContext = this.position.getPositionContext()
|
||||||
|
let restArg = {}
|
||||||
|
if (positionContext.isTable) {
|
||||||
|
const { tdId, trId, tableId } = positionContext
|
||||||
|
restArg = { tdId, trId, tableId }
|
||||||
|
}
|
||||||
const enterText: IElement = {
|
const enterText: IElement = {
|
||||||
value: ZERO
|
value: ZERO,
|
||||||
|
...restArg
|
||||||
}
|
}
|
||||||
if (isCollspace) {
|
if (isCollspace) {
|
||||||
elementList.splice(index + 1, 0, enterText)
|
elementList.splice(index + 1, 0, enterText)
|
||||||
@@ -277,8 +322,16 @@ export class CanvasEvent {
|
|||||||
const { index } = cursorPosition
|
const { index } = cursorPosition
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
const isCollspace = startIndex === endIndex
|
const isCollspace = startIndex === endIndex
|
||||||
|
// 表格需要上下文信息
|
||||||
|
const positionContext = this.position.getPositionContext()
|
||||||
|
let restArg = {}
|
||||||
|
if (positionContext.isTable) {
|
||||||
|
const { tdId, trId, tableId } = positionContext
|
||||||
|
restArg = { tdId, trId, tableId }
|
||||||
|
}
|
||||||
const inputData: IElement[] = data.split('').map(value => ({
|
const inputData: IElement[] = data.split('').map(value => ({
|
||||||
value
|
value,
|
||||||
|
...restArg
|
||||||
}))
|
}))
|
||||||
let start = 0
|
let start = 0
|
||||||
if (isCollspace) {
|
if (isCollspace) {
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
import { ElementType } from "../.."
|
import { ElementType } from "../.."
|
||||||
import { ZERO } from "../../dataset/constant/Common"
|
import { ZERO } from "../../dataset/constant/Common"
|
||||||
import { IEditorOption } from "../../interface/Editor"
|
import { IEditorOption } from "../../interface/Editor"
|
||||||
import { IElement, IElementPosition } from "../../interface/Element"
|
import { IElementPosition } from "../../interface/Element"
|
||||||
import { ICurrentPosition } from "../../interface/Position"
|
import { ICurrentPosition, IGetPositionByXYPayload, IPositionContext } from "../../interface/Position"
|
||||||
import { Draw } from "../draw/Draw"
|
import { Draw } from "../draw/Draw"
|
||||||
|
|
||||||
export class Position {
|
export class Position {
|
||||||
|
|
||||||
private cursorPosition: IElementPosition | null
|
private cursorPosition: IElementPosition | null
|
||||||
|
private positionContext: IPositionContext
|
||||||
private positionList: IElementPosition[]
|
private positionList: IElementPosition[]
|
||||||
private elementList: IElement[]
|
|
||||||
|
|
||||||
private draw: Draw
|
private draw: Draw
|
||||||
private options: Required<IEditorOption>
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
this.positionList = []
|
this.positionList = []
|
||||||
this.elementList = []
|
|
||||||
this.cursorPosition = null
|
this.cursorPosition = null
|
||||||
|
this.positionContext = {
|
||||||
|
isTable: false
|
||||||
|
}
|
||||||
|
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
this.options = draw.getOptions()
|
this.options = draw.getOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getOriginalPositionList(): IElementPosition[] {
|
||||||
|
return this.positionList
|
||||||
|
}
|
||||||
|
|
||||||
public getPositionList(): IElementPosition[] {
|
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 || []
|
||||||
|
}
|
||||||
return this.positionList
|
return this.positionList
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +43,7 @@ export class Position {
|
|||||||
this.positionList = payload
|
this.positionList = payload
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCursorPosition(position: IElementPosition) {
|
public setCursorPosition(position: IElementPosition | null) {
|
||||||
this.cursorPosition = position
|
this.cursorPosition = position
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,22 +51,69 @@ export class Position {
|
|||||||
return this.cursorPosition
|
return this.cursorPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPositionByXY(x: number, y: number): ICurrentPosition {
|
public getPositionContext(): IPositionContext {
|
||||||
this.elementList = this.draw.getElementList()
|
return this.positionContext
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPositionContext(payload: IPositionContext) {
|
||||||
|
this.positionContext = payload
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPositionByXY(payload: IGetPositionByXYPayload): ICurrentPosition {
|
||||||
|
const { x, y, isTable } = payload
|
||||||
|
let { elementList, positionList } = payload
|
||||||
|
if (!elementList) {
|
||||||
|
elementList = this.draw.getOriginalElementList()
|
||||||
|
}
|
||||||
|
if (!positionList) {
|
||||||
|
positionList = this.positionList
|
||||||
|
}
|
||||||
const curPageNo = this.draw.getPageNo()
|
const curPageNo = this.draw.getPageNo()
|
||||||
for (let j = 0; j < this.positionList.length; j++) {
|
for (let j = 0; j < positionList.length; j++) {
|
||||||
const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = this.positionList[j]
|
const { index, pageNo, coordinate: { leftTop, rightTop, leftBottom } } = positionList[j]
|
||||||
if (curPageNo !== pageNo) continue
|
if (curPageNo !== pageNo) continue
|
||||||
// 命中元素
|
// 命中元素
|
||||||
if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) {
|
if (leftTop[0] <= x && rightTop[0] >= x && leftTop[1] <= y && leftBottom[1] >= y) {
|
||||||
let curPostionIndex = j
|
let curPostionIndex = j
|
||||||
const element = this.elementList[j]
|
const element = elementList[j]
|
||||||
|
// 表格被命中
|
||||||
|
if (element.type === ElementType.TABLE) {
|
||||||
|
for (let t = 0; t < element.trList!.length; t++) {
|
||||||
|
const tr = element.trList![t]
|
||||||
|
for (let d = 0; d < tr.tdList.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
const tablePosition = this.getPositionByXY({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
td,
|
||||||
|
tablePosition: positionList[j],
|
||||||
|
isTable: true,
|
||||||
|
elementList: td.value,
|
||||||
|
positionList: td.positionList
|
||||||
|
})
|
||||||
|
if (~tablePosition.index) {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
isImage: tablePosition.isImage,
|
||||||
|
isDirectHit: tablePosition.isDirectHit,
|
||||||
|
isTable: true,
|
||||||
|
tdIndex: d,
|
||||||
|
trIndex: t,
|
||||||
|
tdValueIndex: tablePosition.index,
|
||||||
|
tdId: td.id,
|
||||||
|
trId: tr.id,
|
||||||
|
tableId: element.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 图片区域均为命中
|
// 图片区域均为命中
|
||||||
if (element.type === ElementType.IMAGE) {
|
if (element.type === ElementType.IMAGE) {
|
||||||
return { index: curPostionIndex, isDirectHit: true, isImage: true }
|
return { index: curPostionIndex, isDirectHit: true, isImage: true }
|
||||||
}
|
}
|
||||||
// 判断是否在文字中间前后
|
// 判断是否在文字中间前后
|
||||||
if (this.elementList[index].value !== ZERO) {
|
if (elementList[index].value !== ZERO) {
|
||||||
const valueWidth = rightTop[0] - leftTop[0]
|
const valueWidth = rightTop[0] - leftTop[0]
|
||||||
if (x < leftTop[0] + valueWidth / 2) {
|
if (x < leftTop[0] + valueWidth / 2) {
|
||||||
curPostionIndex = j - 1
|
curPostionIndex = j - 1
|
||||||
@@ -66,8 +125,22 @@ export class Position {
|
|||||||
// 非命中区域
|
// 非命中区域
|
||||||
let isLastArea = false
|
let isLastArea = false
|
||||||
let curPostionIndex = -1
|
let curPostionIndex = -1
|
||||||
|
// 判断是否在表格内
|
||||||
|
if (isTable) {
|
||||||
|
const { td, tablePosition } = payload
|
||||||
|
if (td && tablePosition) {
|
||||||
|
const { leftTop } = tablePosition.coordinate
|
||||||
|
const tdX = td.x! + leftTop[0]
|
||||||
|
const tdY = td.y! + leftTop[1]
|
||||||
|
const tdWidth = td.width!
|
||||||
|
const tdHeight = td.height!
|
||||||
|
if (!(tdX < x && x < tdX + tdWidth && tdY < y && y < tdY + tdHeight)) {
|
||||||
|
return { index: curPostionIndex }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 判断所属行是否存在元素
|
// 判断所属行是否存在元素
|
||||||
const firstLetterList = this.positionList.filter(p => p.isLastLetter && p.pageNo === curPageNo)
|
const firstLetterList = positionList.filter(p => p.isLastLetter && p.pageNo === curPageNo)
|
||||||
for (let j = 0; j < firstLetterList.length; j++) {
|
for (let j = 0; j < firstLetterList.length; j++) {
|
||||||
const { index, pageNo, coordinate: { leftTop, leftBottom } } = firstLetterList[j]
|
const { index, pageNo, coordinate: { leftTop, leftBottom } } = firstLetterList[j]
|
||||||
if (curPageNo !== pageNo) continue
|
if (curPageNo !== pageNo) continue
|
||||||
@@ -75,7 +148,7 @@ export class Position {
|
|||||||
const isHead = x < this.options.margins[3]
|
const isHead = x < this.options.margins[3]
|
||||||
// 是否在头部
|
// 是否在头部
|
||||||
if (isHead) {
|
if (isHead) {
|
||||||
const headIndex = this.positionList.findIndex(p => p.rowNo === firstLetterList[j].rowNo)
|
const headIndex = positionList.findIndex(p => p.rowNo === firstLetterList[j].rowNo)
|
||||||
curPostionIndex = ~headIndex ? headIndex : index
|
curPostionIndex = ~headIndex ? headIndex : index
|
||||||
} else {
|
} else {
|
||||||
curPostionIndex = index
|
curPostionIndex = index
|
||||||
@@ -86,7 +159,7 @@ export class Position {
|
|||||||
}
|
}
|
||||||
if (!isLastArea) {
|
if (!isLastArea) {
|
||||||
// 当前页最后一行
|
// 当前页最后一行
|
||||||
return { index: firstLetterList[firstLetterList.length - 1]?.index || this.positionList.length - 1 }
|
return { index: firstLetterList[firstLetterList.length - 1]?.index || positionList.length - 1 }
|
||||||
}
|
}
|
||||||
return { index: curPostionIndex }
|
return { index: curPostionIndex }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum ElementType {
|
export enum ElementType {
|
||||||
TEXT = 'text',
|
TEXT = 'text',
|
||||||
IMAGE = 'image'
|
IMAGE = 'image',
|
||||||
|
TABLE = 'table'
|
||||||
}
|
}
|
||||||
+4
-16
@@ -1,5 +1,4 @@
|
|||||||
import './assets/css/index.css'
|
import './assets/css/index.css'
|
||||||
import { ZERO } from './dataset/constant/Common'
|
|
||||||
import { IEditorOption } from './interface/Editor'
|
import { IEditorOption } from './interface/Editor'
|
||||||
import { IElement } from './interface/Element'
|
import { IElement } from './interface/Element'
|
||||||
import { Draw } from './core/draw/Draw'
|
import { Draw } from './core/draw/Draw'
|
||||||
@@ -7,8 +6,8 @@ import { Command } from './core/command/Command'
|
|||||||
import { CommandAdapt } from './core/command/CommandAdapt'
|
import { CommandAdapt } from './core/command/CommandAdapt'
|
||||||
import { Listener } from './core/listener/Listener'
|
import { Listener } from './core/listener/Listener'
|
||||||
import { RowFlex } from './dataset/enum/Row'
|
import { RowFlex } from './dataset/enum/Row'
|
||||||
import { getUUID } from './utils'
|
|
||||||
import { ElementType } from './dataset/enum/Element'
|
import { ElementType } from './dataset/enum/Element'
|
||||||
|
import { formatElementList } from './utils/element'
|
||||||
|
|
||||||
export default class Editor {
|
export default class Editor {
|
||||||
|
|
||||||
@@ -42,22 +41,11 @@ export default class Editor {
|
|||||||
marginIndicatorSize: 35,
|
marginIndicatorSize: 35,
|
||||||
marginIndicatorColor: '#BABABA',
|
marginIndicatorColor: '#BABABA',
|
||||||
margins: [100, 120, 100, 120],
|
margins: [100, 120, 100, 120],
|
||||||
|
tdPadding: 5,
|
||||||
|
defaultTdHeight: 40,
|
||||||
...options
|
...options
|
||||||
}
|
}
|
||||||
if (elementList[0]?.value !== ZERO) {
|
formatElementList(elementList)
|
||||||
elementList.unshift({
|
|
||||||
value: ZERO
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for (let i = 0; i < elementList.length; i++) {
|
|
||||||
const el = elementList[i]
|
|
||||||
if (el.value === '\n') {
|
|
||||||
el.value = ZERO
|
|
||||||
}
|
|
||||||
if (el.type === ElementType.IMAGE) {
|
|
||||||
el.id = getUUID()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 监听
|
// 监听
|
||||||
this.listener = new Listener()
|
this.listener = new Listener()
|
||||||
// 启动
|
// 启动
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { IElementPosition } from "./Element"
|
||||||
|
import { IRow } from "./Row"
|
||||||
|
|
||||||
export interface IDrawOption {
|
export interface IDrawOption {
|
||||||
curIndex?: number;
|
curIndex?: number;
|
||||||
isSetCursor?: boolean;
|
isSetCursor?: boolean;
|
||||||
@@ -17,3 +20,19 @@ export interface IImageParticleCreateResult {
|
|||||||
resizerImageContainer: HTMLDivElement;
|
resizerImageContainer: HTMLDivElement;
|
||||||
resizerImage: HTMLImageElement;
|
resizerImage: HTMLImageElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDrawRowPayload {
|
||||||
|
positionList: IElementPosition[];
|
||||||
|
rowList: IRow[];
|
||||||
|
pageNo: number;
|
||||||
|
startIndex: number;
|
||||||
|
startX: number;
|
||||||
|
startY: number;
|
||||||
|
innerWidth: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDrawRowResult {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
@@ -23,5 +23,7 @@ export interface IEditorOption {
|
|||||||
resizerSize?: number;
|
resizerSize?: number;
|
||||||
marginIndicatorSize?: number;
|
marginIndicatorSize?: number;
|
||||||
marginIndicatorColor?: string,
|
marginIndicatorColor?: string,
|
||||||
margins?: [top: number, right: number, bootom: number, left: number]
|
margins?: [top: number, right: number, bootom: number, left: number],
|
||||||
|
tdPadding?: number;
|
||||||
|
defaultTdHeight?: number;
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { ElementType } from "../dataset/enum/Element"
|
import { ElementType } from "../dataset/enum/Element"
|
||||||
import { RowFlex } from "../dataset/enum/Row"
|
import { RowFlex } from "../dataset/enum/Row"
|
||||||
|
import { IColgroup } from "./table/Colgroup"
|
||||||
|
import { ITr } from "./table/Tr"
|
||||||
|
|
||||||
export interface IElementMetrics {
|
export interface IElementBasic {
|
||||||
width: number;
|
id?: string;
|
||||||
height: number;
|
type?: ElementType;
|
||||||
boundingBoxAscent: number;
|
value: string;
|
||||||
boundingBoxDescent: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IElementStyle {
|
export interface IElementStyle {
|
||||||
@@ -23,13 +24,27 @@ export interface IElementStyle {
|
|||||||
rowMargin?: number;
|
rowMargin?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IElementBasic {
|
export interface ITableAttr {
|
||||||
id?: string;
|
colgroup?: IColgroup[];
|
||||||
type?: ElementType;
|
trList?: ITr[];
|
||||||
value: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IElement = IElementBasic & IElementStyle
|
export interface ITableElement {
|
||||||
|
tdId?: string;
|
||||||
|
trId?: string;
|
||||||
|
tableId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ITable = ITableAttr & ITableElement
|
||||||
|
|
||||||
|
export type IElement = IElementBasic & IElementStyle & ITable
|
||||||
|
|
||||||
|
export interface IElementMetrics {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
boundingBoxAscent: number;
|
||||||
|
boundingBoxDescent: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IElementPosition {
|
export interface IElementPosition {
|
||||||
pageNo: number;
|
pageNo: number;
|
||||||
|
|||||||
@@ -1,5 +1,36 @@
|
|||||||
|
import { IElement } from ".."
|
||||||
|
import { IElementPosition } from "./Element"
|
||||||
|
import { ITd } from "./table/Td"
|
||||||
|
|
||||||
export interface ICurrentPosition {
|
export interface ICurrentPosition {
|
||||||
index: number;
|
index: number;
|
||||||
isImage?: boolean;
|
isImage?: boolean;
|
||||||
|
isTable?: boolean;
|
||||||
isDirectHit?: boolean;
|
isDirectHit?: boolean;
|
||||||
|
trIndex?: number;
|
||||||
|
tdIndex?: number;
|
||||||
|
tdValueIndex?: number;
|
||||||
|
tdId?: string;
|
||||||
|
trId?: string;
|
||||||
|
tableId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGetPositionByXYPayload {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
isTable?: boolean;
|
||||||
|
td?: ITd;
|
||||||
|
tablePosition?: IElementPosition;
|
||||||
|
elementList?: IElement[];
|
||||||
|
positionList?: IElementPosition[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPositionContext {
|
||||||
|
isTable: boolean;
|
||||||
|
index?: number;
|
||||||
|
trIndex?: number;
|
||||||
|
tdIndex?: number;
|
||||||
|
tdId?: string;
|
||||||
|
trId?: string;
|
||||||
|
tableId?: string;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface IColgroup {
|
||||||
|
id?: string;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { IElement, IElementPosition } from "../Element"
|
||||||
|
import { IRow } from "../Row"
|
||||||
|
|
||||||
|
export interface ITd {
|
||||||
|
id?: string;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
colspan: number;
|
||||||
|
rowspan: number;
|
||||||
|
value: IElement[];
|
||||||
|
isLastRowTd?: boolean;
|
||||||
|
isLastColTd?: boolean;
|
||||||
|
isLastTd?: boolean;
|
||||||
|
rowIndex?: number;
|
||||||
|
colIndex?: number;
|
||||||
|
rowList?: IRow[];
|
||||||
|
positionList?: IElementPosition[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ITd } from "./Td"
|
||||||
|
|
||||||
|
export interface ITr {
|
||||||
|
id?: string;
|
||||||
|
height: number;
|
||||||
|
tdList: ITd[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { getUUID } from "."
|
||||||
|
import { ElementType, IElement } from ".."
|
||||||
|
import { ZERO } from "../dataset/constant/Common"
|
||||||
|
|
||||||
|
export function formatElementList(elementList: IElement[]) {
|
||||||
|
if (elementList[0]?.value !== ZERO) {
|
||||||
|
elementList.unshift({
|
||||||
|
value: ZERO
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (let i = 0; i < elementList.length; i++) {
|
||||||
|
const el = elementList[i]
|
||||||
|
if (el.type === ElementType.TABLE) {
|
||||||
|
const tableId = getUUID()
|
||||||
|
el.id = tableId
|
||||||
|
if (el.trList) {
|
||||||
|
for (let t = 0; t < el.trList.length; t++) {
|
||||||
|
const tr = el.trList[t]
|
||||||
|
const trId = getUUID()
|
||||||
|
tr.id = trId
|
||||||
|
for (let d = 0; d < tr.tdList.length; d++) {
|
||||||
|
const td = tr.tdList[d]
|
||||||
|
const tdId = getUUID()
|
||||||
|
td.id = tdId
|
||||||
|
formatElementList(td.value)
|
||||||
|
for (let v = 0; v < td.value.length; v++) {
|
||||||
|
const value = td.value[v]
|
||||||
|
value.tdId = tdId
|
||||||
|
value.trId = trId
|
||||||
|
value.tableId = tableId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (el.value === '\n') {
|
||||||
|
el.value = ZERO
|
||||||
|
}
|
||||||
|
if (el.type === ElementType.IMAGE) {
|
||||||
|
el.id = getUUID()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+182
-5
@@ -3,7 +3,7 @@ import Editor, { ElementType, IElement, RowFlex } from './editor'
|
|||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
|
||||||
const text = `人民医院门诊病历\n主诉:\n发热三天,咳嗽五天。\n现病史:\n患者于三天前无明显诱因,感冒后发现面部水肿,无皮疹,尿量减少,出现乏力,在外治疗无好转,现来我院就诊。\n既往史:\n有糖尿病10年,有高血压2年,有传染性疾病1年。没有报告其他既往疾病。\n流行病史:\n否认14天内接触过新冠肺炎确诊患者、疑似患者、无症状感染者及其密切接触者;否认14天内去过以下场所:水产、肉类批发市场,农贸市场,集市,大型超市,夜市;否认14天内与以下场所工作人员密切接触:水产、肉类批发市场,农贸市场,集市,大型超市;否认14天内周围(如家庭、办公室)有2例以上聚集性发病;否认14天内接触过有发热或呼吸道症状的人员;否认14天内自身有发热或呼吸道症状;否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形;陪同家属{有无选择代码}有以上情况。\n体格检查:\nT:36.5℃,P:80bpm,R:20次/分,BP:120/80mmHg;\n辅助检查:\n2020年6月10日,普放:血细胞比容36.50%(偏低)40~50;单核细胞绝对值0.75*10^9/L(偏高)参考值:0.1~0.6;\n门诊诊断:\n1.高血压\n2.糖尿病\n3.病毒性感冒\n4.过敏性鼻炎\n5.过敏性鼻息肉\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】`
|
const text = `人民医院门诊病历\n主诉:\n发热三天,咳嗽五天。\n现病史:\n患者于三天前无明显诱因,感冒后发现面部水肿,无皮疹,尿量减少,出现乏力,在外治疗无好转,现来我院就诊。\n既往史:\n有糖尿病10年,有高血压2年,有传染性疾病1年。没有报告其他既往疾病。\n流行病史:\n否认14天内接触过新冠肺炎确诊患者、疑似患者、无症状感染者及其密切接触者;否认14天内去过以下场所:水产、肉类批发市场,农贸市场,集市,大型超市,夜市;否认14天内与以下场所工作人员密切接触:水产、肉类批发市场,农贸市场,集市,大型超市;否认14天内周围(如家庭、办公室)有2例以上聚集性发病;否认14天内接触过有发热或呼吸道症状的人员;否认14天内自身有发热或呼吸道症状;否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形;陪同家属{有无选择代码}有以上情况。\n体格检查:\nT:36.5℃,P:80bpm,R:20次/分,BP:120/80mmHg;\n辅助检查:\n2020年6月10日,普放:血细胞比容36.50%(偏低)40~50;单核细胞绝对值0.75*10^9/L(偏高)参考值:0.1~0.6;\n门诊诊断:\n1.高血压\n2.糖尿病\n3.病毒性感冒\n4.过敏性鼻炎\n5.过敏性鼻息肉\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】\n其他记录:`
|
||||||
// 模拟行居中
|
// 模拟行居中
|
||||||
const centerText = ['人民医院门诊病历']
|
const centerText = ['人民医院门诊病历']
|
||||||
const centerIndex: number[] = centerText.map(c => {
|
const centerIndex: number[] = centerText.map(c => {
|
||||||
@@ -11,7 +11,7 @@ window.onload = function () {
|
|||||||
return ~i ? Array(c.length).fill(i).map((_, j) => i + j) : []
|
return ~i ? Array(c.length).fill(i).map((_, j) => i + j) : []
|
||||||
}).flat()
|
}).flat()
|
||||||
// 模拟加粗字
|
// 模拟加粗字
|
||||||
const boldText = ['主诉:', '现病史:', '既往史:', '流行病史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:']
|
const boldText = ['主诉:', '现病史:', '既往史:', '流行病史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:', '其他记录:']
|
||||||
const boldIndex: number[] = boldText.map(b => {
|
const boldIndex: number[] = boldText.map(b => {
|
||||||
const i = text.indexOf(b)
|
const i = text.indexOf(b)
|
||||||
return ~i ? Array(b.length).fill(i).map((_, j) => i + j) : []
|
return ~i ? Array(b.length).fill(i).map((_, j) => i + j) : []
|
||||||
@@ -69,12 +69,116 @@ window.onload = function () {
|
|||||||
id: 'signature',
|
id: 'signature',
|
||||||
type: ElementType.IMAGE
|
type: ElementType.IMAGE
|
||||||
})
|
})
|
||||||
|
data.push({
|
||||||
|
type: ElementType.TABLE,
|
||||||
|
value: `\n`,
|
||||||
|
colgroup: [{
|
||||||
|
width: 180
|
||||||
|
}, {
|
||||||
|
width: 80
|
||||||
|
}, {
|
||||||
|
width: 130
|
||||||
|
}, {
|
||||||
|
width: 130
|
||||||
|
}],
|
||||||
|
trList: [{
|
||||||
|
height: 40,
|
||||||
|
tdList: [{
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 2,
|
||||||
|
value: [
|
||||||
|
{ value: `1`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `2`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 2,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `3`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
height: 40,
|
||||||
|
tdList: [{
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `4`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `5`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `6`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
height: 40,
|
||||||
|
tdList: [{
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `7`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `8`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `9`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
value: [
|
||||||
|
{ value: `1`, size: 16 },
|
||||||
|
{ value: `0`, size: 16 },
|
||||||
|
{ value: '.', size: 16 }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
data.push(...[{
|
||||||
|
value: 'E',
|
||||||
|
size: 16
|
||||||
|
}, {
|
||||||
|
value: 'O',
|
||||||
|
size: 16
|
||||||
|
}, {
|
||||||
|
value: 'F',
|
||||||
|
size: 16
|
||||||
|
}])
|
||||||
// 初始化编辑器
|
// 初始化编辑器
|
||||||
const container = document.querySelector<HTMLDivElement>('.editor')!
|
const container = document.querySelector<HTMLDivElement>('.editor')!
|
||||||
const instance = new Editor(container, data, {
|
const instance = new Editor(container, data, {
|
||||||
margins: [100, 120, 100, 120]
|
margins: [100, 120, 100, 120]
|
||||||
})
|
})
|
||||||
console.log('编辑器实例: ', instance)
|
console.log('实例: ', instance)
|
||||||
|
|
||||||
// 撤销、重做、格式刷、清除格式
|
// 撤销、重做、格式刷、清除格式
|
||||||
const undoDom = document.querySelector<HTMLDivElement>('.menu-item__undo')!
|
const undoDom = document.querySelector<HTMLDivElement>('.menu-item__undo')!
|
||||||
@@ -183,7 +287,81 @@ window.onload = function () {
|
|||||||
const li = evt.target as HTMLLIElement
|
const li = evt.target as HTMLLIElement
|
||||||
instance.command.executeRowMargin(Number(li.dataset.rowmargin!))
|
instance.command.executeRowMargin(Number(li.dataset.rowmargin!))
|
||||||
}
|
}
|
||||||
// 图片上传、搜索、打印
|
// 表格插入、图片上传、搜索、打印
|
||||||
|
const tableDom = document.querySelector<HTMLDivElement>('.menu-item__table')!
|
||||||
|
const tablePanelContainer = document.querySelector<HTMLDivElement>('.menu-item__table__collapse')!
|
||||||
|
const tableClose = document.querySelector<HTMLDivElement>('.table-close')!
|
||||||
|
const tableTitle = document.querySelector<HTMLDivElement>('.table-select')!
|
||||||
|
const tablePanel = document.querySelector<HTMLDivElement>('.table-panel')!
|
||||||
|
// 绘制行列
|
||||||
|
const tableCellList: HTMLDivElement[][] = []
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const tr = document.createElement('tr')
|
||||||
|
tr.classList.add('table-row')
|
||||||
|
const trCellList: HTMLDivElement[] = []
|
||||||
|
for (let j = 0; j < 10; j++) {
|
||||||
|
const td = document.createElement('td')
|
||||||
|
td.classList.add('table-cel')
|
||||||
|
tr.append(td)
|
||||||
|
trCellList.push(td)
|
||||||
|
}
|
||||||
|
tablePanel.append(tr)
|
||||||
|
tableCellList.push(trCellList)
|
||||||
|
}
|
||||||
|
let colIndex = 0
|
||||||
|
let rowIndex = 0
|
||||||
|
// 移除所有格选择
|
||||||
|
function removeAllTableCellSelect() {
|
||||||
|
tableCellList.forEach(tr => {
|
||||||
|
tr.forEach(td => td.classList.remove('active'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 设置标题内容
|
||||||
|
function setTableTitle(payload: string) {
|
||||||
|
tableTitle.innerText = payload
|
||||||
|
}
|
||||||
|
// 恢复初始状态
|
||||||
|
function recoveryTable() {
|
||||||
|
// 还原选择样式、标题、选择行列
|
||||||
|
removeAllTableCellSelect()
|
||||||
|
setTableTitle('插入')
|
||||||
|
colIndex = 0
|
||||||
|
rowIndex = 0
|
||||||
|
// 隐藏panel
|
||||||
|
tablePanelContainer.style.display = 'none'
|
||||||
|
}
|
||||||
|
tableDom.onclick = function () {
|
||||||
|
console.log('table')
|
||||||
|
tablePanelContainer!.style.display = 'block'
|
||||||
|
}
|
||||||
|
tablePanel.onmousemove = function (evt) {
|
||||||
|
const celSize = 16
|
||||||
|
const rowMarginTop = 10
|
||||||
|
const celMarginRight = 6
|
||||||
|
const { offsetX, offsetY } = evt
|
||||||
|
// 移除所有选择
|
||||||
|
removeAllTableCellSelect()
|
||||||
|
colIndex = Math.ceil(offsetX / (celSize + celMarginRight)) || 1
|
||||||
|
rowIndex = Math.ceil(offsetY / (celSize + rowMarginTop)) || 1
|
||||||
|
// 改变选择样式
|
||||||
|
tableCellList.forEach((tr, trIndex) => {
|
||||||
|
tr.forEach((td, tdIndex) => {
|
||||||
|
if (tdIndex < colIndex && trIndex < rowIndex) {
|
||||||
|
td.classList.add('active')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 改变表格标题
|
||||||
|
setTableTitle(`${rowIndex}×${colIndex}`)
|
||||||
|
}
|
||||||
|
tableClose.onclick = function () {
|
||||||
|
recoveryTable()
|
||||||
|
}
|
||||||
|
tablePanel.onclick = function () {
|
||||||
|
// 应用选择
|
||||||
|
instance.command.executeInsertTable(rowIndex, colIndex)
|
||||||
|
recoveryTable()
|
||||||
|
}
|
||||||
const imageDom = document.querySelector<HTMLDivElement>('.menu-item__image')!
|
const imageDom = document.querySelector<HTMLDivElement>('.menu-item__image')!
|
||||||
const imageFileDom = document.querySelector<HTMLInputElement>('#image')!
|
const imageFileDom = document.querySelector<HTMLInputElement>('#image')!
|
||||||
imageDom.onclick = function () {
|
imageDom.onclick = function () {
|
||||||
@@ -308,7 +486,6 @@ window.onload = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance.listener.pageScaleChange = function (payload) {
|
instance.listener.pageScaleChange = function (payload) {
|
||||||
console.log('payload: ', payload);
|
|
||||||
document.querySelector<HTMLSpanElement>('.page-scale-percentage')!.innerText = `${Math.floor(payload * 10 * 10)}%`
|
document.querySelector<HTMLSpanElement>('.page-scale-percentage')!.innerText = `${Math.floor(payload * 10 * 10)}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-2
@@ -78,7 +78,7 @@ ul {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 0 6px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item>div:hover {
|
.menu-item>div:hover {
|
||||||
@@ -97,7 +97,7 @@ ul {
|
|||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item>div span {
|
.menu-item>div>span {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -272,6 +272,91 @@ ul {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item__table {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item__table i {
|
||||||
|
background-image: url('./assets/images/table.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__table__collapse {
|
||||||
|
width: 270px;
|
||||||
|
height: 310px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
|
||||||
|
border: 1px solid #e2e6ed;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 2px;
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
z-index: 99;
|
||||||
|
top: 25px;
|
||||||
|
left: 0;
|
||||||
|
padding: 14px 27px;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__table__collapse .table-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__table__collapse .table-close:hover {
|
||||||
|
color: #7d7e80;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__table__collapse:hover {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .menu-item__table__collapse .table-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #e2e6ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #3d4757;
|
||||||
|
display: inline;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel .table-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-top: 10px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel .table-cel {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #e2e6ed;
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel .table-cel.active {
|
||||||
|
border: 1px solid rgba(73, 145, 242, .2);
|
||||||
|
background: rgba(73, 145, 242, .15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-panel .table-row .table-cel:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-item__search {
|
.menu-item__search {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user