更新大理市民卡app
diff --git a/uview-ui/components/u-tabbar/u-tabbar.vue b/uview-ui/components/u-tabbar/u-tabbar.vue
new file mode 100644
index 0000000..448206c
--- /dev/null
+++ b/uview-ui/components/u-tabbar/u-tabbar.vue
@@ -0,0 +1,328 @@
+<template>
+ <view v-if="show" class="u-tabbar" @touchmove.stop.prevent="() => {}">
+ <view class="u-tabbar__content safe-area-inset-bottom" :style="{
+ height: $u.addUnit(height),
+ backgroundColor: bgColor,
+ }" :class="{
+ 'u-border-top': borderTop
+ }">
+ <view class="u-tabbar__content__item" v-for="(item, index) in list" :key="index" :class="{
+ 'u-tabbar__content__circle': midButton &&item.midButton
+ }" @tap.stop="clickHandler(index)" :style="{
+ backgroundColor: bgColor
+ }">
+ <view :class="[
+ midButton && item.midButton ? 'u-tabbar__content__circle__button' : 'u-tabbar__content__item__button'
+ ]">
+ <u-icon
+ :size="midButton && item.midButton ? midButtonSize : iconSize"
+ :name="elIconPath(index)"
+ img-mode="scaleToFill"
+ :color="elColor(index)"
+ :custom-prefix="item.customIcon ? 'custom-icon' : 'uicon'"
+ ></u-icon>
+ <u-badge :count="item.count" :is-dot="item.isDot"
+ v-if="item.count"
+ :offset="[-2, getOffsetRight(item.count, item.isDot)]"
+ ></u-badge>
+ </view>
+ <view class="u-tabbar__content__item__text" :style="{
+ color: elColor(index)
+ }">
+ <text class="u-line-1">{{item.text}}</text>
+ </view>
+ </view>
+ <view v-if="midButton" class="u-tabbar__content__circle__border" :class="{
+ 'u-border': borderTop,
+ }" :style="{
+ backgroundColor: bgColor,
+ left: midButtonLeft
+ }">
+ </view>
+ </view>
+ <!-- 这里加上一个48rpx的高度,是为了增高有凸起按钮时的防塌陷高度(也即按钮凸出来部分的高度) -->
+ <view class="u-fixed-placeholder safe-area-inset-bottom" :style="{
+ height: `calc(${$u.addUnit(height)} + ${midButton ? 48 : 0}rpx)`,
+ }"></view>
+ </view>
+</template>
+
+<script>
+ export default {
+ props: {
+ // 显示与否
+ show: {
+ type: Boolean,
+ default: true
+ },
+ // 通过v-model绑定current值
+ value: {
+ type: [String, Number],
+ default: 0
+ },
+ // 整个tabbar的背景颜色
+ bgColor: {
+ type: String,
+ default: '#ffffff'
+ },
+ // tabbar的高度,默认50px,单位任意,如果为数值,则为rpx单位
+ height: {
+ type: [String, Number],
+ default: '50px'
+ },
+ // 非凸起图标的大小,单位任意,数值默认rpx
+ iconSize: {
+ type: [String, Number],
+ default: 40
+ },
+ // 凸起的图标的大小,单位任意,数值默认rpx
+ midButtonSize: {
+ type: [String, Number],
+ default: 90
+ },
+ // 激活时的演示,包括字体图标,提示文字等的演示
+ activeColor: {
+ type: String,
+ default: '#303133'
+ },
+ // 未激活时的颜色
+ inactiveColor: {
+ type: String,
+ default: '#606266'
+ },
+ // 是否显示中部的凸起按钮
+ midButton: {
+ type: Boolean,
+ default: false
+ },
+ // 配置参数
+ list: {
+ type: Array,
+ default () {
+ return []
+ }
+ },
+ // 切换前的回调
+ beforeSwitch: {
+ type: Function,
+ default: null
+ },
+ // 是否显示顶部的横线
+ borderTop: {
+ type: Boolean,
+ default: true
+ },
+ // 是否隐藏原生tabbar
+ hideTabBar: {
+ type: Boolean,
+ default: true
+ },
+ },
+ data() {
+ return {
+ // 由于安卓太菜了,通过css居中凸起按钮的外层元素有误差,故通过js计算将其居中
+ midButtonLeft: '50%',
+ pageUrl: '', // 当前页面URL
+ }
+ },
+ created() {
+ // 是否隐藏原生tabbar
+ if(this.hideTabBar) uni.hideTabBar();
+ // 获取引入了u-tabbar页面的路由地址,该地址没有路径前面的"/"
+ let pages = getCurrentPages();
+ // 页面栈中的最后一个即为项为当前页面,route属性为页面路径
+ this.pageUrl = pages[pages.length - 1].route;
+ },
+ computed: {
+ elIconPath() {
+ return (index) => {
+ // 历遍u-tabbar的每一项item时,判断是否传入了pagePath参数,如果传入了
+ // 和data中的pageUrl参数对比,如果相等,即可判断当前的item对应当前的tabbar页面,设置高亮图标
+ // 采用这个方法,可以无需使用v-model绑定的value值
+ let pagePath = this.list[index].pagePath;
+ // 如果定义了pagePath属性,意味着使用系统自带tabbar方案,否则使用一个页面用几个组件模拟tabbar页面的方案
+ // 这两个方案对处理tabbar item的激活与否方式不一样
+ if(pagePath) {
+ if(pagePath == this.pageUrl || pagePath == '/' + this.pageUrl) {
+ return this.list[index].selectedIconPath;
+ } else {
+ return this.list[index].iconPath;
+ }
+ } else {
+ // 普通方案中,索引等于v-model值时,即为激活项
+ return index == this.value ? this.list[index].selectedIconPath : this.list[index].iconPath
+ }
+ }
+ },
+ elColor() {
+ return (index) => {
+ // 判断方法同理于elIconPath
+ let pagePath = this.list[index].pagePath;
+ if(pagePath) {
+ if(pagePath == this.pageUrl || pagePath == '/' + this.pageUrl) return this.activeColor;
+ else return this.inactiveColor;
+ } else {
+ return index == this.value ? this.activeColor : this.inactiveColor;
+ }
+ }
+ }
+ },
+ mounted() {
+ this.midButton && this.getMidButtonLeft();
+ },
+ methods: {
+ async clickHandler(index) {
+ if(this.beforeSwitch && typeof(this.beforeSwitch) === 'function') {
+ // 执行回调,同时传入索引当作参数
+ // 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
+ // 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
+ let beforeSwitch = this.beforeSwitch.bind(this.$u.$parent.call(this))(index);
+ // 判断是否返回了promise
+ if (!!beforeSwitch && typeof beforeSwitch.then === 'function') {
+ await beforeSwitch.then(res => {
+ // promise返回成功,
+ this.switchTab(index);
+ }).catch(err => {
+
+ })
+ } else if(beforeSwitch === true) {
+ // 如果返回true
+ this.switchTab(index);
+ }
+ } else {
+ this.switchTab(index);
+ }
+ },
+ // 切换tab
+ switchTab(index) {
+ // 发出事件和修改v-model绑定的值
+ this.$emit('change', index);
+ // 如果有配置pagePath属性,使用uni.switchTab进行跳转
+ if(this.list[index].pagePath) {
+ uni.switchTab({
+ url: this.list[index].pagePath
+ })
+ } else {
+ // 如果配置了papgePath属性,将不会双向绑定v-model传入的value值
+ // 因为这个模式下,不再需要v-model绑定的value值了,而是通过getCurrentPages()适配
+ this.$emit('input', index);
+ }
+ },
+ // 计算角标的right值
+ getOffsetRight(count, isDot) {
+ // 点类型,count大于9(两位数),分别设置不同的right值,避免位置太挤
+ if(isDot) {
+ return -20;
+ } else if(count > 9) {
+ return -40;
+ } else {
+ return -30;
+ }
+ },
+ // 获取凸起按钮外层元素的left值,让其水平居中
+ getMidButtonLeft() {
+ let windowWidth = this.$u.sys().windowWidth;
+ // 由于安卓中css计算left: 50%的结果不准确,故用js计算
+ this.midButtonLeft = (windowWidth / 2) + 'px';
+ }
+ }
+ }
+</script>
+
+<style scoped lang="scss">
+ @import "../../libs/css/style.components.scss";
+ .u-fixed-placeholder {
+ /* #ifndef APP-NVUE */
+ box-sizing: content-box;
+ /* #endif */
+ }
+
+ .u-tabbar {
+
+ &__content {
+ @include vue-flex;
+ align-items: center;
+ position: relative;
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ z-index: 998;
+ /* #ifndef APP-NVUE */
+ box-sizing: content-box;
+ /* #endif */
+
+ &__circle__border {
+ border-radius: 100%;
+ width: 110rpx;
+ height: 110rpx;
+ top: -48rpx;
+ position: absolute;
+ z-index: 4;
+ background-color: #ffffff;
+ // 由于安卓的无能,导致只有3个tabbar item时,此css计算方式有误差
+ // 故使用js计算的形式来定位,此处不注释,是因为js计算有延后,避免出现位置闪动
+ left: 50%;
+ transform: translateX(-50%);
+
+ &:after {
+ border-radius: 100px;
+ }
+ }
+
+ &__item {
+ flex: 1;
+ justify-content: center;
+ height: 100%;
+ padding: 12rpx 0;
+ @include vue-flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+
+ &__button {
+ position: absolute;
+ top: 14rpx;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ &__text {
+ color: $u-content-color;
+ font-size: 26rpx;
+ line-height: 28rpx;
+ position: absolute;
+ bottom: 14rpx;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ }
+
+ &__circle {
+ position: relative;
+ @include vue-flex;
+ flex-direction: column;
+ justify-content: space-between;
+ z-index: 10;
+ /* #ifndef APP-NVUE */
+ height: calc(100% - 1px);
+ /* #endif */
+
+ &__button {
+ width: 90rpx;
+ height: 90rpx;
+ border-radius: 100%;
+ @include vue-flex;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ background-color: #ffffff;
+ top: -40rpx;
+ left: 50%;
+ z-index: 6;
+ transform: translateX(-50%);
+ }
+ }
+ }
+ }
+</style>