<template>
  <view v-if="value" class="tn-select-class tn-select">
    <tn-popup
      v-model="value"
      mode="bottom"
      :popup="false"
      length="auto"
      :safeAreaInsetBottom="safeAreaInsetBottom"
      :maskCloseable="maskCloseable"
      :zIndex="elZIndex"
      @close="close"
    >
      <view class="tn-select__content">
        <!-- 头部 -->
        <view class="tn-select__content__header" @touchmove.stop.prevent>
          <view
            class="tn-select__content__header__btn tn-select__content__header--cancel"
            :style="{ color: cancelColor }"
            hover-class="tn-hover-class"
            hover-stay-time="150"
            @tap="getResult('cancel')"
          >{{ cancelText }}</view>
          <view class="tn-select__content__header__title">{{ title }}</view>
          <view
            class="tn-select__content__header__btn tn-select__content__header--confirm"
            :style="{ color: confirmColor }"
            hover-class="tn-hover-class"
            hover-stay-time="150"
            @tap="getResult('confirm')"
          >{{ confirmText }}</view>
        </view>
        <!-- 列表内容 -->
        <view class="tn-select__content__body">
          <picker-view
            class="tn-select__content__body__view"
            :value="defaultSelector"
            @pickstart="pickStart"
            @pickend="pickEnd"
            @change="columnChange"
          >
            <picker-view-column v-for="(item, index) in columnData" :key="index">
              <view class="tn-select__content__body__item" v-for="(sub_item, sub_index) in item" :key="sub_index">
                <view class="tn-text-ellipsis">
                  {{ sub_item[labelName] }}
                </view>
              </view>
            </picker-view-column>
          </picker-view>
        </view>
      </view>
    </tn-popup>
  </view>
</template>

<script>
  export default {
    name: 'tn-select',
    props: {
      value: {
        type: Boolean,
        default: false
      },
      // 列表模式
      // single 单列 multi 多列 multi-auto 多列联动
      mode: {
        type: String,
        default: 'single'
      },
      // 列数据
      list: {
        type: Array,
        default() {
          return []
        }
      },
      // value属性名
      valueName: {
        type: String,
        default: 'value'
      },
      // label属性名
      labelName: {
        type: String,
        default: 'label'
      },
      // 当mode=multi-auto时,children的属性名
      childName: {
        type: String,
        default: 'children'
      },
      // 默认值
      defaultValue: {
        type: Array,
        default() {
          return [0]
        }
      },
      // 顶部标题
      title: {
        type: String,
        default: ''
      },
      // 取消按钮文字
      cancelText: {
        type: String,
        default: '取消'
      },
      // 取消按钮文字颜色
      cancelColor: {
        type: String,
        default: ''
      },
      // 确认按钮文字
      confirmText: {
        type: String,
        default: '确认'
      },
      // 确认按钮文字颜色
      confirmColor: {
        type: String,
        default: ''
      },
      // 点击遮罩关闭
      maskCloseable: {
        type: Boolean,
        default: true
      },
      // 预留安全区域
      safeAreaInsetBottom: {
        type: Boolean,
        default: false
      },
      // zIndex
      zIndex: {
        type: Number,
        default: 0
      }
    },
    computed: {
      elZIndex() {
        return this.zIndex ? this.zIndex : this.$t.zIndex.popup
      }
    },
    data() {
      return {
        // 列是否还在滑动中,微信小程序如果在滑动中就点确定,结果可能不准确
        moving: false,
        // 用户保存当前列的索引,用于判断下一次变化时改变的列
        defaultSelector: [0],
        // picker-view数据
        columnData: [],
        // 保存用户选择的结果
        selectValue: [],
        // 上一次改变时的index
        lastSelectIndex: [],
        // 列数
        columnNum: 0
      }
    },
    watch: {
      // 在select弹起的时候,重新初始化所有数据
      value: {
        handler(val) {
          if (val) setTimeout(() => this.init(), 10)
        },
        immediate: true
      }
    },
    methods: {
      // 标识滑动开始,只有微信小程序才有这样的事件
      pickStart() {
      	// #ifdef MP-WEIXIN
      	this.moving = true;
      	// #endif
      },
      // 标识滑动结束
      pickEnd() {
      	// #ifdef MP-WEIXIN
      	this.moving = false;
      	// #endif
      },
      init() {
        this.setColumnNum()
        this.setDefaultSelector()
        this.setColumnData()
        this.setSelectValue()
      },
      // 获取默认选中列下标
      setDefaultSelector() {
        // 如果没有传入默认值,生成用0填充长度为columnNum的数组
        this.defaultSelector = this.defaultValue.length === this.columnNum ? this.defaultValue : Array(this.columnNum).fill(0)
        this.lastSelectIndex = this.$t.deepClone(this.defaultSelector)
      },
      // 计算列数
      setColumnNum() {
        // 单列的数量为1
        if (this.mode === 'single') this.columnNum = 1
        // 多列时取list的长度
        else if (this.mode === 'multi') this.columnNum = this.list.length
        // 多列联动时,通过遍历list的第一个元素,得出有多少列
        else if (this.mode === 'multi-auto') {
          let num = 1
          let column = this.list
          // 如果存在children属性,再次遍历
          while (column[0][this.childName]) {
            column = column[0] ? column[0][this.childName] : {},
            num++
          }
          this.columnNum = num
        }
      },
      // 获取需要展示在picker中的列数据
      setColumnData() {
        let data = []
        this.selectValue = []
        if (this.mode === 'multi-auto') {
          // 获取所有数据中的第一个元素
          let column = this.list[this.defaultSelector.length ? this.defaultSelector[0] : 0]
          // 通过循环所有列数,再根据设定列的数组,得出当前需要渲染的整个列数组
          for (let i = 0; i < this.columnNum; i++) {
            // 第一列默认为整个list数组
            if (i === 0) {
              data[i] = this.list
              column = column[this.childName]
            } else {
              // 大于第一列时,判断是否有默认选中的,如果没有就用该列的第一项
              data[i] = column
              column = column[this.defaultSelector[i]][this.childName]
            }
          }
        } else if (this.mode === 'single') {
          data[0] = this.list
        } else {
          data = this.list
        }
        this.columnData = data
      },
      // 获取默认选中的值,如果没有设置,则默认选中第一项
      setSelectValue() {
        let tmp = null
        for (let i = 0; i < this.columnNum; i++) {
          tmp = this.columnData[i][this.defaultSelector[i]]
          let data = {
            value: tmp ? tmp[this.valueName] : null,
            label: tmp ? tmp[this.labelName] : null
          }
          // 判断是否存在额外的参数
          if (tmp && tmp.extra) data.extra = tmp.extra
          this.selectValue.push(data)
        }
      },
      // 列选项
      columnChange(event) {
        let index = null
        let columnIndex = event.detail.value
        
        this.selectValue = []
        if (this.mode === 'multi-auto') {
          // 对比前后两个数组,判断变更的是那一列
          this.lastSelectIndex.map((v, idx) => {
            if (v != columnIndex[idx]) index = idx
          })
          this.defaultSelector = columnIndex
          // 当前变化列的下一列的数据,需要获取上一列的数据,同时需要指定是上一列的第几个的children,再往后的
          // 默认是队列的第一个为默认选项
          for (let i = index + 1; i < this.columnNum; i++) {
            this.columnData[i] = this.columnData[i - 1][i - 1 == index ? columnIndex[index] : 0][this.childName]
            this.defaultSelector[i] = 0
          }
          // 在历遍的过程中,可能由于上一步修改this.columnData,导致产生连锁反应,程序触发columnChange,会有多次调用
          // 只有在最后一次数据稳定后的结果是正确的,此前的历遍中,可能会产生undefined,故需要判断
          columnIndex.map((item, index) => {
            let data = this.columnData[index][columnIndex[index]]
            let tmp = {
              value: data ? data[this.valueName] : null,
              label: data ? data[this.labelName] : null
            }
            if (data && data.extra !== undefined) tmp.extra = data.extra
            this.selectValue.push(tmp)
          })
          this.lastSelectIndex = columnIndex
        } else if (this.mode === 'single') {
          let data = this.columnData[0][columnIndex[0]]
          let tmp = {
            value: data ? data[this.valueName] : null,
            label: data ? data[this.labelName] : null
          }
          if (data && data.extra !== undefined) tmp.extra = data.extra
          this.selectValue.push(tmp)
        } else if (this.mode === 'multi') {
          columnIndex.map((item, index) => {
            let data = this.columnData[index][columnIndex[index]]
            let tmp = {
              value: data ? data[this.valueName] : null,
              label: data ? data[this.labelName] : null
            }
            if (data && data.extra !== undefined) tmp.extra = data.extra
            this.selectValue.push(tmp)
          })
        }
      },
      close() {
        this.$emit('input', false)
      },
      getResult(event = null) {
        // #ifdef MP-WEIXIN
        if (this.moving) return;
        // #endif
        if (event) this.$emit(event, this.selectValue)
        this.close()
      }
    }
  }
</script>

<style lang="scss" scoped>
  
  .tn-select {
    
    &__content {
      position: relative;
      
      &__header {
        position: relative;
        display: flex;
        flex-direction: row;
        width: 100%;
        height: 90rpx;
        padding: 0 40rpx;
        align-items: center;
        justify-content: space-between;
        box-sizing: border-box;
        font-size: 30rpx;
        background-color: #FFFFFF;
        
        &__btn {
          padding: 16rpx;
          box-sizing: border-box;
          text-align: center;
          text-decoration: none;
        }
        
        &__title {
          color: $tn-font-color;
        }
        
        &--cancel {
          color: $tn-font-sub-color;
        }
        
        &--confirm {
          color: $tn-main-color;
        }
      }
      
      &__body {
        width: 100%;
        height: 500rpx;
        overflow: hidden;
        background-color: #FFFFFF;
        
        &__view {
          height: 100%;
          box-sizing: border-box;
        }
        
        &__item {
          display: flex;
          flex-direction: row;
          align-items: center;
          justify-content: center;
          font-size: 32rpx;
          color: $tn-font-color;
          padding: 0 8rpx;
        }
      }
    }
    
  }
</style>