forked from github/dataease
feat: 画布升级 增加矩阵 边界 对齐 吸附等功能
This commit is contained in:
parent
704d86adf4
commit
dba5eeb571
@ -1,6 +1,8 @@
|
|||||||
package io.dataease.controller.handler;
|
package io.dataease.controller.handler;
|
||||||
|
|
||||||
import io.dataease.controller.ResultHolder;
|
import io.dataease.controller.ResultHolder;
|
||||||
|
import io.dataease.controller.handler.annotation.I18n;
|
||||||
|
import io.dataease.i18n.Translator;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||||
@ -66,7 +68,8 @@ public class GlobalExceptionHandler implements ErrorController {
|
|||||||
errorMessage = "The server responds " + code + " but no detailed message.";
|
errorMessage = "The server responds " + code + " but no detailed message.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ResultHolder.error(errorMessage);
|
|
||||||
|
return ResultHolder.error(Translator.get(errorMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
|
||||||
|
@ -239,7 +239,7 @@ i18n_processing_data=Processing data now, Refresh later
|
|||||||
i18n_union_already_exists=Union relation already exists
|
i18n_union_already_exists=Union relation already exists
|
||||||
i18n_union_field_exists=The same field can't in two dataset
|
i18n_union_field_exists=The same field can't in two dataset
|
||||||
i18n_cron_time_error=Start time can't greater then end time
|
i18n_cron_time_error=Start time can't greater then end time
|
||||||
i18n_auth_source_be_canceled=This Auth Resource Already Be Canceled
|
i18n_auth_source_be_canceled=This Auth Resource Already Be Canceled,Please Connect Admin
|
||||||
i18n_username_exists=ID is already exists
|
i18n_username_exists=ID is already exists
|
||||||
i18n_ds_name_exists=Datasource name used
|
i18n_ds_name_exists=Datasource name used
|
||||||
i18n_sync_job_exists=There is already a synchronization task running, please try again later
|
i18n_sync_job_exists=There is already a synchronization task running, please try again later
|
||||||
|
@ -239,7 +239,7 @@ i18n_processing_data=正在处理数据,稍后刷新
|
|||||||
i18n_union_already_exists=关联关系已存在
|
i18n_union_already_exists=关联关系已存在
|
||||||
i18n_union_field_exists=两个数据集之间关联不能出现多次相同字段
|
i18n_union_field_exists=两个数据集之间关联不能出现多次相同字段
|
||||||
i18n_cron_time_error=开始时间不能大于结束时间
|
i18n_cron_time_error=开始时间不能大于结束时间
|
||||||
i18n_auth_source_be_canceled=当前资源授权权限已经被取消
|
i18n_auth_source_be_canceled=本用户当前资源所有授权权限已经被取消,如需再次开通,请联系管理员
|
||||||
i18n_username_exists=用户 ID 已存在
|
i18n_username_exists=用户 ID 已存在
|
||||||
i18n_ds_name_exists=数据源名称已被使用
|
i18n_ds_name_exists=数据源名称已被使用
|
||||||
i18n_sync_job_exists=已经有同步任务在运行,稍后重试
|
i18n_sync_job_exists=已经有同步任务在运行,稍后重试
|
||||||
|
@ -241,7 +241,7 @@ i18n_processing_data=正在處理數據,稍後刷新
|
|||||||
i18n_union_already_exists=關聯關系已存在
|
i18n_union_already_exists=關聯關系已存在
|
||||||
i18n_union_field_exists=兩個數據集之間關聯不能出現多次相同字段
|
i18n_union_field_exists=兩個數據集之間關聯不能出現多次相同字段
|
||||||
i18n_cron_time_error=開始時間不能大於結束時間
|
i18n_cron_time_error=開始時間不能大於結束時間
|
||||||
i18n_auth_source_be_canceled=當前資源授權權限已經被取消
|
i18n_auth_source_be_canceled=本用户当前资源所有授权权限已经被取消,如需再次开通,请联系管理员
|
||||||
i18n_username_exists=用戶ID已存在
|
i18n_username_exists=用戶ID已存在
|
||||||
i18n_ds_name_exists=數據源名稱已被使用
|
i18n_ds_name_exists=數據源名稱已被使用
|
||||||
i18n_sync_job_exists=已經有同步任務在運行,稍後重試
|
i18n_sync_job_exists=已經有同步任務在運行,稍後重試
|
||||||
|
1597
frontend/src/components/DeDrag/index.vue
Normal file
1597
frontend/src/components/DeDrag/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
26
frontend/src/components/DeDrag/option.js
Normal file
26
frontend/src/components/DeDrag/option.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export const events = {
|
||||||
|
mouse: {
|
||||||
|
start: 'mousedown',
|
||||||
|
move: 'mousemove',
|
||||||
|
stop: 'mouseup'
|
||||||
|
},
|
||||||
|
touch: {
|
||||||
|
start: 'touchstart',
|
||||||
|
move: 'touchmove',
|
||||||
|
stop: 'touchend'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 禁止用户选取
|
||||||
|
export const userSelectNone = {
|
||||||
|
userSelect: 'none',
|
||||||
|
MozUserSelect: 'none',
|
||||||
|
WebkitUserSelect: 'none',
|
||||||
|
MsUserSelect: 'none'
|
||||||
|
}
|
||||||
|
// 用户选中自动
|
||||||
|
export const userSelectAuto = {
|
||||||
|
userSelect: 'auto',
|
||||||
|
MozUserSelect: 'auto',
|
||||||
|
WebkitUserSelect: 'auto',
|
||||||
|
MsUserSelect: 'auto'
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg class="grid" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
<svg class="grid" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<pattern id="smallGrid" width="7.236328125" height="7.236328125" patternUnits="userSpaceOnUse">
|
<!-- <pattern id="smallGrid" width="5" height="5" patternUnits="userSpaceOnUse">-->
|
||||||
|
<!-- <path-->
|
||||||
|
<!-- d="M 5 0 L 0 0 0 5"-->
|
||||||
|
<!-- fill="none"-->
|
||||||
|
<!-- stroke="rgba(207, 207, 207, 0.3)"-->
|
||||||
|
<!-- stroke-width="1"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </pattern>-->
|
||||||
|
<pattern id="grid" :width="matrixStyle.width" :height="matrixStyle.height" patternUnits="userSpaceOnUse">
|
||||||
|
<rect :width="matrixStyle.width" :height="matrixStyle.height" fill="url(#smallGrid)" />
|
||||||
<path
|
<path
|
||||||
d="M 7.236328125 0 L 0 0 0 7.236328125"
|
:d="pathD"
|
||||||
fill="none"
|
|
||||||
stroke="rgba(207, 207, 207, 0.3)"
|
|
||||||
stroke-width="1"
|
|
||||||
/>
|
|
||||||
</pattern>
|
|
||||||
<pattern id="grid" width="36.181640625" height="36.181640625" patternUnits="userSpaceOnUse">
|
|
||||||
<rect width="36.181640625" height="36.181640625" fill="url(#smallGrid)" />
|
|
||||||
<path
|
|
||||||
d="M 36.181640625 0 L 0 0 0 36.181640625"
|
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="rgba(186, 186, 186, 0.5)"
|
stroke="rgba(186, 186, 186, 0.5)"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
@ -23,6 +23,28 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
matrixStyle: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
pathD: function() {
|
||||||
|
return 'M ' + this.matrixStyle.width + ' 0 L 0 0 0 ' + this.matrixStyle.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.grid {
|
.grid {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
v-if="showDrag"
|
||||||
id="editor"
|
id="editor"
|
||||||
class="editor"
|
class="editor"
|
||||||
:class="{ edit: isEdit }"
|
:class="{ edit: isEdit }"
|
||||||
@ -8,18 +9,27 @@
|
|||||||
@mousedown="handleMouseDown"
|
@mousedown="handleMouseDown"
|
||||||
>
|
>
|
||||||
<!-- 网格线 -->
|
<!-- 网格线 -->
|
||||||
<Grid />
|
<Grid v-if="canvasStyleData.auxiliaryMatrix" :matrix-style="matrixStyle" />
|
||||||
|
|
||||||
<!--页面组件列表展示-->
|
<!--页面组件列表展示-->
|
||||||
<Shape
|
<de-drag
|
||||||
v-for="(item, index) in componentData"
|
v-for="(item, index) in componentData"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
:index="index"
|
||||||
|
:x="getShapeStyleIntDeDrag(item.style,'left')"
|
||||||
|
:y="getShapeStyleIntDeDrag(item.style,'top')"
|
||||||
|
:w="getShapeStyleIntDeDrag(item.style,'width')"
|
||||||
|
:h="getShapeStyleIntDeDrag(item.style,'height')"
|
||||||
|
:r="item.style.rotate"
|
||||||
|
:parent="true"
|
||||||
|
:rotatable="rotatable"
|
||||||
:default-style="getShapeStyleInt(item.style)"
|
:default-style="getShapeStyleInt(item.style)"
|
||||||
:style="getShapeStyle(item.style)"
|
|
||||||
:active="item === curComponent"
|
:active="item === curComponent"
|
||||||
:element="item"
|
:element="item"
|
||||||
:index="index"
|
class-name-active="de-drag-active"
|
||||||
:class="{ lock: item.isLock }"
|
:class="{'gap_class':canvasStyleData.panel.gap==='yes'}"
|
||||||
|
:snap="true"
|
||||||
|
:snap-tolerance="5"
|
||||||
|
@refLineParams="getRefLineParams"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
:is="item.component"
|
:is="item.component"
|
||||||
@ -63,19 +73,33 @@
|
|||||||
:element="item"
|
:element="item"
|
||||||
@input="handleInput"
|
@input="handleInput"
|
||||||
/> -->
|
/> -->
|
||||||
</Shape>
|
</de-drag>
|
||||||
<!-- 右击菜单 -->
|
<!-- 右击菜单 -->
|
||||||
<ContextMenu />
|
<ContextMenu />
|
||||||
<!-- 标线 (临时去掉标线 吸附等功能)-->
|
<!-- 标线 (临时去掉标线 吸附等功能)-->
|
||||||
<!-- <MarkLine />-->
|
<!-- <MarkLine />-->
|
||||||
<!-- 选中区域 -->
|
<!-- 选中区域 -->
|
||||||
<Area v-show="isShowArea" :start="start" :width="width" :height="height" />
|
<!-- <Area v-show="isShowArea" :start="start" :width="width" :height="height" />-->
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in vLine"
|
||||||
|
v-show="item.display"
|
||||||
|
:key="'v_'+index"
|
||||||
|
class="ref-line v-line"
|
||||||
|
:style="{
|
||||||
|
left: item.position,
|
||||||
|
top: item.origin,
|
||||||
|
height: item.lineLength,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import Shape from './Shape'
|
import Shape from './Shape'
|
||||||
|
import DeDrag from '@/components/DeDrag'
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import { getStyle, getComponentRotatedStyle } from '@/components/canvas/utils/style'
|
import { getStyle, getComponentRotatedStyle } from '@/components/canvas/utils/style'
|
||||||
import { $ } from '@/components/canvas/utils/utils'
|
import { $ } from '@/components/canvas/utils/utils'
|
||||||
@ -89,7 +113,7 @@ import { Condition } from '@/components/widget/bean/Condition'
|
|||||||
import bus from '@/utils/bus'
|
import bus from '@/utils/bus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Shape, ContextMenu, MarkLine, Area, Grid },
|
components: { Shape, ContextMenu, MarkLine, Area, Grid, DeDrag },
|
||||||
props: {
|
props: {
|
||||||
isEdit: {
|
isEdit: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -128,7 +152,23 @@ export default {
|
|||||||
needToChangeWidth: [
|
needToChangeWidth: [
|
||||||
'left',
|
'left',
|
||||||
'width'
|
'width'
|
||||||
]
|
],
|
||||||
|
// private 是否可旋转
|
||||||
|
rotatable: false,
|
||||||
|
// 矩阵大小
|
||||||
|
matrixStyle: {
|
||||||
|
width: 80,
|
||||||
|
height: 20
|
||||||
|
},
|
||||||
|
// 矩阵数量 默认 12 * 24
|
||||||
|
matrixCount: {
|
||||||
|
x: 12,
|
||||||
|
y: 24
|
||||||
|
},
|
||||||
|
customStyleHistory: null,
|
||||||
|
showDrag: true,
|
||||||
|
vLine: [],
|
||||||
|
hLine: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -146,7 +186,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
customStyle() {
|
customStyle() {
|
||||||
let style = {
|
let style = {
|
||||||
width: this.format(this.canvasStyleData.width, this.scaleWidth) + 'px',
|
width: this.format(this.canvasStyleData.width, this.scaleWidth) + 'px',
|
||||||
@ -327,19 +366,26 @@ export default {
|
|||||||
handleContextMenu(e) {
|
handleContextMenu(e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
// 计算菜单相对于编辑器的位移
|
|
||||||
let target = e.target
|
let target = e.target
|
||||||
let top = e.offsetY
|
|
||||||
let left = e.offsetX
|
|
||||||
while (target instanceof SVGElement) {
|
while (target instanceof SVGElement) {
|
||||||
target = target.parentNode
|
target = target.parentNode
|
||||||
}
|
}
|
||||||
|
let top = 0
|
||||||
|
let left = 0
|
||||||
|
// 如果档期有计划的组件 坐标取当前组件的加上偏移量
|
||||||
|
if (this.curComponent && !target.className.includes('editor')) {
|
||||||
|
top = this.curComponent.style.top * this.scaleHeight / 100 + e.offsetY
|
||||||
|
left = this.curComponent.style.left * this.scaleWidth / 100 + e.offsetX
|
||||||
|
} else {
|
||||||
|
// 计算菜单相对于编辑器的位移
|
||||||
|
top = e.offsetY
|
||||||
|
left = e.offsetX
|
||||||
|
|
||||||
while (!target.className.includes('editor')) {
|
while (!target.className.includes('editor')) {
|
||||||
left += target.offsetLeft
|
left += target.offsetLeft
|
||||||
top += target.offsetTop
|
top += target.offsetTop
|
||||||
target = target.parentNode
|
target = target.parentNode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('showContextMenu', { top, left })
|
this.$store.commit('showContextMenu', { top, left })
|
||||||
@ -443,11 +489,62 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeScale() {
|
changeScale() {
|
||||||
|
// 获取当前宽高(宽高改变后重新渲染画布)
|
||||||
|
const style = {
|
||||||
|
width: this.format(this.canvasStyleData.width, this.scaleWidth) + 'px',
|
||||||
|
height: this.format(this.canvasStyleData.height, this.scaleHeight) + 'px'
|
||||||
|
}
|
||||||
|
if (this.customStyleHistory && this.customStyleHistory !== style) {
|
||||||
|
this.showDrag = false
|
||||||
|
this.$nextTick(() => (this.showDrag = true))
|
||||||
|
}
|
||||||
|
this.customStyleHistory = style
|
||||||
|
|
||||||
|
if (this.canvasStyleData.matrixCount) {
|
||||||
|
this.matrixCount = this.canvasStyleData.matrixCount
|
||||||
|
}
|
||||||
if (this.outStyle.width && this.outStyle.height) {
|
if (this.outStyle.width && this.outStyle.height) {
|
||||||
|
// 矩阵计算
|
||||||
|
if (!this.canvasStyleData.selfAdaption) {
|
||||||
|
this.matrixStyle.width = this.canvasStyleData.width / this.matrixCount.x
|
||||||
|
this.matrixStyle.height = this.canvasStyleData.height / this.matrixCount.y
|
||||||
|
} else {
|
||||||
|
this.matrixStyle.width = this.outStyle.width / this.matrixCount.x
|
||||||
|
this.matrixStyle.height = this.outStyle.height / this.matrixCount.y
|
||||||
|
}
|
||||||
this.scaleWidth = parseInt(this.outStyle.width * 100 / this.canvasStyleData.width)
|
this.scaleWidth = parseInt(this.outStyle.width * 100 / this.canvasStyleData.width)
|
||||||
this.scaleHeight = parseInt(this.outStyle.height * 100 / this.canvasStyleData.height)
|
this.scaleHeight = parseInt(this.outStyle.height * 100 / this.canvasStyleData.height)
|
||||||
this.$store.commit('setCurCanvasScale', { scaleWidth: this.scaleWidth, scaleHeight: this.scaleHeight })
|
this.$store.commit('setCurCanvasScale',
|
||||||
|
{
|
||||||
|
scaleWidth: this.scaleWidth,
|
||||||
|
scaleHeight: this.scaleHeight,
|
||||||
|
matrixStyleWidth: this.matrixStyle.width,
|
||||||
|
matrixStyleHeight: this.matrixStyle.height
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
getShapeStyleIntDeDrag(style, prop) {
|
||||||
|
if (prop === 'rotate') {
|
||||||
|
return style['rotate']
|
||||||
|
}
|
||||||
|
if (prop === 'width') {
|
||||||
|
return this.format(style['width'], this.scaleWidth)
|
||||||
|
}
|
||||||
|
if (prop === 'left') {
|
||||||
|
return this.format(style['left'], this.scaleWidth)
|
||||||
|
}
|
||||||
|
if (prop === 'height') {
|
||||||
|
return this.format(style['height'], this.scaleHeight)
|
||||||
|
}
|
||||||
|
if (prop === 'top') {
|
||||||
|
return this.format(style['top'], this.scaleHeight)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRefLineParams(params) {
|
||||||
|
console.log(params)
|
||||||
|
const { vLine, hLine } = params
|
||||||
|
this.vLine = vLine
|
||||||
|
this.hLine = hLine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,7 +553,7 @@ export default {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.editor {
|
.editor {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
/*background: #fff;*/
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background-size:100% 100% !important;
|
background-size:100% 100% !important;
|
||||||
|
|
||||||
@ -465,10 +562,35 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.edit {
|
.edit {
|
||||||
|
outline: 1px solid gainsboro;
|
||||||
.component {
|
.component {
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap_class{
|
||||||
|
padding:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽组件样式
|
||||||
|
|
||||||
|
.de-drag-active{
|
||||||
|
outline: 1px solid #70c0ff;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ref-line {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #70c0ff;;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
.v-line {
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
.h-line {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
|
|
||||||
|
<div class="canvas-config" style="margin-right: 10px">
|
||||||
|
<el-switch v-model="canvasStyleData.auxiliaryMatrix" :width="35" label="矩阵设计" name="auxiliaryMatrix" />
|
||||||
|
<span>矩阵设计</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="canvas-config" style="margin-right: 10px">
|
<div class="canvas-config" style="margin-right: 10px">
|
||||||
<el-switch v-model="canvasStyleData.selfAdaption" :width="35" label="自适应画布区域" name="selfAdaption" />
|
<el-switch v-model="canvasStyleData.selfAdaption" :width="35" label="自适应画布区域" name="selfAdaption" />
|
||||||
<span>自适应画布区域 </span>
|
<span>自适应画布区域 </span>
|
||||||
|
@ -2,41 +2,41 @@ import { swap } from '@/components/canvas/utils/utils'
|
|||||||
import toast from '@/components/canvas/utils/toast'
|
import toast from '@/components/canvas/utils/toast'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mutations: {
|
mutations: {
|
||||||
upComponent({ componentData, curComponentIndex }) {
|
upComponent({ componentData, curComponentIndex }) {
|
||||||
// 上移图层 index,表示元素在数组中越往后
|
// 上移图层 index,表示元素在数组中越往后
|
||||||
if (curComponentIndex < componentData.length - 1) {
|
if (curComponentIndex < componentData.length - 1) {
|
||||||
swap(componentData, curComponentIndex, curComponentIndex + 1)
|
swap(componentData, curComponentIndex, curComponentIndex + 1)
|
||||||
} else {
|
} else {
|
||||||
toast('已经到顶了')
|
toast('已经到顶了')
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
downComponent({ componentData, curComponentIndex }) {
|
|
||||||
// 下移图层 index,表示元素在数组中越往前
|
|
||||||
if (curComponentIndex > 0) {
|
|
||||||
swap(componentData, curComponentIndex, curComponentIndex - 1)
|
|
||||||
} else {
|
|
||||||
toast('已经到底了')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
topComponent({ componentData, curComponentIndex }) {
|
|
||||||
// 置顶
|
|
||||||
if (curComponentIndex < componentData.length - 1) {
|
|
||||||
swap(componentData, curComponentIndex, componentData.length - 1)
|
|
||||||
} else {
|
|
||||||
toast('已经到顶了')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
bottomComponent({ componentData, curComponentIndex }) {
|
|
||||||
// 置底
|
|
||||||
if (curComponentIndex > 0) {
|
|
||||||
swap(componentData, curComponentIndex, 0)
|
|
||||||
} else {
|
|
||||||
toast('已经到底了')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
downComponent({ componentData, curComponentIndex }) {
|
||||||
|
// 下移图层 index,表示元素在数组中越往前
|
||||||
|
if (curComponentIndex > 0) {
|
||||||
|
swap(componentData, curComponentIndex, curComponentIndex - 1)
|
||||||
|
} else {
|
||||||
|
toast('已经到底了')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
topComponent({ componentData, curComponentIndex }) {
|
||||||
|
// 置顶
|
||||||
|
if (curComponentIndex < componentData.length - 1) {
|
||||||
|
swap(componentData, curComponentIndex, componentData.length - 1)
|
||||||
|
} else {
|
||||||
|
toast('已经到顶了')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
bottomComponent({ componentData, curComponentIndex }) {
|
||||||
|
// 置底
|
||||||
|
if (curComponentIndex > 0) {
|
||||||
|
swap(componentData, curComponentIndex, 0)
|
||||||
|
} else {
|
||||||
|
toast('已经到底了')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
recordSnapshot(state) {
|
recordSnapshot(state) {
|
||||||
|
console.log('recordSnapshot')
|
||||||
// 添加新的快照
|
// 添加新的快照
|
||||||
state.snapshotData[++state.snapshotIndex] = deepCopy(state.componentData)
|
state.snapshotData[++state.snapshotIndex] = deepCopy(state.componentData)
|
||||||
state.snapshotStyleData[state.snapshotIndex] = deepCopy(state.canvasStyleData)
|
state.snapshotStyleData[state.snapshotIndex] = deepCopy(state.canvasStyleData)
|
||||||
|
@ -952,6 +952,7 @@ export default {
|
|||||||
close_aided_design: 'Close Component Aided Design',
|
close_aided_design: 'Close Component Aided Design',
|
||||||
open_style_design: 'Open Style Design',
|
open_style_design: 'Open Style Design',
|
||||||
close_style_design: 'Close Style Design',
|
close_style_design: 'Close Style Design',
|
||||||
|
matrix_design: 'Matrix Design',
|
||||||
left: 'X-Axis',
|
left: 'X-Axis',
|
||||||
top: 'Y-Axis',
|
top: 'Y-Axis',
|
||||||
height: 'Height',
|
height: 'Height',
|
||||||
|
@ -952,6 +952,7 @@ export default {
|
|||||||
close_aided_design: '关闭组件辅助设计',
|
close_aided_design: '关闭组件辅助设计',
|
||||||
open_style_design: '打开样式设计',
|
open_style_design: '打开样式设计',
|
||||||
close_style_design: '关闭样式设计',
|
close_style_design: '关闭样式设计',
|
||||||
|
matrix_design: '矩阵设计',
|
||||||
left: 'x 坐标',
|
left: 'x 坐标',
|
||||||
top: 'y 坐标',
|
top: 'y 坐标',
|
||||||
height: '高',
|
height: '高',
|
||||||
|
@ -952,6 +952,7 @@ export default {
|
|||||||
close_aided_design: '关闭组件辅助设计',
|
close_aided_design: '关闭组件辅助设计',
|
||||||
open_style_design: '打开样式设计',
|
open_style_design: '打开样式设计',
|
||||||
close_style_design: '关闭样式设计',
|
close_style_design: '关闭样式设计',
|
||||||
|
matrix_design: '矩阵设计',
|
||||||
left: 'x 坐标',
|
left: 'x 坐标',
|
||||||
top: 'y 坐标',
|
top: 'y 坐标',
|
||||||
height: '高',
|
height: '高',
|
||||||
|
@ -27,6 +27,7 @@ Vue.use(VueClipboard)
|
|||||||
Vue.use(widgets)
|
Vue.use(widgets)
|
||||||
Vue.prototype.$api = api
|
Vue.prototype.$api = api
|
||||||
|
|
||||||
|
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
Vue.prototype.$echarts = echarts
|
Vue.prototype.$echarts = echarts
|
||||||
|
@ -72,7 +72,7 @@ const data = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setCurComponent(state, { component, index }) {
|
setCurComponent(state, { component, index }) {
|
||||||
// console.log('curComponent' + JSON.stringify(component))
|
console.log('curComponent' + JSON.stringify(component))
|
||||||
state.curComponent = component
|
state.curComponent = component
|
||||||
state.curComponentIndex = index
|
state.curComponentIndex = index
|
||||||
},
|
},
|
||||||
@ -82,12 +82,12 @@ const data = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setShapeStyle({ curComponent, canvasStyleData, curCanvasScale }, { top, left, width, height, rotate }) {
|
setShapeStyle({ curComponent, canvasStyleData, curCanvasScale }, { top, left, width, height, rotate }) {
|
||||||
if (top) curComponent.style.top = parseInt(canvasStyleData.selfAdaption ? (top * 100 / curCanvasScale.scaleHeight) : top)
|
if (top || top === 0) curComponent.style.top = parseInt(canvasStyleData.selfAdaption ? (top * 100 / curCanvasScale.scaleHeight) : top)
|
||||||
if (left) curComponent.style.left = parseInt(canvasStyleData.selfAdaption ? (left * 100 / curCanvasScale.scaleWidth) : left)
|
if (left || left === 0) curComponent.style.left = parseInt(canvasStyleData.selfAdaption ? (left * 100 / curCanvasScale.scaleWidth) : left)
|
||||||
if (width) curComponent.style.width = parseInt(canvasStyleData.selfAdaption ? (width * 100 / curCanvasScale.scaleWidth) : width)
|
if (width || width === 0) curComponent.style.width = parseInt(canvasStyleData.selfAdaption ? (width * 100 / curCanvasScale.scaleWidth) : width)
|
||||||
if (height) curComponent.style.height = parseInt(canvasStyleData.selfAdaption ? (height * 100 / curCanvasScale.scaleHeight) : height)
|
if (height || height === 0) curComponent.style.height = parseInt(canvasStyleData.selfAdaption ? (height * 100 / curCanvasScale.scaleHeight) : height)
|
||||||
if (rotate) curComponent.style.rotate = rotate
|
if (rotate || rotate === 0) curComponent.style.rotate = rotate
|
||||||
// console.log('setShapeStyle' + JSON.stringify(curComponent))
|
// console.log('setShapeStyle:curComponent' + 'top:' + top + ';left:' + left + '====' + JSON.stringify(curComponent))
|
||||||
},
|
},
|
||||||
|
|
||||||
setShapeSingleStyle({ curComponent }, { key, value }) {
|
setShapeSingleStyle({ curComponent }, { key, value }) {
|
||||||
|
60
frontend/src/utils/dom.js
Normal file
60
frontend/src/utils/dom.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { isFunction } from './fns'
|
||||||
|
|
||||||
|
// 将选择器与父元素匹配
|
||||||
|
export function matchesSelectorToParentElements(el, selector, baseNode) {
|
||||||
|
let node = el
|
||||||
|
|
||||||
|
const matchesSelectorFunc = [
|
||||||
|
'matches',
|
||||||
|
'webkitMatchesSelector',
|
||||||
|
'mozMatchesSelector',
|
||||||
|
'msMatchesSelector',
|
||||||
|
'oMatchesSelector'
|
||||||
|
].find(func => isFunction(node[func]))
|
||||||
|
|
||||||
|
if (!isFunction(node[matchesSelectorFunc])) return false
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (node[matchesSelectorFunc](selector)) return true
|
||||||
|
if (node === baseNode) return false
|
||||||
|
node = node.parentNode
|
||||||
|
} while (node)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getComputedSize($el) {
|
||||||
|
const style = window.getComputedStyle($el)
|
||||||
|
|
||||||
|
return [
|
||||||
|
parseFloat(style.getPropertyValue('width'), 10),
|
||||||
|
parseFloat(style.getPropertyValue('height'), 10)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// 添加事件
|
||||||
|
export function addEvent(el, event, handler) {
|
||||||
|
if (!el) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (el.attachEvent) {
|
||||||
|
el.attachEvent('on' + event, handler)
|
||||||
|
} else if (el.addEventListener) {
|
||||||
|
el.addEventListener(event, handler, true)
|
||||||
|
} else {
|
||||||
|
el['on' + event] = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除事件
|
||||||
|
export function removeEvent(el, event, handler) {
|
||||||
|
if (!el) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (el.detachEvent) {
|
||||||
|
el.detachEvent('on' + event, handler)
|
||||||
|
} else if (el.removeEventListener) {
|
||||||
|
el.removeEventListener(event, handler, true)
|
||||||
|
} else {
|
||||||
|
el['on' + event] = null
|
||||||
|
}
|
||||||
|
}
|
60
frontend/src/utils/fns.js
Normal file
60
frontend/src/utils/fns.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
export function isFunction(func) {
|
||||||
|
return (typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对齐栅格
|
||||||
|
export function snapToGrid(grid, pendingX, pendingY, scale = 1) {
|
||||||
|
const x = Math.round((pendingX / scale) / grid[0]) * grid[0]
|
||||||
|
const y = Math.round((pendingY / scale) / grid[1]) * grid[1]
|
||||||
|
return [x, y]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取rect模型
|
||||||
|
export function getSize(el) {
|
||||||
|
const rect = el.getBoundingClientRect()
|
||||||
|
return [
|
||||||
|
parseInt(rect.width),
|
||||||
|
parseInt(rect.height)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeWidth(parentWidth, left, right) {
|
||||||
|
return parentWidth - left - right
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeHeight(parentHeight, top, bottom) {
|
||||||
|
return parentHeight - top - bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restrictToBounds(value, min, max) {
|
||||||
|
if (min !== null && value < min) {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max !== null && max < value) {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回相对于参考点旋转后的坐标
|
||||||
|
export function rotatedPoint(originX, originY, offsetX, offsetY, rotate) {
|
||||||
|
const rad = (Math.PI / 180) * rotate
|
||||||
|
const cos = Math.cos(rad)
|
||||||
|
const sin = Math.sin(rad)
|
||||||
|
const x = offsetX - originX
|
||||||
|
const y = offsetY - originY
|
||||||
|
return {
|
||||||
|
x: x * cos - y * sin + originX,
|
||||||
|
y: x * sin + y * cos + originY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据相对坐标返回角度,正方形为顺时针
|
||||||
|
export function getAngle(x, y) {
|
||||||
|
let theta = Math.atan2(y, x) // 正切转弧度
|
||||||
|
theta = Math.round((180 / Math.PI) * theta) // 弧度转角度
|
||||||
|
if (theta < 0) theta = 360 + theta // 控制角度在0~360度
|
||||||
|
return theta // 返回角度
|
||||||
|
}
|
@ -7,8 +7,8 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
>
|
>
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-radio v-model="panelStyleForm.gap" label="yes" @change="onChangePanelStyle">{{ $t('panel.gap') }} </el-radio>
|
<el-radio v-model="panel.gap" label="yes" @change="onChangePanelStyle">{{ $t('panel.gap') }} </el-radio>
|
||||||
<el-radio v-model="panelStyleForm.gap" label="no" @change="onChangePanelStyle">{{ $t('panel.no_gap') }}</el-radio>
|
<el-radio v-model="panel.gap" label="no" @change="onChangePanelStyle">{{ $t('panel.no_gap') }}</el-radio>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-button slot="reference" size="mini" class="shape-item">{{ $t('panel.component_gap') }} <i class="el-icon-setting el-icon--right" /></el-button>
|
<el-button slot="reference" size="mini" class="shape-item">{{ $t('panel.component_gap') }} <i class="el-icon-setting el-icon--right" /></el-button>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@ -18,18 +18,32 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { DEFAULT_PANEL_STYLE } from '@/views/panel/panel'
|
import { DEFAULT_PANEL_STYLE } from '@/views/panel/panel'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { deepCopy } from '@/components/canvas/utils/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BackgroundSelector',
|
name: 'BackgroundSelector',
|
||||||
props: {
|
props: {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
panelStyleForm: JSON.parse(JSON.stringify(DEFAULT_PANEL_STYLE))
|
panel: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: mapState([
|
||||||
|
'canvasStyleData'
|
||||||
|
]),
|
||||||
|
|
||||||
|
created() {
|
||||||
|
// 初始化赋值
|
||||||
|
this.panel = this.canvasStyleData.panel
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onChangePanelStyle() {
|
onChangePanelStyle() {
|
||||||
this.$emit('onChangePanelStyle', this.panelStyleForm)
|
const canvasStyleData = deepCopy(this.canvasStyleData)
|
||||||
|
canvasStyleData.panel = this.panel
|
||||||
|
this.$store.commit('setCanvasStyle', canvasStyleData)
|
||||||
|
this.$store.commit('recordSnapshot')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<el-collapse-item :title="$t('panel.panel')" name="panel">
|
<el-collapse-item :title="$t('panel.panel')" name="panel">
|
||||||
<el-row style="background-color: #f7f8fa; margin: 5px">
|
<el-row style="background-color: #f7f8fa; margin: 5px">
|
||||||
<background-selector class="attr-selector" />
|
<background-selector class="attr-selector" />
|
||||||
<!-- <component-gap class="attr-selector" />-->
|
<component-gap class="attr-selector" />
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item :title="$t('chart.module_style')" name="component">
|
<el-collapse-item :title="$t('chart.module_style')" name="component">
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
</de-aside-container>
|
</de-aside-container>
|
||||||
|
|
||||||
<!--画布区域-->
|
<!--画布区域-->
|
||||||
<de-main-container id="canvasInfo-main" style="margin-left: 5px;margin-right: 5px">
|
<de-main-container id="canvasInfo-main">
|
||||||
<!--左侧抽屉-->
|
<!--左侧抽屉-->
|
||||||
<el-drawer
|
<el-drawer
|
||||||
:visible.sync="show"
|
:visible.sync="show"
|
||||||
|
Loading…
Reference in New Issue
Block a user