123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- <template>
- <el-card class="box-card">
- <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
- <template #detail>
- <div style="width: 100%; padding: 0 20px">
- <el-table :data="[formData.data]" :row-style="{ height: '35px' }" header-row-class-name="tableHeader" border>
- <el-table-column label="产品基础信息" width="320">
- <template #default="{ row }">
- <div style="line-height: 35px">
- <span style="color: black; font-weight: 700">商品名称: </span>
- <span>{{ row.skuSpecName }}</span>
- </div>
- <div style="line-height: 35px">
- <span style="color: black; font-weight: 700">产品尺寸: </span>
- <span>{{ row.length }} * {{ row.width }} * {{ row.height }}</span>
- </div>
- <div style="line-height: 35px">
- <span style="color: black; font-weight: 700">SKU品号: </span>
- <span>{{ row.skuSpecCode }}</span>
- </div>
- <div style="line-height: 35px">
- <span style="color: black; font-weight: 700">BOM品号: </span>
- <span>{{ row.bomSpecCode }}</span>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="客户定制图样" width="260">
- <template #default="{ row }">
- <div style="display: flex; width: 100%">
- <div style="width: 60px; height: 32px; line-height: 32px">设计图:</div>
- <div style="width: calc(100% - 60px)">
- <el-image
- fit="scale-down"
- style="width: 160px; border: 1px solid #ccc; cursor: pointer"
- v-if="row.blueprint"
- :src="row.blueprint"
- @click="openFile(row.blueprint)">
- <template #error>
- <div class="image-slot">
- <el-icon><Picture /></el-icon>
- </div>
- </template>
- </el-image>
- <el-button type="primary" @click="clickToView()" text>查看定制图样</el-button>
- </div>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="设计样图" width="280">
- <template #default="{ row }">
- <div style="display: flex; width: 100%">
- <div style="width: 70px; height: 32px; line-height: 32px">设计样图:</div>
- <div style="width: calc(100% - 70px)">
- <el-image
- fit="scale-down"
- style="width: 160px; border: 1px solid #ccc; cursor: pointer"
- v-if="row.proofingImg"
- :src="row.proofingImg"
- @click="openFile(row.proofingImg)">
- </el-image>
- <el-upload
- :show-file-list="false"
- action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
- :data="uploadImgData"
- :before-upload="uploadImgFile"
- :on-success="handleImgSuccess"
- style="width: 100%"
- accept=".jpg,.jpeg,.png,.GIF,.JPG,.PNG">
- <el-button type="primary" text>上传图片</el-button>
- </el-upload>
- </div>
- </div>
- <div style="display: flex; margin-top: 20px; width: 100%">
- <div style="width: 70px; height: 32px; line-height: 32px">图稿文件:</div>
- <div style="width: calc(100% - 70px); line-height: 32px">
- <a
- style="color: #409eff; cursor: pointer; word-break: break-all; margin-right: 10px"
- @click="openFile(row.productionDocument)"
- v-if="row.productionDocument">
- {{ row.productionDocument }}
- </a>
- <el-upload
- :show-file-list="false"
- action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
- :data="uploadData"
- :before-upload="uploadFile"
- :on-success="handleSuccess"
- style="width: 100%">
- <el-button type="primary" text>上传ez3/dxf文件</el-button>
- </el-upload>
- </div>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="图稿确认历史" min-width="500">
- <template #default="{}">
- <div style="width: 100%">
- <el-table :data="logList" :row-style="{ height: '35px' }" header-row-class-name="tableHeader">
- <el-table-column label="时间" prop="createTime" width="160" />
- <el-table-column label="操作人" prop="operator" width="140" />
- <el-table-column label="类型" width="120">
- <template #default="{ row }">
- <div>{{ dictKeyValue(row.type, logType) }}</div>
- </template>
- </el-table-column>
- <el-table-column label="备注" prop="remark" />
- </el-table>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </template>
- </byForm>
- <div style="padding: 10px 0; text-align: center">
- <el-button style="width: 120px" @click="clickCancel()" v-preReClick>取 消</el-button>
- <el-button type="primary" style="width: 120px" @click="clickSubmit()" v-preReClick>提交图稿</el-button>
- </div>
- <el-dialog title="定制图样" v-if="openDrawing" v-model="openDrawing" width="70%">
- <div style="height: calc(100vh - 94px); overflow: auto; display: flex; align-items: center; justify-content: center; position: relative">
- <div style="position: absolute; left: 10px; top: 10px">
- <div style="font-size: 20; font-weight: 700">裸垫参数:</div>
- <div style="margin: 4px 8px 4px">长: {{ formData.data.length }}cm</div>
- <div style="margin: 4px 8px 4px">宽: {{ formData.data.width }}cm</div>
- <div style="margin-top: 16px; font-size: 20; font-weight: 700">元素参数:</div>
- <div style="margin: 4px 8px 4px">长: {{ elLength }}cm</div>
- <div style="margin: 4px 8px 4px">宽: {{ elWidth }}cm</div>
- <div style="margin: 4px 8px 4px">旋转角度: {{ elAngle }}</div>
- <div style="margin-top: 16px; font-size: 20; font-weight: 700">比例:</div>
- <div style="margin: 4px 8px 4px">{{ ratio }}px : 1cm</div>
- </div>
- <div style="width: 757px; height: 813px; border: 2px dashed #bdbdbd" ref="mainDIV">
- <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">
- <canvas ref="canvas" :width="width" :height="height" style="border: 1px solid #ccc"></canvas>
- </div>
- </div>
- </div>
- </el-dialog>
- </el-card>
- </template>
- <script setup>
- import { useRoute, useRouter } from "vue-router";
- import useTagsViewStore from "/src/store/modules/tagsView.js";
- import byForm from "/src/components/byForm/index";
- import { nextTick } from "vue";
- import { ElMessage } from "element-plus";
- const { proxy } = getCurrentInstance();
- const route = useRoute();
- const router = useRouter();
- const formOption = reactive({
- inline: true,
- labelWidth: "100px",
- itemWidth: 100,
- rules: [],
- labelPosition: "right",
- });
- const formConfig = computed(() => {
- return [
- {
- type: "title",
- title: "产品",
- label: "",
- },
- {
- type: "slot",
- slotName: "detail",
- },
- ];
- });
- const formData = reactive({
- data: {},
- });
- const rules = ref({
- isProofing: [{ required: true, message: "请选择是否打样", trigger: "change" }],
- quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
- consignee: [{ required: true, message: "请输入联系人", trigger: "blur" }],
- consigneeNumber: [{ required: true, message: "请输入联系人电话", trigger: "blur" }],
- province: [{ required: true, message: "请输入省", trigger: "blur" }],
- city: [{ required: true, message: "请输入市", trigger: "blur" }],
- county: [{ required: true, message: "请输入区/县", trigger: "blur" }],
- streetCode: [{ required: true, message: "请输入街道", trigger: "blur" }],
- detailedAddress: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
- skuCode: [{ required: true, message: "请输入SKU品号", trigger: "blur" }],
- });
- const logList = ref([]);
- const logType = ref([
- { dictKey: 10, dictValue: "上传设计图" },
- { dictKey: 20, dictValue: "上传打样图" },
- { dictKey: 21, dictValue: "上传打样图稿" },
- { dictKey: 22, dictValue: "驳回打样图稿" },
- { dictKey: 30, dictValue: "确认打样图" },
- { dictKey: 40, dictValue: "上传设计图稿" },
- ]);
- const submit = ref(null);
- onMounted(() => {
- if (route.query && route.query.id) {
- proxy.post("/orderSkuArtworkMake/detail", { id: route.query.id }).then((res) => {
- formData.data = res;
- proxy.post("/orderSkuArtworkLog/list", { orderSkuId: res.orderSkuId }).then((res) => {
- logList.value = res;
- });
- });
- }
- });
- const openFile = (path) => {
- window.open(path, "_blank");
- };
- const uploadImgData = ref({});
- const uploadImgFile = async (file) => {
- const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
- uploadImgData.value = res.uploadBody;
- file.id = res.id;
- file.fileName = res.fileName;
- file.fileUrl = res.fileUrl;
- return true;
- };
- const handleImgSuccess = (response, UploadFile) => {
- formData.data.proofingImg = UploadFile.raw.fileUrl;
- };
- const uploadData = ref({});
- const uploadFile = async (file) => {
- const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
- uploadData.value = res.uploadBody;
- file.id = res.id;
- file.fileName = res.fileName;
- file.fileUrl = res.fileUrl;
- return true;
- };
- const handleSuccess = (response, UploadFile) => {
- formData.data.productionDocument = UploadFile.raw.fileUrl;
- };
- const openDrawing = ref(false);
- const width = ref(0);
- const height = ref(0);
- const ratio = ref(1);
- const canvas = ref(null);
- const ctx = ref("");
- const clickCoordinate = { x: 0, y: 0 };
- /** 图片数据 名字 位置等 */
- const imagesData = ref([]);
- const elLength = ref("");
- const elWidth = ref("");
- const elAngle = ref("");
- const elCoordinates = ref([]);
- const init = () => {
- ctx.value = canvas.value.getContext("2d");
- canvas.value.addEventListener("mousedown", mousedownFn, false);
- let list = JSON.parse(formData.data.blueprintDetails)[0].imagesData;
- if (list && list.length > 0) {
- for (let i = 0; i < list.length; i++) {
- if (list[i].imgStatus) {
- let img = new Image();
- img.setAttribute("crossOrigin", "anonymous");
- img.src = list[i].imgUrl.replace("https://os.winfaster.cn/sd_sell", import.meta.env.VITE_APP_IPS + "/sd_sell");
- img.onload = function () {
- const obj = { ...list[i], img };
- imagesData.value.push(obj);
- drawByObj(obj);
- };
- } else {
- imagesData.value.push(list[i]);
- drawText(list[i]);
- }
- }
- }
- };
- const drawByObj = (obj) => {
- if (obj.selectStatus) {
- drawGuideByObj(obj);
- }
- ctx.value.save(); // 保存旧的坐标系状态
- ctx.value.translate(obj.x + obj.w / 2, obj.y + obj.h / 2); // 坐标原点移动到旋转中心
- ctx.value.rotate((obj.angle * Math.PI) / 180); // 旋转坐标系
- if (obj.verticalMirrorStatus) {
- ctx.value.scale(1, -1); //上下镜像翻转
- }
- if (obj.horizontalMirrorStatus) {
- ctx.value.scale(-1, 1); //左右镜像翻转
- }
- ctx.value.translate(-(obj.x + obj.w / 2), -(obj.y + obj.h / 2)); // 坐标原点还原
- ctx.value.drawImage(obj.img, obj.x, obj.y, obj.w, obj.h);
- ctx.value.restore(); // 还原之前的坐标系状态
- };
- const drawGuideByObj = (obj) => {
- if (obj.imgStatus) {
- ctx.value.save(); // 保存旧的坐标系状态
- ctx.value.beginPath();
- ctx.value.lineWidth = 4;
- ctx.value.strokeStyle = "green";
- ctx.value.translate(obj.x + obj.w / 2, obj.y + obj.h / 2); // 坐标原点移动到旋转中心
- ctx.value.rotate((obj.angle * Math.PI) / 180); // 旋转坐标系
- ctx.value.translate(-(obj.x + obj.w / 2), -(obj.y + obj.h / 2)); // 坐标原点还原
- ctx.value.rect(obj.x, obj.y, obj.w, obj.h); // 以新坐标系为参照,画出矩形。
- ctx.value.stroke();
- ctx.value.restore(); // 还原之前的坐标系状态
- } else {
- let textPixel = getStringPixel(obj.text, obj.size + "px 宋体");
- let textWidth = textPixel.width;
- let fontHeight = textPixel.fontBoundingBoxAscent + textPixel.fontBoundingBoxDescent;
- ctx.value.save(); // 保存旧的坐标系状态
- ctx.value.beginPath();
- ctx.value.lineWidth = 2;
- ctx.value.strokeStyle = "green";
- ctx.value.translate(obj.x + textWidth / 2, obj.y - fontHeight / 2); // 坐标原点移动到旋转中心
- ctx.value.rotate((obj.angle * Math.PI) / 180); // 旋转坐标系
- ctx.value.translate(-(obj.x + textWidth / 2), -(obj.y - fontHeight / 2)); // 坐标原点还原
- ctx.value.rect(obj.x, obj.y - fontHeight, textWidth, fontHeight); // 以新坐标系为参照,画出矩形。
- ctx.value.stroke();
- ctx.value.restore(); // 还原之前的坐标系状态
- }
- };
- const mousedownFn = (e) => {
- clickCoordinate.x = e.offsetX - canvas.value.offsetLeft;
- clickCoordinate.y = e.offsetY - canvas.value.offsetTop;
- checkElement();
- };
- const getStringPixel = (text, font) => {
- ctx.value.font = font;
- return ctx.value.measureText(text);
- };
- const drawText = (obj) => {
- if (obj.selectStatus) {
- drawGuideByObj(obj);
- }
- let textPixel = getStringPixel(obj.text, obj.size + "px 宋体");
- let textWidth = textPixel.width;
- let fontHeight = textPixel.fontBoundingBoxAscent + textPixel.fontBoundingBoxDescent;
- ctx.value.save(); // 保存旧的坐标系状态
- ctx.value.translate(obj.x + textWidth / 2, obj.y - fontHeight / 2); // 坐标原点移动到旋转中心
- ctx.value.rotate((obj.angle * Math.PI) / 180); // 旋转坐标系
- if (obj.verticalMirrorStatus) {
- ctx.value.scale(1, -1); //上下镜像翻转
- }
- if (obj.horizontalMirrorStatus) {
- ctx.value.scale(-1, 1); //左右镜像翻转
- }
- ctx.value.translate(-(obj.x + textWidth / 2), -(obj.y - fontHeight / 2)); // 坐标原点还原
- ctx.value.beginPath();
- ctx.value.font = obj.size + "px 宋体";
- ctx.value.fillStyle = obj.color;
- ctx.value.textBaseline = "bottom";
- ctx.value.textAlign = "start";
- ctx.value.fillText(obj.text, obj.x, obj.y);
- ctx.value.restore(); // 还原之前的坐标系状态
- };
- const repaint = () => {
- // 清空画布
- ctx.value.clearRect(0, 0, 2 * canvas.value.width, 2 * canvas.value.height);
- // 清空画布以后重新绘制
- imagesData.value.forEach((i) => {
- if (i.imgStatus) {
- drawByObj(i);
- } else {
- drawText(i);
- }
- });
- nextTick(() => {
- elCoordinates.value.forEach((i) => {
- if (i.textStatus) {
- ctx.value.beginPath();
- ctx.value.font = "14px 宋体";
- ctx.value.fillStyle = i.color;
- if (i.crosswise) {
- if (i.y > height.value / 2) {
- ctx.value.textAlign = "start";
- ctx.value.textBaseline = "bottom";
- } else {
- ctx.value.textAlign = "start";
- ctx.value.textBaseline = "top";
- }
- } else {
- if (i.x > width.value / 2) {
- ctx.value.textAlign = "end";
- ctx.value.textBaseline = "top";
- } else {
- ctx.value.textAlign = "start";
- ctx.value.textBaseline = "top";
- }
- }
- ctx.value.fillText(i.text, i.x, i.y);
- } else {
- ctx.value.beginPath();
- ctx.value.moveTo(i.x1, i.y1);
- ctx.value.lineTo(i.x2, i.y2);
- ctx.value.lineWidth = 1;
- ctx.value.strokeStyle = "red";
- ctx.value.stroke();
- }
- });
- });
- };
- /** 判断点击的是哪个 */
- const checkElement = () => {
- elLength.value = "";
- elWidth.value = "";
- elAngle.value = "";
- elCoordinates.value = [];
- try {
- imagesData.value.forEach((item, index) => {
- drawGuideByObj(item);
- if (ctx.value.isPointInPath(clickCoordinate.x, clickCoordinate.y)) {
- imagesData.value = imagesData.value.map((itemList, listIndex) => {
- let selectStatusA = false;
- if (listIndex === index) {
- selectStatusA = true;
- }
- return {
- ...itemList,
- selectStatus: selectStatusA,
- };
- });
- imagesData.value.sort((a, b) => (b.selectStatus ? -1 : 0));
- elementMarking(item);
- throw "选中";
- } else {
- item.selectStatus = false;
- repaint();
- }
- });
- } catch (e) {
- console.log(e);
- repaint();
- }
- };
- const elementMarking = (item) => {
- elCoordinates.value = [];
- elAngle.value = item.angle;
- let x = Number(Math.round(item.x));
- let y = Number(Math.round(item.y));
- let w = 0;
- let h = 0;
- let x1 = 0;
- let y1 = 0;
- let x3 = 0;
- let y3 = 0;
- let cx = 0;
- let cy = 0;
- if (item.imgStatus) {
- w = Number(Math.round(item.w));
- h = Number(Math.round(item.h));
- elLength.value = Number(Math.round(item.w / ratio.value));
- elWidth.value = Number(Math.round(item.h / ratio.value));
- x1 = x;
- y1 = y;
- x3 = x;
- y3 = y + h;
- cx = x + w / 2;
- cy = y + h / 2;
- } else {
- let textPixel = getStringPixel(item.text, item.size + "px 宋体");
- w = Number(Math.round(textPixel.width));
- h = Number(Math.round(textPixel.fontBoundingBoxAscent + textPixel.fontBoundingBoxDescent));
- elLength.value = Number(Math.round(textPixel.width / ratio.value));
- elWidth.value = Number(Math.round((textPixel.fontBoundingBoxAscent + textPixel.fontBoundingBoxDescent) / ratio.value));
- x1 = x;
- y1 = y - h;
- x3 = x;
- y3 = y;
- cx = x + w / 2;
- cy = y - h / 2;
- }
- let x1_rotated = (x1 - cx) * Math.cos((item.angle * Math.PI) / 180) - (y1 - cy) * Math.sin((item.angle * Math.PI) / 180);
- let y1_rotated = (x1 - cx) * Math.sin((item.angle * Math.PI) / 180) + (y1 - cy) * Math.cos((item.angle * Math.PI) / 180);
- let x3_rotated = (x3 - cx) * Math.cos((item.angle * Math.PI) / 180) - (y3 - cy) * Math.sin((item.angle * Math.PI) / 180);
- let y3_rotated = (x3 - cx) * Math.sin((item.angle * Math.PI) / 180) + (y3 - cy) * Math.cos((item.angle * Math.PI) / 180);
- x1_rotated += cx;
- y1_rotated += cy;
- x3_rotated += cx;
- y3_rotated += cy;
- let xLine = 0;
- let yLine = 0;
- if ((x1_rotated == x3_rotated && y1_rotated < y3_rotated) || x1_rotated < x3_rotated) {
- xLine = x1_rotated;
- yLine = y1_rotated;
- } else {
- xLine = x3_rotated;
- yLine = y3_rotated;
- }
- elCoordinates.value.push({ textStatus: false, x1: 0, y1: yLine, x2: xLine, y2: yLine });
- elCoordinates.value.push({ textStatus: false, x1: xLine, y1: 0, x2: xLine, y2: yLine });
- elCoordinates.value.push({ textStatus: true, x: xLine / 2, y: yLine, color: "red", text: Number(Math.round(xLine / ratio.value)) + "cm", crosswise: true });
- elCoordinates.value.push({ textStatus: true, x: xLine, y: yLine / 2, color: "red", text: Number(Math.round(yLine / ratio.value)) + "cm", crosswise: false });
- };
- const clickToView = () => {
- openDrawing.value = true;
- nextTick(() => {
- let divWidth = proxy.$refs.mainDIV.clientWidth;
- let divHeight = proxy.$refs.mainDIV.clientHeight;
- ratio.value = Math.min(Math.floor(divWidth / formData.data.width), Math.floor(divHeight / formData.data.length));
- width.value = Number(Math.round(formData.data.width * ratio.value));
- height.value = Number(Math.round(formData.data.length * ratio.value));
- nextTick(() => {
- init();
- });
- });
- };
- const clickCancel = () => {
- const useTagsStore = useTagsViewStore();
- useTagsStore.delVisitedView(router.currentRoute.value);
- router.replace({ path: "/sell/draft-design/draft-design-management" });
- };
- const clickSubmit = () => {
- if (!formData.data.proofingImg) {
- return ElMessage("请上传设计样图");
- }
- if (!formData.data.productionDocument) {
- return ElMessage("请上传图稿文件");
- }
- proxy.post("/orderSkuArtworkMake/edit", formData.data).then(() => {
- ElMessage({ message: "提交成功", type: "success" });
- const useTagsStore = useTagsViewStore();
- useTagsStore.delVisitedView(router.currentRoute.value);
- router.replace({ path: "/sell/draft-design/draft-design-management" });
- });
- };
- </script>
- <style lang="scss" scoped>
- :deep(.el-table__cell) {
- vertical-align: top;
- }
- ::v-deep(.image-slot) {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- background: var(--el-scale-down-color-light);
- color: var(--el-text-color-secondary);
- font-size: 30px;
- }
- ::v-deep(.image-slot .el-icon) {
- font-size: 30px;
- }
- ::v-deep(.tableHeader th) {
- .cell {
- line-height: 35px !important;
- }
- }
- :deep(.el-dialog) {
- margin-top: 10px !important;
- margin-bottom: 10px !important;
- }
- ::v-deep(.el-dialog__body) {
- padding: 10px !important;
- }
- </style>
|