BTabar组件封装(三)
上篇提供了两种思想:
- BTabBar页面,如何在同一个页面中显示加载不同的Item项对应的页面问题。
- BTabBar页面中,如何加载配置数据(也就是写在store/index.js文件中的数据)。
有了这两个知识储备,再结合第一篇中的组件封装,效果应该就出来了。
遇到的问题参考:uni-app 父子组件传值,子组件修改值后页面不起作用问题
- 在组件中定义的数据,我这里需要在组件页面中根据展示的交互效果来修改数据的值。本以为数据是双向绑定就直接修改就可以了,然而在自定义组件页中,我修改了,但是在展示(或者说调用组件的页面)的页面中,是没有渲染出我想要的修改后的效果的。也就是所谓的子组件修改值后页面不起作用。
解决:
使用 this.$set(object, propertyName, value) 方法修改相应的属性
object:修改的目标
propertyName:修改的属性
value:值
如果是集合(数组):
object:修改的集合(数组)
propertyName:集合(数组)下标
value:已修改的元素
最后的效果:
接着就详细看看是如何实现的:
1. 目录结构:
首先还是定义我们需要的全局配置store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// 全局变量定义处
state: {
BTabBar: {
backgroundColor: "#f8f8f8",
color: "#8F8F94",
selectedColor: "#f33e54",
list: [{
"iconPath": "/static/logo.png",
"selectedIconPath": "/static/gszx.png",
"text": "首页"
},
{
"iconPath": "/static/logo.png",
"selectedIconPath": "/static/gszx.png",
"text": "中间"
},
{
"iconPath": "/static/logo.png",
"selectedIconPath": "/static/gszx.png",
"text": "关于"
}
],
},
currentIndex: 0
},
// 全局方法定义处
mutations: {
}
});
export default store;
然后,就是在main.js文件中声明引入我们的store/index.js文件到全局中:
import Vue from 'vue'
import App from './App'
// 1. 导入index.js
import store from './store/index.js'
Vue.config.productionTip = false
// 2. 全局挂载
Vue.prototype.$store = store
App.mpType = 'app'
const app = new Vue({
store, // 3.
...App
})
app.$mount()
可以使用定义的“配置文件”,就可以开始自定义组件了,自定义组件我这里使用components/BTabBar/BTabBar.vue中:
<template>
<view class="tabbar" :style="{ background: bg }">
<block v-for="(item, index) in list" :key="index">
<view class="bitem" @touchstart="itemClick" :id="index">
<view class="img">
<image :src="iconPath[index]"></image>
</view>
<view class="text" :style="{ color: textColor[index] }">
{{ item.text }}
</view>
</view>
</block>
</view>
</template>
<script>
export default {
name: 'BTabbar', //组件名
data() {
return {
textColor: [], // 字体也是一个列表,因为有点击事件
iconPath: [] ,//图片地址应该是一个列表
};
},
// 调用vue声明周期函数,来加载初始化数据
created(){
this.init();
this.settingByCurrentIndex();
},
// 定义属性,用于外部传入,这里由于是加载配置文件,故而就不需要传入参数
props: {},
computed: {
// 定义的属性test,如果需要动态修改,就需要设置get和set方法
// 否则报错:Computed property "test" was assigned to but it has no setter.
list: {
get() {
return this.$store.state.BTabBar.list;
}
},
// 文字颜色,由于一直都是统一只显示一个颜色,故而就定义一个变量存储就可以了
color: {
get() {
return this.$store.state.BTabBar.color;
}
},
// 背景颜色
bg: {
get() {
return this.$store.state.BTabBar.backgroundColor;
}
}
},
methods: {
// 初始化
init: function(){
// 1. 初始化,加载默认的字体
// 字体还是加载成一个列表,因为有点击事件
// this.textColor = this.$store.state.BTabBar.color;
let colors = [];
// 2. 初始化,加载默认的图片地址
// 因为图片不止一个,故而我们需要取出来全部
let iconpaths = [];
let list = this.$store.state.BTabBar.list;
let len = list.length;
// 字体初始化
for(let i=0;i<len;i++){
colors.push(this.$store.state.BTabBar.color);
}
this.textColor = colors;
// 图片路径初始化
for(var i = 0 ; i < len; i++){
iconpaths.push(list[i].iconPath);
}
this.iconPath = iconpaths;
},
// 根据currentIndex的值,来设置当前的样式字体,和图片地址
settingByCurrentIndex: function(){
let index = this.$store.state.currentIndex;
// textColor: [], // 字体也是一个列表,因为有点击事件
// iconPath: [] //图片地址应该是一个列表
// 由于textColor和iconPath都是单个列表,可以用下标直接修改
// 修改字体颜色
// this.textColor[index] = this.$store.state.BTabBar.selectedColor; 失败!!
// 父子组件,子组件修改后父组件;没有渲染,要用this.$set(object, propertyName, value) 来修改
// 修改Item字体颜色
this.$set(this.textColor, index, this.$store.state.BTabBar.selectedColor);
// 修改Item图标
this.$set(this.iconPath, index, this.$store.state.BTabBar.list[index].selectedIconPath);
},
itemClick: function(e){
// 点击做三件事:更新字体样式,图片样式,“跳转”页面
let index = e.currentTarget.id;
// 修改currentIndex
this.$store.state.currentIndex = index;
//更改图片地址,字体样式,其实调用settingCurrentIndex就可以了
this.init();
this.settingByCurrentIndex();
// “跳转”页面,不妨传入调用的页面中,让调用者处理
// 传入当前Item对应的下标
this.$emit('itemClick', {index: index}); // 传入当前item下标
}
}
};
</script>
<style>
.tabbar {
width: 100%;
height: 130rpx;
position: fixed;
bottom: 0;
display: flex;
flex-direction: row;
text-align: center;
padding-top: 8rpx;
padding-bottom: 2rpx;
z-index: 999999;
letter-spacing: 0.1rem;
}
.bitem{
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
padding-top: 20rpx;
flex: 1;
}
.bitem image{
width: 50rpx;
height: 50rpx;
}
.bitem view.img{
flex: 11;
}
.bitem view.text{
flex: 1;
font-size: 0.8rem;
}
</style>
最后,就是在我们需要的页面中使用这个自定义组件,这里我是在Index.vue中引用的:
<template>
<view>
<home :style="{ display: [currentIndex == 0 ? 'block' : 'none'] }"></home>
<test :style="{ display: [currentIndex == 1 ? 'block' : 'none'] }"></test>
<about :style="{ display: [currentIndex == 2 ? 'block' : 'none'] }"></about>
<BTabBar @itemClick="barItemClick"></BTabBar>
</view>
</template>
<script>
import BTabBar from "../../components/BTabBar/BTabBar.vue";
// 使用,需要将自己需要绑定的页面声明组件,然后引入页面中
import about from "../../pages/about/about.vue";
import test from "../../pages/test/test.vue";
import home from "../../pages/home/home.vue";
export default {
components: {
BTabBar,
test,
about,
home
},
data() {
return {
currentIndex: 0
}
},
onLoad() {
},
computed: {
// 定义的属性test,如果需要动态修改,就需要设置get和set方法
// 否则报错:Computed property "test" was assigned to but it has no setter.
test:{
get(){
return this.$store.state.currentIndex;
},
set(val){
this.$store.state.currentIndex = 123;
}
}
},
methods: {
change:function(){
this.test = 234;
},
barItemClick: function(e){
this.currentIndex = e.index;
}
}
}
</script>
<style>
</style>
因为我全篇的思想都是将所有需要的页面加载到一个页面中去,然后用currentIndex来控制是否显示,故而这里就会看到下面的目录结构:
也就是我的应用展现的只有三个页面,也即是about、home和test三个页面,而实际上多定义了一个页面,也就是这里的index页面就是容器,该容器用来拼接页面和我们自定义的BTabBar组件。
不管如何,这个组件效果是实现了。
如果,你只需要两个Item,其实修改"配置文件"store/index.js文件中的配置,然后页面引入的时候,只引入需要的就可以实现了,其他的推而及广。这里给出两个的修改部分的代码:
store/index.js
list: [{
"iconPath": "/static/logo.png",
"selectedIconPath": "/static/gszx.png",
"text": "首页"
},
{
"iconPath": "/static/logo.png",
"selectedIconPath": "/static/gszx.png",
"text": "关于"
}],
index/index.vue
<view>
<home :style="{ display: [currentIndex == 0 ? 'block' : 'none'] }"></home>
<about :style="{ display: [currentIndex == 1 ? 'block' : 'none'] }"></about>
<BTabBar @itemClick="barItemClick"></BTabBar>
</view>
</template>
<script>
import BTabBar from "../../components/BTabBar/BTabBar.vue";
// 使用,需要将自己需要绑定的页面声明组件,然后引入页面中
import about from "../../pages/about/about.vue";
import home from "../../pages/home/home.vue";
export default {
components: {
BTabBar,
about,
home
},
上面只给出了涉及的部分,其余没有涉及修改的和前面一样,就没有保证格式。
效果:
就先到这里,对于区分用户,然后显示不同的TabBar,其实应该也还是很简单,就是动态设置一下判断就可以了。
有空了再实现这个功能。