9.1
This commit is contained in:
parent
7270482bd9
commit
82e0a5c10d
64
api/api.js
64
api/api.js
@ -81,13 +81,6 @@ const api = {
|
||||
return request('/expertAPI/my', data, 'post', false);
|
||||
},
|
||||
|
||||
getVideoList(data) {
|
||||
return request('/video/list', data, 'get', true);
|
||||
},
|
||||
getVideoDetail(data) {
|
||||
return request('/video/detail', data, 'get', true);
|
||||
},
|
||||
|
||||
// 新闻详情
|
||||
getNewsDetail(data) {
|
||||
return request('/expertAPI/newsDetail', data, 'post', false);
|
||||
@ -160,6 +153,63 @@ const api = {
|
||||
return request('/expertAPI/addBonusPointsN', data, 'post', false);
|
||||
},
|
||||
|
||||
//视频顶部轮播
|
||||
videoRoll(data){
|
||||
return request('/expertAPI/videoRoll', data, 'post', false);
|
||||
},
|
||||
//视频标签
|
||||
videoTagList(data){
|
||||
return request('/expertAPI/videoTagList', data, 'post', false);
|
||||
},
|
||||
//视频首页
|
||||
videoIndexN(data){
|
||||
return request('/expertAPI/videoIndexN', data, 'post', false);
|
||||
},
|
||||
//视频类型
|
||||
expertVideoTypeList(data){
|
||||
return request('/expertAPI/expertVideoTypeList', data, 'post', false);
|
||||
},
|
||||
//视频搜索
|
||||
videoByKeyWordsNew(data){
|
||||
return request('/expertAPI/videoByKeyWordsNew', data, 'post', false);
|
||||
},
|
||||
|
||||
// 指南杂志 - 治疗指南列表
|
||||
searchLibraryU(data){
|
||||
return request('/expertAPI/searchLibraryU', data, 'post', false);
|
||||
},
|
||||
// 指南杂志 - 治疗指南最火TOP10
|
||||
top10ByType(data){
|
||||
return request('/expertAPI/top10ByType', data, 'post', false);
|
||||
},
|
||||
// 指南杂志 - 治疗指南分类
|
||||
guideType(data){
|
||||
return request('/expertAPI/guideType', data, 'post', false);
|
||||
},
|
||||
//指南标签
|
||||
guideTag(data){
|
||||
return request('/expertApp/tagList', data, 'post', false);
|
||||
},
|
||||
|
||||
// 肝胆新闻相关API
|
||||
// 顶部轮播
|
||||
newsRollNew(data){
|
||||
return request('/expertAPI/newsRollNew', data, 'post', false);
|
||||
},
|
||||
// 新闻列表
|
||||
defaultNewsListNew(data){
|
||||
return request('/expertAPI/defaultNewsListNew', data, 'post', false);
|
||||
},
|
||||
// 根据标签查询新闻列表
|
||||
newsListNew(data){
|
||||
return request('/expertAPI/newsListNew', data, 'post', false);
|
||||
},
|
||||
// 新闻标签列表
|
||||
newsTagList(data){
|
||||
return request('/expertAPI/newsTagList', data, 'post', false);
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default api
|
||||
@ -2,15 +2,14 @@
|
||||
"name": "uniapp",
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
||||
},
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"js-base64": "^3.7.8",
|
||||
"js-md5": "^0.8.3",
|
||||
"uview-plus": "^3.4.73"
|
||||
|
||||
40
pages.json
40
pages.json
@ -511,6 +511,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "zhinanList/zhinanList",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "newsList/newsList",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "personInfo/personInfo",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pptDetail/pptDetail",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "uni-app分页",
|
||||
"app": {
|
||||
"bounce": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "patientMsg/patientMsg",
|
||||
"style": {
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<!-- Fixed Banner Swiper -->
|
||||
<view class="banner-container">
|
||||
<view class="swipemask">
|
||||
<view class="banner-subtitle">111111111</view>
|
||||
<view class="banner-subtitle">{{bannerList[currentBannerIndex].summary}}</view>
|
||||
<view class="dotbox">
|
||||
<view class="circle" :class="{active:currentBannerIndex==index} "v-for="(banner, index) in bannerList"
|
||||
:key="banner.id"></view>
|
||||
@ -39,19 +39,12 @@
|
||||
>
|
||||
<view class="banner-item">
|
||||
<image
|
||||
style="width:100%;"
|
||||
class="banner-image"
|
||||
:src="banner.thumbnail || '/static/big_background_my.png'"
|
||||
mode="aspectFill"
|
||||
:src="docUrl +banner.headImg"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
<view class="banner-content">
|
||||
<view class="doctor-avatar" v-if="banner.doctorAvatar">
|
||||
<image :src="banner.doctorAvatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="banner-info">
|
||||
<view class="banner-title">{{banner.title}}</view>
|
||||
<view class="banner-subtitle">{{banner.subtitle || '专业医学视频教育平台'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
@ -59,7 +52,7 @@
|
||||
|
||||
<scroll-view class="news-list" scroll-y="true">
|
||||
<!-- 肝胆新闻 -->
|
||||
<view class="news-item" @click="goToNews('hepatoBiliaryNews')">
|
||||
<view class="news-item" @click="goToNews('news')">
|
||||
<view class="news-icon blue">
|
||||
<uni-icons type="paperplane" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
@ -73,7 +66,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 互动圈 -->
|
||||
<view class="news-item" @click="goToNews('interactionCircle')">
|
||||
<!-- <view class="news-item" @click="goToNews('interactionCircle')">
|
||||
<view class="news-icon purple">
|
||||
<uni-icons type="redo" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
@ -84,10 +77,10 @@
|
||||
<view class="news-arrow">
|
||||
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 科研项目 -->
|
||||
<view class="news-item" @click="goToNews('researchProjects')">
|
||||
<!-- <view class="news-item" @click="goToNews('researchProjects')">
|
||||
<view class="news-icon orange">
|
||||
<uni-icons type="star" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
@ -99,9 +92,9 @@
|
||||
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
-->
|
||||
<!-- 临床招募 -->
|
||||
<view class="news-item" @click="goToNews('clinicalRecruitment')">
|
||||
<!-- <view class="news-item" @click="goToNews('clinicalRecruitment')">
|
||||
<view class="news-icon green">
|
||||
<uni-icons type="personadd" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
@ -112,7 +105,7 @@
|
||||
<view class="news-arrow">
|
||||
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 肝胆会议 -->
|
||||
<view class="news-item" @click="goToCourse('hepatoBiliaryConference')">
|
||||
@ -129,7 +122,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 肝胆活动 -->
|
||||
<view class="news-item" @click="goToNews('hepatoBiliaryActivities')">
|
||||
<!-- <view class="news-item" @click="goToNews('hepatoBiliaryActivities')">
|
||||
<view class="news-icon pink">
|
||||
<uni-icons type="gift" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
@ -140,18 +133,48 @@
|
||||
<view class="news-arrow">
|
||||
<uni-icons type="forward" size="16" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import api from '@/api/api.js';
|
||||
import docUrl from "@/utils/docUrl.js";
|
||||
import navTo from '../../utils/navTo';
|
||||
|
||||
onShow(()=>{
|
||||
bannerList.value = getMockBannerData();
|
||||
})
|
||||
onMounted(() => {
|
||||
loadNewsBanner();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
loadNewsBanner();
|
||||
});
|
||||
|
||||
// 加载新闻轮播数据
|
||||
const loadNewsBanner = async () => {
|
||||
try {
|
||||
console.log(api)
|
||||
const res = await api.newsRollNew({});
|
||||
console.log('新闻轮播响应:', res);
|
||||
|
||||
if(res && res.code === 200) {
|
||||
bannerList.value = res.data || [];
|
||||
console.log('获取到轮播数据:', bannerList.value);
|
||||
} else {
|
||||
console.error('加载新闻轮播失败:', res.message);
|
||||
// 如果接口失败,使用模拟数据作为备选
|
||||
bannerList.value = getMockBannerData();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('加载新闻轮播异常:', e);
|
||||
// 如果接口异常,使用模拟数据作为备选
|
||||
bannerList.value = getMockBannerData();
|
||||
}
|
||||
};
|
||||
// Banner相关数据
|
||||
const bannerList = ref([]);
|
||||
const currentBannerIndex = ref(0);
|
||||
@ -204,21 +227,17 @@
|
||||
|
||||
// 新闻相关方法
|
||||
const goToNews = (type) => {
|
||||
console.log('跳转到新闻类型:', type);
|
||||
// 这里可以根据不同的新闻类型跳转到相应的页面
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
if(type=="news"){
|
||||
navTo({
|
||||
url:'/pages_app/newsList/newsList'
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const goToCourse = (type) => {
|
||||
console.log('跳转到课程类型:', type);
|
||||
// 这里可以根据不同的课程类型跳转到相应的页面
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
navTo({
|
||||
url:'/pages/live/live'
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -271,6 +290,10 @@
|
||||
.banner-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.8);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.dotbox{
|
||||
|
||||
718
pages_app/newsList/newsList.vue
Normal file
718
pages_app/newsList/newsList.vue
Normal file
@ -0,0 +1,718 @@
|
||||
<template>
|
||||
<view class="news-list-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="肝胆新闻"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
></uni-nav-bar>
|
||||
|
||||
<!-- 导航标签 -->
|
||||
<view class="nav-tabs">
|
||||
<scroll-view class="tabs-scroll" scroll-x="true" :show-scrollbar="false">
|
||||
<view class="tabs-container">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === index }"
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
@click="changeTab(index)"
|
||||
>
|
||||
<text>{{ tab.NAME }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 新闻列表 -->
|
||||
<scroll-view
|
||||
class="news-scroll-view"
|
||||
scroll-y="true"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
@scrolltolower="onScrollToLower"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<!-- 顶部轮播 -->
|
||||
<view class="banner-container">
|
||||
<view class="swipemask">
|
||||
<view class="banner-subtitle" v-if="bannerList.length>0">{{bannerList[currentBannerIndex].summary}}</view>
|
||||
<view class="dotbox">
|
||||
<view class="circle" :class="{active:currentBannerIndex==index} "v-for="(banner, index) in bannerList"
|
||||
:key="banner.id"></view>
|
||||
</view>
|
||||
</view>
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
:indicator-dots="false"
|
||||
:autoplay="true"
|
||||
:interval="5000"
|
||||
:duration="300"
|
||||
indicator-color="rgba(255,255,255,0.4)"
|
||||
indicator-active-color="#fff"
|
||||
circular
|
||||
@change="onSwiperChange"
|
||||
>
|
||||
|
||||
<swiper-item
|
||||
v-for="(banner, index) in bannerList"
|
||||
:key="banner.id"
|
||||
@click="playBannerVideo(banner)"
|
||||
>
|
||||
<view class="banner-item">
|
||||
<image
|
||||
style="width:100%;"
|
||||
class="banner-image"
|
||||
:src="docUrl +banner.headImg"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<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"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="important-tag" v-if="news.isImportant">重要通知</view>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ news.title }}</view>
|
||||
<view class="item-meta">
|
||||
<text class="item-date">{{ formatDate(news.createDate) }}</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 || 0 }}</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 } from '@dcloudio/uni-app';
|
||||
import api from '@/api/api.js';
|
||||
import docUrl from '@/utils/docUrl.js';
|
||||
import dayjs from 'dayjs';
|
||||
// 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 loadMoreText = reactive({
|
||||
contentdown: '上拉显示更多',
|
||||
contentrefresh: '正在加载...',
|
||||
contentnomore: '没有更多数据了'
|
||||
});
|
||||
const newsTags = ref([]); // 新闻标签列表
|
||||
onShow(() => {
|
||||
loadNewsBanner();
|
||||
loadNewsTags(); // 加载新闻标签列表
|
||||
if(activeTab.value<0){
|
||||
// 重新加载数据
|
||||
loadNewsListIndex();
|
||||
}else{
|
||||
loadNewsList();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
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(activeTab.value<0){
|
||||
// 重新加载数据
|
||||
loadNewsListIndex();
|
||||
}else{
|
||||
loadNewsList();
|
||||
}
|
||||
} 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 changeTab = (index) => {
|
||||
activeTab.value = index;
|
||||
|
||||
// 确保 tabs 数组存在且有数据
|
||||
if (tabs.value && tabs.value.length > 0 && tabs.value[index]) {
|
||||
newstagid.value = tabs.value[index].ID ;
|
||||
} else {
|
||||
newstagid.value = '';
|
||||
console.warn('标签数据不完整,无法设置newstagid');
|
||||
}
|
||||
|
||||
page.value = 1; // Reset page when changing tab
|
||||
newsList.value = []; // Clear existing list
|
||||
loadNewsList(true); // Load new data for the selected tab
|
||||
};
|
||||
|
||||
// 加载新闻轮播数据
|
||||
const loadNewsBanner = async () => {
|
||||
try {
|
||||
const res = await api.newsRollNew({});
|
||||
console.log('新闻轮播响应:', res);
|
||||
|
||||
if (res && res.code === 200) {
|
||||
bannerList.value = res.data || [];
|
||||
console.log('获取到轮播数据:', bannerList.value);
|
||||
} else {
|
||||
console.error('加载新闻轮播失败:', res.message);
|
||||
// Fallback to mock data if API fails
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载新闻轮播异常:', e);
|
||||
// Fallback to mock data if API fails
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 加载新闻标签列表
|
||||
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 loadNewsList = 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.newsListNew({
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
newstagid: newstagid.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(activeTab.value<0){
|
||||
// 重新加载数据
|
||||
await loadNewsListIndex(true);
|
||||
}else{
|
||||
await loadNewsList(true);
|
||||
}
|
||||
|
||||
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
};
|
||||
|
||||
const goToNewsDetail = (item) => {
|
||||
// Implement navigation to news detail page
|
||||
console.log('Go to news detail:', item);
|
||||
uni.navigateTo({
|
||||
url: `/pages_app/newsDetail/newsDetail?id=${item.uuid || item.id}`
|
||||
});
|
||||
};
|
||||
|
||||
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:200rpx;
|
||||
height: calc(100vh - 200rpx); // 减去导航栏、标签栏和banner的高度
|
||||
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>
|
||||
280
pages_app/personInfo/personInfo.vue
Normal file
280
pages_app/personInfo/personInfo.vue
Normal file
@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<view class="person-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="个人资料"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
></uni-nav-bar>
|
||||
|
||||
<scroll-view class="content" scroll-y>
|
||||
<!-- 基本资料分组 -->
|
||||
<view class="section-header">基本资料</view>
|
||||
|
||||
<!-- 头像 -->
|
||||
<view class="row" @click="onChooseAvatar">
|
||||
<view class="label"><text>头像</text><text class="req">*</text></view>
|
||||
<view class="value value-avatar">
|
||||
<image :src="form.avatar" mode="aspectFill" class="avatar" />
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 姓名 -->
|
||||
<view class="row">
|
||||
<view class="label"><text>姓名</text><text class="req">*</text></view>
|
||||
<view class="value">{{ form.name }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 性别 -->
|
||||
<view class="row" @click="onPickGender">
|
||||
<view class="label"><text>性别</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.gender }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 出生日期 -->
|
||||
<view class="row" @click="onPickBirthday">
|
||||
<view class="label"><text>出生日期</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.birthday || '' }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号码 -->
|
||||
<view class="row" @click="onEditPhone">
|
||||
<view class="label"><text>手机号</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.mobile }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 邮箱 -->
|
||||
<view class="row" @click="onEditEmail">
|
||||
<view class="label"><text>邮箱</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.email || '' }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 专业资料分组 -->
|
||||
<view class="section-header">专业资料</view>
|
||||
|
||||
<!-- 医院 -->
|
||||
<view class="row" @click="onPickHospital">
|
||||
<view class="label"><text>医院</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.hospital }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 科室 -->
|
||||
<view class="row" @click="onPickDept">
|
||||
<view class="label"><text>科室</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.department }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 科室电话 -->
|
||||
<view class="row" @click="onEditDeptPhone">
|
||||
<view class="label"><text>科室电话</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.departmentPhone }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 职称 -->
|
||||
<view class="row" @click="onPickTitle">
|
||||
<view class="label"><text>职称</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.title }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 执业医师证编号 -->
|
||||
<view class="row" @click="onEditLicenseNo">
|
||||
<view class="label"><text>执业医师证编号</text><text class="req">*</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.licenseNo }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 执业医师证照片/胸牌 -->
|
||||
<view class="row" @click="onChooseLicenseImg">
|
||||
<view class="label"><text>执业医师证图片或胸牌</text><text class="req">*</text></view>
|
||||
<view class="value value-license">
|
||||
<image :src="form.licenseImg" mode="aspectFill" class="license-thumb" />
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 专长 -->
|
||||
<view class="row" @click="onEditSpecialty">
|
||||
<view class="label"><text>专长</text></view>
|
||||
<view class="value with-arrow">
|
||||
<text>{{ form.specialty }}</text>
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 个人简介 -->
|
||||
<view class="row" @click="onEditIntro">
|
||||
<view class="label"><text>个人简介</text></view>
|
||||
<view class="value with-arrow">
|
||||
<uni-icons type="right" size="18" color="#bbb" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
|
||||
const form = ref({
|
||||
avatar: '/static/big_background_my.png',
|
||||
name: '邱建东',
|
||||
gender: '男',
|
||||
birthday: '',
|
||||
mobile: '17600668628',
|
||||
email: '',
|
||||
hospital: '北京博爱医院',
|
||||
department: '肝病科',
|
||||
departmentPhone: '1234567890',
|
||||
title: '主任中医师',
|
||||
licenseNo: '12345678',
|
||||
licenseImg: '/static/big_background_my.png',
|
||||
specialty: '肝炎、肝硬化'
|
||||
});
|
||||
|
||||
const goBack = () => uni.navigateBack();
|
||||
|
||||
// 通用打开裁剪器
|
||||
const openCropper = (callback, opts = {}) => {
|
||||
const { destWidth = 600, rectWidth = 300, fileType = 'jpg' } = opts;
|
||||
const url = `/uni_modules/uview-plus/components/u-avatar-cropper/u-avatar-cropper?destWidth=${destWidth}&rectWidth=${rectWidth}&fileType=${fileType}`;
|
||||
uni.navigateTo({
|
||||
url,
|
||||
success: (res) => {
|
||||
res.eventChannel.on('uAvatarCropper', (path) => {
|
||||
if (typeof callback === 'function') callback(path);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onChooseAvatar = () => {
|
||||
openCropper((path) => {
|
||||
form.value.avatar = path;
|
||||
});
|
||||
};
|
||||
|
||||
const onChooseLicenseImg = () => {
|
||||
openCropper((path) => {
|
||||
form.value.licenseImg = path;
|
||||
}, { destWidth: 1000, rectWidth: 500 });
|
||||
};
|
||||
|
||||
const onPickGender = () => {};
|
||||
const onPickBirthday = () => {};
|
||||
const onEditPhone = () => {};
|
||||
const onEditEmail = () => {};
|
||||
const onPickHospital = () => {};
|
||||
const onPickDept = () => {};
|
||||
const onEditDeptPhone = () => {};
|
||||
const onPickTitle = () => {};
|
||||
const onEditLicenseNo = () => {};
|
||||
const onEditSpecialty = () => {};
|
||||
const onEditIntro = () => {};
|
||||
|
||||
onShow(() => {
|
||||
// 可在此处拉取并回填个人资料
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.person-page {
|
||||
background-color: #ffffff;
|
||||
height: 100vh;
|
||||
}
|
||||
.content {
|
||||
position: fixed;
|
||||
top: calc(var(--status-bar-height) + 44px);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
background-color: #d9d9d9;
|
||||
color: #8B2316;
|
||||
font-size: 28rpx;
|
||||
padding: 18rpx 24rpx;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 24rpx;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
}
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
color: #222;
|
||||
}
|
||||
.req {
|
||||
color: #e44d3a;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
.value {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.with-arrow text {
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
.value-avatar {
|
||||
gap: 14rpx;
|
||||
}
|
||||
.avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.value-license {
|
||||
gap: 14rpx;
|
||||
}
|
||||
.license-thumb {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 8rpx;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
@ -16,7 +16,7 @@
|
||||
<!-- 排序和筛选栏 -->
|
||||
<view class="filter-bar">
|
||||
<view class="filter-item" @click="toggleSort">
|
||||
<text class="filter-text">最新</text>
|
||||
<text class="filter-text">{{sort==2?'最新':'最热'}}</text>
|
||||
<view class="newbox">
|
||||
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
|
||||
</view>
|
||||
@ -104,7 +104,9 @@
|
||||
import downImg from "@/static/cb_up.png"
|
||||
import tougaoImg from "@/static/kejiantougao.png"
|
||||
import downLoadImg from "@/static/wdxz.png"
|
||||
import api from '@/api/api.js';
|
||||
const isFilterActive=ref(false)
|
||||
const sort = ref(2);
|
||||
// 响应式数据
|
||||
const refreshing = ref(false);
|
||||
const loading = ref(false);
|
||||
@ -113,44 +115,7 @@
|
||||
const pageSize = ref(10);
|
||||
|
||||
// 响应式数据
|
||||
const coursewareList = ref([
|
||||
{
|
||||
title: "颜学兵: 广东省基孔肯雅热诊疗指引",
|
||||
author: "颜学兵 徐州医科大学附属医院",
|
||||
views: 179,
|
||||
price: "10.0"
|
||||
},
|
||||
{
|
||||
title: "颜学兵: 佛山市基孔肯雅热中医诊治推荐用方",
|
||||
author: "颜学兵 徐州医科大学附属医院",
|
||||
views: 122,
|
||||
price: "10.0"
|
||||
},
|
||||
{
|
||||
title: "抗菌药物的临床应用",
|
||||
author: "肝胆相照官方账号 北京肝胆相照公益基金会",
|
||||
views: 656,
|
||||
price: "15.0"
|
||||
},
|
||||
{
|
||||
title: "中国肝癌多学科综合治疗专家共识 (2025)",
|
||||
author: "肝胆相照官方账号 北京肝胆相照公益基金会",
|
||||
views: 208,
|
||||
price: "15.0"
|
||||
},
|
||||
{
|
||||
title: "耐药结核病全口服短程治疗专家共识 (2025)",
|
||||
author: "肝胆相照官方账号 北京肝胆相照公益基金会",
|
||||
views: 256,
|
||||
price: "15.0"
|
||||
},
|
||||
{
|
||||
title: "结核分枝杆菌合并乙型肝炎病毒治专家共识",
|
||||
author: "肝胆相照官方账号 北京肝胆相照公益基金会",
|
||||
views: 358,
|
||||
price: "15.0"
|
||||
}
|
||||
]);
|
||||
const coursewareList = ref([]);
|
||||
|
||||
// 方法
|
||||
const goBack = () => {
|
||||
@ -171,10 +136,7 @@
|
||||
};
|
||||
|
||||
const toggleSort = () => {
|
||||
uni.showToast({
|
||||
title: '排序功能',
|
||||
icon: 'none'
|
||||
});
|
||||
sort.value=sort.value==1?2:1
|
||||
};
|
||||
|
||||
|
||||
@ -192,110 +154,141 @@
|
||||
icon: 'none'
|
||||
});
|
||||
};
|
||||
// 模拟API数据
|
||||
const mockApiData = [
|
||||
{
|
||||
id: 7,
|
||||
title: "肝胆疾病影像学诊断指南",
|
||||
author: "影像科专家团队 北京协和医院",
|
||||
views: 445,
|
||||
price: "20.0"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "肝硬化并发症治疗新进展",
|
||||
author: "消化内科专家 上海交通大学医学院",
|
||||
views: 332,
|
||||
price: "18.0"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "肝胆外科手术技巧精要",
|
||||
author: "外科专家 中山大学附属第一医院",
|
||||
views: 567,
|
||||
price: "25.0"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: "肝胆疾病护理规范",
|
||||
author: "护理专家团队 中国护理学会",
|
||||
views: 289,
|
||||
price: "12.0"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
// 下拉刷新
|
||||
const onRefresh = async () => {
|
||||
refreshing.value = true;
|
||||
const onRefresh = async () => {
|
||||
refreshing.value = true;
|
||||
|
||||
try {
|
||||
// 重置数据
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
|
||||
try {
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 重置数据
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
|
||||
// 重新加载第一页数据
|
||||
await loadData(true);
|
||||
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
refreshing.value = false;
|
||||
}
|
||||
};
|
||||
// 重新加载第一页数据
|
||||
await loadData(true);
|
||||
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
refreshing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 上拉加载更多
|
||||
const onLoadMore = async () => {
|
||||
console.log('加载更多')
|
||||
if (loading.value || noMore.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
|
||||
// 加载下一页数据
|
||||
await loadData(false);
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
// 上拉加载更多
|
||||
const onLoadMore = async () => {
|
||||
console.log('加载更多')
|
||||
if (loading.value || noMore.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
// 加载下一页数据
|
||||
await loadData(false);
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '加载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
const loadData = async (isRefresh = false) => {
|
||||
// 模拟API分页逻辑
|
||||
const startIndex = (page.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
const newData = mockApiData.slice(startIndex, endIndex);
|
||||
// 加载数据
|
||||
const loadData = async (isRefresh = false) => {
|
||||
if (isRefresh) {
|
||||
page.value = 1;
|
||||
coursewareList.value = [];
|
||||
noMore.value = false;
|
||||
}
|
||||
|
||||
if (loading.value || noMore.value) return;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
console.log(`开始加载第${page.value}页课件数据`);
|
||||
|
||||
if (isRefresh) {
|
||||
// 刷新时替换所有数据
|
||||
coursewareList.value = coursewareList.value.slice(0, 6); // 保留原有6条数据
|
||||
} else {
|
||||
// 加载更多时追加数据
|
||||
if (newData.length > 0) {
|
||||
coursewareList.value.push(...newData);
|
||||
page.value++;
|
||||
// 调用API获取课件列表
|
||||
const res = await api.ganDanFileByKeyWords({
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
sort: sort.value,
|
||||
// 可以根据需要添加其他筛选参数
|
||||
keywords: '',
|
||||
title: '',
|
||||
// fileType: selectedFileType.value,
|
||||
// sortBy: sortType.value
|
||||
});
|
||||
|
||||
console.log('课件列表API响应:', res);
|
||||
|
||||
if (res && res.code === 200) {
|
||||
let newItems = [];
|
||||
let totalCount = 0;
|
||||
|
||||
// 解析API返回的数据结构
|
||||
if (res.data && res.data.list) {
|
||||
newItems = res.data.list;
|
||||
totalCount = res.data.total || res.data.totalRow || 0;
|
||||
console.log('使用 res.data.list 结构');
|
||||
} else if (res.data && Array.isArray(res.data)) {
|
||||
newItems = res.data;
|
||||
totalCount = res.total || 0;
|
||||
console.log('使用 res.data 数组结构');
|
||||
} else {
|
||||
console.error('无法识别的数据结构:', res.data);
|
||||
noMore.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('解析后的数据:', { newItems, totalCount, newItemsLength: newItems.length });
|
||||
|
||||
if (Array.isArray(newItems)) {
|
||||
if (isRefresh) {
|
||||
coursewareList.value = newItems;
|
||||
console.log('刷新模式:替换列表数据');
|
||||
} else {
|
||||
coursewareList.value.push(...newItems);
|
||||
console.log('加载更多:追加数据到列表');
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
if (newItems.length < pageSize.value || coursewareList.value.length >= totalCount) {
|
||||
noMore.value = true;
|
||||
console.log('没有更多数据了');
|
||||
} else {
|
||||
page.value++;
|
||||
console.log(`还有更多数据,下一页: ${page.value}`);
|
||||
}
|
||||
} else {
|
||||
console.error('API返回的数据不是数组格式:', newItems);
|
||||
noMore.value = true;
|
||||
}
|
||||
} else {
|
||||
console.error('加载课件列表失败:', res.message);
|
||||
uni.showToast({
|
||||
title: res.message || '加载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('加载课件列表异常:', error);
|
||||
uni.showToast({
|
||||
title: '网络异常,请重试',
|
||||
icon: 'error'
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const showFilter = ref(false)
|
||||
|
||||
// 筛选弹窗相关数据
|
||||
@ -340,7 +333,9 @@
|
||||
// 这里可以根据选中的标签进行数据筛选
|
||||
};
|
||||
onShow(() => {
|
||||
// 页面显示时的逻辑
|
||||
// 页面显示时加载数据
|
||||
console.log('页面显示,开始加载课件数据');
|
||||
loadData(true);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@ -634,4 +629,39 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加载状态样式
|
||||
.loading-state,
|
||||
.empty-state,
|
||||
.load-more-state,
|
||||
.no-more-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 20rpx;
|
||||
color: $text-secondary;
|
||||
font-size: 28rpx;
|
||||
|
||||
text {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
.load-more-state {
|
||||
padding: 40rpx 20rpx;
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
.no-more-state {
|
||||
padding: 40rpx 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
147
pages_app/pptDetail/pptDetail.vue
Normal file
147
pages_app/pptDetail/pptDetail.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<view class="ppt-detail-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="课件详情"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<view class="nav-actions">
|
||||
<uni-icons type="paperplane" size="22" color="#8B2316"></uni-icons>
|
||||
<uni-icons type="heart" size="22" color="#8B2316" style="margin-left: 20rpx;"></uni-icons>
|
||||
</view>
|
||||
</template>
|
||||
</uni-nav-bar>
|
||||
|
||||
<!-- 下载提示条 -->
|
||||
<view class="download-bar">
|
||||
<view class="download-inner">
|
||||
<u-icon name="download" color="#fff" size="28"></u-icon>
|
||||
<text class="download-text">本课件下载</text>
|
||||
<text class="download-price">{{ price }}</text>
|
||||
<text class="download-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 图片浏览 -->
|
||||
<view class="viewer">
|
||||
<swiper
|
||||
class="ppt-swiper"
|
||||
:indicator-dots="false"
|
||||
:circular="true"
|
||||
:autoplay="false"
|
||||
@change="onSwiperChange"
|
||||
>
|
||||
<swiper-item v-for="(img, idx) in images" :key="idx">
|
||||
<view class="slide">
|
||||
<image :src="img" mode="widthFix" class="slide-image" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 页码指示 -->
|
||||
<view class="page-indicator">{{ currentIndex + 1 }}/{{ images.length }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
|
||||
const images = ref([
|
||||
'/static/big_background_my.png',
|
||||
'/static/big_background_my.png',
|
||||
'/static/big_background_my.png'
|
||||
]);
|
||||
const currentIndex = ref(1); // 对应截图显示 2/23 的第二页效果
|
||||
const price = ref('1.0');
|
||||
|
||||
const onSwiperChange = (e) => {
|
||||
currentIndex.value = e.detail.current;
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
// 可在此根据路由参数拉取课件详情与图片列表
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ppt-detail-page {
|
||||
background-color: #ffffff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.download-bar {
|
||||
position: sticky;
|
||||
top: calc(var(--status-bar-height) + 44px);
|
||||
z-index: 9;
|
||||
width: 100%;
|
||||
background-color: #6F6F6F; // 接近截图灰条
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.download-inner {
|
||||
padding: 0 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
}
|
||||
.download-text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.download-price {
|
||||
margin-left: 8rpx;
|
||||
color: #ff3b30; // 红色价格
|
||||
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.download-unit {
|
||||
margin-left: 4rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.viewer {
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.ppt-swiper {
|
||||
width: 100%;
|
||||
min-height: 400rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.slide {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.slide-image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page-indicator {
|
||||
margin-top: 20rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@ -6,8 +6,9 @@
|
||||
|
||||
<!-- Fixed Banner Swiper -->
|
||||
<view class="banner-container">
|
||||
<view class="swipemask">
|
||||
<view class="banner-subtitle">111111111</view>
|
||||
|
||||
<view class="swipemask" >
|
||||
<view class="banner-subtitle" v-if="bannerList.length>0">{{bannerList[currentBannerIndex].name}}</view>
|
||||
<view class="dotbox">
|
||||
<view class="circle" :class="{active:currentBannerIndex==index} "v-for="(banner, index) in bannerList"
|
||||
:key="banner.id"></view>
|
||||
@ -33,18 +34,10 @@
|
||||
<view class="banner-item">
|
||||
<image
|
||||
class="banner-image"
|
||||
:src="banner.thumbnail || '/static/big_background_my.png'"
|
||||
:src="docUrl+banner.imgpath"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="banner-content">
|
||||
<view class="doctor-avatar" v-if="banner.doctorAvatar">
|
||||
<image :src="banner.doctorAvatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="banner-info">
|
||||
<view class="banner-title">{{banner.title}}</view>
|
||||
<view class="banner-subtitle">{{banner.subtitle || '专业医学视频教育平台'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
@ -57,25 +50,25 @@
|
||||
class="tab-item"
|
||||
@click="showAllVideoPopup=!showAllVideoPopup"
|
||||
>
|
||||
<up-image :src="allImg" width="30rpx" height="30rpx" ></up-image>
|
||||
<text class="tab-text">全部视频</text>
|
||||
<up-image :src="selectImg" width="30rpx" height="30rpx" ></up-image>
|
||||
<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">最新</text>
|
||||
<text class="tab-text active">{{sort==2?'最新':'最热'}}</text>
|
||||
<view class="newbox">
|
||||
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
|
||||
<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">筛选</text>
|
||||
<text class="tab-text " :class="{active:isFilterActive}">筛选</text>
|
||||
<view class="filterbox">
|
||||
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
|
||||
</view>
|
||||
@ -100,22 +93,22 @@
|
||||
<view
|
||||
class="video-item"
|
||||
v-for="(video, index) in videoList"
|
||||
:key="video.id"
|
||||
:key="video.id || video.uuid"
|
||||
@click="playVideo(video)"
|
||||
>
|
||||
<view class="video-thumbnail">
|
||||
<image :src="video.thumbnail" mode="aspectFill"></image>
|
||||
|
||||
<image :src="docUrl + video.imgpath" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="video-info">
|
||||
<view class="video-title">{{video.title}}</view>
|
||||
<view class="video-title">{{video.title || video.name}}</view>
|
||||
<view class="video-meta">
|
||||
<text class="author">{{video.author}}</text>
|
||||
<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.viewCount}}</text>
|
||||
<text class="view-count">{{video.readnum}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -137,7 +130,7 @@
|
||||
<text>暂无视频内容</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="btnbox">
|
||||
<view class="btnbox" @click="goPatientVideo">
|
||||
<up-image :src="videoImg" width="44rpx" height="44rpx" ></up-image>
|
||||
患教视频
|
||||
|
||||
@ -154,7 +147,7 @@
|
||||
:class="{ active: tag.selected }"
|
||||
@click="toggleTag(index)"
|
||||
>
|
||||
{{ tag.name }}
|
||||
{{ tag.DES }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -180,18 +173,28 @@
|
||||
:key="category.value"
|
||||
@click="selectCategory(category.value)"
|
||||
>
|
||||
{{ category.label }}
|
||||
<view class="category-label">{{ category.label }}</view>
|
||||
<view class="category-date" v-if="category.value !== '全部' && category.originalDate">
|
||||
{{ formatDate(category.originalDate) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-area">
|
||||
<view class="content-list">
|
||||
|
||||
|
||||
<view
|
||||
class="content-item"
|
||||
|
||||
v-for="item in filteredContent"
|
||||
:key="item.id"
|
||||
:key="item.uuid"
|
||||
:class="{active:selectYearContent.uuid==item.uuid}"
|
||||
@click="selectContent(item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
{{item.name }}
|
||||
</view>
|
||||
<view v-if="filteredContent.length === 0" class="empty-content">
|
||||
<text>该分类暂无内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -202,7 +205,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
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"
|
||||
@ -212,8 +215,11 @@
|
||||
import filter from "@/static/cb_screen_no.png"
|
||||
import filterOn from "@/static/cb_screen_yes.png"
|
||||
import upImg from "@/static/cb_up.png"
|
||||
import downImg from "@/static/cb_up.png"
|
||||
import downImg from "@/static/cb_down.png"
|
||||
import videoImg from "@/static/patient_video.png"
|
||||
import docUrl from '@/utils/docUrl';
|
||||
import navTo from '@/utils/navTo';
|
||||
const isAllActive=ref(false)
|
||||
// 响应式数据
|
||||
const videoList = ref([]);
|
||||
const bannerVideo = ref(null);
|
||||
@ -224,13 +230,18 @@
|
||||
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('');
|
||||
const typeName=ref('全部视频')
|
||||
// Banner相关数据
|
||||
const bannerList = ref([]);
|
||||
const currentBannerIndex = ref(0);
|
||||
const isFilterActive=ref(false)
|
||||
const showFilter = ref(false);
|
||||
|
||||
const yearList=ref([]);
|
||||
const selectYearContent=reactive({})
|
||||
// 筛选弹窗相关数据
|
||||
const filterTags = ref([
|
||||
{ name: '指南解读', selected: false },
|
||||
@ -249,65 +260,171 @@
|
||||
|
||||
// 全部视频弹窗相关数据
|
||||
const showAllVideoPopup = ref(false);
|
||||
const selectedCategory = ref('all');
|
||||
const selectedCategory = ref('全部');
|
||||
const categoryList = ref([
|
||||
{ value: 'all', label: '全部' },
|
||||
{ value: 'guide', label: '指南解读' },
|
||||
{ value: 'case', label: '病例分析' },
|
||||
{ value: 'lecture', label: '学术讲座' },
|
||||
{ value: 'surgery', label: '手术视频' },
|
||||
{ value: 'research', label: '研究进展' }
|
||||
{ value: 'all', label: '全部' }
|
||||
]);
|
||||
|
||||
// 分类内容数据
|
||||
// 分类内容数据 - 将根据API返回的类型动态生成
|
||||
const contentByCategory = ref({
|
||||
all: [
|
||||
{ id: 1, title: '《2025年版慢加急性肝衰竭指南》解读' },
|
||||
{ id: 2, title: '自身免疫性肝病专栏|免疫治疗的双刃剑' },
|
||||
{ id: 3, title: '徐医感染:硬化出血发热路,关关难过关关过' },
|
||||
{ id: 4, title: '徐医感染:一场呼吸的迷局' },
|
||||
{ id: 5, title: '南京市第二医院疑难肝病病理读片会' }
|
||||
],
|
||||
guide: [
|
||||
{ id: 6, title: '《2025年版慢加急性肝衰竭指南》解读' },
|
||||
{ id: 7, title: '《原发性肝癌诊疗指南》更新要点' },
|
||||
{ id: 8, title: '《慢性乙型肝炎防治指南》解读' }
|
||||
],
|
||||
case: [
|
||||
{ id: 9, title: '自身免疫性肝病专栏|免疫治疗的双刃剑' },
|
||||
{ id: 10, title: '徐医感染:硬化出血发热路,关关难过关关过' },
|
||||
{ id: 11, title: '徐医感染:一场呼吸的迷局' }
|
||||
],
|
||||
lecture: [
|
||||
{ id: 12, title: '南京市第二医院疑难肝病病理读片会' },
|
||||
{ id: 13, title: '肝胆胰外科手术技巧分享' },
|
||||
{ id: 14, title: '肝移植围手术期管理' }
|
||||
],
|
||||
surgery: [
|
||||
{ id: 15, title: '腹腔镜肝切除术操作要点' },
|
||||
{ id: 16, title: '机器人辅助肝切除术' },
|
||||
{ id: 17, title: '肝移植手术技术进展' }
|
||||
],
|
||||
research: [
|
||||
{ id: 18, title: '肝癌免疫治疗最新研究进展' },
|
||||
{ id: 19, title: '肝纤维化分子机制研究' },
|
||||
{ id: 20, title: '肝细胞再生与修复研究' }
|
||||
]
|
||||
all: []
|
||||
});
|
||||
|
||||
// 存储API返回的原始类型数据
|
||||
const videoTypesData = ref([]);
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 使用模拟数据初始化列表
|
||||
// 调用真实API获取数据
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
loadMockVideoData(true);
|
||||
// 初始化banner数据
|
||||
bannerList.value = getMockBannerData();
|
||||
loadVideoData(true);
|
||||
loadBannerData();
|
||||
loadVideoTags();
|
||||
loadVideoTypes();
|
||||
|
||||
// 延迟加载"全部"分类的视频数据,确保类型数据先加载完成
|
||||
setTimeout(() => {
|
||||
loadCategoryVideos('all');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 加载轮播数据
|
||||
const loadBannerData = async () => {
|
||||
try {
|
||||
const response = await api.videoRoll({});
|
||||
console.log('轮播API原始响应:', response);
|
||||
bannerList.value=response.data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载轮播数据失败:', error);
|
||||
// 使用模拟数据作为备用
|
||||
bannerList.value = getMockBannerData();
|
||||
}
|
||||
};
|
||||
|
||||
// 加载视频标签
|
||||
const loadVideoTags = async () => {
|
||||
try {
|
||||
const response = await api.videoTagList({
|
||||
type:2
|
||||
});
|
||||
console.log('标签API原始响应:', response);
|
||||
|
||||
if(response.code==200){
|
||||
filterTags.value=response.data;
|
||||
console.log(22222)
|
||||
console.log(filterTags.value)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载视频标签失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载视频类型
|
||||
const loadVideoTypes = async () => {
|
||||
try {
|
||||
const response = await api.expertVideoTypeList({
|
||||
|
||||
});
|
||||
console.log('类型API原始响应:', response);
|
||||
if(response.code==200){
|
||||
yearList.value=response.data;
|
||||
filteredContent.value=(response.data[0]).list;
|
||||
console.log(33)
|
||||
let arr=response.data.map(item=>{
|
||||
return {
|
||||
value:item.name,
|
||||
label:item.name
|
||||
}
|
||||
})
|
||||
categoryList.value=arr
|
||||
}
|
||||
console.log(2222)
|
||||
console.log(categoryList.value)
|
||||
} catch (error) {
|
||||
console.error('加载视频类型失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载分类视频数据
|
||||
const loadCategoryVideos = async (categoryValue) => {
|
||||
try {
|
||||
console.log('加载分类视频:', categoryValue);
|
||||
|
||||
let response;
|
||||
if (categoryValue === '全部') {
|
||||
// 加载所有视频
|
||||
response = await api.videoByKeyWordsNew({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
keywords: '',
|
||||
sort: 0,
|
||||
typeUuid: '',
|
||||
tags: ''
|
||||
});
|
||||
} else {
|
||||
// 根据日期分类加载对应的视频类型数据
|
||||
// 找到该日期对应的视频类型
|
||||
const typesForDate = videoTypesData.value.filter(type => type.create_date === categoryValue);
|
||||
console.log(`日期 ${categoryValue} 对应的视频类型:`, typesForDate);
|
||||
|
||||
if (typesForDate.length > 0) {
|
||||
// 获取这些类型的UUID列表
|
||||
const typeUuids = typesForDate.map(type => type.id || type.uuid || type.value).join(',');
|
||||
console.log('类型UUID列表:', typeUuids);
|
||||
|
||||
// 加载特定类型的视频
|
||||
response = await api.videoByKeyWordsNew({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
keywords: '',
|
||||
sort: 0,
|
||||
typeUuid: typeUuids,
|
||||
tags: ''
|
||||
});
|
||||
} else {
|
||||
console.warn(`未找到日期 ${categoryValue} 对应的视频类型`);
|
||||
contentByCategory.value[categoryValue] = [];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 } = videoData;
|
||||
// 更新对应分类的内容
|
||||
contentByCategory.value[categoryValue] = list.map(video => ({
|
||||
id: video.id || video.uuid,
|
||||
title: video.title || video.name,
|
||||
name: video.name || video.title
|
||||
}));
|
||||
|
||||
console.log(`分类 ${categoryValue} 的内容:`, contentByCategory.value[categoryValue]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分类视频失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载视频数据
|
||||
const loadVideoData = async (isRefresh = false) => {
|
||||
if (loading.value && !isRefresh) return;
|
||||
@ -316,58 +433,60 @@
|
||||
|
||||
try {
|
||||
// 调用真实API
|
||||
const response = await api.getVideoList({
|
||||
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.id).join(',');
|
||||
console.log('请求参数:', {
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
category: activeTab.value // 根据选中的标签获取不同类别的视频
|
||||
keywords: keywords.value,
|
||||
sort: sort.value,
|
||||
typeUuid:typeUuid.value,
|
||||
tags: selectedTags
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
const { list, hasMore } = response.data.data;
|
||||
const response = await api.videoByKeyWordsNew({
|
||||
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, hasMore, total } = videoData;
|
||||
console.log('视频列表数据:', list);
|
||||
|
||||
if (isRefresh) {
|
||||
videoList.value = list;
|
||||
videoList.value = list || [];
|
||||
currentPage.value = 1;
|
||||
} else {
|
||||
videoList.value = [...videoList.value, ...list];
|
||||
videoList.value = [...videoList.value, ...(list || [])];
|
||||
}
|
||||
|
||||
// 设置banner视频(第一个视频)
|
||||
if (isRefresh || currentPage.value === 1) {
|
||||
bannerVideo.value = list[0];
|
||||
}
|
||||
|
||||
hasMoreData.value = hasMore;
|
||||
hasMoreData.value = hasMore !== false;
|
||||
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
|
||||
} else {
|
||||
throw new Error(response.data.message || '获取数据失败');
|
||||
throw new Error(response?.message || '获取数据失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载视频失败:', error);
|
||||
|
||||
// 如果API失败,使用模拟数据作为备用方案
|
||||
const mockData = await getMockVideoData(currentPage.value, pageSize.value);
|
||||
|
||||
if (isRefresh) {
|
||||
videoList.value = mockData.list;
|
||||
currentPage.value = 1;
|
||||
} else {
|
||||
videoList.value = [...videoList.value, ...mockData.list];
|
||||
}
|
||||
|
||||
// 设置banner视频(第一个视频)
|
||||
if (isRefresh || currentPage.value === 1) {
|
||||
bannerVideo.value = mockData.list[0];
|
||||
}
|
||||
|
||||
hasMoreData.value = mockData.hasMore;
|
||||
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
|
||||
|
||||
uni.showToast({
|
||||
title: '网络连接异常,显示离线数据',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
refreshing.value = false;
|
||||
@ -399,7 +518,7 @@
|
||||
refreshing.value = true;
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
loadMockVideoData(true);
|
||||
loadVideoData(true);
|
||||
};
|
||||
|
||||
// 上拉加载更多
|
||||
@ -408,7 +527,7 @@
|
||||
|
||||
loadMoreStatus.value = 'loading';
|
||||
currentPage.value++;
|
||||
loadMockVideoData();
|
||||
loadVideoData();
|
||||
};
|
||||
|
||||
// 切换标签
|
||||
@ -417,13 +536,15 @@
|
||||
// 可以根据标签加载不同的数据
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
loadMockVideoData(true);
|
||||
loadVideoData(true);
|
||||
};
|
||||
|
||||
// 播放视频
|
||||
const playVideo = (video) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/videoDetail/videoDetail?id=${video.id}`
|
||||
const videoId = video.id || video.uuid;
|
||||
|
||||
navTo({
|
||||
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`
|
||||
});
|
||||
};
|
||||
|
||||
@ -436,6 +557,61 @@
|
||||
});
|
||||
};
|
||||
|
||||
// 跳转到患教视频列表
|
||||
const goPatientVideo = () => {
|
||||
navTo({
|
||||
url: '/pages_app/patientVideo/patientVideo'
|
||||
});
|
||||
};
|
||||
|
||||
// 搜索视频
|
||||
const searchVideos = async (keywords) => {
|
||||
if (!keywords || keywords.trim() === '') {
|
||||
loadVideoData(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('搜索关键词:', keywords.trim());
|
||||
const response = await api.videoByKeyWordsNew({
|
||||
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'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Banner相关方法
|
||||
const getMockBannerData = () => {
|
||||
return [
|
||||
@ -468,8 +644,10 @@
|
||||
};
|
||||
|
||||
const playBannerVideo = (banner) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/videoDetail/videoDetail?id=${banner.id}`
|
||||
const videoId = banner.id || banner.uuid;
|
||||
|
||||
navTo({
|
||||
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`
|
||||
});
|
||||
};
|
||||
|
||||
@ -481,23 +659,35 @@
|
||||
const closeAllVideoPopup = () => {
|
||||
showAllVideoPopup.value = false;
|
||||
};
|
||||
|
||||
const selectCategory = (categoryValue) => {
|
||||
const selectCategory = async (categoryValue) => {
|
||||
selectedCategory.value = categoryValue;
|
||||
console.log(yearList.value)
|
||||
console.log('选择分类:', categoryValue);
|
||||
for (var i = 0; i < yearList.value.length; i++) {
|
||||
|
||||
if(categoryValue==yearList.value[i].name){
|
||||
console.log(yearList.value[i]);
|
||||
filteredContent.value=yearList.value[i].list;
|
||||
console.log(filteredContent.value)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const selectContent = (content) => {
|
||||
// 这里可以根据选中的内容进行相应的操作
|
||||
console.log('选中内容:', content);
|
||||
closeAllVideoPopup();
|
||||
// 可以跳转到对应的视频页面或执行其他操作
|
||||
Object.assign(selectYearContent,content);
|
||||
typeUuid.value=content.uuid;
|
||||
typeName.value=content.name;
|
||||
isAllActive.value=true;
|
||||
|
||||
};
|
||||
|
||||
// 计算属性:根据选中的分类过滤内容
|
||||
const filteredContent = computed(() => {
|
||||
return contentByCategory.value[selectedCategory.value] || [];
|
||||
});
|
||||
|
||||
|
||||
// 筛选弹窗相关方法
|
||||
const showFilterPopup = () => {
|
||||
showFilter.value = true;
|
||||
@ -513,15 +703,37 @@
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
// 清空所有已选标签
|
||||
filterTags.value.forEach(tag => tag.selected = false);
|
||||
// 关闭筛选激活态
|
||||
isFilterActive.value = 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].DES
|
||||
}else{
|
||||
keywords.value=selectedTags[i].DES
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
isFilterActive.value=true;
|
||||
hideFilterPopup();
|
||||
// 这里可以根据选中的标签进行数据筛选
|
||||
// 根据选中的标签重新加载数据
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
loadVideoData(true);
|
||||
};
|
||||
|
||||
// 模拟数据 - 实际开发时替换为真实API
|
||||
@ -565,6 +777,13 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
const toggleSort = () => {
|
||||
sort.value = sort.value === 1 ? 2 : 1; // 1=最新, 2=最热(与后端约定)
|
||||
currentPage.value = 1;
|
||||
hasMoreData.value = true;
|
||||
loadVideoData(true);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -697,11 +916,16 @@
|
||||
color: #666;
|
||||
}
|
||||
.tab-item:first-child .tab-text{
|
||||
max-width:600rpx;
|
||||
max-width:295rpx;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tab-item .tab-text.active {
|
||||
color: #8B2316;
|
||||
|
||||
}
|
||||
/* Fixed Banner Container */
|
||||
.banner-container {
|
||||
position: fixed;
|
||||
@ -805,6 +1029,9 @@
|
||||
.banner-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.8);
|
||||
white-space: nowrap; /* 单行显示 */
|
||||
overflow: hidden; /* 隐藏溢出 */
|
||||
text-overflow: ellipsis; /* 超出显示省略号 */
|
||||
}
|
||||
|
||||
/* Video List */
|
||||
@ -837,18 +1064,15 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.play-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: rgba(0,0,0,0.6);
|
||||
border-radius: 50%;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
.video-duration {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.video-duration text {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
@ -857,7 +1081,7 @@
|
||||
|
||||
.video-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 16rpx;
|
||||
@ -866,6 +1090,7 @@
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
overflow: hidden;
|
||||
height: 2.8em; /* 固定2行高度:1.4 * 2 = 2.8em */
|
||||
}
|
||||
|
||||
.video-meta {
|
||||
@ -988,6 +1213,17 @@
|
||||
|
||||
}
|
||||
|
||||
.category-label {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.category-date {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
@ -1011,13 +1247,20 @@
|
||||
border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.content-item:hover {
|
||||
background-color: #e8f4fd;
|
||||
border-color: #4A90E2;
|
||||
|
||||
|
||||
.content-item.active {
|
||||
|
||||
color: #8B2316;
|
||||
}
|
||||
|
||||
.content-item:active {
|
||||
transform: scale(0.98);
|
||||
.empty-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200rpx;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
// 筛选弹窗样式
|
||||
.filter-popup {
|
||||
@ -1033,8 +1276,8 @@
|
||||
z-index: 9999;
|
||||
|
||||
.filter-content {
|
||||
margin-top:329rpx;
|
||||
|
||||
margin-top:660rpx;
|
||||
overflow-y:scroll;
|
||||
background-color: $white;
|
||||
// border-radius: 20rpx 20rpx 0 0;
|
||||
padding: 20rpx 30rpx 60rpx;
|
||||
@ -1052,11 +1295,13 @@
|
||||
.tag-item {
|
||||
background-color: #f8f8f8;
|
||||
color: $gray-dark;
|
||||
padding: 8rpx 24rpx;
|
||||
padding: 8rpx 0; /* 去掉左右padding,仅保留上下 */
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
border: 2rpx solid #efefef;
|
||||
transition: all 0.3s ease;
|
||||
width: 152rpx; /* 固定宽度 */
|
||||
text-align: center; /* 文本居中 */
|
||||
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
@ -1073,6 +1318,7 @@
|
||||
bottom: 30rpx;
|
||||
left: 30rpx;
|
||||
right: 30rpx;
|
||||
background-color: #fff; /* 背景色为白色 */
|
||||
|
||||
.btn-reset,
|
||||
.btn-confirm {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
370
pages_app/zhinanList/zhinanList.vue
Normal file
370
pages_app/zhinanList/zhinanList.vue
Normal file
@ -0,0 +1,370 @@
|
||||
<template>
|
||||
<view class="zhinan-list-page">
|
||||
<!-- 头部导航栏 -->
|
||||
<uni-nav-bar
|
||||
left-icon="left"
|
||||
title="诊疗指南"
|
||||
@clickLeft="goBack"
|
||||
fixed
|
||||
color="#8B2316"
|
||||
height="140rpx"
|
||||
:border="false"
|
||||
backgroundColor="#eeeeee"
|
||||
></uni-nav-bar>
|
||||
|
||||
<!-- 指南列表 -->
|
||||
<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
|
||||
v-if="item.can_download"
|
||||
class="download-btn"
|
||||
@click="downloadGuideline(item)"
|
||||
>
|
||||
<up-icon name="download" color="#8D2316" size="28"></up-icon>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="view-btn"
|
||||
@click="viewGuideline(item)"
|
||||
>
|
||||
查看
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-status">
|
||||
<view v-if="isLoading && currentPage === 1" class="loading">
|
||||
<uni-load-more status="loading" :content-text="loadingText"></uni-load-more>
|
||||
</view>
|
||||
<view v-else-if="hasMoreData && !isLoading" class="load-more" @click="loadMoreData">
|
||||
<uni-load-more status="more" :content-text="loadingText"></uni-load-more>
|
||||
</view>
|
||||
<view v-else-if="!hasMoreData && guidelinesList.length > 0" class="no-more">
|
||||
<uni-load-more status="noMore" :content-text="loadingText"></uni-load-more>
|
||||
</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>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted} from 'vue'
|
||||
import api from '@/api/api.js'
|
||||
import { onShow, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
|
||||
|
||||
// 响应式数据
|
||||
const guidelinesList = ref([])
|
||||
const isLoading = ref(false)
|
||||
const hasMoreData = ref(true)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const isRefreshing = ref(false)
|
||||
|
||||
// 加载文本配置
|
||||
const loadingText = {
|
||||
contentdown: '上拉显示更多',
|
||||
contentrefresh: '正在加载...',
|
||||
contentnomore: '没有更多数据了'
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
onMounted(() => {
|
||||
loadGuidelinesList()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 页面显示时可以刷新数据
|
||||
loadGuidelinesList()
|
||||
})
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh(() => {
|
||||
refreshData()
|
||||
})
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom(() => {
|
||||
if (hasMoreData.value && !isLoading.value) {
|
||||
loadMoreData()
|
||||
}
|
||||
})
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = async () => {
|
||||
if (isRefreshing.value) return
|
||||
|
||||
try {
|
||||
isRefreshing.value = true
|
||||
currentPage.value = 1
|
||||
hasMoreData.value = true
|
||||
|
||||
await loadGuidelinesList()
|
||||
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('刷新失败:', error)
|
||||
uni.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
uni.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载指南列表
|
||||
const loadGuidelinesList = async (isLoadMore = false) => {
|
||||
if (isLoading.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
|
||||
const params = {
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
type: 1, // 诊疗指南类型
|
||||
keywords: ''
|
||||
}
|
||||
|
||||
const res = await api.searchLibraryU(params)
|
||||
console.log('指南列表响应:', res)
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
const newData = res.data
|
||||
|
||||
if (isLoadMore) {
|
||||
guidelinesList.value = [...guidelinesList.value, ...newData]
|
||||
} else {
|
||||
guidelinesList.value = newData
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
hasMoreData.value = newData.length === pageSize.value
|
||||
} else {
|
||||
console.error('加载指南列表失败:', res.message)
|
||||
uni.showToast({
|
||||
title: res.message || '加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载指南列表异常:', error)
|
||||
uni.showToast({
|
||||
title: '网络异常,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多数据
|
||||
const loadMoreData = () => {
|
||||
if (hasMoreData.value && !isLoading.value) {
|
||||
currentPage.value++
|
||||
loadGuidelinesList(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 下载指南
|
||||
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'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 查看指南
|
||||
const viewGuideline = (item) => {
|
||||
console.log('查看指南:', item)
|
||||
// 跳转到指南详情页或预览页
|
||||
uni.navigateTo({
|
||||
url: `/pages_app/zhinanDetail/zhinanDetail?uuid=${item.uuid}&title=${encodeURIComponent(item.title)}`
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
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;
|
||||
|
||||
.zhinan-list-page {
|
||||
background-color: $bg-color;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 指南列表
|
||||
.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;
|
||||
}
|
||||
|
||||
.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%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
.loading-status {
|
||||
padding: 20rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.loading, .load-more, .no-more {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 无数据提示
|
||||
.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>
|
||||
BIN
static/xingxing1.png
Normal file
BIN
static/xingxing1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/xingxing2.png
Normal file
BIN
static/xingxing2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@ -54,6 +54,7 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
|
||||
version: '4.0.0',
|
||||
user_uuid:userInfo.uuid,
|
||||
client_type: 'A', //client_type,
|
||||
timestamp:new Date().getTime()
|
||||
}
|
||||
postData={
|
||||
...data,
|
||||
@ -65,16 +66,13 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
|
||||
if(token){
|
||||
header['Authorization']='Bearer ' +token;
|
||||
let userInfo= uni.getStorageSync('userInfo')
|
||||
defaultData = {
|
||||
defaultData = {
|
||||
version: '4.0.0',
|
||||
user_uuid:userInfo.uuid,
|
||||
client_type: 'A', //client_type,
|
||||
timestamp:new Date().getTime()
|
||||
}
|
||||
defaultData = {
|
||||
version: '4.0.0',
|
||||
user_uuid:userInfo.uuid,
|
||||
client_type: 'A', //client_type,
|
||||
}
|
||||
|
||||
postData={
|
||||
...data,
|
||||
...defaultData
|
||||
@ -87,7 +85,7 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
|
||||
return new Promise(function(e, n) {
|
||||
let timestamp = Date.now();
|
||||
uni.request({
|
||||
data: {...data,...defaultData},
|
||||
data: postData,
|
||||
url: url.indexOf('http') != -1 ? url : encodeURI(BASE_URL + url),
|
||||
method: method,
|
||||
sslVerify: false,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user