tn-nav-bar.vue 8.3 KB

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