From 0ebe932b66b1772c7e47bcc2d2b2e05e9581aa91 Mon Sep 17 00:00:00 2001 From: lijun_ay Date: Sun, 3 Apr 2022 15:40:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=9A=E5=88=B6=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E8=A1=A8=E5=A4=B4=E5=8D=95=E5=85=83=E6=A0=BC=E8=A1=8C?= =?UTF-8?q?=E6=95=B0=E5=8A=9F=E8=83=BD=EF=BC=9A=201.=E9=80=9A=E8=BF=87=20u?= =?UTF-8?q?seCustomHeaderRowSpan=20=E8=AE=BE=E7=BD=AE=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=85=81=E8=AE=B8=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=A1=A8=E5=A4=B4=E5=8D=95=E5=85=83=E6=A0=BC=E8=A1=8C=E6=95=B0?= =?UTF-8?q?=E3=80=82=202.=E9=80=9A=E8=BF=87=20customRowSpan=20=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=8D=95=E5=85=83=E6=A0=BC=E6=89=80=E5=8D=A0=E8=A1=8C?= =?UTF-8?q?=E6=95=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: lijun_ay --- examples/views/grid/Group.vue | 109 ++++++++++++++- examples/views/table/base/Group.vue | 201 ++++++++++++++++++++++++++++ packages/export/src/hook.ts | 44 +++++- packages/header/src/header.ts | 5 +- packages/header/src/util.ts | 45 ++++++- packages/table/src/column.ts | 2 + packages/table/src/columnInfo.ts | 1 + packages/table/src/props.ts | 4 +- packages/table/src/table.ts | 9 +- types/colgroup.d.ts | 4 + types/column.d.ts | 8 +- types/table.d.ts | 6 +- 12 files changed, 417 insertions(+), 21 deletions(-) diff --git a/examples/views/grid/Group.vue b/examples/views/grid/Group.vue index aa079989a..cd3909f33 100644 --- a/examples/views/grid/Group.vue +++ b/examples/views/grid/Group.vue @@ -10,6 +10,17 @@ {{ demoCodes[0] }} {{ demoCodes[1] }} + +

定制表头单元格所占的行

+ + +

{{ $t('app.body.button.showCode') }}

+ +
+      {{ demoCodes[2] }}
+      {{ demoCodes[3] }}
+    
+ @@ -53,9 +64,49 @@ export default defineComponent({ { id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, address: 'Shenzhen' } ] }) - + const gridOptions2 = reactive({ + border: true, + stripe: true, + resizable: true, + useCustomHeaderRowSpan: true, + height: 500, + columns: [ + { + title: '序号', + customRowSpan: 3, + fixed: 'left', + children: [{ type: 'seq', title: '1' }] + }, + { + title: '基本信息', + children: [ + { title: 'Name', customRowSpan: 2, children: [{ field: 'name', title: '2' }] }, + { + title: '其他信息', + children: [ + { title: 'Nickname', children: [{ field: 'nickname', title: '3' }] }, + { title: 'Age', children: [{ field: 'age', title: '4' }] } + ] + }, + { title: 'Sex', customRowSpan: 2, children: [{ field: 'sex', title: '5' }] } + ] + }, + { title: 'Address', customRowSpan: 3, children: [{ field: 'address', title: '6', showOverflow: true }] } + ], + data: [ + { id: 10001, name: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 28, address: 'Shenzhen' }, + { id: 10002, name: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' }, + { id: 10003, name: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' }, + { id: 10004, name: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 23, address: 'Shenzhen' }, + { id: 10005, name: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' }, + { id: 10006, name: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 21, address: 'Shenzhen' }, + { id: 10007, name: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 29, address: 'Shenzhen' }, + { id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, address: 'Shenzhen' } + ] + }) return { gridOptions, + gridOptions2, demoCodes: [ ` @@ -106,6 +157,62 @@ export default defineComponent({ } } }) + `, + ` + + `, + ` + import { defineComponent, reactive } from 'vue' + import { VxeGridProps } from 'vxe-table' + + export default defineComponent({ + setup () { + const gridOptions = reactive({ + border: true, + stripe: true, + resizable: true, + useCustomHeaderRowSpan: true, + height: 500, + columns: [ + { + title: '序号', + customRowSpan: 3, + fixed: 'left', + children: [{ type: 'seq', title: '1' }] + }, + { + title: '基本信息', + children: [ + { title: 'Name', customRowSpan: 2, children: [{ field: 'name', title: '2' }] }, + { + title: '其他信息', + children: [ + { title: 'Nickname', children: [{ field: 'nickname', title: '3' }] }, + { title: 'Age', children: [{ field: 'age', title: '4' }] } + ] + }, + { title: 'Sex', customRowSpan: 2, children: [{ field: 'sex', title: '5' }] } + ] + }, + { title: 'Address', customRowSpan: 3, children: [{ field: 'address', title: '6', showOverflow: true }] } + ], + data: [ + { id: 10001, name: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 28, address: 'Shenzhen' }, + { id: 10002, name: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' }, + { id: 10003, name: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' }, + { id: 10004, name: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 23, address: 'Shenzhen' }, + { id: 10005, name: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' }, + { id: 10006, name: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 21, address: 'Shenzhen' }, + { id: 10007, name: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 29, address: 'Shenzhen' }, + { id: 10008, name: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, address: 'Shenzhen' } + ] + }) + + return { + gridOptions + } + } + }) ` ] } diff --git a/examples/views/table/base/Group.vue b/examples/views/table/base/Group.vue index ffba00b62..d58b972fc 100644 --- a/examples/views/table/base/Group.vue +++ b/examples/views/table/base/Group.vue @@ -1,5 +1,6 @@ @@ -123,11 +194,39 @@ export default defineComponent({ } } + const xTable3 = ref({} as VxeTableInstance) + const tableData3 = ref([ + { id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' }, + { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' }, + { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' }, + { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, address: 'test abc' }, + { id: 10005, name: 'Test5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' }, + { id: 10006, name: 'Test6', role: 'Designer', sex: 'Women', age: 21, address: 'test abc' }, + { id: 10007, name: 'Test7', role: 'Test', sex: 'Man', age: 29, address: 'test abc' }, + { id: 10008, name: 'Test8', role: 'Develop', sex: 'Man', age: 35, address: 'test abc' } + ]) + + const toggleFixedColumn3 = (field: string, type: VxeColumnPropTypes.Fixed) => { + const $table = xTable3.value + const column = $table.getColumnByField(field) + if (column) { + const groupFixed = column.fixed ? null : type + // 将分组整体设置固定列 + XEUtils.eachTree([column], column => { + column.fixed = groupFixed + }) + // 刷新列 + $table.refreshColumn() + } + } return { tableData1, xTable2, tableData2, toggleFixedColumn, + xTable3, + tableData3, + toggleFixedColumn3, demoCodes: [ ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `, + ` + import { defineComponent, ref } from 'vue' + import { VxeTableInstance, VxeColumnPropTypes } from 'vxe-table' + import XEUtils from 'xe-utils' + + export default defineComponent({ + setup () { + const xTable3 = ref({} as VxeTableInstance) + + const tableData3 = ref([ + { id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' }, + { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' }, + { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' }, + { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, address: 'test abc' }, + { id: 10005, name: 'Test5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' }, + { id: 10006, name: 'Test6', role: 'Designer', sex: 'Women', age: 21, address: 'test abc' }, + { id: 10007, name: 'Test7', role: 'Test', sex: 'Man', age: 29, address: 'test abc' }, + { id: 10008, name: 'Test8', role: 'Develop', sex: 'Man', age: 35, address: 'test abc' } + ]) + + const toggleFixedColumn3 = (field: string, type: VxeColumnPropTypes.Fixed) => { + const $table = xTable3.value + const column = $table.getColumnByField(field) + if (column) { + const groupFixed = column.fixed ? null : type + // 将分组整体设置固定列 + XEUtils.eachTree([column], column => { + column.fixed = groupFixed + }) + // 刷新列 + $table.refreshColumn() + } + } + + return { + xTable3, + tableData3, + toggleFixedColumn3 + } + } + } ` ] } diff --git a/packages/export/src/hook.ts b/packages/export/src/hook.ts index 38a25dd78..c73aefb5f 100644 --- a/packages/export/src/hook.ts +++ b/packages/export/src/hook.ts @@ -31,7 +31,7 @@ const getConvertColumns = (columns: any) => { return result } -const convertToRows = (originColumns: any): any[][] => { +const convertToRows = (originColumns: any, useCustomRowSpan? : boolean): any[][] => { let maxLevel = 1 const traverse = (column: any, parent?: any) => { if (parent) { @@ -63,14 +63,46 @@ const convertToRows = (originColumns: any): any[][] => { } const allColumns = getConvertColumns(originColumns) - + const getGroupParentRowSpan = (columnId:string) => { + let r = 0 + for (let i = 0; i < allColumns.length; i++) { + const column = allColumns[i] + if (column.id === columnId) { + if (column._rowSpan && column._rowSpan > 1) { + r = r + column._rowSpan + } + if (column.parentId) { + r = r + getGroupParentRowSpan(column.parentId) + } + } + } + return r + } allColumns.forEach((column: any) => { - if (column.childNodes && column.childNodes.length) { - column._rowSpan = 1 + if (useCustomRowSpan) { + if (column.customRowSpan) { + column._rowSpan = column.customRowSpan + } else { + column._rowSpan = 1 + } + let alevel = column._level - 1 + if (column.parentId) { + let parentRowSpan = getGroupParentRowSpan(column.parentId) + parentRowSpan = parentRowSpan > 0 ? parentRowSpan - 1 : 0 + alevel = alevel + parentRowSpan + if (alevel >= maxLevel) { + alevel = maxLevel - 1 + } + } + rows[alevel].push(column) } else { - column._rowSpan = maxLevel - column._level + 1 + if (column.childNodes && column.childNodes.length) { + column._rowSpan = 1 + } else { + column._rowSpan = maxLevel - column._level + 1 + } + rows[column._level - 1].push(column) } - rows[column._level - 1].push(column) }) return rows diff --git a/packages/header/src/header.ts b/packages/header/src/header.ts index afec0dffc..25798340a 100644 --- a/packages/header/src/header.ts +++ b/packages/header/src/header.ts @@ -15,7 +15,8 @@ export default defineComponent({ tableColumn: Array as PropType, tableGroupColumn: Array as PropType, fixedColumn: Array as PropType, - fixedType: { type: String as PropType, default: null } + fixedType: { type: String as PropType, default: null }, + useCustomHeaderRowSpan: { type: Boolean, default: false } }, setup (props) { const $xetable = inject('$xetable', {} as VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods) @@ -35,7 +36,7 @@ export default defineComponent({ const uploadColumn = () => { const { isGroup } = tableReactData - headerColumn.value = isGroup ? convertToRows(props.tableGroupColumn) : [] + headerColumn.value = isGroup ? convertToRows(props.tableGroupColumn, props.useCustomHeaderRowSpan) : [] } const resizeMousedown = (evnt: MouseEvent, params: any) => { diff --git a/packages/header/src/util.ts b/packages/header/src/util.ts index 39618b54e..c91654ffe 100644 --- a/packages/header/src/util.ts +++ b/packages/header/src/util.ts @@ -14,7 +14,7 @@ const getAllColumns = (columns: any, parentColumn?: any) => { return result } -export const convertToRows = (originColumns: any): any[][] => { +export const convertToRows = (originColumns: any, useCustomRowSpan?: boolean): any[][] => { let maxLevel = 1 const traverse = (column: any, parent?: any) => { if (parent) { @@ -49,13 +49,48 @@ export const convertToRows = (originColumns: any): any[][] => { const allColumns = getAllColumns(originColumns) + // rowSpan 计算 + const getGroupParentRowSpan = (columnId: string) => { + let r = 0 + for (let i = 0; i < allColumns.length; i++) { + const column = allColumns[i] + if (column.id === columnId) { + if (column.rowSpan && column.rowSpan > 1) { + r = r + column.rowSpan + } + if (column.parentId) { + r = r + getGroupParentRowSpan(column.parentId) + } + } + } + return r + } + allColumns.forEach((column) => { - if (column.children && column.children.length && column.children.some((column: any) => column.visible)) { - column.rowSpan = 1 + if (useCustomRowSpan) { + if (column.customRowSpan) { + column.rowSpan = column.customRowSpan + } else { + column.rowSpan = 1 + } + let alevel = column.level - 1 + if (column.parentId) { + let parentRowSpan = getGroupParentRowSpan(column.parentId) + parentRowSpan = parentRowSpan > 0 ? parentRowSpan - 1 : 0 + alevel = alevel + parentRowSpan + if (alevel >= maxLevel) { + alevel = maxLevel - 1 + } + } + rows[alevel].push(column) } else { - column.rowSpan = maxLevel - column.level + 1 + if (column.children && column.children.length && column.children.some((column: any) => column.visible)) { + column.rowSpan = 1 + } else { + column.rowSpan = maxLevel - column.level + 1 + } + rows[column.level - 1].push(column) } - rows[column.level - 1].push(column) }) return rows diff --git a/packages/table/src/column.ts b/packages/table/src/column.ts index 4ab9d3ae7..d8476582e 100644 --- a/packages/table/src/column.ts +++ b/packages/table/src/column.ts @@ -27,6 +27,8 @@ export const columnProps = { headerAlign: String as PropType, // 表尾列的对齐方式 footerAlign: String as PropType, + // 定制行高 + customRowSpan: { type: [Number, String] as PropType, default: 1 }, // 当内容过长时显示为省略号 showOverflow: { type: [Boolean, String] as PropType, default: null }, // 当表头内容过长时显示为省略号 diff --git a/packages/table/src/columnInfo.ts b/packages/table/src/columnInfo.ts index 8fe97ad0c..48a698c02 100644 --- a/packages/table/src/columnInfo.ts +++ b/packages/table/src/columnInfo.ts @@ -75,6 +75,7 @@ export class ColumnInfo { className: _vm.className, headerClassName: _vm.headerClassName, footerClassName: _vm.footerClassName, + customRowSpan: _vm.customRowSpan, formatter: formatter, sortable: _vm.sortable, sortBy: _vm.sortBy, diff --git a/packages/table/src/props.ts b/packages/table/src/props.ts index db9229c57..94c25b627 100644 --- a/packages/table/src/props.ts +++ b/packages/table/src/props.ts @@ -160,5 +160,7 @@ export default { // (可能会被废弃的参数,不要使用) delayHover: { type: Number as PropType, default: () => GlobalConfig.table.delayHover as number }, // 额外的参数 - params: Object as PropType + params: Object as PropType, + // 使用自定义表头单元格行数方式 + useCustomHeaderRowSpan: { type: Boolean as PropType, default: () => GlobalConfig.table.useCustomHeaderRowSpan } } diff --git a/packages/table/src/table.ts b/packages/table/src/table.ts index 77699a4d0..9d0bfe2b8 100644 --- a/packages/table/src/table.ts +++ b/packages/table/src/table.ts @@ -63,6 +63,7 @@ export default defineComponent({ parentHeight: 0, // 是否使用分组表头 isGroup: false, + useCustomHeaderRowSpan: false, isAllOverflow: false, // 复选框属性,是否全选 isAllSelected: false, @@ -892,6 +893,7 @@ export default defineComponent({ const mouseOpts = computeMouseOpts.value const isGroup = collectColumn.some(hasChildrenList) let isAllOverflow = !!props.showOverflow + const useCustomHeaderRowSpan = !!props.useCustomHeaderRowSpan let expandColumn: any let treeNodeColumn: any let checkboxColumn: any @@ -969,7 +971,7 @@ export default defineComponent({ errLog('vxe.error.errConflicts', ['mouse-config.area', 'column.type=expand']) } } - + reactData.useCustomHeaderRowSpan = useCustomHeaderRowSpan reactData.isGroup = isGroup reactData.treeNodeColumn = treeNodeColumn reactData.expandColumn = expandColumn @@ -5897,7 +5899,7 @@ export default defineComponent({ const renderVN = () => { const { loading, stripe, showHeader, height, treeConfig, mouseConfig, showFooter, highlightCell, highlightHoverRow, highlightHoverColumn, editConfig } = props - const { isGroup, overflowX, overflowY, scrollXLoad, scrollYLoad, scrollbarHeight, tableData, tableColumn, tableGroupColumn, footerTableData, initStore, columnStore, filterStore } = reactData + const { isGroup, useCustomHeaderRowSpan, overflowX, overflowY, scrollXLoad, scrollYLoad, scrollbarHeight, tableData, tableColumn, tableGroupColumn, footerTableData, initStore, columnStore, filterStore } = reactData const { leftList, rightList } = columnStore const tipConfig = computeTipConfig.value const treeOpts = computeTreeOpts.value @@ -5956,7 +5958,8 @@ export default defineComponent({ ref: refTableHeader, tableData, tableColumn, - tableGroupColumn + tableGroupColumn, + useCustomHeaderRowSpan }) : createCommentVNode(), /** * 表体 diff --git a/types/colgroup.d.ts b/types/colgroup.d.ts index 6917cd057..e3b57c179 100644 --- a/types/colgroup.d.ts +++ b/types/colgroup.d.ts @@ -64,6 +64,10 @@ export type VxeColgroupProps = { * 是否可视 */ visible?: VxeColumnPropTypes.Visible + /** + * 定制行高 + */ + customRowSpan?: VxeColumnPropTypes.CustomRowSpan /** * 额外的参数 */ diff --git a/types/column.d.ts b/types/column.d.ts index 658eae0f3..5ba7dd901 100644 --- a/types/column.d.ts +++ b/types/column.d.ts @@ -23,6 +23,7 @@ export namespace VxeColumnPropTypes { export type Align = 'left' | 'center' | 'right' | null export type HeaderAlign = Align export type FooterAlign = Align + export type CustomRowSpan = string | number export type ShowOverflow = VxeTablePropTypes.ShowOverflow export type ShowHeaderOverflow = ShowOverflow export type ShowFooterOverflow = ShowOverflow @@ -159,7 +160,6 @@ export namespace VxeColumnPropTypes { } export type Params = any - interface FilterSlotParams { $panel: VxeFilterPanel column: { @@ -363,5 +363,9 @@ export type VxeColumnProps = { /** * 额外的参数 */ - params?: VxeColumnPropTypes.Params + params?: VxeColumnPropTypes.Params, + /** + * 定制单元格行高 + */ + customRowSpan?: VxeColumnPropTypes.CustomRowSpan, } diff --git a/types/table.d.ts b/types/table.d.ts index ae355128c..f74555f82 100644 --- a/types/table.d.ts +++ b/types/table.d.ts @@ -759,6 +759,8 @@ export interface TableReactData { parentHeight: number // 是否使用分组表头 isGroup: boolean + // 自定义表头单元格行数 + useCustomHeaderRowSpan: boolean isAllOverflow: boolean // 复选框属性,是否全选 isAllSelected: boolean @@ -1940,6 +1942,7 @@ export namespace VxeTablePropTypes { } export type Params = any + export type UseCustomHeaderRowSpan = boolean } export type VxeTableProps = { @@ -2053,6 +2056,7 @@ export type VxeTableProps = { scrollX?: VxeTablePropTypes.ScrollX scrollY?: VxeTablePropTypes.ScrollY params?: VxeTablePropTypes.Params + useCustomHeaderRowSpan?: VxeTablePropTypes.UseCustomHeaderRowSpan } export type VxeTableEmits = [ @@ -2615,4 +2619,4 @@ export namespace VxeTableEvents { export type ValidError = (params: VxeTableDefines.ValidErrorEventParams) => void export type Scroll = (params: VxeTableDefines.ScrollEventParams) => void export type Custom = (params: VxeTableDefines.CustomEventParams) => void -} \ No newline at end of file +} -- Gitee