tn-goods-nav.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <template>
  2. <view
  3. class="tn-goods-nav-class tn-goods-nav"
  4. :class="[
  5. backgroundColorClass,
  6. {
  7. 'tn-goods-nav--fixed': fixed,
  8. 'tn-safe-area-inset-bottom': safeAreaInsetBottom,
  9. 'tn-goods-nav--shadow': shadow
  10. }
  11. ]"
  12. :style="[backgroundColorStyle, navStyle]"
  13. >
  14. <view class="options">
  15. <view
  16. v-for="(item, index) in optionsData"
  17. :key="index"
  18. class="options__item"
  19. :class="[{'options__item--avatar': item.showAvatar}]"
  20. @tap="handleOptionClick(index)"
  21. >
  22. <block v-if="item.showAvatar">
  23. <tn-avatar
  24. :src="item.avatar"
  25. size="sm"
  26. :badge="item.showBadge"
  27. :badgeText="item.count"
  28. :badgeBgColor="item.countBackgroundColor"
  29. :badgeColor="item.countFontColor"
  30. :badgeSize="26"
  31. ></tn-avatar>
  32. </block>
  33. <block v-else>
  34. <view class="options__item__icon" :class="[`tn-icon-${item.icon}`]" :style="[optionStyle(index, 'icon')]">
  35. <tn-badge v-if="item.showBadge" :absolute="true" :backgroundColor="item.countBackgroundColor" :fontColor="item.countFontColor" :fontSize="16" padding="2rpx 5rpx">{{ item.count }}</tn-badge>
  36. </view>
  37. <view class="options__item__text" :style="[optionStyle(index, 'text')]">{{ item.text }}</view>
  38. </block>
  39. </view>
  40. </view>
  41. <view class="buttons">
  42. <view
  43. v-for="(item, index) in buttonGroupsData"
  44. :key="index"
  45. class="buttons__item"
  46. :class="[buttonClass(index)]"
  47. :style="[buttonStyle(index)]"
  48. @tap="handleButtonClick(index)"
  49. >
  50. <view class="buttons__item__text">{{ item.text }}</view>
  51. </view>
  52. </view>
  53. </view>
  54. </template>
  55. <script>
  56. export default {
  57. name: 'tn-goods-nav',
  58. props: {
  59. // 选项信息
  60. // 建议不超过3个
  61. // {
  62. // icon: '', // 图标名称
  63. // text: '', // 显示的文本
  64. // count: '', // 角标的值
  65. // countBackgroundColor: '', // 角标背景颜色
  66. // countFontColor: '', // 角标字体颜色
  67. // iconColor: '', // 图标颜色
  68. // textColor: '', // 文本颜色
  69. // avatar: '', // 显示头像(此时将不显示图标和文本)
  70. // }
  71. options: {
  72. type: Array,
  73. default() {
  74. return [{
  75. icon: 'shop',
  76. text: '店铺'
  77. },{
  78. icon: 'service',
  79. text: '客服'
  80. },{
  81. icon: 'star',
  82. text: '收藏'
  83. }]
  84. }
  85. },
  86. // 按钮组
  87. // 建议不超过2个
  88. // {
  89. // text: '', // 显示的文本
  90. // backgroundColor: '', // 按钮背景颜色
  91. // color: '' // 文本颜色
  92. // }
  93. buttonGroups: {
  94. type: Array,
  95. default() {
  96. return [{
  97. text: '加入购物车',
  98. backgroundColor: '#FFA726',
  99. color: '#FFFFFF'
  100. },{
  101. text: '结算',
  102. backgroundColor: '#FF7043',
  103. color: '#FFFFFF'
  104. }]
  105. }
  106. },
  107. // 背景颜色
  108. backgroundColor: {
  109. type: String,
  110. default: ''
  111. },
  112. // 导航的高度,单位rpx
  113. height: {
  114. type: Number,
  115. default: 0
  116. },
  117. // 显示阴影
  118. shadow: {
  119. type: Boolean,
  120. default: false
  121. },
  122. // 导航的层级
  123. zIndex: {
  124. type: Number,
  125. default: 0
  126. },
  127. // 按钮类型
  128. // rect -> 方形 paddingRect -> 上下带边距方形 round -> 圆角
  129. buttonType: {
  130. type: String,
  131. default: 'rect'
  132. },
  133. // 是否固定在底部
  134. fixed: {
  135. type: Boolean,
  136. default: false
  137. },
  138. // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
  139. safeAreaInsetBottom: {
  140. type: Boolean,
  141. default: false
  142. }
  143. },
  144. computed: {
  145. backgroundColorStyle() {
  146. return this.$t.color.getBackgroundColorStyle(this.backgroundColor)
  147. },
  148. backgroundColorClass() {
  149. return this.$t.color.getBackgroundColorInternalClass(this.backgroundColor)
  150. },
  151. navStyle() {
  152. let style = {}
  153. if (this.height) {
  154. style.height = this.height + 'rpx'
  155. }
  156. style.zIndex = this.zIndex ? this.zIndex : this.$t.zIndex.goodsNav
  157. return style
  158. },
  159. // 选项style
  160. optionStyle() {
  161. return (index, type) => {
  162. let style = {}
  163. const item = this.optionsData[index]
  164. if (type === 'icon' && item.iconColor) {
  165. style.color = item.iconColor
  166. } else if (type === 'text' && item.fontColor) {
  167. style.color = item.fontColor
  168. }
  169. return style
  170. }
  171. },
  172. // 按钮class
  173. buttonClass() {
  174. return (index) => {
  175. let clazz = ''
  176. const item = this.buttonGroupsData[index]
  177. if (item.backgroundColorClass) {
  178. clazz += ` ${item.backgroundColorClass}`
  179. }
  180. if (item.colorClass) {
  181. clazz += ` ${item.colorClass}`
  182. }
  183. clazz += ` buttons__item--${this.$t.string.humpConvertChar(this.buttonType, '-')}`
  184. return clazz
  185. }
  186. },
  187. // 按钮style
  188. buttonStyle() {
  189. return (index) => {
  190. let style = {}
  191. const item = this.buttonGroupsData[index]
  192. if (item.backgroundColorStyle) {
  193. style.backgroundColor = item.backgroundColorStyle
  194. }
  195. if (item.colorStyle) {
  196. style.color = item.colorStyle
  197. }
  198. return style
  199. }
  200. }
  201. },
  202. watch: {
  203. options(val) {
  204. this.initData()
  205. },
  206. buttonGroups(val) {
  207. this.initData()
  208. }
  209. },
  210. data() {
  211. return {
  212. // 保存选项数据
  213. optionsData: [],
  214. // 保存按钮组数据
  215. buttonGroupsData: []
  216. }
  217. },
  218. created() {
  219. this.initData()
  220. },
  221. methods: {
  222. // 初始化选项和按钮数据
  223. initData() {
  224. this.handleOptionsData()
  225. this.handleButtonGroupsData()
  226. },
  227. // 选项点击事件
  228. handleOptionClick(index) {
  229. this.$emit('optionClick', {
  230. index: index
  231. })
  232. },
  233. // 按钮点击事件
  234. handleButtonClick(index) {
  235. this.$emit('buttonClick', {
  236. index: index
  237. })
  238. },
  239. // 处理选项组数据
  240. handleOptionsData() {
  241. this.optionsData = this.options.map((item) => {
  242. let option = {...item}
  243. option.showAvatar = item.hasOwnProperty('avatar')
  244. if (item.hasOwnProperty('count')) {
  245. const count = this.$t.number.formatNumberString(item.count, 2)
  246. option.showBadge = true
  247. option.count = typeof count === 'number' ? String(count) : count
  248. option.countBackgroundColor = item.countBackgroundColor ? item.countBackgroundColor : '#01BEFF'
  249. option.countFontColor = item.countFontColor ? item.countFontColor : '#FFFFFF'
  250. }
  251. return option
  252. })
  253. },
  254. // 处理按钮组数据
  255. handleButtonGroupsData() {
  256. this.buttonGroupsData = this.buttonGroups.map((item) => {
  257. let button = {...item}
  258. if (item.hasOwnProperty('backgroundColor')) {
  259. button.backgroundColorClass = this.$t.color.getBackgroundColorInternalClass(item.backgroundColor)
  260. button.backgroundColorStyle = this.$t.color.getBackgroundColorStyle(item.backgroundColor)
  261. }
  262. if (item.hasOwnProperty('color')) {
  263. button.colorClass = this.$t.color.getBackgroundColorInternalClass(item.color)
  264. button.colorStyle = this.$t.color.getBackgroundColorStyle(item.color)
  265. }
  266. return button
  267. })
  268. }
  269. }
  270. }
  271. </script>
  272. <style lang="scss" scoped>
  273. .tn-goods-nav {
  274. background-color: #FFFFFF;
  275. display: flex;
  276. flex-direction: row;
  277. align-items: center;
  278. height: 88rpx;
  279. width: 100%;
  280. box-sizing: content-box;
  281. &--shadow {
  282. box-shadow: 0rpx -10rpx 30rpx 0rpx rgba(0, 0, 0, 0.05);
  283. &::before {
  284. content: " ";
  285. position: absolute;
  286. width: 100%;
  287. height: 100%;
  288. bottom: 0;
  289. left: 0;
  290. right: 0;
  291. margin: auto;
  292. background-color: transparent;
  293. z-index: -1;
  294. }
  295. }
  296. &--fixed {
  297. position: fixed;
  298. bottom: 0;
  299. left: 0;
  300. right: 0;
  301. }
  302. .options {
  303. display: flex;
  304. flex-direction: row;
  305. align-items: center;
  306. height: 100%;
  307. color: #AAAAAA;
  308. &__item {
  309. padding: 0 26rpx;
  310. &--avatar {
  311. padding: 0rpx 0rpx 0rpx 26rpx !important;
  312. }
  313. &__icon {
  314. position: relative;
  315. font-size: 36rpx;
  316. margin-bottom: 6rpx;
  317. }
  318. &__text {
  319. font-size: 22rpx;
  320. }
  321. }
  322. }
  323. .buttons {
  324. flex: 1;
  325. display: flex;
  326. flex-direction: row;
  327. align-items: center;
  328. height: 100%;
  329. &__item {
  330. flex: 1;
  331. padding: 0 10rpx;
  332. display: flex;
  333. align-items: center;
  334. justify-content: center;
  335. &--rect {
  336. height: 100%;
  337. }
  338. &--padding-rect {
  339. height: 80%;
  340. }
  341. &--round {
  342. height: 75%;
  343. &:first-child {
  344. border-top-left-radius: 100rpx;
  345. border-bottom-left-radius: 100rpx;
  346. }
  347. &:last-child {
  348. border-top-right-radius: 100rpx;
  349. border-bottom-right-radius: 100rpx;
  350. }
  351. }
  352. &__text {
  353. display: inline-block;
  354. font-weight: bold;
  355. font-size: 30rpx;
  356. white-space: nowrap;
  357. text-overflow: ellipsis;
  358. overflow: hidden;
  359. }
  360. }
  361. }
  362. }
  363. </style>