This commit is contained in:
zpc 2025-06-21 13:22:49 +08:00
parent 692191ad05
commit 396d39c76d
9 changed files with 283 additions and 32 deletions

View File

@ -1,5 +1,5 @@
import HttpRequest from "../system/request";
import { sleep } from "@/common/utils";
/**
* 获取推荐新闻列表
* @param {Number} page 页码
@ -28,7 +28,10 @@ export const getHotNewsList = async (page = 1, pageSize = 10, title = "") => {
* @param {Number} id 新闻id
* @returns {Promise} 新闻详情
*/
export const getNewsDetail = async (id) => {
const res = await HttpRequest.getOrCache('/get_news_detail', { id });
export const getNewsDetail = async (id, current = 0) => {
const res = await HttpRequest.getOrCache('/get_news_detail', { id, current });
if (res.status == 0) {
return null;
}
return res.data;
}

3
components.d.ts vendored
View File

@ -8,10 +8,13 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
LoadingData: typeof import('./components/youdas-container/loading-data.vue')['default']
NewsListItem: typeof import('./components/youdas-container/news-list-item.vue')['default']
NoData: typeof import('./components/youdas-container/no-data.vue')['default']
PageContainer: typeof import('./components/youdas-container/page-container.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
UniNavBar: typeof import('./components/uni-nav-bar/uni-nav-bar.vue')['default']
UniStatusBar: typeof import('./components/uni-nav-bar/uni-status-bar.vue')['default']
}
}

View File

@ -0,0 +1,61 @@
<template>
<view class="loading-state">
<image class="loading-image" :src="imageSrc" mode="aspectFit"></image>
<text class="loading-text">{{ message }}</text>
</view>
</template>
<script>
export default {
name: 'LoadingData',
props: {
// 使kong.png
imageSrc: {
type: String,
default: '/static/app-plus/index_login.gif'
},
//
message: {
type: String,
default: '努力加载中'
}
}
}
</script>
<style lang="scss">
.loading-state {
width: 100%;
height: 100%;
// border: 1px solid red;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
animation: emptyFadeIn 0.5s ease-out;
// background-color: #F7F7F7;
}
.loading-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.loading-text {
font-size: 28rpx;
color: #999999;
}
@keyframes emptyFadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<view class="content">
<z-paging v-if="firstLoaded || isCurrentPage" ref="paging" show-refresher-update-time loading-more-no-more-text="我也是有底线的!" v-model="dataList" @query="queryList" :fixed="false">
<z-paging v-if="firstLoaded || isCurrentPage" ref="paging" show-refresher-update-time
loading-more-no-more-text="我也是有底线的!" v-model="dataList" @query="queryList" :fixed="false">
<template v-for="(item, index) in dataList">
<view v-if="item.cover_image !== ''" class="view-data" :key="index" @click="toDetails(item.id)">
<view class="title">
@ -31,6 +32,9 @@
<template #empty>
<no-data />
</template>
<template #loading>
<loading-data />
</template>
</z-paging>
</view>
</template>
@ -46,7 +50,7 @@ const isCurrentPage = ref(false);
//
const toDetails = (id) => {
if (!id) return;
emit('clickItem', id);
emit('clickItem', id);
}
const props = defineProps({
@ -74,7 +78,7 @@ watch(() => props.currentIndex, (newVal) => {
// z-paging
setTimeout(() => {
isCurrentPage.value = true;
}, 100);
}, 50);
}
}
}, {

View File

@ -1,12 +1,19 @@
<!-- 详情 -->
<template>
<view class="page-container">
<uni-nav-bar :title="title" @clickLeft="onClickLeft" :leftIcon="leftIcon" />
<view class="page-container__content">
<!-- <view class="page-container__divider" v-if="divider"></view> -->
<slot></slot>
<z-paging ref="paging" refresher-only @onRefresh="onRefresh">
<template #top>
<uni-nav-bar :title="title" @clickLeft="onClickLeft" :leftIcon="leftIcon" />
</template>
<view class="page-container">
<view class="page-container__content">
<view class="page-container__not-data" v-if="showNotData">
<no-data />
</view>
<slot></slot>
</view>
</view>
</view>
</z-paging>
</template>
<script setup>
@ -19,7 +26,16 @@ const props = defineProps({
type: Boolean,
default: false
},
showNotData: {
type: Boolean,
default: false
},
refresh: {
type: Function,
default: (res) => { }
}
});
let paging = ref(null);
const leftIcon = computed(() => {
if (props.showBack) {
return "/static/back.png";
@ -30,7 +46,15 @@ const onClickLeft = () => {
uni.navigateBack();
}
}
const onRefresh = () => {
props.refresh(paging.value);
}
const getPaging = () => {
return paging.value;
}
defineExpose({
getPaging
})
</script>
<style lang="scss" scoped>
@ -39,8 +63,10 @@ const onClickLeft = () => {
min-height: 100vh;
box-sizing: border-box;
&__content {
width: 100%;
height: 100%;
box-sizing: border-box;
position: relative;
}
@ -58,5 +84,13 @@ const onClickLeft = () => {
margin-bottom: 10rpx;
/* 在分割线下方添加一些间距 */
}
&__not-data {
width: 100%;
height: 80vh;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>

View File

@ -1,7 +1,7 @@
<!-- 详情 -->
<template>
<view>
<page-container title="详情" show-back>
<page-container ref="pageContainer" title="详情" show-back :show-not-data="showNotData" :refresh="onRefresh">
<view class="news-details">
<view class="news-details__title">
{{ title }}
@ -13,33 +13,98 @@
<rich-text :nodes="content"></rich-text>
</view>
</view>
<view class="news-details__footer">
<view class="news-details__footer-item">
<view class="flex-row" v-if="prev_data" @click="goToNews(prev_data)">
<image src="/static/app-plus/news/prev.png" mode="aspectFill"
style="width:20px; height:20px;position: relative;top:5rpx;"></image>
<text>上一篇</text>
</view>
</view>
<view class="news-details__footer-item">
<view class="flex-row" v-if="next_data" @click="goToNews(next_data)">
<text>下一篇</text>
<image src="/static/app-plus/news/next.png" mode="aspectFill"
style="width:20px; height:20px;position: relative;top:8rpx;"></image>
</view>
</view>
</view>
<view class="news-details__recommend_news">
<view class="news-details__recommend_news-title">
推荐
</view>
<view class="news-details__recommend_news-list">
<view class="news-details__recommend_news-item" v-for="item in recommend_news" :key="item.id"
@click="goToNews(item)">
<view class="news-details__recommend_news-item-image">
<image :src="item.cover_image" mode="aspectFill"></image>
</view>
<view class="news-details__recommend_news-item-content">
<text>{{ item.title }}</text>
</view>
</view>
</view>
</view>
</page-container>
</view>
</template>
<script>
import { getNewsDetail } from '@/common/server/news';
import { sleep } from '@/common/utils';
export default {
data() {
return {
title: "",
content: "",
publish_time: "",
author_name: ""
author_name: "",
showNotData: false,
options: {},
next_data: null,
prev_data: null,
recommend_news: []
}
},
onLoad(options) {
async onLoad(options) {
console.log("optionsoptionsoptionsoptions", options);
this.options = options;
this.title = "";
getNewsDetail(options.id).then(res => {
console.log(res);
this.title = res.title;
this.content = res.content;
this.publish_time = res.publish_time;
this.author_name = res.author_name;
})
this.getNewsDetail(options.id, options.current);
},
methods: {
async onRefresh(paging) {
console.log("onRefreshonRefreshonRefreshonRefresh");
await this.getNewsDetail(this.options.id, this.options.current);
await sleep(500);
paging.complete();
},
async goToNews(item) {
this.options.id = item.id;
await this.getNewsDetail(item.id, this.options.current);
this.$refs.pageContainer.getPaging().scrollToTop();
// uni.navigateTo(`/pages/news/news-details?id=${id}&current=${this.options.current}`);
},
getTitle(title) {
return title.length > 10 ? title.slice(0, 10) + "..." : title;
},
async getNewsDetail(id, current) {
const res = await getNewsDetail(id, current);
console.log("resresresres", res);
if (res) {
this.title = res.title;
this.content = res.content;
this.publish_time = res.publish_time;
this.author_name = res.author_name;
this.next_data = res.next_data;
this.prev_data = res.prev_data;
this.recommend_news = res.recommend_news;
} else {
this.showNotData = true;
}
}
}
}
</script>
@ -47,6 +112,7 @@ export default {
<style lang="scss" scoped>
.news-details {
padding: 30rpx 10rpx;
min-height: 70vh;
&__title {
text-align: center;
@ -67,6 +133,83 @@ export default {
}
&__footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 10rpx;
&-item {
padding-left: 20rpx;
padding-right: 20rpx;
height: 70rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
&__recommend_news {
margin-top: 30rpx;
padding: 0 20rpx;
&-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
&-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
&-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 16rpx;
background-color: #f9f9f9;
border-radius: 12rpx;
&-image {
width: 160rpx;
height: 120rpx;
margin-right: 20rpx;
image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
&-content {
flex: 1;
text {
font-size: 28rpx;
color: #333;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
}
}
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 8rpx;
}
//
::v-deep rich-text img {
max-width: 95% !important;

View File

@ -1,7 +1,7 @@
<template>
<z-paging-swiper>
<template #top>
<view class="" style="width: 100%; height: 180.19rpx; background-color: white; position: relative;">
<view class="" style="width: 100%; height: 225.19rpx; background-color: white; position: relative;">
<view class="tab">
<view class="tab-item" v-for="(item, i) in tabList" :key="i" :class="current == i ? 'act' : 'unact'"
@click="tabChange(i)">
@ -10,7 +10,6 @@
</view>
</view>
</template>
<swiper class="swiper" :current="current" @animationfinish="swiperAnimationfinish">
<swiper-item class="swiper-item" v-for="(item, index) in tabList" :key="index">
<news-list-item @clickItem="onClickItem" :responseCallback="queryList" ref="listItem" :tabIndex="index"
@ -47,14 +46,8 @@ const tabChange = (index) => {
};
const onClickItem = (id) => {
console.log('id', id);
navigateTo(`/pages/news/news-details?id=${id}`)
navigateTo(`/pages/news/news-details?id=${id}&current=${current.value}`)
};
//
// onShow(() => {
// // onShow
// // listItem.value && reloadCurrentList();
// });
// swiper
const swiperAnimationfinish = (e) => {
current.value = e.detail.current;
@ -71,6 +64,16 @@ const reloadCurrentList = () => {
height: 100%;
}
.search {
height: 50rpx;
background-color: #F7F7F7;
border-radius: 10rpx;
padding: 0 20rpx;
margin: 0 20rpx;
display: flex;
align-items: center;
}
.tab {
width: 100%;
display: flex;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B