|
|
@@ -0,0 +1,267 @@
|
|
|
+<script setup lang='ts'>
|
|
|
+import Back from '@/components/back.uvue'
|
|
|
+import Loading from '@/components/loading.uvue'
|
|
|
+import { ref, onMounted, nextTick, computed } from 'vue'
|
|
|
+import { type SubjectCatalogResult, querySubjectCatalog, querySubjectCatalogNoLogin } from '@/services/subject/catalog'
|
|
|
+import { type SubjectCourseResult, getSubjectCoursePage, getSubjectCoursePageNoLogin } from '@/services/subject/course'
|
|
|
+import { type GetPage } from '@/services/types/index'
|
|
|
+
|
|
|
+import { config } from '@/config'
|
|
|
+import { dict } from '@/.cool/store'
|
|
|
+import { router, debounce, user } from "@/.cool";
|
|
|
+import Lock from '@/components/lock.uvue'
|
|
|
+
|
|
|
+const isLoading = ref(true)
|
|
|
+const dataList = ref<SubjectCatalogResult[]>([])
|
|
|
+const catalog = ref<SubjectCatalogResult>()
|
|
|
+const courseList = ref<SubjectCourseResult[]>([])
|
|
|
+const cardsScrollView = ref<any>(null)
|
|
|
+const pageNum = ref(1)
|
|
|
+const loading = ref(false)
|
|
|
+const finish = ref(false)
|
|
|
+async function getDataList() {
|
|
|
+ let res: SubjectCatalogResult[] = []
|
|
|
+ if (user.token === 'Basic ZW5kOmVuZA==') {
|
|
|
+ res = await querySubjectCatalogNoLogin({
|
|
|
+ subjectId: router.query().id
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ res = await querySubjectCatalog({
|
|
|
+ subjectId: router.query().id
|
|
|
+ })
|
|
|
+ }
|
|
|
+ dataList.value = res || []
|
|
|
+ catalog.value = res?.[0] as SubjectCatalogResult
|
|
|
+ handleSelect(catalog.value)
|
|
|
+}
|
|
|
+async function handleSelect(val: SubjectCatalogResult) {
|
|
|
+ catalog.value = val
|
|
|
+ courseList.value = []
|
|
|
+ finish.value = false
|
|
|
+ pageNum.value = 1
|
|
|
+ await getInfo()
|
|
|
+ cardsScrollView.value.scrollTo({
|
|
|
+ top: 0, // 减去顶部偏移
|
|
|
+ animated: true
|
|
|
+ })
|
|
|
+}
|
|
|
+async function getInfo() {
|
|
|
+ loading.value = true
|
|
|
+ let res: GetPage<SubjectCourseResult> = {
|
|
|
+ total: 0,
|
|
|
+ rows: [],
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ pages: 0
|
|
|
+ }
|
|
|
+ if (user.token === 'Basic ZW5kOmVuZA==') {
|
|
|
+ res = await getSubjectCoursePageNoLogin({
|
|
|
+ catalogId: catalog.value?.id, pageSize: 12, pageNum: pageNum.value,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ res = await getSubjectCoursePage({
|
|
|
+ catalogId: catalog.value?.id, pageSize: 12, pageNum: pageNum.value,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ courseList.value = [...courseList.value, ...(res.rows || [])]
|
|
|
+ loading.value = false
|
|
|
+ finish.value = res.pages === pageNum.value
|
|
|
+}
|
|
|
+async function handleScrollToLower() {
|
|
|
+ if (loading.value || finish.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ pageNum.value++
|
|
|
+ await getInfo()
|
|
|
+}
|
|
|
+const handleScrollToLowerDebounce = debounce(handleScrollToLower, 1000)
|
|
|
+onMounted(async () => {
|
|
|
+ await getDataList()
|
|
|
+ isLoading.value = false
|
|
|
+})
|
|
|
+function handleDetail(item: SubjectCatalogResult) {
|
|
|
+ // if (!item.payFlag && !item.trialPlay) {
|
|
|
+ // uni.showToast({
|
|
|
+ // title: '请先购买',
|
|
|
+ // icon: 'none'
|
|
|
+ // })
|
|
|
+ // return
|
|
|
+ // }
|
|
|
+ if (userInfo.value?.memberLevel === 'default') return
|
|
|
+ router.push({
|
|
|
+ path: "/pages/programming/progress",
|
|
|
+ query: {
|
|
|
+ id: item.id,
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+const userInfo = computed(() => user.info.value?.userInfo)
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <Loading v-show="isLoading" />
|
|
|
+ <cl-page v-show="!isLoading">
|
|
|
+ <Back />
|
|
|
+ <!-- 顶部标题栏 -->
|
|
|
+ <image mode="aspectFill" src="https://oss.xiaoxiongcode.com/static/百科/bg.jpg" alt=""
|
|
|
+ class="w-full h-full object-cover" />
|
|
|
+ <view class="content">
|
|
|
+ <!-- 左侧导航菜单 -->
|
|
|
+ <view class="w-[20vw] h-[100vh] bg-[#a9e3f2bb] pt-[70px] pb-[20px] px-[20px]">
|
|
|
+ <scroll-view direction="vertical" :show-scrollbar="false" class="sidebar">
|
|
|
+ <view v-for="category in dataList" :key="category.id" class="sidebar-item"
|
|
|
+ :class="{ active: catalog?.id === category.id }" @tap="handleSelect(category)">
|
|
|
+ <!-- <cl-image :src="category?.ossIconPath" mode="heightFix" class="!h-[28px]"></cl-image> -->
|
|
|
+ <!-- {{ category.name }} -->
|
|
|
+ <text class="we">{{ category.name }}</text>
|
|
|
+ <cl-tag type="success" rounded>{{ category.courseNum || 0 }}</cl-tag>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ <!-- 右侧卡片网格 -->
|
|
|
+ <view class="cards-container">
|
|
|
+ <view class="header">
|
|
|
+ {{ catalog?.name }}
|
|
|
+ <cl-tag type="success" rounded>{{ catalog?.courseNum || 0 }}</cl-tag>
|
|
|
+
|
|
|
+ </view>
|
|
|
+ <scroll-view direction="vertical" :show-scrollbar="false" class="cards" ref="cardsScrollView"
|
|
|
+ @scroll="handleScrollToLowerDebounce">
|
|
|
+ <view class="grid grid-cols-4 gap-4">
|
|
|
+ <view class="card" v-for="card in courseList" :key="card.id" :class="`category-${card.catalogId}`"
|
|
|
+ @tap="handleDetail(card)">
|
|
|
+ <view class="w-full h-[11vw] rounded-xl overflow-hidden">
|
|
|
+ <image :src="card?.ossIconPath" mode="aspectFill" class="w-full h-full object-cover"></image>
|
|
|
+ </view>
|
|
|
+ <cl-text ellipsis :pt="{
|
|
|
+ className: '!text-[1.5vw] !font-bold'
|
|
|
+ }">{{
|
|
|
+ card.sortNum }}、{{
|
|
|
+ card.mainTitle }}</cl-text>
|
|
|
+ <cl-text ellipsis color="#666" :pt="{
|
|
|
+ className: '!text-[1.2vw]'
|
|
|
+ }">{{
|
|
|
+ card.assistantTitle }}</cl-text>
|
|
|
+ <Lock v-if="userInfo?.memberLevel === 'default'" :record="card" type="vip" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <cl-loadmore :loading="loading" :finish="finish"></cl-loadmore>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </cl-page>
|
|
|
+</template>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.header {
|
|
|
+ @apply absolute left-1/2 top-5 text-[#333] font-bold text-xl px-4 py-1 rounded-full flex items-center gap-2 flex-row;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ text-align: center;
|
|
|
+ background-color: #fff;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .sidebar {
|
|
|
+ height: calc(100vh - 90px);
|
|
|
+
|
|
|
+ .sidebar-item {
|
|
|
+ @apply text-white rounded-full py-1 px-2 cursor-pointer flex flex-row items-center;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .we {
|
|
|
+ color: #fff !important;
|
|
|
+ font-size: 1.6vw !important;
|
|
|
+ overflow: hidden;
|
|
|
+ flex: 1;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: rgba(255, 255, 255, 1);
|
|
|
+ color: #f3759b;
|
|
|
+ font-weight: bold;
|
|
|
+
|
|
|
+ .we {
|
|
|
+ color: #1899b9 !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ margin-right: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .cards-container {
|
|
|
+ @apply relative;
|
|
|
+ flex: 1;
|
|
|
+ width: 100%;
|
|
|
+ padding: 20px;
|
|
|
+ padding-top: 70px;
|
|
|
+ // background: linear-gradient(0deg, #2EB2FD, #0B85F4);
|
|
|
+
|
|
|
+ .cards {
|
|
|
+ height: calc(100vh - 90px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .category-section {
|
|
|
+ margin-bottom: 40px;
|
|
|
+
|
|
|
+ .category-title {
|
|
|
+ @apply text-white font-bold text-xl mb-4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .card {
|
|
|
+ @apply rounded-2xl border-[3px] border-[#1D4BD9] border-solid border-b-[6px] relative p-1 bg-white pb-3;
|
|
|
+ // background-color: rgba(255, 255, 255, 0.9);
|
|
|
+ // border-radius: 16px;
|
|
|
+ // padding: 16px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ // box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ aspect-ratio: 8/9;
|
|
|
+
|
|
|
+ .card-content {
|
|
|
+ width: 100%;
|
|
|
+ height: 120px;
|
|
|
+ background-color: #E3F2FD;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ aspect-ratio: 3/4;
|
|
|
+
|
|
|
+ .question-mark {
|
|
|
+ font-size: 60px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #1E88E5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-title {
|
|
|
+ @apply text-center font-bold absolute left-0 w-full text-[#0E3E87] text-[1.5vw];
|
|
|
+ bottom: 13%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|