dedsudiyu 1 week ago
parent
commit
143beae775

+ 7 - 1
pages.json

@@ -1199,7 +1199,13 @@
 				{
 				{
 					"path": "views/study/index",
 					"path": "views/study/index",
 					"style": {
 					"style": {
-						"navigationBarTitleText": "学习任务"
+						"navigationBarTitleText": "知识学习"
+					}
+				},
+				{
+					"path": "views/studyHourTask/index",
+					"style": {
+						"navigationBarTitleText": "学时任务"
 					}
 					}
 				},
 				},
 				{
 				{

+ 16 - 0
pages_safetyEducationExamination/api/index.js

@@ -109,6 +109,14 @@ export const examUserCoursewareLearningList = (data) => {
 		data: { ...data }
 		data: { ...data }
 	})
 	})
 };
 };
+//知识点列表
+export const examElKnowledgePointTreeList = (data) => {
+	return apiResquest({
+		url: `/exam/elKnowledgePoint/tree-list`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
 //工作台按知识点分组查询试题数量
 //工作台按知识点分组查询试题数量
 export const examElQuestionBankGroupByKnowledgePoint = (data) => {
 export const examElQuestionBankGroupByKnowledgePoint = (data) => {
 	return apiResquest({
 	return apiResquest({
@@ -117,6 +125,14 @@ export const examElQuestionBankGroupByKnowledgePoint = (data) => {
 		data: { ...data }
 		data: { ...data }
 	})
 	})
 };
 };
+//刷题练习列表(按知识点分组)
+export const examUserPracticeList = (data) => {
+	return apiResquest({
+		url: `/exam/user/practice/list`,
+		method: 'POST',
+		data: { ...data }
+	})
+};
 //查询毕业报告
 //查询毕业报告
 export const examElLabEduRelationGraduationReport = (data) => {
 export const examElLabEduRelationGraduationReport = (data) => {
   return apiResquest({
   return apiResquest({

+ 26 - 4
pages_safetyEducationExamination/views/home/component/docResource.vue

@@ -17,10 +17,11 @@
 				</view>
 				</view>
 				<view class="card-title-box">
 				<view class="card-title-box">
 					<view>学习资料</view>
 					<view>学习资料</view>
-					<view>查看更多</view>
+					<view @click="goPage(1)">查看更多</view>
 				</view>
 				</view>
 				<view class="card-for-list-box">
 				<view class="card-for-list-box">
-					<view class="card-for-box" v-for="(item,index) in topList" :key="'top_'+index">
+					<view class="card-for-box"  @click="goPage(2,item)"
+					v-for="(item,index) in topList" :key="index">
 						<view class="for-left-box">📄</view>
 						<view class="for-left-box">📄</view>
 						<view class="for-center-box">
 						<view class="for-center-box">
 							<view>{{ item.coursewareName }}</view>
 							<view>{{ item.coursewareName }}</view>
@@ -32,10 +33,11 @@
 				</view>
 				</view>
 				<view class="card-title-box">
 				<view class="card-title-box">
 					<view>题库</view>
 					<view>题库</view>
-					<view>查看更多</view>
+					<view @click="goPage(3)">查看更多</view>
 				</view>
 				</view>
 				<view class="card-for-list-box-2">
 				<view class="card-for-list-box-2">
-					<view class="card-for-box" v-for="(item,index) in bottomList" :key="'bottom_'+index">
+					<view class="card-for-box"  @click="goPage(4,item)"
+					v-for="(item,index) in bottomList" :key="index">
 						<view class="for-left-box">📖</view>
 						<view class="for-left-box">📖</view>
 						<view class="for-right-box">
 						<view class="for-right-box">
 							<view>{{item.knowledgePointName}}</view>
 							<view>{{item.knowledgePointName}}</view>
@@ -74,6 +76,26 @@
 			this.examElDisciplineTypeList();
 			this.examElDisciplineTypeList();
 		},
 		},
 		methods: {
 		methods: {
+			goPage(type,item){
+				if(type == 1){
+					//文章列表
+					uni.navigateTo({
+						url: '/pages_safetyEducationExamination/views/study/index?pageType=2'
+					});
+				}else if(type == 2){
+					//学习文章
+				}else if(type == 3){
+					//知识点刷题列表(带学科类型参数)
+					uni.navigateTo({
+						url: `/pages_safetyEducationExamination/views/practice/index?disciplineTypeId=${this.tableList[this.topCheckIndex].id}`
+					});
+				}else if(type == 4){
+					//知识点指向题库(带知识点+学科类型参数)
+					uni.navigateTo({
+						url: `/pages_safetyEducationExamination/views/practice/index?knowledgePointId=${item.knowledgePointId}&disciplineTypeId=${this.tableList[this.topCheckIndex].id}`
+					});
+				}
+			},
 			//选项卡切换
 			//选项卡切换
 			checkTable(index){
 			checkTable(index){
 				if(this.topCheckIndex!=index){
 				if(this.topCheckIndex!=index){

+ 0 - 1
pages_safetyEducationExamination/views/home/component/examTask.vue

@@ -47,7 +47,6 @@
 					uni.navigateTo({
 					uni.navigateTo({
 						url: '/pages_safetyEducationExamination/views/exam/index',
 						url: '/pages_safetyEducationExamination/views/exam/index',
 					});
 					});
-					
 				}else if(type == 2){
 				}else if(type == 2){
 					//开始考试相关业务
 					//开始考试相关业务
 				}
 				}

+ 5 - 8
pages_safetyEducationExamination/views/home/component/mockPractice.vue

@@ -18,7 +18,7 @@
 					</view>
 					</view>
 					<view class="card-name-box">已完成 / 总题数</view>
 					<view class="card-name-box">已完成 / 总题数</view>
 					<view class="card-num-box">
 					<view class="card-num-box">
-						<view :style="'width:'+num+'rpx;'"></view>
+						<view :style="'width:'+num+'%;'"></view>
 					</view>
 					</view>
 				</view>
 				</view>
 				<view class="card-right-bxo">
 				<view class="card-right-bxo">
@@ -27,7 +27,7 @@
 					</view>
 					</view>
 					<view class="card-name-box">模拟正确率</view>
 					<view class="card-name-box">模拟正确率</view>
 					<view class="card-num-box">
 					<view class="card-num-box">
-						<view :style="'width:'+newData.correctRate+'rpx;'"></view>
+						<view :style="'width:'+newData.correctRate+'%;'"></view>
 					</view>
 					</view>
 				</view>
 				</view>
 			</view>
 			</view>
@@ -61,17 +61,14 @@
 		},
 		},
 		methods: {
 		methods: {
 			goToPage(){
 			goToPage(){
-				
+				uni.navigateTo({
+					url: '/pages_safetyEducationExamination/views/practice/index',
+				});
 			},
 			},
 			async examUserPracticeSummary() {
 			async examUserPracticeSummary() {
 				try {
 				try {
 					const { data } = await examUserPracticeSummary();
 					const { data } = await examUserPracticeSummary();
 					if (data.code === 200){
 					if (data.code === 200){
-						data.data={
-							completedCount: 50,
-							correctRate: 50,
-							totalCount: 100,
-						}
 						this.$set(this,'newData',data.data);
 						this.$set(this,'newData',data.data);
 						this.$set(this,'num',this.completedPercentage(data.data));
 						this.$set(this,'num',this.completedPercentage(data.data));
 					}
 					}

+ 4 - 0
pages_safetyEducationExamination/views/home/component/userInfoCard.vue

@@ -112,6 +112,9 @@
 
 
 <script>
 <script>
 	import {
 	import {
+		config
+	} from '@/api/request/config.js'
+	import {
 		examElLabEduRelationBindStatus
 		examElLabEduRelationBindStatus
 	} from '@/pages_safetyEducationExamination/api/index.js'
 	} from '@/pages_safetyEducationExamination/api/index.js'
 	import BindLabModal from './BindLabModal.vue'
 	import BindLabModal from './BindLabModal.vue'
@@ -119,6 +122,7 @@
 		components: { BindLabModal },
 		components: { BindLabModal },
 		data() {
 		data() {
 			return {
 			return {
+				baseUrl: config.base_url,
 				userInfo: {
 				userInfo: {
 					userType: '',
 					userType: '',
 					userName: '',
 					userName: '',

+ 7 - 1
pages_safetyEducationExamination/views/home/index.vue

@@ -25,7 +25,7 @@
 						<view class="header-left">
 						<view class="header-left">
 							<view class="card-title">视频学习资源</view>
 							<view class="card-title">视频学习资源</view>
 						</view>
 						</view>
-						<text class="header-link" @click="goToPage('1')">更多 ›</text>
+						<text class="header-link" @click="goToPage()">更多 ›</text>
 					</view>
 					</view>
 					<view class="card-body">
 					<view class="card-body">
 						<view class="top-table-box">
 						<view class="top-table-box">
@@ -99,6 +99,12 @@
 			this.examElDisciplineTypeList();
 			this.examElDisciplineTypeList();
 		},
 		},
 		methods: {
 		methods: {
+			goToPage(){
+				//跳转资源学习列表
+				uni.navigateTo({
+					url: '/pages_safetyEducationExamination/views/study/index',
+				});
+			},
 			//滚动加载
 			//滚动加载
 			scrollGet(){
 			scrollGet(){
 				console.log('123')
 				console.log('123')

+ 283 - 23
pages_safetyEducationExamination/views/practice/index.vue

@@ -1,45 +1,305 @@
 <!-- 刷题练习列表 -->
 <!-- 刷题练习列表 -->
 <template>
 <template>
 	<view class="safetyEducationExamination-practice">
 	<view class="safetyEducationExamination-practice">
-		
+		<!-- 顶部类型标签 -->
+		<view class="tag-bar" v-if="!hideTagBar">
+			<scroll-view scroll-x class="tag-scroll">
+				<view class="tag-scroll-inner">
+					<text
+						v-for="(tag, index) in typeTags"
+						:key="index"
+						class="tag-item"
+						:class="{ 'tag-item-active': activeTagIndex === index }"
+						@click="onTagChange(index)"
+					>{{ tag.label }}</text>
+				</view>
+			</scroll-view>
+		</view>
+
+		<!-- 列表 -->
+		<scroll-view scroll-y class="scroll-content">
+			<view class="panel">
+				<view
+					v-for="(item, index) in practiceList"
+					:key="index"
+					class="practice-card"
+					@click="goToPractice(item)"
+				>
+					<view class="card-top">
+						<text class="tag" :class="item.tagClass">{{ item.knowledgePointName }}</text>
+						<text class="total-count" :style="'color:' + item.progressColor">{{ item.totalCount }} 题</text>
+					</view>
+					<text class="card-title">{{ item.knowledgePointName }}</text>
+					<text class="card-sub" :class="item.practiceStatus > 0 ? '' : 'text-gray'">
+						{{ item.practiceStatus > 0 ? '已答 ' + item.answeredCount + ' 题 · 正确率 ' + item.correctRate + '%' : '未开始' }}
+					</text>
+					<view class="progress-row">
+						<view class="progress-wrap">
+							<view class="progress-bar" :style="'width:' + (item.progress || 0) + '%;background:' + item.progressColor"></view>
+						</view>
+						<text class="progress-text" :style="'color:' + item.progressColor">{{ item.progress || 0 }}%</text>
+					</view>
+				</view>
+				<view v-if="!practiceList[0] && !loading" class="empty-tip">暂无题库数据</view>
+				<view v-if="loading" class="loading-tip">加载中...</view>
+			</view>
+		</scroll-view>
 	</view>
 	</view>
 </template>
 </template>
 
 
 <script>
 <script>
-	// import {
-	// 	systemMineUserEdit
-	// } from '@/pages_safetyEducationExamination/api/index.js'
-	// import {
-	// 	workbench
-	// } from '@/pages_safetyEducationExamination/component/workbench/index'
+	import {
+		examUserPracticeList,
+		examElKnowledgePointTreeList,
+	} from '@/pages_safetyEducationExamination/api/index.js'
+
+	// 根据索引返回标签样式类和进度条颜色
+	const TAG_COLORS = [
+		{ tagClass: 'tag-blue', color: '#1677ff' },
+		{ tagClass: 'tag-green', color: '#52c41a' },
+		{ tagClass: 'tag-orange', color: '#fa8c16' },
+		{ tagClass: 'tag-purple', color: '#722ed1' },
+		{ tagClass: 'tag-cyan', color: '#13c2c2' },
+	];
+
 	export default {
 	export default {
-		// components: {
-		// 	workbench,
-		// },
 		data() {
 		data() {
 			return {
 			return {
-				
+				typeTags: [{ label: '全部', value: null }],
+				activeTagIndex: 0,
+				practiceList: [],
+				loading: false,
+				// 跳转参数
+				hideTagBar: false,
+				disciplineTypeId: null,
+				knowledgePointId: null,
+			}
+		},
+		onLoad(options) {
+			// 接收跳转参数
+			if (options.disciplineTypeId || options.knowledgePointId) {
+				this.disciplineTypeId = options.disciplineTypeId || null;
+				this.knowledgePointId = options.knowledgePointId || null;
+				this.hideTagBar = true;
 			}
 			}
 		},
 		},
 		created() {
 		created() {
-			
+			this.loadTypeTags();
 		},
 		},
 		onShow() {
 		onShow() {
-			
+			this.loadPracticeList();
 		},
 		},
 		methods: {
 		methods: {
-
+			// 加载知识点类型标签(dataScope=2)
+			async loadTypeTags() {
+				try {
+					const { data } = await examElKnowledgePointTreeList({ dataScope: 2 });
+					if (data.code === 200 && data.data) {
+						const tags = (data.data || []).map(item => ({
+							label: item.knowledgePointName,
+							value: item.id,
+						}));
+						this.$set(this, 'typeTags', [{ label: '全部', value: null }, ...tags]);
+					}
+				} catch (e) {
+					console.error('获取知识点类型失败', e);
+				}
+			},
+			// 加载题库分组列表
+			async loadPracticeList() {
+				if (this.loading) return;
+				this.loading = true;
+				try {
+					const tag = this.typeTags[this.activeTagIndex];
+					const obj = {
+						page: 1,
+						pageSize: 50,
+						knowledgePointId: this.knowledgePointId || tag.value || undefined,
+						disciplineTypeId: this.disciplineTypeId || undefined,
+					};
+					const { data } = await examUserPracticeList(obj);
+					if (data.code === 200) {
+						const records = (data.data.records || []).map((item, index) => {
+							const colorInfo = TAG_COLORS[index % TAG_COLORS.length];
+							return {
+								...item,
+								tagClass: colorInfo.tagClass,
+								progressColor: item.practiceStatus > 0 ? colorInfo.color : '#bbb',
+							};
+						});
+						this.$set(this, 'practiceList', records);
+					}
+				} catch (e) {
+					console.error('获取题库列表失败', e);
+				} finally {
+					this.loading = false;
+				}
+			},
+			onTagChange(index) {
+				this.activeTagIndex = index;
+				this.loadPracticeList();
+			},
+			goToPractice(item) {
+				// 跳转刷题详情,传递 knowledgePointId
+			},
 		},
 		},
-		
 	}
 	}
 </script>
 </script>
 
 
 <style lang="stylus" scoped>
 <style lang="stylus" scoped>
-	.safetyEducationExamination-study {
-		height: 100%;
-		display: flex;
-		flex-direction: column;
-		overflow: hidden;
-		background-color: #f0f4fb;
-	}
-</style>
+	.safetyEducationExamination-practice
+		height 100%
+		display flex
+		flex-direction column
+		overflow hidden
+		background-color #f0f4fb
+
+	// 顶部标签栏
+	.tag-bar
+		background #fff
+		border-bottom 1rpx solid #e8edf3
+		flex-shrink 0
+		padding 0 24rpx
+
+	.tag-scroll
+		white-space nowrap
+
+	.tag-scroll-inner
+		display inline-flex
+		gap 16rpx
+		padding 16rpx 0
+
+	.tag-item
+		display inline-block
+		font-size 24rpx
+		padding 10rpx 24rpx
+		border-radius 30rpx
+		background #eef1f6
+		color #666
+		border 1rpx solid transparent
+		white-space nowrap
+
+	.tag-item-active
+		background #e6f0ff
+		color #1677ff
+		border-color #91caff
+		font-weight 600
+
+	// 内容滚动区
+	.scroll-content
+		flex 1
+		overflow hidden
+
+	.panel
+		padding 24rpx
+
+	// 题库卡片
+	.practice-card
+		background #fff
+		border-radius 20rpx
+		padding 24rpx 28rpx
+		margin-bottom 20rpx
+		box-shadow 0 2rpx 12rpx rgba(0,0,0,0.04)
+		border 1rpx solid #dde3ed
+
+	.card-top
+		display flex
+		justify-content space-between
+		align-items center
+		margin-bottom 16rpx
+
+	// 标签
+	.tag
+		font-size 20rpx
+		padding 4rpx 12rpx
+		border-radius 8rpx
+		border 1rpx solid transparent
+
+	.tag-blue
+		background #e6f4ff
+		color #1677ff
+		border-color #91caff
+
+	.tag-green
+		background #f6ffed
+		color #389e0d
+		border-color #b7eb8f
+
+	.tag-orange
+		background #fff7e6
+		color #d46b08
+		border-color #ffd591
+
+	.tag-purple
+		background #f9f0ff
+		color #722ed1
+		border-color #d3adf7
+
+	.tag-cyan
+		background #e6fffb
+		color #08979c
+		border-color #87e8de
+
+	.total-count
+		font-size 22rpx
+		color #1677ff
+		font-weight 600
+
+	.text-hint
+		color #bbb
+
+	// 卡片文字
+	.card-title
+		display block
+		font-size 28rpx
+		font-weight 600
+		color #222
+		margin-bottom 8rpx
+
+	.card-sub
+		display block
+		font-size 22rpx
+		color #666
+		margin-bottom 16rpx
+
+	.text-gray
+		color #999
+
+	// 进度条
+	.progress-row
+		display flex
+		align-items center
+		gap 12rpx
+
+	.progress-wrap
+		flex 1
+		height 10rpx
+		background #eef1f6
+		border-radius 10rpx
+		overflow hidden
+
+	.progress-bar
+		height 100%
+		border-radius 10rpx
+		background #1677ff
+		transition width 0.3s
+
+	.progress-text
+		font-size 22rpx
+		font-weight 700
+		width 64rpx
+		text-align right
+
+	// 提示
+	.empty-tip
+		text-align center
+		line-height 80rpx
+		color #aaa
+		font-size 24rpx
+
+	.loading-tip
+		text-align center
+		line-height 60rpx
+		color #aaa
+		font-size 22rpx
+</style>

+ 465 - 24
pages_safetyEducationExamination/views/study/index.vue

@@ -1,45 +1,486 @@
-<!-- 学习任务列表 -->
+<!-- 知识学习列表 -->
 <template>
 <template>
 	<view class="safetyEducationExamination-study">
 	<view class="safetyEducationExamination-study">
-		
+		<!-- 知识点类型标签 -->
+		<scroll-view scroll-x class="tag-scroll tag-scroll-border" enable-flex>
+			<view class="tag-scroll-inner">
+				<text
+					v-for="(tag, index) in typeTags"
+					:key="index"
+					class="filter-tag"
+					:class="{ 'filter-tag-active': activeTagIndex === index }"
+					@click="onTagChange(index)"
+				>{{ tag.label }}</text>
+			</view>
+		</scroll-view>
+
+		<!-- 视频/文章二级 Tab -->
+		<view class="sub-tab-bar">
+			<view
+				v-for="(sub, index) in subTabs"
+				:key="index"
+				class="sub-tab-item"
+				:class="{ active: activeSubTab === index }"
+				@click="switchSubTab(index)"
+			>{{ sub }}</view>
+		</view>
+
+		<!-- 内容区 -->
+		<scroll-view scroll-y class="scroll-content" @scrolltolower="onScrollToLower">
+			<!-- 视频资源 -->
+			<view v-if="activeSubTab === 0" class="panel">
+				<view class="video-grid">
+					<view
+						v-for="(item, index) in videoList"
+						:key="index"
+						class="video-card"
+						@click="goKnowledgeDetail(item)"
+					>
+						<view class="video-thumb" :style="'background:' + item.coverGradient">
+							<image v-if="item.exampleImg" :src="baseUrl + item.exampleImg" class="thumb-img" mode="aspectFill" />
+							<text v-else class="play-icon">▶</text>
+						</view>
+						<view class="video-info">
+							<text class="video-title">{{ item.coursewareName }}</text>
+							<text class="video-meta">{{ formatDuration(item.minLearningDuration) }}分钟 | {{ item.creditHours }}学时 | {{ item.points }}积分</text>
+							<view class="mt8">
+								<text class="status-tag" :class="item.statusClass">{{ item.statusName }}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view v-if="!videoList[0] && !videoLoading" class="empty-tip">暂无视频资源</view>
+				<view v-if="videoLoading" class="loading-tip">加载中...</view>
+				<view v-if="videoNoMore && videoList[0]" class="loading-tip">没有更多了</view>
+			</view>
+
+			<!-- 文章资料 -->
+			<view v-if="activeSubTab === 1" class="panel">
+				<view
+					v-for="(item, index) in articleList"
+					:key="index"
+					class="course-card"
+					@click="goKnowledgeDetail(item)"
+				>
+					<view class="course-cover" :style="'background:' + item.coverGradient">
+						<text class="cover-icon">{{ item.coverIcon || '📄' }}</text>
+					</view>
+					<view class="course-info">
+						<view class="course-info-top">
+							<text class="course-name">{{ item.coursewareName }}</text>
+							<text class="status-tag" :class="item.statusClass">{{ item.statusName }}</text>
+						</view>
+						<text class="course-meta">{{ formatDuration(item.minLearningDuration) }}分钟 | {{ item.creditHours }}学时 | {{ item.points }}积分</text>
+					</view>
+				</view>
+				<view v-if="!articleList[0] && !articleLoading" class="empty-tip">暂无文章资料</view>
+				<view v-if="articleLoading" class="loading-tip">加载中...</view>
+				<view v-if="articleNoMore && articleList[0]" class="loading-tip">没有更多了</view>
+			</view>
+		</scroll-view>
 	</view>
 	</view>
 </template>
 </template>
 
 
 <script>
 <script>
-	// import {
-	// 	systemMineUserEdit
-	// } from '@/pages_safetyEducationExamination/api/index.js'
-	// import {
-	// 	workbench
-	// } from '@/pages_safetyEducationExamination/component/workbench/index'
+	import {
+		config
+	} from '@/api/request/config.js'
+	import {
+		examUserCoursewareLearningList,
+		examElKnowledgePointTreeList,
+	} from '@/pages_safetyEducationExamination/api/index.js'
+
+	// 封面渐变色池
+	const COVER_GRADIENTS = [
+		'linear-gradient(135deg,#ffd666,#fa8c16)',
+		'linear-gradient(135deg,#b37feb,#722ed1)',
+		'linear-gradient(135deg,#ff9c6e,#f5222d)',
+		'linear-gradient(135deg,#5cdbd3,#13c2c2)',
+		'linear-gradient(135deg,#1857d4,#5b8af0)',
+		'linear-gradient(135deg,#10a37f,#34d399)',
+		'linear-gradient(135deg,#6c63ff,#a78bfa)',
+		'linear-gradient(135deg,#95de64,#237804)',
+	];
+
+	// 学习状态映射
+	function getStatusInfo(status) {
+		const map = {
+			0: { name: '未开始', cls: 'tag-gray' },
+			1: { name: '学习中', cls: 'tag-orange' },
+			2: { name: '已完成', cls: 'tag-green' },
+		};
+		return map[status] || map[0];
+	}
+
 	export default {
 	export default {
-		// components: {
-		// 	workbench,
-		// },
 		data() {
 		data() {
 			return {
 			return {
-				
+				baseUrl: config.base_url,
+				subTabs: ['视频资源', '文章资料'],
+				activeSubTab: 0,
+				// 类型标签
+				typeTags: [{ label: '全部', value: null }],
+				activeTagIndex: 0,
+				// 视频资源
+				videoList: [],
+				videoLoading: false,
+				videoPage: 1,
+				videoPageSize: 10,
+				videoTotal: 0,
+				videoNoMore: false,
+				// 文章资料
+				articleList: [],
+				articleLoading: false,
+				articlePage: 1,
+				articlePageSize: 10,
+				articleTotal: 0,
+				articleNoMore: false,
+			}
+		},
+		onLoad(options) {
+			// 接收跳转参数,pageType=2 时直接展示文章资料
+			if (options.pageType == 2) {
+				this.activeSubTab = 1;
 			}
 			}
 		},
 		},
 		created() {
 		created() {
-			
+			this.loadTypeTags();
 		},
 		},
 		onShow() {
 		onShow() {
-			
+			this.refreshCurrentTab();
 		},
 		},
 		methods: {
 		methods: {
-
+			// 加载知识点类型标签(dataScope=1)
+			async loadTypeTags() {
+				try {
+					const { data } = await examElKnowledgePointTreeList({ dataScope: 1 });
+					if (data.code === 200 && data.data) {
+						const tags = (data.data || []).map(item => ({
+							label: item.knowledgePointName,
+							value: item.id,
+						}));
+						this.$set(this, 'typeTags', [{ label: '全部', value: null }, ...tags]);
+					}
+				} catch (e) {
+					console.error('获取知识点类型失败', e);
+				}
+			},
+			switchSubTab(index) {
+				this.activeSubTab = index;
+				this.refreshCurrentTab();
+			},
+			onTagChange(index) {
+				this.activeTagIndex = index;
+				this.refreshCurrentTab();
+			},
+			refreshCurrentTab() {
+				if (this.activeSubTab === 0) {
+					this.resetVideoList();
+				} else {
+					this.resetArticleList();
+				}
+			},
+			// 秒数转分钟
+			formatDuration(seconds) {
+				return seconds ? Math.ceil(seconds / 60) : 0;
+			},
+			// 重置视频列表
+			resetVideoList() {
+				this.videoPage = 1;
+				this.videoNoMore = false;
+				this.$set(this, 'videoList', []);
+				this.loadVideoList();
+			},
+			// 重置文章列表
+			resetArticleList() {
+				this.articlePage = 1;
+				this.articleNoMore = false;
+				this.$set(this, 'articleList', []);
+				this.loadArticleList();
+			},
+			// 知识学习-视频(materialType=1)
+			async loadVideoList() {
+				if (this.videoLoading || this.videoNoMore) return;
+				this.videoLoading = true;
+				try {
+					const tag = this.typeTags[this.activeTagIndex];
+					const { data } = await examUserCoursewareLearningList({
+						page: this.videoPage,
+						pageSize: this.videoPageSize,
+						materialType: 1,
+						knowledgePointId: tag.value || undefined,
+					});
+					if (data.code === 200) {
+						const records = (data.data.records || []).map((item, i) => {
+							const s = getStatusInfo(item.learnStatus);
+							const colorIndex = (this.videoList.length + i) % COVER_GRADIENTS.length;
+							return {
+								...item,
+								coverGradient: COVER_GRADIENTS[colorIndex],
+								statusName: s.name,
+								statusClass: s.cls,
+							};
+						});
+						const newList = this.videoPage === 1 ? records : [...this.videoList, ...records];
+						this.$set(this, 'videoList', newList);
+						this.videoTotal = data.data.total;
+						if (newList.length >= this.videoTotal) {
+							this.videoNoMore = true;
+						} else {
+							this.videoPage++;
+						}
+					}
+				} catch (e) {
+					console.error('获取视频资源失败', e);
+				} finally {
+					this.videoLoading = false;
+				}
+			},
+			// 知识学习-文章(materialType=2)
+			async loadArticleList() {
+				if (this.articleLoading || this.articleNoMore) return;
+				this.articleLoading = true;
+				try {
+					const tag = this.typeTags[this.activeTagIndex];
+					const { data } = await examUserCoursewareLearningList({
+						page: this.articlePage,
+						pageSize: this.articlePageSize,
+						materialType: 2,
+						knowledgePointId: tag.value || undefined,
+					});
+					if (data.code === 200) {
+						const records = (data.data.records || []).map((item, i) => {
+							const s = getStatusInfo(item.learnStatus);
+							const colorIndex = (this.articleList.length + i) % COVER_GRADIENTS.length;
+							return {
+								...item,
+								coverGradient: COVER_GRADIENTS[colorIndex],
+								statusName: s.name,
+								statusClass: s.cls,
+							};
+						});
+						const newList = this.articlePage === 1 ? records : [...this.articleList, ...records];
+						this.$set(this, 'articleList', newList);
+						this.articleTotal = data.data.total;
+						if (newList.length >= this.articleTotal) {
+							this.articleNoMore = true;
+						} else {
+							this.articlePage++;
+						}
+					}
+				} catch (e) {
+					console.error('获取文章资料失败', e);
+				} finally {
+					this.articleLoading = false;
+				}
+			},
+			// 滚动到底部
+			onScrollToLower() {
+				if (this.activeSubTab === 0) {
+					this.loadVideoList();
+				} else {
+					this.loadArticleList();
+				}
+			},
+			goKnowledgeDetail(item) {
+				// 跳转知识学习详情,传递 coursewareId
+			},
 		},
 		},
-		
 	}
 	}
 </script>
 </script>
 
 
 <style lang="stylus" scoped>
 <style lang="stylus" scoped>
-	.safetyEducationExamination-study {
-		height: 100%;
-		display: flex;
-		flex-direction: column;
-		overflow: hidden;
-		background-color: #f0f4fb;
-	}
-</style>
+	.safetyEducationExamination-study
+		height 100%
+		display flex
+		flex-direction column
+		overflow hidden
+		background-color #f0f4fb
+
+	// 类型标签横滚
+	.tag-scroll
+		background #fff
+		white-space nowrap
+		flex-shrink 0
+
+	.tag-scroll-border
+		border-bottom 1rpx solid #e8edf3
+
+	.tag-scroll-inner
+		display inline-flex
+		gap 16rpx
+		padding 16rpx 24rpx
+
+	.filter-tag
+		display inline-block
+		font-size 22rpx
+		padding 8rpx 20rpx
+		border-radius 30rpx
+		background #eef1f6
+		color #666
+		border 1rpx solid transparent
+		white-space nowrap
+
+	.filter-tag-active
+		background #e6f0ff
+		color #1677ff
+		border-color #91caff
+		font-weight 600
+
+	// 二级 Tab(视频/文章)
+	.sub-tab-bar
+		background #fff
+		border-bottom 1rpx solid #e8edf3
+		display flex
+		padding 0 24rpx
+		flex-shrink 0
+
+		.sub-tab-item
+			flex 1
+			text-align center
+			padding 16rpx 8rpx 14rpx
+			font-size 26rpx
+			color #888
+			border-bottom 4rpx solid transparent
+
+		.sub-tab-item.active
+			color #1677ff
+			font-weight 600
+			border-bottom-color #1677ff
+
+	// 滚动区
+	.scroll-content
+		flex 1
+		overflow hidden
+
+	.panel
+		padding 24rpx
+
+	// 视频宫格
+	.video-grid
+		display grid
+		grid-template-columns 1fr 1fr
+		gap 20rpx
+
+	.video-card
+		background #fff
+		border-radius 16rpx
+		overflow hidden
+		box-shadow 0 2rpx 10rpx rgba(0,0,0,0.05)
+		border 1rpx solid #dde3ed
+
+	.video-thumb
+		width 100%
+		height 160rpx
+		display flex
+		align-items center
+		justify-content center
+		position relative
+
+	.thumb-img
+		width 100%
+		height 100%
+
+	.play-icon
+		font-size 48rpx
+		color rgba(255,255,255,0.9)
+
+	.video-info
+		padding 16rpx
+
+	.video-title
+		display block
+		font-size 24rpx
+		font-weight 600
+		color #222
+		margin-bottom 8rpx
+		line-height 36rpx
+		overflow hidden
+		text-overflow ellipsis
+		display -webkit-box
+		-webkit-line-clamp 2
+		-webkit-box-orient vertical
+
+	.video-meta
+		display block
+		font-size 20rpx
+		color #999
+
+	.mt8
+		margin-top 8rpx
+
+	// 课程卡片(文章用)
+	.course-card
+		background #fff
+		border-radius 20rpx
+		margin-bottom 20rpx
+		overflow hidden
+		box-shadow 0 2rpx 12rpx rgba(0,0,0,0.04)
+		border 1rpx solid #dde3ed
+
+	.course-cover
+		width 100%
+		height 180rpx
+		display flex
+		align-items center
+		justify-content center
+
+	.cover-icon
+		font-size 64rpx
+
+	.course-info
+		padding 20rpx 24rpx
+
+	.course-info-top
+		display flex
+		justify-content space-between
+		align-items center
+		margin-bottom 8rpx
+
+	.course-name
+		flex 1
+		font-size 28rpx
+		font-weight 600
+		color #222
+		margin-right 12rpx
+
+	.course-meta
+		font-size 22rpx
+		color #999
+
+	// 状态标签
+	.status-tag
+		font-size 20rpx
+		padding 4rpx 12rpx
+		border-radius 8rpx
+		border 1rpx solid transparent
+		flex-shrink 0
+
+	.tag-gray
+		background #f5f5f5
+		color #999
+		border-color #e8e8e8
+
+	.tag-orange
+		background #fff7e6
+		color #d46b08
+		border-color #ffd591
+
+	.tag-green
+		background #f6ffed
+		color #389e0d
+		border-color #b7eb8f
+
+	// 提示
+	.empty-tip
+		text-align center
+		line-height 80rpx
+		color #aaa
+		font-size 24rpx
+
+	.loading-tip
+		text-align center
+		line-height 60rpx
+		color #aaa
+		font-size 22rpx
+</style>