提交 ac0b4e8e authored 作者: 王鹏飞's avatar 王鹏飞

updates

上级 1e4ffbda
...@@ -12,20 +12,23 @@ ...@@ -12,20 +12,23 @@
"deploy": "node ./deploy.js" "deploy": "node ./deploy.js"
}, },
"dependencies": { "dependencies": {
"@vueuse/core": "^8.2.6",
"axios": "^0.26.1", "axios": "^0.26.1",
"blueimp-md5": "^2.19.0", "blueimp-md5": "^2.19.0",
"lodash-es": "^4.17.21",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"qs": "^6.10.3", "qs": "^6.10.3",
"sass": "^1.50.0", "sass": "^1.50.0",
"swiper": "^8.1.0", "swiper": "^8.1.0",
"vant": "^3.4.7", "vant": "^3.4.7",
"vue": "^3.2.32", "vue": "^3.2.33",
"vue-router": "^4.0.14" "vue-router": "^4.0.14"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.1.2", "@rushstack/eslint-patch": "^1.1.3",
"@types/blueimp-md5": "^2.18.0", "@types/blueimp-md5": "^2.18.0",
"@types/node": "^17.0.23", "@types/lodash-es": "^4.17.6",
"@types/node": "^17.0.24",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.3.1", "@vitejs/plugin-vue": "^2.3.1",
"@vue/eslint-config-typescript": "^10.0.0", "@vue/eslint-config-typescript": "^10.0.0",
...@@ -33,8 +36,8 @@ ...@@ -33,8 +36,8 @@
"eslint": "^8.13.0", "eslint": "^8.13.0",
"eslint-plugin-vue": "^8.6.0", "eslint-plugin-vue": "^8.6.0",
"typescript": "~4.6.3", "typescript": "~4.6.3",
"vite": "^2.9.4", "vite": "^2.9.5",
"vite-plugin-checker": "^0.4.5", "vite-plugin-checker": "^0.4.6",
"vue-tsc": "^0.34.6" "vue-tsc": "^0.34.6"
} }
} }
差异被折叠。
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { ref, computed, nextTick } from 'vue'
import { ImagePreview } from 'vant' import { ImagePreview } from 'vant'
const props = defineProps<{ data: any }>() const props = defineProps<{ data: any }>()
const emit = defineEmits(['submitComment', 'load'])
const imageList = computed<string[]>(() => { const imageList = computed<string[]>(() => {
try { try {
...@@ -10,9 +11,41 @@ const imageList = computed<string[]>(() => { ...@@ -10,9 +11,41 @@ const imageList = computed<string[]>(() => {
return [] return []
} }
}) })
const onImagePreview = function (index: number) { // 图片预览
const onImagePreview = (index: number) => {
ImagePreview({ images: imageList.value, startPosition: index }) ImagePreview({ images: imageList.value, startPosition: index })
} }
// 评论
const commentInput = ref<HTMLElement | null>(null)
const commentVisible = ref<boolean>(false)
const commentValue = ref<string>()
const commentActive = ref<any>()
const commentPlaceholder = computed(() => {
return commentActive.value ? `回复${commentActive.value?.user_info?.name}:` : '评论'
})
// 显示评论输入框
const showComment = (data?: any) => {
commentActive.value = data
commentVisible.value = true
nextTick(() => {
commentInput.value?.focus()
})
}
const hideComment = () => {
commentVisible.value = false
commentValue.value = ''
}
// 提交评论
const onSubmitComment = (data: any) => {
data.comment = commentActive.value ? commentPlaceholder.value + data.comment : data.comment
emit(
'submitComment',
Object.assign({}, commentActive.value || props.data, data),
commentActive.value ? 'reply' : 'comment'
)
hideComment()
}
</script> </script>
<template> <template>
...@@ -30,18 +63,32 @@ const onImagePreview = function (index: number) { ...@@ -30,18 +63,32 @@ const onImagePreview = function (index: number) {
</ul> </ul>
<div class="publish-tools"> <div class="publish-tools">
<p class="t1">{{ data.created_time }}</p> <p class="t1">{{ data.created_time }}</p>
<p class="t2">评论</p> <p class="t2" @click="showComment()">评论</p>
</div> </div>
<!-- 评论 --> <!-- 评论 -->
<div class="publish-comments" v-if="data.comments.total"> <div class="publish-comments" v-if="data.comments.total">
<div class="comment-item" v-for="item in data.comments.list" :key="item.id"> <div class="comment-item" v-for="item in data.comments.list" :key="item.id" @click="showComment(item)">
<div class="comment-item-hd">{{ item.user_name }}</div> <div class="comment-item-hd">{{ item.user_name }}</div>
<div class="comment-item-bd">{{ item.content }}</div> <div class="comment-item-bd">{{ item.content }}</div>
</div> </div>
<div class="comment-more">查看{{ data.comments.total }}条评论 <van-icon name="arrow" /></div> <div class="comment-more" v-if="data.comments.total > data.comments.list.length" @click="$emit('load')">
查看{{ data.comments.total }}条评论 <van-icon name="arrow" />
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="comment" v-if="commentVisible">
<van-form @submit="onSubmitComment">
<van-field
v-model="commentValue"
name="comment"
:placeholder="commentPlaceholder"
autosize
ref="commentInput"
@blur="hideComment"
/>
</van-form>
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
...@@ -103,9 +150,13 @@ const onImagePreview = function (index: number) { ...@@ -103,9 +150,13 @@ const onImagePreview = function (index: number) {
color: #999999; color: #999999;
} }
.t2 { .t2 {
padding-left: 0.38rem;
font-size: 0.24rem; font-size: 0.24rem;
color: #999999; color: #999999;
line-height: 0.36rem; line-height: 0.36rem;
background: url('https://webapp-pub.ezijing.com/project/prp-h5/icon_comment.png') no-repeat left center;
background-size: 0.26rem;
cursor: pointer;
} }
} }
.publish-comments { .publish-comments {
...@@ -136,4 +187,12 @@ const onImagePreview = function (index: number) { ...@@ -136,4 +187,12 @@ const onImagePreview = function (index: number) {
border-top: 0.01rem solid #d3d3d3; border-top: 0.01rem solid #d3d3d3;
cursor: pointer; cursor: pointer;
} }
.comment {
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 0 29px #0000001a;
z-index: 1000;
}
</style> </style>
...@@ -14,8 +14,9 @@ export function getCourseView(params: { id: string }) { ...@@ -14,8 +14,9 @@ export function getCourseView(params: { id: string }) {
export function getChapterView(params: { chapter_id: string; page_size?: number; page?: number }) { export function getChapterView(params: { chapter_id: string; page_size?: number; page?: number }) {
return httpRequest.get('/api/psp/v1/learning/chapter-view', { params }) return httpRequest.get('/api/psp/v1/learning/chapter-view', { params })
} }
// 打卡
export function coursePublish(data: { // 课程章节打卡
export function createCourseRecord(data: {
chapter_id: string chapter_id: string
course_id: string course_id: string
content: string content: string
...@@ -24,3 +25,13 @@ export function coursePublish(data: { ...@@ -24,3 +25,13 @@ export function coursePublish(data: {
}) { }) {
return httpRequest.post('/api/psp/v1/learning/upload', data) return httpRequest.post('/api/psp/v1/learning/upload', data)
} }
// 打卡记录评论
export function createCourseComment(data: { entity_id: string; to_comment_id?: string; content: string }) {
return httpRequest.post('/api/psp/v1/learning/comment', data)
}
// 获取打卡记录评论
export function getRecordComment(params: { id: string; page_size?: number; page: number }) {
return httpRequest.get('/api/psp/v1/learning/record-comments', { params })
}
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useInfiniteScroll } from '@vueuse/core'
import ChapterItemRecord from './ChapterItemRecord.vue'
import { getChapterView } from '../api'
interface Info {
loading: boolean
page: number
total: number
list: any[]
}
const props = defineProps<{ courseId: string; data: any }>()
// 章节详情
const chapterVisible = ref<boolean>(false)
const dataset = reactive<Info>({ loading: false, page: 1, total: 0, list: [] })
// 查看本章所有打卡记录
const showChapterRecord = () => {
chapterVisible.value = true
dataset.page = 1
getChapterRecord()
}
// 获取章节打卡记录
const getChapterRecord = () => {
dataset.loading = true
getChapterView({ chapter_id: props.data.id, page: dataset.page, page_size: 10 })
.then(res => {
const { total, list } = res.data
dataset.total = total
dataset.list = dataset.list.concat(list)
if (dataset.list.length <= total) {
dataset.page++
}
})
.finally(() => {
dataset.loading = false
})
}
// 滚动加载
const el = ref<HTMLElement>()
useInfiniteScroll(
el,
() => {
!dataset.loading && getChapterRecord()
},
{ distance: 10 }
)
</script>
<template>
<div class="course-chapter-item">
<h2 class="chapter-title">{{ data.chapter_name }}</h2>
<ChapterItemRecord v-for="publish in data.records.list" :data="publish" :key="publish.id"></ChapterItemRecord>
<div class="chapter-view" @click="showChapterRecord">查看本章所有打卡记录</div>
</div>
<van-popup v-model:show="chapterVisible" round position="bottom" :style="{ height: '80%' }">
<div class="course-chapter" ref="el">
<ChapterItemRecord v-for="publish in dataset.list" :data="publish" :key="publish.id"></ChapterItemRecord>
<van-button
block
round
class="my-button button-fixed"
:to="{ path: '/course/publish', query: { course_id: courseId, chapter_id: data.id } }"
>我也要拍照得星星</van-button
>
</div>
</van-popup>
</template>
<style lang="scss">
.chapter-title {
margin-bottom: 0.22rem;
font-size: 0.28rem;
font-weight: bold;
color: #333333;
line-height: 0.28rem;
}
.chapter-view {
margin-bottom: 0.56rem;
padding: 0.16rem 0;
font-size: 0.24rem;
color: #033974;
line-height: 0.36rem;
text-align: center;
border-top: 0.01rem solid #d3d3d3;
cursor: pointer;
}
.course-chapter {
height: 100%;
overflow-y: auto;
padding: 0.38rem 0.24rem 1rem;
box-sizing: border-box;
}
.button-fixed {
position: fixed;
bottom: 0.2rem;
left: 0.2rem;
right: 0.2rem;
width: auto;
}
</style>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { uniqWith } from 'lodash-es'
import PublishItem from '@/components/PublishItem.vue'
import { getRecordComment, createCourseComment } from '../api'
const props = defineProps<{ data: any }>()
const page = ref<number>(1)
const dataset = reactive(props.data)
// 数据合并
const mergeList = (arr: any[], arr2: any[]) => {
return uniqWith(arr.concat(arr2), (a, b) => a.id === b.id)
}
// 获取打卡评论
const getRecordCommentList = () => {
getRecordComment({ id: props.data.id, page: page.value, page_size: 5 }).then(res => {
const { total, list } = res.data.comments
dataset.comments.total = total
dataset.comments.list = page.value === 1 ? list : mergeList(dataset.comments.list, list)
})
}
// 评论
const onSubmitComment = (data: any, action: string) => {
const params =
action === 'comment'
? { entity_id: data.id, content: data.comment } // 评论
: { entity_id: data.entity_id, content: data.comment, to_comment_id: data.id } // 回复
createCourseComment(params).then(() => {
page.value = 1
getRecordCommentList()
})
}
const onLoadMore = () => {
page.value++
getRecordCommentList()
}
</script>
<template>
<PublishItem :data="dataset" :key="dataset.id" @submitComment="onSubmitComment" @load="onLoadMore"></PublishItem>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' import { reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { coursePublish } from '../api' import { createCourseRecord } from '../api'
import UploadImageList from '@/components/UploadImageList.vue' import UploadImageList from '@/components/UploadImageList.vue'
import { Toast } from 'vant' import { Toast } from 'vant'
const router = useRouter() const router = useRouter()
...@@ -12,7 +12,7 @@ const form = reactive({ ...{ chapter_id: '', course_id: '', content: '', picture ...@@ -12,7 +12,7 @@ const form = reactive({ ...{ chapter_id: '', course_id: '', content: '', picture
const pictureValidator = () => !!form.picture.length const pictureValidator = () => !!form.picture.length
function onSubmit() { function onSubmit() {
const params = Object.assign({}, form, { picture: JSON.stringify(form.picture) }) const params = Object.assign({}, form, { picture: JSON.stringify(form.picture) })
coursePublish(params).then(() => { createCourseRecord(params).then(() => {
Toast.success('发布成功') Toast.success('发布成功')
router.push({ name: 'courseView', params: { id: form.course_id } }) router.push({ name: 'courseView', params: { id: form.course_id } })
}) })
...@@ -40,7 +40,7 @@ function onSubmit() { ...@@ -40,7 +40,7 @@ function onSubmit() {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep .van-cell { :deep(.van-cell) {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
&::after { &::after {
......
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { Toast } from 'vant' import { Toast } from 'vant'
import PublishItem from '@/components/PublishItem.vue' import ChapterItem from '../components/ChapterItem.vue'
import { getCourseView, getChapterView } from '../api' import { getCourseView } from '../api'
const props = defineProps<{ id: string }>() const props = defineProps<{ id: string }>()
// 课程 // 课程
const data = ref() const data = ref()
function getCourse() { const getCourse = () => {
const toast = Toast.loading({ message: '加载中...', forbidClick: true }) const toast = Toast.loading({ message: '加载中...', forbidClick: true })
getCourseView({ id: props.id }).then(res => { getCourseView({ id: props.id }).then(res => {
data.value = res.data.course data.value = res.data.course
toast.clear() toast.clear()
}) })
} }
onMounted(getCourse) onMounted(() => {
getCourse()
// 章节 })
const show = ref<boolean>(false)
// 当前选中的章节
const activeChapter = ref()
// 打卡记录
const chapterRecord = ref<{ total: number; list: any[] }>({ total: 0, list: [] })
// 获取章节打卡记录
const getChapterRecord = function () {
const toast = Toast.loading({ message: '加载中...', forbidClick: true })
const params = {
chapter_id: activeChapter.value.id
}
getChapterView(params).then(res => {
chapterRecord.value = res.data
toast.clear()
})
}
const showChapterRecord = function (data: any) {
activeChapter.value = data
show.value = true
getChapterRecord()
}
</script> </script>
<template> <template>
...@@ -53,8 +33,8 @@ const showChapterRecord = function (data: any) { ...@@ -53,8 +33,8 @@ const showChapterRecord = function (data: any) {
<span>{{ data.course_chapters.small_total }}课时</span> <span>{{ data.course_chapters.small_total }}课时</span>
</li> </li>
<li class="l2"> <li class="l2">
<span>{{ data.course_chapters.big_total }}人看过</span> <span>{{ data.pv }}人看过</span>
<span>{{ data.course_chapters.small_total }}人评论</span> <span>{{ data.records_total }}人评论</span>
</li> </li>
</ul> </ul>
<div class="star"> <div class="star">
...@@ -75,26 +55,10 @@ const showChapterRecord = function (data: any) { ...@@ -75,26 +55,10 @@ const showChapterRecord = function (data: any) {
<div class="course-bottom"> <div class="course-bottom">
<div class="course-tips">如果你也是知识获得者,请晒出你的海报、说出你的感想,得到你的星星。</div> <div class="course-tips">如果你也是知识获得者,请晒出你的海报、说出你的感想,得到你的星星。</div>
<div class="course-chapters"> <div class="course-chapters">
<div class="course-chapter-item" v-for="item in data.course_chapters.list" :key="item.id"> <ChapterItem v-for="item in data.course_chapters.list" :courseId="id" :data="item" :key="item.id"></ChapterItem>
<h2 class="chapter-title">{{ item.chapter_name }}</h2>
<PublishItem v-for="publish in item.records.list" :data="publish" :key="publish.id"></PublishItem>
<div class="chapter-view" @click="showChapterRecord(item)">查看本章所有打卡记录</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<van-popup v-model:show="show" round position="bottom" :style="{ height: '80%' }">
<div class="course-chapter">
<PublishItem v-for="publish in chapterRecord.list" :data="publish" :key="publish.id"></PublishItem>
<van-button
block
round
class="my-button"
:to="{ path: '/course/publish', query: { course_id: id, chapter_id: activeChapter.id } }"
>我也要拍照得星星</van-button
>
</div>
</van-popup>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -140,12 +104,14 @@ const showChapterRecord = function (data: any) { ...@@ -140,12 +104,14 @@ const showChapterRecord = function (data: any) {
margin: 0.07rem 0; margin: 0.07rem 0;
p { p {
display: inline-block; display: inline-block;
padding: 0 0.15rem; padding: 0 0.15rem 0 0.38rem;
height: 0.3rem; height: 0.3rem;
font-size: 0.2rem; font-size: 0.2rem;
line-height: 0.3rem; line-height: 0.3rem;
border-radius: 0.15rem; border-radius: 0.15rem;
border: 0.01rem solid #80b0e5; border: 0.01rem solid #80b0e5;
background: url('https://webapp-pub.ezijing.com/project/prp-h5/icon_star.png') no-repeat 0.1rem center;
background-size: 0.22rem;
b { b {
color: #033974; color: #033974;
} }
...@@ -193,25 +159,5 @@ const showChapterRecord = function (data: any) { ...@@ -193,25 +159,5 @@ const showChapterRecord = function (data: any) {
background-color: #fff; background-color: #fff;
border-top-left-radius: 0.2rem; border-top-left-radius: 0.2rem;
border-top-right-radius: 0.2rem; border-top-right-radius: 0.2rem;
.chapter-title {
margin-bottom: 0.22rem;
font-size: 0.28rem;
font-weight: bold;
color: #333333;
line-height: 0.28rem;
}
}
.chapter-view {
margin-bottom: 0.56rem;
padding: 0.16rem 0;
font-size: 0.24rem;
color: #033974;
line-height: 0.36rem;
text-align: center;
border-top: 0.01rem solid #d3d3d3;
cursor: pointer;
}
.course-chapter {
padding: 0.38rem 0.24rem;
} }
</style> </style>
...@@ -13,3 +13,8 @@ export function getVideoList(params?: { page_size: number; page: number }) { ...@@ -13,3 +13,8 @@ export function getVideoList(params?: { page_size: number; page: number }) {
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 })
} }
// 陪伴问答评论
export function createQuestionComment(data: { question_id: string; to_comment_id?: string; content: string }) {
return httpRequest.post('/api/psp/v1/question/create-comment', data)
}
...@@ -7,12 +7,12 @@ import { getCourseList } from '../api' ...@@ -7,12 +7,12 @@ import { getCourseList } from '../api'
// 学习进度 // 学习进度
const dataset = ref<{ total: number; list: ICourseItem[] }>({ total: 0, list: [] }) const dataset = ref<{ total: number; list: ICourseItem[] }>({ total: 0, list: [] })
function fetchCourseList() { const fetchCourseList = () => {
getCourseList().then(res => { getCourseList().then(res => {
dataset.value = res.data dataset.value = res.data
}) })
} }
const studyStatus = function (progress: number) { const studyStatus = (progress: number) => {
if (progress === 100) { if (progress === 100) {
return '已完成' return '已完成'
} else if (progress > 0) { } else if (progress > 0) {
......
...@@ -7,7 +7,7 @@ import { getVideoList } from '../api' ...@@ -7,7 +7,7 @@ import { getVideoList } from '../api'
// 课程导学 // 课程导学
const dataset = ref<{ total: number; list: IVideoItem[] }>({ total: 0, list: [] }) const dataset = ref<{ total: number; list: IVideoItem[] }>({ total: 0, list: [] })
function fetchVideoList() { const fetchVideoList = () => {
getVideoList().then(res => { getVideoList().then(res => {
dataset.value = res.data dataset.value = res.data
}) })
......
<script setup lang="ts"> <script setup lang="ts">
import { Toast } from 'vant'
import PublishItem from '@/components/PublishItem.vue' import PublishItem from '@/components/PublishItem.vue'
import { createQuestionComment } from '../api'
defineProps<{ data: any }>() defineProps<{ data: any }>()
// 评论
const onSubmitComment = (data: any, action: string) => {
if (action === 'comment') {
// 评论
createQuestionComment({
question_id: data.id,
content: data.comment
}).then(() => {
Toast.success('评论成功')
})
} else {
// 回复
createQuestionComment({
question_id: data.entity_id,
content: data.comment,
to_comment_id: data.id
}).then(() => {
Toast.success('回复成功')
})
}
}
</script> </script>
<template> <template>
...@@ -8,12 +33,12 @@ defineProps<{ data: any }>() ...@@ -8,12 +33,12 @@ defineProps<{ data: any }>()
<template #header-aside> <template #header-aside>
<div class="button">发表问答</div> <div class="button">发表问答</div>
</template> </template>
<PublishItem v-for="item in data.list" :data="item" :key="item.id"></PublishItem> <PublishItem v-for="item in data.list" :data="item" :key="item.id" @submitComment="onSubmitComment"></PublishItem>
</AppCard> </AppCard>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep .publish-item { :deep(.publish-item) {
padding: 0.24rem; padding: 0.24rem;
margin-bottom: 0.2rem; margin-bottom: 0.2rem;
background: #fff; background: #fff;
......
...@@ -18,7 +18,7 @@ const data = ref<HomeInfo>({ ...@@ -18,7 +18,7 @@ const data = ref<HomeInfo>({
questions: {} questions: {}
}) })
// 获取首页数据 // 获取首页数据
function fetchHomeData() { const fetchHomeData = () => {
api.getHomeData().then(res => { api.getHomeData().then(res => {
data.value = res.data data.value = res.data
}) })
......
...@@ -4,7 +4,7 @@ import { getMyInfo } from '../api' ...@@ -4,7 +4,7 @@ import { getMyInfo } from '../api'
const info = ref() const info = ref()
const teamInfo = ref() const teamInfo = ref()
function fetchMyInfo() { const fetchMyInfo = () => {
getMyInfo().then(res => { getMyInfo().then(res => {
info.value = res.data.info || {} info.value = res.data.info || {}
teamInfo.value = res.data.team_info || { star: 0 } teamInfo.value = res.data.team_info || { star: 0 }
......
...@@ -23,7 +23,7 @@ export default defineConfig(({ mode }) => { ...@@ -23,7 +23,7 @@ export default defineConfig(({ mode }) => {
changeOrigin: true, changeOrigin: true,
rewrite: path => path.replace('/api/psp/', '/') rewrite: path => path.replace('/api/psp/', '/')
}, },
'/api': 'https://learn-api.ezijing.com' '/api': 'https://project-api.ezijing.com'
} }
}, },
resolve: { resolve: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论