小程序公司部门选人组件 树状单选多选实现

小程序公司部门选人组件 树状多选实现

最近要做一个公司管理用的小程序,其中审批工作流涉及人员选择,官方的picker不支持多选,无法满足场景使用需求。网上找了一遍,最后找到一篇(@华子_tm)兄弟写的简书文章实现了。贴了代码很详细。

遇到问题

1.复制代码试运行功能基本正常,但是开发工具有个报错,在这里插入图片描述
意思是说cover-view不支持嵌套chekbox,查了官方文档的cover-view(可覆盖的原生组件包括 map、video、canvas、camera、live-player、live-pusher ,只支持嵌套 cover-view、cover-image,可在 cover-view 中使用 button),感觉局限性很大。然后就不想用cover-view了,全部改为view。

2.修改为view之后,就没报错了,但是有个页面中用到textarea,textarea的内容会显示文字至于最顶层, 跟选择人员组件的会重叠文字。网上很多人在说这个,textarea显示到顶层问题无法解决,然后还是乖乖的用回cover-view,把checkbox删掉换成cover-image,自己实现一个类似checkbox的功能就好了。(汗,一开始就这样不好嘛。走弯路了)

增加属性

1.增加了一个单选和多选的属性(mul)。单选的时候选中就会自动关闭选择组件
2.增加了一个禁用属性(pick_disable),等于1的时候不可点击。
3.增加一个选择类型属性(chooseType),暂时未用到

组件代码

picker-multiple.js的 代码片.

Component({
  properties:{
    list: {
      type: Array,
      value :[],
      observer() {
        this.init();
      }
    },
    rootText:{
      type: String,
      value : 'root'
    },
    children:{
      type: String,
      value: 'children'
    },
    selected:{
      type: Array,
      value: []
    },
    mul:{
      type: Boolean,
      value: true 
    },
    dataIndex:{
      type: Number,
      value: 0
    },
    chooseType:{
      type: Number,
      value: 0
    },
    pick_disable:{
      type: Number,
      value: 0 
    },
  },
  data:{
    showPicker: false,
    firstShow: false,
    outlist: [],
    path: [],
    current: {},
    isRoot: true,
    tselected: []
  },
  methods:{
    // 展示picker
        showPicker() {
          console.log(this.properties.selected);
          if(this.properties.pick_disable != 1){
            this.init();
            if (!this.data.firstShow) {
                this.setData({
                    firstShow: true
                })
            }
            this.setData({
                showPicker: true,
            })
          }
    },
    // 隐藏picker
        hidePicker() {
            this.setData({
                showPicker: false
            })
        },
        // 取消按钮事件
        cancal() {
            this.hidePicker();
    },
    // 确定按钮事件
        sure() {
            this.hidePicker();
            this.triggerEvent('click', {
                selected: this.data.tselected,
                dataIndex: this.properties.dataIndex,
                chooseType:this.properties.chooseType
      });
      // this.triggerEvent('click', { value: user });
        },
    init(){
      this.data.tselected = JSON.parse(JSON.stringify(this.properties.selected));
      this.toRoot();
    },
    toRoot(){
      // console.log(this.properties.list)
      this.properties.list.forEach((item)=>{
        item.itemShow='';
        item.itemTrans='';
      })
      this.setData({
        outlist: JSON.parse(JSON.stringify(this.properties.list)),
        current:{},
        isRoot: true,
        path:[]
      })
    },
    intoNextPath(e){
      var caninto = e.currentTarget.dataset.caninto;
      // console.log(e.currentTarget.dataset)
      if(!caninto){
        return
      }
      var item = e.currentTarget.dataset.item;
      var index = e.currentTarget.dataset.index;
      var path = this.data.path;
      path.push(index);
      item = this.initUser(item)
      this.setData({
        path: path,
        current: item,
        outlist: item[this.properties.children]==undefined?[]:item[this.properties.children],
        isRoot: false
      });
      this.scrollTop();
    },
    backLastPath(e){
      var path = this.data.path;
      var floor = path.length;
      if (floor <= 1){
        this.toRoot()
      }else{
        path.pop();
        console.log('back:' + path)
        var list = JSON.parse(JSON.stringify(this.properties.list));
        var item = {};
        path.forEach((index) =>{
          item = list[index];
          list = item[this.properties.children]
        })
        var isRoot = false;
        if (this.data.path.length <= 0){
          isRoot = true;
        }
        // console.log(item)
        item = this.initUser(item);
        this.setData({
          path: path,
          current: item,
          outlist: list,
          isRoot: isRoot
        })
      }
      this.scrollTop();
    },
    openContent(e){
      var index = e.currentTarget.dataset.index;
      var itemShow = this.data.outlist[index].itemShow
      if (itemShow == null || itemShow == ''){
        this.setData({
          ['outlist[' + index + '].itemShow']: 'item-show',
          ['outlist[' + index + '].itemTrans']:'item-trans90'
        })
      }else{
        this.setData({
          ['outlist[' + index + '].itemShow']: '',
          ['outlist[' + index + '].itemTrans']: ''
        })
      }

    },
    checkboxChange(e) {

      var id=e.currentTarget.dataset.id;
      var checked=e.currentTarget.dataset.checked;
      var newChecked=!checked;

      let checkarr =[];
      if(this.properties.mul){
        checkarr=this.data.tselected;
      }
      
      const items = this.data.current;
      var values =[];

      if (newChecked && !this.properties.mul) {
        values.push(id);
      }else{
        for (let i = 0, lenI = items.userlist.length; i < lenI; ++i) {
          if(parseInt(items.userlist[i].id)!=id&&items.userlist[i].checked){
            values.push(items.userlist[i].id);
          }else{
            if(newChecked){
              values.push(id);
            }
          }
        }
      }

      // const values = e.detail.value;

      
      for (let i = 0, lenI = items.userlist.length; i < lenI; ++i) {
        items.userlist[i].checked = false
        if(checkarr.indexOf(items.userlist[i].id) > -1) {
          checkarr.splice(checkarr.indexOf(items.userlist[i].id), 1);
        }
        for (let j = 0, lenJ = values.length; j < lenJ; ++j) {
          if (parseInt(items.userlist[i].id) === parseInt(values[j])) {
            items.userlist[i].checked = true;
            if(!checkarr.includes(values[j])) {
              checkarr.push(values[j]);
            }
            break
          }
        }
      }

      this.setData({
        current: items,
        tselected: checkarr
      })

      if(!this.properties.mul&&checkarr.length!=0){
        this.sure();
      }
    },
    initUser(dept){
      var selected = this.data.tselected;
      var userlist = dept.userlist;
      userlist.forEach((user) => {
        user.checked = false;
        for(let j = 0, lenJ = selected.length; j < lenJ; ++j) {
          if (parseInt(selected[j]) == parseInt(user.id)) {
            user.checked = true;
          }
        }
      })
      dept.userlist = userlist
      return dept
    },
    scrollTop(){
      setTimeout(function () {
        wx.pageScrollTo({
          scrollTop: 0,
          duration: 300
        })
      }.bind(this), 200)
    }
  }
})

picker-multiple.wxml的 代码片.

<view bindtap='showPicker'>
  <slot><text class='showtext'>选择人员</text></slot>
</view>

<cover-view class='{{ showPicker ? ".shade-container" : "hide-container" }}' wx:if='{{ firstShow }}'>
  <cover-view class='left-shade' bindtap='hidePicker'></cover-view>
  <cover-view class='right-choose'>
    <cover-view class='picker-container'>
      <cover-view class="title padder">
        <cover-view wx:if="{{ isRoot }}">
          <cover-view class="padder-l">{{ rootText }}</cover-view>
        </cover-view>
        <cover-view wx:else>
          <cover-view class="icon">
            <cover-image class="image" mode="heightFix" src="/static/images/icon/left-arrow.png" bindtap="backLastPath">
            </cover-image>
          </cover-view>
          <cover-view class="padder-l">{{ current.name }}</cover-view>
        </cover-view>
      </cover-view>
      <cover-view bindchange="checkboxChange">
        <cover-view class="padder-l-md padder-r-md item-group">
          <block wx:for="{{current.userlist}}" wx:key="id">
            <cover-view wx:if="{{index != 0}}" class="line"></cover-view>
            <cover-view data-checked="{{item.checked}}" bindtap="checkboxChange" data-id="{{item.id}}" class="item">
              <cover-view class="checkbox-item">
                <cover-view class="checkbox"></cover-view>
                <cover-image wx:if="{{item.checked}}" class="checked" src="/static/images/icon/checked.png">
                </cover-image>
                <cover-view class="checkbox-label">{{item.name}}</cover-view>
              </cover-view>
            </cover-view>
          </block>

        </cover-view>
        <cover-view wx:for="{{ outlist }}" wx:key="index" class="padder-l-md cell-group">
          <cover-view wx:if="{{index != 0 || current.userlist.length>0}}" class="line"></cover-view>
          <cover-view class="cell {{ (current == null || !current.userlist.length) && index == 0 ? 'cell-group':'' }}"
            bindtap='intoNextPath' data-item="{{item}}"
            data-caninto="{{ item.children.length > 0 || item.userlist.length > 0 }}" data-index="{{ index }}">
            <cover-view class="left_text">{{ item.name }}</cover-view>
            <cover-view class="icon cell_righticon" wx:if="{{ item.children.length > 0 || item.userlist.length > 0 }}">
              <cover-image class="image" mode="heightFix" src="/static/images/icon/right-arrow.png"
                bindtap="backLastPath">
              </cover-image>
            </cover-view>
          </cover-view>
        </cover-view>
      </cover-view>
    </cover-view>
    <cover-view class="line"></cover-view>
    <cover-view class='button-container'>
      <cover-view class='sure' bindtap='sure'>确定</cover-view>
      <cover-view class='cancal' bindtap='cancal'>取消</cover-view>
    </cover-view>
  </cover-view>
</cover-view>

picker-multiple.wxss的 代码片.

.shade-container {
  height: 100%;
  width: 100%;
}

.showPicker {
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  font-size: 30rpx;
  background-color: paleturquoise;
  text-align: center;
}
.showtext{
  font-size: 32rpx;
  color: rgb(32, 154, 224);
}

.shade-container {
  position: fixed;
  height: 100%;
  width: 100%;
  top: 0;
  right: -200%;
  display: flex;
  justify-content: space-around;
  transform: translateX(-200%);
  transition: all 0.5s ease;
  z-index: 9999;
}

.hide-container {
  position: fixed;
  height: 100%;
  width: 100%;
  top: 0;
  right: -200%;
  z-index: 9999;
  display: flex;
  justify-content: space-between;
  transform: translateX(100%);
  transition: all 0.5s ease-in;
}

.left-shade {
  width: 30%;
  background-color: rgba(0, 0, 0, 0.5);
  height: 100%;
}

.right-choose {
  width: 70%;
  height: 100%;
  background-color: #fff;
  padding: 40rpx;
}

.picker-container {
  height: calc(100% - 230rpx);
  overflow-x: hidden;
  overflow-y: scroll;
}

.picker-container::-webkit-scrollbar {
  display: none;
}

.title{
  font-size: 36rpx;
  font-weight: bold;
  border-bottom: 1px solid #dddee1
}
.tran-icon{
  color: #5cadff;
}
.padder{
  padding: 10px;
}
.padder-l{
  height: 50rpx;
  line-height: 50rpx;
  padding-left: 10px; 
  display: inline-block;
}
.padder-l-sm{
  padding-left: 5px; 
}
.padder-l-md{
  padding-left: 15rpx; 
}
.padder-r-md{
  padding-right: 15rpx; 
}
.cell-group{
  border: 0px !important;
  position: relative;
}
.left_icon{
  position: absolute;
  top: 18rpx;
  left: 0;
}
.left_text{
  margin-left: 45rpx;
}
.b-t{
  border-top: 1px solid #e9eaec!important;
}
.cell{
  padding: 20rpx 10rpx;
}
.cell_righticon{
  position: absolute;
  right: 0rpx;
  top: 15rpx;
}
.item-group .item:last-child{
  border: 0px !important;
}
.item{
  padding: 15px 0;
  padding-left: 30rpx;
}
.item-show{
  display: block;
}
.item-trans90{
  transform:rotate(90deg);
  -ms-transform:rotate(90deg); /* Internet Explorer */
  -moz-transform:rotate(90deg); /* Firefox */
  -webkit-transform:rotate(90deg); /* Safari 和 Chrome */
  -o-transform:rotate(90deg); /* Opera */
}

.button-container {
  width: 100%;
  height: 100rpx;
  padding-top: 30rpx;
  display: flex;
  justify-content: space-between;
  font-size: 24rpx;
  text-align: center;
  /* border-top: 2rpx solid #eaeaea; */
}

.cancal {
  width: 150rpx;
  height: 80rpx;
  line-height: 80rpx;
  border: 2rpx solid #ddd;
  border-radius: 8rpx;
}

.sure {
  width: 150rpx;
  height: 80rpx;
  line-height: 80rpx;
  border: 2rpx solid rgb(132, 235, 132);
  border-radius: 8rpx;
}

.icon{
  display: inline-block;
  width: 50rpx;
  height: 50rpx;
}

.image{
  width: 50rpx;
  height: 50rpx;
}

.line{
  height: 1px;
  width: 100%;
  background-color: #eaeaea;
}

.checkbox-item{
  height: 50rpx;
  line-height: 50rpx;
}

.checkbox{
  width: 50rpx;
  height: 50rpx;
  border: 1px solid #eaeaea;
  position: absolute;
}

.checked{
  width: 56rpx;
  height: 56rpx;
}

.checkbox-label{
  height: 50rpx;
  line-height: 50rpx;
  margin-left: 70rpx;
}

wx-cover-image{ position: absolute; width: 100%; height: 100%; }

在这里插入图片描述
在这里插入图片描述
项目代码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值