city-picker/index.js

/**
 * @component CityPicker
 * @description
 *
 * ## 城市联动选择组件 / CityPicker组件
 *
 * ### 简述
 *
 * 这是一个二级/三级联动进行城市选择的组件, 数据获取可修改fetchData函数进行, 可以是本地数据也可以是网络数据.
 *
 * ### fetchData函数
 *
 * fetchData函数用于根据code获取picker单列的数据, 这部分由业务确定, 有些情况需要使用本地数据, 有时是网络数据. 函数参考下面的用法, 需要在返回的数组中包含的对象格式是:
 *
 * 名称 / Name | 类型 / Type | 描述 / Description
 * ----------|-----------|------------------
 * text      | string    | 显示的文本
 * value     | *         | 对显示文本的值
 * disabled  | boolean   | 是否禁用
 *
 * ### 如何引入
 * ```
 * import { CityPicker } from 'vimo'
 * ```
 *
 * @props {Array} selectedCity - 默认选中的值, 这个也对应组件是两级还是三级的标志, 可以是数据: ['',''] ['','',''], 默认显示北京
 * @props {String/Number} [startCode='1'] - 获取省份数据的code值, 默认是 中国 1
 * @props {Function} [onCancel] - 点击取消的操作
 * @props {Function} [onSelect] - 点击确认的操作
 * @props {Function} fetchData - 获取城市数据的来源, 这个funtion传入code返回promise格式的数据, 其中需要返回的数据格式如下
 *
 * @demo #/city-picker
 * @usage
 * function openCityPicker () {
 *    CityPicker.present({
 *      onSelect (data) {
 *        console.log('onSelect')
 *        console.log(data)
 *      },
 *      onCancel () {
 *        console.log('onCancel')
 *      },
 *      startCode: '1',
 *      selectedCity: ['', ''],
 *      fetchData (code) {
 *        return new Promise((resolve, reject) => {
 *          if (code) {
 *            axios(`static/address-data/${code}.json`)
 *            .then((response) => {
 *              response.data.forEach((item) => {
 *                item.text = item.divisionName
 *                item.value = item.divisionCode
 *                item.disabled = false
 *              })
 *              resolve(response.data)
 *            })
 *            .catch(() => {
 *              resolve([])
 *              console.error('无法获取数据')
 *            })
 *          } else {
 *            resolve([])
 *            console.error('没有查询的code值')
 *          }
 *        })
 *      }
 *    })
 *  }
 *
 */
import { isArray } from '../../util/type'
import Picker from '../picker'

const CityPicker = {
  present (options) {
    let startCode = options.startCode || '1' // 全国  1
    let selectedCity = options.selectedCity
    // null number/string/any [] [''] ['',''] ['1','2','3','4']
    if (
      !selectedCity ||
      !isArray(selectedCity) ||
      (isArray(selectedCity) &&
        (selectedCity.length === 0 || selectedCity.length > 3))
    ) {
      // 非法的情况, 默认是双列 北京 北京
      selectedCity = ['110000', '110100']
    }

    if (isArray(selectedCity)) {
      if (selectedCity.length === 2 && !selectedCity[0]) {
        selectedCity = ['110000', '110100']
      }
      if (selectedCity.length === 3 && (!selectedCity[0] || !selectedCity[1])) {
        selectedCity = ['110000', '110100', '110101']
      }
    }

    let provinceCode = selectedCity[0] // 默认北京 110000, 也就是默认最少显示两级
    let cityCode = selectedCity[1] // 北京 110100 这个自己填入
    let districtCode = selectedCity[2]
    let defaultFetchData = function () {
      return new Promise(resolve => resolve([]))
    }
    let fetchData = options.fetchData || defaultFetchData
    let columns = []
    fetchData(startCode).then(data => {
      if (data && isArray(data) && data.length > 0) {
        // 获得 province 的数据
        columns = [
          {
            name: 'province',
            selectedIndex: getCodeIndex(provinceCode, data),
            align: 'right',
            options: data
          }
        ]
        console.assert(provinceCode, 'provinceCode数据为空, 请检查代码!')
        fetchData(provinceCode).then(data => {
          // 获得 city 的数据
          columns.push({
            name: 'city',
            selectedIndex: getCodeIndex(cityCode, data),
            align: 'left',
            options: data
          })

          if (districtCode) {
            fetchData(cityCode).then(data => {
              // 获得 district 的数据
              columns.push({
                name: 'district',
                selectedIndex: getCodeIndex(districtCode, data),
                align: 'left',
                options: data
              })

              initPicker(columns)
            })
          } else {
            initPicker(columns)
          }
        })
      } else {
        console.error('CityPicker无法获取省份数据, 请检查代码')
      }
    })

    /**
     * 根据code从data中找他的位置
     * @private
     * */
    function getCodeIndex (code = '', data) {
      code = code.toString()
      if (code) {
        for (let i = 0, len = data.length; len > i; i++) {
          let item = data[i]
          if (item.value && item.value.toString() === code) {
            return i
          }
        }
      }
      return 0
    }

    /**
     * 初始化Picker
     * @private
     * */
    function initPicker (columns) {
      if (columns.length > 2) {
        columns.forEach(column => {
          column.optionsWidth = '80px'
        })
      }

      let data = {
        cssClass: 'small-text',
        isH5: true,
        buttons: [
          {
            role: 'cancel',
            text: '取消',
            handler () {
              'use strict'
              options.onCancel && options.onCancel()
            }
          },
          {
            text: '确定',
            handler: data => {
              options.onSelect && options.onSelect(data)
            }
          }
        ],
        columns,
        onSelect: onSelectHandler
      }

      Picker.present(data)
    }

    /**
     * Picker选择时触发
     * @private
     * */
    function onSelectHandler (data) {
      if (data.columnIndex === 0) {
        fetchData(data.value).then(data => {
          columns[1].options = data
          Picker.resetColumn(1)
          if (columns[1].options[0] && selectedCity.length > 2) {
            fetchData(columns[1].options[0].value).then(data => {
              columns[2].options = data
              Picker.resetColumn(2)
            })
          }
        })
      }
      if (data.columnIndex === 1 && selectedCity.length > 2) {
        fetchData(data.value).then(data => {
          columns[2].options = data
          Picker.resetColumn(2)
        })
      }
    }
  }
}
export default CityPicker