| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- <template>
- <n-data-table class="normal-table"
- v-if="columns.length > 0"
- :columns="columns as DataTableColumns"
- :data="tableData"
- :single-line="false"
- />
- </template>
- <script setup lang="ts">
- import { h, ref, onMounted } from 'vue-demi'
- import { withBase } from 'vitepress';
- import { NDataTable, DataTableColumn, DataTableColumns, DataTableColumnGroup, DataTableBaseColumn, DataTableSelectionColumn, DataTableExpandColumn } from 'naive-ui'
- interface ChildColumnConfig {
- title: string;
- key?: string;
- children?: ChildTableConfig;
- align?: 'left' | 'right' | 'center'; // 列文本对齐方式,默认left
- titleAlign?: 'left' | 'right' | 'center'; // 列标题对齐方式,默认同文本
- minWidth?: number | string;
- maxWidth?: number | string;
- width?: number | string;
- vertical?: boolean; // 控制标题文件的显示方向为纵向
- }
- interface ColumnConfig extends ChildColumnConfig {
- // 类型选择,不填写默认为普通列
- // selection: 选项列,用于勾选数据行
- // expand: 可展开列,用于展开行
- type?: 'selection' | 'expand';
- // selection参数
- selectionMultiple?: boolean; // 默认true,false-单选,true-多选
- }
- type ChildTableConfig = (string | ChildColumnConfig)[]
- type TableConfig = (string | ColumnConfig)[]
- // type TableRowData = Record<string, unknown>;
- type TableRowData = CellDataType;
- // type TableColumn = DataTableColumn<TableRowData>;
- // type TableColumns = DataTableColumns<TableRowData>;
- type TableBaseColumn = DataTableBaseColumn<TableRowData>;
- // type TableColumnGroup = DataTableColumnGroup<TableRowData>;
- type TableSelectionColumn = DataTableSelectionColumn<TableRowData>;
- type TableExpandColumn = DataTableExpandColumn<TableRowData>;
- // 规避naive-ui库bug,children类型定义错误
- interface DataTableColumnGroupEx<T = TableRowData> extends Omit<DataTableColumnGroup<T>, 'children'> {
- children: ChildTableColumns
- }
- type DataTableColumnEx<T = TableRowData> = Exclude<DataTableColumn<T>, DataTableColumnGroup<T>> | DataTableColumnGroupEx<T>;
- type TableColumnGroup = DataTableColumnGroupEx<TableRowData>;
- type TableColumn = DataTableColumnEx<TableRowData>;
- type TableColumns = Array<DataTableColumnEx<TableRowData>>;
- type ChildTableColumn = DataTableBaseColumn<TableRowData> | DataTableColumnGroupEx<TableRowData>;
- type ChildTableColumns = Array<(DataTableBaseColumn<TableRowData> | DataTableColumnGroupEx<TableRowData>)>;
- interface CellDataExt {
- text: string;
- colspan?: number | undefined; // 向右合并 N 个单元格
- rowspan?: number | undefined; // 向下合并 N 个单元格
- }
- type CellDataType = {
- [title: string]: string | CellDataExt,
- } & {
- // expand参数
- _expandable?: boolean;
- _expandString?: string;
- }
- interface Props {
- config?: TableConfig;
- data?: CellDataType[];
- jsonFile?: string;
- }
- const props = withDefaults(defineProps<Props>(), {
- config: () => [],
- data: () => [],
- jsonFile: () => ''
- })
- const dataPointer: {data: CellDataType[]} = {
- data: props.data
- }
- const columns = ref<TableColumns>([])
- const tableData = ref<any[]>([])
- const handleRowSpan = (rowData: TableRowData, rowIndex: number, key: string): number => {
- const data = dataPointer.data[rowIndex]
- if (!data) return 1
- const keyValue = data[key]
- if (!keyValue) return 1
- if (typeof keyValue === "string") return 1
- return keyValue.rowspan ?? 1
- }
- const handleColSpan = (rowData: TableRowData, rowIndex: number, key: string): number => {
- const data = dataPointer.data[rowIndex]
- if (!data) return 1
- const keyValue = data[key]
- if (!keyValue) return 1
- if (typeof keyValue === "string") return 1
- return keyValue.colspan ?? 1
- }
- function createStringColumn(column: string, parentKey?: string): TableBaseColumn {
- const key = parentKey ? parentKey + `.${column}` : column
- const columnObj: TableBaseColumn = {
- title: column,
- key,
- rowSpan: (rowData, rowIndex) => {
- return handleRowSpan(rowData, rowIndex, key)
- },
- colSpan: (rowData, rowIndex) => {
- return handleColSpan(rowData, rowIndex, key)
- }
- }
- return columnObj
- }
- // 标题纵向渲染
- const verticalTitle = (text: string) => () =>
- h('div', {
- style: {
- writingMode: 'vertical-rl',
- transform: 'rotate(180deg)',
- whiteSpace: 'nowrap',
- textAlign: 'center'
- }
- }, text)
- function createBaseColumn(column: ChildColumnConfig, parentKey?: string): TableBaseColumn | TableColumnGroup {
- const columnKey = column.key ? column.key : column.title
- const key = parentKey ? parentKey + `.${columnKey}` : columnKey
- const title = column.vertical ? verticalTitle(column.title) : column.title
- const columnObj: TableBaseColumn | TableColumnGroup = {
- title,
- key,
- align: column.align,
- titleAlign: column.titleAlign,
- minWidth: column.minWidth,
- maxWidth: column.maxWidth,
- width: column.width,
- ...(() => {
- if (column.children === undefined) {
- return {
- rowSpan: (rowData, rowIndex) => {
- return handleRowSpan(rowData, rowIndex, key)
- },
- colSpan: (rowData, rowIndex) => {
- return handleColSpan(rowData, rowIndex, key)
- }
- }
- } else {
- const children = traversalChildren(column.children, key)
- return children ? { children } : {}
- }
- })()
- }
- return columnObj
- }
- function createSelectionColumn(column: ColumnConfig): TableSelectionColumn {
- const columnObj: TableSelectionColumn = {
- type: 'selection',
- multiple: column.selectionMultiple
- }
- return columnObj
- }
- function createExpandColumn(column: ColumnConfig): TableExpandColumn {
- const columnObj: TableExpandColumn = {
- type: 'expand',
- expandable: (rowData: TableRowData) => {
- return rowData._expandable ? rowData._expandable : false
- },
- renderExpand: (rowData: TableRowData, rowIndex: number) => {
- if (rowData._expandable === undefined || rowData._expandable === false) return undefined
- return rowData._expandString
- }
- }
- return columnObj
- }
- function traversalChildren(child: ChildTableConfig | undefined, parentKey?: string): ChildTableColumns | undefined {
- if (child === undefined) return undefined
- return child.map(item => {
- let obj: TableColumn | undefined = undefined
- if (typeof item === 'string') {
- obj = { ...createStringColumn(item, parentKey) }
- } else {
- obj = { ...createBaseColumn(item, parentKey) }
- }
- return obj
- }).filter(item => item !== undefined)
- }
- function createColumns(config: TableConfig): TableColumns {
- return config.map(column => {
- let obj: TableColumn | undefined = undefined
- if (typeof column === 'string') {
- obj = { ...createStringColumn(column) }
- } else if (column.type === 'selection') {
- obj = { ...createSelectionColumn(column) }
- } else if (column.type === 'expand') {
- obj = { ...createExpandColumn(column) }
- } else {
- obj = { ...createBaseColumn(column) }
- }
- return obj
- }).filter(item => item !== undefined)
- }
- function createData(data: CellDataType[]) {
- return data.map((row, index) => {
- let obj = {
- key: index
- }
- for (const key in row) {
- const value = row[key as keyof typeof row];
- if (typeof value === "string") {
- obj[key] = value
- } else {
- obj[key] = value.text
- }
- }
- return obj
- })
- }
- // const columns = createColumns(props.config)
- // const tableData = createData(props.data)
- interface JsonData {
- config: TableConfig,
- data: CellDataType[]
- }
- const getJson = async (path: string): Promise<JsonData | undefined> => {
- const res = await fetch(withBase(path)).catch((err) => {
- console.log("table file err: ", err)
- })
- if (!res) return undefined
- const json = await res.json()
- return json as JsonData
- }
- let jsonDatePromise: Promise<JsonData | undefined> | undefined = undefined
- if (props.jsonFile !== "") {
- jsonDatePromise = getJson(props.jsonFile)
- }
- const loadingLocalData = () => {
- columns.value.length = 0
- tableData.value.length = 0
- columns.value.push(...createColumns(props.config))
- tableData.value.push(...createData(props.data))
- }
- onMounted(async () => {
- if (jsonDatePromise === undefined) {
- loadingLocalData()
- } else {
- jsonDatePromise.then((value) => {
- if (value === undefined) {
- loadingLocalData()
- return
- }
- // 尝试处理数据
- try {
- const jsonColumns = createColumns(value.config)
- const jsonTableData = createData(value.data)
- dataPointer.data = value.data
- columns.value.push(...jsonColumns)
- tableData.value.push(...jsonTableData)
- } catch(err) {
- console.error('json表格数据处理错误:', err)
- loadingLocalData()
- }
- }).catch((reason) => {
- console.error('获取json表格数据失败:', reason)
- loadingLocalData()
- })
- }
-
- })
- </script>
- <style scoped>
- /* 覆盖.vp-doc table */
- .normal-table :deep(table) {
- margin: 0 0 !important;
- }
- .normal-table :deep(td) {
- white-space: pre-wrap; /* 保留换行和空格 */
- }
- </style>
|