You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

606 lines
15 KiB
Vue

<!--
* @description: 左侧组件列表
* @Date: 2022-05-24 15:35:07
* @Author: xingheng
-->
<template>
<transition name="slide-fade">
<div
class="bs-left-panel"
@click.stop
>
<div
:class="fold ? 'page-left page-left-fold' : 'page-left'"
:style="{ height }"
>
<el-tabs
v-model="activeName"
tab-position="left"
style="height: 200px"
class="left-tabs-box"
@tab-click="tabClick"
>
<el-tab-pane
name="default"
@click.native="changeActiveCode('')"
>
<span
slot="label"
class="menu-slot"
name="default"
@click="toggleSidebar"
>
<i
class="iconfont-bigscreen menu-icon"
:class="fold ? 'icon-zhankaicaidan' : 'icon-shouqicaidan'"
/>
<span class="menu-title-span">{{ foldText }}</span>
</span>
</el-tab-pane>
<el-tab-pane name="layer">
<div
slot="label"
class="menu-slot"
name="layer"
@dbclick="toggleSidebar"
>
<i
:class="['iconfont-bigscreen', 'icon-layer']"
class="menu-icon"
/>
<span class="menu-title-span">图层</span>
</div>
<div class="page-left-content">
<div class="page-left-content-title">
<div class="page-left-content-title-text">
图层
</div>
</div>
<div class="page-left-content-components">
<el-scrollbar>
<LayerList @openRightPanel="openRightPanel" />
</el-scrollbar>
</div>
</div>
</el-tab-pane>
<el-tab-pane
v-for="menu in menuList"
:key="menu.id"
:name="menu.name"
@click.stop.native="
fold = false
changeActiveCode('')
"
>
<div
slot="label"
class="menu-slot"
@dbclick="toggleSidebar"
>
<i
:class="['iconfont-bigscreen', menu.icon]"
class="menu-icon"
/>
<span class="menu-title-span">{{ menu.title }}</span>
</div>
<div class="page-left-content">
<div class="page-left-content-title">
<div class="page-left-content-title-text">
{{ menu.title }}
</div>
</div>
<el-scrollbar>
<div class="page-left-content-components">
<div class="draggable chat-list">
<div
v-for="element in menu.components"
:key="element.type + element.name"
:class="element.component
? 'item menu-component drag-node'
: 'item drag-node'
"
draggable="true"
:data-type="element.type"
:data-name="element.name"
>
<div class="component-name">
{{ element.title || element.name }}
</div>
<div
class="img_dispaly chooseDragNode"
@click.stop="addComponent(element)"
>
<!-- <svg
v-if="element.icon"
class="icon-svg"
aria-hidden="true"
>
<use :xlink:href="`#icon-a-${element.icon}`" />
</svg> -->
<icon-svg
v-if="element.icon"
:name="element.icon"
class="page-opt-list-icon"
/>
<img
v-else-if="element.img"
:src="element.img"
class="page-opt-list-img"
alt=""
>
<component
:is="element.component"
:key="new Date().getTime() + 1"
class="page-opt-list-component"
/>
</div>
</div>
</div>
</div>
</el-scrollbar>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</transition>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep'
import basicComponents from 'data-room-ui/js/config/basicComponentsConfig'
import g2PlotComponents, { getCustomPlots } from '../G2Plots/plotList'
import echartsComponents from '../Echarts/echartList'
import borderComponents from 'data-room-ui/js/config/borderComponentsConfig'
import decorationComponents from 'data-room-ui/js/config/decorationComponentsConfig'
import LayerList from './LayerList/index.vue'
import { mapMutations } from 'vuex'
import IconSvg from 'data-room-ui/SvgIcon'
import { customSerialize } from 'data-room-ui/js/utils/jsonSerialize.js'
export default {
name: 'PageLeftPanel',
components: {
LayerList,
IconSvg
},
props: {
headerShow: {
type: Boolean,
default: true
},
height: {
type: String,
default: '100vh'
}
},
data () {
return {
echartsComponents,
g2PlotComponents,
activeName: 'chart', // 设置左侧tab栏的默认值
fold: false, // 控制左侧菜单栏伸缩
currentTab: 'basic',
menuList: [
{
id: 1,
name: 'chart',
title: '基础',
icon: 'icon-zujian',
components: basicComponents
},
{
id: 2,
name: 'g2PlotComponents',
title: '图表',
icon: 'icon-jichushuju',
components: this.g2PlotComponents
},
{
id: 7,
name: 'echart',
title: '3D',
icon: 'icon-jichushuju',
components: this.echartsComponents
},
{
id: 3,
name: 'dataV',
title: '边框',
icon: 'icon-border-outer',
components: borderComponents
},
{
id: 4,
name: 'decoration',
title: '装饰',
icon: 'icon-a-1',
components: decorationComponents
},
{
id: 5,
name: 'source',
title: '资源',
icon: 'icon-tupian',
components: []
},
{
id: 6,
name: 'component',
title: '组件',
icon: 'icon-zujian1',
components: ''
}
],
currentActive: 'chart'
}
},
computed: {
// 获取当前类型的组件
currentComponentList () {
return this.componentList.filter((item) => item.type === this.currentTab)
},
foldText () {
return this.fold ? '展开' : '收起'
}
},
watch: {
fold (isExpand) {
if (isExpand && this.activeName === 'default') {
this.activeName = 'chart'
}
}
},
created () {
this.initList()
this.g2PlotComponents = [...this.g2PlotComponents, ...getCustomPlots()]
this.menuList[1].components = this.g2PlotComponents
this.menuList[2].components = this.echartsComponents
},
mounted () {
this.nodeDrag()
},
methods: {
...mapMutations('bigScreen', ['changeActiveCode']),
nodeDrag () {
this.$nextTick(() => {
const nodes = document.querySelectorAll('.drag-node')
nodes.forEach((node) => {
node.addEventListener('dragstart', (event) => {
const type = node.getAttribute('data-type')
const name = node.getAttribute('data-name')
// 从menuList中获取当前拖拽的组件
const element = this.menuList
.find((item) => item.name === this.activeName)
?.components.find(
(item) => item.type === type && item.name === name
)
/* 设置拖拽传输数据 */
event.dataTransfer.setData(
'dragComponent',
customSerialize({
...element,
offsetX: event.offsetX,
offsetY: event.offsetY
})
)
})
})
// 阻止默认动作
document.addEventListener(
'drop',
(e) => {
e.preventDefault()
},
false
)
})
},
onClone (e) {
return cloneDeep(e)
},
onStart (e) {
// this.$emit('onStart', e)
},
// 拖拽组件时触发
onEnd (e) { },
// 点击左侧组件时触发
addComponent (element) {
this.$store.commit('bigScreen/changeActiveItem', element)
this.$emit('addComponent', element)
},
// 初始化
initList () { },
// 点击tab标签
tabClick (tab) {
this.nodeDrag()
if (tab.index !== '0') {
this.fold = false
this.currentActive = this.activeName
}
if (tab.name === 'source') {
this.fold = true
this.$emit('toggleLeftSidebar')
this.$emit('openResource')
this.$emit('toggleLeftSidebar')
}
if (tab.name === 'component') {
this.fold = true
this.$emit('toggleLeftSidebar')
this.$emit('openComponent')
}
},
toggleSidebar () {
this.fold = !this.fold
this.$emit('toggleLeftSidebar')
setTimeout(() => {
this.activeName = this.currentActive
})
},
openRightPanel (config) {
this.$emit('openRightPanel', config)
}
}
}
</script>
<style lang="scss" scoped>
@import '../BigScreenDesign/fonts/iconfont.css';
.bs-left-panel {
display: flex;
background-color: var(--bs-background-1);
.bs-folder-wrap {
width: 20px;
position: relative;
i {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
font-size: 20px;
color: #fff;
cursor: pointer;
z-index: 1;
}
&:hover {
background: rgba(143, 225, 255, 0.1);
}
}
.page-left {
box-sizing: border-box;
>* {
color: #fff;
}
.iconfont-bigscreen {
color: #fff;
}
.flexible {
width: 45px;
/* border-right: 1px solid #ccc; */
text-align: center;
}
.el-tabs {
width: 250;
position: relative;
height: 100% !important;
overflow: visible;
.is-active {
.iconfont-bigscreen {
color: var(--bs-el-color-primary);
}
.menu-title-span {
color: var(--bs-el-color-primary);
}
}
.el-tab-pane {
height: 100%;
}
.page-left-content {
height: 100%;
}
::v-deep .el-tabs__content {
height: 100%;
width: 160px;
.page-left-content-title {
background-color: var(--bs-background-2);
color: var(--bs-el-title);
font-size: 14px;
margin: 8px;
padding: 8px 0;
.page-left-content-title-text {
/*border-left: 4px solid #007aff;*/
position: relative;
padding-left: 12px;
&:after {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
content: '';
width: 4px;
height: 14px;
background-color: var(--bs-el-color-primary);
}
}
}
.el-scrollbar__view {
height: calc(100vh - 55px);
}
.page-left-content-components {
width: 100%;
text-align: center;
padding-bottom: 20px;
margin-bottom: 20px;
.draggable {
display: flex;
flex-wrap: wrap;
cursor: pointer;
box-sizing: border-box;
justify-content: center;
padding: 8px;
cursor: move;
.item {
width: 100%;
background: var(--bs-background-2);
margin-bottom: 8px;
.component-name {
background: var(--bs-el-background-3);
color: var(--bs-el-title);
font-size: 12px;
padding: 4px 8px;
text-align: left;
}
.sampleImg {
margin: 0 auto;
width: 102px;
height: 73px;
display: block;
}
.img_dispaly {
padding: 8px 0;
margin: 0 auto;
text-align: center;
width: 120px;
.icon-svg {
width: 60px !important;
height: 60px !important;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
img {
height: 60px;
max-width: 100%;
}
}
}
.menu-component {
.page-opt-list-component {
width: 102px;
height: 75px;
margin: 0 auto;
}
.img_dispaly {
height: 80px;
}
}
}
}
}
}
::v-deep .el-tabs__header {
width: 45px;
height: 100%;
margin-right: 0 !important;
}
::v-deep .el-tabs--left .el-tabs__nav-wrap.is-left::after {
width: 0 !important;
}
.el-tabs__active-bar {
transform: none !important;
height: 0 !important;
}
.el-tabs__nav-wrap.is-left::after {
left: 0;
}
.el-tabs__nav-wrap {
height: 100%;
/* border-right: 1px solid #ccc; */
}
::v-deep .el-tabs__nav-scroll {
background-color: var(--bs-background-2);
}
}
.page-left-fold {
width: 45px;
overflow: hidden;
/* border-right: 1px solid #ccc; */
.el-tabs__content {
border: none;
}
}
.left-tabs-box {
::v-deep .el-tabs__item {
height: 70px !important;
.menu-slot {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
color: #bcc9d4;
.menu-icon {
height: 20px;
}
.menu-title-span {
display: block;
width: 100%;
font-size: 12px;
text-align: center;
}
}
}
}
}
.slide-fade-enter-active {
transition: all 0.3s ease;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to
/* .slide-fade-leave-active for below version 2.1.8 */
{
transform: translateX(10px);
opacity: 0;
}
::v-deep .el-tabs__item.is-left {
text-align: center;
padding: 0;
}
</style>