| import { ref, PropType, onMounted, reactive } from 'vue'; | 
|   | 
| export type TabsSize = 'large' | 'normal' | 'small'; | 
|   | 
| export class Title { | 
|   title = ''; | 
|   subTitle = ''; | 
|   //@ts-ignore | 
|   titleSlot?: VNode[]; | 
|   paneKey = ''; | 
|   disabled = false; | 
|   badgeValue = 0; | 
|   constructor() {} | 
| } | 
|   | 
| export const tabsProps = { | 
|   modelValue: { | 
|     type: [String, Number], | 
|     default: 0, | 
|   }, | 
|   color: { | 
|     type: String, | 
|     default: '', | 
|   }, | 
|   direction: { | 
|     type: String as PropType<'horizontal' | 'vertical'>, | 
|     default: 'horizontal', //vertical | 
|   }, | 
|   size: { | 
|     type: String as PropType<TabsSize>, | 
|     default: 'normal', | 
|   }, | 
|   type: { | 
|     type: String, | 
|     default: 'smile', //card、line、smile | 
|   }, | 
|   titleScroll: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   ellipsis: { | 
|     type: Boolean, | 
|     default: true, | 
|   }, | 
|   swipeable: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   autoHeight: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   background: { | 
|     type: String, | 
|     default: '', | 
|   }, | 
|   animatedTime: { | 
|     type: [Number, String], | 
|     default: 0, | 
|   }, | 
|   titleGutter: { | 
|     type: [Number, String], | 
|     default: 0, | 
|   }, | 
|   sticky: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   top: { | 
|     type: Number, | 
|     default: 0, | 
|   }, | 
|   name: { | 
|     type: String, | 
|     default: '', | 
|   }, | 
|   fullHeight: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   showPaneContent: { | 
|     type: Boolean, | 
|     default: true, | 
|   }, | 
|   flexTitle: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   isTransparent: { | 
|     type: Boolean, | 
|     default: false, | 
|   }, | 
|   showSmile: { | 
|     type: Boolean, | 
|     default: true, | 
|   }, | 
| }; | 
|   | 
| export const TypeOfFun = (value: any) => { | 
|   if (null === value) { | 
|     return 'null'; | 
|   } | 
|   | 
|   const type = typeof value; | 
|   if ('undefined' === type || 'string' === type) { | 
|     return type; | 
|   } | 
|   | 
|   const typeString = toString.call(value); | 
|   switch (typeString) { | 
|     case '[object Array]': | 
|       return 'array'; | 
|     case '[object Date]': | 
|       return 'date'; | 
|     case '[object Boolean]': | 
|       return 'boolean'; | 
|     case '[object Number]': | 
|       return 'number'; | 
|     case '[object Function]': | 
|       return 'function'; | 
|     case '[object RegExp]': | 
|       return 'regexp'; | 
|     case '[object Object]': | 
|       if (undefined !== value.nodeType) { | 
|         if (3 === value.nodeType) { | 
|           return /\S/.test(value.nodeValue) ? 'textnode' : 'whitespace'; | 
|         } else { | 
|           return 'element'; | 
|         } | 
|       } else { | 
|         return 'object'; | 
|       } | 
|     default: | 
|       return 'unknow'; | 
|   } | 
| }; | 
|   | 
| export type RectItem = { | 
|   bottom: number; | 
|   dataset: { sid: string }; | 
|   height: number; | 
|   id: string; | 
|   left: number; | 
|   right: number; | 
|   top: number; | 
|   width: number; | 
| }; | 
|   | 
| const _window = window as any; | 
|   | 
| export function requestAniFrame() { | 
|   if (typeof _window !== 'undefined') { | 
|     return ( | 
|       _window.requestAnimationFrame || | 
|       _window.webkitRequestAnimationFrame || | 
|       function (callback: Function) { | 
|         _window.setTimeout(callback, 1000 / 60); | 
|       } | 
|     ); | 
|   } else { | 
|     return function (callback: Function) { | 
|       setTimeout(callback, 1000 / 60); | 
|     }; | 
|   } | 
| } | 
|   | 
| export const raf = requestAniFrame(); | 
|   | 
| export const pxCheck = (value: string | number): string => { | 
|   return isNaN(Number(value)) ? String(value) : `${value}px`; | 
| }; | 
|   | 
| const MIN_DISTANCE = 10; | 
|   | 
| type Direction = '' | 'vertical' | 'horizontal'; | 
|   | 
| function getDirection(x: number, y: number) { | 
|   if (x > y && x > MIN_DISTANCE) { | 
|     return 'horizontal'; | 
|   } | 
|   if (y > x && y > MIN_DISTANCE) { | 
|     return 'vertical'; | 
|   } | 
|   return ''; | 
| } | 
|   | 
| type TouchPosition = 'left' | 'right' | 'top' | 'bottom' | ''; | 
|   | 
| export function useTouch() { | 
|   const startX = ref(0); | 
|   const startY = ref(0); | 
|   const moveX = ref(0); | 
|   const moveY = ref(0); | 
|   const deltaX = ref(0); | 
|   const deltaY = ref(0); | 
|   const offsetX = ref(0); | 
|   const offsetY = ref(0); | 
|   const direction = ref<Direction>(''); | 
|   | 
|   const isVertical = () => direction.value === 'vertical'; | 
|   const isHorizontal = () => direction.value === 'horizontal'; | 
|   | 
|   const reset = () => { | 
|     deltaX.value = 0; | 
|     deltaY.value = 0; | 
|     offsetX.value = 0; | 
|     offsetY.value = 0; | 
|     direction.value = ''; | 
|   }; | 
|   | 
|   const start = (event: TouchEvent) => { | 
|     reset(); | 
|     startX.value = event.touches[0].clientX; | 
|     startY.value = event.touches[0].clientY; | 
|   }; | 
|   | 
|   const move = (event: TouchEvent) => { | 
|     const touch = event.touches[0]; | 
|     deltaX.value = touch.clientX - startX.value; | 
|     deltaY.value = touch.clientY - startY.value; | 
|     moveX.value = touch.clientX; | 
|     moveY.value = touch.clientY; | 
|     offsetX.value = Math.abs(deltaX.value); | 
|     offsetY.value = Math.abs(deltaY.value); | 
|   | 
|     if (!direction.value) { | 
|       direction.value = getDirection(offsetX.value, offsetY.value); | 
|     } | 
|   }; | 
|   | 
|   return { | 
|     move, | 
|     start, | 
|     reset, | 
|     startX, | 
|     startY, | 
|     moveX, | 
|     moveY, | 
|     deltaX, | 
|     deltaY, | 
|     offsetX, | 
|     offsetY, | 
|     direction, | 
|     isVertical, | 
|     isHorizontal, | 
|   }; | 
| } | 
|   | 
| export const useTabContentTouch = (props: any, tabMethods: any, taro?: any, useTaroRect?: any) => { | 
|   const tabsContentRef = ref<HTMLElement>(); | 
|   | 
|   const tabsContentRefRect = ref({ width: 0, height: 0 }); | 
|   const initTaroWidth = () => { | 
|     if (taro && taro.getEnv() !== taro.ENV_TYPE.WEB) { | 
|       useTaroRect(tabsContentRef).then( | 
|         (rect: any) => { | 
|           tabsContentRefRect.value.width = rect?.width || 0; | 
|           tabsContentRefRect.value.height = rect?.height || 0; | 
|         }, | 
|         () => {} | 
|       ); | 
|     } else { | 
|       tabsContentRefRect.value.width = tabsContentRef.value?.clientWidth || 0; | 
|       tabsContentRefRect.value.height = tabsContentRef.value?.clientHeight || 0; | 
|     } | 
|   }; | 
|   | 
|   onMounted(() => { | 
|     setTimeout(() => { | 
|       initTaroWidth(); | 
|     }, 100); | 
|   }); | 
|   const touchState = reactive({ | 
|     offset: 0, | 
|     moving: false, | 
|   }); | 
|   const touch = useTouch(); | 
|   let position: TouchPosition = ''; | 
|   const setoffset = (deltaX: number, deltaY: number) => { | 
|     let offset = deltaX; | 
|     if (props.direction === 'horizontal') { | 
|       position = deltaX > 0 ? 'right' : 'left'; | 
|       // 计算拖拽 百分比 | 
|       offset = (Math.abs(offset) / tabsContentRefRect.value.width) * 100; | 
|     } else { | 
|       position = deltaY > 0 ? 'bottom' : 'top'; | 
|       offset = deltaY; | 
|       // 计算拖拽 百分比 | 
|       offset = (Math.abs(offset) / tabsContentRefRect.value?.height) * 100; | 
|     } | 
|     // 拖拽阈值 85% | 
|     if (offset > 85) { | 
|       offset = 85; | 
|     } | 
|     switch (position) { | 
|       case 'left': | 
|       case 'top': | 
|         // 起始tab拖拽拦截 | 
|         if (tabMethods.isEnd()) { | 
|           offset = 0; | 
|           touchState.moving = false; | 
|         } | 
|         break; | 
|       case 'right': | 
|       case 'bottom': | 
|         offset = -offset; | 
|         // 末位tab拖拽拦截 | 
|         if (tabMethods.isBegin()) { | 
|           offset = 0; | 
|           touchState.moving = false; | 
|         } | 
|         break; | 
|     } | 
|     touchState.offset = offset; | 
|   }; | 
|   const touchMethods = { | 
|     onTouchStart(event: TouchEvent) { | 
|       if (!props.swipeable) return; | 
|       touch.start(event); | 
|     }, | 
|     onTouchMove(event: TouchEvent) { | 
|       if (!props.swipeable) return; | 
|       touch.move(event); | 
|       touchState.moving = true; | 
|       setoffset(touch.deltaX.value, touch.deltaY.value); | 
|   | 
|       if (props.direction === 'horizontal' && touch.isHorizontal()) { | 
|         event.preventDefault(); | 
|         event.stopPropagation(); | 
|       } | 
|       if (props.direction === 'vertical' && touch.isVertical()) { | 
|         event.preventDefault(); | 
|         event.stopPropagation(); | 
|       } | 
|     }, | 
|     onTouchEnd() { | 
|       if (touchState.moving) { | 
|         touchState.moving = false; | 
|         switch (position) { | 
|           case 'left': | 
|           case 'top': | 
|             // 大于 35%阈值 切换至下一 Tab | 
|             if (touchState.offset > 35) { | 
|               tabMethods.next(); | 
|             } | 
|             break; | 
|           case 'right': | 
|           case 'bottom': | 
|             if (touchState.offset < -35) { | 
|               tabMethods.prev(); | 
|             } | 
|             break; | 
|         } | 
|       } | 
|     }, | 
|   }; | 
|   return { touchMethods, touchState, tabsContentRef }; | 
| }; |