uniapp做一个新闻类的demo演示-青年新闻完整版

这个是全部的代码,我贴出来,供大家学习参考。

项目截图

uniapp代码

api detail detail.js

import request from '@/api/request.js'

export const getDetail = (data) => {
	return request({
		url:'/news/detail.php',
		method:'GET',
		data: data
	})
}

api index index.js

import request from '@/api/request.js'


export const getNavList = () => {
	return request({
		url:'/news/navlist.php',
		method: 'GET'
	})
}



export const getNewsList = (data)=>{
	return request({
		url:'/news/newslist.php',
		method:'GET',
		data:data
	})
}

api request.js

let baseUrl = 'https://ku.qingnian8.com/dataApi'; 

let instance  = (config) => {
	return new Promise((resolve,rejected)=>{
		uni.showLoading({
			title:'正在获取数据...',
			mask:true
		});
		
		uni.request({
			url: baseUrl + config.url,
			timeout: 5000,
			data:config.data,
			method: config.method,
			success(res){
				//成功
				if(res.statusCode == 200){
					resolve(res);
				}else{
					//请求失败,提示错误
					uni.showToast({
						title:res.errMsg,
						icon:'error',
						mask:true,
						duration:2000
					});
				}				
			},
			complete(res) {
				//关闭提示
				uni.hideLoading({
					complete:()=>{
						//成功
						// if(res.statusCode != 200){
						// 	//请求失败,提示错误
						// 	uni.showToast({
						// 		title:res.errMsg,
						// 		icon:'error',
						// 		mask:true,
						// 		duration:2000
						// 	});
						// }
					}
				});
				
			}
		})
	})
}

export default instance;

components newsbox newsbox.vue

<template>
	<view class="newsbox">
		<view class="pic">
			<image :src="item.picurl" mode="aspectFill" class="image"></image>
		</view>
		<view class="text">
			<view class="title">
				{{item.title}}
			</view>
			<view class="info" v-if="!item.lookTime">
				<text class="txt">{{item.author}}</text>
				<text class="txt">{{item.hits}}浏览</text>
			</view>
			<view class="info" v-else>
				<text class="txt">浏览时间:{{item.lookTime}}</text>
			</view>
		</view>
	</view>
</template>

<script>

	export default {
		name:"newsbox",
		data() {
			return {
				
			};
		},
		props:{
			item:{
				type:Object,
				default(){
					return {
						title:'组件内默认的标题',
						author:'张三',
						hits: 998,
						picurl:'../../static/logo.png',
						lookTime:'2023-01-01 10:10:20'
					}
				}
			}
		}
	}
</script>

<style lang="scss" scoped>
	.newsbox{
		display: flex;
		justify-content: center;
		.pic{
			width: 230rpx;
			height: 160rpx;
			.image{
				width: 100%;
				height: 100%;
			}
		}
		
		.text{
			flex: 1;
			padding-left: 20rpx;
			display: flex;
			flex-direction: column;
			justify-content: space-between;
			.title{
				font-size: 36rpx;
				color: #333;
				//
				text-overflow: -o-ellipsis-lastline;
				overflow: hidden;  //溢出内容隐藏
				text-overflow: ellipsis; //文本溢出部分用省略号
				display: -webkit-box; //特别显示模式
				-webkit-line-clamp: 2; //行数
				line-clamp: 2;
				-webkit-box-orient: vertical; //盒子中内容垂直排列
			}
			
			.info{
				font-size: 26rpx;
				color: #999;
				.txt{
					padding-right: 30rpx;
				}
			}
		}
	}
</style>

pages detail detail.vue

<template>
	<view class="detail">
		<view class="title">
			<text 
			<!-- #ifndef MP-WEIXIN -->
			:selectable="true" 
			<!-- #endif -->
			:user-select="true">
				{{detailData.title}}
			</text>
		</view>
		<view class="info">
			<view class="author">
				作者:{{detailData.author}}
			</view>
			<view class="time">
				发布日期:{{detailData.posttime}}
			</view>
		</view>
		<view class="content">
			<rich-text :nodes="detailData.content"></rich-text>
		</view>
		<view class="desc">
			<text 
			<!-- #ifndef MP-WEIXIN -->
			:selectable="true" 
			<!-- #endif -->
			:user-select="true">
				声明:本站的内容均采集自腾讯新闻,如有侵权请联系管理员(51389435760qq.com)进行整改删除,本站进行了内容采集不代表本站及作者观点,若有侵犯请及时联系管理员,谢谢您的支持。
			</text>|
		</view>
	</view>
</template>

<script>
	import {getDetail} from '@/api/detail/detail.js'
	import { parseTime } from '@/utils/tool.js'
	
	export default {
		data() {
			return {
				id:null,
				cid:null,
				detailData:[]
			};
		},
		onLoad(options) {
			console.log("id",options.id)
			console.log("cid",options.cid)
			this.id = options.id;
			this.cid = options.cid;
			//
			this.getData();
			
		},
		methods:{
			//把保存浏览历史
			saveHistory(){
				//先从本地存储,获取是否存在
				let historyData = uni.getStorageSync("b-history") || [];
				//转换,只取主要的内容
				let news = {
					id: this.detailData.id,
					classid: this.detailData.classid,
					picurl: this.detailData.picurl,
					title: this.detailData.title,
					lookTime: parseTime(Date.now())
				}
				
				//先判断,缓存中是否已存在相同记录
				let index = historyData.findIndex(ele=>{
					return ele.id == news.id
				})
				//说明有重复的,删除
				if(index >= 0){
					historyData.splice(index,1);
				}
				
				//塞入数组
				historyData.unshift(news);
				//避免缓存中存放太多数据,仅保留10条最新记录
				historyData = historyData.slice(0,10);
				//存回本地
				uni.setStorageSync("b-history",historyData);
			},
			//
			getData(){
				getDetail({
					cid: this.cid,
					id: this.id
				}).then(res=>{
					console.log(res)
					//时间戳转换时间字符串格式
					res.data.posttime = parseTime(res.data.posttime);
					//修改小程序中图片超过屏幕右侧的bug
					res.data.content = res.data.content.replace(/<img/gi,'<img style="max-width:100%"');
					this.detailData = res.data;
					//顶部栏修改标题
					// uni.setNavigationBarTitle({
					// 	title: this.detailData.title
					// })
					this.saveHistory();
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
	.detail{
		padding: 30rpx;
		
		.title{
			font-size: 46rpx;
			color: #333;
		}
		
		.info{
			background-color: #f6f6f6;
			padding: 20rpx;
			font-size: 25rpx;
			color: #666;
			display: flex;
			justify-content: space-between;
			margin: 40rpx 0;
		}
		
		.content{
			
			padding-bottom: 50rpx;
			/* #ifdef H5 */
			/deep/ img{
				max-width: 100%;
			}
			/* #endif */
			
		}
		
		.desc{
			
			background-color: #fef0f0;
			font-size: 24rpx;
			padding: 20rpx;
			color: #f89898;
			line-height: 1.8em;
		}
	}
</style>

pages index index.vue

<template>
	<view class="home">
		<scroll-view scroll-x class="nav-scorll">
			<view 
				class="item" 
				:class="navIndex == index ? 'active': ''" 
				v-for="(item,index) in navData" 
				:key="item.id" 
				@tap="clickNav(index,item.id)">
					{{item.classname}}
				</view>
		</scroll-view>
		
		<view class="content">
			<view class="row" v-for="(item,index) in newsData" :key="item.id">
				<newsbox :item="item" @tap.native="goDetail(item)"></newsbox>
			</view>
		</view>
		
		<view class="nodata" v-if="!newsData.length">
			<text>此分类下,暂无数据!!!</text>
		</view>
		
		<view class="loading" v-if="newsData.length">
			<view v-if="loading === 1">数据加载中...</view>
			<view v-if="loading === 2">没有更多了~</view>
		</view>
		
		
	</view>
</template>

<script>
	import {getNavList,getNewsList} from '@/api/index/index.js'
	
	export default {
		data() {
			return {
				loading:0,  //0默认,1 加载中 ,2没有更多了
				//当前页
				currentPage:1,
				//每页条数
				pagesize:6,
				//分类索引id
				id:0,
				//导航栏索引
				navIndex:0,
				//分类索引数据
				navData:[],
				//新闻数据
				newsData:[]
			}
		},
		onLoad() {
			//获取索引
			this.getNavData();
			//获取新闻
			this.getNewsData();
			
		},
		//触底加载
		onReachBottom() {
			//如果已经触底,就不再多余发送请求
			if(this.loading == 2){
				return;
			}
			//
			this.currentPage++;
			//
			this.loading = 1;
			//
			this.getNewsData();
		},
		methods: {
			//获取新闻数据,默认50
			getNewsData(id=50){
				//通过id,在请求对应的新闻数据
				getNewsList({
					cid: id,
					num: this.pagesize,
					page: this.currentPage
				}).then(res=>{
					console.log(res);
					this.newsData.push(...res.data);
					if(res.data.length <= 0){
						console.log('没有了')
						//底部提示
						this.loading = 2;
						//弹窗提示
						uni.showToast({
							title:'没有更多新闻了!',
							icon:'none',
							mask:true,
							duration:2500
						})
					}
				})
			},
			//获取分类索引数据
			getNavData(){
				getNavList().then(res=>{
					//console.log(res);
					this.navData = res.data;
				})
			},
			//前往新闻详情页
			goDetail(obj){
				uni.navigateTo({
					url:`/pages/detail/detail?cid=${obj.classid}&id=${obj.id}`
				});
			},
			//点击导航滑动分类栏
			clickNav(index,id){
				//点击时保存当前索引值
				this.navIndex = index;
				//切换时,先重置数据
				this.newsData = [];
				this.currentPage = 1;
				this.loading = 0;
				//
				this.getNewsData(id);
			}
		}
	}
</script>

<style lang="scss" scoped>
	.home{
		
		.nav-scorll{
			height: 100rpx;
			background-color: #f7f8fa;
			white-space: nowrap;
			position: fixed;
			top: var(--window-top);
			left: 0;
			z-index: 10;

			/deep/ ::-webkit-scrollbar{
				width: 4px !important;
				height: 1px !important;
				overflow: auto !important;
				background: transparent !important;
				-webkit-appearance: auto !important;
				display: block;
				}
				
			.item{
				font-size: 40rpx;
				line-height: 100rpx;
				display: inline-block;
				padding: 0 55rpx;
				color: #333;
				&.active{
					color: #31c27c;
				}
			}
		}
		
		.content{
			margin-top: 100rpx;
			padding: 30rpx;
			
			.row{
				border-bottom: 1px dotted #efefef;
				padding: 20rpx 0;
			}
		}
		
		.nodata{
			width: 100%;
			padding: 50rpx;
			color: #999;
			text-align: center;
		}
		
		.loading{
			text-align: center;
			font-size: 26rpx;
			color: #888;
			line-height: 2em;
		}
	}
</style>

pages user user.vue

<template>
	<view class="user">
		<view class="top">
			<icon type="waiting" size="65" class="image"/>
			<text>浏览历史</text>
		</view>
		
		<view class="content" v-if="newsData.length">
			<view class="row" v-for="(item,index) in newsData" :key="index">
				<newsbox :item="item" @tap.native="goDetail(item)"></newsbox>
			</view>
		</view>
		
		<view class="no-history" v-if="!newsData.length">
			<view class="text">暂无浏览记录</view>
		</view>
		<button type="default" @tap="onClear">清除历史记录</button>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				//新闻数据
				newsData:[]
			};
		},
		methods:{
			//清除历史记录
			onClear(){
				uni.showModal({
					title:'删除提示',
					content:'您确定要删除全部记录吗?',
					confirmColor:'red',
					confirmText:'删除',
					success(res){
						if(res.confirm){
							uni.showToast({
								title:'删除成功!',
								icon:'success'
							});
							uni.clearStorageSync();
							setTimeout(()=>{
								// #ifdef H5
								window.location.reload();
								// #endif
								
								// #ifdef MP-WEIXIN || APP
								uni.reLaunch({
									url:'/pages/user/user'
								});
								// #endif
							},1500);							
							
						}else{
							uni.showToast({
								title:'已取消删除!',
								icon:'none'
							});
						}
					}
				})
				
			},
			//获取数据
			getData(){
				let historyData = uni.getStorageSync("b-history") || [];
				this.newsData = historyData;
			},
			//前往新闻详情页
			goDetail(obj){
				uni.navigateTo({
					url:`/pages/detail/detail?cid=${obj.classid}&id=${obj.id}`
				});
			}
		},
		onLoad() {
			//load只执行一次
			//this.getData();
		},
		onShow() {
			this.getData();
		}
	}
</script>

<style lang="scss" scoped>
.user{
		
		.top{
			padding: 35rpx 0;
			background-color: #f8f8f8;
			color: #666;
			display: flex;
			flex-direction: column;
			justify-content: center;
			align-items: center;
			
			.image{
				
			}
			
			text{
				font-size: 30rpx;
				padding-top: 20rpx;
			
			}
		}
			
		
		.content{
			padding: 30rpx;
			
			.row{
				border-bottom: 1px dotted #efefef;
				padding: 20rpx 0;
			}
		}
		
		.no-history{
			display: flex;
			justify-content: center;
			align-items: center;
			width: 100%;
			height: 100rpx;
			.text{
				font-size: 26rpx;
				color: #888;
				padding: 20rpx 0;

			}
		}
	}
</style>

utils

//时间间隔函数
export function timeInterval(timesData) {
  //如果时间格式是正确的,那下面这一步转化时间格式就可以不用了
  var dateBegin = timesData;//将-转化为/,使用new Date    
  var dateEnd = new Date();//获取当前时间   
  var dateDiff = Math.abs( dateEnd.getTime() - dateBegin );  //时间差的毫秒数
  var yearDiff = Math.floor(dateDiff / (24 * 3600 * 1000*365));
  var dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000));  //计算出相差天数
  var leave1 = dateDiff % (24 * 3600 * 1000)    //计算天数后剩余的毫秒数
  var hours = Math.floor(leave1 / (3600 * 1000))//计算出小时数
  //计算相差分钟数
  var leave2 = leave1 % (3600 * 1000)    //计算小时数后剩余的毫秒数
  var minutes = Math.floor(leave2 / (60 * 1000))//计算相差分钟数
  //计算相差秒数
  var leave3 = leave2 % (60 * 1000)      //计算分钟数后剩余的毫秒数
  var seconds = Math.round(leave3 / 1000);
  var timesString = '';
  if (yearDiff!=0){
    timesString = yearDiff + '年前';
  } else if (yearDiff == 0   && dayDiff != 0) {
    timesString = dayDiff + '天前';
  } else if (dayDiff == 0 && hours != 0) {
    timesString = hours + '小时前';
  } else if (hours == 0 && minutes != 0) {      
    timesString = minutes + '分钟前';      
  } else if (minutes == 0 && seconds<60){ 
    timesString = '刚刚'; 
  } 
  return timesString 
}


// 日期格式化
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
      time = parseInt(time)
    } else if (typeof time === 'string') {
      time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
    }
    if ((typeof time === 'number') && (time.toString().length === 10)) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

app.vue

<script>
	export default {
		onLaunch: function() {
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

<style>
	/*每个页面公共css */
	*{
		padding: 0;
		margin: 0;
		box-sizing: border-box;
	}
</style>

pages.json

{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "首页"
			}
		},
		{
			"path" : "pages/user/user",
			"style" : 
			{
				"navigationBarTitleText" : "个人中心",
				"enablePullDownRefresh" : false
			}
		},
		{
			"path" : "pages/detail/detail",
			"style" : 
			{
				"navigationBarTitleText" : "新闻详情",
				"enablePullDownRefresh" : false
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "white",
		"navigationBarTitleText": "青年新闻",
		"navigationBarBackgroundColor": "#2CAF73",
		"backgroundColor": "#F8F8F8"
	},
	"uniIdRouter": {},
	"tabBar": {
		"list": [
			{
				"pagePath": "pages/index/index",
				"text": "首页",
				"iconPath": "static/tabbar/home.png",
				"selectedIconPath": "static/tabbar/home-s.png"
			},
			{
				"pagePath": "pages/user/user",
				"text": "个人中心",
				"iconPath": "static/tabbar/mine.png",
				"selectedIconPath": "static/tabbar/mine-s.png"
			}
		]
	}
}

manifest.json

 /* 小程序特有相关 */
    "mp-weixin" : {
        "appid" : "wx86209e8eccbc4307",
        "setting" : {
            "urlCheck" : false,
            "es6" : true,
            "postcss" : true,
            "minified" : true
        },
        "lazyCodeLoading" : "requiredComponents"
    },

测试

首页

 详情页

 个人中心页

 清除历史

 以上就是全部代码,希望对各位有所帮助。

另外本项目是学习自青年帮新闻项目而来,在此感谢咸虾米老师,无私的讲解项目的视频教程,让我对学习uniapp受益良多,感谢感谢。

  • 64
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虾米大王

有你的支持,我会更有动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值