| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- <template>
- <!--
- 学生卡片组件
- 功能:显示单个学生的信息和考勤状态
- 支持:
- 1. 学生基本信息显示(头像、姓名、编号)
- 2. 考勤状态可视化(出勤、旷课、迟到、早退、请假)
- 3. 点击切换考勤状态(仅在点名模式下)
- 4. 一寸证件照比例的头像显示
- -->
- <view
- class="student-card"
- @click="handleCardClick"
- >
- <!-- 学生头像 -->
- <view class="avatar-container">
- <image
- class="avatar"
- :src="student.avatar || '/static/images/default-avatar.png'"
- mode="aspectFill"
- @error="handleImageError"
- />
- </view>
-
- <!-- 学生信息 -->
- <view class="student-info">
- <view class="student-code">{{ student.code }}</view>
- <view class="student-name">{{ student.name }}</view>
- </view>
-
- <!-- 底部状态指示线 -->
- <view class="status-line-container">
- <!-- 默认灰色底线 -->
- <view class="status-line-base"></view>
- <!-- 迟到:左半边红线 -->
- <view v-if="student.status === 11" class="status-line-overlay status-late-overlay"></view>
- <!-- 旷课:整条红线 -->
- <view v-if="student.status === 1" class="status-line-overlay status-absent-overlay"></view>
- <!-- 早退:右半边红线 -->
- <view v-if="student.status === 21" class="status-line-overlay status-early-overlay"></view>
- </view>
- <!-- 临时:整个卡片可点击,用于测试 -->
- </view>
- </template>
- <script setup>
- /**
- * 学生卡片组件
- *
- * 主要功能:
- * 1. 显示学生基本信息(头像、姓名、编号)
- * 2. 考勤状态的可视化显示
- * 3. 考勤状态的点击切换功能
- * 4. 支持一寸证件照比例的头像显示
- *
- * 考勤状态说明(底部指示线):
- * - 81: 出勤(灰色底线)
- * - 1: 旷课(整条红线)
- * - 11: 迟到(左半边红线)
- * - 21: 早退(右半边红线)
- * - 0: 请假(灰色底线)
- */
- import { defineProps, defineEmits } from 'vue'
- const props = defineProps({
- student: {
- type: Object,
- required: true,
- default: () => ({
- id: '',
- name: '',
- code: '',
- avatar: '',
- row: 1,
- col: 1,
- status: 81 // 81-出勤, 1-旷课, 11-迟到, 21-早退, 0-请假
- })
- },
- // 是否在点名模式下(影响交互)
- attendanceMode: {
- type: Boolean,
- default: false
- }
- })
- const emit = defineEmits(['click', 'statusChange'])
- // 处理卡片点击
- const handleCardClick = () => {
- console.log('点击了学生卡片:', props.student.name, '当前模式:', props.attendanceMode)
- if (!props.attendanceMode) {
- console.log('不在点名模式,忽略点击')
- return // 只在点名模式下响应点击
- }
- console.log('开始状态切换,当前状态:', props.student.status)
-
- // 状态切换逻辑:81(出勤) → 1(旷课) → 11(迟到) → 21(早退) → 81(出勤)
- let newStatus
- switch (props.student.status) {
- case 81: // 出勤 → 旷课
- newStatus = 1
- break
- case 1: // 旷课 → 迟到
- newStatus = 11
- break
- case 11: // 迟到 → 早退
- newStatus = 21
- break
- case 21: // 早退 → 出勤
- newStatus = 81
- break
- default: // 其他状态 → 出勤
- newStatus = 81
- }
-
- emit('statusChange', {
- studentId: props.student.id,
- oldStatus: props.student.status,
- newStatus: newStatus
- })
-
- emit('click', props.student)
- }
- // 处理头像加载失败
- const handleImageError = () => {
- console.log('头像加载失败:', props.student.name)
- }
- </script>
- <style lang="scss" scoped>
- .student-card {
- width: 200rpx;
- background-color: #fff;
- border: 2rpx solid #E5E5E5;
- border-radius: 8rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
- overflow: hidden;
- transition: all 0.3s ease;
- padding: 20rpx 20rpx 0 20rpx; // 上右下左,底部不加padding因为有状态线
- box-sizing: border-box;
- // 头像容器 - 一寸证件照比例 (2.5:3.5 ≈ 5:7)
- .avatar-container {
- width: 100%; // 占满容器宽度
- height: 224rpx; // 按一寸证件照比例 (160 * 1.4 = 224)
- margin-top: 0;
- border-radius: 8rpx;
- overflow: hidden;
- .avatar {
- width: 100%;
- height: 100%;
- background-color: #F5F5F5;
- }
- }
-
- // 学生信息
- .student-info {
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 20rpx;
- margin-bottom: 20rpx; // 添加底部边距,为状态线留出空间
-
- .student-code {
- font-size: 24rpx;
- color: #666;
- text-align: left;
- }
-
- .student-name {
- font-size: 28rpx;
- color: #333;
- font-weight: bold;
- text-align: right;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- }
-
- // 底部状态指示线样式
- .status-line-container {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 10rpx;
- pointer-events: none;
- }
- // 默认灰色底线
- .status-line-base {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 10rpx;
- background-color: #e0e0e0;
- border-radius: 8rpx;
- }
- // 状态指示线覆盖层
- .status-line-overlay {
- position: absolute;
- bottom: 0;
- height: 10rpx;
- background-color: #ff4444;
- border-radius: 8rpx;
- }
- // 迟到:左半边红线
- .status-late-overlay {
- left: 0;
- width: 50%;
- }
- // 旷课:整条红线
- .status-absent-overlay {
- left: 0;
- width: 100%;
- }
- // 早退:右半边红线
- .status-early-overlay {
- right: 0;
- width: 50%;
- }
-
- // 底部点击条
- .click-bar {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- width: 100%;
- height: 40rpx; // 增加高度,更容易点击
- background-color: rgba(0, 0, 0, 0.05); // 淡灰色背景
- cursor: pointer;
- z-index: 10;
- display: flex;
- align-items: center;
- justify-content: center;
- .click-hint {
- font-size: 20rpx;
- color: #999;
- opacity: 0.7;
- }
- // 点击时的反馈效果
- &:active {
- background-color: rgba(0, 0, 0, 0.1);
- }
- }
- // 状态样式
- &.status-normal {
- opacity: 1;
- }
- &.status-late {
- opacity: 1;
- }
- &.status-early {
- opacity: 1;
- }
- &.status-absent {
- opacity: 1;
- }
- }
- </style>
|