uniapp-app/pages_chat/searchArticle/searchArticle.vue
2025-10-14 17:46:23 +08:00

763 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<view class="search-header">
<view class="back-icon" @click="goBack">
<uni-icons type="left" size="30" color="#8B2316"></uni-icons>
</view>
<view class="search-input-wrapper">
<view class="search-icon">
<uni-icons type="search" size="20" color="#999"></uni-icons>
</view>
<input
class="search-input"
placeholder="请输入搜索内容"
v-model="keywords"
@input="onSearchInput"
@confirm="onSearchConfirm"
confirm-type="search"
/>
</view>
<view class="cancel-btn" @click="goSearch">
<text class="cancel-text">搜索</text>
</view>
</view>
<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 goSearch = () => {
if(!keywords.value){
uni.showToast({
title: '请输入搜索内容',
icon: 'none'
});
return;
}
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 = () => {
uni.navigateTo({
url: '/pages_app/search/search'
});
};
// 下拉刷新
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 goBack = () => {
uni.navigateBack({
delta:1,
fail(){
uni.redirectTo({
url:'/pages/index/index'
})
}
});
};
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;
}
}
}
}
// 头部搜索栏
.search-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background-color: #eeeeee;
display: flex;
height:140rpx;
align-items: center;
padding:0 30rpx;
gap: 20rpx;
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
background-color: #fff;
border-radius: 20rpx;
padding: 0 20rpx;
height:70rpx;
.search-icon {
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 80rpx;
font-size: 28rpx;
color: #333;
}
.clear-icon {
margin-left: 15rpx;
padding: 10rpx;
}
}
.cancel-btn {
padding: 10rpx 20rpx;
background:#8B2316;
border-radius: 10rpx;
.cancel-text {
font-size: 28rpx;
color: #fff;
}
}
}
// 筛选栏
.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:140rpx;
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>