新建组件:AFTableColumn.vue
<template> <!--判断 slots.default可知道是否存在子元素--> <el-table-column v-if="slots.default" v-bind="attrs" :class-name="className" :min-width="minWidth"> <template #default="scope"> <slot v-bind="scope"></slot> </template> </el-table-column> <!--使用 slot 自定义 header--> <el-table-column v-else-if="slots.header" v-bind="attrs" :class-name="className" :min-width="minWidth"> <template #header="scope"> <slot name="header" v-bind="scope"></slot> </template> </el-table-column> <!--默认情况使用原始 el-table-column--> <el-table-column v-else :class-name="className" v-bind="attrs" :min-width="minWidth"> </el-table-column> </template> <script lang="ts"> export default { name: 'af-table-column', // inheritAttrs: false, } </script> <script setup lang="ts" name="af-table-column"> import { getCurrentInstance, ref, reactive, watch, computed, nextTick, useAttrs, useSlots } from 'vue' import $consts from '../../configs/constants'; const { proxy } = getCurrentInstance() as any; const attrs = useAttrs() as any; const slots = useSlots(); let minLength = ref(10); // 初始也不要写成0, 避免未及时设置宽度太丑 let getComputedWidth = ref(10); // 自定义列中获取元素计算的宽度 const values = computed(() => { const data = proxy.$parent.data || [] return data.map((item: any) => item[attrs.prop]) }) // 是否自适应列宽, 默认是 const isFit = computed(() => { return (proxy.$parent.$attrs.autoFit === undefined && attrs.fit === undefined) || (proxy.$parent.$attrs.autoFit === false && attrs.fit !== undefined) }) // 为存在scope的添加className const className = computed(() => { const parentClass = attrs['class-name'] || '' const classPre = attrs.prop || `encode-${transChar(attrs.label)}` // 有的列因为有slotScope而不给prop return classPre ? `${parentClass} ${classPre}-column` : '' }) // 列最小宽度 const minWidth = computed(() => { if (!attrs.label) return undefined; if (!isFit.value) return undefined; const maxOne = Math.max(minLength.value, attrs.label.length * (fontRate.value as any).CHAR_RATE) * fontSize.value + 20; return attrs.width || Math.max(maxOne, getComputedWidth.value) }) // 字体大小 const fontSize = computed(() => attrs.fontSize || $consts.fontSize) // 字体比例 const fontRate = computed(() => { return { ...$consts.fontRate, // 默认值 ...attrs.fontRate || {}, // 父组件属性 } }) watch(values, (val: any) => { return isFit.value !== false && nextTick(() => { // 详情中的列表需要在nextTick才能生效 if (!slots.default) { // 存在自定义节点 minLength.value = getMaxLength(val); return; } setTimeout(() => { // 首次获取不到子节点, setTimeout才行 // 可能存在贴边列, 需要使用包含 fixed 的类名 const bodyWrapper = attrs.fixed ? (document as any).querySelector(`.el-table__fixed${attrs.fixed === 'right' ? `-${attrs.fixed}` : ''}`).querySelector('.el-table__fixed-body-wrapper') : document.querySelector('.el-table__body-wrapper') const nodes = bodyWrapper.querySelectorAll(`.${attrs.prop || `encode-${transChar(attrs.label)}`}-column`) if (nodes.length) { // 有可能会来不及获取nodes, 就切换菜单进入下一页了, 再研究吧 const target = reactive<Array<String>>([]); const getComputedWidths = reactive<Array<number>>([]); Array.from(nodes).map((item: any) => { let text = item.innerText; target.push(text); // 有可能存在自定义列内容超出容器, 取 scrollWidth 才行 getComputedWidths.push(item.querySelector('.cell').scrollWidth); }) getComputedWidth.value = Math.max(...getComputedWidths); minLength.value = getMaxLength(target); } }, 0) }) }) /** * 获取数组中元素按字体比例最长长度 * @param arr */ const getMaxLength = (arr: any) => { return arr.reduce((length: any, item: any) => { if (item) { const str = item.toString() const char = str.match(/[\u2E80-\u9FFF]/g) const charLength = char ? char.length : 0 const num = str.match(/\d|\./g) const numLength = num ? num.length : 0 const otherLength = str.length - charLength - numLength let newLength = charLength * $consts.fontRate.CHAR_RATE + numLength * $consts.fontRate.NUM_RATE + otherLength * $consts.fontRate.OTHER_RATE if (str.includes('\n')) newLength = getMaxLength(str.split('\n')) if (length < newLength) { length = newLength } } return length }, 0) } /** * 转换汉字为class支持的字母 * @param char */ const transChar = (char: any) => { return encodeURIComponent(char).replace(/[^a-zA-z]/g, 'eUC') } </script>
配置文件:constants.ts
export default { // 字体大小默认14 fontSize: 14, // 计算字体比率 fontRate: { CHAR_RATE: 1.1, // 汉字比率 NUM_RATE: 0.65, // 数字 OTHER_RATE: 0.8, // 除汉字和数字以外的字符的比率 }, };
使用:
import AFTableColumn from '@/components/AFTableColumn/AFTableColumn.vue' <el-table :data="tableData" :header-cell-style="{ background: '#F4F5F7 ' }" style="width: 100%"> <AFTableColumn prop="code" label="編號"> </AFTableColumn> <AFTableColumn prop="name" label="姓名"> </AFTableColumn> <AFTableColumn prop="phone" label="聯繫電話"> </AFTableColumn> <AFTableColumn prop="department" label="部問"> </AFTableColumn> <AFTableColumn prop="address" label="地址"> </AFTableColumn> <!-- <el-table-column label="經緯度" width="280px"> <template #default="scope"> <div>{{ scope.row.longitude }} , {{ scope.row.latitude }}</div> </template> </el-table-column> --> <AFTableColumn label="經緯度"> <template #default="scope"> <div>{{ (scope as any).row.longitude }},{{ (scope as any).row.latitude }}</div> </template> </AFTableColumn> <AFTableColumn label="操作" width="110"> <template #default="scope"> <span class="iconfont icon-bianji2 edit" @click="handleEdit((scope as any).$index, (scope as any).row)"></span> <span class="iconfont icon-shanchu1 delete" @click="handleDelete((scope as any).$index, (scope as any).row)"></span> </template> </AFTableColumn> </el-table>
效果:
源码来自:
https://github.com/legendJaden/AFTableColumn
评论区