410 lines
9.0 KiB
Vue
410 lines
9.0 KiB
Vue
|
|
<template>
|
|||
|
|
<BasicLayout>
|
|||
|
|
<!-- 课程信息卡片 -->
|
|||
|
|
<view class="course-card mx-15 my-15 bg-white white-bg-color r-md p-15">
|
|||
|
|
<view class="flex-row items-center mb-15">
|
|||
|
|
<view class="course-icon flex-center mr-10">
|
|||
|
|
<u-icon name="calendar" color="#4080ff" size="20"></u-icon>
|
|||
|
|
</view>
|
|||
|
|
<text class="font-16 font-bold">机器人创客</text>
|
|||
|
|
<text class="font-14 cor-999 ml-auto">2024-12-25 (周三)</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 考勤统计 -->
|
|||
|
|
<view class="attendance-stats flex-row">
|
|||
|
|
<view class="stat-item flex-col items-center">
|
|||
|
|
<text class="font-18 font-bold">18</text>
|
|||
|
|
<text class="font-12 cor-666 mt-3">应到</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="stat-item flex-col items-center">
|
|||
|
|
<text class="font-18 font-bold cor-primary">18</text>
|
|||
|
|
<text class="font-12 cor-666 mt-3">实到</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="stat-item flex-col items-center">
|
|||
|
|
<text class="font-18 font-bold cor-warning">0</text>
|
|||
|
|
<text class="font-12 cor-666 mt-3">请假</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="stat-item flex-col items-center">
|
|||
|
|
<text class="font-18 font-bold cor-danger">0</text>
|
|||
|
|
<text class="font-12 cor-666 mt-3">缺勤</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="stat-circle flex-col flex-center ml-auto">
|
|||
|
|
<text class="font-20 font-bold">18</text>
|
|||
|
|
<text class="font-10 cor-666">总人数</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 学生列表 -->
|
|||
|
|
<view class="student-list mb-30 white-bg-color">
|
|||
|
|
<view class="student-grid">
|
|||
|
|
<view
|
|||
|
|
v-for="(student, index) in studentList"
|
|||
|
|
:key="index"
|
|||
|
|
class="student-item bg-white r-md p-12"
|
|||
|
|
>
|
|||
|
|
<view class="flex-row items-center">
|
|||
|
|
<view class="avatar-container mr-8">
|
|||
|
|
<image
|
|||
|
|
class="student-avatar"
|
|||
|
|
:src="student.avatar || '/static/images/default-avatar.png'"
|
|||
|
|
mode="aspectFill"
|
|||
|
|
></image>
|
|||
|
|
</view>
|
|||
|
|
<view class="flex-1 overflow-hidden">
|
|||
|
|
<view class="flex-row items-center mb-3">
|
|||
|
|
<text class="font-14 font-bold mr-5 text-ellipsis">{{
|
|||
|
|
student.name
|
|||
|
|
}}
|
|||
|
|
</text>
|
|||
|
|
<view
|
|||
|
|
class="status-tag"
|
|||
|
|
:class="getStatusClass(student.status)"
|
|||
|
|
@click="openStatusPicker(student)"
|
|||
|
|
>
|
|||
|
|
{{ student.status }}
|
|||
|
|
<u-icon name="arrow-down" size="10"></u-icon>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<text class="font-12 cor-666">{{ student.className }}</text>
|
|||
|
|
<view class="contact-parent mt-8 flex-center">
|
|||
|
|
<text class="font-12 cor-primary">联系家长</text>
|
|||
|
|
<u-icon
|
|||
|
|
name="phone"
|
|||
|
|
color="#4080ff"
|
|||
|
|
size="14"
|
|||
|
|
class="ml-2"
|
|||
|
|
></u-icon>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 状态选择弹窗 -->
|
|||
|
|
<u-picker
|
|||
|
|
:show="statusPickerVisible"
|
|||
|
|
:columns="[statusOptions]"
|
|||
|
|
@confirm="confirmStatus"
|
|||
|
|
@cancel="statusPickerVisible = false"
|
|||
|
|
></u-picker>
|
|||
|
|
|
|||
|
|
<template #bottom>
|
|||
|
|
<view class="submit-btn-wrap py-10 px-20 bg-white">
|
|||
|
|
<button class="submit-btn" @click="submit">提交</button>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
</BasicLayout>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import {onMounted, ref} from "vue";
|
|||
|
|
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
|||
|
|
import {dicApi} from "@/api/system/dic";
|
|||
|
|
|
|||
|
|
// 模拟学生数据
|
|||
|
|
const studentList = ref([
|
|||
|
|
{
|
|||
|
|
id: 1,
|
|||
|
|
name: "伍添昊",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar1.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 2,
|
|||
|
|
name: "时振宇",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar2.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 3,
|
|||
|
|
name: "程子璇",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar3.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 4,
|
|||
|
|
name: "柘延兴",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar4.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 5,
|
|||
|
|
name: "张茜溪",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar5.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 6,
|
|||
|
|
name: "孟嘉乐",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar6.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 7,
|
|||
|
|
name: "韩汝鑫",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar7.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 8,
|
|||
|
|
name: "曹佳毅",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar8.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 9,
|
|||
|
|
name: "郎甜",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar9.png",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 10,
|
|||
|
|
name: "萧文懿",
|
|||
|
|
status: "正常",
|
|||
|
|
className: "三年八班",
|
|||
|
|
avatar: "/static/images/avatar10.png",
|
|||
|
|
},
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// 状态选择相关
|
|||
|
|
const statusPickerVisible = ref(false);
|
|||
|
|
const statusOptions = ref<Array<{ text: string, value: string }>>([]);
|
|||
|
|
const currentStudent = ref<any>(null);
|
|||
|
|
|
|||
|
|
// 获取状态对应的样式类
|
|||
|
|
const getStatusClass = (status: string) => {
|
|||
|
|
switch (status) {
|
|||
|
|
case "正常":
|
|||
|
|
return "status-normal";
|
|||
|
|
case "请假":
|
|||
|
|
return "status-leave";
|
|||
|
|
case "缺勤":
|
|||
|
|
return "status-absent";
|
|||
|
|
default:
|
|||
|
|
return "status-normal";
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取学生状态选项
|
|||
|
|
const fetchStatusOptions = async () => {
|
|||
|
|
try {
|
|||
|
|
// 假设字典表中出勤状态的pid为810984651,根据实际情况修改
|
|||
|
|
const res = await dicApi({pid: 810984651});
|
|||
|
|
if (res && res.result) {
|
|||
|
|
statusOptions.value = res.result.map((item: any) => {
|
|||
|
|
return {
|
|||
|
|
text: item.dictionaryValue,
|
|||
|
|
value: item.dictionaryCode
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("获取状态选项失败", error);
|
|||
|
|
// 使用默认状态
|
|||
|
|
statusOptions.value = [
|
|||
|
|
{text: "正常", value: "1"},
|
|||
|
|
{text: "请假", value: "2"},
|
|||
|
|
{text: "缺勤", value: "3"}
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 打开状态选择器
|
|||
|
|
const openStatusPicker = (student: any) => {
|
|||
|
|
currentStudent.value = student;
|
|||
|
|
statusPickerVisible.value = true;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 确认选择状态
|
|||
|
|
const confirmStatus = (e: any) => {
|
|||
|
|
if (currentStudent.value && e.value && e.value[0]) {
|
|||
|
|
const selectedStatus = statusOptions.value.find(
|
|||
|
|
(option: any) => option.value === e.value[0]
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (selectedStatus) {
|
|||
|
|
// 更新当前学生状态
|
|||
|
|
currentStudent.value.status = selectedStatus.text;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
statusPickerVisible.value = false;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 导航相关方法
|
|||
|
|
const navigateBack = () => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const toRollCallRecord = () => {
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: "/pages/base/groupTeaching/rollCallRecord",
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 联系家长
|
|||
|
|
const contactParent = (student: any) => {
|
|||
|
|
console.log("联系家长", student.name);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 提交数据
|
|||
|
|
const submit = () => {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "提交成功",
|
|||
|
|
icon: "success",
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 页面加载时获取状态选项
|
|||
|
|
onMounted(() => {
|
|||
|
|
fetchStatusOptions();
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.container {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header {
|
|||
|
|
height: 44px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.course-card {
|
|||
|
|
border-radius: 8px;
|
|||
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.course-icon {
|
|||
|
|
width: 30px;
|
|||
|
|
height: 30px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
background-color: rgba(64, 128, 255, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.attendance-stats {
|
|||
|
|
padding: 10px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-circle {
|
|||
|
|
width: 60px;
|
|||
|
|
height: 60px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.student-list {
|
|||
|
|
padding: 0 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.student-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(2, 1fr);
|
|||
|
|
gap: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.student-item {
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.avatar-container {
|
|||
|
|
width: 46px;
|
|||
|
|
height: 46px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
padding: 3px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.student-avatar {
|
|||
|
|
width: 40px;
|
|||
|
|
height: 40px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-tag {
|
|||
|
|
font-size: 10px;
|
|||
|
|
padding: 1px 5px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-normal {
|
|||
|
|
color: #4080ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-leave {
|
|||
|
|
color: #ff9900;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-absent {
|
|||
|
|
color: #ff4d4f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.text-ellipsis {
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
max-width: 60px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.contact-parent {
|
|||
|
|
padding: 3px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #4080ff;
|
|||
|
|
display: inline-flex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.submit-btn {
|
|||
|
|
background-color: #4080ff;
|
|||
|
|
color: #fff;
|
|||
|
|
height: 44px;
|
|||
|
|
border-radius: 22px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-primary {
|
|||
|
|
color: #4080ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-warning {
|
|||
|
|
color: #ff9900;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-danger {
|
|||
|
|
color: #ff4d4f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-666 {
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-999 {
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
</style>
|