提交 94320ddb authored 作者: matian's avatar matian

updates

上级 1cff1c18
......@@ -20,7 +20,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '100'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -73,7 +74,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '101'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -112,7 +114,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '102'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -151,7 +154,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '103'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -190,7 +194,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '104'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -229,7 +234,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '105'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -268,7 +274,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '106'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -307,7 +314,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '107'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅰ)',
......@@ -346,6 +354,7 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '108'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
}
]
......@@ -19,7 +19,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '200'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
......@@ -59,7 +60,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '201'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -98,7 +100,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '202'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -137,7 +140,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '203'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -176,7 +180,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '204'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -215,7 +220,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '205'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -254,7 +260,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '206'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -293,7 +300,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '207'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAP(Ⅱ)',
......@@ -332,6 +340,7 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '208'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
}
]
......@@ -20,7 +20,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '300'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAM',
......@@ -59,7 +60,8 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '301'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
},
{
category: 'PAAM',
......@@ -98,6 +100,7 @@ export default [
course_chapter: '56 节课程',
course_total_hour: '80 小时',
course_hour: '20课时',
course_id: '302'
course_id: '6998547457529348096',
class_id: '6998519782265847808'
}
]
......@@ -12,6 +12,7 @@
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"pinia": "^2.0.23",
"qrcode.vue": "^3.3.3",
"swiper": "^8.4.4",
"vue": "^3.2.41",
"vue-router": "^4.1.6"
......@@ -3544,6 +3545,14 @@
"node": ">=6"
}
},
"node_modules/qrcode.vue": {
"version": "3.3.3",
"resolved": "https://registry.npmmirror.com/qrcode.vue/-/qrcode.vue-3.3.3.tgz",
"integrity": "sha512-OsD4tQjIbxg/K6D5ZkWjBdYI9eg9K2i8qeYILdEAX5mdAydSAxV7xKmmZSP/hA12olLqEMZ9ryqDQrwa9jEMgw==",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz",
......@@ -7184,6 +7193,12 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"qrcode.vue": {
"version": "3.3.3",
"resolved": "https://registry.npmmirror.com/qrcode.vue/-/qrcode.vue-3.3.3.tgz",
"integrity": "sha512-OsD4tQjIbxg/K6D5ZkWjBdYI9eg9K2i8qeYILdEAX5mdAydSAxV7xKmmZSP/hA12olLqEMZ9ryqDQrwa9jEMgw==",
"requires": {}
},
"qs": {
"version": "6.11.0",
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz",
......
......@@ -19,6 +19,7 @@
"axios": "^1.1.3",
"element-plus": "^2.2.19",
"pinia": "^2.0.23",
"qrcode.vue": "^3.3.3",
"swiper": "^8.4.4",
"vue": "^3.2.41",
"vue-router": "^4.1.6"
......
......@@ -27,3 +27,10 @@ export function uploadFile(data: object) {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
// 获取已经购买的课程
export function getBuyShop() {
return httpRequest.get('https://learn-api.ezijing.com/api/lms-ep/v2/education/courses/my', {
headers: { tenant: 'paa' }
})
}
......@@ -8,7 +8,7 @@ const attrs = useAttrs()
<div class="app-layout">
<AppHeader v-bind="attrs"></AppHeader>
<section class="app-layout-container">
<router-view></router-view>
<router-view :key="$route.fullPath"></router-view>
</section>
<AppFooter></AppFooter>
</div>
......
import httpRequest from '@/utils/axios'
/**
* 创建订单
* */
export function createOrder(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/add', data)
}
/**
* 获取订单
* */
export function getOrderList(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/shop/order/search', data)
}
/**
* 获取用户OpenId
* */
export function getOpenId(data?: any) {
return httpRequest.post('https://shop-show-pc.ezijing.com/api/usercenter/v1/wechat/get-openid', data, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user'
const user = useUserStore()
defineProps({
shopItem: {
type: Object
}
})
const payMode = ref(1)
const isAgree = ref(false)
const isAgreeChecked = ref(false)
const emit = defineEmits<{
(e: 'success', params: object): void
}>()
const handlePay = () => {
if (isAgree.value === false) {
isAgreeChecked.value = true
ElMessage.warning('请先勾选紫荆金保服务协议')
} else {
const params = {
payMode: payMode.value,
status: 'order'
}
emit('success', params)
}
}
</script>
<template>
<div>
<div class="main_con">
<div class="con_tit">课程信息确认</div>
<div class="con_pay">
<div class="pay_course">
<img
src="https://img1.baidu.com/it/u=3009731526,373851691&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500"
alt=""
/>
<div class="course_info">
<div class="info_tit">{{ shopItem?.title }}</div>
<div class="info_range">有效期:{{ shopItem?.start_time }}{{ shopItem?.end_time }}</div>
<div class="info_price">
<div class="price_icon">¥</div>
<div class="price_num">{{ shopItem?.price }}</div>
</div>
</div>
</div>
<div class="con_message" v-if="user.isLogin">课程提醒将发送到您的手机:{{ user?.mobile }}</div>
<div class="con_mode">
<div class="mode_tit">支付方式</div>
<div class="mode_radio">
<el-radio-group v-model="payMode">
<el-radio :label="1" size="large" border>
<img src="https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/pay_ali.png" />
<span class="radio_tit">支付宝支付</span>
</el-radio>
<el-radio :label="2" size="large" border>
<img src="https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/pay_wechat.png" />
<span class="radio_tit">微信支付</span>
</el-radio>
</el-radio-group>
</div>
</div>
<div class="con_footer">
<div class="footer_left">
<el-checkbox v-model="isAgree"
><span style="color: #666666; font-size: 16px; font-weight: 400">同意</span
><a style="color: #e3a232; font-size: 16px; font-weight: 400">紫荆金保服务协议</a></el-checkbox
>
<div class="left_desc" :class="isAgreeChecked === true ? 'left_desc_active' : ''">
请先勾选紫荆金保服务协议
</div>
</div>
<div class="footer_right">
<div class="right_top">
<div class="top_tit">需付金额:</div>
<div class="top_price">
<div class="price_icon">¥</div>
<div class="price_num">{{ shopItem?.price }}</div>
</div>
</div>
<div class="right_btn" @click="handlePay">立即支付</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.main_con {
width: 1200px;
margin: auto;
.con_tit {
font-size: 28px;
font-weight: 500;
line-height: 34px;
color: #333333;
padding-top: 39px;
}
.con_pay {
height: 721px;
background: #ffffff;
margin-top: 26px;
padding: 52px 60px 107px 40px;
box-sizing: border-box;
.pay_course {
display: flex;
padding-bottom: 41px;
border-bottom: 1px solid #e6e6e6;
img {
width: 257px;
height: 166px;
}
.course_info {
margin-left: 35px;
.info_tit {
font-size: 24px;
font-weight: 500;
line-height: 34px;
color: #333333;
}
.info_range {
font-size: 14px;
font-weight: 400;
line-height: 34px;
color: #666666;
margin-top: 26px;
}
.info_price {
display: flex;
align-items: flex-end;
margin-top: 42px;
.price_icon {
font-size: 20px;
font-weight: 500;
color: #aa1941;
}
.price_num {
font-size: 30px;
font-weight: normal;
line-height: 34px;
color: #aa1941;
}
}
}
}
}
.con_message {
margin-top: 30px;
font-size: 18px;
font-weight: 500;
line-height: 34px;
color: #333333;
}
.con_mode {
margin-top: 43px;
display: flex;
align-items: center;
.mode_tit {
font-size: 16px;
font-weight: 400;
line-height: 34px;
color: #333333;
}
.mode_radio {
margin-left: 44px;
}
}
.con_footer {
margin-top: 74px;
display: flex;
justify-content: space-between;
.footer_left {
padding-top: 12px;
.left_desc {
font-size: 14px;
font-weight: 400;
color: #999999;
margin: 14px 0 0 21px;
}
.left_desc_active {
color: #aa1941;
}
}
.footer_right {
display: flex;
flex-direction: column;
align-items: flex-end;
.right_top {
display: flex;
.top_tit {
font-size: 16px;
font-weight: 400;
line-height: 34px;
color: #333333;
}
.top_price {
display: flex;
margin-left: 53px;
.price_icon {
font-size: 22px;
font-weight: 500;
line-height: 34px;
color: #aa1941;
}
.price_num {
font-size: 36px;
font-weight: normal;
line-height: 34px;
color: #aa1941;
}
}
}
.right_btn {
width: 153px;
height: 48px;
background: #edb24c;
border-radius: 24px;
font-size: 18px;
font-weight: 400;
line-height: 48px;
color: #ffffff;
text-align: center;
margin-top: 20px;
cursor: pointer;
}
}
}
}
:deep(.el-radio) {
width: 220px;
height: 83px;
background: #ffffff;
border: 2px solid #e2e2e2;
display: flex;
align-items: center;
justify-content: center;
}
:deep(.el-radio.is-bordered.is-checked) {
border: 2px solid #ebaf48;
background: rgba(255, 251, 245, 0.39);
}
:deep(.el-radio__input) {
display: none;
}
:deep(.el-radio__label) {
img {
margin-right: 10px;
}
.radio_tit {
font-size: 16px;
font-weight: 400;
line-height: 34px;
color: #666666;
}
}
</style>
<script setup lang="ts">
import { createOrder, getOrderList } from '../api'
import QrcodeVue from 'qrcode.vue'
import { useUserStore } from '@/stores/user'
const user = useUserStore()
const timer = ref()
const qrCodeUrl = ref('')
const payStatus = ref('')
// const UA = navigator.userAgent
// const isMobile = /iphone/i.test(UA) || (/android/i.test(UA) && /mobile/i.test(UA))
// const isWechat = /micromessenger/i.test(UA)
// const isAliPay = /alipayclient/i.test(UA)
const props = defineProps({
payMethod: {
type: Number
},
shopItem: {
type: Object
}
})
const emit = defineEmits<{
(e: 'update', params: object): void
}>()
onMounted(() => {
const params: any = {
shop_id: '6998523899570814976',
spu_id: '6998525810348916736',
sku_id: '6998525810365693952',
redirect_url: '',
buy_count: '1',
notify_url: `https://ep-lms-api.ezijing.com/v2/student/push?tenant=paa&sso_id=${user.user?.id}&class_id=${props.shopItem.class_id}&course_flag=1&course_id=${props.shopItem.course_id}`
}
// 支付宝扫码支付
if (props.payMethod === 1) {
params.payment_method = '11'
// 微信扫码支付
} else if (props.payMethod === 2) {
params.payment_method = '1'
}
createOrder(params).then((res: any) => {
console.log(res)
qrCodeUrl.value = res.payment_url
timer.value = setInterval(() => {
// 通过定时器每间隔一会去请求查询微信支付状态(具体参数根据项目需要而定)
handleGetOrderList(res.order_detail_id)
}, 2000)
})
})
const handleGetOrderList = (id: any) => {
getOrderList({ order_detail_id: id }).then(res => {
payStatus.value = res.data[0].order_status
if (payStatus.value === '4') {
const params = {
orderId: res.data[0].pay_order_id,
status: 'success',
payStatus: res.data[0].order_status
}
clearInterval(timer.value)
emit('update', params)
}
})
}
</script>
<template>
{{ payMethod }}
<div class="main">
<div class="main_order">
<div class="order_id">
<div class="id_tit">商品订单:</div>
<div class="id_num">11111</div>
</div>
<div class="order_price">
<div class="price_tit">支付金额:</div>
<div class="price_con">
<div class="con_icon">¥</div>
<div class="con_num">1999.00</div>
</div>
</div>
<div class="line"></div>
<div class="order_mode">
<div class="mode_tit">支付方式:</div>
<div class="mode_con">
<img
:src="
payMethod === 1
? 'https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/pay_ali.png'
: 'https://webapp-pub.ezijing.com/project_online/fi/pay_wechat.png'
"
class="con_style"
/>
<span class="radio_tit">{{ payMethod === 1 ? '支付宝支付' : '微信支付' }}</span>
<img src="https://webapp-pub.ezijing.com/project_online/fi/icon_pay_checked.png" class="checked_img" />
</div>
</div>
<div class="order_qaCode">
<div class="qaCode_left">
<div class="left_code">
<qrcode-vue :value="qrCodeUrl" :size="197" level="H"></qrcode-vue>
</div>
<div class="left_desc" :class="payMethod === 1 ? 'left_desc_ali' : 'left_desc_wechat'">
{{ payMethod === 1 ? '请打开手机支付宝,扫一扫完成支付' : '请打开手机微信,扫一扫完成支付' }}
</div>
</div>
<img
:src="
payMethod === 1
? 'https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/icon_aliPay_img1.png'
: 'https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/icon_wechat_img1.png'
"
class="right_img"
/>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.main {
width: 100%;
padding: 50px 0 46px 0;
.main_order {
width: 1200px;
background: #ffffff;
margin: auto;
padding: 59px 0 137px 40px;
.order_id {
display: flex;
font-size: 16px;
font-weight: 400;
line-height: 34px;
.id_tit {
color: #333333;
}
.id_num {
color: #999999;
}
}
.order_price {
display: flex;
font-size: 16px;
font-weight: 400;
line-height: 34px;
margin-top: 22px;
.price_tit {
color: #333333;
}
.price_con {
display: flex;
.con_icon {
font-size: 22px;
font-weight: 400;
line-height: 34px;
color: #aa1941;
}
.con_num {
font-size: 36px;
font-weight: normal;
line-height: 34px;
color: #aa1941;
}
}
}
.line {
margin-top: 26px;
border-bottom: 1px solid #e6e6e6;
}
.order_mode {
.mode_tit {
font-size: 16px;
font-weight: 500;
line-height: 34px;
color: #333333;
}
.mode_con {
margin-top: 29px;
width: 160px;
height: 66px;
border: 1px solid #4586d0;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.con_style {
margin-right: 10px;
}
.checked_img {
position: absolute;
right: -1px;
bottom: -1px;
}
}
}
.order_qaCode {
display: flex;
margin-top: 39px;
.qaCode_left {
.left_code {
width: 197px;
}
.left_desc {
width: 197px;
height: 31px;
font-size: 12px;
font-weight: 400;
line-height: 31px;
color: #ffffff;
text-align: center;
}
.left_desc_ali {
background: #0f76fb;
}
.left_desc_wechat {
background: #01c71e;
}
}
.right_img {
margin: -11px 0 0 124px;
}
}
}
}
</style>
<script setup lang="ts">
const router = useRouter()
const props = defineProps({
orderId: {
type: String
},
shopItem: {
type: Object
},
payStatus: {
type: String
}
})
const handleStudy = () => {
window.open('https://paa-learning.ezijing.com')
}
const handlePrev = () => {
router.push({
path: `/shop/detail/${props.shopItem?.id}`,
query: {
payStatus: props.payStatus
}
})
}
</script>
<template>
<div class="main_pay">
<div class="pay_con">
<div class="con_top">
<img src="https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/icon_sucess.png" alt="" />
<div class="top_tit">支付成功!</div>
</div>
<div class="con_info">
<div class="info_order">
<div class="order_tit">商品订单:</div>
<div class="order_id">{{ orderId }}</div>
</div>
<div class="info_price">
<div class="price_tit">支付金额:</div>
<div class="price_id">¥{{ shopItem?.price }}</div>
</div>
<div class="info_tips">若有疑问请与客服联系,我们将尽快为您提供服务</div>
<div class="info_btn">
<div class="btn_prev" @click="handlePrev">返回</div>
<div class="btn_study" @click="handleStudy">开始学习</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.main_pay {
width: 100%;
padding: 50px 0 60px 0;
.pay_con {
width: 1200px;
margin: auto;
background: #ffffff;
border-radius: 6px;
.con_top {
width: 1200px;
height: 106px;
background: #f4fff5;
border-radius: 6px 6px 0px 0px;
display: flex;
align-items: center;
padding: 40px 0 40px 67px;
box-sizing: border-box;
.top_tit {
margin-left: 14px;
}
}
.con_info {
padding: 0 0 82px 67px;
.info_order {
display: flex;
font-weight: 400;
margin-top: 43px;
.order_tit {
color: #333333;
}
.order_id {
color: #999999;
}
}
.info_price {
display: flex;
font-weight: 400;
margin-top: 29px;
.price_tit {
color: #333333;
}
.price_id {
color: #999999;
}
}
.info_tips {
font-size: 14px;
font-weight: 400;
color: #999999;
margin-top: 30px;
}
.info_btn {
margin-top: 46px;
display: flex;
.btn_prev {
width: 71px;
height: 32px;
border: 1px solid #999999;
border-radius: 16px;
line-height: 32px;
font-size: 16px;
font-weight: 400;
color: #666666;
text-align: center;
cursor: pointer;
}
.btn_study {
width: 96px;
height: 32px;
background: #edb24c;
border-radius: 24px;
font-size: 16px;
font-weight: 400;
line-height: 32px;
color: #ffffff;
text-align: center;
margin-left: 21px;
cursor: pointer;
}
}
}
}
}
</style>
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
export const routes: Array<RouteRecordRaw> = [
{
path: '/pay',
component: AppLayout,
children: [{ path: '/pay/:id', component: () => import('./views/Index.vue'), props: true }]
}
]
<script setup lang="ts">
import Confirm from '../components/Confirm.vue'
import Order from '../components/Order.vue'
import PaySucess from '../components/PaySucess.vue'
import { useShopStore } from '@/stores/shop'
const { shopItem } = useShopStore()
const status = ref('confirm') // confirm | order | success
const payMethod = ref(0)
const payStatus = ref('')
const orderId = ref('')
const handleSuccess = (data: any) => {
payMethod.value = data.payMode
status.value = data.status
}
const handleOrder = (data: any) => {
orderId.value = data.orderId
status.value = data.status
payStatus.value = data.payStatus
}
</script>
<template>
<div class="pay_main">
<Confirm :shopItem="shopItem" @success="handleSuccess" v-if="status === 'confirm'" />
<Order :shopItem="shopItem" :payMethod="payMethod" @update="handleOrder" v-if="status === 'order'" />
<PaySucess :shopItem="shopItem" :orderId="orderId" :payStatus="payStatus" v-if="status === 'success'" />
</div>
</template>
<style lang="scss" scoped>
.pay_main {
background: #f7f8fa;
}
</style>
<script setup lang="ts">
defineProps({
// 是否弹框
buyDialogVisible: {
type: Boolean,
required: true
}
})
const emit = defineEmits<{
(e: 'update:buyDialogVisible', buyDialogVisible: false): void
}>()
// 关闭弹框
const handleCancel = () => {
emit('update:buyDialogVisible', false)
}
</script>
<template>
<div>
<el-dialog :model-value="buyDialogVisible" width="25%" top="16%" :modal="false" :before-close="handleCancel">
<div class="buy_alert">
<img src="https://webapp-pub.ezijing.com/project_online/fi/icon_contact.png" alt="" />
<div class="alert_con">
<div class="con_tit">请联系我们</div>
<div class="con_desc">PAA秘书处:13263110169(同微信)</div>
</div>
</div>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
.buy_alert {
display: flex;
align-items: center;
.alert_con {
margin-left: 10px;
.con_tit {
font-size: 16px;
font-weight: 400;
line-height: 34px;
color: #333333;
}
.con_desc {
font-size: 14px;
font-weight: 400;
line-height: 34px;
color: #666666;
}
}
}
:deep(.el-dialog) {
width: 315px;
box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.16);
border-radius: 10px;
margin-left: 50%;
.el-dialog__header {
padding-bottom: 0;
}
}
</style>
......@@ -42,7 +42,23 @@ const defaultProps = {
</div>
</div>
<div class="info_con1" v-else>
<el-tree :data="shopItem?.content_list" :props="defaultProps" />
<el-tree
:data="shopItem?.content_list"
:props="defaultProps"
accordion
highlight-current
node-key="id"
:default-expanded-keys="[shopItem?.content_list[0].id]"
>
<template #default="{ node }">
<span class="custom-tree-node">
<span style="width: 403px; display: inline-block">{{ node.label }}</span>
<span style="margin-left: 205px" class="icon">
<img src="https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/icon_jt.png" alt="" />
</span>
</span>
</template>
</el-tree>
</div>
</div>
</div>
......@@ -51,7 +67,6 @@ const defaultProps = {
<style lang="scss" scoped>
.con_course {
margin-top: 20px;
padding: 28px 45px 53px 45px;
background: #ffffff;
box-shadow: 0px 1px 18px rgba(0, 0, 0, 0.1);
......@@ -102,6 +117,7 @@ const defaultProps = {
img {
width: 140px;
height: 80px;
border-radius: 6px;
}
.list_info {
height: 80px;
......@@ -141,14 +157,20 @@ const defaultProps = {
color: #333333;
}
:deep(.el-tree-node__children) {
background: #ffffff !important ;
.el-tree-node__content {
background: #ffffff !important;
border-bottom: 1px solid #f4f4f4;
}
.el-tree-node__label {
font-size: 14px;
font-weight: 400;
line-height: 32px;
color: #666666;
display: inline !important;
}
.icon {
display: none;
}
}
</style>
<script setup lang="ts">
const router = useRouter()
defineProps({
courseItem: {
type: Object
......@@ -11,7 +12,12 @@ defineProps({
<div class="include_course">
<div class="course_tit">包含该课程的系列课</div>
<div class="course_card">
<div class="card_course" v-for="(item, index) in courseItem" :key="index">
<div
class="card_course"
v-for="(item, index) in courseItem"
:key="index"
@click="router.push(`/shop/detail/${item?.id}`)"
>
<img
:src="
item.image_url ||
......@@ -36,6 +42,7 @@ defineProps({
background: #ffffff;
box-shadow: 0px 1px 18px rgba(0, 0, 0, 0.1);
border-radius: 15px;
margin-bottom: 20px;
.course_tit {
padding: 20px 0 17px 20px;
font-size: 18px;
......@@ -57,6 +64,7 @@ defineProps({
padding: 19px;
box-sizing: border-box;
border-radius: 6px;
cursor: pointer;
img {
width: 160px;
......
......@@ -4,13 +4,13 @@ import { Grid, Navigation } from 'swiper'
import 'swiper/css'
import 'swiper/css/grid'
import 'swiper/css/navigation'
import router from '@/router'
defineProps({
shopRelatedList: {
type: Array
}
})
const swiper1 = ref(null)
function prev(swiper) {
console.log(swiper, '111')
swiper?.slidePrev()
......@@ -18,6 +18,9 @@ function prev(swiper) {
function next(swiper) {
swiper?.slideNext()
}
const handleDetail = item => {
router.push(`/shop/detail/${item.id}`)
}
</script>
<template>
......@@ -35,7 +38,12 @@ function next(swiper) {
:modules="[Navigation, Grid]"
@swiper="swiper => (swiper1 = swiper)"
>
<SwiperSlide v-for="(item, index) in shopRelatedList" :key="index" class="course-item">
<SwiperSlide
v-for="(item, index) in shopRelatedList"
:key="index"
class="course-item"
@click="handleDetail(item)"
>
<img
:src="
item.avatar ||
......@@ -97,9 +105,11 @@ function next(swiper) {
box-sizing: border-box;
display: flex;
align-items: center;
cursor: pointer;
.img {
width: 140px;
height: 80px;
border-radius: 6px;
}
.item_right {
margin-left: 14px;
......
<script setup lang="ts">
import QrcodeVue from 'qrcode.vue'
const qrCodeUrl = ref(window.location.href)
defineProps({
// 是否弹框
shareDialogVisible: {
type: Boolean,
required: true
}
})
const emit = defineEmits<{
(e: 'update:shareDialogVisible', shareDialogVisible: false): void
}>()
// 关闭弹框
const handleCancel = () => {
emit('update:shareDialogVisible', false)
}
</script>
<template>
<div>
<el-dialog :model-value="shareDialogVisible" width="15%" top="15%" :modal="false" :before-close="handleCancel">
<div class="share_alert">
<!-- <img src="https://webapp-pub.ezijing.com/project_online/fi/qa_code.png" alt="" /> -->
<qrcode-vue :value="qrCodeUrl" :size="214" level="H"></qrcode-vue>
<div class="alert_con">
<img
src="https://webapp-pub.oss-cn-beijing.aliyuncs.com/project_online/fi/icon_weixin.png"
class="con_icon"
/>
<span class="con_tit">微信扫一扫</span>
</div>
</div>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-dialog) {
width: 315px;
box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.16);
border-radius: 10px;
margin-left: 57%;
.el-dialog__header {
padding-bottom: 0;
}
}
.share_alert {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.alert_con {
margin-top: 5px;
.con_icon {
margin-right: 7px;
}
.con_tit {
font-size: 14px;
font-weight: 400;
line-height: 24px;
color: #666666;
}
}
}
</style>
<script lang="ts" setup>
import CourseList from '../components/CourseList.vue'
import { useShopStore } from '@/stores/shop'
const { filters, list } = useShopStore()
console.log(filters, list)
const { filters, shopList } = useShopStore()
const handleTabClick = (tab: any) => {
if (tab.index === '4') {
window.open('https://prp.ezijing.com')
window.location.reload()
}
}
</script>
......@@ -20,7 +21,7 @@ const handleTabClick = (tab: any) => {
<template #label>
{{ item.label }}
</template>
<CourseList :type="item.value" :courseList="list" :key="index" />
<CourseList :type="item.value" :courseList="shopList" :key="index" />
</el-tab-pane>
</el-tabs>
</div>
......
......@@ -3,18 +3,42 @@ import IncludeCourseCard from '../components/IncludeCourseCard.vue'
import CourseIntroduceCard from '../components/CourseIntroduceCard.vue'
import TeacherCard from '../components/TeacherCard.vue'
import RecommendCourse from '../components/RecommendCourse.vue'
import ShareDialog from '../components/ShareDialog.vue'
import ContactDialog from '../components/ContactDialog.vue'
import { useShopStore } from '@/stores/shop'
const { shopItem, shopRelatedList, list, shopRelatedListOther } = useShopStore()
const router = useRouter()
const route = useRoute()
console.log(shopItem, '------', shopRelatedList, '---111', shopRelatedListOther)
const courseItem: any = ref([])
const courseItem: any = ref([]) // 课程所属课程包
courseItem.value = list.filter((item: any) => item.type === '课程包' && item.child_ids.includes(shopItem?.id))
const recommendCourse = computed(() => {
if (shopItem?.type === '课程包') {
// 课程包推荐课程
return shopRelatedList
} else {
// 单一课程推荐的课程
return shopRelatedListOther
}
})
const buyDialogVisible = ref(false)
const shareDialogVisible = ref(false)
const handleBuyCourse = () => {
if (shopItem?.type === '课程包') {
buyDialogVisible.value = true
} else {
if (route.query.payStatus === '4') {
window.open('https://paa-learning.ezijing.com')
} else {
router.push(`/pay/${shopItem?.id}`)
}
}
}
// 分享
const handleShare = () => {
shareDialogVisible.value = true
}
</script>
<template>
......@@ -34,7 +58,7 @@ const recommendCourse = computed(() => {
</div>
</div>
<div class="con_share">
<div class="share_btn">
<div class="share_btn" @click="handleShare">
<img src="https://webapp-pub.ezijing.com/project_online/fi/icon_share.png" alt="" />
分享
</div>
......@@ -43,18 +67,28 @@ const recommendCourse = computed(() => {
</div>
<div class="detail_buy">
<div class="buy_left">
<div class="left_icon">¥</div>
<div class="left_price">{{ shopItem.price }}</div>
<div class="left_price" v-if="route.query.payStatus !== '4'">
<div class="price_icon">¥</div>
<div class="price_num">{{ shopItem?.price }}</div>
</div>
<div class="left_status" v-else>已购买</div>
<div class="left_tip tip1">支付安全</div>
<div class="left_tip tip2">课程回放</div>
</div>
<div class="buy_right">立即购买</div>
<div class="buy_right" @click="handleBuyCourse">
{{ route.query.payStatus === '4' ? '立即学习' : '立即购买' }}
</div>
</div>
<div class="detail_con">
<div class="con_left">
<div>
<IncludeCourseCard v-if="shopItem?.type !== '课程包'" :courseItem="courseItem" />
</div>
<div>
<CourseIntroduceCard :shopItem="shopItem" />
</div>
</div>
<div class="con_right">
<TeacherCard :lecturerList="shopItem?.lecturer_list" />
<RecommendCourse :shopRelatedList="recommendCourse" />
......@@ -65,12 +99,18 @@ const recommendCourse = computed(() => {
<div class="left_tit">全部课程价格</div>
<div class="left_desc">根据课程价格累积计算</div>
</div>
<div class="footer_price">
<div class="footer_price" v-if="route.query.payStatus !== '4'">
<div class="price_icon">¥</div>
<div class="price_price">{{ shopItem?.price }}</div>
</div>
<div class="footer_btn">立即购买</div>
<div class="left_status" v-else>已购买</div>
<div class="footer_btn" @click="handleBuyCourse">
{{ route.query.payStatus === '4' ? '立即学习' : '立即购买' }}
</div>
</div>
<ShareDialog v-model:shareDialogVisible="shareDialogVisible" />
<ContactDialog v-model:buyDialogVisible="buyDialogVisible" />
</div>
</template>
<style lang="scss" scoped>
......@@ -91,6 +131,7 @@ const recommendCourse = computed(() => {
.con_img {
width: 400px;
height: 225px;
border-radius: 6px;
}
.con_info {
margin-left: 41px;
......@@ -124,6 +165,7 @@ const recommendCourse = computed(() => {
}
.con_share {
padding-top: 29px;
cursor: pointer;
.share_btn {
padding: 0 17px;
......@@ -134,6 +176,7 @@ const recommendCourse = computed(() => {
font-weight: 400;
line-height: 30px;
color: #666666;
cursor: pointer;
}
}
}
......@@ -154,20 +197,29 @@ const recommendCourse = computed(() => {
.buy_left {
display: flex;
align-items: center;
.left_icon {
.left_price {
display: flex;
.price_icon {
font-size: 24px;
font-weight: 500;
line-height: 34px;
color: #aa1941;
margin-top: 6px;
}
.left_price {
.price_num {
font-size: 40px;
font-weight: normal;
line-height: 34px;
color: #aa1941;
margin-left: 3px;
}
}
.left_status {
font-size: 24px;
font-weight: normal;
line-height: 34px;
color: #666666;
}
.left_tip {
padding: 4px 6px 3px 7px;
background: #f4eadb;
......
import { defineStore } from 'pinia'
import { useUserStore } from './user'
const { courses } = useUserStore()
interface ShopFilter {
label: string
value: string
......@@ -19,23 +20,28 @@ export const useShopStore = defineStore({
list: (window as any).SHOP.SHOP_LIST // 商品列表
}),
getters: {
shopItem({ list }) {
shopList({ list }): ShopListItem[] {
return list.map(item => {
item.isBuy = !!courses.find((course: any) => course.course_id === item.course_id)
return item
})
},
shopItem() {
const route = useRoute()
const found = list.find(item => item.id === route.params.id)
console.log(found)
const found = this.shopList.find(item => item.id === route.params.id)
if (found) {
found.course_list =
found.child_ids && found.child_ids.length
? found.child_ids.map((id: string) => list.find(item => item.id === id))
: list.filter(item => item.child_ids?.includes(found.id))
? found.child_ids.map((id: string) => this.shopList.find(item => item.id === id))
: this.shopList.filter(item => item.child_ids?.includes(found.id))
}
return found
},
shopRelatedList({ list }): ShopListItem[] {
return list.filter((item: ShopListItem) => item.category !== this.shopItem?.category)
shopRelatedList(): ShopListItem[] {
return this.shopList.filter((item: ShopListItem) => item.category !== this.shopItem?.category)
},
shopRelatedListOther({ list }): ShopListItem[] {
return list.filter(
shopRelatedListOther(): ShopListItem[] {
return this.shopList.filter(
(item: ShopListItem) => item.category === this.shopItem?.category && item.id !== this.shopItem?.id
)
}
......
import { defineStore } from 'pinia'
import { getUser, logout } from '@/api/base'
import { getUser, logout, getBuyShop } from '@/api/base'
import type { UserState } from '@/types'
interface State {
user: UserState | null
courses: any
}
export const useUserStore = defineStore({
id: 'user',
state: (): State => ({
user: null
user: null,
courses: []
}),
getters: {
isLogin: state => !!state.user,
userName: ({ user }) => {
if (!user) return ''
return user.realname || user.nickname || user.username || ''
},
mobile: ({ user }) => {
if (!user) return ''
return user.mobile
}
},
actions: {
async getUser() {
const res = await getUser()
if (res.code === 0) {
this.courses = await getBuyShop()
}
this.user = res.data
},
async logout() {
await logout()
this.user = null
......
......@@ -10,6 +10,9 @@ const httpRequest = axios.create({
httpRequest.interceptors.response.use(
function (response) {
const { data } = response
if (data.code === undefined) {
return data
}
// 正常返回
if (data.code === 0) {
return data
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论