766f511e11
- daka store query() SUPERVISOR 与 TEACHER 同等处理 - 添加 console.debug 日志便于 vConsole 排查 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
290 lines
8.3 KiB
Vue
290 lines
8.3 KiB
Vue
<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>
|