From 7f4400f34d11ec28a9c45e346bc348456d326c27 Mon Sep 17 00:00:00 2001 From: zxc <1171051090@qq.com> Date: Fri, 21 Mar 2025 21:51:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=B2=98=E8=B4=B4?= =?UTF-8?q?=E6=9D=A5=E8=87=AAPPT=E8=A1=A8=E6=A0=BC=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/clipboard.ts | 26 ++++++++++ .../ChartStylePanel/ChartDataEditor.vue | 48 +++++++++++-------- .../element/TableElement/CustomTextarea.vue | 42 ++++++++++------ 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/utils/clipboard.ts b/src/utils/clipboard.ts index 3da01565..eb9bd388 100644 --- a/src/utils/clipboard.ts +++ b/src/utils/clipboard.ts @@ -68,5 +68,31 @@ export const pasteExcelClipboardString = (text: string): string[][] | null => { if (colCount === -1) colCount = data[index].length else if (colCount !== data[index].length) return null } + return data +} + +// 尝试解析剪贴板内容是否为HTML table代码 +export const pasteHTMLTableClipboardString = (text: string): string[][] | null => { + const parser = new DOMParser() + const doc = parser.parseFromString(text, 'text/html') + const table = doc.querySelector('table') + const data: string[][] = [] + + if (!table) return data + + const rows = table.querySelectorAll('tr') + for (const row of rows) { + const rowData = [] + const cells = row.querySelectorAll('td, th') + for (const cell of cells) { + const text = cell.textContent ? cell.textContent.trim() : '' + const colspan = parseInt(cell.getAttribute('colspan') || '1', 10) + for (let i = 0; i < colspan; i++) { + rowData.push(text) + } + } + data.push(rowData) + } + return data } \ No newline at end of file diff --git a/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/ChartDataEditor.vue b/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/ChartDataEditor.vue index d93b11ed..ea8623f1 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/ChartDataEditor.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/ChartStylePanel/ChartDataEditor.vue @@ -95,7 +95,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue' import type { ChartData, ChartType } from '@/types/slides' import { KEYS } from '@/configs/hotkey' import { CHART_TYPE_MAP } from '@/configs/chart' -import { pasteCustomClipboardString, pasteExcelClipboardString } from '@/utils/clipboard' +import { pasteCustomClipboardString, pasteExcelClipboardString, pasteHTMLTableClipboardString } from '@/utils/clipboard' import Button from '@/components/Button.vue' import Popover from '@/components/Popover.vue' import PopoverMenuItem from '@/components/PopoverMenuItem.vue' @@ -265,6 +265,18 @@ const clear = () => { } } +const fillTableData = (data: string[][], rowIndex: number, colIndex: number) => { + const maxRow = rowIndex + data.length + const maxCol = colIndex + data[0].length + for (let i = rowIndex; i < maxRow; i++) { + for (let j = colIndex; j < maxCol; j++) { + const inputRef = document.querySelector(`#cell-${i}-${j}`) as HTMLInputElement + if (!inputRef) continue + inputRef.value = data[i - rowIndex][j - colIndex] + } + } +} + // 自定义粘贴事件(尝试读取剪贴板中的表格数据) const handlePaste = (e: ClipboardEvent, rowIndex: number, colIndex: number) => { e.preventDefault() @@ -273,24 +285,22 @@ const handlePaste = (e: ClipboardEvent, rowIndex: number, colIndex: number) => { const clipboardDataFirstItem = e.clipboardData.items[0] - if (clipboardDataFirstItem && clipboardDataFirstItem.kind === 'string' && clipboardDataFirstItem.type === 'text/plain') { - clipboardDataFirstItem.getAsString(text => { - const clipboardData = pasteCustomClipboardString(text) - if (typeof clipboardData === 'object') return - - const excelData = pasteExcelClipboardString(text) - if (excelData) { - const maxRow = rowIndex + excelData.length - const maxCol = colIndex + excelData[0].length - for (let i = rowIndex; i < maxRow; i++) { - for (let j = colIndex; j < maxCol; j++) { - const inputRef = document.querySelector(`#cell-${i}-${j}`) as HTMLInputElement - if (!inputRef) continue - inputRef.value = excelData[i - rowIndex][j - colIndex] - } - } - } - }) + if (clipboardDataFirstItem && clipboardDataFirstItem.kind === 'string') { + if (clipboardDataFirstItem.type === 'text/plain') { + clipboardDataFirstItem.getAsString(text => { + const clipboardData = pasteCustomClipboardString(text) + if (typeof clipboardData === 'object') return + + const excelData = pasteExcelClipboardString(text) + if (excelData) fillTableData(excelData, rowIndex, colIndex) + }) + } + else if (clipboardDataFirstItem.type === 'text/html') { + clipboardDataFirstItem.getAsString(html => { + const htmlData = pasteHTMLTableClipboardString(html) + if (htmlData) fillTableData(htmlData, rowIndex, colIndex) + }) + } } } diff --git a/src/views/components/element/TableElement/CustomTextarea.vue b/src/views/components/element/TableElement/CustomTextarea.vue index f0dad954..5061a633 100644 --- a/src/views/components/element/TableElement/CustomTextarea.vue +++ b/src/views/components/element/TableElement/CustomTextarea.vue @@ -12,7 +12,7 @@