0
点赞
收藏
分享

微信扫一扫

基于单片机的智能宠物喂食系统设计

圣杰 2024-12-04 阅读 5

支持多选、单选,自由配置label、value属性

一、组件

<!-- 树形层级选择器-->
<!-- 1、支持单选、多选 -->
<template>
  <view>
    <view
      class="tree-cover"
      :class="{ show: showDialog }"
      @tap="_cancel"
    ></view>
    <view class="tree-dialog" :class="{ show: showDialog }">
      <view class="tree-bar">
        <view
          class="tree-bar-cancel"
          :style="{ color: cancelColor }"
          hover-class="hover-c"
          @tap="_cancel"
          >取消
        </view>
        <view class="tree-bar-title" :style="{ color: titleColor }">{{
          title
        }}</view>
        <view
          class="tree-bar-confirm"
          :style="{ color: confirmColor }"
          hover-class="hover-c"
          @tap="_confirm"
        >
          {{ multiple ? "确定" : "" }}
        </view>
      </view>
      <view class="tree-view">
        <scroll-view class="tree-list" :scroll-y="true">
          <!-- <uni-easyinput
            class="uni-mt-5"
            suffixIcon="search"
            v-model="value"
            placeholder="输入系统/门店进行搜索"
            @iconClick="iconClick"
          ></uni-easyinput> -->
          <block v-for="(item, index) in treeList" :key="index">
            <view
              class="tree-item"
              :style="[
                {
                  paddingLeft: item.level * 30 + 'rpx',
                },
              ]"
              :class="{
                itemBorder: border === true,
                show: item.isShow,
              }"
            >
              <view class="item-label">
                <view
                  class="item-icon uni-inline-item"
                  @tap.stop="_onItemSwitch(item, index)"
                >
                  <view
                    v-if="!item.isLastLevel && item.isShowChild"
                    class="switch-on"
                    :style="{ 'border-left-color': switchColor }"
                  >
                  </view>
                  <view
                    v-else-if="!item.isLastLevel && !item.isShowChild"
                    class="switch-off"
                    :style="{ 'border-top-color': switchColor }"
                  >
                  </view>
                  <view
                    v-else
                    class="item-last-dot"
                    :style="{ 'border-top-color': switchColor }"
                  >
                  </view>
                </view>

                <view class="item-flex uni-flex-item uni-inline-item">
                  <view
                    class="item-name"
                    @tap.stop="_onItemSwitch(item, index)"
                  >
                    {{ item[textKey] }}
                  </view>
                  <view
                    class="item-check"
                    v-if="selectParent ? true : item.isLastLevel"
                    @tap.stop="_onItemSelect(item, index)"
                  >
                    <view
                      class="item-check-yes"
                      v-if="item.checkStatus == 1"
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    >
                      <view
                        class="item-check-yes-part"
                        :style="{ 'background-color': confirmColor }"
                      >
                      </view>
                    </view>
                    <view
                      class="item-check-yes"
                      v-else-if="item.checkStatus == 2"
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    >
                      <view
                        class="item-check-yes-all"
                        :style="{ 'background-color': confirmColor }"
                      >
                      </view>
                    </view>
                    <view
                      class="item-check-no"
                      v-else
                      :class="{ radio: !multiple }"
                      :style="{ 'border-color': confirmColor }"
                    ></view>
                  </view>
                </view>
              </view>
            </view>
          </block>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  emits: ["select-change"],
  name: "ba-tree-picker",
  props: {
    valueKey: {
      type: String,
      default: "id",
    },
    textKey: {
      type: String,
      default: "name",
    },
    children: {
      type: String,
      default: "children",
    },
    sourceList: {
      type: Array,
      default: function () {
        return [];
      },
    },
    localTreeList: {
      //在已经格式化好的数据
      type: Array,
      default: function () {
        return [];
      },
    },
    selectedData: {
      type: Array,
      default: function () {
        return [];
      },
    },
    title: {
      type: String,
      default: "",
    },
    multiple: {
      // 是否可以多选
      type: Boolean,
      default: true,
    },
    selectParent: {
      //是否可以选父级
      type: Boolean,
      default: true,
    },
    confirmColor: {
      // 确定按钮颜色
      type: String,
      default: "", // #0055ff
    },
    cancelColor: {
      // 取消按钮颜色
      type: String,
      default: "", // #757575
    },
    titleColor: {
      // 标题颜色
      type: String,
      default: "", //
    },
    switchColor: {
      // 节点切换图标颜色
      type: String,
      default: "", // #666
    },
    border: {
      // 是否有分割线
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      showDialog: false,
      treeList: [],
      value: "",
    };
  },
  computed: {},
  methods: {
    show() {
      this.showDialog = true;
      // 回显数据勾选
    },
    _hide() {
      this.showDialog = false;
      this.value = "";
    },
    _cancel() {
      this._hide();
      this.$emit("cancel", "");
    },
    _confirm() {
      //多选
      let selectedList = []; //如果子集全部选中,只返回父级 id
      let selectedNames;
      let selectedItem;
      let currentLevel = -1;
      this.treeList.forEach((item, index) => {
        if (currentLevel >= 0 && item.level > currentLevel) {
        } else {
          if (item.checkStatus === 2) {
            currentLevel = item.level;
            selectedList.push(item[this.valueKey]);
            selectedNames = selectedNames
              ? selectedNames + " / " + item[this.textKey]
              : item[this.textKey];
            selectedItem = item.node;
          } else {
            currentLevel = -1;
          }
        }
      });
      this._hide();
      this.$emit("select-change", selectedList, selectedNames, selectedItem);
    },
    iconClick() {
      this.$emit("iconClickChange", this.value);
    },
    //格式化原数据(原数据为tree结构)
    _formatTreeData(list = [], level = 0, parentItem, isShowChild = true) {
      let nextIndex = 0;
      let parentId = -1;
      let initCheckStatus = 0;
      if (parentItem) {
        nextIndex =
          this.treeList.findIndex(
            (item) => item[this.valueKey] === parentItem[this.valueKey]
          ) + 1;
        parentId = parentItem[this.valueKey];
        if (!this.multiple) {
          //单选
          initCheckStatus = 0;
        } else initCheckStatus = parentItem.checkStatus == 2 ? 2 : 0;
      }
      list.forEach((item) => {
        let isLastLevel = true;
        if (item && item[this.children]) {
          let children = item[this.children];
          if (Array.isArray(children) && children.length > 0) {
            isLastLevel = false;
          }
        }

        let itemT = {
          [this.valueKey]: item[this.valueKey],
          [this.textKey]: item[this.textKey],
          level,
          isLastLevel,
          isShow: isShowChild,
          isShowChild: false,
          checkStatus: initCheckStatus,
          orCheckStatus: 0,
          parentId,
          children: item[this.children],
          childCount: item[this.children] ? item[this.children].length : 0,
          childCheckCount: 0,
          childCheckPCount: 0,
          node: item,
        };

        if (this.selectedData.indexOf(itemT[this.valueKey]) >= 0) {
          itemT.checkStatus = 2;
          itemT.orCheckStatus = 2;
          itemT.childCheckCount = itemT.children ? itemT.children.length : 0;
          this._onItemParentSelect(itemT, nextIndex);
        }

        this.treeList.splice(nextIndex, 0, itemT);
        nextIndex++;
      });
    },
    // 节点打开、关闭切换
    _onItemSwitch(item, index) {
      if (item.isLastLevel === true) {
        return;
      }
      item.isShowChild = !item.isShowChild;
      if (item.children) {
        this._formatTreeData(item.children, item.level + 1, item);
        item.children = undefined;
      } else {
        this._onItemChildSwitch(item, index);
      }
    },
    _onItemChildSwitch(item, index) {
      const firstChildIndex = index + 1;
      if (firstChildIndex > 0)
        for (var i = firstChildIndex; i < this.treeList.length; i++) {
          let itemChild = this.treeList[i];
          if (itemChild.level > item.level) {
            if (item.isShowChild) {
              if (itemChild.parentId === item[this.valueKey]) {
                itemChild.isShow = item.isShowChild;
                if (!itemChild.isShow) {
                  itemChild.isShowChild = false;
                }
              }
            } else {
              itemChild.isShow = item.isShowChild;
              itemChild.isShowChild = false;
            }
          } else {
            return;
          }
        }
    },
    // 节点选中、取消选中
    _onItemSelect(item, index) {
      if (!this.multiple) {
        //单选
        item.checkStatus = item.checkStatus == 0 ? 2 : 0;

        this.treeList.forEach((v, i) => {
          if (i != index) {
            this.treeList[i].checkStatus = 0;
          } else {
            this.treeList[i].checkStatus = 2;
          }
        });

        let selectedList = [];
        let selectedNames;
        let selectedItem;
        selectedList.push(item[this.valueKey]);
        selectedNames = item[this.textKey];
        selectedItem = item.node;
        this._hide();
        this.$emit("select-change", selectedList, selectedNames, selectedItem);
        return;
      }

      let oldCheckStatus = item.checkStatus;
      switch (oldCheckStatus) {
        case 0:
          item.checkStatus = 2;
          item.childCheckCount = item.childCount;
          item.childCheckPCount = 0;
          break;
        case 1:
        case 2:
          item.checkStatus = 0;
          item.childCheckCount = 0;
          item.childCheckPCount = 0;
          break;
        default:
          break;
      }
      //子节点 全部选中
      this._onItemChildSelect(item, index);
      //父节点 选中状态变化
      this._onItemParentSelect(item, index, oldCheckStatus);
    },
    _onItemChildSelect(item, index) {
      let allChildCount = 0;
      if (item.childCount && item.childCount > 0) {
        index++;
        while (
          index < this.treeList.length &&
          this.treeList[index].level > item.level
        ) {
          let itemChild = this.treeList[index];
          itemChild.checkStatus = item.checkStatus;
          if (itemChild.checkStatus == 2) {
            itemChild.childCheckCount = itemChild.childCount;
            itemChild.childCheckPCount = 0;
          } else if (itemChild.checkStatus == 0) {
            itemChild.childCheckCount = 0;
            itemChild.childCheckPCount = 0;
          }
          index++;
        }
      }
    },
    _onItemParentSelect(item, index, oldCheckStatus) {
      const parentIndex = this.treeList.findIndex(
        (itemP) => itemP[this.valueKey] == item.parentId
      );
      if (parentIndex >= 0) {
        let itemParent = this.treeList[parentIndex];
        let count = itemParent.childCheckCount;
        let oldCheckStatusParent = itemParent.checkStatus;

        if (oldCheckStatus == 1) {
          itemParent.childCheckPCount -= 1;
        } else if (oldCheckStatus == 2) {
          itemParent.childCheckCount -= 1;
        }
        if (item.checkStatus == 1) {
          itemParent.childCheckPCount += 1;
        } else if (item.checkStatus == 2) {
          itemParent.childCheckCount += 1;
        }

        if (
          itemParent.childCheckCount <= 0 &&
          itemParent.childCheckPCount <= 0
        ) {
          itemParent.childCheckCount = 0;
          itemParent.childCheckPCount = 0;
          itemParent.checkStatus = 0;
        } else if (itemParent.childCheckCount >= itemParent.childCount) {
          itemParent.childCheckCount = itemParent.childCount;
          itemParent.childCheckPCount = 0;
          itemParent.checkStatus = 2;
        } else {
          itemParent.checkStatus = 1;
        }
        this._onItemParentSelect(itemParent, parentIndex, oldCheckStatusParent);
      }
    },
    // 重置数据
    _reTreeList() {
      this.treeList.forEach((v, i) => {
        this.treeList[i].checkStatus = v.orCheckStatus;
      });
    },
    _initTree() {
      this.treeList = [];
      this._formatTreeData(this.sourceList);
    },
  },
  watch: {
    sourceList() {
      this._initTree();
    },
    localTreeList() {
      this.treeList = this.localTreeList;
    },
  },
  mounted() {
    this._initTree();
  },
};
</script>

<style scoped>
:deep(.uni-easyinput) {
  width: 100%;
  position: relative;
  text-align: left;
  color: #333;
  font-size: 14px;
}
.tree-cover {
  position: fixed;
  top: 0rpx;
  right: 0rpx;
  bottom: 0rpx;
  left: 0rpx;
  z-index: 99999;
  background-color: rgba(0, 0, 0, 0.4);
  opacity: 0;
  transition: all 0.3s ease;
  visibility: hidden;
}

.tree-cover.show {
  visibility: visible;
  opacity: 1;
}

.tree-dialog {
  position: fixed;
  top: 0rpx;
  right: 0rpx;
  bottom: 0rpx;
  left: 0rpx;
  background-color: #fff;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: column;
  z-index: 999999;
  top: 20%;
  transition: all 0.3s ease;
  transform: translateY(100%);
}

.tree-dialog.show {
  transform: translateY(0);
}

.tree-bar {
  /* background-color: #fff; */
  height: 90rpx;
  padding-left: 25rpx;
  padding-right: 25rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-sizing: border-box;
  border-bottom-width: 1rpx !important;
  border-bottom-style: solid;
  border-bottom-color: #f5f5f5;
  font-size: 32rpx;
  color: #757575;
  line-height: 1;
}

.tree-bar-confirm {
  color: #0055ff;
  padding: 15rpx;
}

.tree-bar-title {
}

.tree-bar-cancel {
  color: #757575;
  padding: 15rpx;
}

.tree-view {
  flex: 1;
  padding: 20rpx;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: column;
  overflow: hidden;
  height: 100%;
}
.item-flex {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tree-list {
  flex: 1;
  height: 100%;
  overflow: hidden;
}

.tree-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  line-height: 1;
  height: 0;
  opacity: 0;
  transition: 0.2s;
  overflow: hidden;
}

.tree-item.show {
  height: 90rpx;
  opacity: 1;
}

.tree-item.showchild:before {
  transform: rotate(90deg);
}

.tree-item.last:before {
  opacity: 0;
}

.switch-on {
  width: 0;
  height: 0;
  border-left: 10rpx solid transparent;
  border-right: 10rpx solid transparent;
  border-top: 15rpx solid #666;
}

.switch-off {
  width: 0;
  height: 0;
  border-bottom: 10rpx solid transparent;
  border-top: 10rpx solid transparent;
  border-left: 15rpx solid #666;
}

.item-last-dot {
  position: absolute;
  width: 10rpx;
  height: 10rpx;
  border-radius: 100%;
  background: #666;
}

.item-icon {
  width: 26rpx;
  height: 26rpx;
  margin-right: 8rpx;
  padding-right: 20rpx;
  padding-left: 20rpx;
}

.item-label {
  flex: 1;
  display: flex;
  align-items: center;
  height: 100%;
  line-height: 1.2;
}

.item-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 450rpx;
}

.item-check {
  width: 40px;
  height: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.item-check-yes,
.item-check-no {
  width: 20px;
  height: 20px;
  border-top-left-radius: 20%;
  border-top-right-radius: 20%;
  border-bottom-right-radius: 20%;
  border-bottom-left-radius: 20%;
  border-top-width: 1rpx;
  border-left-width: 1rpx;
  border-bottom-width: 1rpx;
  border-right-width: 1rpx;
  border-style: solid;
  border-color: #0055ff;
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
}

.item-check-yes-part {
  width: 12px;
  height: 12px;
  border-top-left-radius: 20%;
  border-top-right-radius: 20%;
  border-bottom-right-radius: 20%;
  border-bottom-left-radius: 20%;
  background-color: #0055ff;
}

.item-check-yes-all {
  margin-bottom: 5px;
  border: 2px solid #007aff;
  border-left: 0;
  border-top: 0;
  height: 12px;
  width: 6px;
  transform-origin: center;
  /* #ifndef APP-NVUE */
  transition: all 0.3s;
  /* #endif */
  transform: rotate(45deg);
}

.item-check .radio {
  border-top-left-radius: 50%;
  border-top-right-radius: 50%;
  border-bottom-right-radius: 50%;
  border-bottom-left-radius: 50%;
}

.item-check .radio .item-check-yes-b {
  border-top-left-radius: 50%;
  border-top-right-radius: 50%;
  border-bottom-right-radius: 50%;
  border-bottom-left-radius: 50%;
}

.hover-c {
  opacity: 0.6;
}

.itemBorder {
  border-bottom: 1px solid #e5e5e5;
}
</style>

二、使用

<TreeSearch
      ref="typeTreeRef"
      :multiple="false"
      :title="$t('AssetList.Pleaseselectanassetcategoryfirst')"
      :sourceList="TypeOption"
      @select-change="confirmType"
      valueKey="id"
      textKey="name"
      children="children"
      :selectedData="[formData.typeId]"
    />

三、方法

1.展示-- typeTreeRef.value.show();
2.确认数据--
let confirmType = async (id, name) => {

};
举报

相关推荐

0 条评论