diff --git a/frontend/src/lib/mind-elixir/src/plugin/exportImage.ts b/frontend/src/lib/mind-elixir/src/plugin/exportImage.ts
index 1813424..0ed9772 100644
--- a/frontend/src/lib/mind-elixir/src/plugin/exportImage.ts
+++ b/frontend/src/lib/mind-elixir/src/plugin/exportImage.ts
@@ -52,6 +52,296 @@ function generateSvgText(tpc: HTMLElement, tpcStyle: CSSStyleDeclaration, x: num
return g
}
+// 表格转SVG转换器类
+class TableToSVGConverter {
+ private table: HTMLTableElement
+ private cellWidth: number
+ private cellHeight: number
+ private fontSize: number
+ private fontFamily: string
+
+ constructor(table: HTMLTableElement, fontSize: number, fontFamily: string) {
+ this.table = table
+ this.fontSize = fontSize
+ this.fontFamily = fontFamily
+ this.cellWidth = 80 // 默认单元格宽度
+ this.cellHeight = 30 // 默认单元格高度
+ }
+
+ // 分析表格结构,处理rowspan和colspan
+ analyzeStructure() {
+ const structure: Array<{
+ row: number
+ col: number
+ rowspan: number
+ colspan: number
+ content: string
+ isHeader: boolean
+ }> = []
+
+ const rows = this.table.querySelectorAll('tr')
+ rows.forEach((row, rowIndex) => {
+ const cells = row.querySelectorAll('td, th')
+ let colIndex = 0
+
+ cells.forEach(cell => {
+ const htmlCell = cell as HTMLTableCellElement
+ const rowspan = parseInt(htmlCell.getAttribute('rowspan') || '1')
+ const colspan = parseInt(htmlCell.getAttribute('colspan') || '1')
+ const content = htmlCell.textContent?.trim() || ''
+ const isHeader = htmlCell.tagName.toLowerCase() === 'th'
+
+ structure.push({
+ row: rowIndex,
+ col: colIndex,
+ rowspan,
+ colspan,
+ content,
+ isHeader
+ })
+
+ colIndex += colspan
+ })
+ })
+
+ return structure
+ }
+
+ // 计算布局尺寸
+ calculateLayout(structure: any[]) {
+ const maxCols = Math.max(...structure.map(cell => cell.col + cell.colspan))
+ const maxRows = Math.max(...structure.map(cell => cell.row + cell.rowspan))
+
+ // 为每列计算最合适的宽度
+ const columnWidths: number[] = new Array(maxCols).fill(0)
+
+ structure.forEach(cell => {
+ // 计算这个单元格内容需要的宽度
+ // 中文字符宽度大约是字体大小的1倍,英文是0.6倍
+ let contentWidth = 0
+ for (const char of cell.content) {
+ if (/[\u4e00-\u9fa5]/.test(char)) {
+ // 中文字符
+ contentWidth += this.fontSize * 1.0
+ } else {
+ // 英文字符
+ contentWidth += this.fontSize * 0.6
+ }
+ }
+
+ // 加上内边距
+ contentWidth += 16 // 左右各8px的padding
+
+ // 考虑colspan,平均分配宽度
+ const avgWidthPerCol = contentWidth / cell.colspan
+
+ // 更新这一行涉及的列的最大宽度
+ for (let col = cell.col; col < cell.col + cell.colspan; col++) {
+ columnWidths[col] = Math.max(columnWidths[col], avgWidthPerCol)
+ }
+ })
+
+ // 设置最小列宽,确保不会太窄
+ columnWidths.forEach((width, index) => {
+ columnWidths[index] = Math.max(width, 80) // 最小80px
+ })
+
+ // 计算总宽度
+ const totalWidth = columnWidths.reduce((sum, width) => sum + width, 0)
+
+ // 计算行高,考虑多行文本
+ this.cellHeight = Math.max(35, this.fontSize * 2) // 增加行高
+
+ // 为每行计算实际高度(考虑多行文本)
+ const rowHeights: number[] = new Array(maxRows).fill(this.cellHeight)
+
+ structure.forEach(cell => {
+ const lines = cell.content.split('\n').length
+ const cellHeight = Math.max(this.cellHeight, lines * this.fontSize * 1.4 + 10)
+
+ // 更新这一行涉及的行的高度
+ for (let row = cell.row; row < cell.row + cell.rowspan; row++) {
+ rowHeights[row] = Math.max(rowHeights[row], cellHeight)
+ }
+ })
+
+ const totalHeight = rowHeights.reduce((sum, height) => sum + height, 0)
+
+ return {
+ totalWidth,
+ totalHeight,
+ cols: maxCols,
+ rows: maxRows,
+ columnWidths,
+ rowHeights
+ }
+ }
+
+ // 生成SVG元素
+ generateSVG(structure: any[], layout: any, x: number, y: number) {
+ const svgGroup = document.createElementNS(ns, 'g')
+
+ // 绘制表格边框
+ const tableRect = document.createElementNS(ns, 'rect')
+ setAttributes(tableRect, {
+ x: x + '',
+ y: y + '',
+ width: layout.totalWidth + '',
+ height: layout.totalHeight + '',
+ fill: 'white',
+ stroke: '#ccc',
+ 'stroke-width': '1'
+ })
+ svgGroup.appendChild(tableRect)
+
+ // 绘制垂直网格线(使用动态列宽)
+ let currentX = x
+ for (let i = 0; i < layout.columnWidths.length - 1; i++) {
+ currentX += layout.columnWidths[i]
+ const line = document.createElementNS(ns, 'line')
+ setAttributes(line, {
+ x1: currentX + '',
+ y1: y + '',
+ x2: currentX + '',
+ y2: y + layout.totalHeight + '',
+ stroke: '#ccc',
+ 'stroke-width': '1'
+ })
+ svgGroup.appendChild(line)
+ }
+
+ // 绘制水平网格线(使用动态行高)
+ let currentY = y
+ for (let i = 0; i < layout.rowHeights.length - 1; i++) {
+ currentY += layout.rowHeights[i]
+ const line = document.createElementNS(ns, 'line')
+ setAttributes(line, {
+ x1: x + '',
+ y1: currentY + '',
+ x2: x + layout.totalWidth + '',
+ y2: currentY + '',
+ stroke: '#ccc',
+ 'stroke-width': '1'
+ })
+ svgGroup.appendChild(line)
+ }
+
+ // 绘制单元格内容和背景
+ structure.forEach(cell => {
+ // 计算单元格的实际位置和尺寸
+ let cellX = x
+ for (let i = 0; i < cell.col; i++) {
+ cellX += layout.columnWidths[i]
+ }
+
+ let cellY = y
+ for (let i = 0; i < cell.row; i++) {
+ cellY += layout.rowHeights[i]
+ }
+
+ // 计算单元格宽度(考虑colspan)
+ let cellWidth = 0
+ for (let i = cell.col; i < cell.col + cell.colspan; i++) {
+ cellWidth += layout.columnWidths[i]
+ }
+
+ // 计算单元格高度(考虑rowspan)
+ let cellHeight = 0
+ for (let i = cell.row; i < cell.row + cell.rowspan; i++) {
+ cellHeight += layout.rowHeights[i]
+ }
+
+ // 绘制单元格背景
+ if (cell.isHeader) {
+ const bgRect = document.createElementNS(ns, 'rect')
+ setAttributes(bgRect, {
+ x: cellX + '',
+ y: cellY + '',
+ width: cellWidth + '',
+ height: cellHeight + '',
+ fill: '#f5f5f5',
+ stroke: 'none'
+ })
+ svgGroup.appendChild(bgRect)
+ }
+
+ // 绘制文本内容
+ if (cell.content) {
+ const text = document.createElementNS(ns, 'text')
+ setAttributes(text, {
+ x: cellX + cellWidth / 2 + '',
+ y: cellY + cellHeight / 2 + this.fontSize / 3 + '',
+ 'text-anchor': 'middle',
+ 'dominant-baseline': 'central',
+ 'font-family': this.fontFamily,
+ 'font-size': this.fontSize + '',
+ 'font-weight': cell.isHeader ? 'bold' : 'normal',
+ fill: '#333'
+ })
+
+ // 处理多行文本
+ const lines = cell.content.split('\n')
+ if (lines.length === 1) {
+ text.textContent = cell.content
+ } else {
+ lines.forEach((line: string, index: number) => {
+ const tspan = document.createElementNS(ns, 'tspan')
+ setAttributes(tspan, {
+ x: cellX + cellWidth / 2 + '',
+ dy: index === 0 ? '0' : '1.2em'
+ })
+ tspan.textContent = line
+ text.appendChild(tspan)
+ })
+ }
+
+ svgGroup.appendChild(text)
+ }
+ })
+
+ return svgGroup
+ }
+
+ // 转换表格为SVG
+ convert(x: number, y: number) {
+ const structure = this.analyzeStructure()
+ const layout = this.calculateLayout(structure)
+ return this.generateSVG(structure, layout, x, y)
+ }
+}
+
+// 清理HTML内容,修复SVG解析错误
+function cleanHtmlForSvg(html: string): string {
+ if (!html) return html
+
+ // 修复表格中的
标签问题 - 将