feat: 下拉面板可勾选, 在线列表创建时间排序不可点击

This commit is contained in:
huweihang
2025-12-24 17:16:10 +08:00
parent 0a74f1a064
commit c462eb00a2
2 changed files with 115 additions and 16 deletions

View File

@@ -135,6 +135,7 @@
<!-- AI 播放器相关批量语种设置 / 开启AI处理 -->
<el-button
v-if="aiPermission"
class="btn-ai-process"
type="primary"
@click="setLanguage()"
icon="el-icon-connection"
@@ -144,6 +145,7 @@
</el-button>
<el-button
v-if="aiPermission"
class="btn-ai-process"
type="primary"
@click="enableAI()"
icon="el-icon-switch-button"
@@ -159,7 +161,13 @@
@click="!exportLoading && handleExport()" aria-label="导出"></div>
</el-tooltip>
</div>
<el-table :data="pageData" @sort-change="handleSortChange" @selection-change="handleSelectionChange" :default-sort="{ prop: 'sysCreateTime', order: 'descending' }">
<el-table
ref="courseTable"
:data="pageData"
@sort-change="handleSortChange"
@selection-change="handleSelectionChange"
:default-sort="{ prop: 'sysCreateTime', order: 'descending' }"
>
<!-- AI 播放器相关多选勾选列 -->
<el-table-column v-if="aiPermission" type="selection" width="55"></el-table-column>
<el-table-column v-if="forChoose" label="选择" width="80" align="center">
@@ -240,8 +248,14 @@
<span class="common-cell" v-else>-</span>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="sysCreateTime" min-width="200" show-overflow-tooltip align="center"
sortable="custom">
<el-table-column
label="创建时间"
prop="sysCreateTime"
min-width="200"
show-overflow-tooltip
align="center"
sortable="custom"
>
<template slot-scope="scope">
<span class="common-cell">{{ scope.row.sysCreateTime }}</span>
</template>
@@ -858,6 +872,10 @@ export default {
aiAbstractTip: '一键提炼课程视频核心要点,助力学员课前高效掌握重点,快速筛选学习资源',
aiDraftTip: '分段展示视频内容并精准同步时间轴,实现视频进度与文稿双向定位,学习内容触手可及',
aiTranslateTip: '智能转换视频字幕与语音为多语种,支持全球学员按需切换语言,打破学习边界',
// 创建时间默认排序锁定:初始禁止点击改变排序,先点其他列后再解锁
defaultSysCreateSortLocked: true,
// 内部程序触发排序时使用的防重入标志,避免在 sort-change 里再次触发 sort 导致死循环
isProgrammaticSorting: false,
};
},
created() {
@@ -1462,6 +1480,15 @@ export default {
// 重置排序
this.orderField = 'sysCreateTime';
this.orderAsc = false;
// 恢复“创建时间降序”默认排序,并重新锁定
this.defaultSysCreateSortLocked = true;
this.$nextTick(() => {
if (this.$refs.courseTable) {
// 标记为程序内部排序,避免触发 handleSortChange 里的业务逻辑
this.isProgrammaticSorting = true;
this.$refs.courseTable.sort('sysCreateTime', 'descending');
}
});
this.searchData(true);
},
...mapActions({
@@ -1564,15 +1591,42 @@ export default {
handleSortChange({ column, prop, order }) {
// order: ascending(升序) | descending(降序) | null(取消排序)
console.log('排序变化:', { column, prop, order });
// 如果是内部程序调用的排序(比如 reset 时恢复默认排序),只重置标志位,不做任何业务处理
if (this.isProgrammaticSorting) {
this.isProgrammaticSorting = false;
return;
}
// 1. 默认状态下(还没选过其它列),禁止通过点击“创建时间”改变排序,只保持默认降序箭头
if (prop === 'sysCreateTime' && this.defaultSysCreateSortLocked) {
if (this.$refs.courseTable) {
this.$nextTick(() => {
// 再次用程序方式把排序设回创建时间降序,同时打标,避免死循环
this.isProgrammaticSorting = true;
this.$refs.courseTable.sort('sysCreateTime', 'descending');
});
}
// 不触发查询,不修改当前排序参数
return;
}
if (order) {
// 有排序:保存排序字段和顺序
this.orderField = prop;
this.orderAsc = order === 'ascending';
// 2. 一旦选择了非“创建时间”的排序,解锁创建时间列,让它也可以正常切换排序
if (prop !== 'sysCreateTime') {
this.defaultSysCreateSortLocked = false;
}
} else {
// 取消排序:清空排序字段
this.orderField = '';
this.orderAsc = true;
// 主动清理表格内部的排序状态
if (this.$refs.courseTable) {
this.$refs.courseTable.clearSort();
}
}
// 重新查询数据

View File

@@ -31,7 +31,7 @@
</el-option>
</el-select>
<div
v-if="panelVisible && selectedDisplay.length"
v-if="panelVisible && selectedPanelItems.length"
class="selected-panel el-select-dropdown el-popper is-multiple"
x-placement="bottom-start"
:style="panelStyle"
@@ -41,9 +41,10 @@
<div class="el-select-dropdown__wrap el-scrollbar__wrap">
<ul class="el-scrollbar__view el-select-dropdown__list">
<li
v-for="item in selectedDisplay"
v-for="item in selectedPanelItems"
:key="item.value"
class="el-select-dropdown__item"
:class="['el-select-dropdown__item', { selected: isSelected(item.value) }]"
@click.stop="togglePanelItem(item)"
>
<span>{{ item.label }}</span>
<span v-if="item.code" class="option-code">{{ item.code }}</span>
@@ -128,13 +129,11 @@ export default {
tagsContainerEl: null,
panelMinWidth: '',
outsideHandler: null,
selectedPanelItems: [],
// 标记:由点击 +N 主动关闭 el-select 下拉引起的 visible-change(false),需要忽略一次
ignoreNextVisibleChange: false,
};
},
computed: {
noDataText() {
return this.keyword && this.keyword.length >= this.minQueryLen ? '无数据' : `请至少输入${this.minQueryLen}个字`;
},
},
watch: {
value: {
immediate: true,
@@ -219,24 +218,30 @@ export default {
this.innerValue = [];
this.options = [];
this.panelVisible = false;
this.selectedPanelItems = [];
this.emitChange();
},
handleVisibleChange(visible) {
// 如果是由点击 +N 主动触发的关闭,下拉的 visible-change(false) 需要被忽略一次,避免把自定义面板也关掉
if (!visible && this.ignoreNextVisibleChange) {
this.ignoreNextVisibleChange = false;
return;
}
// 聚焦/下拉打开时不自动展示面板
if (visible) {
// 打开时展开标签,避免点击 +N 看不到具体选项
if (this.expandOnVisible) {
this.innerCollapseTags = false;
}
if (!this.keyword) {
this.options = [];
}
// 无论关键字是否存在,都清空现有搜索结果,下次输入再重新远程拉取
this.options = [];
this.updatePanelWidth();
this.panelVisible = false;
} else {
// 关闭时恢复原始折叠配置
this.innerCollapseTags = this.collapseTags;
this.panelVisible = false;
this.selectedPanelItems = [];
}
},
// 用缓存兜底,保证已选项能在面板中展示
@@ -249,8 +254,21 @@ export default {
event.stopPropagation();
}
if (!this.showSelectedPanel) return;
const canShow = this.selectedDisplay.length > 0;
this.panelVisible = canShow ? !this.panelVisible : false;
const hasSelected = this.selectedDisplay.length > 0;
if (!hasSelected) {
this.panelVisible = false;
return;
}
const nextVisible = !this.panelVisible;
if (nextVisible) {
// 准备已选面板,并忽略接下来由关闭 el-select 下拉触发的一次 visible-change(false)
this.ignoreNextVisibleChange = true;
this.prepareSelectedPanel();
this.closeSelectDropdown();
} else {
this.selectedPanelItems = [];
}
this.panelVisible = nextVisible;
},
bindCountClick() {
this.$nextTick(() => {
@@ -316,6 +334,7 @@ export default {
const insideSelect = selectEl && selectEl.contains(target);
if (!insidePanel && !insideSelect) {
this.panelVisible = false;
this.selectedPanelItems = [];
}
};
document.addEventListener('mousedown', this.outsideHandler, true);
@@ -337,6 +356,32 @@ export default {
this.panelMinWidth = `${minWidth}px`;
});
},
prepareSelectedPanel() {
// 在展开已选列表时,冻结当前已选项,支持在面板内反复勾选/取消
this.selectedPanelItems = this.selectedDisplay.map(item => ({ ...item }));
},
closeSelectDropdown() {
// 关闭原生下拉,避免与已选列表面板同时展示;保留输入内容
const select = this.$refs.selectRef;
if (select && select.visible) {
if (typeof select.handleClose === 'function') {
select.handleClose();
} else {
select.visible = false;
}
this.syncInputQuery(this.keyword || '');
}
},
isSelected(val) {
return (this.innerValue || []).includes(val);
},
togglePanelItem(item) {
if (!item || !item.value) return;
const current = this.innerValue || [];
const exists = this.isSelected(item.value);
this.innerValue = exists ? current.filter(v => v !== item.value) : [...current, item.value];
this.emitChange();
},
},
computed: {
noDataText() {