uni-app开发(三)(项目实战之仿糗百首页)

一、底部导航栏 首先,首页有个底部导航——tabBar,分为四个模块,故对应在pages下创建模块,pages.json文件中代码如下:

{ "pages": [ //pages数组中第一项表示应用启动页 { "path": "pages/index/index", "style": {} ,{ "path" : "pages/news/news", "style" : {} } ,{ "path" : "pages/paper/paper", "style" : {} } ,{ "path" : "pages/home/home", "style" : {} } ], "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "仿糗事百科", "navigationBarBackgroundColor": "#FFFFFF", "backgroundColor": "#FFFFFF" }, "tabBar": { "color" : "#B5B5B5", "selectedColor" : "#FEE42B", "borderStyle" : "black", "backgroundColor" : "#FFFFFF", "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/paper/paper", "text":"小纸条", "iconPath":"/static/tabbar/paper.png", "selectedIconPath":"/static/tabbar/papered.png" }, { "pagePath":"pages/home/home", "text":"我的", "iconPath":"/static/tabbar/home.png", "selectedIconPath":"/static/tabbar/homeed.png" } ] } }

然后,参照原型图对启动页设置自定义导航栏(app-plus/titleNView),中间一个搜索框(searchInput) + 两侧各一图标(buttons),在对应page下的style中设置,代码如下:
{ "path": "pages/index/index", "style": {//不显示标题==》搜索框 + 左右图标 "app-plus":{//设置编译到 App 平台的特定样式 "scrollIndicator":"none",//隐藏滚动条 "titleNView":{//导航栏 "searchInput":{// 搜索框配置 "align":"center", "backgroundColor":"#F7F7F7", "borderRadius":"4px", "placeholder":"搜索糗事", "placeholderColor":"#CCCCCC", "disabled":true }, "buttons":[//配置按钮 {// 左边 "color":"#FF9619", "colorPressed":"#BBBBBB", "float":"left", "fontSize":"22px", "fontSrc":"/static/font/icon.ttf", "text":"\ue609" }, {// 右边 "color":"#000000", "colorPressed":"#BBBBBB", "float":"right", "fontSize":"22px", "fontSrc":"/static/font/icon.ttf", "text":"\ue653" } ] } } } }

二、列表样式及数据展示并封装 首先,在实现界面效果时,先要对一些属性的状态有所了解。
关于 Flex容器属性 - 决定主轴的方向flex-direction:row | row-reverse | column | column-reverse; - 如果一条轴线排不下,如何换行flex-wrap:nowrap | wrap | wrap-reverse; - flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap flex-flow: || ; - 定义了项目在主轴上的对齐方式justify-content:flex-start | flex-end | center | space-between | space-around; - 定义项目在交叉轴上如何对齐align-items:flex-start | flex-end | center | baseline | stretch; - 定义了多根轴线的对齐方式align-content:flex-start | flex-end | center | space-between | space-around | stretch; Flex项目属性 order:定义项目的排列顺序。数值越小,排列越靠前,默认为0 flex-grow:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大 flex-shrink:定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小 flex-basis:定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。 flex: flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto align-self:允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch 取值如 auto | flex-start | flex-end | center | baseline | stretch

根据项目需求,封装定义flex相关属性的文件common.css
/* flex布局 自定义封装*/ .u-f,.u-f-ac,.u-f-ajc{ display: flex; } .u-f-ac,.u-f-ajc{ align-items: center; } .u-f-ajc{ justify-content: center; } .u-f-jsb{ justify-content: space-between; }

对应index.vue文件中添加数据,并封装对应list的布局样式

index-list.vue文件
//scoped? .index-list{ padding: 20upx; border-bottom: 1upx solid #EEEEEE; } .index-list1>view:first-child{ color: #999999; } .index-list1>view:first-child image{ width: 85upx; height: 85upx; border-radius: 100%; margin-right: 10upx; } .index-list1>view:last-child{ background: #F4F4F4; border-radius:5upx; padding: 0 10upx; } .index-list2{ padding-top: 15upx; font-size: 32upx; } .index-list3{ position: relative; padding-top: 15upx; } .index-list3>image{ width: 100%; border-radius: 20upx; } .index-list4 view{ color: #999999; } .index-list4{ padding: 15upx 0; } .index-list4>view>view>view,.index-list4>view>view:first-child{ margin-right: 10upx; } .index-list-play{ position: absolute; font-size: 140upx; color: #FFFFFF; } .index-list-playinfo{ position: absolute; background: rgba(51, 51, 51, 0.72); color: #FFFFFF; bottom: 8upx; right: 8upx; border-radius: 40upx; font-size: 22upx; padding: 0 12upx; } .index-list4 .active,.index-list4 .active>view{ color: #C5F61C; }

问题记录:
1 props什么意思?什么情况下使用?
==》
2 style中设置scoped的作用
==》
三、滚动tab导航功能实现及封装 在官方提供的Demo中可拖动顶部选项卡:
scroll-view-——循环block(tabBars)
默认选中:active
{{tab.name}}

data() { return { tabIndex:0, tabBars:[ { name:"关注",id:"guanzhu" }, { name:"推荐",id:"tuijian" }, { name:"体育",id:"tiyu"}, { name:"热点",id:"redian"}, { name:"财经",id:"caijing" }, { name:"娱乐",id:"yule"}, ], } }

添加点击事件:@tap
methods: { tabtap(index){ this.tabIndex=index; } }

样式优化:
.uni-swiper-tab{ border-bottom: 1upx solid #EEEEEE; } .swiper-tab-list{ color: #969696; font-weight: bold; } .uni-tab-bar .active{ color: #343434; } .active .swiper-tab-line{ border-bottom: 6upx solid #FEDE33; width: 70upx; margin: auto; border-top: 6upx solid #FEDE33; border-radius:20upx; }

显示中间部分swiper-view(动态绑定style不支持upx)

需计算高度:
onLoad() { uni.getSystemInfo({ success: (res)=> {//ES5(加function?)与ES6写法的区?此处是ES6的写法 let height=res.windowHeight-uni.upx2px(100) this.swiperheight=height; } }); }, methods: { // tabbar点击事件 tabtap(index){ this.tabIndex=index; }, // 滑动事件 tabChange(e){ this.tabIndex=e.detail.current; } },

添加显示数据
data() { return { tabIndex:0, tabBars:[ { name:"关注",id:"guanzhu" }, { name:"推荐",id:"tuijian" }, { name:"体育",id:"tiyu"}, { name:"热点",id:"redian"}, { name:"财经",id:"caijing" }, { name:"娱乐",id:"yule"}, ], newslist:[ { list:[ { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:true, title:"我是标题", type:"video", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", playnum:"20w", long:"2:47", infonum:{ index:1,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, } ] }, { list:[ { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:true, title:"我是标题", type:"video", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", playnum:"20w", long:"2:47", infonum:{ index:1,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:true, title:"我是标题", type:"video", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", playnum:"20w", long:"2:47", infonum:{ index:1,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, } ] }, { list:[ { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:true, title:"我是标题", type:"video", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", playnum:"20w", long:"2:47", infonum:{ index:1,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }, { userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:true, title:"我是标题", type:"video", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", playnum:"20w", long:"2:47", infonum:{ index:1,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, } ] }, { list:[] }, { list:[] }, { list:[] } ], } }

【uni-app开发(三)(项目实战之仿糗百首页)】封装横向滚动组件:props初始化数据? + 赋值组件间的通信
swiper-tab-head.vue
.uni-swiper-tab{ border-bottom: 1upx solid #EEEEEE; } .swiper-tab-list{ color: #969696; font-weight: bold; } .uni-tab-bar .active{ color: #343434; } .active .swiper-tab-line{ border-bottom: 6upx solid #FEDE33; width: 70upx; margin: auto; border-top: 6upx solid #FEDE33; border-radius:20upx; }

index.vue

四、上拉加载组件开发及封装 布局显示及触底事件scrolltolower
+ +{{items.loadtext}}

并在每个list上添加loadtext
newslist:[ { +loadtext:"上拉加载更多", list:[ {......}, {.....} ] }, { +loadtext:"上拉加载更多", list:[ {......}, {......}, {......}, {......} ] }, { +loadtext:"上拉加载更多", list:[ {......}, {......}, {......}, {......} ] }, { +loadtext:"上拉加载更多", list:[] }, { +loadtext:"上拉加载更多", list:[] }, { +loadtext:"上拉加载更多", list:[] } ],

loadmore四种状态处理
// 上拉加载 loadmore(index){ if(this.newslist[index].loadtext!="上拉加载更多"){ return; } // 修改状态 this.newslist[index].loadtext="加载中..."; // 获取数据 setTimeout(()=> { //获取完成 let obj={ userpic:"../../static/demo/userpic/12.jpg", username:"昵称", isguanzhu:false, title:"我是标题", type:"img", // img:图文,video:视频 titlepic:"../../static/demo/datapic/11.jpg", infonum:{ index:0,//0:没有操作,1:顶,2:踩; dingnum:11, cainum:11, }, commentnum:10, sharenum:10, }; this.newslist[index].list.push(obj); this.newslist[index].loadtext="上拉加载更多"; }, 1000); // this.newslist[index].loadtext="没有更多数据了"; }

下拉反弹样式的隐藏
pages.json的app-plus中添加“bounce”: “none”
封装上拉加载更多效果
第一步:创建新的load-more.vue文件
.load-more{ text-align: center; color: #AAAAAA; padding: 15upx; }

第二步:调用load-more.vue文件

五、优化图文列表组件 5.1 列表中添加动画显示效果(index-list.vue)
animated + 动画效果如rotateIn/fadeInLeft + 速度如fast

5.2 关注事件:当关注成功弹toast提示,并隐藏关注按钮
+ 关注

methods:{ // 关注 guanzhu(){ this.item.isguanzhu=true; uni.showToast({ title: '关注成功', }); }, },

5.3 顶踩逻辑处理
+ {{item.infonum.dingnum}} + {{item.infonum.cainum}}

// 顶踩 caozuo(type){ switch (type){ case "ding": if(this.item.infonum.index==1){ return; } this.item.infonum.dingnum++; if(this.item.infonum.index==2){ this.item.infonum.cainum--; } this.item.infonum.index=1; break; case "cai": if(this.item.infonum.index==2){ return; } this.item.infonum.cainum++; if(this.item.infonum.index==1){ this.item.infonum.dingnum--; } this.item.infonum.index=2; break; } },

5.4 点击跳转到详情界面处理
+ {{item.title}}+

// 进入详情页 opendetail(){ console.log("进入详情页") }

5.5 无数据默认组件及封装
第一步:无数据的页面布局文件nothing.vue
.nothing{ background: #FFFFFF; position: absolute; top: 0; left: 0; right: 0; bottom: 0; } .nothing image{ width: 50%; }

第二步:index.vue中通过条件渲染处理显示无数据界面
+ +

引入控件并注册
import indexList from "../../components/index/index-list.vue"; import swiperTabHead from "../../components/index/swiper-tab-head.vue"; import loadMore from "../../components/common/load-more.vue"; + import noThing from "../../components/common/no-thing.vue"; export default { components:{ indexList, swiperTabHead, loadMore, +noThing }, }

    推荐阅读