dedsudiyu hace 16 horas
padre
commit
7c249f67ca

+ 13 - 1
pages.json

@@ -1178,8 +1178,20 @@
 					}
 				}
 			]
+		},
+		{
+			/* 危废回收分包 */
+			"root": "pages_safetyEducationExamination", //分包根路径
+			"name": "safetyEducationExamination", //分包名字可写可不写
+			"pages": [
+				{
+					"path": "views/home",
+					"style": {
+						"navigationBarTitleText": "安全教育考试"
+					}
+				}
+			]
 		}
-
 	],
 	"globalStyle": {
 		"navigationBarTextStyle": "white", //导航文字颜色

+ 64 - 0
pages_safetyEducationExamination/api/index.js

@@ -0,0 +1,64 @@
+import { apiResquest,apiResquestForm,apiResquestJsonList,apiResquestFormVideo,apiResquestTimer,apiResquestOutside} from '@/api/request/request.js'
+
+//查询当前登录用户的实验室绑定状态(仅新入学硕士研究生有数据,其他用户返回 null)
+export const examElLabEduRelationBindStatus = (data) => {
+	return apiResquest({
+		url: `/exam/elLabEduRelation/bindStatus`,
+		method: 'get',
+		data: { ...data }
+	})
+};
+
+//查询学年进度列表(从入学年到当前学年,每学年返回状态及通过分数)
+export const examElLabEduRelationAcademicYearProgress = (data) => {
+  return apiResquest({
+    url: `/exam/elLabEduRelation/academicYearProgress`,
+		method: 'get',
+		data: { ...data }
+  })
+}
+
+//实验室搜索
+export const examElLabEduRelationLabLists = (data) => {
+  return apiResquest({
+    url: `/exam/elLabEduRelation/labLists`,
+		method: 'get',
+		data: { ...data }
+  })
+}
+
+//学习任务列表
+export const examUserCourseLearningList = (data) => {
+	return apiResquest({
+		url: `/exam/user/course/learning/list`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
+
+//模拟考试列表
+export const examUserExamMockList = (data) => {
+	return apiResquest({
+		url: `/exam/user/exam/mockList`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
+
+//工作台查询考试列表
+export const examUserExamStudyTaskList = (data) => {
+	return apiResquest({
+		url: `/exam/user/exam/studyTaskList`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
+
+//开始考试-生成id
+export const examUserExamStart = (data) => {
+	return apiResquest({
+		url: `/exam/user/exam/start`,
+		method: 'POST',
+		data: { ...data }
+	})
+};

+ 27 - 0
pages_safetyEducationExamination/component/learning/index.vue

@@ -0,0 +1,27 @@
+<template>
+	<view class='safetyEducationExamination-learning'>
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		created() {
+			
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-learning {
+		
+	}
+</style>

+ 27 - 0
pages_safetyEducationExamination/component/onlineExams/index.vue

@@ -0,0 +1,27 @@
+<template>
+	<view class='safetyEducationExamination-onlineExams'>
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		created() {
+			
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-onlineExams {
+		
+	}
+</style>

+ 27 - 0
pages_safetyEducationExamination/component/practice/index.vue

@@ -0,0 +1,27 @@
+<template>
+	<view class='safetyEducationExamination-practice'>
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		created() {
+			
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-practice {
+		
+	}
+</style>

+ 121 - 0
pages_safetyEducationExamination/component/tabBar.vue

@@ -0,0 +1,121 @@
+<!-- 教育考试tab导航 -->
+<template>
+	<view class="safetyEducationExamination-tab-bar">
+		<view class="flex-full-p"></view>
+		<view class="button-box" @click="checkButton(1)">
+			<span class="tab-icon">🏠</span>
+			<view :class="checkButtonType == 1?'check-p':''">工作台</view>
+		</view>
+		<view class="flex-full-p"></view>
+		<view class="button-box" @click="checkButton(2)">
+			<span class="tab-icon">📚</span>
+			<view :class="checkButtonType == 2?'check-p':''">学习培训</view>
+		</view>
+		<view class="flex-full-p"></view>
+		<view class="button-box" @click="checkButton(3)">
+			<span class="tab-icon">🎓</span>
+			<view :class="checkButtonType == 3?'check-p':''">在线考试</view>
+		</view>
+		<view class="flex-full-p"></view>
+		<view class="button-box" @click="checkButton(4)">
+			<span class="tab-icon">✏️</span>
+			<view :class="checkButtonType == 4?'check-p':''">刷题练习</view>
+		</view>
+		<view class="flex-full-p"></view>
+	</view>
+</template>
+
+<script>
+	import {
+		config
+	} from '@/api/request/config.js'
+	import {
+		systemMineUserEdit
+	} from '@/pages_safetyEducationExamination/api/index.js'
+	let currentTabCache = 1;
+	export default {
+		data() {
+			return {
+				checkButtonType: currentTabCache,
+			}
+		},
+		created() {
+			uni.$on('update-tab-bar', (type) => {
+				this.checkButtonType = type;
+				currentTabCache = type;
+			});
+		},
+		beforeDestroy() {
+			uni.$off('update-tab-bar');
+		},
+		methods: {
+			checkButton(type){
+				if (this.checkButtonType != type) {
+					this.checkButtonType = type;
+					currentTabCache = type;
+					uni.$emit('update-tab-bar', type);
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-tab-bar {
+		// height:80rpx;
+		display: flex;
+		padding: 20rpx 0 30rpx;
+		background-color: #fff;
+
+		.flex-full-p {
+			flex: 1;
+		}
+
+		.button-box {
+			width: 140rpx;
+			display: flex; 
+			flex-direction: column; 
+			align-items: center;
+			view {
+				text-align: center;
+				margin-top:20rpx;
+				font-size:24rpx;
+			}
+			.check-p{
+				color:#1857d4;
+			}
+			span {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				width: 50rpx;
+				height: 50rpx;
+				font-size: 50rpx;
+				--blue: #1857d4;
+				--blue-deep: #0f3ea8;
+				--blue-mid: #2e6fe0;
+				--blue-light: #eef3fd;
+				--blue-xlight: #f5f8ff;
+				--green: #10a37f;
+				--green-dark: #0d8f6f;
+				--green-light: #edfaf6;
+				--orange: #e67e22;
+				--orange-light: #fef6ec;
+				--red: #e53e3e;
+				--red-light: #fff5f5;
+				--gray: #8896a7;
+				--gray-light: #f4f6fa;
+				--gray-border: #dde3ed;
+				--text-primary: #1a2233;
+				--text-secondary: #4a5568;
+				--text-hint: #8896a7;
+				--bg: #f0f4fb;
+				--white: #ffffff;
+				--radius: 12px;
+				--shadow: 0 2px 12px rgba(24, 87, 212, .08);
+				--shadow-md: 0 4px 20px rgba(24, 87, 212, .12);
+				--header-bg: linear-gradient(135deg, #1857d4 0%, #2e6fe0 100%);
+			}
+		}
+	}
+</style>

+ 468 - 0
pages_safetyEducationExamination/component/workbench/BindLabModal.vue

@@ -0,0 +1,468 @@
+<template>
+	<!-- 弹窗遮罩层 -->
+	<view class="bind-lab-modal" v-if="visible">
+		<view class="bind-lab-content" @tap.stop>
+			<!-- 标题栏 -->
+			<view class="modal-header">
+				<text class="modal-title">绑定实验室</text>
+				<text class="modal-close" @click="$emit('close')">×</text>
+			</view>
+
+			<view class="modal-body">
+				<!-- 搜索框区域:使用 view 包裹以兼容点击事件 -->
+				<view class="search-box" @tap="handleFocus">
+					<input class="search-input" type="text" placeholder="输入实验室名称、房号或楼栋关键词搜索…" v-model="keyword"
+						@input="handleSearch" />
+					<text class="search-icon">⌄</text>
+				</view>
+
+				<!-- 搜索提示 -->
+				<view class="search-tip">
+					<text>此处请选择需要后续进入的科研实验室的名称,输入实验室名称、房号或楼栋关键词,再从检索结果中选择目标实验室。</text>
+				</view>
+
+				<!-- 搜索结果下拉 -->
+				<scroll-view v-if="showDropdown" scroll-y class="bind-lab-dropdown">
+					<!-- 情况1:有数据,显示列表 -->
+					<view v-if="searchList.length > 0">
+						<view class="bind-lab-option" v-for="(lab, index) in searchList" :key="index" @click="selectLab(lab)">
+							<text>{{ lab.deptName }}{{ lab.subName }}{{ lab.roomNum?'('+lab.roomNum+')':'' }}{{ lab.buildName }}{{ lab.floorName }}</text>
+						</view>
+					</view>
+					<!-- 情况2:无数据,显示暂无数据 -->
+					<view v-else class="bind-lab-empty">
+						<text>暂无数据</text>
+					</view>
+				</scroll-view>
+
+				<!-- 实验室信息表格 -->
+				<view class="bind-lab-info" v-if="selectedLab">
+					<view class="info-row">
+						<view class="info-label">实验室名称</view>
+						<view class="info-value text-bold">{{ selectedLab.labName }}</view>
+						<view class="info-label">所在位置</view>
+						<view class="info-value text-bold">{{ selectedLab.location }}</view>
+					</view>
+					<view class="info-row">
+						<view class="info-label">负责人</view>
+						<view class="info-value">{{ selectedLab.head }}</view>
+						<view class="info-label">所属单位</view>
+						<view class="info-value">{{ selectedLab.dept }}</view>
+					</view>
+					<view class="info-row">
+						<view class="info-label">安全分级</view>
+						<view class="info-value">{{ selectedLab.level }}</view>
+						<view class="info-label">安全分类</view>
+						<view class="info-value">{{ selectedLab.category }}</view>
+					</view>
+					<view class="info-row">
+						<view class="info-label">实验室类型</view>
+						<view class="info-value full-width">{{ selectedLab.type }}</view>
+					</view>
+				</view>
+
+				<!-- 无需进入实验室 -->
+				<view class="no-lab-box">
+					<view class="checkbox-row" @click="isNoLabChecked = !isNoLabChecked">
+						<view class="custom-checkbox" :class="{ 'checked': isNoLabChecked }"></view>
+						<text class="no-lab-title">无需进入实验室</text>
+					</view>
+					<view class="no-lab-desc">
+						<text>人文、体育等相关学院,无需进入实验室的新研究生可选择此项,选择后学时要求为4小时,需通过应知应会考试。</text>
+					</view>
+				</view>
+
+				<!-- 阅读协议 -->
+				<view class="agree-row" @click="isAgreeChecked = !isAgreeChecked">
+					<view class="custom-checkbox" :class="{ 'checked': isAgreeChecked }"></view>
+					<view class="agree-text">
+						<text>我已阅读并同意</text>
+						<text class="agree-link">《新生绑定实验室须知及责任说明》</text>
+					</view>
+				</view>
+
+				<!-- 底部按钮 -->
+				<view class="modal-footer">
+					<view class="btn-cancel" @click="$emit('close')">取消</view>
+					<view class="btn-confirm" @click="handleConfirm">确认绑定</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		examElLabEduRelationLabLists
+	} from '@/pages_safetyEducationExamination/api/index.js'
+
+	export default {
+		name: 'BindLabModal',
+		props: {
+			visible: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				keyword: '',
+				showDropdown: false,
+				searchList: [],
+				selectedLab: null,
+				isNoLabChecked: false,
+				isAgreeChecked: false,
+				searchTimer: null
+			}
+		},
+		watch: {
+			visible(val) {
+				if (val) {
+					this.resetState();
+				}
+			}
+		},
+		methods: {
+			resetState() {
+				this.keyword = '';
+				this.showDropdown = false;
+				this.searchList = [];
+				this.selectedLab = null;
+				this.isNoLabChecked = false;
+				this.isAgreeChecked = false;
+			},
+			// 搜索输入处理
+			handleSearch() {
+				clearTimeout(this.searchTimer);
+				const trimKeyword = this.keyword.trim();
+
+				// 逻辑1:少于2个字符,隐藏下拉框
+				if (trimKeyword.length < 2) {
+					this.showDropdown = false;
+					return;
+				}
+
+				// 逻辑2:防抖请求
+				this.searchTimer = setTimeout(async () => {
+					try {
+						const res = await examElLabEduRelationLabLists({
+							searchValue: trimKeyword
+						});
+						if (res.data.code === 200) {
+							this.searchList = res.data.data || [];
+							// 只要有搜索结果(哪怕为空数组),都显示下拉框,由模板决定显示列表还是“暂无数据”
+							this.showDropdown = true;
+						} else {
+							this.searchList = [];
+							this.showDropdown = true; // 接口报错也显示“暂无数据”
+						}
+					} catch (e) {
+						console.error('搜索失败', e);
+						this.searchList = [];
+						this.showDropdown = true;
+					}
+				}, 300);
+			},
+			// 搜索框聚焦/点击处理
+			handleFocus() {
+				// 逻辑3:点击时,如果有关键词且之前搜过有结果,直接显示列表
+				// 注意:这里需要加一个延时,确保 input 获取焦点后再显示,避免键盘弹起冲突
+				setTimeout(() => {
+					if (this.keyword.trim().length >= 2 && this.searchList.length > 0) {
+						this.showDropdown = true;
+					}
+				}, 100);
+			},
+			selectLab(lab) {
+				this.selectedLab = lab;
+				this.showDropdown = false;
+				this.keyword = lab.labName;
+			},
+			handleConfirm() {
+				if (!this.isAgreeChecked) {
+					return uni.showToast({
+						title: '请先阅读并同意协议',
+						icon: 'none'
+					});
+				}
+				if (!this.selectedLab && !this.isNoLabChecked) {
+					return uni.showToast({
+						title: '请选择实验室或勾选无需进入',
+						icon: 'none'
+					});
+				}
+				this.$emit('bind-success');
+				this.$emit('close');
+				uni.showToast({
+					title: '绑定成功',
+					icon: 'success'
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.bind-lab-modal {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		z-index: 800;
+		background: rgba(0, 0, 0, 0.5);
+	}
+
+	.bind-lab-content {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		background: #ffffff;
+		border-radius: 24rpx;
+		width: 85%;
+		max-width: 840rpx;
+		min-height: 400rpx;
+		max-height: 90vh;
+		overflow-y: auto;
+		box-shadow: 0 16rpx 64rpx rgba(0, 0, 0, 0.18);
+		box-sizing: border-box;
+	}
+
+	.modal-header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 32rpx 40rpx 24rpx;
+	}
+
+	.modal-title {
+		font-size: 34rpx;
+		font-weight: 700;
+		color: #1a1a1a;
+	}
+
+	.modal-close {
+		font-size: 40rpx;
+		color: #999;
+		line-height: 1;
+	}
+
+	.modal-body {
+		padding: 0 40rpx 32rpx;
+	}
+
+	.search-box {
+		position: relative;
+		margin-bottom: 16rpx;
+		/* 确保点击区域覆盖整个输入框 */
+		z-index: 10;
+	}
+
+	.search-input {
+		width: 100%;
+		box-sizing: border-box;
+		height: 80rpx;
+		line-height: 80rpx;
+		padding: 0 42rpx 0 24rpx;
+		border: 1px solid #d9d9d9;
+		border-radius: 16rpx;
+		font-size: 26rpx;
+		color: #333;
+		background: #fafafa;
+		/* 确保 input 在最上层,可点击 */
+		position: relative;
+		z-index: 11;
+	}
+
+	.search-icon {
+		position: absolute;
+		right: 20rpx;
+		top: 40%;
+		transform: translateY(-50%);
+		color: #bbb;
+		font-size: 30rpx;
+		pointer-events: none;
+	}
+
+	.search-tip {
+		font-size: 22rpx;
+		color: #1677ff;
+		margin-bottom: 24rpx;
+		line-height: 1.5;
+	}
+
+	/* 下拉列表样式 */
+	.bind-lab-dropdown {
+		max-height: 600rpx;
+		border: 1px solid #e8e8e8;
+		border-radius: 16rpx;
+		margin-bottom: 24rpx;
+		background: #fff;
+		box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+		z-index: 99;
+		position: relative;
+	}
+
+	.bind-lab-option {
+		padding: 24rpx 28rpx;
+		font-size: 26rpx;
+		color: #333;
+		border-bottom: 1px solid #f0f0f0;
+		box-sizing: border-box;
+
+		&:last-child {
+			border-bottom: none;
+		}
+
+		&:active {
+			background: #f9f9f9;
+		}
+	}
+
+	/* 暂无数据样式 */
+	.bind-lab-empty {
+		padding: 30rpx 0;
+		text-align: center;
+		color: #999;
+		font-size: 26rpx;
+	}
+
+	.bind-lab-info {
+		border: 1px solid #e8e8e8;
+		border-radius: 16rpx;
+		overflow: hidden;
+		margin-bottom: 28rpx;
+	}
+
+	.info-row {
+		display: flex;
+		border-bottom: 1px solid #f0f0f0;
+
+		&:last-child {
+			border-bottom: none;
+		}
+	}
+
+	.info-label {
+		padding: 18rpx 24rpx;
+		color: #888;
+		background: #fafafa;
+		width: 28%;
+		font-size: 24rpx;
+		white-space: nowrap;
+	}
+
+	.info-value {
+		padding: 18rpx 24rpx;
+		color: #333;
+		font-size: 24rpx;
+		flex: 1;
+	}
+
+	.full-width {
+		flex: 3;
+	}
+
+	.no-lab-box {
+		background: #f6f8ff;
+		border-radius: 16rpx;
+		padding: 24rpx 28rpx;
+		margin-bottom: 28rpx;
+	}
+
+	.checkbox-row {
+		display: flex;
+		align-items: flex-start;
+		gap: 16rpx;
+	}
+
+	.custom-checkbox {
+		position: relative;
+		width: 32rpx;
+		height: 32rpx;
+		border-radius: 6rpx;
+		border: 2rpx solid #ccc;
+		margin-top: 4rpx;
+		flex-shrink: 0;
+		box-sizing: border-box;
+
+		&.checked {
+			background: #1677ff;
+			border-color: #1677ff;
+
+			&::after {
+				content: '';
+				position: absolute;
+				top: 45%;
+				left: 20%;
+				width: 10rpx;
+				height: 20rpx;
+				border: solid #ffffff;
+				border-width: 0 3rpx 3rpx 0;
+				transform: rotate(45deg) translate(-50%, -50%);
+			}
+		}
+	}
+
+	.no-lab-title {
+		font-size: 26rpx;
+		font-weight: 600;
+		color: #1a1a1a;
+	}
+
+	.no-lab-desc {
+		font-size: 22rpx;
+		color: #888;
+		margin-top: 10rpx;
+		line-height: 1.6;
+		padding-left: 40rpx;
+	}
+
+	.agree-row {
+		display: flex;
+		align-items: center;
+		gap: 16rpx;
+		margin-bottom: 36rpx;
+	}
+
+	.agree-text {
+		display: flex;
+		font-size: 24rpx;
+		color: #555;
+	}
+
+	.agree-link {
+		color: #1677ff;
+		font-weight: 500;
+	}
+
+	.modal-footer {
+		display: flex;
+		gap: 20rpx;
+		justify-content: flex-end;
+	}
+
+	.btn-cancel,
+	.btn-confirm {
+		padding: 16rpx 44rpx;
+		border-radius: 12rpx;
+		font-size: 26rpx;
+	}
+
+	.btn-cancel {
+		border: 1px solid #d9d9d9;
+		background: #fff;
+		color: #555;
+	}
+
+	.btn-confirm {
+		background: #1677ff;
+		color: #fff;
+		font-weight: 600;
+	}
+
+	.text-bold {
+		font-weight: 700;
+	}
+</style>

+ 41 - 0
pages_safetyEducationExamination/component/workbench/index.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class='safetyEducationExamination-workbench'>
+		<user-info-card></user-info-card>
+		<Workbench-stats-cards></Workbench-stats-cards>
+	</view>
+</template>
+
+<script>
+	import {
+		userInfoCard
+	} from '@/pages_safetyEducationExamination/component/workbench/userInfoCard'
+	import {
+		workbenchStatsCards
+	} from '@/pages_safetyEducationExamination/component/workbench/workbenchStatsCards.vue'
+	export default {
+		components: {
+			userInfoCard,
+			workbenchStatsCards,
+		},
+		data() {
+			return {
+				
+			}
+		},
+		created() {
+			
+		},
+		methods: {
+			
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-workbench {
+		flex:1;
+		display: flex;
+		flex-direction: column;
+		overflow-y:scroll;
+	}
+</style>

+ 513 - 0
pages_safetyEducationExamination/component/workbench/userInfoCard.vue

@@ -0,0 +1,513 @@
+<template>
+	<view class='safetyEducationExamination-workbench-userInfoCard'>
+		<!-- 顶部用户信息卡片 -->
+		<view class="top-max-big-box">
+			<img v-if="userInfo.avatar" class="avatar-img" :src="baseUrl+userInfo.avatar">
+			<img v-else class="avatar-img" :src="imagesUrl('commonality/icon_01.png')">
+			<view class="center-info-box">
+				<view class="name-box">
+					<view>{{userInfo.userName}}</view>
+					<view>奖励分 {{userInfo.totalPoints}}</view>
+					<view>信用分 {{userInfo.totalPoints}}</view>
+				</view>
+				<view class="account-box">
+					<view>{{userInfo.userType == 2?userInfo.educationName:(userInfo.userType == 1?'教职工':'系统用户')}}</view>
+					<view> · </view>
+					<view>{{userInfo.account}}</view>
+				</view>
+				<view class="dept-box">
+					<view>{{userInfo.deptName}}</view>
+				</view>
+			</view>
+			<!-- <view class="right-type-box">
+				<view></view>
+				<view>正常</view>
+			</view> -->
+		</view>
+
+		<!-- 底部详情卡片 -->
+		<view class="bottom-max-big-box">
+			<!-- 1. 绑定实验室 -->
+			<view class="lab-bind-box" v-if="userInfo.userType == 2">
+				<view class="lab-title">{{userInfo.bound?'实验室信息':'绑定实验室'}}</view>
+				<view class="lab-info-row">
+					<view class="dot dot-green" v-if="userInfo.bound"></view>
+					<view class="dot dot-green" style="background-color: #999;" v-if="!userInfo.bound"></view>
+					<text class="lab-name" v-if="userInfo.bound">{{ userInfo.labName }}</text>
+					<text class="lab-name" style="color:#999;" v-if="!userInfo.bound&&userInfo.educationType == 1">暂无绑定实验室</text>
+					<text class="lab-name" style="color:#999;" v-if="!userInfo.bound&&userInfo.educationType == 2">{{userInfo.isFinishExam?'暂无绑定实验室':'请先完成入学考试'}}</text>
+					<text class="tag" style="color:#1857d4;font-size:28rpx;font-weight:600;" v-if="(!userInfo.bound&&userInfo.educationType == 1)||(!userInfo.bound&&userInfo.educationType == 2&&userInfo.isFinishExam)" @click="shadeButton(4)">去绑定</text>
+					<text class="tag tag-green" v-if="userInfo.bound">{{userInfo.bindStatus}}</text>
+				</view>
+			</view>
+
+			<!-- 2. 学年进度 -->
+			<view class="year-progress-card">
+				<view class="card-header">
+					<view class="tags-group">
+						<text class="tag tag-blue">{{userInfo.gradeRange}}</text>
+						<text class="tag" :style="'color:'+userInfo.levelColor+';border:1rpx solid '+userInfo.levelColor+';'"
+							v-if="userInfo.levelName">{{userInfo.levelName}}</text>
+						<text class="tag tag-gray" v-if="userInfo.typeName">{{userInfo.typeName}}</text>
+					</view>
+				</view>
+				<view class="progress-info" v-if="userInfo.totalStudyHours>0">
+					<view class="info-row">
+						<text class="label">当前学年学时完成进度</text>
+						<text class="value text-blue text-bold">{{ userInfo.completedStudyHours }} / {{ userInfo.totalStudyHours }} 学时</text>
+					</view>
+					<view class="progress-wrap">
+						<view class="progress-bar" :style="{ width: (userInfo.studyHourRate) + '%' }"></view>
+					</view>
+				</view>
+				<view class="card-footer">
+					<view class="footer-left">
+						<text class="text-sm text-gray">{{userInfo.totalStudyHours>0?'学时完成度':'学时要求'}}</text>
+						<text class="text-bold text-blue">{{userInfo.totalStudyHours>0?userInfo.studyHourRate+'%':'无'}}</text>
+					</view>
+				</view>
+				<view class="card-footer" style="margin-top:10rpx;">
+					<view class="footer-right">
+						<text class="text-sm text-gray">目标考试</text>
+						<text class="exam-link"
+							@click="handleExamClick">{{userInfo.targetExamName?userInfo.targetExamName:'无'}}</text>
+					</view>
+				</view>
+			</view>
+
+			<!-- 3. 任务状态 -->
+			<view class="task-status-box">
+				<view class="task-title">当前学年任务状态</view>
+				<view class="task-grid">
+					<view class="task-item">
+						<view class="task-label" :class="userInfo.examPassStatus==1?'task-item-active':''">考试状态</view>
+						<text class="tag" :class="!userInfo.examPassStatus||userInfo.examPassStatus==1?'tag-gray':(userInfo.examPassStatus==2?'tag-green':(userInfo.examPassStatus==3||userInfo.examPassStatus==4?'tag-error':''))">
+						{{!userInfo.examPassStatus?'暂无':(userInfo.examPassStatus==1?'考试中':((userInfo.examPassStatus==2?'已通过':(userInfo.examPassStatus==3?'未通过':(userInfo.examPassStatus==4?'缺考':'')))))}}
+						</text>
+					</view>
+					<view class="task-item" :class="userInfo.certificateStatus==1?'task-item-active':''">
+						<view class="task-label">证书状态</view>
+						<text class="tag" :class="!userInfo.certificateStatus?'tag-gray':(userInfo.certificateStatus==1?'tag-green':(userInfo.certificateStatus==2||userInfo.certificateStatus==3||userInfo.certificateStatus==4?'tag-error':''))">
+						{{!userInfo.certificateStatus?'暂无':(userInfo.certificateStatus==1?'已通过':(userInfo.certificateStatus==2?'失效':(userInfo.certificateStatus==3?'已撤销':(userInfo.certificateStatus==4?'未签发':''))))}}
+						</text>
+					</view>
+					<view class="task-item" :class="userInfo.approvalStatus==1?'task-item-active':''" v-if="userInfo.approvalStatus!=3&&userInfo.userType == 2">
+						<view class="task-label">审批状态</view>
+						<text class="tag" :class="userInfo.approvalStatus==0?'tag-gray':(userInfo.approvalStatus==1?'tag-green':(userInfo.approvalStatus==2?'tag-error':'tag-gray'))">
+						{{userInfo.bound&&userInfo.approvalStatus==0?'待审批':(userInfo.approvalStatus==1?'已通过':(userInfo.approvalStatus==2?'未通过':'待绑定'))}}
+						</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<BindLabModal 
+			:visible="showBindLabModal" 
+			@close="showBindLabModal = false"
+			@bind-success="refreshPageData"
+		/>
+	</view>
+</template>
+
+<script>
+	import {
+		examElLabEduRelationBindStatus
+	} from '@/pages_safetyEducationExamination/api/index.js'
+	import BindLabModal from './BindLabModal.vue'
+	export default {
+		components: { BindLabModal },
+		data() {
+			return {
+				userInfo: {
+					userType: '',
+					userName: '',
+					account: '',
+					educationName: '',
+					deptName: '',
+					labName: '',
+					bound: '',
+					approvalStatus: '',
+					certificateStatus: '',
+					examPassStatus: '',
+					gradeRange: '',
+					typeName: '',
+					completedStudyHours: '',
+					totalStudyHours: '',
+					studyHourRate: '',
+					targetExamName: '',
+				},
+				showBindLabModal:true,
+			}
+		},
+		mounted() {
+			this.examElLabEduRelationBindStatus();
+		},
+		methods: {
+			// 绑定成功后的回调
+			refreshPageData() {
+				// 重新获取用户信息或刷新页面数据
+				this.examElLabEduRelationBindStatus();
+			},
+			handleExamClick() {
+				// 这里写跳转到考试详情页的逻辑
+				uni.navigateTo({
+					url: '/pages_safetyEducationExamination/screen-exam-detail/screen-exam-detail' // 根据实际路径修改
+				});
+			},
+			// 获取用户信息
+			async examElLabEduRelationBindStatus() {
+				const {
+					data
+				} = await examElLabEduRelationBindStatus({});
+				if (data.code == 200) {
+					// 使用 Object.assign 保留默认值,同时更新接口返回的数据
+					// Object.assign(this.userInfo, data.data);
+					this.$set(this,'userInfo',data.data);
+				}
+			},
+			//关闭弹层
+			shadeButton(type){
+				let self = this;
+				if(type == 1){
+					//提交
+					this.$refs["shadeForm"].validate(valid => {
+						if (valid) {
+							this.$confirm('确认提交?', "提示", {
+								confirmButtonText: "确认",
+								cancelButtonText: "取消",
+								type: "warning"
+							}).then(function() {
+							}).then(() => {
+								self.examElLabEduRelationBind();
+							}).catch(() => {});
+						}
+					})
+				}else if(type == 2){
+					//查看通知
+					this.$set(this,'shadeType',2);
+				}else if(type == 3 || type == 33){
+					if(type == 3){
+						//关闭通知
+						this.$set(this.shadeFormData,'checkType',true);
+						this.$set(this,'shadeType',1);
+					}else if(type == 33){
+						//关闭通知
+						this.$set(this.shadeFormData,'checkType',false);
+						this.$set(this,'shadeType',1);
+					}
+				}else if(type == 4||type == 5){
+					//开启
+					this.$set(this,'shadeFormData',{
+						subId:null,
+						reason:'',
+						isEnter:false,
+						checkType:false,
+						offType:type == 4?true:false,
+					});
+					this.$set(this,'shadeType',1);
+				}else if(type == 6){
+					//关闭
+					this.$set(this,'shadeType',null);
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-workbench-userInfoCard {
+		.top-max-big-box {
+			z-index: 0;
+			height: 200rpx;
+			background-color: #1959D5;
+			overflow: hidden;
+			display: flex;
+
+			.avatar-img {
+				border: 4rpx solid #87A8E9;
+				border-radius: 50%;
+				width: 98rpx;
+				height: 98rpx;
+				margin: 32rpx 32rpx 0 32rpx;
+			}
+
+			.center-info-box {
+				flex: 1;
+
+				.name-box {
+					display: flex;
+					margin-top: 18rpx;
+
+					view:nth-child(1) {
+						font-size: 34rpx;
+						font-weight: 700;
+						color: #fff;
+						margin-right: 20rpx;
+					}
+
+					view:nth-child(2) {
+						background: rgba(255, 215, 0, .2);
+						color: #ffe066;
+						font-size: 22rpx;
+						padding: 4rpx 16rpx;
+						border-radius: 40rpx;
+						border: 2rpx solid rgba(255, 215, 0, .35);
+						margin-right: 20rpx;
+					}
+
+					view:nth-child(3) {
+						background: rgba(0, 210, 140, .18);
+						color: #7fffd4;
+						font-size: 22rpx;
+						padding: 4rpx 16rpx;
+						border-radius: 40rpx;
+						border: 2rpx solid rgba(0, 210, 140, .4);
+						margin-right: 20rpx;
+					}
+				}
+
+				.account-box {
+					display: flex;
+					margin-top: 10rpx;
+
+					view {
+						color: rgba(255, 255, 255, .8);
+						font-size: 24rpx;
+					}
+
+					view:nth-child(2) {
+						margin: 0 10rpx;
+					}
+				}
+
+				.dept-box {
+					display: flex;
+					margin-top: 10rpx;
+
+					view {
+						color: rgba(255, 255, 255, .8);
+						font-size: 24rpx;
+					}
+				}
+			}
+
+			.right-type-box {
+				width: 70rpx;
+				height: 30rpx;
+				background: rgba(0, 210, 140, .18);
+				color: #7fffd4;
+				font-size: 22rpx;
+				padding: 4rpx 16rpx;
+				border-radius: 40rpx;
+				border: 2rpx solid rgba(0, 210, 140, .4);
+				display: flex;
+				margin: 60rpx 20rpx 0 0;
+
+				view:nth-child(1) {
+					width: 10rpx;
+					height: 10rpx;
+					border-radius: 50%;
+					background-color: rgba(0, 210, 140, 1);
+					text-align: center;
+					margin: 10rpx 10rpx 0 0;
+				}
+			}
+		}
+
+		.bottom-max-big-box {
+			z-index: 1;
+			position: relative;
+			border-radius: 32rpx;
+			padding: 32rpx;
+			margin: -36rpx 24rpx 24rpx;
+			background-color: #fff;
+
+			// 1. 绑定实验室样式
+			.lab-bind-box {
+				background: #eef3fd;
+				border-radius: 16rpx;
+				padding: 20rpx;
+				margin-bottom: 28rpx;
+
+				.lab-title {
+					font-size: 24rpx;
+					color: #8896a7;
+					margin-bottom: 16rpx;
+				}
+
+				.lab-info-row {
+					display: flex;
+					align-items: center;
+					gap: 12rpx;
+
+					.dot-green {
+						width: 12rpx;
+						height: 12rpx;
+						border-radius: 50%;
+						background-color: #0d8f6f;
+					}
+
+					.lab-name {
+						font-weight: 500;
+						color: #1857d4;
+						font-size: 28rpx;
+					}
+				}
+			}
+
+			// 2. 学年进度样式
+			.year-progress-card {
+				background-color: #f4f6fa;
+				border-radius: 20rpx;
+				padding: 28rpx;
+				margin-bottom: 24rpx;
+
+				.card-header {
+					margin-bottom: 24rpx;
+				}
+
+				.tags-group {
+					display: flex;
+					align-items: center;
+					gap: 16rpx;
+				}
+
+				.progress-info {
+					margin-bottom: 16rpx;
+				}
+
+				.info-row {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					margin-bottom: 8rpx;
+				}
+
+				.label {
+					font-size: 24rpx;
+					color: #8896a7;
+				}
+
+				.value {
+					font-size: 24rpx;
+				}
+
+				.progress-wrap {
+					background-color: #e8edf6;
+					border-radius: 8rpx;
+					height: 14rpx;
+					overflow: hidden;
+				}
+
+				.progress-bar {
+					height: 100%;
+					border-radius: 8rpx;
+					background: linear-gradient(90deg, #2e6fe0, #1857d4);
+					transition: width 0.4s;
+				}
+
+				.card-footer {
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+				}
+
+				.footer-left,
+				.footer-right {
+					display: flex;
+					align-items: center;
+					gap: 8rpx;
+				}
+
+				.exam-link {
+					color: #1857d4;
+					font-weight: 600;
+					background-color: #eef3fd;
+					padding: 4rpx 16rpx;
+					border-radius: 12rpx;
+					border: 1px solid rgba(24, 87, 212, 0.25);
+					font-size: 24rpx;
+				}
+			}
+
+			// 3. 任务状态样式
+			.task-status-box {
+				.task-title {
+					font-size: 28rpx;
+					color: #8896a7;
+					font-weight: 700;
+					margin-bottom: 16rpx;
+				}
+
+				.task-grid {
+					display: grid;
+					grid-template-columns: 1fr 1fr 1fr;
+					gap: 16rpx;
+
+					.task-item {
+						border: 1px solid #e8edf6;
+						border-radius: 16rpx;
+						padding: 20rpx;
+						text-align: center;
+
+						.task-label {
+							font-size: 24rpx;
+							color: #8896a7;
+							margin-bottom: 8rpx;
+						}
+					}
+
+					.task-item-active {
+						border: 1px solid #e0ffc1;
+						background-color: #f6ffed;
+					}
+				}
+			}
+		}
+
+		// 全局标签样式
+		.tag {
+			display: inline-flex;
+			align-items: center;
+			padding: 4rpx 16rpx;
+			border-radius: 40rpx;
+			font-size: 22rpx;
+			font-weight: 500;
+		}
+
+		.tag-blue {
+			background-color: #eef3fd;
+			color: #1857d4;
+		}
+
+		.tag-green {
+			background-color: #edfaf6;
+			color: #0d8f6f;
+		}
+
+		.tag-gray {
+			background-color: #f4f6fa;
+			color: #8896a7;
+		}
+		
+		.tag-error {
+			background-color: #fff7d8;
+			color: #e67e22;
+		}
+
+		.text-sm {
+			font-size: 24rpx;
+		}
+
+		.text-bold {
+			font-weight: 600;
+		}
+
+		.text-blue {
+			color: #1857d4;
+		}
+
+		.text-gray {
+			color: #8896a7;
+		}
+	}
+</style>

+ 346 - 0
pages_safetyEducationExamination/component/workbench/workbenchStatsCards.vue

@@ -0,0 +1,346 @@
+<template>
+	<view class="workbench-stats-cards">
+		<!-- 1. 学年数据统计 -->
+		<view class="card">
+			<view class="card-header">
+				<text class="card-title">📊 学年数据统计</text>
+				<view class="btn-outline" @click="handleShowReport">毕业报告</view>
+			</view>
+			<view class="card-body">
+				<view class="year-row" v-for="(item, index) in yearStatsList" :key="index">
+					<text class="year-label">{{ item.year }}</text>
+					<text :class="['year-status', item.statusClass]">{{ item.statusText }}</text>
+				</view>
+				<view v-if="!yearStatsList.length" class="empty-tip">暂无统计数据</view>
+			</view>
+		</view>
+
+		<!-- 2. 学习任务 -->
+		<view class="card">
+			<view class="card-header">
+				<view class="header-left">
+					<text class="card-title">📚 学习任务 <text class="badge" v-if="taskListTotal">{{ taskListTotal }}</text></text>
+					<text class="header-subtitle">待完成的学习任务</text>
+				</view>
+				<text class="header-link" @click="goToPage('/pages_safetyEducationExamination/screen-knowledge-list/screen-knowledge-list?tab=2')">更多 ›</text>
+			</view>
+			<view class="card-body">
+				<view class="list-item" v-for="(item, index) in taskList" :key="index" @click="goToPage('/pages_safetyEducationExamination/screen-study-detail/screen-study-detail?id=' + item.id)">
+					<view class="list-left">
+						<text class="item-title">{{ item.courseName }}</text>
+						<text class="item-desc">{{ item.deptName }}下发 · <text class="text-orange">待完成</text></text>
+					</view>
+					<text class="list-arrow">›</text>
+				</view>
+				<view v-if="!taskList.length" class="empty-tip">暂无待完成任务</view>
+			</view>
+		</view>
+
+		<!-- 3. 模拟考试 -->
+		<view class="card">
+			<view class="card-header">
+				<view class="header-left">
+					<text class="card-title">📝 模拟考试 <text class="badge" v-if="mockExamListTotal">{{ mockExamListTotal }}</text></text>
+					<text class="header-subtitle">待完成的模拟练习</text>
+				</view>
+				<text class="header-link" @click="goToPage('/pages_safetyEducationExamination/screen-exam-list/screen-exam-list?tab=1')">更多 ›</text>
+			</view>
+			<view class="card-body">
+				<view class="list-item" v-for="(item, index) in mockExamList" :key="index" @click="goToPage('/pages_safetyEducationExamination/screen-mock-detail/screen-mock-detail?id=' + item.id)">
+					<view class="list-left">
+						<text class="item-title">{{ item.examName }}</text>
+						<text class="item-desc">最高成绩: <text class="text-orange">{{ item.maxScore?item.maxScore:0 }} 分</text> · 及格: {{ item.passScore }}分</text>
+					</view>
+					<text class="list-arrow">›</text>
+				</view>
+				<view v-if="!mockExamList.length" class="empty-tip">暂无模拟考试</view>
+			</view>
+		</view>
+
+		<!-- 4. 在线考试 -->
+		<view class="card">
+			<view class="card-header">
+				<view class="header-left">
+					<text class="card-title">🎓 在线考试</text>
+					<text class="header-subtitle">待完成的在线考试</text>
+				</view>
+				<text class="header-link" @click="goToPage('/pages_safetyEducationExamination/screen-exam-list/screen-exam-list?tab=0')">更多 ›</text>
+			</view>
+			<view class="card-body">
+				<view 
+					class="exam-task-card" 
+					:class="{ 'active-exam': item.isActive }" 
+					v-for="(item, index) in onlineExamList" 
+					:key="index"
+					@click="goToPage('/pages_safetyEducationExamination/screen-exam-detail/screen-exam-detail?id=' + item.id)"
+				>
+					<view class="exam-header-row">
+						<view class="exam-tag-row">
+							<text class="tag" :class="item.isActive ? 'tag-blue' : 'tag-orange'">{{ item.examTypeName }}</text>
+							<view class="dot dot-orange" v-if="item.isActive"></view>
+							<text class="text-orange text-xs" v-if="item.isActive">进行中</text>
+						</view>
+						<text class="list-arrow" :class="item.isActive ? 'text-blue' : ''">›</text>
+					</view>
+					<text class="exam-title">{{ item.title }}</text>
+				</view>
+				<view v-if="!onlineExamList[0]" class="empty-tip">暂无在线考试</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { 
+		examElLabEduRelationAcademicYearProgress, 
+		examUserCourseLearningList, 
+		examUserExamMockList, 
+		examUserExamStudyTaskList 
+	} from '@/pages_safetyEducationExamination/api/index.js'
+
+	export default {
+		name: 'WorkbenchStatsCards',
+		data() {
+			return {
+				yearStatsList: [],
+				taskList: [],
+				taskListTotal:0,
+				mockExamList: [],
+				mockExamListTotal: 0,
+				onlineExamList: [],
+			}
+		},
+		created() {
+			this.fetchAllData();
+		},
+		methods: {
+			async fetchAllData() {
+				// 并行请求所有数据,提升加载速度
+				await Promise.all([
+					this.examElLabEduRelationAcademicYearProgress(),
+					this.examUserCourseLearningList(),
+					this.examUserExamMockList(),
+					this.examUserExamStudyTaskList(),
+				]);
+			},
+			//年度数据
+			async examElLabEduRelationAcademicYearProgress() {
+				try {
+					const { data } = await examElLabEduRelationAcademicYearProgress();
+					if (data.code === 200) {
+						this.yearStatsList = (data.data || []).map(item => ({
+							year: item.academicYear,
+							statusText: item.statusCode == '0' ? `未开始` : (item.statusCode == '2' ? `通过丨`+item.score : (item.statusCode == '1' ? `进行中` : '未通过')),
+							statusClass: item.statusCode == '2' ? 'year-status-pass' : (item.statusCode == '1' ? 'year-status-going' : 'year-status-text')
+						}));
+					}
+				} catch (e) { console.error('获取学年统计失败', e); }
+			},
+			//学习任务
+			async examUserCourseLearningList() {
+				try {
+					const { data } = await examUserCourseLearningList({page:1,pageSize:2,learnStatus:'01'});
+					if (data.code === 200) {
+						this.taskList = data.data.records || [];
+						this.taskListTotal = data.data.total || 0;
+					}
+				} catch (e) { console.error('获取学习任务失败', e); }
+			},
+			//模拟考试
+			async examUserExamMockList() {
+				try {
+					const { data } = await examUserExamMockList({page:1,pageSize:1});
+					if (data.code === 200){
+						this.mockExamList = data.data.records || [];
+						this.mockExamListTotal = data.data.total || 0;
+					}
+				} catch (e) { console.error('获取模拟考试失败', e); }
+			},
+			//在线考试(查询1条特殊考试1条普通考试并组合数据)
+			async examUserExamStudyTaskList() {
+				try {
+        const [res1, res2] = await Promise.all([
+					examUserExamStudyTaskList({ page: 1, pageSize: 1, examStatus: 2, isSpecialExam: true }),
+					examUserExamStudyTaskList({ page: 1, pageSize: 1, examStatus: 2, isSpecialExam: false })
+        ]);
+        let list = [];
+        if (res1.data.code === 200) {
+					list = [...list, ...(res1.data.data.records || [])];
+        }
+        if (res2.data.code === 200) {
+					list = [...list, ...(res2.data.data.records || [])];
+        }
+				console.log('res1',res1)
+				console.log('res2',res2)
+				console.log('list',list)
+				this.$set(this,'onlineExamList',list);
+				} catch (e) { console.error('获取在线考试失败', e); }
+			},
+
+			// 根据考试类型动态生成底部信息字段
+			buildExamFields(item) {
+				const fields = [];
+				if (item.requiredHours !== undefined) {
+					fields.push({
+						label: '学时要求',
+						value: `${item.completedHours || 0}/${item.requiredHours} 学时`,
+						valueClass: item.hoursMet ? '' : 'text-orange',
+						extra: item.hoursMet ? '' : '未达标'
+					});
+				}
+				if (item.taskStatus) {
+					fields.push({ label: '学习任务', value: item.taskStatus, valueClass: item.taskStatus === '已完成' ? '' : 'text-orange' });
+				}
+				if (item.mockStatus) {
+					fields.push({ label: '模拟考试', value: item.mockStatus, valueClass: item.mockStatus === '已完成' ? '' : 'text-orange' });
+				}
+				if (item.examConfirmStatus) {
+					fields.push({ label: '考试状态', value: item.examConfirmStatus, valueClass: 'text-gray' });
+				}
+				return fields;
+			},
+
+			goToPage(url) {
+				uni.navigateTo({ url });
+			},
+			
+			handleShowReport() {
+				// 触发事件让父组件处理弹窗,或直接在组件内处理
+				uni.navigateTo({ url: '/pages_safetyEducationExamination/graduation-report/graduation-report' });
+			},
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.workbench-stats-cards {
+		padding:0 32rpx;
+		.card {
+			background: #fff;
+			border-radius: 24rpx;
+			overflow: hidden;
+			margin-bottom: 24rpx;
+			box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.03);
+			border:1px solid #dde3ed;
+		}
+
+		.card-header {
+			background: #f4f6fa;
+			padding: 20rpx 28rpx;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+		}
+
+		.header-left {
+			display: flex;
+			align-items: center;
+			gap: 12rpx;
+		}
+
+		.card-title {
+			font-size: 28rpx;
+			font-weight: 700;
+			color: #333;
+		}
+
+		.header-subtitle {
+			font-size: 22rpx;
+			color: #8896a7;
+		}
+
+		.header-link {
+			font-size: 24rpx;
+			color: #1857d4;
+		}
+
+		.btn-outline {
+			font-size: 22rpx;
+			color: #1857d4;
+			border: 1px solid rgba(24, 87, 212, 0.3);
+			padding: 6rpx 16rpx;
+			border-radius: 8rpx;
+		}
+
+		.badge {
+			background: #ff4d4f;
+			color: #fff;
+			font-size: 20rpx;
+			padding: 2rpx 10rpx;
+			border-radius: 20rpx;
+			margin-left: 8rpx;
+			font-weight: 400;
+		}
+
+		.card-body {
+			padding: 16rpx 28rpx 24rpx;
+		}
+
+		// 学年统计样式
+		.year-row {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 12rpx 0;
+			border-bottom: 1px solid #f4f6fa;
+			&:last-child { border-bottom: none; }
+		}
+		.year-label { font-size: 26rpx; color: #333; font-weight: 500; }
+		.year-status { font-size: 24rpx; font-weight: 500; }
+		.year-status-pass { color: #0d8f6f; }
+		.year-status-going { color: #1857d4; }
+		.year-status-text { color: #333; }
+
+		// 列表项样式
+		.list-item {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 16rpx 0;
+			border-bottom: 1px solid #f4f6fa;
+			&:last-child { border-bottom: none; }
+		}
+		.item-title { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 8rpx; display: block; }
+		.item-desc { font-size: 22rpx; color: #8896a7; }
+		.list-arrow { font-size: 32rpx; color: #ccc; }
+
+		// 在线考试卡片样式
+		.exam-task-card {
+			background: #f4f6fa;
+			border-radius: 16rpx;
+			padding: 20rpx;
+			margin-bottom: 16rpx;
+			&:last-child { margin-bottom: 0; }
+		}
+		.active-exam {
+			background: #eef3fd;
+			border: 1px solid rgba(24, 87, 212, 0.15);
+		}
+		.exam-header-row {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin-bottom: 12rpx;
+		}
+		.exam-tag-row { display: flex; align-items: center; gap: 8rpx; }
+		.exam-title { font-size: 28rpx; font-weight: 500; color: #333; margin-bottom: 16rpx; display: block; }
+		
+		// 通用工具类
+		.tag {
+			display: inline-flex;
+			padding: 4rpx 12rpx;
+			border-radius: 8rpx;
+			font-size: 22rpx;
+			font-weight: 500;
+		}
+		.tag-blue { background: #eef3fd; color: #1857d4; }
+		.tag-orange { background: #fef6ec; color: #e67e22; }
+		.dot { width: 12rpx; height: 12rpx; border-radius: 50%; }
+		.dot-orange { background: #e67e22; }
+		.text-xs { font-size: 22rpx; }
+		.text-orange { color: #e67e22; }
+		.text-blue { color: #1857d4; }
+		.text-gray { color: #8896a7; }
+		.empty-tip { text-align: center; padding: 30rpx 0; color: #ccc; font-size: 24rpx; }
+	}
+</style>

+ 101 - 0
pages_safetyEducationExamination/views/home.vue

@@ -0,0 +1,101 @@
+<!-- 教育考试首页 -->
+<template>
+	<view class="safetyEducationExamination-home">
+		<view class="safetyEducationExamination-home-page">
+			<workbench class="safetyEducationExamination-content" v-show="currentTab == 1"></workbench>
+			<learning class="safetyEducationExamination-content" v-show="currentTab == 2"></learning>
+			<online-exams class="safetyEducationExamination-content" v-show="currentTab == 3"></online-exams>
+			<practice class="safetyEducationExamination-content" v-show="currentTab == 4"></practice>
+		</view>
+		<tab-bar></tab-bar>
+	</view>
+</template>
+
+<script>
+	import {
+		systemMineUserEdit
+	} from '@/pages_safetyEducationExamination/api/index.js'
+	import {
+		workbench
+	} from '@/pages_safetyEducationExamination/component/workbench/index'
+	import {
+		learning
+	} from '@/pages_safetyEducationExamination/component/learning/index'
+	import {
+		onlineExams
+	} from '@/pages_safetyEducationExamination/component/onlineExams/index'
+	import {
+		practice
+	} from '@/pages_safetyEducationExamination/component/practice/index'
+	import {
+		tabBar
+	} from '@/pages_safetyEducationExamination/component/tabBar'
+	export default {
+		components: {
+			workbench,
+			learning,
+			onlineExams,
+			practice,
+			tabBar,
+		},
+		data() {
+			return {
+				currentTab: 1 ,
+				tabTitles: {
+					1: { title: '安全教育考试', bgColor: '#1857d4', frontColor: '#ffffff' },
+					2: { title: '学习培训', bgColor: '#10a37f', frontColor: '#ffffff' },
+					3: { title: '在线考试', bgColor: '#e67e22', frontColor: '#ffffff' },
+					4: { title: '刷题练习', bgColor: '#ffffff', frontColor: '#000000' }
+				}
+			}
+		},
+		created() {
+			uni.$on('update-tab-bar', (type) => {
+				this.currentTab = type;
+				const style = this.tabTitles[type];
+				uni.setNavigationBarTitle({
+					title: style.title
+				});
+				uni.setNavigationBarColor({
+					frontColor: style.frontColor,      // 仅支持 #ffffff 和 #000000
+					backgroundColor: style.bgColor,    // 必须是十六进制颜色
+					animation: {
+						duration: 200,                 // 设置 200ms 的过渡动画,体验更丝滑
+						timingFunc: 'easeIn'
+					}
+				});
+			});
+		},
+		onShow() {
+			uni.$emit('update-tab-bar', this.currentTab);
+		},
+		methods: {
+
+		},
+		beforeDestroy() {
+			uni.$off('update-tab-bar');
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.safetyEducationExamination-home {
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+		background-color: #f0f4fb;
+		.safetyEducationExamination-home-page{
+			flex:1;
+			display: flex;
+			flex-direction: column;
+			overflow: hidden;
+			.safetyEducationExamination-content{
+				flex:1;
+				display: flex;
+				flex-direction: column;
+				overflow: hidden;
+			}
+		}
+	}
+</style>