|
|
|
|
@ -1,189 +1,132 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="task app-container">
|
|
|
|
|
<div class="task-toolbar">
|
|
|
|
|
<div class="online">
|
|
|
|
|
<span>教学班:</span>
|
|
|
|
|
<el-select placeholder="请选择教学班" v-model="classId" clearable class="ml10 class-select" @change="getTaskList">
|
|
|
|
|
<el-option v-for="school in teachingClassList" :key="school.schoolClassId" :label="school.className" :value="school.schoolClassId" />
|
|
|
|
|
<section class="task-toolbar">
|
|
|
|
|
<div class="class-picker">
|
|
|
|
|
<span>教学班</span>
|
|
|
|
|
<el-select v-model="classId" placeholder="请选择教学班" class="class-select" @change="loadPublication">
|
|
|
|
|
<el-option v-for="item in teachingClassList" :key="item.schoolClassId" :label="item.className" :value="item.schoolClassId" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</div>
|
|
|
|
|
<el-tag v-if="selectedClass" :type="canOperateSelectedClass ? 'success' : 'info'">
|
|
|
|
|
{{ canOperateSelectedClass ? "可分配" : "仅查看" }}
|
|
|
|
|
<el-tag v-if="classId" :type="configured ? 'success' : 'warning'">
|
|
|
|
|
{{ configured ? '已保存发布配置' : '默认全部发布(未配置)' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<span v-if="classId" class="task-tip">{{ configured ? '仅勾选的实训任务会出现在学生端。' : '当前所有实训任务均已默认勾选,保存后将按勾选结果发布。' }}</span>
|
|
|
|
|
<el-button type="primary" :disabled="!canOperateSelectedClass || !classId" :loading="saving" @click="savePublication">保存发布配置</el-button>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<el-alert v-if="classId && !canOperateSelectedClass" title="只能配置自己创建的教学班,当前班级仅可查看。" type="info" :closable="false" show-icon />
|
|
|
|
|
|
|
|
|
|
<el-table
|
|
|
|
|
:data="tableData"
|
|
|
|
|
ref="tableRef"
|
|
|
|
|
:data="tasks"
|
|
|
|
|
row-key="taskKey"
|
|
|
|
|
border
|
|
|
|
|
v-loading="loading"
|
|
|
|
|
class="task-table"
|
|
|
|
|
height="600"
|
|
|
|
|
ref="tableRef"
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
color: '#fff',
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
}"
|
|
|
|
|
row-key="id"
|
|
|
|
|
@select="handleSelect"
|
|
|
|
|
@select-all="handleSelectAll"
|
|
|
|
|
@selection-change="handleSelectionChange"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column label="勾选" type="selection" width="100" align="center" :reserve-selection="true" :selectable="canSelectTask"></el-table-column>
|
|
|
|
|
<el-table-column label="实训任务" prop="module" align="center"></el-table-column>
|
|
|
|
|
<el-table-column type="selection" width="58" :reserve-selection="true" :selectable="() => canOperateSelectedClass" />
|
|
|
|
|
<el-table-column prop="taskName" label="实训任务" min-width="240" align="center" header-align="center" />
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
import { listTrainingTasks } from "@/api/trainingTask";
|
|
|
|
|
import * as indexApi from "@/api/teacher";
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
import * as indexApi from "@/api/teacher";
|
|
|
|
|
import { onMounted } from "vue";
|
|
|
|
|
const classList = ref([]);
|
|
|
|
|
const tableData = ref([]);
|
|
|
|
|
const tasks = ref([]);
|
|
|
|
|
const classId = ref("");
|
|
|
|
|
const selectedTaskKeys = ref([]);
|
|
|
|
|
const configured = ref(false);
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const saving = ref(false);
|
|
|
|
|
const tableRef = ref(null);
|
|
|
|
|
const classId = ref("");
|
|
|
|
|
|
|
|
|
|
const currentUserId = computed(() => userStore.userInfo.userId);
|
|
|
|
|
const selectedClass = computed(() => classList.value.find((item) => item.schoolClassId === classId.value));
|
|
|
|
|
const teachingClassList = computed(() => classList.value.filter((item) => normalizeClassType(item) === "TEACHING"));
|
|
|
|
|
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING" || !item.classType && item.createdBy));
|
|
|
|
|
const selectedClass = computed(() => teachingClassList.value.find((item) => item.schoolClassId === classId.value));
|
|
|
|
|
const canOperateSelectedClass = computed(() => selectedClass.value?.createdBy === currentUserId.value);
|
|
|
|
|
|
|
|
|
|
const normalizeClassType = (schoolClass) => {
|
|
|
|
|
if (schoolClass?.classType) return schoolClass.classType;
|
|
|
|
|
return schoolClass?.createdBy ? "TEACHING" : "ADMIN";
|
|
|
|
|
};
|
|
|
|
|
// 获取班级列表
|
|
|
|
|
const getClassList = () => {
|
|
|
|
|
indexApi.getClassListBySchoolId({ schoolId: userStore.userInfo.schoolId }).then((res) => {
|
|
|
|
|
classList.value = res.data || [];
|
|
|
|
|
classId.value = teachingClassList.value[0]?.schoolClassId || "";
|
|
|
|
|
getTaskList();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 获取任务列表
|
|
|
|
|
const getTaskList = () => {
|
|
|
|
|
tableRef.value?.clearSelection?.();
|
|
|
|
|
if (!classId.value) {
|
|
|
|
|
tableData.value = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
loading.value = true;
|
|
|
|
|
indexApi
|
|
|
|
|
.getTaskList({ classId: classId.value })
|
|
|
|
|
.then((res) => {
|
|
|
|
|
tableData.value = res.data || [];
|
|
|
|
|
setInitialSelection();
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 其他引入和定义保持不变
|
|
|
|
|
async function loadClasses() {
|
|
|
|
|
const res = await indexApi.getClassListBySchoolId({ schoolId: userStore.userInfo.schoolId });
|
|
|
|
|
classList.value = res.data || [];
|
|
|
|
|
classId.value = teachingClassList.value[0]?.schoolClassId || "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isInitializing = ref(true); // 添加初始化标志
|
|
|
|
|
async function loadTasks() {
|
|
|
|
|
const res = await listTrainingTasks({ enabledOnly: false });
|
|
|
|
|
tasks.value = res.data || [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改初始化方法
|
|
|
|
|
const setInitialSelection = () => {
|
|
|
|
|
isInitializing.value = true;
|
|
|
|
|
function applySelection() {
|
|
|
|
|
tableRef.value?.clearSelection();
|
|
|
|
|
proxy.$nextTick(() => {
|
|
|
|
|
tableData.value.forEach((row) => {
|
|
|
|
|
if (row.disabledStatus == 0) {
|
|
|
|
|
tableRef.value.toggleRowSelection(row, true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
isInitializing.value = false; // 初始化完成
|
|
|
|
|
const selected = new Set(selectedTaskKeys.value);
|
|
|
|
|
tasks.value.forEach((task) => tableRef.value?.toggleRowSelection(task, selected.has(task.taskKey)));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
// 修改单选处理方法
|
|
|
|
|
const handleSelect = async (selection, row) => {
|
|
|
|
|
if (isInitializing.value) return; // 初始化时不处理
|
|
|
|
|
if (!canOperateSelectedClass.value) return;
|
|
|
|
|
row.disabledStatus = row.disabledStatus == 0 ? 1 : 0;
|
|
|
|
|
handleTaskDistribution(tableData.value);
|
|
|
|
|
};
|
|
|
|
|
// 修改全选处理方法
|
|
|
|
|
const handleSelectAll = async (selection) => {
|
|
|
|
|
if (isInitializing.value) return; // 初始化时不处理
|
|
|
|
|
if (!canOperateSelectedClass.value) return;
|
|
|
|
|
const newStatus = selection.length > 0 ? 0 : 1; // 全选为0,取消为1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadPublication() {
|
|
|
|
|
selectedTaskKeys.value = [];
|
|
|
|
|
configured.value = false;
|
|
|
|
|
if (!classId.value) return;
|
|
|
|
|
loading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
// 更新本地状态
|
|
|
|
|
tableData.value.forEach((item) => {
|
|
|
|
|
item.disabledStatus = newStatus;
|
|
|
|
|
});
|
|
|
|
|
handleTaskDistribution(tableData.value);
|
|
|
|
|
selection.length = 0;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("批量更新失败:", error);
|
|
|
|
|
// 恢复选中状态
|
|
|
|
|
proxy.$nextTick(() => {
|
|
|
|
|
if (newStatus === 0) {
|
|
|
|
|
tableRef.value.clearSelection();
|
|
|
|
|
} else {
|
|
|
|
|
tableRef.value.toggleAllSelection();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
const res = await indexApi.getTaskPublication({ classId: classId.value });
|
|
|
|
|
configured.value = res.data?.configured === true;
|
|
|
|
|
selectedTaskKeys.value = configured.value
|
|
|
|
|
? (res.data?.taskKeys || [])
|
|
|
|
|
: tasks.value.map((task) => task.taskKey);
|
|
|
|
|
applySelection();
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
// let { missingInArr2, missingInArr1 } = findMissingItems(selection, tableData.value);
|
|
|
|
|
// missingInArr1.forEach((item) => {
|
|
|
|
|
// item.disabledStatus = 1;
|
|
|
|
|
// });
|
|
|
|
|
// selection.forEach((item) => {
|
|
|
|
|
// item.disabledStatus = 0;
|
|
|
|
|
// });
|
|
|
|
|
// console.log(missingInArr1, selection, selection.length > 0 ? "selection" : "missingInArr1");
|
|
|
|
|
// handleTaskDistribution(selection.length > 0 ? selection : missingInArr1);
|
|
|
|
|
};
|
|
|
|
|
function findMissingItems(arr1, arr2) {
|
|
|
|
|
// 找出arr1中有但arr2中没有的元素
|
|
|
|
|
const missingInArr2 = arr1.filter((item) => !arr2.includes(item));
|
|
|
|
|
// 找出arr2中有但arr1中没有的元素
|
|
|
|
|
const missingInArr1 = arr2.filter((item) => !arr1.includes(item));
|
|
|
|
|
return {
|
|
|
|
|
missingInArr2,
|
|
|
|
|
missingInArr1,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// 修改任务分配
|
|
|
|
|
const handleTaskDistribution = (selection) => {
|
|
|
|
|
if (!canOperateSelectedClass.value) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const uniqueUsers = [...new Map(selection.map((item) => [item.module, item])).values()];
|
|
|
|
|
indexApi.addTask({ taskAllocationList: uniqueUsers, classId: classId.value, schoolId: userStore.userInfo.schoolId, userId: currentUserId.value }).then((res) => {
|
|
|
|
|
getTaskList();
|
|
|
|
|
proxy.$modal.msgSuccess("分配成功");
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const canSelectTask = () => canOperateSelectedClass.value;
|
|
|
|
|
// 恢复选中状态
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
getClassList();
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang='scss' scoped>
|
|
|
|
|
.task {
|
|
|
|
|
height: calc(100vh - 70px);
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
function handleSelectionChange(selection) {
|
|
|
|
|
selectedTaskKeys.value = selection.map((task) => task.taskKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 14px;
|
|
|
|
|
async function savePublication() {
|
|
|
|
|
if (!canOperateSelectedClass.value || !classId.value) return;
|
|
|
|
|
saving.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const taskAllocationList = selectedTaskKeys.value.map((taskKey, index) => ({ module: taskKey, disabledStatus: 0, sort: index + 1 }));
|
|
|
|
|
await indexApi.addTask({
|
|
|
|
|
taskAllocationList,
|
|
|
|
|
classId: classId.value,
|
|
|
|
|
schoolId: userStore.userInfo.schoolId,
|
|
|
|
|
userId: currentUserId.value,
|
|
|
|
|
});
|
|
|
|
|
configured.value = true;
|
|
|
|
|
proxy.$modal.msgSuccess("发布配置已保存");
|
|
|
|
|
} finally {
|
|
|
|
|
saving.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.class-select {
|
|
|
|
|
width: 240px;
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
await Promise.all([loadClasses(), loadTasks()]);
|
|
|
|
|
await loadPublication();
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
.task-table {
|
|
|
|
|
width: 80%;
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.task { min-height: calc(100vh - 70px); }
|
|
|
|
|
.task-toolbar { display:flex; align-items:center; flex-wrap:wrap; gap:12px; margin-bottom:16px; padding:16px 18px; border:1px solid rgba(31,185,244,.32); border-radius:10px; background:rgba(3,31,48,.7); }
|
|
|
|
|
.class-picker { display:flex; align-items:center; gap:10px; color:#d7f7ff; font-weight:700; }
|
|
|
|
|
.class-select { width:260px; }
|
|
|
|
|
.task-tip { flex:1; min-width:280px; color:#8cc9df; font-size:13px; }
|
|
|
|
|
.task-table { width:100%; }
|
|
|
|
|
@media (max-width: 760px) { .class-picker,.class-select { width:100%; } .task-toolbar :deep(.el-button) { width:100%; } }
|
|
|
|
|
</style>
|
|
|
|
|
|