|
|
@@ -1,206 +1,177 @@
|
|
|
-<script setup lang='ts'>
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { fetchSubjectAppInfo } from '@/services/subject/info'
|
|
|
+import { querySubjectCourseTest } from '@/services/subject/test'
|
|
|
+import type { SubjectCatalogResult } from '@/services/subject/catalog'
|
|
|
+import type { SubjectCourseTestResult } from '@/services/subject/test'
|
|
|
+import Lock from '@/components/lock.uvue'
|
|
|
import Back from '@/components/back.uvue'
|
|
|
import Loading from '@/components/loading.uvue'
|
|
|
-import { ref, onMounted, nextTick } from 'vue'
|
|
|
-import { type SubjectKnowledgeCardResult, getSubjectKnowledgeCard } from '@/services/subject/card'
|
|
|
-import { fetchSubjectConfigInfo } from '@/services/subject/info'
|
|
|
-import type { SubjectCatalogResult } from '@/services/subject/catalog'
|
|
|
import { config } from '@/config'
|
|
|
import { dict } from '@/.cool/store'
|
|
|
+import { router } from "@/.cool";
|
|
|
|
|
|
-const activeCategory = ref<string>()
|
|
|
-const activeTab = ref<string>('knowledge')
|
|
|
const isLoading = ref(true)
|
|
|
-const categories = ref<SubjectCatalogResult[]>([])
|
|
|
-const cards = ref<SubjectKnowledgeCardResult[]>([])
|
|
|
-const cardsScrollView = ref<any>(null)
|
|
|
-const categoryRefs = ref<any>()
|
|
|
+const visible = ref<boolean>(false)
|
|
|
+const dataList = ref<SubjectCatalogResult[]>([])
|
|
|
+const catalog = ref<SubjectCatalogResult>()
|
|
|
+const testList = ref<SubjectCourseTestResult[]>()
|
|
|
async function getDataList() {
|
|
|
const id = dict.getValueByLabelMapByType('index_subject_id')['物理']
|
|
|
- const res = await getSubjectKnowledgeCard({ subjectId: id })
|
|
|
- cards.value = res.userCardList || []
|
|
|
- categories.value = res.catalogList || []
|
|
|
+ const res = await fetchSubjectAppInfo({ id })
|
|
|
+ dataList.value = res.catalogList || []
|
|
|
+ catalog.value = res?.catalogList?.[0]
|
|
|
+ const testRes = await querySubjectCourseTest()
|
|
|
+ testList.value = testRes || []
|
|
|
+
|
|
|
}
|
|
|
-function handleSelect(categoryId: string) {
|
|
|
- activeCategory.value = categoryId
|
|
|
- uni.createSelectorQuery().select(`.category-${categoryId}`).boundingClientRect().exec((rect) => {
|
|
|
+onMounted(async () => {
|
|
|
+ try {
|
|
|
+ await getDataList()
|
|
|
+ isLoading.value = false
|
|
|
+ } catch (err) {
|
|
|
+ console.log(err);
|
|
|
+ isLoading.value = false
|
|
|
+ }
|
|
|
+})
|
|
|
+const cardsScrollView = ref<any>(null)
|
|
|
+
|
|
|
+function handleDetail(item: SubjectCourseTestResult) {
|
|
|
+ router.push({
|
|
|
+ path: "/pages/catalog/web-view",
|
|
|
+ query: {
|
|
|
+ src: item.animationPath,
|
|
|
+ progress: 2,
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+function handleSelect(val: SubjectCatalogResult) {
|
|
|
+ catalog.value = val
|
|
|
+ visible.value = false
|
|
|
+ uni.createSelectorQuery().select(`.category-${val.id}`).boundingClientRect().exec((rect) => {
|
|
|
if (cardsScrollView.value && rect[0]) {
|
|
|
cardsScrollView.value.scrollTo({
|
|
|
- top: rect[0].top - 70, // 减去顶部偏移
|
|
|
+ left: rect[0].left - 50, // 减去顶部偏移
|
|
|
animated: true
|
|
|
})
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
async function onScroll(e: any) {
|
|
|
- categories.value.forEach(async (category) => {
|
|
|
+ console.log();
|
|
|
+ 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.top <= 150 && rect.bottom >= 150) {
|
|
|
- activeCategory.value = category.id
|
|
|
+ if (rect.left <= 50) {
|
|
|
+ catalog.value = category
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
-onMounted(async () => {
|
|
|
- await getDataList()
|
|
|
- isLoading.value = false
|
|
|
- nextTick(() => {
|
|
|
- handleSelect(categories.value[0].id as string)
|
|
|
- })
|
|
|
-})
|
|
|
</script>
|
|
|
+
|
|
|
<template>
|
|
|
<Loading v-show="isLoading" />
|
|
|
<cl-page v-show="!isLoading">
|
|
|
<Back />
|
|
|
- <!-- 顶部标题栏 -->
|
|
|
- <view class="content">
|
|
|
- <!-- 左侧导航菜单 -->
|
|
|
- <scroll-view direction="vertical" :show-scrollbar="false" class="sidebar">
|
|
|
- <view v-for="category in categories" :key="category.id" class="sidebar-item"
|
|
|
- :class="{ active: activeCategory === category.id }" @tap="handleSelect(category.id as string)">
|
|
|
- <cl-image :src="config.baseUrl + category?.fileList?.[0]?.url" mode="heightFix" class="!h-[28px]"></cl-image>
|
|
|
- <text class="sidebar-text">{{ category.name }}</text>
|
|
|
+ <img src="https://oss.xiaoxiongcode.com/static/home/2.png" alt="" class="w-full h-full object-cover" />
|
|
|
+ <!-- 顶部右侧光标签 -->
|
|
|
+ <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="onScroll">
|
|
|
+ <view class="scroll-view-item_H bg-[white]" v-for="test in testList || []" :class="`category-${test.catalogId}`"
|
|
|
+ :key="test.id" @tap="handleDetail(test)" ref="categoryRefs">
|
|
|
+ <cl-image :src="test.animationJavascriptPath" mode="heightFix"
|
|
|
+ class="!w-full !h-[26vh] mb-[2px] rounded-xl"></cl-image>
|
|
|
+ <text class="text-[16px] font-bold">{{
|
|
|
+ test.name }}</text>
|
|
|
+ <text class="text-[14px] text-[#666]">{{
|
|
|
+ test.remark }}</text>
|
|
|
+ <Lock v-if="!test.lockFlag" :record="test" />
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
- <!-- 右侧卡片网格 -->
|
|
|
- <view class="cards-container">
|
|
|
- <view class="header">
|
|
|
- <view class="title-tabs">
|
|
|
- <view class="tab" :class="{ active: activeTab === 'knowledge' }" @tap="activeTab = 'knowledge'">知识卡
|
|
|
- </view>
|
|
|
- <view class="tab" :class="{ active: activeTab === 'honor' }" @tap="activeTab = 'honor'">荣誉</view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <scroll-view direction="vertical" :show-scrollbar="false" class="cards" ref="cardsScrollView"
|
|
|
- @scroll="onScroll">
|
|
|
- <view class="grid grid-cols-5 gap-4">
|
|
|
- <view class="card" v-for="card in cards" :key="card.id" :class="`category-${card.catalogId}`"
|
|
|
- ref="categoryRefs">
|
|
|
- <image v-if="card.userCardId" :src="config.baseUrl + card.iconPath" class="w-full h-full" />
|
|
|
- <image v-else src="https://oss.xiaoxiongcode.com/static/home/21.png" class="w-full h-full" />
|
|
|
- <view class="card-title">{{ card.cardName }}</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>
|
|
|
- </view>
|
|
|
- </scroll-view>
|
|
|
+ </cl-col>
|
|
|
+ </cl-row>
|
|
|
</view>
|
|
|
- </view>
|
|
|
+ </cl-popup>
|
|
|
</cl-page>
|
|
|
</template>
|
|
|
-<style lang="scss" scoped>
|
|
|
-.header {
|
|
|
- @apply absolute left-1/2 top-5;
|
|
|
- transform: translateX(-50%);
|
|
|
- text-align: center;
|
|
|
|
|
|
- .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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+<style lang="scss" scoped>
|
|
|
+.boxs {
|
|
|
+ @apply w-[calc(100vw-100px)] h-[50vh] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1];
|
|
|
}
|
|
|
|
|
|
-.content {
|
|
|
- display: flex;
|
|
|
+.scroll-view_H {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
flex-direction: row;
|
|
|
+}
|
|
|
|
|
|
- .sidebar {
|
|
|
- width: 200px;
|
|
|
- background-color: #116FE9;
|
|
|
- padding: 20px;
|
|
|
- padding-top: 70px;
|
|
|
- height: 100vh;
|
|
|
-
|
|
|
- .sidebar-item {
|
|
|
- @apply text-white rounded-full py-1 cursor-pointer flex flex-row items-center;
|
|
|
- margin-bottom: 8px;
|
|
|
- transition: all 0.3s ease;
|
|
|
- font-size: 12px;
|
|
|
- width: 100%;
|
|
|
+.scroll-view-item_H {
|
|
|
+ @apply w-[40vh] h-[50vh] mr-[20px] rounded-2xl border-[5px] border-[#1D4BD9] border-solid border-b-[10px] p-1 flex items-center relative;
|
|
|
+}
|
|
|
|
|
|
- &.active {
|
|
|
- background-color: rgba(255, 255, 255, 1);
|
|
|
- color: #1E88E5;
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
+.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-icon {
|
|
|
- font-size: 28px;
|
|
|
- margin-right: 2px;
|
|
|
- }
|
|
|
- }
|
|
|
+ .light-icon {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-right: 3px;
|
|
|
}
|
|
|
|
|
|
- .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;
|
|
|
+ .light-text {
|
|
|
+ font-size: 16px;
|
|
|
+ min-width: 100px;
|
|
|
+ padding-right: 5px;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- .category-title {
|
|
|
- @apply text-white font-bold text-xl mb-4;
|
|
|
- }
|
|
|
- }
|
|
|
+.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;
|
|
|
+}
|
|
|
|
|
|
- .card {
|
|
|
- // background-color: rgba(255, 255, 255, 0.9);
|
|
|
- // border-radius: 16px;
|
|
|
- // padding: 16px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- // box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
- aspect-ratio: 3/4;
|
|
|
+.selected {
|
|
|
+ @apply border-green-500;
|
|
|
+}
|
|
|
|
|
|
- .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;
|
|
|
+.footer {
|
|
|
+ @apply absolute bottom-2 right-5 z-[1] flex flex-row items-center justify-center gap-4;
|
|
|
+}
|
|
|
|
|
|
- .question-mark {
|
|
|
- font-size: 60px;
|
|
|
- font-weight: bold;
|
|
|
- color: #1E88E5;
|
|
|
- }
|
|
|
- }
|
|
|
+.text-stroke-custom {
|
|
|
+ color: white;
|
|
|
+ text-shadow:
|
|
|
+ /* 左上角投影 */
|
|
|
+ -1px -1px 0 #1D4BD9,
|
|
|
+ /* 右上角投影 */
|
|
|
+ 1px -1px 0 #1D4BD9,
|
|
|
+ /* 左下角投影 */
|
|
|
+ -1px 1px 0 #1D4BD9,
|
|
|
+ /* 右下角投影 */
|
|
|
+ 1px 1px 0 #1D4BD9;
|
|
|
|
|
|
- .card-title {
|
|
|
- @apply text-center font-bold absolute left-0 w-full text-[#0E3E87] text-[1.5vw];
|
|
|
- bottom: 13%;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
</style>
|