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

updates

上级 477b7357
...@@ -8,6 +8,7 @@ module.exports = { ...@@ -8,6 +8,7 @@ module.exports = {
'vue/setup-compiler-macros': true 'vue/setup-compiler-macros': true
}, },
rules: { rules: {
'vue/multi-word-component-names': 'off' 'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off'
} }
} }
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
}, },
"dependencies": { "dependencies": {
"axios": "^0.26.1", "axios": "^0.26.1",
"blueimp-md5": "^2.19.0",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"qs": "^6.10.3", "qs": "^6.10.3",
"sass": "^1.50.0", "sass": "^1.50.0",
...@@ -23,7 +24,9 @@ ...@@ -23,7 +24,9 @@
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.1.2", "@rushstack/eslint-patch": "^1.1.2",
"@types/blueimp-md5": "^2.18.0",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"@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",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
......
...@@ -2,11 +2,14 @@ lockfileVersion: 5.3 ...@@ -2,11 +2,14 @@ lockfileVersion: 5.3
specifiers: specifiers:
'@rushstack/eslint-patch': ^1.1.2 '@rushstack/eslint-patch': ^1.1.2
'@types/blueimp-md5': ^2.18.0
'@types/node': ^17.0.23 '@types/node': ^17.0.23
'@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
'@vue/tsconfig': ^0.1.3 '@vue/tsconfig': ^0.1.3
axios: ^0.26.1 axios: ^0.26.1
blueimp-md5: ^2.19.0
eslint: ^8.13.0 eslint: ^8.13.0
eslint-plugin-vue: ^8.6.0 eslint-plugin-vue: ^8.6.0
pinia: ^2.0.13 pinia: ^2.0.13
...@@ -23,6 +26,7 @@ specifiers: ...@@ -23,6 +26,7 @@ specifiers:
dependencies: dependencies:
axios: 0.26.1 axios: 0.26.1
blueimp-md5: 2.19.0
pinia: 2.0.13_typescript@4.6.3+vue@3.2.32 pinia: 2.0.13_typescript@4.6.3+vue@3.2.32
qs: 6.10.3 qs: 6.10.3
sass: 1.50.0 sass: 1.50.0
...@@ -33,7 +37,9 @@ dependencies: ...@@ -33,7 +37,9 @@ dependencies:
devDependencies: devDependencies:
'@rushstack/eslint-patch': 1.1.2 '@rushstack/eslint-patch': 1.1.2
'@types/blueimp-md5': 2.18.0
'@types/node': 17.0.23 '@types/node': 17.0.23
'@types/qs': 6.9.7
'@vitejs/plugin-vue': 2.3.1_vite@2.9.4+vue@3.2.32 '@vitejs/plugin-vue': 2.3.1_vite@2.9.4+vue@3.2.32
'@vue/eslint-config-typescript': 10.0.0_a62cbc2f4797496d74696b1f6538012a '@vue/eslint-config-typescript': 10.0.0_a62cbc2f4797496d74696b1f6538012a
'@vue/tsconfig': 0.1.3_@types+node@17.0.23 '@vue/tsconfig': 0.1.3_@types+node@17.0.23
...@@ -133,6 +139,10 @@ packages: ...@@ -133,6 +139,10 @@ packages:
resolution: {integrity: sha512-oe5WJEDaVsW8fBlGT7udrSCgOwWfoYHQOmSpnh8X+0GXpqqcRCP8k4y+Dxb0taWJDPpB+rdDUtumIiBwkY9qGA==} resolution: {integrity: sha512-oe5WJEDaVsW8fBlGT7udrSCgOwWfoYHQOmSpnh8X+0GXpqqcRCP8k4y+Dxb0taWJDPpB+rdDUtumIiBwkY9qGA==}
dev: true dev: true
/@types/blueimp-md5/2.18.0:
resolution: {integrity: sha512-f4A+++lGZGJvVSgeyMkqA7BEf2BVQli6F+qEykKb49c5ieWQBkfpn6CP5c1IZr2Yi2Ofl6Fj+v0e1fN18Z8Cnw==}
dev: true
/@types/json-schema/7.0.11: /@types/json-schema/7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true dev: true
...@@ -141,6 +151,10 @@ packages: ...@@ -141,6 +151,10 @@ packages:
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==} resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
dev: true dev: true
/@types/qs/6.9.7:
resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
dev: true
/@typescript-eslint/eslint-plugin/5.19.0_f34adc8488d2e4f014fe61432d70cbf2: /@typescript-eslint/eslint-plugin/5.19.0_f34adc8488d2e4f014fe61432d70cbf2:
resolution: {integrity: sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==} resolution: {integrity: sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -573,6 +587,10 @@ packages: ...@@ -573,6 +587,10 @@ packages:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'} engines: {node: '>=8'}
/blueimp-md5/2.19.0:
resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==}
dev: false
/brace-expansion/1.1.11: /brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies: dependencies:
......
...@@ -84,3 +84,9 @@ body { ...@@ -84,3 +84,9 @@ body {
html { html {
font-size: 50px; font-size: 50px;
} }
.my-button {
color: #fff !important;
border: 0 !important;
background: linear-gradient(90deg, #f7c988 0%, #e5a448 100%) !important;
}
import AppCard from '@/components/base/AppCard.vue' import AppCard from '@/components/base/AppCard.vue'
import AppContainer from '@/components/base/AppContainer.vue' import AppContainer from '@/components/base/AppContainer.vue'
import Avatar from '@/components/Avatar.vue'
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
AppCard: typeof AppCard AppCard: typeof AppCard
AppContainer: typeof AppContainer AppContainer: typeof AppContainer
Avatar: typeof Avatar
} }
} }
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{ src: string }>()
const avatarUrl = computed(() => {
return props.src || 'https://webapp-pub.ezijing.com/weapp/share/default.jpg'
})
</script>
<template>
<img :src="avatarUrl" />
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ImagePreview } from 'vant'
const props = defineProps<{ data: any }>()
const imageList = computed<string[]>(() => {
try {
return JSON.parse(props.data.data.content)
} catch (error) {
return []
}
})
const onImagePreview = function (index: number) {
ImagePreview({ images: imageList.value, startPosition: index })
}
</script>
<template>
<div class="publish-item">
<div class="publish-item-hd">
<Avatar :src="data.user_info.avatar" class="publish-avatar"></Avatar>
<div class="publish-item-hd-info">
<h5>{{ data.user_info.name }}</h5>
</div>
</div>
<div class="publish-item-bd">
<div class="publish-item-content" v-html="data.content || data.desc"></div>
<ul class="publish-item-picture">
<li v-for="(item, index) in imageList" :key="index" @click="onImagePreview(index)"><img :src="item" /></li>
</ul>
<div class="publish-item-tools">
<p class="t1">{{ data.created_time }}</p>
<p class="t2">评论</p>
</div>
</div>
</div>
</template>
<style lang="scss">
.publish-item {
margin-bottom: 0.56rem;
}
.publish-item-hd {
display: flex;
align-items: center;
}
.publish-item-hd-info {
flex: 1;
margin-left: 0.18rem;
h5 {
font-size: 0.24rem;
font-weight: 400;
color: #333333;
}
}
.publish-avatar {
width: 0.68rem;
height: 0.68rem;
border-radius: 50%;
overflow: hidden;
object-fit: cover;
}
.publish-item-bd {
margin-left: 0.86rem;
}
.publish-item-content {
font-size: 0.24rem;
font-weight: 400;
line-height: 0.3rem;
color: #333333;
}
.publish-item-picture {
margin-top: 0.1rem;
display: grid;
grid-template-columns: repeat(3, 1fr);
row-gap: 0.06rem;
column-gap: 0.06rem;
li {
height: 2rem;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
.publish-item-tools {
margin-top: 0.1rem;
display: flex;
align-items: center;
justify-content: space-between;
.t1 {
font-size: 0.18rem;
font-weight: 300;
color: #999999;
}
.t2 {
font-size: 0.24rem;
color: #999999;
line-height: 0.36rem;
}
}
</style>
<script setup lang="ts">
defineProps({ list: { type: Array } })
</script>
<template>
<div class="comment-item" v-for="item in list" :key="item.id">
<div class="comment-item-hd">
<img :src="item.user_avatar" />
<div class="comment-item-hd-info">
<h5>{{ item.user_name }}</h5>
<p>{{ item.create_time }}</p>
</div>
</div>
<div class="comment-item-bd"></div>
</div>
</template>
<script setup lang="ts">
import md5 from 'blueimp-md5'
import { ref, withDefaults, watch } from 'vue'
import { getSignature, uploadFile } from '@/api/base'
import type { UploaderFileListItem } from 'vant'
const props = withDefaults(defineProps<{ modelValue: string[]; prefix?: string }>(), { prefix: 'upload/psp/' })
const emit = defineEmits(['update:modelValue'])
const fileList = ref<UploaderFileListItem[]>([])
watch(props.modelValue, files => {
fileList.value = files.map((url: string) => ({ url }))
})
// 上传
const afterRead = async (file: any) => {
// 获取签名
const signature: any = await getSignature()
const rawFile = file.file
const rawFileName = rawFile?.name || ''
const key = props.prefix + md5(rawFileName + new Date().getTime()) + rawFileName.substr(rawFileName.lastIndexOf('.'))
file.status = 'uploading'
file.message = '上传中...'
file.url = `${signature.host}/${key}`
// 上传文件
const params = {
key,
OSSAccessKeyId: signature.accessid,
policy: signature.policy,
signature: signature.signature,
success_action_status: '200',
file: rawFile
}
uploadFile(params)
.then(() => {
file.status = 'done'
file.message = '上传成功'
emit(
'update:modelValue',
fileList.value.map((item: any) => item.url)
)
})
.catch(() => {
file.status = 'failed'
file.message = '上传失败'
})
}
// 删除
const onDelete = () => {
emit(
'update:modelValue',
fileList.value.map((item: any) => item.url)
)
}
</script>
<template>
<van-uploader v-model="fileList" :after-read="afterRead" @delete="onDelete" />
</template>
<script setup lang="ts">
import md5 from 'blueimp-md5'
import { ref, withDefaults, watch } from 'vue'
import { getSignature, uploadFile } from '@/api/base'
import type { UploaderFileListItem } from 'vant'
const props = withDefaults(defineProps<{ modelValue: string[]; prefix?: string }>(), { prefix: 'upload/psp/' })
const emit = defineEmits(['update:modelValue'])
const fileList = ref<UploaderFileListItem[]>([])
watch(props.modelValue, files => {
fileList.value = files.map((url: string) => ({ url }))
})
// 上传
const afterRead = async (file: any) => {
// 获取签名
const signature: any = await getSignature()
const rawFile = file.file
const rawFileName = rawFile?.name || ''
const key = props.prefix + md5(rawFileName + new Date().getTime()) + rawFileName.substr(rawFileName.lastIndexOf('.'))
file.status = 'uploading'
file.message = '上传中...'
file.url = `${signature.host}/${key}`
// 上传文件
const params = {
key,
OSSAccessKeyId: signature.accessid,
policy: signature.policy,
signature: signature.signature,
success_action_status: '200',
file: rawFile
}
uploadFile(params)
.then(() => {
file.status = 'done'
file.message = '上传成功'
emit(
'update:modelValue',
fileList.value.map((item: any) => item.url)
)
})
.catch(() => {
file.status = 'failed'
file.message = '上传失败'
})
}
// 删除
const onDelete = () => {
emit(
'update:modelValue',
fileList.value.map((item: any) => item.url)
)
}
</script>
<template>
<van-uploader v-model="fileList" :after-read="afterRead" @delete="onDelete" />
</template>
<script setup lang="ts"> <script setup lang="ts">
import { withDefaults } from 'vue' import { withDefaults, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
interface Props { interface Props {
title?: string title?: string
headerAlign?: boolean headerAlign?: string
backgroundColor?: string backgroundColor?: string
} }
withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
headerAlign: false headerAlign: 'left',
backgroundColor: 'transparent'
}) })
const router = useRouter() const router = useRouter()
function handleBack() { function handleBack() {
router.push('/') router.back()
} }
const containerPaddingValue = computed(() => {
return props.backgroundColor === 'transparent' ? '0' : '.24rem'
})
</script> </script>
<template> <template>
...@@ -22,7 +26,7 @@ function handleBack() { ...@@ -22,7 +26,7 @@ function handleBack() {
<div class="app-container-hd"> <div class="app-container-hd">
<slot name="header"> <slot name="header">
<van-icon name="arrow-left" @click="handleBack" /> <van-icon name="arrow-left" @click="handleBack" />
<h2 class="app-container-hd__title" @click="handleBack" v-if="title">{{ title }}</h2> <h2 class="app-container-hd__title" v-if="title">{{ title }}</h2>
<div class="app-container-hd-aside"> <div class="app-container-hd-aside">
<slot name="header-aside"></slot> <slot name="header-aside"></slot>
</div> </div>
...@@ -35,15 +39,23 @@ function handleBack() { ...@@ -35,15 +39,23 @@ function handleBack() {
</template> </template>
<style lang="scss"> <style lang="scss">
.app-container {
padding: v-bind(containerPaddingValue);
border-radius: 0.2rem;
background: v-bind(backgroundColor);
}
.app-container-hd { .app-container-hd {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 0.32rem; font-size: 0.32rem;
padding: 0.2rem 0;
} }
.app-container-hd__title { .app-container-hd__title {
margin-left: 0.22rem; flex: 1;
padding: 0 0.2rem;
font-size: 0.32rem; font-size: 0.32rem;
font-weight: 600; font-weight: 600;
color: #333333; color: #333333;
text-align: v-bind(headerAlign);
} }
</style> </style>
...@@ -7,12 +7,13 @@ import 'vant/lib/index.css' ...@@ -7,12 +7,13 @@ import 'vant/lib/index.css'
// 公共组件 // 公共组件
import AppCard from '@/components/base/AppCard.vue' import AppCard from '@/components/base/AppCard.vue'
import AppContainer from '@/components/base/AppContainer.vue' import AppContainer from '@/components/base/AppContainer.vue'
import Avatar from '@/components/Avatar.vue'
import modules from './modules' import modules from './modules'
const app = createApp(App) const app = createApp(App)
// 注册公共组件 // 注册公共组件
app.component('AppCard', AppCard).component('AppContainer', AppContainer) app.component('AppCard', AppCard).component('AppContainer', AppContainer).component('Avatar', Avatar)
// 注册模块 // 注册模块
modules({ router }) modules({ router })
......
...@@ -9,3 +9,14 @@ export function getCourseList(params: { page_size: number; page: number }) { ...@@ -9,3 +9,14 @@ export function getCourseList(params: { page_size: number; page: number }) {
export function getCourseView(params: { id: string }) { export function getCourseView(params: { id: string }) {
return httpRequest.get('/api/psp/v1/learning/course-view', { params }) return httpRequest.get('/api/psp/v1/learning/course-view', { params })
} }
// 获取课程列表
export function coursePublish(data: {
chapter_id: string
course_id: string
content: string
title?: string
file?: string
}) {
return httpRequest.post('/api/psp/v1/learning/upload', data)
}
...@@ -7,7 +7,8 @@ export const routes: Array<RouteRecordRaw> = [ ...@@ -7,7 +7,8 @@ export const routes: Array<RouteRecordRaw> = [
component: AppLayout, component: AppLayout,
children: [ children: [
{ path: '', component: () => import('./views/Index.vue') }, { path: '', component: () => import('./views/Index.vue') },
{ 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') }
] ]
} }
] ]
<script setup lang="ts">
import { reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { coursePublish } from '../api'
import UploadImageList from '@/components/UploadImageList.vue'
import { Toast } from 'vant'
const router = useRouter()
const route = useRoute()
const form = reactive({ ...{ chapter_id: '', course_id: '', content: '', picture: [] }, ...route.query })
const pictureValidator = () => !!form.picture.length
function onSubmit() {
const params = Object.assign({}, form, { picture: JSON.stringify(form.picture) })
coursePublish(params).then(() => {
Toast.success('发布成功')
router.push({ name: 'courseView', params: { id: form.course_id } })
})
}
</script>
<template>
<AppContainer title="打卡" backgroundColor="#fff" headerAlign="center">
<van-form @submit="onSubmit">
<van-field
v-model="form.content"
type="textarea"
placeholder="请输入打卡内容"
:autosize="{ minHeight: 200 }"
:rules="[{ required: true, message: '请输入打卡内容' }]"
/>
<van-field :rules="[{ validator: pictureValidator, message: '请上传图片' }]">
<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>
::v-deep .van-cell {
padding-left: 0;
padding-right: 0;
&::after {
left: 0;
right: 0;
}
}
.my-button {
margin: 1rem 0;
}
</style>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { Toast } from 'vant'
import PublishItem from '@/components/PublishItem.vue'
import { getCourseView } from '../api' import { getCourseView } from '../api'
const props = defineProps<{ id: string }>() const props = defineProps<{ id: string }>()
const data = ref() const data = ref()
function getCourse() { function getCourse() {
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()
}) })
} }
onMounted(() => { onMounted(() => {
...@@ -48,6 +52,20 @@ onMounted(() => { ...@@ -48,6 +52,20 @@ onMounted(() => {
</div> </div>
<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-chapter-item" v-for="item in data.course_chapters.list" :key="item.id">
<h2 class="chapter-title">{{ item.chapter_name }}</h2>
<div class="chapter-button">
<router-link
:to="{ path: '/course/publish', query: { course_id: id, chapter_id: item.id } }"
class="chapter-button"
>
课程总结
</router-link>
</div>
<PublishItem v-for="publish in item.records.list" :data="publish" :key="publish.id"></PublishItem>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -132,8 +150,10 @@ onMounted(() => { ...@@ -132,8 +150,10 @@ onMounted(() => {
.course-bottom { .course-bottom {
margin-top: 0.28rem; margin-top: 0.28rem;
margin-bottom: 0.28rem;
background-color: #033974; background-color: #033974;
border-radius: 0.2rem; border-radius: 0.2rem;
overflow: hidden;
} }
.course-tips { .course-tips {
padding: 0.3rem; padding: 0.3rem;
...@@ -141,4 +161,30 @@ onMounted(() => { ...@@ -141,4 +161,30 @@ onMounted(() => {
color: #ffffff; color: #ffffff;
line-height: 0.36rem; line-height: 0.36rem;
} }
.course-chapters {
padding: 0.38rem 0.24rem;
background-color: #fff;
border-top-left-radius: 0.2rem;
border-top-right-radius: 0.2rem;
.chapter-title {
font-size: 0.28rem;
font-weight: bold;
color: #333333;
line-height: 0.28rem;
}
}
.chapter-button {
text-align: right;
a {
display: inline-block;
height: 0.4rem;
margin: 0.22rem 0 0.22rem auto;
padding: 0 0.15rem;
font-size: 0.22rem;
line-height: 0.4rem;
color: #ffffff;
background: linear-gradient(164deg, #f7c988 0%, #e5a448 100%);
border-radius: 0.2rem;
}
}
</style> </style>
...@@ -4,7 +4,7 @@ defineProps<{ docs: IDocItem[]; videos: IVideoItem[] }>() ...@@ -4,7 +4,7 @@ defineProps<{ docs: IDocItem[]; videos: IVideoItem[] }>()
</script> </script>
<template> <template>
<AppCard title="入学指南"> <AppCard title="入学指南" id="admission">
<div class="admission"> <div class="admission">
<div class="admission-left"> <div class="admission-left">
<h2>解释文档</h2> <h2>解释文档</h2>
......
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<AppCard title="考试攻略"></AppCard> <AppCard title="考试攻略" id="exam"></AppCard>
</template> </template>
...@@ -9,7 +9,7 @@ const active = ref<number>(0) ...@@ -9,7 +9,7 @@ const active = ref<number>(0)
</script> </script>
<template> <template>
<AppCard title="学习地图"> <AppCard title="学习地图" id="learning">
<template #header-aside> <template #header-aside>
<div class="button">去学习</div> <div class="button">去学习</div>
</template> </template>
......
...@@ -8,34 +8,34 @@ const menus: Array<{ ...@@ -8,34 +8,34 @@ const menus: Array<{
icon: string icon: string
}> = [ }> = [
{ {
path: '/', path: '#admission',
name: '入学指南', name: '入学指南',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_1.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_1.png'
}, },
{ {
path: '/', path: '#learning',
name: '学习地图', name: '学习地图',
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'
}, },
{ {
path: '/', path: '#team',
name: '荣誉总榜', name: '荣誉总榜',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_4.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_4.png'
}, },
{ {
path: '/', path: '#exam',
name: '伴随问答',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_5.png'
},
{
path: '/',
name: '考试攻略', name: '考试攻略',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_6.png' icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_6.png'
},
{
path: '#qa',
name: '陪伴问答',
icon: 'https://webapp-pub.ezijing.com/project/prp-h5/home_menu_5.png'
} }
] ]
</script> </script>
...@@ -44,10 +44,10 @@ const menus: Array<{ ...@@ -44,10 +44,10 @@ const menus: Array<{
<nav class="home-nav"> <nav class="home-nav">
<swiper slides-per-view="auto" :space-between="12"> <swiper slides-per-view="auto" :space-between="12">
<swiper-slide v-for="(item, index) in menus" :key="index" class="nav-item"> <swiper-slide v-for="(item, index) in menus" :key="index" class="nav-item">
<router-link :to="item.path"> <a :href="item.path">
<img :src="item.icon" /> <img :src="item.icon" />
<p>{{ item.name }}</p> <p>{{ item.name }}</p>
</router-link> </a>
</swiper-slide> </swiper-slide>
</swiper> </swiper>
</nav> </nav>
......
<script setup lang="ts"></script> <script setup lang="ts">
import PublishItem from '@/components/PublishItem.vue'
defineProps<{ data: any }>()
</script>
<template> <template>
<AppCard title="陪伴问答"> <AppCard title="陪伴问答" id="qa">
<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>
</AppCard> </AppCard>
</template> </template>
<style lang="scss" scoped>
::v-deep .publish-item {
padding: 0.24rem;
margin-bottom: 0.2rem;
background: #fff;
border-radius: 0.2rem;
}
</style>
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<AppCard title="荣誉总榜"> <AppCard title="荣誉总榜" id="team">
<template #header-aside> <template #header-aside>
<div class="more">查看更多 <van-icon name="arrow" /></div> <div class="more">查看更多 <van-icon name="arrow" /></div>
</template> </template>
......
...@@ -5,6 +5,7 @@ export interface HomeInfo { ...@@ -5,6 +5,7 @@ export interface HomeInfo {
admission_guide_docs: IDocItem[] admission_guide_docs: IDocItem[]
admission_guide_videos: IVideoItem[] admission_guide_videos: IVideoItem[]
learning_map_docs: IDocItem[] learning_map_docs: IDocItem[]
questions: any
} }
export interface IBanner { export interface IBanner {
......
...@@ -14,7 +14,8 @@ const data = ref<HomeInfo>({ ...@@ -14,7 +14,8 @@ const data = ref<HomeInfo>({
banner: [], banner: [],
admission_guide_docs: [], admission_guide_docs: [],
admission_guide_videos: [], admission_guide_videos: [],
learning_map_docs: [] learning_map_docs: [],
questions: {}
}) })
// 获取首页数据 // 获取首页数据
function fetchHomeData() { function fetchHomeData() {
...@@ -40,5 +41,5 @@ onMounted(() => { ...@@ -40,5 +41,5 @@ onMounted(() => {
<!-- 考试攻略 --> <!-- 考试攻略 -->
<ExamStrategy></ExamStrategy> <ExamStrategy></ExamStrategy>
<!-- 陪伴问答 --> <!-- 陪伴问答 -->
<Questions></Questions> <Questions :data="data.questions"></Questions>
</template> </template>
...@@ -54,7 +54,7 @@ const menus: Array<{ ...@@ -54,7 +54,7 @@ const menus: Array<{
<template> <template>
<div class="my" v-if="info"> <div class="my" v-if="info">
<div class="user"> <div class="user">
<img :src="info.avatar" /> <Avatar :src="info.avatar"></Avatar>
<p>{{ info.name }}</p> <p>{{ info.name }}</p>
</div> </div>
<div class="quantity"> <div class="quantity">
......
...@@ -2,10 +2,6 @@ import { createRouter, createWebHistory } from 'vue-router' ...@@ -2,10 +2,6 @@ import { createRouter, createWebHistory } from 'vue-router'
// import { useUserStore } from '@/stores/user' // import { useUserStore } from '@/stores/user'
const router = createRouter({ const router = createRouter({
scrollBehavior() {
// 始终滚动到顶部
return { top: 0 }
},
history: createWebHistory(), history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)*', redirect: '/' }] routes: [{ path: '/:pathMatch(.*)*', redirect: '/' }]
}) })
......
import axios from 'axios' import axios from 'axios'
import qs from 'qs'
const httpRequest = axios.create({ const httpRequest = axios.create({
// baseURL: 'https://learn-api.ezijing.com', // baseURL: 'https://learn-api.ezijing.com',
timeout: 60000, timeout: 60000,
withCredentials: true withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}) })
// 请求拦截
httpRequest.interceptors.request.use(
function (config) {
if (config.headers?.['Content-Type'] === 'application/x-www-form-urlencoded') {
config.data = qs.stringify(config.data)
}
if (config.headers?.['Content-Type'] === 'multipart/form-data') {
const formData = new window.FormData()
for (const key in config.data) {
formData.append(key, config.data[key])
}
config.data = formData
}
return config
},
function (error) {
return Promise.reject(error)
}
)
// 响应拦截 // 响应拦截
httpRequest.interceptors.response.use( httpRequest.interceptors.response.use(
function (response) { function (response) {
const { data } = response const { data } = response
// 正常返回
if (data.code === 0) {
return data
}
// 未登录 // 未登录
if (data.code === 4001) { if (data.code === 4001) {
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}` location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
return Promise.reject(data)
} }
if (data.code === 1) {
return Promise.reject(data) return Promise.reject(data)
}
return data
}, },
function (error) { function (error) {
if (error.response) { if (error.response) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论