Tab.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <template>
  2. <view class="component-view-tab">
  3. <scroll-nav :fixed="tabFix" ref="scrollNav" :background="background" :scrollData="scrollData" :activeKey.sync="activeKey" @change="changeTab" @tabClick="tabClick" :animate="animate" :height="height" :line="line" :font="font"></scroll-nav>
  4. <view class="scroll-view">
  5. <view :class="['tab-wrapper',{animate}]" :style="[{left:tabview.left+'px'}]">
  6. <view class="tab" v-for="(item,key) in scrollData" :key="key">
  7. <view v-if="forceRender ? true :activeKey === key" class="tab-content" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd($event,key)">
  8. <slot name="{{item.slot}}"/>
  9. </view>
  10. </view>
  11. </view>
  12. </view>
  13. </view>
  14. </template>
  15. <script>
  16. import scrollNav from './ScrollNav';
  17. export default {
  18. components: {scrollNav},
  19. props: {
  20. navData:{
  21. default:()=>[],
  22. type:Array
  23. },
  24. value:{
  25. default:0,
  26. type:Number
  27. },
  28. animate:{
  29. default:true,
  30. type:Boolean
  31. },
  32. scrollThreshold:{
  33. default:50,
  34. type:Number
  35. },
  36. height:{
  37. default:'70rpx',
  38. type:String
  39. },
  40. line:Object,
  41. font:Object,
  42. background:String,
  43. forceRender:{
  44. default:false,
  45. type:Boolean
  46. },
  47. tabFix:Object
  48. },
  49. // mixins: [require('@/components/mixins/Component').default],
  50. data() {
  51. return {
  52. tabview:{
  53. left:0
  54. },
  55. scrollData:[],
  56. activeKey:0
  57. };
  58. },
  59. emits:['change','update:value','tabClick','rendered'],
  60. created() {
  61. },
  62. mounted() {},
  63. unmounted() {},
  64. computed: {
  65. },
  66. watch: {
  67. navData:{
  68. handler(n){
  69. this.scrollData = n;
  70. this.$nextTick(this.resizeStyle);
  71. },
  72. deep:true,
  73. immediate:true
  74. },
  75. value:{
  76. handler(n){
  77. this.setPosition(n,'set');
  78. },
  79. deep:true,
  80. immediate:true
  81. }
  82. },
  83. methods: {
  84. async init(){
  85. },
  86. load(){},
  87. $document(self, select, cfg) {
  88. return new Promise((res, rej) => {
  89. if (!self || !select) rej('$document:params error');
  90. uni.createSelectorQuery().in(self).select(select).fields(Object.assign({
  91. id: true,
  92. dataset: true,
  93. rect:true,
  94. size: true,
  95. scrollOffset: true,
  96. context:true
  97. },cfg),data=>res(data)).exec();
  98. })
  99. },
  100. async changeTab(e){
  101. await this.setPosition(e);
  102. },
  103. tabClick(e){
  104. this.$emit('tabClick',this.handleItem(e));
  105. },
  106. handleItem(e){
  107. return {
  108. index:e,
  109. item:this.scrollData[e]
  110. }
  111. },
  112. touchStart(e){
  113. if(this.locked) return;
  114. var client = e.touches[0];
  115. Object.assign(this,{
  116. client,
  117. position:[client]
  118. });
  119. },
  120. touchMove(e){
  121. if(!this.position) return;
  122. var client = e.touches[0];
  123. if(Math.abs(client.clientY - this.client.clientY) < 25){
  124. this.position.push(client);
  125. const
  126. positions = this.position.map(item=>Object.assign({},item)),
  127. sorted = positions.sort((a,b)=>a.clientX - b.clientX);
  128. if(sorted[0].clientX !== client.clientX && sorted.slice(-1)[0].clientX !== client.clientX){
  129. this.clearPosition();
  130. }
  131. }else{
  132. this.clearPosition();
  133. }
  134. },
  135. async touchEnd(e,key){
  136. var
  137. positions = this.position,
  138. act;
  139. if(positions){
  140. var move = positions.slice(-1)[0].clientX - positions[0].clientX;
  141. if(Math.abs(move) < this.scrollThreshold) return;
  142. act = move > 0 ? 1 : -1;
  143. var changeKey = key - act;
  144. if(changeKey < 0 || changeKey > this.navData.length - 1) return;
  145. await this.setPosition(changeKey);
  146. this.clearPosition();
  147. }
  148. },
  149. async setPosition(key,act){
  150. this.locked = true;
  151. switch(act){
  152. case 'set':
  153. break;
  154. default:
  155. this.$emit('update:value',key);
  156. this.$emit('change',this.handleItem(key));
  157. }
  158. this.activeKey = key;
  159. try{
  160. !this.itemWidth && (this.itemWidth = (await this.$document(this,'.tab')).width);
  161. this.tabview.left = -key * this.itemWidth;
  162. }catch(e){}
  163. await new Promise(r=>setTimeout(r,300));
  164. this.locked && (this.locked = false);
  165. },
  166. clearPosition(){
  167. this.position = null;
  168. },
  169. resizeStyle(){
  170. this.$refs.scrollNav.changeStyle(this.activeKey);
  171. }
  172. },
  173. directives: {},
  174. errorCaptured() {},
  175. renderTracked() {},
  176. renderTriggered() {},
  177. };
  178. </script>
  179. <style lang='stylus'>
  180. .component-view-tab
  181. width 100%;overflow auto;
  182. .scroll-view
  183. overflow hidden;width 100%;max-width 100%;
  184. .tab-wrapper
  185. width 100%;display flex;felx-wrap:nowrap;position relative;
  186. &.animate
  187. transition all .3s;
  188. .tab
  189. min-width 100%;max-width 100%;
  190. .tab-content
  191. overflow hidden;width 100%;height 100%;
  192. </style>