本文主要介绍了三方面内容:
登录页开发,包括页面配置、登录页UI构建、登录类型切换、登录表单验证和第三方登录组件开发;
个人空间开发,包括页面配置、头部组件开发、选项卡开发和切换,以及操作菜单;
全局功能开发和优化。
新建登录页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更新为最新状态。
显示:
可以看到,实现了页面跳转。
登录页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>
可以看到,实现了登录页UI布局。
现实现验证码登录和账号密码登录切换,两者的切换通过布尔变量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>
显示:
可以看到,实现了登录类型切换。
需要对登录表单进行验证。
先实现输入框有未输入时按钮应处于禁用状态,都输入才能切换到可点击状态,使用计算属性实现。
如下:
<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>
显示:
可以看到,只有手机号输入正确,才会发送验证码并进行验证登录。
现完善第三方登录,需要使用接口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>
可以达到之前同样的效果,但是此时更加灵活。
先搭建个人空间页面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'
});
},
可以看到,配置成功。
开发头部组件如下:
<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>
可以看到,实现了头部组件。
个人空间选项卡和我的好友选项卡类似,如下:
<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>
显示:
可以看到,实现了选项卡。
现实现点击主页选项卡显示首页信息、点击帖子选项卡显示帖子列表、点击动态选项卡显示动态列表。
先实现主页,如下:
<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>
再实现帖子和动态,如下:
<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>
显示:
可以看到,实现了帖子和动态栏。
现实现点击右上角菜单按钮弹出操作菜单,可参考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>
显示:
可以看到,实现了下拉弹出框显示加入黑名单和聊天。
需要通过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>
显示:
可以看到,实现了清除缓存。
在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()
监听网络状态使用接口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中代码更简洁。
热更新即打开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>
现给帖子列表加载添加动画效果,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在此基础上细分为应用生命周期、页面生命周期和组件生命周期,为开发者提供了很多便利。同时全局配置有利于优化项目结构、减少冗余代码。