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.

649 lines
18 KiB
Vue

<template>
<div
v-if="hasPermission"
class="bs-page-design-wrap"
>
<PageTopSetting
v-show="headerShow"
ref="PageTopSetting"
:right-fold="rightVisiable"
@updateRightVisiable="updateRightVisiable"
@showPageInfo="showPageInfo"
@changeZoom="changeScreenZoom"
@empty="empty"
/>
<div class="drag-wrap">
<!-- 左侧面板 -->
<LeftPanel
:header-show="headerShow"
:height="height"
@openRightPanel="openRightPanel"
@openResource="initDialog"
@openComponent="openComponent"
@toggleLeftSidebar="toggleLeftSidebar"
/>
<!-- 中间组件展示面板 -->
<div
v-loading="pageLoading"
class="grid-wrap-box"
:style="{
height: 'calc(100vh - 48px)'
}"
tabindex="1000"
@keydown="designKeydown"
>
<div
id="minimap"
class="minimap"
>
<div class="mapHeader" id="mapHeader">
<div>
<span>小地图</span>
</div>
<div class="showMap" @click="showMinimap">
<i class="el-icon-arrow-down" style="width:20px;height:20px;color:#fff;" v-if="!mapShow"/>
<i class="el-icon-arrow-up" style="width:20px;height:20px;color:#fff;" v-if="mapShow"/>
</div>
</div>
<div
id="selectWin"
class="selectWin"
v-show="mapShow"
>
<div
id="selectionWin"
class="selectionWin"
/>
</div>
</div>
<SketchDesignRuler
ref="Rules"
:width="3000"
:height="3000"
:page-width="pageConfig.w"
:page-height="pageConfig.h"
@changeStart="changeStart"
>
<MouseSelect
:offset-x="offset.x"
:offset-y="offset.y"
@selectArea="onSelectArea"
>
<Render
ref="Render"
:class="{
'grid-bg': hasGrid
}"
@openRightPanel="openRightPanel"
@openDataViewDialog="openDataViewDialog"
/>
</MouseSelect>
</SketchDesignRuler>
<!-- <div class="footer-tools-bar">
<el-slider
class="bs-slider-wrap"
:value="zoom"
:min="10"
style="width: 200px; margin-right: 20px"
@input="changeScreenZoom"
/>
<span class="select-zoom-text">缩放比例</span>
<el-select
class="bs-el-select"
popper-class="bs-el-select"
:value="zoom"
@change="changeScreenZoom"
>
<el-option
v-for="(zoom,index) in zoomList"
:key="index"
:label="zoom.label"
:value="zoom.value"
/>
</el-select>
</div> -->
</div>
<!-- 右侧折叠设置面板 -->
<SettingPanel
:header-show="headerShow"
:height="height"
:right-visiable.sync="rightVisiable"
:page-info-visiable="pageInfoVisiable"
@updateSetting="updateSetting"
@updateDataSetting="updateDataSetting"
@updatePage="updatePage"
@styleHandler="styleHandler"
>
<template #dataSetSelect="{ value }">
<slot
name="dataSetSelect"
:value="value"
/>
</template>
</SettingPanel>
<!-- 添加资源面板 -->
<SourceDialog
ref="SourceDialog"
@getImg="setImg"
/>
<ComponentDialog
ref="componentDialog"
@setComponent="setComponent"
@setRemoteComponent="setRemoteComponent"
/>
<iframe-dialog
v-if="iframeDialog"
ref="iframeDialog"
/>
</div>
<data-view-dialog
ref="dataViewDialog"
/>
</div>
<NotPermission v-else-if="!hasPermission" />
</template>
<script>
import SourceDialog from './SourceDialog/index.vue'
import ComponentDialog from './ComponentDialog/index.vue'
import iframeDialog from 'data-room-ui/BasicComponents/LinkChart/iframeDialog'
import {
dataConfig,
settingConfig
} from 'data-room-ui/BasicComponents/Picture/settingConfig'
import LeftPanel from './LeftPanel.vue'
import SettingPanel from './SettingPanel.vue'
import PageTopSetting from './PageDesignTop.vue'
import Render from '../Render'
import { mapActions, mapMutations, mapState } from 'vuex'
import SketchDesignRuler from 'data-room-ui/BigScreenDesign/RulerTool/SketchRuler.vue'
import multipleSelectMixin from 'data-room-ui/js/mixins/multipleSelectMixin'
import { getScreenInfo } from 'data-room-ui/js/api/bigScreenApi'
import plotSettings from 'data-room-ui/G2Plots/settings'
import MouseSelect from './MouseSelect/index.vue'
import cloneDeep from 'lodash/cloneDeep'
import { randomString } from '../js/utils'
import { isFirefox } from 'data-room-ui/js/utils/userAgent'
import { handleResData } from 'data-room-ui/js/store/actions.js'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import NotPermission from 'data-room-ui/NotPermission'
import DataViewDialog from 'data-room-ui/BigScreenDesign/DataViewDialog'
export default {
name: 'BigScreenDesign',
components: {
PageTopSetting,
LeftPanel,
Render,
SketchDesignRuler,
MouseSelect,
SettingPanel,
SourceDialog,
ComponentDialog,
iframeDialog,
NotPermission,
DataViewDialog
},
mixins: [multipleSelectMixin],
props: {
code: {
type: String,
default: ''
},
headerShow: {
type: Boolean,
default: true
},
height: {
type: String,
default: 'calc(100vh - 40px)'
}
},
data () {
return {
mapShow: true, // 小地图显示与否
hasPermission: true,
rightVisiable: false,
pageInfoVisiable: false,
ruleStartX: 100,
ruleStartY: 100,
zoomList: [
{
label: '自适应',
value: 'auto'
},
{
label: '100%',
value: 100
},
{
label: '80%',
value: 80
},
{
label: '50%',
value: 50
},
{
label: '20%',
value: 20
}
]
}
},
watch: {
chartList (val) {
// if(val.findIndex(item=>item.code==this.activeCode)==-1){
// this.updateRightVisiable(false)
// }
// if(val.length==0){
// this.updateRightVisiable(false)
// }
},
mapShow (value) {
const mapElement = document.getElementById('minimap')
// const selectElement = document.getElementById('selectWin')
if (!value) {
mapElement.style.bottom = parseFloat(window.getComputedStyle(mapElement).bottom) + 150 + 'px'
} else {
this.$refs.Rules.handleScroll()
mapElement.style.bottom = parseFloat(window.getComputedStyle(mapElement).bottom) - 150 + 'px'
}
},
fitZoom (zoom) {
this.zoomList[0] = {
label: `自适应(${zoom}%)`,
value: zoom
}
}
},
computed: {
...mapState({
pageInfo: (state) => state.bigScreen.pageInfo,
chartList: (state) => state.bigScreen.pageInfo.chartList,
pageConfig: (state) => state.bigScreen.pageInfo.pageConfig,
pageLoading: (state) => state.bigScreen.pageLoading,
hoverCode: (state) => state.bigScreen.hoverCode,
presetLine: (state) => state.bigScreen.presetLine,
updateKey: (state) => state.bigScreen.updateKey,
hasGrid: (state) => state.bigScreen.hasGrid,
zoom: (state) => state.bigScreen.zoom,
fitZoom: (state) => state.bigScreen.fitZoom,
iframeDialog: (state) => state.bigScreen.iframeDialog,
activeCode: state => state.bigScreen.activeCode
}),
pageCode () {
return this.code || this.$route.query.code
},
offset () {
return {
x: 220 + 50 - this.ruleStartX,
y: 55 + 50 - this.ruleStartY
}
}
},
created () {
this.changePageLoading(true)
this.permission()
/**
* 以下是为了解决在火狐浏览器上推拽时弹出tab页到搜索问题
* @param event
*/
if (isFirefox()) {
document.body.ondrop = function (event) {
event.preventDefault()
event.stopPropagation()
}
}
},
mounted () {
EventBus.$on('closeRightPanel', () => {
this.updateRightVisiable(false)
})
},
beforeDestroy () {
this.clearTimeline()
EventBus.$off('closeRightPanel')
},
methods: {
...mapActions('bigScreen', ['initLayout']),
...mapMutations('bigScreen', [
'changeLayout',
'changePageLoading',
'resetPresetLine',
'changeActiveCode',
'changeActiveCodes',
'changePageConfig',
'changeChartConfig',
'changeChartKey',
'changeZoom',
'clearTimeline',
'saveTimeLine',
'changeIframeDialog',
'changePageInfo',
'changeActiveItemConfig',
'emptyDataset',
'emptyComputedDatas'
]),
// 控制小地图显示与隐藏
showMinimap () {
this.mapShow = !this.mapShow
},
// 判断页面权限
permission () {
this.$dataRoomAxios.get(`/bigScreen/permission/check/${this.pageCode}`).then(res => {
this.hasPermission = res
if (res) {
this.init()
}
})
},
// 添加资源弹窗初始化
initDialog () {
this.$refs.SourceDialog.init()
},
openComponent () {
this.$refs.componentDialog.init()
},
// 从组件库添加组件模板到当前画布
setComponent (component) {
// 根据component获取页面详情
getScreenInfo(component.code).then(res => {
res.chartList.forEach((item) => {
if (!item.border) {
item.border = { type: '', titleHeight: 60, fontSize: 16, isTitle: true, padding: [0, 0, 0, 0] }
}
if (!item.border.padding) {
item.border.padding = [0, 0, 0, 0]
}
if (item.type == 'customComponent') {
plotSettings[Symbol.iterator] = function * () {
const keys = Object.keys(plotSettings)
for (const k of keys) {
yield [k, plotSettings[k]]
}
}
for (const [key, value] of plotSettings) {
if (item.name == value.name) {
const settings = JSON.parse(JSON.stringify(value.setting))
item.setting = settings.map((x) => {
const index = item.setting.findIndex(y => y.field == x.field)
x.field = item.setting[index].field
x.value = item.setting[index].value
return x
})
}
}
}
})
// 给组件库导入的组件加入统一的前缀
const randomStr = randomString(8)
const pageInfo = handleResData(res)
const chartList = pageInfo.chartList.reverse()
chartList.forEach((chart) => {
// 如果组件存在数据联动则将数据联动的code也加上相同的前缀
if (chart.linkage && chart.linkage.components && chart.linkage.components.length) {
chart.linkage.components.forEach((com) => { com.componentKey = randomStr + com.componentKey })
}
const newChart = {
...chart,
offsetX: 0,
group: randomStr,
code: randomStr + chart.code
}
// 如果是从组件库中添加的自定义组件则不需要初始化theme
const isComponent = true
this.$refs.Render.addChart(newChart, { x: chart.x, y: chart.y }, isComponent)
this.updateRightVisiable(false)
})
})
},
// 添加远程组件
setRemoteComponent (component) {
const newChart = {
...component,
offsetX: 0,
offsetY: 0,
code: randomString(8)
}
this.$refs.Render.addChart(newChart, { x: 0, y: 0 })
},
setImg (val) {
this.$refs.Render.addSourceChart(
JSON.stringify({
title: val.originalName,
name: val.originalName,
icon: null,
className:
'com.gccloud.dataroom.core.module.chart.components.ScreenPictureChart',
w: 300,
h: 300,
x: 0,
y: 0,
type: 'picture',
option: {
...cloneDeep(settingConfig)
},
setting: {}, // 右侧面板自定义配置
dataHandler: {}, // 数据自定义处理js脚本
...cloneDeep(dataConfig),
customize: {
url: val.url,
radius: 0,
opacity: 100
}
}),
{ x: 150, y: 100 }
)
},
init () {
this.changePageLoading(true)
this.initLayout(this.pageCode)
.then(() => {
this.changePageLoading(false)
})
.finally(() => {
setTimeout(() => {
this.resetPresetLine()
}, 500)
})
},
// 点击当前组件时打开右侧面板
openRightPanel (card) {
this.rightVisiable = true
this.pageInfoVisiable = false
this.$refs.Rules.initRuleHeight()
},
openDataViewDialog (config) {
this.$refs.dataViewDialog.init(config)
},
/**
* @description: 清空页面
*/
empty () {
this.$confirm('确定清空页面吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'bs-el-message-box'
})
.then(() => {
this.changeLayout([])
// 清空缓存的数据库的内容
this.emptyDataset()
this.emptyComputedDatas()
this.resetPresetLine()
this.saveTimeLine('清空画布')
})
.catch(() => {})
},
// 切换主题时针对远程组件触发样式修改的方法
styleHandler (config) {
this.$nextTick(() => {
this.$refs.Render?.$refs['RenderCard' + config.code][0]?.$refs[
config.code
]?.changeStyle(cloneDeep(config), true)
})
},
// 自定义属性更新
updateSetting (config) {
if (config.type === 'map' || config.type === 'screenScrollBoard' || config.type === 'remoteComponent' || config.type === 'video' || config.type === 'flyMap') {
config.key = new Date().getTime()
}
this.changeChartConfig(cloneDeep(config))
// 如果是tab内的组件
if (config.parentCode) {
const dom = this.$refs.Render?.$refs['RenderCard' + config.parentCode][0]?.$refs[config.parentCode]?.$refs['RenderCard' + config.code]?.$refs[config.code]
if (dom) {
dom?.changeStyle(cloneDeep(config))
}
} else {
if (this.$refs.Render?.$refs['RenderCard' + config.code]) {
this.$refs.Render?.$refs['RenderCard' + config.code][0]?.$refs[
config.code
]?.changeStyle(cloneDeep(config))
}
}
},
// 动态属性更新
updateDataSetting (config) {
config.key = new Date().getTime()
this.changeChartConfig(config)
},
onSelectArea (area) {
const { startX, startY, endX, endY } = area
// 计算所有在此区域中的组件如果在此区域中将其code添加到activeCodes数组中
const activeCodes = this.chartList
?.filter((chart) => {
const { x, y, w, h } = chart
return startX - 50 <= x && x + w <= endX && startY - 50 <= y && y + h <= endY
})
?.map((chart) => chart.code)
this.changeActiveCodes(activeCodes)
},
changeStart ({ x, y }) {
this.ruleStartX = x
this.ruleStartY = y
},
// 保存并预览
saveAndPreview () {
this.$refs.PageTopSetting.execRun()
},
// 保存
save () {
this.$refs.PageTopSetting.save('saveLoading')
},
changeScreenZoom (zoom) {
// 自适应
if (zoom === 'auto') {
this.$refs.Rules.initZoom()
} else {
this.changeZoom(zoom)
}
},
updateRightVisiable (visiable) {
this.rightVisiable = visiable
this.$refs.Rules.initRuleHeight()
},
toggleLeftSidebar () {
this.$refs.Rules.initRuleHeight()
},
showPageInfo () {
this.pageInfoVisiable = true
this.rightVisiable = true
this.changeActiveCode('')
},
// 页面信息更改
updatePage () {
this.$refs.Rules.initZoom()
}
}
}
</script>
<style lang="scss" scoped>
.bs-page-design-wrap {
overflow: hidden;
.drag-wrap {
display: flex;
background-color: #1d1e20;
height: calc(100vh - 40px);
// overflow: hidden;
.grid-wrap-box {
flex: 1;
// overflow: hidden;
position: relative;
margin: 8px 0 0 8px;
.footer-tools-bar {
position: absolute;
bottom: 0;
width: 100%;
height: 30px;
display: flex;
justify-content: flex-end;
align-items: center;
z-index: 1000;
background-color: var(--bs-background-2);
.bs-select-wrap {
margin-right: 16px;
}
.select-zoom-text {
color: var(--bs-el-title);
margin-right: 16px;
}
::v-deep .el-select {
width: 150px !important;
}
}
}
::v-deep .el-loading-mask {
background-color: transparent !important;
}
}
}
.minimap{
position: absolute ;
bottom: 20px;
right: 20px;
z-index:1000;
}
.minimap .mapHeader{
background-color:#303640;
box-sizing:border-box;
padding: 0 10px;
display: flex;
justify-content: space-between;
align-items: center;
height: 30px;
width: 150px;
font-size: 12px;
color: var(--bs-el-title);
cursor: pointer;
span {
user-select: none;
}
}
.minimap .selectWin{
background-color: #232832;
height: 150px;
width: 150px;
position: relative;
}
.minimap .selectionWin{
position: absolute;
left: 0px;
top: 0px;
width: 30px;
height: 30px;
background-color: white;
opacity: 0.5;
cursor: move;
}
</style>