9.5上午更新

This commit is contained in:
zoujiandong 2025-09-05 10:19:10 +08:00
parent 9a26193ea4
commit 397cf0c88e
13 changed files with 3146 additions and 222 deletions

View File

@ -297,14 +297,6 @@ const api = {
return request('/expertAPI/modifyMobile', data, 'post', false);
},
pointTicketlist(data){
return request('/expertAPI/pointTicketlist', data, 'post', false);
},
pointTicketExchange(data){
return request('/expertAPI/updateStatus', data, 'post', false);
},
smsSend(data){
return request('/expertAPI/smsSend', data, 'post', false);
},
@ -381,7 +373,27 @@ const api = {
cancelRes(data){
return request('/expertAPI/cancelRes', data, 'post', false);
},
polularScienceArticleListByKeywordsNew(data){
return request('/expertAPI/polularScienceArticleListByKeywordsNew', data, 'post', false);
},
patientVideoByKeyWordsNew(data){
return request('/expertAPI/patientVideoByKeyWordsNew', data, 'post', false);
},
patientVideoNew(data){
return request('/expertAPI/patientVideoNew', data, 'post', false);
},
top10ByType(data){
return request('/expertAPI/top10ByType', data, 'post', false);
},
videoBySearchNew(data){
return request('/expertAPI/videoBySearchNew', data, 'post', false);
},
patientVideoBySearchNew(data){
return request('/expertAPI/patientVideoBySearchNew', data, 'post', false);
},
polularScienceArticleListIndexNew(data){
return request('/expertAPI/polularScienceArticleListIndexNew', data, 'post', false);
},
}
export default api

View File

@ -198,10 +198,32 @@
}
}
},
{
"path": "searchNews/searchNews",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "searchVideo/searchVideo",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "search/search",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -285,6 +307,28 @@
}
}
},
{
"path": "hot/hot",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "patientVideo/patientVideo",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "selectPatient/selectPatient",
"style": {
@ -658,6 +702,7 @@
"path": "zhinanList/zhinanList",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -792,16 +837,6 @@
{
"root": "pages_goods",
"pages": [
{
"path": "coupon/coupon",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "积分券",
"app": {
"bounce": "none"
}
}
},
{
"path": "pointMall/pointMall",
"style": {
@ -893,7 +928,7 @@
"list": [
{
"name": "", //
"path": "pages/index/index", //
"path": "pages_app/videoDetail/videoDetail", //
"query": "" //onLoad
}
]

View File

@ -4,7 +4,7 @@
<!-- 课程列表 -->
<view class="course-list">
<!-- 肝胆精品课 -->
<view class="course-item" @click="goToCourse('hepatoBiliary')">
<view class="course-item" @click="goToCourse('course')">
<up-image :src="course" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">肝胆精品课</view>
@ -16,7 +16,7 @@
</view>
<!-- 肝胆视频 -->
<view class="course-item" @click="goToCourse('hepatoBiliaryVideo')">
<view class="course-item" @click="goToCourse('video')">
<up-image :src="videoImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">肝胆视频</view>
@ -28,7 +28,7 @@
</view>
<!-- 肝胆课件 -->
<view class="course-item" @click="goToCourse('hepatoBiliaryCourseware')">
<view class="course-item" @click="goToCourse('ppt')">
<up-image :src="pptImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">肝胆课件</view>
@ -40,7 +40,7 @@
</view>
<!-- 病例讨论 -->
<view class="course-item" @click="goToCourse('caseDiscussion')">
<!-- <view class="course-item" @click="goToCourse('caseDiscussion')">
<up-image :src="talkImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">病例讨论</view>
@ -49,10 +49,10 @@
<view class="course-arrow">
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
</view>
</view>
</view> -->
<!-- 病例荟萃 -->
<view class="course-item" @click="goToCourse('caseCollection')">
<!-- <view class="course-item" @click="goToCourse('caseCollection')">
<up-image :src="huicuiImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">病例荟萃</view>
@ -61,10 +61,10 @@
<view class="course-arrow">
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
</view>
</view>
</view> -->
<!-- 现代肝病学院 -->
<view class="course-item" @click="goToCourse('modernHepatology')">
<!-- <view class="course-item" @click="goToCourse('modernHepatology')">
<up-image :src="xueyuanImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">现代肝病学院</view>
@ -73,10 +73,10 @@
<view class="course-arrow">
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
</view>
</view>
</view> -->
<!-- 新手教程 -->
<view class="course-item" @click="goToCourse('tutorial')">
<!-- <view class="course-item" @click="goToCourse('tutorial')">
<up-image :src="newerImg" width="94rpx" height="94rpx" ></up-image>
<view class="course-content">
<view class="course-title">新手教程</view>
@ -85,7 +85,7 @@
<view class="course-arrow">
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
</view>
</view>
</view> -->
</view>
</view>
<CustomTabbar></CustomTabbar>
@ -102,6 +102,7 @@
import huicuiImg from "@/static/jingdianbingli.png"
import xueyuanImg from "@/static/ganbingxueyuan.png"
import newerImg from "@/static/xinshoujiaocheng.png"
import navTo from '@/utils/navTo.js';
//
//
const goToCourse = (courseType) => {
@ -109,47 +110,20 @@
//
switch(courseType) {
case 'hepatoBiliary':
uni.showToast({
title: '肝胆精品课功能开发中',
icon: 'none'
});
case 'course':
navTo({
url: '/pages_course/index/index'
})
break;
case 'hepatoBiliaryVideo':
uni.showToast({
title: '肝胆视频功能开发中',
icon: 'none'
});
case 'video':
navTo({
url: '/pages_app/video/video'
})
break;
case 'hepatoBiliaryCourseware':
uni.showToast({
title: '肝胆课件功能开发中',
icon: 'none'
});
break;
case 'caseDiscussion':
uni.showToast({
title: '病例讨论功能开发中',
icon: 'none'
});
break;
case 'caseCollection':
uni.showToast({
title: '病例荟萃功能开发中',
icon: 'none'
});
break;
case 'modernHepatology':
uni.showToast({
title: '现代肝病学院功能开发中',
icon: 'none'
});
break;
case 'tutorial':
uni.showToast({
title: '新手教程功能开发中',
icon: 'none'
});
case 'ppt':
navTo({
url: '/pages_app/ppt/ppt'
})
break;
default:
uni.showToast({

View File

@ -253,9 +253,13 @@
import sign from "@/static/home_qiandao_icon.png"
import signImg from "@/static/sign_in_bng_big.png"
import dayjs from 'dayjs'
import docUrl from "@/utils/docUrl.js"
const expertDetail=reactive({})
const showSign=ref(false)
const signInfo=reactive({
news:{
summary:''
}
})
// refs
const tabbarRef = ref(null);
const visible=ref(false)
@ -850,7 +854,6 @@
url: '/pages_course/course/course'
});
};
const signInfo=reactive({})
//
const onSignClick = async () => {
//

View File

@ -20,17 +20,18 @@
<!-- 筛选栏 -->
<view class="filter-bar">
<view class="search-box">
<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="filter-item">
<text>最新</text>
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
<view class="filter-item active" @click="toggleSort">
<text>{{type==1?'最新':'最热'}}</text>
<up-image v-if="type==1" :src="upImg" width="20rpx" height="26rpx" ></up-image>
<up-image v-else :src="downImg" width="20rpx" height="26rpx" ></up-image>
</view>
<view class="divider"></view>
<view class="filter-item" @click="showFilterPopup">
<view class="filter-item" :class="{ active: isFilterActive }" @click="showFilterPopup">
<text>筛选</text>
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
</view>
@ -50,7 +51,7 @@
:class="{ active: tag.selected }"
@click="toggleTag(index)"
>
{{ tag.name }}
{{ tag.DES }}
</view>
</view>
@ -63,8 +64,16 @@
</view>
<!-- 文章列表 -->
<scroll-view class="article-list" scroll-y>
<view class="article-item" v-for="(article, index) in articleList" :key="index" @click="goToDetail(article)">
<scroll-view
class="article-list"
scroll-y
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
:lower-threshold="100"
>
<view class="article-item" v-for="(article, index) in articleList" :key="article.uuid || index" @click="goToDetail(article)">
<image class="article-image" :src="article.image" mode="aspectFill"></image>
<view class="article-content">
<view class="article-title">{{ article.title }}</view>
@ -77,13 +86,30 @@
<text>{{ article.views }}</text>
</view>
<view class="stat-item">
<uni-icons type="heart" size="12" color="#999"></uni-icons>
<uni-icons type="hand-up" size="13" color="#999"></uni-icons>
<text>{{ article.likes }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 加载更多状态 -->
<view class="load-more" v-if="loading">
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
<text class="load-text">加载中...</text>
</view>
<!-- 没有更多数据 -->
<view class="no-more" v-if="!hasMore && articleList.length > 0">
<text class="no-more-text">没有更多数据了</text>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="articleList.length === 0 && !loading">
<uni-icons type="article" size="80" color="#cccccc"></uni-icons>
<text class="empty-text">暂无文章</text>
</view>
</scroll-view>
<!-- 底部导航栏 -->
@ -100,128 +126,177 @@
import downImg from "@/static/cb_up.png"
import filter from "@/static/cb_screen_no.png"
import filterOn from "@/static/cb_screen_yes.png"
import api from "@/api/api.js"
import docUrl from '@/utils/docUrl.js';
import navTo from '@/utils/navTo.js';
const type=ref(1)
//
const currentPage = ref(1);
const pageSize = ref(10);
const hasMore = ref(true);
const loading = ref(false);
const refreshing = ref(false);
//
const activeTab = ref(0);
const showFilter = ref(false);
const isFilterActive = 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 },
{ 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 },
{ 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 },
{ 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 articleList = ref([
{
id: 1,
image: '/static/liver_knowledge.png',
title: '甩掉"乙肝大国"的帽子后,中国第一大肝病变成了它,正在侵...',
date: '今日',
isToday: true,
views: 185,
likes: 0
},
{
id: 2,
image: '/static/doctor_liver.png',
title: '从你我做起,让肝炎止步',
date: '08-01',
isToday: false,
views: 315,
likes: 0
},
{
id: 3,
image: '/static/liver_health.png',
title: '这6个指标正常说明你肝脏健康',
date: '07-25',
isToday: false,
views: 1033,
likes: 0
},
{
id: 4,
image: '/static/diet_liver.png',
title: '无需节食吃饭一个改变脂肪肝好转了4个月肝脏脂肪减...',
date: '07-18',
isToday: false,
views: 1829,
likes: 1
},
{
id: 5,
image: '/static/cooking_spices.png',
title: '这种家家都有的调料,吃太多会增加肝癌风险?很多人已超标!',
date: '07-11',
isToday: false,
views: 2992,
likes: 0
},
{
id: 6,
image: '/static/liver_protection.png',
title: '肝炎拖成肝硬化下一步就是肝癌养肝护肝记住这3点',
date: '07-04',
isToday: false,
views: 1993,
likes: 1
const toggleSort = () => {
type.value = type.value === 1 ? 2 : 1;
//
currentPage.value = 1;
hasMore.value = true;
polularScienceArticleListByKeywordsNew(true);
};
const loadGuideTags = async () => {
try {
const res = await api.guideTag({
type:4
});
if(res && res.code === 200 && res.data) {
// API
filterTags.value = res.data.map(tag => ({
...tag,
selected: false
}));
console.log('指南标签加载成功:', filterTags.value);
}
} catch (e) {
console.error('加载指南标签失败:', e);
}
};
const polularScienceArticleListByKeywordsNew = async (isRefresh = false) => {
if (loading.value) return;
loading.value = true;
try {
const page = isRefresh ? 1 : currentPage.value;
const res = await api.polularScienceArticleListByKeywordsNew({
keywords: keywords.value,
page: page,
type: type.value
});
if (res && res.code === 200 && res.data) {
const newData = res.data.list || [];
if (isRefresh) {
//
articleList.value = newData.map(item => formatArticleData(item));
currentPage.value = 1;
} else {
//
const formattedData = newData.map(item => formatArticleData(item));
articleList.value = [...articleList.value, ...formattedData];
}
//
hasMore.value = newData.length >= pageSize.value;
if (!isRefresh) {
currentPage.value++;
}
console.log('文章数据加载成功:', articleList.value);
}
} catch (error) {
console.error('获取文章列表失败:', error);
uni.showToast({
title: '获取数据失败',
icon: 'error',
duration: 2000
});
} finally {
loading.value = false;
refreshing.value = false;
}
]);
};
//
const formatArticleData = (item) => {
// item
if (!item) return null;
const submitDate = item.submitDate ? new Date(item.submitDate) : new Date();
const today = new Date();
const isToday = submitDate.toDateString() === today.toDateString();
return {
uuid: item.uuid || '',
title: item.topic || '无标题',
summary: item.summary || '',
image: item.imgPath ? docUrl + item.imgPath : '/static/liver_knowledge.png',
date: isToday ? '今日' : formatDate(submitDate),
isToday: isToday,
views: item.readnum || 0,
likes: item.agreenum || 0,
tags: item.tags || '',
path: item.path || '',
submitDate: item.submitDate || ''
};
};
//
const formatDate = (date) => {
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
};
//
const filterTags = ref([]);
//
const articleList = ref([]);
//
const switchTab = (index) => {
activeTab.value = index;
if(index==1){
navTo({
url: '/pages_app/patientVideo/patientVideo'
})
}else if(index==2){
let url=encodeURIComponent('https://wx.igandan.com/wxPatient/index.htm#/problem?link=share&fromtype=doctor')
navTo({
url: '/pages_app/webview/webview?url='+url
})
}
};
const goToDetail = (article) => {
console.log('查看文章详情:', article);
//
if (article.path) {
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encodeURIComponent(docUrl+article.path)}&title=${encodeURIComponent(article.title)}`
});
}
};
//
const goToSearch = () => {
uni.navigateTo({
url: '/pages_app/search/search'
});
};
//
const onRefresh = async () => {
refreshing.value = true;
await polularScienceArticleListByKeywordsNew(true);
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
};
//
const onLoadMore = async () => {
if (!hasMore.value || loading.value) return;
await polularScienceArticleListByKeywordsNew(false);
};
const goToPage = (page) => {
@ -253,7 +328,7 @@
});
isFilterActive.value = false;
};
const keywords=ref('');
const confirmFilter = () => {
//
const hasSelected = filterTags.value.some(tag => tag.selected);
@ -261,16 +336,24 @@
//
if (hasSelected) {
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.name);
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
console.log('选中的标签:', selectedTags);
//
keywords.value = selectedTags.join(',');
} else {
keywords.value = '';
}
//
currentPage.value = 1;
hasMore.value = true;
showFilter.value = false;
polularScienceArticleListByKeywordsNew(true);
};
onShow(() => {
console.log('患教学堂页面显示');
polularScienceArticleListByKeywordsNew(true);
loadGuideTags();
});
</script>
@ -372,8 +455,13 @@ $padding-small: 10px;
gap: 3px;
font-size: 14px;
color: #999;
transition: all 0.3s ease;
}
}
.filter-item.active{
color: $theme-color;
}
//
.article-list {
@ -449,6 +537,46 @@ $padding-small: 10px;
}
}
}
//
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
color: #999;
.load-text {
margin-left: 10rpx;
font-size: 28rpx;
}
}
.no-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
.no-more-text {
font-size: 28rpx;
color: #999;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 30rpx;
.empty-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
}
}
//

707
pages_app/hot/hot.vue Normal file
View File

@ -0,0 +1,707 @@
<template>
<view class="zhinan-list-page">
<!-- 头部导航栏 -->
<uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
>
</uni-nav-bar>
<!-- 使用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="viewGuideline(item)"
>
<up-icon name="download" color="#8D2316" size="28"></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'" 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} 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"
//
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('上传时间');
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(() => {
//
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: 'success'
})
} 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: 'success'
})
}
},
fail: (err) => {
console.error('下载失败:', err)
uni.showToast({
title: '下载失败',
icon: 'none'
})
}
})
}
}
// pdf/doc/docx/xls/xlsx/ppt/pptx/txt
const viewGuideline = (item) => {
const url = docUrl+item.path
if (url) {
const isPDF = /\.(pdf)(\?|$)/i.test(url)
const isOffice = /\.(docx?|xlsx?|pptx?)(\?|$)/i.test(url)
const isText = /\.(txt)(\?|$)/i.test(url)
// H5
// #ifdef H5
if (isPDF) {
try { window.open(url, '_blank') } catch (e) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
return
}
if (isOffice) {
const officeUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`
try { window.open(officeUrl, '_blank') } catch (e) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(officeUrl)}` })
}
return
}
if (isText) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
return
}
// webview
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
return
// #endif
// H5 openDocument退 webview
// #ifndef H5
uni.showToast({ title: '正在打开...', icon: 'none' })
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200 && res.tempFilePath) {
uni.openDocument({
filePath: res.tempFilePath,
showMenu: true,
success: () => {},
fail: () => {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
})
} else {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
},
fail: () => {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
})
return
// #endif
}
// 退
}
//
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
}
}
</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: 140rpx;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
height: calc(100vh - 140rpx);
background-color: $bg-color;
}
//
.search-container-fixed {
position: fixed;
top: 140rpx; //
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>

View File

@ -0,0 +1,855 @@
<template>
<uni-nav-bar left-icon="left"
@clickLeft="goBack" title="患教视频" fixed color="#8B2316" height="140rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
<view class="video-page">
<!-- Header -->
<!-- Fixed Filter Tabs -->
<view class="filter-tabs-container">
<view class="filter-tabs">
<view
class="tab-item"
@click="showAllVideoPopup=!showAllVideoPopup"
>
<up-image :src="isAllActive?allOnImg:allImg" width="30rpx" height="30rpx" ></up-image>
<text class="tab-text" :class="{active:isAllActive}">{{typeName}}</text>
<up-image :src="isAllActive?selectOnImg:selectImg" width="30rpx" height="30rpx" ></up-image>
</view>
<view class="right">
<view
class="tab-item"
@click="toggleSort"
>
<text class="tab-text active">{{sort==2?'最新':'最热'}}</text>
<view class="newbox">
<up-image :src="sort==2?upImg:downImg" width="20rpx" height="26rpx" ></up-image>
</view>
</view>
<view
class="tab-item"
@click="showFilterPopup"
>
<text class="tab-text " :class="{active:isFilterActive}">筛选</text>
<view class="filterbox">
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
</view>
</view>
</view>
</view>
</view>
<!-- Main Content -->
<scroll-view
class="scroll-view"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="100"
>
<!-- Video List -->
<view class="video-list">
<view
class="video-item"
v-for="(video, index) in videoList"
:key="video.id || video.uuid"
@click="playVideo(video)"
>
<view class="video-thumbnail">
<image :src="docUrl + video.imgpath" mode="aspectFill"></image>
</view>
<view class="video-info">
<view class="video-title">{{video.title || video.name}}</view>
<view class="video-meta">
<text class="author">{{video.public_name}}</text>
<view class="stats">
<uni-icons type="eye" size="14" color="#999"></uni-icons>
<text class="view-count">{{video.readnum}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- Loading More -->
<uni-load-more
v-if="videoList.length > 0"
:status="loadMoreStatus"
:content-text="{
contentdown: '上拉加载更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}"
></uni-load-more>
<!-- Empty State -->
<view class="empty-state" v-if="videoList.length === 0 && !loading">
<empty></empty>
</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="all-video-popup" v-if="showAllVideoPopup" @click="closeAllVideoPopup">
<view class="popup-content" @click.stop>
<view class="popup-body">
<view
class="category-item"
:class="{ active: typeUuid === item.uuid }"
v-for="item in videoTypeList"
:key="item.uuid"
@click="selectCategory(item.uuid)"
>
{{ item.name }}
</view>
<view v-if="videoTypeList.length === 0" class="empty-content">
<text>暂无数据</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, computed,reactive } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import api from '@/api/api.js';
import allImg from "@/static/video_all.png"
import allOnImg from "@/static/video_select.png"
import selectImg from "@/static/all_video.png"
import selectOnImg from "@/static/select_video.png"
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_down.png"
import docUrl from '@/utils/docUrl';
import navTo from '@/utils/navTo';
import empty from '@/components/empty/empty.vue';
const isAllActive=ref(false)
//
const videoList = ref([]);
const activeTab = ref(1); // ""
const loading = ref(false);
const refreshing = ref(false);
const loadMoreStatus = ref('more'); // more, loading, noMore
const currentPage = ref(1);
const pageSize = ref(10);
const hasMoreData = ref(true);
const filteredContent=ref([])
const sort =ref(2);
const keywords=ref('');
const typeUuid=ref('0e5fa3d76b8047528fdd3c452b77e9dd');
const typeName=ref('乙肝')
const isFilterActive=ref(false)
const showFilter = ref(false);
const yearList=ref([]);
const selectYearContent=reactive({})
//
const filterTags = ref([]);
//
const showAllVideoPopup = ref(false);
const selectedCategory = ref('全部');
const selectCategory=(uuid)=>{
console.log(222)
console.log(uuid)
typeUuid.value=uuid;
typeName.value=videoTypeList.value.find(item=>item.uuid==uuid).name;
isAllActive.value=true;
closeAllVideoPopup();
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData();
}
const videoTypeList=ref([]);
const patientVideoNew=async ()=>{
const res=await api.patientVideoNew();
if(res.code==200){
videoTypeList.value=res.data;
}
}
//
onMounted(() => {
});
onShow(() => {
// API
currentPage.value = 1;
hasMoreData.value = true;
loadGuideTags();
loadVideoData();
patientVideoNew();
});
//
//
const loadGuideTags = async () => {
try {
const res = await api.guideTag({
type:5
});
console.log('指南标签API响应:', res);
if(res && res.code === 200 && res.data) {
// API
filterTags.value = res.data.map(tag => ({
...tag,
selected: false
}));
console.log('指南标签加载成功:', filterTags.value);
}
} catch (e) {
console.error('加载指南标签失败:', e);
}
};
//
const loadVideoData = async (isRefresh = false) => {
if (loading.value && !isRefresh) return;
loading.value = true;
try {
const response = await api.patientVideoByKeyWordsNew({
page: currentPage.value,
pageSize: pageSize.value,
keywords: keywords.value,
sort:sort.value,
typeUuid:typeUuid.value,
});
console.log('视频列表API原始响应:', response);
//
let videoData = {};
if (response && response.data) {
if (response.data.code === 200) {
videoData = response.data.data || {};
} else if (response.code === 200) {
videoData = response.data || {};
}
} else if (response && response.code === 200) {
videoData = response.data || {};
}
console.log('解析后的视频数据:', videoData);
if (videoData && videoData.list) {
const { list, totalPage,pageNumber } = videoData;
console.log('视频列表数据:', list);
if (isRefresh) {
videoList.value = list || [];
currentPage.value = 1;
} else {
videoList.value = [...videoList.value, ...(list || [])];
}
hasMoreData.value = totalPage>pageNumber;
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
} else {
throw new Error(response?.message || '获取数据失败');
}
} catch (error) {
console.error('加载视频失败:', error);
} finally {
loading.value = false;
refreshing.value = false;
}
};
//
const onRefresh = () => {
refreshing.value = true;
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
//
const onLoadMore = () => {
if (!hasMoreData.value || loading.value) return;
loadMoreStatus.value = 'loading';
currentPage.value++;
loadVideoData();
};
//
const switchTab = (tabIndex) => {
activeTab.value = tabIndex;
//
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData();
};
//
const playVideo = (video) => {
const videoId = video.id || video.uuid;
navTo({
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`
});
};
//
const goSearch = () => {
uni.navigateTo({
url: '/pages_app/search/search'
});
};
//
//
const searchVideos = async (keywords) => {
if (!keywords || keywords.trim() === '') {
loadVideoData();
return;
}
try {
console.log('搜索关键词:', keywords.trim());
const response = await api.patientVideoByKeyWordsNew({
keywords: keywords.trim(),
page: 1,
pageSize: pageSize.value
});
console.log('搜索API原始响应:', response);
//
let searchData = {};
if (response && response.data) {
if (response.data.code === 200) {
searchData = response.data.data || {};
} else if (response.code === 200) {
searchData = response.data || {};
}
} else if (response && response.code === 200) {
searchData = response.data || {};
}
console.log('解析后的搜索数据:', searchData);
if (searchData && searchData.list) {
const { list, hasMore } = searchData;
console.log('搜索结果:', list);
videoList.value = list || [];
hasMoreData.value = hasMore !== false;
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
currentPage.value = 1;
}
} catch (error) {
console.error('搜索视频失败:', error);
uni.showToast({
title: '搜索失败,请重试',
icon: 'none'
});
}
};
//
const openAllVideoPopup = () => {
showAllVideoPopup.value = true;
};
const closeAllVideoPopup = () => {
showAllVideoPopup.value = false;
};
//
const showFilterPopup = () => {
showFilter.value = true;
};
const hideFilterPopup = () => {
showFilter.value = false;
};
const toggleTag = (index) => {
filterTags.value[index].selected = !filterTags.value[index].selected;
};
const resetFilter = () => {
//
filterTags.value.forEach(tag => tag.selected = false);
//
//
keywords.value = '';
//
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
const confirmFilter = () => {
const selectedTags = filterTags.value.filter(tag => tag.selected);
console.log('选中的筛选标签:', selectedTags);
for (var i = 0; i < selectedTags.length; i++) {
if(keywords.value){
keywords.value+=","+selectedTags[i].NAME
}else{
keywords.value=selectedTags[i].NAME
}
}
isFilterActive.value=true;
hideFilterPopup();
//
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData(true);
};
//
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({
url: '/pages/index/index'
});
}
});
}
const toggleSort = () => {
sort.value = sort.value === 1 ? 2 : 1; // 1=, 2=
currentPage.value = 1;
hasMoreData.value = true;
loadVideoData();
};
</script>
<style scoped lang="scss">
$primary-color: #ff6b6b;
$theme-color: #8B2316;
$white: #fff;
$gray-bg: #f5f5f5;
$gray-light: #eee;
$gray-medium: #999;
$gray-dark: #666;
$text-color: #333;
//
$border-radius: 8px;
$border-radius-small: 6px;
$padding: 15px;
$padding-small: 10px;
.video-page {
background-color: #f5f5f5;
height: 100vh;
display: flex;
overflow: hidden;
flex-direction: column;
}
/* Header Styles */
.header {
background-color: #fff;
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
position: relative;
z-index: 100;
}
.header-left, .header-right {
display: flex;
align-items: center;
}
.header-title {
font-size: 36rpx;
font-weight: 600;
color: #8B4513;
}
/* Scroll View */
.scroll-view {
position: fixed;
bottom: 0rpx;
height: calc(100vh - 140rpx); /* 固定高度,减去导航栏高度 */
background-color: #f5f5f5;
top: 172rpx; /* 为导航栏留出空间 */
}
/* Filter Tabs */
.filter-tabs {
background-color: #fff;
display: flex;
justify-content: space-between;
padding: 30rpx 30rpx;
}
.tab-item {
display: flex;
align-items: center;
margin-right: 60rpx;
padding: 10rpx 0;
}
.tab-item:last-child{
margin-right: 0;
}
.right{
display: flex;
}
.tab-item.active .tab-text {
color: #E74C3C;
font-weight: 600;
}
.tab-text {
margin: 0 8rpx;
font-size: 28rpx;
color: #666;
}
.tab-item:first-child .tab-text{
max-width:295rpx;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tab-item .tab-text.active {
color: #8B2316;
}
/* Fixed Filter Tabs Container */
.filter-tabs-container {
position: fixed;
top: 140rpx; /* uni-nav-bar height */
left: 0;
right: 0;
z-index: 40;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.doctor-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
overflow: hidden;
}
.doctor-avatar image {
width: 100%;
height: 100%;
}
/* Video List */
.video-list {
padding: 0 20rpx;
margin-top: 100rpx; /* 为固定的Filter Tabs留出空间 */
display: flex;
flex-wrap: wrap;
gap: 16rpx; /* 卡片间距 */
}
.video-item {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 0;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
width: calc(50% - 8rpx); /* 两列布局减半gap */
min-width: 300rpx; /* 确保最小宽度 */
flex-shrink: 0; /* 防止收缩 */
}
.video-thumbnail {
position: relative;
height: 200rpx;
}
.video-thumbnail image {
width: 100%;
height: 100%;
}
.video-duration {
margin-top: 8rpx;
}
.video-duration text {
font-size: 22rpx;
color: #999;
}
.video-info {
padding: 24rpx;
}
.video-title {
font-size: 28rpx;
font-weight: 400;
color: #333;
line-height: 1.4;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
height: 2.8em; /* 固定2行高度1.4 * 2 = 2.8em */
}
.video-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.author {
font-size: 24rpx;
color: #999;
}
.stats {
display: flex;
align-items: center;
}
.view-count {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-state image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-state text {
color: #999;
font-size: 28rpx;
}
.newbox{
margin-top: -12rpx;
}
/* 全部视频弹窗样式 */
.all-video-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: flex;
align-items: flex-end;
}
.popup-content {
width: 100%;
background-color: #fff;
height:calc(100vh - 272rpx);
overflow-y: scroll;
}
.popup-header {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
}
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.popup-body {
height:100%;
}
.category-item {
padding: 30rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
transition: background-color 0.2s ease;
}
.category-item:last-child {
border-bottom: none;
}
.category-item.active {
background-color: #f8f8f8;
color: #8B2316;
}
.category-item:active {
background-color: #e8f4fd;
}
.empty-content {
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
color: #999;
font-size: 28rpx;
}
//
.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:272rpx;
overflow-y:scroll;
background-color: $white;
// border-radius: 20rpx 20rpx 0 0;
padding: 20rpx 30rpx 60rpx;
width: 100%;
height: calc(100vh - 272rpx);
.filter-tags {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-bottom: 60rpx;
max-height: 65vh;
overflow-y: auto;
.tag-item {
background-color: #f8f8f8;
color: $gray-dark;
padding: 8rpx 0; /* 去掉左右padding仅保留上下 */
border-radius: 30rpx;
font-size: 26rpx;
border: 2rpx solid #efefef;
transition: all 0.3s ease;
width: 152rpx; /* 固定宽度 */
text-align: center; /* 文本居中 */
white-space: nowrap; /* 单行显示 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出显示省略号 */
&.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;
background-color: #fff; /* 背景色为白色 */
.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;
}
}
}
}
</style>

View File

@ -38,7 +38,7 @@
class="category-item"
v-for="(item, index) in categoryList"
:key="index"
@click="onCategoryClick(index)"
@click="onCategoryClick(Index,item.type)"
>
<view class="category-icon">
<up-image :src="item.icon" width="60rpx" height="60rpx" v-if="tab!=index"></up-image>
@ -97,7 +97,7 @@
{ name: '指南', icon:zhinan, activeIcon:zhinanOn,type: 'zhinan' },
{ name: '精品课', icon:course,activeIcon:courseOn, type: 'course' },
{ name: '肝胆课件', icon:ppt,activeIcon:pptOn, type: 'ppt' },
{ name: '患者', icon:patient, patientIcon:videoOn,type: 'patient' },
{ name: '患者', icon:patient, patientIcon:patientOn,type: 'patient' },
{ name: '名院', icon:doctor,activeIcon:doctorOn, type: 'doctor' },
{ name: '名科', icon:department,activeIcon:departmentOn, type: 'department' },
{ name: '名医', icon:doctor, activeIcon:doctorOn,type: 'doctor' },
@ -134,8 +134,33 @@
};
//
const onCategoryClick = (index) => {
const onCategoryClick = (index,type) => {
tab.value=index;
if(type=='video'){
uni.navigateTo({
url: `/pages_app/searchVideo/searchVideo?keywords=${searchKeyword.value}`
})
}else if(type=='videoPatient'){
uni.navigateTo({
url: `/pages_app/searchVideo/searchVideo?keywords=${searchKeyword.value}&from=hjsp`
})
}else if(type=='news'){
uni.navigateTo({
url: `/pages_app/searchNews/searchNews?keywords=${searchKeyword.value}`
})
}else if(type=='kepu'){
uni.navigateTo({
url: `/pages_app/searchNews/searchNews?keywords=${searchKeyword.value}&from=kp`
})
}else if(type=='zhinan'){
uni.navigateTo({
url: `/pages_app/hot/hot?name=${searchKeyword.value}`
})
}else if(type=='course'){
uni.navigateTo({
url: `/pages_app/searchNews/searchNews?keywords=${searchKeyword.value}&from=kp`
})
}
//
};
@ -313,6 +338,7 @@
font-size: 26rpx;
color: #333;
text-align: center;
white-space: nowrap;
}
}
}

View File

@ -0,0 +1,638 @@
<template>
<view class="news-list-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<!-- 新闻列表 -->
<scroll-view
class="news-scroll-view"
scroll-y="true"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onScrollToLower"
:show-scrollbar="false"
>
<view class="news-list">
<view
class="news-item"
v-for="(news, index) in newsList"
:key="news.uuid || index"
@click="goToNewsDetail(news)"
>
<view class="item-image-container">
<image
class="item-image"
:src="docUrl + (news.headImg || news.imgPath) "
mode="aspectFill"
></image>
<view class="important-tag" v-if="news.isImportant">重要通知</view>
</view>
<view class="item-content">
<view class="item-title">{{ news.title || news.topic }}</view>
<view class="item-meta">
<text class="item-date">{{ formatDate(news.createDate || news.submitDate) }}</text>
<view class="item-stats">
<uni-icons type="eye" size="14" color="#999"></uni-icons>
<text>{{ news.readnum || 0 }}</text>
<uni-icons type="hand-up" size="14" color="#999" style="margin-left: 20rpx;"></uni-icons>
<text>{{ news.likenum || news.agreenum }}</text>
</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'" class="no-more">
<text>没有更多数据了</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue';
import { onShow, onPullDownRefresh, onReachBottom,onLoad } from '@dcloudio/uni-app';
import api from '@/api/api.js';
import docUrl from '@/utils/docUrl.js';
import dayjs from 'dayjs';
import navTo from '@/utils/navTo.js';
const from=ref('');
const title=ref('肝胆新闻');
// Banner
const bannerList = ref([]);
const currentBannerIndex = ref(0);
const newstagid=ref('');
//
const activeTab = ref(-1); // '' is the second tab (index 1)
const tabs = ref(['精选', '肝胆新闻', '技术快讯', '基地动态', '专家动态']);
const newsList = ref([]);
const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
const loadMoreStatus = ref('more'); // more, loading, noMore
const isRefreshing = ref(false); //
const keywords=ref('');
const loadMoreText = reactive({
contentdown: '上拉显示更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
});
const newsTags = ref([]); //
onLoad((options) => {
if(options.from){
from.value = options.from
title.value = '患教学堂'
}
if(options.keywords){
keywords.value = options.keywords
}
})
onShow(() => {
if(from.value){
loadNewsListKePu();
}else{
loadNewsListIndex();
}
});
const onSwiperChange = (e) => {
currentBannerIndex.value = e.detail.current;
};
// 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: page.value,
listLength: newsList.value.length
});
if (loadMoreStatus.value === 'more' && !isRefreshing.value) {
console.log('条件满足,开始加载更多数据');
if(from.value){
loadNewsListKePu();
}else{
loadNewsListIndex();
}
} else {
console.log('条件不满足,跳过加载:', {
loadMoreStatus: loadMoreStatus.value,
isRefreshing: isRefreshing.value
});
}
};
//
const goBack = () => {
uni.navigateBack();
};
const goToSearch = () => {
// Implement navigation to search page
console.log('Go to search page');
};
//
//
const loadNewsTags = async () => {
try {
const res = await api.newsTagList({});
console.log('新闻标签响应:', res);
if (res && res.code === 200) {
console.log('获取到标签数据:', res.data);
tabs.value = res.data;
//
if (res.data && Array.isArray(res.data) && res.data.length > 0) {
newstagid.value = res.data[0].ID;
//loadNewsList(true); // Initial load
} else {
console.warn('标签数据为空或格式不正确');
newstagid.value = '';
}
} else {
console.error('加载新闻标签失败:', res.message);
}
} catch (e) {
console.error('加载新闻标签异常:', e);
}
};
//
const loadNewsListKePu = async (isRefresh = false) => {
if (isRefresh) {
page.value = 1;
newsList.value = [];
loadMoreStatus.value = 'more';
console.log('刷新模式:重置分页状态');
}
//
if (loadMoreStatus.value === 'noMore' || loadMoreStatus.value === 'loading') {
console.log('当前状态不允许加载:', loadMoreStatus.value);
return;
}
//
loadMoreStatus.value = 'loading';
console.log(`开始加载第${page.value}页数据newstagid: ${newstagid.value}`);
try {
const res = await api.polularScienceArticleListIndexNew({
page: page.value,
pageSize: pageSize.value,
topic: keywords.value
});
console.log('新闻列表API响应:', res);
if (res && res.code === 200) {
//
let newItems = [];
let totalCount = 0;
if (res.data && res.data.list) {
newItems = res.data.list;
totalCount = res.data.totalRow;
console.log('使用 res.data.list 结构');
}
console.log('解析后的数据:', { newItems, totalCount, newItemsLength: newItems.length });
// newItems
if (Array.isArray(newItems)) {
if (isRefresh) {
newsList.value = newItems;
console.log('刷新模式:替换列表数据');
} else {
newsList.value = [...newsList.value, ...newItems];
console.log('加载更多:追加数据到列表');
}
total.value = totalCount;
console.log(`当前页数据: ${newItems.length}, 总数: ${total.value}, 当前列表长度: ${newsList.value.length}`);
//
if (newItems.length < pageSize.value || newsList.value.length >= total.value) {
loadMoreStatus.value = 'noMore';
console.log('没有更多数据了');
} else {
loadMoreStatus.value = 'more';
page.value++;
console.log(`还有更多数据,下一页: ${page.value}`);
}
} else {
console.error('API返回的数据不是数组格式:', newItems);
loadMoreStatus.value = 'noMore';
}
} else {
console.error('加载新闻列表失败:', res.message);
loadMoreStatus.value = 'noMore';
}
} catch (e) {
console.error('加载新闻列表异常:', e);
loadMoreStatus.value = 'noMore';
}
console.log('=== loadNewsList 结束 ===');
console.log('最终状态:', {
loadMoreStatus: loadMoreStatus.value,
page: page.value,
listLength: newsList.value.length
});
};
const loadNewsListIndex = async (isRefresh = false) => {
console.log('=== loadNewsList 开始 ===');
console.log('当前参数:', { isRefresh, page: page.value, newstagid: newstagid.value });
// newstagid
if (isRefresh) {
page.value = 1;
newsList.value = [];
loadMoreStatus.value = 'more';
console.log('刷新模式:重置分页状态');
}
//
if (loadMoreStatus.value === 'noMore' || loadMoreStatus.value === 'loading') {
console.log('当前状态不允许加载:', loadMoreStatus.value);
return;
}
//
loadMoreStatus.value = 'loading';
console.log(`开始加载第${page.value}页数据newstagid: ${newstagid.value}`);
try {
const res = await api.defaultNewsListNew({
page: page.value,
pageSize: pageSize.value,
});
console.log('新闻列表API响应:', res);
if (res && res.code === 200) {
//
let newItems = [];
let totalCount = 0;
if (res.data && res.data.list) {
newItems = res.data.list;
totalCount = res.data.totalRow || 0;
console.log('使用 res.data.list 结构');
}
console.log('解析后的数据:', { newItems, totalCount, newItemsLength: newItems.length });
// newItems
if (Array.isArray(newItems)) {
if (isRefresh) {
newsList.value = newItems;
console.log('刷新模式:替换列表数据');
} else {
newsList.value = [...newsList.value, ...newItems];
console.log('加载更多:追加数据到列表');
}
total.value = totalCount;
console.log(`当前页数据: ${newItems.length}, 总数: ${total.value}, 当前列表长度: ${newsList.value.length}`);
//
if (newItems.length < pageSize.value || newsList.value.length >= total.value) {
loadMoreStatus.value = 'noMore';
console.log('没有更多数据了');
} else {
loadMoreStatus.value = 'more';
page.value++;
console.log(`还有更多数据,下一页: ${page.value}`);
}
} else {
console.error('API返回的数据不是数组格式:', newItems);
loadMoreStatus.value = 'noMore';
}
} else {
console.error('加载新闻列表失败:', res.message);
loadMoreStatus.value = 'noMore';
}
} catch (e) {
console.error('加载新闻列表异常:', e);
loadMoreStatus.value = 'noMore';
}
console.log('=== loadNewsList 结束 ===');
console.log('最终状态:', {
loadMoreStatus: loadMoreStatus.value,
page: page.value,
listLength: newsList.value.length
});
};
const refreshData = async () => {
console.log('Refreshing data...');
//
page.value = 1;
newsList.value = [];
loadMoreStatus.value = 'more';
if(from.value){
//
await loadNewsListKePu(true);
}else{
await loadNewsListIndex(true);
}
uni.showToast({
title: '刷新成功',
icon: 'success'
});
};
const goToNewsDetail = (item) => {
console.log('Go to news detail:', item);
let url=encodeURIComponent(docUrl+item.path)
navTo({
url: `/pages_app/webview/webview?url=${url}`
});
};
const formatDate = (dateString) => {
if (!dateString) return '';
try {
// 使 dayjs
return dayjs(dateString).format('YYYY-MM-DD');
} catch (error) {
console.error('日期格式化失败:', error, dateString);
return '';
}
};
</script>
<style lang="scss" scoped>
.news-list-page {
background-color: #f8f8f8;
height: 100vh;
overflow-y: hidden;
}
.uni-navbar {
/* Custom styles for uni-nav-bar if needed */
.uni-navbar__content {
height: 44px; // Standard navbar height
}
}
.nav-tabs {
background-color: #ffffff;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
position: sticky;
top: calc(var(--status-bar-height) + 44px); // Below navbar
z-index: 99;
.tabs-scroll {
width: 100%;
white-space: nowrap;
}
.tabs-container {
display: flex;
padding: 0 20rpx;
min-width: 100%;
}
.tab-item {
flex-shrink: 0;
padding: 10rpx 30rpx;
font-size: 28rpx;
color: #666;
position: relative;
margin-right: 20rpx;
&:last-child {
margin-right: 0;
}
&.active {
color: #ff4757;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: -10rpx;
left: 50%;
transform: translateX(-50%);
width: 60%;
height: 4rpx;
background-color: #ff4757;
border-radius: 2rpx;
}
}
}
}
/* Fixed Banner Container */
.banner-container {
position: relative;
background-color: #fff;
.swipemask{
width:100%;
z-index:3;
box-sizing:border-box;
position: absolute;
bottom:0;
padding:0 20rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: rgba(0,0,0,0.7);
.banner-subtitle {
font-size: 24rpx;
color: rgba(255,255,255,0.8);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 60%;
}
.dotbox{
display:flex;
align-items: center;
.circle{
margin:0 4rpx;
width:16rpx;
height:16rpx;
background-color: #eee;
border-radius: 50%;
}
.circle.active{
background-color: #8B2316;
}
}
}
}
.news-scroll-view {
position: fixed;
bottom:0;
width:100%;
top:140rpx;
height: calc(100vh - 140rpx); //
background-color: #f8f8f8;
}
.news-list {
padding: 20rpx;
background-color: #f8f8f8;
.news-item {
display: flex;
background-color: #ffffff;
border-radius: 16rpx;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
overflow: hidden; // Ensure content respects border-radius
.item-image-container {
flex-shrink: 0;
width: 200rpx;
height: 160rpx;
border-radius: 8rpx;
overflow: hidden;
margin-right: 20rpx;
position: relative;
background-color: #f0f0f0; // Placeholder background
.item-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.important-tag {
position: absolute;
top: 0;
left: 0;
background-color: #ff4757;
color: #fff;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-bottom-right-radius: 8rpx;
z-index: 1;
}
}
.item-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.item-title {
font-size: 32rpx;
font-weight: normal; // Removed bold as per previous request
color: #333;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; // Fixed two-line height
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 10rpx;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 24rpx;
color: #999;
.item-date {
flex-shrink: 0;
}
.item-stats {
display: flex;
align-items: center;
text {
margin-left: 8rpx;
}
}
}
}
}
.load-more-status {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 28rpx;
.loading {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
}
.more {
color: #666;
}
.no-more {
color: #ccc;
}
}
</style>

View File

@ -0,0 +1,306 @@
<template>
<view class="video-search-page">
<!-- 头部导航栏 -->
<uni-nav-bar
left-icon="left"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
/>
<!-- 顶部排序标签 -->
<view class="tabs">
<view
class="tab"
:class="{ active: activeTab === 0 }"
@click="switchTab(0)"
>
<text class="tab-text">上传时间</text>
</view>
<view
class="tab"
:class="{ active: activeTab === 1 }"
@click="switchTab(1)"
>
<text class="tab-text">点播量</text>
</view>
</view>
<!-- 列表区域 -->
<scroll-view
class="list-scroll"
scroll-y
:show-scrollbar="false"
@scrolltolower="onReachBottom"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
>
<view class="video-list">
<view class="video-item" v-for="(item, idx) in videoList" :key="idx" @click="openDetail(item)">
<view class="item-title">{{ item.name }}</view>
<view class="item-meta">
<text class="item-tag">{{ item.public_name }}</text>
</view>
<view class="item-date-views">
<text class="item-date">{{ formatDate(item.create_date) }}</text>
<view class="item-views">
<uni-icons type="eye" size="20" color="#999"></uni-icons>
<text class="views-text">{{ item.readnum }}</text>
</view>
</view>
<view class="item-footer">
<text class="item-source">{{ item.video_type_name }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad,onShow } from '@dcloudio/uni-app'
import api from '@/api/api.js'
const keywords=ref('')
const title = ref('肝胆视频')
const activeTab = ref(0)
const page = ref(1)
const pageSize = ref(10)
const hasMore = ref(true)
const isRefreshing = ref(false)
const videoList = ref([])
const sort = ref('2')
const from=ref('');
const loadListHuanJiao=async (reset = false) => {
if (reset) {
page.value = 1
hasMore.value = true
videoList.value = []
}
if (!hasMore.value) return
const res = await api.videoBySearchNew({
name: keywords.value,
page: page.value,
pageSize: pageSize.value,
sort: sort.value,
})
if (res.code === 200 && res.data) {
const list = res.data.list || []
videoList.value = [...videoList.value, ...list]
if (list.length < pageSize.value) {
hasMore.value = false
} else {
page.value += 1
}
} else {
hasMore.value = false
}
}
const loadList = async (reset = false) => {
if (reset) {
page.value = 1
hasMore.value = true
videoList.value = []
}
if (!hasMore.value) return
const res = await api.videoBySearchNew({
name: keywords.value,
page: page.value,
pageSize: pageSize.value,
sort: sort.value,
})
if (res.code === 200 && res.data) {
const list = res.data.list || []
videoList.value = [...videoList.value, ...list]
if (list.length < pageSize.value) {
hasMore.value = false
} else {
page.value += 1
}
} else {
hasMore.value = false
}
}
onShow(() => {
if(from.value){
loadListHuanJiao(true)
}else{
loadList(true)
}
})
const onReachBottom = () => {
if(from.value){
loadListHuanJiao(false)
}else{
loadList(false)
}
}
const onRefresh = async () => {
isRefreshing.value = true
if(from.value){
await loadListHuanJiao(true)
}else{
await loadList(true)
}
isRefreshing.value = false
}
onLoad((options) => {
if (options.keywords) {
keywords.value = options.keywords
}
if(options.from){
from.value = options.from
title.value = '患教视频'
}
})
const switchTab = (index) => {
activeTab.value = index;
if(index==0){
sort.value='2'
}else{
sort.value='1'
}
loadList(true)
//
}
const openDetail = (item) => {
// /
navTo({
url: `/pages_app/videoDetail/videoDetail?uuid=${item.uuid}`
})
}
const goBack = () => {
uni.navigateBack()
}
const formatDate = (val) => {
if (!val) return ''
try {
const d = new Date(val)
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}-${m}-${day}`
} catch (e) {
return val
}
}
</script>
<style lang="scss" scoped>
$primary: #8B2316;
$text-primary: #333;
$text-secondary: #666;
$text-light: #999;
$bg: #f6f6f6;
$white: #ffffff;
.video-search-page {
background: $bg;
min-height: 100vh;
}
.tabs {
position: fixed;
top: 140rpx;
left: 0;
right: 0;
z-index: 90;
display: grid;
grid-template-columns: 1fr 1fr;
background: $white;
border-bottom: 2rpx solid #eee;
.tab {
padding: 28rpx 0;
display: flex;
align-items: center;
justify-content: center;
.tab-text { color: $text-secondary; font-size: 30rpx; }
&.active {
.tab-text { color: $primary; font-weight: 600; }
position: relative;
}
}
}
.list-scroll {
position: fixed;
top: 215rpx; // 140rpx + 100rpx tabs
left: 0;
right: 0;
bottom: 0;
background: $bg;
}
.video-list {
padding: 20rpx 0;
.video-item {
background: $white;
padding: 28rpx 32rpx;
margin: 20rpx 30rpx 0; // 20rpx 30rpx
// 线
.item-title {
font-size: 34rpx;
color: $primary;
line-height: 1.5;
margin-bottom: 18rpx;
}
.item-meta {
margin-bottom: 12rpx;
.item-tag { color: $text-secondary; font-size: 28rpx; }
}
.item-date-views {
display: flex;
align-items: center;
justify-content: space-between; //
width: 100%;
margin-bottom: 16rpx;
padding-bottom: 16rpx;
border-bottom: 2rpx solid #efefef;
.item-date { color: $text-secondary; font-size: 28rpx; }
.item-views { display: flex; align-items: center; gap: 10rpx; white-space: nowrap; }
.views-text { color: $text-secondary; font-size: 28rpx; }
}
.item-footer {
display: flex;
align-items: center;
justify-content: space-between;
.item-source {
font-size: 28rpx;
color: $text-secondary;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
}
}
}
</style>

View File

@ -263,7 +263,7 @@
import delImg from "@/static/delete_paper.png"
import api from '@/api/api.js'
import docUrl from "@/utils/docUrl.js"
import navTo from '../../utils/navTo';
import navTo from '@/utils/navTo.js';
const total=ref(0);
const tab=ref('zhinan')
//
@ -319,7 +319,7 @@ import navTo from '../../utils/navTo';
}
navTo({
url:'/pages_app/zhinanList/zhinanList?keywords='+encodeURIComponent(keywords.value)
url:'/pages_app/hot/hot?keywords='+encodeURIComponent(keywords.value)
})
}
@ -388,9 +388,8 @@ import navTo from '../../utils/navTo';
//
const enterCategory = (item) => {
console.log('进入分类:', item.category);
uni.navigateTo({
url: `/pages/guidelineList/guidelineList?category=${item.category}&title=${item.title}`
navTo({
url: `/pages_app/zhinanList/zhinanList?typeUuid=${item.uuid}&title=${item.name}`
});
};

View File

@ -2,8 +2,8 @@
<view class="zhinan-list-page">
<!-- 头部导航栏 -->
<uni-nav-bar
left-icon="left"
title="诊疗指南"
left-icon="left"
:title="title"
@clickLeft="goBack"
fixed
color="#8B2316"
@ -12,12 +12,26 @@
backgroundColor="#eeeeee"
>
<template v-slot:right>
<view class="nav-right" @click="testLoadMore">
<text style="font-size: 24rpx; color: #8B2316;">测试加载</text>
<view class="nav-right" @click="goGot">
<up-image :src="hotImg" width="40rpx" height="40rpx" ></up-image>
</view>
</template>
</uni-nav-bar>
<!-- 固定搜索栏 -->
<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"
@ -34,6 +48,7 @@
class="guideline-item"
v-for="(item, index) in guidelinesList"
:key="item.uuid || index"
@click="viewGuideline(item)"
>
<!-- 指南信息 -->
<view class="item-content">
@ -43,19 +58,18 @@
<!-- 操作按钮 -->
<view class="item-action">
<view
v-if="item.can_download"
class="download-btn"
@click="downloadGuideline(item)"
@click.stop="viewGuideline(item)"
>
<up-icon name="download" color="#8D2316" size="28"></up-icon>
</view>
<view
<!-- <view
v-else
class="view-btn"
@click="viewGuideline(item)"
@click.stop="viewGuideline(item)"
>
查看
</view>
</view> -->
</view>
</view>
</view>
@ -83,13 +97,32 @@
</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} 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"
//
const guidelinesList = ref([])
const isLoading = ref(false)
@ -98,14 +131,48 @@ 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('上传时间');
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)
keywords.value=decodeURIComponent(options.keywords);
console.log(keywords.value);
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(() => {
//
if (guidelinesList.value.length === 0) {
@ -113,6 +180,16 @@ onShow(() => {
}
})
//
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 下拉刷新触发');
@ -183,6 +260,7 @@ const loadGuidelinesList = async (isRefresh = false) => {
pageSize: pageSize.value,
type: 1, //
sort:2,
typeUuid:typeUuid.value,
keywords:keywords.value
}
@ -289,13 +367,66 @@ const downloadGuideline = (item) => {
}
}
//
// pdf/doc/docx/xls/xlsx/ppt/pptx/txt
const viewGuideline = (item) => {
console.log('查看指南:', item)
//
uni.navigateTo({
url: `/pages_app/zhinanDetail/zhinanDetail?uuid=${item.uuid}&title=${encodeURIComponent(item.title)}`
})
const url = docUrl+item.path
if (url) {
const isPDF = /\.(pdf)(\?|$)/i.test(url)
const isOffice = /\.(docx?|xlsx?|pptx?)(\?|$)/i.test(url)
const isText = /\.(txt)(\?|$)/i.test(url)
// H5
// #ifdef H5
if (isPDF) {
try { window.open(url, '_blank') } catch (e) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
return
}
if (isOffice) {
const officeUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`
try { window.open(officeUrl, '_blank') } catch (e) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(officeUrl)}` })
}
return
}
if (isText) {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
return
}
// webview
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
return
// #endif
// H5 openDocument退 webview
// #ifndef H5
uni.showToast({ title: '正在打开...', icon: 'none' })
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200 && res.tempFilePath) {
uni.openDocument({
filePath: res.tempFilePath,
showMenu: true,
success: () => {},
fail: () => {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
})
} else {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
},
fail: () => {
uni.navigateTo({ url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}` })
}
})
return
// #endif
}
// 退
}
//
@ -350,16 +481,126 @@ $text-light: #999;
$border-color: #e0e0e0;
$bg-color: #f6f6f6;
$white: #ffffff;
.imgbox{
width: 32rpx;
margin-top: -10rpx;
}
.popup-panel {
position: fixed;
top: 256rpx; /* 紧贴筛选栏 */
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: 242rpx;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
height: calc(100vh - 140rpx);
background-color: $bg-color;
}
//
.search-container-fixed {
position: fixed;
top: 140rpx; //
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 {

BIN
static/hot_booklist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB