多维表格
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
4.3 KiB

feat: Field aggregation (#8786) * feat: basic ui for aggregation * feat: update aggregation in ui * feat: aggregation api implementation * feat: attachment aggregation.ts * fix: some changes * fix: rebase * feat: aggregation for links, rollup, ltar, formula, lookup * fix: type errors * fix: move from data-alias controller, service to data-table service, controller * chore: inline docs for aggregations * fix: handle edge cases * fix: ui bugs * feat: working ui aggregation * fix: minor issue * fix: rollup and links fix count * fix: handle ID Column * fix: minor fixes * fix: update aggregation on data change * fix: round to 2 decimal places * fix: stddev computation error replace with stddev_pop * fix: use pg age function * feat: new record layout * fix: shared view aggregations * feat: aggregations based on formula result * fix: temp pagination * feat: ncpagination v2 * feat: ncpagination v2 * fix: playwright tests * fix: pending changes * fix: failing tests * feat: mysql2 aggregations * fix: build * fix: record count * fix: cleanup * fix: disable count aggregation * feat: expiremental sqlite3 aggregation * fix: mysql2 median * fix:minor issues * refactor: rename column to column_query fix: remove default aggregations fix: disable aggregation for specific dbtype and Foreign Key * fix: remove unwanted else case * fix: aggregation not loading * fix: rebase * fix: rebase * fix: pagination fixed height * fix: respect locked mode for aggregations * fix: pagination component * fix: pagination component * fix: replace Math.random
6 months ago
import type { Ref } from 'vue'
import { type ColumnType, CommonAggregations, type TableType, UITypes, type ViewType, getAvailableAggregations } from 'nocodb-sdk'
const [useProvideViewAggregate, useViewAggregate] = useInjectionState(
(
view: Ref<ViewType | undefined>,
meta: Ref<TableType | undefined> | ComputedRef<TableType | undefined>,
where?: ComputedRef<string | undefined>,
) => {
const { $api: api } = useNuxtApp()
const fields = inject(FieldsInj, ref([]))
const isPublic = inject(IsPublicInj, ref(false))
const { gridViewCols, updateGridViewColumn } = useViewColumnsOrThrow()
const { nestedFilters } = useSmartsheetStoreOrThrow()
const { fetchAggregatedData } = useSharedView()
const aggregations = ref({}) as Ref<Record<string, any>>
const reloadAggregate = inject(ReloadAggregateHookInj, createEventHook())
const visibleFieldsComputed = computed(() => {
const fie = fields.value.map((field, index) => ({ field, index })).filter((f) => f.index !== 0)
return fie.map((f) => {
const gridField = gridViewCols.value[f.field.id!]
if (!gridField) {
return { field: null, index: f.index }
}
return {
value: aggregations.value[f.field.title] ?? null,
field: gridField,
column: f.field,
index: f.index,
width: `${Number(gridField.width.replace('px', ''))}px` || '180px',
}
})
})
const displayFieldComputed = computed(() => {
if (!fields.value?.length || !gridViewCols.value)
return {
field: null,
width: '180px',
}
return {
value: aggregations.value[fields.value[0].title] ?? null,
column: fields.value[0],
field: gridViewCols.value[fields.value[0].id!],
width: `${Number((gridViewCols.value[fields.value[0]!.id!].width ?? '').replace('px', '')) + 64}px` || '244px',
}
})
const getAggregations = (column: ColumnType) => {
if (column.uidt === UITypes.Formula && (column.colOptions as any)?.parsed_tree?.dataType) {
return getAvailableAggregations(column.uidt!, (column.colOptions as any).parsed_tree)
}
return getAvailableAggregations(column.uidt!)
}
const loadViewAggregate = async (
fields?: Array<{
field: string
type: string
}>,
) => {
if (!meta.value?.id || !view.value?.id) return
try {
const data = !isPublic.value
? await api.dbDataTableAggregate.dbDataTableAggregate(meta.value.id, {
viewId: view.value.id,
where: where?.value,
...(fields ? { aggregation: fields } : {}),
})
: await fetchAggregatedData({
where: where?.value,
filtersArr: nestedFilters.value,
...(fields ? { aggregation: fields } : {}),
})
Object.assign(aggregations.value, data)
} catch (error) {
console.log(error)
message.error(await extractSdkResponseErrorMsgv2(error as any))
}
}
const updateAggregate = async (fieldId: string, agg: string) => {
loadViewAggregate([
{
field: fieldId,
type: agg,
},
])
await updateGridViewColumn(fieldId, { aggregation: agg })
}
reloadAggregate?.on(async (_fields) => {
if (!_fields || !_fields.field?.length) {
await loadViewAggregate()
}
if (_fields?.field) {
const fieldAggregateMapping = _fields.field.reduce((acc, field) => {
const f = fields.value.find((f) => f.title === field)
acc[f.id] = gridViewCols.value[f.id].aggregation ?? CommonAggregations.None
return acc
}, {} as Record<string, string>)
await loadViewAggregate(
Object.entries(fieldAggregateMapping).map(([field, type]) => ({
field,
type,
})),
)
}
})
return {
loadViewAggregate,
isPublic,
updateAggregate,
getAggregations,
displayFieldComputed,
visibleFieldsComputed,
}
},
)
export { useProvideViewAggregate }
export function useViewAggregateOrThrow() {
const viewAggregate = useViewAggregate()
if (viewAggregate == null) throw new Error('Please call `useProvideViewAggregate` on the appropriate parent component')
return viewAggregate
}