skyouc
2024-12-21 c9c263dc43ad90f95f24a036cee9e6b47afb596c
uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue
@@ -1,367 +1,367 @@
<template>
   <view class="uni-indexed-list" ref="list" id="list">
      <!-- #ifdef APP-NVUE -->
      <list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
         <cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
            <!-- #endif -->
            <!-- #ifndef APP-NVUE -->
            <scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
               <view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
                  <!-- #endif -->
                  <indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
                     @itemClick="onClick"></indexed-list-item>
                  <!-- #ifndef APP-NVUE -->
               </view>
            </scroll-view>
            <!-- #endif -->
            <!-- #ifdef APP-NVUE -->
         </cell>
      </list>
      <!-- #endif -->
      <view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
         @touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
         @mouseleave.stop="mouseleave">
         <view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
            :class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
            <text class="uni-indexed-list__menu-text"
               :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
         </view>
      </view>
      <view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
         <text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
      </view>
   </view>
</template>
<script>
   import indexedListItem from './uni-indexed-list-item.vue'
   // #ifdef APP-NVUE
   const dom = weex.requireModule('dom');
   // #endif
   // #ifdef APP-PLUS
   function throttle(func, delay) {
      var prev = Date.now();
      return function() {
         var context = this;
         var args = arguments;
         var now = Date.now();
         if (now - prev >= delay) {
            func.apply(context, args);
            prev = Date.now();
         }
      }
   }
   function touchMove(e) {
      let pageY = e.touches[0].pageY
      let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
      if (this.touchmoveIndex === index) {
         return false
      }
      let item = this.lists[index]
      if (item) {
         // #ifndef APP-NVUE
         this.scrollViewId = 'uni-indexed-list-' + index
         this.touchmoveIndex = index
         // #endif
         // #ifdef APP-NVUE
         dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
            animated: false
         })
         this.touchmoveIndex = index
         // #endif
      }
   }
   const throttleTouchMove = throttle(touchMove, 40)
   // #endif
   /**
    * IndexedList 索引列表
    * @description 用于展示索引列表
    * @tutorial https://ext.dcloud.net.cn/plugin?id=375
    * @property {Boolean} showSelect = [true|false] 展示模式
    *    @value true 展示模式
    *    @value false 选择模式
    * @property {Object} options 索引列表需要的数据对象
    * @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
    * @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
    */
   export default {
      name: 'UniIndexedList',
      components: {
         indexedListItem
      },
      emits: ['click'],
      props: {
         options: {
            type: Array,
            default () {
               return []
            }
         },
         showSelect: {
            type: Boolean,
            default: false
         }
      },
      data() {
         return {
            lists: [],
            winHeight: 0,
            itemHeight: 0,
            winOffsetY: 0,
            touchmove: false,
            touchmoveIndex: -1,
            scrollViewId: '',
            touchmovable: true,
            loaded: false,
            isPC: false
         }
      },
      watch: {
         options: {
            handler: function() {
               this.setList()
            },
            deep: true
         }
      },
      mounted() {
         // #ifdef H5
         this.isPC = this.IsPC()
         // #endif
         setTimeout(() => {
            this.setList()
         }, 50)
         setTimeout(() => {
            this.loaded = true
         }, 300);
      },
      methods: {
         setList() {
            let index = 0;
            this.lists = []
            this.options.forEach((value, index) => {
               if (value.data.length === 0) {
                  return
               }
               let indexBefore = index
               let items = value.data.map(item => {
                  let obj = {}
                  obj['key'] = value.letter
                  obj['name'] = item
                  obj['itemIndex'] = index
                  index++
                  obj.checked = item.checked ? item.checked : false
                  return obj
               })
               this.lists.push({
                  title: value.letter,
                  key: value.letter,
                  items: items,
                  itemIndex: indexBefore
               })
            })
            // #ifndef APP-NVUE
            uni.createSelectorQuery()
               .in(this)
               .select('#list')
               .boundingClientRect()
               .exec(ret => {
                  this.winOffsetY = ret[0].top
                  this.winHeight = ret[0].height
                  this.itemHeight = this.winHeight / this.lists.length
               })
            // #endif
            // #ifdef APP-NVUE
            dom.getComponentRect(this.$refs['list'], (res) => {
               this.winOffsetY = res.size.top
               this.winHeight = res.size.height
               this.itemHeight = this.winHeight / this.lists.length
            })
            // #endif
         },
         touchStart(e) {
            this.touchmove = true
            let pageY = this.isPC ? e.pageY : e.touches[0].pageY
            let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
            let item = this.lists[index]
            if (item) {
               this.scrollViewId = 'uni-indexed-list-' + index
               this.touchmoveIndex = index
               // #ifdef APP-NVUE
               dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
                  animated: false
               })
               // #endif
            }
         },
         touchMove(e) {
            // #ifndef APP-PLUS
            let pageY = this.isPC ? e.pageY : e.touches[0].pageY
            let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
            if (this.touchmoveIndex === index) {
               return false
            }
            let item = this.lists[index]
            if (item) {
               this.scrollViewId = 'uni-indexed-list-' + index
               this.touchmoveIndex = index
            }
            // #endif
            // #ifdef APP-PLUS
            throttleTouchMove.call(this, e)
            // #endif
         },
         touchEnd() {
            this.touchmove = false
            // this.touchmoveIndex = -1
         },
         /**
          * 兼容 PC @tian
          */
         mousedown(e) {
            if (!this.isPC) return
            this.touchStart(e)
         },
         mousemove(e) {
            if (!this.isPC) return
            this.touchMove(e)
         },
         mouseleave(e) {
            if (!this.isPC) return
            this.touchEnd(e)
         },
         // #ifdef H5
         IsPC() {
            var userAgentInfo = navigator.userAgent;
            var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
            var flag = true;
            for (let v = 0; v < Agents.length - 1; v++) {
               if (userAgentInfo.indexOf(Agents[v]) > 0) {
                  flag = false;
                  break;
               }
            }
            return flag;
         },
         // #endif
         onClick(e) {
            let {
               idx,
               index
            } = e
            let obj = {}
            for (let key in this.lists[idx].items[index]) {
               obj[key] = this.lists[idx].items[index][key]
            }
            let select = []
            if (this.showSelect) {
               this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
               this.lists.forEach((value, idx) => {
                  value.items.forEach((item, index) => {
                     if (item.checked) {
                        let obj = {}
                        for (let key in this.lists[idx].items[index]) {
                           obj[key] = this.lists[idx].items[index][key]
                        }
                        select.push(obj)
                     }
                  })
               })
            }
            this.$emit('click', {
               item: obj,
               select: select
            })
         }
      }
   }
</script>
<style lang="scss" scoped>
   .uni-indexed-list {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: row;
   }
   .uni-indexed-list__scroll {
      flex: 1;
   }
   .uni-indexed-list__menu {
      width: 24px;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: column;
   }
   .uni-indexed-list__menu-item {
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex: 1;
      align-items: center;
      justify-content: center;
      /* #ifdef H5 */
      cursor: pointer;
      /* #endif */
   }
   .uni-indexed-list__menu-text {
      font-size: 12px;
      text-align: center;
      color: #aaa;
   }
   .uni-indexed-list__menu--active {
      // background-color: rgb(200, 200, 200);
   }
   .uni-indexed-list__menu--active {}
   .uni-indexed-list__menu-text--active {
      border-radius: 16px;
      width: 16px;
      height: 16px;
      line-height: 16px;
      background-color: #007aff;
      color: #fff;
   }
   .uni-indexed-list__alert-wrapper {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: row;
      align-items: center;
      justify-content: center;
   }
   .uni-indexed-list__alert {
      width: 80px;
      height: 80px;
      border-radius: 80px;
      text-align: center;
      line-height: 80px;
      font-size: 35px;
      color: #fff;
      background-color: rgba(0, 0, 0, 0.5);
   }
</style>
<template>
   <view class="uni-indexed-list" ref="list" id="list">
      <!-- #ifdef APP-NVUE -->
      <list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
         <cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
            <!-- #endif -->
            <!-- #ifndef APP-NVUE -->
            <scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
               <view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
                  <!-- #endif -->
                  <indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
                     @itemClick="onClick"></indexed-list-item>
                  <!-- #ifndef APP-NVUE -->
               </view>
            </scroll-view>
            <!-- #endif -->
            <!-- #ifdef APP-NVUE -->
         </cell>
      </list>
      <!-- #endif -->
      <view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
         @touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
         @mouseleave.stop="mouseleave">
         <view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
            :class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
            <text class="uni-indexed-list__menu-text"
               :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
         </view>
      </view>
      <view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
         <text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
      </view>
   </view>
</template>
<script>
   import indexedListItem from './uni-indexed-list-item.vue'
   // #ifdef APP-NVUE
   const dom = weex.requireModule('dom');
   // #endif
   // #ifdef APP-PLUS
   function throttle(func, delay) {
      var prev = Date.now();
      return function() {
         var context = this;
         var args = arguments;
         var now = Date.now();
         if (now - prev >= delay) {
            func.apply(context, args);
            prev = Date.now();
         }
      }
   }
   function touchMove(e) {
      let pageY = e.touches[0].pageY
      let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
      if (this.touchmoveIndex === index) {
         return false
      }
      let item = this.lists[index]
      if (item) {
         // #ifndef APP-NVUE
         this.scrollViewId = 'uni-indexed-list-' + index
         this.touchmoveIndex = index
         // #endif
         // #ifdef APP-NVUE
         dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
            animated: false
         })
         this.touchmoveIndex = index
         // #endif
      }
   }
   const throttleTouchMove = throttle(touchMove, 40)
   // #endif
   /**
    * IndexedList 索引列表
    * @description 用于展示索引列表
    * @tutorial https://ext.dcloud.net.cn/plugin?id=375
    * @property {Boolean} showSelect = [true|false] 展示模式
    *    @value true 展示模式
    *    @value false 选择模式
    * @property {Object} options 索引列表需要的数据对象
    * @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
    * @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
    */
   export default {
      name: 'UniIndexedList',
      components: {
         indexedListItem
      },
      emits: ['click'],
      props: {
         options: {
            type: Array,
            default () {
               return []
            }
         },
         showSelect: {
            type: Boolean,
            default: false
         }
      },
      data() {
         return {
            lists: [],
            winHeight: 0,
            itemHeight: 0,
            winOffsetY: 0,
            touchmove: false,
            touchmoveIndex: -1,
            scrollViewId: '',
            touchmovable: true,
            loaded: false,
            isPC: false
         }
      },
      watch: {
         options: {
            handler: function() {
               this.setList()
            },
            deep: true
         }
      },
      mounted() {
         // #ifdef H5
         this.isPC = this.IsPC()
         // #endif
         setTimeout(() => {
            this.setList()
         }, 50)
         setTimeout(() => {
            this.loaded = true
         }, 300);
      },
      methods: {
         setList() {
            let index = 0;
            this.lists = []
            this.options.forEach((value, index) => {
               if (value.data.length === 0) {
                  return
               }
               let indexBefore = index
               let items = value.data.map(item => {
                  let obj = {}
                  obj['key'] = value.letter
                  obj['name'] = item
                  obj['itemIndex'] = index
                  index++
                  obj.checked = item.checked ? item.checked : false
                  return obj
               })
               this.lists.push({
                  title: value.letter,
                  key: value.letter,
                  items: items,
                  itemIndex: indexBefore
               })
            })
            // #ifndef APP-NVUE
            uni.createSelectorQuery()
               .in(this)
               .select('#list')
               .boundingClientRect()
               .exec(ret => {
                  this.winOffsetY = ret[0].top
                  this.winHeight = ret[0].height
                  this.itemHeight = this.winHeight / this.lists.length
               })
            // #endif
            // #ifdef APP-NVUE
            dom.getComponentRect(this.$refs['list'], (res) => {
               this.winOffsetY = res.size.top
               this.winHeight = res.size.height
               this.itemHeight = this.winHeight / this.lists.length
            })
            // #endif
         },
         touchStart(e) {
            this.touchmove = true
            let pageY = this.isPC ? e.pageY : e.touches[0].pageY
            let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
            let item = this.lists[index]
            if (item) {
               this.scrollViewId = 'uni-indexed-list-' + index
               this.touchmoveIndex = index
               // #ifdef APP-NVUE
               dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
                  animated: false
               })
               // #endif
            }
         },
         touchMove(e) {
            // #ifndef APP-PLUS
            let pageY = this.isPC ? e.pageY : e.touches[0].pageY
            let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
            if (this.touchmoveIndex === index) {
               return false
            }
            let item = this.lists[index]
            if (item) {
               this.scrollViewId = 'uni-indexed-list-' + index
               this.touchmoveIndex = index
            }
            // #endif
            // #ifdef APP-PLUS
            throttleTouchMove.call(this, e)
            // #endif
         },
         touchEnd() {
            this.touchmove = false
            // this.touchmoveIndex = -1
         },
         /**
          * 兼容 PC @tian
          */
         mousedown(e) {
            if (!this.isPC) return
            this.touchStart(e)
         },
         mousemove(e) {
            if (!this.isPC) return
            this.touchMove(e)
         },
         mouseleave(e) {
            if (!this.isPC) return
            this.touchEnd(e)
         },
         // #ifdef H5
         IsPC() {
            var userAgentInfo = navigator.userAgent;
            var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
            var flag = true;
            for (let v = 0; v < Agents.length - 1; v++) {
               if (userAgentInfo.indexOf(Agents[v]) > 0) {
                  flag = false;
                  break;
               }
            }
            return flag;
         },
         // #endif
         onClick(e) {
            let {
               idx,
               index
            } = e
            let obj = {}
            for (let key in this.lists[idx].items[index]) {
               obj[key] = this.lists[idx].items[index][key]
            }
            let select = []
            if (this.showSelect) {
               this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
               this.lists.forEach((value, idx) => {
                  value.items.forEach((item, index) => {
                     if (item.checked) {
                        let obj = {}
                        for (let key in this.lists[idx].items[index]) {
                           obj[key] = this.lists[idx].items[index][key]
                        }
                        select.push(obj)
                     }
                  })
               })
            }
            this.$emit('click', {
               item: obj,
               select: select
            })
         }
      }
   }
</script>
<style lang="scss" scoped>
   .uni-indexed-list {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: row;
   }
   .uni-indexed-list__scroll {
      flex: 1;
   }
   .uni-indexed-list__menu {
      width: 24px;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: column;
   }
   .uni-indexed-list__menu-item {
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex: 1;
      align-items: center;
      justify-content: center;
      /* #ifdef H5 */
      cursor: pointer;
      /* #endif */
   }
   .uni-indexed-list__menu-text {
      font-size: 12px;
      text-align: center;
      color: #aaa;
   }
   .uni-indexed-list__menu--active {
      // background-color: rgb(200, 200, 200);
   }
   .uni-indexed-list__menu--active {}
   .uni-indexed-list__menu-text--active {
      border-radius: 16px;
      width: 16px;
      height: 16px;
      line-height: 16px;
      background-color: #007aff;
      color: #fff;
   }
   .uni-indexed-list__alert-wrapper {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      /* #ifndef APP-NVUE */
      display: flex;
      /* #endif */
      flex-direction: row;
      align-items: center;
      justify-content: center;
   }
   .uni-indexed-list__alert {
      width: 80px;
      height: 80px;
      border-radius: 80px;
      text-align: center;
      line-height: 80px;
      font-size: 35px;
      color: #fff;
      background-color: rgba(0, 0, 0, 0.5);
   }
</style>