taskHomePage.vue 10.3 KB
<!--
 * @Date: 2025-11-19 21:00:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-11-20 11:31:17
 * @FilePath: /mlaj/src/views/teacher/taskHomePage.vue
 * @Description: 教师端作业主页(头部介绍、统计、日历与学生完成情况;数据Mock)
-->
<template>
    <div class="TaskHomePage">
        <!-- 头部卡片:名称、介绍、细项(参考图片1结构) -->
        <div class="headCard bg-white rounded-lg shadow px-4 py-4">
            <div class="title text-2xl font-bold text-gray-900 mb-2">{{ task_title }}</div>
            <div class="intro text-base text-gray-700 leading-relaxed mb-3">{{ task_intro }}</div>
            <!-- 三图展示(可选),使用CDN示例地址并加压缩参数 -->
            <!-- <div class="images grid grid-cols-3 gap-3 mb-3">
                <img v-for="(img, idx) in task_images" :key="idx" :src="img"
                    class="rounded-md object-cover w-full h-24" />
            </div> -->
            <div class="details text-sm text-gray-600">
                <div class="detailItem">周期:{{ task_details.cycle }}</div>
                <div class="detailItem">频次:{{ task_details.frequency }}</div>
                <div class="detailItem">时间段:{{ task_details.time_range }}</div>
                <div class="detailItem">附件类型:{{ task_details.attachment_type }}</div>
            </div>
        </div>

        <!-- 统计数据(参考 myClassPage.vue 出勤率/任务完成) -->
        <div class="statsCard bg-white rounded-lg shadow px-4 py-4 mt-4">
            <van-row gutter="16">
                <!-- 出勤率 -->
                <van-col span="12">
                    <div class="text-center">
                        <div class="relative w-16 h-16 mx-auto mb-2">
                            <van-circle v-model:current-rate="checkin_count" :rate="checkin_count" :text="checkin_text"
                                stroke-width="100" color="#10b981" size="64" layer-color="#eee" />
                        </div>
                        <div class="text-sm text-gray-600">出勤率</div>
                    </div>
                </van-col>
                <!-- 任务完成 -->
                <van-col span="12">
                    <div class="text-center">
                        <div class="relative w-16 h-16 mx-auto mb-2">
                            <van-circle v-model:current-rate="upload_count" :rate="upload_count" :text="upload_text"
                                stroke-width="100" color="#3b82f6" size="64" layer-color="#eee" />
                        </div>
                        <div class="text-sm text-gray-600">任务完成</div>
                    </div>
                </van-col>
            </van-row>
        </div>

        <!-- 日历:选择日期后展示该日期的学生完成情况 -->
        <div class="calendarCard bg-white rounded-lg shadow px-4 py-4 mt-4">
            <div class="text-base font-semibold text-gray-800 mb-2">选择日期查看完成情况</div>
            <TaskCalendar v-model="selected_date" @select="on_date_select" />
        </div>

        <!-- 学生完成情况(参考图片2样式) -->
        <div class="studentsCard bg-white rounded-lg shadow px-4 py-4 mt-4">
            <div class="flex items-center justify-between mb-3">
                <div class="text-base font-semibold text-gray-800">完成情况({{ completed_count }}/{{ students.length }})
                </div>
                <div class="text-xs text-gray-500">当前日期:{{ current_date_text }}</div>
            </div>
            <div class="grid grid-cols-5 gap-3 StudentsGrid">
                <div v-for="(stu, idx) in students_status" :key="stu.id"
                    class="studentItem relative rounded-md h-16 flex flex-col items-center justify-center text-center border overflow-hidden"
                    :class="stu.completed ? 'bg-white border-green-500 text-green-600' : 'bg-gray-100 border-gray-300 text-gray-500'"
                    @click="go_student_record(stu)">
                    <div class="text-sm font-semibold">{{ idx + 1 }}</div>
                    <div class="text-sm mt-1">{{ stu.name }}</div>
                    <img v-if="stu.completed" :src="checkCorner" alt="checked" class="cornerIcon" />
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import TaskCalendar from '@/components/ui/TaskCalendar.vue'
import checkCorner from '@/assets/images/dui.png'

const $route = useRoute()
const $router = useRouter()
useTitle('作业主页')

//
// Mock:作业基础信息
//
const task_id = $route.params.id || '0'
const task_title = ref('组长每日共修打卡内容')
const task_intro = ref('组长与副组长以上每日语音打卡内容:《义工宣言》《万事如意祈请文》《吉祥圆满感恩文》')
const task_images = ref([
    'https://cdn.ipadbiz.cn/mlaj/demo-task-1.png?imageMogr2/thumbnail/200x/strip/quality/70',
    'https://cdn.ipadbiz.cn/mlaj/demo-task-2.png?imageMogr2/thumbnail/200x/strip/quality/70',
    'https://cdn.ipadbiz.cn/mlaj/demo-task-3.png?imageMogr2/thumbnail/200x/strip/quality/70'
])
const task_details = ref({
    cycle: '每天',
    frequency: '每日一次',
    time_range: '00:00 ~ 23:59',
    attachment_type: '语音/文本'
})

//
// Mock:统计数据
//
const checkin_count = ref(56)
const upload_count = ref(62)
const checkin_text = computed(() => `${checkin_count.value}%`)
const upload_text = computed(() => `${upload_count.value}%`)

//
// Mock:学生与完成记录
// 注:每个学生给出若干已完成的日期字符串(YYYY-MM-DD)
//
const today = new Date()
const selected_date = ref(format_date(today))
const students = ref([
    { id: '1', name: '王菲', completed_dates: ['2025-11-18', selected_date.value] },
    { id: '2', name: '朱明献', completed_dates: ['2025-11-18'] },
    { id: '3', name: '陈小云', completed_dates: [] },
    { id: '4', name: '冯新虎', completed_dates: [selected_date.value] },
    { id: '5', name: '罗睿', completed_dates: ['2025-11-17', '2025-11-18'] },
    { id: '6', name: '吴绍婷', completed_dates: [] },
    { id: '7', name: '焦淑敏', completed_dates: [selected_date.value] },
    { id: '8', name: '李言斐', completed_dates: [] },
    { id: '9', name: '陈正统', completed_dates: [] },
    { id: '10', name: '杨子娟', completed_dates: [] },
    { id: '11', name: '方萍', completed_dates: [selected_date.value] },
    { id: '12', name: '冯静', completed_dates: [selected_date.value] },
    { id: '13', name: '尤瑞', completed_dates: [] },
    { id: '14', name: '鲁镇伟', completed_dates: [] },
    { id: '15', name: '黄润', completed_dates: [selected_date.value] },
    { id: '16', name: '王亚琼', completed_dates: [selected_date.value] },
    { id: '17', name: '高晓云', completed_dates: [selected_date.value] },
    { id: '18', name: '张朗', completed_dates: [] },
    { id: '19', name: '姚娟', completed_dates: [selected_date.value] },
    { id: '20', name: '李凯', completed_dates: [selected_date.value] },
    { id: '21', name: '李鑫', completed_dates: [] },
    { id: '22', name: '礼忠斌', completed_dates: [] },
    { id: '23', name: '谭小梅', completed_dates: [selected_date.value] },
    { id: '24', name: '赵红梅', completed_dates: [] }
])

/**
 * 将日期对象格式化为 YYYY-MM-DD
 * @param {Date} d - 日期对象
 * @returns {string} 格式化后的日期字符串
 * 注释:补零并返回标准格式,便于与完成记录匹配。
 */
function format_date(d) {
    const y = d.getFullYear()
    const m = String(d.getMonth() + 1).padStart(2, '0')
    const day = String(d.getDate()).padStart(2, '0')
    return `${y}-${m}-${day}`
}

/**
 * 处理日历选择事件
 * @param {any} date - 选中的日期对象
 * @returns {void}
 * 注释:更新选中日期,并联动学生完成情况。
 */
function on_date_select(val) {
    // 自定义日历组件返回 YYYY-MM-DD 字符串
    selected_date.value = val
}

/**
 * 计算某日期下学生完成情况列表
 * @returns {{id:string,name:string,completed:boolean}[]} 学生状态列表
 * 注释:根据 selected_date 在每个学生的 completed_dates 中判断是否完成。
 */
const students_status = computed(() => {
    return students.value.map(stu => ({
        id: stu.id,
        name: stu.name,
        completed: stu.completed_dates.includes(selected_date.value)
    }))
})

/**
 * 完成人数统计文案
 * @returns {number} 完成人数
 */
const completed_count = computed(() => students_status.value.filter(s => s.completed).length)

/**
 * 当前日期的展示文本
 * @returns {string} 文本
 */
const current_date_text = computed(() => selected_date.value)

/**
 * 跳转至学员作业记录页面(固定ID的示例页面)
 * @param {{id:string,name:string,completed:boolean}} stu - 学员对象
 * @returns {void}
 */
function go_student_record(stu) {
    // 跳转到固定ID的作业记录页面,当前版本不使用传入ID
    $router.push({ name: 'StudentRecord' })
}
</script>

<style lang="less" scoped>
.TaskHomePage {
    min-height: 100vh;
    background: linear-gradient(to bottom right, #f0fdf4, #f0fdfa, #eff6ff);
    padding: 1rem;
    padding-bottom: 6rem;

    .headCard {
        .title {
            line-height: 1.3;
        }

        .intro {
            color: #374151;
        }

        .details {
            border-left: 0.15rem solid #10b981;
            padding-left: 0.75rem;
            .detailItem {
                margin-bottom: 0.25rem;
            }
        }
    }

    .studentsCard {
        // 兜底:强制学生列表为5列栅格,避免一行仅1个的问题
        .StudentsGrid {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 0.75rem; // 等同于 tailwind 的 gap-3
        }
        .grid>div {
            transition: all 0.2s ease-in-out;
        }
        .studentItem {
            min-height: 4rem;
            // 卡片圆角裁切,配合右上角图片效果
            overflow: hidden;
        }
        .cornerIcon {
            // 右上角图片样式(对勾角标),贴边显示
            position: absolute;
            top: -1px;
            right: -1px;
            width: 18px;
            height: 18px;
        }
    }
}
</style>