blob: 450242e992683f50442b79c2ca409c6f3d9463bf [file] [log] [blame]
guangchao.xu070005a2020-12-07 09:56:40 +08001<template>
2 <view class="">
3 <view class="u-navbar" :style="[navbarStyle]" :class="{ 'u-navbar-fixed': isFixed, 'u-border-bottom': borderBottom }">
4 <view class="u-status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
5 <view class="u-navbar-inner" :style="[navbarInnerStyle]">
6 <view class="u-back-wrap" v-if="isBack" @tap="goBack">
7 <view class="u-icon-wrap">
8 <u-icon :name="backIconName" :color="backIconColor" :size="backIconSize"></u-icon>
9 </view>
10 <view class="u-icon-wrap u-back-text u-line-1" v-if="backText" :style="[backTextStyle]">{{ backText }}</view>
11 </view>
12 <view class="u-navbar-content-title" v-if="title" :style="[titleStyle]">
13 <view
14 class="u-title u-line-1"
15 :style="{
16 color: titleColor,
17 fontSize: titleSize + 'rpx',
18 fontWeight: titleBold ? 'bold' : 'normal'
19 }">
20 {{ title }}
21 </view>
22 </view>
23 <view class="u-slot-content">
24 <slot></slot>
25 </view>
26 <view class="u-slot-right">
27 <slot name="right"></slot>
28 </view>
29 </view>
30 </view>
31 <!-- 解决fixed定位后导航栏塌陷的问题 -->
32 <view class="u-navbar-placeholder" v-if="isFixed && !immersive" :style="{ width: '100%', height: Number(navbarHeight) + statusBarHeight + 'px' }"></view>
33 </view>
34</template>
35
36<script>
37 // 获取系统状态栏的高度
38 let systemInfo = uni.getSystemInfoSync();
39 let menuButtonInfo = {};
40 // 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
41 // #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
42 menuButtonInfo = uni.getMenuButtonBoundingClientRect();
43 // #endif
44 /**
45 * navbar 自定义导航栏
46 * @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uniapp自带的导航栏。
47 * @tutorial https://www.uviewui.com/components/navbar.html
48 * @property {String Number} height 导航栏高度(不包括状态栏高度在内,内部自动加上),注意这里的单位是px(默认44)
49 * @property {String} back-icon-color 左边返回图标的颜色(默认#606266)
50 * @property {String} back-icon-name 左边返回图标的名称,只能为uView自带的图标(默认arrow-left)
51 * @property {String Number} back-icon-size 左边返回图标的大小,单位rpx(默认30)
52 * @property {String} back-text 返回图标右边的辅助提示文字
53 * @property {Object} back-text-style 返回图标右边的辅助提示文字的样式,对象形式(默认{ color: '#606266' })
54 * @property {String} title 导航栏标题,如设置为空字符,将会隐藏标题占位区域
55 * @property {String Number} title-width 导航栏标题的最大宽度,内容超出会以省略号隐藏,单位rpx(默认250)
56 * @property {String} title-color 标题的颜色(默认#606266)
57 * @property {String Number} title-size 导航栏标题字体大小,单位rpx(默认32)
58 * @property {Function} custom-back 自定义返回逻辑方法
59 * @property {String Number} z-index 固定在顶部时的z-index值(默认980)
60 * @property {Boolean} is-back 是否显示导航栏左边返回图标和辅助文字(默认true)
61 * @property {Object} background 导航栏背景设置,见官网说明(默认{ background: '#ffffff' })
62 * @property {Boolean} is-fixed 导航栏是否固定在顶部(默认true)
63 * @property {Boolean} immersive 沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效(默认false)
64 * @property {Boolean} border-bottom 导航栏底部是否显示下边框,如定义了较深的背景颜色,可取消此值(默认true)
65 * @example <u-navbar back-text="返回" title="剑未配妥,出门已是江湖"></u-navbar>
66 */
67 export default {
68 name: "u-navbar",
69 props: {
70 // 导航栏高度,单位px,非rpx
71 height: {
72 type: [String, Number],
73 default: ''
74 },
75 // 返回箭头的颜色
76 backIconColor: {
77 type: String,
78 default: '#606266'
79 },
80 // 左边返回的图标
81 backIconName: {
82 type: String,
83 default: 'nav-back'
84 },
85 // 左边返回图标的大小,rpx
86 backIconSize: {
87 type: [String, Number],
88 default: '44'
89 },
90 // 返回的文字提示
91 backText: {
92 type: String,
93 default: ''
94 },
95 // 返回的文字的 样式
96 backTextStyle: {
97 type: Object,
98 default () {
99 return {
100 color: '#606266'
101 }
102 }
103 },
104 // 导航栏标题
105 title: {
106 type: String,
107 default: ''
108 },
109 // 标题的宽度,如果需要自定义右侧内容,且右侧内容很多时,可能需要减少这个宽度,单位rpx
110 titleWidth: {
111 type: [String, Number],
112 default: '250'
113 },
114 // 标题的颜色
115 titleColor: {
116 type: String,
117 default: '#606266'
118 },
119 // 标题字体是否加粗
120 titleBold: {
121 type: Boolean,
122 default: false
123 },
124 // 标题的字体大小
125 titleSize: {
126 type: [String, Number],
127 default: 32
128 },
129 isBack: {
130 type: [Boolean, String],
131 default: true
132 },
133 // 对象形式,因为用户可能定义一个纯色,或者线性渐变的颜色
134 background: {
135 type: Object,
136 default () {
137 return {
138 background: '#ffffff'
139 }
140 }
141 },
142 // 导航栏是否固定在顶部
143 isFixed: {
144 type: Boolean,
145 default: true
146 },
147 // 是否沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效
148 immersive: {
149 type: Boolean,
150 default: false
151 },
152 // 是否显示导航栏的下边框
153 borderBottom: {
154 type: Boolean,
155 default: true
156 },
157 zIndex: {
158 type: [String, Number],
159 default: ''
160 },
161 // 自定义返回逻辑
162 customBack: {
163 type: Function,
164 default: null
165 }
166 },
167 data() {
168 return {
169 menuButtonInfo: menuButtonInfo,
170 statusBarHeight: systemInfo.statusBarHeight
171 };
172 },
173 computed: {
174 // 导航栏内部盒子的样式
175 navbarInnerStyle() {
176 let style = {};
177 // 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
178 style.height = this.navbarHeight + 'px';
179 // // 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
180 // #ifdef MP
181 let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
182 style.marginRight = rightButtonWidth + 'px';
183 // #endif
184 return style;
185 },
186 // 整个导航栏的样式
187 navbarStyle() {
188 let style = {};
189 style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.navbar;
190 // 合并用户传递的背景色对象
191 Object.assign(style, this.background);
192 return style;
193 },
194 // 导航中间的标题的样式
195 titleStyle() {
196 let style = {};
197 // #ifndef MP
198 style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
199 style.right = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
200 // #endif
201 // #ifdef MP
202 // 此处是为了让标题显示区域即使在小程序有右侧胶囊的情况下也能处于屏幕的中间,是通过绝对定位实现的
203 let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
204 style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
205 style.right = rightButtonWidth - (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + rightButtonWidth +
206 'px';
207 // #endif
208 style.width = uni.upx2px(this.titleWidth) + 'px';
209 return style;
210 },
211 // 转换字符数值为真正的数值
212 navbarHeight() {
213 // #ifdef APP-PLUS || H5
214 return this.height ? this.height : 44;
215 // #endif
216 // #ifdef MP
217 // 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
218 // 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
219 // return menuButtonInfo.height + (menuButtonInfo.top - this.statusBarHeight) * 2;//导航高度
220 let height = systemInfo.platform == 'ios' ? 44 : 48;
221 return this.height ? this.height : height;
222 // #endif
223 }
224 },
225 created() {},
226 methods: {
227 goBack() {
228 // 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
229 if (typeof this.customBack === 'function') {
230 // 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
231 // 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
232 this.customBack.bind(this.$u.$parent.call(this))();
233 } else {
234 uni.navigateBack();
235 }
236 }
237 }
238 };
239</script>
240
241<style scoped lang="scss">
242 @import "../../libs/css/style.components.scss";
243
244 .u-navbar {
245 width: 100%;
246 }
247
248 .u-navbar-fixed {
249 position: fixed;
250 left: 0;
251 right: 0;
252 top: 0;
253 z-index: 991;
254 }
255
256 .u-status-bar {
257 width: 100%;
258 }
259
260 .u-navbar-inner {
261 @include vue-flex;
262 justify-content: space-between;
263 position: relative;
264 align-items: center;
265 }
266
267 .u-back-wrap {
268 @include vue-flex;
269 align-items: center;
270 flex: 1;
271 flex-grow: 0;
272 padding: 14rpx 14rpx 14rpx 24rpx;
273 }
274
275 .u-back-text {
276 padding-left: 4rpx;
277 font-size: 30rpx;
278 }
279
280 .u-navbar-content-title {
281 @include vue-flex;
282 align-items: center;
283 justify-content: center;
284 flex: 1;
285 position: absolute;
286 left: 0;
287 right: 0;
288 height: 60rpx;
289 text-align: center;
290 flex-shrink: 0;
291 }
292
293 .u-navbar-centent-slot {
294 flex: 1;
295 }
296
297 .u-title {
298 line-height: 60rpx;
299 font-size: 32rpx;
300 flex: 1;
301 }
302
303 .u-navbar-right {
304 flex: 1;
305 @include vue-flex;
306 align-items: center;
307 justify-content: flex-end;
308 }
309
310 .u-slot-content {
311 flex: 1;
312 @include vue-flex;
313 align-items: center;
314 }
315</style>