fix(表): 标签列影响表格排序优化标签展示方案二

This commit is contained in:
xinxin.wu 2024-09-20 14:25:49 +08:00 committed by Craftsman
parent fcc2ee8baa
commit a756bce949
7 changed files with 218 additions and 90 deletions

View File

@ -18,6 +18,7 @@
@change="(data: TableData[], extra: TableChangeExtra, currentData: TableData[]) => handleDragChange(data, extra, currentData)" @change="(data: TableData[], extra: TableChangeExtra, currentData: TableData[]) => handleDragChange(data, extra, currentData)"
@sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)" @sorter-change="(dataIndex: string,direction: string) => handleSortChange(dataIndex, direction)"
@cell-click="(record: TableData,column: TableColumnData,ev: Event) => emit('cell-click',record, column,ev)" @cell-click="(record: TableData,column: TableColumnData,ev: Event) => emit('cell-click',record, column,ev)"
@column-resize="columnResize"
> >
<template #optional="{ rowIndex, record }"> <template #optional="{ rowIndex, record }">
<slot name="optional" v-bind="{ rowIndex, record }" /> <slot name="optional" v-bind="{ rowIndex, record }" />
@ -64,10 +65,12 @@
<div v-if="attrs.showPagination && props.showSelectorAll" class="w-[16px]"></div> <div v-if="attrs.showPagination && props.showSelectorAll" class="w-[16px]"></div>
</template> </template>
</a-table-column> </a-table-column>
<!-- 最小宽度200+16的内边距 -->
<a-table-column <a-table-column
v-for="(item, idx) in currentColumns" v-for="(item, idx) in currentColumns"
:key="idx" :key="idx"
:width="item.isTag || item.isStringTag ? getColumnTagLastWidthMap(item) : item.width" :width="item.isTag || item.isStringTag ? 216 : item.width"
:min-width="item.isTag || item.isStringTag ? 216 : 80"
:align="item.align" :align="item.align"
:fixed="item.fixed" :fixed="item.fixed"
:sortable="item.sortable" :sortable="item.sortable"
@ -148,6 +151,7 @@
:tag-list="record[item.dataIndex as string]" :tag-list="record[item.dataIndex as string]"
type="primary" type="primary"
theme="outline" theme="outline"
show-table
:tag-position="item.tagPosition" :tag-position="item.tagPosition"
/> />
</slot> </slot>
@ -299,6 +303,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { debounce } from 'lodash-es';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsPagination from '@/components/pure/ms-pagination/index'; import MsPagination from '@/components/pure/ms-pagination/index';
@ -513,103 +518,103 @@
immediate: true, immediate: true,
} }
); );
// TODO
// const columnLastWidthMap = ref<Record<string, any>>({});
const columnLastWidthMap = ref<Record<string, any>>({}); // function getColumnTagLastWidthMap(column: MsTableColumnData) {
// const editTgaMinColumnWidth = 200;
// // 200
// if (column?.allowEditTag) {
// if (columnLastWidthMap.value[column.dataIndex as string] < editTgaMinColumnWidth) {
// return editTgaMinColumnWidth;
// }
// return columnLastWidthMap.value[column.dataIndex as string];
// }
// return columnLastWidthMap.value[column.dataIndex as string];
// }
function getColumnTagLastWidthMap(column: MsTableColumnData) { // //
const editTgaMinColumnWidth = 200; // const getTagWidth = (tag: Record<string, any>, lastText: string) => {
// 200 // const maxTagWidth = 144; //
if (column?.allowEditTag) { // const spanPadding = 8; //
if (columnLastWidthMap.value[column.dataIndex as string] < editTgaMinColumnWidth) { // const spanBorder = 2; //
return editTgaMinColumnWidth; // const marginRight = 4; //
} // const fillWidth = 8; //
return columnLastWidthMap.value[column.dataIndex as string];
}
return columnLastWidthMap.value[column.dataIndex as string];
}
// // const el = document.createElement('div');
const getTagWidth = (tag: Record<string, any>, lastText: string) => { // el.style.visibility = 'hidden';
const maxTagWidth = 144; // // el.style.position = 'absolute';
const spanPadding = 8; // // el.style.fontSize = '12px';
const spanBorder = 2; // // // +n+n
const marginRight = 4; // // el.textContent = lastText || tag.name || tag;
const fillWidth = 8; //
const el = document.createElement('div'); // document.body.appendChild(el);
el.style.visibility = 'hidden'; // let width = el.offsetWidth + spanPadding * 2 + spanBorder;
el.style.position = 'absolute';
el.style.fontSize = '12px';
// +n+n
el.textContent = lastText || tag.name || tag;
document.body.appendChild(el); // //
let width = el.offsetWidth + spanPadding * 2 + spanBorder; // document.body.removeChild(el);
// width = width > maxTagWidth ? maxTagWidth + marginRight + fillWidth : width + marginRight + fillWidth;
// return width;
// };
// // //
document.body.removeChild(el); // const getRowTagTotalWidth = (tags: TableData[]) => {
width = width > maxTagWidth ? maxTagWidth + marginRight + fillWidth : width + marginRight + fillWidth; // const maxShowTagCount = 3; // 3
return width; // const tablePadding = 24; //
};
// // const tagArr = tags.length > maxShowTagCount ? tags.slice(0, maxShowTagCount) : tags;
const getRowTagTotalWidth = (tags: TableData[]) => { // const totalWidth = tagArr.reduce((acc, tag, index) => {
const maxShowTagCount = 3; // 3 // const lastText = index === maxShowTagCount - 1 ? `+${tags.length - maxShowTagCount - 1}` : '';
const tablePadding = 24; // // const width = getTagWidth(tag, lastText);
// return acc + width;
// }, 0);
const tagArr = tags.length > maxShowTagCount ? tags.slice(0, maxShowTagCount) : tags; // return totalWidth + tablePadding;
const totalWidth = tagArr.reduce((acc, tag, index) => { // };
const lastText = index === maxShowTagCount - 1 ? `+${tags.length - maxShowTagCount - 1}` : '';
const width = getTagWidth(tag, lastText);
return acc + width;
}, 0);
return totalWidth + tablePadding; // const getAllTagsFromData = (rows: TableData[], dataIndex: string): any[] => {
}; // const allTags: Record<string, any>[] = [];
const getAllTagsFromData = (rows: TableData[], dataIndex: string): any[] => { // const collectTags = (nodes: TableData[]) => {
const allTags: Record<string, any>[] = []; // nodes.forEach((node) => {
// if (node[dataIndex]) {
// allTags.push(node[dataIndex]);
// }
// if (node.children && node.children.length > 0) {
// collectTags(node.children);
// }
// });
// };
const collectTags = (nodes: TableData[]) => { // collectTags(rows);
nodes.forEach((node) => { // return allTags;
if (node[dataIndex]) { // };
allTags.push(node[dataIndex]);
}
if (node.children && node.children.length > 0) {
collectTags(node.children);
}
});
};
collectTags(rows); // // TODO
return allTags; // const getMaxRowTagWidth = (rows: TableData[], dataIndex: string) => {
}; // const allTags = getAllTagsFromData(rows, dataIndex);
// const rowWidths = (allTags || []).map((tags: any) => {
// return getRowTagTotalWidth(tags);
// });
// //
// return Math.max(...rowWidths, 0);
// };
// TODO // watch(
const getMaxRowTagWidth = (rows: TableData[], dataIndex: string) => { // () => attrs.data,
const allTags = getAllTagsFromData(rows, dataIndex); // (val) => {
const rowWidths = (allTags || []).map((tags: any) => { // if (val) {
return getRowTagTotalWidth(tags); // const minTagWidth = 60; // 60
}); // currentColumns.value.forEach((column) => {
// // const dataIndex = column.dataIndex as string;
return Math.max(...rowWidths, 0); // if (column.isTag || column.isStringTag) {
}; // const lastWidth = getMaxRowTagWidth((val as TableData[]) || [], dataIndex);
// columnLastWidthMap.value[dataIndex] = lastWidth < minTagWidth ? minTagWidth : lastWidth;
watch( // }
() => attrs.data, // });
(val) => { // }
if (val) { // }
const minTagWidth = 60; // 60 // );
currentColumns.value.forEach((column) => {
const dataIndex = column.dataIndex as string;
if (column.isTag || column.isStringTag) {
const lastWidth = getMaxRowTagWidth((val as TableData[]) || [], dataIndex);
columnLastWidthMap.value[dataIndex] = lastWidth < minTagWidth ? minTagWidth : lastWidth;
}
});
}
}
);
function handleRadioChange(val: boolean, record: TableData) { function handleRadioChange(val: boolean, record: TableData) {
if (val) { if (val) {
@ -904,6 +909,88 @@
} }
return false; return false;
} }
// TODO
//
function updateTagVisibility(cell: HTMLElement) {
const labelsGroupList = cell.querySelectorAll<HTMLElement>('.ms-tag-group') || [];
const moreTipsNumber = cell.querySelector<HTMLElement>('.ms-tag-num');
const availableWidth = cell.clientWidth; //
let totalWidth = 0;
let hiddenTags = 0;
const visibleTags: HTMLElement[] = [];
labelsGroupList.forEach((label: HTMLElement) => {
label.style.display = 'inline-block'; //
totalWidth += label.offsetWidth; //
//
visibleTags.push(label);
if (moreTipsNumber && totalWidth + moreTipsNumber.offsetWidth > availableWidth) {
// 1
if (visibleTags.length > 1) {
//
label.style.display = 'none';
hiddenTags++;
} else {
// 1
label.style.display = 'inline-block';
}
}
});
if (hiddenTags > 0 && moreTipsNumber) {
moreTipsNumber.textContent = `+${hiddenTags}`;
moreTipsNumber.style.display = 'inline-block'; //
} else if (moreTipsNumber) {
moreTipsNumber.style.display = 'none'; //
}
}
//
const updateAllTagVisibility = () => {
const cellsTagGroupList = document.querySelectorAll<HTMLElement>('.tag-group-class') || [];
cellsTagGroupList.forEach((cell: HTMLElement) => {
updateTagVisibility(cell);
});
};
function columnResize(dataIndex: string) {
if (dataIndex) {
updateAllTagVisibility();
}
}
let lastWindowWidth = window.innerWidth;
const updateTagAdaptive = debounce(() => {
const currentWidth = window.innerWidth;
//
if (currentWidth !== lastWindowWidth) {
lastWindowWidth = currentWidth;
updateAllTagVisibility();
}
}, 100);
watch(
() => attrs.data,
(val) => {
if (val) {
nextTick(() => {
updateAllTagVisibility();
});
}
}
);
onMounted(() => {
//
window.addEventListener('resize', updateTagAdaptive);
});
//
onBeforeUnmount(() => {
window.removeEventListener('resize', updateTagAdaptive);
});
watch( watch(
() => props.columns, () => props.columns,

View File

@ -4,16 +4,31 @@
:class="`tag-group-class ${props.allowEdit ? 'cursor-pointer' : ''}`" :class="`tag-group-class ${props.allowEdit ? 'cursor-pointer' : ''}`"
@click="emit('click')" @click="emit('click')"
> >
<MsTag v-for="tag of showTagList" :key="tag.id" :width="getTagWidth(tag)" :size="props.size" v-bind="attrs"> <MsTag
{{ props.isStringTag ? tag : tag[props.nameKey] }} v-for="tag of showTagList"
:key="tag.id"
class="ms-tag-group"
:width="getTagWidth(tag)"
:size="props.size"
v-bind="attrs"
>
{{ getTagContent(tag) }}
</MsTag> </MsTag>
<a-tooltip <a-tooltip
v-if="props.tagList.length > props.showNum" :disabled="!(props.tagList.length > props.showNum)"
:content="tagsTooltip" :content="tagsTooltip"
:position="props.tagPosition" :position="props.tagPosition"
:mouse-enter-delay="300" :mouse-enter-delay="300"
> >
<MsTag :width="numberTagWidth" :size="props.size" tooltip-disabled v-bind="attrs"> <MsTag
v-show="props.tagList.length > props.showNum"
class="ms-tag-num"
:width="numberTagWidth"
:min-width="60"
:size="props.size"
tooltip-disabled
v-bind="attrs"
>
+ +
{{ props.tagList.length - props.showNum }} {{ props.tagList.length - props.showNum }}
</MsTag> </MsTag>
@ -30,6 +45,8 @@
import MsTag, { Size } from './ms-tag.vue'; import MsTag, { Size } from './ms-tag.vue';
import { characterLimit } from '@/utils';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
tagList: Array<any>; tagList: Array<any>;
@ -38,6 +55,7 @@
isStringTag?: boolean; // isStringTag?: boolean; //
size?: Size; size?: Size;
allowEdit?: boolean; allowEdit?: boolean;
showTable?: boolean;
tagPosition?: tagPosition?:
| 'top' | 'top'
| 'tl' | 'tl'
@ -71,6 +89,10 @@
}); });
const showTagList = computed(() => { const showTagList = computed(() => {
//
if (props.showTable) {
return filterTagList.value;
}
return filterTagList.value.slice(0, props.showNum); return filterTagList.value.slice(0, props.showNum);
}); });
@ -90,11 +112,25 @@
const numberStr = `${props.tagList.length - props.showNum}`; const numberStr = `${props.tagList.length - props.showNum}`;
return numberStr.length + 4; return numberStr.length + 4;
}); });
function getTagContent(tag: { [x: string]: any }) {
let tagContent = props.isStringTag ? tag : tag[props.nameKey];
if (tagContent.length > 16) {
tagContent = characterLimit(tagContent, 9);
}
return tagContent;
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.tag-group-class { .tag-group-class {
overflow: hidden;
max-width: 440px; max-width: 440px;
white-space: nowrap;
@apply flex w-full flex-row; @apply flex w-full flex-row;
} }
.ms-tag-group {
min-width: min-content !important;
max-width: 144px !important;
}
</style> </style>

View File

@ -15,7 +15,7 @@
> >
<slot name="icon"></slot> <slot name="icon"></slot>
<a-tooltip :disabled="props.tooltipDisabled"> <a-tooltip :disabled="props.tooltipDisabled">
<div class="one-line-text"> <div :class="`one-line-text min-w-[20px] ${props.maxWidth || '144px'}`">
<slot></slot> <slot></slot>
</div> </div>
<template #content> <template #content>

View File

@ -93,6 +93,7 @@
is-string-tag is-string-tag
:show-num="1" :show-num="1"
allow-edit allow-edit
show-table
theme="outline" theme="outline"
@click="record.showModuleTree = false" @click="record.showModuleTree = false"
/> />

View File

@ -45,6 +45,7 @@
type="primary" type="primary"
theme="outline" theme="outline"
allow-edit allow-edit
show-table
@click="changeUser(record)" @click="changeUser(record)"
/> />
<MsSelect <MsSelect

View File

@ -43,6 +43,7 @@
:tag-list="record.projectIdNameMap || []" :tag-list="record.projectIdNameMap || []"
theme="outline" theme="outline"
allow-edit allow-edit
show-table
@click="changeUserOrProject(record, 'project')" @click="changeUserOrProject(record, 'project')"
> >
</MsTagGroup> </MsTagGroup>
@ -75,6 +76,7 @@
type="primary" type="primary"
theme="outline" theme="outline"
allow-edit allow-edit
show-table
@click="changeUserOrProject(record, 'user')" @click="changeUserOrProject(record, 'user')"
> >
</MsTagGroup> </MsTagGroup>

View File

@ -46,6 +46,7 @@
type="primary" type="primary"
theme="outline" theme="outline"
allow-edit allow-edit
show-table
@click="handleTagClick(record)" @click="handleTagClick(record)"
/> />
<MsSelect <MsSelect