Files
huike-front/src/pages/daka/index.vue
T
weli 766f511e11 fix: SUPERVISOR 角色打卡查询未设置 teacher_id,添加 debug 日志
- daka store query() SUPERVISOR 与 TEACHER 同等处理
- 添加 console.debug 日志便于 vConsole 排查

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 10:33:06 +08:00

290 lines
8.3 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.
<template>
<div class="main">
<div class="search-bar">
<span class="date-index">
<span @click="state.showCalendar = !state.showCalendar">
<i class="fas" :class="{'fa-caret-down': !state.showCalendar, 'fa-caret-up': state.showCalendar}"></i>
{{ state.start_date || '打卡起始' }}
</span>
<i v-if="state.start_date" @click="changeDate()" class="fa-solid fa-xmark"></i>
</span>
<van-search shape="round" v-model.trim="state.keyword" />
</div>
<div class="list">
<van-pull-refresh v-model="state.loading" @refresh="onRefresh">
<van-list v-model:loading="state.loading"
:finished="state.finished" finished-text="没有更多数据了"
@load="onPage">
<div class="item" v-for="item in dataset" :key="item.id"
@click="$router.push(`/daka/detail?id=${item.id}`)">
<div class="name">
{{ item.name }}
</div>
<div class="title" v-if="item.group_name">
<span class="label">学生小组:</span> {{ item.group_name }}
</div>
<div class="title" v-if="item.clazz">
<span class="label">课程名称:</span> {{ item.clazz.clazz_name }}
</div>
<div class="line">
<label>打卡时限</label><span :class="{expired: expired(item)}">{{ weekShow(item) }}</span><van-tag :show="item.is_yanqi" style="margin-left: 5px" type="warning">已延期</van-tag>
</div>
<div class="line">
<label>学生</label>
<div class="course-tags">
<van-tag type="primary" text-color="#fff" v-for="user in item.students?.val?.users.vals">{{ user.real_name }}</van-tag>
</div>
</div>
<div class="line">
<label>创建老师</label><span>{{ item.teacher_name }}</span>
</div>
<div class="line">
<label>课程</label>
<div class="course-tags">
<van-tag type="primary" text-color="#fff"
v-for="course in item.course_sections">{{ course.course_name }} ({{course.section_name}})</van-tag>
</div>
</div>
<div class="line">
<label>批阅老师</label>
<div class="course-tags">
<van-tag type="primary" text-color="#fff">{{ item.teacher_name }}</van-tag>
<van-tag type="primary" text-color="#fff"
v-for="teacher in item.teachers?.vals">{{ teacher.teacher_name }}</van-tag>
</div>
</div>
<div class="line">
<label>备注</label><span>{{ item.desc }}</span>
</div>
<template v-if="item.lianxi_count">
<div class="divider van-hairline--top"></div>
<div class="line">
<label class="count-label">
<span class="text-primary">{{item.lianxi_count}}</span>个练习<span :class="{'text-danger': item.not_piyue_count, 'text-success': !item.not_piyue_count}">{{item.not_piyue_count || 0}}</span>个未批阅
</label>
</div>
</template>
</div>
</van-list>
</van-pull-refresh>
</div>
<van-calendar v-model:show="state.showCalendar" @confirm="changeDate" :min-date="state.minDate" color="#1989fa" />
<van-icon v-if="is_teacher" name="add" color="#00AA4F" class="btn-add" @click="$router.push('/daka/add')" />
</div>
</template>
<script lang="ts">
import {computed, defineComponent, onMounted, reactive} from 'vue'
import {Calendar, Cell, Icon, List, PullRefresh, Search, Tag} from 'vant'
import {formatDate} from "~/utils";
import useDaka from "~/store/daka";
import useUser from '~/store/user';
import {Daka, HtyBaseRoles} from "~/types";
import dayjs from "dayjs";
export default defineComponent({
name: "daka",
components: {
[List.name]: List,
[PullRefresh.name]: PullRefresh,
[Search.name]: Search,
[Calendar.name]: Calendar,
[Cell.name]: Cell,
[Icon.name]: Icon,
[Tag.name]: Tag
},
setup() {
const usingUser = useUser();
const {store, query, reset} = useDaka();
console.debug('[daka] setup, currentRole:', usingUser.store.currentRole);
const state = reactive({
keyword: '',
loading: false, finished: true,
start_date: '',
page: 1,
page_size: 10,
showCalendar: false,
minDate: new Date(2021, 0, 1)
});
// get cached query params
let cached = store.query_cache;
if (cached) {
state.start_date = (cached.start_date || '').split(" ")[0];
}
const search = async (params: any) => {
state.finished = false;
state.loading = true;
let { start_date } = state;
if (start_date) {
params.start_date = start_date + " 00:00:00";
}
await query(params);
if (store.pages <= state.page) {
state.finished = true;
}
state.loading = false;
}
const onRefresh = async () => {
await search({page: 1, page_size: state.page_size});
}
const onPage = async () => {
if (state.finished) return;
let { page, page_size } = state;
state.page = page + 1;
await search({page: page + 1, page_size})
}
const changeDate = (date?: Date) => {
state.start_date = date ? formatDate(date) : '';
state.showCalendar = false;
onRefresh();
}
const dataset = computed(() => {
let list = store.list;
if (state.keyword) {
list = list.filter((x: Daka) => x.course_sections.some(qs => qs.course_name?.includes(state.keyword)))
}
return list;
})
const weekShow = (item: Daka) => [formatDate(item.start_date), formatDate(item.end_date)].join(' ~ ');
onMounted(() => {
reset();
onRefresh();
});
const expired = (item: Daka) => dayjs(item.end_date).valueOf() < dayjs().startOf('date').valueOf()
const is_teacher = computed(() => usingUser.store.currentRole === HtyBaseRoles.TEACHER);
return {
state, dataset, changeDate, onRefresh, weekShow, expired, onPage, is_teacher
}
}
})
</script>
<style scoped lang="less">
.main {
width: 100%;
height: calc(100% - 1rem);
overflow: hidden;
position: relative;
.search-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF;
.date-index {
display: inline-flex;
align-items: center;
justify-content: space-between;
padding-left: 0.24rem;
width: 2.4rem;
font-weight: bolder;
font-size: 0.24rem;
white-space: nowrap;
color: #0074D9;
}
:deep(.van-search) {
width: calc(100% - 2rem);
}
}
.list {
width: calc(100% - 0.64rem);
height: calc(100% - 0.54rem - 0.64rem);
overflow: auto;
margin: 0.32rem;
&::-webkit-scrollbar {
display: none;
}
.item {
width: 100%;
background: #FFFFFF;
box-shadow: 0 0.02rem 0.08rem 0 rgba(0, 0, 0, 0.03);
border-radius: 0.1rem;
margin-bottom: 0.3rem;
padding: 0.24rem 0.32rem;
.name {
color: #333333;
font-size: 0.34rem;
font-weight: bolder;
margin-bottom: 0.24rem;
}
.title {
font-size: 0.28rem;
font-weight: bolder;
color: #495057;
.label {
margin-right: 0.2rem;
font-size: 0.28rem;
color: #9e9e9e;
}
}
.line {
margin-top: 0.08rem;
display: flex;
align-items: flex-start;
font-size: 0.28rem;
label {
color: #666666;
white-space: nowrap;
&.count-label {
span {
margin: 0;
padding: 0 2px;
font-weight: bolder;
}
}
}
.course-tags {
display: inline-block;
}
span:not(:deep(.van-tag)) {
color: #333333;
&.expired {
color: #ff8917;
}
&:not(:last-child) {
margin-right: 6px;
margin-bottom: 3px;
}
}
}
}
}
.btn-add {
position: absolute;
right: 0.3rem;
bottom: 1rem;
font-size: 0.9rem;
}
}
</style>