2025-10-14 17:46:23 +08:00

719 lines
18 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="news-list-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="肝胆新闻"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
: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>