tn-nav-bar.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <template>
  2. <view
  3. class="tn-custom-nav-bar-class tn-custom-nav-bar"
  4. :style="[navBarStyle]"
  5. >
  6. <view
  7. class="tn-custom-nav-bar__bar"
  8. :class="[barClass]"
  9. :style="[barStyle]"
  10. >
  11. <view v-if="isBack">
  12. <view v-if="customBack">
  13. <view
  14. :style="{
  15. width: customBackStyleInfo.width + 'px',
  16. height: customBackStyleInfo.height + 'px',
  17. marginLeft: customBackStyleInfo.left + 'px'
  18. }"
  19. >
  20. <slot name="back"></slot>
  21. </view>
  22. </view>
  23. <view v-else class="tn-custom-nav-bar__bar__action" @tap="handlerBack">
  24. <text class="tn-custom-nav-bar__bar__action--nav-back" :class="[`tn-icon-${backIcon}`]"></text>
  25. <text class="tn-custom-nav-bar__bar__action--nav-back-text" v-if="backTitle">{{ backTitle }}</text>
  26. </view>
  27. </view>
  28. <view class="tn-custom-nav-bar__bar__content" :style="[contentStyle]">
  29. <slot></slot>
  30. </view>
  31. <view>
  32. <slot name="right"></slot>
  33. </view>
  34. </view>
  35. </view>
  36. </template>
  37. <script>
  38. import componentsColorMixin from '../../libs/mixin/components_color.js'
  39. export default {
  40. name: 'tn-nav-bar',
  41. mixins: [componentsColorMixin],
  42. props: {
  43. // 层级
  44. zIndex: {
  45. type: Number,
  46. default: 0
  47. },
  48. // 导航栏的高度
  49. height: {
  50. type: Number,
  51. default: 0
  52. },
  53. // 高度单位
  54. unit: {
  55. type: String,
  56. default: 'px'
  57. },
  58. // 是否显示返回按钮
  59. isBack: {
  60. type: Boolean,
  61. default: true
  62. },
  63. // 返回按钮的图标
  64. backIcon: {
  65. type: String,
  66. default: 'left'
  67. },
  68. // 返回按钮旁显示的文字
  69. backTitle: {
  70. type: String,
  71. default: '返回'
  72. },
  73. // 透明状态栏
  74. alpha: {
  75. type: Boolean,
  76. default: false
  77. },
  78. // 是否固定在顶部
  79. fixed: {
  80. type: Boolean,
  81. default: true
  82. },
  83. // 是否显示底部阴影
  84. bottomShadow: {
  85. type: Boolean,
  86. default: true
  87. },
  88. // 是否自定义返回按钮
  89. customBack: {
  90. type: Boolean,
  91. default: false
  92. },
  93. // 返回前回调
  94. beforeBack: {
  95. type: Function,
  96. default: null
  97. }
  98. },
  99. computed: {
  100. navBarStyle() {
  101. let style = {}
  102. style.height = this.height === 0 ? this.customBarHeight + this.unit : this.height + this.unit
  103. if (this.fixed) {
  104. style.position = 'fixed'
  105. }
  106. style.zIndex = this.elZIndex
  107. return style
  108. },
  109. barClass() {
  110. let clazz = ''
  111. if (this.backgroundColorClass) {
  112. clazz += ` ${this.backgroundColorClass}`
  113. }
  114. if (this.fontColorClass) {
  115. clazz += `${this.fontColorClass}`
  116. }
  117. if (this.fixed) {
  118. clazz += ' tn-custom-nav-bar__bar--fixed'
  119. }
  120. if (this.alpha) {
  121. clazz += ' tn-custom-nav-bar__bar--alpha'
  122. }
  123. if (this.bottomShadow) {
  124. clazz += ' tn-custom-nav-bar__bar--bottom-shadow'
  125. }
  126. return clazz
  127. },
  128. barStyle() {
  129. let style = {}
  130. style.height = this.height === 0 ? this.customBarHeight + this.unit : this.height + this.unit
  131. if (this.fixed) {
  132. style.paddingTop = this.statusBarHeight + 'px'
  133. }
  134. if(!this.backgroundColorClass) {
  135. style.backgroundColor = this.backgroundColor !== '' ? this.backgroundColor : '#FFFFFF'
  136. }
  137. if (!this.fontColorClass && this.fontColor) {
  138. style.color= this.fontColor
  139. }
  140. style.zIndex = this.elZIndex
  141. return style
  142. },
  143. contentStyle() {
  144. let style = {}
  145. style.top = this.fixed ? this.statusBarHeight + 'px' : '0px'
  146. style.height = this.height === 0 ? (this.customBarHeight - this.statusBarHeight) + this.unit : this.height + this.unit
  147. style.lineHeight = style.height
  148. if (this.isBack) {
  149. if (this.customBack) {
  150. const width = (this.customBackStyleInfo.width + this.customBackStyleInfo.left) * 2
  151. style.width = `calc(100% - ${width}px)`
  152. } else {
  153. style.width = 'calc(100% - 340rpx)'
  154. }
  155. } else {
  156. style.width = '100%'
  157. }
  158. return style
  159. },
  160. elZIndex() {
  161. return this.zIndex ? this.zIndex : this.$t.zIndex.navbar
  162. }
  163. },
  164. data() {
  165. return {
  166. // 状态栏的高度
  167. statusBarHeight: 0,
  168. // 自定义导航栏的高度
  169. customBarHeight: 0,
  170. // 自定义返回按钮时,返回容器的宽高边距信息
  171. customBackStyleInfo: {
  172. width: 86,
  173. height: 32,
  174. left: 15
  175. }
  176. }
  177. },
  178. mounted() {
  179. // 获取vuex中的自定义顶栏的高度
  180. this.updateNavBarInfo()
  181. },
  182. created() {
  183. // 获取胶囊信息
  184. // #ifdef MP-WEIXIN
  185. let custom = wx.getMenuButtonBoundingClientRect()
  186. this.customBackStyleInfo.width = custom.width
  187. this.customBackStyleInfo.height = custom.height
  188. this.customBackStyleInfo.left = uni.upx2px(750) - custom.right
  189. // #endif
  190. },
  191. methods: {
  192. // 更新导航栏的高度
  193. async updateNavBarInfo() {
  194. // 获取vuex中的自定义顶栏的高度
  195. let customBarHeight = this.vuex_custom_bar_height
  196. let statusBarHeight = this.vuex_status_bar_height
  197. // 如果获取失败则重新获取
  198. if (!customBarHeight) {
  199. try {
  200. const navBarInfo = await this.$t.updateCustomBar()
  201. customBarHeight = navBarInfo.customBarHeight
  202. statusBarHeight = navBarInfo.statusBarHeight
  203. } catch(e) {
  204. setTimeout(() => {
  205. this.updateNavBarInfo()
  206. }, 10)
  207. return
  208. }
  209. }
  210. // 更新vuex中的导航栏信息
  211. this && this.$t.vuex('vuex_status_bar_height', statusBarHeight)
  212. this && this.$t.vuex('vuex_custom_bar_height', customBarHeight)
  213. this.customBarHeight = customBarHeight
  214. this.statusBarHeight = statusBarHeight
  215. },
  216. // 处理返回事件
  217. async handlerBack() {
  218. if (this.beforeBack && typeof(this.beforeBack) === 'function') {
  219. // 执行回调,同时传入索引当作参数
  220. // 在微信,支付宝等环境(H5正常),会导致父组件定义的函数体中的this变成子组件的this
  221. // 通过bind()方法,绑定父组件的this,让this的this为父组件的上下文
  222. let beforeBack = this.beforeBack.bind(this.$t.$parent.call(this))()
  223. // 判断是否返回了Promise
  224. if (!!beforeBack && typeof beforeBack.then === 'function') {
  225. await beforeBack.then(res => {
  226. // Promise返回成功
  227. this.navBack()
  228. }).catch(err => {})
  229. } else if (beforeBack === true) {
  230. this.navBack()
  231. }
  232. } else {
  233. this.navBack()
  234. }
  235. },
  236. // 返回上一页
  237. navBack() {
  238. // 通过判断当前页面的页面栈信息,是否有上一页进行返回,如果没有则跳转到首页
  239. const pages = getCurrentPages()
  240. if (pages && pages.length > 0) {
  241. const firstPage = pages[0]
  242. if (pages.length == 1 && (!firstPage.route || firstPage.route != 'pages/index/index')) {
  243. uni.reLaunch({
  244. url: '/pages/index/index'
  245. })
  246. } else {
  247. uni.navigateBack({
  248. delta: 1
  249. })
  250. }
  251. } else {
  252. uni.reLaunch({
  253. url: '/pages/index/index'
  254. })
  255. }
  256. }
  257. }
  258. }
  259. </script>
  260. <style lang="scss" scoped>
  261. .tn-custom-nav-bar {
  262. display: block;
  263. position: relative;
  264. &__bar {
  265. display: flex;
  266. position: relative;
  267. align-items: center;
  268. min-height: 100rpx;
  269. justify-content: space-between;
  270. min-height: 0px;
  271. /* #ifdef MP-WEIXIN */
  272. padding-right: 220rpx;
  273. /* #endif */
  274. /* #ifdef MP-ALIPAY */
  275. padding-right: 150rpx;
  276. /* #endif */
  277. box-shadow: 0rpx 0rpx 0rpx;
  278. z-index: 9999;
  279. &--fixed {
  280. position: fixed;
  281. width: 100%;
  282. top: 0;
  283. }
  284. &--alpha {
  285. background: transparent !important;
  286. box-shadow: none !important;
  287. }
  288. &--bottom-shadow {
  289. box-shadow: 0rpx 0rpx 80rpx 0rpx rgba(0, 0, 0, 0.05);
  290. }
  291. &__action {
  292. display: flex;
  293. align-items: center;
  294. height: 100%;
  295. justify-content: center;
  296. max-width: 100%;
  297. &--nav-back {
  298. /* position: absolute; */
  299. /* top: 50%; */
  300. /* left: 20rpx; */
  301. /* margin-top: -15rpx; */
  302. // width: 25rpx;
  303. // height: 25rpx;
  304. margin-left: 20rpx;
  305. font-size: 38rpx;
  306. line-height: 100%;
  307. // border-width: 0 0 4rpx 4rpx;
  308. // border-color: #000000;
  309. // border-style: solid;
  310. // transform: matrix(0.5, 0.5, -0.5, 0.5, 0, 0);
  311. }
  312. &--nav-back-text {
  313. margin-left: 10rpx;
  314. }
  315. }
  316. &__content {
  317. position: absolute;
  318. text-align: center;
  319. left: 0;
  320. right: 0;
  321. bottom: 0;
  322. margin: auto;
  323. font-size: 32rpx;
  324. cursor: none;
  325. // pointer-events: none;
  326. text-overflow: ellipsis;
  327. white-space: nowrap;
  328. overflow: hidden;
  329. }
  330. }
  331. }
  332. </style>