3.BI可视化编辑器之右击菜单的“置顶、置底“实现

一、思路

1.每次push到list数组的时候,新增一个z用来管理图层的叠放,z应该有个默认值0,如果有z值去多个z值中比较获取最大的,这样拖拽的时候,会是最高层级的的

2.样式的话z-index因为我们用的依赖,所以直接赋值就可以了

二、置顶和置底

1.首先右击的时候获取当前图层的焦点传一个item过来,然后设置一个focused状态,遍历list中的id,跟当前点击的id是否一样,一样就true,否则为flase

2.先找到数组中被选中的项,通过list中多个z值比较,最大最小,从而实现置底和置顶

   
    //封装 左键点击,让当前项获取焦点,其他项取消焦点
    clickDrageeronFoucus(currentItem) {
        currentItem.focused = true;
        this.list.forEach((item)=>{
            item.focused = item.id === currentItem.id 
        })
    },
    //右键菜单-打开的时候获取当前焦点
    onContextMenuOpen(e, item) {
      this.$refs.contextMenu.open(e);//打开右键菜单
      this.clickDrageeronFoucus(item);//右击的时候获取焦点
    },

    //封装去找最小值
    findBottomLayer(currentItem) {
      const minZ = Math.min(...this.list.map(item => item.z)) || 0//获取最小值
      if (currentItem.z === minZ) {
        alert('已经是最底层了')
        return false;//因为还有一个最上层,所以改为false
      }
      return minZ
    },
    //封装去找最大值
    findTopLayer(currentItem) {
      const maxZ = Math.max(...this.list.map(item => item.z)) || 0//获取最大值
      if (currentItem.z === maxZ) {
        alert('已经是最顶层了')
        return;
      }
      return maxZ
    },
//右键菜单项-置顶
    onLayerTop() {
      //先找到数组当中被选中的那项
      const currentItem = this.list.find(item => item.focused)
      const maxZ = this.findTopLayer(currentItem)//找最大值
      if (!maxZ) {//判断最大值是否存在,注意0的时候是不存在的
        return;
      }
      currentItem.z = maxZ + 1;//
      // this.sortList()//排序
      // this.record()//记录
    },
    //右键菜单项-置底
    onLayerBottom() {
      //先找到数组当中被选中的那项
      const currentItem = this.list.find(item => item.focused)
      const minZ = this.findBottomLayer(currentItem)//找最小值
      if (minZ === false) {//0是一个默认值   // 如果 !minZ 相当于!0 就为true就不对了所以要        写===false 
        return;
      }
      // 因为显示不了负数比如-1,最小只能为0,所以要做逻辑处理
      if (minZ - 1 < 0) {
        this.list = this.list.map(item => {
          item.z -= minZ - 1;
          return item
        })
        currentItem.z = 0
      } else {
        currentItem.z = minZ - 1
      }
      // this.sortList()//排序
      // this.record()//记录
    },

三、全部代码

<template>
  <div id="app">
    <!-- 图层列表 -->
    <el-tabs v-model="activeName" class="sidebar">
      <el-tab-pane label="图层列表" name="layer">
        <!-- 图层列表 -->
          <div class="layer" v-for="item in list" :key="item.id">
              {{ item.label }}
          </div>
      </el-tab-pane>
      <el-tab-pane label="组件列表" name="widget">
        <Widget-List :list="widgetList" @onWidgetMouseDown="onWidgetMouseDown" />
      </el-tab-pane>
    </el-tabs>

     <!-- 操作面板 -->
    <div class="panel"  
      @dragover.prevent 
      @drop="onDrop" >
      <Drageer v-for="(item, i) in list" 
      :key="item.id" 
      ref="widget" 
      class="box" 
      :x="item.x" 
      :y="item.y" 
      :z="item.z" 
      :w="item.w" 
      :h="item.h" 
      @contextmenu.native.prevent="onContextMenuOpen($event, item)"

      >
         <component :value="item.value" class="inner-widget" :is="item.component" 
          :styles="item.styles" />
      </Drageer>
    </div>


    <!-- 样式配置区域 -->
    <!-- <style-sider></style-sider> -->

    <!-- 右键菜单 -->
    <Context-menu ref="contextMenu">
      <li>
        <a href="#" @click.prevent="onLayerTop">置顶</a>
      </li>
      <li>
        <a href="#" @click.prevent="onLayerBottom">置底</a>
      </li>
      <li>
        <a href="#" @click.prevent="onLayerUp">上移图层</a>
      </li>
      <li>
        <a href="#" @click.prevent="onLayerDown">下移图层</a>
      </li>
      <li>
        <a href="#" @click.prevent="onLayerRemove">删除</a>
      </li>
      <!-- 可能会冒泡加stop -->
    </Context-menu>

    <!-- 对齐线 纵向 -->
    <!-- <div></div> -->

  </div>
</template>

<script>
let currentId = 0; //id计数器
let widgetX = 0;//差值
let widgetY = 0;//差值
let currentWidget = null;//获取当前点击的图层数据

//右键菜单
import ContextMenu from 'vue-context';

//出现在panel上的组件
import BarChart from '@/components/bar-chart'
import AreaChart from '@/components/area-chart'
import CustomText from '@/components/custom-text'
import CustomVideo from '@/components/custom-video'
import MianjiChart from '@/components/mianji-chart'

//左侧小组件列表
import WidgetList from '@/components/widget-list/'

//静态配置
import * as CONFIG from '@/components/constants/config'

export default {
  name: 'App',
  components: {
    WidgetList,
    BarChart,
    AreaChart,
    CustomText,
    CustomVideo,
    MianjiChart,
    ContextMenu,
  },
  data(){
    return{
      activeName:'widget',
      list:[],
      widgetList: CONFIG.WIDGET_LTST,//组件的数据结构
    }
  },
  methods:{
    //放置组件
    onDrop(e,i){
      //放置的距离左侧的距离-鼠标落下鼠标距离左侧的距离=当前box的x轴位置
      let x = e.offsetX - widgetX;
      let y = e.offsetY - widgetY;

      if (i !== undefined) {
        x += this.list[i].x;
        y += this.list[i].y;
      }
      const newItem = ({
        id: currentId++,//key绑定id
        x,
        y,
      
        z: !this.list.length ? 0 : Math.max(...this.list.map(item => item.z)) + 1,//因为一开是空  所以给个默认值0
      
        ...currentWidget.default,
        // w:this.currentWidget.w,//盒子初始值宽
        // h:this.currentWidget.h,
        label: currentWidget.label,//文字
        component: currentWidget.component, // 新增的组件名
        type: currentWidget.type,//新增组件的类型
        styles: currentWidget.styles,//新增组件的样式
      
      });
      this.list.push(newItem)
      // this.clickDrageeronFoucus(newItem)

      this.$refs.contextMenu.close()//关闭右键菜单
    },
    //在小组件鼠标落下的时候
    onWidgetMouseDown(e, widget) {
      //获取   鼠标距离左侧的距离
      widgetX = e.offsetX;
      widgetY = e.offsetY;
      currentWidget = widget;//当前点击的图层数据赋值
    },
    //右键菜单打开事件
    onContextMenuOpen(e, item) {
      this.$refs.contextMenu.open(e);//打开右键菜单
      this.clickDrageeronFoucus(item);//右击的时候获取焦点
    },
    //封装去找最小值
    findBottomLayer(currentItem) {
      const minZ = Math.min(...this.list.map(item => item.z)) || 0//获取最小值
      if (currentItem.z === minZ) {
        alert('已经是最底层了')
        return false;//因为还有一个最上层,所以改为false
      }
      return minZ
    },
    //封装去找最大值
    findTopLayer(currentItem) {
      const maxZ = Math.max(...this.list.map(item => item.z)) || 0//获取最大值
      if (currentItem.z === maxZ) {
        alert('已经是最顶层了')
        return;
      }
      return maxZ
    },
    //右键菜单项-置顶
    onLayerTop() {
      //先找到数组当中被选中的那项
      const currentItem = this.list.find(item => item.focused)
      const maxZ = this.findTopLayer(currentItem)//找最大值
      if (!maxZ) {//判断最大值是否存在,注意0的时候是不存在的
        return;
      }
      currentItem.z = maxZ + 1;//
      // this.sortList()//排序
      // this.record()//记录
    },
    //右键菜单项-置底
    onLayerBottom() {
      //先找到数组当中被选中的那项
      const currentItem = this.list.find(item => item.focused)
      const minZ = this.findBottomLayer(currentItem)//找最小值
      if (minZ === false) {//0是一个默认值   // 如果 !minZ 相当于!0 就为true就不对了所以要写===false 
        return;
      }
      // 因为显示不了负数比如-1,最小只能为0,所以要做逻辑处理
      if (minZ - 1 < 0) {
        this.list = this.list.map(item => {
          item.z -= minZ - 1;
          return item
        })
        currentItem.z = 0
      } else {
        currentItem.z = minZ - 1
      }
      // this.sortList()//排序
      // this.record()//记录
    },
    // 左键点击,让当前项获取焦点,其他项取消焦点
    clickDrageeronFoucus(currentItem) {
        currentItem.focused = true;
        this.list.forEach((item)=>{
            item.focused = item.id === currentItem.id 
        })
    },
  }
}
</script>


<style>
body {
  margin: 0;
}

#app {
  display: flex;
  width: 100vw;
  height: 100vh;
  /* 可视高度的多少1vh=视窗高度的1% */
}

.sidebar {
  width: 200px;
  background: #e9e9e9;
}

.panel {
  flex: 1;
  background: #f6f6f6;
  position: relative;
  /* 给相对定位主要是因为 要根据panel盒子的左上角来进行绝对定位*/
}

.widget {
  width: 100px;
  height: 100px;
  outline: 1px solid red;
  font-size: 24px;
  text-align: center;
  line-height: 100px;
  margin: 24px;
}

.box {
  /* width: 100px;
  height: 100px; */
  /* 插件自带宽高 */
  /* outline: 1px solid blue; */
  outline: 1px solid rgba(0, 0, 0, 0);

  position: absolute;
}

.inner-widget {
  width: 100%;
  height: 100%;
}

.layer {
  width: 100%;
  height: 50px;
  line-height: 50px;
  background: #e9e9e9;
}

.layer:hover {
  background: #fff;
}

.currentbgm {
  background: #fff;
}

.sider {
  width: 200px;
  background: #e9e9e9;
}

.sider.right {
  width: 300px;
}

.standard-line {
  width: 2px;
  height: 100%;
  /* background: rgba(31, 29, 29, 1); */
  border-left: 2px #0f0f0f dashed;
  position: absolute;
  left: 200px;
}


.standard-line.correnct {
  /* background: red; */
  border-left: 2px red dashed;
}

#frame {
  position: absolute;
  outline: 2px dashed red;
}
</style>

6.效果展示

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值