zoujiandong 82e0a5c10d 9.1
2025-09-01 16:25:46 +08:00

667 lines
14 KiB
Vue

<template>
<!-- 头部导航栏 -->
<uni-nav-bar
left-icon="left"
title="肝胆课件"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<view class="courseware-container">
<!-- 排序和筛选栏 -->
<view class="filter-bar">
<view class="filter-item" @click="toggleSort">
<text class="filter-text">{{sort==2?'最新':'最热'}}</text>
<view class="newbox">
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
</view>
</view>
<view class="divider"></view>
<view class="filter-item" @click="showFilterPopup">
<text class="filter-text">筛选</text>
<view class="filterbox">
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
</view>
</view>
</view>
<!-- 课件列表 -->
<scroll-view class="courseware-list" scroll-y="true"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
:show-scrollbar="false">
<view
class="courseware-item"
v-for="(item, index) in coursewareList"
:key="index"
@click="viewCourseware(item)"
>
<view class="item-left">
<view class="pdf-icon">📄</view>
</view>
<view class="item-content">
<view class="item-title">{{ item.title }}</view>
<view class="item-author">{{ item.author }}</view>
<view class="item-stats">
<view class="views">
<uni-icons type="eye" size="30rpx" color="#999"></uni-icons>
<text class="view-count">{{ item.views }}人阅读</text>
</view>
<view class="price">
<up-image :src="downLoadImg" width="32rpx" height="32rpx" ></up-image>
<text class="price-value">{{ item.price }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 筛选弹窗 -->
<view class="filter-popup" v-if="showFilter" @click="hideFilterPopup">
<view class="filter-content" @click.stop>
<!-- 筛选标签网格 -->
<view class="filter-tags">
<view
class="tag-item"
v-for="(tag, index) in filterTags"
:key="index"
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.name }}
</view>
</view>
<!-- 底部按钮 -->
<view class="filter-buttons">
<view class="btn-reset" @click="resetFilter">重置</view>
<view class="btn-confirm" @click="confirmFilter">确定</view>
</view>
</view>
</view>
<!-- 投稿按钮 -->
<view class="submit-btn" @click="submitCourseware">
<up-image :src="tougaoImg" width="152rpx" height="80rpx" ></up-image>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import filter from "@/static/cb_screen_no.png"
import filterOn from "@/static/cb_screen_yes.png"
import upImg from "@/static/cb_up.png"
import downImg from "@/static/cb_up.png"
import tougaoImg from "@/static/kejiantougao.png"
import downLoadImg from "@/static/wdxz.png"
import api from '@/api/api.js';
const isFilterActive=ref(false)
const sort = ref(2);
// 响应式数据
const refreshing = ref(false);
const loading = ref(false);
const noMore = ref(false);
const page = ref(1);
const pageSize = ref(10);
// 响应式数据
const coursewareList = ref([]);
// 方法
const goBack = () => {
uni.navigateBack({
fail(){
uni.redirectTo({
url: '/pages/index/index'
});
}
});
};
const showSearch = () => {
uni.showToast({
title: '搜索功能',
icon: 'none'
});
};
const toggleSort = () => {
sort.value=sort.value==1?2:1
};
const viewCourseware = (item) => {
uni.showToast({
title: `查看课件: ${item.title}`,
icon: 'none'
});
};
const submitCourseware = () => {
uni.showToast({
title: '课件投稿功能',
icon: 'none'
});
};
// 下拉刷新
const onRefresh = async () => {
refreshing.value = true;
try {
// 重置数据
page.value = 1;
noMore.value = false;
// 重新加载第一页数据
await loadData(true);
uni.showToast({
title: '刷新成功',
icon: 'success'
});
} catch (error) {
uni.showToast({
title: '刷新失败',
icon: 'error'
});
} finally {
refreshing.value = false;
}
};
// 上拉加载更多
const onLoadMore = async () => {
console.log('加载更多')
if (loading.value || noMore.value) return;
loading.value = true;
try {
// 加载下一页数据
await loadData(false);
} catch (error) {
uni.showToast({
title: '加载失败',
icon: 'error'
});
} finally {
loading.value = false;
}
};
// 加载数据
const loadData = async (isRefresh = false) => {
if (isRefresh) {
page.value = 1;
coursewareList.value = [];
noMore.value = false;
}
if (loading.value || noMore.value) return;
loading.value = true;
try {
console.log(`开始加载第${page.value}页课件数据`);
// 调用API获取课件列表
const res = await api.ganDanFileByKeyWords({
page: page.value,
pageSize: pageSize.value,
sort: sort.value,
// 可以根据需要添加其他筛选参数
keywords: '',
title: '',
// fileType: selectedFileType.value,
// sortBy: sortType.value
});
console.log('课件列表API响应:', res);
if (res && res.code === 200) {
let newItems = [];
let totalCount = 0;
// 解析API返回的数据结构
if (res.data && res.data.list) {
newItems = res.data.list;
totalCount = res.data.total || res.data.totalRow || 0;
console.log('使用 res.data.list 结构');
} else if (res.data && Array.isArray(res.data)) {
newItems = res.data;
totalCount = res.total || 0;
console.log('使用 res.data 数组结构');
} else {
console.error('无法识别的数据结构:', res.data);
noMore.value = true;
return;
}
console.log('解析后的数据:', { newItems, totalCount, newItemsLength: newItems.length });
if (Array.isArray(newItems)) {
if (isRefresh) {
coursewareList.value = newItems;
console.log('刷新模式:替换列表数据');
} else {
coursewareList.value.push(...newItems);
console.log('加载更多:追加数据到列表');
}
// 判断是否还有更多数据
if (newItems.length < pageSize.value || coursewareList.value.length >= totalCount) {
noMore.value = true;
console.log('没有更多数据了');
} else {
page.value++;
console.log(`还有更多数据,下一页: ${page.value}`);
}
} else {
console.error('API返回的数据不是数组格式:', newItems);
noMore.value = true;
}
} else {
console.error('加载课件列表失败:', res.message);
uni.showToast({
title: res.message || '加载失败',
icon: 'error'
});
}
} catch (error) {
console.error('加载课件列表异常:', error);
uni.showToast({
title: '网络异常,请重试',
icon: 'error'
});
} finally {
loading.value = false;
}
};
const showFilter = ref(false)
// 筛选弹窗相关数据
const filterTags = ref([
{ name: '指南解读', selected: false },
{ name: '病例分析', selected: false },
{ name: '学术讲座', selected: false },
{ name: '手术视频', selected: false },
{ name: '研究进展', selected: false },
{ name: '肝病治疗', selected: false },
{ name: '肝癌诊断', selected: false },
{ name: '肝移植', selected: false },
{ name: '微创手术', selected: false },
{ name: '免疫治疗', selected: false },
{ name: '靶向治疗', selected: false },
{ name: '化疗方案', selected: false }
]);
// 筛选弹窗相关方法
const showFilterPopup = () => {
showFilter.value = true;
};
const hideFilterPopup = () => {
showFilter.value = false;
};
const toggleTag = (index) => {
filterTags.value[index].selected = !filterTags.value[index].selected;
isFilterActive.value = filterTags.value.some(tag => tag.selected);
};
const resetFilter = () => {
filterTags.value.forEach(tag => tag.selected = false);
isFilterActive.value = false;
};
const confirmFilter = () => {
const selectedTags = filterTags.value.filter(tag => tag.selected);
console.log('选中的筛选标签:', selectedTags);
hideFilterPopup();
// 这里可以根据选中的标签进行数据筛选
};
onShow(() => {
// 页面显示时加载数据
console.log('页面显示,开始加载课件数据');
loadData(true);
});
</script>
<style lang="scss" scoped>
// 变量定义
$primary-color: #e74c3c;
$text-primary: #333333;
$text-secondary: #666666;
$text-light: #999999;
$border-color: #e5e5e5;
$background-color: #f8f9fa;
$white: #ffffff;
$submit-btn-color: #20b2aa;
$theme-color: #8B2316;
$white: #fff;
// 混合器
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
@mixin shadow {
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.courseware-container {
height: calc(100vh - 140rpx);
background-color: $background-color;
}
.status-bar {
height: 44rpx;
background-color: $white;
@include flex-between;
padding: 0 30rpx;
font-size: 24rpx;
color: $text-primary;
.status-left {
.time {
font-weight: 500;
}
}
.status-right {
display: flex;
align-items: center;
gap: 20rpx;
.network-info,
.signal,
.wifi,
.battery {
font-size: 22rpx;
}
}
}
.header {
height: 88rpx;
background-color: $white;
@include flex-between;
padding: 0 30rpx;
border-bottom: 1rpx solid $border-color;
position: sticky;
top: 0;
z-index: 100;
}
.filter-bar {
height: 100rpx;
background-color: $white;
@include flex-center;
gap: 100rpx;
border-bottom: 1rpx solid $border-color;
position: sticky;
top: 140rpx;
z-index: 99;
.divider{
width: 2rpx;
height: 30rpx;
background-color: #999;
}
.filter-item{
display:flex;
.filterbox{
margin-top: 5rpx;
}
}
}
.courseware-list {
position: fixed;
top:240rpx;
flex: 1;
bottom:0;
width:100%;
padding-top: 20rpx;
margin: 0 30rpx;
box-sizing: border-box;
width:auto;
.courseware-item {
background-color: $white;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
@include shadow;
display: flex;
gap: 20rpx;
.item-left {
.pdf-icon {
font-size: 60rpx;
color: $primary-color;
}
}
.item-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 16rpx;
.item-title {
font-size: 32rpx;
color: $text-primary;
font-weight: 500;
line-height: 1.4;
}
.item-author {
font-size: 28rpx;
color: $text-secondary;
line-height: 1.3;
}
.item-stats {
display: flex;
align-items: center;
.views {
display: flex;
align-items: center;
gap: 8rpx;
.eye-icon {
font-size: 24rpx;
color: $text-light;
}
.view-count {
font-size: 26rpx;
color: $text-light;
}
}
.download {
.download-icon {
font-size: 32rpx;
color: $text-light;
}
}
.price {
display: flex;
margin-left: 30rpx;
align-items: baseline;
gap: 4rpx;
.price-symbol {
font-size: 24rpx;
color: $primary-color;
font-weight: 500;
}
.price-value {
font-size: 32rpx;
color: $primary-color;
font-weight: bold;
}
}
}
}
}
}
.submit-btn {
position: fixed;
right: 0rpx;
bottom: 100rpx;
width: 152rpx;
height: 80rpx;
z-index: 100;
.submit-icon {
font-size: 32rpx;
}
.submit-text {
font-size: 28rpx;
color: $white;
font-weight: 500;
}
}
// 筛选弹窗样式
.filter-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
// background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
z-index: 9999;
.filter-content {
margin-top:245rpx;
background-color: $white;
// border-radius: 20rpx 20rpx 0 0;
padding: 20rpx 30rpx 60rpx;
width: 100%;
max-height: 80vh;
.filter-tags {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-bottom: 60rpx;
max-height: 65vh;
overflow-y: auto;
.tag-item {
background-color: #f8f8f8;
color: #666;
padding: 8rpx 24rpx;
border-radius: 30rpx;
font-size: 26rpx;
border: 2rpx solid #efefef;
transition: all 0.3s ease;
&.active {
background-color: #fff;
color: $theme-color;
border-color: $theme-color;
}
}
}
.filter-buttons {
display: flex;
gap: 20rpx;
position: fixed;
bottom: 30rpx;
left: 30rpx;
right: 30rpx;
.btn-reset,
.btn-confirm {
flex: 1;
height: 70rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
}
.btn-reset {
background-color: $white;
color: $theme-color;
border: 2rpx solid $theme-color;
}
.btn-confirm {
border: 2rpx solid $theme-color;
background-color: $theme-color;
color: $white;
}
}
}
}
// 加载状态样式
.loading-state,
.empty-state,
.load-more-state,
.no-more-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 20rpx;
color: $text-secondary;
font-size: 28rpx;
text {
margin-top: 20rpx;
}
}
.loading-state {
color: $primary-color;
}
.empty-state {
color: $text-secondary;
}
.load-more-state {
padding: 40rpx 20rpx;
color: $text-secondary;
}
.no-more-state {
padding: 40rpx 20rpx;
color: #999;
}
</style>