NavBar.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <template>
  2. <view
  3. :class="['nana-nav-title', innerClass]"
  4. :style="{
  5. backgroundColor: theme.resolveThemeColor(backgroundColor),
  6. height: theme.resolveThemeSize(height),
  7. paddingLeft: align === 'left' ? theme.resolveThemeSize('NavBarLeftModePaddingLeft', 20) : undefined,
  8. ...props.innerStyle,
  9. }"
  10. >
  11. <view
  12. v-if="align !== 'left'"
  13. class="nana-nav-button-wrapper" :style="{
  14. marginRight: rightPillSpace ? `${menuButtonInfo.width}px` : undefined,
  15. }"
  16. >
  17. <slot v-if="showLeftButton && leftButton" name="left">
  18. <IconButton
  19. :icon="getButton(leftButton)"
  20. v-bind="iconProps"
  21. shape="square-full"
  22. @click="handleButtonNavBack(leftButton, () => emit('leftButtonPressed'))"
  23. />
  24. </slot>
  25. <IconButton v-else icon="space" shape="square-full" />
  26. </view>
  27. <slot name="center">
  28. <HorizontalScrollText
  29. v-if="titleScroll"
  30. :outerStyle="{
  31. ...titleTextStyle,
  32. ...props.titleStyle,
  33. paddingLeft: 0,
  34. paddingRight: 0,
  35. }"
  36. :innerClass="titleClass"
  37. :color="textColor"
  38. :textAlign="align"
  39. :text="title"
  40. />
  41. <Text
  42. v-else
  43. :outerStyle="{
  44. ...titleTextStyle,
  45. ...props.titleStyle,
  46. flex: 1,
  47. }"
  48. :innerClass="titleClass"
  49. :textAlign="align"
  50. :color="textColor"
  51. :text="title"
  52. />
  53. </slot>
  54. <view
  55. class="nana-nav-button-wrapper-end"
  56. :style="{
  57. marginRight: rightPillSpace ? `${menuButtonInfo.width ?? 0}px` : undefined,
  58. minWidth: theme.resolveThemeSize(height),
  59. }"
  60. >
  61. <slot v-if="showRightButton && rightButton" name="right">
  62. <IconButton
  63. :icon="getButton(rightButton)"
  64. v-bind="iconProps"
  65. shape="square-full"
  66. @click="handleButtonNavBack(rightButton, () => emit('rightButtonPressed'))"
  67. />
  68. </slot>
  69. <IconButton v-else icon="space" shape="square-full" />
  70. </view>
  71. </view>
  72. </template>
  73. <script setup lang="ts">
  74. import type { IconProps } from '../basic/Icon.vue';
  75. import { propGetThemeVar, useTheme } from '../theme/ThemeDefine';
  76. import { DynamicSize } from '../theme/ThemeTools';
  77. import HorizontalScrollText from '../typography/HorizontalScrollText.vue';
  78. import Text from '../basic/Text.vue';
  79. import IconButton from '../basic/IconButton.vue';
  80. export type NavBarButtonTypes = 'back'|'menu'|'search'|'setting'|'custom';
  81. export interface NavBarProps {
  82. /**
  83. * 标题栏高度
  84. * @default '44px'
  85. */
  86. height?: number|string;
  87. /**
  88. * 左侧按钮
  89. */
  90. leftButton?: NavBarButtonTypes,
  91. /**
  92. * 标题文字,支持自定义元素
  93. */
  94. title?: string,
  95. /**
  96. * 标题对齐
  97. * @default 'center'
  98. */
  99. align?: 'center'|'left',
  100. /**
  101. * 右侧按钮
  102. */
  103. rightButton?: NavBarButtonTypes,
  104. /**
  105. * 是否显示右侧按钮
  106. * @default true
  107. */
  108. showRightButton?: boolean;
  109. /**
  110. * 是否显示右侧按钮的胶囊间距
  111. */
  112. rightPillSpace?: boolean,
  113. /**
  114. * 手动指定右侧按钮的胶囊间距(像素)
  115. * @default 50
  116. */
  117. rightPillSpaceForce?: number,
  118. /**
  119. * 是否显示左侧按钮
  120. * @default true
  121. */
  122. showLeftButton?: boolean;
  123. /**
  124. * 自定义背景颜色
  125. */
  126. backgroundColor?: string;
  127. /**
  128. * 自定义文字颜色
  129. * @default Color.black
  130. */
  131. textColor?: string;
  132. /**
  133. * 自定义样式
  134. */
  135. innerStyle?: object;
  136. /**
  137. * 自定义标题文字样式
  138. */
  139. titleStyle?: object;
  140. /**
  141. * 自定义标题文字样式
  142. */
  143. titleClass?: any;
  144. /**
  145. * 标题文字超出时,是否自动滚动
  146. * @default true
  147. */
  148. titleScroll?: boolean;
  149. /**
  150. * 图标透传样式
  151. */
  152. iconProps?: IconProps;
  153. /**
  154. * 自定义类名
  155. */
  156. innerClass?: any;
  157. }
  158. function getButton(type: NavBarButtonTypes) {
  159. let button = '';
  160. switch (type) {
  161. case 'back': button = 'arrow-left-bold'; break;
  162. case 'menu': button = 'elipsis'; break;
  163. case 'search': button = 'search'; break;
  164. case 'setting': button = 'setting'; break;
  165. }
  166. return button;
  167. }
  168. const emit = defineEmits([ 'leftButtonPressed', 'rightButtonPressed' ]);
  169. const theme = useTheme();
  170. const props = withDefaults(defineProps<NavBarProps>(), {
  171. titleScroll: true,
  172. align: () => propGetThemeVar('NavBarAlign', 'center'),
  173. height: () => propGetThemeVar('NavBarHeight', '44px'),
  174. showLeftButton: true,
  175. showRightButton: true,
  176. rightPillSpaceForce: 50,
  177. textColor: () => propGetThemeVar('NavBarTitleColor', 'black'),
  178. });
  179. // #ifdef MP
  180. const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
  181. // #endif
  182. // #ifndef MP
  183. const menuButtonInfo = {
  184. width: props.rightPillSpaceForce,
  185. };
  186. // #endif
  187. const titleTextStyle = theme.useThemeStyle({
  188. flex: 1,
  189. fontSize: DynamicSize('NavBarTitleFontSize', 30),
  190. paddingLeft: DynamicSize('NavBarTitlePaddingHorizontal', 15),
  191. paddingRight: DynamicSize('NavBarTitlePaddingHorizontal', 15),
  192. });
  193. function handleButtonNavBack(button: NavBarButtonTypes, callback: () => void) {
  194. if (button === 'back') {
  195. uni.navigateBack();
  196. } else {
  197. callback();
  198. }
  199. }
  200. </script>
  201. <style>
  202. .nana-nav-title {
  203. position: relative;
  204. width: 100%;
  205. display: flex;
  206. flex-direction: row;
  207. justify-content: space-between;
  208. align-items: center;
  209. }
  210. .nana-nav-button-wrapper {
  211. position: relative;
  212. flex-direction: row;
  213. justify-content: flex-start;
  214. height: 100%;
  215. flex-shrink: 0;
  216. }
  217. .nana-nav-button-wrapper-end {
  218. position: relative;
  219. flex-direction: row;
  220. justify-content: flex-end;
  221. height: 100%;
  222. flex-shrink: 0;
  223. }
  224. </style>