初始化
This commit is contained in:
132
frontend/packages/Render/Configuration.vue
Normal file
132
frontend/packages/Render/Configuration.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div
|
||||
class="configuration-wrap"
|
||||
:class="{
|
||||
'active': activeCodes.includes(config.code) && (!isPreview),
|
||||
'hover': hoverCode === config.code && (!isPreview)
|
||||
}"
|
||||
@mouseenter.stop="changeHover(config.code)"
|
||||
@mouseleave="changeHover('')"
|
||||
@click.stop="changeActive(config)"
|
||||
@contextmenu="onContextmenu($event, config)"
|
||||
>
|
||||
<!-- <span class="point-text" v-show="hoverCode === config.code"> {{ getPoint(config) }}</span>-->
|
||||
<span
|
||||
v-show="!isPreview && config.locked"
|
||||
class="locked-status el-icon-lock"
|
||||
/>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
|
||||
import chartContextMenu from 'data-room-ui/js/mixins/chartContextMenu'
|
||||
export default {
|
||||
name: 'Configuration',
|
||||
mixins: [chartContextMenu],
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
activeCode: state => state.bigScreen.activeCode,
|
||||
activeCodes: state => state.bigScreen.activeCodes,
|
||||
hoverCode: state => state.bigScreen.hoverCode,
|
||||
activeItemConfig: state => state.bigScreen.activeItemConfig,
|
||||
chartList: state => state.bigScreen.pageInfo.chartList,
|
||||
presetLine: state => state.bigScreen.presetLine
|
||||
}),
|
||||
isPreview () {
|
||||
return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
mounted () {},
|
||||
methods: {
|
||||
...mapMutations('bigScreen', [
|
||||
'changeHoverCode',
|
||||
'changeActiveCode',
|
||||
'changeChartConfig',
|
||||
'addItem',
|
||||
'delItem',
|
||||
'resetPresetLine',
|
||||
'changeLayout',
|
||||
'changeZIndex',
|
||||
'changeLocked'
|
||||
]),
|
||||
// 改变hover的组件
|
||||
changeHover (code) {
|
||||
this.changeHoverCode(code)
|
||||
},
|
||||
// 改变激活的组件
|
||||
changeActive (config) {
|
||||
this.changeActiveCode(config.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.configuration-wrap {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 1px solid transparent;
|
||||
//cursor: move;
|
||||
|
||||
.opt-icon-wrap {
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
.obt-item{
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.point-text {
|
||||
position: absolute;
|
||||
top: -36px;
|
||||
left: -110px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
color: #8a7878;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.locked-status {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.hover {
|
||||
border: 1px dashed var(--bs-el-color-primary);
|
||||
}
|
||||
.active {
|
||||
border: -1px solid #c8ff00;
|
||||
}
|
||||
</style>
|
||||
123
frontend/packages/Render/RenderCard.vue
Normal file
123
frontend/packages/Render/RenderCard.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<!--
|
||||
* @description: 渲染组件
|
||||
* @Date: 2022-08-18 09:42:45
|
||||
* @Author: xingheng
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<component
|
||||
:is="resolveComponentType(config.border.type)"
|
||||
v-if="config.border&&config.border.type"
|
||||
:id="`border${config.code}`"
|
||||
:ref="`border${config.code}`"
|
||||
:key="`border${config.key}`"
|
||||
:config="config"
|
||||
/>
|
||||
<div
|
||||
class="render-item-wrap"
|
||||
:style="
|
||||
`height:calc(100% - ${(config.border&&config.title&&config.border.type&&config.border.type!='GcBorder11'&&config.border.isTitle)?config.border.titleHeight:0}px);
|
||||
padding-top:${config.border?config.border.padding[0]:0}px;
|
||||
padding-right:${config.border?config.border.padding[1]:0}px;
|
||||
padding-bottom:${config.border?config.border.padding[2]:0}px;
|
||||
padding-left:${config.border?config.border.padding[3]:0}px
|
||||
`"
|
||||
>
|
||||
<component
|
||||
:is="resolveComponentType(config.type)"
|
||||
:id="`${config.code}`"
|
||||
:ref="config.code"
|
||||
:key="config.key"
|
||||
:config="config"
|
||||
@styleHandler="styleHandler"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import commonMixins from 'data-room-ui/js/mixins/commonMixins'
|
||||
import { mapMutations } from 'vuex'
|
||||
import { resolveComponentType } from 'data-room-ui/js/utils'
|
||||
import pcComponent from 'data-room-ui/js/utils/componentImport'
|
||||
import { dataInit, destroyedEvent } from 'data-room-ui/js/utils/eventBus'
|
||||
import CustomComponent from '../PlotRender/index.vue'
|
||||
import EchartsComponent from '../EchartsRender/index.vue'
|
||||
import Svgs from '../Svgs/index.vue'
|
||||
import RemoteComponent from 'data-room-ui/RemoteComponents/index.vue'
|
||||
const components = {}
|
||||
for (const key in pcComponent) {
|
||||
if (Object.hasOwnProperty.call(pcComponent, key)) {
|
||||
components[key] = pcComponent[key]
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'RenderCard',
|
||||
// mixins: [commonMixins],
|
||||
components: {
|
||||
...components,
|
||||
CustomComponent,
|
||||
Svgs,
|
||||
RemoteComponent,
|
||||
EchartsComponent
|
||||
},
|
||||
props: {
|
||||
// 卡片的属性
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
ruleKey: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
height: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
mounted () {
|
||||
// 调用初始化方法
|
||||
dataInit(this)
|
||||
},
|
||||
beforeDestroy () {
|
||||
destroyedEvent()
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('bigScreen', [
|
||||
'changeChartConfig'
|
||||
]),
|
||||
resolveComponentType,
|
||||
// 切换主题时针对远程组件触发样式修改的方法
|
||||
styleHandler (config) {
|
||||
this.$emit('styleHandler', config)
|
||||
}
|
||||
// // 打开右侧面板
|
||||
// openRightPanel () {
|
||||
// this.$emit('openRightPanel', this.currentChart)
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.render-item-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
98
frontend/packages/Render/RenderCard2.vue
Normal file
98
frontend/packages/Render/RenderCard2.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<!--
|
||||
* @description: 渲染组件
|
||||
* @Date: 2022-08-18 09:42:45
|
||||
* @Author: xingheng
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="render-item-wrap">
|
||||
<component
|
||||
:is="resolveComponentType(config.type)"
|
||||
:id="`${config.code}`"
|
||||
:ref="config.code"
|
||||
:key="config.key"
|
||||
:config="config"
|
||||
@styleHandler="styleHandler"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import commonMixins from 'data-room-ui/js/mixins/commonMixins'
|
||||
import { mapMutations } from 'vuex'
|
||||
import { resolveComponentType } from 'data-room-ui/js/utils'
|
||||
import pcComponent from 'data-room-ui/js/utils/componentImport'
|
||||
import { dataInit, destroyedEvent } from 'data-room-ui/js/utils/eventBus'
|
||||
import CustomComponent from '../PlotRender/index.vue'
|
||||
import Svgs from '../Svgs/index.vue'
|
||||
import EchartsComponent from '../EchartsRender/index.vue'
|
||||
import RemoteComponent from 'data-room-ui/RemoteComponents/index.vue'
|
||||
import Map from 'data-room-ui/BasicComponents/Map/index.vue'
|
||||
import FlyMap from 'data-room-ui/BasicComponents/FlyMap/index.vue'
|
||||
import candlestick from 'data-room-ui/BasicComponents/Candlestick/index.vue'
|
||||
const components = {}
|
||||
for (const key in pcComponent) {
|
||||
if (Object.hasOwnProperty.call(pcComponent, key)) {
|
||||
components[key] = pcComponent[key]
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: 'RenderCard',
|
||||
// mixins: [commonMixins],
|
||||
components: {
|
||||
...components,
|
||||
CustomComponent,
|
||||
Svgs,
|
||||
Map,
|
||||
FlyMap,
|
||||
candlestick,
|
||||
RemoteComponent,
|
||||
EchartsComponent
|
||||
},
|
||||
props: {
|
||||
// 卡片的属性
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
ruleKey: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
mounted () {
|
||||
// 调用初始化方法
|
||||
dataInit(this)
|
||||
},
|
||||
beforeDestroy () {
|
||||
destroyedEvent()
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('bigScreen', [
|
||||
'changeChartConfig'
|
||||
]),
|
||||
resolveComponentType,
|
||||
// 切换主题时针对远程组件触发样式修改的方法
|
||||
styleHandler (config) {
|
||||
this.$emit('styleHandler', config)
|
||||
}
|
||||
// // 打开右侧面板
|
||||
// openRightPanel () {
|
||||
// this.$emit('openRightPanel', this.currentChart)
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.render-item-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
543
frontend/packages/Render/index.vue
Normal file
543
frontend/packages/Render/index.vue
Normal file
@@ -0,0 +1,543 @@
|
||||
<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.customTheme === 'light' ? pageInfo.pageConfig.lightBgColor : pageInfo.pageConfig.bgColor,
|
||||
backgroundImage: pageInfo.pageConfig.customTheme === 'light' ? `url(${getCoverPicture(pageInfo.pageConfig.lightBg)})` : `url(${getCoverPicture(pageInfo.pageConfig.bg)})`
|
||||
}" @drop="drop($event)" @dragover.prevent @click="handleClickOutside($event)">
|
||||
<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="snapTolerance" :style="{
|
||||
zIndex: chart.z || 0,
|
||||
}" :perspective="parseInt(`${chart.perspective == undefined ? 0 : chart.perspective}`)"
|
||||
:transform="`skew(${chart.skewX == undefined ? 0 : chart.skewX}deg, ${chart.skewY == undefined ? 0 : chart.skewY}deg) rotateX(${chart.rotateX == undefined ? 0 : chart.rotateX}deg) rotateY(${chart.rotateY == undefined ? 0 : chart.rotateY}deg) rotateZ(${chart.rotateZ == undefined ? 0 : chart.rotateZ}deg)`"
|
||||
:grid="[1, 1]" :handles="handlesList" class-name-handle="bs-handle-class"
|
||||
@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"
|
||||
@openDataViewDialog="openDataViewDialog">
|
||||
<RenderCard :ref="'RenderCard' + chart.code" :config="chart" @styleHandler="styleHandler" />
|
||||
</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 '@gcpaas/vue-draggable-resizable-gorkys'
|
||||
import 'gc-vue-draggable-resizable/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'
|
||||
import { getFileUrl } from 'data-room-ui/js/utils/file'
|
||||
import { customDeserialize } from 'data-room-ui/js/utils/jsonSerialize.js'
|
||||
|
||||
export default {
|
||||
name: 'BigScreenRender',
|
||||
components: {
|
||||
RenderCard,
|
||||
Configuration,
|
||||
vdr
|
||||
},
|
||||
props: {
|
||||
ruleKey: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
handlesList: ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'], // 缩放手柄的数组
|
||||
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,
|
||||
snapTolerance: (state) => state.bigScreen.snapTolerance
|
||||
})
|
||||
},
|
||||
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',
|
||||
'changeActiveCodes'
|
||||
]),
|
||||
// 判断鼠标点击的是画布中的高亮元素(被框选的)还是非高亮元素或者空白区域
|
||||
// 如果是高亮元素则不会取消高亮状态,如果不是则取消高亮状态
|
||||
handleClickOutside(event) {
|
||||
// 获取被点击的元素
|
||||
const clickedElement = event.target
|
||||
const elementToHighlights = []
|
||||
// 获取需要高亮的元素的引用
|
||||
for (const code of this.activeCodes) {
|
||||
if (this.$refs['RenderCard' + code] && this.$refs['RenderCard' + code].length && this.$refs['RenderCard' + code][0]) {
|
||||
elementToHighlights.push(this.$refs['RenderCard' + code][0])
|
||||
}
|
||||
}
|
||||
|
||||
const isElementInHighlights = elementToHighlights.some((elementToHighlight) => {
|
||||
return elementToHighlight?.$el?.contains(clickedElement)
|
||||
})
|
||||
if (!isElementInHighlights) {
|
||||
this.changeActiveCodes([])
|
||||
}
|
||||
},
|
||||
// 切换主题时针对远程组件触发样式修改的方法
|
||||
styleHandler(config) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs['RenderCard' + config.code][0]?.$refs[
|
||||
config.code
|
||||
]?.changeStyle(cloneDeep(config), true)
|
||||
})
|
||||
},
|
||||
// 获取到后端传来的主题样式并进行修改
|
||||
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)
|
||||
},
|
||||
// 查看数据
|
||||
openDataViewDialog(config) {
|
||||
this.$emit('openDataViewDialog', 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, isComponent) {
|
||||
const { left, top } = this.$el.getBoundingClientRect()
|
||||
const _chart = !chart.code ? customDeserialize(chart) : chart
|
||||
let option = _chart.option
|
||||
if (_chart.type === 'customComponent') {
|
||||
option = {
|
||||
...this.plotList?.find((plot) => plot.name === _chart.name)?.option,
|
||||
theme: this.pageConfig.customTheme === 'dark' ? 'transparent' : 'light'
|
||||
}
|
||||
}
|
||||
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 = isComponent ? randomString(8) : config.code
|
||||
// isComponent = false 从左侧新增时需要初始化theme的内容
|
||||
// isComponent = true从组件库添加自定义组件时不用初始化
|
||||
if (!isComponent) {
|
||||
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 === 'dark' ? 'transparent' : 'light'
|
||||
}
|
||||
}
|
||||
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
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取图片访问地址,如果是相对路径则拼接上文件访问前缀地址
|
||||
* @param url
|
||||
* @returns {*}
|
||||
*/
|
||||
getCoverPicture(url) {
|
||||
return getFileUrl(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
</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 solid #fff !important;
|
||||
}
|
||||
|
||||
//调整拖拽插件的句柄样式
|
||||
//句柄公共样式
|
||||
::v-deep .bs-handle-class {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
//background: #fff;
|
||||
border: 3px solid #c8ff00;
|
||||
}
|
||||
|
||||
// 每个句柄不同样式
|
||||
::v-deep .bs-handle-class-tl {
|
||||
top: -2px !important;
|
||||
left: -2px !important;
|
||||
display: block;
|
||||
cursor: nw-resize;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-tm {
|
||||
top: -2px !important;
|
||||
left: calc(50% - 8px) !important;
|
||||
display: block;
|
||||
cursor: n-resize;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-tr {
|
||||
top: -2px !important;
|
||||
right: -2px !important;
|
||||
display: block;
|
||||
cursor: ne-resize;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-mr {
|
||||
top: calc(50% - 8px) !important;
|
||||
right: -2px !important;
|
||||
display: block;
|
||||
cursor: e-resize;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-br {
|
||||
right: -2px !important;
|
||||
bottom: -2px !important;
|
||||
display: block;
|
||||
cursor: se-resize;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-bm {
|
||||
right: calc(50% - 8px) !important;
|
||||
bottom: -2px !important;
|
||||
display: block;
|
||||
cursor: s-resize;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-bl {
|
||||
left: -2px !important;
|
||||
bottom: -2px !important;
|
||||
display: block;
|
||||
cursor: sw-resize;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
::v-deep .bs-handle-class-ml {
|
||||
top: calc(50% - 8px) !important;
|
||||
left: -2px !important;
|
||||
display: block;
|
||||
cursor: w-resize;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user