From c462eb00a2c1ed2e77b0627a9d775c23bbb851f5 Mon Sep 17 00:00:00 2001 From: huweihang Date: Wed, 24 Dec 2025 17:16:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=8B=E6=8B=89=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E5=8F=AF=E5=8B=BE=E9=80=89,=20=E5=9C=A8=E7=BA=BF=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=88=9B=E5=BB=BA=E6=97=B6=E9=97=B4=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/course/ManageListRemote.vue | 60 +++++++++++++++- .../course/components/NameRemoteSelect.vue | 71 +++++++++++++++---- 2 files changed, 115 insertions(+), 16 deletions(-) diff --git a/src/views/course/ManageListRemote.vue b/src/views/course/ManageListRemote.vue index bb2bcfbf..c6f29727 100644 --- a/src/views/course/ManageListRemote.vue +++ b/src/views/course/ManageListRemote.vue @@ -135,6 +135,7 @@ - + @@ -240,8 +248,14 @@ - - + @@ -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(); + } } // 重新查询数据 diff --git a/src/views/course/components/NameRemoteSelect.vue b/src/views/course/components/NameRemoteSelect.vue index fd151582..90d330a0 100644 --- a/src/views/course/components/NameRemoteSelect.vue +++ b/src/views/course/components/NameRemoteSelect.vue @@ -31,7 +31,7 @@
  • {{ item.label }} ({{ item.code }}) @@ -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() {