多维表格
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.

134 lines
3.9 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
5 months ago
import {
AttachmentAggregations,
BooleanAggregations,
type ColumnType,
CommonAggregations,
DateAggregations,
UITypes,
dateFormats,
timeFormats,
} from 'nocodb-sdk'
import dayjs from 'dayjs'
const getDateValue = (modelValue: string | null | number, col: ColumnType, isSystemCol?: boolean) => {
const dateFormat = !isSystemCol ? parseProp(col.meta)?.date_format ?? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'
if (!modelValue || !dayjs(modelValue).isValid()) {
return ''
}
return dayjs(/^\d+$/.test(String(modelValue)) ? +modelValue : modelValue).format(dateFormat)
}
const roundTo = (num: unknown, precision = 1) => {
if (!num || Number.isNaN(num)) return num
const factor = 10 ** precision
return Math.round(+num * factor) / factor
}
const getDateTimeValue = (modelValue: string | null, col: ColumnType, isXcdbBase?: boolean) => {
if (!modelValue || !dayjs(modelValue).isValid()) {
return ''
}
const dateFormat = parseProp(col?.meta)?.date_format ?? dateFormats[0]
const timeFormat = parseProp(col?.meta)?.time_format ?? timeFormats[0]
const dateTimeFormat = `${dateFormat} ${timeFormat}`
if (!isXcdbBase) {
return dayjs(/^\d+$/.test(modelValue) ? +modelValue : modelValue, dateTimeFormat).format(dateTimeFormat)
}
return dayjs(modelValue).utc().local().format(dateTimeFormat)
}
const getCurrencyValue = (modelValue: string | number | null | undefined, col: ColumnType): string => {
const currencyMeta = {
currency_locale: 'en-US',
currency_code: 'USD',
...parseProp(col.meta),
}
try {
if (modelValue === null || modelValue === undefined || Number.isNaN(modelValue)) {
return modelValue === null || modelValue === undefined ? '' : (modelValue as string)
}
return new Intl.NumberFormat(currencyMeta.currency_locale || 'en-US', {
style: 'currency',
currency: currencyMeta.currency_code || 'USD',
}).format(+modelValue)
} catch (e) {
return modelValue as string
}
}
function formatBytes(bytes, decimals = 2) {
if (!+bytes) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
}
const formatAggregation = (aggregation: any, value: any, column: ColumnType) => {
if ([DateAggregations.EarliestDate, DateAggregations.LatestDate].includes(aggregation)) {
if (column.uidt === UITypes.DateTime) {
return getDateTimeValue(value, column)
} else if (column.uidt === UITypes.Date) {
return getDateValue(value, column)
}
return getDateTimeValue(value, column)
}
if (
[
CommonAggregations.PercentEmpty,
CommonAggregations.PercentFilled,
CommonAggregations.PercentUnique,
BooleanAggregations.PercentChecked,
BooleanAggregations.PercentUnchecked,
].includes(aggregation)
) {
return `${roundTo(value, 1) ?? 0}%`
}
if ([DateAggregations.MonthRange, DateAggregations.DateRange].includes(aggregation)) {
return aggregation === DateAggregations.DateRange ? `${value ?? 0} days` : `${value ?? 0} months`
}
if (
[
CommonAggregations.Count,
CommonAggregations.CountEmpty,
CommonAggregations.CountFilled,
CommonAggregations.CountUnique,
].includes(aggregation)
) {
return value
}
if ([AttachmentAggregations.AttachmentSize].includes(aggregation)) {
return formatBytes(value ?? 0)
}
if (column.uidt === UITypes.Currency) {
return getCurrencyValue(value, column)
}
if (column.uidt === UITypes.Percent) {
return `${roundTo(value, 1)}%`
}
if (column.uidt === UITypes.Duration) {
return convertMS2Duration(value, parseProp(column.meta)?.duration || 0)
}
if (typeof value === 'number') {
return roundTo(value, 1) ?? '∞'
}
return value
}
export { formatAggregation }