669 lines
14 KiB
Vue
669 lines
14 KiB
Vue
<template>
|
||
<navBar title="患教学堂"></navBar>
|
||
<view class="page">
|
||
<view class="top">
|
||
<!-- 筛选栏 -->
|
||
<view class="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="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" :class="{ active: isFilterActive }" @click="showFilterPopup">
|
||
<text>筛选</text>
|
||
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
|
||
</view> -->
|
||
</view>
|
||
|
||
</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.DES }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部按钮 -->
|
||
<view class="filter-buttons">
|
||
<view class="btn-reset" @click="resetFilter">重置</view>
|
||
<view class="btn-confirm" @click="confirmFilter">确定</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 文章列表 -->
|
||
<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>
|
||
<view class="article-meta">
|
||
<view class="date-tag" v-if="article.isToday">今日</view>
|
||
<view class="date" v-else>{{ article.date }}</view>
|
||
<view class="stats">
|
||
<view class="stat-item">
|
||
<uni-icons type="eye" size="12" color="#999"></uni-icons>
|
||
<text>{{ article.views }}</text>
|
||
</view>
|
||
<view class="stat-item">
|
||
<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>
|
||
|
||
<!-- 底部导航栏 -->
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import { onShow } from "@dcloudio/uni-app";
|
||
import upImg from "@/static/cb_up.png"
|
||
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';
|
||
import navBar from '@/components/navBar/navBar.vue';
|
||
|
||
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 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) => {
|
||
|
||
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);
|
||
// 跳转到详情页
|
||
uni.$emit('articelItem', article);
|
||
uni.navigateBack();
|
||
};
|
||
|
||
// 跳转到搜索页面
|
||
const goToSearch = () => {
|
||
navTo({
|
||
url: '/pages_chat/searchArticle/searchArticle'
|
||
});
|
||
};
|
||
|
||
// 下拉刷新
|
||
const onRefresh = async () => {
|
||
refreshing.value = true;
|
||
await polularScienceArticleListByKeywordsNew(true);
|
||
uni.showToast({
|
||
title: '刷新成功',
|
||
icon: 'none',
|
||
duration: 1500
|
||
});
|
||
};
|
||
|
||
// 上拉加载更多
|
||
const onLoadMore = async () => {
|
||
if (!hasMore.value || loading.value) return;
|
||
await polularScienceArticleListByKeywordsNew(false);
|
||
};
|
||
|
||
const goToPage = (page) => {
|
||
console.log('跳转到页面:', page);
|
||
// 根据页面名称进行跳转
|
||
if (page === 'index') {
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
});
|
||
}
|
||
};
|
||
|
||
// 筛选相关方法
|
||
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;
|
||
});
|
||
isFilterActive.value = false;
|
||
};
|
||
const keywords=ref('');
|
||
const confirmFilter = () => {
|
||
// 检查是否有选中的标签
|
||
const hasSelected = filterTags.value.some(tag => tag.selected);
|
||
isFilterActive.value = hasSelected;
|
||
|
||
// 执行筛选逻辑
|
||
if (hasSelected) {
|
||
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(() => {
|
||
polularScienceArticleListByKeywordsNew(true);
|
||
loadGuideTags();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
// 颜色变量
|
||
$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;
|
||
|
||
.page {
|
||
background-color: $gray-bg;
|
||
height: calc(100vh - 180rpx);
|
||
display: flex;
|
||
overflow-y: hidden;
|
||
flex-direction: column;
|
||
// 为固定导航栏留出空间
|
||
}
|
||
.top{
|
||
width:100%;
|
||
position: fixed;
|
||
top:140rpx;
|
||
}
|
||
// 选项卡
|
||
.tabs {
|
||
background-color: $white;
|
||
display: flex;
|
||
border-bottom: 1px solid $gray-light;
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: $padding 0;
|
||
font-size: 16px;
|
||
color: $gray-dark;
|
||
position: relative;
|
||
|
||
&.active {
|
||
color: $theme-color;
|
||
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 30px;
|
||
height: 2px;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 筛选栏
|
||
.filter-bar {
|
||
background-color: $white;
|
||
padding: $padding-small $padding;
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 1px solid $gray-light;
|
||
|
||
.search-box {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 5px;
|
||
|
||
.search-text {
|
||
font-size: 14px;
|
||
color: $gray-medium;
|
||
}
|
||
}
|
||
|
||
.divider {
|
||
width: 1px;
|
||
height: 16px;
|
||
background-color: $gray-medium;
|
||
margin: 0 $padding;
|
||
}
|
||
|
||
.filter-item {
|
||
flex:1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 3px;
|
||
font-size: 14px;
|
||
color: #999;
|
||
transition: all 0.3s ease;
|
||
}
|
||
}
|
||
.filter-item.active{
|
||
color: $theme-color;
|
||
}
|
||
|
||
|
||
// 文章列表
|
||
.article-list {
|
||
position: fixed;
|
||
bottom: 0;
|
||
top:240rpx;
|
||
padding-bottom: 0rpx;
|
||
.article-item {
|
||
background-color: $white;
|
||
margin-bottom: $padding-small;
|
||
border-radius: $border-radius;
|
||
padding: $padding;
|
||
display: flex;
|
||
gap: 12px;
|
||
|
||
.article-image {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: $border-radius-small;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.article-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
|
||
.article-title {
|
||
font-size: 16px;
|
||
color: $text-color;
|
||
line-height: 1.4;
|
||
margin-bottom: $padding-small;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.article-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.date-tag {
|
||
background-color: $primary-color;
|
||
color: $white;
|
||
font-size: 12px;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.date {
|
||
font-size: 12px;
|
||
color: $gray-medium;
|
||
}
|
||
|
||
.stats {
|
||
display: flex;
|
||
gap: $padding;
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
font-size: 12px;
|
||
color: $gray-medium;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载状态样式
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部导航栏
|
||
.bottom-nav {
|
||
background-color: $white;
|
||
display: flex;
|
||
border-top: 1px solid $gray-light;
|
||
padding: 8px 0;
|
||
|
||
.nav-item {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
font-size: 12px;
|
||
color: $gray-medium;
|
||
|
||
&.active {
|
||
color: $primary-color;
|
||
}
|
||
|
||
text {
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 筛选弹窗样式
|
||
.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:329rpx;
|
||
|
||
background-color: $white;
|
||
// border-radius: 20rpx 20rpx 0 0;
|
||
padding: 20rpx 30rpx 60rpx;
|
||
width: 100%;
|
||
max-height: 80vh;
|
||
|
||
.filter-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
margin-bottom: 60rpx;
|
||
max-height: 65vh;
|
||
overflow-y: auto;
|
||
|
||
.tag-item {
|
||
background-color: #f8f8f8;
|
||
color: $gray-dark;
|
||
padding: 8rpx 24rpx;
|
||
border-radius: 30rpx;
|
||
font-size: 26rpx;
|
||
border: 2rpx solid #efefef;
|
||
transition: all 0.3s ease;
|
||
|
||
&.active {
|
||
background-color: #fff;
|
||
color: $theme-color;
|
||
border-color: $theme-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-buttons {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
position: fixed;
|
||
bottom: 30rpx;
|
||
left: 30rpx;
|
||
right: 30rpx;
|
||
|
||
.btn-reset,
|
||
.btn-confirm {
|
||
flex: 1;
|
||
height: 70rpx;
|
||
border-radius: 14rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.btn-reset {
|
||
background-color: $white;
|
||
color: $theme-color;
|
||
border: 2rpx solid $theme-color;
|
||
}
|
||
|
||
.btn-confirm {
|
||
border: 2rpx solid $theme-color;
|
||
background-color: $theme-color;
|
||
color: $white;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|