Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table] support group columns controller #3362

Merged
merged 6 commits into from
Sep 13, 2023
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
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
Loading