uniapp社区交友开发前端模块开发
源码可以提供下载,详情访问末尾码云地址
环境搭建和创建项目
开发环境搭建
- 使用HubilderX
- 安装对应插件
创建uniapp项目
- 创建项目(名称:社区交友)
- 真机调试或微信开发者工具调试
App.vue引入全局公共样式
引入官方css样式库
-
新建模板项目hello uniapp
-
复制模板common下的css
-
在本项目的app.vue进行引入css文件 @import “./common/uni.css ” (还要引入uni.tff文件,否则报错)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xizHMjdI-1649380676444)(uniapp社区交友开发.assets/image-20220322164136184-16479384973031.png)]
引入自定义图标
-
阿里巴巴矢量图标库 https://www.iconfont.cn/
-
下载所选图标至项目打包 得到压缩文件
-
修改icon.css文件去掉url,引入文件 测试图标使用
<view> <text class="iconfont icon-smile" style="font-size: 100rpx; color: red;"></text> </view>
引入css动画库
-
下载animate.css
-
引入animate.css
-
测试css动画库的使用
<view style="display: flex;justify-content: center;padding: 50rpx;"> <view class="" hover-class=" animated rubberBand" style="border: 1rpx solid black; padding: 20rpx;"> 点击效果 </view> </view>
设置全局属性globalStyle
-
解析page.json文件 看官方文档
-
设置导航栏的样式
"globalStyle": { "navigationBarTextStyle": "black", //导航栏字体颜色 "navigationBarTitleText": "社区交友",//导航栏文字 "navigationBarBackgroundColor": "#FFF",//背景颜色 "backgroundColor": "#FFF" }
底部导航开发
-
设置图标(图片为png,81*81)用矢量图标库下载
-
配置tabBar前提要配置pages数组页面
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages { "path": "pages/index/index", "style": { } } ,{ "path" : "pages/news/news", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ,{ "path" : "pages/msg/msg", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ,{ "path" : "pages/my/my", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ], "tabBar": { "color": "#323232", "selectedColor": "#FC5C82", "backgroundColor": "#FFF", "borderStyle": "black", "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tabbar/index.png", "selectedIconPath": "static/tabbar/indexed.png" }, { "pagePath": "pages/news/news", "text": "动态", "iconPath": "static/tabbar/news.png", "selectedIconPath": "static/tabbar/newsed.png" }, { "pagePath": "pages/msg/msg", "text": "消息", "iconPath": "static/tabbar/paper.png", "selectedIconPath": "static/tabbar/papered.png" }, { "pagePath": "pages/my/my", "text": "我的", "iconPath": "static/tabbar/home.png", "selectedIconPath": "static/tabbar/homeed.png" } ] }
uni-app和vuejs基础快速上手
view和text组件和动画的使用
- hover-class的测试使用
- text的测试使用
- 两个内置组件的使用可参考官方文档
uniapp的css3选择器
-
普通的选择器 id就用#,class就用.,什么都不加默认全部
-
父级下的子级菜单的选择器
.box>view:first-of-type{ background-color: red; } .box>view:last-of-type{ background-color: pink; } .box>view:nth-child(2){ background-color: yellow; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvOmDgZ5-1649380676446)(uniapp社区交友开发.assets/image-20220323110150462-16480045116861.png)]
-
奇偶选择器
//奇数选择器 .box>view:nth-of-type(odd){ background-color: red; } //偶数选择器 .box>view:nth-of-type(even){ background-color: green; } //偶数选择器 .box:nth-of-type(even){ background-color: green; }
flex布局快速入手
- display:flex 外层使用,块内元素挤在一行内
- justify-content:常用center,水平居中
- align-items:常用center,垂直居中
- felx-direction:改变排序方式,从行转换成列
- felx-shirink:0 不被压缩
- flex:1占一份 2占两份
数据渲染
- {{}}获取data中的数据渲染
- @tap触发点击事件
class和style的绑定
- 绑定class用冒号:class
- :class={‘class1’:isActive}
- s:tyle=" {‘color’: Color,‘font-size’:num+‘px’}"
条件渲染
- v-if的使用
- v-show是会渲染只是不显示
- 一般在template里面用v-if
列表渲染
- v-for
- 官方文档建议v-for写在
事件处理器
- @tap点击事件
- @tap.stop点里面不会触发外面的事件
监听属性
-
watch
-
测试
<template> <view> <view>{{num}}</view> <button @tap="changNum()">按钮</button> </view> </template> <script> export default { data() { return { num:0 } }, watch:{ num(val){ console.log(val); } }, methods: { changNum(){ this.num++; } } } </script> <style> button{ background: blue; display: flex; justify-content: center; align-items: center; font-size: 15px; color: white; } </style>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gyz1MrF9-1649380676447)(uniapp社区交友开发.assets/image-20220323154220758-16480213421241.png)]
计算属性
-
常用于数据的格式化
-
computed
-
测试运用
<template> <view> {{formatWeight}} </view> </template> <script> export default { data() { return { weight:2100 } }, computed:{ formatWeight(){ return (this.weight>1000)?this.weight/1000+'kg':this.weight+'g'; } } } </script> <style> </style>
首页开发
page.json配置
-
导航栏配置根据官方文档配置
{ "path": "pages/index/index", "style": { "app-plus": { // 导航栏配置 "titleNView":{ // 搜索框配置 "searchInput":{ "align":"center", "backgroundColor":"#F5F4F2", "borderRadius":"4px", "disabled":true, "placeholder":"搜索帖子", "placeholderColor":"#6D6C67" }, "buttons":[ { "color":"#333333", "colorPressed":"#FD597C", "float":"right", "fontSize":"20px", "fontSrc":"/static/iconfont.ttf", "text":"\ue668" } ] } } } }
-
用真机调试成功,微信开发者工具旁边无显示
图文列表样式
-
封装free.css把常用样式封装 如==flex:display;justify-content:center;algin-items:center、
-
引入自定义css库
-
列表开发
<view style="padding: 20rpx;"> <view class="flex;justify-between;align-center"> <!-- 头像,昵称 --> <view class="flex;align-center"> <!-- 头像 --> <image src="/static/common//nothing.png" class="mr-1; rounded" style="width:65rpx;height:65rpx"></image> <view> <view style="font-size: 30rpx;">昵称</view> <view style="color: #9d9589;font-size: 20rpx;">2022-3-15</view> </view> </view > <!-- 按钮 --> <view class="flex;align-center; justify-center" style="background: #FF4A6A; width:90rpx; height:50rpx; color: white;" > 关注 </view> </view> <!-- 文章内容 --> <view> 哈哈哈 </view> <!-- 图片 --> <view class="mt-1" > <image src="/static/demo/datapic/45.jpg" style="width: 100%; height: 350rpx;" lazy-load="true"></image> </view> <!-- 按钮 --> <view class="flex" > <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-dianzan2 "></view> <view>赞</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-cai; mr-2" ></view> <view>踩</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-pinglun2; mr-2" ></view> <view>评论</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-zhuanfa1; mr-2" ></view> <view>转发</view> </view> </view> </view>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-daoAQo1N-1649380676447)(uniapp社区交友开发.assets/image-20220323193232107.png)]
封装列表样式
-
继续使用free.css封装代码
<view> <view class="p-2"> <view class="flex;justify-between;align-center"> <!-- 头像,昵称 --> <view class="flex;align-center"> <!-- 头像 --> <image src="/static/common//nothing.png" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx"></image> <view> <view class="font" style="line-height: 1.5;">昵称</view> <view style=" ine-height: 1.5;" class="font-small; text-light-muted">2022-3-15</view> </view> </view > <!-- 按钮 --> <view class="flex;align-center; justify-center; rounded; text-white; bg-main" style=" width:90rpx; height:50rpx;" > 关注 </view> </view> <!-- 文章内容 --> <view class="font-md; my-1"> 我是标题 </view> <!-- 图片 --> <view class="mt-1" > <image src="/static/demo/datapic/45.jpg" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true"></image> </view> <!-- 按钮 --> <view class="flex" > <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-dianzan2 "></view> <view>赞</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-cai; mr-2" ></view> <view>踩</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-pinglun2; mr-2" ></view> <view>评论</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-zhuanfa1; mr-2" ></view> <view>转发</view> </view> </view> </view> </view>
-
封装本项目公共css(common.css 记得引入)
.bg-main{ background: #FF4A6A; }
-
封装成组件动态渲染
<block v-for="(item,index) in list" :key="index"> <commonList :item="item" :index="index" ></commonList> </block> import commonList from '@/components/common/common-list'; components:{ commonList }, <view class="p-2"> <view class="flex;justify-between;align-center"> <!-- 头像,昵称 --> <view class="flex;align-center"> <!-- 头像 --> <image :src="item.userPic" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx"></image> <view> <view class="font" style="line-height: 1.5;">{{item.username}}</view> <view style=" ine-height: 1.5;" class="font-small; text-light-muted">{{item.newstime}}</view> </view> </view > <!-- 按钮 --> <view class="flex;align-center; justify-center; rounded; text-white; bg-main" style=" width:90rpx; height:50rpx;" > 关注 </view> </view> <!-- 文章内容 --> <view class="font-md; my-1"> {{item.title}} </view> <!-- 图片 --> <view class="mt-1" > <image :src="item.titlePic" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true"></image> </view> <!-- 按钮 --> <view class="flex" > <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-dianzan2; mr-2"></view> <view>{{item.support.support_count}}</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-cai; mr-2" ></view> <view>{{item.support.unsupport_count}}</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-pinglun2; mr-2" ></view> <view>{{item.comment_count}}</view> </view> <view class="flex;align-center; justify-center flex-1"> <view class="iconfont icon-zhuanfa1; mr-2" ></view> <view>{{item.share_num}}</view> </view> </view> </view> export default{ props:{ item:Object, key:Number } }
全局分割线开发
-
封装组件divider.vue
<!-- 分割线样式 --> <view style="height: 15rpx; background-color: #F5F5F4;"></view>
-
引入全局组件(分割线常用)
import divider from '@/components/common/divider.vue'; Vue.component('divider',divider); <block v-for="(item,index) in list" :key="index"> <commonList :item="item" :index="index"></commonList> <divider></divider> </block>
优化列表组件-动画特效
- 图片显示优化
- 关注、图标的点击动画特效(jello)
- 图标主色调变化 加字体颜色
- 为各元素添加click事件 (头像、关注、标题、图片、点赞、踩)
<template>
<view>
<view class="p-2">
<view class="flex;justify-between;align-center">
<!-- 头像,昵称 -->
<view class="flex;align-center">
<!-- 头像 -->
<image :src="item.userPic" class="mr-1; rounded-circle" style="width:65rpx;height:65rpx" @click="openSapce()"></image>
<view>
<view class="font" style="line-height: 1.5;">{{item.username}}</view>
<view style=" ine-height: 1.5;" class="font-small; text-light-muted">{{item.newstime}}</view>
</view>
</view>
<!-- 按钮 -->
<view class="flex;align-center; justify-center; rounded; text-white; bg-main; animated faster "
style=" width:90rpx; height:50rpx;" hover-class="rubberBand" @click="follow()">
关注
</view>
</view>
<!-- 文章内容 -->
<view class="font-md; my-1" @click="openDetail()" >
{{item.title}}
</view>
<!-- 图片 -->
<view class="mt-1" v-if="item.titlePic" @click="openDetail()">
<image :src="item.titlePic" class="rounded" style="width: 100%; height: 350rpx;" lazy-load="true">
</image>
</view>
<!-- 按钮 -->
<view class="flex">
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('support')">
<view class="iconfont icon-dianzan2; mr-2;"></view>
<view>{{item.support.support_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('unsupport')">
<view class="iconfont icon-cai; mr-2 "></view>
<view>{{item.support.unsupport_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="openDetail()">
<view class="iconfont icon-pinglun2; mr-2"></view>
<view>{{item.comment_count}}</view>
</view>
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="openDetail()">
<view class="iconfont icon-zhuanfa1; mr-2"></view>
<view>{{item.share_num}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
item: Object,
index: Number
},
methods:{
openSapce(){
console.log("打开个人空间");
},
follow(){
console.log("关注");
},
openDetail(){
console.log("打开详情页");
},
doSupport(type){
console.log(type);
}
}
}
</script>
<style>
</style>
优化列表组件-关注功能
- 关注按钮的显示
- 子组件触发父组件方法更新isFollowz
<view class="flex;align-center; justify-center; rounded; text-white; bg-main; animated faster "
style=" width:90rpx; height:50rpx;" hover-class="rubberBand" @click="follow()" v-if="!item.isFollow">
关注
</view>
follow(){
// 触发父级follow方法
this.$emit('follow',this.index);
},
<commonList :item="item" :index="index" @follow="follow(index)"></commonList>
methods: {
follow(index){
this.list[index].isFollow=true;
uni.showToast({
title:'关注成功'
})
}
}
优化列表组件-顶踩功能
- 绑定class渲染
- 方法参数传递
- 父方法实现
<view class="flex;align-center; justify-center flex-1 animated faster" hover-class="jello text-main" @click="doSupport('support')"
:class="item.support.type==='support'?'support-active':''">
<view class="iconfont icon-dianzan2; mr-2;"></view>
<view>{{item.support.support_count>0?item.support.support_count:'支持'}}</view>
</view>
doSupport(type){
this.$emit('doSupport',{
type:type,
index:this.index
})
}
<commonList :item="item" :index="index" @follow="follow" @doSupport="doSupport"></commonList>
doSupport(e) {
let obj=this.newList[this.tabIndex].list[e.index]
console.log(obj);
if (obj.support.type === '') {
//无操作
obj.support[e.type + '_count']++;
} else if (obj.support.type === 'support' && e.type === 'unsupport') {
//之前是顶,顶减一,踩加一
obj.support.support_count--;
obj.support.unsupport_count++;
} else if (obj.support.type === 'unsupport' && e.type === 'support') {
//之前是踩,顶加一,踩减一
obj.support.support_count++;
obj.support.unsupport_count--;
}
obj.support.type = e.type;
let msg = e.type === 'support' ? '顶' : '踩';
uni.showToast({
title: msg + '成功'
})
},
滚动tab导航开发
-
顶部导航选项卡
<scroll-view class="scroll-row border-bottom" scroll-x="true" :scroll-into-view="scrollInto" scroll-with-animation="true"> <view v-for="(item,index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md " :id="'tab'+index" :class="tabIndex===index?'text-main font-lg font-weight-bold':''" @click="doTab(index)"> {{item.name}} </view> </scroll-view> tabIndex:0, scrollInto:'', tabBars:[ { name:'首页' }, { name:'体育' }, { name:'军事' }, { name:'热点' }, { name:'新闻' }, { name:'娱乐' }, { name:'电竞' }, { name:'国际' }, { name:'国家' } ], doTab(index){ this.tabIndex=index; this.scrollInto='tab'+index; }
-
下面容器能做到切换与导航栏一样,容器也能拉取
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item v-for="(item,index) in tabBars" :key="index" > <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> <view v-for="i in 100">{{i}}</view> </scroll-view> </swiper-item> </swiper> doTab(index){ this.tabIndex=index; this.scrollInto='tab'+index; }, onChange(e){ this.doTab(e.detail.current); } onLoad() { const res = uni.getSystemInfoSync(); this.scrollH=res.windowHeight-uni.upx2px(101); console.log(this.scrollH); },
-
列表显示
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item v-for="(item,index) in newList" :key="index" > <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> <block v-for="(item2,index2) in item.list" :key="index"> <commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()"></commonList> <divider></divider> </block> </scroll-view> </swiper-item> </swiper> getData(){ let arr=[]; for (var i = 0; i < this.tabBars.length; i++) { arr.push({ list: [{ userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '/static/demo/datapic/45.jpg', support: { type: 'support', support_count: 1, unsupport_count: 2 }, comment_count: 1, share_num: 0 }, { userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '', support: { type: 'unsupport', support_count: 2, unsupport_count: 2 }, comment_count: 1, share_num: 1 }, { userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '', support: { type: '', support_count: 2, unsupport_count: 2 }, comment_count: 1, share_num: 1 } ] }) }; this.newList=arr; },
上拉加载组件开发
-
静态的开发
<scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)"> <block v-for="(item2,index2) in item.list" :key="index"> <commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()"> </commonList> <divider></divider> </block> <view class="py-2 flex justify-center align-center"> <view class="text-light-muted">{{item.loading}}</view> </view> </scroll-view>
-
触底函数的开发
loadMore(index){ let item=this.newList[index]; item.loading='加载中.'; setTimeout(()=>{ //复制文本 item.list=[...item.list,...item.list]; },2000) }
封装上拉加载组件
-
优化加载判断
loadMore(index){ let item=this.newList[index]; if(!item==='上拉加载更多'){ return; } item.loading='加载中...'; setTimeout(()=>{ item.list=[...item.list,...item.list]; item.loading='上拉加载更多'; },10000) }
-
封装load-more.vue 并引入
<template> <view class="py-2 flex justify-center align-center"> <view class="text-light-muted">{{loading}}</view> </view> </template> <script> export default{ props:{ loading:String } } </script> import loadMore from '@/components/common/load-more'; export default { components: { commonList, loadMore }, }
封装无数据默认组件
-
无数据环境测试
const demo=[ { userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '/static/demo/datapic/45.jpg', support: { type: 'support', support_count: 1, unsupport_count: 2 }, comment_count: 1, share_num: 0 }, { userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '/static/demo/datapic/45.jpg', support: { type: 'unsupport', support_count: 2, unsupport_count: 2 }, comment_count: 1, share_num: 1 }, { userPic: '/static/common//nothing.png', username: '', newstime: '', isFollow: false, title: '我是标题', titlePic: '', support: { type: '', support_count: 2, unsupport_count: 2 }, comment_count: 1, share_num: 1 } ] getData() { let arr = []; for (var i = 0; i < this.tabBars.length; i++) { let obj = { loading:'上拉加载更多', list: [] } if(i<2){ obj.list=demo; } arr.push(obj) }; this.newList = arr; },
-
封装nothing.vue组件 全局引入
<template v-if="item.list.length>0"> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)"> <block v-for="(item2,index2) in item.list" :key="index"> <commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()"> </commonList> <divider></divider> </block> <loadMore :loading="item.loading"></loadMore> </scroll-view> </template> <template v-else> <no-thing></no-thing> </template> import noThing from '@/components/common/no-thing.vue'; Vue.component('no-thing',noThing);
搜索页开发
-
创建页面search,配置pages.json
{ "path": "pages/index/index", "style": { "app-plus": { // 导航栏配置 "titleNView":{ // 搜索框配置 "searchInput":{ "align":"center", "backgroundColor":"#F5F4F2", "borderRadius":"4px", "disabled":true, "placeholder":"搜索帖子", "placeholderColor":"#6D6C67" }, "buttons":[ { "color":"#333333", "colorPressed":"#FD597C", "float":"right", "fontSize":"20px", "fontSrc":"/static/iconfont.ttf", "text":"\ue668" } ] } } } } ,{ "path" : "pages/news/news", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ,{ "path" : "pages/msg/msg", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ,{ "path" : "pages/my/my", "style" : { "navigationBarTitleText": "", "enablePullDownRefresh": false } } ,{ "path" : "pages/search/search", "style": { "app-plus": { // 导航栏配置 "titleNView":{ // 搜索框配置 "searchInput":{ "align":"center", "backgroundColor":"#F5F4F2", "borderRadius":"4px", "placeholder":"搜索帖子", "placeholderColor":"#6D6C67" }, "buttons":[ { "color":"#333333", "colorPressed":"#FD597C", "float":"right", "fontSize":"14px", "text":"搜索" } ] } } } }
-
监听点击导航栏搜索框事件, 实现跳转,用官方api
onNavigationBarSearchInputClicked() { uni.navigateTo({ url: '../search/search' }) },
-
搜索历史开发
<view> <view class="py-2 font-md px-2">搜索历史</view> <view class="flex flex-wrap"> <view class="border rounded font mx-2 my-1 px-2 " v-for="(item,index) in list" :key="index" hover-class="bg-light"> {{item}} </view> </view> </view>
-
监听导航输入
onNavigationBarSearchInputChanged(e) { this.searchText=e.text; }
-
监听导航搜索按钮
onNavigationBarButtonTap(e) { if(e.index===0){ this.searchEvent(); } },
-
搜索事件, 收起键盘,处于loading状态, 展示搜索结果,隐藏loading提示框
searchEvent(){ uni.hideKeyboard(); uni.showLoading({ title:'加载中' }) setTimeout(()=>{ this.serachList=demo; uni.hideLoading(); },3000) }
-
搜索结果列表 引入commonlist,遍历,优化搜索历史与列表存在问题
<view> <template v-if="this.serachList.length===0"> <view class="py-2 font-md px-2">搜索历史</view> <view class="flex flex-wrap"> <view class="border rounded font mx-2 my-1 px-2 " v-for="(item,index) in list" :key="index" hover-class="bg-light"> {{item}} </view> </view> </template> <template v-else> <block v-for="(item,index) in serachList" :key="index" > <commonList :item="item" :index="index"></commonList> </block> </template> </view> import commonList from '@/components/common/common-list.vue'; export default { components:{ commonList }, }
-
点击搜索历史的事件
<view class="flex flex-wrap"> <view class="border rounded font mx-2 my-1 px-2 " v-for="(item,index) in list" :key="index" hover-class="bg-light" @click="historyEvent(item)"> {{item}} </view> </view> historyEvent(item){ console.log(item); this.searchText=item; this.searchEvent(); }
发布表单页面开发
自定义导航栏开发
-
新建发布页面add-input,取消原生导航
{ "path" : "pages/add-input/add-input", "style" : { "app-plus": { "titleNView": false } } }
-
首页导航按钮跳转页面
onNavigationBarButtonTap() { uni.navigateTo({ url:'../add-input/add-input' }) },
-
自定义导航栏,添加uni-nav-bar依赖, 根据官方文档调用
<uni-nav-bar left-icon="back" statusBar> <view class="flex justify-center align-center w-100"> 所有人可见<text class="iconfont icon-shezhi ml-1"></text> </view> </uni-nav-bar>
textarea组件使用
-
utextarea, 动态绑定
<textarea v-model="content" placeholder="说一句话吧~" class="px-2 uni-textarea" />
底部操作条组件开发
<view style="height: 85rpx" class="fixed-bottom bg-white flex align-center">
<view class="iconfont icon-caidan footer-btn"></view>
<view class="iconfont icon-huati footer-btn"></view>
<view class="iconfont icon-tupian footer-btn"></view>
<view class="bg-main text-white justify-center align-center ml-auto flex mr-2" style="width: 140rpx; height: 60rpx;">发送</view>
</view>
<style>
.footer-btn{
width: 86rpx;
height: 86rpx;
justify-content: center;
display: flex;
align-items: center;
font-size: 50rpx;
}
</style>
上传多图功能开发
-
upload-image组件开发, 引官方组件,对应引入也需要引入
-
对官方组件的修改 添加mode压缩,修改内边距, 修改圆角
<view class="px-2"> <view class="uni-uploader"> <view class="uni-uploader-head"> <view class="uni-uploader-title">点击可预览选好的图片</view> <view class="uni-uploader-info">{{imageList.length}}/9</view> </view> <view class="uni-uploader-body"> <view class="uni-uploader__files"> <block v-for="(image,index) in imageList" :key="index"> <view class="uni-uploader__file"> <image class="uni-uploader__img rounded" :src="image" :data-src="image" @tap="previewImage" mode="aspectFill"></image> </view> </block> <view class="uni-uploader__input-box"> <view class="uni-uploader__input rounded" @tap="chooseImage"></view> </view> </view> </view> </view> </view>
-
上传图片成功的内容保存 上传图片的回调success方法中
success: (res) => { this.imageList = this.imageList.concat(res.tempFilePaths); this.$emit('choose',this.imageList); }, <uploadImage @choose="choose"></uploadImage> choose(e){ console.log(e); this.imageList=e; }
删除选中图片功能实现
-
静态图标的实现
<block v-for="(image,index) in imageList" :key="index"> <view class="uni-uploader__file position-relative"> <image class="uni-uploader__img rounded" :src="image" :data-src="image" @tap="previewImage" mode="aspectFill"></image> <view class="bg-dark position-absolute top-0 right-0 rounded" style="padding: 0 15rpx; background-color: rgba(0, 0, 0, 0.5);"> <text class="iconfont icon-shanchu text-white"></text> </view> </view> </block>
-
添加删除功能的函数, 通知父组件, 优化父组件方法, 给予交互反馈提示
<text class="iconfont icon-shanchu text-white" @click="deleteImage(index)"></text> deleteImage(index){ uni.showModal({ title:'提示', content:'是否要删除该图片', confirmText:'删除', cancelText:'不删除', success: (res) => { if(res.confirm){ this.imageList.splice(index,1); this.$emit('change',this.imageList); } } }) },
保存草稿功能开发
-
添加返回首页方法
<uni-nav-bar left-icon="back" statusBar @clickLeft="goBack"> <view class="flex justify-center align-center w-100 font-weight-bold"> 所有人可见<text class="iconfont icon-shezhi ml-1"></text> </view> </uni-nav-bar> goBack(){ uni.navigateBack({ delta:1 }) },
-
监听返回, 交互提示反馈 return true即能返回
onBackPress() { if((this.content!==''||this.imageList.length>0)&&!this.showBack){ uni.showModal({ content: '是否保存为草稿', showCancel: true, cancelText: '不保存', confirmText: '保存', success: res => { if(res.confirm){ console.log('保存'); } //手动执行返回 // goBack(){ // uni.navigateBack({ // delta:1 // }) // }, this.goBack(); } }); this.showBack=true; return true; } },
-
保存的方法
store(){ let obj={ content:this.content, imageList:this.imageList }; uni.setStorage({ key:'add-input', data:JSON.stringify(obj) }) }
-
取出的方法 用同步取出
onLoad() { var res=uni.getStorageSync('add-input'); if(res){ var obj=JSON.parse(res); // console.log(result); this.content=obj.content; this.imageList=obj.imageList; } },
-
优化图片的草稿功能, 上传图片的组件imageList要用props传值
<uploadImage :list="imageList" @change="change"></uploadImage> //组件里面 props: { list: Array }, created(){ this.imageList=this.list; },
-
不保存草稿功能的实现, 清楚缓存
onBackPress() { if((this.content!==''||this.imageList.length>0)&&!this.showBack){ uni.showModal({ content: '是否保存为草稿', showCancel: true, cancelText: '不保存', confirmText: '保存', success: res => { if(res.confirm){ // console.log('保存'); this.store(); }else{ uni.removeStorageSync('add-input'); } //手动执行返回 this.goBack(); } }); this.showBack=true; return true; } },
-
下面图标插入图片的方法(优化不需要上传图片时隐藏上传图片组件)
<view class="iconfont icon-tupian footer-btn" @click="iconClickEvent('uploadImage')"></view> <uploadImage ref="uploadImage" :show="show" :list="imageList" @change="change"></uploadImage> iconClickEvent(e){ switch (e){ case 'uploadImage': this.$refs.uploadImage.chooseImage(); break; } } computed:{ show(){ return this.imageList.length>0; } }, 在子组件中加入v-if=“show”
动态列表页开发
导航栏tab导航开发
-
自定义导航栏的静态开发
<uni-nav-bar statusBar="true" border="false" > <view class="flex align-center justify-center font-weight-bold w-100"> <view class="font-lg text-main mx-1">关注</view> <view class="font-md text-light-muted mx-1">话题</view> </view> <view slot="right" class="iconfont icon-fatie_icon"></view> </uni-nav-bar>
-
取消原生导航栏
{ "path": "pages/news/news", "style": { "app-plus": { "titleNView": false } } },
-
导航栏右边图标的单击事件
<uni-nav-bar statusBar="true" border="false" @clickRight="openAddInput"> <view class="flex align-center justify-center font-weight-bold w-100"> <view class="font-lg text-main mx-1">关注</view> <view class="font-md text-light-muted mx-1">话题</view> </view> <view slot="right" class="iconfont icon-fatie_icon"></view> </uni-nav-bar> openAddInput(){ uni.navigateTo({ url: '../add-input/add-input' }) }
-
tabBar的动态循环渲染
<uni-nav-bar statusBar="true" border="false" @clickRight="openAddInput"> <view class="flex align-center justify-center font-weight-bold w-100 font-md text-light-muted"> <view v-for="(item,index) in tabBars" :key="index" :class="tabIndex===index? 'text-main font-lg':''" @click="changeTab(index)" class="mx-1"> {{item.name}} </view> </view> <view slot="right" class="iconfont icon-fatie_icon"></view> </uni-nav-bar> data() { return { tabIndex:0, tabBars:[ { name:'关注' },{ name:'话题' } ] } }, changeTab(index){ this.tabIndex=index; }
关注列表页开发
滑动滚动区域计算
-
引入首页的swipper和scrollview组件, 对应修改即可
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> </scroll-view> </swiper-item> </swiper>
-
计算区域高度
onLoad() { const res = uni.getSystemInfoSync(); this.scrollH=res.screenHeight-res.statusBarHeight-44; console.log(this.scrollH); },
导航列表联动实现
-
有两个swipperitem 表示关注和话题
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> <view v-for=" (item,index) in 100" :key="index">{{item}}</view> </scroll-view> </swiper-item> <swiper-item> <view>话题</view> </swiper-item> </swiper>
-
引入common-list组件
-
分割线的使用
<swiper-item> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> <block v-for="(item,index) in list" :key="index" > <commonList :item="item" :index="index"></commonList> <divider></divider> </block> </scroll-view> </swiper-item> <swiper-item> <view>话题</view> </swiper-item>
-
导航与列表的联动实现
<swiper duration=150 @change="onChangeTab" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'"> <block v-for="(item,index) in list" :key="index" > <commonList :item="item" :index="index"></commonList> <divider></divider> </block> </scroll-view> </swiper-item> <swiper-item> <view>话题</view> </swiper-item> </swiper> onChangeTab(e){ // console.log(e.detail); this.tabIndex=e.detail.current; }
顶踩操作和下拉加载功能 copy首页即可
- 移植顶踩操作
- 下拉加载的开发
关注列表页开发
热门分类组件开发
-
静态页面开发
<swiper-item> <view class="flex justify-between align-center px-2"> <text class="font-md">热门分类</text> <view class="flex align-center font text-secondary"> 更多<text class="iconfont icon-jinru"></text> </view> </view> <view class="flex align-center border-bottom px-2 py-3"> <view class="rounded border bg-light mx-1 px-2">关注</view> <view class="rounded border bg-light mx-1 px-2">关注</view> <view class="rounded border bg-light mx-1 px-2">关注</view> <view class="rounded border bg-light mx-1 px-2">关注</view> </view> </swiper-item>
-
添加点击动画效果
<view class="rounded border bg-light mx-1 px-2 animated" hover-class="jello">关注</view>
封装热门分类组件
-
热门分类对象数组构建
hotCate:[{ name:'关注' }, { name:'推荐' }, { name:'体育' }, { name:'军事' } ]
-
封装组件, 导入组件,遍历输出, 父子传值
-
留两个接口
<hotCate :hotCate="hotCate"></hotCate> <template> <view> <view class="flex justify-between align-center px-2"> <text class="font-md">热门分类</text> <view class="flex align-center font text-secondary animated" hover-class="jello" @click="openMore"> 更多<text class="iconfont icon-jinru"></text> </view> </view> <view class="flex align-center border-bottom px-2 py-3"> <view class="rounded border bg-light mx-1 px-2 animated" hover-class="jello" v-for="(item,index) in hotCate" :key="index" @click="openDetail">{{item.name}}</view> </view> </view> </template> <script> export default{ props:['hotCate'], methods:{ openMore(){ console.log("点击更多"); }, openDetail(){ console.log("点击进入详情页"); } } } </script> <style> </style>
轮播图和搜索框的开发
-
静态页面开发
<view class="p-2"> <view class="flex align-center justify-center py-2 rounded bg-light text-secondary"> <text class="iconfont icon-sousuo mr-2"></text>搜索话题 </view> </view> <swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" class="px-2 pb-2"> <swiper-item> <image src="/static/demo/banner3.jpg" class="w-100 rounded" style="height: 300rpx;"></image> </swiper-item> </swiper> <divider></divider>
话题列表组件开发
-
静态页面开发
<view class="p-2 font-md"> 最近更新</view> <view class="flex align-center p-2"> <image src="../../static/demo/topicpic/1.jpeg" style="width: 150rpx; height: 150rpx;" class="rounded mr-2"></image> <view class="flex flex-column "> <text class="font-md text-dark">话题哈哈哈</text> <text class="font text-secondary">话题描述</text> <view class="flex align-center font text-secondary"> <text class="mr-2">今日:0</text> <text>关注:0</text> </view> </view> </view>
封装话题列表组件
-
声明topicList
topicList: [{ cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' } ]
-
封装组件,引入组件
<block v-for="(item,index) in topicList" :key="index"> <topicList :item="item" :index="index"></topicList> </block> <view class="flex align-center p-2"> <image :src="item.cover" style="width: 150rpx; height: 150rpx;" class="rounded mr-2"></image> <view class="flex flex-column "> <text class="font-md text-dark">{{item.title}}</text> <text class="font text-secondary">{{item.desc}}</text> <view class="flex align-center font text-secondary"> <text class="mr-2">动态:{{item.news_count}}</text> <text>今日:{{item.today_count}}</text> </view> </view> </view>
话题分类页开发
-
新建页面
{ "path" : "pages/topic-nav/topic-nav", "style" : { "navigationBarTitleText": "话题分类" } }
-
导航进入
openMore(){ uni.navigateTo({ url:'../topic-nav/topic-nav' }) },
-
引入首页,修改topic—nav页面,修改common-list组件, 修改数据
<template> <view> <scroll-view class="scroll-row border-bottom border-light-secondary" scroll-x="true" :scroll-into-view="scrollInto" scroll-with-animation="true" style="height: 100rpx;"> <view v-for="(item,index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md " :id="'tab'+index" :class="tabIndex===index?'text-main font-lg font-weight-bold':''" @click="doTab(index)"> {{item.name}} </view> </scroll-view> <swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item v-for="(item,index) in newList" :key="index"> <template v-if="item.list.length>0"> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)"> <block v-for="(item2,index2) in item.list" :key="index"> <!-- <commonList :item="item2" :index="index2" @follow="follow()" @doSupport="doSupport()"> </commonList> --> <topicList :item="item2" :index="index2"></topicList> </block> <loadMore :loading="item.loading"></loadMore> </scroll-view> </template> <template v-else> <no-thing></no-thing> </template> </swiper-item> </swiper> </view> </template> <script> import topicList from '@/components/news/topic-list'; import loadMore from '@/components/common/load-more'; const demo = [{ cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' }, { cover: '/static/demo/topicpic/1.jpeg', title: '话题标题', desc: '话题描述', news_count: '10', today_count: '10' } ]; export default { components: { topicList, loadMore }, data() { return { scrollH: 200, tabIndex: 0, scrollInto: '', newList: [], tabBars: [{ name: '首页' }, { name: '体育' }, { name: '军事' }, { name: '热点' }, { name: '新闻' }, { name: '娱乐' }, { name: '电竞' }, { name: '国际' }, { name: '国家' } ] } }, onNavigationBarSearchInputClicked() { uni.navigateTo({ url: '../search/search' }) }, onLoad() { const res = uni.getSystemInfoSync(); this.scrollH = res.windowHeight - uni.upx2px(101); this.getData(); }, onNavigationBarButtonTap() { uni.navigateTo({ url:'../add-input/add-input' }) }, methods: { getData() { let arr = []; for (var i = 0; i < this.tabBars.length; i++) { let obj = { loading: '上拉加载更多', list: [] } if (i < 2) { obj.list = demo; } arr.push(obj) }; this.newList = arr; }, follow(index) { this.list[index].isFollow = true; uni.showToast({ title: '关注成功' }) }, doSupport(e) { let obj = this.list[e.index]; if (obj.support.type === '') { //无操作 obj.support[e.type + '_count']++; } else if (obj.support.type === 'support' && e.type === 'unsupport') { //之前是顶,顶减一,踩加一 obj.support.support_count--; obj.support.unsupport_count++; } else if (obj.support.type === 'unsupport' && e.type === 'support') { //之前是踩,顶加一,踩减一 obj.support.support_count++; obj.support.unsupport_count--; } obj.support.type = e.type; let msg = e.type === 'support' ? '顶' : '踩'; uni.showToast({ title: msg + '成功' }) }, doTab(index) { this.tabIndex = index; this.scrollInto = 'tab' + index; }, onChange(e) { this.doTab(e.detail.current); }, loadMore(index) { let item = this.newList[index]; if ((item.loading) !== '上拉加载更多') { return; } item.loading = '加载中...'; setTimeout(() => { item.list = [...item.list, ...item.list]; item.loading = '上拉加载更多'; }, 2000) } } } </script> <style> </style>
话题详情页开发
page.json配置
-
新建topic-detail页面
-
配置导航栏, 渐变式透明, 图标
{ "path" : "pages/topic-detail/topic-detail", "style" : { "navigationBarTitleText": "", "app-plus": { "titleNView": { "type": "transparent", "buttons": [ { "type": "menu" } ] } } } }
-
导航到话题详情页, 传递json字符串对象, 接受数据转换object
<view class="flex align-center p-2" @click="openDetail(item)"> openDetail(item){ uni.navigateTo({ url:'../topic-detail/topic-detail?detail='+JSON.stringify(item) }) } onLoad(e){ if(e.detail){ var obj=JSON.parse(e.detail); // console.log(obj); } },
话题介绍组件开发
-
图片模糊状态
-
静态开发
<view> <view class="position-relative"> <image src="/static/demo/topicpic/1.jpeg" mode="aspectFill" style="height: 300rpx;" class="w-100 filter"></image> </view> <view class=" px-2 bg-white position-relative" style="z-index: 10;"> <view class="flex align-center"> <image src="/static/demo/topicpic/1.jpeg" class="w-100" style="height: 150rpx; width: 150rpx; margin-top: -75rpx;"></image> <text class="font-md">#话题标题#</text> </view> <view class="flex align-center font text-secondary mt-2"> <text class="mr-1">动态:0</text> <text>今日:0</text> </view> <view class="font text-secondary">话题描述</view> </view> </view> <style> .filter{ filter: blur(10px); } </style>
封装话题介绍组件
-
新建topic-info组件
-
动态替换, 数据渲染
<template> <view> <view class="position-relative"> <image :src="info.cover" mode="aspectFill" style="height: 300rpx;" class="w-100 filter"> </image> </view> <view class=" px-2 bg-white position-relative pb-1" style="z-index: 10;"> <view class="flex align-center"> <image :src="info.cover" class="w-100" style="height: 150rpx; width: 150rpx; margin-top: -75rpx;"></image> <text class="font-md">{{info.title}}</text> </view> <view class="flex align-center font text-secondary mt-2"> <text class="mr-1">动态:{{info.news_count}}</text> <text>今日:{{info.today_count}}</text> </view> <view class="font text-secondary">{{info.desc}}</view> </view> </view> </template> <script> export default { props:['info'] } </script> <style> </style> <topicInfo :info="info"></topicInfo> <divider></divider>
精华帖子列表组件开发
<view class="flex p-2 border-bottom">
<text class="iconfont icon-xihuan text-main"></text>
<text class="font text-darker text-ellipsis">
【新人必读】uni-app实战项目第四季社区交友开发
</text>
</view>
<view class="flex p-2 border-bottom">
<text class="iconfont icon-xihuan text-main"></text>
<text class="font text-darker text-ellipsis">
【新人必读】uni-app实战项目第四季社区交友开发
</text>
</view>
tab选项卡组件开发
-
优化精华帖子列表开发, 用循环
<block v-for="(item,index) in hotList" :key="index"> <view class="flex p-2 border-bottom"> <text class="iconfont icon-xihuan text-main"></text> <text class="font text-darker text-ellipsis"> {{item.title}} </text> </view> </block> hotList: [{ title: '【新人必读】uni-app实战项目第四季社区交友开发' }, { title: '【新人必读】Hillky社区规范' } ]
-
tab选项开发
<view class="flex align-center py-2"> <text class="flex-1 flex align-center justify-center font-weight-bold font-lg text-main">默认</text> <text class="flex-1 flex align-center justify-center font-weight-bold font-md text-dark">最新</text> </view>
-
列表开发, 引入公共列表, 声明两个数组list1,2
<block v-for="(item,index) in list1" :key="index"> <commonList :item="item" :index="index"></commonList> </block>
-
tab切换实现
<view class="flex align-center py-2" > <text class="flex-1 flex align-center justify-center" v-for="(item,index) in tabBar" :key="index" :class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'" @click="changeTab(index)">{{item.name}}</text> </view> tabIndex: 0, tabBar:[ { name:'默认' }, { name:'最新' } ]
利用计算属性实现列表切换
-
用计算属性切换数组
computed: { listData() { if (this.tabIndex === 0) { return this.list1; } if (this.tabIndex === 1) { return this.list2; } } },
-
遍历列表要分割线
-
判断数组长度, 用nothing组件
<template v-if="listData.length>0"> <block v-for="(item,index) in listData" :key="index"> <commonList :item="item" :index="index"></commonList> <divider></divider> </block> </template> <template v-else> <no-thing></no-thing> </template>
话题详情上拉加载实现
-
触底事件
onReachBottom() { this.loadMore(); },
-
引入上拉加载组件
import loadMore from '@/components/common/load-more'; <loadMore :loading="loadText"></loadMore>
-
loadText计算属性区分是哪个列表的上拉加载
loadText1:'上拉加载更多', loadText2:'上拉加载更多' loadText(){ if(this.tabIndex===0){ return this.loadText1 }else{ return this.loadText2 } }
-
上拉加载更多事件
loadMore(){ let index =this.tabIndex if(this['loadText'+(index+1)]!=='上拉加载更多'){ return; } this['loadText'+(index+1)]='加载中...' setTimeout(()=>{ this['loadText'+(index+1)]='上拉加载更多'; this['list'+(index+1)]=[...this['list'+(index+1)],...this['list'+(index+1)]]; },2000) }
消息列表页面开发
page.json配置
-
配置顶部导航栏, 配置左右图标
{ "path": "pages/msg/msg", "style": { "navigationBarTitleText": "消息列表", "app-plus": { "titleNView": { "buttons": [ { "color": "#333333", "colorPressed": "#FD597C", "float": "left", "fontSize": "20px", "fontSrc": "/static/iconfont.ttf", "text": "\ue611" }, { "color": "#333333", "colorPressed": "#FD597C", "float": "right", "fontSize": "20px", "fontSrc": "/static/iconfont.ttf", "text": "\ue649" } ] } } }
消息列表组件开发
-
静态开发
-
引入数字脚标组件
<view> <view class="flex align-center justify-center p-2 border-bottom border-light-secondary"> <image src="../../static/default.jpg" style="height: 80rpx; width: 80rpx;" class="rounded-circle mr-2"></image> <view class="flex-column flex flex-1"> <view class="flex align-center justify-between"> <text class="font-md text-dark">昵称</text> <text class="font-sm text-secondary">17:00</text> </view> <view class="py-1 flex align-center justify-between"> <view class=" font-sm text-secondary">内容</view> <uni-badge text="1" type="error"></uni-badge> </view> </view> </view> </view>
封装消息列表组件
-
优化内容, 添加对应css和最大宽度
<view class="pt-1 flex align-center justify-between"> <view class=" font-sm text-secondary text-ellipsis" style="max-width: 500rpx;">内容内容内容内容内容内容内容内容内容内容内容内容内容</view> <uni-badge text="1" type="error"></uni-badge> </view>
-
封装数据,封装组件, 时间使用在线时间戳
-
引入time.js, 使用对应库,使用过滤器
import $T from '@/common/time.js' filters:{ formatTime(value){ return $T.gettime(value); } },
-
组件分离
<block v-for="(item,index) in list" :key="index"> <msgList :item="item" :index="index"></msgList> </block> <template> <view class="flex align-center justify-center p-2 border-bottom border-light-secondary"> <image :src="item.avatar" style="height: 80rpx; width: 80rpx;" class="rounded-circle mr-2"></image> <view class="flex-column flex flex-1"> <view class="flex align-center justify-between"> <text class="font-md text-dark">{{item.username}}</text> <text class="font-sm text-secondary">{{item.update_time | formatTime}}</text> </view> <view class="pt-1 flex align-center justify-between"> <view class=" font-sm text-secondary text-ellipsis" style="max-width: 500rpx;">{{item.data}}</view> <uni-badge :text="item.noread" type="error"></uni-badge> </view> </view> </view> </template> <script> import $T from '@/common/time.js' export default { props:{ item:Object, index:Number }, filters: { formatTime(value){ // console.log(value); return $T.gettime(value); } } } </script> <style> </style>
下拉刷新功能实现
-
page.json配置
"enablePullDownRefresh": true,
-
监听下拉刷新, 写入方法
onPullDownRefresh() { this.refresh(); }, refresh(){ setTimeout(()=>{ this.list=demo; uni.stopPullDownRefresh(); },2000); }
-
引入nothing组件, v-if判断
<template> <view> <template v-if="this.list.length>0"> <block v-for="(item,index) in list" :key="index"> <msgList :item="item" :index="index"></msgList> </block> </template> <template v-else> <no-thing></no-thing> </template> </view> </template> <script> const demo=[{ avatar:'../../static/default.jpg', username:'昵称', update_time:1648458088, data:'内容内容内容内容内容内容内容内容内容内容内容内容内容', noread:20 }, { avatar:'../../static/default.jpg', username:'昵称', update_time:1648458088, data:'内容内容内容内容内容内容内容内容内容内容内容内容内容', noread:20 }, { avatar:'../../static/default.jpg', username:'昵称', update_time:1648458088, data:'内容内容内容内容内容内容内容内容内容内容内容内容内容', noread:20 }, { avatar:'../../static/default.jpg', username:'昵称', update_time:1648458088, data:'内容内容内容内容内容内容内容内容内容内容内容内容内容', noread:20 }]; import msgList from '@/components/msg/msg-list'; export default { components:{ msgList }, data() { return { list:[] } }, onLoad() { this.list=demo; console.log(this.list); }, onPullDownRefresh() { this.refresh(); }, methods: { refresh(){ setTimeout(()=>{ this.list=[...this.list,...this.list]; uni.stopPullDownRefresh(); },2000); } } } </script> <style> </style>
下拉弹出层组件使用
-
使用uni-poup组件,
-
监听原生导航栏按钮事件,弹出弹出层
<uni-popup ref="popup" type="top" background-color="#fff">123</uni-popup> onNavigationBarButtonTap(e) { switch (e.index){ case 0: break; case 1: this.$refs.popup.open(); break; } },
下拉弹出选项完善
-
静态开发
-
添加点击事件
<uni-popup ref="popup" type="top" background-color="#fff"> <view class="flex justify-center align-center font-md border-bottom py-2" hover-class="bg-light" @click="popupEvent('findFriend')"> <text class="iconfont icon-sousuo mr-2" ></text>搜索好友 </view> <view class="flex justify-center align-center font-md py-2" hover-class="bg-light" @click="popupEvent('deleteList')"> <text class="iconfont icon-shanchu mr-2"></text>删除列表 </view> </uni-popup> popupEvent(event){ switch (event){ case 'findFriend': { console.log('findFriend'); this.$refs.popup.close(); break; } case 'deleteList': { console.log('deleteList'); this.$refs.popup.close(); break; } } }
我的好友列表页开发
page.json配置
-
新建user-list, 导航进入该页面
onNavigationBarButtonTap(e) { switch (e.index){ case 0: uni.navigateTo({ url:'../user-list/user-list' }) break; case 1: this.$refs.popup.open(); break; } },
-
配置page.json
{ "path": "pages/user-list/user-list", "style": { "navigationBarTitleText": "", "app-plus": { "animationType": "slide-in-left", "titleNView": { "autoBackButton": true, "searchInput": { "align": "center", "backgroundColor": "#F5F4F2", "borderRadius": "4px", "disabled": true, "placeholder": "搜索用户", "placeholderColor": "#6D6C67" }, "buttons": [{ "color": "#333333", "colorPressed": "#FD597C", "float": "right", "fontSize": "14px", "text": "取消" }] } } }
-
监听点击输入框事件, 监听取消按钮事件
onNavigationBarSearchInputClicked() { // console.log('跳转'); uni.navigateTo({ url:'../search/search' }) }, onNavigationBarButtonTap() { uni.navigateBack({ delta: 1 }) },
tab导航组件再次优化
-
引入tabBar导航
-
条件渲染数字
<view class="flex align-center py-2"> <text class="flex-1 flex align-center justify-center" v-for="(item,index) in tabBar" :key="index" :class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'" @click="changeTab(index)">{{item.name}} <text v-if="item.num>0" class="ml-1">{{item.num}}</text> </text> </view>
好友列表组件开发
-
引入首页的scrollview, 下拉, 对应修改和引入
-
列表样式静态开发
<swiper duration=150 @change="onChange" :current="tabIndex" :style="'height:'+scrollH+'px'"> <swiper-item v-for="(item,index) in newList" :key="index"> <template v-if="item.list.length>0"> <scroll-view scroll-y="true" :style="'height:'+scrollH+'px'" @scrolltolower="loadMore(index)"> <block v-for="(item2,index2) in item.list" :key="index2"> <view class="flex align-center p-2 border-bottom border-light-secondary"> <image src="../../static/default.jpg" class="rounded-circle mr-2" style="width: 100rpx; height: 100rpx;"></image> <view class="flex flex-column flex-1"> <text class="font-md text-dark">昵称</text> <text class="font-sm mt-1">性别</text> </view> <view class="uni-icon uni-icon-checkbox-filled text-light-muted "></view> </view> </block> <loadMore :loading="item.loading"></loadMore> </scroll-view> </template> <template v-else> <no-thing></no-thing> </template> </swiper-item> </swiper> getData() { let arr = []; for (var i = 0; i < this.tabBars.length; i++) { let obj = { loading: '上拉加载更多', list: [] } if (i < 2) { obj.list = [1,2,3,4]; } arr.push(obj) }; this.newList = arr; }, changeTab(index){ this.tabIndex=index; }, onChange(e){ this.changeTab(e.detail.current); }, loadMore(index) { let item = this.newList[index]; if ((item.loading) !== '上拉加载更多') { return; } item.loading = '加载中...'; setTimeout(() => { item.list = [...item.list, ...item.list]; item.loading = '上拉加载更多'; }, 2000) }
强化badge组件开发
-
使用uni-badge, 插入图标, 优化性别显示
<view> <text class="iconfont icon-nv text-secondary"style="font-size: 18rpx;"></text> <uni-badge type="error" text="24"> </uni-badge> </view>
封装好友列表组件
-
测试demo数据
const demo=[{ avatar:'../../static/default.jpg', username:'昵称', sex:1, age:24, isFollow:false }, { avatar:'../../static/default.jpg', username:'昵称', sex:2, age:24, isFollow:true } ];
-
数据渲染,动态绑定性别class, 点击灰色
-
封装组件, 引入组件
<template> <view> <view class="flex align-center p-2 border-bottom border-light-secondary " hover-class="bg-light"> <image :src="item.avatar" class="rounded-circle mr-2" style="width: 100rpx; height: 100rpx;"></image> <view class="flex flex-column flex-1"> <text class="font-md text-dark">{{item.username}}</text> <view v-if="item.sex>0"> <text class="iconfont text-secondary" :class="item.sex===1?'icon-nv':'icon-nan'" style="font-size: 18rpx;"></text> <uni-badge :type="item.sex===1?'error':'primary'" :text="item.age"> </uni-badge> </view> </view> <view class="uni-icon uni-icon-checkbox-filled " :class="item.isFollow?'text-light-muted':'text-main'"></view> </view> </view> </template> <script> export default { name:"user-list", props:{ item:Object, index:Number }, data() { return { }; } } </script> <style> </style> import userList from '@/components/user-list/user-list.vue'; <userList :item="item2" :index="index2"></userList>
优化我的好友列表页面
-
优化scrollH问题,引入首页方法
onLoad() { this.getData(); const res = uni.getSystemInfoSync(); this.scrollH = res.windowHeight - uni.upx2px(101); // console.log(this.scrollH); },
-
隐藏上拉加载组件
<loadMore :loading="item.loading" v-if="item.list.length>10"></loadMore>
聊天界面开发
page.json配置
-
新建页面user-chat, 导航进入
-
配置导航栏
{ "path" : "pages/user-chat/user-chat", "style" :{ "app-plus": { "titleNView": { "buttons": [ { "color": "#333333", "colorPressed": "#FD597C", "float": "right", "fontSize": "20px", "fontSrc": "/static/iconfont.ttf", "text": "\ue628" } ] } } }
聊天输入框组件开发
-
底部操作条开发
<view class="flex fixed-bottom align-center bg-white border-top" style="height: 100rpx;"> <view class="flex-1"> <input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" /> </view> <view class="iconfont icon-fabu flex align-center justify-center font-lg animated" style="width: 100rpx;" hover-class="jello text-main"></view> </view>
聊天列表组件开发
-
scrollH获取
<scroll-view :style="'height: '+this.scrollH+'px;'" scroll-y="true"> <view v-for="i in 100" :key="i">{{i}}</view> </scroll-view> onLoad() { const res = uni.getSystemInfoSync(); this.scrollH = res.windowHeight - uni.upx2px(101); },
-
聊天列表组件开发
-
右边气泡的开发
<scroll-view :style="'height: '+this.scrollH+'px;'" scroll-y="true"> <view class="flex align-start px-2 my-2"> <image src="../../static/default.jpg" class="rounded-circle" style="height: 100rpx; width: 100rpx;"></image> <view class="bg-light mx-2 p-2 rounded mt-1" style="max-width: 400rpx; min-width: 100rpx;"> 你好啊 </view> </view> <view class="flex align-start px-2 my-2" style="flex-direction: row-reverse;"> <image src="../../static/default.jpg" class="rounded-circle" style="height: 100rpx; width: 100rpx;"></image> <view class="bg-light mx-2 p-2 rounded mt-1" style="max-width: 400rpx; min-width: 100rpx;"> 你好啊 </view> </view> </scroll-view>
封装聊天列表组件完善时间显示
-
新建user-chat-list, 封装数据
-
计算属性, 是否本人
isShelf(){ return this.item.userId===1 },
-
时间显示开发
<view class="my-2 flex align-center justify-center text-secondary font-sm"> {{shortTime}} </view>
-
引入time.js, 优化时间显示, 用计算属性
shortTime(){ return $T.getChatTime(this.item.create_time,this.preTime); } <userChatList :item="item" :index="index" :preTime=" index>0 ? list[index-1].create_time : 0"></userChatList>
完善聊天页功能
-
输入框绑定内容, 绑定发送事件, 发送功能实现
sendMessage(){ let obj={ userId:1, avatar: '../../static/default.jpg', data: this.content, type: 'text', create_time: (new Date()).getTime() }; if(obj.data===''){ uni.showToast({ title:"请输入内容" }); return; } this.list.push(obj); this.content='' }
-
优化功能, 最小宽度去掉, 键盘推页面false, 清空输入框, 丢弃scrollH
<input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" v-model="content" adjust-position="false" /> <scroll-view style="position: absolute; left: 0 ; top: 0; right: 0; bottom: 100rpx;" scroll-y="true">
-
滚动到底部实现
<scroll-view style="position: absolute; left: 0 ; top: 0; right: 0; bottom: 100rpx;" scroll-y="true" :scroll-into-view="scrollInto" scroll-with-animation> <block v-for="(item,index) in list" :key="index"> <userChatList :id="'chat'+index" :item="item" :index="index" :preTime=" index>0 ? list[index-1].create_time : 0"></userChatList> </block> </scroll-view> pageToBottom(){ let lastIndex=this.list.length-1; if(lastIndex < 0){ return } this.scrollInto='chat'+lastIndex; console.log(this.scrollInto); } onReady() { this.pageToBottom(); },
搜索列表页开发
搜索列表功能完善
-
添加搜索标识
onNavigationBarSearchInputClicked() { uni.navigateTo({ url: '../search/search?type=post' }) },
-
获取类型, 修改搜索占位, 添加如果说是app端
if (e.type) { this.type = e.type; } let pageTitle=''; switch (this.type) { case 'post': pageTitle='搜索帖子' break; case 'topic': pageTitle='搜索话题' break; case 'friend': pageTitle='搜索好友' break; } console.log(this.type); // #ifdef APP-PLUS let currentWebview=this.$scope.$getAppWebview(); let tn=currentWebview.getStyle().titleNView; tn.searchInput.placeholder=pageTitle; console.log(tn); currentWebview.setStyle({ titleNView:tn }) // #e
-
搜索结果完善优化, 根据不同搜索,组件,数据都要不同
searchEvent() { uni.hideKeyboard(); uni.showLoading({ title: '加载中' }) setTimeout(() => { switch (this.type) { case 'post': this.serachList = demo1; break; case 'topic': this.serachList = demo2; break; case 'friend': this.serachList = demo3; break; } uni.hideLoading(); }, 3000) }, <block v-for="(item,index) in serachList" :key="index"> <template v-if="type==='post'"> <commonList :item="item" :index="index"></commonList> </template> <template v-if="type==='topic'"> <topicList :item="item" :index="index"></topicList> </template> <template v-if="type==='friend'"> <userList :item="item" :index="index"></userList> </template> </block>
文章详情页开发
page.json配置
-
新建页面,detail, 导航入口
,{ "path" : "pages/detail/detail", "style" : { "app-plus": { "titleNView": { "buttons": [ { "float": "right", "type": "menu" } ] } } } }
-
把对象作为参数传过去,初始化
openDetail(){ console.log("打开详情页"); uni.navigateTo({ url:'../../pages/detail/detail?detail='+JSON.stringify(this.item) }) }, onLoad(e){ if(e.detail){ this.__init(JSON.parse(e.detail)); } }, methods: { __init(data){ uni.setNavigationBarTitle({ title:data.title }) } }
强化公共列表组件功能
-
修改公共列表组件, 添加isDetail, prop, 评论和分享功能
props: { item: Object, index: { type: Number, default: -1 }, isDetail: { type: Boolean, default: false } }, doComment(){ if(!this.isDetail){ return this.openDetail(); } this.$emit('doComment'); }, doShare(){ if(!this.isDetail){ return this.openDetail(); } this.$emit('doShare'); }
<commonList :item="this.item" :isDetail="true" @doComment="doComment" @doShare="doShare"> 帖子详请 </commonList>
完善详情页关注顶踩功能
-
关注事件, 修改index默认值
follow() { this.info.isFollow = true; } index: { type: Number, default: -1 }
-
顶踩方法改写
doSupport(e) { let obj = this.info if (obj.support.type === e.type) { uni.showToast({ title: '你已经操作过了' }) return; } if (obj.support.type === '') { //无操作 obj.support[e.type + '_count']++; } else if (obj.support.type === 'support' && e.type === 'unsupport') { //之前是顶,顶减一,踩加一 obj.support.support_count--; obj.support.unsupport_count++; } else if (obj.support.type === 'unsupport' && e.type === 'support') { //之前是踩,顶加一,踩减一 obj.support.support_count++; obj.support.unsupport_count--; } obj.support.type = e.type; let msg = e.type === 'support' ? '顶' : '踩'; uni.showToast({ title: msg + '成功' }) }
-
增加content,image
content:"Hillky正在开发ing.....", images:[ { url:"https://tupian.qqw21.com/article/UploadPic/2020-5/20205622141239876.jpg" }, { url:"https://tse1-mm.cn.bing.net/th/id/R-C.df1d553893d9b7888c725b8dbcbcf439?rik=hpbIzO6xZ3Qchw&riu=http%3a%2f%2fwww.chabeichong.com%2fimages%2f2016%2f11%2f12-04122113.jpg&ehk=%2fe971CgX%2bMeAgZuGCVac3td74wDOd1%2bWzz0q4IsP1Lc%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1" } ]
-
增加图片预览功能
<image v-for="(item,index) in info.images" :src="item.url" class="w-100" mode="widthFix" @click="preview(index)"></image> preview(index){ // console.log(this.imageList); uni.previewImage({ urls:this.imageList, current:index }) } computed:{ imageList(){ return this.info.images.map(item=>item.url); } },
评论输入框组件封装
-
先对聊天室底部操作条的封装,测试是否成功
<template> <view class="flex fixed-bottom align-center bg-white border-top" style="height: 100rpx;"> <view class="flex-1"> <input type="text" placeholder="请文明发言" class="rounded ml-2 bg-light p-1" v-model="content" adjust-position="false" /> </view> <view class="iconfont icon-fabu flex align-center justify-center font-lg animated" style="width: 100rpx;" hover-class="jello text-main" @click="sendMessage"></view> </view> </template> <script> export default { data(){ return{ content:'' } }, methods:{ sendMessage(){ if (this.content === '') { uni.showToast({ title: "请输入内容", icon:"none" }); return; } this.$emit('submit',this.content); this.content = ''; } } } </script> <style> </style> <bottomBtn @submit="submit"></bottomBtn> submit(content) { let obj = { userId: 1, avatar: '../../static/default.jpg', data: content, type: 'text', create_time: (new Date()).getTime() }; this.list.push(obj); this.pageToBottom(); },
-
再到detail页面使用该组件,需要占位
列表评论组件开发
-
使用官方评论界面,进行静态开发
<view class="px-2"> <!-- 评论区 start --> <view class="uni-comment"> <view class="uni-comment-list"> <view class="uni-comment-face" style="margin-top: 15rpx;"> <image src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/images/uni@2x.png" mode="widthFix"> </image> </view> <view class="uni-comment-body"> <view class="uni-comment-top"> <text>网友</text> </view> <view class="uni-comment-date"> <text>08/10 08:12</text> </view> <view class="uni-comment-content">很酷的HBuilderX和uni-app,开发一次既能生成小程序,又能生成App</view> </view> </view> </view> </view>
分享功能组件开发
-
弹出层,底部弹出, 监听导航栏按钮触发, 层级关系的修改-z-index
<uni-popup ref="popup" type="bottom" background-color="#fff"> <view style="height: 300rpx;"> 123 </view> </uni-popup> onNavigationBarButtonTap() { this.$refs.popup.open(); }, uni-popup { position: fixed; /* #ifndef APP-NVUE */ z-index: 9999;
-
监听返回事件,隐藏弹出层
onBackPress() { this.$refs.popup.close(); },
-
静态开发
<view class="font-md border-bottom border-light-secondary text-center py-2">分享到</view> <view class="flex align-center "> <view class="flex flex-1 flex-column justify-center align-center py-2"> <view class="iconfont icon-QQ rounded-circle bg-primary text-white flex align-center justify-center font-lg" style="width: 100rpx; height: 100rpx;"></view> <view class="font mt-1 text-light-muted">QQ好友</view> </view> ... </view> <view class="font-md border-top border-light-secondary text-center py-2">取消</view>
-
遍历图标
<block v-for="(item,index) in btnList" :key="index"> <view class="flex flex-1 flex-column justify-center align-center py-2"> <view class="iconfont rounded-circle bg-primary text-white flex align-center justify-center font-lg" :class="item.icon+' '+item.color " style="width: 100rpx; height: 100rpx;"></view> <view class="font mt-1 text-light-muted">{{item.name}}</view> </view> </block>
-
封装组件 more-share.vue
<template> <view> <uni-popup ref="popup" type="bottom" background-color="#fff"> <view class="font-md border-bottom border-light-secondary text-center py-2">分享到</view> <view class="flex align-center "> <block v-for="(item,index) in btnList" :key="index"> <view class="flex flex-1 flex-column justify-center align-center py-2"> <view class="iconfont rounded-circle bg-primary text-white flex align-center justify-center font-lg" :class="item.icon+' '+item.color " style="width: 100rpx; height: 100rpx;"></view> <view class="font mt-1 text-light-muted">{{item.name}}</view> </view> </block> </view> <view class="font-md border-top border-light-secondary text-center py-2">取消</view> </uni-popup> </view> </template> <script> export default { data(){ return{ btnList: [{ "icon": 'icon-QQ', "color": 'bg-primary', "name": 'QQ好友' }, { "icon": 'icon-QQ', "color": 'bg-primary', "name": 'QQ好友' }, { "icon": 'icon-QQ', "color": 'bg-primary', "name": 'QQ好友' }, { "icon": 'icon-QQ', "color": 'bg-primary', "name": 'QQ好友' } ], } }, methods:{ open(){ this.$refs.popup.open(); }, close(){ this.$refs.popup.close(); } } } </script> <style> </style>
<moreShare ref="share"></moreShare> onNavigationBarButtonTap() { this.$refs.share.open(); }, onBackPress() { this.$refs.share.close(); },
-
用官方组件分享动态渲染数据, 子组件用created()
created(){ uni.getProvider({ service: 'share', success: (e) => { console.log(e); let data = [] for (let i = 0; i < e.provider.length; i++) { switch (e.provider[i]) { case 'weixin': data.push({ name: '微信好友', id: 'weixin', icon:'icon-weixin', color:'bg-success', sort:0 }) data.push({ name: '朋友圈', id: 'weixin', icon:'icon-huati', color:'bg-dark', type:'WXSenceTimeline', sort:1 }) break; case 'sinaweibo': data.push({ name: '新浪微博', icon:'icon-xinlangweibo', color:'bg-danger', id: 'sinaweibo', sort:2 }) break; case 'qq': data.push({ name: 'QQ好友', id: 'qq', icon:'icon-QQ', color:'bg-primary', sort:3 }) break; default: break; } } this.providerList = data.sort((x,y) => { return x.sort - y.sort }); }, fail: (e) => { console.log('获取分享通道失败', e); uni.showModal({ content:'获取分享通道失败', showCancel:false }) } }); },
-
分享方法
async share(e) { console.log('分享通道:'+ e.id +'; 分享类型:' + this.shareType); if(!this.shareText && (this.shareType === 1 || this.shareType === 0)){ uni.showModal({ content:'分享内容不能为空', showCancel:false }) return; } if(!this.image && (this.shareType === 2 || this.shareType === 0)){ uni.showModal({ content:'分享图片不能为空', showCancel:false }) return; } let shareOPtions = { provider: e.id, scene: e.type && e.type === 'WXSenceTimeline' ? 'WXSenceTimeline' : 'WXSceneSession', //WXSceneSession”分享到聊天界面,“WXSenceTimeline”分享到朋友圈,“WXSceneFavorite”分享到微信收藏 type: this.shareType, success: (e) => { console.log('success', e); uni.showModal({ content: '已分享', showCancel:false }) }, fail: (e) => { console.log('fail', e) uni.showModal({ content: e.errMsg, showCancel:false }) }, complete:function(){ console.log('分享操作结束!') } } switch (this.shareType){ case 0: shareOPtions.summary = this.shareText; shareOPtions.imageUrl = this.image; shareOPtions.title = '欢迎体验uniapp'; shareOPtions.href = 'https://uniapp.dcloud.io'; break; case 1: shareOPtions.summary = this.shareText; break; case 2: shareOPtions.imageUrl = this.image; break; case 5: shareOPtions.imageUrl = this.image ? this.image : 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/b6304f00-5168-11eb-bd01-97bc1429a9ff.png' shareOPtions.title = '欢迎体验uniapp'; shareOPtions.miniProgram = { id:'gh_33446d7f7a26', path:'/pages/tabBar/component/component', webUrl:'https://uniapp.dcloud.io', type:0 }; break; default: break; } if(shareOPtions.type === 0 && plus.os.name === 'iOS'){//如果是图文分享,且是ios平台,则压缩图片 shareOPtions.imageUrl = await this.compress(); } if(shareOPtions.type === 1 && shareOPtions.provider === 'qq'){//如果是分享文字到qq,则必须加上href和title shareOPtions.href = 'https://uniapp.dcloud.io'; shareOPtions.title = '欢迎体验uniapp'; } uni.share(shareOPtions); },
个人中心页面开发
page.json配置
-
配置page.json
{ "path": "pages/my/my", "style": { "navigationBarTitleText": "我的", "app-plus": { "titleNView": { "buttons": [ { "type": "menu" } ] } } } },
个人中心ui构建
-
静态开发第一个view
<view class="flex align-center p-2"> <image src="../../static/default.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle"></image> <view class="flex flex-column flex-1 px-2"> <text class="font-lg font-weight-bold text-dark ">昵称</text> <text class="font text-muted mt-1"> 总帖子10 今日发帖0</text> </view> <view class="iconfont icon-jinru"></view> </view>
-
第二个view开发,使用遍历方式
<view class="flex align-center px-3 py-2"> <view class="flex-1 flex-column flex align-center justify-center" v-for="(item,index) in this.itemList " :key="index"> <text class="font-lg text-dark">{{item.num}}</text> <text class="text-muted fony">{{item.name}}</text> </view> </view>
-
第三个view, 广告位的开发
<view class="px-3 py-2"> <image src="/static/demo/banner1.jpg" style="height: 300rpx; width: 100%;" mode="aspectFill" class="rounded"></image> </view>
-
引入uni-list-item
<uni-list-item title="浏览历史" :showExtraIcon="true" :extra-icon="extraIcon1" link :border="false"></uni-list-item> <uni-list-item title="社区认证" :showExtraIcon="true" :extra-icon="extraIcon2" link :border="false"> </uni-list-item> <uni-list-item title="审核帖子" :show-extra-icon="true" link :border="false"> <text slot="icon" class="iconfont icon-keyboard font-lg"></text> </uni-list-item> import uniListItem from '@/components/uni-list-item/uni-list-item.vue'; extraIcon1: { type: 'eye', color: '#000000', size: 20 }, extraIcon2: { type: 'vip', color: '#000000', size: 20 },
设置页面开发
-
新建页面,导航进入
-
退出登录静态开发
<template> <view> <uni-list-item title="账号与安全" link :border="false" ></uni-list-item> <uni-list-item title="资料编辑" link :border="false" ></uni-list-item> <uni-list-item title="清楚缓存" link :border="false"></uni-list-item> <uni-list-item title="意见反馈" link :border="false"></uni-list-item> <uni-list-item title="关于社区" link :border="false"></uni-list-item> <view class="py-2 px-3"> <button class="bg-main text-white" style="border-radius: 50rpx;">退出登录</button> </view> </view> </template> <script> import uniListItem from '@/components/uni-list-item/uni-list-item.vue'; export default { components:{ uniListItem }, data() { return { } }, methods: { } } </script> <style> </style>
修改密码页面开发
修改密码UI界面开发
-
新建页面,导航进入
<uni-list-item title="账号与安全" link :border="false" @click="open"></uni-list-item> uni.navigateTo({ url:'../userPassword/userPassword' })
-
静态页面开发
<view class="px-1"> <input class="uni-input" value="" type="text" placeholder="输入旧密码"/> <input class="uni-input" value="" type="text" placeholder="输入新密码"/> <input class="uni-input" value="" type="text" placeholder="输入确认密码"/> <view class="py-2 px-3"> <button class="bg-main text-white" style="border-radius: 50rpx;">设置</button> </view> </view>
表单验证功能实现
-
输入框绑定vue, disabled属性绑定
computed:{ disable(){ return this.oldPassword===''||this.newPassword===''||this.renewPassword===''; } }, <button class="bg-main text-white" style="border-radius: 50rpx;" :disabled="disable">设置</button>
-
验证功能实现
check(){ if(this.newPassword!==this.renewPassword){ uni.showToast({ title:'两次输入密码不一致', icon:"none" }) return false; } return true; }, submit(){ if(this.check()){ console.log('提交成功'); } }
修改邮箱页面开发
修改邮箱页UI界面开发
-
新建页面, 配置page.json
,{ "path" : "pages/userEmail/userEmail", "style" : { "navigationBarTitleText": "设置邮箱" } }
-
拼接字符串, 配置导航,引号不一样用1左边的那个,${}拼接
open(path){ uni.navigateTo({ url:`../${path}/${path}`, fail(e) { console.log(e); } }) }
-
静态页面开发,与修改密码差不多的功能,禁用
<template> <view> <input type="text" class="uni-input" placeholder="请输入邮箱" v-model="email"/> <input type="text" class="uni-input" placeholder="请输入密码" v-model="password"/> <button class="bg-main text-white mt-1" style="border-radius: 50rpx;" :disabled="disable">绑定邮箱</button> </view> </template> <script> export default { data() { return { email:'', password:'' } }, computed:{ disable(){ return this.email===''||this.password==='' } }, methods: { } } </script> <style> </style>
表单验证功能实现
-
利用正则表达式完成邮箱功能验证js常用正则表达式
methods: { check(){ var ePattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; if(!ePattern.test(this.email)){ uni.showToast({ title:'邮箱格式不正确', icon:"none" }) return false; } return true; }, submit(){ if(this.check()){ console.log("提交成功"); } return; } }
编辑资料页面开发
编辑资料UI界面实现
-
新建页面,配置page.json,配置导航
,{ "path" : "pages/userInfo/userInfo", "style" : { "navigationBarTitleText": "资料编辑" } }
-
使用uni-list-item实现静态页面,使用插槽修改
<template> <view> <uni-list-item title="头像" :border="false" > <view slot="right" class="flex"> <image class="rounded" style="width: 100rpx; height: 100rpx;" src="../../static/default.jpg"></image> <text class="flex align-center iconfont icon-bianji1 ml-2" ></text> </view> </uni-list-item> <uni-list-item title="昵称" :border="false"> <view slot="right" class="flex "> 哈哈 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <uni-list-item title="性别" :border="false"> <view slot="right" class="flex "> 未知 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <uni-list-item title="生日" :border="false"> <view slot="right" class="flex "> 2021-4-1 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <uni-list-item title="情感" :border="false"> <view slot="right" class="flex "> 已婚 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <uni-list-item title="职业" :border="false"> <view slot="right" class="flex align-center"> 程序员 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <uni-list-item title="家乡" :border="false"> <view slot="right" class="flex "> 广东广州 <text class="flex align-center iconfont icon-bianji1 ml-2 " ></text> </view> </uni-list-item> <view class="py-2 px-3"> <button class="bg-main text-white" style="border-radius: 50rpx;">完成</button> </view> </view> </template> <script> import uniListItem from '@/components/uni-list-item/uni-list-item.vue'; export default { components:{ uniListItem }, data() { return { } }, methods: { } } </script> <style> </style>
修改头像功能
-
添加点击事件, 动态绑定userPIc
<uni-list-item title="头像" :border="false"> <view slot="right" class="flex"> <image class="rounded" style="width: 100rpx; height: 100rpx;" :src="userPic"></image> <text class="flex align-center iconfont icon-bianji1 ml-2" @click="changePic" ></text> </view> </uni-list-item>
-
uni.chooseImage的使用, 修改头像功能实现
changePic(){ uni.chooseImage({ count:1, sizeType:["compressed"], sourceType:["album","camera"], success:(e)=>{ this.userPic=e.tempFilePaths[0]; } }) }
showActionSheet接口使用
-
修改昵称,用输入框
<input v-model="nickname" class="text-right"/>
-
修改性别, 使用showActionSheet,使用计算属性渲染
<uni-list-item title="性别" :border="false"> <view slot="right" class="flex "> {{sexText}} <text class="flex align-center iconfont icon-bianji1 ml-2 " @click="changeSex"></text> </view> </uni-list-item> const sexArray=["保密","男","女"]; sex:0, sexText(){ return sexArray[this.sex]; }, changeSex(){ uni.showActionSheet({ itemList:sexArray, success:(e)=>{ // this.sex=sexArray[e.tapIndex]; this.sex=e.tapIndex; } }) },
-
修改情感, 使用一样的方法
-
修改职业, 使用一样的方法
changeJob(){ uni.showActionSheet({ itemList:jobArray, success:(e)=>{ this.job=jobArray[e.tapIndex]; } }) }
修改生日功能实现
-
使用picker,完成该功能
<picker mode="date" :value="birthday" @change="onDateChange"> <uni-list-item title="生日" :border="false"> <view slot="right" class="flex "> {{birthday}} <text class="flex align-center iconfont icon-bianji1 ml-2 "></text> </view> </uni-list-item> </picker> onDateChange(e){ this.birthday=e.detail.value; }
三级城市联动多列选择器选择城市
-
使用官方组件mpvueCityPicker
<uni-list-item title="家乡" :border="false"> <view slot="right" class="flex "> {{pickerText}} <text class="flex align-center iconfont icon-bianji1 ml-2 " @click="showCityPicker"></text> </view> </uni-list-item> <mpvue-city-picker :themeColor="themeColor" ref="mpvueCityPicker" :pickerValueDefault="cityPickerValueDefault" @onConfirm="onConfirm"></mpvue-city-picker> import mpvueCityPicker from '@/components/mpvue-citypicker/mpvueCityPicker.vue' mpvueCityPicker themeColor:'#007AFF', cityPickerValueDefault:[0, 0, 1], pickerText:'广东广州' onBackPress() { if (this.$refs.mpvueCityPicker.showPicker) { this.$refs.mpvueCityPicker.pickerCancel(); return true; } }, onUnload() { if (this.$refs.mpvueCityPicker.showPicker) { this.$refs.mpvueCityPicker.pickerCancel() } }, onConfirm(e){ this.pickerText = e.label; }, showCityPicker(){ this.$refs.mpvueCityPicker.show() }
帮助反馈页面开发
帮助反馈UI界面实现
-
新建页面,配置page.json
-
使用组件uni-collapse
<view> <uni-collapse accordion> <uni-collapse-item title="默认开启" > <text>折叠内容</text> </uni-collapse-item> <uni-collapse-item title="默认开启" > <text>折叠内容</text> </uni-collapse-item> </uni-collapse> <view class="py-2 px-3"> <button class="bg-main text-white" style="border-radius: 50rpx;">意见反馈</button> </view> </view>
关于页面开发
关于页面UI界面
-
新建页面,配置page.json
-
静态页面开发
<view> <view class="flex align-center justify-center flex-column pt-4 pb-3"> <image src="../../static/common/nothing.png" style="width: 300rpx; height: 300rpx;" class="rounded-circle"></image> <text class="font text-muted mt-2">version 1.0.1</text> </view> <uni-list-item title="新版本检测" :border="false" link></uni-list-item> <uni-list-item title="社区用户协议" :border="false" link></uni-list-item> </view>
登录页开发
page.json配置
-
新建页面,取消原生导航,导航进入
,{ "path" : "pages/login/login", "style" : { "navigationBarTitleText": "", "app-plus": { "titleNView": false } } } <navigator url="../login/login"> <view class="flex align-center p-2" hover-class="bg-light"> <image src="../../static/default.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle"> </image> <view class="flex flex-column flex-1 px-2"> <text class="font-lg font-weight-bold text-dark ">昵称</text> <text class="font text-muted mt-1"> 总帖子10 今日发帖0</text> </view> <view class="iconfont icon-jinru"></view> </view> </navigator>
-
x的样式开发
<view class="flex iconfont icon-guanbi algin-center justify-center font-lg " style="width: 100rpx; height: 100rpx; position: fixed; left: 0;" :style="'top:'+statusBarHeight+'px;'" hover-class="bg-light" @click="back"></view> var res=uni.getSystemInfoSync(); this.statusBarHeight=res.statusBarHeight; back(){ uni.navigateBack({ delta:1 }) }
登录页UI界面构建
-
对之前的开发进行修改,引用一个view代替状态栏即可
<template> <view> <view class="status_bar"> <!-- 这里是状态栏 --> </view> <view> 状态栏下的文字 </view> </view> </template> <style> .status_bar { height: var(--status-bar-height); width: 100%; } </style>
-
静态页面开发
<view class="flex iconfont icon-guanbi algin-center justify-center font-lg " style="width: 100rpx; height: 100rpx; position: fixed; left: 0;" hover-class="bg-light" @click="back"> </view> <view class="flex algin-center justify-center " style="margin-top: 300rpx;"> <text class="text-secondary " style="font-size: 60rpx;">账号密码登录</text> </view> <view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;"> <input type="text" placeholder="昵称/手机号/邮箱" class="" /> </view> <view class="flex algin-center border-bottom"> <input type="text" placeholder="请输入密码" class="py-3 px-2 flex-1" /> <text style="width: 150rpx;" class="text-muted py-3">忘记密码</text> </view> <view class="py-2 px-3 " style="margin-top: 100rpx;"> <button class="bg-main text-white" style="border-radius: 50rpx;">登录</button> </view> <view class="mt-5 flex algin-center justify-center"> <view class="text-primary pr-2">验证码登录</view> <view class="text-secondary">|</view> <view class="text-primary pl-2">登录遇到问题</view> </view> <view class="flex algin-center justify-center mt-5"> <view style="width: 100rpx; border-bottom: solid #ddd ;" class="mb-2" ></view> <view class="text-muted mx-2">社交账号登录</view> <view style="width: 100rpx; border-bottom: solid #ddd ; "class="mb-2" ></view> </view>
登录类型切换效果实现
-
静态开发图标列表
<view class="flex align-center px-5 py-3" style="padding-left: 100rpx; padding-right: 100rpx;"> <view class="flex-1 flex align-center justify-center"> <view class="iconfont icon-QQ font-lg bg-primary text-white 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 bg-primary text-white 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 bg-primary text-white flex align-center justify-center rounded-circle" style="width: 100rpx;height: 100rpx;"> </view> </view> </view> <view class="flex algin-center justify-center px-5 text-muted"> 注册即代表同意<text class="text-primary">《xxx社区协议》</text> </view>
-
切换验证码的静态显示
<view class="flex algin-center justify-center " style="margin-top: 300rpx;"> <text class="text-secondary " style="font-size: 60rpx;"> {{status?'账号密码登录':'验证码登录'}}</text> </view> <template v-if="status"> <view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;"> <input type="text" placeholder="昵称/手机号/邮箱" class="" /> </view> <view class="flex algin-center border-bottom"> <input type="text" placeholder="请输入密码" class="py-3 px-2 flex-1" /> <text style="width: 150rpx;" class="text-muted py-3">忘记密码</text> </view> </template> <template v-else> <view class="flex algin-center border-bottom py-3 px-2 " style="margin-top: 100rpx;"> <text class="text-dark mr-2">+86</text> <input type="text" placeholder="手机号" class="" /> </view> <view class="flex algin-center border-bottom px-2"> <input type="text" placeholder="请输入验证码" class="py-3 px-2 flex-1" /> <text style="width: 200rpx;" class="text-white bg-main py-3 flex justify-center">获取验证码</text> </view> </template> changeStatus(){ this.status=!this.status }
表单基础功能实现
-
输入框绑定内容,计算属性disable要对于账号密码与手机验证码都ok
<input type="text" placeholder="昵称/手机号/邮箱" v-model="username" class="" /> disabled(){ if((this.username===''||this.password==='')&&(this.phone===''||this.code==='')){ return true; } return false; } <button class="bg-main text-white" style="border-radius: 50rpx;" :disabled="disabled">登录</button>
-
切换登录方式要初始化表单
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>=1){ this.codeTime--; }else{ this.codeTime=0; clearInterval(timer); } },1000) } <text style="width: 200rpx;" class="text-white py-3 flex justify-center" @click="getCode" :class="codeTime>0?'bg-main-disable':'bg-main'"> {{codeTime>0?this.codeTime+'s':'获取验证码'}}</text>
-
验证手机号规则编写,用正则表达式验证, 提交方法绑定
getCode() { if (this.codeTime > 0) { return; } if(!this.validate()){ return; } this.codeTime = 60; let timer = setInterval(() => { if (this.codeTime >= 1) { this.codeTime--; } else { this.codeTime = 0; clearInterval(timer); } }, 1000) }, validate() { var mPattern = /^1[34578]\d{9}$/; var flag = mPattern.test(this.phone) // console.log(flag); if (!flag) { uni.showToast({ title: '手机格式不正确', icon: "none" }) return false; } return true; }, submit() { if(!this.validate()){ return; } console.log('验证成功'); this.validate(); }
第三方登录组件功能实现
-
新建other-login组件,实现第三方登录
<template> <view class="flex align-center px-5 py-3" style="padding-left: 100rpx; padding-right: 100rpx;"> <view class="flex-1 flex align-center justify-center" v-for="(item,index) in this.resultList" :key ="index"> <view :class="item.icon+' '+item.bgColor" class="iconfont font-lg text-white flex align-center justify-center rounded-circle " style="width: 100rpx;height: 100rpx;"> </view> </view> </view> </template> <script> export default{ data(){ return { providerList: [], resultList:[] } }, mounted() { uni.getProvider({ service: 'oauth', success: (result) => { this.providerList = result.provider.map((value) => { let providerName = ''; let icon=''; let bgColor=''; switch (value) { case 'weixin': providerName = '微信登录' icon='icon-weixin'; bgColor='bg-success'; break; case 'qq': providerName = 'QQ登录'; icon='icon-QQ'; bgColor='bg-primary'; break; case 'sinaweibo': providerName = '新浪微博登录' icon='icon-xinlangweibo'; bgColor='bg-danger'; break; default: break; } return { name: providerName, id: value, icon:icon, bgColor:bgColor } }); console.log(this.providerList); for(var i=0;i<this.providerList.length;i++){ if(this.providerList[i].id==='weixin'||this.providerList[i].id==='qq'|| this.providerList[i].id==='sinaweibo'){ this.resultList.push(this.providerList[i]); } } console.log(this.resultList); }, fail: (error) => { console.log('获取登录通道失败', error); } }); } } </script> <style> </style>
个人空间开发
page.json配置
-
新建页面, 配置导航栏按钮,导航进入
,{ "path" : "pages/user-space/user-space", "style" : { "navigationBarTitleText": "", "app-plus": { "titleNView": { "titleText": "个人空间", "buttons": [ { "type": "menu" } ] } } } } openSapce() { uni.navigateTo({ url:'../../pages/user-space/user-space' }) },
个人空间头部开发
-
静态开发
<view class="flex align-center justify-center p-3 border-bottom border-light-secondary"> <image src="../../static/default.jpg" class="rounded " style="width: 180rpx; height: 180rpx;"></image> <view class="flex flex-column flex-1 align-center justify-center " > <view class="flex align-center justify-center " style="width: 400rpx;"> <view class=" flex-1 flex align-center justify-center flex-column "> <text class="font-lg font-weight-bold">1</text> <text class="font text-muted">粉丝</text> </view> <view class=" flex flex-1 align-center justify-center flex-column"> <text class="font-lg font-weight-bold">1</text> <text class="font text-muted">粉丝</text> </view> <view class=" flex flex-1 align-center justify-center flex-column"> <text class="font-lg font-weight-bold">1</text> <text class="font text-muted">粉丝</text> </view> </view> <view class="flex align-center justify-center pt-2"> <button class="bg-main text-white" style="width: 400rpx;">关注</button> </view> </view> </view>
个人空间UI界面实现
-
借用好友列表的tabBar
<view class="flex align-center py-4" style="height: 100rox;"> <text class="flex-1 flex align-center justify-center" v-for="(item,index) in tabBars" :key="index" :class="tabIndex===index?'font-lg text-main font-weight-bold':'font-md text-dark'" @click="changeTab(index)">{{item.name}}</text> </view> data() { return { tabIndex:0, tabBars: [{ name: '主页', }, { name: '帖子', }, { name: '动态', } ] } }, changeTab(index){ this.tabIndex=index; }
-
主页的实现
<template v-if="tabIndex===0"> <view class="px-3 border-bottom py-2"> <view class="font-md">账号信息</view> <view class="font">账号年龄:12个月</view> <view class="font">账号id:1</view> </view> <view class="px-3 border-bottom py-2"> <view class="font-md">个人信息</view> <view class="font">星座:天蝎座</view> <view class="font">职业:IT</view> <view class="font">故乡:中国</view> <view class="font">情感:未婚</view> </view> </template> <template v-else> <text>帖子/动态</text> </template>
-
帖子动态的实现, 引入commonlist组件,loadMore组件,引入方法,添加动画效果
<view class="animated fast fadeIn"> <block v-for="(item,index) in list" :key="index"> <commonList :item="item" :index="index" @follow="follow" @doSupport="doSupport"> </commonList> <divider></divider> </block> <loadMore :loading="loading"></loadMore> </view> follow(index) { let obj=this.tabBars[this.tabIndex].list[index]; obj.isFollow=true; uni.showToast({ title: '关注成功' }) }, doSupport(e) { let obj=this.tabBars[this.tabIndex].list[e.index] console.log(obj); if (obj.support.type === '') { //无操作 obj.support[e.type + '_count']++; } else if (obj.support.type === 'support' && e.type === 'unsupport') { //之前是顶,顶减一,踩加一 obj.support.support_count--; obj.support.unsupport_count++; } else if (obj.support.type === 'unsupport' && e.type === 'support') { //之前是踩,顶加一,踩减一 obj.support.support_count++; obj.support.unsupport_count--; } obj.support.type = e.type; let msg = e.type === 'support' ? '顶' : '踩'; uni.showToast({ title: msg + '成功' }) },
个人空间操作菜单
-
使用弹出层,进行修改即可
<uni-popup ref="popup" type="top" background-color="#fff"> <view class="flex justify-center align-center font-md border-bottom py-2" hover-class="bg-light" @click="popupEvent('findFriend')"> <text class="iconfont icon-sousuo mr-2" ></text>加入黑名单 </view> <view class="flex justify-center align-center font-md py-2" hover-class="bg-light" @click="popupEvent('deleteList')"> <text class="iconfont icon-shanchu mr-2"></text>聊天 </view> </uni-popup> onNavigationBarButtonTap() { this.$refs.popup.open(); },
全局功能开发
清楚缓存的功能
- 添加点击事件,清除缓存的方法
==文档只有一些功能缺失,但源码已经完善所有功能,详情请点击码云地址下载源码学习 https://gitee.com/HelLichu/friend ==