yingwinwin / yingwinwin.github.io

一个前端菜鸟的自我救赎之路...
https://yingwinwin.github.io/
8 stars 5 forks source link

Picker组件 #15

Closed yingwinwin closed 3 years ago

yingwinwin commented 4 years ago

打算从头整理一下最近项目中写的picker组件的内容,比较多要从头开始写。

yingwinwin commented 4 years ago

初始结构

image

import React, { Component } from "react";
import "../css/picker.css";

export default class Picker extends Component {
  render() {
    return (
      <div className="picker-wrap">
        <div className="picker-title">
          <div className="picker-cancel">取消</div>
          <div className="picker-sure">确定</div>
        </div>
        <div className="picker-content-wrap">
          <ul className="picker-content">
            <li className="picker-list">123</li>
            <li className="picker-list">123</li>
            <li className="picker-list">123</li>
            <li className="picker-list">123</li>
            <li className="picker-list">123</li>
          </ul>
          <div className="picker-up-shadow"></div>
          <div className="picker-line"></div>
          <div className="picker-down-shadow"></div>
        </div>
      </div>
    );
  }
}
.mask() {
  position: absolute;
  width: 100%;
  pointer-events: none;
  padding: 0 0.533rem;
  box-sizing: border-box;
}
* {
  padding: 0;
  margin: 0;
}

ul li {
  list-style: none;
}
.picker-wrap {
  width: 100vw;
  height: 250px;
  overflow: hidden;
  position: absolute;
  left: 0;
  background: #fff;
  touch-action: none;
  color: #333;
  border-radius: 0.32rem 0.32rem 0 0;
  display: flex;
  flex-direction: column;

  .picker-title {
    display: flex;
    justify-content: space-between;
    padding: 0 0.533rem;
    height: 50px;
    line-height: 50px;
    box-sizing: border-box;
    position: relative;
    border-bottom: 1px solid rgb(#526279, 0.1);
  }
  .picker-sure {
    color: #4c97ff;
  }
}

.picker-content-wrap {
  position: relative;
  width: 100vw;
  background: #fff;
  display: flex;
  overflow: hidden;

  .picker-content {
    position: relative;
    flex: 1;
    transition: 0.03s ease-out 0s;
    -webkit-transition: -webkit-transform 0.03s ease-out 0s;
    transition: transform 0.2s ease-out;

    .picker-list {
      height: 40px;
      line-height: 40px;
      position: relative;
      text-align: center;
      text-overflow: ellipsis;
      overflow: hidden;
    }
  }

  .picker-up-shadow {
    .mask();
    top: 0;
    height: 80px;
    background-image: linear-gradient(
      to bottom,
      #fff,
      rgba(255, 255, 255, 0.5)
    );
    z-index: 50;
  }

  .picker-down-shadow {
    .mask();
    background-image: linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5));
    z-index: 50;
    top: 120px;
    height: 80px;
  }

  .picker-line {
    width: 95vw;
    left: 50%;
    box-sizing: border-box;
    transform: translate3d(-50%, 0, 0);
    -webkit-transform: translate3d(-50%, 0, 0);
    position: absolute;
    pointer-events: none;
    border-top: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    height: 40px;
    top: 80px;
  }
}
yingwinwin commented 4 years ago

数据结构

import React, { Component } from "react";
import Picker from "./components/Picker";

export default class App extends Component {
  handleDate = () => {
    let hour = [];
    for (let i = 0; i < 23; i++) {
      hour.push({ value: i, key: i });
    }

    let min = [];
    for (let i = 0; i < 59; i++) {
      min.push({ value: i, key: i });
    }
    return [hour, min, min];
  };

  handleData = () => {
    let prov = [
      { value: "北京", key: "01" },
      { value: "杭州", key: "33" },
    ];
    let city = {
      "01": [{ value: "北京市", key: "0101" }],
      "33": [
        { value: "江干区", key: "3301" },
        { value: "滨江区", key: "3302" },
        { value: "西湖区", key: "3303" },
      ],
    };
    let area = {
      "0101": [{ value: "丰台区", key: "010101" }],
      "3301": [
        { value: "彭埠街道", key: "330101" },
        { value: "四季青街道", key: "330102" },
      ],
      "3302": [
        { value: "长河街道", key: "330201" },
        { value: "西兴街道", key: "330202" },
      ],
      "3303": [
        { value: "蒋村街道", key: "330301" },
        { value: "古荡街道", key: "330302" },
        { value: "灵隐街道", key: "330303" },
      ],
    };
    return [prov, city, area];
  };
  render() {
    return <Picker data={this.handleData()} format={[2, 2]} isLink={true} />;
  }
}
yingwinwin commented 4 years ago

简单滚动问题

import React, { Component } from "react";
import { array } from "prop-types";
import "../css/picker.css";

export default class Picker extends Component {
  static propTypes = {
    data: array.isRequired,
  };

  static defaultProps = {
    data: [],
  };

  constructor(props) {
    super();
    this.state = {
      translateY: props.data.map((item) => 0),
    };
    this.liHeight = 40;
  }

  //普通滚动
  handleStart = (event) => {
    event.preventDefault();
    this.startFinger = event.changedTouches[0].clientY;
    this.startFinger = event.targetTouches[0].pageY;
    this.temp = [...this.state.translateY];
  };

  handleMove = (event, idx) => {
    event.preventDefault();
    let nowFinger = event.changedTouches[0].clientY;
    let distance = nowFinger - this.startFinger;
    let tempTramslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = this.temp[idx] + distance;
      }
      return item;
    });
    this.setState({
      translateY: tempTramslateY,
    });
  };

  handleEnd = (event, idx, len) => {
    event.preventDefault();
    let temTranslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = item > 0 ? 0 : item;
        item =
          item < (-len + 1) * this.liHeight ? (-len + 1) * this.liHeight : item;

        let sub = item % this.liHeight;

        if (sub < this.liHeight / 2) {
          item = item - sub;
        } else {
          item = item + (this.liHeight - sub);
        }
      }
      return item;
    });
    this.setState({
      translateY: temTranslateY,
    });
  };

  renderPickerCol = (data) => {
    return data.map((item, idx) => (
      <ul
        className="picker-content"
        key={idx}
        onTouchStart={this.handleStart}
        onTouchMove={(e) => this.handleMove(e, idx)}
        onTouchEnd={(e) => this.handleEnd(e, idx, item.length)}
        style={{
          transform: `translate3d(0, ${this.state.translateY[idx]}px, 0)`,
        }}
      >
        {this.renderPickerList(item)}
      </ul>
    ));
  };

  renderPickerList = (item) => {
    return item.map((ele) => (
      <li className="picker-list" key={ele.key}>
        {ele.value}
      </li>
    ));
  };

  render() {
    return (
      <div className="picker-wrap">
        <div className="picker-title">
          <div className="picker-cancel">取消</div>
          <div className="picker-sure">确定</div>
        </div>
        <div className="picker-content-wrap">
          {this.renderPickerCol(this.props.data)}
          <div className="picker-up-shadow"></div>
          <div className="picker-line"></div>
          <div className="picker-down-shadow"></div>
        </div>
      </div>
    );
  }
}
yingwinwin commented 4 years ago

联动问题

import React, { Component } from "react";
import { array, bool } from "prop-types";
import "../css/picker.css";

export default class Picker extends Component {
  static propTypes = {
    data: array.isRequired,
    format: array,
    isLink: bool,
  };

  static defaultProps = {
    data: [],
    format: [2, 2],
    isLink: false,
  };

  constructor(props) {
    super();
    this.state = {
      translateY: props.data.map((item) => 0),
      link: props.data.map((item) => 0),
    };
    this.liHeight = 40;
  }

  setLink = (idx) => {
    let length = this.props.data.length;
    let translateY = [...this.state.translateY];
    let link = [...this.state.link];
    translateY[idx] = parseInt(this.state.translateY[idx]);
    link[idx] = parseInt(Math.abs(this.state.translateY[idx])) / this.liHeight;
    if (this.props.isLink) {
      for (let i = 1; i < length - idx; i++) {
        translateY[length - i] = 0;
        link[length - i] = 0;
      }
    }
    this.setState({
      translateY,
      link,
    });
  };

  //普通滚动
  handleStart = (event) => {
    event.preventDefault();
    this.startFinger = event.changedTouches[0].clientY;
    this.startFinger = event.targetTouches[0].pageY;
    this.temp = [...this.state.translateY];
  };

  handleMove = (event, idx) => {
    event.preventDefault();
    let nowFinger = event.changedTouches[0].clientY;
    let distance = nowFinger - this.startFinger;
    let tempTramslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = this.temp[idx] + distance;
      }
      return item;
    });
    this.setState({
      translateY: tempTramslateY,
    });
  };

  handleEnd = (event, idx, len) => {
    event.preventDefault();
    let tempTranslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = item > 0 ? 0 : item;
        item =
          item < (-len + 1) * this.liHeight ? (-len + 1) * this.liHeight : item;

        let sub = item % this.liHeight;

        if (sub < this.liHeight / 2) {
          item = item - sub;
        } else {
          item = item + (this.liHeight - sub);
        }
      }
      return item;
    });
    this.setState(
      {
        translateY: tempTranslateY,
      },
      () => {
        this.setLink(idx);
      }
    );
  };

  handleFormat = (format) => {
    let formatArr = [];
    while (format--) {
      formatArr.push(<li key={format} className="picker-list"></li>);
    }
    return formatArr;
  };

  renderPickerCol = (data, format, isLink, link) => {
    if (!data.length) return;
    const { translateY } = this.state;
    let arr = [];
    arr[0] = data[0];
    let selectArr = [];
    selectArr[0] = data[0][link[0]];
    for (let i = 1; i < data.length; i++) {
      if (isLink) {
        if (data[i][selectArr[i - 1].key]) {
          selectArr.push(data[i][selectArr[i - 1].key][link[i]]);
          arr.push(data[i][arr[i - 1][link[i - 1]].key]);
        } else {
          alert("数据配置错误!");
          return;
        }
      } else if (!isLink) {
        selectArr.push(data[i][link[i]]);
        arr.push(data[i]);
      }
    }
    return arr.map((item, idx) => (
      <ul
        className="picker-content"
        key={idx}
        onTouchStart={this.handleStart}
        onTouchMove={(e) => this.handleMove(e, idx)}
        onTouchEnd={(e) => this.handleEnd(e, idx, item.length)}
        style={{ transform: `translate3d(0, ${translateY[idx]}px, 0)` }}
      >
        {this.handleFormat(format[0])}
        {this.renderPickerList(item)}
        {this.handleFormat(format[1])}
      </ul>
    ));
  };

  renderPickerList = (item) => {
    return item.map((ele) => (
      <li className="picker-list" key={ele.key}>
        {ele.value}
      </li>
    ));
  };

  render() {
    const { data, format, isLink } = this.props;
    const { link } = this.state;
    return (
      <div className="picker-wrap">
        <div className="picker-title">
          <div className="picker-cancel">取消</div>
          <div className="picker-sure">确定</div>
        </div>
        <div className="picker-content-wrap">
          {this.renderPickerCol(data, format, isLink, link)}
          <div className="picker-up-shadow"></div>
          <div className="picker-line"></div>
          <div className="picker-down-shadow"></div>
        </div>
      </div>
    );
  }
}
yingwinwin commented 4 years ago

初始值问题

popup和遮罩层

export default class App extends Component { state = { show: false, text: "", initValue: [] } handleDate = () => { let hour = []; for (let i = 0; i < 23; i++) { hour.push({ value: i, key: i }); }

let min = [];
for (let i = 0; i < 59; i++) {
  min.push({ value: i, key: i });
}
return [hour, min, min];

};

handleData = () => { let prov = [ { value: "北京", key: "01" }, { value: "杭州", key: "33" }, ]; let city = { "01": [{ value: "北京市", key: "0101" }], "33": [ { value: "江干区", key: "3301" }, { value: "滨江区", key: "3302" }, { value: "西湖区", key: "3303" }, ], }; let area = { "0101": [{ value: "丰台区", key: "010101" }], "3301": [ { value: "彭埠街道", key: "330101" }, { value: "四季青街道", key: "330102" }, ], "3302": [ { value: "长河街道", key: "330201" }, { value: "西兴街道", key: "330202" }, ], "3303": [ { value: "蒋村街道", key: "330301" }, { value: "古荡街道", key: "330302" }, { value: "灵隐街道", key: "330303" }, ], }; return [prov, city, area]; };

handleClosed = () => { this.setState({ show: false }) }

handleSure = (v) => { this.setState({ show: false, text: ${v[0].value}${v[1].value}${v[2].value}, initValue: v }) console.log(v) }

openPicker = () => { this.setState({ show: true }) }

handleChange = () => { this.setState({ initValue: this.state.text }) }

render() { return (

{this.state.show && }
);

} }

- 组件
```jsx
import React, { Component } from "react";
import { array, bool, func } from "prop-types";
import "../css/picker.css";

export default class Picker extends Component {
  static propTypes = {
    data:array.isRequired,
    format: array,
    isLink: bool,
    initValue: array,
    handleClosed: func, 
    handleSure: func
  };

  static defaultProps = {
    data: [],
    format: [2, 2],
    isLink: false,
    initValue: []
  };

  constructor(props) {
    super();
    this.state = {
      translateY: props.data.map((item) => 0),
      link: props.data.map((item) => 0),
    };
    this.liHeight = 40;
    this.select = props.initValue.length > 0 && props.initValue[0].key !== '' ? props.initValue : this.defaultInitValue(props) 
  }

  componentDidMount() {
    const { data, initValue } = this.props;
    if(!(initValue.length > 0 && data.length > 0)) return; // 如果父组件没有穿初始值就不设置初始值
    for(let i = 0; i < data.length; i++) {
      if(initValue[i].key === '' || initValue[i].value === '') return  // 如果父组件没有穿初始值就不设置初始值
    }
    this.setInitValue();   // 如果设置了,就设置初始值
  }

  /* 默认初始值,没有给初始值,默认数组的第一项 */
  defaultInitValue = (props) => {
    const { data, initValue, isLink } = props;
    if(!initValue.length > 0) {
      let initValue = [];
      for(let i = 0; i < data.length; i ++) {
        if(isLink && i === 0) {
          initValue[i] = data[i][0];   // 联动
        } else {
          initValue[i] = data[i][initValue[i - 1].key][0];  // 非联动
        }
      }
    }
    return initValue;
  }

  /* 设置初始值 */
  setInitValue = () => {
    const { data, initValue, isLink } = this.props;

    let link = [...this.state.link];
    let translateY = [...this.state.translateY];
    // 在data里面把用户传过来的值找到,转换为初始值,translateY的高度和数组的下标随之改变
    for(let out = 0; out < data.length; out ++) {
      if( out === 0 || !isLink) {
        for(let i = 0; i < data[out].length; i ++) {
          if(data[out][i].key  === initValue[out].key) {
            link[out] = i;
            translateY[out] = -i * this.liHeight;
          }
        }
      } else {
        if(data[out][initValue[out - 1].key]){
          for(let i = 0; i < data[out][initValue[out - 1].key].length; i++) {
            if(data[out][initValue[out - 1].key][i].key === initValue[out].key) {
              link[out] = i;
              translateY[out] = -i * this.liHeight;
            }
          }
        } else {
          alert('数据配置错误')
        }
      }
    }
    this.setState({
      link,
      translateY
    });
  }

  /* 获取数据 */
  getSelect = () => {
    const { data, isLink } = this.props;
    const { link } = this.state;
    let selecArr = [];
    selecArr[0] = data[0][link[0]];
    for(let i = 1; i < data.length; i ++) {
      if(isLink) {
        selecArr.push(data[i][selecArr[i - 1].key][link[i]]);
      } else if (!isLink){
        selecArr.push(data[i][link[i]]);
      }
    }
    return selecArr;
  }

  // 设置联动
  setLink = (idx) => {
    let length = this.props.data.length;
    let translateY = [...this.state.translateY];
    let link = [...this.state.link];
    translateY[idx] = parseInt(this.state.translateY[idx]);  // 当前滚动的距离
    link[idx] = parseInt(Math.abs(this.state.translateY[idx])) / this.liHeight;  // 当前滚动的数组下标
    // 如果是联动的话
    if (this.props.isLink) {
      for (let i = 1; i < length - idx; i++) {
        translateY[length - i] = 0;
        link[length - i] = 0;
      }
    }
    this.setState({
      translateY,
      link,
    });
  };

  //普通滚动
  handleStart = (event) => {
    event.preventDefault();
    this.startFinger = event.changedTouches[0].clientY;
    this.temp = [...this.state.translateY];
  };

  // 处理移动手势
  handleMove = (event, idx) => {
    event.preventDefault();
    let nowFinger = event.changedTouches[0].clientY;
    let distance = nowFinger - this.startFinger;
    let tempTramslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = this.temp[idx] + distance;   // 点击到的距离 + 滑动的距离 = 移动的距离
      }
      return item;
    });
    this.setState({
      translateY: tempTramslateY,
    });
  };

  /* 处理结束时滚动 */
  handleEnd = (event, idx, len) => {
    // 阻止冒泡
    event.preventDefault();
    let tempTranslateY = [...this.state.translateY].map((item, index) => {
      // 如果是当前滑动的这一项
      if (idx === index) {
        item = item > 0 ? 0 : item;  // 如果小于 0 , 就等于 0 否则就是它移动的距离
        item =
          item < (-len + 1) * this.liHeight ? (-len + 1) * this.liHeight : item; // 如果小于最大高度就是当前的距离,否则就是最大高度

        let sub = item % this.liHeight;   // 如果当前的距离 膜 高度

        // 如果小于高度的一半就是当前高度 - 膜出来的距离, 否则就加上
        if (sub < this.liHeight / 2) {
          item = item - sub;
        } else {
          item = item + (this.liHeight - sub);
        }
      }
      return item;
    });
    this.setState(
      {
        translateY: tempTranslateY,
      },
      () => {
        this.setLink(idx);
      }
    );
  };

  // 处理数据结构
  handleFormat = (format) => {
    let formatArr = [];
    while (format--) {
      formatArr.push(<li key={format} className="picker-list"></li>);
    }
    return formatArr;
  };

  /* 渲染picker组件的列 */
  renderPickerCol = (data, format, isLink, link) => {
    if (!data.length) return;
    const { translateY } = this.state;
    let arr = [];
    arr[0] = data[0];
    let selectArr = [];
    selectArr[0] = data[0][link[0]];
    for (let i = 1; i < data.length; i++) {
      // 如果是联动的话
      if (isLink) {
        if (data[i][selectArr[i - 1].key]) {
          selectArr.push(data[i][selectArr[i - 1].key][link[i]]);
          arr.push(data[i][arr[i - 1][link[i - 1]].key]);
        } else {
          alert("数据配置错误!");
          return;
        }
        // 不联动的话
      } else if (!isLink) {
        selectArr.push(data[i][link[i]]);
        arr.push(data[i]);
      }
    }
    this.select = selectArr
    return arr.map((item, idx) => (
      <ul
        className="picker-content"
        key={idx}
        onTouchStart={this.handleStart}
        onTouchMove={(e) => this.handleMove(e, idx)}
        onTouchEnd={(e) => this.handleEnd(e, idx, item.length)}
        style={{ transform: `translate3d(0, ${translateY[idx]}px, 0)` }}
      >
        {this.handleFormat(format[0])}
        {this.renderPickerList(item)}
        {this.handleFormat(format[1])}
      </ul>
    ));
  };

  // 渲染picker组件的每一项
  renderPickerList = (item) => {
    return item.map((ele) => (
      <li className="picker-list" key={ele.key}>
        {ele.value}
      </li>
    ));
  };

  handleClosed = () => {
    this.props.handleClosed()
  }

  handleSure = (e) => {
    e.stopPropagation();
    this.props.handleSure(this.getSelect());
  }

  render() {
    const { data, format, isLink } = this.props;
    const { link } = this.state;
    return (
      <div className="picker-mask">
        <div className="picker-wrap">
          <div className="picker-title">
            <div className="picker-cancel" onClick={this.handleClosed}>取消</div>
            <div className="picker-sure" onClick={this.handleSure}>确定</div>
          </div>
          <div className="picker-content-wrap">
            {this.renderPickerCol(data, format, isLink, link)}
            <div className="picker-up-shadow"></div>
            <div className="picker-line"></div>
            <div className="picker-down-shadow"></div>
          </div>
        </div>
      </div>
    );
  }
}

ul li{ list-style: none; }

.picker-mask{ position: fixed; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.5); top: 0; left: 0; z-index: 1;

.picker-wrap {
    width: 100vw;
    height: 250px;
    overflow: hidden;
    position: absolute;
    bottom: 0;
    left: 0;
    background: #fff;
    touch-action: none;
    color: #333;
    border-radius: 0.32rem 0.32rem 0 0;
    display: flex;
    flex-direction: column;

    .picker-title {
        display: flex;
        justify-content: space-between;
        padding: 0 .533rem;
        height: 50px;
        line-height: 50px;
        box-sizing: border-box;
        position: relative;
        border-bottom: 1px solid rgb(#526279, 0.1);
    }
    .picker-sure {
        color: #4c97ff;
    }
}

.picker-content-wrap {
    position: relative;
    width: 100vw;
    background: #fff;
    display: flex;
    overflow: hidden;

    .picker-content {
        position: relative;
        flex: 1;
        transition: 0.03s ease-out 0s;
        -webkit-transition: -webkit-transform 0.03s ease-out 0s;
        transition: transform 0.2s ease-out;
        z-index: 2;

        .picker-list {
            height: 40px;
            line-height: 40px;
            position: relative;
            text-align: center;
            text-overflow: ellipsis;
            overflow: hidden;
            z-index: 2;
        }
    }

    .picker-up-shadow {
        .mask();
        top:0;
        height: 80px;
        background-image: linear-gradient(to bottom, #fff, rgba(255, 255, 255, 0.5));
        z-index: 50;
    }

    .picker-down-shadow {
        .mask();
        background-image: linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5));
        z-index: 50;
        top: 120px;
        height: 80px;
    }

    .picker-line {
        width: 95vw;
        left: 50%;
        box-sizing: border-box;
        transform: translate3d(-50%, 0, 0);
        -webkit-transform: translate3d(-50%, 0, 0);
        position: absolute;
        pointer-events: none;
        border-top: 1px solid #ccc;
        border-bottom: 1px solid #ccc;
        height: 40px;
        top: 80px;
    }
}

}


![image](https://user-images.githubusercontent.com/55273635/87178912-7c44d400-c310-11ea-8a4f-d08b764e873d.png)
yingwinwin commented 4 years ago

报错问题

import React, { Component } from "react";
import { array, bool, func } from "prop-types";
import "../css/picker.css";

export default class Picker extends Component {
  static propTypes = {
    data:array.isRequired,
    format: array,
    isLink: bool,
    initValue: array,
    handleClosed: func, 
    handleSure: func
  };

  static defaultProps = {
    data: [],
    format: [2, 2],
    isLink: false,
    initValue: []
  };

  constructor(props) {
    super();
    this.state = {
      translateY: props.data.map((item) => 0),
      link: props.data.map((item) => 0),
    };
    this.liHeight = 40;
    this.checkProps(props);
    this.select = props.initValue.length > 0 && props.initValue[0].key !== '' ? props.initValue : this.defaultInitValue(props) 
  }

  componentDidMount() {
    const { data, initValue } = this.props;
    if(!(initValue.length > 0 && data.length > 0)) return; // 如果父组件没有穿初始值就不设置初始值
    for(let i = 0; i < data.length; i++) {
      if(initValue[i].key === '' || initValue[i].value === '') return  // 如果父组件没有穿初始值就不设置初始值
    }
    this.setInitValue();   // 如果设置了,就设置初始值
  }

  /* 检查传过来的内容是否正确 */
  checkProps = (props) => {
    /* 如果data,没有长度就return */
    if(!(props.data.length > 0)) return console.error('请传入数据!');
    /* 如果data的【0】不是数组,或data不是数组 */
    if(!Array.isArray(props.data[0]) || !Array.isArray(props.data)) {
      console.error('数据结构错误!');
      return;
    } else {
      if( props.isLink) {
        for(let i = 1; i < props.data.length; i++) {
          if(Object.prototype.toString.call(props.data[i]) !== "[object Object]") return console.error('数据结构错误!');
        } 
      } else {
        for( let i = 1; i < props.data.length; i++) {
          if(!Array.isArray(props.data[i])) return console.error('数据结构错误!');
        }
      }
    }

    if (!(props.initValue.length > 0 && props.data.length > 0)) return;

    if(!Array.isArray(props.initValue)) {
      console.error('初始值结构错误!');
      return;
    } else {
      for(let i = 0; i < props.initValue.length; i++) {
        if(Object.prototype.toString.call(props.initValue[i]) !== "[object Object]") return console.error('初始值结构错误!');
      }
    }
  }

  /* 默认初始值,没有给初始值,默认数组的第一项 */
  defaultInitValue = (props) => {
    const { data, initValue, isLink } = props;
    if(!initValue.length > 0) {
      let initValue = [];
      for(let i = 0; i < data.length; i ++) {
        if(isLink && i === 0) {
          initValue[i] = data[i][0];   // 联动
        } else {
          initValue[i] = data[i][initValue[i - 1].key][0];  // 非联动
        }
      }
    }
    return initValue;
  }

  /* 设置初始值 */
  setInitValue = () => {
    const { data, initValue, isLink } = this.props;

    let link = [...this.state.link];
    let translateY = [...this.state.translateY];
    // 在data里面把用户传过来的值找到,转换为初始值,translateY的高度和数组的下标随之改变
    for(let out = 0; out < data.length; out ++) {
      if( out === 0 || !isLink) {
        for(let i = 0; i < data[out].length; i ++) {
          if(data[out][i].key  === initValue[out].key) {
            link[out] = i;
            translateY[out] = -i * this.liHeight;
          }
        }
      } else {
        if(data[out][initValue[out - 1].key]){
          for(let i = 0; i < data[out][initValue[out - 1].key].length; i++) {
            if(data[out][initValue[out - 1].key][i].key === initValue[out].key) {
              link[out] = i;
              translateY[out] = -i * this.liHeight;
            }
          }
        } else {
          alert('数据配置错误')
        }
      }
    }
    this.setState({
      link,
      translateY
    });
  }

  /* 获取数据 */
  getSelect = () => {
    const { data, isLink } = this.props;
    const { link } = this.state;
    let selecArr = [];
    selecArr[0] = data[0][link[0]];
    for(let i = 1; i < data.length; i ++) {
      if(isLink) {
        selecArr.push(data[i][selecArr[i - 1].key][link[i]]);
      } else if (!isLink){
        selecArr.push(data[i][link[i]]);
      }
    }
    return selecArr;
  }

  // 设置联动
  setLink = (idx) => {
    let length = this.props.data.length;
    let translateY = [...this.state.translateY];
    let link = [...this.state.link];
    translateY[idx] = parseInt(this.state.translateY[idx]);  // 当前滚动的距离
    link[idx] = parseInt(Math.abs(this.state.translateY[idx])) / this.liHeight;  // 当前滚动的数组下标
    // 如果是联动的话
    if (this.props.isLink) {
      for (let i = 1; i < length - idx; i++) {
        translateY[length - i] = 0;
        link[length - i] = 0;
      }
    }
    this.setState({
      translateY,
      link,
    });
  };

  //普通滚动
  handleStart = (event) => {
    event.preventDefault();
    this.startFinger = event.changedTouches[0].clientY;
    this.temp = [...this.state.translateY];
  };

  // 处理移动手势
  handleMove = (event, idx) => {
    event.preventDefault();
    let nowFinger = event.changedTouches[0].clientY;
    let distance = nowFinger - this.startFinger;
    let tempTramslateY = [...this.state.translateY].map((item, index) => {
      if (idx === index) {
        item = this.temp[idx] + distance;   // 点击到的距离 + 滑动的距离 = 移动的距离
      }
      return item;
    });
    this.setState({
      translateY: tempTramslateY,
    });
  };

  /* 处理结束时滚动 */
  handleEnd = (event, idx, len) => {
    // 阻止冒泡
    event.preventDefault();
    let tempTranslateY = [...this.state.translateY].map((item, index) => {
      // 如果是当前滑动的这一项
      if (idx === index) {
        item = item > 0 ? 0 : item;  // 如果小于 0 , 就等于 0 否则就是它移动的距离
        item =
          item < (-len + 1) * this.liHeight ? (-len + 1) * this.liHeight : item; // 如果小于最大高度就是当前的距离,否则就是最大高度

        let sub = item % this.liHeight;   // 如果当前的距离 膜 高度

        // 如果小于高度的一半就是当前高度 - 膜出来的距离, 否则就加上
        if (sub < this.liHeight / 2) {
          item = item - sub;
        } else {
          item = item + (this.liHeight - sub);
        }
      }
      return item;
    });
    this.setState(
      {
        translateY: tempTranslateY,
      },
      () => {
        this.setLink(idx);
      }
    );
  };

  // 处理数据结构
  handleFormat = (format) => {
    let formatArr = [];
    while (format--) {
      formatArr.push(<li key={format} className="picker-list"></li>);
    }
    return formatArr;
  };

  /* 渲染picker组件的列 */
  renderPickerCol = (data, format, isLink, link) => {
    if (!data.length) return;
    const { translateY } = this.state;
    let arr = [];
    arr[0] = data[0];
    let selectArr = [];
    selectArr[0] = data[0][link[0]];
    for (let i = 1; i < data.length; i++) {
      // 如果是联动的话
      if (isLink) {
        if (data[i][selectArr[i - 1].key]) {
          selectArr.push(data[i][selectArr[i - 1].key][link[i]]);
          arr.push(data[i][arr[i - 1][link[i - 1]].key]);
        } else {
          alert("数据配置错误!");
          return;
        }
        // 不联动的话
      } else if (!isLink) {
        selectArr.push(data[i][link[i]]);
        arr.push(data[i]);
      }
    }
    this.select = selectArr
    return arr.map((item, idx) => (
      <ul
        className="picker-content"
        key={idx}
        onTouchStart={this.handleStart}
        onTouchMove={(e) => this.handleMove(e, idx)}
        onTouchEnd={(e) => this.handleEnd(e, idx, item.length)}
        style={{ transform: `translate3d(0, ${translateY[idx]}px, 0)` }}
      >
        {this.handleFormat(format[0])}
        {this.renderPickerList(item)}
        {this.handleFormat(format[1])}
      </ul>
    ));
  };

  // 渲染picker组件的每一项
  renderPickerList = (item) => {
    return item.map((ele) => (
      <li className="picker-list" key={ele.key}>
        {ele.value}
      </li>
    ));
  };

  handleClosed = () => {
    this.props.handleClosed()
  }

  handleSure = (e) => {
    e.stopPropagation();
    this.props.handleSure(this.getSelect());
  }

  render() {
    const { data, format, isLink } = this.props;
    const { link } = this.state;
    return (
      <div className="picker-mask">
        <div className="picker-wrap">
          <div className="picker-title">
            <div className="picker-cancel" onClick={this.handleClosed}>取消</div>
            <div className="picker-sure" onClick={this.handleSure}>确定</div>
          </div>
          <div className="picker-content-wrap">
            {this.renderPickerCol(data, format, isLink, link)}
            <div className="picker-up-shadow"></div>
            <div className="picker-line"></div>
            <div className="picker-down-shadow"></div>
          </div>
        </div>
      </div>
    );
  }
}