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.

446 lines
12 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div
ref="bs-render-wrap"
:key="`${pageInfo.pageConfig.w}${pageInfo.pageConfig.h}`"
class="bs-render-wrap design-drag-wrap render-theme-wrap"
:style="{
width: pageInfo.pageConfig.w + 'px',
height: pageInfo.pageConfig.h + 'px',
backgroundColor: pageInfo.pageConfig.bgColor,
backgroundImage: `url(${pageInfo.pageConfig.bg})`
}"
@drop="drop($event)"
@dragover.prevent
>
<vdr
v-for="chart in chartList"
:id="chart.code"
:key="chart.updateKey || chart.code"
class="drag-item"
:class="{
'multiple-selected': activeCodes.includes(chart.code),
}"
:scale-ratio="scale"
:x="chart.x"
:y="chart.y"
:w="chart.w"
:h="chart.h"
:min-width="10"
:min-height="10"
:draggable="!chart.locked"
:resizable="!chart.locked"
:parent="true"
:debug="false"
:is-conflict-check="false"
:snap="true"
:snap-tolerance="2"
:style="{ zIndex: chart.z || 0 }"
:grid="[1,1]"
@activated="activated(...arguments, chart)"
@dragging="onDrag(...arguments, chart)"
@resizing="onResize(...arguments, chart)"
@resizestop="resizestop(...arguments, chart)"
@dragstop="dragstop(...arguments, chart)"
@refLineParams="getRefLineParams"
@mouseleave.native="resetPresetLineDelay"
>
<Configuration
v-if="isInit"
:config="chart"
@openRightPanel="openRightPanel"
>
<RenderCard
:ref="'RenderCard' + chart.code"
:config="chart"
/>
</Configuration>
</vdr>
<span
v-for="(vl, index) in vLine"
v-show="vl.display"
:key="index + 'vLine'"
class="ref-line v-line"
:style="{ left: vl.position, top: vl.origin, height: vl.lineLength }"
/>
<span
v-for="(hl, index) in hLine"
v-show="hl.display"
:key="index + 'hLine'"
class="ref-line h-line"
:style="{ top: hl.position, left: hl.origin, width: hl.lineLength }"
/>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import RenderCard from './RenderCard.vue'
import Configuration from './Configuration.vue'
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import vdr from 'vue-draggable-resizable-gorkys'
import 'vue-draggable-resizable-gorkys/dist/VueDraggableResizable.css'
import { randomString } from '../js/utils'
import { compile } from 'tiny-sass-compiler/dist/tiny-sass-compiler.esm-browser.prod.js'
import plotList, { getCustomPlots } from '../G2Plots/plotList'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
export default {
name: 'BigScreenRender',
components: {
RenderCard,
Configuration,
vdr
},
props: {
ruleKey: {
type: Number,
default: 0
}
},
data () {
return {
vLine: [],
hLine: [],
themeCss: '',
// 临时冻结拖拽
freeze: false,
plotList,
rawChart: []
}
},
computed: {
...mapState({
pageConfig: (state) => state.bigScreen.pageInfo.pageConfig,
pageInfo: (state) => state.bigScreen.pageInfo,
chartList: (state) => state.bigScreen.pageInfo.chartList,
activeCode: (state) => state.bigScreen.activeCode,
activeCodes: (state) => state.bigScreen.activeCodes,
hoverCode: (state) => state.bigScreen.hoverCode,
themeJson: (state) => state.bigScreen.pageInfo.pageConfig.themeJson,
isInit: (state) => !state.bigScreen.pageLoading,
scale: (state) => state.bigScreen.zoom / 100
})
},
watch: {
pageConfig: {
handler (pageConfig) {
this.$nextTick(() => {
const style = document.createElement('style')
if (
pageConfig &&
pageConfig.themeJson &&
pageConfig.themeJson.themeCss
) {
const themeCss = pageConfig.themeJson.themeCss
if (themeCss) {
const themeStr = compile(themeCss).code
style.type = 'text/css'
style.innerText = themeStr
document.getElementsByTagName('head')[0].appendChild(style)
}
}
})
},
deep: true,
immediate: true
}
},
mounted () {
this.styleSet()
this.plotList = [...this.plotList, ...getCustomPlots()]
},
methods: {
...mapMutations('bigScreen', [
'changeLayout',
'changeActiveCode',
'changeChartConfig',
'changeActiveItemConfig',
'changeActiveItemWH',
'addItem',
'delItem',
'resetPresetLine',
'changeGridShow',
'setPresetLine',
'saveTimeLine'
]),
// 获取到后端传来的主题样式并进行修改
styleSet () {
const style = document.createElement('style')
if (this.themeJson && this.themeJson.themeCss) {
const styleStr = this.themeJson.themeCss
const themeCss = compile(styleStr).code
style.type = 'text/css'
style.innerText = themeCss
document.getElementsByTagName('head')[0].appendChild(style)
} else {
style.remove()
}
},
resetPresetLineDelay () {
setTimeout(() => {
this.resetPresetLine()
}, 500)
},
// 点击当前组件时打开右侧面板
openRightPanel (config) {
this.$emit('openRightPanel', config)
},
drop (e) {
e.preventDefault()
// 解决:火狐拖放后,总会默认打开百度搜索,如果是图片,则会打开图片的问题。
e.stopPropagation()
const transferData = e.dataTransfer.getData('dragComponent')
if (transferData) {
this.addChart(transferData, { x: e?.x, y: e?.y })
}
},
/**
* 改变组件大小
* @param x
* @param y
* @param width
* @param height
* @param chart
*/
onResize (x, y, width, height, chart) {
chart.x = x
chart.y = y
chart.w = width
chart.h = height
this.changeGridShow(true)
this.setPresetLine({
...chart
})
},
/**
*
* @param x
* @param y
* @param chart
*/
onDrag (x, y, chart) {
// 防止事件冒泡
event.stopPropagation()
if (chart.group) {
// 查找和自己是一个组合的组件
this.dragGroupChart(x, y, chart)
} else {
chart.x = x
chart.y = y
}
this.changeGridShow(true)
this.setPresetLine({
...chart
})
},
resizestop (left, top, width, height, chart) {
this.changeChartConfig({
...chart,
w: width,
h: height,
x: left,
y: top
})
this.changeActiveItemConfig({
...chart,
w: width,
h: height,
x: left,
y: top
})
if (chart.code === this.activeCode) {
this.changeActiveItemWH({
code: chart.code,
w: width,
h: height
})
}
this.saveTimeLine(`改变${chart?.title}大小`)
this.changeGridShow(false)
},
activated (chart) {
this.rawChart = cloneDeep(chart)
},
dragstop (left, top, chart) {
if (!this.freeze) {
if (this.rawChart.x !== left || this.rawChart.y !== top) {
this.changeChartConfig({
...chart,
x: left,
y: top
})
this.changeActiveItemConfig({
...chart,
x: left,
y: top
})
if (chart.code === this.activeCode) {
this.changeActiveItemWH({
code: chart.code,
x: left,
y: top
})
}
this.rawChart = cloneDeep(chart)
}
} else {
const index = this.chartList.findIndex(
(_chart) => _chart.code === chart.code
)
this.$set(this.chartList, index, chart)
this.changeChartConfig({
...chart,
updateKey: new Date().getTime()
})
}
this.changeGridShow(false)
this.freeze = false
this.saveTimeLine(`拖拽${chart?.title}`)
},
// 辅助线
getRefLineParams (params) {
const { vLine, hLine } = params
this.vLine = vLine
this.hLine = hLine
},
// 新增元素
addChart (chart, position) {
const { left, top } = this.$el.getBoundingClientRect()
const _chart = !chart.code ? JSON.parse(chart) : chart
let option = _chart.option
if (_chart.type === 'customComponent') {
option = {
...this.plotList?.find((plot) => plot.name === _chart.name)?.option,
theme: this.pageConfig.customTheme
}
}
const config = {
..._chart,
x: parseInt(!chart.code
? (position.x - left - _chart.offsetX) / this.scale
: position.x),
y: parseInt(!chart.code
? (position.y - top - _chart.offsetX) / this.scale
: position.y),
width: 200 * this.scale,
height: 200 * this.scale,
code: !chart.code ? randomString(8) : chart.code,
option
}
config.key = config.code
// TODO:新添加一个组件时应该有默认的两套主题
// 先暂时只考虑g2组件
if (['customComponent'].includes(_chart.type)) {
config.theme = settingToTheme(config, 'dark')
config.theme = settingToTheme(config, 'light')
}
this.addItem(config)
},
addSourceChart (chart, position) {
const { left, top } = this.$el.getBoundingClientRect()
const _chart = JSON.parse(chart)
let option = _chart.option
if (_chart.type === 'customComponent') {
option = {
...this.plotList?.find((plot) => plot.name === _chart.name)?.option,
theme: this.pageConfig.customTheme
}
}
const config = {
..._chart,
x: parseInt((position.x - left) / this.scale),
y: parseInt((position.y - top) / this.scale),
width: 200 * this.scale,
height: 200 * this.scale,
code: randomString(8),
option
}
config.key = config.code
this.addItem(config)
},
/**
* 拖拽相同组合的组件
* @param x 组合元素当前x
* @param y 组合元素当前y
* @param chart
*/
dragGroupChart (x, y, chart) {
if (chart.group) {
const diffX = x - chart.x
const diffY = y - chart.y
const group = chart.group
// 找到相同group的组件并找到边界
const groupChartList = this.chartList.filter(
(groupChart) => groupChart.group === group
)
const groupMinX = Math.min(
...groupChartList?.map((groupChart) => groupChart.x + diffX)
)
const groupMinY = Math.min(
...groupChartList?.map((groupChart) => groupChart.y + diffY)
)
const groupMaxX = Math.max(
...groupChartList?.map(
(groupChart) => groupChart.x + diffX + groupChart.w
)
)
const groupMaxY = Math.max(
...groupChartList?.map(
(groupChart) => groupChart.y + diffY + groupChart.h
)
)
// 如果其中某个组件超出画布,则不移动 (此处无法阻止移动,故在拖拽结束后重置位置)
if (
(groupMinX <= 0 ||
groupMinY <= 0 ||
groupMaxX >= this.pageConfig.w ||
groupMaxY >= this.pageConfig.h) &&
// 偏移的绝对值要大于0
(Math.abs(diffX) > 0 || Math.abs(diffY) > 0)
) {
this.freeze = true
return
}
// 移动相应的diff距离
groupChartList?.map((groupChart) => {
this.changeChartConfig({
...groupChart,
x: groupChart.x + diffX,
y: groupChart.y + diffY
})
})
}
}
}
}
</script>
<style lang="scss" scoped>
.bs-render-wrap {
position: relative;
background-size: cover;
.drag-item {
cursor: move;
}
::v-deep .vdr {
border: none;
}
.h-line {
border-bottom: 1px dashed #0089d0;
}
.v-line {
border-left: 1px dashed #0089d0;
}
.ref-line {
background-color: transparent;
}
}
.design-drag-wrap {
box-shadow: 0 0 30px 0 rgba(0, 0, 0, 0.5);
}
.multiple-selected {
border: 1px dashed #fff !important;
}
</style>