前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >uni-app实战之社区交友APP(8)搜索列表页和文章详情页开发

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

作者头像
cutercorley
发布2021-02-04 11:06:55
2.3K0
发布2021-02-04 11:06:55
举报
文章被收录于专栏:Corley的开发笔记

文章目录

前言

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

一、搜索列表页开发

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

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

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

代码语言:javascript
复制
// 监听导航栏搜索框
onNavigationBarSearchInputClicked() {
    uni.navigateTo({
        url: '../search/search?type=post'
    })
},

news.vue如下:

代码语言:javascript
复制
<!-- 搜索框 -->
<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如下:

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

msg.vue如下:

代码语言:javascript
复制
// 弹出层选项点击事件
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完善搜索类型和占位符,如下:

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
/* 内外边距 */
.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如下:

代码语言:javascript
复制
<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中配置页面右上角的菜单按钮,如下:

代码语言:javascript
复制
{
    "path" : "pages/detail/detail",
    "style" :                                                                                    
    {
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "app-plus": {
            "titleNView": {
                "buttons": [
                    {
                        "type":"menu",
                        "float":"right"
                    }
                ]
            }
        }
    }
    
}

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

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

detail.vue修改如下:

代码语言:javascript
复制
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如下:

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
<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组件的复用。

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

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
<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.详情页关注和顶踩功能完善

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

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
// 切换选项
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中测试数据如下:

代码语言:javascript
复制
// 测试数据
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如下:

代码语言:javascript
复制
<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抽离底部输入框,如下:

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
<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官方提供的模板,如下:

代码语言:javascript
复制
<!-- 评论 -->
<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/uni@2x.png" mode="widthFix"></image>
    </view>
    <view class="uni-comment-body">
        <view class="uni-comment-top"><text>Olivia</text>
        </view>
        <view class="uni-comment-content">大佬,写的不错</view>
        <view class="uni-comment-date">
            <view>2天前</view>
        </view>
    </view>
</view>

显示:

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

7.分享功能组件开发

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

代码语言:javascript
复制
<!-- 分享组件 -->
<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组件,如下:

代码语言:javascript
复制
<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如下:

代码语言:javascript
复制
<!-- 分享组件 -->
<more-share ref="share"></more-share>

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

效果与之前相同。

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

代码语言:javascript
复制
<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/share-logo@3.png'
			}
		},
		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/share-logo@3.png'
						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,这样可以大大加快开发效率。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、搜索列表页开发
    • 1.搜索类型传递和占位符设置
      • 2.搜索功能实现
      • 二、帖子详情页开发
        • 1.pages.json配置和页面通信
          • 2.公共列表组件功能优化
            • 3.详情页关注和顶踩功能完善
              • 4.帖子内容和图片展示
                • 5.评论输入框组件开发和封装
                  • 6.评论列表组件开发
                    • 7.分享功能组件开发
                    • 总结
                    相关产品与服务
                    云开发 CloudBase
                    云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档