refactor(web): Header 只管顶栏,全局的东西挂回 App
Header 一直兼着「全局挂载点」:课堂求助的提示、新求助 toast、求助列表和 教师端协作弹框都寄生在里面,只因为顶栏是全局的。它其实不是 —— /admin/* 走的是 admin.vue,没有 Header,老师一进后台这四个消费者全部 卸载:求助照收,提示、角标、弹框一个都不出现,正好是 collab.ts 里写的 「老师可能正在后台改题时收到求助」那个场景。 这些东西跟着连接走,而连接在 App.vue 按登录态开关,所以搬进 CollabHost 挂在同一层。Header 因此不必再是单根组件,default.vue 那个靠 class 传 居中样式的写法也换成外层 div,连带删掉「必须挂在根 n-flex 内部」那段 补丁注释。 顺带: - 圆环扩散的暗黑切换抽成 useDarkTransition - 求助的角标/toast/列表不再限桌面端 —— 老师缩窗口也得知道有人在等; 接单确实要在电脑上写代码,那道闸挪进 HelpRequestList - 站名改 text 按钮,能 tab 到、回车能按 - logout 的两步收进 userStore.signOut() - 清掉死代码:handleMenuSelect 只认一个不存在的 key、active 里 ["user","setting"] 永远不生效的排除、两个 show:false 的菜单项 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QqqZwxtXLo2GTqMi51C94D
This commit is contained in:
82
apps/web/src/shared/components/CollabHost.vue
Normal file
82
apps/web/src/shared/components/CollabHost.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import type { CollabRequestItem } from "shared/composables/websocket"
|
||||
import { useCollabStore } from "shared/store/collab"
|
||||
import CollabModal from "./CollabModal.vue"
|
||||
import HelpRequestList from "./HelpRequestList.vue"
|
||||
|
||||
/**
|
||||
* 课堂求助的全局界面:一次性提示、新求助 toast、求助列表、教师端协作弹框。
|
||||
*
|
||||
* 挂在 App.vue 而不是顶栏或 default.vue 布局里。这些东西跟着**连接**走,
|
||||
* 而连接是全局常驻的(App.vue 按登录态开关)—— 挂在顶栏里的时候,老师一进
|
||||
* /admin 就换成了 admin.vue 布局,顶栏连同这几个消费者一起卸载:求助照收,
|
||||
* 提示、角标、协作弹框全都不出现,正好错过 collab.ts 里写的那句「老师可能
|
||||
* 正在后台改题时收到求助」。放在这里才真的全局。
|
||||
*
|
||||
* 位置要求:n-message-provider 的后代(useMessage 需要)。
|
||||
*/
|
||||
const collabStore = useCollabStore()
|
||||
const message = useMessage()
|
||||
|
||||
/**
|
||||
* 一次性提示统一在这里消费。
|
||||
*
|
||||
* 学生排着队切去看提交记录,老师这时候取消了他的求助,那条「老师已取消你的
|
||||
* 求助」挂在题目页上就永远没人消费 —— 教师端的 error 提示(比如「请先退出
|
||||
* 当前协作」)同理。
|
||||
*/
|
||||
watch(
|
||||
() => collabStore.noticeSeq,
|
||||
() => {
|
||||
const text = collabStore.consumeNotice()
|
||||
if (text) message.info(text)
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* 新求助进来只有角标默默 +1,上课走动的时候根本注意不到,补一条 toast。
|
||||
*
|
||||
* 只在数字**变大**时弹:老师自己接单、拒绝、别的老师接走都会让它变小,那些
|
||||
* 不该打扰人。断线重连后服务端会重推一份全量列表,队里还有人的话这里会再弹
|
||||
* 一次 —— 那正好是「你刚断过线,这些人还等着」,留着。
|
||||
*/
|
||||
watch(
|
||||
() => collabStore.pendingCount,
|
||||
(count, previous) => {
|
||||
if (count <= previous) return
|
||||
let latest: CollabRequestItem | null = null
|
||||
for (const item of collabStore.requests) {
|
||||
if (item.status !== "pending") continue
|
||||
if (!latest || item.createdAt > latest.createdAt) latest = item
|
||||
}
|
||||
const text = latest
|
||||
? `${latest.studentName} 求助:${latest.problemTitle}`
|
||||
: "有新的求助"
|
||||
// 内容传 render 函数(naive 的 content 支持),这样整条 toast 可点:
|
||||
// 点一下直接开求助列表,省得再去点名字、再点菜单。
|
||||
const notice = message.info(
|
||||
() =>
|
||||
h(
|
||||
"span",
|
||||
{
|
||||
style: { cursor: "pointer" },
|
||||
onClick: () => {
|
||||
collabStore.helpPanelOpen = true
|
||||
notice.destroy()
|
||||
},
|
||||
},
|
||||
`${text} · 点击处理`,
|
||||
),
|
||||
{ duration: 5000 },
|
||||
)
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HelpRequestList
|
||||
v-if="collabStore.isTeacher"
|
||||
v-model:show="collabStore.helpPanelOpen"
|
||||
/>
|
||||
<CollabModal v-if="collabStore.isTeacher" />
|
||||
</template>
|
||||
Reference in New Issue
Block a user