uni-app实战之社区交友APP(8)搜索列表页和文章详情页开发

文章目录

  • 前言
  • 一、搜索列表页开发
    • 1.搜索类型传递和占位符设置
    • 2.搜索功能实现
  • 二、帖子详情页开发
    • 1.pages.json配置和页面通信
    • 2.公共列表组件功能优化
    • 3.详情页关注和顶踩功能完善
    • 4.帖子内容和图片展示
    • 5.评论输入框组件开发和封装
    • 6.评论列表组件开发
    • 7.分享功能组件开发
  • 总结

小编目前在做毕业设计,主题为“高考志愿信息交流平台”,面向高中生和大学生,辛苦各位读者大佬朋友们填下问卷,点击链接https://www.wjx.cn/jq/98944127.aspx或扫描二维码、微信小程序码均可,希望各位能提供一些调查数据,先在这里谢过各位了(*^_^*)
问卷1
问卷微信小程序码

前言

本文先介绍了搜索结果页开发,包括搜索类型的传递、占位符设置和搜索功能实现;
再介绍了帖子详情页的开发,包括页面配置和通信、公共列表组件优化、关注顶踩功能完善、帖子内容和图片展示、评论输入框组件开发和封装、评论列表组件和分享功能组件开发等。

一、搜索列表页开发

首页有搜索帖子、动态页有搜索话题、消息页有搜索用户,因此需要实现搜索页。

1.搜索类型传递和占位符设置

不同页面的搜索类型不同,需要通过标识进行区别,index.vue如下:

// 监听导航栏搜索框
onNavigationBarSearchInputClicked() {
     
    uni.navigateTo({
     
        url: '../search/search?type=post'
    })
},

news.vue如下:


<view class="p-2">
    <view class="bg-light rounded flex align-center justify-center py-2 text-secondry" @click="openSearch()">
        <text class="iconfont icon-sousuo mr-2">text>搜索话题
    view>
view>

// 打开搜索页
openSearch() {
    uni.navigateTo({
        url: '../search/search?type=topic'
    });
}

user-list.vue如下:

// 监听点击输入框事件
onNavigationBarSearchInputClicked() {
     
    uni.navigateTo({
     
        url: '../search/search?type=user'
    });
},

msg.vue如下:

// 弹出层选项点击事件
popupEvent(e) {
     
    switch (e) {
     
        case 'friend':
            console.log('Adding friend');
            uni.navigateTo({
     
                url: '../search/search?type=user'
            });
            break;
        case 'clear':
            console.log('Clearing list');
            break;
        default:
            break;
    }
    // 关闭弹出层
    this.$refs.popup.close();
}

search.vue完善搜索类型和占位符,如下:

<template>
	<view>
		<template v-if="searchList.length === 0">
			
			<view class="py-2 font-md px-2">搜索历史view>
			<view class="flex flex-wrap">
				<view class="border rounded font mx-2 my-1 px-2" hover-class="bg-light" v-for="(item, index) in list" :key="index"
				 @click="clickSearchHistory(item)">{
    {item}}view>
			view>
		template>
		<template v-else>
			
			<block v-for="(item, index) in searchList" :key="index">
				<common-list :item="item" :index="index">common-list>
			block>
		template>
	view>
template>

<script>
	// 测试数据
	const test_data = [{
      
			username: "Corley",
			userpic: "/static/img/userpic/12.jpg",
			newstime: "2021-01-24 上午11:30",
			isFollow: false,
			title: "uni-app入门教程",
			titlepic: "/static/img/datapic/42.jpg",
			support: {
      
				type: "support", // 顶
				support_count: 1,
				unsupport_count: 2
			},
			comment_count: 2,
			share_count: 2
		},
		{
      
			username: "Brittany",
			userpic: "/static/img/userpic/16.jpg",
			newstime: "2021-01-24 下午14:00",
			isFollow: false,
			title: "商业数据分析从入门到入职",
			support: {
      
				type: "unsupport", // 踩
				support_count: 2,
				unsupport_count: 3
			},
			comment_count: 5,
			share_count: 1
		},
		{
      
			username: "Ashley",
			userpic: "/static/img/userpic/20.jpg",
			newstime: "2021-01-24 下午18:20",
			isFollow: true,
			title: "uni-app实战之社区交友APP",
			titlepic: "/static/img/datapic/30.jpg",
			support: {
      
				type: "support",
				support_count: 5,
				unsupport_count: 1
			},
			comment_count: 3,
			share_count: 0
		}
	];
	import commonList from '@/components/common/common-list.vue';
	export default {
      
		data() {
      
			return {
      
				list: [
					'uni-app实战之社区交友APP',
					'uni-app入门教程',
					'面试之算法基础系列',
					'Python全栈',
					'商业数据分析从入门到入职',
					'Python数据分析实战',
					'Django+Vue开发生鲜电商平台'
				],
				searchText: '',
				// 搜索结果
				searchList: [],
				// 当前搜索类型
				type: 'post'
			}
		},
		components: {
      
			commonList
		},
		// 监听导航栏搜索框输入
		onNavigationBarSearchInputChanged(e) {
      
			console.log(e);
			this.searchText = e.text;
		},
		// 监听点击导航栏搜索按钮
		onNavigationBarButtonTap(e) {
      
			console.log(e);
			if (e.index === 0) {
      
				this.searchEvent();
			}
		},
		onLoad(e) {
      
			console.log(e);
			if (e.type) {
      
				this.type = e.type;
			}
			let pageTitle = '帖子';
			switch (this.type) {
      
				case 'post':
					pageTitle = '帖子'
					break;
				case 'topic':
					pageTitle = '话题'
					break;
				case 'user':
					pageTitle = '用户'
					break;
				default:
					break;
			}
			// 修改搜索占位
			// #ifdef APP-PLUS
			let currentWebview = this.$scope.$getAppWebview(); // 当前窗口实例
			let tn = currentWebview.getStyle().titleNView; // 当前窗口原生导航栏
			tn.searchInput.placeholder = '搜索' + pageTitle; // 修改placeholder
			currentWebview.setStyle({
      
				titleNView: tn
			}); // 修改原生导航栏
			// #endif

		},
		methods: {
      
			// 搜索事件
			searchEvent() {
      
				// 收起键盘
				uni.hideKeyboard();
				// 显示loading状态
				uni.showLoading({
      
					title: '加载中...',
					mask: false
				});
				// 请求搜索
				setTimeout(() => {
      
					this.searchList = test_data;
					// 隐藏loading状态
					uni.hideLoading();
				}, 2500)
			},
			// 点击搜索历史
			clickSearchHistory(text) {
      
				this.searchText = text;
				this.searchEvent();
			}
		}
	}
script>

<style>

style>

base.css如下:

/* 内外边距 */
.p-2 {
     
	padding: 20rpx;
}

/* flex布局 */
.flex {
     
	/* #ifndef APP-APP-PLUS-NVUE */
	display: flex;
	/* #endif */
	flex-direction: row;
}

.flex-wrap {
     
	flex-wrap: wrap;
}

.flex-column {
     
	flex-direction: column;
}

.align-center {
     
	align-items: center;
}

.align-start {
     
	align-items: flex-start;
}

.justify-between {
     
	justify-content: space-between;
}

.justify-center {
     
	justify-content: center;
}

.flex-1 {
     
	flex: 1;
}

/* 圆角 */
.rounded-circle {
     
	border-radius: 100%;
}

.rounded {
     
	border-radius: 8rpx;
}

/* margin */
.mr-2 {
     
	margin-right: 20rpx;
}

.mr-1 {
     
	margin-right: 10rpx;
}

.my-2 {
     
	margin-top: 20rpx;
	margin-bottom: 20rpx;
}

.my-1 {
     
	margin-top: 10rpx;
	margin-bottom: 10rpx;
}

.mx-2 {
     
	margin-left: 20rpx;
	margin-right: 20rpx;
}

.mx-1 {
     
	margin-left: 10rpx;
	margin-right: 10rpx;
}

.mt-1 {
     
	margin-top: 10rpx;
}

.ml-auto {
     
	margin-left: auto;
}

.ml-2 {
     
	margin-left: 20rpx;
}

/* padding */
.p-2 {
     
	padding-left: 20rpx;
	padding-right: 20rpx;
	padding-top: 20rpx;
	padding-bottom: 20rpx;
}

.px-5 {
     
	padding-left: 50rpx;
	padding-right: 50rpx;
}

.px-3 {
     
	padding-left: 30rpx;
	padding-right: 30rpx;
}

.px-2 {
     
	padding-left: 20rpx;
	padding-right: 20rpx;
}

.px-1 {
     
	padding-left: 10rpx;
	padding-right: 10rpx;
}

.py-3 {
     
	padding-top: 30rpx;
	padding-bottom: 30rpx;
}

.py-2 {
     
	padding-top: 20rpx;
	padding-bottom: 20rpx;
}

.pt-7 {
     
	padding-top: 70rpx;
}

.pb-2 {
     
	padding-bottom: 20rpx;
}

/* 边框 */
.border {
     
	border-width: 1rpx;
	border-style: solid;
	border-color: #DEE2E6;
}

.border-bottom {
     
	border-bottom: 1rpx solid #DEE2E6;
}

.border-top {
     
	border-top: 1rpx solid #DEE2E6;
}

.border-light-secondary {
     
	border: 1rpx solid #AAA8AB;
}

/* 字体 */
.font-lg {
     
	font-size: 40rpx;
}

.font-md {
     
	font-size: 35rpx;
}

.font {
     
	font-size: 30rpx;
}

.font-sm {
     
	font-size: 25rpx;
}

.font-weight-bold {
     
	font-weight: bold;
}

/* 文字 */
.text-white {
     
	color: #FFFFFF;
}

.text-light-muted {
     
	color: #A9A5A0;
}

.text-muted {
     
	color: #B2B2B2;
}

.text-center {
     
	text-align: center;
}

/* 文字换行溢出处理 */
.text-ellipsis {
     
	/* #ifndef APP-PLUS-APP-PLUS-NVUE */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	/* #endif */
	/* #ifdef APP-PLUS-APP-PLUS-NVUE */
	lines: 1;
	/* #endif */
}

/* 宽度 */
/* #ifndef APP-PLUS-NVUE */
.w-100 {
     
	width: 100%;
}

/* #endif */

/* scroll-view */
/* #ifndef APP-PLUS-NVUE */
.scroll-row {
     
	width: 100%;
	white-space: nowrap;
}

.scroll-row-item {
     
	display: inline-block !important;
}

/* #endif */

/* 背景 */
.bg-light {
     
	background-color: #F8F9FA;
}

.bg-secondary {
     
	background-color: #AAA8AB;
}

.bg-white {
     
	background-color: #FFFFFF;
}

.bg-dark {
     
	background-color: #333333;
}

.bg-green {
     
	background-color: #1EBE9A;
}

.bg-brown {
     
	background-color: #4E4E4E;
}

.bg-red {
     
	background-color: #FB6B5A;
}

.bg-blue {
     
	background-color: #4C82D1;
}

/* 定位 */
.position-relative {
     
	position: relative;
}

.position-absolute {
     
	position: absolute;
}

.position-fixed {
     
	position: fixed;
}

/* 定位-固定顶部 */
.fixed-top {
     
	position: fixed;
	top: 0;
	right: 0;
	left: 0;
	z-index: 1030;
}

/* 定位-固定底部 */
.fixed-bottom {
     
	position: fixed;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 1030;
}

.top-0 {
     
	top: 0;
}

.left-0 {
     
	left: 0;
}

.right-0 {
     
	right: 0;
}

.bottom-0 {
     
	bottom: 0;
}

其中,$getAppWebview()是用来获取当前webview的对象实例,从而可以实现对 webview 更强大的控制,这里是用来根据搜索类型改变导航栏搜索框占位符的。

显示:

可以看到,根据不同的搜索类型显示了不同的占位符placeholder。

2.搜索功能实现

需要根据不同的搜索类型搜索不同的内容,search.vue如下:

<template>
	<view>
		<template v-if="searchList.length === 0">
			
			<view class="py-2 font-md px-2">搜索历史view>
			<view class="flex flex-wrap">
				<view class="border rounded font mx-2 my-1 px-2" hover-class="bg-light" v-for="(item, index) in list" :key="index"
				 @click="clickSearchHistory(item)">{
    {item}}view>
			view>
		template>
		<template v-else>
			<block v-for="(item, index) in searchList" :key="index">
				
				<template v-if="type === 'post'">
					<common-list :item="item" :index="index" :key="'user'+index">common-list>
				template>
				
				<template v-else-if="type === 'topic'">
					<topic-list :item="item" :index="index" :key="'user'+index">topic-list>
				template>
				
				<template v-else>
					<user-list :item="item" :index="index" :key="'user'+index">user-list>
				template>
			block>
		template>
	view>
template>

<script>
	// 测试数据
	const post_test_data = [{
      
			username: "Corley",
			userpic: "/static/img/userpic/12.jpg",
			newstime: "2021-01-24 上午11:30",
			isFollow: false,
			title: "uni-app入门教程",
			titlepic: "/static/img/datapic/42.jpg",
			support: {
      
				type: "support", // 顶
				support_count: 1,
				unsupport_count: 2
			},
			comment_count: 2,
			share_count: 2
		},
		{
      
			username: "Brittany",
			userpic: "/static/img/userpic/16.jpg",
			newstime: "2021-01-24 下午14:00",
			isFollow: false,
			title: "商业数据分析从入门到入职",
			support: {
      
				type: "unsupport", // 踩
				support_count: 2,
				unsupport_count: 3
			},
			comment_count: 5,
			share_count: 1
		},
		{
      
			username: "Ashley",
			userpic: "/static/img/userpic/20.jpg",
			newstime: "2021-01-24 下午18:20",
			isFollow: true,
			title: "uni-app实战之社区交友APP",
			titlepic: "/static/img/datapic/30.jpg",
			support: {
      
				type: "support",
				support_count: 5,
				unsupport_count: 1
			},
			comment_count: 3,
			share_count: 0
		}
	];
	const topic_test_data = [{
      
			cover: '/static/img/topicpic/14.jpeg',
			title: '毛伟明当选湖南省人民政府省长',
			desc: '毛伟明当选湖南省人民政府省长',
			news_count: 10,
			today_count: 1
		},
		{
      
			cover: '/static/img/topicpic/2.jpeg',
			title: '个别自美赴华乘客篡改阳性记录',
			desc: '中国驻旧金山总领馆:个别自美赴华乘客篡改、隐瞒阳性记录',
			news_count: 7,
			today_count: 2
		},
		{
      
			cover: '/static/img/topicpic/5.jpeg',
			title: '中纪委披露安徽太和县骗保事件',
			desc: '安徽省太和县发生骗保事件,中纪委网站披露骗保百般花样',
			news_count: 21,
			today_count: 3
		},
		{
      
			cover: '/static/img/topicpic/8.jpeg',
			title: '篮网正式签约佩莱',
			desc: '对阵篮网4次封盖,13分钟7个篮板,怒帽小乔丹的佩莱加盟篮网',
			news_count: 11,
			today_count: 2
		},
		{
      
			cover: '/static/img/topicpic/10.jpeg',
			title: '被孟佳团队抄袭图片的模特发文',
			desc: '孟佳团队背锅承认抄袭,外国模特发文回应:没有在第一时间联系',
			news_count: 7,
			today_count: 0
		},
		{
      
			cover: '/static/img/topicpic/16.jpeg',
			title: 'FF将通过并购在纳斯达克上市',
			desc: 'FF将通过与PSAC合并在纳斯达克上市;传蚂蚁集团将重组为央行监管的...',
			news_count: 15,
			today_count: 4
		},
		{
      
			cover: '/static/img/topicpic/11.jpeg',
			title: '"现实版樊胜美"弟弟疑遭人肉网暴',
			desc: '现实版樊胜美弟弟疑遭人肉网暴 挂出其母弟弟住址电话',
			news_count: 6,
			today_count: 0
		}
	];
	const user_test_data = [{
      
			avatar: '/static/img/userpic/15.jpg',
			username: 'Corley',
			sex: 1, // 0未知、1女性、2男性
			age: 23,
			isFollow: true
		},
		{
      
			avatar: '/static/img/userpic/7.jpg',
			username: 'Casey',
			sex: 0,
			age: 15,
			isFollow: false
		},
		{
      
			avatar: '/static/img/userpic/13.jpg',
			username: 'Henry',
			sex: 2,
			age: 18,
			isFollow: true
		}
	]
	import commonList from '@/components/common/common-list.vue';
	import topicList from '@/components/news/topic-list.vue';
	import userList from '@/components/user-list/user-list.vue';
	export default {
      
		data() {
      
			return {
      
				list: [
					'uni-app实战之社区交友APP',
					'uni-app入门教程',
					'面试之算法基础系列',
					'Python全栈',
					'商业数据分析从入门到入职',
					'Python数据分析实战',
					'Django+Vue开发生鲜电商平台'
				],
				searchText: '',
				// 搜索结果
				searchList: [],
				// 当前搜索类型
				type: 'post'
			}
		},
		components: {
      
			commonList,
			topicList,
			userList
		},
		// 监听导航栏搜索框输入
		onNavigationBarSearchInputChanged(e) {
      
			console.log(e);
			this.searchText = e.text;
		},
		// 监听点击导航栏搜索按钮
		onNavigationBarButtonTap(e) {
      
			console.log(e);
			if (e.index === 0) {
      
				this.searchEvent();
			}
		},
		onLoad(e) {
      
			console.log(e);
			if (e.type) {
      
				this.type = e.type;
			}
			let pageTitle = '帖子';
			switch (this.type) {
      
				case 'post':
					pageTitle = '帖子'
					break;
				case 'topic':
					pageTitle = '话题'
					break;
				case 'user':
					pageTitle = '用户'
					break;
				default:
					break;
			}
			// 修改搜索占位
			// #ifdef APP-PLUS
			let currentWebview = this.$scope.$getAppWebview(); // 当前窗口实例
			let tn = currentWebview.getStyle().titleNView; // 当前窗口原生导航栏
			tn.searchInput.placeholder = '搜索' + pageTitle; // 修改placeholder
			currentWebview.setStyle({
      
				titleNView: tn
			}); // 修改原生导航栏
			// #endif

		},
		methods: {
      
			// 搜索事件
			searchEvent() {
      
				// 收起键盘
				uni.hideKeyboard();
				// 显示loading状态
				uni.showLoading({
      
					title: '加载中...',
					mask: false
				});
				// 请求搜索
				setTimeout(() => {
      
					switch (this.type) {
      
						case 'post':
							this.searchList = post_test_data;
							break;
						case 'topic':
							this.searchList = topic_test_data;
							break;
						case 'user':
							this.searchList = user_test_data;
							break;
						default:
							break;
					}

					// 隐藏loading状态
					uni.hideLoading();
				}, 2500)
			},
			// 点击搜索历史
			clickSearchHistory(text) {
      
				this.searchText = text;
				this.searchEvent();
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了根据不同的搜索类型显示不同的搜索内容。

二、帖子详情页开发

1.pages.json配置和页面通信

先新建帖子详情页detail.vue,再在pages.json中配置页面右上角的菜单按钮,如下:

{
     
    "path" : "pages/detail/detail",
    "style" :                                                                                    
    {
     
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "app-plus": {
     
            "titleNView": {
     
                "buttons": [
                    {
     
                        "type":"menu",
                        "float":"right"
                    }
                ]
            }
        }
    }
    
}

帖子详情页一般从common-list组件进入,如下:

// 进入详情页
openDetail() {
     
    uni.navigateTo({
     
        url: '../../pages/detail/detail?detail='+JSON.stringify(this.item),
    });
},

detail.vue修改如下:

onLoad(e) {
     
    console.log(e);
    // 初始化操作
    if (e.detail) {
     
        this.__init(JSON.parse(e.detail));
    }
    
},
methods: {
     
    __init(data) {
     
        // 修改标题
        uni.setNavigationBarTitle({
     
            title:data.title
        });
        // 请求API
    }
}

显示:

可以看到,页面的标题显示的是帖子的标题。

2.公共列表组件功能优化

对公共列表组件功能进行优化,以使其可以兼容到帖子详情页头部,此时传的属性值增加一个值isdetail用来判断是否是详情页,同时添加插槽用来插入帖子内容,common-list.vue如下:

<template>
	<view class="p-2">
		
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;" @click="openSpace"
				 lazy-load>image>
				
				<view>
					<view class="font" style="line-height: 1.5;">{
    {item.username}}view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{
    {item.newstime}}text>
				view>
			view>
			
			<view v-if="!item.isFollow" class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster"
			 hover-class="animate__jello" style="width: 90rpx; height: 50rpx;" @click="follow">
				关注
			view>
		view>
		
		<view class="font my-1" @click="openDetail">
			{
    {item.title}}
		view>
		
		<slot>
			
			<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;" @click="openDetail">image>
		slot>
		
		<view class="flex align-center">
			
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('support')" :class="item.support.type === 'support' ? 'support-active' : ''">
				<text class="iconfont icon-dianzan mr-2">text>
				<text>{
    {item.support.support_count > 0 ? item.support.support_count : '支持'}}text>
			view>
			
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('unsupport')" :class="item.support.type === 'unsupport' ? 'support-active' : ''">
				<text class="iconfont icon-cai mr-2">text>
				<text>{
    {item.support.unsupport_count > 0 ? item.support.unsupport_count : '反对'}}text>
			view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-pinglun mr-2">text>
				<text>{
    {item.comment_count > 0 ? item.comment_count : '评论'}}text>
			view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-fenxiang mr-2">text>
				<text>{
    {item.share_count > 0 ? item.share_count : '分享'}}text>
			view>
		view>
	view>
template>

<script>
	export default {
      
		props: {
      
			item: Object,
			index: Number,
			isdetail: {
      
				type: Boolean,
				default: false
			}
		},
		methods: {
      
			// 打开个人空间
			openSpace() {
      
				console.log('Space opened');
			},
			// 关注
			follow() {
      
				console.log('Followed');
				// 通知父组件
				this.$emit('follow', this.index);
			},
			// 进入详情页
			openDetail() {
      
				// 处于详情页
				if (this.isdetail) return;
				uni.navigateTo({
      
					url: '../../pages/detail/detail?detail='+JSON.stringify(this.item),
				});
			},
			// 顶踩操作
			doSupport(type) {
      
				console.log(type);
				// 通知父组件
				this.$emit('doSupport', {
      
					type,
					index: this.index
				})
			}
		}
	}
script>

<style>
	.support-active {
      
		color: #FF4A6A;
	}
style>

detail.vue如下:

<template>
	<view>
		
		<common-list :item="info" isdetail>
			帖子详情
		common-list>
	view>
template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      }
			}
		},
		components: {
      
			commonList
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}

		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了common-list组件的复用。

再给评论和分享按钮添加点击事件,如下:

<template>
	<view class="p-2">
		
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;" @click="openSpace"
				 lazy-load>image>
				
				<view>
					<view class="font" style="line-height: 1.5;">{
    {item.username}}view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{
    {item.newstime}}text>
				view>
			view>
			
			<view v-if="!item.isFollow" class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster"
			 hover-class="animate__jello" style="width: 90rpx; height: 50rpx;" @click="follow">
				关注
			view>
		view>
		
		<view class="font my-1" @click="openDetail">
			{
    {item.title}}
		view>
		
		<slot>
			
			<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;" @click="openDetail">image>
		slot>
		
		<view class="flex align-center">
			
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('support')" :class="item.support.type === 'support' ? 'support-active' : ''">
				<text class="iconfont icon-dianzan mr-2">text>
				<text>{
    {item.support.support_count > 0 ? item.support.support_count : '支持'}}text>
			view>
			
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('unsupport')" :class="item.support.type === 'unsupport' ? 'support-active' : ''">
				<text class="iconfont icon-cai mr-2">text>
				<text>{
    {item.support.unsupport_count > 0 ? item.support.unsupport_count : '反对'}}text>
			view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doComment">
				<text class="iconfont icon-pinglun mr-2">text>
				<text>{
    {item.comment_count > 0 ? item.comment_count : '评论'}}text>
			view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doShare">
				<text class="iconfont icon-fenxiang mr-2">text>
				<text>{
    {item.share_count > 0 ? item.share_count : '分享'}}text>
			view>
		view>
	view>
template>

<script>
	export default {
      
		props: {
      
			item: Object,
			index: Number,
			isdetail: {
      
				type: Boolean,
				default: false
			}
		},
		methods: {
      
			// 打开个人空间
			openSpace() {
      
				console.log('Space opened');
			},
			// 关注
			follow() {
      
				console.log('Followed');
				// 通知父组件
				this.$emit('follow', this.index);
			},
			// 进入详情页
			openDetail() {
      
				// 处于详情页
				if (this.isdetail) return;
				uni.navigateTo({
      
					url: '../../pages/detail/detail?detail='+JSON.stringify(this.item),
				});
			},
			// 顶踩操作
			doSupport(type) {
      
				console.log(type);
				// 通知父组件
				this.$emit('doSupport', {
      
					type,
					index: this.index
				})
			},
			// 评论
			doComment() {
      
				if (!this.isdetail) {
      
					return this.openDetail();
				}
				this.$emit('doComment');
			},
			// 分享
			doShare() {
      
				if (!this.isdetail) {
      
					return this.openDetail();
				}
				this.$emit('doShare');
			}
		}
	}
script>

<style>
	.support-active {
      
		color: #FF4A6A;
	}
style>

detail.vue如下:

<template>
	<view>
		
		<common-list :item="info" isdetail @doComment="doComment" @doShare="doShare">
			帖子详情
		common-list>
	view>
template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      }
			}
		},
		components: {
      
			commonList
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}

		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			},
			// 点击评论
			doComment() {
      
				console.log('Commenting...');
			},
			// 点击分享
			doShare() {
      
				console.log('Sharing...');
			}
		}
	}
script>

<style>

style>

显示:

可以看到,在贴子列表页点击评论和分享按钮时进入帖子详情页,在帖子详情页点击时会触发评论和分享事件。

3.详情页关注和顶踩功能完善

在详情页实现关注和顶踩功能,如下:

<template>
	<view>
		
		<common-list :item="info" isdetail @doComment="doComment" @doShare="doShare" @follow="follow" @doSupport="doSupport">
			帖子详情
		common-list>
	view>
template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      }
			}
		},
		components: {
      
			commonList
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}
		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			},
			// 关注
			follow() {
      
				this.info.isFollow = true;
				uni.showToast({
      
					title: '关注成功'
				});
			},
			// 顶踩操作
			doSupport(e) {
      
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前操作过
				if(this.info.support.type === e.type) {
      
					return uni.showToast({
      
						title: '您已经' + msg + '过了'
					});
				}
				// 之前未操作过
				if(this.info.support.type === '') {
      
					this.info.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if(this.info.support.type === 'support' && e.type === 'unsupport') {
      
					this.info.support.support_count--;
					this.info.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if(this.info.support.type === 'unsupport' && e.type === 'support') {
      
					this.info.support.unsupport_count--;
					this.info.support.support_count++;
				}
				this.info.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				});
			},
			// 点击评论
			doComment() {
      
				console.log('Commenting...');
			},
			// 点击分享
			doShare() {
      
				console.log('Sharing...');
			}
		}
	}
script>

<style>

style>

index.vue如下:

// 切换选项
changeTab(index) {
     
    if (this.tabIndex === index) {
     
        return;
    }
    this.tabIndex = index;
    this.list = this.newsList[this.tabIndex].list;
    // 滚动到指定元素
    this.scrollInto = 'tab' + index;
},

// 获取数据
getData() {
     
    var arr = [];
    for (let i = 0; i < this.tabBars.length; i++) {
     
        // 生成列表模板
        let obj = {
     
            // 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
            loadmore: "上拉加载更多",
            list: []
        }
        if (i % 3 !== 2) {
     
            obj.list = test_data;
        }
        arr.push(obj)
    }
    this.newsList = arr;
    this.list = this.newsList[this.tabIndex].list;
},

显示:

显然,已经实现了在首页和详情页进行关注和顶踩功能。

4.帖子内容和图片展示

给帖子数据增加content字段用于保存帖子内容、images字段(数组)用于保存图片,index.vue中测试数据如下:

// 测试数据
const test_data = [{
     
        username: "Corley",
        userpic: "/static/img/userpic/12.jpg",
        newstime: "2021-01-24 上午11:30",
        isFollow: false,
        title: "uni-app入门教程",
        titlepic: "/static/img/datapic/42.jpg",
        support: {
     
            type: "support", // 顶
            support_count: 1,
            unsupport_count: 2
        },
        comment_count: 2,
        share_count: 2,
        content: 'uni-app是DCloud官方推出的使用Vue.js开发跨平台应用的前端框架,一套代码可编译到iOS、Android、微信小程序等多个平台,学习和开发成本较低。在进行uni-app开发之前需要先搭建环境,下载并安装HBuilderX、微信开发者工具;新建项目时选择类型,创建之后会自动生成项目的默认目录,可以通过多种方式编译运行。一个典型的项目包括App.vue、main.js等文件和pages、static等目录;uni-app遵守Vue单文件组件规范,vue文件包括模板、脚本和样式3个顶级语言块。更多内容可点击https://blog.csdn.net/CUFEECR/article/details/111088889。',
        images: [
            {
     
                url: 'https://img-blog.csdnimg.cn/20210202134847418.png'
            },
            {
     
                url: 'https://img-blog.csdnimg.cn/20210202135211479.png'
            }
        ]
    },
    {
     
        username: "Brittany",
        userpic: "/static/img/userpic/16.jpg",
        newstime: "2021-01-24 下午14:00",
        isFollow: false,
        title: "商业数据分析从入门到入职",
        support: {
     
            type: "unsupport", // 踩
            support_count: 2,
            unsupport_count: 3
        },
        comment_count: 5,
        share_count: 1
    },
    {
     
        username: "Jessica",
        userpic: "/static/img/userpic/7.jpg",
        newstime: "2021-01-24 下午14:44",
        isFollow: true,
        title: "Django+Vue开发生鲜电商平台",
        titlepic: "/static/img/datapic/11.jpg",
        support: {
     
            type: "", // 未操作
            support_count: 2,
            unsupport_count: 7
        },
        comment_count: 0,
        share_count: 2
    },
    {
     
        username: "Ashley",
        userpic: "/static/img/userpic/20.jpg",
        newstime: "2021-01-24 下午18:20",
        isFollow: true,
        title: "uni-app实战之社区交友APP",
        titlepic: "/static/img/datapic/30.jpg",
        support: {
     
            type: "support",
            support_count: 5,
            unsupport_count: 1
        },
        comment_count: 3,
        share_count: 0
    }
];

detail.vue如下:

<template>
	<view>
		
		<common-list :item="info" isdetail @doComment="doComment" @doShare="doShare" @follow="follow" @doSupport="doSupport">
			<view>{
    {info.content}}view>
			<view class="">
				<block v-for="(item, index) in info.images">
					<image :src="item.url" class="w-100" mode="widthFix" @click="preview(index)">image>
				block>
			view>
		common-list>
	view>
template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      }
			}
		},
		components: {
      
			commonList
		},
		computed: {
      
			imagesList() {
      
				return this.info.images.map(item=>item.url);
			}
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}
		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			},
			// 关注
			follow() {
      
				this.info.isFollow = true;
				uni.showToast({
      
					title: '关注成功'
				});
			},
			// 顶踩操作
			doSupport(e) {
      
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前操作过
				if(this.info.support.type === e.type) {
      
					return uni.showToast({
      
						title: '您已经' + msg + '过了'
					});
				}
				// 之前未操作过
				if(this.info.support.type === '') {
      
					this.info.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if(this.info.support.type === 'support' && e.type === 'unsupport') {
      
					this.info.support.support_count--;
					this.info.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if(this.info.support.type === 'unsupport' && e.type === 'support') {
      
					this.info.support.unsupport_count--;
					this.info.support.support_count++;
				}
				this.info.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				});
			},
			// 点击评论
			doComment() {
      
				console.log('Commenting...');
			},
			// 点击分享
			doShare() {
      
				console.log('Sharing...');
			},
			// 预览图片
			preview(index) {
      
				uni.previewImage({
      
					urls: this.imagesList,
					current: index
				})
			}
		}
	}
script>

<style>

style>

显示:

可以看到,显示了帖子内容,还可以预览图片。

5.评论输入框组件开发和封装

评论输入框和聊天页输入框类似,在components/common下新建组件bottom-input抽离底部输入框,如下:

<template>
	<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
		<input type="text" v-model="content" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" @confirm="submit()" />
		<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;" @click="submit()">view>
	view>
template>

<script>
	export default {
      
		data() {
      
			return {
      
				content: ''
			}
		},
		methods: {
      
			submit() {
      
				// 判断是否为空
				if (this.content === ''){
      
					return uni.showToast({
      
						title: '消息不能为空',
						icon: 'none'
					});
				}
				this.$emit('submit', this.content);
				// 清空输入框
				this.content = '';
			}
		},
	}
script>

<style>
style>

user-chat.vue如下:

<template>
	<view>
		
		<scroll-view scroll-y="true" style="position: absolute; left: 0; top: 0; right: 0; bottom: 100rpx;" :scroll-into-view="scrollInto" scroll-with-animation>
			<block v-for="(item, index) in list">
				<view :id="'chat'+index">
					<user-chat-list :item="item" :index="index" :preTime="index > 0 ? list[index-1].create_time : 0">user-chat-list>
				view>
			block>
		scroll-view>
		
		<bottom-input @submit="submit">bottom-input>
	view>
template>

<script>
	import userChatList from '@/components/user-chat/user-chat-list.vue';
	import bottomInput from '@/components/common/bottom-input.vue';
	export default {
      
		data() {
      
			return {
      
				list: [
					{
      
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '大佬,你好',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156712
					},
					{
      
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '我想请教一个关于uni-app的问题,不知道是否方便?',
						type: 'text',
						create_time: 1612156872
					},
					{
      
						user_id: 1,
						username: 'Natalia',
						avatar: '/static/img/userpic/11.jpg',
						data: '你好啊,大佬不敢当',
						type: 'text',
						create_time: 1612156905
					},
					{
      
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '有什么你就说吧',
						type: 'text',
						create_time: 1612157023
					},
					{
      
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '只要我会的都会解答',
						type: 'text',
						create_time: 1612157029
					},
					{
      
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '有几个问题',
						type: 'text',
						create_time: 1612157411
					},
					{
      
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '1.在导航栏上单击搜索输入监听搜索框的事件该写在什么位置啊,为什么我写的触发不了?',
						type: 'text',
						create_time: 1612157439
					},
					{
      
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '2.关注顶踩的动画css怎么获取到的啊?',
						type: 'text',
						create_time: 1612157455
					},
					{
      
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '3.首页开发最后代码写完,再点击关注和点赞,踩,就会报错。辛苦看一看啊',
						type: 'text',
						create_time: 1612157503
					},
					{
      
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '好的,我马上看',
						type: 'text',
						create_time: 1612157821
					}
				],
				scrollInto: '',
				username: ''
			}
		},
		components: {
      
			userChatList,
			bottomInput
		},
		onLoad(e) {
      
			console.log(e);
			this.username = e.username;
		},
		// 页面加载完成
		onReady() {
      
			this.pageToBottom();
			uni.setNavigationBarTitle({
      
			    title: this.username
			});
		},
		methods: {
      
			// 发送消息
			submit(data) {
      
				let obj = {
      
					user_id: 1,
					username: 'Corley',
					avatar: '/static/img/userpic/11.jpg',
					data: data,
					type: 'text',
					create_time: (new Date()).getTime()
				}
				this.list.push(obj);
				// 滚动到底部
				this.pageToBottom();
			},
			// 滚动到底部
			pageToBottom() {
      
				let lastIndex = this.list.length - 1;
				if (lastIndex < 0) return;
				this.scrollInto = 'chat' + lastIndex;
			}
		}
	}
script>

<style>

style>

detail.vue如下:

<template>
	<view>
		
		<common-list :item="info" isdetail @doComment="doComment" @doShare="doShare" @follow="follow" @doSupport="doSupport">
			<view>{
    {info.content}}view>
			<view class="">
				<block v-for="(item, index) in info.images">
					<image :src="item.url" class="w-100" mode="widthFix" @click="preview(index)">image>
				block>
			view>
		common-list>
		
		<view style="height: 100rpx;">view>
		<bottom-input @submit="submit">bottom-input>
	view>
template>

<script>
	import commonList from '@/components/common/common-list.vue';
	import bottomInput from '@/components/common/bottom-input.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      }
			}
		},
		components: {
      
			commonList,
			bottomInput
		},
		computed: {
      
			imagesList() {
      
				return this.info.images.map(item=>item.url);
			}
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}
		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			},
			// 关注
			follow() {
      
				this.info.isFollow = true;
				uni.showToast({
      
					title: '关注成功'
				});
			},
			// 顶踩操作
			doSupport(e) {
      
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前操作过
				if(this.info.support.type === e.type) {
      
					return uni.showToast({
      
						title: '您已经' + msg + '过了'
					});
				}
				// 之前未操作过
				if(this.info.support.type === '') {
      
					this.info.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if(this.info.support.type === 'support' && e.type === 'unsupport') {
      
					this.info.support.support_count--;
					this.info.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if(this.info.support.type === 'unsupport' && e.type === 'support') {
      
					this.info.support.unsupport_count--;
					this.info.support.support_count++;
				}
				this.info.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				});
			},
			// 点击评论
			doComment() {
      
				console.log('Commenting...');
			},
			// 点击分享
			doShare() {
      
				console.log('Sharing...');
			},
			// 预览图片
			preview(index) {
      
				uni.previewImage({
      
					urls: this.imagesList,
					current: index
				})
			},
			// 提交评论
			submit(data) {
      
				console.log(data);				
			}
		}
	}
script>

<style>

style>

显示:

可以看到,底部输入框组件封装成功,模拟出了添加评论。

6.评论列表组件开发

评论列表组件使用uni-app官方提供的模板,如下:


<view class="p-2 font-md font-weight-bold">
    最新评论 3
view>
<view class="uni-comment-list px-2">
    <view class="uni-comment-face">
        <image src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/images/[email protected]" mode="widthFix">image>
    view>
    <view class="uni-comment-body">
        <view class="uni-comment-top"><text>Oliviatext>
        view>
        <view class="uni-comment-content">大佬,写的不错view>
        <view class="uni-comment-date">
            <view>2天前view>
        view>
    view>
view>

显示:
uni-app实战之社区交友APP(8)搜索列表页和文章详情页开发_第1张图片

可以看到,实现了评论列表。

7.分享功能组件开发

分享功能也是使用uni-popup组件实现,类型是bottom从底部弹出,如下:


<uni-popup ref="popup" type="bottom">
    <view class="popup-content text-center py-2 font-md border-bottom border-light-secondary">分享到view>
    <view class="popup-content flex align-center">
        <block v-for="(item, index) in shareList" :key="index">
            <view  class="flex-1 flex flex-column align-center justify-center py-2" hover-class="bg-light">
                <view class="iconfont text-white font-lg rounded-circle flex align-center justify-center" :class="item.icon+' '+item.color" style="width: 100rpx; height: 100rpx;">view>
                <text class="font-sm mt-1 text-muted">{
    {item.name}}text>
            view>
        block>
    view>
    <view class="popup-content text-center py-2 font-md border-top border-light-secondary" hover-class="bg-light">取消view>
uni-popup>

<script>
	import commonList from '@/components/common/common-list.vue';
	import bottomInput from '@/components/common/bottom-input.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
      
		data() {
      
			return {
      
				// 当前帖子信息
				info: {
      },
				shareList: [
					{
      
						icon: 'icon-weixin',
						color: 'bg-green',
						name:'微信好友'
					},
					{
      
						icon: 'icon-pengyouquan',
						color: 'bg-brown',
						name:'朋友圈'
					},
					{
      
						icon: 'icon-weibo',
						color: 'bg-red',
						name:'微博'
					},
					{
      
						icon: 'icon-qq',
						color: 'bg-blue',
						name:'QQ好友'
					}
				]
			}
		},
		components: {
      
			commonList,
			bottomInput,
			uniPopup
		},
		computed: {
      
			imagesList() {
      
				return this.info.images.map(item => item.url);
			}
		},
		onLoad(e) {
      
			console.log(e);
			// 初始化操作
			if (e.detail) {
      
				this.__init(JSON.parse(e.detail));
			}
		},
		onNavigationBarButtonTap() {
      
			this.$refs.popup.open();
		},
		onBackPress() {
      
			this.$refs.popup.close();
		},
		methods: {
      
			__init(data) {
      
				// 修改标题
				uni.setNavigationBarTitle({
      
					title: data.title
				});
				// 请求API
				this.info = data;
			},
			// 关注
			follow() {
      
				this.info.isFollow = true;
				uni.showToast({
      
					title: '关注成功'
				});
			},
			// 顶踩操作
			doSupport(e) {
      
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前操作过
				if (this.info.support.type === e.type) {
      
					return uni.showToast({
      
						title: '您已经' + msg + '过了'
					});
				}
				// 之前未操作过
				if (this.info.support.type === '') {
      
					this.info.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (this.info.support.type === 'support' && e.type === 'unsupport') {
      
					this.info.support.support_count--;
					this.info.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (this.info.support.type === 'unsupport' && e.type === 'support') {
      
					this.info.support.unsupport_count--;
					this.info.support.support_count++;
				}
				this.info.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				});
			},
			// 点击评论
			doComment() {
      
				console.log('Commenting...');
			},
			// 点击分享
			doShare() {
      
				console.log('Sharing...');
			},
			// 预览图片
			preview(index) {
      
				uni.previewImage({
      
					urls: this.imagesList,
					current: index
				})
			},
			// 提交评论
			submit(data) {
      
				console.log(data);
			}
		}
	}
script>

为了本文项目练手所需,需要在https://www.iconfont.cn/中下载微信朋友圈微博QQ等图标,同时将icon.css和iconfont.ttf更新为最新状态。

显示:

可以看到,实现了图标。

再进一步封装为组件,在components/common目录下新建more-share组件,如下:

<template>
	<uni-popup ref="popup" type="bottom">
		<view class="popup-content text-center py-2 font-md border-bottom border-light-secondary">分享到view>
		<view class="popup-content flex align-center">
			<block v-for="(item, index) in shareList" :key="index">
				<view  class="flex-1 flex flex-column align-center justify-center py-2" hover-class="bg-light">
					<view class="iconfont text-white font-lg rounded-circle flex align-center justify-center" :class="item.icon+' '+item.color" style="width: 100rpx; height: 100rpx;">view>
					<text class="font-sm mt-1 text-muted">{
    {item.name}}text>
				view>
			block>
		view>
		<view class="popup-content text-center py-2 font-md border-top border-light-secondary" hover-class="bg-light">取消view>
	uni-popup>
template>

<script>
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
      
		data() {
      
			return {
      
				shareList: [
					{
      
						icon: 'icon-weixin',
						color: 'bg-green',
						name:'微信好友'
					},
					{
      
						icon: 'icon-pengyouquan',
						color: 'bg-brown',
						name:'朋友圈'
					},
					{
      
						icon: 'icon-weibo',
						color: 'bg-red',
						name:'微博'
					},
					{
      
						icon: 'icon-qq',
						color: 'bg-blue',
						name:'QQ好友'
					}
				]
			}
		},
		components: {
      
			uniPopup
		},
		methods: {
      
			open() {
      
				this.$refs.popup.open();
			},
			close() {
      
				this.$refs.popup.close();
			}
		}
	}
script>

<style>
style>

detail.vue如下:


<more-share ref="share">more-share>

onNavigationBarButtonTap() {
    this.$refs.share.open();
},
onBackPress() {
    this.$refs.share.close();
},

效果与之前相同。

分享接口可参考hello_uniapp参考项目下pages/API下的share页面,使用的是uni.getProvider()接口动态获取服务商,如下:

<template>
	<uni-popup ref="popup" type="bottom">
		<view class="popup-content text-center py-2 font-md border-bottom border-light-secondary">分享到view>
		<view class="popup-content flex align-center">
			<block v-for="(item, index) in providerList" :key="index">
				<view class="flex-1 flex flex-column align-center justify-center py-2" hover-class="bg-light" @tap="share(item)">
					<view class="iconfont text-white font-lg rounded-circle flex align-center justify-center" :class="item.icon+' '+item.color"
					 style="width: 100rpx; height: 100rpx;">view>
					<text class="font-sm mt-1 text-muted">{
    {item.name}}text>
				view>
			block>
		view>
		<view class="popup-content text-center py-2 font-md border-top border-light-secondary" hover-class="bg-light">取消view>
	uni-popup>
template>

<script>
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
      
		data() {
      
			return {
      
				providerList: [],
				title: 'share',
				shareText: 'uni-app是DCloud官方推出的使用Vue.js开发跨平台应用的前端框架,一套代码可编译到iOS、Android、微信小程序等多个平台,学习和开发成本较低。在进行uni-app开发之前需要先搭建环境,下载并安装HBuilderX、微信开发者工具;新建项目时选择类型,创建之后会自动生成项目的默认目录,可以通过多种方式编译运行。一个典型的项目包括App.vue、main.js等文件和pages、static等目录;uni-app遵守Vue单文件组件规范,vue文件包括模板、脚本和样式3个顶级语言块。更多内容可点击https://blog.csdn.net/CUFEECR/article/details/111088889。',
				href: "https://blog.csdn.net/CUFEECR",
				image: 'https://img-blog.csdnimg.cn/20210202134847418.png',
				shareType: 1
			}
		},
		components: {
      
			uniPopup
		},
		computed: {
      
			isDisableButton() {
      
				return function(item) {
      
					if (this.shareType === 0 && item.id === 'qq') {
      
						return true;
					}
					if (this.shareType === 5 && item.name !== '分享到微信好友') {
      
						return true;
					}
					return false;
				}
			}
		},
		mounted() {
      
			uni.getProvider({
      
				service: 'share',
				success: (e) => {
      
					console.log('success', e);
					let data = []
					for (let i = 0; i < e.provider.length; i++) {
      
						switch (e.provider[i]) {
      
							case 'weixin':
								data.push({
      
									name: '微信好友',
									icon: 'icon-weixin',
									color: 'bg-green',
									id: 'weixin',
									sort: 0
								})
								data.push({
      
									name: '朋友圈',
									icon: 'icon-pengyouquan',
									color: 'bg-brown',
									id: 'weixin',
									type: 'WXSenceTimeline',
									sort: 1
								})
								break;
							case 'sinaweibo':
								data.push({
      
									name: '微博',
									icon: 'icon-weibo',
									color: 'bg-red',
									id: 'sinaweibo',
									sort: 2
								})
								break;
							case 'qq':
								data.push({
      
									name: 'QQ好友',
									icon: 'icon-qq',
									color: 'bg-blue',
									id: 'qq',
									sort: 3
								})
								break;
							default:
								break;
						}
					}
					this.providerList = data.sort((x, y) => {
      
						return x.sort - y.sort
					});
				},
				fail: (e) => {
      
					console.log('获取分享通道失败', e);
					uni.showModal({
      
						content: '获取分享通道失败',
						showCancel: false
					})
				}
			});
		},
		onShareAppMessage() {
      
			return {
      
				title: this.shareText ? this.shareText : "欢迎体验uni-app",
				path: '/pages/tabBar/component/component',
				imageUrl: this.image ? this.image : 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/[email protected]'
			}
		},
		beforeDestroy() {
      
			this.shareText = 'uni-app可以同时发布成原生App、小程序、H5,邀请你一起体验!',
				this.href = 'https://uniapp.dcloud.io',
				this.image = '';
		},
		methods: {
      
			open() {
      
				this.$refs.popup.open();
			},
			close() {
      
				this.$refs.popup.close();
			},
			async share(e) {
      
				console.log('分享通道:' + e.id + '; 分享类型:' + this.shareType);

				if (!this.shareText && (this.shareType === 1 || this.shareType === 0)) {
      
					uni.showModal({
      
						content: '分享内容不能为空',
						showCancel: false
					})
					return;
				}

				if (!this.image && (this.shareType === 2 || this.shareType === 0)) {
      
					uni.showModal({
      
						content: '分享图片不能为空',
						showCancel: false
					})
					return;
				}

				let shareOPtions = {
      
					provider: e.id,
					scene: e.type && e.type === 'WXSenceTimeline' ? 'WXSenceTimeline' : 'WXSceneSession', //WXSceneSession”分享到聊天界面,“WXSenceTimeline”分享到朋友圈,“WXSceneFavorite”分享到微信收藏     
					type: this.shareType,
					success: (e) => {
      
						console.log('success', e);
						uni.showModal({
      
							content: '已分享',
							showCancel: false
						})
					},
					fail: (e) => {
      
						console.log('fail', e)
						uni.showModal({
      
							content: e.errMsg,
							showCancel: false
						})
					},
					complete: function() {
      
						console.log('分享操作结束!')
					}
				}

				switch (this.shareType) {
      
					case 0:
						shareOPtions.summary = this.shareText;
						shareOPtions.imageUrl = this.image;
						shareOPtions.title = '欢迎体验uniapp';
						shareOPtions.href = 'https://uniapp.dcloud.io';
						break;
					case 1:
						shareOPtions.summary = this.shareText;
						break;
					case 2:
						shareOPtions.imageUrl = this.image;
						break;
					case 5:
						shareOPtions.imageUrl = this.image ? this.image :
							'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/[email protected]'
						shareOPtions.title = '欢迎体验uniapp';
						shareOPtions.miniProgram = {
      
							id: 'gh_33446d7f7a26',
							path: '/pages/tabBar/component/component',
							webUrl: 'https://uniapp.dcloud.io',
							type: 0
						};
						break;
					default:
						break;
				}

				if (shareOPtions.type === 0 && plus.os.name === 'iOS') {
       //如果是图文分享,且是ios平台,则压缩图片 
					shareOPtions.imageUrl = await this.compress();
				}
				if (shareOPtions.type === 1 && shareOPtions.provider === 'qq') {
       //如果是分享文字到qq,则必须加上href和title
					shareOPtions.href = 'https://uniapp.dcloud.io';
					shareOPtions.title = '欢迎体验uniapp';
				}
				uni.share(shareOPtions);
			},
			compress() {
       //压缩图片 图文分享要求分享图片大小不能超过20Kb
				console.log('开始压缩');
				let img = this.image;
				return new Promise((res) => {
      
					var localPath = plus.io.convertAbsoluteFileSystem(img.replace('file://', ''));
					console.log('after' + localPath);
					// 压缩size
					plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
      
						entry.file((file) => {
       // 可通过entry对象操作图片 
							console.log('getFile:' + JSON.stringify(file));
							if (file.size > 20480) {
       // 压缩后size 大于20Kb
								plus.zip.compressImage({
      
									src: img,
									dst: img.replace('.jpg', '2222.jpg').replace('.JPG', '2222.JPG'),
									width: '10%',
									height: '10%',
									quality: 1,
									overwrite: true
								}, (event) => {
      
									console.log('success zip****' + event.size);
									let newImg = img.replace('.jpg', '2222.jpg').replace('.JPG', '2222.JPG');
									res(newImg);
								}, function(error) {
      
									uni.showModal({
      
										content: '分享图片太大,需要请重新选择图片!',
										showCancel: false
									})
								});
							}
						});
					}, (e) => {
      
						console.log('Resolve file URL failed: ' + e.message);
						uni.showModal({
      
							content: '分享图片太大,需要请重新选择图片!',
							showCancel: false
						})
					});
				})
			}
		}
	}
script>

<style>
style>

显示:

可以看到,实现了分享到微信好友和朋友圈。

总结

在进行uni-app开发时,因为官方提供了很多模板、组件和接口,可以实现常见的功能,我们可以在其基础上进行稍微的优化和改动,即可用于自己项目,例如评论列表模板、分享组件和API,这样可以大大加快开发效率。

你可能感兴趣的:(移动应用开发实战,uni-app实战,社区交友APP,搜索列表页和文章详情页开发)