Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineComponent, toRef } from 'vue';
import { defineComponent, toRef, inject } from 'vue';
import type { PropType } from 'vue';
import { Column } from '../column/column-types';
import { TABLE_TOKEN } from '../../table-types';
import { useFixedColumn } from '../../composable/use-table';

export default defineComponent({
Expand All @@ -20,14 +21,13 @@ export default defineComponent({
},
},
setup(props: { column: Column; row: any; index: number }) {
const table = inject(TABLE_TOKEN);
const column = toRef(props, 'column');

// 固定列
const { stickyCell, offsetStyle } = useFixedColumn(column);
const { stickyClass, stickyStyle } = useFixedColumn(column);

return () => (
<td class={stickyCell.value} style={offsetStyle.value}>
{column.value.renderCell?.(props.row, props.index)}
<td class={stickyClass.value} style={stickyStyle.value}>
{column.value.renderCell?.(props.row, column.value, table.store, props.index)}
</td>
);
},
Expand Down
21 changes: 6 additions & 15 deletions packages/devui-vue/devui/table/src/components/body/body.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineComponent, inject, computed } from 'vue';
import { TABLE_TOKEN } from '../../table-types';
import { TABLE_TOKEN, DefaultRow } from '../../table-types';
import { Column } from '../column/column-types';
import TD from '../body-td/body-td';
import { Checkbox } from '../../../../checkbox';
import { useNamespace } from '../../../../shared/hooks/use-namespace';
import { useMergeCell } from './use-body';
import './body.scss';
Expand All @@ -10,33 +10,24 @@ export default defineComponent({
name: 'DTableBody',
setup() {
const table = inject(TABLE_TOKEN);
const { _data: data, flatColumns, _checkList: checkList, isFixedLeft } = table.store.states;
const { _data: data, flatColumns } = table.store.states;
const ns = useNamespace('table');
const hoverEnabled = computed(() => table.props.rowHoveredHighlight);
const { tableSpans, removeCells } = useMergeCell();
const tdAttrs = computed(() => (isFixedLeft.value ? { class: `${ns.m('sticky-cell')} left`, style: 'left:0;' } : null));

const renderCheckbox = (index: number) =>
table.props.checkable ? (
<td class={ns.e('checkable-cell')} {...tdAttrs.value}>
<Checkbox v-model={checkList.value[index]} />
</td>
) : null;

return () => (
<tbody class={ns.e('tbody')}>
{data.value.map((row, rowIndex) => {
{data.value.map((row: DefaultRow, rowIndex: number) => {
return (
<tr key={rowIndex} class={{ 'hover-enabled': hoverEnabled.value }}>
{renderCheckbox(rowIndex)}
{flatColumns.value.map((column, columnIndex) => {
{flatColumns.value.map((column: Column, columnIndex: number) => {
const cellId = `${rowIndex}-${columnIndex}`;
const [rowspan, colspan] = tableSpans.value[cellId] ?? [1, 1];

if (removeCells.value.includes(cellId)) {
return null;
}
return <TD column={column} index={columnIndex} row={row} rowspan={rowspan} colspan={colspan} />;
return <TD column={column} index={rowIndex} row={row} rowspan={rowspan} colspan={colspan} />;
})}
</tr>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import type { PropType, ExtractPropTypes, VNode, Slot, ComponentInternalInstance } from 'vue';
import { DefaultRow } from '../../table-types';
import { TableStore } from '../../store/store-types';

export type Formatter<T = any, R = any> = (row: T, cellValue: R, index: number) => VNode[];
// eslint-disable-next-line no-use-before-define
export type Formatter = (row: DefaultRow, column: Column, cellValue: any, rowIndex: number) => VNode[];

export type CompareFn<T = any> = (field: string, a: T, b: T) => boolean;

export type ColumnType = 'checkable' | '';

export interface FilterConfig {
id: number | string;
name: string;
value: any;
checked?: boolean;
}

export const TableColumnProps = {
export const tableColumnProps = {
type: {
type: String as PropType<ColumnType>,
default: '',
},
header: {
type: String,
default: '',
Expand Down Expand Up @@ -63,7 +72,7 @@ export const TableColumnProps = {
},
};

export type TableColumnPropsTypes = ExtractPropTypes<typeof TableColumnProps>;
export type TableColumnProps = ExtractPropTypes<typeof tableColumnProps>;

export type FilterResults = (string | number)[];

Expand All @@ -74,8 +83,9 @@ export interface CustomFilterProps {

export type CustomFilterSlot = (props: CustomFilterProps) => VNode[];

export interface Column<T> {
export interface Column {
id?: string;
type?: ColumnType;
field?: string;
width?: number;
minWidth?: number;
Expand All @@ -88,15 +98,15 @@ export interface Column<T> {
filterList?: FilterConfig[];
fixedLeft?: string;
fixedRight?: string;
renderHeader?: () => void;
renderCell?: (row: T, index: number) => void;
formatter?: Formatter<T>;
compareFn?: CompareFn<T>;
renderHeader?: (column: Column, store: TableStore) => VNode;
renderCell?: (rowData: DefaultRow, columnItem: Column, store: TableStore, rowIndex: number) => VNode;
formatter?: Formatter;
compareFn?: CompareFn;
customFilterTemplate?: CustomFilterSlot;
subColumns?: Slot;
}

export interface TableColumn<T> extends ComponentInternalInstance {
export interface TableColumn extends ComponentInternalInstance {
columnId: string;
columnConfig: Partial<Column<T>>;
columnConfig: Partial<Column>;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { inject, defineComponent, onBeforeUnmount, onMounted, toRefs, watch, ref, getCurrentInstance, onBeforeMount, h } from 'vue';
import { TableColumnProps, TableColumnPropsTypes, TableColumn } from './column-types';
import { tableColumnProps, TableColumnProps, TableColumn } from './column-types';
import { TABLE_TOKEN, Table, DefaultRow } from '../../table-types';
import { createColumn, useRender } from './use-column';

let columnIdInit = 1;

export default defineComponent({
name: 'DColumn',
props: TableColumnProps,
setup(props: TableColumnPropsTypes, ctx) {
const instance = getCurrentInstance() as TableColumn<unknown>;
props: tableColumnProps,
setup(props: TableColumnProps, ctx) {
const instance = getCurrentInstance() as TableColumn;
const column = createColumn(toRefs(props), ctx.slots);
const owner = inject(TABLE_TOKEN) as Table<DefaultRow>;
const isSubColumn = ref(false);
Expand Down
28 changes: 28 additions & 0 deletions packages/devui-vue/devui/table/src/components/column/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { h } from 'vue';
import type { VNode } from 'vue';
import { DefaultRow } from '../../table-types';
import { Column } from './column-types';
import { TableStore } from '../../store/store-types';
import { Checkbox } from '../../../../checkbox';

export const cellMap = {
checkable: {
renderHeader(column: Column, store: TableStore): VNode {
return h(Checkbox, {
modelValue: store.states._checkAll.value,
halfchecked: store.states._halfChecked.value,
onChange: (val: boolean) => {
store.states._checkAll.value = val;
},
});
},
renderCell(rowData: DefaultRow, column: Column, store: TableStore, rowIndex: number): VNode {
return h(Checkbox, {
modelValue: store.states._checkList.value[rowIndex],
onChange: (val: boolean) => {
store.states._checkList.value[rowIndex] = val;
},
});
},
},
};
40 changes: 22 additions & 18 deletions packages/devui-vue/devui/table/src/components/column/use-column.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { watch, reactive, onBeforeMount, computed, h, getCurrentInstance } from 'vue';
import type { ToRefs, Slots, ComputedRef } from 'vue';
import { Table } from '../../table-types';
import { Column, TableColumnPropsTypes, TableColumn } from './column-types';
import { Table, DefaultRow } from '../../table-types';
import { Column, TableColumnProps, TableColumn } from './column-types';
import { TableStore } from '../../store/store-types';
import { formatWidth, formatMinWidth } from '../../utils';
import { cellMap } from './config';

function defaultRenderHeader<T>(this: Column<T>) {
return h('span', { class: 'title' }, this.header);
}

export function createColumn<T extends Record<string, unknown> = any>(props: ToRefs<TableColumnPropsTypes>, templates: Slots): Column<T> {
export function createColumn(props: ToRefs<TableColumnProps>, templates: Slots): Column {
const {
type,
field,
header,
sortable,
Expand All @@ -24,15 +23,20 @@ export function createColumn<T extends Record<string, unknown> = any>(props: ToR
fixedLeft,
fixedRight,
} = props;
const column: Column<T> = reactive({});
const column: Column = reactive({});
column.type = type.value;

function defaultRenderHeader(columnItem: Column) {
return h('span', { class: 'title' }, columnItem.header);
}

function defaultRenderCell<K extends Record<string, unknown>>(rowData: K, index: number) {
const value = rowData[this.field];
function defaultRenderCell(rowData: DefaultRow, columnItem: Column, store: TableStore, rowIndex: number) {
const value = columnItem.field ? rowData[columnItem.field] : '';
if (templates.default) {
return templates.default(rowData);
}
if (this.formatter) {
return this.formatter(rowData, value, index);
if (columnItem.formatter) {
return columnItem.formatter(rowData, columnItem, value, rowIndex);
}

return value?.toString?.() ?? '';
Expand Down Expand Up @@ -69,8 +73,8 @@ export function createColumn<T extends Record<string, unknown> = any>(props: ToR
watch(
[fixedLeft, fixedRight],
([left, right]) => {
column.fixedLeft = left?.value;
column.fixedRight = right?.value;
column.fixedLeft = left;
column.fixedRight = right;
},
{ immediate: true }
);
Expand All @@ -84,8 +88,8 @@ export function createColumn<T extends Record<string, unknown> = any>(props: ToR

// 基础渲染功能
onBeforeMount(() => {
column.renderHeader = defaultRenderHeader;
column.renderCell = defaultRenderCell;
column.renderHeader = type.value ? cellMap[type.value].renderHeader : defaultRenderHeader;
column.renderCell = type.value ? cellMap[type.value].renderCell : defaultRenderCell;
column.formatter = formatter?.value;
column.customFilterTemplate = templates.customFilterTemplate;
column.subColumns = templates.subColumns;
Expand All @@ -95,10 +99,10 @@ export function createColumn<T extends Record<string, unknown> = any>(props: ToR
}

export function useRender<T>(): {
columnOrTableParent: ComputedRef<Table<T> | TableColumn<T>>;
columnOrTableParent: ComputedRef<Table<T> | TableColumn>;
getColumnIndex: (children: Array<unknown>, child: unknown) => number;
} {
const instance = getCurrentInstance() as TableColumn<T>;
const instance = getCurrentInstance() as TableColumn;
const columnOrTableParent = computed(() => {
let parent: any = instance?.parent;
while (parent && !parent.tableId && !parent.columnId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineComponent, inject, toRefs } from 'vue';
import { PropType } from 'vue';
import type { PropType } from 'vue';
import { Column } from '../column/column-types';
import { TABLE_TOKEN } from '../../table-types';
import { Sort } from '../sort';
Expand All @@ -20,12 +20,12 @@ export default defineComponent({
const { column } = toRefs(props);
const directionRef = useSort(table.store, column);
const filteredRef = useFilter(table.store, column);
const { stickyCell, offsetStyle } = useFixedColumn(column);
const { stickyClass, stickyStyle } = useFixedColumn(column);

return () => (
<th class={stickyCell.value} style={offsetStyle.value}>
<th class={stickyClass.value} style={stickyStyle.value}>
<div class="header-container">
{props.column.renderHeader?.()}
{props.column.renderHeader?.(column.value, table.store)}
{props.column.filterable && (
<Filter v-model={filteredRef.value} filterList={props.column.filterList} customTemplate={props.column.customFilterTemplate} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
font-size: $devui-font-size-card-title;
color: $devui-text;
font-weight: 700;
border: none;
background-color: $devui-base-bg;

th {
Expand All @@ -19,7 +20,6 @@
position: relative;
display: flex;
align-items: center;
padding-left: 2px;
padding-right: 8px;

.title {
Expand Down
20 changes: 3 additions & 17 deletions packages/devui-vue/devui/table/src/components/header/header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { defineComponent, inject, computed } from 'vue';
import { TABLE_TOKEN } from '../../table-types';
import { Checkbox } from '../../../../checkbox';
import { defineComponent } from 'vue';
import TH from '../header-th/header-th';
import { useNamespace } from '../../../../shared/hooks/use-namespace';
import { useHeader } from './use-header';
Expand All @@ -10,26 +8,14 @@ import '../body/body.scss';
export default defineComponent({
name: 'DTableHeader',
setup() {
const table = inject(TABLE_TOKEN);
const { _checkAll: checkAll, _halfChecked: halfChecked, isFixedLeft } = table.store.states;
const ns = useNamespace('table');
const { headerRows } = useHeader();

const thAttrs = computed(() => (isFixedLeft.value ? { class: `${ns.m('sticky-cell')} left`, style: 'left:0;' } : null));
const checkbox = computed(() =>
table.props.checkable ? (
<th class={ns.e('checkable-cell')} {...thAttrs.value}>
<Checkbox v-model={checkAll.value} halfchecked={halfChecked.value} />
</th>
) : null
);

return () => (
<thead class={ns.e('thead')}>
{headerRows.value.map((subColumns, rowIndex) => (
{headerRows.value.map((subColumns) => (
<tr>
{checkbox.value}
{subColumns.map((column, columnIndex) => (
{subColumns.map((column, columnIndex: number) => (
<TH key={columnIndex} column={column} colspan={column.colSpan} rowspan={column.rowSpan} />
))}
</tr>
Expand Down
Loading