|
|
@@ -1,221 +1,219 @@
|
|
|
-<script lang="ts" setup>
|
|
|
-import { user } from '@/.cool'
|
|
|
-import { ref, onMounted, computed } from 'vue'
|
|
|
-import { fetchSubjectAppInfo } from '@/services/subject/info'
|
|
|
-import type { SubjectCatalogResult } from '@/services/subject/catalog'
|
|
|
-import type { SubjectCourseResult } from '@/services/subject/course'
|
|
|
-
|
|
|
-import Progress from '../catalog/components/progress.uvue'
|
|
|
+<script setup lang='ts'>
|
|
|
import Back from '@/components/back.uvue'
|
|
|
-import Lock from '@/components/lock.uvue'
|
|
|
import Loading from '@/components/loading.uvue'
|
|
|
+import { ref, onMounted, nextTick } from 'vue'
|
|
|
+import { type SubjectCatalogResult, querySubjectCatalog } from '@/services/subject/catalog'
|
|
|
+import { type SubjectCourseResult, getSubjectCoursePage } from '@/services/subject/course'
|
|
|
import { config } from '@/config'
|
|
|
-import { router } from "@/.cool";
|
|
|
import { dict } from '@/.cool/store'
|
|
|
+
|
|
|
const isLoading = ref(true)
|
|
|
-const visible = ref<boolean>(false)
|
|
|
const dataList = ref<SubjectCatalogResult[]>([])
|
|
|
const catalog = ref<SubjectCatalogResult>()
|
|
|
-const record = ref<any>()
|
|
|
const courseList = ref<SubjectCourseResult[]>()
|
|
|
+const cardsScrollView = ref<any>(null)
|
|
|
+const pageNum = ref(1)
|
|
|
async function getDataList() {
|
|
|
const id = dict.getValueByLabelMapByType('index_subject_id')['数学']
|
|
|
- const res = await fetchSubjectAppInfo({ id, delFlag: false, })
|
|
|
- record.value = res
|
|
|
- dataList.value = res.catalogList || []
|
|
|
- catalog.value = res?.catalogList?.[0]
|
|
|
- courseList.value = res?.courseList || []
|
|
|
+ const res = await querySubjectCatalog({
|
|
|
+ subjectId: id, dataScope: {
|
|
|
+ sortBy: 'asc',
|
|
|
+ sortName: 'sortNum',
|
|
|
+ }
|
|
|
+ })
|
|
|
+ dataList.value = res || []
|
|
|
+ catalog.value = res?.[0] as SubjectCatalogResult
|
|
|
+ handleSelect(catalog.value)
|
|
|
}
|
|
|
-onMounted(async () => {
|
|
|
- try {
|
|
|
- await getDataList()
|
|
|
- isLoading.value = false
|
|
|
- } catch (err) {
|
|
|
- console.log(err);
|
|
|
- isLoading.value = false
|
|
|
- }
|
|
|
-})
|
|
|
-const cardsScrollView = ref<any>(null)
|
|
|
-const scrollLeft = ref<any>(0)
|
|
|
async function handleSelect(val: SubjectCatalogResult) {
|
|
|
catalog.value = val
|
|
|
- visible.value = false
|
|
|
- uni.createSelectorQuery().select(`.category-${val.id}`).boundingClientRect().exec(async (rect) => {
|
|
|
- if (cardsScrollView.value && rect[0]) {
|
|
|
- cardsScrollView.value.scrollTo({
|
|
|
- left: scrollLeft.value + rect[0].left - dict.getWindowHeight() / 2, // 减去顶部偏移
|
|
|
- animated: true
|
|
|
- })
|
|
|
- }
|
|
|
+ pageNum.value = 1
|
|
|
+ await getInfo()
|
|
|
+ cardsScrollView.value.scrollTo({
|
|
|
+ top: 0, // 减去顶部偏移
|
|
|
+ animated: true
|
|
|
})
|
|
|
}
|
|
|
-function debouncedOnScroll(e: any) {
|
|
|
- console.log(e);
|
|
|
- scrollLeft.value = e.detail.scrollLeft
|
|
|
- dataList.value.forEach(async (category) => {
|
|
|
- const selector = `.category-${category.id}`
|
|
|
- await uni.createSelectorQuery().selectAll(selector).boundingClientRect().exec((rects) => {
|
|
|
- for (const rect of rects[0]) {
|
|
|
- if (rect.left <= (dict.getWindowHeight() / 2) && rect.left > 0) {
|
|
|
- catalog.value = category
|
|
|
- return
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
+async function getInfo() {
|
|
|
+ const res = await getSubjectCoursePage({
|
|
|
+ catalogId: catalog.value?.id, pageNum: pageNum.value, dataScope: {
|
|
|
+ sortBy: 'asc',
|
|
|
+ sortName: 'sort_num',
|
|
|
+ },
|
|
|
})
|
|
|
+ courseList.value = res.rows || []
|
|
|
}
|
|
|
-function handleDetail(item: SubjectCatalogResult) {
|
|
|
- // if (!item.payFlag && !item.trialPlay) {
|
|
|
- // uni.showToast({
|
|
|
- // title: '请先购买',
|
|
|
- // icon: 'none'
|
|
|
- // })
|
|
|
- // return
|
|
|
- // }
|
|
|
- router.push({
|
|
|
- path: "/pages/english/detail",
|
|
|
- query: {
|
|
|
- id: item.id,
|
|
|
- }
|
|
|
- });
|
|
|
+async function handleScrollToLower() {
|
|
|
+
|
|
|
}
|
|
|
-const userInfo = computed(() => user.info.value?.userInfo)
|
|
|
+onMounted(async () => {
|
|
|
+ await getDataList()
|
|
|
+ isLoading.value = false
|
|
|
+})
|
|
|
</script>
|
|
|
-
|
|
|
<template>
|
|
|
<Loading v-show="isLoading" />
|
|
|
<cl-page v-show="!isLoading">
|
|
|
<Back />
|
|
|
+ <!-- 顶部标题栏 -->
|
|
|
<image mode="aspectFill" src="https://oss.xiaoxiongcode.com/static/home/math-bg.png" alt=""
|
|
|
class="w-full h-full object-cover" />
|
|
|
- <!-- 精灵图动画 -->
|
|
|
- <cl-image src="https://oss.xiaoxiongcode.com/static/home/3.gif" mode="heightFix"
|
|
|
- class="!absolute bottom-0 left-0 !w-[44vh] !h-[55vh] z-[1]" />
|
|
|
- <view>
|
|
|
-
|
|
|
- </view>
|
|
|
- <!-- 顶部右侧光标签 -->
|
|
|
- <view class="light-tag" @tap="visible = true">
|
|
|
- <image class="light-icon" v-if="catalog?.fileList?.[0]?.url" :src="config.baseUrl + catalog?.fileList?.[0]?.url">
|
|
|
- </image>
|
|
|
- <text class="light-text">{{ catalog?.name }}</text>
|
|
|
- <cl-icon name="arrow-left-right-line" color="primary"></cl-icon>
|
|
|
- </view>
|
|
|
- <view class="boxs">
|
|
|
- <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false" ref="cardsScrollView"
|
|
|
- @scroll="debouncedOnScroll">
|
|
|
- <view class="scroll-view-item_H bg-[white]" v-for="course in courseList || []"
|
|
|
- :class="`category-${course.catalogId}`" :key="course.id" @tap="handleDetail(course)">
|
|
|
- <cl-image :src="course?.ossIconPath" mode="heightFix"
|
|
|
- class="!w-full !h-[26vh] mb-[2px] rounded-xl"></cl-image>
|
|
|
- <cl-text ellipsis :pt="{
|
|
|
- className: '!text-[4vh] !font-bold'
|
|
|
- }">{{
|
|
|
- course.sortNum }}、{{
|
|
|
- course.mainTitle }}</cl-text>
|
|
|
- <cl-text ellipsis color="#666" :pt="{
|
|
|
- className: '!text-[3vh]'
|
|
|
- }">{{
|
|
|
- course.assistantTitle }}</cl-text>
|
|
|
- <!-- <view>
|
|
|
- <Progress :progress="30" />
|
|
|
- </view> -->
|
|
|
- <Lock v-if="userInfo?.memberLevel === 'default'" :record="course" type="vip" />
|
|
|
- </view>
|
|
|
- </scroll-view>
|
|
|
- </view>
|
|
|
- <!-- <view class="footer">
|
|
|
- <view>
|
|
|
- <cl-image src="https://oss.xiaoxiongcode.com/static/home/4.png" mode="heightFix"
|
|
|
- class=" !h-[40px] mb-[2px] rounded-xl"></cl-image>
|
|
|
- <text class="text-[14px] text-white font-bold text-stroke-custom">虚拟实验</text>
|
|
|
+ <view class="content">
|
|
|
+ <!-- 左侧导航菜单 -->
|
|
|
+ <view class="w-[20vw] h-[100vh] bg-[#116FE988] pt-[70px] pb-[20px] px-[20px]">
|
|
|
+ <scroll-view direction="vertical" :show-scrollbar="false" class="sidebar" @scrolltolower="handleScrollToLower">
|
|
|
+ <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 }}
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
- <view>
|
|
|
- <cl-image src="https://oss.xiaoxiongcode.com/static/home/5.png" mode="heightFix"
|
|
|
- class=" !h-[40px] mb-[2px] rounded-xl"></cl-image>
|
|
|
- <text class="text-[14px] text-white font-bold text-stroke-custom">我的收获</text>
|
|
|
- </view>
|
|
|
- <view>
|
|
|
- <cl-image src="https://oss.xiaoxiongcode.com/static/home/6.png" mode="heightFix"
|
|
|
- class=" !h-[40px] mb-[2px] rounded-xl"></cl-image>
|
|
|
- <text class="text-[14px] text-white font-bold text-stroke-custom">学习报告</text>
|
|
|
- </view>
|
|
|
- </view> -->
|
|
|
- <cl-popup v-model="visible" :show-header="false" direction="center" :size="600">
|
|
|
- <view class="p-4">
|
|
|
- <cl-row :gutter="0">
|
|
|
- <cl-col :span="6" v-for="item in dataList || []" :key="item.id" :pt="{
|
|
|
- className: '!p-2'
|
|
|
- }" @tap="handleSelect(item)">
|
|
|
- <view class="select-item" :class="{ selected: item.id === catalog?.id }">
|
|
|
- <image :src="config.baseUrl + item?.fileList?.[0]?.url" class="w-[30rpx] h-[30rpx] mb-[2px]"></image>
|
|
|
- <text>{{ item.name }}</text>
|
|
|
+ <!-- 右侧卡片网格 -->
|
|
|
+ <view class="cards-container">
|
|
|
+ <view class="header">
|
|
|
+ {{ catalog?.name }}
|
|
|
+ </view>
|
|
|
+ <scroll-view direction="vertical" :show-scrollbar="false" class="cards" ref="cardsScrollView"
|
|
|
+ @scroll="onScroll">
|
|
|
+ <view class="grid grid-cols-4 gap-4">
|
|
|
+ <view class="card" v-for="card in courseList" :key="card.id" :class="`category-${card.catalogId}`"
|
|
|
+ ref="categoryRefs">
|
|
|
+ <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>
|
|
|
</view>
|
|
|
- </cl-col>
|
|
|
- </cl-row>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
</view>
|
|
|
- </cl-popup>
|
|
|
+ </view>
|
|
|
</cl-page>
|
|
|
</template>
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
<style lang="scss" scoped>
|
|
|
-.boxs {
|
|
|
- @apply w-[calc(100vw-52vh)] h-[50vh] absolute top-1/2 left-[50vh] z-[1];
|
|
|
- transform: translateY(-50%);
|
|
|
+.header {
|
|
|
+ @apply absolute left-1/2 top-5 text-[#333] font-bold text-xl px-4 py-1 rounded-full;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ text-align: center;
|
|
|
+ background-color: #fff;
|
|
|
+
|
|
|
+ .title-tabs {
|
|
|
+ @apply flex flex-row items-center justify-center rounded-full;
|
|
|
+ background-color: #9AD2FA;
|
|
|
+
|
|
|
+ .tab {
|
|
|
+ @apply rounded-full;
|
|
|
+ padding: 5px 10px;
|
|
|
+ font-size: 16px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: white;
|
|
|
+ color: #1E88E5;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.scroll-view_H {
|
|
|
+.content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- flex-direction: row;
|
|
|
-}
|
|
|
-
|
|
|
-.scroll-view-item_H {
|
|
|
- @apply w-[40vh] h-[45vh] mr-[20px] rounded-2xl border-[5px] border-[#1D4BD9] border-solid border-b-[10px] p-1 flex items-center justify-between pb-[20px] relative;
|
|
|
-}
|
|
|
|
|
|
-.light-tag {
|
|
|
- @apply absolute top-3 left-1/2 z-[1] flex flex-row items-center bg-white px-3 py-2 font-bold rounded-full shadow-md;
|
|
|
- transform: translateX(-50%);
|
|
|
+ .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;
|
|
|
+ font-size: 1.5vw;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background-color: rgba(255, 255, 255, 1);
|
|
|
+ color: #1E88E5;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
|
|
|
- .light-icon {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- margin-right: 3px;
|
|
|
+ .sidebar-icon {
|
|
|
+ font-size: 28px;
|
|
|
+ margin-right: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .light-text {
|
|
|
- font-size: 16px;
|
|
|
- min-width: 100px;
|
|
|
- padding-right: 5px;
|
|
|
- }
|
|
|
-}
|
|
|
+ .cards-container {
|
|
|
+ @apply relative;
|
|
|
+ flex: 1;
|
|
|
+ width: 100%;
|
|
|
+ padding: 20px;
|
|
|
+ padding-top: 70px;
|
|
|
+ // background: linear-gradient(0deg, #2EB2FD, #0B85F4);
|
|
|
|
|
|
-.select-item {
|
|
|
- @apply flex items-center justify-center rounded-xl border-[3px] border-[#1D4BD9] border-solid border-b-[5px] px-4 py-2 font-bold;
|
|
|
-}
|
|
|
+ .cards {
|
|
|
+ height: calc(100vh - 90px);
|
|
|
+ }
|
|
|
|
|
|
-.selected {
|
|
|
- @apply border-green-500;
|
|
|
-}
|
|
|
+ .category-section {
|
|
|
+ margin-bottom: 40px;
|
|
|
|
|
|
-.footer {
|
|
|
- @apply absolute bottom-2 right-5 z-[1] flex flex-row items-center justify-center gap-4;
|
|
|
-}
|
|
|
+ .category-title {
|
|
|
+ @apply text-white font-bold text-xl mb-4;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-.text-stroke-custom {
|
|
|
- color: white;
|
|
|
- text-shadow:
|
|
|
- /* 左上角投影 */
|
|
|
- -1px -1px 0 #1D4BD9,
|
|
|
- /* 右上角投影 */
|
|
|
- 1px -1px 0 #1D4BD9,
|
|
|
- /* 左下角投影 */
|
|
|
- -1px 1px 0 #1D4BD9,
|
|
|
- /* 右下角投影 */
|
|
|
- 1px 1px 0 #1D4BD9;
|
|
|
+ .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>
|