vue+elementUI小工具(加载进度条、穿梭框、Dialog定位、2种selectTree等,持续更新)

记录一下自己开发的小插件

1、加载进度条,倒计时90秒(带遮罩)

<div class="loading-overlay" v-if="loadingVisible">
      <el-progress type="circle" :percentage="loadingPercentage" :stroke-width="20" ></el-progress>
      <p class="tips_css">升级中,请稍后!</p>
</div>


//data
loadingPercentage: 0,//进度百分比
timer: null,//定时器
loadingVisible: false,//开启遮罩


//methods
startLoading() {
      this.loadingVisible = true;
      this.loadingPercentage = 0;
      
      this.timer = setInterval(() => {
        if (this.loadingPercentage < 100) {
          this.loadingPercentage += 1;
        } else {
          clearInterval(this.timer); // 停止计时器
          
          // 延时 1 秒后执行关闭遮罩或其他操作
          setTimeout(() => {
            this.loadingVisible = false;
            // 关闭遮罩或执行其他操作
          }, 1000);
        }
      }, 900); // 每 900 毫秒(0.9秒)增加 1% 进度
},


//css
.loading-overlay {
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}
::v-deep .el-progress__text{
  font-size: 24px !important;
  color: rgb(30, 160, 225) !important;
}
::v-deep .el-progress-circle{
  width: 150px !important;
  height: 150px !important;
}
.tips_css{
  font-size: 24px;
  color: rgb(30, 160, 225)
}

2.带查询搜索功能的穿梭框,组件代码如下

<template>
  <div class="compent-dialog-body">
    <div style="padding:20px;">
      <el-row :gutter="8">
        <el-col :span="8">
          <p class="kw_div_p">列表仓库</p>
        </el-col>
        <el-col :span="16">
            <p class="kw_div_p">库位管理</p>
        </el-col>
      </el-row>
      
      <el-row :gutter="24" style="height:100%">
        <el-col :span="8">
          <div class="kw_div">
            <ul class="kw_ul">
              <li v-for="(i,index) in warehouseList" :key="index" class="kw_li" @click="liClick(i,index)" :class="isActive == index?'active_color':''">
                {{i.name}}
              </li>
            </ul>
          </div>
        </el-col>
        <el-col :span="16">
          <div class="kw_div">
            <el-row :gutter="8">
              <el-col :span="10">
                <div class="trans_box">
                  <div class="trans_box_top">
                    <span class="trans_box_top_title">
                      <el-checkbox v-model="qbkwQx" @change="qbkwQxF" style="margin-right:10px;"></el-checkbox>全部库位
                    </span>
                    <span class="trans_box_top_tips">{{selectAllKwList.length}}/{{allKwLength}}</span>
                  </div>
                  <div class="trans_box_main">
                    <div class="trans_search">
                      <el-input placeholder="请输入库位查询" prefix-icon="el-icon-search" v-model="allSearchV" class="trans_search_input" @input='inputAll'> </el-input>
                    </div>
                    <div class="trans_list">
                      <template v-for="(i,index) in allKwList">
                        <label class="trans_list_label" :key="index">
                          <el-checkbox v-model="i.isCheck" class="trans_list_checkbox" @change='chooseAll(i)'><span class="trans_list_value">{{i.label}}</span></el-checkbox>
                        </label>
                      </template>
                    </div>
                  </div>
                </div>
              </el-col>
              <el-col :span="2">
                <div class="trans_mid">
                  <el-button icon="el-icon-arrow-left" type="primary" circle class="trans_mid_btn" :disabled='leftDis' @click="goLeft()"></el-button>
                  <el-button icon="el-icon-arrow-right" type="primary" circle class="trans_mid_btn" :disabled='rightDis' @click="goRight()"></el-button>
                </div>
              </el-col>
              <el-col :span="10">
                <div class="trans_box">
                  <div class="trans_box_top">
                    <span class="trans_box_top_title">
                      <el-checkbox v-model="dqkwQx" @change="dqkwQxF" style="margin-right:10px;"></el-checkbox>当前库位
                    </span>
                    <span class="trans_box_top_tips">{{selectNowKwList.length}}/{{nowKwLength}}</span>
                  </div>
                  <div class="trans_box_main">
                    <div class="trans_search">
                      <el-input placeholder="请输入库位查询" prefix-icon="el-icon-search" v-model="nowSearchV" class="trans_search_input" @input='inputNow'> </el-input>
                    </div>
                    <div class="trans_list">
                      <template v-for="(i,index) in nowKwList">
                        <label class="trans_list_label" :key="index">
                          <el-checkbox v-model="i.isCheck" class="trans_list_checkbox" @change='chooseNow(i)'><span class="trans_list_value">{{i.label}}</span></el-checkbox>
                        </label>
                      </template>
                    </div>
                  </div>
                </div>
              </el-col>
            </el-row>
            
          </div>
        </el-col>
      </el-row>
    </div>
    <div class="el-dialog__footer">
      <el-button size="mini" @click="close">取消</el-button>
      <el-button size="mini" type="primary" @click="submit">确认</el-button>
    </div>
  </div>

</template>

<script>
  import * as wmsGoodsPosMsts from '@/api/wmsGoodsPosMsts'//请求api
  import * as wmsGoodsAreaPosTbls from '@/api/wmsGoodsAreaPosTbls'//请求api
  import waves from '@/directive/waves' // 水波纹指令
  export default {
    name: 'storageLocation',
    components: {
      // Pagination
    },
    directives: {
      waves
    },
    props: ['shooseData',],
    data() {
      return {
        allSearchV:'',//全部库位搜索
        nowSearchV:'',//当前库位搜索
        checkAllList:[],//多选选中
        allKwList:[],//全部库位列表
        selectAllKwList:[],//选择全部库位数据
        nowKwList:[],//当前库位列表
        selectNowKwList:[],//当前库位数据
        leftDis:true,//左按钮状态
        rightDis:true,//右按钮状态
        beginAllKwList:[],//纪录初始库位
        beginNowKwList:[],//纪录初始库位
        qbkwQx:false,//全部库位全选
        dqkwQx:false,//当前库位全选


        multipleSelection: [], // 列表checkbox选中的值
        currentRoleId: this.roleId,
        tableKey: 0,
        list: null,
        total: 0,
        listLoading: true,
        listQuery: { // 查询条件
          page: 1,
          limit: 20,
          key: '',
          stockCode:'',
          goodsAreaCode: ''
        },
        
        goodsareaDatas:[],
        goodsareaDatasBase:[],
        isActive:0,
        warehouseList: [],//仓库列表
        allKwLength:0,
        nowKwLength:0,
      }
    },

    created() {
      this.listQuery.stockCode = this.shooseData.stockCode
      this.listQuery.goodsAreaCode = this.shooseData.goodsAreaCode
      this.getWarehouseList();//获取仓库列表
      this.getUserLocation();//获取用户库位
    },
    mounted() {
      
    },
    methods: {
      //全部库位全选
      qbkwQxF(val){
        if(val){
          for (let i = 0; i < this.allKwList.length; i++) {
            this.allKwList[i].isCheck = true
          }
          this.selectAllKwList = this.allKwList
        }else{
          for (let i = 0; i < this.allKwList.length; i++) {
            this.allKwList[i].isCheck = false
          }
          this.selectAllKwList = []
        }
      },
      // 当前库位全选
      dqkwQxF(val){
        if(val){
          for (let i = 0; i < this.nowKwList.length; i++) {
            this.nowKwList[i].isCheck = true
          }
          this.selectNowKwList = this.nowKwList
        }else{
          for (let i = 0; i < this.nowKwList.length; i++) {
            this.nowKwList[i].isCheck = false
          }
          this.selectNowKwList = []
        }
      },
      //全部库位列表搜索
      inputAll(value){
        this.allKwList = this.beginAllKwList
        var t = []
        if(value != '' && value != null && value != undefined ){
          var reg = new RegExp(value)
          for (let i = 0; i < this.allKwList.length; i++) {
            if(reg.test(this.allKwList[i].name)){
              t.push(this.allKwList[i])
            }
          }
          this.allKwList = t
        }
        
        //全部库位删除选择数据
        this.allKwList = this.allKwList.filter(item => !this.nowKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));
        this.selectAllKwList = []//清空选择
        this.allKwList.forEach(i=>{i.isCheck = false})//清空选择状态
        this.qbkwQx = false//重置全选
        this.allKwLength = this.allKwList.length//重新统计数量
      },
      //当前库位列表搜索
      inputNow(value){
        this.nowKwList = this.beginNowKwList
        var t = []
        if(value != '' && value != null && value != undefined ){
          var reg = new RegExp(value)
          for (let i = 0; i < this.nowKwList.length; i++) {
            if(reg.test(this.nowKwList[i].name)){
              t.push(this.nowKwList[i])
            }
          }
          this.nowKwList = t
        }
        //当前库位删除选择数据
        this.nowKwList = this.nowKwList.filter(item => !this.allKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));
        this.selectNowKwList = []//清空选择
        this.nowKwList.forEach(i=>{i.isCheck = false})//清空选择状态
        this.dqkwQx = false//重置全选
        this.nowKwLength = this.nowKwList.length//重新统计数量
      },
      //全部库位列表选择
      chooseAll(row){
        this.selectAllKwList = []
        // 改变选择状态
        for (let i = 0; i < this.allKwList.length; i++) {
          if(this.allKwList[i].goodsPosCode == row.goodsPosCode){
            this.allKwList[i].isCheck = row.isCheck
          }
        }
        // 选中数据赋值
        for (let i = 0; i < this.allKwList.length; i++) {
          if(this.allKwList[i].isCheck){
            this.selectAllKwList.push(this.allKwList[i])
          }
        }
        
        var s= 0
        // 改变全选状态
        for (let i = 0; i < this.allKwList.length; i++) {
          if(!this.allKwList[i].isCheck){
            this.qbkwQx = false
          }else{
            s +=1
          }
        }
        if(s == this.allKwList.length){
          this.qbkwQx = true
        }
      },
      //当前库位列表选择
      chooseNow(row){
        this.selectNowKwList = []
        // 改变选择状态
        for (let i = 0; i < this.nowKwList.length; i++) {
          if(this.nowKwList[i].goodsPosCode == row.goodsPosCode){
            this.nowKwList[i].isCheck = row.isCheck
          }
        }
        // 选中数据赋值
        for (let i = 0; i < this.nowKwList.length; i++) {
          if(this.nowKwList[i].isCheck){
            this.selectNowKwList.push(this.nowKwList[i])
          }
        }

        var s= 0
        // 改变全选状态
        for (let i = 0; i < this.nowKwList.length; i++) {
          if(!this.nowKwList[i].isCheck){
            this.dqkwQx = false
          }else{
            s +=1
          }
        }
        if(s == this.nowKwList.length){
          this.dqkwQx = true
        }
      },
      //当前库位删除- 向全部库位添加
      goLeft(){
        //重置选择
        for (let i = 0; i < this.selectNowKwList.length; i++) {
          this.selectNowKwList[i].isCheck = false
        }
        this.nowKwList.forEach(item =>{
          item.isCheck = false
        })
        //全部库位添加数据
        this.allKwList = this.allKwList.concat(this.selectNowKwList)
        this.beginAllKwList = this.allKwList
        //当前库位删除选择数据
        this.nowKwList = this.nowKwList.filter(item => !this.allKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));
        this.selectNowKwList=[]
        this.dqkwQx = false
        this.allKwLength = this.allKwList.length
        this.nowKwLength = this.nowKwList.length
      },
      //全部库位删除- 向当前库位添加
      goRight(){
        //重置选择
        for (let i = 0; i < this.selectAllKwList.length; i++) {
          this.selectAllKwList[i].isCheck = false
        }
        this.allKwList.forEach(item =>{
          item.isCheck = false
        })
        //赋值
        this.nowKwList = this.nowKwList.concat(this.selectAllKwList)
        this.beginNowKwList = this.nowKwList

        
        //全部库位删除选择数据
        this.allKwList = this.allKwList.filter(item => !this.nowKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));

        this.selectAllKwList =[]
        this.qbkwQx = false
        this.allKwLength = this.allKwList.length
        this.nowKwLength = this.nowKwList.length
      },
      //获取仓库列表
      getWarehouseList(){
          this.warehouseList = JSON.parse(JSON.stringify(this.$store.state.stockDatas))
          var t = {
            name:'全部仓库',
            stockCode:'allStockCode'
          }
          this.warehouseList.unshift(t)
      }, 
      //获取用户库位
      getUserLocation(){
        var tdata={
          goodsAreaCode:this.listQuery.goodsAreaCode,
          // key:this.listQuery.key
        }
        this.$store.commit("updataListLoading",true)
        // 请求自己的数据
        wmsGoodsAreaPosTbls.searchPosByArea(tdata).then((res) => {
          var tdata = res.result
          for (let i = 0; i < tdata.length; i++) {
            for (let j = 0; j < this.warehouseList.length; j++) {
              if(tdata[i].stockCode){
                if(this.warehouseList[j].stockCode == tdata[i].stockCode){
                  tdata[i].label = tdata[i].name + ' - ' + this.warehouseList[j].name;
                }
              }else{
                tdata[i].label = tdata[i].name
              }
            }
          }
          this.nowKwList = tdata
          this.beginNowKwList = tdata
          this.getAllKwList();

          this.nowKwLength = this.nowKwList.length
          this.$store.commit("updataListLoading",false)
        }).catch(() => {
          this.$store.commit("updataListLoading",false)
        })
      },
      //获取所有库位
      getAllKwList(){
        // 请求自己的数据
        wmsGoodsPosMsts.searchAllPos().then((res) => {
          // 数据处理
          var tdata = res.result
          for (let i = 0; i < tdata.length; i++) {
            tdata[i].isCheck = false
            for (let j = 0; j < this.warehouseList.length; j++) {
              if(this.warehouseList[j].stockCode == tdata[i].stockCode){
                tdata[i].label = tdata[i].name + ' - ' + this.warehouseList[j].name;
              }
            }
          }
          
          // 所有库位删除当前库位
          this.allKwList = tdata.filter(item => !this.nowKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));

          // 缓存全部库位
          this.beginAllKwList = this.allKwList
          this.allKwLength = this.allKwList.length
        }).catch(() => {
          // this.warehouseList = []
        })
      },
      //点击行改变状态,从全部库位中筛选
      liClick(row,index){
        var tdata={}
        if(row.stockCode == 'allStockCode'){
          this.getAllKwList()
        }else{
          tdata={
            stockCode:row.stockCode
          }
          // 请求自己的数据
          wmsGoodsPosMsts.LoadForstockCode(tdata).then((res) => {
            // 数据处理
            var tdata = res.result
            for (let i = 0; i < tdata.length; i++) {
              tdata[i].isCheck = false
              for (let j = 0; j < this.warehouseList.length; j++) {
                if(this.warehouseList[j].stockCode == tdata[i].stockCode){
                  tdata[i].label = tdata[i].name + ' - ' + this.warehouseList[j].name;
                }
              }
            }
            // 所有库位删除当前库位
            this.allKwList = tdata.filter(item => !this.nowKwList.some(ele=>ele.goodsPosCode===item.goodsPosCode));

            // 缓存全部库位
            this.beginAllKwList = this.allKwList
            this.selectAllKwList = []//切换时清空选择列表
          })
        }
        this.isActive = index
      },
      close() {
        this.$emit('close')
      },
      submit() {
        var subList = this.nowKwList
        for (let i = 0; i < subList.length; i++) {
          subList[i].goodsAreaCode = this.shooseData.goodsAreaCode
        }

        if(subList.length <= 0){
          subList = [{goodsAreaCode:this.shooseData.goodsAreaCode}]
        }
        this.$store.commit("updataListLoading",true)
        // 请求自己的数据
        wmsGoodsAreaPosTbls.saveareapos(subList).then((res) => {
          this.$emit('close')
          this.$message.success(res.message)
          this.$store.commit("updataListLoading",false)
        }).catch(() => {
          this.$message.error('操作失败,请重试')
          this.$store.commit("updataListLoading",false)
        })
      },
      
    },
    watch:{
      //当前库位选择数据变换
      'selectNowKwList'(val){
        if(val.length>0){
          this.leftDis = false
        }else{
          this.leftDis = true
        }
      },
      //全部库位选择数据变换
      'selectAllKwList'(val){
        if(val.length>0){
          this.rightDis  = false
        }else{
          this.rightDis  = true
        }
      },
    }
  }

</script>
<style  lang="scss" scoped>
.trans_box{
  width: 100%;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
  display: inline-block;
  vertical-align: middle;
  max-height: 100%;
  box-sizing: border-box;
  position: relative;
  .trans_box_top{
    height: 40px;
    line-height: 40px;
    background: #f5f7fa;
    margin: 0;
    padding-left: 15px;
    border-bottom: 1px solid #ebeef5;
    box-sizing: border-box;
    color: #000;
    .trans_box_top_title{
      font-size: 16px;
      color: #303133;
      font-weight: 400
    }
    .trans_box_top_tips{
      position: absolute;
      right: 15px;
      color: #909399;
      font-size: 12px;
      font-weight: 400;
    }
  }
  .trans_box_main{
    .trans_search{
      text-align: center;
      margin: 15px;
      box-sizing: border-box;
      display: block;
      width: auto;
    }
    .trans_list{
      height: 400px;
      overflow: hidden;
      overflow-y: scroll;
      .trans_list_label{
        cursor: pointer;
        display: block;
        height: 30px;
        line-height: 30px;
        padding-left: 15px;
        // .trans_list_checkbox{
        //   background-color: #fff;
        //   border: 1px solid #dcdfe6;
        // }
        .trans_list_value{
          width: 100%;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
          box-sizing: border-box;
          padding-left: 14px;
          line-height: 30px;
          color: #606266;
          font-weight: 500;
          font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif !important;
        }
        
      }
    }
    .trans_list::-webkit-scrollbar {
      display: none; /* Chrome Safari */
    }
  }
}

.trans_mid{
  margin-top: 200px;
  .trans_mid_btn{
    display: block;
    margin: 20px 0;
  }
}

  .custom-theme .el-transfer-panel__body,.el-transfer-panel__body{
    height: 500px !important;
  }
  .custom-theme .el-transfer-panel__list,.el-transfer-panel__list{
    height: 500px;
  }
  .compent-dialog-body{
    min-height: 500px;
    /* 库位 */
    .kw_div_p{
      margin: 0 0 10px 0px;
      border-left: 3px solid #409eff;
      padding-left: 10px;
    }
    .kw_ul{
      text-decoration: none;
      list-style: none;
      padding: 0;
      margin-top: 0;
      .kw_li{
        height: 30px;
        line-height: 30px;
        padding-left: 20px;
        cursor: pointer;
      }
      .active_color{
        background: #f5f7fa;
      }
    }
  }
  
</style>

3.Dialog指定位置打开

项目需求点击图片放大,弹窗适配大小,页面可放大缩小,弹窗唤出位置就需要再点击位置出唤出(避免页面放大时在屏幕外弹出等)

<!-- 图片点击代码 -->
<img :src="XXX" v-for="(j,jdx) in  lIst" :key="jdx" @click="showImg($event, j.filePath)" style="cursor: pointer;"/>

<!-- 图片放大查看 -->
     <el-dialog :visible.sync="imgDialog" width="30%" :modal-append-to-body="true" :style="dialogStyle"  >
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>

//查看图片
    showImg(event,url){
      // 计算Dialog的位置,这里简单地将Dialog定位在点击位置的右侧下方  
      const rect = event.target.getBoundingClientRect();
      this.dialogStyle = {  
        // this.rowHeight自定义偏移位置
        top: `${rect.top - this.rowHeight}px`,  
        left: `${rect.left -this.rowHeight}px`  
      };  
      this.dialogImageUrl = url
      this.imgDialog = true
    },

4.两种selectTree组件,

        1、第一种单击选择,效果如下图

        

        直接上代码,子组件:

<!--
 * @description:树形下拉选择
 * @author: Long 
 * @version: 1.0
 * @updateDate: 2024-7-8
 * @usage:用法
    defaultValue: 默认值
    treeData: 树形数据
    disabled: 是否禁用
    defaultProps: 配置选项
    @clickSelectTree: 点击树形节点时触发
    @clearSelectInput: 清空输入框时触发
    
    <SelectTree v-model="editFormFl.faterClassName" :defaultValue="editFormFl.faterClassCode" :treeData="treeData" :disabled="isEditFl" :defaultProps="defaultProps" @clickSelectTree="handleSelectValueChange" style="width: 100%;"></SelectTree>
-->
<template>
  <div style="display: inline-block;">
    <el-select class="main-select-tree" ref="selectRef" v-model="value" style="width: 100%" clearable @clear="clearSelectInput" :disabled="disabled">
      <el-input style="width: 100%; margin-left: 10px; margin-bottom: 10px" placeholder="输入关键字进行过滤" v-model="filterText" clearable ></el-input>
      <el-option v-for="item in formatData(treeData)" :key="item.value" :label="item.label" :value="item.value" style="display: none" />
      <el-tree class="my_select_Tree" ref="treeRef" :data="treeData" node-key="id" highlight-current :props="defaultProps" @node-click="handleNodeClick" :current-node-key="value" :expand-on-click-node="true" default-expand-all :filter-node-method="filterNode" />
    </el-select>
  </div>
</template>

<script>
export default {
  name: "SelectTree",
  props: {
    selectValue: {
      type: String,
      default: "",
    },
    treeData: {
      type: Array,
      default: () => ([])
    },
    // 配置选项
    defaultProps: {
      type: Object,
      default: () => ({ // 属性值为后端返回的对应的字段名
        children: 'children',
        label: 'className',
        value: 'id'
      })
    },
    //是否禁用
    disabled: {
      type: Boolean,
      default: false
    },
    //默认选中值
    defaultValue: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      filterText: "",
      value: "",
    };
  },
  watch: {
    filterText(val) {
      this.$refs.treeRef.filter(val);
    },
  },
  mounted() {
    this.initialization()//初始化赋值
  },
  methods: {
    // 初始化赋值
    initialization(){
      var that = this
      this.findNodeById(this.treeData, this.defaultValue, function(node) {
        that.value = node.className;
        console.log('Found node name:', that.value); // 立即查看结果
      });
    },
    //递归方法,获取树形结构中指定id的节点
    findNodeById(tree, id, callback) {  
      for (let node of tree) {  
        if (node.id === id) {  
          // 找到节点,调用回调函数  
          callback(node);  
          return; // 如果只需要找到第一个匹配的节点,可以返回  
        }  
        if (node.children) {  
          // 如果节点有子节点,递归查找  
          this.findNodeById(node.children, id, callback);  
        }  
      }  
    },
    //筛选方法
    filterNode(value, data) {
      if (!value) return true;
      return data.className.indexOf(value) !== -1;
    },
    // 递归遍历数据
    formatData(data) {
      let options = [];
      const formatDataRecursive = (data) => {
        data.forEach((item) => {
          options.push({ label: item.className, value: item.id });
          if (item.children && item.children.length > 0) {
            formatDataRecursive(item.children);
          }
        });
      };
      formatDataRecursive(data);
      return options;
    },
    // 点击事件
    handleNodeClick(node) {
      this.value = node.className;
      this.$refs.selectRef.blur();
      this.$emit("clickSelectTree", node);
    },
    // 清空事件
    clearSelectInput() {
      this.$emit("clickSelectTree", "");
      // 获取 el-tree 实例的引用
      const elTree = this.$refs.treeRef;
      // 将当前选中的节点设置为 null
      elTree.setCurrentKey(null);
    },
  },
};
</script>

<style lang="scss" scoped>
.my_select_Tree .el-tree-node .is-current > .el-tree-node__content {
  font-weight: bold;
  color: #409eff;
}
.my_select_Tree .el-tree-node.is-current > .el-tree-node__content {
  font-weight: bold;
  color: #409eff;
}
</style>

        2、第二种带多选框的下拉树形组件

<!--
 * @description:树形下拉选择
 * @author: Long 
 * @version: 1.0
 * @updateDate: 2023-01-17 09:04:12
 * @usage:用法
    options: 树形数据
    defaultProps: 树形数据属性
    defaultValue: 默认选中的值
    selectWidth: 下拉框宽度
    placeholder: 下拉框提示语
    filterable: 是否可搜索
    appendToBody: 下拉框是否插入至body元素上
    checkStrictly: 是否父子节点不关联
    isCanDelete: 是否可删除
    changeSelectDataList: 选中值变化时触发
    disabledSelect: 是否禁用

    <TreeSelect :options="areaOptions" :defaultProps="defaultProps" :defaultValue="defaultValue"  @changeSelectDataList="changeSelectDataList" :selectWidth="'300'" style="margin:0 10px;" :placeholder="'请选择区域'"></TreeSelect>
-->
<template>
  <div style="display: inline-block;">
    <el-select v-model="selectValue" multiple :placeholder="placeholder" :filterable="filterable" :filter-method="dataFilter" :popper-append-to-body="appendToBody"
      @remove-tag="removeTag" @clear="clearAll" :clearable="isCanDelete" :style="{ width: selectWidth + 'px'}" :disabled="disabledSelect" collapse-tags>
      <el-option :value="selectTree" v-loading="treeLoading" element-loading-background="rgba(255, 255, 255, 0.5)"
        element-loading-text="loading" class="option-style" disabled>
        <div class="check-box">
          <el-button type="text"  @click="handlecheckAll">全选</el-button>
          <el-button type="text"  @click="handleReset">清空</el-button>
          <el-button type="text"  @click="handleReverseCheck">反选</el-button>
        </div>
        <el-tree :data="options" :props="defaultProps" class="tree-style" ref="treeNode" show-checkbox :node-key="defaultProps.value" :filter-node-method="filterNode" :default-checked-keys="defaultValue" :check-strictly="checkStrictly" @check-change="handleNodeChange"> </el-tree>
      </el-option>
  </el-select>
  </div>
</template>

<script>
export default {
  name: 'TreeSelect',
  props: {
    //编辑时回显的数组
    defaultValue: {
      type: Array,
      default: () => ([])
    },
    //可用选项的数组
    options: {
      type: Array,
      default: () => ([])
    },
    // 配置选项
    defaultProps: {
      type: Object,
      default: () => ({ // 属性值为后端返回的对应的字段名
        children: 'children',
		    label: 'label',
        value: 'value'
      })
    },
    // 是否将组件添加到body上面(组件在弹窗或者表格里面时可设为true)
    appendToBody: {
      type: Boolean,
      default: false
    },
    // 是否可搜索
    filterable: {
      type: Boolean,
      default: true // 不可以搜索
    },
    // 是否禁用下拉框
    disabledSelect: {
      type: Boolean,
      default: false
    },
    // 父子不互相关联
    checkStrictly: {
      type: Boolean,
      default: false // 关联
    },
    // 父类id字段名 (如果父子联动则必传该字段,不联动则不用传)
    parentValue: {
      type: String,
      default: 'parentValue'
    },
    // 回显的值是否可被删除 true: 可以删除;false:不能删除
    isCanDelete: {
      type: Boolean,
      default: true
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    // 不可删除报错
    errMessage: {
      type: String,
      default: '该选项不可被删除'
    },
    //select宽度
    selectWidth: {
      type: String,
      default: '200'
    },
  },
  data () {
    return {
      selectTree: [], // 绑定el-option的值
      selectValue: [], // 文本框中的标签
      VALUE_NAME: this.defaultProps.value, // value转换后的字段
      VALUE_TEXT: this.defaultProps.label, // label转换后的字段
      treeLoading: false // 加载loading~
    }
  },
  watch: {
    // 监听回显的数据
    defaultValue (val) {
      if (val.length) {
        this.$nextTick(() => {
          let  datalist = this.$refs.treeNode.getCheckedNodes()
          if (!this.checkStrictly) {
          const parentList = datalist.filter(v => v[this.defaultProps.children]).map(v => v[this.VALUE_NAME])
          datalist = datalist.filter(v => parentList.indexOf(v[this.parentValue]) === -1)
        }
          this.selectTree = datalist
          this.selectValue = datalist.map(v => v[this.VALUE_TEXT])
        })
      }
    }
  },
  methods: {
    // 全选
    handlecheckAll () {
      this.treeLoading = true
      setTimeout(() => {
        this.$refs.treeNode.setCheckedNodes(this.options)
        this.treeLoading = false
      }, 200)
    },
    // 清空
    handleReset () {
      if (this.isCanDelete) {
        this.treeLoading = true
        setTimeout(() => {
          this.$refs.treeNode.setCheckedNodes([])
          this.treeLoading = false
        }, 200)
      } else {
        this.$message.error(this.errMessage)
      }
    },
    // 反选处理方法
    batchSelect(nodes, refs, flag, seleteds) {
      if (Array.isArray(nodes)) {
        nodes.forEach(element => {
          refs.setChecked(element, flag, true)
        })
      }
      if (Array.isArray(seleteds)) {
        seleteds.forEach(node => {
          refs.setChecked(node, !flag, true)
        })
      }
    },
    // 反选
    handleReverseCheck() {
      if (this.isCanDelete) {
        this.treeLoading = true
        setTimeout(() => {
          let res = this.$refs.treeNode
          let nodes = res.getCheckedNodes(true, true)
          this.batchSelect(this.options, res, true, nodes)
          this.treeLoading = false
        }, 200)
      } else {
        this.$message.error(this.errMessage)
      }
    },
    // 输入框关键字
    dataFilter (val) {
      this.$refs.treeNode.filter(val)
    },
    // 过滤
    filterNode (value, data) {
      if (!value) return true
      return data[this.defaultProps.label].toLowerCase().indexOf(value.toLowerCase()) !== -1
    },
    // 选中节点
    handleNodeChange(data,self,child) {
      const flag = this.defaultValue.some(v => v === data[this.VALUE_NAME])
      let  datalist = this.$refs.treeNode.getCheckedNodes()
      if (!self && !this.isCanDelete && flag) {
        this.$message.error(this.errMessage)
        this.$refs.treeNode.setChecked(data,true, true)
      }
      if (!this.checkStrictly) { // 如果联动则需处理父子值关系
        const parentList = datalist.filter(v => v[this.defaultProps.children]).map(v => v[this.VALUE_NAME])
        datalist = datalist.filter(v => parentList.indexOf(v[this.parentValue]) === -1)
      }
      this.selectTree = datalist
      this.selectValue = datalist.map(v => v[this.VALUE_TEXT])
      this.$emit('changeSelectDataList', this.selectTree)
    },
    // 移除单个标签
    removeTag(tagName){
      const flagName= this.selectTree.filter(v => v[this.VALUE_NAME] === this.defaultValue.find(item => item === v[this.VALUE_NAME])).map(v => v[this.VALUE_TEXT])
      const flag = flagName.includes(tagName)
		  if (this.isCanDelete) { // 判断回显的值是否可删除
          this.selectTree = this.selectTree.filter(v => v[this.VALUE_TEXT] !== tagName)
          const selectTreeValue = this.selectTree.map(v => v[this.VALUE_NAME])
          let setlist = this.$refs.treeNode.getCheckedNodes()
          setlist = setlist.filter(v => v[this.VALUE_NAME] === selectTreeValue.find(item => item === v[this.VALUE_NAME]))
          this.$nextTick(() => {
            this.$refs.treeNode.setCheckedNodes(setlist)
          })
          this.$emit('changeSelectDataList', this.selectTree)
        } else {
          if (!flag) {  // 判断回显时新增的是否可删除
            this.selectTree = this.selectTree.filter(v => v[this.VALUE_TEXT] !== tagName)
            const selectTreeValue = this.selectTree.map(v => v[this.VALUE_NAME])
            let setlist = this.$refs.treeNode.getCheckedNodes()
            setlist = setlist.filter(v => v[this.VALUE_NAME] === selectTreeValue.find(item => item === v[this.VALUE_NAME]))
            this.$nextTick(() => {
              this.$refs.treeNode.setCheckedNodes(setlist)
            })
            this.$emit('changeSelectDataList', this.selectTree)
          } else {
            this.selectValue = this.selectTree.map(v => v[this.VALUE_TEXT])
            this.$message.error(this.errMessage)
          }
        }
	  },
    // 文本框清空
    clearAll(){
      this.selectTree = []
      this.$refs.treeNode.setCheckedNodes([])
      this.$emit('changeSelectDataList', this.selectTree)
    }
  }

}
</script>

<style lang="scss" scoped>
.check-box{
  padding: 0 20px;
}
::v-deep .el-scrollbar{
  height: 280px;
  .el-select-dropdown__wrap{
    max-height: 280px;
    overflow: hidden;
    .el-select-dropdown__list{
      padding: 0;
    }
  }
}
.option-style {
  height: 280px;
  padding: 0 0 10px 0 !important;
  margin: 0;
  overflow-y: auto;
  cursor: default !important;
}
</style>

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现渐变色进度条,你可以使用 Vue.js 架和 Element UI 组件库。具体实现如下: 1. 在 Vue 组件中使用 el-progress 组件,设置 type 为 circle 或 line,根据需求设置不同的属性。 2. 在组件中定义 CSS 样式,使用渐变色作为进度条的背景颜色。例如: ```css .el-progress__text { color: #000; } .el-progress__text { color: #000; } .el-progress-bar__outer { background: linear-gradient(to right, #f00, #ff0, #0f0); border-radius: 100px; } .el-progress-bar__inner { border-radius: 100px; } ``` 其中,linear-gradient() 函数用于设置渐变色,to right 表示从左到右渐变,#f00、#ff0、#0f0 是三颜色,可以根据实际需求调整。 3. 在 Vue 组件中的 el-progress 组件中添加 slot 插槽,插入自定义的内容。例如: ```html <el-progress :percentage="percentage" :type="type"> <div class="progress-text">{{ percentage }}%</div> </el-progress> ``` 其中,percentage 是进度条的百分比,type 是进度条的类型,progress-text 是自定义的样式类名。 4. 在 Vue 组件中定义 data 数据,包括进度条的百分比和类型等。 完整的代码示例如下: ```html <template> <div> <el-progress :percentage="percentage" :type="type"> <div class="progress-text">{{ percentage }}%</div> </el-progress> </div> </template> <script> export default { data() { return { percentage: 50, type: 'circle', }; }, }; </script> <style> .el-progress__text { color: #000; } .el-progress-bar__outer { background: linear-gradient(to right, #f00, #ff0, #0f0); border-radius: 100px; } .el-progress-bar__inner { border-radius: 100px; } .progress-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 14px; font-weight: bold; } </style> ``` 通过以上步骤,你就可以实现一个具有渐变色的进度条了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值