Browse Source

fix: some bug fixes

nc-expand-rows
DarkPhoenix2704 1 month ago
parent
commit
9860e7d895
  1. 30
      packages/nc-gui/components/dlg/ExpandTable.vue
  2. 3
      packages/nc-gui/components/smartsheet/grid/Table.vue
  3. 60
      packages/nc-gui/composables/useData.ts
  4. 137
      packages/nc-gui/composables/useMultiSelect/index.ts
  5. 45
      packages/nc-gui/composables/useViewData.ts
  6. 3
      packages/nc-gui/lib/types.ts

30
packages/nc-gui/components/dlg/ExpandTable.vue

@ -5,6 +5,7 @@ const props = defineProps<{
fields?: number
rows?: number
modelValue: boolean
affectedRows?: number
}>()
const emit = defineEmits(['update:expand', 'cancel', 'update:modelValue'])
@ -40,13 +41,16 @@ onKeyDown('esc', () => {
</div>
</template>
<div class="flex flex-col mt-1">
<div class="mb-4">
To fit your pasted data into the table, we need to add
<div v-if="(affectedRows ?? 0) > 0" class="mb-2">
The data you pasted will update {{ affectedRows }} records in subsequent pages.
</div>
<div v-if="(rows ?? 0) > 0" class="mb-4">
To fit your pasted data into the table, we need to add
<span class="font-semibold text-gray-800"> {{ rows }} more records. </span>
</div>
<a-radio-group v-model:value="expand">
<a-radio-group v-if="(rows ?? 0) > 0" v-model:value="expand">
<a-radio
:style="{
display: 'flex',
@ -86,22 +90,4 @@ onKeyDown('esc', () => {
</NcModal>
</template>
<style scoped lang="scss">
.ant-form-item {
@apply mb-0;
}
.nc-input-text-area {
padding-block: 8px !important;
}
.nc-table-advanced-options {
max-height: 0;
transition: 0.3s max-height;
overflow: hidden;
&.active {
max-height: 100px;
}
}
</style>
<style scoped lang="scss"></style>

3
packages/nc-gui/components/smartsheet/grid/Table.vue

@ -793,7 +793,7 @@ function scrollToRow(row?: number) {
const isOpen = ref(false)
async function expandRows(rowCount: number) {
async function expandRows(rowCount: number, rowsAffected: number) {
isOpen.value = true
const options = {
@ -808,6 +808,7 @@ async function expandRows(rowCount: number) {
const { close } = useDialog(resolveComponent('DlgExpandTable'), {
'modelValue': isOpen,
'rows': rowCount,
'affectedRows': rowsAffected,
'onUpdate:expand': closeDialog,
'onUpdate:modelValue': closeDlg,
})

60
packages/nc-gui/composables/useData.ts

@ -1,4 +1,5 @@
import {
type Api,
type ColumnType,
type LinkToAnotherRecordType,
type PaginatedType,
@ -9,7 +10,7 @@ import {
} from 'nocodb-sdk'
import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue'
import type { CellRange } from '#imports'
import type { CellRange, Row } from '#imports'
export function useData(args: {
meta: Ref<TableType | undefined> | ComputedRef<TableType | undefined>
@ -18,7 +19,11 @@ export function useData(args: {
paginationData: Ref<PaginatedType>
callbacks?: {
changePage?: (page: number) => Promise<void>
loadData?: () => Promise<void>
loadData?: (
param?: Parameters<Api<any>['dbViewRow']['list']>[4],
shouldShowLoading?: boolean,
updateLocalState?: boolean,
) => Promise<void> | Promise<Row[]>
globalCallback?: (...args: any[]) => Promise<void>
syncCount?: () => Promise<void>
syncPagination?: () => Promise<void>
@ -161,15 +166,34 @@ export function useData(args: {
isPaginationLoading.value = true
try {
const ogUpdateRows = formattedData.value
.filter((row) =>
updateRows.some(
(r) =>
extractPkFromRow(r.oldRow, metaValue?.columns as ColumnType[]) ===
extractPkFromRow(row.row, metaValue?.columns as ColumnType[]),
),
)
.map(clone)
// Identify the rows that need to be fetched from the server
const rowsToFetch = updateRows.filter((row) => row.rowMeta?.isExistingRow)
const pagesToFetch = Array.from(new Set(rowsToFetch.map((row) => row.rowMeta.page))).filter(
(page): page is number => page !== undefined && page !== paginationData.value.page,
)
const fetchedData: Record<number, Row[]> = {}
for (const pageToFetch of pagesToFetch) {
fetchedData[pageToFetch] = (await callbacks?.loadData?.(
{
offset: (pageToFetch - 1) * paginationData.value.pageSize!,
},
false,
false,
)) as Row[]
}
const getPk = (row: Row) => extractPkFromRow(row.row, metaValue?.columns as ColumnType[])
const ogUpdateRows = updateRows.map((row) => {
if (row.rowMeta?.page && row.rowMeta.page !== paginationData.value.page) {
const fetchedRow = fetchedData[row.rowMeta.page]?.[row.rowMeta.rowInPage!]
return fetchedRow ? clone(fetchedRow) : row
}
const currentPageRow = formattedData.value.find((formattedRow) => getPk(formattedRow) === getPk(row))
return currentPageRow ? clone(currentPageRow) : row
})
const cleanRow = (row: any) => {
const cleanedRow = { ...row }
@ -179,6 +203,20 @@ export function useData(args: {
return cleanedRow
}
updateRows = updateRows.map((row) => {
if (row.rowMeta?.page && row.rowMeta.page !== paginationData.value.page) {
const fetchedRow = fetchedData[row.rowMeta.page]?.[row.rowMeta.rowInPage!]
if (fetchedRow) {
return {
...fetchedRow,
row: { ...fetchedRow.row, ...row.row },
oldRow: fetchedRow.row,
}
}
}
return row
})
const rowsToUpsert = {
insert: insertRows.map((row) => cleanRow(row.row)),
update: updateRows.map((row) => cleanRow(row.row)),

137
packages/nc-gui/composables/useMultiSelect/index.ts

@ -848,98 +848,101 @@ export function useMultiSelect(
const selectionRowCount = Math.max(clipboardMatrix.length, selectedRange.end.row - selectedRange.start.row + 1)
const pasteMatrixRows = selectionRowCount
const pasteMatrixCols = clipboardMatrix[0].length
const colsToPaste = unref(fields).slice(activeCell.col, activeCell.col + pasteMatrixCols)
const rowsToPaste = unref(data).slice(activeCell.row, activeCell.row + selectionRowCount)
const { totalRows = 0, page = 1, pageSize = 100 } = unref(paginationDataRef)!
const totalRowsBeforeActiveCell = (page - 1) * pageSize + activeCell.row
const availableRowsToUpdate = Math.max(0, totalRows - totalRowsBeforeActiveCell)
const rowsToAdd = Math.max(0, selectionRowCount - availableRowsToUpdate)
const rowsInCurrentPage = unref(data).length
const rowsAffectedInCurrentPage = Math.min(selectionRowCount, rowsInCurrentPage - activeCell.row)
const rowsAffectedInOtherPages = Math.max(0, selectionRowCount - rowsAffectedInCurrentPage)
let options = {
continue: false,
expand: true,
expand: rowsToAdd > 0 || rowsAffectedInOtherPages > 0 || (rowsAffectedInOtherPages > 0 && rowsToAdd > 0),
}
const rowToPasteLen = rowsToPaste.length
if (pasteMatrixRows > rowsToPaste.length) {
rowsToPaste.push(
...Array(pasteMatrixRows - rowsToPaste.length)
.fill({})
.map(() => ({
row: {},
oldRow: {},
rowMeta: {
isExpandedData: true,
},
})),
)
options = await expandRows(pasteMatrixRows - rowToPasteLen)
if (!options.continue) {
return
}
if (options.expand) {
options = await expandRows(rowsToAdd, rowsAffectedInOtherPages)
if (!options.continue) return
}
const propsToPaste: string[] = []
let pastedRows = 0
const updatedRows: any[] = []
const newRows: any[] = []
const propsToPaste: string[] = []
let isInfoShown = false
for (let i = 0; i < pasteMatrixRows; i++) {
const pasteRow = rowsToPaste[i]
// TODO handle insert new row
if (!pasteRow || pasteRow.rowMeta.new) break
pastedRows++
for (let j = 0; j < pasteMatrixCols; j++) {
const pasteCol = colsToPaste[j]
if (!isPasteable(pasteRow, pasteCol)) {
if ((isBt(pasteCol) || isOo(pasteCol) || isMm(pasteCol)) && !isInfoShown) {
message.info(t('msg.info.groupPasteIsNotSupportedOnLinksColumn'))
isInfoShown = true
}
continue
for (let i = 0; i < selectionRowCount; i++) {
const clipboardRowIndex = i % clipboardMatrix.length
let targetRow: any
if (i < availableRowsToUpdate) {
const absoluteRowIndex = totalRowsBeforeActiveCell + i
targetRow =
i < unref(data).length
? unref(data)[i]
: {
row: {},
oldRow: {},
rowMeta: {
isExistingRow: true,
page: Math.floor(absoluteRowIndex / pageSize) + 1,
rowInPage: absoluteRowIndex % pageSize,
},
}
updatedRows.push(targetRow)
} else {
targetRow = {
row: {},
oldRow: {},
rowMeta: {
isExistingRow: false,
},
}
newRows.push(targetRow)
}
propsToPaste.push(pasteCol.title!)
const pasteValue = convertCellData(
{
// Repeat the clipboard data array if the matrix is smaller than the selection
value: clipboardMatrix[i % clipboardMatrix.length][j],
to: pasteCol.uidt as UITypes,
column: pasteCol,
appInfo: unref(appInfo),
oldValue: pasteCol.uidt === UITypes.Attachment ? pasteRow.row[pasteCol.title!] : undefined,
},
isMysql(meta.value?.source_id),
true,
)
for (let j = 0; j < clipboardMatrix[clipboardRowIndex].length; j++) {
const column = colsToPaste[j]
if (column && isPasteable(targetRow, column)) {
propsToPaste.push(column.title!)
const pasteValue = convertCellData(
{
value: clipboardMatrix[clipboardRowIndex][j],
to: column.uidt as UITypes,
column,
appInfo: unref(appInfo),
oldValue: column.uidt === UITypes.Attachment ? targetRow.row[column.title!] : undefined,
},
isMysql(meta.value?.source_id),
true,
)
if (pasteValue !== undefined) {
pasteRow.row[pasteCol.title!] = pasteValue
if (pasteValue !== undefined) {
targetRow.row[column.title!] = pasteValue
}
} else if ((isBt(column) || isOo(column) || isMm(column)) && !isInfoShown) {
message.info(t('msg.info.groupPasteIsNotSupportedOnLinksColumn'))
isInfoShown = true
}
}
}
const expandedRows = rowsToPaste.filter((row) => row.rowMeta.isExpandedData)
const updatedRows = rowsToPaste.filter((row) => !row.rowMeta.isExpandedData)
if (options.expand) {
await bulkUpsertRows?.(expandedRows!, updatedRows, propsToPaste)
await bulkUpsertRows?.(newRows, updatedRows, propsToPaste)
} else {
await bulkUpdateRows?.(updatedRows, propsToPaste)
}
if (scrollToCell) {
scrollToCell(activeCell.row, activeCell.col)
}
if (pastedRows > 0) {
// highlight the pasted range
selectedRange.startRange({ row: activeCell.row, col: activeCell.col })
selectedRange.endRange({ row: activeCell.row + pastedRows - 1, col: activeCell.col + pasteMatrixCols - 1 })
}
selectedRange.startRange({ row: activeCell.row, col: activeCell.col })
selectedRange.endRange({ row: activeCell.row + selectionRowCount - 1, col: activeCell.col + pasteMatrixCols - 1 })
} else {
if (selectedRange.isSingleCell()) {
const rowObj = unref(data)[activeCell.row]

45
packages/nc-gui/composables/useViewData.ts

@ -161,7 +161,11 @@ export function useViewData(
const controller = ref()
async function loadData(params: Parameters<Api<any>['dbViewRow']['list']>[4] = {}, shouldShowLoading = true) {
async function loadData(
params: Parameters<Api<any>['dbViewRow']['list']>[4] = {},
shouldShowLoading = true,
updateLocalState = true,
) {
if ((!base?.value?.id || !metaId.value || !viewMeta.value?.id) && !isPublic.value) return
if (controller.value) {
@ -213,27 +217,34 @@ export function useViewData(
console.error(error)
return message.error(await extractSdkResponseErrorMsg(error))
}
formattedData.value = formatData(response.list)
paginationData.value = response.pageInfo || paginationData.value || {}
// if public then update sharedPaginationData
if (isPublic.value) {
sharedPaginationData.value = paginationData.value
}
if (updateLocalState) {
formattedData.value = formatData(response.list)
paginationData.value = response.pageInfo || paginationData.value || {}
// if public then update sharedPaginationData
if (isPublic.value) {
sharedPaginationData.value = paginationData.value
}
excludePageInfo.value = !response.pageInfo
isPaginationLoading.value = false
excludePageInfo.value = !response.pageInfo
isPaginationLoading.value = false
// to cater the case like when querying with a non-zero offset
// the result page may point to the target page where the actual returned data don't display on
if (paginationData.value.totalRows !== undefined && paginationData.value.totalRows !== null) {
const expectedPage = Math.max(1, Math.ceil(paginationData.value.totalRows! / paginationData.value.pageSize!))
if (expectedPage < paginationData.value.page!) {
await changePage(expectedPage)
// to cater the case like when querying with a non-zero offset
// the result page may point to the target page where the actual returned data don't display on
if (paginationData.value.totalRows !== undefined && paginationData.value.totalRows !== null) {
const expectedPage = Math.max(1, Math.ceil(paginationData.value.totalRows! / paginationData.value.pageSize!))
if (expectedPage < paginationData.value.page!) {
await changePage(expectedPage)
}
}
if (viewMeta.value?.type === ViewTypes.GRID) {
loadAggCommentsCount()
}
}
if (viewMeta.value?.type === ViewTypes.GRID) {
loadAggCommentsCount()
if (!updateLocalState) {
return formatData(response.list)
}
}

3
packages/nc-gui/lib/types.ts

@ -84,6 +84,9 @@ interface Row {
//
isExpandedData?: boolean
isExistingRow?: boolean
page?: number
rowInPage?: number
// use in datetime picker component
isUpdatedFromCopyNPaste?: Record<string, boolean>

Loading…
Cancel
Save