diff --git a/src/api/teacher.js b/src/api/teacher.js
index ef5afa8..7822217 100644
--- a/src/api/teacher.js
+++ b/src/api/teacher.js
@@ -485,6 +485,30 @@ export function addStudent(params) {
params,
});
}
+export function addTeacherRosterStudent(data) {
+ return request({
+ url: "/api/user/teacher-roster/students",
+ method: "POST",
+ params: {
+ name: data.name,
+ userName: data.userName,
+ teachingClassId: data.teachingClassId,
+ operatorId: data.operatorId,
+ },
+ });
+}
+export function updateTeacherRosterStudent(data) {
+ return request({
+ url: `/api/user/teacher-roster/students/${data.userId}`,
+ method: "POST",
+ params: {
+ name: data.name,
+ userName: data.userName,
+ teachingClassId: data.teachingClassId,
+ operatorId: data.operatorId,
+ },
+ });
+}
// 学生管理 编辑
export function updateStudent(data) {
const { operatorId, teachingClassId, ...body } = data;
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 4a1a930..8ab8f1a 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -95,15 +95,15 @@
color: var(--el-color-primary) !important;
}
-.app-container .el-table .el-loading-mask {
- background: rgba(4, 18, 29, 0.72) !important;
- backdrop-filter: blur(1px);
+.el-loading-mask {
+ background: rgba(4, 18, 29, 0.78) !important;
+ backdrop-filter: blur(2px);
}
-.app-container .el-table .el-loading-spinner .path {
+.el-loading-spinner .path {
stroke: #20d6ff;
}
-.app-container .el-table .el-loading-text {
+.el-loading-text {
color: #d7f8ff;
}
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index acb4823..d7ea4d7 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -33,7 +33,14 @@ const router = useRouter();
const role = ref(JSON.parse(getUserInfo()).roleId);
const isDemo = computed(() => isStudentDemo());
const demoClassName = computed(() => getStudentDemoSession()?.userInfo?.className || "教学班");
-function exitDemo() { clearStudentDemoSession(); window.location.replace("/student-demo"); }
+function exitDemo() {
+ clearStudentDemoSession();
+ window.close();
+ // 仅在浏览器不允许关闭窗口时兜底,避免停留在演示会话中转页。
+ window.setTimeout(() => {
+ if (!window.closed) window.location.replace("/index");
+ }, 100);
+}
const mainClass = computed(() => {
if (Number(role.value) === 4) return "app-main";
if (Number(role.value) === 3) return "teacher-main";
@@ -314,6 +321,7 @@ router.beforeEach(async (to, from, next) => {
--el-table-border-color: rgba(54, 159, 212, 0.2);
--el-table-header-bg-color: rgba(9, 48, 73, 0.96);
--el-table-tr-bg-color: rgba(2, 16, 27, 0.78);
+ --el-fill-color-lighter: rgba(2, 16, 27, 0.72);
--el-table-row-hover-bg-color: rgba(0, 111, 177, 0.18);
margin-top: 16px !important;
color: #dff4ff;
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 384904f..b702ca7 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -78,9 +78,10 @@ import HeaderSearch from "@/components/HeaderSearch";
import RuoYiGit from "@/components/RuoYi/Git";
import RuoYiDoc from "@/components/RuoYi/Doc";
import useAppStore from "@/store/modules/app";
-import useUserStore from "@/store/modules/user";
-import useSettingsStore from "@/store/modules/settings";
-import * as indexApi from "@/api/teacher";
+import useUserStore from "@/store/modules/user";
+import useSettingsStore from "@/store/modules/settings";
+import * as indexApi from "@/api/teacher";
+import { getStudentDemoSession, isStudentDemo } from "@/utils/studentDemo";
const { proxy } = getCurrentInstance();
const router = useRouter();
const appStore = useAppStore();
@@ -275,6 +276,19 @@ const roleList = computed(() => {
});
// 获取用户详情
const getUserInfo = () => {
+ if (isStudentDemo()) {
+ const demoUser = getStudentDemoSession()?.userInfo || userStore.userInfo;
+ form.value = {
+ name: demoUser.name || "演示学生",
+ username: demoUser.username || "",
+ schoolFacultyId: "演示模式",
+ schoolMajorId: "演示模式",
+ schoolClassId: demoUser.className || "教学班",
+ password: "******",
+ passwords: "",
+ };
+ return;
+ }
if (role.value === 1) {
form.value = {
name: userStore.userInfo.name,
@@ -295,8 +309,12 @@ const getUserInfo = () => {
.catch(() => {});
};
// 修改密码
-const changePassword = () => {
- indexApi
+const changePassword = () => {
+ if (isStudentDemo()) {
+ proxy.$modal.msgWarning("演示模式不支持修改密码");
+ return;
+ }
+ indexApi
.updatePassword({ userId: userStore.userInfo.userId, password: form.value.passwords })
.then(() => {
proxy.$modal.msgSuccess("修改成功");
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index ac7f03e..8acd820 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -52,8 +52,8 @@
-
-
+
+
@@ -76,9 +76,11 @@ const route = useRoute();
const router = useRouter();
const appStore = useAppStore();
const settingsStore = useSettingsStore();
-const permissionStore = usePermissionStore();
-const sidebarRouters = ref([...constantRoutes]);
-const routerStatus = {
+const permissionStore = usePermissionStore();
+const sidebarRouters = ref([...constantRoutes]);
+const visibleSidebarRouters = computed(() => filterRoutesByOrganizationMode(sidebarRouters.value));
+const sidebarMenuKey = computed(() => visibleSidebarRouters.value.map((route) => route.path).join("|"));
+const routerStatus = {
行业需求分析: [
{
id: "9f413263-e6ff-4ee5-984a-7a231cad6818",
@@ -337,8 +339,8 @@ const teacherManageNav = [
{
title: "教学管理",
children: [
- { title: "院系管理", path: "/school/index" },
- { title: "专业管理", path: "/major/index" },
+ { title: "院系管理", path: "/school/index", requiresFacultyManagement: true },
+ { title: "专业管理", path: "/major/index", requiresMajorManagement: true },
{ title: "班级管理", path: "/class/index" },
{ title: "教师管理", path: "/teacher/index" },
{ title: "学生管理", path: "/student/index" },
@@ -374,7 +376,7 @@ const trainingTaskKeyByPath = {
"/assessment/diagnosis": "product-diagnosis-iteration",
};
const trainingTaskTitleByPath = ref({});
-const currentCourseNav = computed(() => (isTeacherRole.value ? teacherManageNav : studentCourseNav));
+const currentCourseNav = computed(() => (isTeacherRole.value ? filterTeacherManageNavigation(teacherManageNav) : studentCourseNav));
const studentIconByPath = {
"/assistant/index": "MagicStick",
"/foundation/new-product-survey": "Search",
@@ -561,10 +563,11 @@ function excludeRoutesByMetaTitle(routes, excludeTitles) {
return result;
}, []);
}
-const getTaskList = () => {
+const getTaskList = async () => {
if (userStore.userInfo.roleId == 1) {
sidebarRouters.value = [...constantRoutes, ...platformAdminRoutes];
} else if (userStore.userInfo.roleId == 2) {
+ await userStore.ensureSchoolProductConfig().catch(() => null);
sidebarRouters.value = [...constantRoutes, ...filterRoutesByOrganizationMode(schoolAdminRoutes)];
} else if (userStore.userInfo.roleId != 3) {
indexApi.getStudentTaskList({ userId: userStore.userInfo.userId }).then((res) => {
@@ -572,26 +575,41 @@ const getTaskList = () => {
console.log(processModuleStatus(groupAndSortModules(res.data)));
proxy.$cache.session.setJSON("disabledChildren", processModuleStatus(groupAndSortModules(res.data)).disabledChildren);
proxy.$cache.session.setJSON("disabledTitles", disabledTitles);
- sidebarRouters.value = [...constantRoutes, ...dynamicRoutes];
- });
+ sidebarRouters.value = [...constantRoutes, ...dynamicRoutes];
+ });
} else {
+ await userStore.ensureSchoolProductConfig().catch(() => null);
sidebarRouters.value = [...constantRoutes, ...filterRoutesByOrganizationMode(teacherRoutes)];
}
};
-function filterRoutesByOrganizationMode(routes) {
+function isSchoolNavigationVisible(item) {
const config = userStore.schoolProductConfig;
- const legacyAdminEnabled = config?.organizationMode !== "TEACHING_CLASS_ONLY";
- const facultyEnabled = config?.facultyManagementEnabled ?? legacyAdminEnabled;
- const majorEnabled = config?.majorManagementEnabled ?? legacyAdminEnabled;
- const adminClassEnabled = config?.adminClassManagementEnabled ?? legacyAdminEnabled;
- return routes.filter((route) => {
- if (route.meta?.requiresManagerTeacher && userStore.userInfo.teacherAdmin !== true) return false;
- if (route.meta?.requiresFacultyManagement && !facultyEnabled) return false;
- if (route.meta?.requiresMajorManagement && !majorEnabled) return false;
- if (route.meta?.requiresAdminClass && !adminClassEnabled) return false;
- return true;
- });
+ const facultyEnabled = config?.facultyManagementEnabled === true;
+ const majorEnabled = config?.majorManagementEnabled === true;
+ const adminClassEnabled = config?.adminClassManagementEnabled === true;
+ const capability = item.meta || item;
+ if (capability.requiresManagerTeacher && userStore.userInfo.teacherAdmin !== true) return false;
+ if (capability.requiresFacultyManagement && !facultyEnabled) return false;
+ if (capability.requiresMajorManagement && !majorEnabled) return false;
+ if (capability.requiresAdminClass && !adminClassEnabled) return false;
+ return true;
+}
+
+function filterRoutesByOrganizationMode(routes) {
+ return routes.filter(isSchoolNavigationVisible);
+}
+
+function filterTeacherManageNavigation(sections) {
+ return sections
+ .map((section) => {
+ if (!section.children) return section;
+ return {
+ ...section,
+ children: section.children.filter(isSchoolNavigationVisible),
+ };
+ })
+ .filter((section) => (section.children ? section.children.length > 0 : isSchoolNavigationVisible(section)));
}
watch(
() => userStore.schoolProductConfig?.organizationMode,
diff --git a/src/router/index.js b/src/router/index.js
index c32a9b5..cb3a37f 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -511,7 +511,7 @@ export const teacherRoutes = [
component: Layout,
redirect: "/teacher/index",
roles: ["teacher"],
- meta: { title: "教师管理", icon: "教师管理", affix: true, requiresAdminClass: true },
+ meta: { title: "教师管理", icon: "教师管理", affix: true },
children: [
// 教师管理
{
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index ebf5aff..e5328a2 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -68,7 +68,7 @@ const useUserStore = defineStore("user", {
if (this.schoolProductConfig) {
return Promise.resolve(this.schoolProductConfig);
}
- if (!this.userInfo?.schoolId || this.userInfo?.roleId == 1) {
+ if (this.userInfo?.roleId == 1) {
return Promise.resolve(null);
}
return getCurrentSchoolProductConfig().then((res) => {
diff --git a/src/views/login.vue b/src/views/login.vue
index d5d6e6f..3509e6d 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -179,8 +179,8 @@ getCookie();
-webkit-text-fill-color: #303133;
}
- :deep(.password-input .el-input__inner::placeholder),
- :deep(.password-input input::placeholder) {
+ :deep(.el-input__inner::placeholder),
+ :deep(input::placeholder) {
color: #b8c0cc;
-webkit-text-fill-color: #b8c0cc;
opacity: 1;
diff --git a/src/views/teacherEnd/class/index.vue b/src/views/teacherEnd/class/index.vue
index 8cde940..cfbad32 100644
--- a/src/views/teacherEnd/class/index.vue
+++ b/src/views/teacherEnd/class/index.vue
@@ -23,12 +23,12 @@
-
+
{{ row.schoolFacultyName || "-" }}
-
+
{{ row.schoolMajorName || "-" }}
diff --git a/src/views/teacherEnd/student/index.vue b/src/views/teacherEnd/student/index.vue
index b35c7f9..81cd6cb 100644
--- a/src/views/teacherEnd/student/index.vue
+++ b/src/views/teacherEnd/student/index.vue
@@ -77,7 +77,8 @@
-
+
+
@@ -96,6 +97,12 @@
+
+
+
+
+
+
@@ -121,6 +128,7 @@ import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
const currentUserId = computed(() => userStore.userInfo.userId);
const currentSchoolId = computed(() => userStore.userInfo.schoolId);
+const isTeacherRosterMode = computed(() => userStore.schoolProductConfig?.studentRosterOwner === "TEACHER");
import * as indexApi from "@/api/teacher";
const dialogVisible = ref(false);
const loading = ref(false);
@@ -130,6 +138,7 @@ const formInline = ref({
schoolMajorId: "",
schoolMajorName: "",
schoolClassId: "",
+ teachingClassId: "",
schoolName: "",
userName: "",
});
@@ -139,17 +148,25 @@ const createEmptyForm = () => ({
schoolMajorId: "",
schoolMajorName: "",
schoolClassId: "",
+ teachingClassId: "",
schoolName: "",
userName: "",
name: "",
});
-const rules = ref({
+const organizationRules = {
schoolFacultyId: [{ required: true, message: "请选择所属院系", trigger: "change" }],
schoolMajorId: [{ required: true, message: "请选择所属专业", trigger: "change" }],
schoolClassId: [{ required: true, message: "请选择所属班级", trigger: "change" }],
+};
+const studentIdentityRules = {
name: [{ required: true, message: "请输入学生姓名", trigger: "blur" }],
userName: [{ required: true, message: "请输入学号", trigger: "blur" }],
-});
+};
+const teacherRosterRules = {
+ ...studentIdentityRules,
+ teachingClassId: [{ required: true, message: "请选择所属教学班", trigger: "change" }],
+};
+const studentFormRules = computed(() => (isTeacherRosterMode.value ? teacherRosterRules : { ...organizationRules, ...studentIdentityRules }));
const paramsquery = ref({
index: 1,
size: 10,
@@ -194,18 +211,30 @@ const editSchool = (row) => {
let data = { ...row };
formInline.value = data;
formInline.value.userName = row.username;
+ formInline.value.teachingClassId = isTeacherRosterMode.value ? (row.teachingClassId || currentTeachingClassId()) : "";
classInfo.value = {
className: row.className || row.schoolClassName || "",
};
dialogVisible.value = true;
status.value = false;
- indexApi.getFacultyAndMajorName({ schoolMajorId: row.schoolMajorId }).then((res) => {
- classInfo.value.name = Object.keys(res.data)[0];
- classInfo.value.value = Object.values(res.data)[0];
- });
+ if (!isTeacherRosterMode.value && row.schoolMajorId) {
+ indexApi.getFacultyAndMajorName({ schoolMajorId: row.schoolMajorId }).then((res) => {
+ classInfo.value.name = Object.keys(res.data)[0];
+ classInfo.value.value = Object.values(res.data)[0];
+ });
+ }
};
const add = () => {
formInline.value.schoolId = currentSchoolId.value;
+ formInline.value.operatorId = currentUserId.value;
+ if (isTeacherRosterMode.value) {
+ indexApi.addTeacherRosterStudent(formInline.value).then((res) => {
+ proxy.$message.success("添加成功");
+ dialogVisible.value = false;
+ getList();
+ });
+ return;
+ }
formInline.value.schoolMajorName = majorList.value?.filter((item) => item.schoolMajorId == formInline.value.schoolMajorId)[0].schoolMajorName;
formInline.value.schoolClassName = classList1.value?.filter((item) => item.schoolClassId == formInline.value.schoolClassId)[0].className;
formInline.value.userId = currentUserId.value;
@@ -218,6 +247,14 @@ const add = () => {
const edit = () => {
formInline.value.status = true;
formInline.value.operatorId = currentUserId.value;
+ if (isTeacherRosterMode.value) {
+ indexApi.updateTeacherRosterStudent(formInline.value).then((res) => {
+ proxy.$message.success("编辑成功");
+ dialogVisible.value = false;
+ getList();
+ });
+ return;
+ }
formInline.value.teachingClassId = currentTeachingClassId();
indexApi.updateStudent(formInline.value).then((res) => {
proxy.$message.success("编辑成功");
@@ -352,6 +389,14 @@ const canUseAdminClassForStudent = (schoolClass) => {
};
const classMap = computed(() => new Map(classList.value.map((item) => [item.schoolClassId, item])));
const adminClassList = computed(() => classList.value.filter((item) => canUseAdminClassForStudent(item)));
+const teachingClassOptions = computed(() =>
+ classList.value.filter(
+ (schoolClass) =>
+ normalizeClassType(schoolClass) === "TEACHING" &&
+ sameValue(schoolClass.schoolId, currentSchoolId.value) &&
+ sameValue(schoolClass.createdBy, currentUserId.value)
+ )
+);
const canOperateStudent = (row) => {
const selectedClass = classMap.value.get(paramsquery.value.schoolClassId);
if (selectedClass && normalizeClassType(selectedClass) === "TEACHING") {
@@ -424,10 +469,13 @@ const initPassword = (row, status) => {
.catch(() => {});
}
};
-onMounted(() => {
+onMounted(async () => {
+ await userStore.ensureSchoolProductConfig().catch(() => null);
getList();
getClassList();
- getFacultyList();
+ if (!isTeacherRosterMode.value) {
+ getFacultyList();
+ }
});