2025-12-03 08:40:39 +08:00

892 lines
22 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">
<!-- 头部导航栏 -->
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eeeeee"
>
<template v-slot:right>
<view class="nav-right" @click="goGot" >
<up-image :src="hotImg" width="40rpx" height="40rpx"></up-image>
</view>
</template>
</uni-nav-bar>
</view>
<!-- 固定搜索栏 -->
<view class="search-container-fixed filter-bar">
<view class="search-box" @click="goToSearch">
<uni-icons type="search" size="16" color="#999"></uni-icons>
<text class="search-text">搜索</text>
</view>
<view class="divider"></view>
<view class="current-sort" @click="toggleInnerSort">
<text class="sort-text">{{ innerSortTitle }}</text>
<view class="imgbox">
<up-image :src="upImg" width="26rpx" height="26rpx"></up-image>
</view>
</view>
</view>
<!-- 使用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"
>
<!-- 指南信息 -->
<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)">
<uni-icons type="arrow-down" size="26" color="#8D2316"></uni-icons>
<!-- <up-icon name="download" color="#8D2316" size="56rpx" ></up-icon> -->
</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' && currentPage > 1" 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>
<!-- 组内排序弹窗 -->
<view v-if="showInnerSort" class="popup-panel">
<view
class="popup-item"
:class="{ active: sort == 1 }"
@click.stop="chooseInnerSort(1)"
>
<text class="item-text">下载量</text>
<uni-icons
v-if="sort == 1"
type="checkmarkempty"
color="#8B2316"
size="22"
></uni-icons>
</view>
<view class="popup-divider"></view>
<view
class="popup-item"
:class="{ active: sort == 2 }"
@click.stop="chooseInnerSort(2)"
>
<text class="item-text">上传时间</text>
<uni-icons
v-if="sort == 2"
type="checkmarkempty"
color="#8B2316"
size="22"
></uni-icons>
</view>
<view class="popup-divider"></view>
<view
class="popup-item"
:class="{ active: sort == 3 }"
@click.stop="chooseInnerSort(3)"
>
<text class="item-text">标题</text>
<uni-icons
v-if="sort == 3"
type="checkmarkempty"
color="#8B2316"
size="22"
></uni-icons>
</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";
let downList = uni.getStorageSync("downLoadPdf") || [];
const downLoadList = ref(downList);
// 响应式数据
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 showInnerSort = ref(false);
const innerSortTitle = ref("上传时间");
import downloadStore from "@/store/downloadStoreFile.js";
const downloadTasks=ref([]);
const downLoadtaskList=computed(()=>{
return downloadTasks.value.filter((item)=>item.status == "completed").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) => {
console.log(options);
if (options.typeUuid) {
typeUuid.value = options.typeUuid;
}
if (options.title) {
title.value = options.title;
}
if (options.keywords) {
keywords.value = decodeURIComponent(options.keywords);
title.value = decodeURIComponent(options.title);
}
});
const goGot = () => {
navTo({
url: "/pages_app/hot/hot?from=hot&typeUuid=" + typeUuid.value,
});
};
const toggleInnerSort = () => {
showInnerSort.value = !showInnerSort.value;
};
onShow(() => {
console.log('onShow');
syncTasksFromStore();
// 监听store变化
downloadStore.addListener((tasks) => {
downloadTasks.value = tasks;
});
// 恢复正在下载的任务
resumeDownloadingTasks();
// 页面显示时,如果列表为空则加载数据
if (guidelinesList.value.length === 0) {
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}`,
});
// const goSearch=()=>{
// uni.sendNativeEvent('goHomeSearch', {
// msg: 'goHomeSearch'
// },ret => {
// console.log(ret);
// })
// }
};
// scroll-view 下拉刷新
const onRefresh = async () => {
console.log("scroll-view 下拉刷新触发");
isRefreshing.value = true;
await refreshData();
isRefreshing.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("条件满足开始加载更多数据");
loadMoreData();
} 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";
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,
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: "下载失败1",
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;
}
const pdfUrl = docUrl + cell.path;
console.log('下载列表');
console.log(downloadTasks.value);
downloadTasks.value.forEach(item => {
if(item.id == cell.uuid) {
uni.openDocument({
filePath: item.localPath,
fileType: 'pdf',
success: () => console.log('打开成功'),
fail: (err) => {
uni.showModal({
title: '提示',
content: JSON.stringify(err),
showCancel: false,
confirmText: '确定',
success: (res) => {
if(res.confirm){
}
}
});
}
});
// let url = "https://view.xdocin.com/view?src=" + encodeURIComponent(pdfUrl);
// uni.downloadFile({
// url: pdfUrl,
// success: (res) => {
// if (res.statusCode === 200) {
// uni.openDocument({
// filePath: res.tempFilePath, // 临时文件路径
// fileType: 'pdf',
// success: () => console.log('打开成功'),
// fail: (err) => console.error('打开失败', err)
// });
// }
// },
// });
}
});
}
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 添加下载任务
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"){
return false;
};
uni.showToast({
title: "正在下载中",
icon: "none",
});
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",
});
removeTask(index);
uni.showToast({
title: "下载失败2",
icon: "none",
});
}
},
fail: (err) => {
downloadStore.updateTask(index, {
status: "failed",
});
removeTask(index);
uni.showToast({
title: "下载失败3: " + (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 removeTask = (index) => {
downloadStore.removeTask(index);
syncTasksFromStore();
};
// 恢复正在下载的任务
const resumeDownloadingTasks = () => {
// 使用 nextTick 确保在页面渲染后再恢复下载任务
nextTick(() => {
downloadStore.resumeDownloadingTasks((index) => {
// 重新开始下载uni.downloadFile不支持断点续传所以从0开始
console.log("恢复下载任务:", downloadStore.getTask(index)?.url);
startDownload(index);
});
});
};
// 测试加载更多功能
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;
}
};
</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:285rpx;
left: 0;
right: 0;
background: #fff;
z-index: 100;
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: calc(var(--status-bar-height) + 44px + 106rpx);
left: 0;
right: 0;
bottom: 0;
z-index: 99;
height: calc(100vh - var(--status-bar-height) - 44px - 106rpx);
background-color: $bg-color;
}
// 固定搜索容器
.search-container-fixed {
position: fixed;
top: calc(var(--status-bar-height) + 44px); // 导航栏高度
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>