feat: Add "lesson" page and related components

This commit adds a new "lesson" page along with the necessary components and types. The "lesson" page displays the details of a specific lesson and includes a dynamic route parameter for the lesson name. Additionally, the commit includes updates to the `pages.json` file to configure the navigation bar title for the "lesson" page.
This commit is contained in:
Timothy Yin 2024-09-19 15:13:42 +08:00
parent 8f0c422dd8
commit 53287e4cbd
10 changed files with 163 additions and 39 deletions

3
components.d.ts vendored
View File

@ -12,8 +12,11 @@ declare module 'vue' {
WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
WdCellGroup: typeof import('wot-design-uni/components/wd-cell-group/wd-cell-group.vue')['default']
WdCollapse: typeof import('wot-design-uni/components/wd-collapse/wd-collapse.vue')['default']
WdCollapseItem: typeof import('wot-design-uni/components/wd-collapse-item/wd-collapse-item.vue')['default']
WdForm: typeof import('wot-design-uni/components/wd-form/wd-form.vue')['default']
WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
WdProgress: typeof import('wot-design-uni/components/wd-progress/wd-progress.vue')['default']
WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']
WdTabbarItem: typeof import('wot-design-uni/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']

View File

@ -1,4 +1,7 @@
import http from "@/http/HttpClient";
import { useUser } from "@/stores/useUser";
import type { PagedData } from "@/types/api/common";
import type { Lesson } from "@/types/api/lesson";
import type { User } from "@/types/api/user";
export interface LoginRequest extends Record<string, string> {
@ -25,4 +28,17 @@ export default class BussApi {
})
.then((res) => res.data);
}
static lessons(page: number = 1, limit: number = 20): Promise<PagedData<Lesson>> {
const user = useUser();
return http
.server()
.get("/lesson/task", {
headers: {
Authorization: `Bearer ${user.token}`,
},
params: { page, limit },
})
.then((res) => res.data);
}
}

View File

@ -38,9 +38,12 @@ export default class ApiClient {
if (response.data.code === 10001) {
const pages = getCurrentPages() as any[];
const user = useUser();
user.logout();
setTimeout(() => {
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" });
uni.showToast({
title: !!user.token ? "登录已过期,请重新登录" : "请先登录",
icon: "none",
});
user.logout();
}, 300);
if (
!pages[pages.length - 1].$page ||

View File

@ -20,6 +20,13 @@
"style": {
"navigationBarTitleText": "登录"
}
},
{
"name": "lesson",
"path": "pages/lesson/index",
"style": {
"navigationBarTitleText": "课程进度"
}
}
],
"tabBar": {

View File

@ -1,43 +1,70 @@
<script setup lang="ts">
import BussApi from '@/api/BussApi';
import pageWrapper from '@/components/page-wrapper.vue';
import type { Lesson } from '@/types/api/lesson';
import { useRouter } from 'uni-mini-router';
import { onMounted, ref } from 'vue';
import { useToast } from 'wot-design-uni';
const toast = useToast()
const router = useRouter()
const expandedCourse = ref(['lesson'])
const groupLessons = ref<{ [key: string]: Lesson[] }>({})
const openLessonDetail = (lessonName: string) => {
console.log(router.routes);
router.push({
name: 'lesson',
params: {
lessonName
}
})
}
onMounted(() => {
toast.loading({
msg: '加载中...'
})
BussApi.lessons().then(res => {
toast.close()
const groupData = res.data.reduce((acc: any, cur: any) => {
if (!acc[cur.m_lesson_name]) {
acc[cur.m_lesson_name] = []
}
acc[cur.m_lesson_name].push(cur)
return acc
}, {})
groupLessons.value = groupData
})
})
</script>
<template>
<page-wrapper>
<div class="content">
<img class="logo" src="/static/logo.png" />
<div class="flex flex-col items-center gap-4">
<p class="title text-4xl text-neutral-300 font-bold">
XSH PPMS
</p>
</div>
<div>
<wd-collapse v-model="expandedCourse">
<wd-collapse-item v-for="(lessons, lessonName) in groupLessons" :title="`${lessonName}`" :name="`${lessonName}`"
:key="lessonName">
<div class="w-full">
<!-- <wd-card type="rectangle" v-for="(course, i) in lessons" :title="course.course_name">
todo
</wd-card> -->
<div v-for="(course, i) in lessons" :key="i" @click="openLessonDetail(`${lessonName}`)"
class="w-full flex justify-between items-center gap-20" style="justify-content: space-between;">
<div class="">
{{ course.course_name || '无标题课程' }}
</div>
<div class="flex-1">
<wd-progress :percentage="30" />
</div>
</div>
</div>
</wd-collapse-item>
</wd-collapse>
</div>
</page-wrapper>
</template>
<script setup lang="ts">
import pageWrapper from '@/components/page-wrapper.vue';
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
}
</style>
<style></style>

View File

@ -0,0 +1,16 @@
<script lang="ts" setup>
import { useRoute } from 'uni-mini-router';
const route = useRoute()
</script>
<template>
<div>
<h1>lesson detail</h1>
<p>{{ decodeURI(route.params?.lessonName) }}</p>
</div>
</template>
<style scoped>
</style>

View File

@ -1,6 +1,7 @@
<script lang="ts" setup>
import BussApi from '@/api/BussApi';
import { useUser } from '@/stores/useUser';
import { useTabbar } from '@/stores/useTabbar';
import { useRouter } from 'uni-mini-router';
import { reactive, ref } from 'vue';
import { useToast } from 'wot-design-uni';
@ -8,6 +9,7 @@ import { useToast } from 'wot-design-uni';
const router = useRouter()
const toast = useToast()
const user = useUser()
const tab = useTabbar()
const model = reactive<{
email: string
@ -41,6 +43,7 @@ const handleSubmit = () => {
user.userinfo = res
toast.success({ msg: '登录成功' })
setTimeout(() => {
tab.activeTab = 'home'
router.pushTab('/pages/index/index')
}, 1000)
}).catch(err => {

View File

@ -4,8 +4,10 @@ import pageWrapper from '@/components/page-wrapper.vue';
import { useUser } from '@/stores/useUser';
import { useRouter } from 'uni-mini-router';
import { onMounted } from 'vue';
import { useToast } from 'wot-design-uni';
const router = useRouter()
const toast = useToast()
const user = useUser()
const logout = () => {
@ -14,7 +16,11 @@ const logout = () => {
}
onMounted(() => {
toast.loading({
msg: '加载中...'
})
BussApi.profile(user.token!).then(res => {
toast.close()
user.userinfo = res
})
})

26
src/types/api/common.ts Normal file
View File

@ -0,0 +1,26 @@
export interface PagedRequest {
page: number;
limit: number;
}
export interface PagedData<T> {
current_page: number;
data: T[];
first_page_url: string;
from: number;
last_page: number;
last_page_url: string;
links: Link[];
next_page_url: null;
path: string;
per_page: number;
prev_page_url: null;
to: number;
total: number;
}
export interface Link {
url: null | string;
label: string;
active: boolean;
}

17
src/types/api/lesson.ts Normal file
View File

@ -0,0 +1,17 @@
export interface Lesson {
id: number;
course_name: string;
m_lesson_name: string;
user_id: number | null;
schedule_status: number;
script_confirm_time: number;
video_confirm_time: number;
finish_time: number;
script_upload_time: number;
video_capture_time: number;
script_file: null;
material_file: null;
capture_file: null;
advise: null;
created_at: string;
}