uni-app实战之社区交友APP(10)登录、个人空间开发和动画优化

文章目录

  • 前言
  • 一、登录页开发
    • 1.pages.json配置
    • 2.登录页UI构建
    • 3.登录类型切换实现
    • 4.登录表单验证功能实现
    • 5.第三方登录组件功能实现
  • 二、个人空间开发
    • 1.pages.json配置
    • 2.头部组件开发
    • 3.个人空间选项卡开发
    • 4.选项卡内容切换实现
    • 5.个人空间操作菜单
  • 三、全局功能开发和动画优化
    • 1.清除缓存功能
    • 2.全局方法和配置封装
    • 3.监听网络状态
    • 4.资源热更新
    • 5.动画效果优化
  • 总结

前言

本文主要介绍了三方面内容:
登录页开发,包括页面配置、登录页UI构建、登录类型切换、登录表单验证和第三方登录组件开发;
个人空间开发,包括页面配置、头部组件开发、选项卡开发和切换,以及操作菜单;
全局功能开发和优化。

一、登录页开发

1.pages.json配置

新建登录页login,配置pages.json如下:

{
     
    "path" : "pages/login/login",
    "style" :                                                                                    
    {
     
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "app-plus": {
     
            "titleNView": false
        }
    }
    
}

其入口暂定在我的页面个人信息栏,my.vue如下:

<template>
	<view>
		<navigator url="../login/login">
			
			<view class="flex align-center p-2" hover-class="bg-light">
				<image src="/static/img/userpic/6.jpg" class="rounded-circle" style="width: 100rpx; height: 100rpx;">image>
				<view class="flex flex-column flex-1 px-2">
					<text class="font-lg font-weight-bold">Corleytext>
					<text class="font text-muted">总帖子13 今日发帖1text>
				view>
				<text class="iconfont icon-bangzhujinru">text>
			view>
		navigator>
		
		
		<view class="flex align-center px-3 py-2">
			<block v-for="(item, index) in myData" :key="index">
				<view class="flex flex-column align-center justify-center flex-1">
					<text class="font-lg font-weight-bold">{
    {item.num}}text>
					<text class="font text-muted">{
    {item.name}}text>
				view>
			block>
		view>
		
		<view class="px-3 py-2">
			<image src="/static/img/banner/banner3.jpg" mode="aspectFill" style="height: 170rpx; width: 100%;" class="rounded">image>
		view>
		
		<uni-list-item title="浏览历史" showExtraIcon>
			<text slot="icon" class="iconfont icon-liulan">text>
		uni-list-item>
		<uni-list-item title="社区认证" showExtraIcon>
			<text slot="icon" class="iconfont icon-huiyuanvip">text>
		uni-list-item>
		<uni-list-item title="审核帖子" showExtraIcon>
			<text slot="icon" class="iconfont icon-shenhe">text>
		uni-list-item>
	view>
template>

login.vue中配置顶部返回图标按钮,如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      }
		},
		components: {
      
			uniStatusBar
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			}
		}
	}
script>

<style>

style>

base.vue如下:

/* 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;
}

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

.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-2 {
     
	margin-top: 20rpx;
}

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

.mb-2 {
     
	margin-bottom: 20rpx;
}

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

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

/* padding */
.p-3 {
     
	padding: 30rpx;
}

.p-2 {
     
	padding: 20rpx;
}

.px-7 {
     
	padding-left: 70rpx;
	padding-right: 70rpx;
}

.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;
}

.pt-4 {
     
	padding-top: 40rpx;
}

.pt-2 {
     
	padding-top: 20rpx;
}

.pt-1 {
     
	padding-top: 10rpx;
}

.pb-3 {
     
	padding-bottom: 30rpx;
}

.pb-4 {
     
	padding-bottom: 40rpx;
}

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

.pl-3 {
     
	padding-left: 30rpx;
}

/* 边框 */
.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-right {
     
	text-align: right;
}

/* 文字换行溢出处理 */
.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;
}

common.css如下:

/* 本项目全局样式 */
/* 背景 */
.bg-main {
     
	background-color: #FF4A6A!important;
}

.bg-main-disabled {
     
	background-color: #F87F97!important;
}

/* 文本颜色 */
.text-main {
     
	color: #FF4A6A;
}

.text-secondry {
     
	color: #AAA8AB;
}

.text-dark {
     
	color: #333333;
}

.text-primary {
     
	color: #3EA0C3;
}

/* 下拉弹出框样式 */
.popup-content {
     
	background-color: #fff;
	padding: 7px;
}


使用了之前导入的 uni-app官方扩展组件uni-status-bar作为状态栏。
为了本文项目练手所需,需要在https://www.iconfont.cn/中下载关闭黑名单聊天等图标,同时将icon.css和iconfont.ttf更新为最新状态。

显示:

可以看到,实现了页面跳转。

2.登录页UI构建

登录页UI构建如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			账号密码登录
		view>

		<view class="px-2">
			<view class="mb-2">
				<input type="text" value="" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
			view>
			<view class="mb-2 flex align-stretch">
				<input type="text" value="" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
				<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
			view>
		view>

		<view class="py-2 py-3 px-2">
			<button class="bg-main text-white" style="border-radius: 50rpx; border: 0;" type="primary">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font">验证码登录view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<view class="flex align-center px-7 py-3">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weixin font-lg text-white bg-green flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-qq font-lg text-white bg-blue flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weibo font-lg text-white bg-red flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
		view>
		
		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      }
		},
		components: {
      
			uniStatusBar
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			}
		}
	}
script>

<style>

style>

显示:
uni-app实战之社区交友APP(10)登录、个人空间开发和动画优化_第1张图片

可以看到,实现了登录页UI布局。

3.登录类型切换实现

现实现验证码登录和账号密码登录切换,两者的切换通过布尔变量status来实现,如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			{
    {status ? '手机验证码登录' : '账号密码登录'}}
		view>

		<view class="px-2">
			<template v-if="!status">
				<view class="mb-2">
					<input type="text" value="" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" value="" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
				view>
			template>
			<template v-else>
				<view class="mb-2 flex align-stretch">
					<view class="border-bottom flex align-center justify-center font px-2">+86view>
					<input type="text" value="" placeholder="手机号" class="border-bottom p-2 flex-1" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" value="" placeholder="请输入验证码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font-sm text-white bg-main" style="width: 180rpx;">获取验证码view>
				view>
			template>
		view>

		<view class="py-2 py-3 px-2">
			<button class="bg-main text-white" style="border-radius: 50rpx; border: 0;" type="primary">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font" @click="changeStatus">
				{
    {status ? '账号密码登录' : '验证码登录'}}
			view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<view class="flex align-center px-7 py-3">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weixin font-lg text-white bg-green flex align-center justify-center rounded-circle"
				 style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-qq font-lg text-white bg-blue flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weibo font-lg text-white bg-red flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
		view>

		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      
				status: false
			}
		},
		components: {
      
			uniStatusBar
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			},
			// 切换登录方式
			changeStatus() {
      
				this.status = !this.status;
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了登录类型切换。

4.登录表单验证功能实现

需要对登录表单进行验证。
先实现输入框有未输入时按钮应处于禁用状态,都输入才能切换到可点击状态,使用计算属性实现。
如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			{
    {status ? '手机验证码登录' : '账号密码登录'}}
		view>

		<view class="px-2">
			<template v-if="!status">
				<view class="mb-2">
					<input type="text" v-model="username" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="password" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
				view>
			template>
			<template v-else>
				<view class="mb-2 flex align-stretch">
					<view class="border-bottom flex align-center justify-center font px-2">+86view>
					<input type="text" v-model="phone" placeholder="手机号" class="border-bottom p-2 flex-1" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="code" placeholder="请输入验证码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font-sm text-white bg-main" style="width: 180rpx;">获取验证码view>
				view>
			template>
		view>

		<view class="py-2 py-3 px-2">
			<button class="text-white" style="border-radius: 50rpx; border: 0;" :disabled="disabled" :class="disabled ? 'bg-main-disabled': 'bg-main'"
			 type="primary">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font" @click="changeStatus">
				{
    {status ? '账号密码登录' : '验证码登录'}}
			view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<view class="flex align-center px-7 py-3">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weixin font-lg text-white bg-green flex align-center justify-center rounded-circle"
				 style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-qq font-lg text-white bg-blue flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weibo font-lg text-white bg-red flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
		view>

		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      
				status: false,
				username: '',
				password: '',
				phone: '',
				code: ''
			}
		},
		components: {
      
			uniStatusBar
		},
		computed: {
      
			disabled() {
      
				if ((this.username === '' || this.password === '') && (this.phone === '' || this.code === '')) {
      
					return true;
				}
				return false;
			}
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			},
			// 初始化表单字段
			initForm() {
      
				this.username = '';
				this.password = '';
				this.phone = '';
				this.code = '';
			},
			// 切换登录方式
			changeStatus() {
      
				// 初始化表单
				this.initForm();
				this.status = !this.status;
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了输入验证。

再实现点击获取验证码,并对点击频率进行限制,如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			{
    {status ? '手机验证码登录' : '账号密码登录'}}
		view>

		<view class="px-2">
			<template v-if="!status">
				<view class="mb-2">
					<input type="text" v-model="username" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="password" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
				view>
			template>
			<template v-else>
				<view class="mb-2 flex align-stretch">
					<view class="border-bottom flex align-center justify-center font px-2">+86view>
					<input type="text" v-model="phone" placeholder="手机号" class="border-bottom p-2 flex-1" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="code" placeholder="请输入验证码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-white" :class="codeTime > 0 ? 'bg-main-disabled' : ' bg-main'" style="width: 180rpx;" @click="getCode()">{
    {codeTime > 0 ? codeTime+' s' : '获取验证码'}}view>
				view>
			template>
		view>

		<view class="py-2 py-3 px-2">
			<button class="text-white" style="border-radius: 50rpx; border: 0;" :disabled="disabled" :class="disabled ? 'bg-main-disabled': 'bg-main'"
			 type="primary">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font" @click="changeStatus">
				{
    {status ? '账号密码登录' : '验证码登录'}}
			view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<view class="flex align-center px-7 py-3">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weixin font-lg text-white bg-green flex align-center justify-center rounded-circle"
				 style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-qq font-lg text-white bg-blue flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weibo font-lg text-white bg-red flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
		view>

		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      
				status: false,
				username: '',
				password: '',
				phone: '',
				code: '',
				codeTime: 0
			}
		},
		components: {
      
			uniStatusBar
		},
		computed: {
      
			disabled() {
      
				if ((this.username === '' || this.password === '') && (this.phone === '' || this.code === '')) {
      
					return true;
				}
				return false;
			}
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			},
			// 初始化表单字段
			initForm() {
      
				this.username = '';
				this.password = '';
				this.phone = '';
				this.code = '';
			},
			// 切换登录方式
			changeStatus() {
      
				// 初始化表单
				this.initForm();
				this.status = !this.status;
			},
			// 获取验证码
			getCode() {
      
				// 防止重复点击
				if (this.codeTime > 0) {
      
					return;
				}
				// 倒计时
				this.codeTime = 60;
				let timer = setInterval(()=>{
      
					if (this.codeTime > 0){
      
						this.codeTime--;
					}
					else {
      
						clearInterval(timer);
					}
				}, 1000)
			}
		}
	}
script>

<style>

style>

显示:

可以看到实现了验证码倒计时,并且限制在倒计时时不能再点击触发事件。

再实现验证手机号码,验证成功后再发送短信验证码,如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			{
    {status ? '手机验证码登录' : '账号密码登录'}}
		view>

		<view class="px-2">
			<template v-if="!status">
				<view class="mb-2">
					<input type="text" v-model="username" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="password" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
				view>
			template>
			<template v-else>
				<view class="mb-2 flex align-stretch">
					<view class="border-bottom flex align-center justify-center font px-2">+86view>
					<input type="text" v-model="phone" placeholder="手机号" class="border-bottom p-2 flex-1" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="code" placeholder="请输入验证码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-white" :class="codeTime > 0 ? 'bg-main-disabled' : ' bg-main'" style="width: 180rpx;" @click="getCode()">{
    {codeTime > 0 ? codeTime+' s' : '获取验证码'}}view>
				view>
			template>
		view>

		<view class="py-2 py-3 px-2">
			<button class="text-white" style="border-radius: 50rpx; border: 0;" :disabled="disabled" :class="disabled ? 'bg-main-disabled': 'bg-main'"
			 type="primary" @click="submit()">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font" @click="changeStatus">
				{
    {status ? '账号密码登录' : '验证码登录'}}
			view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<view class="flex align-center px-7 py-3">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weixin font-lg text-white bg-green flex align-center justify-center rounded-circle"
				 style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-qq font-lg text-white bg-blue flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont icon-weibo font-lg text-white bg-red flex align-center justify-center rounded-circle" style="width: 100rpx; height: 100rpx;">view>
			view>
		view>

		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	export default {
      
		data() {
      
			return {
      
				status: false,
				username: '',
				password: '',
				phone: '',
				code: '',
				codeTime: 0
			}
		},
		components: {
      
			uniStatusBar
		},
		computed: {
      
			disabled() {
      
				if ((this.username === '' || this.password === '') && (this.phone === '' || this.code === '')) {
      
					return true;
				}
				return false;
			}
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			},
			// 初始化表单字段
			initForm() {
      
				this.username = '';
				this.password = '';
				this.phone = '';
				this.code = '';
			},
			// 切换登录方式
			changeStatus() {
      
				// 初始化表单
				this.initForm();
				this.status = !this.status;
			},
			// 获取验证码
			getCode() {
      
				// 防止重复点击
				if (this.codeTime > 0) {
      
					return;
				}
				// 验证手机号
				if(!this.validate()) return;
				// 倒计时
				this.codeTime = 60;
				let timer = setInterval(()=>{
      
					if (this.codeTime > 0){
      
						this.codeTime--;
					}
					else {
      
						clearInterval(timer);
					}
				}, 1000);
			},
			// 验证表单
			validate() {
      
				//手机号正则表达式
				var mPattern = /^1[345789]\d{9}$/;
				if(!mPattern.test(this.phone)) {
      
					uni.showToast({
      
						title: '手机号格式不正确'
					});
					return false;
				}
				return true;
			},
			// 提交
			submit() {
      
				// 表单验证
				if(!this.validate()) return;
				// 提交后端
				
				// 登录成功处理
			}
		}
	}
script>

<style>

style>

显示:

可以看到,只有手机号输入正确,才会发送验证码并进行验证登录。

5.第三方登录组件功能实现

现完善第三方登录,需要使用接口uni.getProvider(OBJECT),可以参考hello_uniapp演示项目提供的模板,即pages/API/login/login.vue。

components/common目录下新建other-login,如下:

<template>
	<view class="flex align-center px-7 py-3">
		<block v-for="(item, index) in providerList" :key="index">
			<view class="flex-1 flex align-center justify-center">
				<view class="iconfont font-lg text-white flex align-center justify-center rounded-circle" :class="item.icon+' '+item.bgColor"
				 style="width: 100rpx; height: 100rpx;">view>
			view>
		block>
	view>
template>

<script>
	export default {
      
		data() {
      
			return {
      
				providerList: []
			}
		},
		mounted() {
      
			uni.getProvider({
      
				service: 'oauth',
				success: (result) => {
      
					result = result.provider.splice(1);
					this.providerList = result.map((value) => {
      
						let providerName = '';
						let icon = '';
						let bgColor = '';
						switch (value) {
      
							case 'weixin':
								providerName = '微信登录'
								icon = 'icon-weixin'
								bgColor = 'bg-green'
								break;
							case 'qq':
								providerName = 'QQ登录'
								icon = 'icon-qq'
								bgColor = 'bg-blue'
								break;
							case 'sinaweibo':
								providerName = '新浪微博登录'
								icon = 'icon-weibo'
								bgColor = 'bg-red'
								break;
						}
						return {
      
							name: providerName,
							id: value,
							icon: icon,
							bgColor: bgColor
						}
					});
				},
				fail: (error) => {
      
					console.log('获取登录通道失败', error);
				}
			});
		}
	}
script>

<style>
style>

login.vue如下:

<template>
	<view>
		<uni-status-bar>uni-status-bar>
		<view>
			<view class="iconfont icon-guanbi flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"
			 hover-class="bg-light" @click="back">
			view>
		view>

		<view class="text-center" style="padding-top: 130rpx; padding-bottom: 70rpx; font-size: 55rpx;">
			{
    {status ? '手机验证码登录' : '账号密码登录'}}
		view>

		<view class="px-2">
			<template v-if="!status">
				<view class="mb-2">
					<input type="text" v-model="username" placeholder="昵称/手机号/邮箱" class="border-bottom p-2" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="password" placeholder="请输入密码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-muted" style="width: 150rpx;">忘记密码?view>
				view>
			template>
			<template v-else>
				<view class="mb-2 flex align-stretch">
					<view class="border-bottom flex align-center justify-center font px-2">+86view>
					<input type="text" v-model="phone" placeholder="手机号" class="border-bottom p-2 flex-1" />
				view>
				<view class="mb-2 flex align-stretch">
					<input type="text" v-model="code" placeholder="请输入验证码" class="border-bottom p-2 flex-1" />
					<view class="border-bottom flex align-center justify-center font text-white" :class="codeTime > 0 ? 'bg-main-disabled' : ' bg-main'"
					 style="width: 180rpx;" @click="getCode()">{
    {codeTime > 0 ? codeTime+' s' : '获取验证码'}}view>
				view>
			template>
		view>

		<view class="py-2 py-3 px-2">
			<button class="text-white" style="border-radius: 50rpx; border: 0;" :disabled="disabled" :class="disabled ? 'bg-main-disabled': 'bg-main'"
			 type="primary" @click="submit()">登录button>
		view>

		<view class="flex align-center justify-center pt-2 pb-4">
			<view class="text-primary font" @click="changeStatus">
				{
    {status ? '账号密码登录' : '验证码登录'}}
			view>
			<text class="text-muted mx-2">|text>
			<view class="text-primary font">登录遇到问题view>
		view>

		<view class="flex align-center justify-center">
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
			<view class="mx-2 text-muted font">社交帐号登录view>
			<view style="width: 120rpx; height: 3rpx; background-color: #DDDDDD;">view>
		view>

		<other-login>other-login>

		<view class="flex align-center justify-center text-muted">
			注册即代表同意<text class="text-primary font">《XXX社区协议》text>
		view>
	view>
template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue';
	import otherLogin from '@/components/common/other-login.vue';
	export default {
      
		data() {
      
			return {
      
				status: false,
				username: '',
				password: '',
				phone: '',
				code: '',
				codeTime: 0
			}
		},
		components: {
      
			uniStatusBar,
			otherLogin
		},
		computed: {
      
			disabled() {
      
				if ((this.username === '' || this.password === '') && (this.phone === '' || this.code === '')) {
      
					return true;
				}
				return false;
			}
		},
		methods: {
      
			back() {
      
				uni.navigateBack({
      
					delta: 1
				});
			},
			// 初始化表单字段
			initForm() {
      
				this.username = '';
				this.password = '';
				this.phone = '';
				this.code = '';
			},
			// 切换登录方式
			changeStatus() {
      
				// 初始化表单
				this.initForm();
				this.status = !this.status;
			},
			// 获取验证码
			getCode() {
      
				// 防止重复点击
				if (this.codeTime > 0) {
      
					return;
				}
				// 验证手机号
				if (!this.validate()) return;
				// 倒计时
				this.codeTime = 60;
				let timer = setInterval(() => {
      
					if (this.codeTime > 0) {
      
						this.codeTime--;
					} else {
      
						clearInterval(timer);
					}
				}, 1000);
			},
			// 验证表单
			validate() {
      
				//手机号正则表达式
				var mPattern = /^1[345789]\d{9}$/;
				if (!mPattern.test(this.phone)) {
      
					uni.showToast({
      
						title: '手机号格式不正确'
					});
					return false;
				}
				return true;
			},
			// 提交
			submit() {
      
				// 表单验证
				if (!this.validate()) return;
				// 提交后端

				// 登录成功处理
			}
		}
	}
script>

<style>

style>

可以达到之前同样的效果,但是此时更加灵活。

二、个人空间开发

1.pages.json配置

先搭建个人空间页面user-space,并配置pages.json如下:

{
     
    "path" : "pages/user-space/user-space",
    "style" :                                                                                    
    {
     
        "navigationBarTitleText": "个人空间",
        "enablePullDownRefresh": false,
        "app-plus": {
     
            "titleNView": {
     
                "buttons":[
                    {
     
                        "type":"menu"
                    }
                ]
            }
        }
    }
    
}

个人空间入口为common-list组件,点击用户头像时即进入对应用户的个人空间,common-list.vue如下:

// 打开个人空间
openSpace() {
     
    uni.navigateTo({
     
        url: '/pages/user-space/user-space'
    });
},

显示:
uni-app实战之社区交友APP(10)登录、个人空间开发和动画优化_第2张图片

可以看到,配置成功。

2.头部组件开发

开发头部组件如下:

<template>
	<view>
		
		<view class="flex align-center p-3 border-bottom">
			<image src="/static/img/userpic/5.jpg" style="width: 180rpx; height: 180rpx;" class="rounded-circle">image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">2text>
						<text class="font text-muted">获赞text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">1text>
						<text class="font text-muted">关注text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">6text>
						<text class="font text-muted">粉丝text>
					view>
				view>
				<view class="flex align-center justify-center">
					<button type="primary" size="mini" class="bg-main" style="width: 400rpx;">关注button>
				view>
			view>
		view>
	view>
template>

<script>
	export default {
      
		data() {
      
			return {
      
				
			}
		},
		methods: {
      
			
		}
	}
script>

<style>

style>

显示:
uni-app实战之社区交友APP(10)登录、个人空间开发和动画优化_第3张图片

可以看到,实现了头部组件。

3.个人空间选项卡开发

个人空间选项卡和我的好友选项卡类似,如下:

<template>
	<view>
		
		<view class="flex align-center p-3 border-bottom">
			<image src="/static/img/userpic/5.jpg" style="width: 180rpx; height: 180rpx;" class="rounded-circle">image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">2text>
						<text class="font text-muted">获赞text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">1text>
						<text class="font text-muted">关注text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">6text>
						<text class="font text-muted">粉丝text>
					view>
				view>
				<view class="flex align-center justify-center">
					<button type="primary" size="mini" class="bg-main" style="width: 400rpx;">关注button>
				view>
			view>
		view>
		
		
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{
    {item.name}}view>
		view>
	view>
template>

<script>
	export default {
      
		data() {
      
			return {
      
				tabIndex: 0,
				tabBars: [{
      
						name: '主页'
					},
					{
      
						name: '帖子'
					},
					{
      
						name: '动态'
					}
				],
			}
		},
		methods: {
      
			changeTab(index) {
      
				this.tabIndex = index;
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了选项卡。

4.选项卡内容切换实现

现实现点击主页选项卡显示首页信息、点击帖子选项卡显示帖子列表、点击动态选项卡显示动态列表。

先实现主页,如下:

<template>
	<view>
		
		<view class="flex align-center p-3 border-bottom">
			<image src="/static/img/userpic/5.jpg" style="width: 180rpx; height: 180rpx;" class="rounded-circle">image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">2text>
						<text class="font text-muted">获赞text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">1text>
						<text class="font text-muted">关注text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">6text>
						<text class="font text-muted">粉丝text>
					view>
				view>
				<view class="flex align-center justify-center">
					<button type="primary" size="mini" class="bg-main" style="width: 400rpx;">关注button>
				view>
			view>
		view>
		
		
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{
    {item.name}}view>
		view>
		
		<template v-if="tabIndex === 0">
			<view class="p-3 border-bottom">
				<view class="font-md">账号信息view>
				<view class="font">账号年龄:3个月view>
				<view class="font">账号id:1view>
			view>
			<view class="p-3 border-bottom">
				<view class="font-md">个人信息view>
				<view class="font">星座:天蝎座view>
				<view class="font">职业:ITview>
				<view class="font">家乡:江苏苏州view>
				<view class="font">情感:保密view>
			view>
		template>
		<template v-else-if="tabIndex === 1">
			<view>帖子view>
		template>
		<template v-else>
			<view>动态view>
		template>
	view>
template>

显示:
uni-app实战之社区交友APP(10)登录、个人空间开发和动画优化_第4张图片

再实现帖子和动态,如下:

<template>
	<view>
		
		<view class="flex align-center p-3 border-bottom">
			<image src="/static/img/userpic/5.jpg" style="width: 180rpx; height: 180rpx;" class="rounded-circle">image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">2text>
						<text class="font text-muted">获赞text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">1text>
						<text class="font text-muted">关注text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">6text>
						<text class="font text-muted">粉丝text>
					view>
				view>
				<view class="flex align-center justify-center">
					<button type="primary" size="mini" class="bg-main" style="width: 400rpx;">关注button>
				view>
			view>
		view>
		
		
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{
    {item.name}}view>
		view>
		
		<template v-if="tabIndex === 0">
			<view class="animate__animated animate__fast animate__fadeIn">
				<view class="p-3 border-bottom">
					<view class="font-md">账号信息view>
					<view class="font">账号年龄:3个月view>
					<view class="font">账号id:1view>
				view>
				<view class="p-3 border-bottom">
					<view class="font-md">个人信息view>
					<view class="font">星座:天蝎座view>
					<view class="font">职业:ITview>
					<view class="font">家乡:江苏苏州view>
					<view class="font">情感:保密view>
				view>
			view>
		template>
		<template v-else>
			<view class="animate__animated animate__fast animate__fadeIn">
				<block v-for="(item, index) in list" :key="index">
					<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport">common-list>
					<divider>divider>
				block>
				<load-more :loadmore="loadmore">load-more>
			view>
		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,
			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
		}
	];
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	export default {
      
		data() {
      
			return {
      
				tabIndex: 0,
				tabBars: [{
      
						name: '主页'
					},
					{
      
						name: '帖子',
						list: test_data,
						loadmore: '上拉加载更多'
					},
					{
      
						name: '动态',
						list: test_data,
						loadmore: '上拉加载更多'
					}
				],
			}
		},
		components: {
      
			commonList,
			loadMore
		},
		computed: {
      
			list() {
      
				return this.tabBars[this.tabIndex].list; 
			},
			loadmore() {
      
				return this.tabBars[this.tabIndex].loadmore; 
			},
		},
		methods: {
      
			changeTab(index) {
      
				this.tabIndex = index;
			},
			// 关注
			follow(e) {
      
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
      
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
      
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				console.log(0);
				// 之前未顶踩过
				if (item.support.type === '') {
      
					console.log(1);
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
      
					
					console.log(2);
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
      
					console.log(3);
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				})
			},
		}
	}
script>

<style>

style>

显示:

可以看到,实现了帖子和动态栏。

5.个人空间操作菜单

现实现点击右上角菜单按钮弹出操作菜单,可参考msg.vue。

如下:

<template>
	<view>
		
		<view class="flex align-center p-3 border-bottom">
			<image src="/static/img/userpic/5.jpg" style="width: 180rpx; height: 180rpx;" class="rounded-circle">image>
			<view class="pl-3 flex flex-column flex-1">
				<view class="flex align-center">
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">2text>
						<text class="font text-muted">获赞text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">1text>
						<text class="font text-muted">关注text>
					view>
					<view class="flex-1 flex flex-column align-center justify-center">
						<text class="font-lg font-weight-bold">6text>
						<text class="font text-muted">粉丝text>
					view>
				view>
				<view class="flex align-center justify-center">
					<button type="primary" size="mini" class="bg-main" style="width: 400rpx;">关注button>
				view>
			view>
		view>
		
		
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{
    {item.name}}view>
		view>
		
		<template v-if="tabIndex === 0">
			<view class="animate__animated animate__fast animate__fadeIn">
				<view class="p-3 border-bottom">
					<view class="font-md">账号信息view>
					<view class="font">账号年龄:3个月view>
					<view class="font">账号id:1view>
				view>
				<view class="p-3 border-bottom">
					<view class="font-md">个人信息view>
					<view class="font">星座:天蝎座view>
					<view class="font">职业:ITview>
					<view class="font">家乡:江苏苏州view>
					<view class="font">情感:保密view>
				view>
			view>
		template>
		<template v-else>
			<view class="animate__animated animate__fast animate__fadeIn">
				<block v-for="(item, index) in list" :key="index">
					<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport">common-list>
					<divider>divider>
				block>
				<load-more :loadmore="loadmore">load-more>
			view>
		template>
		
		
		<uni-popup ref="popup" type="top">
			<view class="popup-content flex align-center justify-center font-md border-bottom" hover-class="bg-light">
				<text class="iconfont icon-heimingdan mr-2">text> 加入黑名单
			view>
			<view class="popup-content flex align-center justify-center font-md" hover-class="bg-light">
				<text class="iconfont icon-conversation_icon mr-2">text> 聊天
			view>
		uni-popup>
	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,
			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
		}
	];
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
      
		data() {
      
			return {
      
				tabIndex: 0,
				tabBars: [{
      
						name: '主页'
					},
					{
      
						name: '帖子',
						list: test_data,
						loadmore: '上拉加载更多'
					},
					{
      
						name: '动态',
						list: test_data,
						loadmore: '上拉加载更多'
					}
				],
			}
		},
		components: {
      
			commonList,
			loadMore,
			uniPopup
		},
		computed: {
      
			list() {
      
				return this.tabBars[this.tabIndex].list; 
			},
			loadmore() {
      
				return this.tabBars[this.tabIndex].loadmore; 
			}
		},
		onNavigationBarButtonTap() {
      
			this.$refs.popup.open();
		},
		methods: {
      
			changeTab(index) {
      
				this.tabIndex = index;
			},
			// 关注
			follow(e) {
      
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
      
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
      
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				console.log(0);
				// 之前未顶踩过
				if (item.support.type === '') {
      
					console.log(1);
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
      
					
					console.log(2);
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
      
					console.log(3);
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
      
					title: msg + '成功'
				})
			},
		}
	}
script>

<style>

style>

显示:

可以看到,实现了下拉弹出框显示加入黑名单和聊天。

三、全局功能开发和动画优化

1.清除缓存功能

需要通过uni.getStorageInfoSync()接口获取缓存信息、并显示缓存大小,并使用uni.clearStorageSync()接口清除缓存。

user-set.vue如下:

<template>
	<view>
		<uni-list-item title="账号与安全" :clickable="true" :showIcon="true" @click="open('user-password')">uni-list-item>
		<uni-list-item title="绑定邮箱" :clickable="true" :showIcon="true" @click="open('user-email')">uni-list-item>
		<uni-list-item title="资料编辑" :clickable="true" :showIcon="true" @click="open('user-userinfo')">uni-list-item>
		<uni-list-item title="清除缓存" :clickable="true" :showIcon="true" @click="clearCache()">
			<view slot="footer" class="text-muted">
				{
    {currentSize | format}}
			view>
		uni-list-item>
		<uni-list-item title="意见反馈" :clickable="true" :showIcon="true" @click="open('user-feedback')">uni-list-item>
		<uni-list-item title="关于社区" :clickable="true" :showIcon="true" @click="open('about')">uni-list-item>
		<view class="py-2 py-3 px-2">
			<button class="bg-main text-white" style="border-radius: 50rpx; border: 0;" type="primary">退出登录button>
		view>
	view>
template>

<script>
	import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue';
	export default {
      
		data() {
      
			return {
      
				currentSize: 0
			}
		},
		components: {
      
			uniListItem
		},
		filters: {
      
			format(value) {
      
				return value > 1024 ? (value / 1024).toFixed(2) + 'MB' : value.toFixed(2) + 'KB';
			}
		},
		onLoad() {
      
			this.getStorageInfo();
		},
		methods: {
      
			open(path) {
      
				uni.navigateTo({
      
					url: `../${
        path}/${
        path}`
				});
			},
			// 获取缓存数据
			getStorageInfo() {
      
				let res = uni.getStorageInfoSync();
				console.log(res);
				this.currentSize = res.currentSize;
			},
			// 清除缓存
			clearCache() {
      
				uni.showModal({
      
					title: '清除缓存提示',
					content: '是否要清除所有缓存?',
					showCancel: true,
					cancelText: '不清除',
					confirmText: '清除',
					success: res => {
      
						if (res.confirm) {
      
							uni.clearStorageSync();
							this.getStorageInfo();
							uni.showToast({
      
								title: '清除成功'
							});
						}
					}
				});
			}
		}
	}
script>

<style>

style>

显示:

可以看到,实现了清除缓存。

2.全局方法和配置封装

在common目录下新建config.js文件,作为本项目配置文件,如下:

export default {
     
	// API请求前缀
	webUrl: 'http://127.0.0.1',
	// websocket地址
	webSocketUrl: 'http://127.0.0.1/wss'
}

同时在main.js中引入配置文件,如下:

import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

// 引入全局组件
import divider from './components/common/divider.vue';
Vue.component('divider', divider)
import noThing from './components/common/no-thing.vue';
Vue.component('no-thing', noThing)

// 引入配置文件
import $C from './common/config.js';
Vue.prototype.$C = $C

App.mpType = 'app'

const app = new Vue({
     
    ...App
})
app.$mount()

此时即可在某个页面中通过this来使用某些配置项,例如this.$C.webSocketUrl等。
同样,在common目录下新建工具(助手)函数库util.js,用来保存常见的工具函数,并在main.js中挂载:

import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

// 引入全局组件
import divider from './components/common/divider.vue';
Vue.component('divider', divider)
import noThing from './components/common/no-thing.vue';
Vue.component('no-thing', noThing)

// 引入配置文件
import $C from './common/config.js';
Vue.prototype.$C = $C

// 挂载工具函数库
import $U from './common/util.js';
Vue.prototype.$U = $U

App.mpType = 'app'

const app = new Vue({
     
    ...App
})
app.$mount()

3.监听网络状态

监听网络状态使用接口uni.getNetworkType(OBJECT)uni.onNetworkStatusChange(CALLBACK),主要在App.vue中实现,如下:

<script>
	export default {
      
		onLaunch: function() {
      
			console.log('App Launch')
			// 获取网络状态
			uni.getNetworkType({
      
			    success: function (res) {
      
			        console.log(res);
					if (res.networkType=== 'none') {
      
						uni.showToast({
      
							title: '当前处于断网状态'
						});
					}
			    }
			});
			// 监听网络状态
			uni.onNetworkStatusChange(function (res) {
      
			    console.log(res);
			    if (res.networkType=== 'none') {
      
			    	uni.showToast({
      
			    		title: '当前处于断网状态,请连接网络'
			    	});
			    }
			});
		},
		onShow: function() {
      
			console.log('App Show')
		},
		onHide: function() {
      
			console.log('App Hide')
		}
	}
script>

<style>
	/*每个页面公共css */
	/* 官方CSS库 */
	@import url("./common/uni.css");
	/* 自定义图标库 */
	@import url("./common/icon.css");
	/* 动画库 */
	@import url("./common/animate.css");
	/* 自定义样式库 */
	@import url("./common/base.css");
	/* 全局样式 */
	@import url("./common/common.css");
style>

当处于断网状态时,就会发出提醒。

还可以将其封装到工具函数库util.js中,如下:

export default {
     
	// 监听网络
	onNetwork() {
     
		let func = (res) => {
     
			console.log(res);
			if (res.networkType === 'none') {
     
				uni.showToast({
     
					title: '当前处于断网状态,请连接网络'
				});
			}
		}
		// 获取网络状态
		uni.getNetworkType({
     
			success: func
		});
		// 监听网络状态
		uni.onNetworkStatusChange(func);
	}
}

App.vue如下:

<script>
	export default {
      
		onLaunch: function() {
      
			console.log('App Launch')
			// 网络监听
			this.$U.onNetwork();
		},
		onShow: function() {
      
			console.log('App Show')
		},
		onHide: function() {
      
			console.log('App Hide')
		}
	}
script>

效果相同,但是App.vue中代码更简洁。

4.资源热更新

热更新即打开App时遇到的即时更新,uni-app也可以实现资源在线升级(热更新),一般需要在启动APP时或者新版本检测页面实现热更新。
可参考https://ask.dcloud.net.cn/article/35667。

热更新也在util.js中实现,如下:

export default {
     
	// 监听网络
	onNetwork() {
     
		let func = (res) => {
     
			console.log(res);
			if (res.networkType === 'none') {
     
				uni.showToast({
     
					title: '当前处于断网状态,请连接网络'
				});
			}
		}
		// 获取网络状态
		uni.getNetworkType({
     
			success: func
		});
		// 监听网络状态
		uni.onNetworkStatusChange(func);
	},
	// 热更新
	update() {
     
		// #ifdef APP-PLUS  
		plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
     
			uni.request({
     
				url: 'http://127.0.0.1/update/',
				data: {
     
					version: widgetInfo.version,
					name: widgetInfo.name
				},
				success: (result) => {
     
					var data = result.data;
					if (data.update && data.wgtUrl) {
     
						uni.downloadFile({
     
							url: data.wgtUrl,
							success: (downloadResult) => {
     
								if (downloadResult.statusCode === 200) {
     
									plus.runtime.install(downloadResult.tempFilePath, {
     
										force: false
									}, function() {
     
										console.log('install success...');
										plus.runtime.restart();
									}, function(e) {
     
										console.error('install fail...');
									});
								}
							}
						});
					}
				}
			});
		});
		// #endif
	}
}

App.vue如下::

<script>
	export default {
      
		onLaunch: function() {
      
			console.log('App Launch')
			// 网络监听
			this.$U.onNetwork();
			// 检测更新
			this.$U.update();
		},
		onShow: function() {
      
			console.log('App Show')
		},
		onHide: function() {
      
			console.log('App Hide')
		}
	}
script>

5.动画效果优化

现给帖子列表加载添加动画效果,common-list.vue如下:

<template>
	<view class="p-2 animate__animated animate__fast animate__fadeIn">
		
		<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>

再给消息列表添加动画效果,如下:

<template>
	<view class="flex align-center p-2 border-bottom animate__animated animate__fast animate__fadeIn" hover-class="bg-light" @click="open()">
		<image :src="item.avatar" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2">image>
		<view class="flex flex-column flex-1">
			<view class="flex align-center justify-between">
				<text class="font-md">{
    {item.username}}text>
				<text class="font-sm text-secondry">{
    {item.update_time | formatTime}}text>
			view>
			<view class="flex align-center justify-between">
				<text class="text-secondry text-ellipsis" style="max-width: 500rpx;">{
    {item.data}}text>
				<uni-badge :text="item.noread" type="error">uni-badge>
			view>
		view>
	view>
template>

显示:

可以看到,给帖子列表和消息列表添加了淡入效果。

总结

Vue有生命周期,可以进行相应的操作,uni-app在此基础上细分为应用生命周期、页面生命周期和组件生命周期,为开发者提供了很多便利。同时全局配置有利于优化项目结构、减少冗余代码。

你可能感兴趣的:(移动应用开发实战,uni-app实战,社区交友APP,登录,个人空间开发和动画优化)