| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- <!-- 知识学习列表 -->
- <template>
- <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>
- </template>
- <script>
- import {
- config
- } from '@/api/request/config.js'
- import {
- examUserCoursewareLearningList,
- examElKnowledgePointTreeList,
- } from '@/pages_safetyEducationExamination/api/index.js'
- import {
- goLearning,
- } from '@/pages_safetyEducationExamination/utils/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 {
- data() {
- 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() {
- this.loadTypeTags();
- },
- onShow() {
- this.refreshCurrentTab();
- },
- 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) {
- goLearning(item);
- },
- //重新开始学习课件
- async examUserCoursewareLearningRestart(coursewareId) {
- try {
- const { data } = await examUserCoursewareLearningRestart({coursewareId:coursewareId});
- if (data.code === 200) {
- uni.navigateTo({
- url: `/pages_safetyEducationExamination/views/webViewPage/coursewareLearning?coursewareId=${coursewareId}`,
- });
- }
- } catch (e) { console.error('重新开始学习课件失败', e); }
- },
- },
- }
- </script>
- <style lang="stylus" scoped>
- .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>
|