659 lines
24 KiB
Vue
659 lines
24 KiB
Vue
<template>
|
||
<div class="app-container student-center">
|
||
<!-- 头部欢迎栏 -->
|
||
<el-card class="box-card header-card" shadow="never">
|
||
<div class="welcome-bar">
|
||
<div class="title-area">
|
||
<h2>SYFJGYD学员管理系统 · 学员个人培训中心</h2>
|
||
<p class="subtitle">学员专属自服务空间,全流程掌握班级参训状态、排课日程、开班报到凭据与个人考勤成长画像。</p>
|
||
</div>
|
||
<div v-if="studentProfile" class="profile-chip">
|
||
<el-tag effect="dark" type="primary" class="role-tag">参训学员</el-tag>
|
||
<span class="user-name">{{ studentProfile.sName || '参训职工' }}</span>
|
||
<span class="org-name">({{ studentProfile.orgName || '房建公寓段' }})</span>
|
||
</div>
|
||
</div>
|
||
<!-- 403 业务未绑定友好告警 -->
|
||
<el-alert
|
||
v-if="identityErrorMsg"
|
||
:title="identityErrorMsg"
|
||
type="warning"
|
||
show-icon
|
||
:closable="false"
|
||
style="margin-top: 15px;"
|
||
/>
|
||
</el-card>
|
||
|
||
<!-- 主体区域 -->
|
||
<el-row :gutter="20" style="margin-top: 20px;">
|
||
<!-- 左侧:学员个人主档卡片 -->
|
||
<el-col :xs="24" :sm="24" :md="7" :lg="6">
|
||
<el-card class="box-card profile-card" shadow="hover">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-postcard" /> 学员基本档案</span>
|
||
</div>
|
||
<div v-loading="profileLoading">
|
||
<div v-if="studentProfile" class="profile-detail">
|
||
<div class="profile-avatar">
|
||
<el-avatar :size="72" icon="el-icon-user" style="background-color: #409eff;" />
|
||
<div class="name-block">{{ studentProfile.sName }}</div>
|
||
<div class="id-block">学号:{{ studentProfile.sId }}</div>
|
||
</div>
|
||
<el-divider />
|
||
<div class="info-list">
|
||
<div class="info-item">
|
||
<span class="label">性别:</span>
|
||
<span class="val">{{ formatGender(studentProfile.sGender) }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">出生日期:</span>
|
||
<span class="val">{{ studentProfile.sBirth || '-' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">所属单位:</span>
|
||
<span class="val">{{ studentProfile.orgName || '-' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">联系电话:</span>
|
||
<span class="val">{{ studentProfile.sPhone || '-' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">电子邮箱:</span>
|
||
<span class="val">{{ studentProfile.sEmail || '-' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">身份证件:</span>
|
||
<span class="val">{{ studentProfile.idCardMasked || (studentProfile.idCardLast4 ? '****' + studentProfile.idCardLast4 : '-') }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">学员状态:</span>
|
||
<el-tag size="mini" :type="studentProfile.status === 1 ? 'success' : 'danger'">
|
||
{{ studentProfile.status === 1 ? '正常' : '已停用' }}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<el-empty v-else description="暂无关联学员档案" :image-size="80" />
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 累计学习时长卡片 -->
|
||
<el-card class="box-card duration-card" shadow="hover" style="margin-top: 20px;">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-time" /> 累计培训学时</span>
|
||
</div>
|
||
<div v-loading="durationLoading">
|
||
<div v-if="durationList && durationList.length > 0" class="duration-summary">
|
||
<div class="total-badge">
|
||
<div class="num">{{ totalHours }}</div>
|
||
<div class="unit">总完成学时 (小时)</div>
|
||
</div>
|
||
<el-divider style="margin: 12px 0;" />
|
||
<div v-for="(item, idx) in durationList" :key="idx" class="duration-row">
|
||
<span class="course-name">{{ item.courseName || ('课程 ' + (idx + 1)) }}</span>
|
||
<span class="course-hour">{{ item.totalDurationHours || item.duration || 0 }} 学时</span>
|
||
</div>
|
||
</div>
|
||
<el-empty v-else description="暂无累计学时记录" :image-size="70" />
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
|
||
<!-- 右侧:培训各功能区 Tab -->
|
||
<el-col :xs="24" :sm="24" :md="17" :lg="18">
|
||
<el-tabs v-model="activeTab" type="border-card" class="student-tabs">
|
||
<!-- Tab 1: 我的培训班级 -->
|
||
<el-tab-pane label="我的培训" name="enrollments">
|
||
<div class="tab-header">
|
||
<div class="header-desc">
|
||
<i class="el-icon-guide" /> 我参与的培训班级与参训状态
|
||
</div>
|
||
<el-button icon="el-icon-refresh" size="mini" plain @click="loadEnrollments">刷新</el-button>
|
||
</div>
|
||
|
||
<el-table v-loading="enrollmentsLoading" :data="enrollmentList" border stripe style="width: 100%; margin-top: 15px;">
|
||
<el-table-column prop="planId" label="培训班编号" width="130" align="center" />
|
||
<el-table-column prop="planName" label="培训班名称" min-width="180" show-overflow-tooltip />
|
||
<el-table-column prop="period" label="期次" width="100" align="center" />
|
||
<el-table-column prop="startDate" label="开始日期" width="120" align="center" />
|
||
<el-table-column prop="endDate" label="结训日期" width="120" align="center" />
|
||
<el-table-column prop="status" label="参训状态" width="120" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" :type="formatEnrollmentStatusType(scope.row.status)">
|
||
{{ formatEnrollmentStatusText(scope.row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<template slot="empty">
|
||
<el-empty description="当前暂未参与任何培训班级" :image-size="80" />
|
||
</template>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<!-- Tab 2: 我的课表日程 -->
|
||
<el-tab-pane label="我的课表" name="timetable">
|
||
<div class="tab-header">
|
||
<div class="header-desc">
|
||
<i class="el-icon-date" /> 所在班级排课日程明细
|
||
</div>
|
||
<el-button icon="el-icon-refresh" size="mini" plain @click="loadTimetable">刷新</el-button>
|
||
</div>
|
||
|
||
<el-table v-loading="timetableLoading" :data="timetableList" border stripe style="width: 100%; margin-top: 15px;">
|
||
<el-table-column prop="lessonDate" label="上课日期" width="120" align="center" />
|
||
<el-table-column label="上课时间" width="140" align="center">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.startTime || '-' }} ~ {{ scope.row.endTime || '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="planName" label="培训班级" min-width="150" show-overflow-tooltip />
|
||
<el-table-column prop="courseName" label="课程名称" min-width="140" show-overflow-tooltip />
|
||
<el-table-column prop="teacherName" label="授课教师" width="110" align="center" />
|
||
<el-table-column prop="classroomName" label="教室/实训场地" min-width="130" show-overflow-tooltip>
|
||
<template slot-scope="scope">
|
||
{{ scope.row.classroomName || scope.row.siteName || '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<template slot="empty">
|
||
<el-empty description="当前周期内暂无排课日程" :image-size="80" />
|
||
</template>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<!-- Tab 3: 我的开班报到凭据 -->
|
||
<el-tab-pane label="我的报到" name="report">
|
||
<div class="tab-header">
|
||
<div class="header-desc">
|
||
<i class="el-icon-document-checked" /> 开班报到确认凭据与回执信息
|
||
</div>
|
||
<el-button icon="el-icon-refresh" size="mini" plain @click="loadReport">刷新</el-button>
|
||
</div>
|
||
|
||
<div v-loading="reportLoading" style="margin-top: 20px;">
|
||
<div v-if="reportDetail" class="report-receipt-box">
|
||
<el-result icon="success" title="开班报到已确认" sub-title="您已顺利完成培训班级开班报到手续。">
|
||
<template slot="extra">
|
||
<div class="receipt-details">
|
||
<el-descriptions border :column="2">
|
||
<el-descriptions-item label="报到流水ID">{{ reportDetail.reportId || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="培训班编号">{{ reportDetail.planId || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="报到状态">
|
||
<el-tag size="small" type="success">已确认报到</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="报到确认时间">{{ reportDetail.reportTime || reportDetail.createTime || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="报到方式">{{ reportDetail.reportMethod || '电子班牌/终端报到' }}</el-descriptions-item>
|
||
<el-descriptions-item label="确认人员">{{ reportDetail.createBy || '教务管理员' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</template>
|
||
</el-result>
|
||
</div>
|
||
<el-empty v-else description="暂无开班报到确认凭据" :image-size="90" />
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- Tab 4: 我的考勤打卡流水 -->
|
||
<el-tab-pane label="我的考勤" name="attendance">
|
||
<div class="tab-header">
|
||
<div class="header-desc">
|
||
<i class="el-icon-check" /> 课堂考勤打卡记录
|
||
</div>
|
||
<el-button icon="el-icon-refresh" size="mini" plain @click="loadAttendance">刷新</el-button>
|
||
</div>
|
||
|
||
<el-table v-loading="attendanceLoading" :data="attendanceList" border stripe style="width: 100%; margin-top: 15px;">
|
||
<el-table-column prop="lessonId" label="课次编号" width="110" align="center" />
|
||
<el-table-column prop="planName" label="培训班级" min-width="150" show-overflow-tooltip />
|
||
<el-table-column prop="courseName" label="课程名称" min-width="140" show-overflow-tooltip />
|
||
<el-table-column prop="checkinStatus" label="签到状态" width="100" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" :type="formatAttendanceStatusType(scope.row.checkinStatus)">
|
||
{{ formatAttendanceStatusText(scope.row.checkinStatus) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="checkinTime" label="签到打卡时间" min-width="150" align="center" />
|
||
<el-table-column prop="checkoutStatus" label="签退状态" width="100" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" :type="formatAttendanceStatusType(scope.row.checkoutStatus)">
|
||
{{ formatAttendanceStatusText(scope.row.checkoutStatus) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="checkoutTime" label="签退打卡时间" min-width="150" align="center" />
|
||
<template slot="empty">
|
||
<el-empty description="暂无考勤打卡流水记录" :image-size="80" />
|
||
</template>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<!-- Tab 5: 学员客观概况 -->
|
||
<el-tab-pane label="学业与纪律概况" name="portrait">
|
||
<div class="tab-header">
|
||
<div class="header-desc">
|
||
<i class="el-icon-data-analysis" /> 个人培训表现与纪律概况
|
||
</div>
|
||
<el-button icon="el-icon-refresh" size="mini" plain @click="loadPortrait">刷新</el-button>
|
||
</div>
|
||
|
||
<div v-loading="portraitLoading" style="margin-top: 20px;">
|
||
<!-- 考勤与参训客观事实 -->
|
||
<el-row :gutter="16">
|
||
<el-col :xs="24" :sm="12">
|
||
<el-card shadow="never" class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-time" style="color: #67C23A;" /> 考勤打卡记录</span>
|
||
</div>
|
||
<div style="padding: 10px 0;">
|
||
<p><b>正常出勤:</b>{{ attendanceNormalCount }} 次</p>
|
||
<p><b>异常考勤:</b>{{ attendanceAbnormalCount }} 次(迟到/早退/请假/缺勤)</p>
|
||
<p><b>应出勤总课次:</b>{{ timetableList.length }} 节</p>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
|
||
<el-col :xs="24" :sm="12">
|
||
<el-card shadow="never" class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-collection" style="color: #409EFF;" /> 班级参训与开班报到</span>
|
||
</div>
|
||
<div style="padding: 10px 0;">
|
||
<p><b>当前参与班级:</b>{{ enrollmentList.length }} 个</p>
|
||
<p><b>开班报到确认:</b>
|
||
<el-tag size="mini" :type="reportDetail ? 'success' : 'info'">
|
||
{{ reportDetail ? '已完成报到' : '待确认报到' }}
|
||
</el-tag>
|
||
</p>
|
||
<p><b>累计已计学时:</b>{{ totalHours }} 小时</p>
|
||
</div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 成绩与自学状态客观提示 -->
|
||
<el-row :gutter="16" style="margin-top: 16px;">
|
||
<el-col :xs="24" :sm="12">
|
||
<el-card shadow="never" class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-medal" style="color: #E6A23C;" /> 考核成绩状态</span>
|
||
</div>
|
||
<el-result icon="info" title="成绩暂未发布" sub-title="当前班级成绩正在录入中。">
|
||
<template slot="extra">
|
||
<el-tag size="small" type="info">录入中</el-tag>
|
||
</template>
|
||
</el-result>
|
||
</el-card>
|
||
</el-col>
|
||
|
||
<el-col :xs="24" :sm="12">
|
||
<el-card shadow="never" class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span><i class="el-icon-reading" style="color: #67C23A;" /> 自主实训与学习平台</span>
|
||
</div>
|
||
<el-result icon="info" title="暂无自主学习记录" sub-title="暂无外部自主学习记录。">
|
||
<template slot="extra">
|
||
<el-tag size="small" type="info">暂无记录</el-tag>
|
||
</template>
|
||
</el-result>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import * as echarts from 'echarts'
|
||
import {
|
||
getStudentProfile,
|
||
getMyEnrollments,
|
||
getMyTimetable,
|
||
getMyReport,
|
||
getMyAttendance,
|
||
getMyDuration,
|
||
getMyPortrait
|
||
} from '@/api/train/meStudent'
|
||
|
||
export default {
|
||
name: 'StudentCenter',
|
||
data() {
|
||
return {
|
||
activeTab: 'enrollments',
|
||
profileLoading: false,
|
||
enrollmentsLoading: false,
|
||
timetableLoading: false,
|
||
reportLoading: false,
|
||
attendanceLoading: false,
|
||
durationLoading: false,
|
||
portraitLoading: false,
|
||
|
||
studentProfile: null,
|
||
identityErrorMsg: '',
|
||
|
||
enrollmentList: [],
|
||
timetableList: [],
|
||
reportDetail: null,
|
||
attendanceList: [],
|
||
durationList: [],
|
||
portraitData: null,
|
||
hasPortraitData: false,
|
||
chartInstance: null
|
||
}
|
||
},
|
||
computed: {
|
||
totalHours() {
|
||
if (!this.durationList || this.durationList.length === 0) return 0
|
||
return this.durationList.reduce((acc, cur) => acc + Number(cur.totalDurationHours || cur.duration || 0), 0)
|
||
},
|
||
attendanceNormalCount() {
|
||
if (!this.attendanceList || this.attendanceList.length === 0) return 0
|
||
return this.attendanceList.filter(i => i.checkinStatus === 1).length
|
||
},
|
||
attendanceAbnormalCount() {
|
||
if (!this.attendanceList || this.attendanceList.length === 0) return 0
|
||
return this.attendanceList.filter(i => i.checkinStatus != null && i.checkinStatus !== 1).length
|
||
}
|
||
},
|
||
created() {
|
||
this.initStudentData()
|
||
},
|
||
mounted() {
|
||
window.addEventListener('resize', this.handleResize)
|
||
},
|
||
beforeDestroy() {
|
||
window.removeEventListener('resize', this.handleResize)
|
||
if (this.chartInstance) {
|
||
this.chartInstance.dispose()
|
||
this.chartInstance = null
|
||
}
|
||
},
|
||
methods: {
|
||
initStudentData() {
|
||
this.loadProfile()
|
||
},
|
||
loadProfile() {
|
||
this.profileLoading = true
|
||
getStudentProfile()
|
||
.then(res => {
|
||
if (res.code === 200) {
|
||
this.studentProfile = res.data
|
||
this.identityErrorMsg = ''
|
||
// 学员身份校验通过后,再并发加载学业与选课各Tab数据
|
||
this.loadEnrollments()
|
||
this.loadTimetable()
|
||
this.loadReport()
|
||
this.loadAttendance()
|
||
this.loadDuration()
|
||
this.loadPortrait()
|
||
} else if (res.code === 403) {
|
||
this.identityErrorMsg = res.msg || '当前登录账号未绑定任何学员档案,请联系管理员核对分配。'
|
||
}
|
||
this.profileLoading = false
|
||
})
|
||
.catch(err => {
|
||
this.profileLoading = false
|
||
this.identityErrorMsg = (err && err.message && !err.message.includes('error')) ? err.message : '当前登录账号未绑定任何学员业务档案,禁止访问学员个人培训中心。请联系管理员分配绑定。'
|
||
})
|
||
},
|
||
loadEnrollments() {
|
||
this.enrollmentsLoading = true
|
||
getMyEnrollments()
|
||
.then(res => {
|
||
this.enrollmentList = res.rows || res.data || []
|
||
this.enrollmentsLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.enrollmentList = []
|
||
this.enrollmentsLoading = false
|
||
})
|
||
},
|
||
loadTimetable() {
|
||
this.timetableLoading = true
|
||
getMyTimetable()
|
||
.then(res => {
|
||
this.timetableList = res.data || res.rows || []
|
||
this.timetableLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.timetableList = []
|
||
this.timetableLoading = false
|
||
})
|
||
},
|
||
loadReport() {
|
||
this.reportLoading = true
|
||
getMyReport()
|
||
.then(res => {
|
||
this.reportDetail = res.data
|
||
this.reportLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.reportDetail = null
|
||
this.reportLoading = false
|
||
})
|
||
},
|
||
loadAttendance() {
|
||
this.attendanceLoading = true
|
||
getMyAttendance()
|
||
.then(res => {
|
||
this.attendanceList = res.rows || res.data || []
|
||
this.attendanceLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.attendanceList = []
|
||
this.attendanceLoading = false
|
||
})
|
||
},
|
||
loadDuration() {
|
||
this.durationLoading = true
|
||
getMyDuration()
|
||
.then(res => {
|
||
this.durationList = res.data || res.rows || []
|
||
this.durationLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.durationList = []
|
||
this.durationLoading = false
|
||
})
|
||
},
|
||
loadPortrait() {
|
||
this.portraitLoading = true
|
||
getMyPortrait()
|
||
.then(res => {
|
||
this.portraitData = res.data
|
||
this.portraitLoading = false
|
||
})
|
||
.catch(() => {
|
||
this.portraitData = null
|
||
this.portraitLoading = false
|
||
})
|
||
},
|
||
handleResize() {
|
||
// 预留扩展
|
||
},
|
||
formatEnrollmentStatusText(status) {
|
||
const map = { 0: '待报到', 1: '已报到', 2: '请假', 3: '退训', 4: '已完成' }
|
||
return map[status] || '未知'
|
||
},
|
||
formatEnrollmentStatusType(status) {
|
||
const map = { 0: 'warning', 1: 'success', 2: 'info', 3: 'danger', 4: '' }
|
||
return map[status] || ''
|
||
},
|
||
formatAttendanceStatusText(status) {
|
||
// 状态严格使用:0 缺勤, 1 正常, 2 迟到, 3 早退, 4 请假。严禁将 1 显示为“已签到”!
|
||
const map = { 0: '缺勤', 1: '正常', 2: '迟到', 3: '早退', 4: '请假' }
|
||
return map[status] || '-'
|
||
},
|
||
formatAttendanceStatusType(status) {
|
||
const map = { 0: 'danger', 1: 'success', 2: 'warning', 3: 'warning', 4: 'info' }
|
||
return map[status] || ''
|
||
},
|
||
formatGender(gender) {
|
||
if (gender === '0' || gender === 0 || gender === '男') return '男'
|
||
if (gender === '1' || gender === 1 || gender === '女') return '女'
|
||
return gender || '未知'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.student-center {
|
||
padding: 20px;
|
||
background-color: #f0f2f5;
|
||
min-height: calc(100vh - 84px);
|
||
|
||
.header-card {
|
||
border-radius: 4px;
|
||
background: #fff;
|
||
|
||
.welcome-bar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
|
||
.title-area {
|
||
h2 {
|
||
margin: 0 0 8px 0;
|
||
font-size: 20px;
|
||
color: #1f2f3d;
|
||
}
|
||
.subtitle {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
.profile-chip {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 10px;
|
||
|
||
.role-tag {
|
||
margin-right: 8px;
|
||
}
|
||
.user-name {
|
||
font-weight: bold;
|
||
font-size: 15px;
|
||
color: #303133;
|
||
}
|
||
.org-name {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.profile-card,
|
||
.duration-card {
|
||
border-radius: 4px;
|
||
background: #fff;
|
||
|
||
.profile-avatar {
|
||
text-align: center;
|
||
padding: 10px 0;
|
||
|
||
.name-block {
|
||
margin-top: 10px;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #303133;
|
||
}
|
||
.id-block {
|
||
margin-top: 4px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
.info-list {
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 8px 0;
|
||
border-bottom: 1px dashed #ebeef5;
|
||
font-size: 13px;
|
||
|
||
.label {
|
||
color: #909399;
|
||
}
|
||
.val {
|
||
color: #303133;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.duration-summary {
|
||
.total-badge {
|
||
text-align: center;
|
||
padding: 10px 0;
|
||
|
||
.num {
|
||
font-size: 32px;
|
||
font-weight: bold;
|
||
color: #1890ff;
|
||
}
|
||
.unit {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
margin-top: 4px;
|
||
}
|
||
}
|
||
|
||
.duration-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 6px 0;
|
||
font-size: 13px;
|
||
|
||
.course-name {
|
||
color: #606266;
|
||
}
|
||
.course-hour {
|
||
color: #303133;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.student-tabs {
|
||
border-radius: 4px;
|
||
background: #fff;
|
||
min-height: 520px;
|
||
|
||
.tab-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
|
||
.header-desc {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
}
|
||
}
|
||
|
||
.report-receipt-box {
|
||
background: #fafafa;
|
||
border-radius: 6px;
|
||
padding: 15px;
|
||
|
||
.receipt-details {
|
||
max-width: 680px;
|
||
margin: 0 auto;
|
||
text-align: left;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|