精品课
This commit is contained in:
+34
-241
@@ -28,7 +28,6 @@
|
||||
<image :src="banner.sroll_img || banner.image || '/static/lunbo_bg.png'" mode="aspectFill" class="banner-image"></image>
|
||||
<view class="banner-content">
|
||||
<text class="banner-title">{{ banner.title || '课程标题' }}</text>
|
||||
<text class="banner-subtitle">{{ banner.subtitle || '课程描述' }}</text>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
@@ -54,7 +53,7 @@
|
||||
:key="typeItem.id"
|
||||
@click="goVideoType(typeItem.id, typeItem.name)">
|
||||
<view class="function-icon">
|
||||
<image :src="typeItem.img" mode="aspectFit"></image>
|
||||
<image style="height: 50rpx;" :src="typeItem.img" mode="aspectFit"></image>
|
||||
<!-- <uni-icons :type="getFunctionIconType(index)" size="24" :color="getFunctionIconColor(index)"></uni-icons> -->
|
||||
</view>
|
||||
<text class="function-text">{{ typeItem.name }}</text>
|
||||
@@ -65,7 +64,7 @@
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">精品小课</text>
|
||||
<view class="more-btn" @click="goMorePremium">
|
||||
<view class="more-btn" @click="goMorePremium('精品小课')">
|
||||
<text>更多</text>
|
||||
<uni-icons type="arrowright" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
@@ -80,7 +79,7 @@
|
||||
<text class="course-title">{{ course.title }}</text>
|
||||
<view class="course-meta">
|
||||
<view class="participant-count">{{ course.study_num }}人</view>
|
||||
<text class="course-price" v-if="course.discount_price > 0">¥{{ course.discount_price }}</text>
|
||||
<text class="course-price" v-if="course.discount_price > 0">¥{{ (course.discount_price / 100).toFixed(2) }}</text>
|
||||
<text class="course-price free" v-else>免费</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -95,7 +94,7 @@
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">学完返现</text>
|
||||
<view class="more-btn" @click="goMoreRewards">
|
||||
<view class="more-btn" @click="goMoreRewards('学完返现')">
|
||||
<text>更多</text>
|
||||
<uni-icons type="arrowright" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
@@ -114,7 +113,7 @@
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">福利课堂</text>
|
||||
<view class="more-btn" @click="goMoreWelfare">
|
||||
<view class="more-btn" @click="goMoreWelfare('福利课堂')">
|
||||
<text>更多</text>
|
||||
<uni-icons type="arrowright" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
@@ -139,11 +138,11 @@
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 热门课程 -->
|
||||
<!-- 课程推荐 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">热门课程</text>
|
||||
<view class="more-btn" @click="goMoreHot">
|
||||
<text class="section-title">课程推荐</text>
|
||||
<view class="more-btn" @click="goMoreHot('课程推荐')">
|
||||
<text>更多</text>
|
||||
<uni-icons type="arrowright" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
@@ -154,7 +153,7 @@
|
||||
<view class="course-content">
|
||||
<text class="course-name">{{ course.title }}</text>
|
||||
<text class="course-desc">{{ course.tags || '' }}</text>
|
||||
<text class="course-cost">¥{{ course.discount_price }}</text>
|
||||
<text class="course-cost">¥{{ (course.discount_price / 100).toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -188,115 +187,7 @@ const premiumCourseList = ref([]) // 新增:精品小课数据
|
||||
const rewardCourseList = ref([]) // 新增:学完返现数据
|
||||
const welfareCourseList = ref([]) // 新增:福利课堂数据
|
||||
const recommendedCourseList = ref([]) // 新增:课程推荐数据
|
||||
const bannerList = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: "小懂医生讲HIV和感染",
|
||||
subtitle: "授课专家: 黄湛镰 副主任医师 | 中山大学附属第三医院感染科",
|
||||
image: "/static/lunbo_bg.png",
|
||||
link: "/pages_course/hiv_detail"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "肝脏肿瘤临床影像学习",
|
||||
subtitle: "授课专家: 王学浩 教授 | 南京医科大学第一附属医院",
|
||||
image: "/static/paper_bg.png",
|
||||
link: "/pages_course/tumor_detail"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "慢性肝病营养治疗指南",
|
||||
subtitle: "授课专家: 段钟平 教授 | 首都医科大学附属北京佑安医院",
|
||||
image: "/static/pap_bg.png",
|
||||
link: "/pages_course/nutrition_detail"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "肝移植术后管理要点",
|
||||
subtitle: "授课专家: 郑树森 院士 | 浙江大学医学院附属第一医院",
|
||||
image: "/static/bo_bg.png",
|
||||
link: "/pages_course/transplant_detail"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "肝癌早期筛查与预防",
|
||||
subtitle: "授课专家: 陈孝平 院士 | 华中科技大学同济医学院",
|
||||
image: "/static/livebg.png",
|
||||
link: "/pages_course/screening_detail"
|
||||
}
|
||||
])
|
||||
const hotCourses = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
description: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
price: "20.00",
|
||||
thumbnail: "/static/jingpinkecheng.png"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
description: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
price: "20.00",
|
||||
thumbnail: "/static/jingpinkecheng.png"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
description: "小懂医生讲HIV和感染-各类抗菌药特性解读III",
|
||||
price: "20.00",
|
||||
thumbnail: "/static/jingpinkecheng.png"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "小懂医生讲HIV和感染物",
|
||||
description: "",
|
||||
price: "50.00",
|
||||
thumbnail: "/static/jingpinkecheng.png"
|
||||
}
|
||||
])
|
||||
const welfareList = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: "《中生肝脏病学》云解读系列会七",
|
||||
subtitle: "汪晖 院士 北京大学医学院",
|
||||
teacher: "汪晖 院士",
|
||||
price: "免费",
|
||||
image: "/static/fulicard.png"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "肝病诊疗新进展研讨会",
|
||||
subtitle: "王学浩 教授 南京医科大学",
|
||||
teacher: "王学浩 教授",
|
||||
price: "免费",
|
||||
image: "/static/paper_bg.png"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "肝癌早期筛查与预防",
|
||||
subtitle: "陈孝平 院士 华中科技大学",
|
||||
teacher: "陈孝平 院士",
|
||||
price: "免费",
|
||||
image: "/static/pap_bg.png"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "慢性肝病营养治疗指南",
|
||||
subtitle: "段钟平 教授 首都医科大学",
|
||||
teacher: "段钟平 教授",
|
||||
price: "免费",
|
||||
image: "/static/bo_bg.png"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "肝移植术后管理要点",
|
||||
subtitle: "郑树森 院士 浙江大学医学院",
|
||||
teacher: "郑树森 院士",
|
||||
price: "免费",
|
||||
image: "/static/livebg.png"
|
||||
}
|
||||
])
|
||||
const bannerList = ref([])
|
||||
|
||||
// 方法
|
||||
const getSystemInfo = () => {
|
||||
@@ -311,7 +202,9 @@ const getSystemInfo = () => {
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
|
||||
const goSearch = () => {
|
||||
@@ -322,6 +215,9 @@ const goSearch = () => {
|
||||
|
||||
const goCategory = (type, name) => {
|
||||
console.log('跳转分类:', type, name)
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/course_filter/course_filter?type=${type}`
|
||||
})
|
||||
}
|
||||
|
||||
const getCategoryIconClass = (index) => {
|
||||
@@ -337,29 +233,6 @@ const getCategoryIconClass = (index) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getFunctionIconClass = (index) => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return 'fire-icon';
|
||||
case 1:
|
||||
return 'star-icon';
|
||||
case 2:
|
||||
return 'gift-icon';
|
||||
case 3:
|
||||
return 'heart-icon';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const goHotCourses = () => {
|
||||
console.log('跳转热门课程')
|
||||
}
|
||||
|
||||
const goPremiumCourses = () => {
|
||||
console.log('跳转精品小课')
|
||||
}
|
||||
|
||||
const goPremiumCourse = (course) => {
|
||||
console.log('跳转精品小课详情:', course)
|
||||
@@ -369,9 +242,6 @@ const goPremiumCourse = (course) => {
|
||||
})
|
||||
}
|
||||
|
||||
const goStudyRewards = () => {
|
||||
console.log('跳转学克返现')
|
||||
}
|
||||
|
||||
const goRewardCourse = (course) => {
|
||||
console.log('跳转返现课程:', course)
|
||||
@@ -381,24 +251,32 @@ const goRewardCourse = (course) => {
|
||||
})
|
||||
}
|
||||
|
||||
const goWelfareCourses = () => {
|
||||
console.log('跳转福利课堂')
|
||||
}
|
||||
|
||||
const goMorePremium = () => {
|
||||
const goMorePremium = (name) => {
|
||||
console.log('查看更多精品课程')
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/course_filter/course_filter?special_type=${specialTypeList.value.find(item => item.name == name).id}&special_name=${name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goMoreRewards = () => {
|
||||
const goMoreRewards = (name) => {
|
||||
console.log('查看更多返现')
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/course_filter/course_filter?special_type=${specialTypeList.value.find(item => item.name == name).id}&special_name=${name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goMoreWelfare = () => {
|
||||
const goMoreWelfare = (name) => {
|
||||
console.log('查看更多福利')
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/course_filter/course_filter?special_type=${specialTypeList.value.find(item => item.name == name).id}&special_name=${name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goMoreHot = () => {
|
||||
const goMoreHot = (name) => {
|
||||
console.log('查看更多热门')
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/course_filter/course_filter?special_type=${specialTypeList.value.find(item => item.name == name).id}&special_name=${name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goCourseDetail = (course) => {
|
||||
@@ -421,7 +299,7 @@ const goBannerDetail = (banner) => {
|
||||
console.log('横幅详情:', banner)
|
||||
// 跳转到课程详情页面
|
||||
uni.navigateTo({
|
||||
url: '/pages_course/course_detail/course_detail'
|
||||
url: '/pages_course/course_detail/course_detail?id=' + banner.id
|
||||
})
|
||||
}
|
||||
|
||||
@@ -487,104 +365,19 @@ const getIndex = async () => {
|
||||
}
|
||||
} else {
|
||||
console.warn('API返回数据格式异常:', res)
|
||||
setDefaultData()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取分类数据失败:', error)
|
||||
setDefaultData()
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认数据
|
||||
const setDefaultData = () => {
|
||||
// 设置默认分类数据作为备用
|
||||
firstTypeList.value = [
|
||||
{ id: 185, name: "肝病", img: "/static/icon_home_my_patient.png" },
|
||||
{ id: 188, name: "肿瘤", img: "/static/icon_home_my_library.png" },
|
||||
{ id: 195, name: "感染", img: "/static/icon_home_video.png" }
|
||||
]
|
||||
|
||||
// 设置默认功能按钮数据作为备用
|
||||
specialTypeList.value = [
|
||||
{ type_id: 189, type_name: "课程推荐" },
|
||||
{ type_id: 190, type_name: "精品小课" },
|
||||
{ type_id: 191, type_name: "学完返现" },
|
||||
{ type_id: 192, type_name: "福利课堂" }
|
||||
]
|
||||
|
||||
// 设置默认精品小课数据作为备用
|
||||
premiumCourseList.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: "肝脏胖瘦临床、影像学习交流",
|
||||
study_num: 28,
|
||||
discount_price: 20,
|
||||
index_img: "/static/jingpingke.png"
|
||||
}
|
||||
]
|
||||
|
||||
// 设置默认学完返现数据作为备用
|
||||
rewardCourseList.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: "重症肝病的抗凝治疗",
|
||||
discount_price: 2,
|
||||
back_bon: 2,
|
||||
index_img: "/static/paper_bg.png"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "重症肝病的抗凝治疗2",
|
||||
discount_price: 2,
|
||||
back_bon: 2,
|
||||
index_img: "/static/pap_bg.png"
|
||||
}
|
||||
]
|
||||
|
||||
// 设置默认福利课堂数据作为备用
|
||||
welfareCourseList.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: "《中生肝脏病学》云解读系列会七",
|
||||
special_type_name: "福利课堂",
|
||||
study_num: 8,
|
||||
fuli_bon: 100,
|
||||
index_img: "/static/fulicard.png"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "肝病诊疗新进展研讨会",
|
||||
special_type_name: "福利课堂",
|
||||
study_num: 5,
|
||||
fuli_bon: 88,
|
||||
index_img: "/static/paper_bg.png"
|
||||
}
|
||||
]
|
||||
|
||||
// 设置默认课程推荐数据作为备用
|
||||
recommendedCourseList.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: "测试精品课上报02",
|
||||
tags: "测试,肝胆相照",
|
||||
discount_price: 100,
|
||||
index_img: "/static/jingpinkecheng.png"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "测试精品课上报01",
|
||||
tags: "肝胆相照,健康中国",
|
||||
discount_price: 100,
|
||||
index_img: "/static/jingpinkecheng.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const goVideoType = (typeId, typeName) => {
|
||||
console.log('跳转视频类型:', typeId, typeName)
|
||||
// 跳转到视频类型详情页面
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/video_type_detail/video_type_detail?type_id=${typeId}`
|
||||
url: `/pages_course/course_filter/course_filter?special_type=${typeId}&special_name=${typeName}`
|
||||
})
|
||||
}
|
||||
// 生命周期
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">课程详情02</view>
|
||||
<view class="nav-title">{{ courseDetail.title || '肝硬化与重症肝病' }}</view>
|
||||
<view class="nav-right" @click="goShare">
|
||||
<uni-icons type="redo" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
@@ -106,7 +106,7 @@
|
||||
<!-- 课程目录内容 -->
|
||||
<view class="course-catalog" v-if="activeTab === 'catalog'">
|
||||
<!-- 课程概览 -->
|
||||
<view class="catalog-overview">
|
||||
<!-- <view class="catalog-overview">
|
||||
<view class="overview-item">
|
||||
<text class="overview-label">总课时</text>
|
||||
<text class="overview-value">{{ courseDetail.video_num || 0 }}节课</text>
|
||||
@@ -119,7 +119,7 @@
|
||||
<text class="overview-label">学习进度</text>
|
||||
<text class="overview-value">{{ courseDetail.study_status || 0 }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 课程章节列表 -->
|
||||
<view class="chapter-list">
|
||||
@@ -130,14 +130,14 @@
|
||||
<uni-icons :type="chapter.expanded ? 'down' : 'right'" size="16" color="#666"></uni-icons>
|
||||
<text class="chapter-name">{{ chapter.title }}</text>
|
||||
</view>
|
||||
<view class="chapter-meta">
|
||||
<!-- <view class="chapter-meta">
|
||||
<text class="lesson-count">{{ chapter.lessons.length }}节课</text>
|
||||
<text class="duration">{{ chapter.duration }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="chapter-status">
|
||||
<!-- <view class="chapter-status">
|
||||
<text class="status-text" :class="chapter.status">{{ chapter.statusText }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 课时列表 -->
|
||||
@@ -150,7 +150,7 @@
|
||||
</view>
|
||||
<view class="lesson-meta">
|
||||
<text class="lesson-duration">{{ lesson.duration }}</text>
|
||||
<text class="lesson-teacher">{{ lesson.teacher }}</text>
|
||||
<!-- <text class="lesson-teacher">{{ lesson.teacher }}</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="lesson-status">
|
||||
@@ -168,7 +168,9 @@
|
||||
<text class="review-title">课程评价</text>
|
||||
<view class="review-actions-header">
|
||||
<text class="review-count">共{{ reviewTotal || courseDetail.comment_num || 0 }}条评价</text>
|
||||
<button class="write-review-btn" @click="goToReview" v-if="courseDetail.is_buy === 1">写评价</button>
|
||||
<button class="write-review-btn" @click="goToReview" v-if="courseDetail.is_buy === 1">
|
||||
{{ courseDetail.is_commented === 1 ? '追加评论' : '写评价' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -254,7 +256,15 @@
|
||||
<uni-icons type="chat" size="80" color="#E0E0E0"></uni-icons>
|
||||
</view>
|
||||
<text class="empty-text">暂无评价</text>
|
||||
<text class="empty-desc">成为第一个评价的人吧</text>
|
||||
<text class="empty-desc" v-if="courseDetail.is_buy === 1 && courseDetail.is_commented === 1">
|
||||
您已经评价过此课程,可以追加评论
|
||||
</text>
|
||||
<text class="empty-desc" v-else-if="courseDetail.is_buy === 1">
|
||||
成为第一个评价的人吧
|
||||
</text>
|
||||
<text class="empty-desc" v-else>
|
||||
购买课程后即可评价
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载中状态 -->
|
||||
@@ -268,15 +278,17 @@
|
||||
</view>
|
||||
|
||||
<!-- 底部购买栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="bottom-bar" v-if="courseDetail.is_buy === 0">
|
||||
<view class="price-section">
|
||||
<text class="price">¥{{ currentPriceYuan.toFixed(2) }}</text>
|
||||
<text class="price" >¥{{ currentPriceYuan.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="buy-section">
|
||||
<button class="buy-btn" @click="goBuy" v-if="courseDetail.is_buy === 0">{{ courseDetail.is_buy === 0 ? '立即购买' : '已购买' }}</button>
|
||||
<button class="buy-btn" @click="goStudy" v-else style="background-color: #4CAF50;">开始学习</button>
|
||||
<button class="buy-btn" @click="goBuy" >立即购买</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-bar" v-else>
|
||||
<button class="buy-btn" @click="goStudy" style=" width: 100%;background-color: #4CAF50;color: #fff;">开始学习</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -569,48 +581,70 @@ const submitReply = (reviewId) => {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 找到对应的评价
|
||||
const review = reviewList.value.find(item => item.id === reviewId)
|
||||
|
||||
|
||||
if (review) {
|
||||
// 添加新回复
|
||||
const newReply = {
|
||||
id: Date.now(),
|
||||
userName: '我',
|
||||
avatar: '/static/icon_home_my_patient.png',
|
||||
time: new Date().toLocaleDateString(),
|
||||
content: replyText.value.trim(),
|
||||
isAuthor: false
|
||||
}
|
||||
|
||||
if (!review.replies) {
|
||||
review.replies = []
|
||||
}
|
||||
review.replies.push(newReply)
|
||||
|
||||
// 重置状态
|
||||
activeReplyId.value = null
|
||||
replyText.value = ''
|
||||
|
||||
uni.showToast({
|
||||
title: '回复成功',
|
||||
icon: 'success'
|
||||
course_api.addExcellencourseComment({
|
||||
excellentcourse_id: courseId.value,
|
||||
comment: replyText.value.trim(),
|
||||
p_id: reviewId,
|
||||
star:0,
|
||||
type:0
|
||||
}).then(res => {
|
||||
console.log('评价提交结果:', res)
|
||||
if(res.code == 200){
|
||||
uni.hideLoading()
|
||||
// 添加新回复
|
||||
const newReply = {
|
||||
id: Date.now(),
|
||||
userName: '我',
|
||||
avatar: '/static/icon_home_my_patient.png',
|
||||
time: new Date().toLocaleDateString(),
|
||||
content: replyText.value.trim(),
|
||||
isAuthor: false
|
||||
}
|
||||
|
||||
if (!review.replies) {
|
||||
review.replies = []
|
||||
}
|
||||
review.replies.push(newReply)
|
||||
|
||||
// 重置状态
|
||||
activeReplyId.value = null
|
||||
replyText.value = ''
|
||||
|
||||
uni.showToast({
|
||||
title: '回复成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const goToReview = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages_course/course_review/course_review'
|
||||
url: '/pages_course/course_review/course_review?id=' + courseId.value + '&is_commented=' + courseDetail.value.is_commented
|
||||
})
|
||||
}
|
||||
|
||||
const getCourseDetail = () => {
|
||||
course_api.excellencourseDetail(courseId.value).then(res => {
|
||||
course_api.excellencourseDetail(courseId.value).then(res => {
|
||||
console.log('课程详情数据:', res)
|
||||
if(res.code == 200){
|
||||
const data = res.data
|
||||
courseDetail.value = data
|
||||
|
||||
// 调试 is_commented 字段
|
||||
console.log('is_commented 字段值:', data.is_commented)
|
||||
console.log('is_buy 字段值:', data.is_buy)
|
||||
|
||||
// 更新页面数据
|
||||
updatePageData(data)
|
||||
// 优惠价格与倒计时
|
||||
@@ -694,8 +728,8 @@ const updatePageData = (data) => {
|
||||
title: lesson.title,
|
||||
duration: lesson.time || '未知时长',
|
||||
teacher: teacherName,
|
||||
status: (data.is_buy === 1 || data.type === 0) ? 'unlocked' : 'locked',
|
||||
statusText: (data.is_buy === 1 || data.type === 0) ? (data.type === 0 ? '试播' : '已解锁') : '未解锁',
|
||||
status: (data.is_buy === 1 || lesson.type === 0) ? 'unlocked' : 'locked',
|
||||
statusText: (data.is_buy === 1 || lesson.type === 0) ? (lesson.type === 0 ? '试播' : '已解锁') : '未解锁',
|
||||
vid: lesson.vid,
|
||||
type: lesson.type
|
||||
}
|
||||
@@ -1570,7 +1604,7 @@ onUnmounted(() => {
|
||||
border-top: 1rpx solid #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
padding: 0 24rpx 20rpx 24rpx;
|
||||
z-index: 999;
|
||||
|
||||
.price-section {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#FF4757"></uni-icons>
|
||||
<uni-icons type="left" size="24" color="#FF4757"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">肝病精品课</view>
|
||||
<view class="nav-title">{{ special_name?special_name:'肝病精品课' }}</view>
|
||||
<view class="nav-right" @click="goSearch">
|
||||
<uni-icons type="search" size="24" color="#FF4757"></uni-icons>
|
||||
</view>
|
||||
@@ -27,11 +27,11 @@
|
||||
indicator-color="#d8d8d8"
|
||||
indicator-active-color="#FF4757"
|
||||
>
|
||||
<swiper-item v-for="(banner, idx) in bannerList" :key="idx">
|
||||
<view class="banner-content" :style="{ background: banner.background }">
|
||||
<view class="main-logo">{{ banner.title }}</view>
|
||||
<view class="sub-title">{{ banner.subTitle }}</view>
|
||||
<view class="description">{{ banner.description }}</view>
|
||||
<swiper-item v-for="(banner, idx) in bannerList" :key="banner.id || idx">
|
||||
<image :src="banner.sroll_img || banner.image || '/static/lunbo_bg.png'" mode="aspectFill" class="banner-image"></image>
|
||||
<view class="banner-content">
|
||||
<text class="banner-title">{{ banner.title || '' }}</text>
|
||||
<text class="banner-subtitle">{{ banner.subtitle || '' }}</text>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
@@ -39,30 +39,63 @@
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<view class="filter-bar">
|
||||
<view class="filter-item" @click="showCategoryFilter">
|
||||
<text>全部课程</text>
|
||||
<uni-icons type="arrowdown" size="16" color="#666"></uni-icons>
|
||||
<view class="filter-item">
|
||||
<uni-data-select
|
||||
v-model="selectedCategory"
|
||||
:localdata="categoryOptions"
|
||||
:clear="false"
|
||||
placeholder="全部课程"
|
||||
@change="onCategoryChange"
|
||||
>
|
||||
<template v-slot:selected="{selectedItems}">
|
||||
<view class="filter-display">
|
||||
<text>{{ selectedItems.length > 0 ? selectedItems[0].text : '全部课程' }}</text>
|
||||
<uni-icons type="arrowdown" size="16" color="#666"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-data-select>
|
||||
</view>
|
||||
<view class="filter-item" @click="showSortFilter">
|
||||
<text>默认排序</text>
|
||||
<uni-icons type="arrowdown" size="16" color="#666"></uni-icons>
|
||||
<view class="filter-item">
|
||||
<uni-data-select
|
||||
v-model="selectedSort"
|
||||
:localdata="sortOptions"
|
||||
:clear="false"
|
||||
placeholder="默认排序"
|
||||
@change="onSortChange"
|
||||
>
|
||||
<template v-slot:selected="{selectedItems}">
|
||||
<view class="filter-display">
|
||||
<text>{{ selectedItems.length > 0 ? selectedItems[0].text : '默认排序' }}</text>
|
||||
<uni-icons type="arrowdown" size="16" color="#666"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-data-select>
|
||||
</view>
|
||||
<view class="separator"></view>
|
||||
<view class="filter-item" @click="toggleLimitedOffer">
|
||||
<text :class="{ active: filters.limitedOffer }">限时优惠</text>
|
||||
<view v-if="special_type == 0" class="separator"></view>
|
||||
<view v-if="special_type == 0" class="filter-item" :class="{ active: filters.limitedOffer }" @click="toggleLimitedOffer">
|
||||
<text>限时优惠</text>
|
||||
</view>
|
||||
<view class="separator"></view>
|
||||
<view class="filter-item" @click="toggleFreeCourses">
|
||||
<text :class="{ active: filters.freeCourses }">免费课程</text>
|
||||
<view v-if="special_type == 0" class="separator"></view>
|
||||
<view v-if="special_type == 0" class="filter-item" :class="{ active: filters.freeCourses }" @click="toggleFreeCourses">
|
||||
<text>免费课程</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 课程列表 -->
|
||||
<view class="course-list">
|
||||
<!-- 空状态 -->
|
||||
<view v-if="courseList.length === 0" class="empty-state">
|
||||
<image src="/static/empty_course.png" mode="aspectFit" class="empty-image"></image>
|
||||
<text class="empty-text">暂无相关课程</text>
|
||||
<text class="empty-tip">试试调整筛选条件吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 课程列表 -->
|
||||
<view
|
||||
v-else
|
||||
class="course-item"
|
||||
v-for="(course, index) in courseList"
|
||||
:key="index"
|
||||
:key="course.id"
|
||||
@click="goToCourseDetail(course)"
|
||||
>
|
||||
<view class="course-image">
|
||||
@@ -70,16 +103,26 @@
|
||||
<view class="learner-count" v-if="course.learnerCount">
|
||||
{{ course.learnerCount }}人学
|
||||
</view>
|
||||
<!-- <view class="video-count" v-if="course.videoNum">
|
||||
{{ course.videoNum }}课时
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="course-info">
|
||||
<view class="course-title">{{ course.title }}</view>
|
||||
<view class="course-instructor" v-if="course.instructor">
|
||||
{{ course.instructor }}
|
||||
</view>
|
||||
<view class="course-tags" v-if="course.tags && course.tags.length > 0">
|
||||
<text class="tag" v-for="tag in course.tags.slice(0, 2)" :key="tag">{{ tag }}</text>
|
||||
</view>
|
||||
<view class="course-price">
|
||||
<text class="current-price">¥{{ course.price }}</text>
|
||||
<text class="original-price" v-if="course.originalPrice">¥{{ course.originalPrice }}</text>
|
||||
</view>
|
||||
<view class="course-bonuses" v-if="course.backBonus > 0 || course.welfareBonus > 0">
|
||||
<text class="bonus back-bonus" v-if="course.backBonus > 0">返现{{ course.backBonus }}积分</text>
|
||||
<text class="bonus welfare-bonus" v-if="course.welfareBonus > 0">福利{{ course.welfareBonus }}积分</text>
|
||||
</view>
|
||||
<view class="limited-offer" v-if="course.limitedOffer">限时优惠</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -97,148 +140,52 @@
|
||||
<text>我的课程</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分类筛选弹窗 -->
|
||||
<uni-popup ref="categoryPopup" type="bottom">
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<text>选择课程分类</text>
|
||||
<view class="close-btn" @click="closeCategoryPopup">
|
||||
<uni-icons type="close" size="20" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-options">
|
||||
<view
|
||||
class="popup-option"
|
||||
:class="{ active: selectedCategory === option.value }"
|
||||
v-for="option in categoryOptions"
|
||||
:key="option.value"
|
||||
@click="selectCategory(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 排序筛选弹窗 -->
|
||||
<uni-popup ref="sortPopup" type="bottom">
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<text>选择排序方式</text>
|
||||
<view class="close-btn" @click="closeSortPopup">
|
||||
<uni-icons type="close" size="20" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-options">
|
||||
<view
|
||||
class="popup-option"
|
||||
:class="{ active: selectedSort === option.value }"
|
||||
v-for="option in sortOptions"
|
||||
:key="option.value"
|
||||
@click="selectSort(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import course_api from '@/api/course_api'
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
const navBarHeight = ref(88)
|
||||
// 轮播横幅
|
||||
const bannerList = ref([
|
||||
{
|
||||
title: '辩以明思',
|
||||
subTitle: '慢乙肝治疗新起点',
|
||||
description: '肝胆相照·全国中青年医师 学术争鸣辩论赛',
|
||||
background: 'linear-gradient(135deg, #87CEEB 0%, #E0F6FF 100%)'
|
||||
},
|
||||
{
|
||||
title: '名医大讲堂',
|
||||
subTitle: '临床思维系统课程',
|
||||
description: '顶尖专家带你系统化掌握诊疗要点',
|
||||
background: 'linear-gradient(135deg, #FFE8E8 0%, #FFF5F5 100%)'
|
||||
},
|
||||
{
|
||||
title: '专科提升',
|
||||
subTitle: '循证与实践结合',
|
||||
description: '以真实病例训练临床路径与决策',
|
||||
background: 'linear-gradient(135deg, #E6F7FF 0%, #F2FFFC 100%)'
|
||||
const bannerList = ref([])
|
||||
|
||||
const getIndexBanners = async () => {
|
||||
try {
|
||||
const res = await course_api.index()
|
||||
if (res.code === 200 && res.data && res.data.scroll_list) {
|
||||
bannerList.value = res.data.scroll_list
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取横幅失败:', e)
|
||||
}
|
||||
])
|
||||
}
|
||||
// 筛选条件
|
||||
const filters = ref({
|
||||
limitedOffer: false,
|
||||
freeCourses: false
|
||||
})
|
||||
// 选中的分类和排序
|
||||
const selectedCategory = ref('all')
|
||||
const selectedSort = ref('default')
|
||||
const selectedCategory = ref('')
|
||||
const selectedSort = ref('0')
|
||||
// 分类选项
|
||||
const categoryOptions = ref([
|
||||
{ value: 'all', label: '全部课程' },
|
||||
{ value: 'liver', label: '肝病相关' },
|
||||
{ value: 'hepatitis', label: '肝炎治疗' },
|
||||
{ value: 'cirrhosis', label: '肝硬化' },
|
||||
{ value: 'cancer', label: '肝癌' }
|
||||
{ value: '', text: '全部课程' }
|
||||
])
|
||||
// 排序选项
|
||||
const sortOptions = ref([
|
||||
{ value: 'default', label: '默认排序' },
|
||||
{ value: 'newest', label: '最新发布' },
|
||||
{ value: 'popular', label: '最受欢迎' },
|
||||
{ value: 'price', label: '价格排序' }
|
||||
{ value: '0', text: '默认排序' },
|
||||
{ value: '0', text: '最新上架' },
|
||||
{ value: '1', text: '人气最高' },
|
||||
{ value: '2', text: '课时数' },
|
||||
{ value: '3', text: '价格升序' },
|
||||
{ value: '4', text: '价格降序' }
|
||||
])
|
||||
// 课程列表
|
||||
const courseList = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '找到已购买课程即学习找到已购买课程即可学习',
|
||||
instructor: '佑安医院 临床思维 陈煜',
|
||||
price: 39,
|
||||
image: '/static/placeholder_doctor.png',
|
||||
learnerCount: 123
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '找到已购买课程即学习找到已购买课程即可学习',
|
||||
price: 39,
|
||||
image: '/static/placeholder_doctor.png',
|
||||
learnerCount: 89
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '找到已购买课程即学习找到已购买课程即可学习',
|
||||
price: 29,
|
||||
originalPrice: 39,
|
||||
image: '/static/placeholder_doctor.png',
|
||||
limitedOffer: true,
|
||||
learnerCount: 156
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '找到已购买课程即学习找到已购买课程即可学习',
|
||||
price: 29,
|
||||
originalPrice: 39,
|
||||
image: '/static/placeholder_doctor.png',
|
||||
limitedOffer: true,
|
||||
learnerCount: 78
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '找到已购买课程即学习找到已购买课程即可学习',
|
||||
price: 39,
|
||||
image: '/static/placeholder_doctor.png',
|
||||
learnerCount: 234
|
||||
}
|
||||
])
|
||||
const courseList = ref([])
|
||||
|
||||
// 方法
|
||||
const getSystemInfo = () => {
|
||||
@@ -274,44 +221,7 @@ const goToCourseDetail = (course) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 显示分类筛选
|
||||
const showCategoryFilter = () => {
|
||||
// 这里需要获取弹窗引用,暂时注释
|
||||
// this.$refs.categoryPopup.open()
|
||||
console.log('显示分类筛选')
|
||||
}
|
||||
|
||||
// 关闭分类筛选
|
||||
const closeCategoryPopup = () => {
|
||||
// this.$refs.categoryPopup.close()
|
||||
console.log('关闭分类筛选')
|
||||
}
|
||||
|
||||
// 选择分类
|
||||
const selectCategory = (value) => {
|
||||
selectedCategory.value = value
|
||||
closeCategoryPopup()
|
||||
filterCourses()
|
||||
}
|
||||
|
||||
// 显示排序筛选
|
||||
const showSortFilter = () => {
|
||||
// this.$refs.sortPopup.open()
|
||||
console.log('显示排序筛选')
|
||||
}
|
||||
|
||||
// 关闭排序筛选
|
||||
const closeSortPopup = () => {
|
||||
// this.$refs.sortPopup.close()
|
||||
console.log('关闭排序筛选')
|
||||
}
|
||||
|
||||
// 选择排序
|
||||
const selectSort = (value) => {
|
||||
selectedSort.value = value
|
||||
closeSortPopup()
|
||||
sortCourses()
|
||||
}
|
||||
// 分类选择已通过uni-data-select组件处理
|
||||
|
||||
// 切换限时优惠
|
||||
const toggleLimitedOffer = () => {
|
||||
@@ -328,18 +238,119 @@ const toggleFreeCourses = () => {
|
||||
// 筛选课程
|
||||
const filterCourses = () => {
|
||||
// 这里实现课程筛选逻辑
|
||||
console.log('筛选条件:', filters.value, selectedCategory.value)
|
||||
console.log('筛选条件:', filters.value, selectedCategory.value, selectedSort.value)
|
||||
|
||||
let data = {
|
||||
first_type: type_id.value,
|
||||
second_type: selectedCategory.value,
|
||||
sort: selectedSort.value,
|
||||
page: 1
|
||||
}
|
||||
if(filters.value.limitedOffer){
|
||||
data.discount_type = 1
|
||||
}
|
||||
if(filters.value.freeCourses){
|
||||
data.free = 1
|
||||
}
|
||||
|
||||
course_api.excellencourseList(data).then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data && res.data.list) {
|
||||
// 转换API数据为课程列表格式
|
||||
courseList.value = res.data.list.map(item => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
instructor: item.special_type_name || '专业讲师',
|
||||
price: ((item.discount_price || item.account || 0) / 100).toFixed(2), // 分转元
|
||||
originalPrice: item.discount_price && item.account && item.discount_price !== item.account ? (item.account / 100).toFixed(2) : null, // 分转元
|
||||
image: item.index_img || '/static/placeholder_doctor.png',
|
||||
learnerCount: item.study_num || 0,
|
||||
limitedOffer: item.discount_type === 1,
|
||||
videoNum: item.video_num || 0,
|
||||
backBonus: item.back_bon || 0,
|
||||
welfareBonus: item.fuli_bon || 0,
|
||||
tags: item.tags ? item.tags.split(',') : []
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 排序课程
|
||||
const sortCourses = () => {
|
||||
// 这里实现课程排序逻辑
|
||||
console.log('排序方式:', selectedSort.value)
|
||||
const special_type = ref(0)
|
||||
const special_name = ref('')
|
||||
const type_id = ref(0)
|
||||
|
||||
const getFirstType = () => {
|
||||
course_api.listExcellencourseFirstType().then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data) {
|
||||
// 转换数据结构为uni-data-select需要的格式
|
||||
const options = res.data.map(item => ({
|
||||
value: item.id.toString(),
|
||||
text: item.name
|
||||
}))
|
||||
// 添加"全部课程"选项
|
||||
categoryOptions.value = [
|
||||
{ value: '', text: '全部课程' },
|
||||
...options
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
const getSecondType = () => {
|
||||
course_api.listExcellencourseSecondType(type_id.value).then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data) {
|
||||
// 转换数据结构为uni-data-select需要的格式
|
||||
const options = res.data.map(item => ({
|
||||
value: item.id.toString(),
|
||||
text: item.name
|
||||
}))
|
||||
// 添加"全部课程"选项
|
||||
categoryOptions.value = [
|
||||
{ value: '', text: '全部课程' },
|
||||
...options
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onCategoryChange = (e) => {
|
||||
selectedCategory.value = e
|
||||
console.log('选择的分类:', e)
|
||||
// 自动触发课程筛选
|
||||
filterCourses()
|
||||
}
|
||||
|
||||
const onSortChange = (e) => {
|
||||
selectedSort.value = e
|
||||
console.log('选择的排序:', e)
|
||||
filterCourses()
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 获取页面参数
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage.options || {}
|
||||
|
||||
type_id.value = options.type
|
||||
special_type.value = options.special_type
|
||||
special_name.value = options.special_name
|
||||
console.log('特殊类型ID:', options.special_type)
|
||||
console.log('类型ID:', options.type)
|
||||
getSystemInfo()
|
||||
if(type_id.value > 0){
|
||||
getSecondType()
|
||||
}
|
||||
if(special_type.value > 0){
|
||||
getFirstType()
|
||||
}
|
||||
// 获取顶部横幅,与course.vue一致
|
||||
getIndexBanners()
|
||||
|
||||
// 初始加载课程列表
|
||||
filterCourses()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -389,42 +400,46 @@ onMounted(() => {
|
||||
|
||||
// 主横幅
|
||||
.main-banner {
|
||||
margin: 0rpx;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
|
||||
.banner-swiper {
|
||||
height: 300rpx;
|
||||
height: 380rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
padding: 48rpx 32rpx;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.main-logo {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #FF4757;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
background-color: #FF4757;
|
||||
color: #fff;
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
display: inline-block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #0066CC;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 32rpx 24rpx;
|
||||
background: linear-gradient(transparent, rgba(0,0,0,0.6));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6rpx;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.banner-subtitle {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: rgba(255,255,255,0.9);
|
||||
line-height: 1.4;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0,0,0,0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,70 +448,133 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
background: #ffffff;
|
||||
border-bottom: 1rpx solid #f1f3f4;
|
||||
gap: 8rpx; // 减少间距
|
||||
flex-wrap: nowrap; // 不换行
|
||||
position: relative; // 添加相对定位
|
||||
z-index: 100; // 提高层级
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.3s;
|
||||
padding: 12rpx 0rpx; // 恢复左右内边距
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 24rpx; // 稍微减小字体
|
||||
color: #495057;
|
||||
font-weight: 500;
|
||||
flex: 1; // 平均分配空间
|
||||
min-width: 0; // 允许压缩
|
||||
justify-content: center; // 居中显示
|
||||
position: relative; // 添加相对定位
|
||||
z-index: 101; // 提高层级
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
color: #FF4757;
|
||||
}
|
||||
// 让选择器组件自适应宽度
|
||||
uni-data-select {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
z-index: 102; // 最高层级
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f5f5f5;
|
||||
&:hover {
|
||||
background: #e9ecef;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #FF4757;
|
||||
color: #ffffff;
|
||||
border-color: #FF4757;
|
||||
|
||||
text { color: #ffffff; }
|
||||
}
|
||||
|
||||
text {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
white-space: nowrap; // 文字不换行
|
||||
overflow: hidden; // 超出隐藏
|
||||
text-overflow: ellipsis; // 显示省略号
|
||||
}
|
||||
|
||||
.filter-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4rpx; // 减少间距
|
||||
width: 100%;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 2rpx;
|
||||
height: 32rpx;
|
||||
background-color: #e0e0e0;
|
||||
margin: 0 16rpx;
|
||||
width: 1rpx;
|
||||
height: 24rpx;
|
||||
background: #dee2e6;
|
||||
margin: 0 2rpx; // 减少间距
|
||||
flex: 0 0 auto; // 分隔符不压缩
|
||||
}
|
||||
}
|
||||
|
||||
// 课程列表
|
||||
.course-list {
|
||||
padding: 24rpx;
|
||||
background: #f8f9fa;
|
||||
position: relative; // 添加相对定位
|
||||
z-index: 1; // 降低层级,确保在筛选栏下方
|
||||
|
||||
.course-item {
|
||||
display: flex;
|
||||
padding: 24rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
background: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-2rpx);
|
||||
}
|
||||
|
||||
.course-image {
|
||||
position: relative;
|
||||
margin-right: 24rpx;
|
||||
margin-right: 20rpx;
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.learner-count {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
left: 8rpx;
|
||||
background-color: #FF4757;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
.video-count {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
right: 8rpx;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,50 +586,123 @@ onMounted(() => {
|
||||
|
||||
.course-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
color: #212529;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 8rpx;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.course-instructor {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 16rpx;
|
||||
color: #6c757d;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.course-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.tag {
|
||||
font-size: 22rpx;
|
||||
color: #FF4757;
|
||||
background: #FFF5F5;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid #FF4757;
|
||||
}
|
||||
}
|
||||
|
||||
.course-price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.current-price {
|
||||
font-size: 32rpx;
|
||||
color: #FF4757;
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
color: #adb5bd;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.course-bonuses {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.bonus {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid;
|
||||
}
|
||||
|
||||
.back-bonus {
|
||||
color: #FF4757;
|
||||
border-color: #FF4757;
|
||||
background: #FFF5F5;
|
||||
}
|
||||
|
||||
.welfare-bonus {
|
||||
color: #28a745;
|
||||
border-color: #28a745;
|
||||
background: #F2FFFC;
|
||||
}
|
||||
}
|
||||
|
||||
.limited-offer {
|
||||
background-color: #FF4757;
|
||||
background: #FF4757;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
border-radius: 4rpx;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.empty-image {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 32rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部导航栏
|
||||
@@ -560,9 +711,9 @@ onMounted(() => {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background-color: #fff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
height: 100rpx;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid #e9ecef;
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
|
||||
@@ -572,11 +723,13 @@ onMounted(() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
gap: 4rpx;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-size: 22rpx;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@@ -586,53 +739,4 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗样式
|
||||
.popup-content {
|
||||
background-color: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 32rpx;
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.popup-option {
|
||||
padding: 24rpx;
|
||||
text-align: center;
|
||||
border-radius: 16rpx;
|
||||
background-color: #f8f9fa;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.active {
|
||||
background-color: #FF4757;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">课程支付</view>
|
||||
<view class="nav-right"></view>
|
||||
@@ -88,6 +88,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import course_api from "@/api/course_api.js"
|
||||
import { requestPayment } from '@/utils/payment.js'
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
@@ -203,12 +204,35 @@ const confirmPayment = () => {
|
||||
|
||||
order_pay_type.value = computeOrderPayType()
|
||||
console.log('确认支付,支付方式:', { usePoints: usePoints.value, useBalance: useBalance.value, order_pay_type: order_pay_type.value })
|
||||
course_api.createExcellencourseMixedOrder(courseId.value, order_pay_type.value).then(res => {
|
||||
let openid = ""
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
openid = uni.getStorageSync('DEV_APPID');
|
||||
} else {
|
||||
openid = uni.getStorageSync('AUTH_APPID');
|
||||
}
|
||||
course_api.createExcellencourseMixedOrder(courseId.value, order_pay_type.value, openid).then(res => {
|
||||
console.log('创建订单:', res)
|
||||
if (res.code == 200) {
|
||||
uni.redirectTo({
|
||||
url: '/pages_course/course_detail/course_detail?id='+courseId.value
|
||||
})
|
||||
|
||||
const payParams = res.data.order
|
||||
|
||||
console.log(payParams)
|
||||
requestPayment(
|
||||
payParams,
|
||||
// 支付成功
|
||||
(res) => {
|
||||
uni.showToast({ title: '支付成功', icon: 'success' })
|
||||
// 可以跳转到支付成功页,或者查询订单状态
|
||||
},
|
||||
// 支付失败
|
||||
(err) => {
|
||||
console.error('支付失败', err)
|
||||
uni.showToast({ title: '支付失败或取消', icon: 'none' })
|
||||
}
|
||||
)
|
||||
// uni.redirectTo({
|
||||
// url: '/pages_course/course_detail/course_detail?id='+courseId.value
|
||||
// })
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">评价课程</view>
|
||||
<view class="nav-right"></view>
|
||||
@@ -15,22 +15,22 @@
|
||||
<view class="page-content" :style="{ paddingTop: navBarHeight + 'px' }">
|
||||
<!-- 星级评分区域 -->
|
||||
<view class="rating-section">
|
||||
<text class="rating-title">点击星星评分</text>
|
||||
<view class="stars-container">
|
||||
<view
|
||||
class="star-item"
|
||||
v-for="star in 5"
|
||||
:key="star"
|
||||
@click="setRating(star)"
|
||||
>
|
||||
<uni-icons
|
||||
:type="star <= currentRating ? 'star-filled' : 'star'"
|
||||
size="48"
|
||||
:color="star <= currentRating ? '#FFB800' : '#E0E0E0'"
|
||||
></uni-icons>
|
||||
</view>
|
||||
<uni-rate
|
||||
v-model="currentRating"
|
||||
:max="5"
|
||||
:value="currentRating"
|
||||
:size="48"
|
||||
:margin="16"
|
||||
:allow-half="true"
|
||||
:touchable="true"
|
||||
@change="onRatingChange"
|
||||
/>
|
||||
</view>
|
||||
<view class="rating-display">
|
||||
<text class="rating-score">{{ currentRating * 2 }}分</text>
|
||||
<text class="rating-feedback">{{ ratingFeedback }}</text>
|
||||
</view>
|
||||
<text class="rating-feedback">{{ ratingFeedback }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 评价输入区域 -->
|
||||
@@ -62,6 +62,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import course_api from "@/api/course_api.js"
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
@@ -82,22 +83,29 @@ const getSystemInfo = () => {
|
||||
// #endif
|
||||
}
|
||||
|
||||
const setRating = (rating) => {
|
||||
currentRating.value = rating
|
||||
const onRatingChange = (e) => {
|
||||
currentRating.value = e.value
|
||||
updateRatingFeedback()
|
||||
}
|
||||
|
||||
const updateRatingFeedback = () => {
|
||||
const feedbacks = {
|
||||
0.5: '很差,课程几乎没有帮助',
|
||||
1: '差评,课程没有任何帮助',
|
||||
1.5: '较差,课程帮助很小',
|
||||
2: '一般,课程内容一般',
|
||||
2.5: '一般偏上,课程有一定帮助',
|
||||
3: '还行,课程有一定帮助',
|
||||
3.5: '不错,课程内容较好',
|
||||
4: '不错,课程内容很好',
|
||||
4.5: '很好,课程内容很棒',
|
||||
5: '很好,课程非常棒'
|
||||
}
|
||||
ratingFeedback.value = feedbacks[currentRating.value] || ''
|
||||
}
|
||||
|
||||
const courseId = ref(0)
|
||||
const is_commented = ref(0)
|
||||
const submitReview = () => {
|
||||
if (currentRating.value === 0) {
|
||||
uni.showToast({
|
||||
@@ -119,20 +127,23 @@ const submitReview = () => {
|
||||
uni.showLoading({
|
||||
title: '提交中...'
|
||||
})
|
||||
course_api.addExcellencourseComment({
|
||||
excellentcourse_id: courseId.value,
|
||||
comment: reviewContent.value,
|
||||
star: currentRating.value,
|
||||
type: is_commented.value
|
||||
}).then(res => {
|
||||
console.log('评价提交结果:', res)
|
||||
if(res.code == 200){
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '评价提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
uni.navigateBack()
|
||||
})
|
||||
|
||||
// 模拟提交
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '评价提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
@@ -142,6 +153,14 @@ const goBack = () => {
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
// 获取页面参数
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage.options || {}
|
||||
is_commented.value = options.is_commented
|
||||
|
||||
courseId.value = options.id
|
||||
console.log('课程ID:', options.id)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -203,19 +222,26 @@ onMounted(() => {
|
||||
.stars-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.star-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.rating-feedback {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
.rating-display {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.rating-score {
|
||||
font-size: 32rpx;
|
||||
color: #FFB800;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.rating-feedback {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,712 @@
|
||||
<template>
|
||||
<view class="invoice-page">
|
||||
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">开具发票</view>
|
||||
<view class="nav-right">
|
||||
<view class="info-btn" @click="showInfo">
|
||||
<uni-icons type="info" size="20" color="#666"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 标签页 -->
|
||||
<view class="tabs" :style="{ paddingTop: navBarHeight + 'px' }">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'pending' }"
|
||||
@click="switchTab('pending')"
|
||||
>
|
||||
<text>待开发票</text>
|
||||
</view>
|
||||
<view class="tab-divider"></view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'history' }"
|
||||
@click="switchTab('history')"
|
||||
>
|
||||
<text>开票历史</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 待开发票内容 -->
|
||||
<view v-if="activeTab === 'pending'" class="pending-content">
|
||||
<!-- 课程列表 -->
|
||||
<scroll-view class="course-list" scroll-y>
|
||||
<view
|
||||
class="course-item"
|
||||
v-for="(course, index) in courseList"
|
||||
:key="index"
|
||||
@click="toggleSelect(index)"
|
||||
>
|
||||
<view class="checkbox" :class="{ selected: course.selected }">
|
||||
<text v-if="course.selected" class="checkmark">✓</text>
|
||||
</view>
|
||||
<image class="course-image" :src="course.image" mode="aspectFill"></image>
|
||||
<view class="course-info">
|
||||
<text class="course-desc">{{ course.description }}</text>
|
||||
<view class="course-meta">
|
||||
<text class="course-type">{{ course.typeName }}</text>
|
||||
<text class="course-price">实际支付: ¥{{ course.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="select-all" @click="toggleSelectAll">
|
||||
<view class="checkbox" :class="{ selected: isAllSelected }">
|
||||
<text v-if="isAllSelected" class="checkmark">✓</text>
|
||||
</view>
|
||||
<text class="select-all-text">全选</text>
|
||||
</view>
|
||||
<view class="invoice-info">
|
||||
<text class="invoice-amount">开票金额: ¥{{ totalAmount }}</text>
|
||||
<text class="selected-count">(已选{{ selectedCount }}个课程)</text>
|
||||
</view>
|
||||
<view class="invoice-btn" @click="issueInvoice">
|
||||
<text>开具发票</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 开票历史内容 -->
|
||||
<view v-if="activeTab === 'history'" class="history-content">
|
||||
<scroll-view class="history-list" scroll-y>
|
||||
<view
|
||||
class="history-item"
|
||||
v-for="(item, index) in historyList"
|
||||
:key="index"
|
||||
@click="viewHistoryDetail(item)"
|
||||
>
|
||||
<view class="history-left">
|
||||
<text class="history-date">{{ item.date }}</text>
|
||||
<text class="history-count">{{ item.courseCount }}</text>
|
||||
</view>
|
||||
<view class="history-right">
|
||||
<text class="history-status" :class="item.status">{{ item.statusText }}</text>
|
||||
<view class="history-arrow">›</view>
|
||||
<text class="history-amount">{{ item.amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import course_api from "@/api/course_api.js"
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
const navBarHeight = ref(88)
|
||||
const activeTab = ref('pending')
|
||||
const courseList = ref([])
|
||||
|
||||
// 开票历史数据
|
||||
const historyList = ref([])
|
||||
|
||||
// 计算属性
|
||||
const selectedCount = computed(() => {
|
||||
return courseList.value.filter(course => course.selected).length
|
||||
})
|
||||
|
||||
const totalAmount = computed(() => {
|
||||
return courseList.value
|
||||
.filter(course => course.selected)
|
||||
.reduce((sum, course) => sum + course.price, 0)
|
||||
.toFixed(2)
|
||||
})
|
||||
|
||||
const isAllSelected = computed(() => {
|
||||
return courseList.value.length > 0 && courseList.value.every(course => course.selected)
|
||||
})
|
||||
|
||||
// 方法
|
||||
const getSystemInfo = () => {
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = systemInfo.statusBarHeight
|
||||
// #ifdef MP-WEIXIN
|
||||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||||
// #endif
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const showInfo = () => {
|
||||
uni.showToast({
|
||||
title: '发票说明',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const switchTab = (tab) => {
|
||||
activeTab.value = tab
|
||||
if (tab === 'history') {
|
||||
course_api.listExcellencourseOrderInvoiceHistory(1).then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data && res.data.list) {
|
||||
// 将API返回的开票历史数据映射到组件使用的数据结构
|
||||
historyList.value = res.data.list.map(item => ({
|
||||
id: item.id,
|
||||
date: formatDate(item.create_date),
|
||||
courseCount: `共计${item.num}个课程`,
|
||||
status: getStatusClass(item.status),
|
||||
statusText: getStatusText(item.status),
|
||||
amount: `¥${item.amount.toFixed(2)}`
|
||||
}))
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取开票历史失败:', err)
|
||||
uni.showToast({
|
||||
title: '获取开票历史失败',
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
return `${year}年${month}月${day}日`
|
||||
}
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return 'success'
|
||||
case 0:
|
||||
return 'processing'
|
||||
case 2:
|
||||
return 'failed'
|
||||
default:
|
||||
return 'failed'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '已完成'
|
||||
case 0:
|
||||
return '开票中'
|
||||
case 2:
|
||||
return '信息有误'
|
||||
default:
|
||||
return '开票失败'
|
||||
}
|
||||
}
|
||||
|
||||
const toggleSelect = (index) => {
|
||||
courseList.value[index].selected = !courseList.value[index].selected
|
||||
}
|
||||
|
||||
const toggleSelectAll = () => {
|
||||
const newState = !isAllSelected.value
|
||||
courseList.value.forEach(course => {
|
||||
course.selected = newState
|
||||
})
|
||||
}
|
||||
|
||||
const issueInvoice = () => {
|
||||
if (selectedCount.value === 0) {
|
||||
uni.showToast({
|
||||
title: '请选择要开票的课程',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 跳转到填写发票信息页面
|
||||
uni.navigateTo({
|
||||
url: '/pages_course/invoice_info/invoice_info'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看开票历史详情
|
||||
const viewHistoryDetail = (item) => {
|
||||
console.log('查看开票历史详情:', item)
|
||||
|
||||
// 跳转到发票详情页面
|
||||
uni.navigateTo({
|
||||
url: `/pages_course/invoice_detail/invoice_detail?invoice_id=${item.id}`
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
|
||||
course_api.listExcellencourseOrderNoInvoice().then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data) {
|
||||
// 将API返回的数据映射到组件使用的数据结构
|
||||
courseList.value = res.data.map(item => ({
|
||||
id: item.uuid,
|
||||
description: item.excellencourse_title,
|
||||
price: item.account,
|
||||
image: item.excellencourse_img,
|
||||
selected: false,
|
||||
typeName: item.type_name
|
||||
}))
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取课程列表失败:', err)
|
||||
uni.showToast({
|
||||
title: '获取课程列表失败',
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 30rpx 0;
|
||||
height: 88rpx;
|
||||
|
||||
.time {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.status-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
|
||||
.signal, .wifi, .battery {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background-color: #000000;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #e5e5e5;
|
||||
|
||||
.nav-content {
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
|
||||
.nav-left, .nav-right {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.info-btn {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 30rpx;
|
||||
height: 88rpx;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||
border-bottom: 1rpx solid #e9ecef;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
text {
|
||||
font-size: 32rpx;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.active {
|
||||
text {
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 120rpx;
|
||||
height: 60rpx;
|
||||
// background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
||||
// border-radius: 30rpx;
|
||||
z-index: 1;
|
||||
// box-shadow: 0 4rpx 16rpx rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 80rpx;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(90deg, #ff6b35 0%, #ff8c42 100%);
|
||||
border-radius: 2rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.active):hover {
|
||||
text {
|
||||
color: #ff6b35;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-divider {
|
||||
width: 2rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(180deg, #dee2e6 0%, #adb5bd 100%);
|
||||
margin: 0 20rpx;
|
||||
border-radius: 1rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.course-list {
|
||||
flex: 1;
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.course-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
.checkbox {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #ffffff;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.selected {
|
||||
background-color: #ff6b35;
|
||||
border-color: #ff6b35;
|
||||
|
||||
.checkmark {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.course-image {
|
||||
width: 120rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 20rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.course-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
|
||||
.course-desc {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.course-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
margin-top: 5rpx;
|
||||
|
||||
.course-type {
|
||||
font-size: 24rpx;
|
||||
color: #6c757d;
|
||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.course-price {
|
||||
font-size: 28rpx;
|
||||
color: #ff0000;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 30rpx;
|
||||
gap: 30rpx;
|
||||
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15rpx;
|
||||
|
||||
.checkbox {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #ffffff;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.selected {
|
||||
background-color: #ff6b35;
|
||||
border-color: #ff6b35;
|
||||
|
||||
.checkmark {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-all-text {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5rpx;
|
||||
|
||||
.invoice-amount {
|
||||
font-size: 32rpx;
|
||||
color: #ff0000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selected-count {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-btn {
|
||||
background-color: #ff0000;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
|
||||
text {
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 开票历史样式
|
||||
.history-content {
|
||||
flex: 1;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||
border: 1rpx solid #f0f0f0;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 6rpx;
|
||||
background: linear-gradient(180deg, #ff6b35 0%, #ff8c42 100%);
|
||||
border-radius: 0 3rpx 3rpx 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.history-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
flex: 1;
|
||||
|
||||
.history-date {
|
||||
font-size: 30rpx;
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5rpx;
|
||||
}
|
||||
|
||||
.history-count {
|
||||
font-size: 26rpx;
|
||||
color: #7f8c8d;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.history-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.history-status {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
min-width: 80rpx;
|
||||
text-align: center;
|
||||
|
||||
&.success {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.3);
|
||||
}
|
||||
|
||||
&.processing {
|
||||
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.history-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #bdc3c7;
|
||||
font-weight: 300;
|
||||
margin: 0 8rpx;
|
||||
}
|
||||
|
||||
.history-amount {
|
||||
font-size: 28rpx;
|
||||
color: #e74c3c;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,666 @@
|
||||
<template>
|
||||
<view class="invoice-detail-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">开票详情</view>
|
||||
<view class="nav-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<view class="main-content" :style="{ paddingTop: navBarHeight + 'px' }">
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="isLoading" class="loading-container">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<view v-else-if="hasError" class="error-container">
|
||||
<text class="error-icon">!</text>
|
||||
<text class="error-text">加载失败,请重试</text>
|
||||
<view class="retry-btn" @click="retryLoad">
|
||||
<text class="retry-text">重新加载</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 正常内容 -->
|
||||
<template v-else>
|
||||
<!-- 发票状态指示器 -->
|
||||
<view class="status-indicator" :class="getStatusClass(invoiceDetail.status)">
|
||||
<view class="status-dot" :class="getStatusClass(invoiceDetail.status)"></view>
|
||||
<text class="status-text">{{ getStatusText(invoiceDetail.status) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 发票详情卡片 -->
|
||||
<view class="detail-card">
|
||||
<!-- 发票抬头 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">发票抬头</text>
|
||||
<text class="item-value">{{ invoiceDetail.title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 单位税号 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">单位税号</text>
|
||||
<text class="item-value">{{ invoiceDetail.taxId }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 发票内容 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">发票内容</text>
|
||||
<text class="item-value">{{ invoiceDetail.content }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 发票金额 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">发票金额</text>
|
||||
<text class="item-value amount">{{ invoiceDetail.amount }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 电子邮箱 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">电子邮箱</text>
|
||||
<text class="item-value">{{ invoiceDetail.email }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 创建日期 -->
|
||||
<view class="detail-item">
|
||||
<text class="item-label">创建日期</text>
|
||||
<text class="item-value">{{ invoiceDetail.createDate }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="detail-item" v-if="invoiceDetail.note">
|
||||
<text class="item-label">备注</text>
|
||||
<text class="item-value">{{ invoiceDetail.note }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 相关课程 -->
|
||||
<view class="related-courses">
|
||||
<view class="section-title">
|
||||
<view class="title-icon"></view>
|
||||
<text class="title-text">相关课程</text>
|
||||
</view>
|
||||
|
||||
<view v-if="relatedCourses.length > 0" class="course-list">
|
||||
<view
|
||||
class="course-item"
|
||||
v-for="(course, index) in relatedCourses"
|
||||
:key="index"
|
||||
>
|
||||
<view class="course-bullet"></view>
|
||||
<image class="course-image" :src="course.image" mode="aspectFill"></image>
|
||||
<view class="course-info">
|
||||
<text class="course-desc">{{ course.description }}</text>
|
||||
<text class="course-price">实际支付: {{ course.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="empty-courses">
|
||||
<text class="empty-text">暂无相关课程信息</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 底部下载按钮 -->
|
||||
<view class="download-section" v-if="!isLoading && !hasError">
|
||||
<view class="download-btn" @click="downloadInvoice">
|
||||
<text class="download-text">下载发票</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import course_api from "@/api/course_api.js"
|
||||
|
||||
const invoice_id = ref('')
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
const navBarHeight = ref(88)
|
||||
const invoiceDetail = ref({
|
||||
title: '',
|
||||
taxId: '',
|
||||
content: '',
|
||||
amount: '',
|
||||
email: '',
|
||||
status: 1,
|
||||
downUrl: '',
|
||||
createDate: '',
|
||||
note: ''
|
||||
})
|
||||
|
||||
const relatedCourses = ref([])
|
||||
const isLoading = ref(true)
|
||||
const hasError = ref(false)
|
||||
|
||||
// 基础方法
|
||||
const getSystemInfo = () => {
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = systemInfo.statusBarHeight
|
||||
// #ifdef MP-WEIXIN
|
||||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||||
// #endif
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const downloadInvoice = () => {
|
||||
// 检查是否有下载链接
|
||||
if (invoiceDetail.value.downUrl) {
|
||||
uni.showToast({
|
||||
title: '正在打开下载链接...',
|
||||
icon: 'loading'
|
||||
})
|
||||
|
||||
// 使用uni.downloadFile下载文件
|
||||
uni.downloadFile({
|
||||
url: invoiceDetail.value.downUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 保存文件到本地
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: (saveRes) => {
|
||||
console.log('文件保存成功:', saveRes.savedFilePath)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('文件保存失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('下载失败:', err)
|
||||
uni.showToast({
|
||||
title: '下载失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '暂无下载链接',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const retryLoad = () => {
|
||||
isLoading.value = true
|
||||
hasError.value = false
|
||||
// 重新加载数据
|
||||
setTimeout(() => {
|
||||
loadInvoiceData()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return 'success'
|
||||
case 0:
|
||||
return 'processing'
|
||||
case 2:
|
||||
return 'failed'
|
||||
default:
|
||||
return 'success'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '发票已开具'
|
||||
case 0:
|
||||
return '开票中'
|
||||
case 2:
|
||||
return '信息有误'
|
||||
default:
|
||||
return '发票已开具'
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return ''
|
||||
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
} catch (error) {
|
||||
console.error('日期格式化失败:', error)
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
const loadInvoiceData = () => {
|
||||
course_api.getExcellencourseOrderInvoice(invoice_id.value).then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 200 && res.data) {
|
||||
const data = res.data
|
||||
|
||||
// 更新发票详情数据
|
||||
invoiceDetail.value = {
|
||||
title: data.head || '欣欣相照科技股份有限公司',
|
||||
taxId: data.tax_number || '22913u9u4923u293',
|
||||
content: data.type || '培训费',
|
||||
amount: `¥${data.amount?.toFixed(2) || '0.00'}`,
|
||||
email: data.email || '153746774@qq.com',
|
||||
status: data.status || 1,
|
||||
downUrl: data.down_url || '',
|
||||
createDate: formatDate(data.create_date) || '',
|
||||
note: data.note || ''
|
||||
}
|
||||
|
||||
// 更新相关课程数据
|
||||
if (data.excellencourseList && data.excellencourseList.length > 0) {
|
||||
relatedCourses.value = data.excellencourseList.map(course => ({
|
||||
image: course.excellencourse_img || 'https://via.placeholder.com/120x80/ff6b35/ffffff?text=课程',
|
||||
description: course.excellencourse_title || '找到已购买课程即可学习',
|
||||
price: `¥${course.account?.toFixed(2) || '0.00'}`
|
||||
}))
|
||||
} else {
|
||||
// 如果没有课程数据,使用默认数据
|
||||
relatedCourses.value = [
|
||||
{
|
||||
image: 'https://via.placeholder.com/120x80/ff6b35/ffffff?text=课程',
|
||||
description: '找到已购买课程即可学习',
|
||||
price: '¥39'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
} else {
|
||||
console.error('获取发票详情失败:', res.msg || '未知错误')
|
||||
hasError.value = true
|
||||
isLoading.value = false
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取发票详情失败:', err)
|
||||
hasError.value = true
|
||||
isLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage.options || {}
|
||||
|
||||
invoice_id.value = options.invoice_id
|
||||
|
||||
if (!invoice_id.value) {
|
||||
hasError.value = true
|
||||
isLoading.value = false
|
||||
uni.showToast({
|
||||
title: '缺少发票ID参数',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 加载发票数据
|
||||
loadInvoiceData()
|
||||
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: '开票详情'
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-detail-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
border-bottom: 1rpx solid #e5e5e5;
|
||||
|
||||
.nav-content {
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
|
||||
.nav-left, .nav-right {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.loading-spinner {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border: 6rpx solid #f3f3f3;
|
||||
border-top: 6rpx solid #ff6b35;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.error-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: #ff6b35;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 60rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
|
||||
.retry-text {
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&.success {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
box-shadow: 0 4rpx 16rpx rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
|
||||
&.processing {
|
||||
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
|
||||
box-shadow: 0 4rpx 16rpx rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 77, 79, 0.3);
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 15rpx;
|
||||
|
||||
&.success {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4rpx rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
&.processing {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4rpx rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4rpx rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
max-width: 400rpx;
|
||||
text-align: right;
|
||||
|
||||
&.amount {
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.related-courses {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.title-icon {
|
||||
width: 8rpx;
|
||||
height: 32rpx;
|
||||
background: #ff6b35;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.course-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.course-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
|
||||
.course-bullet {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: #ff6b35;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.course-image {
|
||||
width: 120rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 20rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.course-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
|
||||
.course-desc {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.course-price {
|
||||
font-size: 24rpx;
|
||||
color: #ff6b35;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-courses {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.download-section {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 30rpx;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid #e9ecef;
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background: linear-gradient(135deg, #20c997 0%, #17a2b8 100%);
|
||||
border-radius: 12rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.download-text {
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<view class="invoice-info-page">
|
||||
<!-- 状态栏 -->
|
||||
<view class="status-bar">
|
||||
<text class="time">9:41</text>
|
||||
<view class="status-icons">
|
||||
<view class="signal"></view>
|
||||
<view class="wifi"></view>
|
||||
<view class="battery"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<text class="back-icon">‹</text>
|
||||
</view>
|
||||
<text class="title">填写发票信息</text>
|
||||
<view class="info-btn" @click="showInfo">
|
||||
<text class="info-icon">!</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<view class="main-content">
|
||||
<!-- 表单卡片 -->
|
||||
<view class="form-card">
|
||||
<!-- 发票抬头 -->
|
||||
<view class="form-field">
|
||||
<text class="field-label">发票抬头:</text>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="请输入抬头名称(必填)"
|
||||
placeholder-class="placeholder"
|
||||
v-model="formData.title"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<view class="field-divider"></view>
|
||||
|
||||
<!-- 单位税号 -->
|
||||
<view class="form-field">
|
||||
<text class="field-label">单位税号:</text>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="请输入单位税号(必填)"
|
||||
placeholder-class="placeholder"
|
||||
v-model="formData.taxId"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<view class="field-divider"></view>
|
||||
|
||||
<!-- 发票内容 -->
|
||||
<view class="form-field">
|
||||
<text class="field-label">发票内容:</text>
|
||||
<text class="field-value">{{ formData.content }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<view class="field-divider"></view>
|
||||
|
||||
<!-- 发票金额 -->
|
||||
<view class="form-field">
|
||||
<text class="field-label">发票金额:</text>
|
||||
<text class="field-value amount">{{ formData.amount }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<view class="field-divider"></view>
|
||||
|
||||
<!-- 电子邮箱 -->
|
||||
<view class="form-field">
|
||||
<text class="field-label">电子邮箱:</text>
|
||||
<input
|
||||
class="field-input"
|
||||
placeholder="请输入您的邮箱(必填)"
|
||||
placeholder-class="placeholder"
|
||||
v-model="formData.email"
|
||||
type="email"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<view class="submit-btn" @click="submitForm">
|
||||
<text class="submit-text">提交</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'InvoiceInfo',
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
title: '',
|
||||
taxId: '',
|
||||
content: '培训费',
|
||||
amount: '¥59.00',
|
||||
email: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
showInfo() {
|
||||
uni.showToast({
|
||||
title: '发票信息说明',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
submitForm() {
|
||||
// 验证必填字段
|
||||
if (!this.formData.title.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入发票抬头',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.formData.taxId.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入单位税号',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.formData.email.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入电子邮箱',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证邮箱格式
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(this.formData.email)) {
|
||||
uni.showToast({
|
||||
title: '请输入正确的邮箱格式',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
uni.showModal({
|
||||
title: '确认提交',
|
||||
content: '确认提交发票信息吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({
|
||||
title: '提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-info-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 30rpx 0;
|
||||
height: 88rpx;
|
||||
|
||||
.time {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.status-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
|
||||
.signal, .wifi, .battery {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background-color: #000000;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
height: 88rpx;
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.back-icon {
|
||||
font-size: 48rpx;
|
||||
color: #ff0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.info-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #ff0000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.info-icon {
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 30rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
min-height: 80rpx;
|
||||
|
||||
.field-label {
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
min-width: 160rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
|
||||
&.amount {
|
||||
color: #ff0000;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.field-divider {
|
||||
height: 1rpx;
|
||||
background-color: #f0f0f0;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
padding: 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background-color: #20b2aa;
|
||||
border-radius: 8rpx;
|
||||
padding: 25rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.submit-text {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 750rpx) {
|
||||
.form-field {
|
||||
padding: 25rpx;
|
||||
|
||||
.field-label {
|
||||
font-size: 28rpx;
|
||||
min-width: 140rpx;
|
||||
}
|
||||
|
||||
.field-input, .field-value {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 20rpx;
|
||||
|
||||
.submit-text {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#FF4757"></uni-icons>
|
||||
<uni-icons type="left" size="24" color="#FF4757"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">我的课程</view>
|
||||
<view class="nav-right" @click="goSearch">
|
||||
@@ -28,26 +28,38 @@
|
||||
|
||||
<!-- 课程列表 -->
|
||||
<view class="course-list">
|
||||
<view class="course-item" v-for="(course, index) in currentCourseList" :key="course.id" @click="goCourseDetail(course)">
|
||||
<view class="course-left">
|
||||
<image :src="course.image" mode="aspectFill" class="course-image"></image>
|
||||
</view>
|
||||
<view class="course-right">
|
||||
<text class="course-title">{{ course.title }}</text>
|
||||
<view class="course-meta">
|
||||
<text class="meta-text">{{ course.lessonCount }}节</text>
|
||||
<text class="meta-separator">•</text>
|
||||
<text class="meta-text">{{ course.status }}</text>
|
||||
<text class="meta-separator">•</text>
|
||||
<text class="meta-text">已学{{ course.learnedCount }}节</text>
|
||||
<!-- 有课程数据时显示课程列表 -->
|
||||
<view v-if="currentCourseList.length > 0">
|
||||
<view class="course-item" v-for="(course, index) in currentCourseList" :key="course.id" @click="goCourseDetail(course)">
|
||||
<view class="course-left">
|
||||
<image :src="course.image" mode="aspectFill" class="course-image"></image>
|
||||
</view>
|
||||
<view class="course-tags" v-if="course.tags && course.tags.length > 0">
|
||||
<view class="tag-item" v-for="tag in course.tags" :key="tag.id">
|
||||
<text class="tag-text">{{ tag.text }}</text>
|
||||
<view class="course-right">
|
||||
<text class="course-title">{{ course.title }}</text>
|
||||
<view class="course-meta">
|
||||
<text class="meta-text">{{ course.lessonCount }}节</text>
|
||||
<text class="meta-separator">•</text>
|
||||
<text class="meta-text">{{ course.status }}</text>
|
||||
<text class="meta-separator">•</text>
|
||||
<text class="meta-text">已学{{ course.learnedCount }}节</text>
|
||||
</view>
|
||||
<view class="course-tags" v-if="course.tags && course.tags.length > 0">
|
||||
<view class="tag-item" v-for="tag in course.tags" :key="tag.id">
|
||||
<text class="tag-text">{{ tag.text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态显示 -->
|
||||
<view v-else class="empty-state">
|
||||
<view class="empty-icon">
|
||||
<uni-icons type="book" size="80" color="#E0E0E0"></uni-icons>
|
||||
</view>
|
||||
<text class="empty-title">{{ getEmptyTitle() }}</text>
|
||||
<text class="empty-desc">{{ getEmptyDesc() }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单记录浮动按钮 -->
|
||||
@@ -77,6 +89,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import course_api from "@/api/course_api.js"
|
||||
|
||||
// 响应式数据
|
||||
const statusBarHeight = ref(0)
|
||||
@@ -170,12 +183,14 @@ const getSystemInfo = () => {
|
||||
const switchTab = (tab) => {
|
||||
activeTab.value = tab
|
||||
console.log('切换到标签:', tab)
|
||||
// 切换标签页后重新获取对应的课程数据
|
||||
getMyCourses()
|
||||
}
|
||||
|
||||
const goCourseDetail = (course) => {
|
||||
console.log('进入课程详情:', course.title)
|
||||
uni.navigateTo({
|
||||
url: '/pages_course/course_detail/course_detail'
|
||||
url: '/pages_course/course_detail/course_detail?id=' + course.id
|
||||
})
|
||||
}
|
||||
|
||||
@@ -208,9 +223,73 @@ const goMyCourses = () => {
|
||||
console.log('当前已在我的课程页面')
|
||||
}
|
||||
|
||||
// 获取空状态标题
|
||||
const getEmptyTitle = () => {
|
||||
if (activeTab.value === 'learning') {
|
||||
return '暂无学习中的课程'
|
||||
} else if (activeTab.value === 'completed') {
|
||||
return '暂无已完成的课程'
|
||||
}
|
||||
return '暂无课程'
|
||||
}
|
||||
|
||||
// 获取空状态描述
|
||||
const getEmptyDesc = () => {
|
||||
if (activeTab.value === 'learning') {
|
||||
return '快去购买课程开始学习吧'
|
||||
} else if (activeTab.value === 'completed') {
|
||||
return '继续学习,完成更多课程'
|
||||
}
|
||||
return '快去探索更多课程吧'
|
||||
}
|
||||
|
||||
const getMyCourses = () => {
|
||||
// 根据activeTab确定state参数
|
||||
let state = 1 // 默认学习中
|
||||
if (activeTab.value === 'completed') {
|
||||
state = 2 // 已学完
|
||||
}
|
||||
|
||||
course_api.listMyExcellencourse(state, 1).then(res => {
|
||||
console.log('API返回数据:', res)
|
||||
if (res.code === 200 && res.data && res.data.list) {
|
||||
// 将API数据转换为课程列表格式
|
||||
const apiCourseList = res.data.list.map(item => ({
|
||||
id: item.excellencourse_id,
|
||||
title: item.excellencourse_title,
|
||||
lessonCount: item.excellencourse_video_num,
|
||||
status: item.excellencourse_upload_num === item.excellencourse_video_num ? '已完结' : '更新中',
|
||||
learnedCount: item.study_num,
|
||||
image: item.excellencourse_index_img || '/static/icon_home_my_patient.png',
|
||||
tags: item.special_type_name ? [{ id: Date.now(), text: item.special_type_name }] : []
|
||||
}))
|
||||
|
||||
// 根据当前标签页更新对应的课程列表
|
||||
if (activeTab.value === 'learning') {
|
||||
courseList.value.learning = apiCourseList
|
||||
} else if (activeTab.value === 'completed') {
|
||||
courseList.value.completed = apiCourseList
|
||||
}
|
||||
} else {
|
||||
console.error('获取课程列表失败:', res.msg)
|
||||
uni.showToast({
|
||||
title: res.msg || '获取课程列表失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('API调用失败:', err)
|
||||
uni.showToast({
|
||||
title: '获取课程列表失败',
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
getMyCourses()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -369,6 +448,31 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态样式
|
||||
.empty-state {
|
||||
padding: 120rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.empty-icon {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #ccc;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
// 订单记录浮动按钮
|
||||
.floating-order {
|
||||
position: fixed;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content">
|
||||
<view class="nav-left" @click="goBack">
|
||||
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">订单记录</view>
|
||||
<view class="nav-right"></view>
|
||||
@@ -90,6 +90,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮发票按钮 -->
|
||||
<view class="floating-invoice-btn" @click="goToInvoice">
|
||||
<view class="btn-content">
|
||||
<view class="icon-wrapper">
|
||||
<uni-icons type="receipt" size="20" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<text class="btn-text">开具发票1</text>
|
||||
</view>
|
||||
<view class="btn-shadow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -102,65 +113,7 @@ const statusBarHeight = ref(0)
|
||||
const navBarHeight = ref(88)
|
||||
const activeTab = ref('paid') // 默认激活"已支付"标签
|
||||
// 订单数据
|
||||
const orderList = ref({
|
||||
paid: [
|
||||
{
|
||||
id: 1,
|
||||
orderNumber: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
courseName: '找到已购买课程即可学习找到学习找到到到已到已购买',
|
||||
orderTime: '2020.30.21 12:23:00',
|
||||
courseAmount: '62.00',
|
||||
balancePayment: '50.00',
|
||||
actualPayment: '12.00',
|
||||
status: 'paid'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
orderNumber: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
courseName: '找到已购买课程即可学习找到学习找到到到已到已购买',
|
||||
orderTime: '2020.30.21 12:23:00',
|
||||
courseAmount: '62.00',
|
||||
balancePayment: '50.00',
|
||||
actualPayment: '12.00',
|
||||
status: 'paid'
|
||||
}
|
||||
],
|
||||
unpaid: [
|
||||
{
|
||||
id: 3,
|
||||
orderNumber: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
courseName: '找到已购买课程即可学习找到学习找到到到已到已购买',
|
||||
orderTime: '2020.30.21 12:23:00',
|
||||
courseAmount: '62.00',
|
||||
balancePayment: '50.00',
|
||||
actualPayment: '12.00',
|
||||
remainingTime: '12:23:00',
|
||||
status: 'pending'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
orderNumber: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
courseName: '找到已购买课程即可学习找到学习找到到到已到已购买',
|
||||
orderTime: '2020.30.21 12:23:00',
|
||||
courseAmount: '62.00',
|
||||
balancePayment: '50.00',
|
||||
actualPayment: '12.00',
|
||||
remainingTime: '12:23:00',
|
||||
status: 'timeout'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
orderNumber: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
courseName: '找到已购买课程即可学习找到学习找到到到已到已购买',
|
||||
orderTime: '2020.30.21 12:23:00',
|
||||
courseAmount: '62.00',
|
||||
pointsPayment: '200',
|
||||
balancePayment: '42.00',
|
||||
actualPayment: '0.00',
|
||||
status: 'failed'
|
||||
}
|
||||
]
|
||||
})
|
||||
const orderList = ref([])
|
||||
|
||||
// 计算属性
|
||||
const currentOrderList = computed(() => {
|
||||
@@ -292,6 +245,13 @@ const enterLearning = (order) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到开具发票页面
|
||||
const goToInvoice = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages_course/invoice/invoice'
|
||||
})
|
||||
}
|
||||
|
||||
const viewOrder = (order) => {
|
||||
console.log('查看订单详情:', order.id)
|
||||
uni.showToast({
|
||||
@@ -323,7 +283,7 @@ const getOrderList = () => {
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
orderNumber: item.order_id || item.uuid || '-',
|
||||
orderNumber: item.uuid || '-',
|
||||
courseName: item.excellencourse_title || '-',
|
||||
orderTime: formatTs(item.create_date),
|
||||
courseAmount: toYuan(item.account),
|
||||
@@ -577,4 +537,75 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 悬浮发票按钮
|
||||
.floating-invoice-btn {
|
||||
position: fixed;
|
||||
right: 40rpx;
|
||||
bottom: 140rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
z-index: 999;
|
||||
cursor: pointer;
|
||||
|
||||
.btn-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 3rpx solid rgba(255, 255, 255, 0.2);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.92);
|
||||
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 8rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
color: #ffffff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.1);
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-shadow {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
left: 8rpx;
|
||||
right: -8rpx;
|
||||
bottom: -8rpx;
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
|
||||
border-radius: 50%;
|
||||
z-index: -1;
|
||||
filter: blur(8rpx);
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover .btn-shadow {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user