Переглянути джерело

feat(gameEvent): 重构参赛证生成界面与逻辑

- 重新设计参赛证生成对话框布局,预览面板占满宽度
- 实现元素拖拽功能,支持Logo、二维码、赛事名称和号码位置调整
- 添加画布缩放功能,支持1/4到原始大小的比例调节
- 优化图片上传逻辑,直接使用原始图片不进行裁剪
- 增加坐标系统可视化,提供元素位置精确调整
- 改进表单配置面板,整合字体设置和元素控制
- 完善参数传递逻辑,确保前后端数据一致性
- 优化样式布局,提升用户操作体验
zhou 1 день тому
батько
коміт
662e86cc54

+ 2 - 2
src/views/system/gameEvent/TaskList.vue

@@ -142,10 +142,10 @@ const handleStopTask = async (taskId: number) => {
 };
 
 // 下载任务结果
-const handleDownload = async (taskId: number) => {
+const handleDownload = (taskId: number) => {
   downloadingTasks.value.add(taskId);
   try {
-    await downloadTask(taskId);
+    downloadTask(taskId);
     ElMessage.success('下载完成');
   } catch (error) {
     ElMessage.error('下载失败');

+ 362 - 254
src/views/system/gameEvent/components/bibViewerDialog.vue

@@ -1,43 +1,171 @@
 <template>
   <!-- 生成参赛证对话框 -->
-  <el-dialog v-model="bibDialog.visible" title="生成参赛证" width="800px" append-to-body @close="handleCloseBibDialog">
+  <el-dialog v-model="bibDialog.visible" title="生成参赛证" width="900px" append-to-body @close="handleCloseBibDialog">
     <div class="bib-generator">
-      <el-row :gutter="20">
-        <!-- 左侧配置面板 -->
-        <el-col :span="12">
-          <el-form :model="bibForm" label-width="100px">
-            <el-form-item label="背景图片" required>
-              <div class="upload-container">
-                <el-upload ref="bgUploadRef" :limit="1" :auto-upload="false" :on-change="handleBgImageChange" accept="image/*" drag>
-                  <el-icon class="el-icon--upload">
-                    <i-ep-upload-filled />
-                  </el-icon>
-                  <div class="el-upload__text">拖拽背景图片到此处,或<em>点击上传</em></div>
-                  <div class="el-upload__tip">图片将自动调整为3:2横屏比例,建议尺寸:600×400px</div>
+      <!-- 预览面板 - 占满宽度 -->
+      <div class="preview-container" ref="previewContainer">
+        <div class="preview-canvas" :style="{ 
+          backgroundImage: bgImageUrl ? `url(${bgImageUrl})` : 'none',
+          backgroundSize: '100% 100%',
+          backgroundRepeat: 'no-repeat',
+          transform: `scale(${canvasScale})`,
+          transformOrigin: 'center center'
+        }">
+          <!-- Logo元素 -->
+          <div
+            v-if="logoImageUrl"
+            class="draggable-element logo-element"
+            :style="{
+              left: bibForm.logoX + '%',
+              top: bibForm.logoY + '%',
+              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>
+          <!-- 示例条形码 -->
+          <div
+            class="draggable-element barcode-element"
+            :style="{
+              left: (bibForm.qRCodeX || 11.67) + '%',
+              top: (bibForm.qRCodeY || 32.5) + '%',
+              transform: `translateX(-50%) scale(${bibForm.barcodeScale})`,
+              transformOrigin: 'top left',
+            }"
+            @mousedown="startDrag($event, 'barcode')"
+            @click="selectedElement = 'barcode'"
+            :class="{ selected: selectedElement === 'barcode' }"
+          >
+            <svg
+              t="1755833734016"
+              class="icon"
+              viewBox="0 0 1024 1024"
+              version="1.1"
+              xmlns="http://www.w3.org/2000/svg"
+              p-id="2399"
+              width="32"
+              height="32"
+            >
+              <path
+                d="M540.9 866h59v59h-59v-59zM422.8 423.1V98.4H98.1v324.8h59v59h59v-59h206.7z m-265.7-59V157.4h206.7v206.7H157.1z m0 0"
+                p-id="2400"
+              ></path>
+              <path
+                d="M216.2 216.4h88.6V305h-88.6v-88.6zM600 98.4v324.8h324.8V98.4H600z m265.7 265.7H659V157.4h206.7v206.7z m0 0"
+                p-id="2401"
+              ></path>
+              <path
+                d="M718.1 216.4h88.6V305h-88.6v-88.6zM216.2 718.3h88.6v88.6h-88.6v-88.6zM98.1 482.2h59v59h-59v-59z m118.1 0h59.1v59h-59.1v-59z m0 0"
+                p-id="2402"
+              ></path>
+              <path
+                d="M275.2 600.2H98.1V925h324.8V600.2h-88.6v-59h-59v59z m88.6 59.1V866H157.1V659.3h206.7z m118.1-531.4h59v88.6h-59v-88.6z m0 147.6h59v59h-59v-59zM659 482.2H540.9v-88.6h-59v88.6H334.3v59H600v59h59v-118z m0 118h59.1v59H659v-59z m-177.1 0h59v88.6h-59v-88.6z m0 147.7h59V866h-59V747.9zM600 688.8h59V866h-59V688.8z m177.1-88.6h147.6v59H777.1v-59z m88.6-118h59v59h-59v-59z m-147.6 0h118.1v59H718.1v-59z m0 206.6h59v59h-59v-59z m147.6 59.1h-29.5v59h59v-59h29.5v-59h-59v59z m-147.6 59h59V866h-59v-59.1z m59 59.1h147.6v59H777.1v-59z m0 0"
+                p-id="2403"
+              ></path>
+            </svg>
+          </div>
+
+          <!-- 赛事名称预览 -->
+          <div
+            v-if="bibForm.eventName"
+            class="event-name-preview draggable-element"
+            :style="{
+              fontSize: Math.min(28, Math.max(18, bibForm.fontSize * 0.7)) + 'px',
+              color: 'black',
+              fontFamily: '黑体',
+              left: (bibForm.eventX || 50) + '%',
+              top: (bibForm.eventY || 5) + '%',
+              transform: `translateX(-50%) scale(${bibForm.eventScale})`,
+              transformOrigin: 'top center'
+            }"
+            @mousedown="startDrag($event, 'event')"
+            @click="selectedElement = 'event'"
+            :class="{ selected: selectedElement === 'event' }"
+          >
+            {{ bibForm.eventName }}
+          </div>      
+
+          <!-- 示例数字 1234 -->
+          <div
+            class="draggable-element number-element"
+            :style="{
+              left: (bibForm.numberX || 50) + '%',
+              top: (bibForm.numberY || 50) + '%',
+              transform: `translate(-50%, -50%) scale(${bibForm.numberScale})`,
+              fontSize: Math.min(bibForm.fontSize, 56) + 'px',
+              color: bibForm.fontColorHex,
+              fontFamily: bibForm.fontName,
+              transformOrigin: 'center'
+            }"
+            @mousedown="startDrag($event, 'number')"
+            @click="selectedElement = 'number'"
+            :class="{ selected: selectedElement === 'number' }"
+          >
+            1234
+          </div>
+        </div>
+      </div>
+
+      <!-- 上传按钮区域 - 显示在预览面板下方 -->
+      <div class="upload-section">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="upload-item">
+              <div class="upload-row">
+                <el-upload 
+                  ref="bgUploadRef" 
+                  :limit="1" 
+                  :auto-upload="false" 
+                  :on-change="handleBgImageChange" 
+                  :show-file-list="false"
+                  accept="image/*"
+                >
+                  <el-button type="primary" :icon="bgImageFile ? 'Select' : 'Upload'">
+                    {{'上传背景图片' }}
+                  </el-button>
                 </el-upload>
-                <div v-if="bgImageFile" class="file-info">
-                  <span class="file-name">{{ bgImageFile.name }}</span>
-                  <el-button type="text" @click="removeBgImage" class="remove-btn">删除</el-button>
-                </div>
+                <span class="upload-tip">建议尺寸:600×400px</span>
               </div>
-            </el-form-item>
-
-            <el-form-item label="Logo图片" required>
-              <div class="upload-container">
-                <el-upload ref="logoUploadRef" :limit="1" :auto-upload="false" :on-change="handleLogoImageChange" accept="image/*" drag>
-                  <el-icon class="el-icon--upload">
-                    <i-ep-upload-filled />
-                  </el-icon>
-                  <div class="el-upload__text">拖拽Logo图片到此处,或<em>点击上传</em></div>
-                  <div class="el-upload__tip">建议尺寸:80×80px</div>
+              <div v-if="bgImageFile" class="file-info">
+                <span class="file-name">{{ bgImageFile.name }}</span>
+                <el-button type="text" @click="removeBgImage" class="remove-btn">删除</el-button>
+              </div>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="upload-item">
+              <div class="upload-row">
+                <el-upload 
+                  ref="logoUploadRef" 
+                  :limit="1" 
+                  :auto-upload="false" 
+                  :on-change="handleLogoImageChange" 
+                  :show-file-list="false"
+                  accept="image/*"
+                >
+                  <el-button type="primary" :icon="logoImageFile ? 'Select' : 'Upload'">
+                    {{'上传Logo图片' }}
+                  </el-button>
                 </el-upload>
-                <div v-if="logoImageFile" class="file-info">
-                  <span class="file-name">{{ logoImageFile.name }}</span>
-                  <el-button type="text" @click="removeLogoImage" class="remove-btn">删除</el-button>
-                </div>
+                <span class="upload-tip">建议尺寸:80×80px</span>
               </div>
-            </el-form-item>
+              <div v-if="logoImageFile" class="file-info">
+                <span class="file-name">{{ logoImageFile.name }}</span>
+                <el-button type="text" @click="removeLogoImage" class="remove-btn">删除</el-button>
+              </div>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
 
+      <!-- 配置面板 -->
+      <el-form :model="bibForm" label-width="100px" class="config-form">
+        <el-row :gutter="20">
+          <el-col :span="12">
             <el-form-item label="字体设置">
               <div style="display: flex; gap: 15px; align-items: center">
                 <el-select v-model="bibForm.fontName" placeholder="字体" style="width: 100px">
@@ -49,7 +177,30 @@
                 <el-color-picker v-model="bibForm.fontColor" @change="handleFontColorChange"></el-color-picker>
               </div>
             </el-form-item>
-            <!-- 在字体设置表单项后添加缩放控制 -->
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="赛事名称">
+              <el-input v-model="bibForm.eventName" placeholder="请输入赛事名称" maxlength="50" show-word-limit />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="画布比例">
+              <el-select v-model="canvasScale" placeholder="选择画布比例" style="width: 200px">
+                <el-option label="1/4 (四分之一)" :value="0.25"></el-option>
+                <el-option label="1/3 (三分之一)" :value="0.33"></el-option>
+                <el-option label="1/2 (二分之一)" :value="0.5"></el-option>
+                <el-option label="3/4 (四分之三)" :value="0.75"></el-option>
+                <el-option label="1/1 (原始大小)" :value="1"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
             <el-form-item label="选中元素">
               <div style="display: flex; gap: 10px; align-items: center">
                 <el-select v-model="selectedElement" placeholder="选择元素" style="width: 120px">
@@ -83,121 +234,9 @@
                 </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>
-          </el-form>
-        </el-col>
-
-        <!-- 右侧预览面板 -->
-        <el-col :span="12">
-          <div class="preview-container" ref="previewContainer">
-            <div class="preview-canvas" :style="{ 
-              backgroundImage: bgImageUrl ? `url(${bgImageUrl})` : 'none',
-              backgroundSize: 'contain',
-              backgroundRepeat: 'no-repeat'
-            }">
-              <!-- Logo元素 -->
-              <div
-                v-if="logoImageUrl"
-                class="draggable-element logo-element"
-                :style="{
-                  left: bibForm.logoX + '%',
-                  top: bibForm.logoY + '%',
-                  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>
-
-              <!-- 示例条形码 -->
-              <div
-                class="draggable-element barcode-element"
-                :style="{
-                  left: (bibForm.qRCodeX || 11.67) + '%',
-                  top: (bibForm.qRCodeY || 32.5) + '%',
-                  transform: `translateX(-50%) scale(${bibForm.barcodeScale})`,
-                  transformOrigin: 'top left',
-                }"
-                @mousedown="startDrag($event, 'barcode')"
-                @click="selectedElement = 'barcode'"
-                :class="{ selected: selectedElement === 'barcode' }"
-              >
-                <svg
-                  t="1755833734016"
-                  class="icon"
-                  viewBox="0 0 1024 1024"
-                  version="1.1"
-                  xmlns="http://www.w3.org/2000/svg"
-                  p-id="2399"
-                  width="32"
-                  height="32"
-                >
-                  <path
-                    d="M540.9 866h59v59h-59v-59zM422.8 423.1V98.4H98.1v324.8h59v59h59v-59h206.7z m-265.7-59V157.4h206.7v206.7H157.1z m0 0"
-                    p-id="2400"
-                  ></path>
-                  <path
-                    d="M216.2 216.4h88.6V305h-88.6v-88.6zM600 98.4v324.8h324.8V98.4H600z m265.7 265.7H659V157.4h206.7v206.7z m0 0"
-                    p-id="2401"
-                  ></path>
-                  <path
-                    d="M718.1 216.4h88.6V305h-88.6v-88.6zM216.2 718.3h88.6v88.6h-88.6v-88.6zM98.1 482.2h59v59h-59v-59z m118.1 0h59.1v59h-59.1v-59z m0 0"
-                    p-id="2402"
-                  ></path>
-                  <path
-                    d="M275.2 600.2H98.1V925h324.8V600.2h-88.6v-59h-59v59z m88.6 59.1V866H157.1V659.3h206.7z m118.1-531.4h59v88.6h-59v-88.6z m0 147.6h59v59h-59v-59zM659 482.2H540.9v-88.6h-59v88.6H334.3v59H600v59h59v-118z m0 118h59.1v59H659v-59z m-177.1 0h59v88.6h-59v-88.6z m0 147.7h59V866h-59V747.9zM600 688.8h59V866h-59V688.8z m177.1-88.6h147.6v59H777.1v-59z m88.6-118h59v59h-59v-59z m-147.6 0h118.1v59H718.1v-59z m0 206.6h59v59h-59v-59z m147.6 59.1h-29.5v59h59v-59h29.5v-59h-59v59z m-147.6 59h59V866h-59v-59.1z m59 59.1h147.6v59H777.1v-59z m0 0"
-                    p-id="2403"
-                  ></path>
-                </svg>
-              </div>
-
-              <!-- 赛事名称预览 -->
-              <div
-                v-if="bibForm.eventName"
-                class="event-name-preview draggable-element"
-                :style="{
-                  fontSize: Math.min(28, Math.max(18, bibForm.fontSize * 0.7)) + 'px',
-                  color: 'black',
-                  fontFamily: '黑体',
-                  left: (bibForm.eventX || 50) + '%',
-                  top: (bibForm.eventY || 5) + '%', // 移除Y轴翻转,与PDF生成保持一致
-                  transform: `translateX(-50%) scale(${bibForm.eventScale})`,
-                  transformOrigin: 'top center'
-                }"
-                @mousedown="startDrag($event, 'event')"
-                @click="selectedElement = 'event'"
-                :class="{ selected: selectedElement === 'event' }"
-              >
-                {{ bibForm.eventName }}
-              </div>      
-
-              <!-- 示例数字 1234 -->
-              <div
-                class="draggable-element number-element"
-                :style="{
-                  left: (bibForm.numberX || 50) + '%',
-                  top: (bibForm.numberY || 50) + '%', // 移除Y轴翻转,与PDF生成保持一致
-                  transform: `translate(-50%, -50%) scale(${bibForm.numberScale})`,
-                  fontSize: Math.min(bibForm.fontSize, 56) + 'px',
-                  color: bibForm.fontColorHex,
-                  fontFamily: bibForm.fontName,
-                  transformOrigin: 'center'
-                }"
-                @mousedown="startDrag($event, 'number')"
-                @click="selectedElement = 'number'"
-                :class="{ selected: selectedElement === 'number' }"
-              >
-                1234
-              </div>
-            </div>
-          </div>
-        </el-col>
-      </el-row>
+          </el-col>
+        </el-row>
+      </el-form>
     </div>
 
     <template #footer>
@@ -242,6 +281,7 @@ const bibDialog = reactive({
 });
 
 const selectedElement = ref<string>(''); // 当前选中的元素
+const canvasScale = ref<number>(1); // 画布缩放比例
 
 const bibForm = reactive({
   // 统一使用百分比坐标系统 (0-100)
@@ -272,6 +312,7 @@ const bgImageUrl = ref<string>('');
 const logoImageUrl = ref<string>('');
 const previewContainer = ref<HTMLElement>();
 const bgImageDimensions = ref<{ width: number; height: number } | null>(null);
+const logoImageDimensions = ref<{ width: number; height: number } | null>(null);
 const bgUploadRef = ref<UploadInstance>();
 const logoUploadRef = ref<UploadInstance>();
 
@@ -341,7 +382,7 @@ const resetElementScale = () => {
 
 // 重置表单
 const resetBibForm = () => {
-  // 设置默认值(百分比单位)- 适配3:2横屏比例
+  // 设置默认值(百分比单位)
   bibForm.logoX = 4.17;      // 25/600 * 100 = 4.17%
   bibForm.logoY = 6.25;      // 25/400 * 100 = 6.25%
   bibForm.qRCodeX = 11.67;   // 70/600 * 100 = 11.67%
@@ -362,6 +403,8 @@ const resetBibForm = () => {
   logoImageFile.value = null;
   bgImageUrl.value = '';
   logoImageUrl.value = '';
+  bgImageDimensions.value = null;
+  logoImageDimensions.value = null;
 
   // 重置缩放
   bibForm.logoScale = 1;
@@ -370,6 +413,8 @@ const resetBibForm = () => {
   bibForm.eventScale = 1;
   // 重置选中元素
   selectedElement.value = '';
+  // 重置画布比例
+  canvasScale.value = 1;
 
   // 清除上传的文件
   if (bgUploadRef.value) {
@@ -383,19 +428,18 @@ const resetBibForm = () => {
 // 背景图片改变处理
 const handleBgImageChange = async (file: any) => {
   if (file.raw) {
-    // 处理图片比例,转换为3:2横屏(600×400px)
-    const processedFile = await processImageToRatio(file.raw, 3, 2);
-    bgImageFile.value = processedFile;
+    // 直接使用原始图片,不进行裁剪
+    bgImageFile.value = file.raw;
 
     const reader = new FileReader();
     reader.onload = (e) => {
       bgImageUrl.value = e.target?.result as string;
     };
-    reader.readAsDataURL(processedFile);
+    reader.readAsDataURL(file.raw);
 
-    // 获取处理后的图片尺寸
+    // 获取原始图片尺寸
     try {
-      const dimensions = await getImageDimensions(processedFile);
+      const dimensions = await getImageDimensions(file.raw);
       bgImageDimensions.value = dimensions;
     } catch (error) {
       console.error('获取图片尺寸失败:', error);
@@ -407,7 +451,7 @@ const handleBgImageChange = async (file: any) => {
 };
 
 // Logo图片改变处理
-const handleLogoImageChange = (file: any) => {
+const handleLogoImageChange = async (file: any) => {
   if (file.raw) {
     logoImageFile.value = file.raw;
     const reader = new FileReader();
@@ -416,6 +460,14 @@ const handleLogoImageChange = (file: any) => {
     };
     reader.readAsDataURL(file.raw);
 
+    // 获取Logo图片尺寸
+    try {
+      const dimensions = await getImageDimensions(file.raw);
+      logoImageDimensions.value = dimensions;
+    } catch (error) {
+      console.error('获取Logo图片尺寸失败:', error);
+    }
+
     // 添加成功提示
     proxy?.$modal.msgSuccess('Logo图片上传成功');
   }
@@ -436,6 +488,7 @@ const removeBgImage = () => {
 const removeLogoImage = () => {
   logoImageFile.value = null;
   logoImageUrl.value = '';
+  logoImageDimensions.value = null;
   if (logoUploadRef.value) {
     logoUploadRef.value.clearFiles();
   }
@@ -496,34 +549,62 @@ const handleDrag = (event: MouseEvent) => {
   if (dragState.dragTarget === 'logo') {
     bibForm.logoX = newX;
     bibForm.logoY = newY;
+    console.log('Logo位置 - X:', newX.toFixed(2) + '%', 'Y:', newY.toFixed(2) + '%');
   } else if (dragState.dragTarget === 'barcode') {
     bibForm.qRCodeX = newX;
     bibForm.qRCodeY = newY;
+    console.log('二维码位置 - X:', newX.toFixed(2) + '%', 'Y:', newY.toFixed(2) + '%');
   } else if (dragState.dragTarget === 'number') {
     bibForm.numberX = newX;
     bibForm.numberY = newY;
+    console.log('号码位置 - X:', newX.toFixed(2) + '%', 'Y:', newY.toFixed(2) + '%');
   } else if (dragState.dragTarget === 'event') {
     bibForm.eventX = newX;
     bibForm.eventY = newY;
+    console.log('赛事名称位置 - X:', newX.toFixed(2) + '%', 'Y:', newY.toFixed(2) + '%');
   }
 };
 
 // 停止拖拽
 const stopDrag = () => {
+  if (dragState.dragTarget) {
+    let finalX = 0, finalY = 0, targetName = '';
+    
+    if (dragState.dragTarget === 'logo') {
+      finalX = bibForm.logoX;
+      finalY = bibForm.logoY;
+      targetName = 'Logo';
+    } else if (dragState.dragTarget === 'barcode') {
+      finalX = bibForm.qRCodeX;
+      finalY = bibForm.qRCodeY;
+      targetName = '二维码';
+    } else if (dragState.dragTarget === 'number') {
+      finalX = bibForm.numberX;
+      finalY = bibForm.numberY;
+      targetName = '号码';
+    } else if (dragState.dragTarget === 'event') {
+      finalX = bibForm.eventX;
+      finalY = bibForm.eventY;
+      targetName = '赛事名称';
+    }
+    
+    console.log(`✅ ${targetName}最终位置 - X: ${finalX.toFixed(2)}%, Y: ${finalY.toFixed(2)}%`);
+  }
+  
   dragState.isDragging = false;
   dragState.dragTarget = '';
   document.removeEventListener('mousemove', handleDrag);
   document.removeEventListener('mouseup', stopDrag);
 };
 
-// 坐标转换函数:根据3:2横屏比例的背景图片尺寸调整坐标
+// 坐标转换函数:根据背景图片实际尺寸调整坐标
 const convertCoordinatesWithScale = (x: number, y: number): { x: number; y: number } => {
   // 获取预览容器尺寸
   const container = previewContainer.value;
   const previewWidth = container?.clientWidth || container?.offsetWidth || PREVIEW_WIDTH;
   const previewHeight = container?.clientHeight || container?.offsetHeight || PREVIEW_HEIGHT;
 
-  // 使用实际背景图片尺寸,如果没有则使用默认3:2横屏比例尺寸(600×400px)
+  // 使用实际背景图片尺寸,如果没有则使用默认尺寸(600×400px)
   const actualWidth = bgImageDimensions.value?.width || 600;
   const actualHeight = bgImageDimensions.value?.height || 400;
 
@@ -541,7 +622,7 @@ const convertCoordinatesWithScale = (x: number, y: number): { x: number; y: numb
   };
 };
 
-// 新增:获取背景图片实际尺寸的函数
+// 获取背景图片实际尺寸的函数
 const getImageDimensions = (file: File): Promise<{ width: number; height: number }> => {
   return new Promise((resolve) => {
     const img = new Image();
@@ -552,68 +633,6 @@ const getImageDimensions = (file: File): Promise<{ width: number; height: number
   });
 };
 
-// 处理图片比例转换函数
-const processImageToRatio = (file: File, targetRatio: number, targetRatio2: number): Promise<File> => {
-  return new Promise((resolve) => {
-    const img = new Image();
-    img.onload = () => {
-      const canvas = document.createElement('canvas');
-      const ctx = canvas.getContext('2d');
-
-      if (!ctx) {
-        resolve(file);
-        return;
-      }
-
-      const originalWidth = img.width;
-      const originalHeight = img.height;
-      const targetRatioValue = targetRatio / targetRatio2; // 3/2 = 1.5
-
-      let newWidth, newHeight, sourceX, sourceY, sourceWidth, sourceHeight;
-
-      if (originalWidth / originalHeight > targetRatioValue) {
-        // 原图更宽,需要裁剪宽度
-        newHeight = originalHeight;
-        newWidth = originalHeight * targetRatioValue;
-        sourceX = (originalWidth - newWidth) / 2;
-        sourceY = 0;
-        sourceWidth = newWidth;
-        sourceHeight = originalHeight;
-      } else {
-        // 原图更高,需要裁剪高度
-        newWidth = originalWidth;
-        newHeight = originalWidth / targetRatioValue;
-        sourceX = 0;
-        sourceY = (originalHeight - newHeight) / 2;
-        sourceWidth = originalWidth;
-        sourceHeight = newHeight;
-      }
-
-      // 设置画布尺寸为3:2横屏比例
-      canvas.width = newWidth;
-      canvas.height = newHeight;
-
-      // 绘制裁剪后的图片
-      ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, newWidth, newHeight);
-
-      // 转换为Blob
-      canvas.toBlob(
-        (blob) => {
-          if (blob) {
-            const processedFile = new File([blob], file.name, { type: file.type });
-            resolve(processedFile);
-          } else {
-            resolve(file);
-          }
-        },
-        file.type || 'image/jpeg',
-        0.9
-      );
-    };
-    img.src = URL.createObjectURL(file);
-  });
-};
-
 // 创建参赛证生成任务
 const handleCreateTask = async () => {
   // 校验必须上传背景图
@@ -667,6 +686,21 @@ const handleCreateTask = async () => {
     
     console.log('二维码坐标转换 - 原始坐标:', { qRCodeX, qRCodeY }, '有效坐标:', { validQRCodeX, validQRCodeY }, '转换后坐标:', qrCoords);
 
+    // 计算画布比例处理后的宽高
+    const originalWidth = bgImageDimensions.value?.width || 600;
+    const originalHeight = bgImageDimensions.value?.height || 400;
+    const scaledWidth = Math.round(originalWidth * canvasScale.value);
+    const scaledHeight = Math.round(originalHeight * canvasScale.value);
+
+    // 计算Logo尺寸(前端统一处理,避免后端重复计算)
+    // 预览时限制为最大80px,应用用户缩放,计算最终尺寸传递给后端
+    const logoOriginalWidth = logoImageDimensions.value?.width || 80;
+    const logoOriginalHeight = logoImageDimensions.value?.height || 80;
+    const logoPreviewSize = 80; // 前端预览时的最大尺寸
+    const logoScale = Math.min(1.0, logoPreviewSize / Math.max(logoOriginalWidth, logoOriginalHeight));
+    const logoFinalWidth = Math.round(logoOriginalWidth * logoScale * bibForm.logoScale);
+    const logoFinalHeight = Math.round(logoOriginalHeight * logoScale * bibForm.logoScale);
+
     const bibParams = {
       // Logo坐标(百分比)
       logoX: logoCoords.x,
@@ -675,7 +709,7 @@ const handleCreateTask = async () => {
       qRCodeX: qrCoords.x,
       qRCodeY: qrCoords.y,
       fontName: bibForm.fontName || 'simhei',
-      fontSize: Math.round((bibForm.fontSize || 36) * 0.75),
+      fontSize: bibForm.fontSize || 36, // 直接传递原始字体大小,后端会应用numberScale
       fontColor: parseInt((bibForm.fontColor || '#000000').replace('#', ''), 16),
       eventName: bibForm.eventName || '',
       // 位置参数(百分比)
@@ -688,6 +722,12 @@ const handleCreateTask = async () => {
       barcodeScale: bibForm.barcodeScale,
       numberScale: bibForm.numberScale,
       eventScale: bibForm.eventScale,
+      // 画布比例处理后的宽高
+      width: scaledWidth,
+      height: scaledHeight,
+      // Logo尺寸
+      logoWidth: logoFinalWidth,
+      logoHeight: logoFinalHeight,
     };
     
     console.log('发送给后端的参数:', bibParams);
@@ -725,6 +765,8 @@ defineExpose({
   bgImageUrl,
   logoImageUrl,
   bgImageDimensions,
+  logoImageDimensions,
+  canvasScale,
   handleCloseBibDialog,
   resetBibForm,
   handleBgImageChange,
@@ -740,51 +782,60 @@ defineExpose({
 <style scoped lang="scss">
 /* 生成参赛证样式 */
 .bib-generator {
-  padding: 20px;
-}
-
-.bib-generator .el-upload__tip {
-  color: #909399;
-  font-size: 12px;
-  margin-top: 8px;
-  text-align: center;
-}
-
-/* 必填项样式 */
-.bib-generator .el-form-item.is-required .el-form-item__label::before {
-  content: '*';
-  color: #f56c6c;
-  margin-right: 4px;
-}
-
-/* 上传成功状态样式 */
-.bib-generator .el-upload--success {
-  border-color: #67c23a;
-}
-
-.bib-generator .el-upload--success .el-upload__text {
-  color: #67c23a;
+  padding: 10px;
 }
 
+/* 预览容器 - 占满宽度 */
 .preview-container {
   border: 2px dashed #ddd;
   border-radius: 8px;
-  min-height: 200px;
-  max-height: 300px;
+  min-height: 300px;
+  max-height: 400px;
   position: relative;
-  overflow: hidden;
+  overflow: auto;
   aspect-ratio: 3/2;
   display: flex;
   align-items: center;
   justify-content: center;
   width: 100%;
+  margin-bottom: 20px;
+  background-color: #f8f9fa;
+}
+
+/* 上传按钮区域 */
+.upload-section {
+  margin-bottom: 20px;
+  padding: 15px;
+  background-color: #f5f7fa;
+  border-radius: 8px;
+}
+
+.upload-item {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.upload-row {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  
+  .el-upload {
+    display: inline-block;
+  }
+}
+
+/* 配置表单 */
+.config-form {
+  margin-top: 10px;
 }
 
 .preview-canvas {
   width: 100%;
   height: 100%;
   position: relative;
-  background-size: auto;
+  background-size: 100% 100%;
   background-position: center;
   background-repeat: no-repeat;
   background-color: #f5f5f5;
@@ -823,15 +874,18 @@ defineExpose({
   background-color: rgba(103, 194, 58, 0.1);
 }
 
-/* 上传容器样式 */
-.upload-container {
-  width: 100%;
+.upload-tip {
+  color: #909399;
+  font-size: 12px;
+  line-height: 1.5;
+  white-space: nowrap;
 }
 
 .file-info {
   margin-top: 8px;
   padding: 8px;
-  background-color: #f5f7fa;
+  background-color: #fff;
+  border: 1px solid #dcdfe6;
   border-radius: 4px;
   display: flex;
   justify-content: space-between;
@@ -895,4 +949,58 @@ defineExpose({
   outline: 2px solid #409eff;
   outline-offset: 2px;
 }
+
+/* 坐标系统说明样式 */
+.coordinate-info {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+}
+
+.coord-marker {
+  position: absolute;
+  width: 10px;
+  height: 10px;
+  background-color: #f56c6c;
+  border-radius: 50%;
+  border: 2px solid #fff;
+  box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
+  z-index: 100;
+}
+
+.coord-label {
+  position: absolute;
+  left: 15px;
+  top: -5px;
+  background-color: rgba(245, 108, 108, 0.9);
+  color: #fff;
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  white-space: nowrap;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.coord-text {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  text-align: center;
+  color: #909399;
+  font-size: 14px;
+  line-height: 1.8;
+  
+  p:first-child {
+    font-size: 16px;
+    font-weight: bold;
+    color: #606266;
+    margin-bottom: 10px;
+  }
+  
+  p {
+    margin: 5px 0;
+  }
+}
 </style>