From a79c17ceff44dc40e9e86753db7ca6b3b1e98d0e Mon Sep 17 00:00:00 2001 From: pipipi-pikachu Date: Sun, 7 Jun 2026 15:34:57 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=A1=A8=E6=A0=BC=E8=A1=8C=E5=88=97?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E4=BC=98=E5=8C=96=EF=BC=8C=E6=B0=94=E6=B3=A1?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 2 + src/utils/emitter.ts | 10 +- .../FloatingToolbar/TableToolbar.vue | 51 ++++++- .../ElementStylePanel/TableStylePanel.vue | 132 +++++------------- .../element/TableElement/EditableTable.vue | 48 ++++++- .../components/element/TableElement/index.vue | 1 + 6 files changed, 143 insertions(+), 101 deletions(-) diff --git a/components.d.ts b/components.d.ts index d8e88b8e..997fad31 100644 --- a/components.d.ts +++ b/components.d.ts @@ -17,6 +17,7 @@ declare module 'vue' { 'ICustom:filePptist': typeof import('~icons/custom/file-pptist')['default'] 'ICustom:left': typeof import('~icons/custom/left')['default'] 'ICustom:right': typeof import('~icons/custom/right')['default'] + 'IIconParkOutline:add': typeof import('~icons/icon-park-outline/add')['default'] 'IIconParkOutline:addText': typeof import('~icons/icon-park-outline/add-text')['default'] 'IIconParkOutline:alignBottom': typeof import('~icons/icon-park-outline/align-bottom')['default'] 'IIconParkOutline:alignHorizontally': typeof import('~icons/icon-park-outline/align-horizontally')['default'] @@ -109,6 +110,7 @@ declare module 'vue' { 'IIconParkOutline:previewOpen': typeof import('~icons/icon-park-outline/preview-open')['default'] 'IIconParkOutline:quote': typeof import('~icons/icon-park-outline/quote')['default'] 'IIconParkOutline:radarChart': typeof import('~icons/icon-park-outline/radar-chart')['default'] + 'IIconParkOutline:reduce': typeof import('~icons/icon-park-outline/reduce')['default'] 'IIconParkOutline:refresh': typeof import('~icons/icon-park-outline/refresh')['default'] 'IIconParkOutline:right': typeof import('~icons/icon-park-outline/right')['default'] 'IIconParkOutline:rotate': typeof import('~icons/icon-park-outline/rotate')['default'] diff --git a/src/utils/emitter.ts b/src/utils/emitter.ts index b858d9ef..bdcac0de 100644 --- a/src/utils/emitter.ts +++ b/src/utils/emitter.ts @@ -2,6 +2,7 @@ import mitt, { type Emitter } from 'mitt' export const enum EmitterEvents { RICH_TEXT_COMMAND = 'RICH_TEXT_COMMAND', + TABLE_COMMAND = 'TABLE_COMMAND', SYNC_RICH_TEXT_ATTRS_TO_STORE = 'SYNC_RICH_TEXT_ATTRS_TO_STORE', OPEN_CHART_DATA_EDITOR = 'OPEN_CHART_DATA_EDITOR', OPEN_LATEX_EDITOR = 'OPEN_LATEX_EDITOR', @@ -17,8 +18,15 @@ export interface RichTextCommand { action: RichTextAction | RichTextAction[] } +export interface TableCommand { + targetId: string + command: 'insert-row' | 'insert-col' | 'delete-row' | 'delete-col' + position?: 'before' | 'after' +} + type Events = { [EmitterEvents.RICH_TEXT_COMMAND]: RichTextCommand + [EmitterEvents.TABLE_COMMAND]: TableCommand [EmitterEvents.SYNC_RICH_TEXT_ATTRS_TO_STORE]: void [EmitterEvents.OPEN_CHART_DATA_EDITOR]: void [EmitterEvents.OPEN_LATEX_EDITOR]: void @@ -26,4 +34,4 @@ type Events = { const emitter: Emitter = mitt() -export default emitter \ No newline at end of file +export default emitter diff --git a/src/views/Editor/Canvas/ElementFloatLayer/FloatingToolbar/TableToolbar.vue b/src/views/Editor/Canvas/ElementFloatLayer/FloatingToolbar/TableToolbar.vue index 2eec5b36..a3898737 100644 --- a/src/views/Editor/Canvas/ElementFloatLayer/FloatingToolbar/TableToolbar.vue +++ b/src/views/Editor/Canvas/ElementFloatLayer/FloatingToolbar/TableToolbar.vue @@ -10,6 +10,35 @@ + +
+ + + + + + + + + @@ -18,13 +47,15 @@ import { computed, type Ref } from 'vue' import { storeToRefs } from 'pinia' import { useMainStore, useSlidesStore } from '@/store' import type { PPTTableElement, TableCell, TableCellStyle } from '@/types/slides' +import emitter, { EmitterEvents, type TableCommand } from '@/utils/emitter' import useHistorySnapshot from '@/hooks/useHistorySnapshot' import BorderPanel from './BorderPanel.vue' import Popover from '@/components/Popover.vue' +import PopoverMenuItem from '@/components/PopoverMenuItem.vue' import ColorPicker from '@/components/ColorPicker/index.vue' -defineProps<{ +const props = defineProps<{ elementInfo: PPTTableElement }>() @@ -49,6 +80,14 @@ const cellBackcolor = computed(() => { const { addHistorySnapshot } = useHistorySnapshot() +const emitTableCommand = (command: TableCommand['command'], position?: TableCommand['position']) => { + emitter.emit(EmitterEvents.TABLE_COMMAND, { + targetId: props.elementInfo.id, + command, + position, + }) +} + const updateCellBackcolor = (backcolor: string) => { const el = handleTableElement.value if (!el || el.type !== 'table') return @@ -105,4 +144,14 @@ const updateCellBackcolor = (backcolor: string) => { margin-left: 5px; } } +.divider { + width: 1px; + height: 18px; + background-color: $borderColor; + margin: 0 4px; + flex-shrink: 0; +} +.table-command-menu { + width: 100px; +} diff --git a/src/views/Editor/Toolbar/ElementStylePanel/TableStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/TableStylePanel.vue index 36f0c6e7..75cee6f7 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/TableStylePanel.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/TableStylePanel.vue @@ -113,20 +113,32 @@
-
行数:
-
- -
{{rowCount}}
- -
+
操作行:
+ + + + + + +
-
列数:
-
- -
{{colCount}}
- -
+
操作列:
+ + + + + + +
@@ -185,10 +197,10 @@ @@ -426,19 +374,7 @@ const setTableCol = (value: number) => { .switch-wrapper { text-align: right; } -.set-count { - display: flex; - justify-content: center; - align-items: center; - - .btn { - padding: 0 8px; - } - - .count-text { - flex: 1; - text-align: center; - margin: 0 8px; - } +.popover-btn { + padding: 0 3px; } diff --git a/src/views/components/element/TableElement/EditableTable.vue b/src/views/components/element/TableElement/EditableTable.vue index 89a18273..394a4728 100644 --- a/src/views/components/element/TableElement/EditableTable.vue +++ b/src/views/components/element/TableElement/EditableTable.vue @@ -70,6 +70,8 @@ import { useMainStore } from '@/store' import type { PPTElementOutline, TableCell, TableTheme } from '@/types/slides' import type { ContextmenuItem } from '@/components/Contextmenu/types' import { KEYS } from '@/configs/hotkey' +import emitter, { EmitterEvents, type TableCommand } from '@/utils/emitter' +import message from '@/utils/message' import { getCellStyle, getTextStyle, formatText } from './utils' import useHideCells from './useHideCells' import useSubThemeColor from './useSubThemeColor' @@ -77,6 +79,7 @@ import useSubThemeColor from './useSubThemeColor' import CustomTextarea from './CustomTextarea.vue' const props = withDefaults(defineProps<{ + elementId: string data: TableCell[][] width: number cellMinHeight: number @@ -659,6 +662,49 @@ const checkCanDeleteRowOrCol = () => { return { canDeleteRow, canDeleteCol } } +const getCommandTargetCell = () => { + const selectedCell = selectedCells.value.find(cell => !hideCells.value.includes(cell)) + if (!selectedCell) return null + + const [rowIndex, colIndex] = selectedCell.split('_').map(Number) + return { rowIndex, colIndex } +} + +const execTableCommand = (payload: TableCommand) => { + if (payload.targetId !== props.elementId) return + + const targetCell = getCommandTargetCell() + const isBefore = payload.position === 'before' + + if (payload.command === 'insert-row') { + const rowIndex = targetCell ? (isBefore ? targetCell.rowIndex : targetCell.rowIndex + 1) : (isBefore ? 0 : tableCells.value.length) + return insertRow(rowIndex) + } + if (payload.command === 'insert-col') { + const colCount = tableCells.value[0]?.length || 0 + const colIndex = targetCell ? (isBefore ? targetCell.colIndex : targetCell.colIndex + 1) : (isBefore ? 0 : colCount) + return insertCol(colIndex) + } + const { canDeleteRow, canDeleteCol } = checkCanDeleteRowOrCol() + + if (payload.command === 'delete-row') { + if (!canDeleteRow) return message.warning('表格至少保留一行') + return deleteRow(targetCell ? targetCell.rowIndex : tableCells.value.length - 1) + } + if (payload.command === 'delete-col') { + if (!canDeleteCol) return message.warning('表格至少保留一列') + const colCount = tableCells.value[0]?.length || 1 + return deleteCol(targetCell ? targetCell.colIndex : colCount - 1) + } +} + +onMounted(() => { + emitter.on(EmitterEvents.TABLE_COMMAND, execTableCommand) +}) +onUnmounted(() => { + emitter.off(EmitterEvents.TABLE_COMMAND, execTableCommand) +}) + // 检查是否可以合并或拆分 // 必须多选才可以合并 // 必须单选且所选单元格为合并单元格才可以拆分 @@ -835,4 +881,4 @@ table { z-index: 2; cursor: col-resize; } - \ No newline at end of file + diff --git a/src/views/components/element/TableElement/index.vue b/src/views/components/element/TableElement/index.vue index 8598c7c9..e89c43ea 100644 --- a/src/views/components/element/TableElement/index.vue +++ b/src/views/components/element/TableElement/index.vue @@ -19,6 +19,7 @@ >