Skip to content

Commit

Permalink
[Table] support group columns controller (#3362)
Browse files Browse the repository at this point in the history
* feat(table): support group column

* feat(table): update common

* feat(table): support table.locale

* fix(table): lint error

* feat(table): support columnControllerTopContent and columnControllerBottomContent

* fix(table): custom column without button controller
  • Loading branch information
chaishi authored Sep 13, 2023
1 parent a46ef30 commit 0e8037f
Show file tree
Hide file tree
Showing 12 changed files with 482 additions and 86 deletions.
2 changes: 1 addition & 1 deletion src/_common
7 changes: 5 additions & 2 deletions src/config-provider/useConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ export * from './type';
* @returns {t, globalConfig}
* useConfig('pagination')
*/
export function useConfig<T extends keyof GlobalConfigProvider>(componentName?: T) {
export function useConfig<T extends keyof GlobalConfigProvider>(
componentName: T = undefined,
componentLocale?: GlobalConfigProvider[T],
) {
const injectGlobalConfig = getCurrentInstance() ? inject(configProviderInjectKey, null) : globalConfigCopy;
const mergedGlobalConfig = computed(() => injectGlobalConfig?.value || (defaultGlobalConfig as GlobalConfigProvider));
const globalConfig = computed(() => mergedGlobalConfig.value[componentName]);
const globalConfig = computed(() => Object.assign({}, mergedGlobalConfig.value[componentName], componentLocale));

const classPrefix = computed(() => {
return mergedGlobalConfig.value.classPrefix;
Expand Down
68 changes: 60 additions & 8 deletions src/table/_example/custom-col-button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<t-radio-button value="bottom-right">右下角</t-radio-button>
</t-radio-group>
<t-space>
<t-checkbox v-model="groupColumn">分组列配置</t-checkbox>
<t-checkbox v-model="bordered">是否显示边框</t-checkbox>
<t-checkbox v-model="customText">自定义列配置按钮</t-checkbox>
</t-space>
Expand All @@ -19,18 +20,13 @@
<!-- 3. onDisplayColumnsChange 当前显示列发生变化时触发 -->
<!-- 4. 如果希望顶部内容 和 列配置按钮 保持在同一行,可将内容放在 topContent,并调整按钮父元素宽度(CSS) -->
<!-- 5. resizable and tableLayout: fixed is suggested -->
<!-- 受控用法,示例代码有效,勿删 -->
<!-- :locale="tableLocale" 可用于定义列配置弹框所有文本信息 -->
<t-table
v-model:displayColumns="displayColumns"
row-key="index"
:data="data"
:columns="columns"
:column-controller="{
placement,
fields: ['channel', 'detail.email', 'createTime'],
dialogProps: { preventScrollThrough: true },
buttonProps: customText ? { content: '显示列控制', theme: 'primary', variant: 'base' } : undefined,
}"
:column-controller="columnControllerConfig"
:pagination="{ defaultPageSize: 5, defaultCurrent: 1, total: 100 }"
:bordered="bordered"
stripe
Expand All @@ -39,6 +35,15 @@
>
</t-table>

<!--
<template #columnControllerTopContent>
<span>列配置弹框顶部内容</span>
</template>
<template #columnControllerBottomContent>
<span>列配置弹框底部内容</span>
</template>
-->

<!-- 非受控用法,示例代码有效,勿删 -->
<!-- <t-table
row-key="index"
Expand All @@ -53,7 +58,7 @@
</div>
</template>
<script setup lang="jsx">
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { ErrorCircleFilledIcon, CheckCircleFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-vue-next';
const placement = ref('top-right');
Expand All @@ -79,11 +84,54 @@ for (let i = 0; i < 100; i++) {
matters: ['宣传物料制作费用', 'algolia 服务报销', '相关周边制作费', '激励奖品快递费'][i % 4],
time: [2, 3, 1, 4][i % 4],
createTime: ['2022-01-01', '2022-02-01', '2022-03-01', '2022-04-01', '2022-05-01'][i % 4],
data1: '123',
data2: '23414',
data3: '52435',
data4: '132434',
});
}
const data = ref([...initialData]);
const groupColumn = ref(false);
// 可用于隐藏弹框中的 “请选择需要在表格中显示的数据列” 这句话
// const tableLocale = ref({
// columnConfigDescriptionText: '',
// });
const columnControllerConfig = computed(() => ({
// 列配置按钮位置
placement: placement.value,
// 用于设置允许用户对哪些列进行显示或隐藏的控制,默认为全部字段
fields: ['channel', 'detail.email', 'createTime', 'data1', 'data2', 'data3', 'data4'],
// 弹框组件属性透传
dialogProps: { preventScrollThrough: true },
// 列配置按钮组件属性透传
buttonProps: customText.value ? { content: '显示列控制', theme: 'primary', variant: 'base' } : undefined,
// 数据字段分组显示
groupColumns: groupColumn.value
? [
{
label: '指标维度',
value: 'index',
columns: ['applicant', 'status', 'channel'],
},
{
label: '次要维度',
value: 'secondary',
columns: ['detail.email', 'createTime'],
},
{
label: '数据维度',
value: 'data',
columns: ['data1', 'data2', 'data3', 'data4'],
},
]
: undefined,
}));
const staticColumn = ['applicant', 'status'];
const displayColumns = ref(staticColumn.concat(['channel', 'detail.email', 'createTime']));
Expand All @@ -105,6 +153,10 @@ const columns = ref([
{ colKey: 'channel', title: '签署方式', width: '120' },
{ colKey: 'detail.email', title: '邮箱地址', ellipsis: true },
{ colKey: 'createTime', title: '申请时间' },
{ colKey: 'data1', title: 'Data A', align: 'right' },
{ colKey: 'data2', title: 'Data B', align: 'right' },
{ colKey: 'data3', title: 'Data C', align: 'right' },
{ colKey: 'data4', title: 'Data D', align: 'right' },
]);
const onColumnChange = (params) => {
Expand Down
71 changes: 61 additions & 10 deletions src/table/_example/custom-col.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
<template>
<div class="tdesign-demo-block-column-large">
<!-- 按钮操作区域 -->
<div>
<t-space align="center">
<t-button @click="columnControllerVisible = true">显示列配置弹窗</t-button>
</div>
<t-checkbox v-model="groupColumn">分组列配置</t-checkbox>
</t-space>

<!-- 1. defaultDisplayColumns = ['platform'] 设置默认显示哪些列,仅第一次有效 -->
<!-- 2. displayColumns 动态设置显示哪些列,受控属性,支持 displayColumns.sync 语法糖 -->
<!-- 3. onDisplayColumnsChange 当前显示列发生变化时触发 -->
<!-- 受控用法,示例代码有效,勿删 -->
<!-- :locale="tableLocale" 可用于定义列配置弹框所有文本信息 -->
<t-table
v-model:displayColumns="displayColumns"
v-model:columnControllerVisible="columnControllerVisible"
row-key="index"
:data="data"
:columns="columns"
:column-controller="{
fields: ['channel', 'detail.email', 'createTime'],
dialogProps: { preventScrollThrough: true },
hideTriggerButton: true,
}"
:column-controller="columnControllerConfig"
:locale="tableLocale"
:pagination="{ defaultPageSize: 5, defaultCurrent: 1, total: 100 }"
stripe
resizable
:on-column-resize-change="onColumnResizeChange"
@column-change="onColumnChange"
></t-table>
>
<template #columnControllerTopContent>
<div>You can custom top content of column controller dialog.</div>
</template>
<template #columnControllerBottomContent>
<div>You can custom bottom content of column controller dialog.</div>
</template>
</t-table>

<!-- 非受控用法,示例代码有效,勿删 -->
<!-- <t-table
Expand All @@ -41,7 +46,7 @@
</div>
</template>
<script setup lang="jsx">
import { ref } from 'vue';
import { ref, computed } from 'vue';
import { ErrorCircleFilledIcon, CheckCircleFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-vue-next';
const statusNameListMap = {
Expand All @@ -63,6 +68,10 @@ for (let i = 0; i < 100; i++) {
matters: ['宣传物料制作费用', 'algolia 服务报销', '相关周边制作费', '激励奖品快递费'][i % 4],
time: [2, 3, 1, 4][i % 4],
createTime: ['2022-01-01', '2022-02-01', '2022-03-01', '2022-04-01', '2022-05-01'][i % 4],
data1: '123',
data2: '23414',
data3: '52435',
data4: '132434',
});
}
Expand All @@ -71,8 +80,46 @@ const data = ref([...initialData]);
const staticColumn = ['applicant', 'status'];
const displayColumns = ref(staticColumn.concat(['channel', 'detail.email', 'createTime']));
const tableLocale = ref({
columnConfigDescriptionText: 'Please check columns need to show in table.',
});
// show columns in controller dialog by group
const groupColumn = ref(true);
const columnControllerVisible = ref(false);
const columnControllerConfig = computed(() => ({
// 隐藏组件内部的 列配置按钮
hideTriggerButton: true,
// 允许哪些列参与显示-隐藏控制
fields: ['channel', 'detail.email', 'createTime', 'data1', 'data2', 'data3', 'data4'],
// 透传弹框组件全部属性
dialogProps: { preventScrollThrough: true },
// 数据字段分组显示
groupColumns: groupColumn.value
? [
{
label: '指标维度',
value: 'index',
columns: ['applicant', 'status', 'channel'],
},
{
label: '次要维度',
value: 'secondary',
columns: ['detail.email', 'createTime'],
},
{
label: '数据维度',
value: 'data',
columns: ['data1', 'data2', 'data3', 'data4'],
},
]
: undefined,
}));
const columns = ref([
{ colKey: 'applicant', title: '申请人', width: '100' },
{
Expand All @@ -91,6 +138,10 @@ const columns = ref([
{ colKey: 'channel', title: '签署方式', width: '120' },
{ colKey: 'detail.email', title: '邮箱地址', ellipsis: true },
{ colKey: 'createTime', title: '申请时间' },
{ colKey: 'data1', title: 'Data A', align: 'right' },
{ colKey: 'data2', title: 'Data B', align: 'right' },
{ colKey: 'data3', title: 'Data C', align: 'right' },
{ colKey: 'data4', title: 'Data D', align: 'right' },
]);
const onColumnChange = (params) => {
console.log(params);
Expand Down
4 changes: 4 additions & 0 deletions src/table/base-table-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ export default {
loadingProps: {
type: Object as PropType<TdBaseTableProps['loadingProps']>,
},
/** 语言配置 */
locale: {
type: Object as PropType<TdBaseTableProps['locale']>,
},
/** 表格最大高度,超出后会出现滚动条。示例:100, '30%', '300'。值为数字类型,会自动加上单位 px */
maxHeight: {
type: [String, Number] as PropType<TdBaseTableProps['maxHeight']>,
Expand Down
112 changes: 112 additions & 0 deletions src/table/column-checkbox-group.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { computed, defineComponent, PropType, toRefs } from 'vue';
import Checkbox, { CheckboxGroup, CheckboxGroupChangeContext, CheckboxGroupProps, CheckboxProps } from '../checkbox';
import intersection from 'lodash/intersection';
import { usePrefixClass } from '../hooks';

export type ColumnCheckboxGroupProps = Pick<CheckboxGroupProps, 'value' | 'onChange' | 'options'> & {
checkboxProps: CheckboxGroupProps;
label?: string;
uniqueKey?: string;
};

export default defineComponent({
name: 'ColumnCheckboxGroup',

props: {
checkboxProps: Object as PropType<ColumnCheckboxGroupProps['checkboxProps']>,
options: {
type: Array as PropType<ColumnCheckboxGroupProps['options']>,
default: () => [] as ColumnCheckboxGroupProps['options'],
},
label: String,
uniqueKey: String,
value: Array as PropType<ColumnCheckboxGroupProps['value']>,
onChange: Function as PropType<ColumnCheckboxGroupProps['onChange']>,
},

setup(props: ColumnCheckboxGroupProps) {
const { checkboxProps, value, options } = toRefs(props);
const classPrefix = usePrefixClass();

const allCheckedColumnKeys = computed(() => {
const allCheckedKeys: CheckboxGroupProps['value'] = [];
options.value.forEach((option) => {
if (typeof option === 'object') {
if (option.disabled) return;
if (option.value) {
allCheckedKeys.push(option.value);
} else if (typeof option.label === 'string') {
allCheckedKeys.push(option.label);
}
} else {
allCheckedKeys.push(option);
}
});
return allCheckedKeys;
});

const intersectionKeys = computed(() => intersection(allCheckedColumnKeys.value, value.value));

const isCheckedAll = computed(() => {
const len = intersectionKeys.value.length;
return Boolean(len && allCheckedColumnKeys.value.length === len);
});

const isIndeterminate = computed(() => {
const len = intersectionKeys.value.length;
return Boolean(len < allCheckedColumnKeys.value.length && len);
});

const onCheckAllColumnsChange: CheckboxProps['onChange'] = (checkAll, ctx) => {
const changeParams: CheckboxGroupChangeContext = {
e: ctx.e,
type: 'check',
current: undefined,
option: undefined,
};
if (checkAll) {
const newKeys = [...new Set(value.value.concat(allCheckedColumnKeys.value))];
props.onChange?.(newKeys, changeParams);
} else {
const newKeys = value.value.filter((val) => !allCheckedColumnKeys.value.includes(val));
props.onChange?.(newKeys, { ...changeParams, type: 'uncheck' });
}
};

const handleCheckChange: CheckboxGroupProps['onChange'] = (val, ctx) => {
props.onChange?.(val, ctx);
};

const classes = computed(() => [
`${classPrefix.value}-table__column-controller-item`,
{
[`${classPrefix.value}-table__${props.uniqueKey}`]: props.uniqueKey,
},
]);

return () => {
return (
<div class={classes.value}>
<div class={`${classPrefix.value}-table__column-controller-block`}>
<Checkbox
indeterminate={isIndeterminate.value}
checked={isCheckedAll.value}
onChange={onCheckAllColumnsChange}
disabled={!allCheckedColumnKeys.value.length}
>
{props.label}
</Checkbox>
</div>
<div class={`${classPrefix.value}-table__column-controller-block`}>
<CheckboxGroup
options={options.value}
{...checkboxProps.value}
modelValue={value.value}
onChange={handleCheckChange}
/>
</div>
</div>
);
};
},
});
Loading

0 comments on commit 0e8037f

Please sign in to comment.