feat:封装画布组件

This commit is contained in:
wangjiahao 2021-03-30 16:11:59 +08:00
parent 8016aba46a
commit af24ba2733
11 changed files with 173 additions and 3690 deletions

View File

@ -0,0 +1,169 @@
<template>
<div class="home">
<Toolbar />
<main>
<!-- 左侧组件列表 -->
<!-- <section class="left">-->
<!-- <ComponentList />-->
<!-- </section>-->
<!-- 中间画布 -->
<section class="center">
<div
class="content"
@drop="handleDrop"
@dragover="handleDragOver"
@mousedown="handleMouseDown"
@mouseup="deselectCurComponent"
>
<Editor />
</div>
</section>
</main>
</div>
</template>
<script>
import Editor from '@/components/Editor/index'
import ComponentList from '@/components/ComponentList' //
import AttrList from '@/components/canvas/components/AttrList' //
import AnimationList from '@/components/canvas/components/AnimationList' //
import EventList from '@/components/canvas/components/EventList' //
import componentList from '@/components/canvas/custom-component/component-list' //
import Toolbar from '@/components/Toolbar'
import { deepCopy } from '@/utils/utils'
import { mapState } from 'vuex'
import generateID from '@/utils/generateID'
import { listenGlobalKeyDown } from '@/utils/shortcutKey'
export default {
components: { Editor, ComponentList, AttrList, AnimationList, EventList, Toolbar },
data() {
return {
activeName: 'attr',
reSelectAnimateIndex: undefined
}
},
computed: mapState([
'componentData',
'curComponent',
'isClickComponent',
'canvasStyleData'
]),
created() {
this.restore()
//
listenGlobalKeyDown()
},
methods: {
restore() {
//
if (localStorage.getItem('canvasData')) {
this.$store.commit('setComponentData', this.resetID(JSON.parse(localStorage.getItem('canvasData'))))
}
if (localStorage.getItem('canvasStyle')) {
this.$store.commit('setCanvasStyle', JSON.parse(localStorage.getItem('canvasStyle')))
}
},
resetID(data) {
data.forEach(item => {
item.id = generateID()
})
return data
},
handleDrop(e) {
e.preventDefault()
e.stopPropagation()
debugger
let component
const id = e.dataTransfer.getData('componentId')
componentList.forEach(componentTemp => {
if (id == componentTemp.id) {
component = deepCopy(componentTemp)
}
})
// const component = deepCopy(componentList[e.dataTransfer.getData('index')])
component.style.top = e.offsetY
component.style.left = e.offsetX
component.id = generateID()
this.$store.commit('addComponent', { component })
this.$store.commit('recordSnapshot')
},
handleDragOver(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
},
handleMouseDown() {
this.$store.commit('setClickComponentStatus', false)
},
deselectCurComponent(e) {
if (!this.isClickComponent) {
this.$store.commit('setCurComponent', { component: null, index: null })
}
// 0 1 2
if (e.button != 2) {
this.$store.commit('hideContextMenu')
}
}
}
}
</script>
<style lang="scss">
.home {
height: 100vh;
background: #fff;
main {
height: calc(100% - 64px);
position: relative;
.left {
position: absolute;
height: 100%;
width: 200px;
left: 0;
top: 0;
padding-top: 10px;
}
.right {
position: absolute;
height: 100%;
width: 262px;
right: 0;
top: 0;
}
.center {
margin-left: 200px;
margin-right: 262px;
background: #f5f5f5;
height: 100%;
overflow: auto;
padding: 20px;
.content {
width: 100%;
height: 100%;
overflow: auto;
}
}
}
.placeholder {
text-align: center;
color: #333;
}
}
</style>

View File

@ -1,60 +0,0 @@
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
}
}

View File

@ -1,40 +0,0 @@
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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
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
}
}

View File

@ -1,40 +0,0 @@
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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,262 +0,0 @@
<template>
<el-row class="panel-design-show">
<div class="container" :style="panelDetails.gridStyle">
<vue-drag-resize-rotate
v-for="item in panelDetails.panelDesigns"
v-show="item.keepFlag"
:key="item.id"
:disabled="status"
:panel-design="item"
:parent="true"
@newStyle="newStyle"
>
<!--视图显示 panelDesign.componentType==='view'-->
<chart-component v-if="item.componentType==='view'" :ref="item.id" :chart-id="item.id" :chart="item.chartView" />
<!--组件显示待开发-->
</vue-drag-resize-rotate>
</div>
</el-row>
</template>
<script>
import { post, get } from '@/api/panel/panel'
import ChartComponent from '@/views/chart/components/ChartComponent'
import VueDragResizeRotate from '@/components/vue-drag-resize-rotate'
import { uuid } from 'vue-uuid'
import bus from '@/utils/bus'
export default {
name: 'DrawingBoard',
components: { ChartComponent, VueDragResizeRotate },
props: {
status: {
type: Boolean,
default: false
}
},
data() {
return {
panelDetails: {
viewsUsable: [],
panelDesigns: [],
gridStyle: null
},
gridStyleDefault: {
position: 'relative',
height: '100%',
width: '100%',
backgroundColor: '#f2f2f2',
// background: 'linear-gradient(-90deg, rgba(0, 0, 0, .1) 1px, transparent 1px), linear-gradient(rgba(0, 0, 0, .1) 1px, transparent 1px)',
backgroundSize: '20px 20px, 20px 20px'
},
ViewActiveName: 'Views'
}
},
computed: {
panelInfo() {
return this.$store.state.panel.panelInfo
}
},
watch: {
panelInfo(newVal, oldVal) {
this.panelDesign(newVal.id)
}
},
mounted() {
const panelId = this.$store.state.panel.panelInfo.id
if (panelId) {
this.panelDesign(panelId)
}
bus.$on('panel-drawing-load', (panelId) => {
panelId && this.panelDesign(panelId)
})
bus.$on('panel-view-add', (view) => {
view && this.panelViewAdd(view)
})
bus.$on('panel-drawing-save', () => {
this.savePanel()
})
bus.$on('panel-drawing-preview', () => {
this.preViewShow()
})
},
methods: {
//
// panel design
panelDesign(panelId) {
get('panel/group/findOne/' + panelId).then(res => {
const panelDetailsInfo = res.data
if (panelDetailsInfo) {
this.panelDetails = panelDetailsInfo
}
if (!panelDetailsInfo.gridStyle) {
this.panelDetails.gridStyle = this.gridStyleDefault
}
})
},
panelViewAdd(view) {
const panelDesigns = this.panelDetails.panelDesigns
this.panelDetails.viewsUsable.forEach(function(item, index) {
if (item.id === view.id) {
const newComponent = {
id: uuid.v1(),
keepFlag: true,
chartView: item,
componentType: 'view',
styleInit: false
}
panelDesigns.push(newComponent)
}
})
},
newStyle(viewId, newStyleInfo) {
this.$nextTick(() => {
this.$refs[viewId][0].chartResize()
})
this.panelInfo.preStyle = JSON.stringify(newStyleInfo)
},
//
start1(e) {
console.log(e)
},
end1(e) {
console.log(e)
},
//
start2(e) {
console.log(e)
},
end2(e) {
console.log(e)
},
// move
onMove(e, originalEvent) {
console.log(e)
return true
},
preViewShow() {
this.$router.replace('/preview')
},
savePanel() {
post('panel/group/saveGroupWithDesign', this.panelDetails, () => {
})
this.$success(this.$t('commons.save_success'))
}
}
}
</script>
<style scoped>
.view-list {
height: 100%;
width: 20%;
min-width: 180px;
max-width: 220px;
border: 1px solid #E6E6E6;
border-left: 0 solid;
overflow-y: auto;
}
.view-list-thumbnails-outline {
height: 100%;
overflow-y: auto;
}
.view-list-thumbnails {
width: 100%;
padding: 0px 15px 15px 0px;
}
.panel-design {
height: 100%;
min-width: 500px;
border-top: 1px solid #E6E6E6;
}
.panel-design-head {
height: 40px;
}
.panel-design-show {
height: 100%;
width: 100%;
border-top: 1px solid #E6E6E6;
}
.padding-lr {
padding: 0 6px;
}
.itxst {
margin: 10px;
text-align: left;
}
.col {
width: 40%;
flex: 1;
padding: 10px;
border: solid 1px #eee;
border-radius: 5px;
float: left;
}
.col + .col {
margin-left: 10px;
}
.item {
padding: 2px 12px;
margin: 3px 3px 0 3px;
border: solid 1px #eee;
background-color: #f1f1f1;
text-align: left;
display: block;
}
.item + .item {
border-top: none;
margin-top: 3px;
}
.item:hover {
background-color: #fdfdfd;
cursor: pointer;
}
.item-axis {
padding: 2px 12px;
margin: 3px 3px 0 3px;
border: solid 1px #eee;
background-color: #f1f1f1;
text-align: left;
}
.item-axis:hover {
background-color: #fdfdfd;
cursor: pointer;
}
.el-form-item {
margin-bottom: 0;
}
.container {
width: 100%;
height: 600px;
border: 1px solid #000;
position: relative;
box-sizing: border-box;
}
span {
font-size: 12px;
}
</style>

View File

@ -33,13 +33,12 @@ import DeContainer from '@/components/dataease/DeContainer'
import DeAsideContainer from '@/components/dataease/DeAsideContainer'
// import Group from './group/Group'
import PanelList from './list/PanelList'
import PanelView from './list/PanelView'
import PanelViewShow from './list/PanelViewShow'
import ShareTree from './GrantAuth/shareTree'
export default {
name: 'Panel',
components: { DeMainContainer, DeContainer, DeAsideContainer, PanelList, PanelView, PanelViewShow, ShareTree },
components: { DeMainContainer, DeContainer, DeAsideContainer, PanelList, PanelViewShow, ShareTree },
data() {
return {
component: PanelViewShow,

View File

@ -1,393 +0,0 @@
<template xmlns:el-col="http://www.w3.org/1999/html">
<el-row style="height: 100%;overflow-y: hidden;width: 100%;">
<span>仪表盘名称{{ panelName }}</span>
</el-row>
</template>
<script>
import { loadTable, getScene, addGroup, delGroup, addTable, delTable, groupTree } from '@/api/dataset/dataset'
export default {
name: 'PanelView',
data() {
return {
sceneMode: false,
dialogTitle: '',
search: '',
editGroup: false,
editTable: false,
tData: [],
tableData: [],
currGroup: {},
expandedArray: [],
groupForm: {
name: '',
pid: null,
level: 0,
type: '',
children: [],
sort: 'type desc,name asc'
},
tableForm: {
name: '',
mode: '',
sort: 'type asc,create_time desc,name asc'
},
groupFormRules: {
name: [
{ required: true, message: this.$t('commons.input_content'), trigger: 'blur' }
]
},
tableFormRules: {
name: [
{ required: true, message: this.$t('commons.input_content'), trigger: 'blur' }
],
mode: [
{ required: true, message: this.$t('commons.input_content'), trigger: 'blur' }
]
}
}
},
computed: {
panelName: function() {
console.log(this.$store.state.panel.panelName)
return this.$store.state.panel.panelName
}
},
watch: {
// search(val){
// this.groupForm.name = val;
// this.tree(this.groupForm);
// }
},
mounted() {
this.tree(this.groupForm)
this.refresh()
this.tableTree()
// this.$router.push('/dataset');
},
methods: {
clickAdd(param) {
// console.log(param);
this.add(param.type)
this.groupForm.pid = param.data.id
this.groupForm.level = param.data.level + 1
},
beforeClickAdd(type, data, node) {
return {
'type': type,
'data': data,
'node': node
}
},
clickMore(param) {
console.log(param)
switch (param.type) {
case 'rename':
this.add(param.data.type)
this.groupForm = JSON.parse(JSON.stringify(param.data))
break
case 'move':
break
case 'delete':
this.delete(param.data)
break
case 'editTable':
this.editTable = true
this.tableForm = JSON.parse(JSON.stringify(param.data))
this.tableForm.mode = this.tableForm.mode + ''
break
case 'deleteTable':
this.deleteTable(param.data)
break
}
},
beforeClickMore(type, data, node) {
return {
'type': type,
'data': data,
'node': node
}
},
add(type) {
switch (type) {
case 'group':
this.dialogTitle = this.$t('dataset.group')
break
case 'scene':
this.dialogTitle = this.$t('dataset.scene')
break
}
this.groupForm.type = type
this.editGroup = true
},
saveGroup(group) {
// console.log(group);
this.$refs['groupForm'].validate((valid) => {
if (valid) {
addGroup(group).then(res => {
this.close()
this.$message({
message: this.$t('commons.save_success'),
type: 'success',
showClose: true
})
this.tree(this.groupForm)
})
} else {
this.$message({
message: this.$t('commons.input_content'),
type: 'error',
showClose: true
})
return false
}
})
},
saveTable(table) {
// console.log(table)
table.mode = parseInt(table.mode)
this.$refs['tableForm'].validate((valid) => {
if (valid) {
addTable(table).then(response => {
this.closeTable()
this.$message({
message: this.$t('commons.save_success'),
type: 'success',
showClose: true
})
this.tableTree()
// this.$router.push('/dataset/home')
this.$emit('switchComponent', { name: '' })
this.$store.dispatch('dataset/setTable', null)
})
} else {
this.$message({
message: this.$t('commons.input_content'),
type: 'error',
showClose: true
})
return false
}
})
},
delete(data) {
this.$confirm(this.$t('dataset.confirm_delete'), this.$t('dataset.tips'), {
confirmButtonText: this.$t('dataset.confirm'),
cancelButtonText: this.$t('dataset.cancel'),
type: 'warning'
}).then(() => {
delGroup(data.id).then(response => {
this.$message({
type: 'success',
message: this.$t('dataset.delete_success'),
showClose: true
})
this.tree(this.groupForm)
})
}).catch(() => {
})
},
deleteTable(data) {
this.$confirm(this.$t('dataset.confirm_delete'), this.$t('dataset.tips'), {
confirmButtonText: this.$t('dataset.confirm'),
cancelButtonText: this.$t('dataset.cancel'),
type: 'warning'
}).then(() => {
delTable(data.id).then(response => {
this.$message({
type: 'success',
message: this.$t('dataset.delete_success'),
showClose: true
})
this.tableTree()
// this.$router.push('/dataset/home')
this.$emit('switchComponent', { name: '' })
this.$store.dispatch('dataset/setTable', null)
})
}).catch(() => {
})
},
close() {
this.editGroup = false
this.groupForm = {
name: '',
pid: null,
level: 0,
type: '',
children: [],
sort: 'type desc,name asc'
}
},
closeTable() {
this.editTable = false
this.tableForm = {
name: ''
}
},
tree(group) {
groupTree(group).then(res => {
this.tData = res.data
})
},
tableTree() {
this.tableData = []
if (this.currGroup.id) {
loadTable({
sort: 'type asc,create_time desc,name asc',
sceneId: this.currGroup.id
}).then(res => {
this.tableData = res.data
})
}
},
nodeClick(data, node) {
// console.log(data);
// console.log(node);
if (data.type === 'scene') {
this.sceneMode = true
this.currGroup = data
this.$store.dispatch('dataset/setSceneData', this.currGroup.id)
}
if (node.expanded) {
this.expandedArray.push(data.id)
} else {
const index = this.expandedArray.indexOf(data.id)
if (index > -1) {
this.expandedArray.splice(index, 1)
}
}
// console.log(this.expandedArray);
},
back() {
this.sceneMode = false
// const route = this.$store.state.permission.currentRoutes
// console.log(route)
// this.$router.push('/dataset/index')
this.$store.dispatch('dataset/setSceneData', null)
this.$emit('switchComponent', { name: '' })
},
clickAddData(param) {
// console.log(param);
switch (param.type) {
case 'db':
this.addDB()
break
case 'sql':
this.$message(param.type)
break
case 'excel':
this.$message(param.type)
break
case 'custom':
this.$message(param.type)
break
}
},
beforeClickAddData(type) {
return {
'type': type
}
},
addDB() {
// this.$router.push({
// name: 'add_db',
// params: {
// scene: this.currGroup
// }
// })
this.$emit('switchComponent', { name: 'AddDB', param: this.currGroup })
},
sceneClick(data, node) {
// console.log(data);
this.$store.dispatch('dataset/setTable', null)
this.$store.dispatch('dataset/setTable', data.id)
// this.$router.push({
// name: 'table',
// params: {
// table: data
// }
// })
this.$emit('switchComponent', { name: 'ViewTable' })
},
refresh() {
const path = this.$route.path
if (path === '/dataset/table') {
this.sceneMode = true
const sceneId = this.$store.state.dataset.sceneData
getScene(sceneId).then(res => {
this.currGroup = res.data
})
}
},
panelDefaultClick(data, node) {
// console.log(data);
// console.log(node);
}
}
}
</script>
<style scoped>
.header-title {
font-size: 14px;
flex: 1;
color: #606266;
font-weight: bold;
}
.el-divider--horizontal {
margin: 12px 0
}
.search-input {
padding: 12px 0;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.custom-position {
flex: 1;
display: flex;
align-items: center;
font-size: 14px;
flex-flow: row nowrap;
}
.form-item {
margin-bottom: 0;
}
.title-css {
height: 26px;
}
.title-text {
line-height: 26px;
}
</style>

View File

@ -20,7 +20,7 @@
<de-container>
<de-main-container class="ms-main-container">
<drawing-board :disabled="true" />
<!-- <drawing-board :disabled="true" />-->
</de-main-container>
</de-container>
</el-container>
@ -29,13 +29,11 @@
<script>
import DeMainContainer from '@/components/dataease/DeMainContainer'
import DeContainer from '@/components/dataease/DeContainer'
import DrawingBoard from '../DrawingBoard'
// import DrawingBoard from '../DrawingBoard'
export default {
components: {
DeMainContainer,
DeContainer,
DrawingBoard
DeContainer
},
data() {
return {