719 lines
18 KiB
Vue
719 lines
18 KiB
Vue
<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: 'none'
|
||
});
|
||
};
|
||
|
||
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>
|