feat: 接入内容服务端真实接口并优化页面交互
- 迁移内容数据到服务端接口,移除本地模拟数据 - 首页新增推荐课程加载与未读消息检查 - 资讯、课程、搜索页面支持分页加载与错误重试 - 登录页新增恢复课程、消息、帮助页的路由逻辑 - 评估分析页新增处理中状态动效与阶段提示 - API 环境切换到正式域名 airz.hqling.cloud
This commit is contained in:
@@ -15,11 +15,16 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="lesson-section">
|
||||
<view class="section-head"><text class="section-title">{{ activeTopic == '' ? '精选课程' : activeTopicTitle }}</text><view class="all-lessons" @click="activeTopic = ''"><text class="lesson-count">{{ activeTopic == '' ? lessons.length + ' 门课程' : '查看全部' }}</text></view></view>
|
||||
<view class="section-head"><text class="section-title">{{ activeTopic == '' ? '精选课程' : activeTopicTitle }}</text><view class="all-lessons" @click="selectTopic('')"><text class="lesson-count">{{ activeTopic == '' ? total + ' 门课程' : '查看全部' }}</text></view></view>
|
||||
<view v-if="loading" class="content-state"><text>正在加载课程…</text></view>
|
||||
<view v-else-if="error.length > 0" class="content-state"><text>{{ error }}</text><text class="content-retry" @click="reload">重新加载</text></view>
|
||||
<view v-else-if="lessons.length == 0" class="content-state"><text>暂无课程</text></view>
|
||||
<view class="lesson-card" v-for="item in lessons" :key="item.id" @click="openLesson(item.id)">
|
||||
<image :src="item.image" mode="aspectFill" class="lesson-card-image"></image>
|
||||
<view class="lesson-copy"><text class="lesson-title">{{ item.title }}</text><text class="lesson-desc">{{ item.desc }}</text><view class="lesson-meta"><text class="lesson-duration">{{ item.duration }}</text><text class="lesson-tag">{{ item.tag }}</text></view></view>
|
||||
</view>
|
||||
<text v-if="moreError.length > 0" class="content-state">{{ moreError }}</text>
|
||||
<text v-if="hasMore" class="content-retry" @click="more">{{ loadingMore ? '正在加载…' : '加载更多' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -27,28 +32,51 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { usePageLayout } from '@/services/layout.uts'
|
||||
import { financeLessons, FinanceLesson } from '@/packages/content/services/content.uts'
|
||||
import AppHeader from '@/components/AppHeader.uvue'
|
||||
import AppIcon from '@/components/AppIcon.uvue'
|
||||
const { contentStyle, pageStyle } = usePageLayout()
|
||||
type LessonTopic = { id: string, title: string, desc: string, icon: string }
|
||||
const topics: LessonTopic[] = [
|
||||
{ id: 'credit', title: '个人信用提升', desc: '了解信用记录与还款能力', icon: 'user' },
|
||||
{ id: 'business', title: '小微经营融资', desc: '梳理经营材料与融资成本', icon: 'building' },
|
||||
{ id: 'documents', title: '材料准备清单', desc: '按申请场景,逐项检查资料', icon: 'file-text' }
|
||||
]
|
||||
const activeTopic = ref('')
|
||||
const activeTopicTitle = computed((): string => topics.find((item: LessonTopic): boolean => item.id == activeTopic.value)?.title ?? '精选课程')
|
||||
const lessons = computed((): FinanceLesson[] => {
|
||||
if (activeTopic.value == '') return financeLessons
|
||||
return financeLessons.filter((item: FinanceLesson): boolean => item.topics.includes(activeTopic.value))
|
||||
})
|
||||
const selectTopic = (id: string): void => { activeTopic.value = activeTopic.value == id ? '' : id }
|
||||
const openLesson = (id: string): void => { uni.navigateTo({ url: '/packages/content/lesson/lesson?id=' + id }) }
|
||||
import { usePageLayout } from '@/services/layout.uts'
|
||||
import { fetchLessons, FinanceLesson } from '@/services/content.uts'
|
||||
import AppHeader from '@/components/AppHeader.uvue'
|
||||
import AppIcon from '@/components/AppIcon.uvue'
|
||||
const { contentStyle, pageStyle } = usePageLayout()
|
||||
type LessonTopic = { id: string, title: string, desc: string, icon: string }
|
||||
const topics: LessonTopic[] = [
|
||||
{ id: 'credit', title: '个人信用提升', desc: '了解信用记录与还款能力', icon: 'user' },
|
||||
{ id: 'business', title: '小微经营融资', desc: '梳理经营材料与融资成本', icon: 'building' },
|
||||
{ id: 'documents', title: '材料准备清单', desc: '按申请场景,逐项检查资料', icon: 'file-text' }
|
||||
]
|
||||
const activeTopic = ref(''); const lessons = ref<FinanceLesson[]>([]); const total = ref(0); const hasMore = ref(false)
|
||||
const loading = ref(false); const loadingMore = ref(false); const error = ref(''); const moreError = ref('')
|
||||
let generation = 0; let visible = false; let page = 1
|
||||
const activeTopicTitle = computed((): string => topics.find((item: LessonTopic): boolean => item.id == activeTopic.value)?.title ?? '精选课程')
|
||||
async function load(append: boolean) {
|
||||
if (!visible || loading.value || loadingMore.value || (append && !hasMore.value)) return
|
||||
const request = append ? generation : ++generation
|
||||
if (append) { loadingMore.value = true; moreError.value = '' }
|
||||
else { loading.value = true; error.value = ''; lessons.value = []; total.value = 0; hasMore.value = false; page = 1 }
|
||||
try {
|
||||
const result = await fetchLessons(activeTopic.value, append ? page + 1 : 1)
|
||||
if (!visible || request != generation) return
|
||||
lessons.value = append ? lessons.value.concat(result.items) : result.items
|
||||
total.value = result.total; hasMore.value = result.hasMore
|
||||
if (append) page++
|
||||
} catch (err) {
|
||||
if (visible && request == generation) {
|
||||
const message = err instanceof Error ? err.message : '课程加载失败,请重试'
|
||||
if (append) moreError.value = message; else error.value = message
|
||||
}
|
||||
} finally { if (request == generation) { loading.value = false; loadingMore.value = false } }
|
||||
}
|
||||
function reload() { load(false) }
|
||||
const more = () => { load(true) }
|
||||
const selectTopic = (id: string) => { generation++; loading.value = false; loadingMore.value = false; activeTopic.value = activeTopic.value == id ? '' : id; reload() }
|
||||
const openLesson = (id: string) => { uni.navigateTo({ url: '/packages/content/lesson/lesson?id=' + id }) }
|
||||
onShow(() => { visible = true; loading.value = false; loadingMore.value = false; reload() })
|
||||
onHide(() => { visible = false; generation++ })
|
||||
onUnload(() => { visible = false; generation++ })
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.content-state { padding: 30rpx 0; color: #66758a; font-size: 25rpx; line-height: 40rpx; }
|
||||
.content-retry { color: #0868f7; font-size: 25rpx; padding: 22rpx 0; }
|
||||
.classroom-content { padding: 24rpx 28rpx 48rpx; }
|
||||
.classroom-hero { border-radius: 20rpx; background-color: #e8f2ff; flex-direction: row; overflow: hidden; align-items: center; }
|
||||
.hero-copy { flex: 1; padding: 28rpx 12rpx 28rpx 26rpx; }
|
||||
|
||||
Reference in New Issue
Block a user