You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
15 KiB
Vue

<template>
<div style="overflow:hidden;">
<div class="top">
<div style="line-height: 60px;margin-left: 12px;">
<el-button-group>
<el-button :type="pageType === 'A3' ? 'primary' : ''" @click="setPage('A3',42,29.7)">A3</el-button>
<el-button :type="pageType === 'A4' ? 'primary' : ''" @click="setPage('A4',21,29.7)">A4</el-button>
<el-button :type="pageType === 'A5' ? 'primary' : ''" @click="setPage('A5',14.8,21)">A5</el-button>
<el-button :type="pageType === 'B3' ? 'primary' : ''" @click="setPage('B3',35.3,50)">B3</el-button>
<el-button :type="pageType === 'B4' ? 'primary' : ''" @click="setPage('B4',25,35.2)">B4</el-button>
<el-button :type="pageType === 'B5' ? 'primary' : ''" @click="setPage('B5',17.6,25)">B5</el-button>
<el-button :type="pageType === 'zdy' ? 'primary' : ''" @click="setPage('zdy')"></el-button>
</el-button-group>
<el-input-number :disabled="pageType !== 'zdy'" v-model="pageWidth" style="width: 80px;margin-left: 8px;"
:min="0" :controls="false" />
<span style="margin: 0 8px">x</span>
<el-input-number :disabled="pageType !== 'zdy'" v-model="pageHeight" style="width: 80px" :min="0"
:controls="false" />
<el-button @click="zoomChange(-0.1)" icon="ZoomOut" circle style="margin-left: 12px;" />
<el-input v-model="zoomInput" disabled style="margin-left: 8px;width:100px" />
<el-button @click="zoomChange(0.1)" icon="ZoomIn" circle style="margin-left: 8px;" />
<el-button type="primary" @click="view" style="margin-left: 12px;">预览</el-button>
<el-button type="primary" @click="save" style="margin-left: 12px;">保存</el-button>
<el-button type="primary" @click="clear" style="margin-left: 12px;">清空</el-button>
</div>
</div>
<div class="bottom">
<div class="leftPanel">
<h4>基础组件</h4>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'text',pid)" class="mx-1" size="large">文字</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'image',pid)" class="mx-1" size="large">图片</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'barCode',pid)" class="mx-1" size="large">条形码
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'QRCode',pid)" class="mx-1" size="large">二维码
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'dateTime',pid)" class="mx-1" size="large">日期时间
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'select',pid)" class="mx-1" size="large">可选择
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'transverseLine',pid)" class="mx-1" size="large">横线
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'verticalLine',pid)" class="mx-1" size="large">竖线
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'rectangle',pid)" class="mx-1" size="large">矩形
</el-tag>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'circle',pid)" class="mx-1" size="large">圆形
</el-tag>
<h4>表单组件</h4>
<el-tag :draggable="true" @dragstart="onDragStart($event, 'text',pid,{name:'deviceName'})" class="mx-1"
size="large">
设备名称
</el-tag>
</div>
<div class="content" @drop="onDrop">
<div class="flowArea" :style="{width:zoom * cmToPx(pageWidth)+'px',height:zoom * cmToPx(pageHeight)+'px'}">
<VueFlow :autoPanSpeed="0" :min-zoom="0.01" ref="flowRef" v-model:nodes="nodes" v-model:edges="edges"
:zoomOnScroll="false"
:snapToGrid="isSnapToGrid" :snapGrid="[cmToPx(0.5), cmToPx(0.5)]"
:panOnDrag="false"
fit-view-on-init
default-marker-color="#409EFF"
@dragover="onDragOver"
@node-click="logEvent('click', $event)"
@pane-click="logEvent('paneClick', $event)"
@node-drag-start="logEvent('nodeDrag', $event)"
@node-drag-stop="logEvent('nodeDragEnd', $event)"
>
<Background variant="lines" :size="0.4" :gap="cmToPx(0.5)" pattern-color="#ddd"
style="background-color: #fff" />
<template #node-area="areaNodeProps">
<AreaNode v-bind="areaNodeProps"
:pageData="{width: cmToPx(pageWidth)+'px',height: cmToPx(pageHeight)+'px'}"></AreaNode>
</template>
<template #node-text="textNodeProps">
<TextNode v-bind="textNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></TextNode>
</template>
<template #node-image="imageNodeProps">
<ImageNode v-bind="imageNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></ImageNode>
</template>
<template #node-barCode="barCodeNodeProps">
<BarCodeNode v-bind="barCodeNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></BarCodeNode>
</template>
<template #node-QRCode="QRCodeNodeProps">
<QRCodeNode v-bind="QRCodeNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></QRCodeNode>
</template>
<template #node-dateTime="dateTimeNodeProps">
<TimeNode v-bind="dateTimeNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></TimeNode>
</template>
<template #node-select="selectNodeProps">
<SelectNode v-bind="selectNodeProps"
:pageSize="{width:zoom * cmToPx(pageWidth) ,height:zoom * cmToPx(pageHeight) }"></SelectNode>
</template>
</VueFlow>
</div>
</div>
<div class="rightPanel" style="padding: 8px">
<el-form :model="nodeAttrForm" label-width="auto" style="max-width: 600px">
<el-form-item label="组件大小" v-if="Object.keys(nodeData).length>0">
<el-input-number :min="1" :controls="false" v-model="nodeData.dimensions.width"
style="width: calc(50% - 12px)"
@change="nodeData.dimensions.width = Math.max(1,nodeData.dimensions.width);nodeChange()" />
<div style="display: inline-block;width: 20px;text-align: center">x</div>
<el-input-number :min="1" :controls="false" v-model="nodeData.dimensions.height"
style="width: calc(50% - 12px)"
@change="nodeData.dimensions.height = Math.max(1,nodeData.dimensions.height);nodeChange()" />
</el-form-item>
<el-form-item label="组件位置" v-if="Object.keys(nodeData).length>0">
<el-input-number :precision="2" :min="1" :controls="false" v-model="nodeData.position.x"
style="width: calc(50% - 12px)"
@change="nodeData.position.x = Math.max(1,nodeData.position.x);nodeChange()" />
<div style="display: inline-block;width: 20px;text-align: center">x</div>
<el-input-number :precision="2" :min="1" :controls="false" v-model="nodeData.position.y"
style="width: calc(50% - 12px)"
@change="nodeData.position.y = Math.max(1,nodeData.position.y);nodeChange()" />
</el-form-item>
<el-form-item label="名称" v-if="Object.keys(nodeAttrForm).includes('name')">
<el-input v-model="nodeAttrForm.name" style="width: 100%" />
</el-form-item>
<el-form-item label="字段名" v-if="Object.keys(nodeAttrForm).includes('field')">
<el-input v-model="nodeAttrForm.field" style="width: 100%" />
</el-form-item>
<el-form-item label="缺省显示" v-if="Object.keys(nodeAttrForm).includes('default')">
<el-input v-model="nodeAttrForm.default" style="width: 100%" />
</el-form-item>
<el-form-item label="图片地址" v-if="Object.keys(nodeAttrForm).includes('imageSrc')">
<el-input v-model="nodeAttrForm.imageSrc" style="width: 100%" />
</el-form-item>
<el-form-item label="时间类型" v-if="Object.keys(nodeAttrForm).includes('dateTimeType')">
<el-select
v-model="nodeAttrForm.dateTimeType"
placeholder="Select"
>
<el-option
label="实时"
value="1"
/>
</el-select>
</el-form-item>
<el-form-item v-if="Object.keys(nodeAttrForm).includes('selectOptions')" label="选项集合">
<el-input-tag
v-model="nodeAttrForm.selectOptions"
/>
</el-form-item>
<el-form-item v-if="Object.keys(nodeAttrForm).includes('horizontalAlign')" label="水平对齐">
<el-select
v-model="nodeAttrForm.horizontalAlign"
placeholder="Select"
>
<el-option
label="左对齐"
value="left"
/>
<el-option
label="居中对齐"
value="center"
/>
<el-option
label="右对齐"
value="right"
/>
</el-select>
</el-form-item>
<el-form-item v-if="Object.keys(nodeAttrForm).includes('verticalAlign')" label="垂直对齐">
<el-select
v-model="nodeAttrForm.verticalAlign"
placeholder="Select"
>
<el-option
label="顶部对齐"
value="start"
/>
<el-option
label="居中对齐"
value="center"
/>
<el-option
label="底部对齐"
value="end"
/>
</el-select>
</el-form-item>
</el-form>
</div>
</div>
<!-- <View v-if="isView" v-model:isView="isView" />-->
</div>
</template>
<script setup lang="ts">
import { v4 as uuidv4 } from 'uuid';
import { useRouter } from 'vue-router';
import { useVueFlow, VueFlow } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import tool, { options } from './tool';
import AreaNode from './nodes/areaNode.vue';
import TextNode from './nodes/textNode.vue';
import ImageNode from './nodes/image.vue';
import BarCodeNode from './nodes/barCodeNode.vue';
import QRCodeNode from './nodes/QRCodeNode.vue';
import TimeNode from './nodes/timeNode.vue';
import SelectNode from './nodes/selectNode.vue';
const router = useRouter();
const { updateNode } = useVueFlow();
// 获取自定义的方法
const { onDragStart, onDrop, onDragOver } = tool();
const { cmToPx } = options;
const internalInstance = getCurrentInstance();
const flowRef = ref();
// const isView = ref(false);
const isSnapToGrid = ref(true);
const pageWidth = ref(21);
const pageHeight = ref(29.7);
const pageType = ref('A4');
const zoom = ref(1);
const zoomInput = ref('100%');
const nodeAttrForm = ref({});
const nodeData = ref({});
const pid = ref(`area_${uuidv4().replaceAll('-', '_')}`);
const logEvent = async (eventname, event) => {
switch (eventname) {
case 'paneClick':
nodeAttrForm.value = {};
nodeData.value = {};
break;
case 'click':
nodeAttrForm.value = event.node.data.options;
nodeData.value = JSON.parse(JSON.stringify(event.node));
break;
case 'nodeDrag':
if (event.nodes.length === 1) {
nodeAttrForm.value = event.node.data.options;
nodeData.value = JSON.parse(JSON.stringify(event.node));
} else {
nodeAttrForm.value = {};
nodeData.value = {};
}
break;
case 'nodeDragEnd':
if (event.nodes.length === 1) {
nodeAttrForm.value = event.node.data.options;
nodeData.value = JSON.parse(JSON.stringify(event.node));
} else {
nodeAttrForm.value = {};
nodeData.value = {};
}
break;
}
};
const nodes = ref([{
id: pid.value,
name: 'area',
type: 'area',
position: {
x: 0,
y: 0
},
data: {}
}]);
const edges = ref([]);
const setPage = (type, width, height) => {
pageType.value = type;
pageHeight.value = height || pageHeight.value;
pageWidth.value = width || pageWidth.value;
};
const zoomChange = (e) => {
console.log(flowRef.value.autoPanSpeed);
flowRef.value.viewport.x = 0;
flowRef.value.viewport.y = 0;
let num = zoom.value;
num += e;
zoom.value = parseFloat(Math.min(2, Math.max(0.2, num)).toFixed(2));
zoomInput.value = (zoom.value * 100).toFixed(0) + '%';
flowRef.value.setTransform({
x: 0,
y: 0,
zoom: zoom.value
});
};
const view = () => {
localStorage.setItem('printNodes', JSON.stringify(nodes.value));
console.log();
internalInstance.appContext.config.globalProperties.$openPrint([{}, {}, {}, {}, {}], 'demo1');
// isView.value = true;
};
const save = () => {
localStorage.setItem('printNodes', JSON.stringify(nodes.value));
};
const clear = () => {
};
onMounted(() => {
flowRef.value.onInit(() => {
flowRef.value.setTransform({
x: 0,
y: 0,
zoom: 1
});
});
});
watch(() => flowRef.value?.viewport, () => {
if (flowRef.value && (flowRef.value.viewport.x !== 0 || flowRef.value.viewport.y !== 0)) {
console.log('重置原点');
flowRef.value.setTransform({
x: 0,
y: 0,
zoom: zoom.value
});
}
}, { deep: true });
const toggleSnapToGrid = (e) => {
if (e.key === 'Alt') {
isSnapToGrid.value = false;
}
};
const keyup = () => {
isSnapToGrid.value = true;
};
window.addEventListener('keydown', toggleSnapToGrid);
window.addEventListener('keyup', keyup);
const nodeChange = () => {
updateNode(nodeData.value.id, JSON.parse(JSON.stringify(nodeData.value)), { replace: false });
};
</script>
<style lang="less" scoped>
:deep(.vue-flow__node-area) {
z-index: -1 !important;
pointer-events: none !important;
}
:deep(.el-tag) {
margin-bottom: 8px;
}
.top {
height: 64px;
margin: 8px 8px 0 8px;
border: 1px solid #ccc;
width: calc(100% - 16px);
display: inline-block;
}
.bottom {
height: calc(100vh - 64px - 16px - 8px);
}
.leftPanel, .rightPanel {
vertical-align: top;
display: inline-block;
width: 300px;
height: 100%;
margin: 8px 8px 0;
overflow: auto;
border: 1px solid #ccc;
}
.content {
vertical-align: top;
display: inline-block;
width: calc(100vw - 300px - 300px - 16px - 16px);
height: 100%;
margin-top: 8px;
border: 1px solid #ccc;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
}
.flowArea {
border-right: 1px solid #ccc;
}
</style>