chore: init read-star monorepo

This commit is contained in:
2026-01-16 13:06:44 +08:00
commit 1e510efe8e
508 changed files with 44803 additions and 0 deletions

View File

@ -0,0 +1,315 @@
import type { FlatResponseData } from '@sa/axios'
import type { PaginationData, TableColumnCheck, UseTableOptions } from '@sa/hooks'
import type { PaginationProps } from 'naive-ui'
import type { Ref } from 'vue'
import { useBoolean, useTable } from '@sa/hooks'
import { jsonClone } from '@sa/utils'
import { computed, effectScope, onScopeDispose, reactive, shallowRef, watch } from 'vue'
import { $t } from '@/locales'
import { useAppStore } from '@/store/modules/app'
export type UseNaiveTableOptions<ResponseData, ApiData, Pagination extends boolean> = Omit<
UseTableOptions<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, Pagination>,
'pagination' | 'getColumnChecks' | 'getColumns'
> & {
/**
* get column visible
*
* @param column
*
* @default true
*
* @returns true if the column is visible, false otherwise
*/
getColumnVisible?: (column: NaiveUI.TableColumn<ApiData>) => boolean
}
const SELECTION_KEY = '__selection__'
const EXPAND_KEY = '__expand__'
export function useNaiveTable<ResponseData, ApiData>(options: UseNaiveTableOptions<ResponseData, ApiData, false>) {
const scope = effectScope()
const appStore = useAppStore()
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, false>({
...options,
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
getColumns,
})
// calculate the total width of the table this is used for horizontal scrolling
const scrollX = computed(() => {
return result.columns.value.reduce((acc, column) => {
return acc + Number(column.width ?? column.minWidth ?? 120)
}, 0)
})
scope.run(() => {
watch(
() => appStore.locale,
() => {
result.reloadColumns()
},
)
})
onScopeDispose(() => {
scope.stop()
})
return {
...result,
scrollX,
}
}
type PaginationParams = Pick<PaginationProps, 'page' | 'pageSize'>
type UseNaivePaginatedTableOptions<ResponseData, ApiData> = UseNaiveTableOptions<ResponseData, ApiData, true> & {
paginationProps?: Omit<PaginationProps, 'page' | 'pageSize' | 'itemCount'>
/**
* whether to show the total count of the table
*
* @default true
*/
showTotal?: boolean
onPaginationParamsChange?: (params: PaginationParams) => void | Promise<void>
}
export function useNaivePaginatedTable<ResponseData, ApiData>(
options: UseNaivePaginatedTableOptions<ResponseData, ApiData>,
) {
const scope = effectScope()
const appStore = useAppStore()
const isMobile = computed(() => appStore.isMobile)
const showTotal = computed(() => options.showTotal ?? true)
const pagination = reactive({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: true,
pageSizes: [10, 15, 20, 25, 30],
prefix: showTotal.value ? page => $t('datatable.itemCount', { total: page.itemCount }) : undefined,
onUpdatePage(page) {
pagination.page = page
},
onUpdatePageSize(pageSize) {
pagination.pageSize = pageSize
pagination.page = 1
},
...options.paginationProps,
}) as PaginationProps
// this is for mobile, if the system does not support mobile, you can use `pagination` directly
const mobilePagination = computed(() => {
const p: PaginationProps = {
...pagination,
pageSlot: isMobile.value ? 3 : 9,
prefix: !isMobile.value && showTotal.value ? pagination.prefix : undefined,
}
return p
})
const paginationParams = computed(() => {
const { page, pageSize } = pagination
return {
page,
pageSize,
}
})
const result = useTable<ResponseData, ApiData, NaiveUI.TableColumn<ApiData>, true>({
...options,
pagination: true,
getColumnChecks: cols => getColumnChecks(cols, options.getColumnVisible),
getColumns,
onFetched: (data) => {
pagination.itemCount = data.total
pagination.pageSize = data.pageSize
},
})
async function getDataByPage(page: number = 1) {
if (page !== pagination.page) {
pagination.page = page
return
}
await result.getData()
}
scope.run(() => {
watch(
() => appStore.locale,
() => {
result.reloadColumns()
},
)
watch(paginationParams, async (newVal) => {
await options.onPaginationParamsChange?.(newVal)
await result.getData()
})
})
onScopeDispose(() => {
scope.stop()
})
return {
...result,
getDataByPage,
pagination,
mobilePagination,
}
}
export function useTableOperate<TableData>(
data: Ref<TableData[]>,
idKey: keyof TableData,
getData: () => Promise<void>,
) {
const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean()
const operateType = shallowRef<NaiveUI.TableOperateType>('add')
function handleAdd() {
operateType.value = 'add'
openDrawer()
}
/** the editing row data */
const editingData = shallowRef<TableData | null>(null)
function handleEdit(id: TableData[keyof TableData]) {
operateType.value = 'edit'
const findItem = data.value.find(item => item[idKey] === id) || null
editingData.value = jsonClone(findItem)
openDrawer()
}
/** the checked row keys of table */
const checkedRowKeys = shallowRef<string[]>([])
/** the hook after the batch delete operation is completed */
async function onBatchDeleted() {
window.$message?.success($t('common.deleteSuccess'))
checkedRowKeys.value = []
await getData()
}
/** the hook after the delete operation is completed */
async function onDeleted() {
window.$message?.success($t('common.deleteSuccess'))
await getData()
}
return {
drawerVisible,
openDrawer,
closeDrawer,
operateType,
handleAdd,
editingData,
handleEdit,
checkedRowKeys,
onBatchDeleted,
onDeleted,
}
}
export function defaultTransform<ApiData>(
response: FlatResponseData<any, Api.Common.PaginatingQueryRecord<ApiData>>,
): PaginationData<ApiData> {
const { data, error } = response
if (!error) {
const { records, current, size, total } = data
return {
data: records,
pageNum: current,
pageSize: size,
total,
}
}
return {
data: [],
pageNum: 1,
pageSize: 10,
total: 0,
}
}
function getColumnChecks<Column extends NaiveUI.TableColumn<any>>(
cols: Column[],
getColumnVisible?: (column: Column) => boolean,
) {
const checks: TableColumnCheck[] = []
cols.forEach((column) => {
if (isTableColumnHasKey(column)) {
checks.push({
key: column.key as string,
title: column.title!,
checked: true,
visible: getColumnVisible?.(column) ?? true,
})
}
else if (column.type === 'selection') {
checks.push({
key: SELECTION_KEY,
title: $t('common.check'),
checked: true,
visible: getColumnVisible?.(column) ?? false,
})
}
else if (column.type === 'expand') {
checks.push({
key: EXPAND_KEY,
title: $t('common.expandColumn'),
checked: true,
visible: getColumnVisible?.(column) ?? false,
})
}
})
return checks
}
function getColumns<Column extends NaiveUI.TableColumn<any>>(cols: Column[], checks: TableColumnCheck[]) {
const columnMap = new Map<string, Column>()
cols.forEach((column) => {
if (isTableColumnHasKey(column)) {
columnMap.set(column.key as string, column)
}
else if (column.type === 'selection') {
columnMap.set(SELECTION_KEY, column)
}
else if (column.type === 'expand') {
columnMap.set(EXPAND_KEY, column)
}
})
const filteredColumns = checks.filter(item => item.checked).map(check => columnMap.get(check.key) as Column)
return filteredColumns
}
export function isTableColumnHasKey<T>(column: NaiveUI.TableColumn<T>): column is NaiveUI.TableColumnWithKey<T> {
return Boolean((column as NaiveUI.TableColumnWithKey<T>).key)
}