mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-24 18:22:53 +08:00
feat: 下拉面板可勾选, 在线列表创建时间排序不可点击
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
// 重新查询数据
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user