新增打印模块功能,支持图片分析生成原生模板JSON,查询可用打印机,服务端直打功能,优化打印设计器界面,添加打印机选择和快速打印选项,同时更新依赖项以支持PDF处理。
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="element-wrapper" :class="{ active }" :style="wrapperStyle" @pointerdown.stop="startDrag" @click.stop="emit('select', element.id)">
|
||||
<slot />
|
||||
<template v-if="active && resizable">
|
||||
<span v-for="handle in handles" :key="handle" class="resize-handle" :class="`handle-${handle}`" @pointerdown.stop="startResize($event, handle)" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { calcDragRect, calcResizeRect } from '../core/dragResize';
|
||||
import type { NativeElement } from '../core/types';
|
||||
const PX_PER_MM = 3.7795275591;
|
||||
|
||||
const props = defineProps<{
|
||||
element: NativeElement;
|
||||
active: boolean;
|
||||
scale: number;
|
||||
gridSize: number;
|
||||
pageWidth: number;
|
||||
pageHeight: number;
|
||||
movable?: boolean;
|
||||
resizable?: boolean;
|
||||
dragBounds?: {
|
||||
minX: number;
|
||||
maxX: number;
|
||||
minY: number;
|
||||
maxY: number;
|
||||
};
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update', payload: { id: string; patch: Partial<NativeElement> }): void;
|
||||
(e: 'select', id: string): void;
|
||||
(e: 'dragging', payload: { id: string; rect: { x: number; y: number; w: number; h: number }; active: boolean }): void;
|
||||
}>();
|
||||
|
||||
const handles = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
position: 'absolute',
|
||||
left: `${props.element.x}mm`,
|
||||
top: `${props.element.y}mm`,
|
||||
width: `${props.element.w}mm`,
|
||||
height: `${props.element.h}mm`,
|
||||
zIndex: props.element.zIndex,
|
||||
outline: props.active ? '1px solid #1677ff' : '1px dashed transparent',
|
||||
userSelect: 'none',
|
||||
}));
|
||||
const movable = computed(() => props.movable !== false);
|
||||
const resizable = computed(() => props.resizable !== false);
|
||||
|
||||
function clampByBounds(rect: { x: number; y: number; w: number; h: number }) {
|
||||
const bounds = props.dragBounds;
|
||||
if (!bounds) return rect;
|
||||
return {
|
||||
...rect,
|
||||
x: Math.max(bounds.minX, Math.min(bounds.maxX, rect.x)),
|
||||
y: Math.max(bounds.minY, Math.min(bounds.maxY, rect.y)),
|
||||
};
|
||||
}
|
||||
|
||||
function startDrag(event: PointerEvent) {
|
||||
emit('select', props.element.id);
|
||||
if ((event.target as HTMLElement)?.classList.contains('resize-handle')) return;
|
||||
if (!movable.value) return;
|
||||
const start = { x: props.element.x, y: props.element.y, w: props.element.w, h: props.element.h };
|
||||
const startX = event.clientX;
|
||||
const startY = event.clientY;
|
||||
const onMove = (moveEvent: PointerEvent) => {
|
||||
// 鼠标位移是 px,画布坐标是 mm,这里必须做单位换算才会跟手
|
||||
const deltaX = (moveEvent.clientX - startX) / props.scale / PX_PER_MM;
|
||||
const deltaY = (moveEvent.clientY - startY) / props.scale / PX_PER_MM;
|
||||
const next = calcDragRect(start, { width: props.pageWidth, height: props.pageHeight }, deltaX, deltaY, props.gridSize);
|
||||
const bounded = clampByBounds(next);
|
||||
emit('update', { id: props.element.id, patch: bounded });
|
||||
emit('dragging', { id: props.element.id, rect: bounded, active: true });
|
||||
};
|
||||
const onUp = () => {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
emit('dragging', { id: props.element.id, rect: { x: props.element.x, y: props.element.y, w: props.element.w, h: props.element.h }, active: false });
|
||||
};
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
}
|
||||
|
||||
function startResize(event: PointerEvent, direction: any) {
|
||||
emit('select', props.element.id);
|
||||
if (!resizable.value) return;
|
||||
const start = { x: props.element.x, y: props.element.y, w: props.element.w, h: props.element.h };
|
||||
const startX = event.clientX;
|
||||
const startY = event.clientY;
|
||||
const onMove = (moveEvent: PointerEvent) => {
|
||||
// 鼠标位移是 px,画布尺寸是 mm,缩放时同样要按单位换算
|
||||
const deltaX = (moveEvent.clientX - startX) / props.scale / PX_PER_MM;
|
||||
const deltaY = (moveEvent.clientY - startY) / props.scale / PX_PER_MM;
|
||||
const next = calcResizeRect(direction, start, { width: props.pageWidth, height: props.pageHeight }, deltaX, deltaY, props.gridSize);
|
||||
emit('update', { id: props.element.id, patch: next });
|
||||
};
|
||||
const onUp = () => {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
};
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.element-wrapper {
|
||||
box-sizing: border-box;
|
||||
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #1677ff;
|
||||
border-radius: 50%;
|
||||
margin: -3px;
|
||||
}
|
||||
.handle-nw {
|
||||
left: 0;
|
||||
top: 0;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
.handle-n {
|
||||
left: 50%;
|
||||
top: 0;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.handle-ne {
|
||||
left: 100%;
|
||||
top: 0;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
.handle-e {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.handle-se {
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
.handle-s {
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.handle-sw {
|
||||
left: 0;
|
||||
top: 100%;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
.handle-w {
|
||||
left: 0;
|
||||
top: 50%;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user