Commit 98eefb15 by linjinhong

feat:对接详情页排版和下载素材

parent 67b322c3
......@@ -390,12 +390,13 @@ async function createWindow() {
if (newWindow) newWindow.webContents.send("getProductionNoInfo", v);
});
ipcMain.on("request-select-folder", event => {
ipcMain.on("request-select-folder", (event, currentPath) => {
// 打开文件夹选择对话框(Electron 6.x 回调写法,更稳定)
dialog.showOpenDialog(
{
properties: ["openDirectory"], // 仅允许选择文件夹
title: "选择下载保存位置"
title: "选择下载保存位置",
defaultPath: currentPath || ""
},
result => {
// 处理结果并返回给渲染进程
......@@ -513,12 +514,34 @@ async function createWindow() {
const { width, height } = win.getBounds();
win.webContents.send("window-size", { width, height });
});
// IPC 通信
ipcMain.on("download:submit-batch-list", (event, list) => {
downloadQueue.addBatchList(list);
event.returnValue = { code: 200, msg: "已处理" };
});
ipcMain.on("get-install-path", event => {
let psScriptPath;
if (process.env.NODE_ENV === "development") {
psScriptPath = __dirname;
} else {
psScriptPath = path.dirname(app.getPath("exe"));
}
const dtfDir = path.join(psScriptPath, "DTF");
try {
// 2. 确保目录存在:不存在则递归创建,已存在则直接跳过,不会报错
fs.mkdirSync(dtfDir, { recursive: true });
// 3. 返回 DTF 目录路径
event.returnValue = dtfDir;
} catch (err) {
console.error("创建 DTF 文件夹失败:", err);
// 创建失败时兜底返回安装根目录,避免渲染进程拿不到值报错
event.returnValue = psScriptPath;
}
});
ipcMain.on("download:get-status", event => {
try {
// 无论正常异常都保证回复,避免前端挂起
......
......@@ -39,7 +39,7 @@
<slot name="time"></slot>
<!-- 名称和价格 -->
<div
<!-- <div
v-if="cardItem?.productName"
class="commodity-card-name-price flex flex-justify-space-between"
>
......@@ -47,7 +47,7 @@
<span>{{ cardItem?.productName }}</span>
</slot>
<slot name="price"></slot>
</div>
</div> -->
<!-- SKU信息 -->
<div
......@@ -124,7 +124,23 @@ export default {
return true;
},
mainImageSrc() {
return "https://tcustom.jomalls.com/shengchengtu/xiaoguotu/20260603/1780453208357TTp79stDPWRzooRZ.jpg";
const item = this.cardItem;
// 使用索引访问避免联合类型的属性访问问题
if (
this.imageField === "variantImage" &&
typeof item[this.imageField] === "string"
) {
return item[this.imageField];
}
if (
this.imageField === "mainImage" &&
typeof item[this.imageField] === "string"
) {
return item[this.imageField];
}
// 默认返回空字符串
return "";
}
},
methods: {}
......
......@@ -213,8 +213,8 @@ export default {
}
},
mounted() {
console.log("tableConfig", this.tableConfig);
console.log("tableData", this.tableData);
// console.log("tableConfig", this.tableConfig);
// console.log("tableData", this.tableData);
}
};
</script>
......
......@@ -6,13 +6,37 @@
:before-close="handleClose"
:show-close="true"
>
<div class="order-detail-container" style="display: flex; gap: 32px;">
<div class="order-detail-container" style="display: flex;height: 800px;">
<!-- 左侧图片区域 -->
<div class="left-img" style="width: 36%; text-align: center;">
<p style="font-size: 22px; font-weight: bold; margin: 0 0 10px;">B</p>
<div style="background-color: #f9f7ee; padding: 45px 20px;">
<img :src="imgUrl" alt="成衣图片" style="width: 85%;" />
</div>
<div class="left-images" style="width: 50%; text-align: center;">
<el-carousel
v-if="parseImageAry(info.imageAry).length > 0"
style="height: 100%"
:autoplay="false"
>
<el-carousel-item
v-for="(item, index) in parseImageAry(info.imageAry)"
:key="index"
style="height: 100%"
>
<div class="left-image">
<b v-show="item?.title && item?.url">
{{ item.title }}
<span
v-if="item?.id"
style="
text-decoration: underline;
cursor: pointer;
color: blue;
"
>
(DID:{{ item.id }}
</span>
</b>
<img :src="item.url" alt="" />
</div>
</el-carousel-item>
</el-carousel>
</div>
<!-- 右侧信息区域 -->
......@@ -22,51 +46,42 @@
class="customer-order-row"
style="display: flex; justify-content: space-between; border: 1px solid #e4e7ed; padding: 15px 20px; margin-bottom: 18px;"
>
<div>
<span style="color: #606266;">客户:</span>
<span style="color: #f56c6c; font-size: 18px; font-weight: bold;">{{
info.customer
}}</span>
<div class="text-label">
<span>客户:</span>
<span style="color: #f56c6c;">{{ info.customer }}</span>
</div>
<div>
<span style="color: #606266;">订单号:</span>
<span style="color: #f56c6c; font-size: 18px; font-weight: bold;">{{
info.orderNo
}}</span>
<div class="text-label">
<span>订单号:</span>
<span style="color: #f56c6c;">{{ info.orderNo }}</span>
</div>
</div>
<!-- 操作单信息区块 -->
<div class="div-text"><b>操作单信息</b></div>
<div
class="op-info-box"
class="info-box"
style="border: 1px solid #e4e7ed; padding: 18px 22px; margin-bottom: 26px;"
>
<h4 style="margin: 0 0 18px; font-size: 16px;">操作单信息</h4>
<div
style="display: grid; grid-template-columns: 1fr 1fr; row-gap: 14px;"
>
<div>
<span style="color: #909399;">操作单号:</span>{{ info.opNo }}
</div>
<div>
<span style="color: #909399;">生产工艺:</span>{{ info.tech }}
<div class="text-label">
<span>操作单号:</span>{{ info.operationNo }}
</div>
<div>
<span style="color: #909399;">基版:</span>{{ info.basePlate }}
<div class="text-label">
<span>生产工艺:</span>{{ info.craftName }}
</div>
<div>
<span style="color: #909399;">变体SKU:</span>{{ info.sku }}
<div class="text-label"><span>基版:</span>{{ info.baseSku }}</div>
<div class="text-label">
<span>变体SKU:</span>{{ info.variantSku }}
</div>
<div><span style="color: #909399;">数量:</span>{{ info.num }}</div>
<div>
<span style="color: #909399;">尺寸:</span>{{ info.size }}
<div class="text-label"><span>数量:</span>{{ info.quantity }}</div>
<div class="text-label"><span>尺寸:</span>{{ info.size }}</div>
<div class="text-label">
<span>店铺单号:</span>{{ info.shopNumber }}
</div>
<div>
<span style="color: #909399;">店铺单号:</span>{{ info.shopNo }}
</div>
<div>
<span style="color: #909399;">创建时间:</span
>{{ info.createTime }}
<div class="text-label">
<span>创建时间:</span>{{ info.createTime }}
</div>
</div>
</div>
......@@ -74,11 +89,6 @@
<!-- 底部按钮 -->
<div style="text-align: right;">
<el-button type="primary" size="medium">下载素材</el-button>
<el-checkbox
v-model="scanDownloadCheck"
label="扫码下载素材"
style="margin-left: 10px;"
/>
</div>
</div>
</div>
......@@ -94,26 +104,10 @@ export default {
type: Boolean,
default: false
},
// 图片地址
imgUrl: {
type: String,
default: ""
},
// 订单详情数据
info: {
type: Object,
default: () => ({
customer: "",
orderNo: "",
opNo: "",
tech: "",
basePlate: "",
sku: "",
num: "",
size: "",
shopNo: "",
createTime: ""
})
default: () => ({})
}
},
computed: {
......@@ -134,7 +128,92 @@ export default {
methods: {
handleClose() {
this.$emit("update:show", false);
},
parseImageAry(imageAry) {
if (typeof imageAry !== "string" || !imageAry.trim()) return [];
const trimmed = imageAry.trim();
if (trimmed.startsWith("http")) {
return trimmed
.split(",")
.map(url => ({ url: url.trim() }))
.filter(item => item.url);
}
try {
const parsed = JSON.parse(trimmed);
if (!Array.isArray(parsed)) return [{ url: trimmed }];
const result = [];
for (const item of parsed) {
if (typeof item === "string" && item) {
result.push({ url: item });
} else if (item && typeof item === "object" && "url" in item) {
const url = item.url;
if (url) {
result.push({
url,
title: item.title
});
}
}
}
return result;
} catch {
return [{ url: trimmed }];
}
}
}
};
</script>
<style scoped lang="less">
.left-images {
display: flex;
width: 95%;
height: 100%;
flex-direction: column;
b {
color: black;
text-align: center;
margin-bottom: 15px;
}
.left-image {
display: flex;
height: 100%;
flex-direction: column;
justify-content: center;
img {
height: auto;
width: 100%;
max-height: 90%;
object-fit: contain;
}
b {
color: black;
font-size: 18px;
margin-bottom: 15px;
}
}
}
.div-text {
b {
position: relative;
background: white;
top: 9px;
left: 13px;
padding: 0 10px;
font-size: 22px;
color: black;
z-index: 3;
}
}
.text-label {
flex: 50%;
font-size: 16px;
}
::v-deep.el-carousel {
.el-carousel__container {
height: 100% !important;
}
}
</style>
......@@ -21,7 +21,8 @@ export const pathMap = {
CN: "factory/podJomallOrderProductCn/completeDelivery",
US: "factory/podJomallOrderProductUs/completeDelivery",
GC: "factory/podJomallOrderProduct/completeDelivery",
OP: "factory/podOrderOperation/printerCompleteDelivery"
OP: "factory/podOrderOperation/printerCompleteDelivery",
DTF: "factory/podOrderOperation/completeDelivery"
},
//惠立彩素材下载接口
processTransparentBackground: {
......@@ -48,7 +49,10 @@ export const pathMap = {
psReComposingDesignImages:
"factory/podOrderBatchDownload/psReComposingDesignImages",
//根据批次分页查询操作单
listOperationByBatch: "factory/podOrderBatchDownload/listOperationByBatch"
listOperationByBatch: "factory/podOrderBatchDownload/listOperationByBatch",
//排版
replenishmentComposingDesignImages:
"factory/podOrderOperation/replenishmentComposingDesignImages"
};
// 操作单状态枚举
export const statusMap = {
......
......@@ -4,7 +4,8 @@ import {
toSend,
writeProfileXml,
copySingleImage,
createBatchFolder
createBatchFolder,
downloadDTFFiles
} from "@/server/utils";
const { downloadPngList } = require("@/utils/download-queue");
const {
......@@ -155,6 +156,7 @@ export default {
env = getHostApi().apiApiHost;
fileEnv = getHostApi().fileApiUrl;
const params = req.body;
const targetDir = params.targetDir || "";
const token = req.headers["jwt-token"];
const forceProduction = params.forceProduction || false;
const downloadBySubOrderNumber = pathMap["downloadBySubOrderNumber"];
......@@ -196,20 +198,23 @@ export default {
let files = [path];
files = files.map(el => ({ url: `${fileEnv}${el}` }));
const downloadFunc =
params.device === 2 ? downloadOtherImage : downloadImage;
const result = await downloadFunc(files, {
processDesignA,
processDesignB
});
let result;
if (params.device === 4) {
result = await downloadDTFFiles(files[0].url, targetDir);
} else {
const downloadFunc =
params.device === 2 ? downloadOtherImage : downloadImage;
result = await downloadFunc(files, {
processDesignA,
processDesignB
});
}
console.log("result200", result);
res.json({ code: 200, data: result });
} catch (err) {
console.error("Download error:", err);
res.status(500).json({ code: 500, msg: err });
res.status(500).json({ code: 500, msg: err.message });
}
},
findByPodProductionNo: async (req, res) => {
......@@ -267,7 +272,8 @@ export default {
podJomallOrderUsId: params.podJomallOrderUsId || ""
},
GC: { id: params.id },
OP: { ...params.data }
OP: { ...params.data },
DTF: params.ids
};
let url = pathMap["completeDelivery"][params.orderType];
......@@ -778,5 +784,37 @@ export default {
console.log("checkInventory", error);
res.json({ code: 500, msg: error });
}
},
replenishmentComposingDesignImages: async (req, res) => {
env = getHostApi().apiApiHost;
const token = req.headers["jwt-token"];
const { ids, type, templateWidth, targetDir } = req.body;
let url = pathMap["replenishmentComposingDesignImages"];
try {
let { data } = await axios.post(
`${env}/${url}?type=${type}&templateWidth=${templateWidth}`,
ids,
{
headers: { "jwt-token": token }
}
);
if (data.code !== 200) {
return res.json(data);
}
console.log("data", data);
let path = data.message;
// 转义中文
if (path) {
path = encodeURIComponent(path);
}
let files = [path];
files = files.map(el => ({ url: `${fileEnv}${el}` }));
const result = await downloadDTFFiles(files[0].url, targetDir);
res.json({ code: 200, data: result });
} catch (error) {
console.log("checkInventory", error);
res.json({ code: 500, msg: error });
}
}
};
......@@ -74,6 +74,12 @@ autoRegisterRouter(
"psReComposingDesignImages",
fn.psReComposingDesignImages
);
//重新合成排版
autoRegisterRouter(
router,
"replenishmentComposingDesignImages",
fn.replenishmentComposingDesignImages
);
//根据批次分页查询操作单
autoRegisterRouter(router, "listOperationByBatch", fn.listOperationByBatch);
......
import { exec } from "child_process";
var fs = require("fs");
const https = require("https");
const http = require("http");
var path = require("path");
var request = require("request");
var uuid = require("uuid");
......@@ -84,9 +85,6 @@ export const downloadImage = (list, isForcedProduction) => {
if (downloadItems.length === 0) {
return resolve(list);
}
console.log("list1111", list);
console.log("downloadItems", downloadItems);
// 并行处理所有下载
const downloadPromises = downloadItems.map(item => {
return new Promise(async itemResolve => {
......@@ -494,19 +492,10 @@ export const writeProfileXml = b => {
const fsPromises = fs.promises; // 明确获取 promises 异步 API
export async function copySingleImage(sourceImagePath, targetDir) {
// 先打印传入的原始路径(方便验证是否和预期一致)
// console.log(`传入参数验证:
// 源图片路径:${sourceImagePath}
// 目标文件夹路径:${targetDir}`);
try {
// 步骤1:路径规范化(修正潜在的分隔符/隐形字符问题)
const normalizedSourcePath = path.resolve(sourceImagePath);
const normalizedTargetDir = path.resolve(targetDir);
// console.log(`路径规范化后:
// 源图片路径:${normalizedSourcePath}
// 目标文件夹路径:${normalizedTargetDir}`);
// 步骤2:校验源文件是否为有效图片文件
let fileStat;
try {
......@@ -521,8 +510,6 @@ export async function copySingleImage(sourceImagePath, targetDir) {
if (!fileStat.isFile()) {
throw new Error(`源路径不是有效文件,是文件夹:${normalizedSourcePath}`);
}
// console.log("校验通过:源路径是有效文件");
// 2.2 校验是否为图片格式(可选,过滤非图片文件)
const validImageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp"];
const fileExt = path.extname(normalizedSourcePath).toLowerCase();
......@@ -533,8 +520,6 @@ export async function copySingleImage(sourceImagePath, targetDir) {
)})`
);
}
// console.log(`校验通过:源文件是图片格式(${fileExt})`);
// 步骤3:获取文件名 + 拼接目标图片完整路径
const imageFileName = path.basename(normalizedSourcePath);
const targetImagePath = path.join(normalizedTargetDir, imageFileName);
......@@ -640,3 +625,160 @@ export async function createBatchList(basePath, batchNoList) {
console.error(`创建完成:成功 ${successCount} 个,跳过 ${skipCount} 个`);
return true;
}
/**
* 格式化当前时间为 YYYY-MM-DD HH-mm-ss
* @returns {string}
*/
function formatTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hour = String(now.getHours()).padStart(2, "0");
const minute = String(now.getMinutes()).padStart(2, "0");
const second = String(now.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day}_${hour}.${minute}.${second}`;
}
/**
* 下载文件(图片/zip)到指定目录,zip格式自动解压
* @param {string} fileUrl - 文件链接(支持png/jpg/zip等)
* @param {string} targetDir - 目标文件夹绝对路径
* @param {object} options - 可选配置
* @param {boolean} options.deleteZipAfterUnzip - 解压后是否删除原zip文件,默认false
* @param {boolean} options.unzipToSubdir - zip是否解压到同名子文件夹,默认false(直接散列到目标目录)
* @returns {Promise<{success: boolean, type: string, filePath: string, fileName: string, unzipDir?: string}>}
*/
export function downloadDTFFiles(fileUrl, targetDir, options = {}) {
const { deleteZipAfterUnzip = true, unzipToSubdir = false } = options;
console.log("downloadDTFFiles---------", fileUrl, targetDir);
return new Promise((resolve, reject) => {
try {
// 1. 解析URL,判断协议
const urlObj = new URL(fileUrl);
const requestLib = urlObj.protocol === "https:" ? https : http;
// 2. 递归创建目标目录
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
// 3. 提取文件名(去除参数/哈希,无后缀默认补.jpg)
let cleanPath = urlObj.pathname;
try {
// 先解码URL编码字符,还原 / 和 \
cleanPath = decodeURIComponent(urlObj.pathname);
} catch (e) {
// 解码失败则使用原始路径兜底
cleanPath = urlObj.pathname;
}
// 统一所有反斜杠为正斜杠,兼容Windows路径格式
cleanPath = cleanPath.replace(/\\/g, "/");
// 提取最后一段:纯文件名(含后缀)
const originalFileName = path.basename(cleanPath);
// 最终命名:时间 + 原文件名,兜底空文件名
const fileName = originalFileName
? `${formatTime()} ${originalFileName}`
: `${formatTime()}.png`;
const savePath = path.join(targetDir, fileName);
// 4. 发起网络请求
const request = requestLib.get(fileUrl, response => {
// 处理301/302重定向
if ([301, 302, 307, 308].includes(response.statusCode)) {
const redirectUrl = response.headers.location;
const finalRedirectUrl = redirectUrl.startsWith("http")
? redirectUrl
: new URL(redirectUrl, fileUrl).href;
downloadDTFFiles(finalRedirectUrl, targetDir, options)
.then(resolve)
.catch(reject);
response.resume();
return;
}
if (response.statusCode !== 200) {
reject(new Error(`下载失败,HTTP状态码:${response.statusCode}`));
return;
}
// 5. 流式写入文件
const writeStream = fs.createWriteStream(savePath);
response.pipe(writeStream);
// 写入完成:判断格式,zip自动解压
writeStream.on("finish", () => {
writeStream.close(async () => {
const fileExt = path.extname(fileName).toLowerCase();
// 非zip文件:直接返回
if (fileExt !== ".zip") {
resolve({
success: true,
type: "image",
filePath: savePath,
fileName: fileName
});
return;
}
// zip文件:执行解压
try {
// 解压目标目录:直接解压到目标目录 / 或解压到同名子文件夹
let unzipTargetDir = targetDir;
if (unzipToSubdir) {
const dirName = path.basename(fileName, ".zip");
unzipTargetDir = path.join(targetDir, dirName);
if (!fs.existsSync(unzipTargetDir)) {
fs.mkdirSync(unzipTargetDir, { recursive: true });
}
}
// 执行解压
await compressing.zip.uncompress(savePath, unzipTargetDir);
// 可选:解压完成后删除原zip文件
if (deleteZipAfterUnzip) {
fs.unlinkSync(savePath);
}
resolve({
success: true,
type: "zip",
filePath: savePath,
fileName: fileName,
unzipDir: unzipTargetDir
});
} catch (unzipErr) {
reject(new Error(`ZIP解压失败:${unzipErr.message}`));
}
});
});
// 写入异常:删除残留文件
writeStream.on("error", err => {
if (fs.existsSync(savePath)) {
fs.unlink(savePath, () => {});
}
reject(new Error(`文件写入失败:${err.message}`));
});
});
// 网络请求异常
request.on("error", err => {
reject(new Error(`网络请求失败:${err.message}`));
});
// 超时控制
request.setTimeout(15000, () => {
request.destroy();
reject(new Error("下载超时,请检查网络或链接有效性"));
});
} catch (err) {
reject(new Error(`参数校验失败:${err.message}`));
}
});
}
......@@ -202,6 +202,12 @@ module.exports = {
},
getIsOver: () => store.get("isOver") || false,
//储存用户信息
setUser: value => {
store.set("userData", value);
},
getUser: () => store.get("userData"),
//本地储存 DTG、DTF生产类型
setProductType: value => {
store.set("productType", value);
......
......@@ -26,6 +26,7 @@ function endLoading() {
service.interceptors.request.use(
config => {
const user = Vue.prototype.$dataStore.get("user");
if (user) {
config.headers["jwt-token"] = user.token;
}
......@@ -82,8 +83,26 @@ service.interceptors.response.use(
},
error => {
endLoading();
// do something with response error
return Promise.reject(error);
let errMsg = "请求失败,请稍后重试";
// 1. 有服务端响应:提取后端返回的 msg
if (error.response && error.response.data) {
const data = error.response.data;
// 优先取后端返回的 msg(适配你后端 {code:500, msg:'xxx'} 的格式)
errMsg =
data.msg || data.message || `请求错误(${error.response.status})`;
}
// 2. 无响应:网络层面错误
else if (error.message) {
if (error.message.includes("timeout")) {
errMsg = "请求超时,请检查网络";
} else if (error.message.includes("Network Error")) {
errMsg = "网络连接异常,请检查网络";
} else {
errMsg = error.message;
}
}
return Promise.reject(new Error(errMsg));
}
);
......
<script>
import PHead from "./head/index.vue";
import PMain from "./main/index.vue";
const { getUser } = require("@/server/utils/store");
export default {
name: "design-dtg",
components: { PHead, PMain },
data() {
return {
user: {},
factoryType: "CN"
factoryType: "CN",
userData: getUser()
};
},
mounted() {},
......@@ -17,7 +20,7 @@ export default {
<template>
<div class="page">
<p-head :user="user" :factoryType="factoryType" />
<p-head :user="userData.user" :factoryType="userData.factoryType" />
<p-main :factoryType="factoryType" />
</div>
</template>
......
<script>
import UpdateDialog from "@/views/design/updateDialog.vue";
import { Loading } from "element-ui";
const { getProductType, setProductType } = require("@/server/utils/store");
const {
getProductType,
setProductType,
setUser
} = require("@/server/utils/store");
export default {
components: { UpdateDialog },
......@@ -24,7 +28,7 @@ export default {
this.$refs.updateDialog?.open(data);
}
});
setUser({ user: this.user, factoryType: this.factoryType });
const routerName = this.$router.currentRoute.name;
const routerMap = { DTG: "design-dtg", DTF: "design-dtf" };
......
......@@ -2,28 +2,31 @@
const { getDTFSetting } = require("@/server/utils/store");
const { ipcRenderer } = require("electron");
const { statusMap } = require("@/config/index.js");
const { pathMap } = require("@/config/index.js");
const { getUser } = require("@/server/utils/store");
var path = require("path");
export default {
props: {
user: {
default: () => ({
avatar: "",
factory: {}
}),
type: Object
},
factoryType: { type: String, default: "CN" }
selection: { type: Array, default: () => [] },
podOrderProductIds: { type: Array, default: () => [] }
},
data() {
return {
dialogVisible: false,
dtfSetting: getDTFSetting(),
radio: "",
curBatchArrangeNum: "",
searchForm: {},
statusMap: statusMap
statusMap: statusMap,
userData: getUser()
};
},
computed: {},
mounted() {},
mounted() {
const params = this.$route.params;
this.curBatchArrangeNum = params.batchArrangeNum;
},
created() {},
methods: {
......@@ -61,65 +64,111 @@ export default {
goBack() {
this.$router.push({ name: "design-dtf" });
},
goSetting() {},
async goSetting() {
if (!this.podOrderProductIds.length) {
return this.$message.warning("请选择操作单");
}
try {
let res = await this.$api.post(
pathMap["downloadBySubOrderNumber"]["OP"],
{
ids: this.podOrderProductIds,
device: 4,
orderType: "OP",
targetDir: path.join(
getDTFSetting().downloadDir,
`批次号:${this.curBatchArrangeNum}`,
"原素材"
)
}
);
console.log(77, res);
} catch (error) {
this.$message.error(error);
}
},
getDataInfo() {
this.$emit("search", this.searchForm);
},
productionCompleted() {
this.$confirm("确认完成生产?", "提示", {
if (!this.selection.length) {
return this.$message.warning("请选择操作单");
}
this.$confirm(`确定生产完成?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {})
.catch(() => {});
}).then(async () => {
await this.$api.post(pathMap.completeDelivery["DTF"], {
orderType: "DTF",
ids: this.selection
});
});
},
changeTypesetting() {
if (!this.selection.length) {
return this.$message.warning("请选择操作单");
}
this.dialogVisible = true;
this.radio = "";
},
async submit() {
if (!this.radio) {
return this.$message.error("请选择排版宽度");
}
try {
if (!this.radio) {
return this.$message.error("请选择排版宽度");
}
const payload = {
ids: this.selection,
type: "png",
templateWidth: this.radio,
targetDir: path.join(
getDTFSetting().downloadDir,
`批次号:${this.curBatchArrangeNum}`,
"手动排版素材"
)
};
if (this.dtfSetting.widthStrategy == 3 && this.radio == 60) {
this.$confirm(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版",
"提示",
{
confirmButtonText: "确定",
type: "warning"
}
);
const { data } = await this.$api.post(
pathMap["replenishmentComposingDesignImages"],
payload
);
console.log("submit", data);
if (this.dtfSetting.widthStrategy == 3 && this.radio == 60) {
this.$confirm(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版",
"提示",
{
confirmButtonText: "确定",
type: "warning"
}
);
await this.$api.post("/psReComposingDesignImages", {
batchArrangeNum: this.templateBatchArrangeNum,
templateWidth: this.radio
});
this.$message(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
} else {
await this.$api.post("/psReComposingDesignImages", {
batchArrangeNum: this.templateBatchArrangeNum,
templateWidth: this.radio
});
this.$message(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
this.$message.success("DTF排版成功,请到手动排版素材中查看");
} else {
const { data } = await this.$api.post(
pathMap["replenishmentComposingDesignImages"],
payload
);
console.log("submit", data);
this.$message.success("DTF排版成功,请到手动排版素材中查看");
}
this.dialogVisible = false;
this.getDataInfo();
} catch (error) {
console.log("submit", error);
}
},
async goFliePage(value, type) {
console.log(value);
async goFliePage() {
let { data } = await this.$api.post("/createBatchFolder", {
basePath: this.dtfSetting.downloadDir,
downloadDir: value.batchNo,
downloadDir: this.curBatchArrangeNum,
isArray: false
});
console.log("goFliePage", data);
if (type == "check") {
ipcRenderer.send("open-folder-only", data);
if (!data) {
return this.$message.error("下载文件目录未配置或者目录错误");
}
ipcRenderer.send("open-folder-only", data);
}
}
};
......@@ -141,6 +190,7 @@ export default {
<el-input
placeholder="请扫描操作单"
v-model="searchForm.operationNo"
clearable
></el-input
></el-form-item>
<el-form-item label="操作单状态">
......@@ -153,9 +203,9 @@ export default {
></el-option> </el-select
></el-form-item>
<el-form-item label="操作单排版状态">
<el-select v-model="searchForm.layout" placeholder="请选择">
<el-option label="" :value="true"></el-option>
<el-option label="" :value="false"> </el-option> </el-select
<el-select v-model="searchForm.layout" placeholder="请选择" clearable>
<el-option label="排版成功" :value="true"></el-option>
<el-option label="排版失败" :value="false"> </el-option> </el-select
></el-form-item>
<el-form-item>
......@@ -187,21 +237,23 @@ export default {
<div class="right-user">
<div
v-if="user && user.factory"
v-if="userData && userData.factory"
style="font-weight: 700;margin-left: 8px;"
>
{{
user.factory.countryCode
? `${user.factory.countryCode}${getCountryName(
user.factory.countryCode
userData.user.countryCode
? `${userData.user.countryCode}${getCountryName(
userData.user.countryCode
)}`
: "CN(中国)"
}}
</div>
<p v-if="user && user.factory">{{ user.factory.title }}</p>
<p v-if="userData.user && userData.user.factory">
{{ userData.user.factory.title }}
</p>
<el-dropdown @command="dropdownCommand">
<div style="margin-right: 20px">
<b style="cursor:pointer;">{{ user.account }}</b>
<b style="cursor:pointer;">{{ userData.user.account }}</b>
<i class="el-icon-arrow-down"></i>
</div>
......@@ -213,7 +265,7 @@ export default {
</div>
<el-dialog title="排版宽度" :visible.sync="dialogVisible" width="25%">
<el-radio-group v-model="radio">
<el-radio :label="40">40+2cm</el-radio>
<el-radio :label="42">40+2cm</el-radio>
<el-radio :label="60">60cm</el-radio>
</el-radio-group>
<span slot="footer" class="dialog-footer">
......
......@@ -26,42 +26,55 @@ export default {
return {
logVisible: false,
detailVisible: false,
curData: {},
logList: [],
selection: [],
podOrderProductIds: [],
pageInfo: {
currentPage: 1,
pageSize: 10,
total: 2
},
cardList: [],
curBatchArrangeNum: "",
detailRow: {
batchArrangeNum: { label: "批次号", value: 111 },
operationNum: { label: "操作单数量", value: 111 },
materialNum: { label: "素材数量", value: 111 },
craftType: { label: "工艺类型", value: 111 },
standardDesignImage: { label: "规范素材", value: 111 },
createTime: { label: "创建时间", value: 111 },
layoutStatus: { label: "排版状态", value: 111 },
status: { label: "操作单状态", value: 111 }
batchArrangeNum: { label: "批次号", value: "" },
operationNum: { label: "操作单数量", value: "" },
materialNum: { label: "素材数量", value: "" },
craftType: { label: "工艺类型", value: "" },
standardDesignImage: { label: "规范素材", value: "" },
createTime: { label: "创建时间", value: "" },
layoutStatus: { label: "排版状态", value: "" },
status: { label: "操作单状态", value: "" }
},
platformJson: platformJson
platformJson: platformJson,
layoutStatus: {
1: { label: "待排版", type: "warning" },
2: { label: "排版中", type: "info" },
3: { label: "排版成功", type: "success" },
4: { label: "排版失败", type: "danger" },
5: { label: "部分成功", type: "success" }
}
};
},
mounted() {
this.loadRowData();
this.curData = {};
this.selection = [];
this.podOrderProductIds = [];
},
methods: {
async loadRowData() {
try {
const params = this.$route.params;
console.log("params", params);
this.curBatchArrangeNum = params.batchArrangeNum;
Object.keys(this.detailRow).forEach(key => {
// 路由里有对应值就赋值,没有则保留默认值
if (params[key] !== undefined) {
this.detailRow[key].value = params[key];
}
});
await this.getCardList({ batchArrangeNumber: params.batchArrangeNum });
await this.getCardList();
} catch (error) {
console.log(error);
}
......@@ -69,7 +82,8 @@ export default {
async getCardList(value) {
let params = {
currentPage: this.pageInfo.currentPage,
pageSize: this.pageInfo.pageSize
pageSize: this.pageInfo.pageSize,
batchArrangeNumber: this.curBatchArrangeNum
};
if (value) {
params = { ...params, ...value };
......@@ -80,6 +94,7 @@ export default {
params
);
this.cardList = data.records;
this.pageInfo.total = data.total;
console.log("listOperationByBatch", data);
} catch (error) {
console.log(error);
......@@ -119,26 +134,34 @@ export default {
{
label: "全部选择",
click: () => {
this.cardList.forEach(el => {
el.active = true;
});
const cardIds = this.cardList.map(el => el.id);
const cardProIds = this.cardList.map(el => el.podOrderProductId);
this.podOrderProductIds = Array.from(
new Set([...this.podOrderProductIds, ...cardProIds])
);
this.selection = Array.from(
new Set([...this.selection, ...cardIds])
);
}
},
{
label: "取消选择",
click: () => {
this.cardList.forEach(el => {
el.active = false;
});
const cardIdSet = new Set(this.cardList.map(el => el.id));
const cardProIdsSet = new Set(
this.cardList.map(el => el.podOrderProductId)
);
this.selection = this.selection.filter(id => !cardIdSet.has(id));
this.podOrderProductIds = this.selection.filter(
id => !cardProIdsSet.has(id)
);
}
}
];
const menu = Menu.buildFromTemplate(template);
menu.popup({ x: e.clientX, y: e.clientY });
},
getItemTags(tagsId) {},
setProductMark(tagsId) {},
parseImageAry(imageAry) {
if (typeof imageAry !== "string" || !imageAry.trim()) return [];
const trimmed = imageAry.trim();
......@@ -170,17 +193,30 @@ export default {
return [{ url: trimmed }];
}
},
openLogDialog(tagsId) {
console.log(999);
openLogDialog() {
this.logVisible = true;
},
changeActive(item) {
item.active = !item.active;
const index = this.selection.indexOf(item.id);
const podIndex = this.podOrderProductIds.indexOf(item.podOrderProductId);
if (podIndex > -1) {
this.podOrderProductIds.splice(podIndex, 1);
} else {
this.podOrderProductIds.push(item.podOrderProductId);
}
if (index > -1) {
this.selection.splice(index, 1);
} else {
this.selection.push(item.id);
}
},
isSelected(item) {
return this.selection.includes(item.id);
},
handleViewDetail(item) {
try {
this.detailVisible = true;
this.curData = item;
} catch (e) {
console.error(e);
}
......@@ -192,7 +228,7 @@ export default {
);
if (item) {
console.log(item.icon);
// console.log(item.icon);
return item.icon.split("/").pop();
}
......@@ -205,7 +241,13 @@ export default {
<template>
<div style="height:100%" @contextmenu.prevent="openMenu">
<div class="page">
<DTFhead :user="user" :factoryType="factoryType"></DTFhead>
<DTFhead
:user="user"
:factoryType="factoryType"
@search="getCardList"
:selection="selection"
:podOrderProductIds="podOrderProductIds"
></DTFhead>
<div class="rowBox">
<div class="rowItem" v-for="(item, index) in detailRow" :key="index">
<div
......@@ -213,7 +255,14 @@ export default {
flex-direction: column; align-items: center;"
>
<div style="min-height: 22px;">{{ item.label }}</div>
<div style="min-height: 22px;">{{ item.value }}</div>
<div style="min-height: 22px;" v-if="item.label == '排版状态'">
<el-tag :type="layoutStatus[item.value]?.type">{{
layoutStatus[item.value]?.label
}}</el-tag>
</div>
<div v-else style="min-height: 22px;">
{{ item.value }}
</div>
</div>
</div>
</div>
......@@ -223,12 +272,12 @@ export default {
class="card-grid-item"
v-for="(item, index) in cardList"
:key="index"
@click.stop="changeActive(item)"
@click="changeActive(item)"
>
<BaseCard
style="cursor: pointer;"
:card-item="item"
:active="item.active"
:active="isSelected(item)"
:show-sku="false"
:show-product-info="false"
:image-field="'variantImage'"
......@@ -238,7 +287,7 @@ export default {
<span
class="operation-number"
:title="`操作单号:${item.operationNo}`"
@click.stop="() => {}"
@click.stop="copyText(item.operationNo || '')"
>
{{ item.operationNo }}
</span>
......@@ -345,9 +394,9 @@ export default {
<div class="card-info-row full">
<span
class="info-value ellipsis"
:title="item.operationNo || ''"
:title="item.productName || ''"
>
{{ item.operationNo }}
{{ item.productName }}
</span>
</div>
<div class="card-info-row">
......@@ -433,7 +482,7 @@ export default {
small
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageInfo.pageNum"
:current-page="pageInfo.currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
......@@ -450,11 +499,7 @@ export default {
<LogList v-if="logList.length" :log-list="logList" />
<div v-else class="empty-content">暂无数据</div>
</el-dialog>
<DetailDialog
:show.sync="detailVisible"
:img-url="''"
:info="{}"
></DetailDialog>
<DetailDialog :show.sync="detailVisible" :info="curData"></DetailDialog>
</div>
</template>
......
<script>
const { getUser } = require("@/server/utils/store");
export default {
props: {
user: {
default: () => ({
avatar: "",
factory: {}
}),
type: Object
},
factoryType: { type: String, default: "CN" }
},
data() {
return {
searchForm: { batchArrangeNumber: "", layoutStatus: "", startTime: "" }
searchForm: { batchArrangeNumber: "", layoutStatus: "", startTime: "" },
userData: getUser()
};
},
computed: {},
......@@ -81,12 +74,14 @@ export default {
<el-input
v-model="searchForm.batchArrangeNumber"
placeholder="请输入批次号"
clearable
></el-input
></el-form-item>
<el-form-item label="排版状态">
<el-select
v-model="searchForm.layoutStatus"
placeholder="请选择排版状态"
clearable
>
<el-option label="待排版" :value="1"></el-option>
<el-option label="排版中" :value="2"> </el-option>
......@@ -97,6 +92,7 @@ export default {
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
clearable
v-model="searchForm.startTime"
type="datetime"
placeholder="选择创建时间"
......@@ -119,21 +115,23 @@ export default {
<div class="right-user">
<div
v-if="user && user.factory"
v-if="userData.user && userData.user.factory"
style="font-weight: 700;margin-left: 8px;"
>
{{
user.factory.countryCode
? `${user.factory.countryCode}${getCountryName(
user.factory.countryCode
userData.user.countryCode
? `${userData.user.countryCode}${getCountryName(
userData.user.countryCode
)}`
: "CN(中国)"
}}
</div>
<p v-if="user && user.factory">{{ user.factory.title }}</p>
<p v-if="userData.user && userData.user.factory">
{{ userData.user.factory.title }}
</p>
<el-dropdown @command="dropdownCommand">
<div style="margin-right: 20px">
<b style="cursor:pointer;">{{ user.account }}</b>
<b style="cursor:pointer;">{{ userData.user.account }}</b>
<i class="el-icon-arrow-down"></i>
</div>
......
......@@ -23,6 +23,7 @@ export default {
return {
dialogVisible: false,
selectionList: [],
searchForm: {},
radio: "",
templateBatchArrangeNum: "",
pollTimer: null,
......@@ -107,7 +108,7 @@ export default {
},
mounted() {
this.fetchBatchList();
// this.pollTimer = setInterval(() => this.fetchBatchList(), 300000);
this.pollTimer = setInterval(() => this.fetchBatchList(), 300000);
// 单文件下载成功
ipcRenderer.on("download:file-success", (_, data) => {
......@@ -154,13 +155,13 @@ export default {
//定时任务
async fetchBatchList(value) {
// 先判断:有下载任务在跑,本轮直接跳过
console.log("this.isFetching", this.isFetching);
// console.log("this.isFetching", this.isFetching);
if (this.isFetching) return;
this.isFetching = true;
try {
const status = await this.getDownloadStatus();
console.log("status", status);
// console.log("status", status);
if (status.isRunning) return;
let params = {
......@@ -176,7 +177,6 @@ export default {
if (res.code === 200) {
this.tableList = res.data.records || [];
console.log(this.tableList);
this.pageInfo.total = res.data.total;
ipcRenderer.sendSync("download:submit-batch-list", this.tableList);
......@@ -230,7 +230,9 @@ export default {
downloadDir: value.batchArrangeNum,
isArray: false
});
console.log("goFliePage", data);
if (!data) {
return this.$message.error("下载文件目录未配置或者目录错误");
}
if (type == "check") {
ipcRenderer.send("open-folder-only", data);
}
......@@ -238,9 +240,12 @@ export default {
//打开手动排版弹窗
changeTypesetting(value) {
if (this.dtfSetting.autoLayoutStrategy == 3) {
if (
this.dtfSetting.autoLayoutStrategy == 3 ||
[3, 5].includes(value.layoutStatus)
) {
this.dialogVisible = true;
this.templateBatchArrangeNum = value;
this.templateBatchArrangeNum = value.batchArrangeNum;
} else {
this.$message(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
......@@ -279,6 +284,9 @@ export default {
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
}
this.dialogVisible = false;
this.fetchBatchList(this.searchForm);
},
//跳转批次详情页
......@@ -322,7 +330,12 @@ export default {
<DTFhead
:user="user"
:factoryType="factoryType"
@search="fetchBatchList"
@search="
value => {
searchForm = value;
this.fetchBatchList(searchForm);
}
"
></DTFhead>
<BaseTable
v-if="tableList.length"
......@@ -371,7 +384,7 @@ export default {
type="text"
:disabled="scope.row.layoutStatus == 2"
class="btn flex btnBox"
@click="changeTypesetting(scope.row.batchArrangeNum)"
@click="changeTypesetting(scope.row)"
><img
src="../../assets/typesetting.png"
width="30"
......
......@@ -77,6 +77,7 @@
<script>
const { ipcRenderer } = require("electron");
const { setDTFSetting, getDTFSetting } = require("@/server/utils/store");
const { pathMap } = require("@/config/index.js");
export default {
name: "dtf-setting",
......@@ -98,8 +99,13 @@ export default {
// 读取本地配置
async loadLocalConfig() {
try {
const { data } = await this.$api.post("/productionConfig");
const { data } = await this.$api.post(pathMap["productionConfig"]);
this.form = { ...data };
console.log(94, this.form);
if (!this.form.downloadDir) {
const path = ipcRenderer.sendSync("get-install-path");
this.form.downloadDir = path;
}
setDTFSetting({ ...this.form });
} catch (error) {
console.log(error);
......@@ -107,10 +113,11 @@ export default {
},
// 选择文件夹弹窗
selectFolder() {
ipcRenderer.send("request-select-folder");
ipcRenderer.send("request-select-folder", this.form.downloadDir);
ipcRenderer.once("select-folder-result", (_, path) => {
if (path) this.form.downloadDir = path;
});
console.log("selectFolder", this.form);
},
// 保存配置到本地文件
async saveConfig() {
......@@ -124,7 +131,7 @@ export default {
}
);
}
await this.$api.post("/productionConfigUpdate", this.form);
await this.$api.post(pathMap["productionConfigUpdate"], this.form);
setDTFSetting({ ...this.form });
this.$message.success("排版设置保存成功!");
}
......
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