uni-events-helper-wx
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.

260 lines
7.3 KiB

3 years ago
  1. <template>
  2. <view
  3. class="u-subsection"
  4. ref="u-subsection"
  5. :class="[`u-subsection--${mode}`]"
  6. :style="[$u.addStyle(customStyle), wrapperStyle]"
  7. >
  8. <view
  9. class="u-subsection__bar"
  10. ref="u-subsection__bar"
  11. :style="[barStyle]"
  12. :class="[mode === 'button' && 'u-subsection--button__bar', current === 0 && mode === 'subsection' && 'u-subsection__bar--first', current >0 && current < this.list.length - 1 && mode === 'subsection' && 'u-subsection__bar--center', current === this.list.length - 1 && mode === 'subsection' && 'u-subsection__bar--last']"
  13. ></view>
  14. <view
  15. class="u-subsection__item"
  16. :class="[`u-subsection__item--${index}`, index < list.length - 1 && 'u-subsection__item--no-border-right', index === 0 && 'u-subsection__item--first', index === list.length - 1 && 'u-subsection__item--last']"
  17. :ref="`u-subsection__item--${index}`"
  18. :style="[itemStyle(index)]"
  19. @tap="clickHandler(index)"
  20. v-for="(item, index) in list"
  21. :key="index"
  22. >
  23. <text
  24. class="u-subsection__item__text"
  25. :style="[textStyle(index)]"
  26. >{{ item }}</text>
  27. </view>
  28. </view>
  29. </template>
  30. <script>
  31. // #ifdef APP-NVUE
  32. const dom = uni.requireNativePlugin('dom')
  33. const animation = uni.requireNativePlugin('animation')
  34. // #endif
  35. import props from './props.js';
  36. /**
  37. * Subsection 分段器
  38. * @description 该分段器一般用于用户从几个选项中选择某一个的场景
  39. * @tutorial https://www.uviewui.com/components/subsection.html
  40. * @property {Array} list tab的数据
  41. * @property {String Number} current 当前活动的tab的index默认 0
  42. * @property {String} activeColor 激活时的颜色默认 '#3c9cff'
  43. * @property {String} inactiveColor 未激活时的颜色默认 '#303133'
  44. * @property {String} mode 模式选择mode=button为按钮形式mode=subsection时为分段模式默认 'button'
  45. * @property {String Number} fontSize 字体大小单位px默认 12
  46. * @property {Boolean} bold 激活选项的字体是否加粗默认 true
  47. * @property {String} bgColor 组件背景颜色mode为button时有效默认 '#eeeeef'
  48. * @property {Object} customStyle 定义需要用到的外部样式
  49. *
  50. * @event {Function} change 分段器选项发生改变时触发 回调 index选项的index索引值从0开始
  51. * @example <u-subsection :list="list" :current="curNow" @change="sectionChange"></u-subsection>
  52. */
  53. export default {
  54. name: 'u-subsection',
  55. mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
  56. data() {
  57. return {
  58. // 组件尺寸
  59. itemRect: {
  60. width: 0,
  61. height: 0
  62. }
  63. }
  64. },
  65. watch: {
  66. list(newValue, oldValue) {
  67. this.init()
  68. },
  69. current: {
  70. immediate: true,
  71. handler(n) {
  72. // #ifdef APP-NVUE
  73. // 在安卓nvue上,如果通过translateX进行位移,到最后一个时,会导致右侧无法绘制圆角
  74. // 故用animation模块进行位移
  75. const ref = this.$refs?.['u-subsection__bar']?.ref
  76. // 不存在ref的时候(理解为第一次初始化时,需要渲染dom,进行一定延时再获取ref),这里的100ms是经过测试得出的结果(某些安卓需要延时久一点),勿随意修改
  77. uni.$u.sleep(ref ? 0 : 100).then(() => {
  78. animation.transition(this.$refs['u-subsection__bar'].ref, {
  79. styles: {
  80. transform: `translateX(${n * this.itemRect.width}px)`,
  81. transformOrigin: 'center center'
  82. },
  83. duration: 300
  84. })
  85. })
  86. // #endif
  87. }
  88. }
  89. },
  90. computed: {
  91. wrapperStyle() {
  92. const style = {}
  93. // button模式时,设置背景色
  94. if(this.mode === 'button') {
  95. style.backgroundColor = style.bgColor
  96. }
  97. return style
  98. },
  99. // 滑块的样式
  100. barStyle() {
  101. const style = {}
  102. style.width = `${this.itemRect.width}px`
  103. style.height = `${this.itemRect.height}px`
  104. // 通过translateX移动滑块,其移动的距离为索引*item的宽度
  105. // #ifndef APP-NVUE
  106. style.transform = `translateX(${this.current * this.itemRect.width}px)`
  107. // #endif
  108. if (this.mode === 'subsection') {
  109. // 在subsection模式下,需要动态设置滑块的圆角,因为移动滑块使用的是translateX,无法通过父元素设置overflow: hidden隐藏滑块的直角
  110. style.backgroundColor = this.activeColor
  111. }
  112. return style
  113. },
  114. // 分段器item的样式
  115. itemStyle(index) {
  116. return index => {
  117. const style = {}
  118. if (this.mode === 'subsection') {
  119. // 设置border的样式
  120. style.borderColor = this.activeColor;
  121. style.borderWidth = '1px';
  122. style.borderStyle = 'solid';
  123. }
  124. return style
  125. }
  126. },
  127. // 分段器文字颜色
  128. textStyle(index) {
  129. return index => {
  130. const style = {}
  131. style.fontWeight = this.bold && this.current === index ? 'bold' : 'normal'
  132. style.fontSize = uni.$u.addUnit(this.fontSize)
  133. // subsection模式下,激活时默认为白色的文字
  134. if (this.mode === 'subsection') {
  135. style.color = this.current === index ? '#fff' : this.activeColor
  136. } else {
  137. // button模式下,激活时文字颜色默认为activeColor
  138. style.color = this.current === index ? this.activeColor : this.inactiveColor
  139. }
  140. return style
  141. }
  142. }
  143. },
  144. mounted() {
  145. this.init()
  146. },
  147. methods: {
  148. init() {
  149. uni.$u.sleep().then(() => this.getRect())
  150. },
  151. // 获取组件的尺寸
  152. getRect() {
  153. // #ifndef APP-NVUE
  154. this.$uGetRect('.u-subsection__item--0').then(size => {
  155. this.itemRect = size
  156. })
  157. // #endif
  158. // #ifdef APP-NVUE
  159. const ref = this.$refs['u-subsection__item--0'][0]
  160. ref && dom.getComponentRect(ref, res => {
  161. this.itemRect = res.size
  162. })
  163. // #endif
  164. },
  165. clickHandler(index) {
  166. this.$emit('change', index)
  167. }
  168. },
  169. }
  170. </script>
  171. <style lang="scss">
  172. @import "../../libs/css/components.scss";
  173. .u-subsection {
  174. @include flex;
  175. position: relative;
  176. overflow: hidden;
  177. &--button {
  178. height: 32px;
  179. background-color: rgb(238, 238, 239);
  180. padding: 3px;
  181. border-radius: 3px;
  182. align-items: stretch;
  183. &__bar {
  184. background-color: #FFFFFF;
  185. border-radius: 3px!important;
  186. }
  187. }
  188. &--subsection {
  189. height: 30px;
  190. }
  191. &__bar {
  192. position: absolute;
  193. /* #ifndef APP-NVUE */
  194. transition-property: transform, color;
  195. transition-duration: 0.3s;
  196. transition-timing-function: ease-in-out;
  197. /* #endif */
  198. &--first {
  199. border-top-left-radius: 3px;
  200. border-bottom-left-radius: 3px;
  201. border-top-right-radius: 0px;
  202. border-bottom-right-radius: 0px;
  203. }
  204. &--center {
  205. border-top-left-radius: 0px;
  206. border-bottom-left-radius: 0px;
  207. border-top-right-radius: 0px;
  208. border-bottom-right-radius: 0px;
  209. }
  210. &--last {
  211. border-top-left-radius: 0px;
  212. border-bottom-left-radius: 0px;
  213. border-top-right-radius: 3px;
  214. border-bottom-right-radius: 3px;
  215. }
  216. }
  217. &__item {
  218. @include flex;
  219. flex: 1;
  220. justify-content: center;
  221. align-items: center;
  222. &--no-border-right {
  223. border-right-width: 0!important;
  224. }
  225. &--first {
  226. border-top-left-radius: 3px;
  227. border-bottom-left-radius: 3px;
  228. }
  229. &--last {
  230. border-top-right-radius: 3px;
  231. border-bottom-right-radius: 3px;
  232. }
  233. &__text {
  234. font-size: 12px;
  235. line-height: 12px;
  236. @include flex;
  237. align-items: center;
  238. transition-property: color;
  239. transition-duration: 0.3s;
  240. }
  241. }
  242. }
  243. </style>