欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BTabar组件封装(三)

程序员文章站 2022-03-19 21:37:05
...

上篇提供了两种思想:

  1. BTabBar页面,如何在同一个页面中显示加载不同的Item项对应的页面问题。
  2. BTabBar页面中,如何加载配置数据(也就是写在store/index.js文件中的数据)。

有了这两个知识储备,再结合第一篇中的组件封装,效果应该就出来了。


遇到的问题参考:uni-app 父子组件传值,子组件修改值后页面不起作用问题

  1. 在组件中定义的数据,我这里需要在组件页面中根据展示的交互效果来修改数据的值。本以为数据是双向绑定就直接修改就可以了,然而在自定义组件页中,我修改了,但是在展示(或者说调用组件的页面)的页面中,是没有渲染出我想要的修改后的效果的。也就是所谓的子组件修改值后页面不起作用。    

解决:

使用 this.$set(object, propertyName, value) 方法修改相应的属性

object:修改的目标

propertyName:修改的属性

value:值

 

如果是集合(数组):

object:修改的集合(数组)

propertyName:集合(数组)下标

value:已修改的元素

最后的效果:

BTabar组件封装(三)

接着就详细看看是如何实现的:

1. 目录结构:

BTabar组件封装(三)

首先还是定义我们需要的全局配置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来控制是否显示,故而这里就会看到下面的目录结构:

BTabar组件封装(三)

也就是我的应用展现的只有三个页面,也即是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
		},

上面只给出了涉及的部分,其余没有涉及修改的和前面一样,就没有保证格式。

效果:

BTabar组件封装(三)

就先到这里,对于区分用户,然后显示不同的TabBar,其实应该也还是很简单,就是动态设置一下判断就可以了。

有空了再实现这个功能。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关标签: uniapp