Commit f5d555aa by linjinhong

fix:修改黄点模

parent 6d7e3d33
......@@ -156,6 +156,8 @@ export default {
? downloadBySubOrderNumber["CNHLC"]
: downloadBySubOrderNumber["USHLC"];
}
console.log("url", url);
const { data } = await axios.post(`${env}/${url}`, [...params.ids], {
headers: { "jwt-token": token },
});
......@@ -163,6 +165,7 @@ export default {
if (data.code !== 200) {
return res.json(data);
}
console.log("downloadBySubOrderNumber", data);
// 转义中文
if (data.message) {
......
......@@ -407,8 +407,6 @@ async function processInFastMode(inputPath, bounds, options) {
}
}
// ================= 辅助函数 =================
// 应用边界留白
function applyPadding(bounds, padding, imgWidth, imgHeight) {
if (padding > 0) {
......@@ -425,8 +423,12 @@ async function checkImageOutsideGrid() {
const lineDom = document.getElementById("line");
if (!lineDom) {
console.log("未找到网格元素(id=line)");
return false;
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
const lineRect = lineDom.getBoundingClientRect();
const grid = {
left: lineRect.left,
......@@ -436,29 +438,39 @@ async function checkImageOutsideGrid() {
width: lineRect.width,
height: lineRect.height,
};
if (grid.width === 0 || grid.height === 0) return false;
if (grid.width === 0 || grid.height === 0) {
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
const imgDom = document.getElementById("imgBox");
if (!imgDom) {
console.log("图片未加载完成或未找到imgBox元素");
return false;
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
// ✅ 修复 1:确保图片加载完成(最关键!)
// 确保图片加载完成
await new Promise((resolve) => {
if (imgDom.complete) resolve();
else imgDom.onload = resolve;
});
const imgRect = imgDom.getBoundingClientRect();
// ✅ 修复 2:正确获取图片原始尺寸(必须等 onload 之后)
const naturalWidth = imgDom.naturalWidth;
const naturalHeight = imgDom.naturalHeight;
if (naturalWidth === 0 || naturalHeight === 0) {
console.log("图片原始尺寸为0");
return false;
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
const imgDisplayInfo = {
......@@ -470,13 +482,17 @@ async function checkImageOutsideGrid() {
naturalHeight,
};
// ===================== 【判断1】图片是否大于台版 =====================
const isImageBiggerThanGrid =
imgDisplayInfo.width > grid.width || imgDisplayInfo.height > grid.height;
// 如果图片不大于网格,直接返回false,不执行sharp和像素检测
// 如果图片不大于网格,直接返回
if (!isImageBiggerThanGrid) {
console.log("图片尺寸小于/等于网格,无需检测越界");
return false;
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
// ===================== 第二步:Sharp 读取图片原始像素 =====================
......@@ -509,7 +525,6 @@ async function checkImageOutsideGrid() {
const imgDisplayX = imgDisplayInfo.left + x * scaleX;
const imgDisplayY = imgDisplayInfo.top + y * scaleY;
// ✅ 修复 3:防止 NaN 导致误判
if (isNaN(imgDisplayX) || isNaN(imgDisplayY)) continue;
const isOutside =
......@@ -531,75 +546,141 @@ async function checkImageOutsideGrid() {
}
console.log("检测结果:", hasOutsideValidPixel ? "超出网格" : "正常");
return hasOutsideValidPixel;
// ===================== ✅ 返回你要的两个参数 =====================
return {
isImageBiggerThanGrid, // 图片是否大于台版
hasOutsideValidPixel, // 超出部分是否有真实像素(非透明)
};
} catch (error) {
console.error("检测失败:", error);
return false;
// 出错也返回固定结构
return {
isImageBiggerThanGrid: false,
hasOutsideValidPixel: false,
};
}
}
const FIXED_MASK_RATIO = {
left: 0.341,
top: 0.308,
width: 0.443,
height: 0.443,
};
async function cropToPrintArea(inputPath, outputPath, data) {
if (!data) return false;
const { canvasWidth, canvasHeight, rect_info } = data;
// 正确比例(你裁剪必须用这个)
let RATIO = { ...FIXED_MASK_RATIO };
if (rect_info) {
RATIO = {
left: rect_info.leftDistance / canvasWidth,
top: rect_info.topDistance / canvasHeight,
width: rect_info.rectWidth / canvasWidth,
height: rect_info.rectHeight / canvasHeight,
};
}
// 1. 解构数据
const { canvasWidth, canvasHeight, rect_info, drawImage } = data;
// 注意:list 里可能有多张图,这里默认取第一张进行裁剪
// 如果你的业务逻辑需要对特定图层裁剪,需要根据业务调整这里的选择逻辑
// drawImage 结构: [dx, dy, dWidth, dHeight] (画布坐标系)
const [dx, dy, imgRenderW, imgRenderH] = drawImage;
try {
// 1. 获取图片宽高
// 2. 获取原图尺寸
const meta = await sharp(inputPath).metadata();
const width = meta.width;
const height = meta.height;
const realW = meta.width;
const realH = meta.height;
// 3. 核心计算逻辑:坐标映射
// 3.1 计算图片的缩放比例 (渲染尺寸 / 原图尺寸)
// 注意:如果前端有旋转,这里需要更复杂的计算,但你说不考虑旋转
const scaleX = imgRenderW / realW;
const scaleY = imgRenderH / realH;
// 3.2 计算 rect_info 在画布上相对于图片左上角的偏移量
// 公式:画布上的坐标 - 图片在画布上的偏移
// 结果:画布上的打印区域相对于图片渲染内容的位置
const relX = rect_info.leftDistance - dx;
const relY = rect_info.topDistance - dy;
// 3.3 将相对偏移映射回原图坐标系
// 公式:画布上的相对位置 / 缩放比例
let cropX = Math.round(relX / scaleX);
let cropY = Math.round(relY / scaleY);
// 3.4 计算裁剪框的宽高 (同样需要映射回原图尺寸)
// 注意:rect_info 的宽高是画布上的像素宽高,需要除以缩放比例得到原图像素宽高
let cropW = Math.round(rect_info.rectWidth / scaleX);
let cropH = Math.round(rect_info.rectHeight / scaleY);
// 4. 安全边界处理 (非常重要)
// 因为图片可能没有完全覆盖打印区域(比如用户把图片缩小了,或者拖到了一边),
// 计算出来的 cropX/cropY 可能是负数,或者超出图片范围。
// 2. 计算裁剪区域
const cropLeft = Math.round(width * RATIO.left);
const cropTop = Math.round(height * RATIO.top);
const cropWidth = Math.round(width * RATIO.width);
const cropHeight = Math.round(height * RATIO.height);
// 4.1 处理越界逻辑
let finalX = cropX;
let finalY = cropY;
let finalW = cropW;
let finalH = cropH;
// 如果起始点小于 0
if (finalX < 0) {
finalW += finalX; // 宽度减去越界的部分 (finalX 是负数)
finalX = 0;
}
if (finalY < 0) {
finalH += finalY;
finalY = 0;
}
// 如果结束点超出图片范围
if (finalX + finalW > realW) {
finalW = realW - finalX;
}
if (finalY + finalH > realH) {
finalH = realH - finalY;
}
// 3. 执行裁剪
// 4.2 最终检查:如果宽高小于等于0,说明打印区域完全没有覆盖图片,或者图片极小
if (finalW <= 0 || finalH <= 0) {
console.warn(
"警告:计算出的裁剪区域无效,可能图片未覆盖打印区域。输出原图。",
);
// 这种情况可以根据业务决定是报错还是返回原图,这里演示返回原图
await sharp(inputPath).toFile(outputPath);
} else {
// 5. 执行裁剪
await sharp(inputPath)
.extract({
left: cropLeft,
top: cropTop,
width: cropWidth,
height: cropHeight,
left: finalX,
top: finalY,
width: finalW,
height: finalH,
})
.toFile(outputPath);
}
// 4. ✅ 返回你要的结构
// 6. 返回结果结构 (保持你原来的格式)
const results = [
{
file: path.basename(inputPath),
...{
status: "cropped",
outputPath: outputPath,
originalSize: { width, height },
newSize: { width: cropWidth, height: cropHeight },
originalSize: { width: realW, height: realH },
newSize: { width: finalW, height: finalH },
cropArea: {
x: cropLeft,
y: cropTop,
width: cropWidth,
height: cropHeight,
},
x: finalX,
y: finalY,
width: finalW,
height: finalH,
},
},
];
// 删除临时文件
if (fs.existsSync(inputPath)) {
fs.unlinkSync(inputPath);
}
return results;
} catch (error) {
console.error("裁剪过程出错:", error);
throw new Error("图片裁剪失败: " + error.message);
}
}
......
......@@ -248,3 +248,7 @@ export function newMmToPxFn(mm) {
const px = (Number(mm) / 25.4) * 42;
return Number(px.toFixed(1));
}
export function HLCMmToPxFn(mm) {
const px = (Number(mm) / 10) * 18;
return Number(px.toFixed(1));
}
......@@ -169,6 +169,12 @@ export default {
imgList: {
//监听当前网格内数据,并把power设置为true
handler(value) {
const isOver = this.$dataStore.get("isOver");
console.log("isOver", isOver);
if (!isOver) {
this.$dataStore.set("isOver", false);
return;
}
if (this.detail && value?.length && this.desktopDevice === 3) {
const item = this.detail?.saveImgList?.find(
(el) => el.fileName == value[0].fileName,
......@@ -339,6 +345,8 @@ export default {
// console.log(img, "img");
img.onload = null;
const ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = "high";
ctx.drawImage(img, ...item.drawImage);
URL.revokeObjectURL(url);
resolve();
......@@ -423,16 +431,12 @@ export default {
},
async hasDesignImagesCanvasJsonList(designImagesCanvasJsonList, bool) {
let isCustom = false;
let params;
let imageResList = [];
designImagesCanvasJsonList = JSON.parse(designImagesCanvasJsonList);
const isCustom = !designImagesCanvasJsonList[0].images || false;
const {
canvasWidth,
canvasHeight,
rect_info,
} = designImagesCanvasJsonList;
const params = { canvasWidth, canvasHeight, rect_info };
//当adjustable有值时 直接赋值宽高
let urlList = [];
console.log(this.desktopDevice);
// 当adjustable有值时 直接赋值宽高
// if (
// !this.checked &&
// this.detail.diyId &&
......@@ -473,28 +477,41 @@ export default {
// }
// }
if (this.desktopDevice !== 3) {
designImagesCanvasJsonList = JSON.parse(designImagesCanvasJsonList);
isCustom = !designImagesCanvasJsonList[0].images || false;
if (isCustom) {
const {
canvasWidth,
canvasHeight,
rect_info,
list,
} = designImagesCanvasJsonList[0];
const { drawImage } = list[0];
params = { canvasWidth, canvasHeight, rect_info, drawImage };
}
}
// 根据生产单号查找 素材图片 下载到本地 然后返回本地地址去显示
if (!imageResList.length && !bool) {
let newType = this.orderType;
if (this.orderType == "CN" && this.desktopDevice == 3) {
newType = this.orderType == "CN" ? "CNHLC" : "USHLC";
}
let res = await this.$api.post(
pathMap["downloadBySubOrderNumber"][newType],
{
ids: [this.detail.id],
device: this.$store.state.desktopDevice,
device: this.desktopDevice,
orderType: this.orderType,
},
);
console.log("res", res);
if (!res.data.length) return this.$message.warning("未找到素材图!");
res.data.forEach((el) => {
imageResList = imageResList.concat(el.list || []);
});
if (this.desktopDevice == 3) urlList = res.data.map((el) => el.url);
} else if (bool) {
//如果有本地数据 则直接获取本地数据同图片
imageResList = this.detail?.saveImgList || [];
......@@ -559,6 +576,7 @@ export default {
type: "sendFile",
value: [...newImgList],
productMark: this.detail.productMark,
urls: urlList,
};
if (
......@@ -1198,23 +1216,6 @@ export default {
<el-button @click="clearCache" type="primary">清除</el-button>
</template>
</el-dialog>
<el-dialog
title="警告:无法直接生产"
:visible.sync="dialogVisible"
width="30%"
>
<div>
素材效果已经超出台版范围,无法直接打印。请更换打印设
备,或者进行强制生产
</div>
<div>:“自动强制生产”会让每次生产都强制生产</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button>强制生产</el-button>
<el-button type="primary">自动强制生产</el-button>
</span>
</el-dialog>
</div>
</template>
......
......@@ -4,7 +4,7 @@ const { ipcRenderer } = require("electron");
import ImgSetting from "./imgSetting.vue";
import bus from "@/bus";
import PrintDialog from "@/views/design/head/printDialog.vue";
import { mmToPx, newMmToPxFn, extractValue } from "@/utils";
import { mmToPx, newMmToPxFn, extractValue, HLCMmToPxFn } from "@/utils";
const { checkImageOutsideGrid } = require("../../../server/utils/setImage");
const { pathMap } = require("@/config/index.js");
......@@ -228,34 +228,13 @@ export default {
productMark: "",
isMove: false,
isOver: false,
dialogVisible: false,
};
},
watch: {
imgList: {
async handler() {
this.isMove = false;
// const image = document.getElementById("imgBox");
// console.log(234, image);
// if (newValue.length && image) {
// this.$nextTick(() => {
// let dom = document.getElementsByClassName("drr");
// console.log(228, dom);
// this.imgList.forEach((el, i) => {
// let dom_i = document.getElementsByClassName("drr")[i];
// dom_i.style.zIndex =
// dom_i.style.zIndex === "" || !dom_i.style.zIndex
// ? 0
// : dom_i.style.zIndex;
// dom[i].addEventListener("click", () => {
// if (this.newDesktopDevice == 3) {
// return;
// }
// this.selectIndex = i;
// this.selectItem(i);
// });
// });
// });
this.$store.commit("changeImgList", this.imgList);
},
......@@ -396,6 +375,14 @@ export default {
},
immediate: true,
},
// isOver: {
// handler(newValue) {
// this.dialogVisible = true;
// // if (newValue && this.newDesktopDevice == 3) {
// // this.dialogVisible = true;
// // }
// },
// },
},
methods: {
// 重构为可手动触发的方法
......@@ -771,7 +758,7 @@ export default {
);
return img?.productionFile;
},
getBackFile({ files, size }, callback) {
getBackFile({ files, size }, callback, urlList) {
console.log("productMark", this.detail);
const isCp = this.productMark == "custom_part" ? true : false;
let that = this;
......@@ -784,24 +771,30 @@ export default {
files[i].url = files[i]?.productionFile || files[i].url;
that.$nextTick(async () => {
let w = bw / 2;
let h = bh / 2;
// let h = bh / 2;
let width_px, height_px, rate, x, y;
let data = await that.getImageSize(files[i].url);
if (size && !files[i].isCut) {
console.log("size", size);
console.log("data", data);
if (this.newDesktopDevice == 3) {
const scale = bh / data.height;
width_px = data.width * scale;
height_px = data.height * scale;
x = (bw - data.w) / 2;
y = h;
rate = height_px / width_px;
} else {
// if (this.newDesktopDevice == 3) {
// const scale = bh / data.height;
// width_px = data.width * scale;
// height_px = data.height * scale;
// x = (bw - data.w) / 2;
// y = h;
// rate = height_px / width_px;
// } else {
if (isCp) {
width_px = newMmToPxFn(size.width);
height_px = newMmToPxFn(size.height);
width_px =
this.newDesktopDevice == 3
? HLCMmToPxFn(size.width)
: newMmToPxFn(size.width);
height_px =
this.newDesktopDevice == 3
? HLCMmToPxFn(size.height)
: newMmToPxFn(size.height);
} else {
width_px = mmToPx(size.width);
height_px = mmToPx(size.height);
......@@ -810,7 +803,7 @@ export default {
x = w;
y = height_px / 2;
rate = height_px / width_px;
}
// }
} else {
rate = data.height / data.width;
console.log("data", data);
......@@ -846,13 +839,35 @@ export default {
that.selectIndex = that.imgList.length - 1;
that.showImgSetting = true;
if (this.newDesktopDevice != 3) {
// if (this.newDesktopDevice != 3) {
// that.ev("cover");
isCp ? that.ev("x_center") : that.ev("cover");
// }
this.$nextTick(async () => {
this.isOver = await checkImageOutsideGrid();
});
const {
isImageBiggerThanGrid,
hasOutsideValidPixel,
} = await checkImageOutsideGrid();
this.isOver = hasOutsideValidPixel;
console.log("urlList", urlList);
console.log("orderType", this.orderType);
if (urlList.length == 1) {
if (isImageBiggerThanGrid && !hasOutsideValidPixel) {
let res = await this.$api.post(
pathMap["processTransparentBackground"][this.orderType],
{
url: urlList[0],
subOrderNumber: this.detail.factorySubOrderNumber,
orderType: this.orderType,
},
);
console.log("processTransparentBackground", res);
}
}
// if (this.isOver) this.$dataStore.set("isOver", this.isOver);
});
if (i === files.length - 1) {
callback && callback();
}
......@@ -1282,13 +1297,22 @@ export default {
this.imgNaturalSize.height = e.target.naturalHeight;
console.log("图片原始尺寸:", this.imgNaturalSize);
},
async forcedProductionFN(bool) {
if (bool) this.isForcedProduction = true;
const params = {
orderType: this.orderType,
subOrderNumber: this.detail.factorySubOrderNumber,
url: "",
};
},
},
mounted() {
this.imgHeight = window.screen.height + "px";
this.systemSetting = this.$dataStore.get("setting");
bus.$on("busEmit", (v) => {
let { type, value, size, productMark } = v;
let { type, value, size, productMark, urls } = v;
switch (type) {
case "completeMessage":
......@@ -1368,6 +1392,7 @@ export default {
JSON.parse(JSON.stringify(this.imgList)),
);
},
urls,
);
}
}
......@@ -1823,7 +1848,7 @@ export default {
v-if="
detail?.custom_part == 'custom_part' &&
selectImgList.length &&
isOver &&
!isOver &&
!isMove &&
!selectImgList.some((el) => el.isCut)
"
......@@ -1836,21 +1861,29 @@ export default {
v-if="
detail?.custom_part == 'custom_part' &&
selectImgList.length &&
!isOver
isOver
"
>
<b style="color:red"
>素材效果已经超出台版范围,请更换更大台版或和客户协调沟通</b
>
</div>
<div
class="print-tip"
:style="{ left: isView ? '' : '22%' }"
v-if="newDesktopDevice == 3 && !isOver"
>
<b style="color:red"
<div class="print-tip" :style="{ left: isView ? '' : '22%' }">
<b
style="color:red"
v-if="
selectImgList.length &&
newDesktopDevice == 3 &&
isOver &&
!isForcedProduction
"
>该生产单素材已经被强制调整适配台版大小,请关注</b
>
<b
style="color:green"
v-if="selectImgList.length && newDesktopDevice == 3 && !isOver"
>该单无需拖动设计,直接打印</b
>
</div>
</div>
......@@ -1938,6 +1971,25 @@ export default {
</div>
</div>
</div>
<el-dialog
title="警告:无法直接生产"
:visible.sync="dialogVisible"
width="30%"
>
<div>
素材效果已经超出台版范围,无法直接打印。请更换打印设
备,或者进行强制生产
</div>
<div>注:“自动强制生产”会让每次生产都强制生产</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button @click="forcedProductionFN">强制生产</el-button>
<el-button @click="forcedProductionFN(true)" style="color: red;"
>自动强制生产</el-button
>
</span>
</el-dialog>
</div>
</template>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment