tangning 14 godzin temu
rodzic
commit
40226e586e

+ 127 - 99
src/view/case/photos/canvas-photo-editor.js

@@ -1318,138 +1318,166 @@ export class CanvasPhotoEditor {
     }
   }
 
-  async exportPagesAsImages(name) {
+async exportPagesAsImages(paperType = "a4", name, fileType = 'pdf') {
   const loading = ElLoading.service({
     lock: true,
     text: "正在生成图片...",
     background: "rgba(0, 0, 0, 0.7)",
   });
 
-  const DPR = 3; // 高清导出倍率,可改 2 / 3 / 4
+  const DPR = 3;
   const zip = new JSZip();
 
+  // 完全和你 exportPagesToPDF 保持一致的规则
+  const rules = {
+    a4: { perSheet: 1 },
+    a3: { perSheet: 2 },
+    four: { perSheet: 4 },
+  };
+  const perSheet = rules[paperType]?.perSheet || 1;
+
   try {
-    // 遍历每一页
-    for (let i = 0; i < this.pages.length; i++) {
-      const pageIndex = i;
-      const page = this.pages[pageIndex];
+    // 分组:几页拼一张图(a4=1,a3=2,four=4)
+    const groups = [];
+    for (let i = 0; i < this.pages.length; i += perSheet) {
+      groups.push(this.pages.slice(i, i + perSheet));
+    }
+
+    // 遍历每组 → 生成一张图片
+    for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
+      const group = groups[groupIndex];
 
-      // 创建一页大小的画布
+      // 创建画布(和PDF导出尺寸逻辑一致)
       const pageCanvas = document.createElement("canvas");
-      pageCanvas.width = this.pageWidth * DPR;
-      pageCanvas.height = this.pageHeight * DPR;
+      pageCanvas.width = this.pageWidth * DPR * (paperType === "a3" ? 2 : 1);
+      pageCanvas.height = this.pageHeight * DPR * (paperType === "four" ? 2 : 1);
       const ctx = pageCanvas.getContext("2d");
       ctx.scale(DPR, DPR);
 
       // 白色背景
       ctx.fillStyle = "#fff";
-      ctx.fillRect(0, 0, this.pageWidth, this.pageHeight);
+      ctx.fillRect(0, 0, pageCanvas.width, pageCanvas.height);
 
-      // 绘制页面内容(图片 + 文字 + 虚线框)
-      const layout = this.getItemSize(page.layoutMode);
-      const coords = this.getCoordinate(0, layout);
-
-      coords.forEach((coord, idx) => {
-        const photoId = page.list[idx];
-        const photo = this.photos.find(p => p.id === photoId);
-
-        // 相框底板
-        ctx.fillStyle = "#D9D9D9";
-        ctx.fillRect(coord.x, coord.y, coord.width, coord.height);
-        ctx.strokeStyle = "#eee";
-        ctx.strokeRect(coord.x, coord.y, coord.width, coord.height);
-
-        if (photo) {
-          const img = this.imgCache.get(photo.id);
-          if (img && img.complete) {
-            ctx.save();
-            ctx.beginPath();
-            ctx.rect(coord.x, coord.y, coord.width, coord.height);
-            ctx.clip();
-
-            const s = Math.max(coord.width / img.width, coord.height / img.height);
-            const w = img.width * s;
-            const h = img.height * s;
-            const x = coord.x + (coord.width - w) / 2;
-            const y = coord.y + (coord.height - h) / 2;
-            ctx.drawImage(img, x, y, w, h);
-            ctx.restore();
-          }
+      // 绘制本组页面
+      group.forEach((page, idxInSheet) => {
+        let offsetX = 0;
+        let offsetY = 0;
 
-          // 文字
-          const text = photo.name || "说明文字";
-          this.drawCenteredTextWithEllipsis(
-            ctx, text,
-            coord.x + coord.width / 2,
-            coord.y + coord.height + 40,
-            coord.width - 20, 24, 2, "16px Microsoft Yahei"
-          );
-
-          // 虚线框
-          ctx.setLineDash([1, 1]);
-          ctx.strokeRect(coord.x, coord.y + coord.height + 10, coord.width, 50);
-          ctx.setLineDash([]);
+        if (paperType === "a3") offsetX = idxInSheet * this.pageWidth;
+        if (paperType === "four") {
+          offsetX = (idxInSheet % 2) * this.pageWidth;
+          offsetY = Math.floor(idxInSheet / 2) * this.pageHeight;
         }
-      });
-
-      // ======================
-      // 绘制标引(跨页也正常)
-      // ======================
-      this.indexingLineList.forEach(line => {
-        const { points, coordinate, indexingList } = line;
-        const s = indexingList[0];
-        const e = indexingList[1];
-        const currentIsStart = s.pageIndex === pageIndex;
-        const currentIsEnd = e.pageIndex === pageIndex;
-        if (!currentIsStart && !currentIsEnd) return;
 
         ctx.save();
-        ctx.strokeStyle = "#ff0000";
-        ctx.fillStyle = "#ff0000";
-        ctx.lineWidth = 2;
-        ctx.lineCap = "round";
-
-        const pageOffsetX = pageIndex * (this.pageWidth + this.pageMargin);
-        ctx.beginPath();
-        points.forEach(p => {
-          const x = p.x - pageOffsetX;
-          const y = p.y;
-          ctx.lineTo(x, y);
+        ctx.translate(offsetX, offsetY);
+
+        // ==========================================
+        // 🔥 直接复用你 PDF 里一模一样的绘制逻辑
+        // ==========================================
+        const layout = this.getItemSize(page.layoutMode);
+        const coords = this.getCoordinate(0, layout);
+
+        coords.forEach((coord, i) => {
+          const photoId = page.list[i];
+          const photo = this.photos.find((p) => p.id === photoId);
+
+          ctx.fillStyle = "#D9D9D9";
+          ctx.fillRect(coord.x, coord.y, coord.width, coord.height);
+          ctx.strokeStyle = "#eee";
+          ctx.strokeRect(coord.x, coord.y, coord.width, coord.height);
+
+          if (photo) {
+            const img = this.imgCache.get(photo.id);
+            if (img && img.complete) {
+              ctx.save();
+              ctx.beginPath();
+              ctx.rect(coord.x, coord.y, coord.width, coord.height);
+              ctx.clip();
+
+              const s = Math.max(coord.width / img.width, coord.height / img.height);
+              const w = img.width * s;
+              const h = img.height * s;
+              const x = coord.x + (coord.width - w) / 2;
+              const y = coord.y + (coord.height - h) / 2;
+              ctx.drawImage(img, x, y, w, h);
+              ctx.restore();
+            }
+
+            // 文字
+            const text = photo.name || "说明文字";
+            this.drawCenteredTextWithEllipsis(
+              ctx, text,
+              coord.x + coord.width / 2,
+              coord.y + coord.height + 40,
+              coord.width - 20, 24, 2, "16px Microsoft Yahei"
+            );
+
+            ctx.setLineDash([1, 1]);
+            ctx.strokeRect(coord.x, coord.y + coord.height + 10, coord.width, 50);
+            ctx.setLineDash([]);
+          }
         });
-        ctx.stroke();
 
-        if (currentIsStart) {
-          const first = points[0];
+        // 绘制标引(完全复用你修复好的逻辑)
+        this.indexingLineList.forEach((line) => {
+          const { points, coordinate, indexingList } = line;
+          const s = indexingList[0];
+          const e = indexingList[1];
+          const realPageIndex = groupIndex * perSheet + idxInSheet;
+          const isStart = s.pageIndex === realPageIndex;
+          const isEnd = e.pageIndex === realPageIndex;
+          if (!isStart && !isEnd) return;
+
+          ctx.save();
+          ctx.strokeStyle = "#ff0000";
+          ctx.fillStyle = "#ff0000";
+          ctx.lineWidth = 2;
+          ctx.lineCap = "round";
+
+          const pageOffsetX = s.pageIndex * (this.pageWidth + this.pageMargin);
           ctx.beginPath();
-          ctx.arc(first.x - pageOffsetX, first.y, 4, 0, Math.PI * 2);
-          ctx.fill();
-        }
+          points.forEach((p) => {
+            const x = p.x - pageOffsetX;
+            ctx.lineTo(x, p.y);
+          });
+          ctx.stroke();
 
-        if (currentIsEnd) {
-          const last = points[points.length - 1];
-          ctx.beginPath();
-          if (s.pageIndex === e.pageIndex) {
-            ctx.moveTo(coordinate.x - pageOffsetX, last.y);
-            ctx.lineTo(coordinate.x - pageOffsetX + coordinate.width, last.y);
-          } else {
-            ctx.moveTo(last.x - pageOffsetX, coordinate.y);
-            ctx.lineTo(last.x - pageOffsetX, coordinate.y + coordinate.height);
+          if (isStart) {
+            const first = points[0];
+            ctx.beginPath();
+            ctx.arc(first.x - pageOffsetX, first.y, 4, 0, Math.PI * 2);
+            ctx.fill();
           }
-          ctx.stroke();
-        }
+
+          if (isEnd) {
+            const last = points[points.length - 1];
+            ctx.beginPath();
+            if (s.pageIndex === e.pageIndex) {
+              ctx.moveTo(coordinate.x - pageOffsetX, last.y);
+              ctx.lineTo(coordinate.x - pageOffsetX + coordinate.width, last.y);
+            } else {
+              ctx.moveTo(last.x - pageOffsetX, coordinate.y);
+              ctx.lineTo(last.x - pageOffsetX, coordinate.y + coordinate.height);
+            }
+            ctx.stroke();
+          }
+          ctx.restore();
+        });
+
         ctx.restore();
       });
 
-      // 转成图片并加入 ZIP
+      // 转图片并加入 ZIP
       const base64 = pageCanvas.toDataURL("image/png");
-      zip.file(`第${pageIndex + 1}页.png`, base64.split(",")[1], { base64: true });
+      zip.file(`第${groupIndex + 1}张.png`, base64.split(",")[1], { base64: true });
     }
 
-    // 生成 ZIP 并下载
-    let filename = name || '排版图片_' + Date.now()
+    // 下载
+    const filename = name || "排版图片";
     const blob = await zip.generateAsync({ type: "blob" });
-    saveAs(blob, `filename.zip`);
-    ElMessage.success("图片导出成功!");
+    saveAs(blob, `${filename}.zip`);
+    ElMessage.success("导出成功!");
   } catch (err) {
     console.error(err);
     ElMessage.error("导出失败");

+ 23 - 160
src/view/material/exportPhotos.vue

@@ -5,187 +5,50 @@
     label-width="90px"
     class="camera-from dispatch-file-from jm-file-upload"
   >
-    <el-form-item label="分类:" class="mandatory">
-      <el-cascader
-        v-model="caseFile.filesTypeId"
-        :how-all-levels="false"
-        style="width: 300px;"
-        :options="fileOptions"
-        :props="{
-          checkStrictly: false,
-        }"
-        clearable
-      />
+    <el-form-item label="排版:" class="mandatory">
+      <el-radio-group v-model="exportInfo.paperType">
+        <el-radio value="four">4联卡纸</el-radio>
+        <el-radio value="a4">A4纸</el-radio>
+        <el-radio value="a3">A3纸</el-radio>
+      </el-radio-group>
     </el-form-item>
-    <el-form-item label="上传类别:" class="mandatory">
-      <el-select
-        style="width: 300px;"
-        v-model="caseFile.filesType"
-        placeholder="请选择上传类别"
-      >
-        <el-option label="本地上传" :value="1" />
-        <el-option label="媒体库上传" :value="0" />
-      </el-select>
-      <!-- <el-cascader
-        v-model="caseFile.filesTypeId"
-        :how-all-levels="false"
-        style="width: 300px;"
-        :options="fileOptions"
-        :props="{
-          checkStrictly: false,
-        }"
-        clearable
-      /> -->
+    <el-form-item label="格式:" class="mandatory">
+      <el-radio-group v-model="exportInfo.fileType">
+        <el-radio value="pdf">pdf</el-radio>
+        <el-radio value="jpg">jpg</el-radio>
+      </el-radio-group>
     </el-form-item>
-    <el-form-item label="文件:" v-if="caseFile.filesType == 1" class="mandatory uploadFile">
-      <el-upload
-        class="upload-demo"
-        :multiple="false"
-        drag
-        :limit="1"
-        :before-upload="upload"
-        :file-list="fileList"
-        :http-request="uploadNewFile"
-        :on-success="handleSuccess"
-        :on-preview="previewFile"
-        :on-exceed="handleExceed"
-        :accept="accept"
-        :before-remove="removeFile"
-      >
-        <div type="primary" :disabled="!!file">
-          <div>点击或拖拽文件上传</div>
-          <div class="">{{ size }}以内的{{ formatDesc }}</div>
-        </div>
-        <template v-slot:file="{ file }: { file: UploadFile }">
-          <div class="file" @click.stop="previewFile()">
-            <div>
-              <el-icon><Document /></el-icon>
-              <span class="name">{{ file.name }}</span>
-            </div>
-            <el-icon @click.stop="removeFile()"><Close /></el-icon>
-          </div>
-        </template>
-      </el-upload>
-    </el-form-item>
-    
-    <el-form-item label="文件:" v-else class="mandatory uploadFile">
-      <el-button  type="primary" class="mtk" @click="handleAdd"
-      >从媒体库上传</el-button>
-    </el-form-item>
-    <div style="padding-left: 90px" v-if="caseFile.filesType == 0">
-        <viewImg :list="mtkList" edit delete @handleItem="handleItem"/>
-      </div>
   </el-form>
 </template>
 
 <script setup lang="ts">
-import { uploadNewFile, addByMediaLiBrary } from "@/store/case";
-import viewImg from "@/components/viewImg/index.vue"
-import {
-  DrawFormatDesc,
-  DrawFormats,
-  FileDrawType,
-  OtherFormatDesc,
-  OtherFormats,
-} from "@/constant/caseFile";
 import { maxFileSize } from "@/constant/caseFile";
 import { useUpload } from "@/hook/upload";
-import { CaseFile, addCaseFile } from "@/store/caseFile";
-import { ElMessage, UploadFile } from "element-plus";
+import { ElMessage } from "element-plus";
 import { computed, ref, watchEffect, onMounted } from "vue";
 import { addCaseScenes } from "./quisk";
 import { QuiskExpose } from "@/helper/mount";
 import { updateSelectByTreeFileLists } from "@/store/case";
 
 const props = defineProps<{
-  caseId: number;
-  fileType: number;
-  filesTypeName: [string];
-  fileInfo?: Object;
+  showPagesRef: any;
+  title: string
 }>();
-const fileOptions = ref([])
-const mtkList = ref([]);
+const exportInfo = ref({
+  paperType: "four",
+  fileType: "pdf",
+})
 onMounted(async () => {
-  let newfileOptions = await updateSelectByTreeFileLists();
-  if(props.filesTypeName){
-    let newChildren = newfileOptions
-    props.filesTypeName.map(ele => {
-      newChildren = newChildren.find((item) => item.label == ele).children || []
-    })
-    fileOptions.value = newChildren
-    // fileOptions.value = newfileOptions.find((item) => item.label == props.filesTypeName).children || [];
-  } else {
-    fileOptions.value = newfileOptions;
-  }
-  console.log('fileOptions', fileOptions.value, newfileOptions);
-});
-
-const caseFile = ref({
-  caseId: props.caseId,
-  filesTypeId: props.fileType,
-  filesTitle: "",
-  dictId: '',
-  uploadId: '',
-  filesType: 1,
-});
-console.log('caseFile', props.fileInfo);
-const { size, fileList, upload, removeFile, previewFile, file, accept } = useUpload({
-  maxSize: props.fileInfo?.fileSize || 50 * 1024 * 1024,
-  formats: props.fileInfo?.formats || [".jpg", ".jpeg", ".png"],
-});
-
-const formatDesc = computed(() => {
-  return props.fileInfo?.DrawFormatDesc||'jpg、png、jpeg上传'
-});
-
-// 上传请求
-const handleSuccess = (option) => {
-  console.log('handleSuccess', option);
-}
-const handleExceed = (option) => {
-  ElMessage.error("只能上传1个文件!");
-}
-
-const handleAdd = async () => {
-  let fileId =  await addCaseScenes({formats: props.fileInfo?.formats || [".jpg", ".jpeg", ".png"]});
-  mtkList.value = fileId.map(ele => {
-    return {
-      filesUrl: ele.fileUrl,
-      ...ele,
-     }
-  }) || [];
-  console.log("handleItem", mtkList.value);
-};
-const handleItem = (type, item) => {
-  console.log("handleItem", type, item);
-  if(type == 'delete'){
-    mtkList.value = mtkList.value.filter(ele => ele.id != item.id)
-  }
-};
-watchEffect(() => {
-  console.log('file', file.value);
-  if (file.value?.name) {
-    caseFile.value.uploadId = file.value?.response?.data.id
-  }
 });
 
 defineExpose<QuiskExpose>({
   async submit() {
-    let filesTypeId = caseFile.value.filesTypeId && Array.isArray(caseFile.value.filesTypeId)?caseFile.value.filesTypeId.slice(-1):caseFile.value.filesTypeId
-    if (!file.value && caseFile.value.filesType == 1 || caseFile.value.filesType == 0 && mtkList.value.length == 0) {
-      ElMessage.error("请上传文件");
-      throw "请上传文件";
-    } else if (!filesTypeId) {
-      ElMessage.error("文件分类不能为空!");
-      throw "文件分类不能为空!";
+    if (!props.showPagesRef) {
+      ElMessage.error("加载异常,请刷新浏览器");
+      throw "加载异常,请刷新浏览器";
+    }else{
+      props.showPagesRef.exportToPDF(exportInfo.value.paperType,props.title, exportInfo.value.fileType)
     }
-    let uploadId = file.value?.response?.data.id,
-    uploadIds = mtkList.value.map(ele => ele.uploadId) || [];
-    let params = { ...caseFile.value, file: file.value, filesTypeId: filesTypeId[0] };
-    if(caseFile.value.filesType == 1) params.uploadId = uploadId;
-    if(caseFile.value.filesType == 0) params.uploadIds = uploadIds;
-    await addByMediaLiBrary(params);
-    return caseFile.value;
   },
 });
 </script>

+ 6 - 0
src/view/material/quisk.ts

@@ -1,4 +1,5 @@
 import AddCaseFile from "./addCaseFile.vue";
+import exportPhotos from "./exportPhotos.vue";
 import AddLibrary from "./addLibrary.vue";
 import AddScenes from "./addScenes.vue";
 import rollMakingList from "./rollMakingList.vue";
@@ -12,6 +13,11 @@ export const addCaseFile = quiskMountFactory(AddCaseFile, {
   width: 570,
 });
 
+export const exportCasePhotos = quiskMountFactory(exportPhotos, {
+  title: "导出",
+  enterText: "下 载",
+  width: 570,
+});
 export const addSceneImg1 = quiskMountFactory(AddScenesImg, {
   title: "导入方位图",
   width: 955,

+ 8 - 5
src/view/material/sceneImg.vue

@@ -211,7 +211,9 @@
                   >填写</el-button
                       
                 >
-                <el-dropdown trigger="click" @command="exportToPDF" @visible-change="handleOpen">
+                <el-button class="newbut" @click="exportToPDF(true)" :icon="Download">下载</el-button>
+                <!-- @visible-change="handleOpen" -->
+                <!-- <el-dropdown trigger="click" @command="exportToPDF">
                   <span class="el-dropdown-link">
                       <el-button class="newbut" :icon="Download">下载</el-button>
                   </span>
@@ -222,7 +224,7 @@
                       <el-dropdown-item command="a3">A3纸</el-dropdown-item>
                     </el-dropdown-menu>
                   </template>
-                </el-dropdown>
+                </el-dropdown> -->
               </div>
               <div
                 class="itemList"
@@ -239,8 +241,9 @@
                     class="operation"
                     v-if="childrenList.value == items.id"
                   >
+                    <el-icon title="下载" @click="exportToPDF"><Download/></el-icon>
                   
-                <el-dropdown trigger="click" @command="exportToPDF">
+                <!-- <el-dropdown trigger="click" @command="exportToPDF">
                   <span class="el-dropdown-link">
                     <el-icon title="下载"><Download/></el-icon>
                   </span>
@@ -251,7 +254,7 @@
                       <el-dropdown-item command="a3">A3纸</el-dropdown-item>
                     </el-dropdown-menu>
                   </template>
-                </el-dropdown>
+                </el-dropdown> -->
                   <i title="重命名" class="el-icon iconfont icon-rename" @click="handlemtk(items)"></i>
                     <!-- <el-icon title="重命名"
                       @click="
@@ -1166,7 +1169,7 @@ const handleConfirm = async () => {
       casePhotoItem.value.show = false;
       await getList()
 }
-const exportToPDF = async (paperType) => {
+const exportToPDF = async (isAll) => {
   await exportCasePhotos({showPagesRef: showPagesRef.value})
   // showPagesRef.value.exportToPDF(paperType, childrenList.value?.item?.name);
 }

+ 6 - 2
src/view/material/showpages.vue

@@ -78,8 +78,12 @@ onMounted(() => {
     // scale.value = editor.value.scale;
   }
 });
-const exportToPDF = (paperType, name) => {
-  editor.value.exportPagesToPDF(paperType, name);
+const exportToPDF = (paperType, name, fileType = 'pdf') => {
+  if(fileType == 'pdf'){
+    editor.value.exportPagesToPDF(paperType, name);
+  }else{
+    editor.value.exportPagesAsImages(paperType, name);
+  }
 }
 defineExpose({
   exportToPDF

+ 1 - 1
src/view/vrmodel/list.vue

@@ -4,7 +4,7 @@
       <slot name="header" />
       <el-form-item class="searh-btns" style="grid-area: 1 / 6 / 2 / 4">
         <el-button style="visibility: hidden;" type="primary" @click="params.pagging.refresh">查询</el-button>
-        <el-button v-if="upload" type="primary" @click="addUploadSecen">上传场景</el-button>
+        <el-button v-if="upload" type="primary" plain @click="addUploadSecen">上传</el-button>
         <el-button class="float-right" type="primary" plain @click="params.pagging.queryReset"
           >重置</el-button
         >