@@ -40,6 +40,18 @@ const router = useRouter()
|
|||||||
const { isMobile, isDesktop } = useBreakpoints()
|
const { isMobile, isDesktop } = useBreakpoints()
|
||||||
const { learnStep } = useLearnProgress()
|
const { learnStep } = useLearnProgress()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课堂求助的入口收进姓名下拉里了,顶栏只留姓名按钮上的角标 —— 老师不用展开
|
||||||
|
* 菜单也能看见有没有人举手。桌面端限定,教师端接单后要在弹框里替学生写代码。
|
||||||
|
*/
|
||||||
|
const showHelpRequests = ref(false)
|
||||||
|
const hasHelpEntry = computed(
|
||||||
|
() => isDesktop.value && userStore.isTeacherOrAbove,
|
||||||
|
)
|
||||||
|
const pendingHelpCount = computed(() =>
|
||||||
|
hasHelpEntry.value ? collabStore.pendingCount : 0,
|
||||||
|
)
|
||||||
|
|
||||||
const isDark = useDark()
|
const isDark = useDark()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,6 +253,17 @@ const menus = computed<MenuOption[]>(() => [
|
|||||||
])
|
])
|
||||||
|
|
||||||
const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
|
const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
|
||||||
|
{
|
||||||
|
label: pendingHelpCount.value
|
||||||
|
? `课堂求助(${pendingHelpCount.value})`
|
||||||
|
: "课堂求助",
|
||||||
|
key: "help",
|
||||||
|
show: hasHelpEntry.value,
|
||||||
|
icon: renderIcon("streamline-emojis:raising-hands-2"),
|
||||||
|
props: {
|
||||||
|
onClick: () => (showHelpRequests.value = true),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "我的主页",
|
label: "我的主页",
|
||||||
key: "home",
|
key: "home",
|
||||||
@@ -348,15 +371,16 @@ function handleMenuSelect(key: string) {
|
|||||||
>
|
>
|
||||||
{{ screenMode }}
|
{{ screenMode }}
|
||||||
</n-button>
|
</n-button>
|
||||||
<HelpRequestList v-if="isDesktop && userStore.isTeacherOrAbove" />
|
|
||||||
<div v-if="userStore.isFinished">
|
<div v-if="userStore.isFinished">
|
||||||
<n-dropdown v-if="userStore.isAuthed" :options="options" size="large">
|
<n-dropdown v-if="userStore.isAuthed" :options="options" size="large">
|
||||||
|
<n-badge :value="pendingHelpCount" :max="99">
|
||||||
<n-button>
|
<n-button>
|
||||||
<Icon :icon="avatar" height="20"></Icon>
|
<Icon :icon="avatar" height="20"></Icon>
|
||||||
<span style="padding-left: 8px">
|
<span style="padding-left: 8px">
|
||||||
{{ userStore.user!.username }}
|
{{ userStore.user!.username }}
|
||||||
</span>
|
</span>
|
||||||
</n-button>
|
</n-button>
|
||||||
|
</n-badge>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
<n-flex align="center" v-else>
|
<n-flex align="center" v-else>
|
||||||
<n-button
|
<n-button
|
||||||
@@ -389,6 +413,7 @@ function handleMenuSelect(key: string) {
|
|||||||
整个丢弃),header 行随之丢掉 `max-width: 2000px` 那条居中样式。
|
整个丢弃),header 行随之丢掉 `max-width: 2000px` 那条居中样式。
|
||||||
n-modal 默认 teleport 到 body,塞在这里不影响它的实际渲染位置。
|
n-modal 默认 teleport 到 body,塞在这里不影响它的实际渲染位置。
|
||||||
-->
|
-->
|
||||||
|
<HelpRequestList v-if="hasHelpEntry" v-model:show="showHelpRequests" />
|
||||||
<CollabModal v-if="userStore.isTeacherOrAbove" />
|
<CollabModal v-if="userStore.isTeacherOrAbove" />
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,17 +2,34 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import { useCollabStore } from "shared/store/collab"
|
import { useCollabStore } from "shared/store/collab"
|
||||||
|
|
||||||
|
/** 由顶栏的姓名下拉菜单打开 */
|
||||||
|
const show = defineModel<boolean>("show", { default: false })
|
||||||
|
|
||||||
const collabStore = useCollabStore()
|
const collabStore = useCollabStore()
|
||||||
|
|
||||||
// 等待时长要每秒走一格,所以自己转一个 now
|
// 等待时长要每秒走一格,所以自己转一个 now。
|
||||||
|
// 只在弹框开着时转 —— 这个组件跟着顶栏常驻,关着的时候没人看这个数。
|
||||||
const now = ref(Date.now())
|
const now = ref(Date.now())
|
||||||
let timer: number | null = null
|
let timer: number | null = null
|
||||||
onMounted(() => {
|
|
||||||
|
function stopTimer() {
|
||||||
|
if (timer === null) return
|
||||||
|
window.clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(show, (opened) => {
|
||||||
|
if (!opened) {
|
||||||
|
stopTimer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
now.value = Date.now()
|
||||||
|
if (timer === null) {
|
||||||
timer = window.setInterval(() => (now.value = Date.now()), 1000)
|
timer = window.setInterval(() => (now.value = Date.now()), 1000)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
|
||||||
if (timer !== null) window.clearInterval(timer)
|
onUnmounted(stopTimer)
|
||||||
})
|
|
||||||
|
|
||||||
const waited = (createdAt: number) => {
|
const waited = (createdAt: number) => {
|
||||||
const seconds = Math.max(0, Math.floor((now.value - createdAt) / 1000))
|
const seconds = Math.max(0, Math.floor((now.value - createdAt) / 1000))
|
||||||
@@ -25,21 +42,19 @@ const handleAccept = (studentId: number, status: string) => {
|
|||||||
// 已被别的老师接走的不能点
|
// 已被别的老师接走的不能点
|
||||||
if (status === "active") return
|
if (status === "active") return
|
||||||
collabStore.accept(studentId)
|
collabStore.accept(studentId)
|
||||||
|
// 接单后马上要弹 CollabModal,这个列表得让位
|
||||||
|
show.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-popover trigger="click" placement="bottom-end" style="padding: 0">
|
<n-modal
|
||||||
<template #trigger>
|
v-model:show="show"
|
||||||
<n-badge :value="collabStore.pendingCount" :max="99">
|
preset="card"
|
||||||
<n-button>
|
title="课堂求助"
|
||||||
<Icon icon="streamline-emojis:raising-hands-2" height="20" />
|
:style="{ width: '420px' }"
|
||||||
<span style="padding-left: 8px">求助</span>
|
>
|
||||||
</n-button>
|
<div style="max-height: 60vh; overflow: auto">
|
||||||
</n-badge>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div style="width: 320px; max-height: 60vh; overflow: auto; padding: 8px">
|
|
||||||
<n-empty v-if="collabStore.groupedRequests.length === 0" description="暂无求助" />
|
<n-empty v-if="collabStore.groupedRequests.length === 0" description="暂无求助" />
|
||||||
|
|
||||||
<div v-for="group in collabStore.groupedRequests" :key="group.problemId">
|
<div v-for="group in collabStore.groupedRequests" :key="group.problemId">
|
||||||
@@ -94,5 +109,5 @@ const handleAccept = (studentId: number, status: string) => {
|
|||||||
<n-divider style="margin: 4px 0" />
|
<n-divider style="margin: 4px 0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-popover>
|
</n-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user