merge: add student demo window
commit
d3aaeb39b2
@ -0,0 +1,14 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function createStudentDemoSession(teachingClassId) {
|
||||
return request({ url: "/api/teacher/student-demo-sessions", method: "post", data: { teachingClassId } });
|
||||
}
|
||||
|
||||
export function exchangeStudentDemoSession(ticket) {
|
||||
return request({
|
||||
url: "/api/student-demo-sessions/exchange",
|
||||
method: "post",
|
||||
data: { ticket },
|
||||
headers: { isToken: false, repeatSubmit: false },
|
||||
});
|
||||
}
|
||||
@ -1,24 +1,28 @@
|
||||
import Cookies from "js-cookie";
|
||||
import Cookies from "js-cookie";
|
||||
import { clearStudentDemoSession, getStudentDemoSession, isStudentDemo } from "@/utils/studentDemo";
|
||||
|
||||
const TokenKey = "Admin-Token";
|
||||
const user = "UserInfo";
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey);
|
||||
export function getToken() {
|
||||
return getStudentDemoSession()?.token || Cookies.get(TokenKey);
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return Cookies.set(TokenKey, token);
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey);
|
||||
export function removeToken() {
|
||||
if (isStudentDemo()) return clearStudentDemoSession();
|
||||
return Cookies.remove(TokenKey);
|
||||
}
|
||||
export function setUserInfo(userinfo) {
|
||||
return Cookies.set(user, userinfo);
|
||||
}
|
||||
export function getUserInfo() {
|
||||
return Cookies.get(user);
|
||||
}
|
||||
export function removeUserInfo() {
|
||||
return Cookies.remove(user);
|
||||
}
|
||||
export function getUserInfo() {
|
||||
const demo = getStudentDemoSession();
|
||||
return demo?.userInfo ? JSON.stringify(demo.userInfo) : Cookies.get(user);
|
||||
}
|
||||
export function removeUserInfo() {
|
||||
if (isStudentDemo()) return clearStudentDemoSession();
|
||||
return Cookies.remove(user);
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
export const STUDENT_DEMO_STORAGE_KEY = "student-demo-session";
|
||||
|
||||
export function getStudentDemoSession() {
|
||||
try {
|
||||
const value = sessionStorage.getItem(STUDENT_DEMO_STORAGE_KEY);
|
||||
return value ? JSON.parse(value) : null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isStudentDemo() {
|
||||
return Boolean(getStudentDemoSession()?.token);
|
||||
}
|
||||
|
||||
export function saveStudentDemoSession(login) {
|
||||
sessionStorage.setItem(STUDENT_DEMO_STORAGE_KEY, JSON.stringify({
|
||||
token: login.token,
|
||||
userInfo: login,
|
||||
expiresAt: Date.now() + 30 * 60 * 1000,
|
||||
}));
|
||||
}
|
||||
|
||||
export function clearStudentDemoSession() {
|
||||
sessionStorage.removeItem(STUDENT_DEMO_STORAGE_KEY);
|
||||
}
|
||||
|
||||
export function getDemoAnswerKey(taskKey) {
|
||||
return `student-demo-answer:${getStudentDemoSession()?.userInfo?.demoTeachingClassId || ""}:${taskKey}`;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<main class="student-demo-entry"><p>{{ message }}</p></main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { exchangeStudentDemoSession } from "@/api/studentDemo";
|
||||
import { saveStudentDemoSession } from "@/utils/studentDemo";
|
||||
|
||||
const message = ref("正在连接教师演示会话…");
|
||||
|
||||
function receiveTicket(event) {
|
||||
if (event.origin !== window.location.origin || event.source !== window.opener || event.data?.type !== "STUDENT_DEMO_TICKET") return;
|
||||
exchangeStudentDemoSession(event.data.ticket).then((res) => {
|
||||
saveStudentDemoSession(res.data);
|
||||
window.removeEventListener("message", receiveTicket);
|
||||
window.location.replace("/index");
|
||||
}).catch(() => { message.value = "演示链接已失效,请返回教师端重新打开。"; });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!window.opener) { message.value = "演示链接已失效,请返回教师端重新打开。"; return; }
|
||||
window.addEventListener("message", receiveTicket);
|
||||
window.opener.postMessage({ type: "STUDENT_DEMO_READY" }, window.location.origin);
|
||||
});
|
||||
onBeforeUnmount(() => window.removeEventListener("message", receiveTicket));
|
||||
</script>
|
||||
|
||||
<style scoped>.student-demo-entry{display:grid;min-height:100vh;place-items:center;background:#06111d;color:#dff7ff;font-size:18px}</style>
|
||||
@ -0,0 +1,24 @@
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const root = path.resolve(__dirname, "..");
|
||||
const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
|
||||
|
||||
const auth = read("src/utils/auth.js");
|
||||
const demoStorage = read("src/utils/studentDemo.js");
|
||||
const api = read("src/api/studentDemo.js");
|
||||
const teacherPage = read("src/views/teacherEnd/trainingTask/index.vue");
|
||||
const demoPage = read("src/views/studentDemo/index.vue");
|
||||
|
||||
assert(auth.includes("getStudentDemoSession") && demoStorage.includes("sessionStorage"), "demo token must use tab-local session storage");
|
||||
assert(api.includes("/api/teacher/student-demo-sessions"), "teacher ticket API missing");
|
||||
assert(api.includes("/api/student-demo-sessions/exchange"), "ticket exchange API missing");
|
||||
assert(teacherPage.includes("学生端讲解"), "teacher entry missing");
|
||||
assert(teacherPage.includes("window.open("), "teacher entry must open a new window");
|
||||
assert(teacherPage.includes("STUDENT_DEMO_READY"), "teacher must wait for child readiness");
|
||||
assert(demoPage.includes("STUDENT_DEMO_TICKET"), "demo page must receive ticket via postMessage");
|
||||
assert(demoPage.includes("window.location.origin"), "postMessage must validate origin");
|
||||
assert(!teacherPage.includes("ticket="), "ticket must not be sent in URL");
|
||||
|
||||
console.log("student demo mode front-end contract passed");
|
||||
Loading…
Reference in New Issue