Browse Source

feat(bibViewer): 添加元素缩放和位置控制功能

- 新增选中元素的缩放控制功能,支持放大、缩小和重置
- 实现赛事名称和号码元素的位置拖拽调整
- 添加元素选中状态的视觉反馈样式
- 修复元素拖拽时的位置计算逻辑,支持百分比定位
- 完善表单重置功能,包含位置和缩放参数的初始化
- 优化预览区域元素的样式和交互体验
zhou 2 weeks ago
parent
commit
9d0a4048de
1 changed files with 172 additions and 15 deletions
  1. 172 15
      src/views/system/gameEvent/components/bibViewerDialog.vue

+ 172 - 15
src/views/system/gameEvent/components/bibViewerDialog.vue

@@ -37,6 +37,40 @@
                 <el-color-picker v-model="bibForm.fontColor" @change="handleFontColorChange"></el-color-picker>
               </div>
             </el-form-item>
+            <!-- 在字体设置表单项后添加缩放控制 -->
+            <el-form-item label="选中元素">
+              <div style="display: flex; gap: 10px; align-items: center">
+                <el-select v-model="selectedElement" placeholder="选择元素" style="width: 120px">
+                  <el-option label="Logo" value="logo"></el-option>
+                  <el-option label="二维码" value="barcode"></el-option>
+                  <el-option label="号码" value="number"></el-option>
+                  <el-option label="赛事名称" value="event"></el-option>
+                </el-select>
+                <el-button 
+                  v-if="selectedElement" 
+                  @click="scaleElement(0.9)" 
+                  :disabled="!selectedElement"
+                  size="small"
+                >
+                  缩小
+                </el-button>
+                <el-button 
+                  v-if="selectedElement" 
+                  @click="scaleElement(1.1)" 
+                  :disabled="!selectedElement"
+                  size="small"
+                >
+                  放大
+                </el-button>
+                <el-button 
+                  v-if="selectedElement" 
+                  @click="resetElementScale" 
+                  size="small"
+                >
+                  重置
+                </el-button>
+              </div>
+            </el-form-item>
             <el-form-item label="赛事名称">
               <el-input v-model="bibForm.eventName" placeholder="请输入赛事名称" maxlength="50" show-word-limit />
             </el-form-item>
@@ -53,9 +87,13 @@
                 class="draggable-element logo-element"
                 :style="{
                   left: bibForm.logoX + 'px',
-                  top: bibForm.logoY + 'px'
+                  top: bibForm.logoY + 'px',
+                  transform: `translateX(-50%) scale(${bibForm.logoScale})`,
+                  transformOrigin: 'top left'
                 }"
                 @mousedown="startDrag($event, 'logo')"
+                @click="selectedElement = 'logo'"
+                :class="{ selected: selectedElement === 'logo' }"
               >
                 <img :src="logoImageUrl" alt="Logo" style="max-width: 80px; max-height: 80px" />
               </div>
@@ -65,9 +103,13 @@
                 class="draggable-element barcode-element"
                 :style="{
                   left: bibForm.qRCodeX + 'px',
-                  top: bibForm.qRCodeY + 'px'
+                  top: bibForm.qRCodeY + 'px',
+                  transform: `translateX(-50%) scale(${bibForm.barcodeScale})`,
+                  transformOrigin: 'top left',
                 }"
                 @mousedown="startDrag($event, 'barcode')"
+                @click="selectedElement = 'barcode'"
+                :class="{ selected: selectedElement === 'barcode' }"
               >
                 <svg
                   t="1755833734016"
@@ -101,27 +143,38 @@
               <!-- 赛事名称预览 -->
               <div
                 v-if="bibForm.eventName"
-                class="event-name-preview"
+                class="event-name-preview draggable-element"
                 :style="{
                   fontSize: Math.min(28, Math.max(18, bibForm.fontSize * 0.7)) + 'px',
                   color: 'black',
-                  fontFamily: '黑体'
+                  fontFamily: '黑体',
+                  left: bibForm.eventX + '%',
+                  top: bibForm.eventY + '%',
+                  transform: `translateX(-50%) scale(${bibForm.eventScale})`,
+                  transformOrigin: 'top center'
                 }"
+                @mousedown="startDrag($event, 'event')"
+                @click="selectedElement = 'event'"
+                :class="{ selected: selectedElement === 'event' }"
               >
                 {{ bibForm.eventName }}
-              </div>
+              </div>      
 
               <!-- 示例数字 1234 -->
               <div
                 class="draggable-element number-element"
                 :style="{
-                  left: '50%',
-                  top: '50%',
-                  transform: 'translate(-50%, -50%)',
+                  left: bibForm.numberX + '%',
+                  top: bibForm.numberY + '%',
+                  transform: `translate(-50%, -50%) scale(${bibForm.numberScale})`,
                   fontSize: Math.min(bibForm.fontSize, 56) + 'px',
                   color: bibForm.fontColorHex,
-                  fontFamily: bibForm.fontName
+                  fontFamily: bibForm.fontName,
+                  transformOrigin: 'center'
                 }"
+                @mousedown="startDrag($event, 'number')"
+                @click="selectedElement = 'number'"
+                :class="{ selected: selectedElement === 'number' }"
               >
                 1234
               </div>
@@ -165,6 +218,8 @@ const bibDialog = reactive({
   loading: false
 });
 
+const selectedElement = ref<string>(''); // 当前选中的元素
+
 const bibForm = reactive({
   logoX: 25,
   logoY: 25,
@@ -175,6 +230,16 @@ const bibForm = reactive({
   fontColor: '#000000',
   fontColorHex: '#000000',
   eventName: '',
+  // 添加位置字段
+  numberX: 50,  // 百分比位置
+  numberY: 50,  // 百分比位置
+  eventX: 50,   // 百分比位置
+  eventY: 5,    // 百分比位置
+  // 添加元素尺寸控制字段
+  logoScale: 1,
+  barcodeScale: 1,
+  numberScale: 1,
+  eventScale: 1,
 });
 
 const bgImageFile = ref<File | null>(null);
@@ -210,6 +275,46 @@ const handleCloseBibDialog = () => {
   }
 };
 
+// 添加元素缩放方法
+const scaleElement = (factor: number) => {
+  if (!selectedElement.value) return;
+  
+  switch (selectedElement.value) {
+    case 'logo':
+      bibForm.logoScale = Math.max(0.1, bibForm.logoScale * factor);
+      break;
+    case 'barcode':
+      bibForm.barcodeScale = Math.max(0.1, bibForm.barcodeScale * factor);
+      break;
+    case 'number':
+      bibForm.numberScale = Math.max(0.1, bibForm.numberScale * factor);
+      break;
+    case 'event':
+      bibForm.eventScale = Math.max(0.1, bibForm.eventScale * factor);
+      break;
+  }
+};
+
+// 重置元素缩放
+const resetElementScale = () => {
+  if (!selectedElement.value) return;
+  
+  switch (selectedElement.value) {
+    case 'logo':
+      bibForm.logoScale = 1;
+      break;
+    case 'barcode':
+      bibForm.barcodeScale = 1;
+      break;
+    case 'number':
+      bibForm.numberScale = 1;
+      break;
+    case 'event':
+      bibForm.eventScale = 1;
+      break;
+  }
+};
+
 // 重置表单
 const resetBibForm = () => {
   // 设置默认值(像素单位)- 适配2:3横屏比例
@@ -222,11 +327,26 @@ const resetBibForm = () => {
   bibForm.fontColor = '#000000';
   bibForm.fontColorHex = '#000000';
   bibForm.eventName = '';
+  
+  // 添加位置重置
+  bibForm.numberX = 50;
+  bibForm.numberY = 50;
+  bibForm.eventX = 50;
+  bibForm.eventY = 5;
+  
   bgImageFile.value = null;
   logoImageFile.value = null;
   bgImageUrl.value = '';
   logoImageUrl.value = '';
 
+  // 重置缩放
+  bibForm.logoScale = 1;
+  bibForm.barcodeScale = 1;
+  bibForm.numberScale = 1;
+  bibForm.eventScale = 1;
+  // 重置选中元素
+  selectedElement.value = '';
+
   // 清除上传的文件
   if (bgUploadRef.value) {
     bgUploadRef.value.clearFiles();
@@ -291,12 +411,22 @@ const startDrag = (event: MouseEvent, target: string) => {
   dragState.startX = event.clientX;
   dragState.startY = event.clientY;
 
+  selectedElement.value = target;
+
   if (target === 'logo') {
     dragState.startLeft = bibForm.logoX;
     dragState.startTop = bibForm.logoY;
   } else if (target === 'barcode') {
     dragState.startLeft = bibForm.qRCodeX;
     dragState.startTop = bibForm.qRCodeY;
+  } else if (target === 'number') {
+    // 修复:使用存储的位置数据
+    dragState.startLeft = bibForm.numberX;
+    dragState.startTop = bibForm.numberY;
+  } else if (target === 'event') {
+    // 修复:使用存储的位置数据
+    dragState.startLeft = bibForm.eventX;
+    dragState.startTop = bibForm.eventY;
   }
 
   document.addEventListener('mousemove', handleDrag);
@@ -310,12 +440,28 @@ const handleDrag = (event: MouseEvent) => {
   const deltaX = event.clientX - dragState.startX;
   const deltaY = event.clientY - dragState.startY;
 
+  const container = previewContainer.value;
+  const previewWidth = container?.clientWidth || container?.offsetWidth || 600;
+  const previewHeight = container?.clientHeight || container?.offsetHeight || 400;
+
   if (dragState.dragTarget === 'logo') {
     bibForm.logoX = Math.max(0, dragState.startLeft + deltaX);
     bibForm.logoY = Math.max(0, dragState.startTop + deltaY);
   } else if (dragState.dragTarget === 'barcode') {
     bibForm.qRCodeX = Math.max(0, dragState.startLeft + deltaX);
     bibForm.qRCodeY = Math.max(0, dragState.startTop + deltaY);
+  } else if (dragState.dragTarget === 'number') {
+    // 修复:实际更新位置数据
+    const newX = Math.max(0, Math.min(100, dragState.startLeft + (deltaX / previewWidth) * 100));
+    const newY = Math.max(0, Math.min(100, dragState.startTop + (deltaY / previewHeight) * 100));
+    bibForm.numberX = newX;
+    bibForm.numberY = newY;
+  } else if (dragState.dragTarget === 'event') {
+    // 修复:实际更新位置数据
+    const newX = Math.max(0, Math.min(100, dragState.startLeft + (deltaX / previewWidth) * 100));
+    const newY = Math.max(0, Math.min(100, dragState.startTop + (deltaY / previewHeight) * 100));
+    bibForm.eventX = newX;
+    bibForm.eventY = newY;
   }
 };
 
@@ -475,9 +621,14 @@ const handleGenerateBibFile = async () => {
       qRCodeX: qrCoords.x,
       qRCodeY: qrCoords.y,
       fontName: bibForm.fontName || 'simhei',
-      fontSize: Math.round((bibForm.fontSize || 36) * 0.75), // 字体大小转换为PDF点并四舍五入为整数
+      fontSize: Math.round((bibForm.fontSize || 36) * 0.75),
       fontColor: parseInt((bibForm.fontColor || '#000000').replace('#', ''), 16),
       eventName: bibForm.eventName || '',
+      // 缩放参数
+      logoScale: bibForm.logoScale,
+      barcodeScale: bibForm.barcodeScale,
+      numberScale: bibForm.numberScale,
+      eventScale: bibForm.eventScale,
     };
 
     // 显示进度提示
@@ -660,20 +811,20 @@ defineExpose({
   padding: 4px;
   white-space: nowrap;
   user-select: none;
-  pointer-events: none;
+  /* pointer-events: none; */
   text-align: center;
   min-width: 80px;
 }
 
 .event-name-preview {
   position: absolute;
-  top: 5%;
-  left: 50%;
-  transform: translateX(-50%);
+  // top: 5%;
+  // left: 50%;
+  // transform: translateX(-50%);
   font-weight: bold;
   text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
   user-select: none;
-  pointer-events: none;
+  // pointer-events: none;
   z-index: 5;
   text-align: center;
   white-space: nowrap;
@@ -681,4 +832,10 @@ defineExpose({
   overflow: hidden;
   text-overflow: ellipsis;
 }
+
+/* 选中元素样式 */
+.draggable-element.selected {
+  outline: 2px solid #409eff;
+  outline-offset: 2px;
+}
 </style>