新建组件: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
评论区