diff --git a/packages/devui-vue/devui/table/src/body/body.scss b/packages/devui-vue/devui/table/src/body/body.scss index 26e8dac0b5bcf46c90589316c39ae6b9f1efb97b..fc9ea1f32078f75adb246e6c5316be46283dfbfd 100644 --- a/packages/devui-vue/devui/table/src/body/body.scss +++ b/packages/devui-vue/devui/table/src/body/body.scss @@ -24,5 +24,10 @@ position: sticky; z-index: 5; background-color: inherit; - box-shadow: rgba(0,0,0,.05) $devui-shadow-length-slide-right; -} \ No newline at end of file + &.left { + box-shadow: rgba(0,0,0,.05) $devui-shadow-length-slide-right; + } + &.right { + box-shadow: rgba(0,0,0,.05) $devui-shadow-length-slide-left; + } +} diff --git a/packages/devui-vue/devui/table/src/body/body.tsx b/packages/devui-vue/devui/table/src/body/body.tsx index 9e79d142cd01568f63d84cacef52c9d99cbdd7b6..46f0194033f69310281ec89d8ab0ae7f83700f60 100644 --- a/packages/devui-vue/devui/table/src/body/body.tsx +++ b/packages/devui-vue/devui/table/src/body/body.tsx @@ -1,27 +1,32 @@ -import { defineComponent, inject, computed } from 'vue'; -import { TableBodyProps, TableBodyPropsTypes } from './body.type' +import { defineComponent, inject, computed, PropType, toRef } from 'vue'; import { TABLE_TOKEN } from '../table.type'; import { Checkbox } from '../../../checkbox'; import './body.scss'; +import { Column } from '../column/column.type'; +import { useFixedColumn } from '../use-table'; export default defineComponent({ name: 'DTableBody', - // props: TableBodyProps, - setup(props: TableBodyPropsTypes) { - const parent = inject(TABLE_TOKEN); + setup() { + const table = inject(TABLE_TOKEN); const { _data: data, _columns: columns, - _checkList: checkList - } = parent.store.states; + _checkList: checkList, + isFixedLeft + } = table.store.states; // 移动到行上是否高亮 - const hoverEnabled = computed(() => parent.props.rowHoveredHighlight); + const hoverEnabled = computed(() => table.props.rowHoveredHighlight); // 行前的 checkbox - const renderCheckbox = (index: number) => parent.props.checkable ? ( - + const tdAttrs = computed(() => isFixedLeft.value ? ({ + class: 'devui-sticky-cell left', + style: "left:0;" + }) : null); + const renderCheckbox = (index: number) => table.props.checkable ? ( + ) : null; @@ -32,15 +37,40 @@ export default defineComponent({ return ( {renderCheckbox(rowIndex)} - {columns.value.map((column, index) => { - return ( - {column.renderCell(row, index)} - ); - })} + {columns.value.map((column, index) => ( + + ))} ); })} ) } -}); \ No newline at end of file +}); + + +const TD = defineComponent({ + props: { + column: { + type: Object as PropType + }, + row: { + type: Object + }, + index: { + type: Number, + } + }, + setup(props: { column: Column, row: any, index: number }) { + const column = toRef(props, 'column'); + + // 固定列 + const { stickyCell, offsetStyle } = useFixedColumn(column); + + return () => ( + + {column.value.renderCell(props.row, props.index)} + + ); + } +}); diff --git a/packages/devui-vue/devui/table/src/body/use-body.ts b/packages/devui-vue/devui/table/src/body/use-body.ts index 34f68c25c3bac87e888411916f5010cd53ea96ce..49d6726547e9e8aea70066e27da7b486ae8d18d8 100644 --- a/packages/devui-vue/devui/table/src/body/use-body.ts +++ b/packages/devui-vue/devui/table/src/body/use-body.ts @@ -5,17 +5,3 @@ import { TableBodyPropsTypes } from './body.type' interface Data { rowColumns: ComputedRef<(Record & { columns: Column[]; })[]> } - -export const useTableBody = (props: TableBodyPropsTypes): Data => { - const states = props.store.states; - const rowColumns = computed(() => { - return states._data.value.map((row) => { - return { - ...row, - columns: states._columns.value - }; - }); - }); - - return { rowColumns }; -} \ No newline at end of file diff --git a/packages/devui-vue/devui/table/src/column/column.type.ts b/packages/devui-vue/devui/table/src/column/column.type.ts index 451b3be5d8ea2df79e4989b12d79126c7e84315a..99b4700ad11ef7d3fc150d7551406bea9ed4441d 100644 --- a/packages/devui-vue/devui/table/src/column/column.type.ts +++ b/packages/devui-vue/devui/table/src/column/column.type.ts @@ -43,7 +43,13 @@ export const TableColumnProps = { filterList: { type: Array as PropType, default: [] - } + }, + fixedLeft: { + type: String, + }, + fixedRight: { + type: String, + }, }; export type TableColumnPropsTypes = ExtractPropTypes; @@ -78,6 +84,8 @@ export interface Column = any> { filterable?: boolean filterMultiple?: boolean filterList?: FilterConfig[] + fixedLeft?: string + fixedRight?: string renderHeader?: () => void renderCell?: (row: T, index: number) => void formatter?: Formatter diff --git a/packages/devui-vue/devui/table/src/column/use-column.ts b/packages/devui-vue/devui/table/src/column/use-column.ts index 708b180fa086e3c4fca7915b52d7569c7365d1f0..3c63818d2f2a657e683cdf233d604ec84891cee3 100644 --- a/packages/devui-vue/devui/table/src/column/use-column.ts +++ b/packages/devui-vue/devui/table/src/column/use-column.ts @@ -18,7 +18,9 @@ export function createColumn = any>( filterable, filterList, filterMultiple, - order + order, + fixedLeft, + fixedRight } = props; const column: Column = reactive({}); @@ -28,11 +30,13 @@ export function createColumn = any>( column.order = order; }, { immediate: true }); + // 排序功能 watch([sortable, compareFn], ([sortable, compareFn]) => { column.sortable = sortable; column.compareFn = compareFn; }) + // 过滤功能 watch([ filterable, filterList, @@ -43,11 +47,21 @@ export function createColumn = any>( column.filterList = filterList; }, { immediate: true }) + // 固定左右功能 + watch([fixedLeft, fixedRight], ([left, right]) => { + column.fixedLeft = left; + column.fixedRight = right; + }, { immediate: true }); - onBeforeMount(() => { - column.width = formatWidth(width.value); - column.minWidth = formatMinWidth(minWidth.value); + // 宽度 + watch([width, minWidth], ([width, minWidth]) => { + column.width = formatWidth(width); + column.minWidth = formatMinWidth(minWidth); column.realWidth = column.width || column.minWidth; + }); + + // 基础渲染功能 + onBeforeMount(() => { column.renderHeader = defaultRenderHeader; column.renderCell = defaultRenderCell; column.formatter = formatter.value; diff --git a/packages/devui-vue/devui/table/src/header/header.tsx b/packages/devui-vue/devui/table/src/header/header.tsx index 653f6a372764279e5dd0c50b870beb520a2d1881..610a002fe77eadcd50bcb1cc4b080176584145a6 100644 --- a/packages/devui-vue/devui/table/src/header/header.tsx +++ b/packages/devui-vue/devui/table/src/header/header.tsx @@ -1,6 +1,6 @@ -import { defineComponent, inject, computed, ref, shallowRef, PropType, watch, toRefs } from 'vue'; +import { defineComponent, inject, computed, PropType, toRefs } from 'vue'; import { TABLE_TOKEN } from '../table.type'; -import { Column, FilterResults } from '../column/column.type'; +import { Column } from '../column/column.type'; import { Checkbox } from '../../../checkbox'; import { Sort } from './sort'; @@ -9,6 +9,7 @@ import { Filter } from './filter'; import './header.scss'; import '../body/body.scss'; import { useFliter, useSort } from './use-header'; +import { useFixedColumn } from '../use-table'; export default defineComponent({ @@ -18,11 +19,16 @@ export default defineComponent({ const { _checkAll: checkAll, _halfChecked: halfChecked, - _columns: columns + _columns: columns, + isFixedLeft } = table.store.states; + const thAttrs = computed(() => isFixedLeft.value ? ({ + class: 'devui-sticky-cell left', + style: "left:0;" + }) : null); const checkbox = computed(() => table.props.checkable ? ( - + ( - +
{props.column.renderHeader()} {props.column.filterable && > { _checkList: Ref _checkAll: Ref _halfChecked: Ref + isFixedLeft: Ref } insertColumn(column: Column): void sortColumn(): void @@ -29,13 +30,16 @@ export function createStore(dataSource: Ref): TableStore { const { sortData } = createSorter(dataSource, _data); const { filterData, resetFilterData } = createFilter(dataSource, _data); + const { isFixedLeft } = createFixedLogic(_columns); + return { states: { _data, _columns, _checkList, _checkAll, - _halfChecked + _halfChecked, + isFixedLeft }, insertColumn, sortColumn, @@ -204,3 +208,11 @@ const createFilter = (dataSource: Ref, _data: Ref) => { } return { filterData, resetFilterData }; } + +const createFixedLogic = (columns: Ref) => { + const isFixedLeft = computed(() => { + return columns.value.reduce((prev, current) => prev || !!current.fixedLeft, false); + }); + + return { isFixedLeft } +} diff --git a/packages/devui-vue/devui/table/src/use-table.ts b/packages/devui-vue/devui/table/src/use-table.ts index 06dc72a4043d85e04225ada1c7057c8380bbbcdb..641e775f5f1def690cb521826f9508cc3647a2c9 100644 --- a/packages/devui-vue/devui/table/src/use-table.ts +++ b/packages/devui-vue/devui/table/src/use-table.ts @@ -1,4 +1,5 @@ -import { computed, ComputedRef, CSSProperties } from 'vue'; +import { computed, ComputedRef, CSSProperties, Ref, ToRefs } from 'vue'; +import { Column } from './column/column.type'; import { TablePropsTypes } from './table.type'; interface TableConfig { @@ -19,5 +20,38 @@ export function useTable(props: TablePropsTypes): TableConfig { height: props.tableHeight, width: props.tableWidth })); - return {classes, style}; -} \ No newline at end of file + return { classes, style }; +} + + +export const useFixedColumn = (column: Ref): ToRefs<{ stickyCell: string; offsetStyle: string; }> => { + const stickyCell = computed(() => { + const col = column.value; + if (col.fixedLeft) { + return `devui-sticky-cell left`; + } + + if (col.fixedRight) { + return `devui-sticky-cell right`; + } + return undefined; + }); + + const offsetStyle = computed(() => { + const col = column.value; + if (col.fixedLeft) { + return `left:${col.fixedLeft}`; + } + + if (col.fixedRight) { + return `right:${col.fixedRight}`; + } + + return undefined; + }); + + return { + stickyCell, + offsetStyle + }; +} diff --git a/packages/devui-vue/docs/components/table/index.md b/packages/devui-vue/docs/components/table/index.md index d68739292d332ee336b219f23638e16bc3797ddc..762e2d7622194e360986d7311b4a086698e7a476 100644 --- a/packages/devui-vue/docs/components/table/index.md +++ b/packages/devui-vue/docs/components/table/index.md @@ -212,6 +212,97 @@ ::: + +### 固定列 +:::demo +```vue + + + +``` +::: + ### d-table Props | 参数 | 类型 | 默认值 | 说明 |