Commit a19250fe by qinjianhui

feat: 手动排版时接口超时显示进度,提示用户已下载成功了

parent 362269f4
......@@ -148,7 +148,7 @@ export default {
fs.writeFileSync(p, JSON.stringify(data));
res.json({ code: 200 });
} catch (e) {
res.json({ code: 500, msg: e });
res.json({ code: 500, msg: e.message });
}
},
......@@ -242,7 +242,7 @@ export default {
res.setHeader("Act-Request-URL", `${env}/${url}`);
res.json(data);
} catch (err) {
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
getCompanyList: async (req, res) => {
......@@ -254,7 +254,7 @@ export default {
res.send(data);
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
commitApply: async () => {},
......@@ -287,7 +287,7 @@ export default {
res.send(data);
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
downloadByDesignId: async (req, res) => {
......@@ -323,7 +323,7 @@ export default {
});
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
uploadImage: async (req, res) => {
......@@ -354,7 +354,7 @@ export default {
});
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
getPngImg: async (req, res) => {
......@@ -384,7 +384,7 @@ export default {
});
}
} catch (err) {
res.send({ code: 500, msg: err });
res.send({ code: 500, msg: err.message });
}
},
login: async (req, res) => {
......@@ -395,7 +395,7 @@ export default {
res.send(data);
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
toPrint: (req, res) => {
......@@ -409,7 +409,7 @@ export default {
res.send({ code: 200, msg: "操作成功" });
})
.catch(err => {
res.send({ code: 500, msg: err });
res.send({ code: 500, msg: err.message });
});
},
checkUpdate: async (req, res) => {
......@@ -430,7 +430,7 @@ export default {
}
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
......@@ -558,7 +558,7 @@ export default {
});
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
//获取国家名称及代码
......@@ -572,7 +572,7 @@ export default {
res.send(data);
} catch (error) {
console.log(error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//复制文件夹下文件
......@@ -583,7 +583,7 @@ export default {
res.json({ code: 200 });
} catch (error) {
console.log(error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
......@@ -616,7 +616,7 @@ export default {
res.json({ code: 200, data: result });
} catch (err) {
console.log(err);
res.json({ code: 500, msg: err });
res.json({ code: 500, msg: err.message });
}
},
scanProduce: async (req, res) => {
......@@ -638,7 +638,7 @@ export default {
res.send(data);
} catch (error) {
console.log("scanProduce", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
checkInventory: async (req, res) => {
......@@ -659,7 +659,7 @@ export default {
res.send(data);
} catch (error) {
console.log("checkInventory", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
createBatchFolder: async (req, res) => {
......@@ -670,7 +670,7 @@ export default {
res.send(data);
} catch (error) {
console.log("createBatchFolder", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
......@@ -688,7 +688,7 @@ export default {
res.send(data);
} catch (error) {
console.log("productionConfig", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//更新生产配置
......@@ -705,7 +705,7 @@ export default {
res.send(data);
} catch (error) {
console.log("productionConfigUpdate", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//排版列表(分页)
......@@ -722,7 +722,7 @@ export default {
res.send(data);
} catch (error) {
console.log("productionSoftware", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//查询该批次下的所有素材
......@@ -740,7 +740,7 @@ export default {
res.send(data);
} catch (error) {
console.log("listMaterialsByBatch", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//重新合成排版
......@@ -758,7 +758,7 @@ export default {
res.send(data);
} catch (error) {
console.log("psReComposingDesignImages", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
//根据批次分页查询操作单
......@@ -775,7 +775,7 @@ export default {
res.send(data);
} catch (error) {
console.log("listOperationByBatch", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
replenishmentComposingDesignImages: async (req, res) => {
......@@ -807,7 +807,7 @@ export default {
res.json({ code: 200, data: result });
} catch (error) {
console.log("replenishmentComposingDesignImages", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
podOrderBatchDownload: async (req, res) => {
......@@ -824,7 +824,7 @@ export default {
res.json({ code: 200, data });
} catch (error) {
console.log("podOrderBatchDownload", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
},
getPodOrderLog: async (req, res) => {
......@@ -842,7 +842,7 @@ export default {
res.json({ code: 200, data });
} catch (error) {
console.log("getPodOrderLog", error);
res.json({ code: 500, msg: error });
res.json({ code: 500, msg: error.message });
}
}
};
......@@ -30,11 +30,11 @@ service.interceptors.request.use(
if (user) {
config.headers["jwt-token"] = user.token;
}
startLoading();
if (!config.skipLoading) startLoading();
return config;
},
error => {
endLoading();
if (!error.config?.skipLoading) endLoading();
// do something with request error
return Promise.reject(error);
}
......@@ -45,7 +45,7 @@ service.interceptors.response.use(
async response => {
// do something with response data
const res = response.data;
endLoading();
if (!response.config.skipLoading) endLoading();
if (res.code) {
if (res.code !== 200) {
if (res.code === 411 || res.code === 205 || res.code === 301) {
......@@ -82,7 +82,7 @@ service.interceptors.response.use(
return Promise.resolve(res);
},
error => {
endLoading();
if (!error.config?.skipLoading) endLoading();
let errMsg = "请求失败,请稍后重试";
// 1. 有服务端响应:提取后端返回的 msg
......
......@@ -16,6 +16,7 @@ import {
getTypesettingDownloadStatus,
onTypesettingStatusChange
} from "@/utils/download-api";
import { startLoading, endLoading } from "@/utils/axios";
export default {
name: "design-dtf",
......@@ -41,7 +42,9 @@ export default {
isDownloading: false,
TypeDownloading: false,
dtfSetting: getDTFSetting(),
removeStatusListener: null, // 保存取消监听函数
removeStatusListener: null,
composingTasks: [],
composingPollTimer: null,
pageInfo: {
currentPage: 1,
......@@ -120,6 +123,7 @@ export default {
}
};
},
computed: {},
async mounted() {
const savedPage = Number(this.$route.query.page);
if (savedPage > 0) {
......@@ -139,7 +143,8 @@ export default {
beforeDestroy() {
if (this.removeStatusListener) this.removeStatusListener();
if (this.removeListener) this.removeListener();
// 取消待执行的防抖任务,避免销毁后执行报错
this.stopComposingPoll();
this.composingTasks.forEach(t => clearTimeout(t.dismissTimer));
this.downloadFile.cancel?.();
},
methods: {
......@@ -232,6 +237,10 @@ export default {
//打开手动排版弹窗
changeTypesetting(value) {
if (this.TypeDownloading) {
this.$message.warning("其他排版下载中,请等待完成后再操作");
return;
}
if (![2].includes(value.layoutStatus)) {
this.dialogVisible = true;
this.radio = "";
......@@ -242,63 +251,166 @@ export default {
//提交手动排版
async submit() {
if (this.TypeDownloading) {
this.$message.warning("其他排版下载中,请等待完成后再操作");
return;
}
if (!this.radio) {
return this.$message.error("请选择排版宽度");
}
try {
this.TypeDownloading = true;
if (this.dtfSetting.widthStrategy == 3 && this.radio == 60) {
this.$confirm(
if (this.dtfSetting.widthStrategy == 3 && this.radio == 60) {
try {
await this.$confirm(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版",
"提示",
{
confirmButtonText: "确定",
type: "warning"
}
{ confirmButtonText: "确定", type: "warning" }
);
await this.$api.post(pathMap["psReComposingDesignImages"], {
batchArrangeNum: this.templateBatchArrangeNum,
templateWidth: this.radio
});
} else {
await this.$api.post(pathMap["psReComposingDesignImages"], {
} catch {
return;
}
}
const taskId = this.templateBatchArrangeNum;
const templateId = this.templateId;
startLoading();
try {
await this.$api.post(
pathMap["psReComposingDesignImages"],
{
batchArrangeNum: this.templateBatchArrangeNum,
templateWidth: this.radio
});
},
{ skipLoading: true }
);
} catch (error) {
console.warn("排版请求超时:", error.message);
}
endLoading();
this.dialogVisible = false;
const task = {
id: taskId,
batchArrangeNum: taskId,
templateId: templateId,
step: "composing",
progress: "正在检查排版状态...",
dismissTimer: null
};
this.composingTasks = this.composingTasks.filter(t => t.id !== taskId);
this.composingTasks.push(task);
const batch = await this.fetchBatchByNo(taskId);
this.checkTaskStatus(task, batch);
this.fetchBatchList(this.searchForm);
},
async fetchBatchByNo(batchArrangeNum) {
try {
const res = await this.$api.post(
pathMap["productionSoftware"],
{ currentPage: 1, pageSize: 1, batchArrangeNum },
{ skipLoading: true }
);
const list = res.code === 200 ? res.data.records || [] : [];
return list[0] || null;
} catch {
return null;
}
},
checkTaskStatus(task, batch) {
if (batch && [3, 5].includes(batch.layoutStatus)) {
task.step = "downloading";
task.progress = "排版完成,正在下载素材...";
this.doComposingDownload(task.id);
} else if (batch && batch.layoutStatus === 4) {
task.step = "error";
task.progress = "排版失败:" + (batch.failReason || "未知原因");
} else {
task.progress = "排版处理中,等待完成后自动下载...";
this.ensureComposingPoll();
}
},
getTask(taskId) {
return this.composingTasks.find(t => t.id === taskId);
},
setTaskDone(taskId, progress) {
const task = this.getTask(taskId);
if (!task) return;
task.step = "done";
task.progress = progress;
task.dismissTimer = setTimeout(() => this.removeTask(taskId), 5000);
},
removeTask(taskId) {
const task = this.getTask(taskId);
if (task) clearTimeout(task.dismissTimer);
this.composingTasks = this.composingTasks.filter(t => t.id !== taskId);
if (!this.composingTasks.some(t => t.step === "composing")) {
this.stopComposingPoll();
}
},
ensureComposingPoll() {
if (this.composingPollTimer) return;
this.composingPollTimer = setInterval(
() => this.pollComposingStatus(),
15000
);
},
stopComposingPoll() {
if (this.composingPollTimer) {
clearInterval(this.composingPollTimer);
this.composingPollTimer = null;
}
},
async pollComposingStatus() {
const polling = this.composingTasks.filter(t => t.step === "composing");
if (polling.length === 0) {
this.stopComposingPoll();
return;
}
try {
for (const task of polling) {
const batch = await this.fetchBatchByNo(task.batchArrangeNum);
this.checkTaskStatus(task, batch);
}
if (!this.composingTasks.some(t => t.step === "composing")) {
this.stopComposingPoll();
}
} catch (err) {
console.error("轮询排版状态失败", err);
}
},
async doComposingDownload(taskId) {
const task = this.getTask(taskId);
if (!task) return;
try {
this.TypeDownloading = true;
const { data } = await this.$api.post(
pathMap["podOrderBatchDownload"],
{
id: this.templateId
}
{ id: task.templateId },
{ skipLoading: true }
);
const res = await submitTypesettingDownload({
await submitTypesettingDownload({
urlStr: data.data.spUrl,
targetPath: path.join(
getDTFSetting().downloadDir,
`批次号:${this.templateBatchArrangeNum}`,
`批次号:${task.batchArrangeNum}`,
"手动排版素材"
),
batchArrangeNum: this.templateBatchArrangeNum
batchArrangeNum: task.batchArrangeNum
});
console.log(res);
if (res.code == 200) {
this.$message(res.msg);
} else {
this.TypeDownloading = false;
this.$message.error(res.msg);
}
this.dialogVisible = false;
this.fetchBatchList(this.searchForm);
task.progress = "素材下载中...";
} catch (error) {
console.log(error);
console.error(error);
this.TypeDownloading = false;
if (task) {
task.step = "error";
task.progress = "下载失败:" + (error.message || "未知错误");
}
}
},
......@@ -355,9 +467,7 @@ export default {
),
batchArrangeNum: value
});
if (res.code == 200) {
this.$message(res.msg);
} else {
if (res.code !== 200) {
this.isDownloading = false;
this.$message.error(res.msg);
}
......@@ -379,6 +489,20 @@ export default {
if (!status.isRunning && status.result) {
const { success, failed } = status.result;
const { currentBatchNo } = status;
const task =
type === "typesetting" &&
this.composingTasks.find(t => t.batchArrangeNum === currentBatchNo);
if (task) {
const msg = failed.length
? `下载完成:成功 ${success.length} 个,失败 ${failed.length} 个`
: `下载完成:成功 ${success.length} 个文件`;
this.setTaskDone(task.id, msg);
this.fetchBatchList(this.searchForm);
return;
}
this.$message.success(
`批次号:${currentBatchNo},下载完成:成功 ${success.length} 个,失败 ${failed.length} 个`
);
......@@ -388,6 +512,18 @@ export default {
}
},
stepActive(task) {
if (task.step === "composing") return 0;
if (task.step === "downloading") return 1;
if (task.step === "done") return 3;
return task.progress.includes("排版") ? 0 : 1;
},
stepStatus(task) {
if (task.step === "error") return "error";
if (task.step === "done") return "success";
return "process";
},
/**
* 同步当前状态
*/
......@@ -507,6 +643,32 @@ export default {
<el-button type="primary" @click="submit">确 定</el-button>
</span>
</el-dialog>
<!-- 排版进度浮层(多任务) -->
<transition-group name="composing-slide" tag="div" class="composing-panels">
<div
v-for="task in composingTasks"
:key="task.id"
class="composing-progress-panel"
>
<div class="cp-header">
<span class="cp-batch">批次号:{{ task.batchArrangeNum }}</span>
<i class="el-icon-close cp-close" @click="removeTask(task.id)"></i>
</div>
<el-steps
:active="stepActive(task)"
:process-status="stepStatus(task)"
finish-status="success"
size="mini"
class="cp-steps"
>
<el-step title="排版"></el-step>
<el-step title="下载"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<div v-if="task.progress" class="cp-msg">{{ task.progress }}</div>
</div>
</transition-group>
</div>
</template>
......@@ -541,4 +703,70 @@ export default {
filter: grayscale(100%);
opacity: 0.3;
}
.composing-panels {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 2001;
display: flex;
flex-direction: column-reverse;
gap: 10px;
}
.composing-progress-panel {
width: 360px;
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
padding: 14px 16px 10px;
}
.cp-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.cp-batch {
font-size: 13px;
font-weight: 600;
color: #303133;
}
.cp-close {
cursor: pointer;
color: #909399;
font-size: 14px;
&:hover {
color: #606266;
}
}
.cp-steps {
margin-bottom: 6px;
}
::v-deep .cp-steps {
.el-step__title {
font-size: 12px;
line-height: 20px;
}
.el-step__icon {
width: 18px;
height: 18px;
}
.el-step__icon-inner {
font-size: 10px;
}
}
.cp-msg {
font-size: 12px;
color: #909399;
padding-left: 2px;
}
.composing-slide-enter-active,
.composing-slide-leave-active {
transition: all 0.3s ease;
}
.composing-slide-enter,
.composing-slide-leave-to {
opacity: 0;
transform: translateY(20px);
}
</style>
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