单选 联动

Posted 叶为花而生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单选 联动相关的知识,希望对你有一定的参考价值。

<template>
  <div class="cascader-wrapper">
    <crec-popover
      placement="bottomLeft"
      trigger="click"
      :popper-class="popOverClass"
      v-model="showPopover"
    >
      <!-- <div slot="reference"> -->
      <!-- multiple
        mode="tags" -->
      <crec-select
        v-model="selectedLabels"
        :default-value="defaultLabels"
        :placeholder="placeholder"
        :disabled="disabled"
        :size="size"
        :showArrow="true"
        style="width: 100%;"
        popper-class="hide-popper"
        :open="false"
        @focus="handleFocus"
        @dropdown-visible-change="visibleChange"
      ></crec-select>
      <!-- </div> -->
      <template slot="content">
        <div
          class="cascader-menu-wrapper"
          v-clickoutside="hidePopover"
        >
          <template v-if="options.length > 0">
            <ul
              class="crec-cascader-menu cascader-menu"
              :style="{\'width\': panelWidth === \'auto\' ? \'auto\' : panelWidth + \'px\'}"
              v-for="(cas, index) in casTree"
              :key="index"
            >
              <!--  -->
              <!-- \'can-load-children\': !item.isLeaf && !item[childrenKey] && allowLoadChildren && showLoadingIndicator, -->
              <!-- \'loading-children\': !item.isLeaf && item.loading && allowLoadChildren && showLoadingIndicator, -->
              <!-- \'crec-cascader-menu-item-expand\': item[childrenKey] && item[childrenKey].length > 0, -->
              <li
                :class="{
                  \'crec-cascader-menu-item\': true,
                  \'crec-cascader-menu-item-expand\': true,
                  \'has-checked-child\': item.indeterminate || item.hasCheckedChild,
                  \'crec-cascader-menu-item-active\': item.checked,
                }"
                @click="spreadNext(item[childrenKey], index, item)"
                v-for="(item, itemIdx) in cas"
                :key="itemIdx"
                :title="item[labelKey]"
              >
                <crec-checkbox
                  class="cascader-checkbox"
                  @click.native.stop
                  :disabled="item.disabled"
                  v-model="item.checked"
                  :indeterminate="item.indeterminate"
                  @change="({target}) => { checkedChange(item, target.checked) }"
                ></crec-checkbox>
                <span>{{ item[labelKey] }}</span>
                <span
                  v-if="item[childrenKey] && item[childrenKey].length > 0"
                  class="crec-cascader-menu-item-expand-icon"
                >
                  <crec-icon type="right"></crec-icon>
                </span>
              </li>
            </ul>
          </template>
          <template v-else>
            <ul class="crec-cascader-menu cascader-menu">
              <li class="crec-cascader-menu-item dropdown__empty">
                {{ noDataText }}
              </li>
            </ul>
          </template>
        </div>
      </template>

    </crec-popover>
  </div>
</template>

<script>
import Clickoutside from \'./clickoutside\'
import { props, hasArrayChild, deepClone, getId, fireEvent, isPromise } from \'./utils\'
export default {
  name: \'TagSelect\',
  props,
  watch: {
    selectedLabels: {
      deep: true,
      handler () {
        console.log(this.selectedLabels)
        if (this.selectedLabels === \'\') {
          this.selectedLabels = this.defaultLabels
        }
      }
    },
    options: {
      deep: true,
      handler () {
        this.initOpts()
        this.initDatas()
      }
    },
    defaultOptions: {
      deep: true,
      handler () {
        console.log(this.defaultOptions)
        this.defaultLabels = this.defaultOptions.map(e => e.organId)[0]
      }
    },
    value: {
      deep: true,
      handler () {
        console.log(this.selectedValues, this.value)
        if (this.selectedValues !== this.value) {
          this.initOpts()
          this.initDatas()
        }
      }
    },
    disabled (disabled) {
      if (disabled) {
        this.hidePopover()
      }
    },
    showPopover (flag) {
      if (flag) {
        console.log(this.value)
        this.$emit(\'focus\')
      }
    }
  },
  directives: { Clickoutside },
  created () {
    this.classRef = `popper-class-${getId()}`
    this.popOverClass = `cascader-popper ${this.classRef} ${this.popperClass}`
    this.initOpts()
    this.initDatas()
  },
  mounted () {
    // 设置弹出层宽度
    this.elWidth = this.$el.offsetWidth
  },
  destroyed () {
    this.clonedOpts = null
    this.casTree = null
    this.selectedItems = null
    this.selectedLabels = null
    this.selectedvalues = null
  },
  data () {
    return {
      elWidth: \'\',
      popperWidth: \'\',
      popOverClass: \'\',
      classRef: \'\',
      showPopover: false,
      clonedOpts: [],
      casTree: [],
      selectedItems: {},
      selectedLabels: \'\',
      selectedValues: \'\',
      loadChildrenPromise: null,
      defaultLabels: \'\'
    }
  },
  methods: {
    initOpts () {
      console.log(this.defaultOptions)
      this.defaultLabels = this.defaultOptions.map(e => e.organId)[0]
      console.log(this.defaultLabels)
      this.clonedOpts = deepClone(this.options)
      this.casTree = [this.clonedOpts]
      console.log(this.casTree)
    },
    /**
     * 初始化数据
     * 空值初始化,两个绑定不一致的情况
     */
    initDatas () {
      this.pickCheckedItem(this.clonedOpts)
    },

    /**
     * 根据当前节点 checked
     * 更改所有子孙节点 checked
     * 依赖 this.selectChildren
     */
    markChildrenChecked (node) {
      const vm = this
      function loop (children, status) {
        if (children) {
          children.map(child => {
            if (!child.disabled) {
              child.checked = status
              if (child.checked) {
                child.indeterminate = false
              }
            }
            if (hasArrayChild(child, vm.childrenKey)) {
              loop(child[vm.childrenKey], status)
            }
          })
        }
      }
      if (node && hasArrayChild(node, vm.childrenKey) && this.selectChildren) {
        loop(node[vm.childrenKey], node.checked)
      }
    },
    /**
     * 标记父节点 checked、indeterminate 状态
     * 依赖 this.selectChildren
     */
    markParentChecked (node) {
      const vm = this
      node.indeterminate = false
      function loop (node) {
        let checkCount = 0
        if (hasArrayChild(node, vm.childrenKey)) {
          const childIndeterminate = node[vm.childrenKey].some(child => child.indeterminate)
          node[vm.childrenKey].map(child => {
            if (child.checked) {
              checkCount++
            }
          })
          // 子节点全部被选中
          if (checkCount === node[vm.childrenKey].length) {
            node.checked = true
            node.indeterminate = false
          } else {
            node.checked = false
            if (checkCount > 0 || childIndeterminate) {
              node.indeterminate = true
            } else {
              node.indeterminate = false
            }
          }
        }
        if (node.parent) {
          loop(node.parent)
        }
      }
      if (node && node.parent && this.selectChildren) {
        loop(node.parent)
      }
    },
    /**
     * 标记是否有被选子项
     * 依赖 this.selectChildren
     */
    markParentHasCheckChild (node) {
      const vm = this
      node.hasCheckedChild = false
      function loop (node) {
        let checkCount = 0
        if (hasArrayChild(node, vm.childrenKey)) {
          const childHasCheckedChild = node[vm.childrenKey].some(child => child.hasCheckedChild)
          node[vm.childrenKey].map(child => {
            if (child.checked) {
              checkCount++
            }
          })
          // 子节点有被选中
          node.hasCheckedChild = (checkCount > 0) || childHasCheckedChild
        }
        if (node.parent) {
          loop(node.parent)
        }
      }
      if (node && node.parent && !this.selectChildren) {
        loop(node.parent)
      }
    },
    // 展示标签所有层级
    getLevel (node, key, leveled) {
      const levels = []
      function loop (data) {
        levels.push(data[key])
        if (data.parent) {
          loop(data.parent)
        }
      }
      if (leveled) {
        loop(node)
        return levels.reverse().join(this.separator)
      } else {
        return node[key]
      }
    },
    /**
     * 处理已选中
     * 重新遍历tree,pick除已选中项目
     */
    pickCheckedItem (tree) {
      const vm = this
      /**
       * 移除parent引用
       */
      function removeParent (node) {
        const obj = {}
        Object.keys(node).forEach(key => {
          if (key !== \'parent\') {
            obj[key] = node[key]
          }
        })
        if (hasArrayChild(obj, vm.childrenKey)) {
          obj[vm.childrenKey] = obj[vm.childrenKey].map(child => {
            return removeParent(child)
          })
        }
        return obj
      }
      vm.selectedItems = {}
      vm.selectedLabels = \'\'
      vm.selectedValues = \'\'
      function loop (data) {
        if (Array.isArray(data)) {
          data.map(item => {
            if (item.checked) {
              const newItem = removeParent(item)
              vm.selectedItems = (newItem)
              vm.selectedLabels = vm.getLevel(item, vm.labelKey, vm.showAllLevels)
              vm.selectedValues = vm.getLevel(item, vm.valueKey, vm.outputLevelValue)
            }
            if (hasArrayChild(item, vm.childrenKey)) {
              loop(item[vm.childrenKey])
            }
          })
        }
      }
      loop(tree)
    },
    clearTag () {
      const vm = this
      function loop (nodeArr) {
        nodeArr.forEach(node => {
          node.checked = false
          node.indeterminate = false
          if (hasArrayChild(node, vm.childrenKey)) {
            loop(node[vm.childrenKey])
          }
        })
      }
      // 关闭全部状态
      loop(this.clonedOpts)
      this.selectedLabels = \'\'
      this.selectedValues = \'\'
      this.selectedItems = {}
      this.$emit(\'clear\')
      this.syncData()
    },
    // 菜单选中变化
    checkedChange (item, checked) {
      this.defaultLabels = \'\'
      this.clearTag()
      console.log(item, checked)
      item.checked = checked
      this.$emit(\'clickItem\', item)
      this.markChildrenChecked(item)
      this.markParentChecked(item)
      this.markParentHasCheckChild(item)
      this.pickCheckedItem(this.clonedOpts)
      this.refresPopover()
      this.syncData()
    },
    // 同步数据到上层
    syncData () {
      console.log(this.selectedValues)
      this.$emit(\'input\', this.selectedValues)
      this.$emit(\'change\', this.selectedValues, this.selectedItems)
    },
    // 展开下一级
    async spreadNext (children, index, item) {
      const vm = this
      if (
        vm.allowLoadChildren &&
        !children && !item[vm.childrenKey] &&
        vm.loadChildrenMethod &&
        vm.loadChildrenMethod.constructor === Function &&
        !vm.loadChildrenPromise && // promise 不存在
        !item.isLeaf
      ) {
        const isPromiseMethod = this.loadChildrenMethod(item)
        if (isPromise(isPromiseMethod)) {
          vm.loadChildrenPromise = isPromiseMethod
          this.$set(item, \'loading\', true)
          const result = await vm.loadChildrenPromise.catch(e => {
            this.$set(item, \'loading\', false)
          })
          this.$set(item, \'loading\', false)
          vm.loadChildrenPromise = null
          if (result && result.constructor === Array) {
            this.$set(item, vm.childrenKey, result)
            children = result
            this.initDatas()
          } else {
            console.warn(\'The resolved value by loadChildrenMethod must be an Option Array !\')
          }
        } else {
          console.warn(\'You must return a Promise instance in loadChildrenMethod !\')
        }
      }
      if (index || index === 0) {
        if (vm.casTree.indexOf(children) === -1) {
          if (children && children.length > 0) {
            vm.casTree.splice(index + 1, vm.casTree.length - 1, children)
          } else {
            vm.casTree.splice(index + 1, vm.casTree.length - 1)
          }
          vm.$emit(\'spread\', item)
        }
      }
    },
    visibleChange (visible) {
      if (visible) {
        this.showPopover = true
      }
    },
    handleFocus (evt) {
      if (this.disabled) return
      this.$emit(\'focus\', evt)
    },
    hidePopover (evt) {
      this.showPopover = false
      this.$emit(\'blur\', evt)
    },
    refresPopover () {
      setTimeout(() => {
        fireEvent(window, \'resize\')
      }, 66)
    }
  }
}
</script>

<style lang="less">
.hide-popper {
  display: none;
}
.cascader-popper {
  padding: 0px;
}
.crec-popover-inner-content {
  padding: 0;
}
.cascader-menu-wrapper {
  white-space: nowrap;
  .crec-cascader-menu {
    padding: 8px 0;
    height: 240px;
  }
}
.cascader-menu-wrapper .cascader-checkbox {
  margin-right: 10px;
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  user-select: none;
}
.crec-cascader-menu-item.has-checked-child {
  background-color: #f5f7fa !important;
}

.dropdown__empty {
  height: 100%;
  padding-top: 50%;
  margin: 0;
  text-align: center;
  color: #999;
  font-size: 14px;
}
.can-load-children {
  position: relative;
}
.can-load-children::after {
  content: \'\';
  display: inline-block;
  position: absolute;
  width: 5px;
  height: 5px;
  background: #a5d279;
  right: 20px;
  top: 50%;
  border-radius: 50%;
  transform: translateY(-50%);
  -webkit-transform: translateY(-50%);
}
.can-load-children.loading-children::after {
  animation: loading 0.22s infinite alternate;
  -moz-animation: loading 0.22s infinite alternate; /* Firefox */
  -webkit-animation: loading 0.22s infinite alternate; /* Safari 和 Chrome */
  -o-animation: loading 0.22s infinite alternate; /* Opera */
}
@keyframes loading {
  from {
    background: #a5d279;
  }
  to {
    background: #334d19;
  }
}
</style>

以上是关于单选 联动的主要内容,如果未能解决你的问题,请参考以下文章

织梦联动筛选单选版-支持手机站使用

jquery小练习 单选多选 二级联动 员工信息的添加与删除

单选按钮以片段形式传递数据

表单联动总结

二级联动asp下拉式列表菜单

用bootstrap_select.min.js,需要支持二级联动且二级为多选