2025-11-28 10:43:13 +08:00

804 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="zhinan-list-page">
<!-- 头部导航栏 -->
<!-- <uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
>
</uni-nav-bar> -->
<navBar :title="title" />
<!-- 使用scroll-view实现列表 -->
<scroll-view
class="guidelines-scroll-view"
scroll-y="true"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onScrollToLower"
:show-scrollbar="false"
>
<!-- 指南列表 -->
<view class="guidelines-list">
<view
class="guideline-item"
v-for="(item, index) in guidelinesList"
:key="item.uuid || index"
@click="viewGuideline(item)"
>
<!-- 指南信息 -->
<view class="item-content">
<view class="item-title">{{ item.title }}</view>
<view class="item-bottom">
<view class="item-date">{{ formatDate(item.releaseTime) }}</view>
<!-- 操作按钮 -->
<view class="item-action">
<view class="download-btn" @click.stop="downLoad(item)" v-if="!downLoadtaskList.includes(item.uuid)">
<!-- <up-icon name="download" color="#8D2316" size="28" ></up-icon> -->
<uni-icons type="arrow-down" size="26" color="#8D2316"></uni-icons>
</view>
<view
v-else
class="view-btn"
@click.stop="viewGuideline(item)"
>查看</view>
</view>
</view>
</view>
</view>
</view>
<!-- 加载更多状态 -->
<view class="load-more-status">
<view v-if="loadMoreStatus === 'loading'" class="loading">
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
<text>加载中...</text>
</view>
<view v-else-if="loadMoreStatus === 'more'" class="more">
<text>上拉加载更多</text>
</view>
<view v-else-if="loadMoreStatus === 'noMore' && guidelinesList.length > 0" class="no-more">
<text>没有更多数据了</text>
</view>
</view>
<!-- 无数据提示 -->
<view class="no-data" v-if="guidelinesList.length === 0 && !isLoading">
<uni-icons type="info" size="60" color="#999"></uni-icons>
<text>暂无诊疗指南数据</text>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted,computed,nextTick} from 'vue'
import api from '@/api/api.js'
import { onShow,onLoad } from "@dcloudio/uni-app";
import docUrl from "@/utils/docUrl.js"
import navTo from "@/utils/navTo.js"
import hotImg from "@/static/hot_booklist.png"
import navBar from "@/components/navBar/navBar.vue"
// 响应式数据
const guidelinesList = ref([])
const isLoading = ref(false)
const hasMoreData = ref(true)
const currentPage = ref(1)
const pageSize = ref(10)
const isRefreshing = ref(false)
const keywords=ref('');
import upImg from "@/static/triangle_green_theme.png"
import downImg from "@/static/triangle_normal.png"
const loadMoreStatus = ref('more') // 'loading', 'more', 'noMore'
const typeUuid=ref('');
const title=ref('');
const sort=ref(2);
const from=ref('');
const name=ref('');
const showInnerSort=ref(false);
const innerSortTitle=ref('上传时间');
import downloadStore from "@/store/downloadStoreFile.js";
const downloadTasks=ref([]);
const downLoadtaskList=computed(()=>{
console.log('downLoadtaskList');
console.log(downloadTasks.value.map((item)=>item.id));
return downloadTasks.value.map((item)=>item.id);
});
const chooseInnerSort=(index)=>{
sort.value=index;
if(index==1){
innerSortTitle.value='下载量';
}else if(index==2){
innerSortTitle.value='上传时间';
}else if(index==3){
innerSortTitle.value='标题';
}
showInnerSort.value=false;
loadGuidelinesList(true);
}
onLoad((options)=>{
if(options.keywords){
keywords.value=decodeURIComponent(options.keywords);
}
console.log(options)
if(options.from){
from.value=options.from;
}
if(options.name){
name.value=options.name;
}
if(options.typeUuid){
typeUuid.value=options.typeUuid;
}
title.value=options.from?'年度Top10':'诊疗指南';
})
const goGot=()=>{
navTo({
url:'/pages_app/hot/hot?from=hot'
})
}
const toggleInnerSort=()=>{
showInnerSort.value=!showInnerSort.value;
}
onShow(() => {
syncTasksFromStore();
// 监听store变化
downloadStore.addListener((tasks) => {
downloadTasks.value = tasks;
});
// 恢复正在下载的任务
resumeDownloadingTasks();
// 页面显示时,如果列表为空则加载数据
if (guidelinesList.value.length === 0) {
if(from.value){
top10ByType();
}else{
loadGuidelinesList(true)
}
}
})
// 跳转到搜索页
const goToSearch = () => {
const q = keywords.value ? encodeURIComponent(keywords.value) : ''
const t = title.value ? encodeURIComponent(title.value) : ''
const type = typeUuid.value ? `&typeUuid=${encodeURIComponent(typeUuid.value)}` : ''
uni.navigateTo({
url: `/pages_app/search/search?keywords=${q}&title=${t}${type}`
})
}
// scroll-view 下拉刷新
const onRefresh = async () => {
console.log('scroll-view 下拉刷新触发');
isRefreshing.value = true;
await refreshData();
isRefreshing.value = false;
};
const top10ByType=async ()=>{
const res=await api.top10ByType({
type:1,
guide_type_uuid:typeUuid.value
});
if(res.code==200){
guidelinesList.value=res.data;
loadMoreStatus.value = 'noMore'
hasMoreData.value = false
}
}
// scroll-view 滚动到底部
const onScrollToLower = () => {
console.log('=== onScrollToLower 触发 ===');
console.log('当前状态:', {
loadMoreStatus: loadMoreStatus.value,
isRefreshing: isRefreshing.value,
currentPage: currentPage.value,
listLength: guidelinesList.value.length,
hasMoreData: hasMoreData.value,
isLoading: isLoading.value
});
if (loadMoreStatus.value === 'more' && !isRefreshing.value && !isLoading.value) {
console.log('条件满足,开始加载更多数据');
if(from.value){
top10ByType();
}else{
loadGuidelinesList()
}
} else {
console.log('条件不满足,跳过加载:', {
loadMoreStatus: loadMoreStatus.value,
isRefreshing: isRefreshing.value,
isLoading: isLoading.value
});
}
};
// 刷新数据
const refreshData = async () => {
try {
currentPage.value = 1
hasMoreData.value = true
loadMoreStatus.value = 'more'
if(from.value){
await top10ByType();
}else{
await loadGuidelinesList(true)
}
uni.showToast({
title: '刷新成功',
icon: 'none'
})
} catch (error) {
console.error('刷新失败:', error)
uni.showToast({
title: '刷新失败',
icon: 'none'
})
}
}
// 加载指南列表
const loadGuidelinesList = async (isRefresh = false) => {
console.log('=== loadGuidelinesList 开始 ===');
console.log('当前参数:', { isRefresh, page: currentPage.value, keywords: keywords.value });
if (isLoading.value) return
try {
isLoading.value = true
loadMoreStatus.value = 'loading'
const params = {
page: currentPage.value,
pageSize: pageSize.value,
type: 1, // 诊疗指南类型
sort:2,
name:name.value,
typeUuid:typeUuid.value,
keywords:keywords.value
}
const res = await api.searchLibraryU(params)
console.log('指南列表响应:', res)
if (res.code === 200 && res.data) {
const newData = res.data.list || res.data
if (isRefresh) {
guidelinesList.value = newData
console.log('刷新模式:替换列表数据');
} else {
guidelinesList.value = [...guidelinesList.value, ...newData]
console.log('加载更多:追加数据到列表');
}
// 判断是否还有更多数据
if (newData.length < pageSize.value) {
loadMoreStatus.value = 'noMore'
hasMoreData.value = false
console.log('没有更多数据了');
} else {
loadMoreStatus.value = 'more'
hasMoreData.value = true
// 只有在加载更多模式下才递增页码
if (!isRefresh) {
currentPage.value++
console.log(`还有更多数据,下一页: ${currentPage.value}`);
}
}
} else {
console.error('加载指南列表失败:', res.message)
loadMoreStatus.value = 'noMore'
uni.showToast({
title: res.message || '加载失败',
icon: 'none'
})
}
} catch (error) {
console.error('加载指南列表异常:', error)
loadMoreStatus.value = 'noMore'
uni.showToast({
title: '网络异常,请重试',
icon: 'none'
})
} finally {
isLoading.value = false
console.log('=== loadGuidelinesList 结束 ===');
console.log('最终状态:', {
loadMoreStatus: loadMoreStatus.value,
page: currentPage.value,
listLength: guidelinesList.value.length
});
}
}
// 加载更多数据
const loadMoreData = () => {
console.log('=== loadMoreData 被调用 ===');
console.log('检查条件:', {
hasMoreData: hasMoreData.value,
isLoading: isLoading.value,
currentPage: currentPage.value
});
if (hasMoreData.value && !isLoading.value) {
console.log('条件满足,开始加载更多数据');
loadGuidelinesList(false);
} else {
console.log('条件不满足,跳过加载更多');
}
}
// 下载指南
const downloadGuideline = (item) => {
console.log('下载指南:', item)
uni.showToast({
title: '开始下载...',
icon: 'none'
})
// 这里可以调用下载API
// 示例下载PDF文件
if (item.file_path) {
uni.downloadFile({
url: item.file_path,
success: (res) => {
if (res.statusCode === 200) {
uni.showToast({
title: '下载成功',
icon: 'none'
})
}
},
fail: (err) => {
console.error('下载失败:', err)
uni.showToast({
title: '下载失败',
icon: 'none'
})
}
})
}
}
const downLoad = (item) => {
if(!item.path){
uni.showToast({
title: "暂无下载链接",
icon: "none",
});
return;
}
addDownloadTask({
url: docUrl + item.path,
title: item.title,
id: item.uuid,
});
};
// 查看指南支持常用文档直链pdf/doc/docx/xls/xlsx/ppt/pptx/txt
const viewGuideline = (cell) => {
console.log(cell);
if (!cell.path) {
uni.showToast({
title: "暂无下载链接",
icon: "none",
});
return;
}
downloadTasks.value.forEach(item => {
if(item.id == cell.uuid) {
uni.openDocument({
filePath: item.localPath,
success: () => console.log('打开成功'),
fail: (err) => console.error('打开失败', err)
});
}
});
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
// 测试加载更多功能
const testLoadMore = () => {
console.log('=== 手动测试加载更多 ===');
console.log('当前状态:', {
loadMoreStatus: loadMoreStatus.value,
hasMoreData: hasMoreData.value,
isLoading: isLoading.value,
currentPage: currentPage.value,
listLength: guidelinesList.value.length
});
if (loadMoreStatus.value === 'more' && !isLoading.value) {
loadMoreData();
} else {
uni.showToast({
title: `状态: ${loadMoreStatus.value}, 加载中: ${isLoading.value}`,
icon: 'none',
duration: 2000
});
}
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
try {
const date = new Date(dateString)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch (error) {
return dateString
}
}
// 添加下载任务
const addDownloadTask = (item) => {
// 使用store添加任务
const taskIndex = downloadStore.addTask(item);
// 同步任务列表
syncTasksFromStore();
// 开始下载
startDownload(taskIndex);
};
// 开始下载
const startDownload = (index) => {
const taskItem = downloadStore.getTask(index);
if (!taskItem) return;
if(taskItem.status == "loading"){
uni.showToast({
title: "正在下载",
icon: "none",
});
return false;
};
downloadStore.updateTask(index, {
status: "loading"
});
const task = uni.downloadFile({
url: taskItem.url,
success: (res1) => {
//console.log("res1:"+JSON.stringify(res1));
if (res1.statusCode === 200) {
downloadStore.updateTask(index, {
status: "completed",
filePath: res1.tempFilePath,
});
uni.showToast({
title: "下载成功",
icon: "none",
});
uni.saveFile({
tempFilePath: res1.tempFilePath,
success: function (res2) {
console.log("res2:"+JSON.stringify(res2));
uni.getSavedFileInfo({
filePath: res2.savedFilePath,
success: function (res) {
console.log("res:"+JSON.stringify(res));
console.log("size:"+res.size);
downloadStore.updateTask(index, {
status: "completed",
localPath: res2.savedFilePath,
});
},
});
},
});
} else {
downloadStore.updateTask(index, {
status: "failed",
});
uni.showToast({
title: "下载失败",
icon: "none",
});
}
},
fail: (err) => {
downloadStore.updateTask(index, {
status: "failed",
});
uni.showToast({
title: "下载失败: " + (err.errMsg || "未知错误"),
icon: "none",
duration: 2000,
});
},
});
// 监听下载进度
task.onProgressUpdate((update) => {
const currentTask = downloadStore.getTask(index);
if (currentTask && currentTask.status === "downloading") {
downloadStore.updateTask(index, {
progress: update.progress
});
}
});
// 保存task对象到store用于暂停/继续)
downloadStore.updateTask(index, {
task: task,
});
};
const syncTasksFromStore = () => {
downloadTasks.value = downloadStore.getTasks();
};
// 恢复正在下载的任务
const resumeDownloadingTasks = () => {
// 使用 nextTick 确保在页面渲染后再恢复下载任务
nextTick(() => {
downloadStore.resumeDownloadingTasks((index) => {
// 重新开始下载uni.downloadFile不支持断点续传所以从0开始
console.log("恢复下载任务:", downloadStore.getTask(index)?.url);
startDownload(index);
});
});
};
</script>
<style lang="scss" scoped>
// 颜色变量
$primary-color: #ff4757;
$text-primary: #333;
$text-secondary: #666;
$text-light: #999;
$border-color: #e0e0e0;
$bg-color: #f6f6f6;
$white: #ffffff;
.imgbox{
width: 32rpx;
margin-top: -10rpx;
}
.popup-panel {
position: fixed;
top: 242rpx; /* 紧贴筛选栏 */
left: 0;
right: 0;
background: #fff;
z-index: 10;
box-shadow: 0 6rpx 20rpx rgba(0,0,0,0.08);
}
.popup-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 30rpx;
font-size: 30rpx;
color: #333;
}
.popup-divider {
height: 2rpx;
background: #eaeaea;
}
.popup-item.active .item-text {
color: #8B2316;
}
.current-sort {
display: flex;
align-items: center;
gap: 10rpx;
.sort-text {
font-size: 28rpx;
color: #333;
}
.sort-icon.up {
width: 0;
height: 0;
border-left: 8rpx solid transparent;
border-right: 8rpx solid transparent;
border-bottom: 12rpx solid #999;
&.active {
border-bottom-color: #ff0000;
}
}
}
.zhinan-list-page {
background-color: $bg-color;
height: 100vh;
}
.guidelines-scroll-view {
position: fixed;
top: 180rpx;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
height: calc(100vh - 180rpx);
background-color: $bg-color;
}
// 固定搜索容器
.search-container-fixed {
position: fixed;
top: 180rpx; // 导航栏高度
left: 0;
right: 0;
z-index: 90;
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx 30rpx;
background-color: $white;
gap: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
// 筛选栏
.filter-bar {
background-color: $white;
display: flex;
align-items: center;
border-bottom: 2px solid #eee;
.search-box {
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
.search-text {
font-size: 14px;
color:#999;
}
}
.divider {
width: 1px;
height: 16px;
background-color: #999;
margin: 0 60rpx;
}
.filter-item {
flex:1;
display: flex;
align-items: center;
justify-content: center;
gap: 3px;
font-size: 14px;
color: #999;
}
}
// 指南列表
.guidelines-list {
padding: 20rpx 30rpx;
.guideline-item {
background-color: $white;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
.item-content {
flex: 1;
margin-right: 30rpx;
.item-title {
font-size: 32rpx;
color: $text-primary;
line-height: 1.5;
margin-bottom: 16rpx;
font-weight: 500;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.item-bottom {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
.item-date {
font-size: 26rpx;
color: $text-secondary;
}
.item-action {
display: flex;
align-items: center;
.download-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: rgba(255, 71, 87, 0.1);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background-color: rgba(255, 71, 87, 0.2);
}
}
.view-btn {
color:#8B2316;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background-color: darken($primary-color, 10%);
}
}
}
}
}
}
}
// 加载更多状态
.load-more-status {
padding: 20rpx 0;
text-align: center;
.loading, .more, .no-more {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
padding: 20rpx 0;
font-size: 26rpx;
color: $text-light;
}
}
// 无数据提示
.no-data {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
text {
margin-top: 30rpx;
font-size: 28rpx;
color: $text-light;
}
}
</style>