From 63daceea439d9b1405093ad9ca3ac4ce2ebfd3bc Mon Sep 17 00:00:00 2001 From: gudaoxuri Date: Thu, 30 Nov 2023 09:33:37 +0800 Subject: [PATCH] Add copy and paste row functions. --- components.d.ts | 3 +- docs/TableDemo.vue | 24 ++++++++-- src/assets/icon.ts | 1 + src/assets/locales/en.json | 9 +++- src/assets/locales/zh-CN.json | 7 ++- src/components/events.ts | 45 +++++++++++++----- src/components/layout/list/List.vue | 6 ++- src/components/layout/list/RowCopyPaste.vue | 51 +++++++++++++++++++++ src/components/layout/list/RowDelete.vue | 6 +-- 9 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 src/components/layout/list/RowCopyPaste.vue diff --git a/components.d.ts b/components.d.ts index ea922ab..fd73225 100644 --- a/components.d.ts +++ b/components.d.ts @@ -22,7 +22,7 @@ declare module 'vue' { ColumnRename: typeof import('./src/components/layout/list/ColumnRename.vue')['default'] ColumnResize: typeof import('./src/components/layout/list/ColumnResize.vue')['default'] ColumnSort: typeof import('./src/components/layout/list/ColumnSort.vue')['default'] - copy: typeof import('./src/components/layout/list/RowDelete copy.vue')['default'] + copy: typeof import('./src/components/layout/list/RowAdd copy.vue')['default'] Filter: typeof import('./src/components/function/Filter.vue')['default'] Group: typeof import('./src/components/function/Group.vue')['default'] Header: typeof import('./src/components/layout/list/Header.vue')['default'] @@ -31,6 +31,7 @@ declare module 'vue' { Menu: typeof import('./src/components/common/Menu.vue')['default'] Resize: typeof import('./src/components/function/Resize.vue')['default'] RowAdd: typeof import('./src/components/layout/list/RowAdd.vue')['default'] + RowCopyPaste: typeof import('./src/components/layout/list/RowCopyPaste.vue')['default'] RowDelete: typeof import('./src/components/layout/list/RowDelete.vue')['default'] Rows: typeof import('./src/components/layout/list/Rows.vue')['default'] RowSelect: typeof import('./src/components/layout/list/RowSelect.vue')['default'] diff --git a/docs/TableDemo.vue b/docs/TableDemo.vue index ca5299c..0fca385 100644 --- a/docs/TableDemo.vue +++ b/docs/TableDemo.vue @@ -58,16 +58,32 @@ const events = { return d }))) } + else if (Object.values(data[0]).length === 1) { + // Copy + resp2.forEach((resp) => { + const storageRecords = resp.records.filter(record => data.find(d => d.no === record.no)) + if (storageRecords.length > 0) { + let newRecords = JSON.parse(JSON.stringify(storageRecords)) + newRecords = newRecords + .map((record) => { + record.no = getRandomInt(1000, 2000) + return record + }) + resolve(attachDict(newRecords)) + } + }) + } else { // Update resp2.forEach((resp) => { - let storageRecord = resp.records.find(record => record.no === data[0].no) + const storageRecord = resp.records.find(record => record.no === data[0].no) if (storageRecord) { - storageRecord = { - ...storageRecord, + let newRecords = JSON.parse(JSON.stringify(storageRecord)) + newRecords = { + ...newRecords, ...data[0], } - resolve(attachDict([storageRecord])) + resolve(attachDict([newRecords])) } }) } diff --git a/src/assets/icon.ts b/src/assets/icon.ts index e32637b..495b998 100644 --- a/src/assets/icon.ts +++ b/src/assets/icon.ts @@ -34,6 +34,7 @@ export const RENAME = 'octicon-stack-24' export const SIZE = 'octicon-typography-24' export const LOCK = 'octicon-lock-24' export const COPY = 'octicon-copy-24' +export const PASTE = 'octicon-paste-24' export const SORT = 'octicon-sort-desc-24' export const FILTER = 'octicon-filter-24' export const CHEVRON_DOWN = 'octicon-chevron-down-24' diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 3ca7451..6a80992 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -91,7 +91,7 @@ "title": "Fixed" }, "rowDelete": { - "title": "Delete" + "title": "Delete row(s)" }, "cellWrap": { "title": "Break words" @@ -123,6 +123,11 @@ }, "rowAdd": { "new": "New row" + }, + "rowCopyPaste": { + "copyTitle": "Copy row(s)", + "pasteTitle": "Paste row(s)", + "copyAndPasteTitle": "Copy and Paste row(s)" } } -} \ No newline at end of file +} diff --git a/src/assets/locales/zh-CN.json b/src/assets/locales/zh-CN.json index 6733922..b2e4c05 100644 --- a/src/assets/locales/zh-CN.json +++ b/src/assets/locales/zh-CN.json @@ -91,7 +91,7 @@ "title": "固定列" }, "rowDelete": { - "title": "删除" + "title": "删除行" }, "cellWrap": { "title": "换行" @@ -123,6 +123,11 @@ }, "rowAdd": { "new": "新增行" + }, + "rowCopyPaste": { + "copyTitle": "复制行", + "pasteTitle": "粘贴行", + "copyAndPasteTitle": "复制并粘贴行" } } } \ No newline at end of file diff --git a/src/components/events.ts b/src/components/events.ts index da1d237..957fee9 100644 --- a/src/components/events.ts +++ b/src/components/events.ts @@ -90,9 +90,13 @@ export async function loadData(layoutId?: string, moreForGroupedValue?: any) { } } -export const FUN_ADD_DATA_TYPE = Symbol('FUN_ADD_DATA_TYPE') as InjectionKey<(newRecords: { [key: string]: any }[], afterPkId?: number, groupValue?: any, reFilter?: boolean, reSort?: boolean, reLoad?: boolean) => Promise> -export async function addData(newRecords: { [key: string]: any }[], afterPkId?: number, groupValue?: any, reFilter?: boolean, reSort?: boolean, reLoad?: boolean): Promise { +export const FUN_ADD_DATA_TYPE = Symbol('FUN_ADD_DATA_TYPE') as InjectionKey<(newRecords: { [key: string]: any }[], afterPk?: any, groupValue?: any, reFilter?: boolean, reSort?: boolean, reLoad?: boolean) => Promise> +export async function addData(newRecords: { [key: string]: any }[], afterPk?: any, groupValue?: any, reFilter?: boolean, reSort?: boolean, reLoad?: boolean): Promise { const layout = tableLayoutsConf.find(layout => layout.id === currentLayoutId.value)! + + if (Array.isArray(layout.data) && afterPk === undefined && groupValue === undefined) + return false + if (!events.saveData) return false @@ -102,22 +106,39 @@ export async function addData(newRecords: { [key: string]: any }[], afterPkId?: return true } - let data - if (groupValue && Array.isArray(layout.data)) { - data = layout.data.find(d => d.groupValue === groupValue) + if (Array.isArray(layout.data)) { + if (groupValue) { + const data = layout.data.find(d => d.groupValue === groupValue)! + if (afterPk !== undefined) { + const idx = data.records.findIndex(r => r[tableBasicConf.pkColumnName] === afterPk) + data.records.splice(idx + 1, 0, ...savedRecords) + } + else { + data.records.splice(data.records.length, 0, ...savedRecords) + } + } + else { + layout.data.forEach((groupData) => { + if (afterPk !== undefined) { + const idx = groupData.records.findIndex(r => r[tableBasicConf.pkColumnName] === afterPk) + if (idx !== -1) + groupData.records.splice(idx + 1, 0, ...savedRecords) + } + }) + } } else if (layout.data && !Array.isArray(layout.data)) { - data = layout.data + if (afterPk !== undefined) { + const idx = layout.data.records.findIndex(r => r[tableBasicConf.pkColumnName] === afterPk) + layout.data.records.splice(idx + 1, 0, ...savedRecords) + } + else { + layout.data.records.splice(layout.data.records.length, 0, ...savedRecords) + } } else { // Empty,unreachable } - if (data) { - if (afterPkId) - data.records.splice(afterPkId, 0, ...savedRecords) - else - data.records.splice(data.records.length, 0, ...savedRecords) - } return true // TODO agg清空,重新计算 } diff --git a/src/components/layout/list/List.vue b/src/components/layout/list/List.vue index 77003e5..a446740 100644 --- a/src/components/layout/list/List.vue +++ b/src/components/layout/list/List.vue @@ -12,6 +12,7 @@ import ColumnAggsComp from './ColumnAggs.vue' import { setFixedColumnStyles } from './ColumnFixed.vue' import HeaderComp from './Header.vue' import RowAddComp from './RowAdd.vue' +import RowCopyPasteComp from './RowCopyPaste.vue' import RowDeleteComp from './RowDelete.vue' import RowSelectComp from './RowSelect.vue' import RowsComp from './Rows.vue' @@ -116,8 +117,9 @@ function setTableWidth() { :selected-cell-info="selectedCellWrap.cellSelectedInfo" /> - - + + + +import { inject, ref } from 'vue'; +import * as iconSvg from '../../../assets/icon'; +import { FUN_CLOSE_CONTEXT_MENU_TYPE } from '../../common/Menu.vue'; +import { FUN_ADD_DATA_TYPE } from '../../events'; + +const props = defineProps<{ + pkColumnName: string + selectedPks: string[] | number[] +}>() + +const addDataFun = inject(FUN_ADD_DATA_TYPE)! +const closeContextMenuFun = inject(FUN_CLOSE_CONTEXT_MENU_TYPE)! + +const copiedPks = ref([]) + +async function copyAndPasteRow() { + await addDataFun(props.selectedPks.slice().map((pk) => { + return { + [props.pkColumnName]: pk, + } + }), props.selectedPks[props.selectedPks.length - 1]) + closeContextMenuFun() +} + +function copyRow() { + copiedPks.value = props.selectedPks.slice() + closeContextMenuFun() +} + +async function pasteRow() { + await addDataFun(copiedPks.value.slice().map((pk) => { + return { + [props.pkColumnName]: pk, + } + }), props.selectedPks[props.selectedPks.length - 1]) + closeContextMenuFun() +} + + + diff --git a/src/components/layout/list/RowDelete.vue b/src/components/layout/list/RowDelete.vue index 21903be..c6d0be0 100644 --- a/src/components/layout/list/RowDelete.vue +++ b/src/components/layout/list/RowDelete.vue @@ -11,14 +11,14 @@ const props = defineProps<{ const deleteDataFun = inject(FUN_DELETE_DATA_TYPE)! const closeContextMenuFun = inject(FUN_CLOSE_CONTEXT_MENU_TYPE)! -function deleteData() { +async function deleteRow() { + await deleteDataFun(props.selectedPks) closeContextMenuFun() - deleteDataFun(props.selectedPks) }