From 5e0643f3a8247a36503a50773ed2c4a4b03e1390 Mon Sep 17 00:00:00 2001 From: gudaoxuri Date: Wed, 6 Dec 2023 14:06:22 +0800 Subject: [PATCH] Add tree structure function. --- docs/TableDemo.vue | 37 +++---- src/assets/icon.ts | 2 + src/assets/locales/en.json | 4 +- src/assets/locales/zh-CN.json | 4 +- src/components/events.ts | 129 +++++++++++++++++------ src/components/function/RowTree.vue | 87 +++++++++++++++ src/components/layout/list/List.vue | 12 ++- src/components/layout/list/RowAdd.vue | 24 ----- src/components/layout/list/RowNew.vue | 26 +++++ src/components/layout/list/RowSelect.vue | 15 ++- src/components/layout/list/Rows.vue | 76 ++++++------- src/components/props.ts | 9 +- 12 files changed, 294 insertions(+), 131 deletions(-) create mode 100644 src/components/function/RowTree.vue delete mode 100644 src/components/layout/list/RowAdd.vue create mode 100644 src/components/layout/list/RowNew.vue diff --git a/docs/TableDemo.vue b/docs/TableDemo.vue index fac0af9..7b66636 100644 --- a/docs/TableDemo.vue +++ b/docs/TableDemo.vue @@ -3,19 +3,19 @@ import type { TableCellDictItem, TableColumnProps, TableDataSliceReq, TableLayou import { AggregateKind, DATA_DICT_POSTFIX, DataKind, LayoutKind } from '../src/components/props' import { getRandomInt } from '../src/utils/basic' -const columns = [{ name: 'no', dataKind: DataKind.NUMBER, dataEditable: false }, { name: 'name', useDict: true, dictEditable: true }, { name: 'phone' }, { name: 'stats', useDict: true, dictEditable: true, multiValue: true }, { name: 'addr' }, { name: 'time', dataKind: DataKind.DATETIME }] +const columns = [{ name: 'no', dataKind: DataKind.NUMBER, dataEditable: false }, { name: 'pno', dataKind: DataKind.NUMBER, dataEditable: false }, { name: 'name', useDict: true, dictEditable: true }, { name: 'phone' }, { name: 'stats', useDict: true, dictEditable: true, multiValue: true }, { name: 'addr' }, { name: 'time', dataKind: DataKind.DATETIME }] const layouts = [{ id: 'hi', title: 'HI', layoutKind: LayoutKind.LIST, columns: [{ - name: 'no', - width: 80, - }, { name: 'name', }, { name: 'stats', + }, { + name: 'no', + width: 80, }, { name: 'addr', }, { @@ -51,10 +51,11 @@ const events = { resolve(attachDict(data.map((d) => { d.no = getRandomInt(1000, 2000) d.name = 'xy' - d.stats = ['init'] - d.phone = '!Phone2' - d.addr = '!Addr2' - d.time = '2023-10-24' + d.stats = [] + d.phone = '' + d.addr = '' + d.time = '' + d.pno = data[0].pno return d }))) } @@ -246,26 +247,26 @@ const resp1 = { const data2 = [ { no: 1, name: 'xh', stats: ['init'], phone: 'Phone1', addr: 'Addr1 Addr1 Addr1 Addr1 Addr1 Addr1', time: '2023-10-23' }, { no: 2, name: 'xh', stats: ['init'], phone: 'Phone2', addr: 'Addr2', time: '2023-10-24' }, - { no: 3, name: 'xh', stats: ['progress', 'risk'], phone: 'Phone3', addr: 'Addr3', time: '2023-10-25' }, - { no: 4, name: 'xh', stats: ['init'], phone: 'Phone4', addr: 'Addr4', time: '2023-10-26' }, - { no: 5, name: 'xh', stats: ['init'], phone: 'Phone5', addr: 'Addr5', time: '2023-10-27' }, + { no: 3, pno: 1, name: 'xh', stats: ['progress', 'risk'], phone: 'Phone3', addr: 'Addr3', time: '2023-10-25' }, + { no: 4, pno: 1, name: 'xh', stats: ['init'], phone: 'Phone4', addr: 'Addr4', time: '2023-10-26' }, + { no: 5, pno: 1, name: 'xh', stats: ['init'], phone: 'Phone5', addr: 'Addr5', time: '2023-10-27' }, { no: 6, name: 'xh', stats: ['init'], phone: 'Phone6', addr: 'Addr6', time: '2023-10-28' }, { no: 7, name: 'xh', stats: ['init'], phone: 'Phone7', addr: 'Addr7', time: '2023-10-29' }, - { no: 8, name: 'xh', stats: ['init'], phone: 'Phone8', addr: 'Addr8', time: '2023-10-30' }, - { no: 9, name: 'xh', stats: ['init'], phone: 'Phone9', addr: 'Addr9', time: '2023-10-31' }, - { no: 10, name: 'xh', stats: ['close'], phone: 'Phone10', addr: 'Addr10', time: '2023-11-1' }, + { no: 8, pno: 4, name: 'xh', stats: ['init'], phone: 'Phone8', addr: 'Addr8', time: '2023-10-30' }, + { no: 9, pno: 4, name: 'xh', stats: ['init'], phone: 'Phone9', addr: 'Addr9', time: '2023-10-31' }, + { no: 10, pno: 8, name: 'xh', stats: ['close'], phone: 'Phone10', addr: 'Addr10', time: '2023-11-1' }, { no: 11, name: 'xh', stats: ['close'], phone: 'Phone11', addr: 'Addr11', time: '2023-11-2' }, { no: 12, name: 'xh', stats: ['close'], phone: 'Phone12', addr: 'Addr12', time: '2023-11-3' }, { no: 13, name: 'xh', stats: ['close'], phone: 'Phone13', addr: 'Addr13', time: '2023-11-4' }, { no: 14, name: 'xh', stats: ['close'], phone: 'Phone14', addr: 'Addr14', time: '2023-11-5' }, { no: 15, name: 'xh', stats: ['close'], phone: 'Phone15', addr: 'Addr15', time: '2023-11-6' }, - { no: 16, name: 'xh', stats: ['close'], phone: 'Phone16', addr: 'Addr16', time: '2023-11-7' }, + { no: 16, pno: 8, name: 'xh', stats: ['close'], phone: 'Phone16', addr: 'Addr16', time: '2023-11-7' }, { no: 17, name: 'xh', stats: ['finish'], phone: 'Phone17', addr: 'Addr17', time: '2023-11-8' }, { no: 18, name: 'xh', stats: ['finish'], phone: 'Phone18', addr: 'Addr18', time: '2023-11-9' }, { no: 19, name: 'xh', stats: ['finish'], phone: 'Phone19', addr: 'Addr19', time: '2023-11-10' }, { no: 20, name: 'xh', stats: ['finish'], phone: 'Phone20', addr: 'Addr20', time: '2023-11-11' }, { no: 21, name: 'xh', stats: ['finish'], phone: 'Phone21', addr: 'Addr21', time: '2023-11-12' }, - { no: 22, name: 'xh', stats: ['finish'], phone: 'Phone22', addr: 'Addr22', time: '2023-11-13' }, + { no: 22, pno: 1, name: 'xh', stats: ['finish'], phone: 'Phone22', addr: 'Addr22', time: '2023-11-13' }, { no: 23, name: 'xh', stats: ['finish'], phone: 'Phone23', addr: 'Addr23', time: '2023-11-14' }, { no: 24, name: 'xh', stats: ['progress', 'risk'], phone: 'Phone24', addr: 'Addr24', time: '2023-11-15' }, { no: 25, name: 'xh', stats: ['close'], phone: 'Phone25', addr: 'Addr25', time: '2023-11-16' }, @@ -283,7 +284,7 @@ const data3 = [ { no: 114, name: 'xy', stats: ['init'], phone: 'Phone14', addr: 'Addr14', time: '2023-11-5' }, { no: 115, name: 'xy', stats: ['init'], phone: 'Phone15', addr: 'Addr15', time: '2023-11-6' }, { no: 116, name: 'xy', stats: ['init'], phone: 'Phone16', addr: 'Addr16', time: '2023-11-7' }, - { no: 117, name: 'xy', stats: ['init'], phone: 'Phone17', addr: 'Addr17', time: '2023-11-8' }, + { no: 117, pno: 210, name: 'xy', stats: ['init'], phone: 'Phone17', addr: 'Addr17', time: '2023-11-8' }, { no: 118, name: 'xy', stats: ['init'], phone: 'Phone18', addr: 'Addr18', time: '2023-11-9' }, { no: 119, name: 'xy', stats: ['init'], phone: 'Phone19', addr: 'Addr19', time: '2023-11-10' }, { no: 210, name: 'xy', stats: ['init'], phone: 'Phone20', addr: 'Addr20', time: '2023-11-11' }, @@ -345,7 +346,7 @@ const resp2 = [
diff --git a/src/assets/icon.ts b/src/assets/icon.ts index 495b998..1aa1364 100644 --- a/src/assets/icon.ts +++ b/src/assets/icon.ts @@ -43,3 +43,5 @@ export const TRASH = 'octicon-trash-24' export const DICT = 'octicon-book-24' export const PIN = 'octicon-pin-24' export const WRAP = 'octicon-move-to-end-24' +export const EXPAND = 'octicon-triangle-right-24' +export const SHRINK = 'octicon-triangle-down-24' diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 6a80992..dbc68e2 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -121,8 +121,8 @@ "titlePlaceholder": "New name", "submit": "Add" }, - "rowAdd": { - "new": "New row" + "rowNew": { + "newTitle": "New row" }, "rowCopyPaste": { "copyTitle": "Copy row(s)", diff --git a/src/assets/locales/zh-CN.json b/src/assets/locales/zh-CN.json index b2e4c05..4059065 100644 --- a/src/assets/locales/zh-CN.json +++ b/src/assets/locales/zh-CN.json @@ -121,8 +121,8 @@ "titlePlaceholder": "新的字典项名称", "submit": "添加" }, - "rowAdd": { - "new": "新增行" + "rowNew": { + "newTitle": "新增行" }, "rowCopyPaste": { "copyTitle": "复制行", diff --git a/src/components/events.ts b/src/components/events.ts index 957fee9..283b597 100644 --- a/src/components/events.ts +++ b/src/components/events.ts @@ -2,6 +2,7 @@ import type { InjectionKey, Ref } from 'vue' import { toRaw } from 'vue' import type { TableBasicConf, TableColumnConf, TableLayoutColumnConf, TableLayoutConf, TableStyleConf } from './conf' import { getDefaultValueByDataKind } from './conf' +import { sortByTree } from './function/RowTree.vue' import type { TableCellDictItem, TableCellDictItemResp, TableDataResp, TableDataSliceReq, TableEventProps, TableLayoutModifyReq } from './props' import { DATA_DICT_POSTFIX, OperatorKind } from './props' @@ -30,6 +31,10 @@ export async function loadData(layoutId?: string, moreForGroupedValue?: any) { if (layout.filters) filters = toRaw(layout.filters) + const slice = { + offsetNumber: 0, + fetchNumber: layout.fetchDataNumber, + } if (moreForGroupedValue) { const groupFilter = { items: [ @@ -45,6 +50,15 @@ export async function loadData(layoutId?: string, moreForGroupedValue?: any) { filters.push(groupFilter) else filters = [groupFilter] + if (layout.data && Array.isArray(layout.data)) { + const curGroupData = layout.data.find(d => d.groupValue === moreForGroupedValue) + if (curGroupData !== undefined) + slice.offsetNumber = curGroupData.records.length + } + } + else { + if (layout.data && !Array.isArray(layout.data)) + slice.offsetNumber = layout.data.records.length } let sorts if (layout.sorts) @@ -58,12 +72,11 @@ export async function loadData(layoutId?: string, moreForGroupedValue?: any) { if (layout.aggs) aggs = toRaw(layout.aggs) - let slice - if (layout.slice) - slice = toRaw(layout.slice) - const resp = await events.loadData(filters, sorts, group, aggs, slice) if (Array.isArray(resp)) { + resp.forEach((groupData) => { + groupData.records = sortByTree(groupData.records, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) + }) // (Re)group query if (layout.data && Array.isArray(layout.data)) layout.data.splice(0, layout.data.length, ...resp) @@ -76,30 +89,52 @@ export async function loadData(layoutId?: string, moreForGroupedValue?: any) { const groupData = layout.data.find(d => d.groupValue === moreForGroupedValue) if (groupData) { groupData.records.push(...resp.records) + groupData.records = sortByTree(groupData.records, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) groupData.aggs = resp.aggs groupData.totalNumber = resp.totalNumber } } else { + resp.records = sortByTree(resp.records, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) layout.data = resp } } else { // Query without grouping + resp.records = sortByTree(resp.records, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) layout.data = resp } } -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 { +export const FUN_ADD_DATA_TYPE = Symbol('FUN_ADD_DATA_TYPE') as InjectionKey<(newRecords: { [key: string]: any }[], afterPk: any, reFilter?: boolean, reSort?: boolean, reLoad?: boolean) => Promise> +export async function addData(newRecords: { [key: string]: any }[], afterPk: 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 + if (tableBasicConf.parentPkColumnName) { + if (Array.isArray(layout.data)) { + for (const groupData of layout.data) { + const afterRecord = groupData.records.find(record => record[tableBasicConf.pkColumnName] === afterPk) + if (afterRecord) { + newRecords.forEach((record) => { + record[tableBasicConf.parentPkColumnName!] = afterRecord[tableBasicConf.parentPkColumnName!] + }) + break + } + } + } + else if (layout.data && !Array.isArray(layout.data)) { + const afterRecord = layout.data.records.find(record => record[tableBasicConf.pkColumnName] === afterPk) + if (afterRecord) { + newRecords.forEach((record) => { + record[tableBasicConf.parentPkColumnName!] = afterRecord[tableBasicConf.parentPkColumnName!] + }) + } + } + } + const savedRecords = await events.saveData(newRecords) if (reLoad) { await loadData() @@ -107,34 +142,18 @@ export async function addData(newRecords: { [key: string]: any }[], afterPk?: an } 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) + layout.data.forEach((groupData) => { + const idx = groupData.records.findIndex(r => r[tableBasicConf.pkColumnName] === afterPk) + if (idx !== -1) { + groupData.records.splice(idx + 1, 0, ...savedRecords) + groupData.records = sortByTree(groupData.records, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) } - } - 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)) { - 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) - } + const idx = layout.data.records.findIndex(r => r[tableBasicConf.pkColumnName] === afterPk) + layout.data.records.splice(idx + 1, 0, ...savedRecords) + layout.data.records = sortByTree(layout.data, tableBasicConf.pkColumnName, tableBasicConf.parentPkColumnName) } else { // Empty,unreachable @@ -192,10 +211,12 @@ export async function deleteData(deletedPks: any[], reFilter?: boolean, reSort?: } if (Array.isArray(layout.data)) { layout.data.forEach((d) => { + deletedPks = filterTreeDataPks(deletedPks, d.records) d.records = d.records.filter(item => !deletedPks.includes(item[tableBasicConf.pkColumnName])) }) } else if (layout.data && !Array.isArray(layout.data)) { + deletedPks = filterTreeDataPks(deletedPks, layout.data.records) layout.data.records = layout.data.records.filter(item => !deletedPks.includes(item[tableBasicConf.pkColumnName])) } else { @@ -205,6 +226,22 @@ export async function deleteData(deletedPks: any[], reFilter?: boolean, reSort?: // TODO agg清空,重新计算 } +function filterTreeDataPks(filterPks: any[], records: { [key: string]: any }[]): any[] { + if (tableBasicConf.parentPkColumnName === undefined) + return filterPks + + const pksWithChildren: any[] = filterPks.slice() + + pksWithChildren.forEach((pk) => { + const childrenPks = records.filter(record => record[tableBasicConf.parentPkColumnName!] === pk).map(record => record[tableBasicConf.pkColumnName]) + if (childrenPks.length > 0) { + pksWithChildren.push(...childrenPks) + pksWithChildren.push(...filterTreeDataPks(childrenPks, records)) + } + }) + return pksWithChildren +} + export const FUN_LOAD_CELL_DICT_ITEMS_TYPE = Symbol('FUN_LOAD_CELL_DICT_ITEMS_TYPE') as InjectionKey<(columnName: string, filterValue?: any, slice?: TableDataSliceReq) => Promise> export async function loadCellDictItems(columnName: string, filterValue?: any, slice?: TableDataSliceReq): Promise { if (events.loadCellDictItems) { return await events.loadCellDictItems(columnName, filterValue, slice) } @@ -434,6 +471,8 @@ export async function newLayout(newLayoutConf: TableLayoutConf, reFilter?: boole sorts: newLayoutConf.sorts, group: newLayoutConf.group, aggs: newLayoutConf.aggs, + expandDataPks: newLayoutConf.expandDataPks, + fetchDataNumber: newLayoutConf.fetchDataNumber, }))) { if (reLoad) { await loadData() @@ -466,6 +505,28 @@ export async function modifyLayout(changedLayoutReq: TableLayoutModifyReq, reFil changedLayoutReq.sorts && (layout.sorts = changedLayoutReq.sorts) changedLayoutReq.group && (layout.group = changedLayoutReq.group) changedLayoutReq.aggs && (layout.aggs = changedLayoutReq.aggs) + if (changedLayoutReq.newExpandDataPk) { + const idx = layout.expandDataPks.indexOf(changedLayoutReq.newExpandDataPk) + if (idx === -1) + layout.expandDataPks.push(changedLayoutReq.newExpandDataPk) + } + if (changedLayoutReq.deleteExpandDataPk) { + let deleteExpandDataPks = [] + if (Array.isArray(layout.data)) { + layout.data.forEach((groupData) => { + deleteExpandDataPks.push(...filterTreeDataPks([changedLayoutReq.deleteExpandDataPk], groupData.records)) + }) + } + else { + deleteExpandDataPks = filterTreeDataPks([changedLayoutReq.deleteExpandDataPk], (layout.data as TableDataResp).records) + } + deleteExpandDataPks.forEach((deleteExpandDataPk) => { + const idx = layout.expandDataPks.indexOf(deleteExpandDataPk) + if (idx !== -1) + layout.expandDataPks.splice(idx, 1) + }) + } + changedLayoutReq.fetchDataNumber && (layout.fetchDataNumber = changedLayoutReq.fetchDataNumber) if (changedLayoutReq.deletedColumnName) { const oldLayoutColumnIdx = layout.columns.findIndex(column => column.name === changedLayoutReq.deletedColumnName) layout.columns.splice(oldLayoutColumnIdx, 1) diff --git a/src/components/function/RowTree.vue b/src/components/function/RowTree.vue new file mode 100644 index 0000000..13c7852 --- /dev/null +++ b/src/components/function/RowTree.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/src/components/layout/list/List.vue b/src/components/layout/list/List.vue index a446740..8592235 100644 --- a/src/components/layout/list/List.vue +++ b/src/components/layout/list/List.vue @@ -11,8 +11,8 @@ import CellSelectComp from './CellSelect.vue' 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 RowNewComp from './RowNew.vue' import RowDeleteComp from './RowDelete.vue' import RowSelectComp from './RowSelect.vue' import RowsComp from './Rows.vue' @@ -77,10 +77,11 @@ function setTableWidth() { + -import { inject } from 'vue' -import { FUN_ADD_DATA_TYPE } from '../../events' -import * as iconSvg from '../../../assets/icon' - -const props = defineProps<{ - groupValue: any | undefined -}>() - -const addDataFun = inject(FUN_ADD_DATA_TYPE)! - -async function addRow() { - await addDataFun([{ - - }], undefined, props.groupValue) -} - - - diff --git a/src/components/layout/list/RowNew.vue b/src/components/layout/list/RowNew.vue new file mode 100644 index 0000000..53b160b --- /dev/null +++ b/src/components/layout/list/RowNew.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/layout/list/RowSelect.vue b/src/components/layout/list/RowSelect.vue index 7324d02..42b8104 100644 --- a/src/components/layout/list/RowSelect.vue +++ b/src/components/layout/list/RowSelect.vue @@ -34,23 +34,24 @@ function onSelectDragging(event: PointerEvent) { if (!(targetEle instanceof HTMLElement)) return - const parentListEle = getParentWithClass(targetEle, 'iw-list') - if (parentListEle == null) + const cellEle = getParentWithClass(targetEle, 'iw-list-data-cell') + if (cellEle === null) return - if (!targetEle.classList.contains('iw-list-data-cell')) + const parentListEle = getParentWithClass(cellEle, 'iw-list') + if (parentListEle === null) return cleanSelects(parentListEle) - if (getParentWithClass(targetEle, 'iw-list-cell')?.dataset.columnName !== props.pkColumnName) + if (getParentWithClass(cellEle, 'iw-list-cell')?.dataset.columnName !== props.pkColumnName) return - const selectRowEle = getParentWithClass(targetEle, 'iw-list-data-row') + const selectRowEle = getParentWithClass(cellEle, 'iw-list-data-row') if (selectRowEle == null) return startRowIdx = getChildIndex(parentListEle, selectRowEle) - startCellFixedX = targetEle.getBoundingClientRect().left + startCellFixedX = cellEle.getBoundingClientRect().left listEle = parentListEle addSelect(selectRowEle) } @@ -91,12 +92,10 @@ function onRowSelectMove(event: PointerEvent) { cleanSelects(parentListEle) if (startRowIdx < movedRowIdx) { - // eslint-disable-next-line no-unmodified-loop-condition for (let i = startRowIdx; i <= movedRowIdx; i++) addSelect(parentListEle.children[i] as HTMLElement) } else { - // eslint-disable-next-line no-unmodified-loop-condition for (let i = movedRowIdx; i <= startRowIdx; i++) addSelect(parentListEle.children[i] as HTMLElement) } diff --git a/src/components/layout/list/Rows.vue b/src/components/layout/list/Rows.vue index 1a7e115..d3d7f0e 100644 --- a/src/components/layout/list/Rows.vue +++ b/src/components/layout/list/Rows.vue @@ -8,6 +8,7 @@ const props = defineProps<{ pkColumnName: string parentPkColumnName?: string expandDataPks: any[] + pkKindIsNumber: boolean columnsConf: CachedColumnConf[] stylesConf: TableStyleConf setColumnStyles: (colIdx: number) => any @@ -18,49 +19,50 @@ const columnsConfWithoutPk = props.columnsConf.filter(column => column.name !==