This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+331
View File
@@ -0,0 +1,331 @@
<template>
<hr-page page-bg="#FFFFFF" status-bg="#FFFFFF" nav-title="课程库" :no-scroll="true" @right-click="goCert">
<!-- ============ 搜索行(xSearch ============ -->
<view class="cl-search">
<view class="hr-flex1">
<x-search v-model="keyword" placeholder="搜索课题,如 成人 CPR / AED" round="20rpx" height="72rpx"
input-bg-color="#F3F4F6" icon-color="#9CA3AF" font-color="#1F2937"
:show-cancel="false"></x-search>
</view>
</view>
<!-- ============ 一级侧栏 + 三级内容 ============ -->
<view class="cl-split">
<scroll-view class="cl-side" :scroll-y="true" :show-scrollbar="false">
<view v-for="(c, i) in catalogs" :key="c.id"
:class="activeCatalog == i ? 'cl-side-item cl-side-item-on' : 'cl-side-item'" @click="onCatalog(i)">
<view class="cl-side-ic" :style="{ backgroundColor: c.iconBg }">
<text class="cl-side-ic-t" :style="{ color: c.iconColor }">{{ c.icon }}</text>
</view>
<text :class="activeCatalog == i ? 'cl-side-b cl-side-b-on' : 'cl-side-b'">{{ c.name }}</text>
<text class="cl-side-s">{{ c.count }} 课题</text>
</view>
</scroll-view>
<scroll-view class="cl-main" :scroll-y="true" :show-scrollbar="false">
<!-- 搜索过滤结果 -->
<view v-if="keyword != ''">
<view v-for="(r, i) in searchResult" :key="i" class="cl-l3" @click="openSubject(r)">
<view class="cl-l3-no">
<text class="cl-l3-no-t">{{ r.no }}</text>
</view>
<view class="hr-flex1">
<text class="cl-l3-b">{{ r.name }}</text>
<text class="cl-l3-s">{{ r.meta }}</text>
</view>
<text class="cl-l3-go"></text>
</view>
<hr-empty v-if="searchResult.length == 0" icon="⌕" title="没有匹配的课题"
sub="换个关键词试试,例如「CPR」「AED」「气道」"></hr-empty>
</view>
<!-- 二级组标题 + 三级条目 -->
<view v-else-if="activeCatalog == 0">
<view v-for="(g, gi) in groups" :key="gi">
<view class="cl-group">
<view class="cl-group-bar"></view>
<text class="cl-group-b">{{ g.name }}</text>
<view class="cl-group-cnt">
<text class="cl-group-cnt-t">{{ g.count }} 课题</text>
</view>
<view class="cl-group-line"></view>
</view>
<view v-for="(it, ii) in g.items" :key="ii" class="cl-l3" @click="openSubject(it)">
<view class="cl-l3-no">
<text class="cl-l3-no-t">{{ it.no }}</text>
</view>
<view class="hr-flex1">
<text class="cl-l3-b">{{ it.name }}</text>
<text class="cl-l3-s">{{ it.meta }}</text>
</view>
<text class="cl-l3-go"></text>
</view>
</view>
<view style="margin:6px 13px 14px 13px">
<hr-tip theme="info" icon=""
:text="'共 ' + catalogs.length + ' 个一级分类 · 49 个课题。其余分类的课题内容正在建设,敬请期待。'"></hr-tip>
</view>
</view>
<hr-empty v-else icon="▦" :title="catalogName + ' · 内容建设中'" sub="该分类的课题与题库正在录入,可先学习「急救基础」"></hr-empty>
<view style="height:24px"></view>
</scroll-view>
</view>
</hr-page>
</template>
<script lang="uts" setup>
import { ref, computed } from 'vue'
import { catalogList, courseGroups } from '@/mock/index.uts'
import { HrCatalog, HrGroup, HrSubject } from '@/mock/types.uts'
import { navTo } from '@/utils/nav.uts'
/**
* s10 课程库(三级导航)
* 一级侧栏分类 + 二级组标题(不可点,仅作分组)+ 三级课题条目(唯一可点)。
*/
const catalogs = ref<HrCatalog[]>(catalogList)
const groups = ref<HrGroup[]>(courseGroups)
const activeCatalog = ref(0)
const keyword = ref('')
const catalogName = computed(() : string => {
return catalogs.value[activeCatalog.value].name
})
/** 全量课题(用于搜索) */
const allSubjects = computed(() : HrSubject[] => {
const list : HrSubject[] = []
for (let gi = 0; gi < groups.value.length; gi++) {
const g = groups.value[gi]
for (let ii = 0; ii < g.items.length; ii++) {
list.push(g.items[ii])
}
}
return list
})
const searchResult = computed(() : HrSubject[] => {
if (keyword.value == '') { return [] as HrSubject[] }
const kw = keyword.value.toLowerCase()
const list : HrSubject[] = []
for (let i = 0; i < allSubjects.value.length; i++) {
const s = allSubjects.value[i]
if (s.name.toLowerCase().indexOf(kw) > -1) { list.push(s) }
}
return list
})
function onCatalog(i : number) : void {
activeCatalog.value = i
keyword.value = ''
}
function openSubject(s : HrSubject) : void {
navTo('/pages/course/subject?name=' + s.name)
}
function goCert() : void {
navTo('/pages/study/certificates')
}
function onScanSearch() : void {
uni.showToast({ title: '扫码搜索课题', icon: 'none' })
}
</script>
<style>
.cl-search {
display: flex;
flex-direction: row;
align-items: center;
background-color: #FFFFFF;
border-bottom: 2rpx solid #F3F4F6;
padding: 16rpx 28rpx 16rpx 28rpx;
}
.cl-search-scan {
margin-left: 16rpx;
width: 72rpx;
height: 72rpx;
border-radius: 20rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.cl-split {
flex: 1;
display: flex;
flex-direction: row;
}
/* ---------- 左侧一级分类 ---------- */
.cl-side {
width: 184rpx;
background-color: #FAFBFC;
border-right: 2rpx solid #F3F4F6;
}
.cl-side-item {
flex-direction: column;
align-items: center;
padding: 22rpx 12rpx 22rpx 12rpx;
border-left: 6rpx solid transparent;
}
.cl-side-item-on {
background-color: #FFFFFF;
border-left: 6rpx solid #2563EB;
}
.cl-side-ic {
width: 72rpx;
height: 72rpx;
border-radius: 22rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-bottom: 12rpx;
}
.cl-side-ic-t {
font-size: 33.6rpx;
font-weight: bold;
}
.cl-side-b {
font-size: 25.8rpx;
color: #1F2937;
}
.cl-side-b-on {
color: #2563EB;
font-weight: bold;
}
.cl-side-s {
font-size: 20.2rpx;
color: #9CA3AF;
margin-top: 4rpx;
}
/* ---------- 右侧内容 ---------- */
.cl-main {
flex: 1;
background-color: #F3F4F6;
}
.cl-hint {
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx 26rpx 8rpx 26rpx;
}
.cl-hint-i {
font-size: 23.6rpx;
color: #2563EB;
font-weight: bold;
margin-right: 12rpx;
}
.cl-hint-t {
font-size: 23.6rpx;
color: #9CA3AF;
}
.cl-group {
display: flex;
flex-direction: row;
align-items: center;
padding: 26rpx 26rpx 16rpx 26rpx;
}
.cl-group-bar {
width: 6rpx;
height: 24rpx;
border-radius: 4rpx;
background-color: #2563EB;
margin-right: 14rpx;
}
.cl-group-b {
font-size: 29.2rpx;
font-weight: bold;
color: #1F2937;
}
.cl-group-cnt {
margin-left: 14rpx;
background-color: #EFF6FF;
border-radius: 12rpx;
padding: 2rpx 12rpx 2rpx 12rpx;
}
.cl-group-cnt-t {
font-size: 21.2rpx;
font-weight: bold;
color: #2563EB;
}
.cl-group-line {
flex: 1;
height: 2rpx;
background-color: #F3F4F6;
margin-left: 14rpx;
}
.cl-l3 {
display: flex;
flex-direction: row;
align-items: center;
margin: 0rpx 26rpx 16rpx 26rpx;
background-color: #FFFFFF;
border: 2rpx solid #F3F4F6;
border-radius: 22rpx;
padding: 22rpx 24rpx 22rpx 24rpx;
}
.cl-l3-no {
width: 48rpx;
height: 48rpx;
border-radius: 16rpx;
background-color: #EFF6FF;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.cl-l3-no-t {
font-size: 23.6rpx;
font-weight: bold;
color: #2563EB;
}
.cl-l3-b {
font-size: 29.2rpx;
color: #1F2937;
lines: 1;
}
.cl-l3-s {
font-size: 23.6rpx;
color: #9CA3AF;
margin-top: 4rpx;
}
.cl-l3-go {
font-size: 33.6rpx;
color: #9CA3AF;
margin-left: 16rpx;
}
</style>