提交 bc137dec authored 作者: matian's avatar matian

Merge remote-tracking branch 'origin/master'

...@@ -10,6 +10,7 @@ import AppContainer from '@/components/base/AppContainer.vue' ...@@ -10,6 +10,7 @@ import AppContainer from '@/components/base/AppContainer.vue'
import Avatar from '@/components/Avatar.vue' import Avatar from '@/components/Avatar.vue'
import modules from './modules' import modules from './modules'
import { useUserStore } from '@/stores/user'
const app = createApp(App) const app = createApp(App)
// 注册公共组件 // 注册公共组件
...@@ -22,3 +23,6 @@ app.use(router) ...@@ -22,3 +23,6 @@ app.use(router)
app.use(Vant) app.use(Vant)
app.mount('#app') app.mount('#app')
// 初始化用户信息,判断是否登录
useUserStore().getUser()
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
// 获取课程列表 // 获取课程列表
export function getCourseList(params: { page_size: number; page: number }) { export function getCourseList(params?: { page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/learning/course-list', { params }) return httpRequest.get('/api/psp/v1/learning/course-list', { params })
} }
...@@ -20,7 +20,7 @@ export function createCourseRecord(data: { ...@@ -20,7 +20,7 @@ export function createCourseRecord(data: {
chapter_id: string chapter_id: string
course_id: string course_id: string
content: string content: string
title?: string picture?: string
file?: string file?: string
}) { }) {
return httpRequest.post('/api/psp/v1/learning/upload', data) return httpRequest.post('/api/psp/v1/learning/upload', data)
...@@ -32,6 +32,6 @@ export function createCourseComment(data: { entity_id: string; to_comment_id?: s ...@@ -32,6 +32,6 @@ export function createCourseComment(data: { entity_id: string; to_comment_id?: s
} }
// 获取打卡记录评论 // 获取打卡记录评论
export function getRecordComment(params: { id: string; page_size?: number; page: number }) { export function getRecordComment(params: { id: string; page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/learning/record-comments', { params }) return httpRequest.get('/api/psp/v1/learning/record-comments', { params })
} }
...@@ -21,6 +21,7 @@ const dataset = reactive<Info>({ loading: false, page: 1, total: 0, list: [] }) ...@@ -21,6 +21,7 @@ const dataset = reactive<Info>({ loading: false, page: 1, total: 0, list: [] })
const showChapterRecord = () => { const showChapterRecord = () => {
chapterVisible.value = true chapterVisible.value = true
dataset.page = 1 dataset.page = 1
dataset.list = []
getChapterRecord() getChapterRecord()
} }
...@@ -82,8 +83,7 @@ useInfiniteScroll( ...@@ -82,8 +83,7 @@ useInfiniteScroll(
line-height: 0.28rem; line-height: 0.28rem;
} }
.chapter-view { .chapter-view {
margin-bottom: 0.56rem; padding: 0.36rem 0;
padding: 0.16rem 0;
font-size: 0.24rem; font-size: 0.24rem;
color: #033974; color: #033974;
line-height: 0.36rem; line-height: 0.36rem;
......
...@@ -8,7 +8,7 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -8,7 +8,7 @@ export const routes: Array<RouteRecordRaw> = [
children: [ children: [
{ path: '', component: () => import('./views/Index.vue') }, { path: '', component: () => import('./views/Index.vue') },
{ name: 'courseView', path: 'view/:id', component: () => import('./views/View.vue'), props: true }, { name: 'courseView', path: 'view/:id', component: () => import('./views/View.vue'), props: true },
{ path: 'publish', component: () => import('./views/Publish.vue') } { path: 'publish', component: () => import('./views/Publish.vue'), meta: { requireLogin: true } }
] ]
} }
] ]
...@@ -5,12 +5,17 @@ export function getHomeData() { ...@@ -5,12 +5,17 @@ export function getHomeData() {
return httpRequest.get('/api/psp/v1/index/index') return httpRequest.get('/api/psp/v1/index/index')
} }
// 获取导学视频列表 // 获取导学视频列表
export function getVideoList(params?: { page_size: number; page: number }) { export function getVideoList(params?: { page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/learning/video-list', { params }) return httpRequest.get('/api/psp/v1/learning/video-list', { params })
} }
// 获取导学视频详情
export function getVideoView(params: { id: string }) {
return httpRequest.get('/api/psp/v1/learning/video-view', { params })
}
// 获取课程列表 // 获取课程列表
export function getCourseList(params?: { page_size: number; page: number }) { export function getCourseList(params?: { page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/learning/course-list', { params }) return httpRequest.get('/api/psp/v1/learning/course-list', { params })
} }
......
...@@ -3,31 +3,16 @@ import { Swiper, SwiperSlide } from 'swiper/vue' ...@@ -3,31 +3,16 @@ import { Swiper, SwiperSlide } from 'swiper/vue'
import { Pagination } from 'swiper' import { Pagination } from 'swiper'
import 'swiper/css' import 'swiper/css'
import 'swiper/css/pagination' import 'swiper/css/pagination'
import type { IBanner } from '../types'
const list: Array<{ defineProps<{ list: IBanner[] }>()
url: string
imageUrl: string
}> = [
{
url: '/',
imageUrl: 'https://webapp-pub.ezijing.com/project/prp-h5/banner.png'
},
{
url: '/',
imageUrl: 'https://webapp-pub.ezijing.com/project/prp-h5/banner.png'
},
{
url: '/',
imageUrl: 'https://webapp-pub.ezijing.com/project/prp-h5/banner.png'
}
]
</script> </script>
<template> <template>
<nav class="home-banner"> <nav class="home-banner">
<Swiper pagination :modules="[Pagination]"> <Swiper pagination :modules="[Pagination]">
<SwiperSlide v-for="(item, index) in list" :key="index"> <SwiperSlide v-for="(item, index) in list" :key="index">
<img :src="item.imageUrl" /> <img :src="item.cover_page" />
</SwiperSlide> </SwiperSlide>
</Swiper> </Swiper>
</nav> </nav>
......
...@@ -8,7 +8,7 @@ import { getCourseList } from '../api' ...@@ -8,7 +8,7 @@ import { getCourseList } from '../api'
// 学习进度 // 学习进度
const dataset = ref<{ total: number; list: ICourseItem[] }>({ total: 0, list: [] }) const dataset = ref<{ total: number; list: ICourseItem[] }>({ total: 0, list: [] })
const fetchCourseList = () => { const fetchCourseList = () => {
getCourseList().then(res => { getCourseList({ page_size: 100 }).then(res => {
dataset.value = res.data dataset.value = res.data
}) })
} }
...@@ -72,6 +72,7 @@ onMounted(() => { ...@@ -72,6 +72,7 @@ onMounted(() => {
flex: 1; flex: 1;
margin-left: 0.26rem; margin-left: 0.26rem;
line-height: 0.33rem; line-height: 0.33rem;
overflow: hidden;
h5 { h5 {
font-size: 0.24rem; font-size: 0.24rem;
font-weight: 500; font-weight: 500;
......
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { Swiper, SwiperSlide } from 'swiper/vue' import { Swiper, SwiperSlide } from 'swiper/vue'
import 'swiper/css' import 'swiper/css'
import type { IVideoItem } from '../types' import type { IVideoItem } from '../types'
import { getVideoList } from '../api' import { getVideoList, getVideoView } from '../api'
// 课程导学 // 课程导学
const dataset = ref<{ total: number; list: IVideoItem[] }>({ total: 0, list: [] }) const dataset = ref<{ total: number; list: IVideoItem[] }>({ total: 0, list: [] })
const fetchVideoList = () => { const fetchVideoList = () => {
getVideoList().then(res => { getVideoList({ page_size: 100 }).then(res => {
dataset.value = res.data dataset.value = res.data
}) })
} }
onMounted(() => { onMounted(() => {
fetchVideoList() fetchVideoList()
}) })
// 查看详情
const dialogVisible = ref<boolean>(false)
const videoInfo = ref<IVideoItem>()
const videoUrl = computed(() => {
if (videoInfo.value) {
// 优先取mp4
const [first = {}] = videoInfo.value.play_info.filter(item => item.Format === 'mp4')
return first.PlayURL
}
return ''
})
const fetchVideoView = (id: string) => {
getVideoView({ id }).then(res => {
videoInfo.value = res.data
})
}
const onClick = (data: IVideoItem) => {
fetchVideoView(data.id)
dialogVisible.value = true
}
</script> </script>
<template> <template>
<swiper slides-per-view="auto" :space-between="10"> <swiper slides-per-view="auto" :space-between="10">
<swiper-slide v-for="(item, index) in dataset.list" :key="index" class="video-item"> <swiper-slide v-for="(item, index) in dataset.list" :key="index" class="video-item" @click="onClick(item)">
<img :src="item.cover_page" /> <img :src="item.cover_page" />
<h5>{{ item.course_name }}</h5> <h5>{{ item.course_name }}</h5>
<p>{{ item.pv }}播放</p> <p>{{ item.pv }}播放</p>
</swiper-slide> </swiper-slide>
</swiper> </swiper>
<van-popup v-model:show="dialogVisible" round>
<div class="video-wrap" v-if="dialogVisible">
<video controls autoplay :src="videoUrl"></video>
</div>
</van-popup>
</template> </template>
<style lang="scss"> <style lang="scss">
...@@ -54,4 +80,12 @@ onMounted(() => { ...@@ -54,4 +80,12 @@ onMounted(() => {
color: #999999; color: #999999;
} }
} }
.video-wrap {
width: 80vw;
margin: 0.2rem;
video {
width: 100%;
max-height: 80vh;
}
}
</style> </style>
...@@ -18,7 +18,7 @@ const menus: Array<{ ...@@ -18,7 +18,7 @@ const menus: Array<{
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_2.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_2.png'
}, },
{ {
path: '/query', path: '#query',
name: '权益查看', name: '权益查看',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_3.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_3.png'
}, },
......
...@@ -31,7 +31,7 @@ const onSubmitComment = (data: any, action: string) => { ...@@ -31,7 +31,7 @@ const onSubmitComment = (data: any, action: string) => {
<template> <template>
<AppCard title="陪伴问答" id="qa"> <AppCard title="陪伴问答" id="qa">
<template #header-aside> <template #header-aside>
<div class="button">发表问答</div> <div class="button"><router-link to="/qa/publish">发表问答</router-link></div>
</template> </template>
<PublishItem v-for="item in data.list" :data="item" :key="item.id" @submitComment="onSubmitComment"></PublishItem> <PublishItem v-for="item in data.list" :data="item" :key="item.id" @submitComment="onSubmitComment"></PublishItem>
</AppCard> </AppCard>
......
...@@ -12,6 +12,7 @@ export interface IBanner { ...@@ -12,6 +12,7 @@ export interface IBanner {
id: string id: string
title: string title: string
pv: string pv: string
cover_page: string
} }
export interface IDocItem { export interface IDocItem {
......
...@@ -5,6 +5,7 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -5,6 +5,7 @@ export const routes: Array<RouteRecordRaw> = [
{ {
path: '/my', path: '/my',
component: AppLayout, component: AppLayout,
meta: { requireLogin: true },
children: [{ path: '', component: () => import('./views/Index.vue') }] children: [{ path: '', component: () => import('./views/Index.vue') }]
} }
] ]
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { getMyInfo } from '../api' import { getMyInfo } from '../api'
import { logout } from '@/api/base'
const info = ref() const info = ref()
const teamInfo = ref() const teamInfo = ref()
const fetchMyInfo = () => { const fetchMyInfo = () => {
...@@ -50,6 +50,13 @@ const menus: Array<{ ...@@ -50,6 +50,13 @@ const menus: Array<{
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/my_menu_6.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/my_menu_6.png'
} }
] ]
// 退出登录
const onLogout = () => {
logout().then(() => {
window.location.href = '/'
})
}
</script> </script>
<template> <template>
<div class="my" v-if="info"> <div class="my" v-if="info">
...@@ -79,7 +86,7 @@ const menus: Array<{ ...@@ -79,7 +86,7 @@ const menus: Array<{
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="logout">退出登录</div> <div class="logout" @click="onLogout">退出登录</div>
</div> </div>
</template> </template>
......
import httpRequest from '@/utils/axios'
// 获取问答列表
export function getQuestionList(params?: { page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/question/list', { params })
}
// 获取问答详情
export function getQuestionView(params: { id: string; page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/question/view', { params })
}
// 发布问答
export function createQuestion(data: { title: string; desc: string; picture?: string; file?: string }) {
return httpRequest.post('/api/psp/v1/question/create-question', data)
}
// 发布问答评论
export function createQuestionComment(data: { question_id: string; to_comment_id?: string; content: string }) {
return httpRequest.post('/api/psp/v1/question/create-comment', data)
}
...@@ -5,6 +5,9 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -5,6 +5,9 @@ export const routes: Array<RouteRecordRaw> = [
{ {
path: '/qa', path: '/qa',
component: AppLayout, component: AppLayout,
children: [{ path: '', component: () => import('./views/Index.vue') }] children: [
{ path: '', component: () => import('./views/Index.vue') },
{ path: 'publish', component: () => import('./views/Publish.vue'), meta: { requireLogin: true } }
]
} }
] ]
<script setup lang="ts">
import { reactive } from 'vue'
import { useRouter } from 'vue-router'
import { createQuestion } from '../api'
import UploadImageList from '@/components/UploadImageList.vue'
import { Toast } from 'vant'
const router = useRouter()
const form = reactive({ title: '', desc: '', picture: [] })
function onSubmit() {
const params = Object.assign({}, form, { picture: JSON.stringify(form.picture) })
createQuestion(params).then(() => {
Toast.success('发布成功')
router.push({ path: '/#qa' })
})
}
</script>
<template>
<AppContainer title="发布问题" backgroundColor="#fff" headerAlign="center">
<van-form @submit="onSubmit">
<van-field
v-model="form.title"
placeholder="请输入问题标题"
:rules="[{ required: true, message: '请输入问题标题' }]"
/>
<van-field
v-model="form.desc"
type="textarea"
placeholder="请输入问题内容"
:autosize="{ minHeight: 200 }"
:rules="[{ required: true, message: '请输入问题内容' }]"
/>
<van-field>
<template #input>
<UploadImageList v-model="form.picture"></UploadImageList>
</template>
</van-field>
<van-button block round native-type="submit" class="my-button">发布</van-button>
</van-form>
</AppContainer>
</template>
<style lang="scss" scoped>
:deep(.van-cell) {
padding-left: 0;
padding-right: 0;
&::after {
left: 0;
right: 0;
}
}
.my-button {
margin: 1rem 0;
}
</style>
import httpRequest from '@/utils/axios'
// 获取团队列表
export function getTeamList() {
return httpRequest.get('/api/psp/v1/team/list')
}
<template>
<ul class="list-card">
<li v-for="(item, index) in list" :key="index">
<div class="team-order">
<div class="order">{{ index > 2 ? index + 1 : '' }}</div>
</div>
<div class="team-info">
<div class="title">{{ item.name }}</div>
<div class="desc">{{ item.slogan }} | {{ item.members_count }}</div>
</div>
</li>
<!-- <li>
<div class="team-order">
<div class="order"></div>
</div>
<div class="team-info">
<div class="title">团队名称1</div>
<div class="desc">团队1口号 | 3456人</div>
</div>
</li>
<li>
<div class="team-order">
<div class="order"></div>
</div>
<div class="team-info">
<div class="title">团队名称1</div>
<div class="desc">团队1口号 | 3456人</div>
</div>
</li>
<li>
<div class="team-order">
<div class="order">4</div>
</div>
<div class="team-info">
<div class="title">团队名称1</div>
<div class="desc">团队1口号 | 3456人</div>
</div>
</li> -->
</ul>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { getTeamList } from '../api'
// interface List {}
let list = ref<{
name: string,
slogan: string,
members_count: string
}[]>([])
function fetchTeamList() {
getTeamList().then(res => {
list.value = res.data.list.list
})
}
onMounted(() => {
fetchTeamList()
})
</script>
<style lang="scss" scoped>
.list-card{
background: #FFFFFF;
border-radius: 0.15rem;
opacity: 1;
li{
border-bottom: 0.01rem solid #E4E4E4;
padding: 0.35rem 0;
display: flex;
.team-order{
width: 1.8rem;
display: flex;
justify-content: center;
align-items: center;
.order{
position: relative;
font-size: 0.4rem;
// font-weight: bold;
color: #E68626;
&::before{
content: '';
position: absolute;
bottom: 0.05rem;
left: 50%;
opacity: .5;
transform: translateX(-50%);
background: linear-gradient(270deg, #FFEAE6 0%, #FFFFFF 100%);
width: 0.7rem;
height: 0.1rem;
border-radius: 25%;
}
}
}
&:last-child{
border: none;
}
&:nth-child(1){
.order{
width: 0.54rem;
height: 0.6rem;
background: url(https://webapp-pub.ezijing.com/project/prp-h5/team-rk1.png);
background-size: 100% 100%;
&::before{
display: none;
}
}
}
&:nth-child(2){
.order{
width: 0.54rem;
height: 0.6rem;
background: url(https://webapp-pub.ezijing.com/project/prp-h5/team-rk2.png);
background-size: 100% 100%;
&::before{
display: none;
}
}
}
&:nth-child(3){
.order{
width: 0.54rem;
height: 0.6rem;
background: url(https://webapp-pub.ezijing.com/project/prp-h5/team-rk3.png);
background-size: 100% 100%;
&::before{
display: none;
}
}
}
.team-info{
.title{
font-size: 0.3rem;
font-weight: 400;
line-height: 100%;
color: #4E4E4E;
}
.desc{
font-size: 0.26rem;
line-height: 100%;
margin-top: .1rem;
color: #999999;
}
}
}
}
</style>
\ No newline at end of file
<script setup lang="ts"></script>
<template> <template>
<AppContainer title="团队总榜"></AppContainer> <AppContainer title="团队总榜">
<div class="team-handle">
<div class="btn-box">
<img src="https://webapp-pub.ezijing.com/project/prp-h5/team-h2.png">
<div class="b-text">团队创建</div>
</div>
<div class="btn-box">
<img src="https://webapp-pub.ezijing.com/project/prp-h5/team-h1.png">
<div class="b-text">我的团队</div>
</div>
</div>
<TeamList></TeamList>
</AppContainer>
</template> </template>
<script setup lang="ts">
import TeamList from '../components/TeamList.vue'
</script>
<style lang="scss">
.team-handle{
display: flex;
padding: 0.46rem 0 0.42rem;
justify-content: space-evenly;
.btn-box{
img{
width: 0.73rem;
display: block;
margin: 0 auto;
}
.b-text{
margin-top: 0.15rem;
font-size: 0.28rem;
color: #666666;
line-height: 100%;
}
&:nth-child(2){
img{
width: 0.63rem;
}
}
}
}
</style>
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
// import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)*', redirect: '/' }] routes: [{ path: '/:pathMatch(.*)*', redirect: '/' }]
}) })
// router.beforeEach((to, from, next) => { router.beforeEach(async (to, from, next) => {
// const user = useUserStore() const user = useUserStore()
// user.getUser() if (to.meta.requireLogin && !user.isLogin) {
// next() location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
// }) }
next()
})
export default router export default router
...@@ -7,7 +7,7 @@ export interface IVideoItem { ...@@ -7,7 +7,7 @@ export interface IVideoItem {
course_name: string course_name: string
aliyun_video_id: string aliyun_video_id: string
pv: string pv: string
play_info: object play_info: any[]
} }
// 课程 // 课程
...@@ -30,7 +30,7 @@ export interface ICourseItem { ...@@ -30,7 +30,7 @@ export interface ICourseItem {
export interface ICourseChapters { export interface ICourseChapters {
big_total: string big_total: string
small_total: string small_total: string
list: [] list: any[]
} }
// 课程老师 // 课程老师
......
...@@ -2,7 +2,7 @@ import axios from 'axios' ...@@ -2,7 +2,7 @@ import axios from 'axios'
import qs from 'qs' import qs from 'qs'
const httpRequest = axios.create({ const httpRequest = axios.create({
// baseURL: 'https://learn-api.ezijing.com', baseURL: 'https://project-api.ezijing.com',
timeout: 60000, timeout: 60000,
withCredentials: true, withCredentials: true,
headers: { headers: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论