不理解的请备注
效果图:


视频不太清楚
1、安装vue-grid-layout
npm install vue-grid-layout --save
2、安装splitPane
npm install vue - splitpane --save
3、代码块分布

4、PositionsList代码
<!-- 文件管理 -->
<template>
<a-card>
<a-row type="flex" justify="center">
<a-col :span="3" :order="1">
<a-form-item label="长:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }">
<span
><a-input type="number" @change="getLong" :disabled="disabled" v-model="form.longs" placeholder="请输入"
/></span>
</a-form-item>
</a-col>
<a-col :span="3" :order="2">
<a-form-item label="宽:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }">
<span
><a-input type="number" :disabled="disabled" @change="getLong" v-model="form.wides" placeholder="请输入"
/></span>
</a-form-item>
</a-col>
<a-col :span="3" :order="2">
<a-form-item label="高:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }">
<span><a-input type="number" @change="getLong" v-model="form.heigts" placeholder="请输入"/></span>
</a-form-item>
</a-col>
<a-col :span="3" :order="4">
<a-form-item label="层数:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }">
<span><a-input placeholder="请输入"/></span>
</a-form-item>
</a-col>
<a-col :span="3" :order="5">
<a-form-item label="行数:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }">
<span><a-input placeholder="请输入"/></span>
</a-form-item>
</a-col>
<a-col :span="4" :order="6">
<a-form-item label="缩放大小:" :labelCol="{ span: 7 }" :wrapperCol="{ span: 17 }" class="flexble">
<a-slider id="test" v-model="scaleRadios" :default-value="scaleRadios" @change="getZoom" />
</a-form-item>
</a-col>
<a-col :span="4" :order="7" style="margin-left:30px">
<a-space style="margin-top:5px">
<a-button type="primary" @click="generate" v-if="!disabled" :disabled="disabled">生成仓位图</a-button>
<a-button type="primary" @click="getreset" v-else>重置</a-button>
<a-button type="primary">生成区位图</a-button>
</a-space>
</a-col>
</a-row>
<!-- 仓位图 -->
<positchart ref="positForm" :scaleRadio="scaleRadio" :round="form" />
</a-card>
</template>
<script>
import positchart from '@views/erp/housing/modules/positchart'
export default {
components: {
positchart
},
name: 'PositionsList',
data() {
return {
form: {
long: 13,
longs: 13,
wide: 10,
wides: 10,
heigt: 50,
heigts: 50
},
disabled: false,
scaleRadios: 100,
scaleRadio: 1
}
},
methods: {
generate() {
let that = this
that.disabled = true
that.$refs.positForm.handl()
},
getLong() {
this.form.long = this.form.longs - 0
this.form.wide = this.form.wides - 0
this.form.heigt = this.form.heigts - 0
},
getreset() {
let that = this
that.disabled = false
that.form = {
long: 13,
longs: 13,
wide: 10,
wides: 10,
heigt: 50,
heigts: 50
}
that.$refs.positForm.handfe()
},
getZoom() {
this.scaleRadio = this.scaleRadios / 100 - 0
}
}
}
</script>
<style></style>
6、positchart代码块
<template>
<div v-if="layeshow" class="box">
<splitPane
:max-percent="100"
:default-percent="defaultPercentOne"
:min-percent="11"
split="vertical"
class="splitpanes__pane"
style="height: 650px;"
>
<template slot="paneL">
<!-- <button @click="fullScreenAndExit()">全屏和退出</button> -->
<lb-card title="操作区" size="default" style="padding:0px;margin:0px;height:650px;">
<div @drag="drag" @dragend="dragend('operate')" class="operate" draggable="true" unselectable="on">
操作
</div>
<div @drag="drag" @dragend="dragend('workbench')" class="workbench" draggable="true" unselectable="on">
工作台
</div>
<div @drag="drag" @dragend="dragend('Shelves')" class="Shelves" draggable="true" unselectable="on">
货架
</div>
<div @drag="drag" @dragend="dragend('passage')" class="passage" draggable="true" unselectable="on">
通道
</div>
</lb-card>
</template>
<template slot="paneR">
<div class="penclass">
<a-icon type="bug" style="color:#fff;font-size: 10px;" />
</div>
<split-pane :max-percent="100" :default-percent="defaultPercentwe" :min-percent="10" split="vertical">
<template slot="paneL">
<lb-card title="显示区" size="default" style="padding:0px;margin:0px;height: 650px;">
<div id="content" class="layoutBox" :style="`zoom:${scaleRadio ? scaleRadio : 1}`">
<grid-layout
ref="gridlayout"
:layout.sync="layout"
:col-num="round.long"
:row-height="round.heigt"
:responsive="false"
:vertical-compact="false"
:prevent-collision="true"
:use-css-transforms="true"
:is-draggable="true"
:is-resizable="true"
class="grid_cals"
>
<!-- 背景框 -->
<myGridItemPreview :itemData="backgroundData" />
<grid-item
:key="item.i"
v-for="item in layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:style="{ background: getColor(item.text) }"
>
<div class="clmodule" @click="getEdit(item)">
<span>{{ item.text }}</span>
</div>
</grid-item>
</grid-layout>
</div>
</lb-card>
</template>
<template slot="paneR">
<div class="penclass">
<a-icon type="bug" style="color:#fff;font-size: 10px;" />
</div>
<div style="margin-top: -5px;">
<a-tabs type="card" @change="callback" size="small">
<a-tab-pane v-for="item in tabPane" :key="item.key" :tab="item.title">
<positdation ref="dationForm" :jectDation="jectDation" v-if="showpane == 1" />
<positgoods v-if="showpane == 2" />
<positshelves v-if="showpane == 3" />
</a-tab-pane>
</a-tabs>
</div>
</template>
</split-pane>
</template>
</splitPane>
</div>
</template>
<script>
import VueDragResize from 'vue-drag-resize'
import { GridLayout, GridItem } from 'vue-grid-layout'
import splitPane from 'vue-splitpane'
import LbCard from '@views/Acom/LbCard'
// 基础信息
import positdation from '@views/erp/housing/modals/positdation'
// 货品
import positgoods from '@views/erp/housing/modals/positgoods'
// 货架信息
import positshelves from '@views/erp/housing/modals/positshelves'
import uuid from 'uuid'
let mouseXY = { x: null, y: null }
let DragPos = { x: null, y: null, w: 1, h: 1, i: null }
import myGridItemPreview from '@views/erp/housing/modules/itemPreview'
export default {
components: {
GridLayout,
GridItem,
myGridItemPreview,
splitPane,
LbCard,
positgoods,
VueDragResize,
positdation,
positshelves
},
props: {
round: {
type: Object,
required: true
},
scaleRadio: {
type: Number
}
},
data() {
return {
// tabs
jectDation: {},
tabPane: [
{
title: '基础信息',
key: 1
},
{
title: '货品信息',
key: 2
},
{
title: '货架详情',
key: 3
}
],
showpane: 0,
layout: [
{
x: 0,
y: 0,
w: 1,
h: 2,
i: '0',
id: '0',
text: '货架',
name: '0-001',
warehouse: '1层',
client: '云数联客户',
type: '货架',
attribute: '塔型货架',
MaxLength: 2,
width: 1
}
],
backgroundData: [],
defaultPercentOne: 11,
layeshow: false,
defaultPercentwe: 80
}
},
beforeCreate() {
// 屏幕高度
this.screenHeight = document.documentElement.clientHeight
},
mounted() {
document.addEventListener(
'dragover',
function(e) {
mouseXY.x = e.clientX
mouseXY.y = e.clientY
},
false
)
},
beforeDestroy() {},
methods: {
// tabs
callback(key) {
let that = this
that.showpane = key
},
// 显示
handl() {
let that = this
this.initPreviewData()
that.layeshow = true
console.log(this.round)
},
// 隐藏
handfe() {
let that = this
that.layeshow = false
},
initPreviewData() {
this.backgroundData = []
for (let i = 0; i < this.round.long; i++) {
for (let j = 0; j < this.round.wide; j++) {
this.backgroundData.push({
x: i,
y: j,
w: 1,
h: 1,
i: uuid // 防止 key 重复
})
}
}
},
getEdit(item) {
console.log(item)
let that = this
that.showpane = 1
that.jectDation = item
},
drag(e) {
let parentRect = document.getElementById('content').getBoundingClientRect()
let mouseInGrid = false
if (
mouseXY.x > parentRect.left &&
mouseXY.x < parentRect.right &&
mouseXY.y > parentRect.top &&
mouseXY.y < parentRect.bottom
) {
mouseInGrid = true
}
if (mouseInGrid === true && this.layout.findIndex(item => item.i === 'drop') === -1) {
this.layout.push({
x: (this.layout.length * 2) % (this.colNum || this.round.long),
y: this.layout.length + (this.colNum || this.round.long), // puts it at the bottom
w: 1,
h: 1,
i: 'drop'
})
}
let index = this.layout.findIndex(item => item.i === 'drop')
if (index !== -1) {
try {
// this.$refs.gridlayout.$children[this.layout.length].$refs.item.style.display = 'none'
} catch {}
let el = this.$refs.gridlayout.$children[index]
el.dragging = { top: mouseXY.y - parentRect.top, left: mouseXY.x - parentRect.left }
let new_pos = el.calcXY(mouseXY.y - parentRect.top, mouseXY.x - parentRect.left)
if (mouseInGrid === true) {
this.$refs.gridlayout.dragEvent('dragstart', 'drop', new_pos.x, new_pos.y, 1, 1)
DragPos.i = String(index)
DragPos.x = this.layout[index].x
DragPos.y = this.layout[index].y
}
if (mouseInGrid === false) {
this.$refs.gridlayout.dragEvent('dragend', 'drop', new_pos.x, new_pos.y, 1, 1)
this.layout = this.layout.filter(obj => obj.i !== 'drop')
}
}
},
dragend(text) {
let obj = {}
if (text == 'operate') {
obj.text = '操作'
obj.h = 1
}
if (text == 'workbench') {
obj.text = '工作台'
obj.h = 1
}
if (text == 'Shelves') {
obj.text = '货架'
obj.h = 2
}
if (text == 'passage') {
obj.text = '通道'
obj.h = 3
}
let parentRect = document.getElementById('content').getBoundingClientRect()
let mouseInGrid = false
if (
mouseXY.x > parentRect.left &&
mouseXY.x < parentRect.right &&
mouseXY.y > parentRect.top &&
mouseXY.y < parentRect.bottom
) {
mouseInGrid = true
}
if (mouseInGrid === true) {
this.layout = this.layout.filter(obj => obj.i !== 'drop')
this.layout.push({
x: DragPos.x,
y: DragPos.y,
w: 1,
h: obj.h,
i: DragPos.i,
moved: true,
text: obj.text,
type: obj.text,
name: obj.h + DragPos.i,
id: '0-' + obj.h + DragPos.i,
warehouse: '1层',
client: '云数联客户',
attribute: '塔型货架',
MaxLength: obj.h,
width: 1
})
console.log(this.layout)
try {
this.$refs.gridLayout.$children[this.layout.length].$refs.item.style.display = 'block'
} catch {}
}
},
closeItemHandle(item) {
// 预览与正式数据公用一个 item 因此,需两者都清除数据
this.layout.splice(this.layout.findIndex(i => i.i == item.i))
this.backgroundData.splice(this.backgroundData.findIndex(i => i.i == item.i))
},
// 颜色
getColor(text) {
let color = ''
if (text == '操作') color = 'green'
if (text == '工作台') color = 'red'
if (text == '货架') color = 'blue'
if (text == '通道') color = 'orange'
return color
},
// 全屏
// 点击事件
fullScreenAndExit() {
const el = document.querySelector('.box')
this.toggleFullScreen(el)
},
/**
* 切换全屏
* @param {HTMLElement} element dom元素
*/
toggleFullScreen(element) {
if (!this.isFullScreen) {
this.fullScreen(element)
} else {
this.exitFullScreen()
}
this.isFullScreen = !this.isFullScreen
},
/**
* 全屏
* @param {HTMLElement} element dom元素
*/
fullScreen(element) {
const requestMethod =
element.requestFullScreen ||
element.webkitRequestFullScreen ||
element.mozRequestFullScreen ||
element.msRequestFullScreen
if (requestMethod) {
requestMethod.call(element)
}
if (typeof window.ActiveXObject !== 'undefined') {
let wscript = new ActiveXObject('WScript.Shell')
if (wscript !== null) {
wscript.SendKeys('{F11}')
}
}
},
/**
* 退出全屏
*/
exitFullScreen() {
const documentElement = document
const cfs =
documentElement.cancelFullScreen ||
documentElement.webkitCancelFullScreen ||
documentElement.mozCancelFullScreen ||
documentElement.exitFullScreen
if (cfs) {
cfs.call(documentElement)
} else if (typeof window.ActiveXObject !== 'undefined') {
// for IE,这里和fullScreen相同,模拟按下F11键退出全屏
const wscript = new ActiveXObject('WScript.Shell')
if (wscript != null) {
wscript.SendKeys('{F11}')
}
}
}
}
}
</script>
<style lang="less" scoped>
.penclass {
position: absolute;
background: blue;
top: 40%;
left: -5px;
width: 10px;
height: 45px;
display: flex;
justify-content: center;
align-self: center;
align-items: center;
border-radius: 10px;
}
// 操作区
.operate {
height: 60px;
background: green;
margin: 10px 0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
// 货架版
.Shelves {
height: 120px;
background: blue;
margin: 10px 0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
// 工作台
.workbench {
height: 60px;
background: red;
margin: 10px 0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
// 通道版
.passage {
height: 180px;
background: orange;
margin: 10px 0;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
.clmodule {
display: flex;
justify-content: center;
align-items: center;
align-self: center;
height: 100%;
color: #fff;
}
.vue-grid-item .no-drag {
height: 100%;
width: 100%;
}
.vue-grid-item .minMax {
font-size: 12px;
}
.vue-grid-item .add {
cursor: pointer;
}
.layoutBox {
width: 100%;
height: 100%;
}
// .grid_cals {
// width: 100%;
// overflow-x: auto;
// display: -webkit-box;
// -webkit-overflow-scrolling: touch;
// min-height: 600px;
// }
// .grid_cals::-webkit-scrollbar {
// display: none;
// }
</style>
7、itemPreview代码块
<template>
<div>
<grid-item
v-for="item in itemData"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:key="item.i"
:is-draggable="false"
:is-resizable="false"
class="grid-item"
/>
</div>
</template>
<script>
import { GridItem } from 'vue-grid-layout'
export default {
name: 'VueLayoutItem',
props: ['itemData'],
components: { GridItem }
}
</script>
<style lang="less" scoped>
.grid-item {
// border-top: 2px solid rgba(1, 153, 209, 0.5);
border: #999 dashed 1px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
</style>
8、positdation代码块
<!-- 基础信息 -->
<template>
<div>
<span v-if="Object.keys(parent).length == 0">
<a-empty
image="https://gw.alipayobjects.com/mdn/miniapp_social/afts/img/A*pevERLJC9v0AAAAAAAAAAABjAQAAAQ/original"
/>
</span>
<a-row v-else>
<a-col :span="24">
<a-form-model-item label="库位编号:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.name" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="所在仓库:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.warehouse" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="所属客户:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.client" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="库位类型:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.type" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="库位属性:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.attribute" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="最大长度:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.MaxLength" />
</a-form-model-item>
</a-col>
<a-col :span="24">
<a-form-model-item label="最大宽度:" :labelCol="{ span: 6 }" :wrapperCol="{ span: 15 }">
<a-input placeholder="" v-model="parent.width" />
</a-form-model-item>
</a-col>
<a-col :span="12" style="margin-top: 50px;">
<span style="display: flex;justify-content: center;"><a-button type="primary">修改</a-button></span>
</a-col>
<a-col :span="12" style="margin-top: 50px;">
<span style="display: flex;justify-content: center;"><a-button type="primary">确定</a-button></span>
</a-col>
</a-row>
</div>
</template>
<script>
export default {
name: 'positdation',
data() {
return {
parent: {}
}
},
props: {
jectDation: {
type: Object,
required: true
}
},
watch: {
jectDation: {
immediate: true,
handler(val) {
let that = this
that.parent = val
}
}
},
methods: {
// 传递过来的参数
getDation(item) {
let that = this
that.parent = {}
that.parent = item
}
}
}
</script>
<style lang="less" scoped></style>
9、positgoods和positshelves为空文件