You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

381 lines
8.7 KiB

3 weeks ago
  1. <template>
  2. <view class="uni-navbar" :class="{'uni-dark':dark, 'uni-nvue-fixed': fixed}">
  3. <view class="uni-navbar__content" :class="{ 'uni-navbar--fixed': fixed, 'uni-navbar--shadow': shadow, 'uni-navbar--border': border }" :style="{ 'background-color': themeBgColor }">
  4. <status-bar v-if="statusBar" />
  5. <view :style="{ color: themeColor,backgroundColor: themeBgColor ,height:navbarHeight,width:showMenuButtonWidth?navWidth+'px':'100%'}" class="uni-navbar__header">
  6. <view @tap="onClickLeft" class="uni-navbar__header-btns uni-navbar__header-btns-left" :style="{width:leftIconWidth}">
  7. <slot name="left">
  8. <view class="uni-navbar__content_view" v-if="leftIcon.length > 0">
  9. <uni-icons :color="themeColor" :type="leftIcon" size="20" />
  10. </view>
  11. <view :class="{ 'uni-navbar-btn-icon-left': !leftIcon.length > 0 }" class="uni-navbar-btn-text" v-if="leftText.length">
  12. <text :style="{ color: themeColor, fontSize: '12px' }">{{ leftText }}</text>
  13. </view>
  14. </slot>
  15. </view>
  16. <view class="uni-navbar__header-container " @tap="onClickTitle">
  17. <slot>
  18. <view class="uni-navbar__header-container-inner" v-if="title.length>0">
  19. <text class="uni-nav-bar-text uni-ellipsis-1" :style="{color: themeColor }">{{ title }}</text>
  20. </view>
  21. </slot>
  22. </view>
  23. <view @click="onClickRight" class="uni-navbar__header-btns uni-navbar__header-btns-right" :style="{width:rightIconWidth}">
  24. <slot name="right">
  25. <view v-if="rightIcon.length">
  26. <uni-icons :color="themeColor" :type="rightIcon" size="22" />
  27. </view>
  28. <view class="uni-navbar-btn-text" v-if="rightText.length && !rightIcon.length">
  29. <text class="uni-nav-bar-right-text" :style="{ color: themeColor}">{{ rightText }}</text>
  30. </view>
  31. </slot>
  32. </view>
  33. </view>
  34. </view>
  35. <view class="uni-navbar__placeholder" v-if="fixed">
  36. <status-bar v-if="statusBar" />
  37. <view class="uni-navbar__placeholder-view" :style="{ height:navbarHeight}" />
  38. </view>
  39. </view>
  40. </template>
  41. <script>
  42. import statusBar from "./uni-status-bar.vue";
  43. const getVal = (val) => typeof val === 'number' ? val + 'px' : val;
  44. /**
  45. *
  46. *
  47. * NavBar 自定义导航栏
  48. * @description 导航栏组件主要用于头部导航
  49. * @tutorial https://ext.dcloud.net.cn/plugin?id=52
  50. * @property {Boolean} dark 开启黑暗模式
  51. * @property {String} title 标题文字
  52. * @property {String} leftText 左侧按钮文本
  53. * @property {String} rightText 右侧按钮文本
  54. * @property {String} leftIcon 左侧按钮图标图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性)
  55. * @property {String} rightIcon 右侧按钮图标图标类型参考 [Icon 图标](http://ext.dcloud.net.cn/plugin?id=28) type 属性)
  56. * @property {String} color 图标和文字颜色
  57. * @property {String} backgroundColor 导航栏背景颜色
  58. * @property {Boolean} fixed = [true|false] 是否固定顶部
  59. * @property {Boolean} statusBar = [true|false] 是否包含状态栏
  60. * @property {Boolean} shadow = [true|false] 导航栏下是否有阴影
  61. * @property {Boolean} stat 是否开启统计标题上报
  62. * @event {Function} clickLeft 左侧按钮点击时触发
  63. * @event {Function} clickRight 右侧按钮点击时触发
  64. * @event {Function} clickTitle 中间标题点击时触发
  65. */
  66. export default {
  67. name: "UniNavBar",
  68. components: {
  69. statusBar
  70. },
  71. emits: ['clickLeft', 'clickRight', 'clickTitle'],
  72. props: {
  73. dark: {
  74. type: Boolean,
  75. default: false
  76. },
  77. title: {
  78. type: String,
  79. default: ""
  80. },
  81. leftText: {
  82. type: String,
  83. default: ""
  84. },
  85. rightText: {
  86. type: String,
  87. default: ""
  88. },
  89. leftIcon: {
  90. type: String,
  91. default: ""
  92. },
  93. rightIcon: {
  94. type: String,
  95. default: ""
  96. },
  97. fixed: {
  98. type: [Boolean, String],
  99. default: false
  100. },
  101. color: {
  102. type: String,
  103. default: ""
  104. },
  105. backgroundColor: {
  106. type: String,
  107. default: ""
  108. },
  109. statusBar: {
  110. type: [Boolean, String],
  111. default: false
  112. },
  113. shadow: {
  114. type: [Boolean, String],
  115. default: false
  116. },
  117. border: {
  118. type: [Boolean, String],
  119. default: true
  120. },
  121. height: {
  122. type: [Number, String],
  123. default: 44
  124. },
  125. leftWidth: {
  126. type: [Number, String],
  127. default: 60
  128. },
  129. rightWidth: {
  130. type: [Number, String],
  131. default: 60
  132. },
  133. showMenuButtonWidth: {
  134. type: Boolean,
  135. default: false
  136. },
  137. stat: {
  138. type: [Boolean, String],
  139. default: ''
  140. }
  141. },
  142. data() {
  143. return {
  144. navWidth: 'auto'
  145. }
  146. },
  147. computed: {
  148. themeBgColor() {
  149. if (this.dark) {
  150. // 默认值
  151. if (this.backgroundColor) {
  152. return this.backgroundColor
  153. } else {
  154. return this.dark ? '#333' : '#FFF'
  155. }
  156. }
  157. return this.backgroundColor || '#FFF'
  158. },
  159. themeColor() {
  160. if (this.dark) {
  161. // 默认值
  162. if (this.color) {
  163. return this.color
  164. } else {
  165. return this.dark ? '#fff' : '#333'
  166. }
  167. }
  168. return this.color || '#333'
  169. },
  170. navbarHeight() {
  171. // #ifdef MP-WEIXIN
  172. if (this.fixed && this.height === 0) {
  173. const menuBtnInfo = uni.getMenuButtonBoundingClientRect()
  174. const winInfo = uni.getWindowInfo()
  175. const statusHeight = winInfo.statusBarHeight
  176. const spaceHeight = menuBtnInfo.top - statusHeight
  177. return getVal(menuBtnInfo.height + spaceHeight * 2)
  178. }
  179. // #endif
  180. // #ifndef MP-WEIXIN
  181. if (this.fixed && this.height === 0) {
  182. return getVal(44)
  183. }
  184. // #endif
  185. return getVal(this.height)
  186. },
  187. leftIconWidth() {
  188. return getVal(this.leftWidth)
  189. },
  190. rightIconWidth() {
  191. return getVal(this.rightWidth)
  192. }
  193. },
  194. created() {
  195. // #ifdef MP-WEIXIN
  196. if (this.fixed) {
  197. const menuBtnInfo = uni.getMenuButtonBoundingClientRect()
  198. this.navWidth = menuBtnInfo.left
  199. }
  200. // #endif
  201. },
  202. mounted() {
  203. if (uni.report && this.stat && this.title !== '') {
  204. uni.report('title', this.title)
  205. }
  206. },
  207. methods: {
  208. onClickLeft() {
  209. this.$emit("clickLeft");
  210. },
  211. onClickRight() {
  212. this.$emit("clickRight");
  213. },
  214. onClickTitle() {
  215. this.$emit("clickTitle");
  216. }
  217. }
  218. };
  219. </script>
  220. <style lang="scss" scoped>
  221. $nav-height: 44px;
  222. .uni-nvue-fixed {
  223. /* #ifdef APP-NVUE */
  224. position: sticky;
  225. /* #endif */
  226. }
  227. .uni-navbar {
  228. // box-sizing: border-box;
  229. }
  230. .uni-nav-bar-text {
  231. /* #ifdef APP-PLUS */
  232. font-size: 34rpx;
  233. /* #endif */
  234. /* #ifndef APP-PLUS */
  235. font-size: 14px;
  236. /* #endif */
  237. }
  238. .uni-nav-bar-right-text {
  239. font-size: 12px;
  240. }
  241. .uni-navbar__content {
  242. position: relative;
  243. // background-color: #fff;
  244. // box-sizing: border-box;
  245. background-color: transparent;
  246. }
  247. .uni-navbar__content_view {
  248. // box-sizing: border-box;
  249. }
  250. .uni-navbar-btn-text {
  251. /* #ifndef APP-NVUE */
  252. display: flex;
  253. /* #endif */
  254. flex-direction: column;
  255. justify-content: flex-start;
  256. align-items: center;
  257. line-height: 12px;
  258. }
  259. .uni-navbar__header {
  260. /* #ifndef APP-NVUE */
  261. display: flex;
  262. /* #endif */
  263. padding: 0 10px;
  264. flex-direction: row;
  265. height: $nav-height;
  266. font-size: 12px;
  267. }
  268. .uni-navbar__header-btns {
  269. /* #ifndef APP-NVUE */
  270. overflow: hidden;
  271. display: flex;
  272. /* #endif */
  273. flex-wrap: nowrap;
  274. flex-direction: row;
  275. width: 120rpx;
  276. // padding: 0 6px;
  277. justify-content: center;
  278. align-items: center;
  279. /* #ifdef H5 */
  280. cursor: pointer;
  281. /* #endif */
  282. }
  283. .uni-navbar__header-btns-left {
  284. /* #ifndef APP-NVUE */
  285. display: flex;
  286. /* #endif */
  287. width: 120rpx;
  288. justify-content: flex-start;
  289. align-items: center;
  290. }
  291. .uni-navbar__header-btns-right {
  292. /* #ifndef APP-NVUE */
  293. display: flex;
  294. /* #endif */
  295. flex-direction: row;
  296. // width: 150rpx;
  297. // padding-right: 30rpx;
  298. justify-content: flex-end;
  299. align-items: center;
  300. }
  301. .uni-navbar__header-container {
  302. /* #ifndef APP-NVUE */
  303. display: flex;
  304. /* #endif */
  305. flex: 1;
  306. padding: 0 10px;
  307. overflow: hidden;
  308. }
  309. .uni-navbar__header-container-inner {
  310. /* #ifndef APP-NVUE */
  311. display: flex;
  312. /* #endif */
  313. flex: 1;
  314. flex-direction: row;
  315. align-items: center;
  316. justify-content: center;
  317. font-size: 12px;
  318. overflow: hidden;
  319. // box-sizing: border-box;
  320. }
  321. .uni-navbar__placeholder-view {
  322. height: $nav-height;
  323. }
  324. .uni-navbar--fixed {
  325. position: fixed;
  326. z-index: 998;
  327. /* #ifdef H5 */
  328. left: var(--window-left);
  329. right: var(--window-right);
  330. /* #endif */
  331. /* #ifndef H5 */
  332. left: 0;
  333. right: 0;
  334. /* #endif */
  335. }
  336. .uni-navbar--shadow {
  337. box-shadow: 0 1px 6px #ccc;
  338. }
  339. .uni-navbar--border {
  340. border-bottom-width: 1rpx;
  341. border-bottom-style: solid;
  342. border-bottom-color: #eee;
  343. }
  344. .uni-ellipsis-1 {
  345. overflow: hidden;
  346. /* #ifndef APP-NVUE */
  347. white-space: nowrap;
  348. text-overflow: ellipsis;
  349. /* #endif */
  350. /* #ifdef APP-NVUE */
  351. lines: 1;
  352. text-overflow: ellipsis;
  353. /* #endif */
  354. }
  355. // 暗主题配置
  356. .uni-dark {}
  357. </style>