🦄 refactor: 封装统一表格组件,便于批量扩展表格能力
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "klp-list",
|
||||
name: "KLPList",
|
||||
components: {},
|
||||
props: {
|
||||
/** 列表数据源(必传) */
|
||||
@@ -76,11 +76,11 @@ export default {
|
||||
default: false
|
||||
},
|
||||
|
||||
/** 标题最大宽度(像素),控制文字溢出 */
|
||||
titleMaxWidth: {
|
||||
/** 标题最大宽度占容器的百分比(0-100),控制文字溢出 */
|
||||
titleMaxPercent: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 200
|
||||
default: 80 // 默认占容器80%宽度
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -88,7 +88,9 @@ export default {
|
||||
// 内部管理选中状态:存储当前选中项的唯一键值(单选中)
|
||||
selectedKey: null,
|
||||
// 文字溢出检测临时元素(避免重复创建)
|
||||
overflowCheckElements: {}
|
||||
overflowCheckElements: {},
|
||||
// 容器宽度缓存
|
||||
containerWidth: 0
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -107,9 +109,10 @@ export default {
|
||||
* @param {Object} item - 点击的列表项数据
|
||||
*/
|
||||
handleItemClick(item) {
|
||||
const itemKey = item[this.listKey];
|
||||
const itemKey = item[this.listKey];
|
||||
// 单选中逻辑:点击已选中项取消选中,点击未选中项切换选中(取消其他项)
|
||||
this.selectedKey = this.selectedKey === itemKey ? null : itemKey;
|
||||
// this.selectedKey = this.selectedKey === itemKey ? null : itemKey;
|
||||
this.selectedKey = itemKey;
|
||||
|
||||
// 向父组件触发事件:传递当前选中项(null表示无选中)
|
||||
const selectedItem = this.selectedKey ? item : null;
|
||||
@@ -154,6 +157,15 @@ export default {
|
||||
// 内容为空时不显示Tooltip
|
||||
if (!content) return false;
|
||||
|
||||
// 获取容器宽度并增加存在性检查
|
||||
const container = this.$el.querySelector('.klp-list-container');
|
||||
if (!container) return false; // 容器不存在时直接返回
|
||||
this.containerWidth = container.clientWidth;
|
||||
|
||||
// 计算标题内容最大可用宽度(减去标签和边距)
|
||||
const labelWidth = this.$el.querySelector('.title-label')?.offsetWidth || 60;
|
||||
const availableWidth = (this.containerWidth * this.titleMaxPercent / 100) - labelWidth - 20;
|
||||
|
||||
// 创建临时元素测量文字实际宽度(复用元素避免性能问题)
|
||||
if (!this.overflowCheckElements[itemKey]) {
|
||||
const tempEl = document.createElement("span");
|
||||
@@ -171,9 +183,17 @@ export default {
|
||||
this.overflowCheckElements[itemKey] = tempEl;
|
||||
}
|
||||
|
||||
// 比较文字实际宽度与设定的最大宽度
|
||||
// 比较文字实际宽度与可用宽度
|
||||
const tempEl = this.overflowCheckElements[itemKey];
|
||||
return tempEl.offsetWidth > this.titleMaxWidth;
|
||||
return tempEl.offsetWidth > availableWidth;
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听容器宽度变化
|
||||
*/
|
||||
handleResize() {
|
||||
// 宽度变化时重新计算溢出状态
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -190,16 +210,29 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/** 标题最大宽度变化时,强制重绘以重新计算溢出 */
|
||||
titleMaxWidth() {
|
||||
/** 标题最大百分比变化时,重新计算溢出 */
|
||||
titleMaxPercent() {
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
/** 组件销毁时清理临时元素,避免内存泄漏 */
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const container = this.$el.querySelector('.klp-list-container');
|
||||
// 增加存在性检查
|
||||
this.containerWidth = container ? container.clientWidth : 0;
|
||||
});
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 清理临时元素
|
||||
Object.values(this.overflowCheckElements).forEach(el => {
|
||||
document.body.removeChild(el);
|
||||
});
|
||||
|
||||
// 移除事件监听
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -211,6 +244,8 @@ export default {
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
margin-top: 10px;
|
||||
width: 100%; /* 确保容器宽度正确计算 */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 加载状态容器(避免加载时容器塌陷) */
|
||||
@@ -229,6 +264,7 @@ export default {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%; /* 确保列表项占满容器宽度 */
|
||||
}
|
||||
|
||||
/* 列表项选中状态(左侧高亮边框+背景色) */
|
||||
@@ -242,6 +278,8 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 0; /* 关键:允许flex项缩小到内容尺寸以下 */
|
||||
overflow: hidden; /* 确保内容不会超出容器 */
|
||||
}
|
||||
|
||||
/* 标题前置标签样式 */
|
||||
@@ -250,6 +288,7 @@ export default {
|
||||
margin-right: 6px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap; /* 标签不换行 */
|
||||
flex-shrink: 0; /* 标签不缩小 */
|
||||
}
|
||||
|
||||
/* 标题内容样式(溢出省略) */
|
||||
@@ -260,7 +299,8 @@ export default {
|
||||
white-space: nowrap; /* 禁止换行 */
|
||||
overflow: hidden; /* 超出部分隐藏 */
|
||||
text-overflow: ellipsis; /* 超出部分显示省略号 */
|
||||
max-width: v-bind(titleMaxWidth + "px"); /* 绑定父组件传入的最大宽度 */
|
||||
flex: 1; /* 占据剩余空间 */
|
||||
min-width: 0; /* 关键:允许内容区域缩小 */
|
||||
}
|
||||
|
||||
/* 操作按钮组(按钮间距控制) */
|
||||
@@ -268,6 +308,8 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-shrink: 0; /* 操作区不缩小 */
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 空状态样式(居中显示) */
|
||||
@@ -275,4 +317,5 @@ export default {
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user