排课窗口单元格单击双选问题修复
This commit is contained in:
+37
-10
@@ -117,32 +117,59 @@ public class TeachingPlanServiceImpl implements TeachingPlanService {
|
|||||||
"责任单位", "授课教员", "教学场地", "教学内容", "教学方法",
|
"责任单位", "授课教员", "教学场地", "教学内容", "教学方法",
|
||||||
"教员备注", "教务备注", "检查结果", "课程表编号"
|
"教员备注", "教务备注", "检查结果", "课程表编号"
|
||||||
};
|
};
|
||||||
String[] example = {
|
String[][] examples = {
|
||||||
"1", "2026", "", "2026-03-02 08:00:00", "", "1", "高等数学", "1班",
|
{"1", "2026", "", "2026-03-02 08:00:00", "", "1", "高等数学(测试)", "1班",
|
||||||
"基础教研室", "张三", "教学楼A-101", "函数与极限", "讲授",
|
"基础教研室", "张三", "教学楼A-101", "函数与极限", "讲授",
|
||||||
"", "", "", ""
|
"导入测试样例1", "", "通过", ""},
|
||||||
|
{"2", "2026", "", "2026-03-02 10:00:00", "", "3", "大学物理(测试)", "2班",
|
||||||
|
"物理教研室", "李四", "实验楼B-203", "牛顿定律", "实验",
|
||||||
|
"导入测试样例2", "", "", ""},
|
||||||
|
{"3", "2026", "", "2026-03-03 14:00:00", "", "5", "军事理论(测试)", "3班",
|
||||||
|
"指挥教研室", "王五", "阶梯教室C-1", "战役概论", "研讨",
|
||||||
|
"导入测试样例3", "联教联训", "", ""}
|
||||||
};
|
};
|
||||||
try (Workbook wb = new XSSFWorkbook(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
try (Workbook wb = new XSSFWorkbook(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||||
|
org.apache.poi.ss.usermodel.Font headerFont = wb.createFont();
|
||||||
|
headerFont.setFontName("宋体");
|
||||||
|
headerFont.setBold(true);
|
||||||
|
headerFont.setFontHeightInPoints((short) 11);
|
||||||
|
org.apache.poi.ss.usermodel.CellStyle headerStyle = wb.createCellStyle();
|
||||||
|
headerStyle.setFont(headerFont);
|
||||||
|
org.apache.poi.ss.usermodel.Font dataFont = wb.createFont();
|
||||||
|
dataFont.setFontName("宋体");
|
||||||
|
dataFont.setFontHeightInPoints((short) 11);
|
||||||
|
org.apache.poi.ss.usermodel.CellStyle dataStyle = wb.createCellStyle();
|
||||||
|
dataStyle.setFont(dataFont);
|
||||||
|
|
||||||
Sheet sheet = wb.createSheet("教学实施计划");
|
Sheet sheet = wb.createSheet("教学实施计划");
|
||||||
Row header = sheet.createRow(0);
|
Row header = sheet.createRow(0);
|
||||||
for (int i = 0; i < headers.length; i++) {
|
for (int i = 0; i < headers.length; i++) {
|
||||||
header.createCell(i).setCellValue(headers[i]);
|
org.apache.poi.ss.usermodel.Cell cell = header.createCell(i);
|
||||||
sheet.setColumnWidth(i, 14 * 256);
|
cell.setCellValue(headers[i]);
|
||||||
|
cell.setCellStyle(headerStyle);
|
||||||
|
sheet.setColumnWidth(i, 16 * 256);
|
||||||
|
}
|
||||||
|
for (int r = 0; r < examples.length; r++) {
|
||||||
|
Row row = sheet.createRow(r + 1);
|
||||||
|
for (int i = 0; i < examples[r].length; i++) {
|
||||||
|
org.apache.poi.ss.usermodel.Cell cell = row.createCell(i);
|
||||||
|
cell.setCellValue(examples[r][i]);
|
||||||
|
cell.setCellStyle(dataStyle);
|
||||||
}
|
}
|
||||||
Row row = sheet.createRow(1);
|
|
||||||
for (int i = 0; i < example.length; i++) {
|
|
||||||
row.createCell(i).setCellValue(example[i]);
|
|
||||||
}
|
}
|
||||||
Sheet help = wb.createSheet("填写说明");
|
Sheet help = wb.createSheet("填写说明");
|
||||||
String[] notes = {
|
String[] notes = {
|
||||||
"1. 数据从第2行开始填写,第1行表头请勿修改,列顺序固定不可调整。",
|
"1. 数据从第2行开始填写,第1行表头请勿修改,列顺序固定不可调整。",
|
||||||
"2. 「课程名称」必填,课程名称为空的行将被跳过;其余列可留空。",
|
"2. 「课程名称」必填,课程名称为空的行将被跳过;其余列可留空。",
|
||||||
"3. 「序号」「年度」「节次」为整数;「日期」支持 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss。",
|
"3. 「序号」「年度」「节次」为整数;「日期」支持 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss。",
|
||||||
"4. 第2行为示例数据,导入前请删除。",
|
"4. 第2至4行为测试样例,可直接导入验证。",
|
||||||
"5. 标识号、登录编号由系统自动生成,无需填写。"
|
"5. 标识号、登录编号由系统自动生成,无需填写。"
|
||||||
};
|
};
|
||||||
for (int i = 0; i < notes.length; i++) {
|
for (int i = 0; i < notes.length; i++) {
|
||||||
help.createRow(i).createCell(0).setCellValue(notes[i]);
|
org.apache.poi.ss.usermodel.Row noteRow = help.createRow(i);
|
||||||
|
org.apache.poi.ss.usermodel.Cell noteCell = noteRow.createCell(0);
|
||||||
|
noteCell.setCellValue(notes[i]);
|
||||||
|
noteCell.setCellStyle(dataStyle);
|
||||||
}
|
}
|
||||||
help.setColumnWidth(0, 100 * 256);
|
help.setColumnWidth(0, 100 * 256);
|
||||||
wb.write(bos);
|
wb.write(bos);
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ export function importTeachingPlan(file) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/teachingPlan/import',
|
url: '/teachingPlan/import',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: formData
|
data: formData,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
timeout: 60000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
<div class="action-bar">
|
<div class="action-bar">
|
||||||
<div class="bar-center">
|
<div class="bar-center">
|
||||||
<div class="file-area">
|
<div class="file-area">
|
||||||
|
<el-button icon="el-icon-download" @click="handleDownloadTemplate">【教学实施计划模板】下载</el-button>
|
||||||
<input ref="fileInputRef" type="file" accept=".xlsx,.xls" style="display: none" @change="handleFileChange" />
|
<input ref="fileInputRef" type="file" accept=".xlsx,.xls" style="display: none" @change="handleFileChange" />
|
||||||
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
|
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
|
||||||
<span class="file-name">{{ selectedFileName }}</span>
|
<span class="file-name">{{ selectedFileName }}</span>
|
||||||
@@ -144,7 +145,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listTeachingPlan, importTeachingPlan } from '@/api/schedule/teachingPlan'
|
import { listTeachingPlan, importTeachingPlan, downloadTeachingPlanTemplate } from '@/api/schedule/teachingPlan'
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PlanImportIndex',
|
name: 'PlanImportIndex',
|
||||||
@@ -261,6 +263,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 导入到系统 ====================
|
// ==================== 导入到系统 ====================
|
||||||
|
handleDownloadTemplate() {
|
||||||
|
downloadTeachingPlanTemplate().then(blob => {
|
||||||
|
if (blob && blob.type && String(blob.type).indexOf('json') !== -1) {
|
||||||
|
this.$message.error('模板下载失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saveAs(blob, '教学实施计划导入模板.xlsx')
|
||||||
|
this.$message.success('模板下载成功')
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
handleImport() {
|
handleImport() {
|
||||||
if (!this.selectedFile) {
|
if (!this.selectedFile) {
|
||||||
this.$message.warning('请先选择文件')
|
this.$message.warning('请先选择文件')
|
||||||
|
|||||||
@@ -180,7 +180,7 @@
|
|||||||
:class="slotClass(d, jc)"
|
:class="slotClass(d, jc)"
|
||||||
:title="slotTitle(d, jc)"
|
:title="slotTitle(d, jc)"
|
||||||
@mousedown.prevent="startDrag(d.fi, jc, d, $event)"
|
@mousedown.prevent="startDrag(d.fi, jc, d, $event)"
|
||||||
@mouseenter="hoverCell(d.fi, jc)"
|
@mouseenter="hoverCell(d.fi, jc, $event)"
|
||||||
@dblclick="dblclickCell(d, jc)"
|
@dblclick="dblclickCell(d, jc)"
|
||||||
@dragover="onCellDragover(d, jc, $event)"
|
@dragover="onCellDragover(d, jc, $event)"
|
||||||
@dragleave="onCellDragleave(d, jc)"
|
@dragleave="onCellDragleave(d, jc)"
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
:class="slotClass(d, jc)"
|
:class="slotClass(d, jc)"
|
||||||
:title="slotTitle(d, jc)"
|
:title="slotTitle(d, jc)"
|
||||||
@mousedown.prevent="startDrag(d.fi, jc, d, $event)"
|
@mousedown.prevent="startDrag(d.fi, jc, d, $event)"
|
||||||
@mouseenter="hoverCell(d.fi, jc)"
|
@mouseenter="hoverCell(d.fi, jc, $event)"
|
||||||
@dblclick="dblclickCell(d, jc)"
|
@dblclick="dblclickCell(d, jc)"
|
||||||
@dragover="onCellDragover(d, jc, $event)"
|
@dragover="onCellDragover(d, jc, $event)"
|
||||||
@dragleave="onCellDragleave(d, jc)"
|
@dragleave="onCellDragleave(d, jc)"
|
||||||
@@ -608,6 +608,8 @@ export default {
|
|||||||
dragging: false,
|
dragging: false,
|
||||||
/** 本次拖拽是否为追加(Ctrl / Meta) */
|
/** 本次拖拽是否为追加(Ctrl / Meta) */
|
||||||
additiveDrag: false,
|
additiveDrag: false,
|
||||||
|
/** 按下时的指针位置,用于忽略首次聚焦滚动触发的误 hover */
|
||||||
|
dragOrigin: null,
|
||||||
/** 当前聚焦格(⑨ 当前格子编排信息) */
|
/** 当前聚焦格(⑨ 当前格子编排信息) */
|
||||||
focusedCell: null,
|
focusedCell: null,
|
||||||
/** 视图模式:week 单周 / all 全学期 / h 横版 */
|
/** 视图模式:week 单周 / all 全学期 / h 横版 */
|
||||||
@@ -840,6 +842,7 @@ export default {
|
|||||||
this.dragging = false
|
this.dragging = false
|
||||||
this.dragStart = null
|
this.dragStart = null
|
||||||
this.dragEnd = null
|
this.dragEnd = null
|
||||||
|
this.dragOrigin = null
|
||||||
// 尽量保留原来选中的课程
|
// 尽量保留原来选中的课程
|
||||||
const list = this.courses
|
const list = this.courses
|
||||||
this.currentCourse = keepBh ? (list.find(c => c.sskcbh === keepBh) || list[0] || null) : (list[0] || null)
|
this.currentCourse = keepBh ? (list.find(c => c.sskcbh === keepBh) || list[0] || null) : (list[0] || null)
|
||||||
@@ -1024,18 +1027,29 @@ export default {
|
|||||||
// 拖拽调课模式下按住格子只做聚焦,课次块用 HTML5 拖拽移动
|
// 拖拽调课模式下按住格子只做聚焦,课次块用 HTML5 拖拽移动
|
||||||
if (this.moveMode) {
|
if (this.moveMode) {
|
||||||
this.focusCell(day.date, jc)
|
this.focusCell(day.date, jc)
|
||||||
if (this.$refs.gridWrap && this.$refs.gridWrap.focus) this.$refs.gridWrap.focus()
|
const wrap = this.$refs.gridWrap
|
||||||
|
if (wrap && wrap.focus) wrap.focus({ preventScroll: true })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.dragging = true
|
this.dragging = true
|
||||||
this.additiveDrag = !!(ev && (ev.ctrlKey || ev.metaKey))
|
this.additiveDrag = !!(ev && (ev.ctrlKey || ev.metaKey))
|
||||||
this.dragStart = { fi, jc }
|
this.dragStart = { fi, jc }
|
||||||
this.dragEnd = { fi, jc }
|
this.dragEnd = { fi, jc }
|
||||||
|
this.dragOrigin = ev ? { x: ev.clientX, y: ev.clientY } : null
|
||||||
this.focusCell(day.date, jc)
|
this.focusCell(day.date, jc)
|
||||||
if (this.$refs.gridWrap && this.$refs.gridWrap.focus) this.$refs.gridWrap.focus()
|
const wrap = this.$refs.gridWrap
|
||||||
|
if (wrap && wrap.focus) wrap.focus({ preventScroll: true })
|
||||||
},
|
},
|
||||||
hoverCell(fi, jc) {
|
hoverCell(fi, jc, ev) {
|
||||||
if (!this.dragging) return
|
if (!this.dragging) return
|
||||||
|
// 刚进入页面首次单击会 focus 课表导致滚动,DOM 从指针下经过时误发 mouseenter。
|
||||||
|
// 指针未真正移动时不把结束格改成别的格子。
|
||||||
|
const origin = this.dragOrigin
|
||||||
|
if (origin && ev) {
|
||||||
|
const dx = Math.abs(ev.clientX - origin.x)
|
||||||
|
const dy = Math.abs(ev.clientY - origin.y)
|
||||||
|
if (dx < 6 && dy < 6) return
|
||||||
|
}
|
||||||
this.dragEnd = { fi, jc }
|
this.dragEnd = { fi, jc }
|
||||||
},
|
},
|
||||||
finishDrag() {
|
finishDrag() {
|
||||||
@@ -1047,6 +1061,7 @@ export default {
|
|||||||
this.dragStart = null
|
this.dragStart = null
|
||||||
this.dragEnd = null
|
this.dragEnd = null
|
||||||
this.additiveDrag = false
|
this.additiveDrag = false
|
||||||
|
this.dragOrigin = null
|
||||||
if (!s || !e) return
|
if (!s || !e) return
|
||||||
const fiFrom = Math.min(s.fi, e.fi)
|
const fiFrom = Math.min(s.fi, e.fi)
|
||||||
const fiTo = Math.max(s.fi, e.fi)
|
const fiTo = Math.max(s.fi, e.fi)
|
||||||
|
|||||||
Reference in New Issue
Block a user