<template>
  <div
    :class="[
      'inputs',
      formInputsDisplay == 'block'
        ? 'ht-form-inputs__block'
        : 'ht-form-inputs__inline',
    ]"
  >
    <input
      v-if="disabled && isValidate && !isMobile && !isSubCellReadOnly"
      v-model="inputVal"
      v-validate="inputValidate"
      type="hidden"
      :name="inputName"
      :data-vv-as="name"
    />
    <template v-if="inputWriteable && permission_sub != 'n' && !isMobile">
      <template v-if="isSubCellReadOnly">
        <input
          v-if="inputWriteable && isSubCellReadOnly"
          v-model="inputVal"
          v-validate="inputValidate"
          type="hidden"
          :name="inputName"
          :data-vv-as="name"
        />
        <div class="sub-table-cell-readonly" @click="handleCellClick">
          <span>
            {{ fieldValue }}
          </span>
        </div>
      </template>
      <template v-else>
        <el-cascader
          :ref="refName"
          v-model="inputVal"
          v-validate="inputValidate"
          :size="inputSize"
          :props="propsVal"
          :name="inputName"
          :data-vv-as="name"
          :validate="validate"
          :placeholder="placeholder"
          :disabled="disabled"
          :clearable="clearable"
          :show-all-levels="showAllLevels"
          :collapse-tags="collapseTags"
          :separator="separator"
          :popper-class="popperClass"
          @change="(node) => $emit('change', node)"
          @expand-change="(parentNodes) => $emit('expand-change', parentNodes)"
          @blur="(event) => $emit('blur', event)"
          @focus="(event) => $emit('focus', event)"
          @visible-change="(show) => $emit('visible-change', show)"
          @remove-tag="(node) => $emit('remove-tag', node)"
        >
          <slot slot="empty" name="empty"></slot>
          <slot slot-scope="{ node, data }" :node="node" :data="data"></slot>
        </el-cascader>
      </template>
    </template>

    <template v-if="inputWriteable && permission_sub != 'n' && isMobile">
      <!-- 校验
      <input
        v-model="value"
        v-validate="inputValidate"
        type="hidden"
        :name="inputName"
      /> -->
      <!-- <van-field
        v-model="fieldValue"
        v-validate="inputValidate"
        :name="inputName"
        is-link
        readonly
        clearable
        label="级联"
        class="mobile-cascader-input"
        :placeholder="mobilePlaceholder('请选择')"
        @click="openCascader"
      /> -->
      <div class="mobile-select__wrap">
        <!-- 校验 -->
        <input
          v-model="value"
          v-validate="validateRules"
          type="hidden"
          :name="inputName"
          :data-vv-as="name"
        />
        <van-cell
          v-validate="!inputVal && validateRules"
          class="mobile-cell"
          :name="inputName"
          :data-vv-as="name"
          :is-link="!(readonly || disabled)"
          @click="openCascader"
        >
          <template #default>
            <div class="flex">
              <span v-if="fieldValue" class="cell-text">
                {{ fieldValue }}
              </span>
              <span v-if="!fieldValue" class="placeholder-class">
                {{ mobilePlaceholder('请选择') }}
              </span>
              <van-icon
                v-if="fieldValue && clearable && !disabled"
                name="clear"
                color="#bfbfbf"
                @click.stop="clearSelect"
              />
            </div>
          </template>
        </van-cell>
      </div>
      <van-popup v-model="isShowCascader" round position="bottom">
        <van-cascader
          v-model="cascaderValue"
          title="级联选择"
          :options="options"
          active-color="#409EFF"
          :field-names="fieldNames"
          @close="isShowCascader = false"
          @change="onChange"
          @finish="onFinish"
        />
      </van-popup>
    </template>
    <ht-field-tail
      v-if="permission != 'n'"
      :field-name="inputName"
      :readonly="!inputWriteable"
      :input-value="inputVal"
    ></ht-field-tail>
  </div>
</template>

<script>
  import utils from '@/utils.js'
  import permission from '@/mixins/permission.js'
  import inputName from '@/mixins/inputName.js'
  import form from '@/mixins/form.js'
  import ganged from '@/mixins/ganged.js'
  import mobileMode from '@/mixins/mobileMode.js'
  import popupMode from '@/mixins/popupMode.js'
  import tableFieldReadOnly from '@/mixins/tableFieldReadOnly.js'

  export default {
    name: 'HtCascader',
    mixins: [
      permission,
      inputName,
      form,
      ganged,
      mobileMode,
      popupMode('isShowCascader'),
      tableFieldReadOnly,
    ],
    props: {
      value: String,
      disabled: {
        type: Boolean,
        default: false,
      },
      clearable: {
        type: Boolean,
        default: true,
      },
      showAllLevels: {
        type: Boolean,
        default: true,
      },
      collapseTags: {
        type: Boolean,
        default: false,
      },
      separator: {
        type: String,
        default: '>',
      },
      popperClass: {
        type: String,
      },
      isValidate: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      let _this = this
      return {
        refName: `cascader-${utils.getName()}`,
        lazyLoadResolve: null,
        propsVal: {
          value: _this.ganged.valueBind,
          label: _this.ganged.labelBind,
          lazy: true,
          lazyLoad(node, resolve) {
            _this.dynamicLoadOption(node, resolve)
          },
        },
        isShowCascader: false,
        options: [],
        cascaderValue: '',
        fieldNames: {
          text: _this.ganged.labelBind,
          value: _this.ganged.valueBind,
          children: 'children',
        },
        fieldValue: '',
      }
    },
    computed: {
      inputVal: {
        get() {
          if (this.value) {
            if (this.value.constructor == String) {
              if (!this.value) {
                return ''
              }
              let cascaderModel = utils.isJSON(this.value)
                ? JSON.parse(this.value)
                : this.value
              if (!this.inputWriteable) {
                return cascaderModel.pathLabels.join('>')
              }
              return typeof cascaderModel === 'string'
                ? cascaderModel
                : cascaderModel.path
            }
            return this.value.path
          } else {
            return this.value
          }
        },
        set() {
          let selectValArr = this.$refs[this.refName].getCheckedNodes(false)
          if (selectValArr.length > 0 && selectValArr[0] !== null) {
            let result = {
              path: selectValArr[0].path,
              pathLabels: selectValArr[0].pathLabels,
            }
            this.$emit('input', JSON.stringify(result))
          } else {
            this.$emit('input', '')
          }
        },
      },
      validateRules() {
        return this.disabled && !this.isValidate ? '' : this.inputValidate
      },
    },
    watch: {
      value: {
        handler(val) {
          if (val) {
            this.fieldValue = utils.isJSON(val)
              ? JSON.parse(val).pathLabels.join('/')
              : val
          } else {
            this.fieldValue = ''
          }
        },
        immediate: true,
      },
    },
    mounted() {
      if (this.isMobile) {
        this.getOptions({ level: 0, loaded: true, loading: false, root: true })
      }
    },
    methods: {
      clearSelect() {
        this.fieldValue = ''
        this.cascaderValue = ''
        this.$emit('input', '')
      },
      getOptions(node) {
        const params = this.prepareLoadParams(this.ganged.bind, node)
        this.doCustomQuery(params).then(({ data }) =>
          this.dynamicOptionResponse(data, this.ganged.leafJudge, node)
        )
      },
      onChange(val) {
        const { selectedOptions, tabIndex } = val
        if (
          selectedOptions[tabIndex].hasOwnProperty('leaf') &&
          selectedOptions[tabIndex].leaf === 0
        ) {
          this.getOptions({
            level: selectedOptions.length,
            data: selectedOptions[tabIndex],
          })
        }
      },
      /**
       * 获取选中的节点
       */
      getCheckedNodes: function (leafOnly) {
        return this.$refs[this.refName].getCheckedNodes(leafOnly)
      },
      // 动态加载选项
      dynamicLoadOption: function (node, resolve) {
        this.lazyLoadResolve = resolve
        setTimeout(() => {
          const params = this.prepareLoadParams(this.ganged.bind, node)
          this.doCustomQuery(params).then(({ data }) =>
            this.dynamicOptionResponse(data, this.ganged.leafJudge, node)
          )
        }, 0)
      },
      // 准备查询参数
      prepareLoadParams: function (condition, node) {
        let params = {}
        if (condition && !utils.isEmpty(condition)) {
          Object.keys(condition).forEach((k) => {
            const bindSet = condition[k]
            let val = null
            // 参数绑定是对象
            if (bindSet.constructor == Object) {
              // 根节点
              if (node.level == 0) {
                val = !utils.isEmpty(bindSet['default'])
                  ? bindSet['default']
                  : null
              } else {
                val = node.data[bindSet['key']]
              }
            }
            // 参数绑定是字符串
            else if (bindSet.constructor == String && node.level > 0) {
              val = node.data[bindSet]
            }
            if (!utils.isEmpty(val)) {
              params[k] = val
            }
          })
        }
        return params
      },
      setOptionsChildren(data, respData, node) {
        return data.map((item) => {
          if (
            item[this.ganged.valueBind] === node.data[this.ganged.valueBind]
          ) {
            const childrenData = respData.map((it) => {
              return {
                ...it,
                children: it.children
                  ? it.children
                  : it.hasOwnProperty('leaf') && it.leaf === 0
                  ? []
                  : null,
              }
            })
            this.$set(item, 'children', childrenData)
          } else if (item.children && item.children.length) {
            this.setOptionsChildren(item.children, respData, node)
          }
          return item
        })
      },
      // 动态加载选项返回值处理
      dynamicOptionResponse: function (data, leafJudge, node) {
        if (this.isMobile && data && data.length) {
          if (node.level > 0) {
            this.options = this.setOptionsChildren(this.options, data, node)
          } else {
            this.options = data.map((item) => {
              const asyncLoadChildren =
                item.hasOwnProperty('leaf') && item.leaf === 0 ? [] : null
              return {
                ...item,
                children: item.children ? item.children : asyncLoadChildren,
              }
            })
          }
        } else {
          if (this.lazyLoadResolve) {
            // 判定是否叶子节点的方法
            if (leafJudge && leafJudge.constructor == Function) {
              data.forEach((opt) => {
                opt.leaf = opt
              })
            }
            this.setLeaf(data)
            if (node.level > 0 && !node.data.hasOwnProperty('leaf')) {
              this.lazyLoadResolve([])
            } else {
              this.lazyLoadResolve(data)
            }
            // 动态加载选项后触发组件视图更新
            this.$refs[this.refName] &&
              this.$refs[this.refName].computePresentContent()
          }
        }
      },
      //递归给级联没有子节点的设置leaf为true
      setLeaf(val) {
        if (!val || !val.map) {
          return []
        }
        let list = val.map((item) => {
          if (item.children) {
            this.setLeaf(item.children)
          }
          if (item && !item.children && item.leaf !== 0) {
            item.leaf = true
          }
          return item
        })
        return list
      },
      onFinish({ selectedOptions }) {
        this.isShowCascader = false
        const pathLabels = selectedOptions.map(
          (option) => option[this.ganged.labelBind]
        )
        const path = selectedOptions.map(
          (option) => option[this.ganged.valueBind]
        )
        this.$emit('input', JSON.stringify({ path, pathLabels }))
      },
      openCascader() {
        if (this.disabled) return
        this.isShowCascader = true
      },
    },
  }
</script>

<style lang="scss" scoped>
  div[aria-invalid='true'] {
    ::v-deep {
      .el-input__inner,
      .el-input__inner:focus {
        border-color: #f56c6c;
      }
    }
  }

  .inputs {
    ::v-deep {
      .el-cascader {
        width: 100%;
      }
    }
  }
  .mobile-cascader-input {
    padding: 0;
    ::v-deep {
      .van-field__label {
        display: none;
      }
      .van-field__control {
        text-align: right;
      }
    }
    &::after {
      border: none;
    }
  }
  .mobile-select__wrap {
    position: relative;
  }
  .flex {
    display: flex;
    align-items: center;
    .van-icon-clear {
      margin-left: 8px;
    }
  }
  .mobile-cell {
    padding: 0;
    display: flex;
    align-items: center;
    .van-cell__value {
      display: flex;
      align-items: center;
      justify-content: flex-end;
      .cell-text {
        color: #262626;
        font-size: 14px;
        text-align: right;
      }
      .cell-close {
        margin-left: 4px;
      }
    }
    .placeholder-class {
      color: #bfbfbf;
      font-size: 14px;
      text-align: right;
    }
  }
</style>
