diff --git a/src/components/Course/TopCourseSorter.vue b/src/components/Course/TopCourseSorter.vue index 55cf78d6..9e5d8b10 100644 --- a/src/components/Course/TopCourseSorter.vue +++ b/src/components/Course/TopCourseSorter.vue @@ -9,40 +9,40 @@ append-to-body >
-
-
-
-
排序
-
课程名称
-
授课教师
-
-
-
- -
-
{{ index + 1 }}
-
- -
{{ item.name }}
+ + + + + + + + + + + + + +
@@ -57,6 +57,12 @@ import apiCourse from '@/api/modules/course.js'; export default { name: 'TopCourseSorter', + computed: { + // 动态计算"授课教师"列的最小宽度 + teacherColumnWidth() { + return this.calcColumnWidth('授课教师', 'teacherName'); + }, + }, data() { return { dialogVisible: false, @@ -66,6 +72,19 @@ export default { draggingIndex: null, }; }, + watch: { + topList: { + handler(newVal, oldVal) { + // 只在列表长度变化或首次加载时初始化拖拽事件 + if (!oldVal || newVal.length !== oldVal.length) { + this.$nextTick(() => { + this.initDragEvents(); + }); + } + }, + immediate: false, + }, + }, methods: { open() { this.dialogVisible = true; @@ -76,7 +95,12 @@ export default { try { const res = await apiCourse.fetchTopCourseList(); if (res.status === 200) { - this.topList = Array.isArray(res.result) ? [...res.result] : []; + this.topList = res.result.map(item=>{ + return { + ...item, + // teacherName: '对对对柳在一、李玉冰、李国鑫、张添瑞、张子峰、 柳在一、李玉冰、李国鑫、张添瑞、张子峰' + } + }) } else { this.$message.error(res.message || '获取置顶课程失败'); this.topList = []; @@ -88,6 +112,37 @@ export default { this.loading = false; } }, + getRowClassName({ row, rowIndex }) { + if (this.draggingIndex === rowIndex) { + return 'is-dragging'; + } + return ''; + }, + initDragEvents() { + if (!this.$refs.sortTable) return; + + const tableBody = this.$refs.sortTable.$el.querySelector('.el-table__body-wrapper tbody'); + if (!tableBody) return; + + const rows = tableBody.querySelectorAll('tr'); + rows.forEach((row, index) => { + // 移除旧的事件监听器 + row.removeEventListener('dragstart', this.handleDragStart); + row.removeEventListener('dragover', this.handleDragOver); + row.removeEventListener('drop', this.handleDrop); + row.removeEventListener('dragend', this.handleDragEnd); + + // 设置可拖拽 + row.draggable = true; + row.setAttribute('data-index', index); + + // 添加新的事件监听器 + row.addEventListener('dragstart', (e) => this.handleDragStart(index, e)); + row.addEventListener('dragover', (e) => this.handleDragOver(index, e)); + row.addEventListener('drop', (e) => this.handleDrop(index, e)); + row.addEventListener('dragend', () => this.handleDragEnd()); + }); + }, handleDragStart(index, event) { this.draggingIndex = index; if (event && event.dataTransfer) { @@ -95,16 +150,57 @@ export default { event.dataTransfer.setData('text/plain', index); } }, - handleDrop(targetIndex) { + handleDragOver(index, event) { + event.preventDefault(); + if (this.draggingIndex !== null && this.draggingIndex !== index) { + const tableBody = this.$refs.sortTable.$el.querySelector('.el-table__body-wrapper tbody'); + if (tableBody) { + const rows = tableBody.querySelectorAll('tr'); + rows.forEach((row, idx) => { + if (idx === index) { + row.classList.add('drag-over'); + } else { + row.classList.remove('drag-over'); + } + }); + } + } + }, + handleDrop(targetIndex, event) { + event.preventDefault(); if (this.draggingIndex === null || this.draggingIndex === targetIndex) { return; } const movingItem = this.topList.splice(this.draggingIndex, 1)[0]; this.topList.splice(targetIndex, 0, movingItem); this.draggingIndex = targetIndex; + + // 清除拖拽样式 + const tableBody = this.$refs.sortTable.$el.querySelector('.el-table__body-wrapper tbody'); + if (tableBody) { + const rows = tableBody.querySelectorAll('tr'); + rows.forEach((row) => { + row.classList.remove('drag-over'); + }); + } + + // 重新初始化拖拽事件,因为 DOM 顺序已改变 + this.$nextTick(() => { + this.initDragEvents(); + }); }, handleDragEnd() { this.draggingIndex = null; + // 清除所有拖拽样式 + if (this.$refs.sortTable) { + const tableBody = this.$refs.sortTable.$el.querySelector('.el-table__body-wrapper tbody'); + if (tableBody) { + const rows = tableBody.querySelectorAll('tr'); + rows.forEach((row) => { + row.classList.remove('drag-over'); + }); + } + } }, async handleSave() { if (!this.topList.length) { @@ -137,6 +233,26 @@ export default { this.loading = false; this.saving = false; }, + // 计算文本宽度(通过隐藏 span 获取实际宽度) + getTextWidth(text = '') { + if (typeof document === 'undefined') return 0; + const span = document.createElement('span'); + span.innerText = text; + span.style.cssText = 'position:absolute;visibility:hidden;font-size:14px;font-family:inherit;white-space:nowrap;'; + document.body.appendChild(span); + const { width } = span.getBoundingClientRect(); + document.body.removeChild(span); + return Math.ceil(width); + }, + // 计算列宽(头+内容取最大值,加 padding,并控制最小值) + calcColumnWidth(label, prop, padding = 24, min = 130, max = Infinity) { + const contents = (this.topList || []).map(row => this.getTextWidth((row && row[prop]) || '-')); + const maxContentWidth = contents.length ? Math.max(...contents) : 0; + const labelWidth = this.getTextWidth(label || ''); + const baseWidth = Math.max(maxContentWidth, labelWidth) + padding; + const clamped = Math.max(min, Math.min(baseWidth, max)); + return `${clamped}px`; + }, }, }; @@ -148,75 +264,56 @@ export default { } .top-course-sorter__table { - border: 1px solid #ebeef5; - border-radius: 6px; - overflow: hidden; + ::v-deep .el-table__row { + cursor: move; + transition: background-color 0.2s ease; + + &:hover { + background-color: #f9fbff; + } + + &.is-dragging { + opacity: 0.7; + background-color: #ecf5ff; + } + + &.drag-over { + border-top: 2px solid #409eff; + } + } + + ::v-deep .el-table__body-wrapper { + .el-table__row { + &:last-child { + .el-table__cell { + border-bottom: none; + } + } + } + } + + ::v-deep .el-table__cell { + padding: 12px 0; + } } -.sorter-header, -.sorter-row { - display: grid; - grid-template-columns: 60px 80px 1fr 160px; - align-items: center; -} - -.sorter-header { - background-color: #f5f7fa; - height: 48px; - font-weight: 600; - color: #303133; - border-bottom: 1px solid #ebeef5; -} - -.sorter-row { - min-height: 56px; - border-bottom: 1px solid #f2f6fc; - cursor: move; - transition: background-color 0.2s ease; -} - -.sorter-row:last-child { - border-bottom: none; -} - -.sorter-row:hover { - background-color: #f9fbff; -} - -.sorter-row.is-dragging { - opacity: 0.7; - background-color: #ecf5ff; -} - -.header-cell, -.row-cell { - padding: 0 16px; +.drag-handle { display: flex; align-items: center; -} - -.header-cell--handle, -.row-cell--handle { justify-content: center; -} + cursor: move; + width: 100%; + height: 100%; + padding: 0 16px; -.header-cell--order, -.row-cell--order { - justify-content: flex-start; -} + i { + font-size: 20px; + color: #c0c4cc; + } -.row-cell--name, -.row-cell--teacher { - color: #303133; - overflow: hidden; -} - -.row-cell--name { - font-weight: 500; -} - -.row-cell--teacher { - color: #666; + &:hover i { + color: #409eff; + } } .cell-text { @@ -226,9 +323,10 @@ export default { width: 100%; } -.row-cell--handle i { - font-size: 20px; - color: #c0c4cc; +.common-cell { + font-weight: 400; + font-size: 14px; + color: #000000; } .dialog-footer { diff --git a/src/views/course/ManageListRemote.vue b/src/views/course/ManageListRemote.vue index 38fcc8b1..03a9823d 100644 --- a/src/views/course/ManageListRemote.vue +++ b/src/views/course/ManageListRemote.vue @@ -189,52 +189,52 @@ - + @@ -2097,6 +2097,9 @@ export default { font-size: 14px; color: #000000; } + .common-cell-right { + padding-right: 20px; + } .single-line-ellipsis { display: inline-block;