Files
OJ2/apps/web/src/oj/task/components/LeaveModal.vue
T
xuyueandClaude Opus 5.5 29c31640b5 refactor(课堂任务): 「课堂看板」改名「课堂任务」,地址换成 /task;班级 PK 换成 /pk
- 界面上的「课堂看板」「课堂作业」统一叫「课堂任务」,顶栏、老师首页、后台菜单一起改
- 前端 oj/classroom → oj/task、oj/class → oj/pk,两个名字不再只差几个字母却毫不相干;旧地址不留跳转
- 后端 routes/classroom.ts 拆开:课堂任务的接口进 routes/task.ts(/task/board|lesson|today),
  首页的「上次来」「班里在做」进 routes/home.ts;共用的取题口径挪进 services/lesson.ts
- 契约 classroom.ts 同样拆成 task.ts / home.ts,ClassBoard* 改叫 TaskBoard*

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:53:07 +08:00

114 lines
3.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { TaskBoardStudent } from 'utils/types'
/**
* 任务页的「请假」框:勾今天不在的人。规则在任务页那边(只有今天还没交过的人能算请假、
* 交了题或举手就自动算到,见 board.vue 的 canLeave),这里只管勾。
*/
const props = defineProps<{
/** 能勾的人:今天还没交过的(刚上课时就是全班),按名字排好 */
candidates: TaskBoardStudent[]
/** 打开时已经标着请假的 */
current: string[]
}>()
const emit = defineEmits<{ save: [usernames: string[]] }>()
const show = defineModel<boolean>('show', { required: true })
const selected = ref<string[]>([])
watch(show, (open) => {
if (open) selected.value = [...props.current]
})
/**
* 今天一次都没上过线。机房有两人共用一台电脑的,同桌只有一个登着,所以「谁没来」
* 看今天上没上过线,不看此刻在不在线(见契约 seenAt)
*/
const unseen = (student: TaskBoardStudent | undefined) => !!student && !student.seenAt
/** 今天没上过线的分在前面一堆:请假的人基本在这里面,剩下的是和同桌共用电脑的 */
const sections = computed(() =>
[
{ label: '今天没上线', students: props.candidates.filter(unseen) },
{
label: '今天上过线',
students: props.candidates.filter((student) => !unseen(student)),
},
].filter((section) => section.students.length),
)
function selectAll(list: TaskBoardStudent[]) {
selected.value = [...new Set([...selected.value, ...list.map((student) => student.username)])]
}
function save() {
emit('save', selected.value)
show.value = false
}
</script>
<template>
<n-modal
v-model:show="show"
preset="card"
:title="candidates.length ? `今天还没交过的 ${candidates.length} 人里,谁请假了?` : '今天全班都交过题了'"
style="width: 640px; max-width: calc(100vw - 32px)"
>
<n-checkbox-group v-if="candidates.length" v-model:value="selected">
<div v-for="section in sections" :key="section.label" class="leave-section">
<div v-if="sections.length > 1 || unseen(section.students[0])" class="leave-head">
{{ section.label }} {{ section.students.length }} 人
<n-button
v-if="unseen(section.students[0])"
text
type="primary"
:disabled="section.students.every((student) => selected.includes(student.username))"
@click="selectAll(section.students)"
>
全勾上
</n-button>
</div>
<div class="leave-grid">
<n-checkbox v-for="student in section.students" :key="student.userId" :value="student.username" :label="student.username" class="leave-option" />
</div>
</div>
</n-checkbox-group>
<n-text v-else depth="3">请假的人一定是没交过题的,现在没有人可以标。</n-text>
<template #footer>
<n-flex align="center" :wrap="false">
<n-text depth="3" class="meta" style="flex-grow: 1"> 已选 {{ selected.length }} 人 · 只记在这台电脑上,今天有效 </n-text>
<n-button :disabled="!selected.length" @click="selected = []">全部取消</n-button>
<n-button @click="show = false">取消</n-button>
<n-button type="primary" @click="save">确定</n-button>
</n-flex>
</template>
</n-modal>
</template>
<style scoped>
.leave-section + .leave-section {
margin-top: 18px;
}
.leave-head {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 10px;
font-weight: 600;
}
.leave-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 10px 8px;
}
.leave-option {
font-size: 15px;
}
.meta {
font-size: var(--oj-fs-sec);
}
</style>