1208 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			1208 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="layout" @click.prevent="isShowMenu = false, activeIndex = -1"
 | |
|        @contextmenu.prevent="isShowMenu = false, activeIndex = -1">
 | |
|     <div class="layout-header">
 | |
|       <div class="layout-header__left"></div>
 | |
|       <div class="layout-header__middle">
 | |
|         <h2>{{ dashboard.title }}</h2>
 | |
|       </div>
 | |
|       <div class="layout-header__right">
 | |
|         <span type="text" @click="isShowImg = true">素材</span>
 | |
|         <span type="text" @click="back">退出</span>
 | |
|         <span type="text" @click="save">保存</span>
 | |
|       </div>
 | |
|     </div>
 | |
|     <div class="layout-wrapper">
 | |
|       <div class="layout-left">
 | |
|         <h2>全部资产</h2>
 | |
|         <div class="layout-left__wrapper">
 | |
|           <div class="layout-left__left">
 | |
|             <div @click="subIndex = 0, parentIndex = index" :class="parentIndex === index ? 'active' : ''"
 | |
|                  v-for="(item, index) in components" :key="index">
 | |
|               <i class="iconfont iconqiyeguanli"></i>
 | |
|               <span>{{ item.label }}</span>
 | |
|             </div>
 | |
|           </div>
 | |
|           <div class="layout-left__middle">
 | |
|             <div>全部</div>
 | |
|             <div
 | |
|                 class="layout-left__middle--item"
 | |
|                 :class="subIndex === index ? 'active' : ''"
 | |
|                 @click="subIndex = index"
 | |
|                 v-for="(item, index) in components[parentIndex].list"
 | |
|                 :key="index">
 | |
|               {{ item.label }}
 | |
|             </div>
 | |
|           </div>
 | |
|           <div class="layout-left__right">
 | |
|             <div class="layout-left__right--item" v-for="(item, index) in widget" @click="clone(item)" :key="index">
 | |
|               <span class="tools-item-text">{{ item.label }}</span>
 | |
|               <img :src="item.thumb"/>
 | |
|             </div>
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div class="layout-middle">
 | |
|         <div class="canvas-wrapper">
 | |
|           <vue-ruler-tool
 | |
|               v-model="dashboard.presetLine"
 | |
|               class="vueRuler"
 | |
|               :step-length="50"
 | |
|               :parent="true"
 | |
|               :position="'relative'"
 | |
|               :is-scale-revise="true"
 | |
|               :visible.sync="dashboard.presetLineVisible">
 | |
|             <div
 | |
|                 id="workbench"
 | |
|                 class="workbench"
 | |
|                 :style="{
 | |
|                 transform: workbenchTransform,
 | |
|                 width: bigscreenWidth + 'px',
 | |
|                 height: bigscreenHeight + 'px'
 | |
|               }">
 | |
|               <ai-dv-wrapper style="height: 100%" :title="params.name" :theme="dashboard.theme">
 | |
|                 <div style="width: 100%; height: 100%">
 | |
|                   <AiDvBackground
 | |
|                       :theme="dashboard.theme"
 | |
|                       v-if="dashboard.backgroundImage.length || dashboard.theme === '1'"
 | |
|                       :src="dashboard.theme === '1' ? 'https://cdn.cunwuyun.cn/dvcp/dv/img/dj-bg.png' : dashboard.backgroundImage[0].url">
 | |
|                   </AiDvBackground>
 | |
|                   <vue-draggable-resizable
 | |
|                       :w="item.width"
 | |
|                       :h="item.height"
 | |
|                       :x="item.left"
 | |
|                       :y="item.top"
 | |
|                       :scale="heightScale"
 | |
|                       :z="item.zIndex || 0"
 | |
|                       :parent="true"
 | |
|                       :resizable="item.type !== 'display' || item.display === 'summary2' || item.display === 'summary3'"
 | |
|                       class-name-active="drag-active"
 | |
|                       :class="[activeIndex === index ? 'drag-active' : '']"
 | |
|                       class="draggable"
 | |
|                       @contextmenu.native.stop="e => onContextmenu(e, index)"
 | |
|                       @dragging="(x, y) => onDrag(x, y, index)"
 | |
|                       @resizing="(x, y, w, h) => onResizing(x, y, w, h, index, item.type)"
 | |
|                       @activated="onActivated(index)"
 | |
|                       @click.native.stop="activeIndex = index"
 | |
|                       v-for="(item, index) in componentList"
 | |
|                       :key="index">
 | |
|                     <div class="coordinate" v-show="activeIndex === index">
 | |
|                       <div class="coordinate-left"></div>
 | |
|                       <div class="coordinate-top"></div>
 | |
|                       <div class="coordinate-label">{{ item.left }}, {{ item.top }}</div>
 | |
|                     </div>
 | |
|                     <ai-dv-render :data="item" :theme="dashboard.theme" :index="index" :instance="instance" />
 | |
|                   </vue-draggable-resizable>
 | |
|                 </div>
 | |
|               </ai-dv-wrapper>
 | |
|             </div>
 | |
|           </vue-ruler-tool>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div class="layout-right" @click.stop>
 | |
|         <div class="layout-tab" v-if="activeIndex > -1">
 | |
|           <span @click="configIndex = 0" :class="[configIndex === 0 ? 'layout-tab__active' : '']">参数</span>
 | |
|           <span @click="configIndex = 1" :class="[configIndex === 1 ? 'layout-tab__active' : '']">数据</span>
 | |
|         </div>
 | |
|         <div class="layout-right__content" v-if="activeIndex > -1">
 | |
|           <div class="layout-right__content--wrapper" v-show="configIndex === 0">
 | |
|             <div class="layout-config__group">
 | |
|               <h2>基础设置</h2>
 | |
|               <div class="layout-config__item">
 | |
|                 <label>图表尺寸</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input-number size="mini" :min="0" v-model="currLayout.width"
 | |
|                                    controls-position="right"></el-input-number>
 | |
|                   <el-input-number size="mini" :min="0" v-model="currLayout.height"
 | |
|                                    controls-position="right"></el-input-number>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item">
 | |
|                 <label>图表位置</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input-number size="mini" :min="0" v-model="currLayout.left"
 | |
|                                    controls-position="right"></el-input-number>
 | |
|                   <el-input-number size="mini" :min="0" v-model="currLayout.top"
 | |
|                                    controls-position="right"></el-input-number>
 | |
|                 </div>
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
|           <div class="layout-right__content--wrapper" v-show="configIndex === 0">
 | |
|             <div class="layout-config__group">
 | |
|               <h2>组件设置</h2>
 | |
|               <div class="layout-config__item layout-config__item--input">
 | |
|                 <label>标题</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input v-model="currLayout.title" size="mini"></el-input>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item layout-config__item--input" v-if="currLayout.display === 'summary2'">
 | |
|                 <label>标题</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input v-model="currLayout.summaryTitle" size="mini"></el-input>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item" v-if="currLayout.type !== 'display'">
 | |
|                 <label>边框</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-select size="mini" v-model="currLayout.border" placeholder="请选择边框" clearable>
 | |
|                     <el-option
 | |
|                         v-for="(item, index) in borderList"
 | |
|                         :key="index"
 | |
|                         :label="item"
 | |
|                         :value="item">
 | |
|                     </el-option>
 | |
|                   </el-select>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item layout-config__item--input" v-if="currLayout.type === 'video'">
 | |
|                 <label>视频地址</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input v-model="currLayout.src" size="mini"></el-input>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item" v-if="currLayout.type === 'monitor'">
 | |
|                 <label>视频类型</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-select size="mini" v-model="currLayout.monitorType" placeholder="请选择" clearable>
 | |
|                     <el-option label="中国移动" value="cmcc"></el-option>
 | |
|                     <el-option label="海康威视" value="hik"></el-option>
 | |
|                     <el-option label="大华" value="dahua"></el-option>
 | |
|                     <el-option label="视联网" value="slw"></el-option>
 | |
|                   </el-select>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item" v-if="currLayout.type === 'table'">
 | |
|                 <label>显示排名</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-select size="mini" v-model="currLayout.isShowIndex" placeholder="请选择" clearable>
 | |
|                     <el-option
 | |
|                         v-for="(item, index) in tableStatus"
 | |
|                         :key="index"
 | |
|                         :label="item.label"
 | |
|                         :value="item.value">
 | |
|                     </el-option>
 | |
|                   </el-select>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <div class="layout-config__item" v-if="currLayout.type === 'table'">
 | |
|                 <label>表格行数</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-input-number size="mini" style="width: 232px" :min="0" v-model="currLayout.rowNum" controls-position="right"></el-input-number>
 | |
|                 </div>
 | |
|               </div>
 | |
|               <template v-if="currLayout.type === 'map'">
 | |
|                 <div class="layout-config__item">
 | |
|                   <label>遮罩层</label>
 | |
|                   <div class="layout-config__item--right">
 | |
|                     <el-select size="mini" v-model="currLayout.mask" placeholder="请选择" clearable>
 | |
|                       <el-option label="是" value="1"></el-option>
 | |
|                       <el-option label="否" value="2"></el-option>
 | |
|                     </el-select>
 | |
|                   </div>
 | |
|                 </div>
 | |
|                 <div class="layout-config__item">
 | |
|                   <label>选择地区</label>
 | |
|                   <div class="layout-config__item--right">
 | |
|                     <AiAreaGet :instance="instance" :valueLevel="3" v-model="currLayout.areaId" placeholder="请选择地区"></AiAreaGet>
 | |
|                   </div>
 | |
|                 </div>
 | |
|                 <div class="layout-config__item">
 | |
|                   <label>展示光轨</label>
 | |
|                   <div class="layout-config__item--right">
 | |
|                     <ai-select v-model="currLayout.pulseLines" :selectList="dict.getDict('yesOrNo')"/>
 | |
|                   </div>
 | |
|                 </div>
 | |
|                 <div class="layout-config__item layout-config__item--input">
 | |
|                   <label>地图样式</label>
 | |
|                   <div class="layout-config__item--right">
 | |
|                     <el-input size="mini" v-model="currLayout.mapStyle" clearable placeholder="请输入地图样式ID,从UI处获取.."/>
 | |
|                   </div>
 | |
|                 </div>
 | |
|               </template>
 | |
|               <div class="layout-config__item" v-if="currLayout.type === 'summary'">
 | |
|                 <label>数据汇总</label>
 | |
|                 <div class="layout-config__item--right">
 | |
|                   <el-select size="mini" v-model="currLayout.display" placeholder="请选择类型" clearable>
 | |
|                     <el-option
 | |
|                         v-for="(item, index) in summaryList"
 | |
|                         :key="index"
 | |
|                         :label="item"
 | |
|                         :value="item">
 | |
|                     </el-option>
 | |
|                   </el-select>
 | |
|                 </div>
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
|           <div class="layout-right__content--wrapper" v-show="configIndex === 1"
 | |
|                v-if="currLayout.type !== 'title' && currLayout.type !== 'video'">
 | |
|             <data-config
 | |
|                 ref="dataConfig"
 | |
|                 :instance="instance"
 | |
|                 :dict="dict"
 | |
|                 :urlPrefix="urlPrefix"
 | |
|                 :options="currLayout"
 | |
|                 @change="onChange('barChart')">
 | |
|             </data-config>
 | |
|           </div>
 | |
|         </div>
 | |
|         <div class="layout-page__setting layout-right__content" v-if="activeIndex === -1">
 | |
|           <h2>页面设置</h2>
 | |
|           <div class="layout-config__group">
 | |
|             <div class="layout-config__item">
 | |
|               <label>屏幕大小</label>
 | |
|               <div class="layout-config__item--right">
 | |
|                 <el-input-number size="mini" :min="0" v-model="bigscreenWidth" disabled
 | |
|                                  controls-position="right"></el-input-number>
 | |
|                 <el-input-number size="mini" :min="0" v-model="bigscreenHeight" disabled
 | |
|                                  controls-position="right"></el-input-number>
 | |
|               </div>
 | |
|             </div>
 | |
|             <div class="layout-config__item layout-config__item--input">
 | |
|               <label>屏幕标题</label>
 | |
|               <div class="layout-config__item--right">
 | |
|                 <el-input v-model="dashboard.title" size="mini"></el-input>
 | |
|               </div>
 | |
|             </div>
 | |
|             <div class="layout-config__item">
 | |
|               <label>主题设置</label>
 | |
|               <div class="layout-config__item--right">
 | |
|                 <el-select size="mini" v-model="dashboard.theme" placeholder="请选择">
 | |
|                   <el-option label="默认" value="0"></el-option>
 | |
|                   <el-option label="党建" value="1"></el-option>
 | |
|                 </el-select>
 | |
|               </div>
 | |
|             </div>
 | |
|             <div class="layout-config__item">
 | |
|               <label>背景图</label>
 | |
|               <div class="layout-config__item--right layout-config__item--bg">
 | |
|                 <ai-uploader v-model="dashboard.backgroundImage" :maxSize="2" :limit="1" :isShowTip="false"
 | |
|                              :instance="instance">
 | |
|                   <div slot="trigger" class="config-item__banner" v-if="!dashboard.backgroundImage.length">
 | |
|                     <img src="https://cdn.cunwuyun.cn/dvcp/dv/tpl/upload.png">
 | |
|                     <span>点击上传/修改</span>
 | |
|                   </div>
 | |
|                 </ai-uploader>
 | |
|               </div>
 | |
|             </div>
 | |
|             <div class="layout-config__item">
 | |
|               <label>重置</label>
 | |
|               <div class="layout-config__item--right layout-config__item--btn">
 | |
|                 <div @click="dashboard.backgroundImage = []">恢复默认背景</div>
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
|     </div>
 | |
|     <div
 | |
|         class="layout-menu"
 | |
|         v-if="activeIndex > -1 && isShowMenu"
 | |
|         :style="{top: menuY + 'px', left: menuX + 'px'}">
 | |
|       <div class="layout-menu__item" @click="removeLayer">
 | |
|         <span>删除图层</span>
 | |
|       </div>
 | |
|       <div class="layout-menu__item" @click="copyLayer">
 | |
|         <span>复制图层</span>
 | |
|       </div>
 | |
|       <div class="layout-menu__item" @click="setTop">
 | |
|         <span>置顶图层</span>
 | |
|       </div>
 | |
|       <div class="layout-menu__item" @click="setBottom">
 | |
|         <span>置底图层</span>
 | |
|       </div>
 | |
|     </div>
 | |
|     <ai-dialog :visible.sync="isShowImg" append-to-body title="素材" @onConfirm="isShowImg = false">
 | |
|       <el-button type="primary" style="margin-bottom: 20px" @click="isShowAddImg = true">添加素材</el-button>
 | |
|       <ai-table
 | |
|           :tableData="images"
 | |
|           :total="images.length"
 | |
|           :colConfigs="colConfigs"
 | |
|           @getList="() => {}">
 | |
|         <el-table-column slot="img" prop="素材地址" label="素材" align="center">
 | |
|         </el-table-column>
 | |
|         <el-table-column slot="options" width="140px" fixed="right" label="操作" align="center">
 | |
|           <template slot-scope="{ row, $index }">
 | |
|             <div class="table-options">
 | |
|               <el-button type="text" @click="removeImg($index)">删除</el-button>
 | |
|               <el-button type="text" @click="copy(row.url)">复制链接</el-button>
 | |
|             </div>
 | |
|           </template>
 | |
|         </el-table-column>
 | |
|       </ai-table>
 | |
|     </ai-dialog>
 | |
|     <ai-dialog :visible.sync="isShowAddImg" width="580px" append-to-body title="添加素材" @closed="form.images = []" @onConfirm="onImageConfirm">
 | |
|       <ai-uploader
 | |
|           :instance="instance"
 | |
|           v-model="form.images"
 | |
|           fileType="file"
 | |
|           :maxSize="100"
 | |
|           url="/admin/file/add-unlimited"
 | |
|           :limit="9">
 | |
|       </ai-uploader>
 | |
|     </ai-dialog>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| 
 | |
| import {components} from '../config'
 | |
| import VueRulerTool from 'vue-ruler-tool'
 | |
| import VueDraggableResizable from 'vue-draggable-resizable'
 | |
| import 'vue-draggable-resizable/dist/VueDraggableResizable.css'
 | |
| import DataConfig from './form/DataConfig.vue'
 | |
| 
 | |
| export default {
 | |
|   props: {
 | |
|     instance: Function,
 | |
|     dict: Object,
 | |
|     params: Object,
 | |
|     urlPrefix: String
 | |
|   },
 | |
| 
 | |
|   data() {
 | |
|     return {
 | |
|       colConfigs: [
 | |
|         {prop: 'url', label: '图片链接', showOverflowTooltip: false},
 | |
|         // {slot: 'img', label: '图片'}
 | |
|       ],
 | |
|       form: {
 | |
|         images: []
 | |
|       },
 | |
|       isShowAddImg: false,
 | |
|       isShowImg: false,
 | |
|       parentIndex: 0,
 | |
|       subIndex: 0,
 | |
|       sonIndex: 0,
 | |
|       leftIndex: 0,
 | |
|       rightIndex: 1,
 | |
|       configIndex: 0,
 | |
|       components,
 | |
|       bigscreenWidth: 1920,
 | |
|       bigscreenHeight: 1080,
 | |
|       widthPaddingTools: 18,
 | |
|       componentList: [],
 | |
|       dashboard: {
 | |
|         title: '大屏',
 | |
|         width: 1920,
 | |
|         height: 1080,
 | |
|         theme: '0',
 | |
|         backgroundColor: '',
 | |
|         backgroundImage: []
 | |
|       },
 | |
|       menuX: 0,
 | |
|       menuY: 0,
 | |
|       isShowMenu: false,
 | |
|       activeIndex: -1,
 | |
|       middleWidth: 0,
 | |
|       middleHeight: 0,
 | |
|       widthScale: 0,
 | |
|       heightScale: 0,
 | |
|       bigscreenScaleInWorkbench: 0,
 | |
|       tableStatus: [{
 | |
|         label: '是',
 | |
|         value: '1'
 | |
|       }, {
 | |
|         label: '否',
 | |
|         value: '0'
 | |
|       }],
 | |
|       images: [],
 | |
|       summaryList: ['summary0', 'summary1', 'summary2', 'summary3', 'summary4', 'summary6', 'summary5', 'summary7', 'summary8', 'summary9', 'summary10', 'summary11'],
 | |
|       borderList: ['border0', 'border1', 'border2', 'border3', 'border4', 'border5']
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   components: {
 | |
|     DataConfig,
 | |
|     VueRulerTool,
 | |
|     VueDraggableResizable,
 | |
|   },
 | |
| 
 | |
|   computed: {
 | |
|     workbenchTransform() {
 | |
|       return `scale(${this.heightScale})`
 | |
|     },
 | |
| 
 | |
|     currLayout() {
 | |
|       if (this.activeIndex === -1) return {}
 | |
|       return this.componentList[this.activeIndex]
 | |
|     },
 | |
| 
 | |
|     widget() {
 | |
|       if (this.components.length && this.components[this.parentIndex].list.length) {
 | |
|         return this.components[this.parentIndex].list[this.subIndex].list
 | |
|       }
 | |
| 
 | |
|       return []
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   mounted() {
 | |
|     if (this.params && this.params.id) {
 | |
|       this.getInfo(this.params.id)
 | |
|     }
 | |
|     this.$nextTick(() => {
 | |
|       this.middleWidth = document.querySelector('.layout-wrapper').offsetWidth - 670
 | |
|       this.middleHeight = document.querySelector('.layout-wrapper').offsetHeight
 | |
|       this.initCanvas()
 | |
|     })
 | |
|   },
 | |
| 
 | |
|   methods: {
 | |
|     bindEvent() {
 | |
|       this.middleWidth = document.querySelector('.layout-wrapper').offsetWidth - 670
 | |
|       this.middleHeight = document.querySelector('.layout-wrapper').offsetHeight
 | |
| 
 | |
|       this.$nextTick(() => {
 | |
|         this.initCanvas()
 | |
|       })
 | |
|     },
 | |
| 
 | |
|     copy(link) {
 | |
|       let oInput = document.createElement('input')
 | |
|       oInput.value = link
 | |
|       document.body.appendChild(oInput)
 | |
|       oInput.select()
 | |
|       document.execCommand('Copy')
 | |
|       this.$message({
 | |
|         message: '已复制',
 | |
|         type: 'success'
 | |
|       })
 | |
|       oInput.remove()
 | |
|     },
 | |
| 
 | |
|     removeImg(index) {
 | |
|       this.images.splice(index, 1)
 | |
|     },
 | |
| 
 | |
|     onImageConfirm() {
 | |
|       if (!this.form.images.length) {
 | |
|         return this.$message.error('请上传图片')
 | |
|       }
 | |
| 
 | |
|       this.images = [
 | |
|         ...this.images,
 | |
|         ...this.form.images
 | |
|       ]
 | |
| 
 | |
|       this.form.images = []
 | |
|       this.isShowAddImg = false
 | |
|     },
 | |
| 
 | |
|     setTop() {
 | |
|       const maxZindex = Math.max.apply(Math, this.componentList.map(item => {
 | |
|         return item.zIndex
 | |
|       }))
 | |
| 
 | |
|       this.$set(this.componentList[this.activeIndex], 'zIndex', maxZindex + 1)
 | |
|     },
 | |
| 
 | |
|     setBottom() {
 | |
|       const item = this.componentList[this.activeIndex]
 | |
|       this.componentList.splice(this.activeIndex, 1)
 | |
|       this.componentList.unshift(item)
 | |
|     },
 | |
| 
 | |
|     getInfo(id) {
 | |
|       this.instance.post(`${this.urlPrefix}/appdiylargescreen/queryLargeScreenDetailById?id=${id}`).then(res => {
 | |
|         if (res.code === 0) {
 | |
|           const data = JSON.parse(res.data.config)
 | |
|           this.componentList = data.config
 | |
|           this.dashboard = data.dashboard
 | |
|           this.images = data.images || []
 | |
|         }
 | |
|       })
 | |
|     },
 | |
| 
 | |
|     back() {
 | |
|       this.$emit('close')
 | |
|     },
 | |
| 
 | |
|     save() {
 | |
|       this.instance.post(`${this.urlPrefix}/appdiylargescreen/addOrUpdateLargeScreen`, {
 | |
|         config: JSON.stringify({
 | |
|           config: this.componentList,
 | |
|           dashboard: this.dashboard,
 | |
|           images: this.images
 | |
|         }),
 | |
|         status: 1,
 | |
|         title: this.dashboard.title,
 | |
|         id: this.params.id || ''
 | |
|       }).then(res => {
 | |
|         if (res.code == 0) {
 | |
|           this.$message.success('保存成功')
 | |
|           this.$emit('change', res.data)
 | |
|           this.back()
 | |
|         }
 | |
|       })
 | |
|     },
 | |
| 
 | |
|     clone(e) {
 | |
|       this.componentList.push(this.deepClone(e))
 | |
|     },
 | |
| 
 | |
|     onChange(e) {
 | |
|       if (e.indexOf('Chart') > -1) {
 | |
|         this.$refs[`chart${this.activeIndex}`][0].refresh()
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     deepClone(data, hash = new WeakMap()) {
 | |
|       if (typeof data !== 'object' || data === null) {
 | |
|         throw new TypeError('传入参数不是对象')
 | |
|       }
 | |
|       if (hash.has(data)) {
 | |
|         return hash.get(data)
 | |
|       }
 | |
|       let newData = {}
 | |
|       const dataKeys = Object.keys(data)
 | |
|       dataKeys.forEach(value => {
 | |
|         const currentDataValue = data[value]
 | |
|         if (typeof currentDataValue !== "object" || currentDataValue === null) {
 | |
|           newData[value] = currentDataValue
 | |
|         } else if (Array.isArray(currentDataValue)) {
 | |
|           newData[value] = [...currentDataValue]
 | |
|         } else if (currentDataValue instanceof Set) {
 | |
|           newData[value] = new Set([...currentDataValue])
 | |
|         } else if (currentDataValue instanceof Map) {
 | |
|           newData[value] = new Map([...currentDataValue])
 | |
|         } else {
 | |
|           hash.set(data, data)
 | |
|           newData[value] = this.deepClone(currentDataValue, hash)
 | |
|         }
 | |
|       })
 | |
|       return newData
 | |
|     },
 | |
| 
 | |
|     onContextmenu(e, index) {
 | |
|       this.menuX = e.clientX + 10
 | |
|       this.menuY = e.clientY + 10
 | |
|       this.activeIndex = index
 | |
|       this.isShowMenu = true
 | |
| 
 | |
|       e.preventDefault()
 | |
|     },
 | |
| 
 | |
|     copyLayer() {
 | |
|       const layer = this.deepClone(this.componentList[this.activeIndex])
 | |
|       this.componentList.push(layer)
 | |
|     },
 | |
| 
 | |
|     removeLayer() {
 | |
|       this.componentList.splice(this.activeIndex, 1)
 | |
|       this.activeIndex = -1
 | |
|     },
 | |
| 
 | |
|     onActivated(index) {
 | |
|       this.activeIndex = index
 | |
|     },
 | |
| 
 | |
| 
 | |
|     onDrag(x, y, index) {
 | |
|       this.$set(this.componentList[index], 'left', x)
 | |
|       this.$set(this.componentList[index], 'top', y)
 | |
|     },
 | |
| 
 | |
|     onResizing(x, y, w, h, index, type) {
 | |
|       this.$set(this.componentList[index], 'left', x)
 | |
|       this.$set(this.componentList[index], 'top', y)
 | |
|       this.$set(this.componentList[index], 'width', w)
 | |
|       this.$set(this.componentList[index], 'height', h)
 | |
| 
 | |
|       if (type.indexOf('Chart') > -1) {
 | |
|         // this.$refs[`chart${index}`][0].watchResize()
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     initCanvas() {
 | |
|       var widthScale = (this.middleWidth - this.widthPaddingTools) / this.bigscreenWidth
 | |
|       var heightScale = (this.middleHeight - this.widthPaddingTools) / this.bigscreenHeight
 | |
|       this.heightScale = Math.min(widthScale, heightScale)
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
| .layout {
 | |
|   position: fixed;
 | |
|   left: 0;
 | |
|   top: 0;
 | |
|   z-index: 111;
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   color: #fff;
 | |
|   background: #1d1e1f;
 | |
| 
 | |
|   ::v-deep .dv-scroll-board {
 | |
|     height: calc(100%) !important;
 | |
| 
 | |
|     .header-item {
 | |
|       color: rgba(255, 255, 255, 0.8);
 | |
|       font-size: 16px;
 | |
|     }
 | |
| 
 | |
|     .index {
 | |
|       display: inline-block;
 | |
|       width: 26px;
 | |
|       height: 26px;
 | |
|       line-height: 26px;
 | |
|       font-size: 16px;
 | |
|       background-color: #4F57FF !important;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .layout-header {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     justify-content: space-between;
 | |
|     height: 50px;
 | |
|     background: #1D1E1F;
 | |
| 
 | |
|     h2 {
 | |
|       font-size: 16px;
 | |
|       color: #fff;
 | |
|       font-weight: 500;
 | |
|     }
 | |
| 
 | |
|     .layout-header__right {
 | |
|       display: flex;
 | |
|       align-items: center;
 | |
|       justify-content: flex-end;
 | |
|       height: 40px;
 | |
|       padding: 0 20px;
 | |
| 
 | |
|       span {
 | |
|         width: 60px;
 | |
|         height: 50px;
 | |
|         line-height: 50px;
 | |
|         font-size: 14px;
 | |
|         text-align: center;
 | |
|         color: #fff;
 | |
|         transition: all 0.3s ease;
 | |
|         cursor: pointer;
 | |
|         user-select: none;
 | |
|         background: #191d22;
 | |
| 
 | |
|         &:hover {
 | |
|           opacity: 0.8;
 | |
|           background: #191d22;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .layout-wrapper {
 | |
|     display: flex;
 | |
|     height: calc(100% - 50px);
 | |
|   }
 | |
| 
 | |
|   .layout-page__setting {
 | |
|     & > h2 {
 | |
|       height: 40px;
 | |
|       line-height: 40px;
 | |
|       padding: 0 10px;
 | |
|       font-size: 14px;
 | |
|       background: #232B35;
 | |
|     }
 | |
| 
 | |
|     .layout-config__item--bg {
 | |
|       width: 232px;
 | |
|       height: 120px;
 | |
|       background: #262C33;
 | |
|       border: 1px solid #030411;
 | |
|       overflow: hidden;
 | |
|       user-select: none;
 | |
| 
 | |
|       &:hover {
 | |
|         opacity: 0.8;
 | |
|       }
 | |
| 
 | |
|       ::v-deep .el-upload-list--picture-card, ::v-deep.el-upload-list__item-thumbnail, ::v-deep .uploader {
 | |
|         width: 232px;
 | |
|         height: 120px;
 | |
|       }
 | |
| 
 | |
|       ::v-deep .el-upload-list--picture-card .el-upload-list__item {
 | |
|         width: 232px;
 | |
|         height: 120px;
 | |
|       }
 | |
| 
 | |
|       ::v-deep .el-upload--picture-card {
 | |
|         background-color: transparent;
 | |
|       }
 | |
| 
 | |
|       .config-item__banner {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         justify-content: center;
 | |
|         flex-direction: column;
 | |
|         width: 232px;
 | |
|         height: 120px;
 | |
|         line-height: 1;
 | |
| 
 | |
|         span {
 | |
|           margin-top: 4px;
 | |
|           color: #CBDBE7;
 | |
|           font-size: 12px;
 | |
|         }
 | |
| 
 | |
|         img {
 | |
|           width: 32px;
 | |
|           height: 32px;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ::v-deep #dv-full-screen-container {
 | |
|     width: 100% !important;
 | |
|     height: 100% !important;
 | |
|   }
 | |
| 
 | |
|   ::-webkit-scrollbar {
 | |
|     width: 5px;
 | |
|     height: 14px;
 | |
|   }
 | |
| 
 | |
|   ::-webkit-scrollbar-corner {
 | |
|     background: transparent;
 | |
|   }
 | |
| 
 | |
|   ::-webkit-scrollbar-thumb {
 | |
|     min-height: 20px;
 | |
|     background-clip: content-box;
 | |
|     box-shadow: 0 0 0 5px rgba(116, 148, 170, 0.5) inset;
 | |
|   }
 | |
| 
 | |
|   ::-webkit-scrollbar-track {
 | |
|     box-shadow: 1px 1px 5px rgba(116, 148, 170, 0.5) inset;
 | |
|   }
 | |
| 
 | |
|   .coordinate {
 | |
|     div {
 | |
|       position: absolute;
 | |
| 
 | |
|       &.coordinate-label {
 | |
|         left: -10px;
 | |
|         top: -10px;
 | |
|         font-size: 20px;
 | |
|         color: #09f;
 | |
|         transform: translate(-100%, -100%);
 | |
|       }
 | |
| 
 | |
|       &.coordinate-left {
 | |
|         left: 0;
 | |
|         width: 100000px;
 | |
|         height: 0;
 | |
|         border: 1px dashed #09f;
 | |
|         transform: translateX(-100%);
 | |
|       }
 | |
| 
 | |
|       &.coordinate-top {
 | |
|         left: 0;
 | |
|         height: 100000px;
 | |
|         width: 0;
 | |
|         border: 1px dashed #09f;
 | |
|         transform: translateY(-100%);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   * {
 | |
|     box-sizing: border-box;
 | |
|   }
 | |
| 
 | |
|   .layout-tab {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     height: 50px;
 | |
| 
 | |
|     span {
 | |
|       flex: 1;
 | |
|       height: 100%;
 | |
|       line-height: 50px;
 | |
|       text-align: center;
 | |
|       color: #fff;
 | |
|       font-size: 15px;
 | |
|       cursor: pointer;
 | |
|       user-select: none;
 | |
|       background: #14161A;
 | |
|       border-top: 2px solid transparent;
 | |
|       transition: all 0.3s ease;
 | |
| 
 | |
|       &.layout-tab__active {
 | |
|         background: #1D2127;
 | |
|         color: #2266FF;
 | |
|         border-top: 2px solid #3383FF;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .layout-menu {
 | |
|     position: fixed;
 | |
|     top: 0;
 | |
|     left: 0;
 | |
|     z-index: 1100;
 | |
|     width: 150px;
 | |
|     user-select: none;
 | |
| 
 | |
|     .layout-menu__item {
 | |
|       display: flex;
 | |
|       align-items: center;
 | |
|       height: 32px;
 | |
|       line-height: 32px;
 | |
|       padding: 0 20px;
 | |
|       color: #bcc9d4;
 | |
|       font-size: 14px;
 | |
|       background: #2b323b;
 | |
|       cursor: pointer;
 | |
|       transition: all 0.3s ease;
 | |
|       border-left: 2px solid transparent;
 | |
| 
 | |
|       &:hover {
 | |
|         background: #1d262e;
 | |
|         color: #2681ff;
 | |
|         border-left: 2px solid #2681ff;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   * {
 | |
|     box-sizing: border-box;
 | |
|   }
 | |
| 
 | |
|   .layout-left {
 | |
|     width: 330px;
 | |
|     height: 100%;
 | |
|     flex-shrink: 0;
 | |
|     background: #1D2127;
 | |
| 
 | |
|     & > h2 {
 | |
|       height: 40px;
 | |
|       line-height: 40px;
 | |
|       padding: 0 10px;
 | |
|       background: #232B35;
 | |
|       font-size: 14px;
 | |
|       color: #fff;
 | |
|       box-shadow: -1px 0px 0px 0px #000000;
 | |
|     }
 | |
| 
 | |
|     .layout-left__middle {
 | |
|       width: 80px;
 | |
|       height: 100%;
 | |
|       background: #11151A;
 | |
|       overflow-y: auto;
 | |
| 
 | |
|       & > div {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         justify-content: center;
 | |
|         height: 50px;
 | |
|         user-select: none;
 | |
|         transition: all 0.3s ease;
 | |
| 
 | |
|         &.layout-left__middle--item {
 | |
|           cursor: pointer;
 | |
|         }
 | |
| 
 | |
|         &.active, &.layout-left__middle--item:hover {
 | |
|           color: #3383FF;
 | |
|           background: #0A0B0D;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .layout-left__right {
 | |
|       flex: 1;
 | |
|       padding: 0 10px;
 | |
|       background: #0A0B0D;
 | |
| 
 | |
|       .layout-left__right--item {
 | |
|         margin-top: 10px;
 | |
|         background: #212326;
 | |
| 
 | |
|         &:hover {
 | |
|           opacity: 0.8;
 | |
|           cursor: pointer;
 | |
|         }
 | |
| 
 | |
|         img {
 | |
|           width: 180px;
 | |
|           height: 80px;
 | |
|         }
 | |
| 
 | |
|         span {
 | |
|           display: block;
 | |
|           height: 24px;
 | |
|           line-height: 24px;
 | |
|           padding: 0 10px;
 | |
|           font-size: 12px;
 | |
|           background: #212326;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .layout-left__wrapper {
 | |
|       display: flex;
 | |
|       height: calc(100% - 40px);
 | |
|       overflow: hidden;
 | |
|     }
 | |
| 
 | |
|     .layout-left__left {
 | |
|       width: 50px;
 | |
|       background: #1D2126;
 | |
| 
 | |
|       div {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         justify-content: center;
 | |
|         flex-direction: column;
 | |
|         height: 64px;
 | |
|         font-size: 12px;
 | |
|         color: #fff;
 | |
|         cursor: pointer;
 | |
|         transition: all 0.3s ease;
 | |
|         user-select: none;
 | |
|         border-left: 2px solid transparent;
 | |
| 
 | |
|         &.active, &:hover {
 | |
|           color: #3383FF;
 | |
|           border-left: 2px solid #3383FF;
 | |
|           background: #11151A;
 | |
|         }
 | |
| 
 | |
|         span {
 | |
|           margin-top: 6px;
 | |
|           font-size: 12px;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .components-item {
 | |
|       display: flex;
 | |
|       align-items: center;
 | |
|       justify-content: center;
 | |
|       height: 48px;
 | |
|       color: #bfcbd9;
 | |
|       font-size: 14px;
 | |
|       cursor: pointer;
 | |
|       user-select: none;
 | |
| 
 | |
|       .tools-item-icon {
 | |
|         color: #409eff;
 | |
|         margin-right: 10px;
 | |
|         width: 53px;
 | |
|         height: 30px;
 | |
|         line-height: 30px;
 | |
|         text-align: center;
 | |
|         display: block;
 | |
|         border: 1px solid #3a4659;
 | |
|         background: #282a30;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .layout-middle {
 | |
|     flex: 1;
 | |
|     background: #000;
 | |
| 
 | |
|     .canvas-wrapper {
 | |
|       position: relative;
 | |
|       -webkit-transform-origin: 0 0;
 | |
|       transform-origin: 0 0;
 | |
|       -webkit-box-sizing: border-box;
 | |
|       box-sizing: border-box;
 | |
|       height: 100%;
 | |
|       margin: 0;
 | |
|       padding: 0;
 | |
| 
 | |
|       .vueRuler {
 | |
|         width: 100%;
 | |
|         padding: 18px 0 0 18px;
 | |
| 
 | |
|         ::v-deep .vue-ruler-ref-line-v, ::v-deep .vue-ruler-ref-line-h {
 | |
|           display: none;
 | |
|         }
 | |
| 
 | |
|         ::v-deep .vue-ruler-v {
 | |
|           .n {
 | |
|             transform: rotate(180deg);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       .workbench {
 | |
|         background-color: #1e1e1e;
 | |
|         position: relative;
 | |
|         -webkit-user-select: none;
 | |
|         -moz-user-select: none;
 | |
|         -ms-user-select: none;
 | |
|         user-select: none;
 | |
|         -webkit-transform-origin: 0 0;
 | |
|         transform-origin: 0 0;
 | |
|         margin: 0;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     ::v-deep .handle {
 | |
|       border-radius: 100%;
 | |
|       border: none;
 | |
|       background-color: #09f;
 | |
|       width: 14px;
 | |
|       height: 14px;
 | |
| 
 | |
|       &.handle-tl, &.handle-tm {
 | |
|         transform: translate(2px, 2px);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .draggable {
 | |
|       position: absolute;
 | |
|       border: none;
 | |
| 
 | |
|       &::after {
 | |
|         position: absolute;
 | |
|         left: 0;
 | |
|         top: 0;
 | |
|         z-index: 1;
 | |
|         width: 100%;
 | |
|         height: 100%;
 | |
|         cursor: move;
 | |
|         content: ' ';
 | |
|       }
 | |
| 
 | |
|       &:hover {
 | |
|         cursor: move;
 | |
|         border: 1px dashed #09f;
 | |
|         background-color: rgba(115, 170, 229, 0.3);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .drag-active {
 | |
|       cursor: move;
 | |
|       border: 1px dashed #09f;
 | |
|       background-color: rgba(115, 170, 229, .3);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .layout-right {
 | |
|     width: 340px;
 | |
|     height: 100%;
 | |
|     flex-shrink: 0;
 | |
|     border-left: 1px solid #000000;
 | |
|     background: #1D2127;
 | |
| 
 | |
|     ::v-deep .el-input-number {
 | |
|       width: 106px;
 | |
|       margin-right: 20px;
 | |
| 
 | |
|       &:last-child {
 | |
|         margin-right: 0;
 | |
|       }
 | |
| 
 | |
|       .el-input-number__decrease, .el-input-number__increase {
 | |
|         background: #262C33;
 | |
|         color: #fff;
 | |
|         border-color: #030411;
 | |
|       }
 | |
| 
 | |
|       input {
 | |
|         background: #262C33;
 | |
|         font-size: 12px;
 | |
|         color: #fff;
 | |
|         border: 1px solid #030411;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .layout-right__content {
 | |
|       height: calc(100% - 40px);
 | |
|       overflow-y: overlay;
 | |
|       overflow-x: hidden;
 | |
| 
 | |
|       .layout-config__group {
 | |
|         padding: 10px 10px 20px;
 | |
|         border-bottom: 1px solid #000000;
 | |
| 
 | |
|         & > h2 {
 | |
|           margin-bottom: 20px;
 | |
|           color: #FFFFFF;
 | |
|           font-size: 15px;
 | |
|           font-weight: 700;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       .layout-config__item {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         justify-content: space-between;
 | |
|         margin-bottom: 10px;
 | |
| 
 | |
|         &:last-child {
 | |
|           margin-bottom: 0;
 | |
|         }
 | |
| 
 | |
|         ::v-deep .el-select, ::v-deep .el-cascader {
 | |
|           color: #fff;
 | |
|           background: transparent;
 | |
| 
 | |
|           input {
 | |
|             width: 232px;
 | |
|             background: #262C33;
 | |
|             font-size: 12px;
 | |
|             color: #fff;
 | |
|             border: 1px solid #030411;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         label {
 | |
|           flex-shrink: 0;
 | |
|           width: 60px;
 | |
|           color: #FFFFFF;
 | |
|           font-size: 12px;
 | |
|           text-align: right;
 | |
|         }
 | |
| 
 | |
|         .layout-config__item--btn {
 | |
|           flex: 1;
 | |
|           margin-left: 28px;
 | |
| 
 | |
|           div {
 | |
|             width: 106px;
 | |
|             height: 24px;
 | |
|             line-height: 24px;
 | |
|             text-align: center;
 | |
|             font-size: 12px;
 | |
|             color: #3383FF;
 | |
|             background: #1D2127;
 | |
|             border: 1px solid #3383FF;
 | |
|             cursor: pointer;
 | |
|             user-select: none;
 | |
| 
 | |
|             &:hover {
 | |
|               opacity: 0.8;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         &.layout-config__item--input ::v-deep input {
 | |
|           width: 232px;
 | |
|           background: #262C33;
 | |
|           font-size: 12px;
 | |
|           color: #fff;
 | |
|           border: 1px solid #030411;
 | |
|         }
 | |
| 
 | |
|         .layout-config__item--right {
 | |
|           display: flex;
 | |
|           align-items: center;
 | |
|           justify-content: space-between;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </style>
 |