富文本优化

This commit is contained in:
2026-07-14 13:09:35 +08:00
parent 3ded1868a3
commit b909dd693e
@@ -52,6 +52,7 @@
:title="disabled ? '富文本宽屏查看' : '富文本宽屏编辑'" :title="disabled ? '富文本宽屏查看' : '富文本宽屏编辑'"
width="96vw" width="96vw"
:footer="null" :footer="null"
centered
:mask-closable="false" :mask-closable="false"
:body-style="{ padding: '0', height: '82vh', overflow: 'hidden' }" :body-style="{ padding: '0', height: '82vh', overflow: 'hidden' }"
@update:open="handleFullscreenVisibleChange" @update:open="handleFullscreenVisibleChange"
@@ -177,11 +178,11 @@ const toolbarGroups = [
{ key: 'insertUnorderedList', label: '无序列表', command: 'insertUnorderedList' }, { key: 'insertUnorderedList', label: '无序列表', command: 'insertUnorderedList' },
{ key: 'insertOrderedList', label: '有序列表', command: 'insertOrderedList' }, { key: 'insertOrderedList', label: '有序列表', command: 'insertOrderedList' },
], ],
[ // [
{ key: 'insertTable', label: '插入表格', type: 'insertTable' }, // { key: 'insertTable', label: '插入表格', type: 'insertTable' },
{ key: 'undo', label: '撤销', command: 'undo' }, // { key: 'undo', label: '撤销', command: 'undo' },
{ key: 'redo', label: '重做', command: 'redo' }, // { key: 'redo', label: '重做', command: 'redo' },
], // ],
] ]
const savedRanges = { const savedRanges = {
@@ -357,6 +358,10 @@ function handleToolbarAction(action, targetSurface = '') {
insertDefaultTable(targetSurface) insertDefaultTable(targetSurface)
return return
} }
if (['justifyLeft', 'justifyCenter', 'justifyRight'].includes(action.command)) {
applyTextAlignment(action.command, targetSurface)
return
}
runDocumentCommand(action.command, action.value, targetSurface) runDocumentCommand(action.command, action.value, targetSurface)
} }
@@ -396,6 +401,9 @@ function insertHtmlAtCursor(html, targetSurface = '') {
const editor = getEditorElement(surface) const editor = getEditorElement(surface)
insertHtmlByRange(editor, html) insertHtmlByRange(editor, html)
} }
if (/<table[\s>]/i.test(html)) {
focusFirstTableCell(surface)
}
captureSelection(surface) captureSelection(surface)
handleEditorInput(surface) handleEditorInput(surface)
} }
@@ -435,10 +443,10 @@ function insertHtmlByRange(editor, html) {
function buildDefaultTableHtml(rows = 4, cols = 4) { function buildDefaultTableHtml(rows = 4, cols = 4) {
const minWidth = Math.max(640, cols * 140) const minWidth = Math.max(640, cols * 140)
const rowHtml = Array.from({ length: rows }, () => { const rowHtml = Array.from({ length: rows }, () => {
const cellHtml = Array.from({ length: cols }, () => '<td><br></td>').join('') const cellHtml = Array.from({ length: cols }, () => '<td>&nbsp;</td>').join('')
return `<tr>${cellHtml}</tr>` return `<tr>${cellHtml}</tr>`
}).join('') }).join('')
return `<table style="width:100%;min-width:${minWidth}px;border-collapse:collapse;table-layout:auto;margin:8px 0;">${rowHtml}</table><p><br></p>` return `<table style="width:100%;min-width:${minWidth}px;border-collapse:collapse;table-layout:auto;margin:8px 0;border:1px solid #1f2937;">${rowHtml}</table><p><br></p>`
} }
function handlePaste(event, surface) { function handlePaste(event, surface) {
@@ -586,6 +594,7 @@ function applyNormalizedCellStyle(sourceCell, targetCell) {
targetCell.style.wordBreak = 'keep-all' targetCell.style.wordBreak = 'keep-all'
targetCell.style.overflowWrap = 'break-word' targetCell.style.overflowWrap = 'break-word'
targetCell.style.whiteSpace = 'pre-wrap' targetCell.style.whiteSpace = 'pre-wrap'
targetCell.style.lineHeight = '1.7'
targetCell.style.minWidth = '120px' targetCell.style.minWidth = '120px'
if (textAlign) { if (textAlign) {
@@ -678,13 +687,81 @@ function buildPlainTextTableHtml(text) {
.map((cells) => { .map((cells) => {
const cellHtml = Array.from({ length: columnCount }, (_, index) => { const cellHtml = Array.from({ length: columnCount }, (_, index) => {
const value = escapeHtml(String(cells[index] || '').trim()) const value = escapeHtml(String(cells[index] || '').trim())
return `<td>${value || '<br>'}</td>` return `<td>${value || '&nbsp;'}</td>`
}).join('') }).join('')
return `<tr>${cellHtml}</tr>` return `<tr>${cellHtml}</tr>`
}) })
.join('') .join('')
return `<table style="width:100%;min-width:${minWidth}px;border-collapse:collapse;table-layout:auto;margin:8px 0;">${rowHtml}</table><p><br></p>` return `<table style="width:100%;min-width:${minWidth}px;border-collapse:collapse;table-layout:auto;margin:8px 0;border:1px solid #1f2937;">${rowHtml}</table><p><br></p>`
}
function applyTextAlignment(command, targetSurface = '') {
if (props.disabled) {
return
}
const surface = resolveTargetSurface(targetSurface)
activeSurface.value = surface
restoreSelection(surface)
const editor = getEditorElement(surface)
if (!editor) {
return
}
const alignValue = resolveAlignValue(command)
if (!alignValue) {
return
}
const selection = window.getSelection()
if (!selection || !selection.rangeCount) {
editor.style.textAlign = alignValue
handleEditorInput(surface)
return
}
const range = selection.getRangeAt(0)
const block = findAlignmentTarget(range.startContainer, editor)
;(block || editor).style.textAlign = alignValue
captureSelection(surface)
handleEditorInput(surface)
}
function resolveAlignValue(command) {
if (command === 'justifyLeft') return 'left'
if (command === 'justifyCenter') return 'center'
if (command === 'justifyRight') return 'right'
return ''
}
function findAlignmentTarget(node, editor) {
let current = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement || null
while (current && current !== editor) {
const tag = String(current.tagName || '').toLowerCase()
if (['p', 'div', 'li', 'td', 'th'].includes(tag)) {
return current
}
current = current.parentElement
}
return editor
}
function focusFirstTableCell(surface) {
const editor = getEditorElement(surface)
if (!editor) {
return
}
const firstCell = editor.querySelector('td, th')
if (!firstCell) {
return
}
const selection = window.getSelection()
if (!selection) {
return
}
const range = document.createRange()
range.selectNodeContents(firstCell)
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
savedRanges[surface] = range.cloneRange()
} }
function openFullscreen() { function openFullscreen() {
@@ -833,6 +910,7 @@ function escapeHtml(text) {
margin: 8px 0; margin: 8px 0;
border-collapse: collapse; border-collapse: collapse;
table-layout: auto; table-layout: auto;
border: 1px solid #1f2937;
background: #fff; background: #fff;
} }
@@ -845,6 +923,7 @@ function escapeHtml(text) {
word-break: keep-all; word-break: keep-all;
overflow-wrap: break-word; overflow-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
line-height: 1.7;
} }
.editor-surface th { .editor-surface th {