Răsfoiți Sursa

feat: 增加图片拖拽右键菜单交互功能

yusheng 2 luni în urmă
părinte
comite
a17c1aed30
1 a modificat fișierele cu 610 adăugiri și 91 ștergeri
  1. 610 91
      src/views/doc/components/aa.vue

+ 610 - 91
src/views/doc/components/aa.vue

@@ -8,36 +8,31 @@
         </div>
         <div class="preview-panel">
           <div class="canvas-result">
-            <canvas
-              id="outputCanvas"
-              style="width: 1000px"
-              v-show="canvasGenerated"
-            ></canvas>
-            <div class="canvas-buttons" v-if="canvasGenerated">
+            <canvas id="outputCanvas" v-show="canvasGenerated"></canvas>
+            <!-- <div class="canvas-buttons" v-if="canvasGenerated">
               <button @click="downloadCanvasAsImage" class="download">
                 📥 下载图片
               </button>
               <button @click="exportToPDF" class="pdf"> 📄 导出PDF </button>
               <button @click="printCanvas" class="print"> 🖨️ 打印 </button>
-            </div>
+            </div> -->
           </div>
         </div>
-
-        <!-- 图片素材区域 -->
-        <div class="images-panel">
-          <div class="panel-title">图片素材</div>
-          <div class="images-grid">
-            <div
-              v-for="(img, index) in dragImages"
-              :key="index"
-              class="drag-item"
-              draggable="true"
-              @dragstart="handleDragStart($event, img, index)"
-            >
-              <img :src="img.url" :alt="img.name" />
-              <div class="drag-item-name">{{ img.name }}</div>
-            </div>
-          </div>
+      </div>
+    </div>
+    <!-- 图片素材区域 -->
+    <div class="images-panel">
+      <div class="panel-title">图片素材</div>
+      <div class="images-grid">
+        <div
+          v-for="(img, index) in dragImages"
+          :key="index"
+          class="drag-item"
+          draggable="true"
+          @dragstart="handleDragStart($event, img, index)"
+        >
+          <img :src="img.url" :alt="img.name" />
+          <div class="drag-item-name">{{ img.name }}</div>
         </div>
       </div>
     </div>
@@ -90,17 +85,95 @@
           { name: '图片3', url: require('@/assets/ic-500.svg') }
         ],
         droppedImages: [],
-        selectedImage: null
+        selectedImage: null,
+        baseCanvasData: null // 缓存基础内容的 Canvas 数据
       };
     },
     mounted() {
       // 组件挂载后自动渲染一次
       this.$nextTick(() => {
-        // this.renderToCanvas();
+        this.renderToCanvas();
       });
+
+      // 保存组件实例引用,用于事件处理器
+      window._aaComponentInstance = this;
+
+      // 创建全局安全移除函数
+      window._safeRemoveElement = function (element) {
+        try {
+          if (
+            element &&
+            element.parentNode &&
+            document.body.contains(element)
+          ) {
+            element.parentNode.removeChild(element);
+            return true;
+          }
+        } catch (err) {
+          console.warn('安全移除元素时发生警告:', err);
+        }
+        return false;
+      };
+    },
+    beforeDestroy() {
+      // 清理引用
+      if (window._aaComponentInstance === this) {
+        window._aaComponentInstance = null;
+      }
+      if (window._safeRemoveElement) {
+        window._safeRemoveElement = null;
+      }
     },
     methods: {
-      // 设置 Canvas 拖放区域
+      // 获取组件实例
+      getComponentInstance() {
+        return window._aaComponentInstance || this;
+      },
+
+      // 安全的移除DOM元素
+      safeRemoveElement(elementId) {
+        try {
+          const element = document.getElementById(elementId);
+          if (window._safeRemoveElement) {
+            return window._safeRemoveElement(element);
+          }
+          // 回退方案
+          if (
+            element &&
+            element.parentNode &&
+            document.body.contains(element)
+          ) {
+            element.parentNode.removeChild(element);
+            return true;
+          }
+          return false;
+        } catch (err) {
+          console.warn(`移除元素 ${elementId} 时发生警告:`, err);
+          return false;
+        }
+      },
+
+      // 安全移除所有菜单
+      safeRemoveAllMenus() {
+        try {
+          const existingMenus = document.querySelectorAll('[id^="menu_"]');
+
+          // 使用倒序循环,避免在移除时影响NodeList
+          for (let i = existingMenus.length - 1; i >= 0; i--) {
+            const menu = existingMenus[i];
+            if (menu && document.body.contains(menu) && menu.parentNode) {
+              try {
+                menu.parentNode.removeChild(menu);
+              } catch (err) {
+                console.warn('移除菜单时发生警告:', err);
+              }
+            }
+          }
+        } catch (err) {
+          console.warn('移除所有菜单时发生错误:', err);
+        }
+      },
+      // 设置 Canvas 拖放区域和点击事件
       setupCanvasDropZone() {
         const canvas = document.getElementById('outputCanvas');
         if (!canvas) {
@@ -108,7 +181,14 @@
           return;
         }
 
-        console.log('设置拖放区域');
+        console.log('设置拖放区域和点击事件,Canvas ID:', canvas.id);
+        console.log('Canvas 尺寸:', canvas.width, 'x', canvas.height);
+        console.log(
+          'Canvas 可见性:',
+          canvas.style.display,
+          canvas.style.visibility
+        );
+        console.log('Canvas 位置:', canvas.getBoundingClientRect());
 
         canvas.addEventListener('dragover', (e) => {
           e.preventDefault();
@@ -126,6 +206,12 @@
             this.addImageToCanvas(data.url, e.offsetX, e.offsetY);
           }
         });
+
+        // 添加右键点击事件监听
+        canvas.addEventListener('contextmenu', (e) => {
+          e.preventDefault();
+          this.handleCanvasRightClick(e);
+        });
       },
 
       // 处理拖拽开始
@@ -143,7 +229,8 @@
       addImageToCanvas(imgUrl, x, y) {
         const canvas = document.getElementById('outputCanvas');
         if (!canvas || !this.canvasGenerated) {
-          this.showMessage('请先渲染 Canvas', 'error');
+          console.log('请先渲染 Canvas');
+
           return;
         }
 
@@ -168,26 +255,413 @@
           // 绘制图片
           ctx.drawImage(img, posX, posY, width, height);
 
+          const imageId = `img_${Date.now()}_${Math.random()
+            .toString(36)
+            .substr(2, 9)}`;
+
           this.droppedImages.push({
+            id: imageId,
             url: imgUrl,
             x: posX,
             y: posY,
             width,
-            height
+            height,
+            originalWidth: img.width,
+            originalHeight: img.height
           });
 
           console.log('已添加的图片数量:', this.droppedImages.length);
-          this.showMessage('✅ 图片已添加', 'success');
+          this.$message({
+            showClose: true,
+            message: '操作成功',
+            type: 'success'
+          });
+
+          // 设置图片可再次拖动和调整(通过右键菜单)
+          // 不再创建覆盖层,仅通过右键菜单操作
         };
 
         img.onerror = (err) => {
           console.error('图片加载失败:', err);
-          this.showMessage('❌ 图片加载失败', 'error');
+          this.$message({
+            showClose: true,
+            message: '操作失败',
+            type: 'error'
+          });
+          // this.showMessage('⚠️ 图片加载失败', 'error');
         };
 
         img.src = imgUrl;
       },
 
+      // 重新绘制整个 Canvas
+      redrawCanvas() {
+        const canvas = document.getElementById('outputCanvas');
+        if (!canvas || !this.canvasGenerated) return;
+
+        const ctx = canvas.getContext('2d');
+
+        try {
+          // 清空 Canvas
+          ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+          // 如果有缓存的基础内容,直接绘制
+          if (this.baseCanvasData) {
+            ctx.drawImage(this.baseCanvasData, 0, 0);
+          } else {
+            // 如果没有缓存,重新渲染基础内容
+            console.warn('没有缓存的基础内容,可能需要重新渲染');
+            return;
+          }
+
+          // 重新绘制所有拖入的图片
+          this.droppedImages.forEach((image) => {
+            const img = new Image();
+            img.onload = () => {
+              ctx.drawImage(img, image.x, image.y, image.width, image.height);
+            };
+            img.src = image.url;
+          });
+
+          console.log(
+            'Canvas 已快速重绘,包含基础内容和',
+            this.droppedImages.length,
+            '张图片'
+          );
+
+          // 确保事件监听仍然有效
+          setTimeout(() => {
+            this.setupCanvasDropZone();
+          }, 100);
+        } catch (error) {
+          console.error('重新绘制 Canvas 失败:', error);
+        }
+      },
+
+      // 显示图片控制菜单
+      showImageControls(imageId, x, y) {
+        console.log('显示图片控制菜单,位置:', x, y, '图片ID:', imageId);
+
+        // 保存组件实例引用
+        const self = this;
+
+        // 先移除可能存在的其他菜单
+        this.safeRemoveAllMenus();
+
+        // 定义菜单选项(使用局部变量 self)
+        const options = [
+          { text: '放大', action: () => self.resizeImage(imageId, 1.2) },
+          { text: '缩小', action: () => self.resizeImage(imageId, 0.8) },
+          { text: '移动', action: () => self.startMoveImage(imageId, x, y) },
+          { text: '删除', action: () => self.removeImage(imageId) }
+        ];
+
+        const menu = document.createElement('div');
+        menu.id = `menu_${imageId}`;
+        menu.style.position = 'fixed';
+
+        // 调整位置,避免菜单超出屏幕边界
+        const menuWidth = 120;
+        const menuHeight = options.length * 40 + 16; // 估算高度
+
+        let left = x;
+        let top = y;
+
+        // 如果菜单会超出右侧边界,向左移动
+        if (left + menuWidth > window.innerWidth) {
+          left = window.innerWidth - menuWidth - 10;
+        }
+
+        // 如果菜单会超出底部边界,向上移动
+        if (top + menuHeight > window.innerHeight) {
+          top = window.innerHeight - menuHeight - 10;
+        }
+
+        menu.style.left = `${left}px`;
+        menu.style.top = `${top}px`;
+        menu.style.background = 'white';
+        menu.style.border = '1px solid #e2e8f0';
+        menu.style.borderRadius = '8px';
+        menu.style.padding = '8px 0';
+        menu.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)';
+        menu.style.zIndex = '9999'; // 使用更高的 z-index
+        menu.style.minWidth = `${menuWidth}px`;
+        menu.style.opacity = '1';
+        menu.style.visibility = 'visible';
+        menu.style.display = 'block';
+
+        console.log('菜单位置 - 原始:', x, y, '调整后:', left, top);
+
+        options.forEach((option) => {
+          const item = document.createElement('div');
+          item.innerText = option.text;
+          item.style.padding = '8px 16px';
+          item.style.cursor = 'pointer';
+          item.style.fontSize = '14px';
+          item.style.color = '#374151';
+
+          item.addEventListener('mouseenter', () => {
+            item.style.background = '#f3f4f6';
+          });
+
+          item.addEventListener('mouseleave', () => {
+            item.style.background = 'white';
+          });
+
+          item.addEventListener('click', () => {
+            option.action();
+            // 直接移除当前菜单
+            const menuToRemove = document.getElementById(`menu_${imageId}`);
+            if (window._safeRemoveElement) {
+              window._safeRemoveElement(menuToRemove);
+            }
+          });
+
+          menu.appendChild(item);
+        });
+
+        document.body.appendChild(menu);
+        console.log('菜单已添加到 DOM,父元素:', menu.parentElement?.tagName);
+
+        // 点击其他地方关闭菜单
+        const closeMenu = (e) => {
+          console.log('closeMenu 被调用,目标:', e.target);
+          const currentMenu = document.getElementById(`menu_${imageId}`);
+          if (!currentMenu || !document.body.contains(currentMenu)) {
+            // 菜单已经不存在,移除监听器
+            document.removeEventListener('click', closeMenu);
+            return;
+          }
+
+          if (e.target !== currentMenu && !currentMenu.contains(e.target)) {
+            // 使用全局安全函数移除菜单
+            if (window._safeRemoveElement) {
+              window._safeRemoveElement(currentMenu);
+            }
+            document.removeEventListener('click', closeMenu);
+          }
+        };
+
+        setTimeout(() => {
+          console.log('添加全局点击监听器');
+          document.addEventListener('click', closeMenu);
+        }, 0);
+      },
+
+      // 调整图片大小
+      resizeImage(imageId, factor) {
+        const imageIndex = this.droppedImages.findIndex(
+          (img) => img.id === imageId
+        );
+        if (imageIndex === -1) return;
+
+        const image = this.droppedImages[imageIndex];
+        image.width *= factor;
+        image.height *= factor;
+
+        this.redrawCanvas();
+      },
+
+      // 处理 Canvas 右键点击
+      handleCanvasRightClick(e) {
+        console.log('Canvas 右键点击事件触发');
+
+        const canvas = document.getElementById('outputCanvas');
+        if (!canvas) {
+          console.log('Canvas 元素不存在');
+          return;
+        }
+
+        console.log(
+          'Canvas 元素存在,droppedImages 数量:',
+          this.droppedImages.length
+        );
+
+        if (this.droppedImages.length === 0) {
+          console.log('没有已添加的图片');
+          return;
+        }
+
+        const rect = canvas.getBoundingClientRect();
+        console.log('Canvas 边界矩形:', rect);
+
+        const scaleX = canvas.width / rect.width;
+        const scaleY = canvas.height / rect.height;
+
+        // 计算点击位置相对于 Canvas 的坐标
+        const clickX = (e.clientX - rect.left) * scaleX;
+        const clickY = (e.clientY - rect.top) * scaleY;
+
+        console.log('右键点击位置 - 屏幕:', e.clientX, e.clientY);
+        console.log('右键点击位置 - Canvas相对:', clickX, clickY);
+
+        // 查找点击到的图片
+        let clickedImage = null;
+        for (const image of this.droppedImages) {
+          console.log(
+            '检查图片:',
+            image.id,
+            '位置:',
+            image.x,
+            image.y,
+            '尺寸:',
+            image.width,
+            image.height
+          );
+          if (
+            clickX >= image.x &&
+            clickX <= image.x + image.width &&
+            clickY >= image.y &&
+            clickY <= image.y + image.height
+          ) {
+            clickedImage = image;
+            break;
+          }
+        }
+
+        if (clickedImage) {
+          console.log('点击到图片:', clickedImage.id);
+          this.showImageControls(clickedImage.id, e.clientX, e.clientY);
+        } else {
+          console.log('未点击到任何图片');
+          // 可以在这里添加其他右键功能,比如添加新图片等
+        }
+      },
+
+      // 开始移动图片
+      startMoveImage(imageId, menuX, menuY) {
+        console.log('开始移动图片,图片ID:', imageId);
+
+        // 关闭菜单
+        this.safeRemoveElement(`menu_${imageId}`);
+
+        const canvas = document.getElementById('outputCanvas');
+        if (!canvas) {
+          console.error('Canvas 元素未找到');
+          return;
+        }
+
+        const imageIndex = this.droppedImages.findIndex(
+          (img) => img.id === imageId
+        );
+        if (imageIndex === -1) {
+          console.error('图片未找到,ID:', imageId);
+          return;
+        }
+
+        const image = this.droppedImages[imageIndex];
+        console.log('找到图片:', image);
+
+        // 创建一个临时指示器
+        const indicator = document.createElement('div');
+        indicator.id = `move_indicator_${imageId}`;
+        indicator.style.position = 'fixed';
+        indicator.style.left = `${menuX}px`;
+        indicator.style.top = `${menuY}px`;
+        indicator.style.width = '40px';
+        indicator.style.height = '40px';
+        indicator.style.background = 'rgba(59, 130, 246, 0.3)';
+        indicator.style.border = '2px dashed #3b82f6';
+        indicator.style.borderRadius = '50%';
+        indicator.style.zIndex = '3000';
+        indicator.style.pointerEvents = 'none';
+        document.body.appendChild(indicator);
+        console.log('指示器已创建并添加到DOM');
+
+        // 显示提示
+        this.$message({
+          showClose: true,
+          message: '📍 移动鼠标调整位置,释放鼠标完成移动',
+          type: 'info'
+        });
+        // this.showMessage('📍 移动鼠标调整位置,释放鼠标完成移动', 'info');
+
+        const moveHandler = (e) => {
+          console.log('moveHandler 被调用,鼠标位置:', e.clientX, e.clientY);
+          const rect = canvas.getBoundingClientRect();
+          const scaleX = canvas.width / rect.width;
+          const scaleY = canvas.height / rect.height;
+
+          // 计算相对于 canvas 的位置
+          const newX = (e.clientX - rect.left) * scaleX - image.width / 2;
+          const newY = (e.clientY - rect.top) * scaleY - image.height / 2;
+
+          // 限制在 canvas 范围内
+          image.x = Math.max(0, Math.min(newX, canvas.width - image.width));
+          image.y = Math.max(0, Math.min(newY, canvas.height - image.height));
+
+          // 更新指示器位置
+          indicator.style.left = `${e.clientX - 20}px`;
+          indicator.style.top = `${e.clientY - 20}px`;
+
+          // 使用组件实例引用
+          const component = window._aaComponentInstance || this;
+          if (component && component.redrawCanvas) {
+            component.redrawCanvas();
+          }
+        };
+
+        const mouseUpHandler = (e) => {
+          console.log(
+            'mouseUpHandler 被调用,位置:',
+            e.clientX,
+            e.clientY,
+            '目标:',
+            e.target
+          );
+
+          // 无论在哪里释放鼠标,都完成移动
+          console.log('鼠标释放,完成移动');
+
+          // 确保只处理一次
+          if (indicator.parentNode) {
+            console.log('指示器仍在DOM中,移除事件监听器');
+            document.removeEventListener('mousemove', moveHandler);
+            document.removeEventListener('mouseup', mouseUpHandler);
+            indicator.remove();
+            // 使用组件实例引用
+            const component = window._aaComponentInstance || this;
+            if (component) {
+              this.$message({
+                showClose: true,
+                message: '图片已移动',
+                type: 'success'
+              });
+              // component.showMessage('✅ 图片已移动', 'success');
+            }
+          } else {
+            console.log('指示器已不在DOM中');
+          }
+        };
+
+        // 确保事件只绑定一次
+        console.log('绑定事件监听器');
+        document.removeEventListener('mousemove', moveHandler);
+        document.removeEventListener('mouseup', mouseUpHandler);
+        document.addEventListener('mousemove', moveHandler);
+        document.addEventListener('mouseup', mouseUpHandler);
+      },
+
+      // 删除图片
+      removeImage(imageId) {
+        const imageIndex = this.droppedImages.findIndex(
+          (img) => img.id === imageId
+        );
+        if (imageIndex === -1) return;
+
+        this.droppedImages.splice(imageIndex, 1);
+
+        // 删除控制菜单
+        this.safeRemoveElement(`menu_${imageId}`);
+
+        this.redrawCanvas();
+        this.$message({
+          showClose: true,
+          message: '🗑️ 图片已删除',
+          type: 'success'
+        });
+      },
+
       // 渲染Canvas核心函数
       async renderToCanvas() {
         const targetElement = document.getElementById('captureTarget');
@@ -222,7 +696,6 @@
             logging: false,
             allowTaint: false,
             imageTimeout: 5000,
-            width: 1000, // 设置输出宽度为 1000px
             windowWidth: targetElement.scrollWidth,
             windowHeight: targetElement.scrollHeight
           });
@@ -242,19 +715,22 @@
           ctx.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
           ctx.drawImage(canvas, 0, 0);
 
+          // 保存基础内容的 Canvas 数据(用于快速重绘)
+          this.baseCanvasData = canvas;
+
           this.canvasGenerated = true;
 
-          // 设置 Canvas 拖放区域
+          // 设置 Canvas 拖放区域和事件监听
           this.setupCanvasDropZone();
 
           // 显示成功提示
-          this.showMessage('✅ 渲染成功!可下载图片', 'success');
+          // this.showMessage('✅ 渲染成功!可下载图片', 'success');
         } catch (error) {
           console.error('html2canvas 渲染失败: ', error);
           const temp = document.getElementById('tempLoadingMsg');
           if (temp) temp.remove();
           this.canvasGenerated = false;
-          this.showMessage('❌ 渲染失败,请检查HTML结构或外部资源', 'error');
+          // this.showMessage('⚠️ 渲染失败,请检查HTML结构或外部资源', 'error');
         }
       },
 
@@ -266,7 +742,7 @@
           !outputCanvas ||
           outputCanvas.width === 0
         ) {
-          this.showMessage('请先点击"渲染至Canvas"生成图像后再下载', 'error');
+          // this.showMessage('请先点击"渲染至Canvas"生成图像后再下载', 'error');
           return;
         }
 
@@ -279,13 +755,13 @@
           link.download = `canvas-${timestamp}.png`;
           link.href = outputCanvas.toDataURL('image/png');
           link.click();
-          this.showMessage('✅ 下载已开始', 'success');
+          // this.showMessage('✅ 下载已开始', 'success');
         } catch (err) {
           console.error('下载失败', err);
-          this.showMessage(
-            '无法下载图像,可能是跨域限制或Canvas被污染',
-            'error'
-          );
+          // this.showMessage(
+          //   '无法下载图像,可能是跨域限制或Canvas被污染',
+          //   'error'
+          // );
         }
       },
 
@@ -297,10 +773,10 @@
           !outputCanvas ||
           outputCanvas.width === 0
         ) {
-          this.showMessage(
-            '请先点击"渲染至Canvas"生成图像后再导出PDF',
-            'error'
-          );
+          // this.showMessage(
+          //   '请先点击"渲染至Canvas"生成图像后再导出PDF',
+          //   'error'
+          // );
           return;
         }
 
@@ -332,10 +808,53 @@
             .replace(/:/g, '-');
           pdf.save(`canvas-${timestamp}.pdf`);
 
-          this.showMessage('✅ PDF 导出成功', 'success');
+          // this.showMessage('✅ PDF 导出成功', 'success');
         } catch (err) {
           console.error('PDF 导出失败', err);
-          this.showMessage('PDF 导出失败,请稍后重试', 'error');
+          // this.showMessage('PDF 导出失败,请稍后重试', 'error');
+        }
+      },
+
+      // 重新渲染基础内容(当 HTML 内容变化时调用)
+      async refreshBaseContent() {
+        try {
+          const targetElement = document.getElementById('captureTarget');
+          if (!targetElement) return;
+
+          const newCanvas = await html2canvas(targetElement, {
+            scale: 1,
+            backgroundColor: '#ffffff',
+            useCORS: true,
+            logging: false,
+            allowTaint: false,
+            imageTimeout: 5000,
+            width: 1000,
+            windowWidth: targetElement.scrollWidth,
+            windowHeight: targetElement.scrollHeight
+          });
+
+          this.baseCanvasData = newCanvas;
+
+          // 更新主 Canvas
+          const outputCanvas = document.getElementById('outputCanvas');
+          if (outputCanvas && this.canvasGenerated) {
+            const ctx = outputCanvas.getContext('2d');
+            ctx.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
+            ctx.drawImage(newCanvas, 0, 0);
+
+            // 重新绘制所有拖入的图片
+            this.droppedImages.forEach((image) => {
+              const img = new Image();
+              img.onload = () => {
+                ctx.drawImage(img, image.x, image.y, image.width, image.height);
+              };
+              img.src = image.url;
+            });
+          }
+
+          console.log('基础内容已更新');
+        } catch (error) {
+          console.error('更新基础内容失败:', error);
         }
       },
 
@@ -347,10 +866,10 @@
           !outputCanvas ||
           outputCanvas.width === 0
         ) {
-          this.showMessage(
-            '请先点击"渲染至Canvas"生成图像后再获取PDF',
-            'error'
-          );
+          // this.showMessage(
+          //   '请先点击"渲染至Canvas"生成图像后再获取PDF',
+          //   'error'
+          // );
           return Promise.reject(new Error('Canvas 未生成'));
         }
 
@@ -392,7 +911,7 @@
           !outputCanvas ||
           outputCanvas.width === 0
         ) {
-          this.showMessage('请先点击"渲染至Canvas"生成图像后再打印', 'error');
+          // this.showMessage('请先点击"渲染至Canvas"生成图像后再打印', 'error');
           return;
         }
 
@@ -436,10 +955,10 @@
           `);
           printWindow.document.close();
 
-          this.showMessage('✅ 已打开打印窗口,可选择另存为PDF', 'success');
+          // this.showMessage('✅ 已打开打印窗口,可选择另存为PDF', 'success');
         } catch (err) {
           console.error('打印失败', err);
-          this.showMessage('打印失败,请检查浏览器设置', 'error');
+          // this.showMessage('打印失败,请检查浏览器设置', 'error');
         }
       },
 
@@ -449,29 +968,6 @@
         this.$nextTick(() => {
           this.renderToCanvas();
         });
-      },
-
-      // 显示提示消息
-      showMessage(msg, type) {
-        const canvasResultDiv = document.querySelector('.canvas-result');
-        const existingMsg = canvasResultDiv.querySelector('.temp-message');
-        if (existingMsg) existingMsg.remove();
-
-        const messageDiv = document.createElement('div');
-        messageDiv.className = `temp-message message-${type}`;
-        messageDiv.innerText = msg;
-        messageDiv.style.fontSize = '12px';
-        messageDiv.style.marginTop = '8px';
-        messageDiv.style.padding = '4px 8px';
-        messageDiv.style.borderRadius = '20px';
-        messageDiv.style.backgroundColor =
-          type === 'success' ? '#d1fae5' : '#fee2e2';
-        messageDiv.style.color = type === 'success' ? '#065f46' : '#991b1b';
-        canvasResultDiv.appendChild(messageDiv);
-
-        setTimeout(() => {
-          if (messageDiv) messageDiv.remove();
-        }, 2500);
       }
     }
   };
@@ -510,7 +1006,7 @@
   h1 {
     font-size: 2rem;
     font-weight: 700;
-    background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb4d);
+    background: linear-gradient(135deg, #1a2a6c, #3b82f6, #8b5cf6);
     background-clip: text;
     -webkit-background-clip: text;
     color: transparent;
@@ -529,10 +1025,7 @@
 
   .converter-layout {
     display: flex;
-    flex-wrap: nowrap;
-    gap: 2rem;
-    margin-top: 0.5rem;
-    overflow-x: auto;
+    justify-content: center;
   }
 
   .editor-panel {
@@ -627,13 +1120,13 @@
   }
 
   button.pdf {
-    background: #ef4444;
-    border-color: #ef4444;
+    background: #8b5cf6;
+    border-color: #8b5cf6;
     color: white;
   }
 
   button.pdf:hover {
-    background: #dc2626;
+    background: #7c3aed;
     transform: translateY(-1px);
   }
 
@@ -671,7 +1164,7 @@
 
   .preview-panel {
     flex: 1;
-    width: 100%;
+    min-width: 0;
     background: #ffffff;
     border-radius: 1.5rem;
     padding: 1.25rem;
@@ -690,17 +1183,19 @@
     border: 1px solid #eef2ff;
     display: flex;
     flex-direction: column;
-    position: sticky;
-    top: 20px;
+    position: fixed;
+    right: 2rem;
+    top: 40px;
     max-height: calc(100vh - 80px);
     overflow-y: auto;
+    z-index: 100;
   }
 
   .images-grid {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     gap: 12px;
-    flex: 1;
+    /* flex: 1; */
     overflow-y: auto;
   }
 
@@ -714,6 +1209,8 @@
     display: flex;
     flex-direction: column;
     align-items: center;
+    height: 130px; /* 固定高度:图片80px + 内边距8px*2 + 文字区域 */
+    justify-content: space-between;
   }
 
   .drag-item:hover {
@@ -726,9 +1223,10 @@
   .drag-item img {
     width: 100%;
     height: 80px;
-    object-fit: cover;
+    object-fit: contain; /* 改为contain,确保完整显示图片 */
     border-radius: 8px;
     pointer-events: none;
+    background-color: #f1f5f9; /* 添加背景色,防止透明图片看不清 */
   }
 
   .drag-item-name {
@@ -740,6 +1238,9 @@
     text-overflow: ellipsis;
     white-space: nowrap;
     width: 100%;
+    line-height: 1.4;
+    padding: 0 4px;
+    max-height: 32px; /* 限制文字最大高度 */
   }
 
   .preview-header {
@@ -814,8 +1315,26 @@
     .html-editor {
       height: 260px;
     }
+    .converter-layout {
+      justify-content: center;
+      /* flex-wrap: wrap;
+      padding-right: 0;
+      gap: 1rem; */
+    }
     .images-panel {
-      flex: 0 0 auto;
+      position: relative;
+      right: auto;
+      top: auto;
+      width: 100%;
+      max-height: 300px; /* 在小屏幕下限制最大高度 */
+      order: 2; /* 在移动端将图片区域放在底部 */
+      margin-top: 1rem;
+    }
+    .drag-item {
+      height: 120px; /* 在小屏幕下稍微减小高度 */
+    }
+    .drag-item img {
+      height: 70px; /* 在小屏幕下减小图片高度 */
     }
   }
 </style>