Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
electron-printer
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhuzhequan
electron-printer
Commits
1a6d16c7
Commit
1a6d16c7
authored
Jul 07, 2026
by
linjinhong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:对接后端接口
parent
4fbb5e26
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
756 additions
and
370 deletions
+756
-370
src/background.js
+26
-35
src/components/BaseTable.vue
+3
-15
src/config/index.js
+30
-1
src/server/entity/function.js
+111
-4
src/server/routes/index.js
+13
-0
src/server/utils/store.js
+4
-4
src/utils/download-queue.js
+310
-72
src/views/dtf-batch-detail/components/detailHead.vue
+42
-49
src/views/dtf-batch-detail/detail.vue
+18
-28
src/views/dtf-list/components/head.vue
+43
-52
src/views/dtf-list/index.vue
+128
-66
src/views/dtf-setting/index.vue
+28
-44
No files found.
src/background.js
View file @
1a6d16c7
...
...
@@ -4,8 +4,8 @@ import { app, protocol, BrowserWindow, globalShortcut } from "electron";
import
{
createProtocol
}
from
"vue-cli-plugin-electron-builder/lib"
;
import
{
createServer
}
from
"@/server/index.js"
;
import
{
autoUpdater
}
from
"electron-updater"
;
const
axios
=
require
(
"axios"
);
const
createDownloadQueue
=
require
(
"@/utils/download-queue"
);
const
{
createBatchDownloadQueue
}
=
require
(
"@/utils/download-queue"
);
const
fs
=
require
(
"fs"
);
import
path
from
"path"
;
// 引入 path 模块
...
...
@@ -31,23 +31,32 @@ const winURL =
:
`file://
${
__dirname
}
/index.html`
;
// 全局唯一下载队列,服务启动时初始化一次
const
downloadQueue
=
createDownloadQueue
({
concurrency
:
2
,
const
downloadQueue
=
create
Batch
DownloadQueue
({
concurrency
:
3
,
retryTimes
:
2
,
// 根目录:系统下载目录/排版素材,可自行修改为固定盘符
basePath
:
path
.
join
(
app
.
getPath
(
"downloads"
),
"自动排版素材"
),
// 文件名规则,可自定义
fileNameRule
:
task
=>
`
${
task
.
batchArrangeNum
}
_自动排版素材.zip`
,
basePath
:
""
,
// 单文件下载成功
onSuccess
:
task
=>
{
console
.
log
(
`批次
${
task
.
batchArrangeNum
}
下载完成`
);
win
.
webContents
.
send
(
"download:success"
,
task
.
batchArrangeNum
);
win
?.
webContents
.
send
(
"download:file-success"
,
{
batchArrangeNum
:
task
.
batchArrangeNum
,
fileName
:
task
.
fileName
});
},
// 单文件下载失败
onError
:
(
task
,
err
)
=>
{
console
.
error
(
`批次
${
task
.
batchArrangeNum
}
下载失败:`
,
err
.
message
);
win
.
webContents
.
send
(
"download:error"
,
{
win
?.
webContents
.
send
(
"download:file-error"
,
{
batchArrangeNum
:
task
.
batchArrangeNum
,
fileName
:
task
.
fileName
,
msg
:
err
.
message
});
},
// 批次校验失败(新增:返回所有异常批次)
onBatchInvalid
:
invalidList
=>
{
win
?.
webContents
.
send
(
"download:batch-invalid"
,
invalidList
);
},
// 全部任务完成
onIdle
:
()
=>
{
win
?.
webContents
.
send
(
"download:all-finish"
);
}
});
...
...
@@ -504,31 +513,13 @@ async function createWindow() {
const
{
width
,
height
}
=
win
.
getBounds
();
win
.
webContents
.
send
(
"window-size"
,
{
width
,
height
});
});
// 1. 批量添加下载任务
ipcMain
.
on
(
"download:addTasks"
,
(
event
,
tasks
)
=>
{
const
BASE_URL
=
""
;
// 给每个任务挂载获取下载地址的方法
const
taskList
=
tasks
.
map
(
item
=>
({
...
item
,
fetchUrl
:
async
()
=>
{
const
params
=
{};
const
res
=
await
axios
({
method
:
"get"
,
url
:
`
${
BASE_URL
}
/material/getDownloadUrl`
,
params
})({
batchArrangeNum
:
item
.
batchArrangeNum
});
return
res
.
data
.
downloadUrl
;
}
}));
downloadQueue
.
addTasks
(
taskList
);
event
.
returnValue
=
{
code
:
200
,
msg
:
"已加入队列"
};
// IPC 通信
ipcMain
.
on
(
"download:submit-batch-list"
,
(
event
,
list
)
=>
{
downloadQueue
.
addBatchList
(
list
);
event
.
returnValue
=
{
code
:
200
,
msg
:
"已处理"
};
});
// 2. 查询队列运行状态
ipcMain
.
on
(
"download:getStatus"
,
event
=>
{
ipcMain
.
on
(
"download:get-status"
,
event
=>
{
event
.
returnValue
=
{
isRunning
:
downloadQueue
.
isRunning
};
});
}
...
...
src/components/BaseTable.vue
View file @
1a6d16c7
...
...
@@ -44,18 +44,6 @@
v-html=
"col.render(scope.row, scope.column, scope.$index)"
></span>
<!-- 2. tag标签 -->
<el-tag
v-else-if=
"col.type === 'tag'"
:type=
"col.getTagType ? col.getTagType(scope.row) : 'primary'"
:effect=
"col.tagEffect || 'light'"
size=
"mini"
>
{{
col
.
getTagText
?
col
.
getTagText
(
scope
.
row
)
:
scope
.
row
[
col
.
prop
]
}}
</el-tag>
<!-- 3. 图片 -->
<el-image
v-else-if=
"col.type === 'image'"
...
...
@@ -107,7 +95,7 @@
class=
"table-pagination"
@
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"
...
...
@@ -170,12 +158,12 @@ export default {
},
/**
* 分页信息
* {
pageNum
: 1, pageSize: 10, total: 0 }
* {
currentPage
: 1, pageSize: 10, total: 0 }
*/
pageInfo
:
{
type
:
Object
,
default
:
()
=>
({
pageNum
:
1
,
currentPage
:
1
,
pageSize
:
10
,
total
:
0
})
...
...
src/config/index.js
View file @
1a6d16c7
...
...
@@ -34,7 +34,36 @@ export const pathMap = {
},
checkInventory
:
{
OP
:
"factory/podOrderOperation/check-inventory"
}
},
//烫画
//获取生产配置
productionConfig
:
"api/factory/production-config"
,
//更新生产配置
productionConfigUpdate
:
"/api/factory/production-config/update"
,
//排版列表(分页)
productionSoftware
:
"/api/factory/podOrderBatchDownload/page-production-software"
,
//查询该批次下的所有素材
listMaterialsByBatch
:
"/api/factory/podOrderBatchDownload/list-materials-by-batch"
,
//重新合成排版
psReComposingDesignImages
:
"/api/factory/podOrderBatchDownload/psReComposingDesignImages"
,
//根据批次分页查询操作单
listOperationByBatch
:
"/api/factory/podOrderBatchDownload/listOperationByBatch"
};
// 操作单状态枚举
export
const
statusMap
=
{
待排单
:
"PENDING_SCHEDULE"
,
待拣胚
:
"PENDING_PICK"
,
待补胚
:
"PENDING_REPLENISH"
,
生产中
:
"IN_PRODUCTION"
,
待配货
:
"PENDING_PACKING"
,
配货完成
:
"PACKING_COMPLETED"
,
已完成
:
"COMPLETED"
,
已取消
:
"CANCELLED"
,
已归档
:
"ARCHIVE"
};
export
function
autoRegisterRouter
(
router
,
funcKey
,
handler
)
{
...
...
src/server/entity/function.js
View file @
1a6d16c7
...
...
@@ -6,7 +6,7 @@ import {
copySingleImage
,
createBatchFolder
}
from
"@/server/utils"
;
const
{
downloadPngList
}
=
require
(
"@/utils/download-queue"
);
const
{
cropImageTransparentEdges
,
cropTransparentEdges
,
...
...
@@ -668,12 +668,119 @@ export default {
},
downloadMaterial
:
async
(
req
,
res
)
=>
{
try
{
const
{
downloadDir
,
isArray
,
base
Path
}
=
req
.
body
;
res
.
send
();
const
{
urls
,
target
Path
}
=
req
.
body
;
const
data
=
await
downloadPngList
(
urls
,
targetPath
);
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"downloadMaterial"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//获取生产配置
productionConfig
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
let
url
=
pathMap
[
"productionConfig"
];
try
{
let
{
data
}
=
await
axios
.
get
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
}
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//更新生产配置
productionConfigUpdate
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
const
params
=
req
.
body
;
let
url
=
pathMap
[
"productionConfigUpdate"
];
try
{
let
{
data
}
=
await
axios
.
post
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
},
params
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//排版列表(分页)
productionSoftware
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
const
params
=
req
.
body
;
let
url
=
pathMap
[
"productionSoftware"
];
try
{
let
{
data
}
=
await
axios
.
post
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
},
params
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//查询该批次下的所有素材
listMaterialsByBatch
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
const
params
=
req
.
body
;
let
url
=
pathMap
[
"listMaterialsByBatch"
];
try
{
let
{
data
}
=
await
axios
.
get
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
},
params
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//重新合成排版
psReComposingDesignImages
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
const
params
=
req
.
body
;
let
url
=
pathMap
[
"psReComposingDesignImages"
];
try
{
let
{
data
}
=
await
axios
.
get
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
},
params
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
},
//根据批次分页查询操作单
listOperationByBatch
:
async
(
req
,
res
)
=>
{
env
=
getHostApi
().
apiApiHost
;
const
token
=
req
.
headers
[
"jwt-token"
];
const
params
=
req
.
body
;
let
url
=
pathMap
[
"listOperationByBatch"
];
try
{
let
{
data
}
=
await
axios
.
post
(
`
${
env
}
/
${
url
}
`
,
{
headers
:
{
"jwt-token"
:
token
},
params
});
res
.
send
(
data
);
}
catch
(
error
)
{
console
.
log
(
"checkInventory"
,
error
);
res
.
json
({
code
:
500
,
msg
:
error
});
}
}
};
src/server/routes/index.js
View file @
1a6d16c7
...
...
@@ -60,6 +60,19 @@ router.post("/downloadMaterial", fn.downloadMaterial);
//复制文件夹下文件
router
.
post
(
"/copySingleImageFn"
,
fn
.
copySingleImageFn
);
//获取生产配置
router
.
post
(
"/productionConfig"
,
fn
.
productionConfig
);
//更新生产配置
router
.
post
(
"/productionConfigUpdate"
,
fn
.
productionConfigUpdate
);
//排版列表(分页)
router
.
post
(
"/productionSoftware"
,
fn
.
productionSoftware
);
//查询该批次下的所有素材
router
.
post
(
"/productionSoftware"
,
fn
.
productionSoftware
);
//重新合成排版
router
.
post
(
"/psReComposingDesignImages"
,
fn
.
psReComposingDesignImages
);
//根据批次分页查询操作单
router
.
post
(
"/listOperationByBatch"
,
fn
.
listOperationByBatch
);
// 根据生产单号查询详情
autoRegisterRouter
(
router
,
"findByPodProductionNo"
,
fn
.
findByPodProductionNo
);
...
...
src/server/utils/store.js
View file @
1a6d16c7
...
...
@@ -215,13 +215,13 @@ module.exports = {
},
getDTFSetting
:
()
=>
store
.
get
(
"DTFSetting"
)
||
{
overWidthMode
:
"autoScale
"
,
widthStrategy
:
"1
"
,
// 最大素材数模式:batch / custom
ma
xPicMode
:
"batch
"
,
ma
terialStrategy
:
"1
"
,
// 自定义素材数量
customPicNum
:
30
,
materialCount
:
30
,
// 自动排版规格 40+2 / 60 / none
autoLayout
Type
:
"none
"
,
autoLayout
Strategy
:
"3
"
,
// 默认下载目录
downloadDir
:
""
},
...
...
src/utils/download-queue.js
View file @
1a6d16c7
const
fs
=
require
(
"fs"
);
const
path
=
require
(
"path"
);
const
http
=
require
(
"http"
);
const
https
=
require
(
"https"
);
const
{
URL
}
=
require
(
"url"
);
const
{
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
function
create
DownloadQueue
(
options
=
{})
{
//
配置项
export
function
createBatch
DownloadQueue
(
options
=
{})
{
//
========== 1. 配置项 ==========
const
config
=
Object
.
assign
(
{
concurrency
:
2
,
concurrency
:
3
,
retryTimes
:
2
,
basePath
:
""
,
// 根目录,外部传入
fileNameRule
:
task
=>
`
${
task
.
batchArrangeNum
}
_素材.zip`
,
// 默认文件名规则:批次号_素材.zip
onSuccess
:
()
=>
{},
onError
:
()
=>
{},
onIdle
:
()
=>
{}
onSuccess
:
()
=>
{},
// 单文件下载成功
onError
:
()
=>
{},
// 单文件下载失败
onBatchInvalid
:
()
=>
{},
// 批次校验失败回调(返回异常批次列表+原因)
onIdle
:
()
=>
{}
// 全部任务完成
},
options
);
//
内部状态
//
========== 2. 内部状态 ==========
let
pendingQueue
=
[];
let
runningCount
=
0
;
const
successSet
=
new
Set
();
const
runningSet
=
new
Set
();
let
isDestroyed
=
false
;
// 工具方法
const
hasFile
=
targetPath
=>
fs
.
existsSync
(
targetPath
);
const
cleanFile
=
targetPath
=>
hasFile
(
targetPath
)
&&
fs
.
unlinkSync
(
targetPath
);
// ========== 3. 通用工具方法 ==========
const
getRequestModule
=
url
=>
{
try
{
return
new
URL
(
url
).
protocol
===
"https:"
?
https
:
http
;
}
catch
(
e
)
{
return
https
;
}
};
const
isPngUrl
=
url
=>
{
try
{
const
urlObj
=
new
URL
(
url
);
return
urlObj
.
pathname
.
toLowerCase
().
endsWith
(
".png"
);
}
catch
(
e
)
{
return
false
;
}
};
const
getPngFileName
=
url
=>
{
try
{
const
urlObj
=
new
URL
(
url
);
const
name
=
path
.
basename
(
urlObj
.
pathname
);
return
name
.
toLowerCase
().
endsWith
(
".png"
)
?
name
:
`
${
name
||
`img_
${
Date
.
now
()}
`
}
.png`
;
}
catch
(
e
)
{
return
`img_
${
Date
.
now
()}
.png`
;
}
};
// 流式下载核心
const
fileExists
=
targetPath
=>
fs
.
existsSync
(
targetPath
);
const
cleanBadFile
=
targetPath
=>
fileExists
(
targetPath
)
&&
fs
.
unlinkSync
(
targetPath
);
// ========== 4. 流式下载核心 ==========
const
streamDownload
=
(
url
,
targetPath
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
const
ws
=
fs
.
createWriteStream
(
targetPath
);
const
req
=
https
.
get
(
url
,
res
=>
{
// 处理重定向
const
requestModule
=
getRequestModule
(
url
);
const
writeStream
=
fs
.
createWriteStream
(
targetPath
);
const
request
=
requestModule
.
get
(
url
,
res
=>
{
if
([
301
,
302
,
307
,
308
].
includes
(
res
.
statusCode
))
{
w
s
.
close
();
w
riteStream
.
close
();
streamDownload
(
res
.
headers
.
location
,
targetPath
)
.
then
(
resolve
)
.
catch
(
reject
);
return
;
}
if
(
res
.
statusCode
!==
200
)
{
w
s
.
close
();
cleanFile
(
targetPath
);
reject
(
new
Error
(
`状态码
${
res
.
statusCode
}
`
));
w
riteStream
.
close
();
clean
Bad
File
(
targetPath
);
reject
(
new
Error
(
`
响应
状态码
${
res
.
statusCode
}
`
));
return
;
}
res
.
pipe
(
w
s
);
res
.
pipe
(
w
riteStream
);
});
w
s
.
on
(
"finish"
,
()
=>
{
w
s
.
close
();
w
riteStream
.
on
(
"finish"
,
()
=>
{
w
riteStream
.
close
();
resolve
();
});
w
s
.
on
(
"error"
,
err
=>
{
w
s
.
close
();
cleanFile
(
targetPath
);
w
riteStream
.
on
(
"error"
,
err
=>
{
w
riteStream
.
close
();
clean
Bad
File
(
targetPath
);
reject
(
err
);
});
req
.
on
(
"error"
,
err
=>
{
w
s
.
close
();
cleanFile
(
targetPath
);
req
uest
.
on
(
"error"
,
err
=>
{
w
riteStream
.
close
();
clean
Bad
File
(
targetPath
);
reject
(
err
);
});
req
.
setTimeout
(
30000
,
()
=>
{
req
.
destroy
();
w
s
.
close
();
cleanFile
(
targetPath
);
req
uest
.
setTimeout
(
30000
,
()
=>
{
req
uest
.
destroy
();
w
riteStream
.
close
();
clean
Bad
File
(
targetPath
);
reject
(
new
Error
(
"下载超时"
));
});
});
//
队列消费
//
========== 5. 队列消费逻辑 ==========
const
consume
=
()
=>
{
if
(
isDestroyed
)
return
;
if
(
runningCount
>=
config
.
concurrency
||
pendingQueue
.
length
===
0
)
{
...
...
@@ -85,16 +116,11 @@ function createDownloadQueue(options = {}) {
runningCount
++
;
(
async
()
=>
{
try
{
// 自动创建当前批次的目录(仅创建需要下载的批次目录,不批量预创建)
const
targetDir
=
path
.
dirname
(
task
.
targetPath
);
if
(
!
hasFile
(
targetDir
))
fs
.
mkdirSync
(
targetDir
,
{
recursive
:
true
});
// 获取下载地址
const
url
=
typeof
task
.
fetchUrl
===
"function"
?
await
task
.
fetchUrl
(
task
)
:
task
.
downloadUrl
;
await
streamDownload
(
url
,
task
.
targetPath
);
if
(
!
fileExists
(
targetDir
))
{
fs
.
mkdirSync
(
targetDir
,
{
recursive
:
true
});
}
await
streamDownload
(
task
.
downloadUrl
,
task
.
targetPath
);
successSet
.
add
(
task
.
targetPath
);
config
.
onSuccess
(
task
);
}
catch
(
err
)
{
...
...
@@ -112,36 +138,94 @@ function createDownloadQueue(options = {}) {
})();
};
//
对外方法
//
========== 6. 对外暴露方法 ==========
return
{
addTasks
(
tasks
=
[])
{
if
(
isDestroyed
)
return
;
const
validTasks
=
tasks
.
map
(
row
=>
{
// 按规则拼接完整路径:根目录 / 批次号:xxx / 自动排版素材 / 文件名
const
dir
=
path
.
join
(
config
.
basePath
,
`批次号:
${
row
.
batchArrangeNum
}
`
,
"自动排版素材"
);
const
fileName
=
config
.
fileNameRule
(
row
);
const
targetPath
=
path
.
join
(
dir
,
fileName
);
return
{
...
row
,
targetPath
,
remainRetry
:
config
.
retryTimes
};
})
// 三重过滤:本地已存在、已下载成功、正在队列中
.
filter
(
task
=>
!
hasFile
(
task
.
targetPath
)
&&
!
successSet
.
has
(
task
.
targetPath
)
&&
!
runningSet
.
has
(
task
.
targetPath
)
);
addBatchList
(
list
=
[])
{
if
(
isDestroyed
||
!
Array
.
isArray
(
list
))
return
;
if
(
validTasks
.
length
===
0
)
return
;
validTasks
.
forEach
(
t
=>
{
runningSet
.
add
(
t
.
targetPath
);
pendingQueue
.
push
(
t
);
// 第一步:筛选 layoutStatus = 3 或 5 的批次
const
targetBatch
=
list
.
filter
(
item
=>
[
3
,
5
].
includes
(
item
.
layoutStatus
)
);
const
invalidBatchList
=
[];
// 收集异常批次
const
fileTasks
=
[];
// 第二步:逐个校验批次URL合法性
targetBatch
.
forEach
(
batch
=>
{
const
batchNo
=
batch
.
batchArrangeNum
||
"未知批次"
;
// 校验1:URL 不是字符串类型
if
(
typeof
batch
.
url
!==
"string"
)
{
invalidBatchList
.
push
({
batchArrangeNum
:
batchNo
,
reason
:
"下载链接格式错误,非字符串类型"
});
return
;
}
// 校验2:URL 为空字符串/纯空格
if
(
batch
.
url
.
trim
()
===
""
)
{
invalidBatchList
.
push
({
batchArrangeNum
:
batchNo
,
reason
:
"下载链接为空"
});
return
;
}
// 第三步:拆分多URL,过滤空值,校验PNG格式
const
urlList
=
batch
.
url
.
split
(
","
)
.
map
(
u
=>
u
.
trim
())
.
filter
(
Boolean
);
const
validPngUrls
=
urlList
.
filter
(
url
=>
isPngUrl
(
url
));
// 校验3:拆分后无有效PNG链接
if
(
validPngUrls
.
length
===
0
)
{
invalidBatchList
.
push
({
batchArrangeNum
:
batchNo
,
reason
:
"无有效PNG格式下载链接"
});
return
;
}
// 第四步:生成合法下载任务,去重过滤
const
dir
=
path
.
join
(
getDTFSetting
().
downloadDir
,
`批次号:
${
batchNo
}
`
,
"自动排版素材"
);
validPngUrls
.
forEach
(
url
=>
{
const
fileName
=
getPngFileName
(
url
);
const
targetPath
=
path
.
join
(
dir
,
fileName
);
if
(
!
fileExists
(
targetPath
)
&&
!
successSet
.
has
(
targetPath
)
&&
!
runningSet
.
has
(
targetPath
)
)
{
fileTasks
.
push
({
batchArrangeNum
:
batchNo
,
downloadUrl
:
`https://factory.jomalls.com
${
url
}
`
,
targetPath
,
fileName
,
remainRetry
:
config
.
retryTimes
});
}
});
});
consume
();
// 有异常批次,通过回调抛出
if
(
invalidBatchList
.
length
>
0
)
{
config
.
onBatchInvalid
(
invalidBatchList
);
}
// 启动下载队列
if
(
fileTasks
.
length
>
0
)
{
fileTasks
.
forEach
(
task
=>
{
runningSet
.
add
(
task
.
targetPath
);
pendingQueue
.
push
(
task
);
});
consume
();
}
},
get
isRunning
()
{
...
...
@@ -158,4 +242,158 @@ function createDownloadQueue(options = {}) {
};
}
module
.
exports
=
createDownloadQueue
;
/**
* 批量下载PNG图片到指定文件夹
* @param {string[]} urls - PNG图片链接数组
* @param {string} targetDir - 目标文件夹绝对路径
* @param {Object} [options] - 可选配置
* @param {number} [options.concurrency=2] - 同时下载数量
* @param {number} [options.timeout=30000] - 单文件超时时间(ms)
* @param {number} [options.retryTimes=1] - 单文件失败重试次数
* @returns {Promise<{success: string[], failed: {url: string, error: string}[]}>}
*/
export
function
downloadPngList
(
urls
,
targetDir
,
options
=
{})
{
const
config
=
{
concurrency
:
2
,
timeout
:
30000
,
retryTimes
:
1
,
...
options
};
return
new
Promise
(
resolve
=>
{
// 参数合法性校验
if
(
!
Array
.
isArray
(
urls
)
||
urls
.
length
===
0
)
{
resolve
({
success
:
[],
failed
:
[{
url
:
""
,
error
:
"下载链接数组为空"
}]
});
return
;
}
console
.
log
(
`开始批量下载,共
${
urls
.
length
}
个链接,目标目录:
${
targetDir
}
`
);
// 自动创建目标目录(多级递归创建,不需要可注释掉)
if
(
!
fs
.
existsSync
(
targetDir
))
{
fs
.
mkdirSync
(
targetDir
,
{
recursive
:
true
});
}
const
successList
=
[];
// 下载成功的文件路径
const
failedList
=
[];
// 下载失败的链接+原因
let
currentIndex
=
0
;
// 当前处理到第几个链接
let
runningCount
=
0
;
// 当前正在下载的数量
const
total
=
urls
.
length
;
// 从URL中提取PNG文件名,无后缀自动补.png
const
getPngFileName
=
url
=>
{
try
{
const
urlObj
=
new
URL
(
url
);
const
name
=
path
.
basename
(
urlObj
.
pathname
);
return
name
.
toLowerCase
().
endsWith
(
".png"
)
?
name
:
`
${
name
}
.png`
;
}
catch
(
e
)
{
return
`img_
${
Date
.
now
()}
_
${
Math
.
random
()
.
toString
(
36
)
.
slice
(
2
,
6
)}
.png`
;
}
};
// 单文件流式下载核心
const
streamDownload
=
(
url
,
filePath
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
requestModule
=
url
.
startsWith
(
"https:"
)
?
https
:
http
;
const
writeStream
=
fs
.
createWriteStream
(
filePath
);
const
request
=
requestModule
.
get
(
url
,
res
=>
{
// 自动处理301/302/307/308重定向
if
([
301
,
302
,
307
,
308
].
includes
(
res
.
statusCode
))
{
writeStream
.
close
();
streamDownload
(
res
.
headers
.
location
,
filePath
)
.
then
(
resolve
)
.
catch
(
reject
);
return
;
}
if
(
res
.
statusCode
!==
200
)
{
writeStream
.
close
();
fs
.
existsSync
(
filePath
)
&&
fs
.
unlinkSync
(
filePath
);
reject
(
new
Error
(
`HTTP状态码
${
res
.
statusCode
}
`
));
return
;
}
res
.
pipe
(
writeStream
);
});
writeStream
.
on
(
"finish"
,
()
=>
{
console
.
log
(
"downloadPngList---finish"
);
writeStream
.
close
();
resolve
();
});
writeStream
.
on
(
"error"
,
err
=>
{
writeStream
.
close
();
fs
.
existsSync
(
filePath
)
&&
fs
.
unlinkSync
(
filePath
);
reject
(
err
);
});
request
.
on
(
"error"
,
err
=>
{
writeStream
.
close
();
fs
.
existsSync
(
filePath
)
&&
fs
.
unlinkSync
(
filePath
);
reject
(
err
);
});
// 超时兜底
request
.
setTimeout
(
config
.
timeout
,
()
=>
{
request
.
destroy
();
writeStream
.
close
();
fs
.
existsSync
(
filePath
)
&&
fs
.
unlinkSync
(
filePath
);
reject
(
new
Error
(
"下载超时"
));
});
});
};
// 带重试的单任务执行
const
runTask
=
async
url
=>
{
const
fileName
=
getPngFileName
(
url
);
const
filePath
=
path
.
join
(
targetDir
,
fileName
);
// 本地文件已存在,直接跳过
if
(
fs
.
existsSync
(
filePath
))
{
successList
.
push
(
filePath
);
return
;
}
console
.
log
(
`开始下载:
${
fileName
}
`
);
let
remainRetry
=
config
.
retryTimes
;
try
{
await
streamDownload
(
url
,
filePath
);
console
.
log
(
`下载成功:
${
fileName
}
`
);
successList
.
push
(
filePath
);
}
catch
(
err
)
{
if
(
remainRetry
>
0
)
{
remainRetry
--
;
}
else
{
failedList
.
push
({
url
,
error
:
err
.
message
});
}
}
};
// 并发调度器
const
next
=
()
=>
{
// 并发数未满且还有未处理任务,继续启动
while
(
runningCount
<
config
.
concurrency
&&
currentIndex
<
total
)
{
runningCount
++
;
const
url
=
urls
[
currentIndex
].
trim
();
currentIndex
++
;
runTask
(
url
).
finally
(()
=>
{
runningCount
--
;
next
();
});
}
// 所有任务执行完毕,返回结果
if
(
runningCount
===
0
&&
currentIndex
>=
total
)
{
console
.
log
(
`全部任务执行完成,成功:
${
successList
.
length
}
个,失败:
${
failedList
.
length
}
个`
);
resolve
({
success
:
successList
,
failed
:
failedList
});
}
};
next
();
});
}
src/views/dtf-batch-detail/components/detailHead.vue
View file @
1a6d16c7
<
script
>
import
{
mapState
}
from
"vuex"
;
const
{
getDesktopDevice
,
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
const
{
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
const
{
ipcRenderer
}
=
require
(
"electron"
);
export
default
{
...
...
@@ -18,39 +17,13 @@ export default {
return
{
dialogVisible
:
false
,
dtfSetting
:
getDTFSetting
(),
radio
:
""
radio
:
""
,
searchForm
:
{}
};
},
computed
:
{
...
mapState
([
"defaultProportion"
,
"countryList"
,
"orderType"
,
"grid"
,
"imgList"
])
},
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
searchRef
?.
focus
();
if
(
this
.
desktopDevice
!==
3
)
{
this
.
selectGridIndex
=
0
;
this
.
setting
.
gridValue
=
0
;
}
else
{
this
.
selectGridIndex
=
5
;
this
.
setting
.
gridValue
=
5
;
}
});
this
.
$store
.
commit
(
"changeDesktopDevice"
,
getDesktopDevice
());
},
created
()
{
localStorage
.
setItem
(
"desktoVersion"
,
"print"
);
if
(
this
.
$dataStore
.
get
(
"setting"
))
{
this
.
setting
=
this
.
$dataStore
.
get
(
"setting"
);
}
else
{
this
.
$dataStore
.
set
(
"setting"
,
this
.
setting
);
}
},
computed
:
{},
mounted
()
{},
created
()
{},
methods
:
{
dropdownCommand
(
v
)
{
...
...
@@ -87,13 +60,10 @@ export default {
goBack
()
{
this
.
$router
.
push
({
name
:
"design-dtf"
});
},
goSetting
()
{
this
.
$router
.
push
({
path
:
"/DTFsetting"
,
query
:
{
id
:
123
}
});
goSetting
()
{},
getDataInfo
()
{
this
.
$emit
(
"search"
,
this
.
searchForm
);
},
getDataInfo
()
{},
productionCompleted
()
{
this
.
$confirm
(
"确认完成生产?"
,
"提示"
,
{
confirmButtonText
:
"确定"
,
...
...
@@ -106,13 +76,12 @@ export default {
changeTypesetting
()
{
this
.
dialogVisible
=
true
;
},
submit
()
{
console
.
log
(
this
.
radio
);
async
submit
()
{
if
(
!
this
.
radio
)
{
return
this
.
$message
.
error
(
"请选择排版宽度"
);
}
if
(
this
.
dtfSetting
.
overWidthMode
===
"biggerSize"
&&
this
.
radio
==
60
)
{
if
(
this
.
dtfSetting
.
widthStrategy
==
3
&&
this
.
radio
==
60
)
{
this
.
$confirm
(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版"
,
"提示"
,
...
...
@@ -120,13 +89,19 @@ export default {
confirmButtonText
:
"确定"
,
type
:
"warning"
}
).
then
(()
=>
{
this
.
dialogVisible
=
false
;
this
.
$message
(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
);
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
(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
...
...
@@ -161,8 +136,26 @@ export default {
>
返回
</el-button
></el-form-item
>
<el-form-item>
<el-input></el-input></el-form-item>
<el-form-item
label=
"操作单状态"
>
<el-input></el-input></el-form-item>
<el-form-item>
<el-input
placeholder=
"请扫描操作单"
v-model=
"searchForm.operationNo"
></el-input
></el-form-item>
<el-form-item
label=
"操作单状态"
>
<el-select
v-model=
"searchForm.status"
placeholder=
"请选择"
clearable
>
<el-option
v-for=
"(value, label) in statusMap"
:key=
"value"
:label=
"label"
:value=
"value"
></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-form-item>
<el-form-item>
<el-button
@
click=
"getDataInfo"
type=
"primary"
...
...
src/views/dtf-batch-detail/detail.vue
View file @
1a6d16c7
...
...
@@ -18,12 +18,7 @@ export default {
}),
type
:
Object
},
factoryType
:
{
type
:
String
,
default
:
"CN"
},
cardConfig
:
{
imageSrc
:
"./src/assets/template-bg-1.jpg"
,
title
:
"title"
,
time
:
"time"
}
factoryType
:
{
type
:
String
,
default
:
"CN"
}
},
data
()
{
return
{
...
...
@@ -31,7 +26,7 @@ export default {
detailVisible
:
false
,
logList
:
[],
pageInfo
:
{
pageNum
:
1
,
currentPage
:
1
,
pageSize
:
10
,
total
:
2
},
...
...
@@ -49,14 +44,14 @@ export default {
};
}),
detailRow
:
{
name
:
{
label
:
"批次号"
,
value
:
111
},
name1
:
{
label
:
"操作单数量"
,
value
:
111
},
name2
:
{
label
:
"素材数量"
,
value
:
111
},
name3
:
{
label
:
"工艺类型"
,
value
:
111
},
name6
:
{
label
:
"规范素材"
,
value
:
111
},
name4
:
{
label
:
"创建时间"
,
value
:
111
},
name5
:
{
label
:
"排版状态"
,
value
:
111
},
name7
:
{
label
:
"操作单状态"
,
value
:
111
}
batchArrangeNumber
:
{
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
}
}
};
},
...
...
@@ -180,30 +175,25 @@ export default {
{{
item
.
operationNo
}}
</span>
</
template
>
<
template
#
top_
lef
t
>
<
template
#
top_
righ
t
>
<el-tooltip
v-if=
"item.
outOfStock
"
v-if=
"item.
layout
"
effect=
"light"
content=
"
缺货
"
content=
"
排版完成
"
placement=
"bottom"
>
<div
style=
"
background-color: #f
56c6c
;
background-color: #f
0f9eb
;
color: #fff;
padding: 2px 4px;
border-radius: 4px;
font-size: 12px;
"
>
缺
排
</div>
</el-tooltip>
<span
v-if=
"item.tagsId"
class=
"flex-row flex-row-gap6"
>
<el-tag
v-for=
"tag in getItemTags(item.tagsId)"
:key=
"tag.id"
>
{{
tag
.
name
}}
</el-tag>
</span>
</
template
>
<
template
#
images
>
...
...
@@ -291,9 +281,9 @@ export default {
<div
class=
"card-info-row full"
>
<span
class=
"info-value ellipsis"
:title=
"item.
productName
|| ''"
:title=
"item.
operationNo
|| ''"
>
{{
item
.
productName
}}
{{
item
.
operationNo
}}
</span>
</div>
<div
class=
"card-info-row"
>
...
...
src/views/dtf-list/components/head.vue
View file @
1a6d16c7
<
script
>
import
{
mapState
}
from
"vuex"
;
const
{
getDesktopDevice
}
=
require
(
"@/server/utils/store"
);
export
default
{
props
:
{
user
:
{
...
...
@@ -13,38 +11,13 @@ export default {
factoryType
:
{
type
:
String
,
default
:
"CN"
}
},
data
()
{
return
{};
},
computed
:
{
...
mapState
([
"defaultProportion"
,
"countryList"
,
"orderType"
,
"grid"
,
"imgList"
])
},
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
searchRef
?.
focus
();
if
(
this
.
desktopDevice
!==
3
)
{
this
.
selectGridIndex
=
0
;
this
.
setting
.
gridValue
=
0
;
}
else
{
this
.
selectGridIndex
=
5
;
this
.
setting
.
gridValue
=
5
;
}
});
this
.
$store
.
commit
(
"changeDesktopDevice"
,
getDesktopDevice
());
},
created
()
{
localStorage
.
setItem
(
"desktoVersion"
,
"print"
);
if
(
this
.
$dataStore
.
get
(
"setting"
))
{
this
.
setting
=
this
.
$dataStore
.
get
(
"setting"
);
}
else
{
this
.
$dataStore
.
set
(
"setting"
,
this
.
setting
);
}
return
{
searchForm
:
{
batchArrangeNumber
:
""
,
layoutStatus
:
""
,
startTime
:
""
}
};
},
computed
:
{},
mounted
()
{},
created
()
{},
methods
:
{
dropdownCommand
(
v
)
{
...
...
@@ -78,14 +51,7 @@ export default {
return
""
;
},
goBack
()
{
// Vue路由返回
this
.
$router
.
push
({
name
:
"design"
});
// 如果需要关闭当前弹窗/子窗口,搭配:
// ipcRenderer.send("close-current-win");
},
goSetting
()
{
this
.
$router
.
push
({
name
:
"dtf-setting"
...
...
@@ -96,7 +62,9 @@ export default {
name
:
"dtf-batch-detail"
});
},
getDataInfo
()
{}
getDataInfo
()
{
this
.
$emit
(
"search"
,
this
.
searchForm
);
}
}
};
</
script
>
...
...
@@ -104,10 +72,39 @@ export default {
<
template
>
<div>
<div
class=
"page-header"
>
<el-form
style=
"display: flex;flex-wrap: nowrap;"
inline
>
<el-form-item
label=
"批次号"
>
<el-input></el-input></el-form-item>
<el-form-item
label=
"排版状态"
>
<el-input></el-input></el-form-item>
<el-form-item
label=
"创建时间"
>
<el-input></el-input></el-form-item>
<el-form
style=
"display: flex;flex-wrap: nowrap;"
inline
:model=
"searchForm"
>
<el-form-item
label=
"批次号"
>
<el-input
v-model=
"searchForm.batchArrangeNumber"
placeholder=
"请输入批次号"
></el-input
></el-form-item>
<el-form-item
label=
"排版状态"
>
<el-select
v-model=
"searchForm.layoutStatus"
placeholder=
"请选择排版状态"
>
<el-option
label=
"待排版"
:value=
"1"
></el-option>
<el-option
label=
"排版中"
:value=
"2"
>
</el-option>
<el-option
label=
"排版成功"
:value=
"3"
>
</el-option>
<el-option
label=
"排版失败"
:value=
"4"
>
</el-option>
<el-option
label=
"部分成功"
:value=
"5"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label=
"创建时间"
>
<el-date-picker
v-model=
"searchForm.startTime"
type=
"datetime"
placeholder=
"选择创建时间"
format=
"yyyy 年 MM 月 dd 日"
value-format=
"yyyy-MM-dd"
>
</el-date-picker
></el-form-item>
<el-form-item>
<el-button
@
click=
"getDataInfo"
type=
"primary"
>
查询
...
...
@@ -118,12 +115,6 @@ export default {
>
DTF排版设置
</el-button></el-form-item
>
<el-form-item>
<el-button
@
click=
"goSetting1"
type=
"success"
>
详情页
</el-button></el-form-item
>
</el-form>
<div
class=
"right-user"
>
...
...
src/views/dtf-list/index.vue
View file @
1a6d16c7
...
...
@@ -3,6 +3,7 @@ import BaseTable from "../../components/BaseTable.vue";
const
{
remote
,
ipcRenderer
}
=
require
(
"electron"
);
const
{
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
const
{
Menu
}
=
remote
;
const
path
=
require
(
"path"
);
import
DTFhead
from
"./components/head.vue"
;
export
default
{
...
...
@@ -23,30 +24,20 @@ export default {
dialogVisible
:
false
,
selectionList
:
[],
radio
:
""
,
templateBatchArrangeNum
:
""
,
dtfSetting
:
getDTFSetting
(),
pageInfo
:
{
pageNum
:
1
,
currentPage
:
1
,
pageSize
:
10
,
total
:
2
},
tableList
:
Array
.
from
({
length
:
50
},
(
_
,
index
)
=>
{
const
isDtg
=
index
%
2
===
0
;
return
{
id
:
index
+
1
,
batchArrangeNum
:
index
+
1
,
status
:
index
%
3
===
0
?
0
:
1
,
img
:
isDtg
?
"/static/dtg.jpg"
:
"/static/dtf.jpg"
,
subOrderNumber
:
index
%
4
!==
0
,
num
:
isDtg
?
120
+
index
*
2
:
86
+
index
,
failedReason
:
""
};
}),
tableList
:
[],
tableConfig
:
[
{
prop
:
"batchArrangeNum"
,
label
:
"批次号"
,
width
:
100
,
slotName
:
"batch
No
"
slotName
:
"batch
ArrangeNum
"
},
{
prop
:
"operationNum"
,
...
...
@@ -68,7 +59,13 @@ export default {
prop
:
"standardDesignImage"
,
label
:
"规范素材"
,
width
:
100
,
slotName
:
"standardDesignImage"
render
(
row
)
{
return
row
.
standardDesignImage
==
0
?
"否"
:
row
.
standardDesignImage
==
1
?
"是"
:
"混合"
;
}
},
{
label
:
"创建时间"
,
...
...
@@ -77,7 +74,8 @@ export default {
{
label
:
"排版状态"
,
width
:
140
,
prop
:
"status"
prop
:
"layoutStatus"
,
slotName
:
"layoutStatus"
},
{
prop
:
"failReason"
,
...
...
@@ -87,7 +85,7 @@ export default {
},
{
label
:
"排版参数"
,
prop
:
"
layoutParameters
"
,
prop
:
"
composingParam
"
,
width
:
300
},
{
...
...
@@ -95,68 +93,88 @@ export default {
width
:
260
,
slotName
:
"operation"
}
]
],
layoutStatus
:
{
1
:
{
label
:
"待排版"
,
type
:
"warning"
},
2
:
{
label
:
"排版中"
,
type
:
"info"
},
3
:
{
label
:
"排版成功"
,
type
:
"success"
},
4
:
{
label
:
"排版失败"
,
type
:
"danger"
},
5
:
{
label
:
"部分成功"
,
type
:
"success"
}
}
};
},
mounted
()
{
this
.
fetchBatchList
();
// 开启5分钟轮询
this
.
pollTimer
=
setInterval
(()
=>
{
this
.
fetchBatchList
();
},
5
*
60
*
1000
);
this
.
pollTimer
=
setInterval
(()
=>
this
.
fetchList
(),
300000
);
//
监听下载结果
ipcRenderer
.
on
(
"download:
success"
,
(
_
,
batchNum
)
=>
{
this
.
$message
.
success
(
`批次
${
batchNum
}
素材
下载完成`
);
//
单文件下载成功
ipcRenderer
.
on
(
"download:
file-success"
,
(
_
,
data
)
=>
{
console
.
log
(
`批次
${
data
.
batchArrangeNum
}
:
${
data
.
fileName
}
下载完成`
);
});
ipcRenderer
.
on
(
"download:error"
,
(
_
,
{
batchArrangeNum
,
msg
})
=>
{
this
.
$message
.
error
(
`批次
${
batchArrangeNum
}
下载失败:
${
msg
}
`
);
// 单文件下载失败
ipcRenderer
.
on
(
"download:file-error"
,
(
_
,
data
)
=>
{
this
.
$message
.
error
(
`批次
${
data
.
batchArrangeNum
}
:
${
data
.
fileName
}
下载失败,
${
data
.
msg
}
`
);
});
// 新增:批次校验失败,批量提示
ipcRenderer
.
on
(
"download:batch-invalid"
,
(
_
,
invalidList
)
=>
{
const
msg
=
invalidList
.
map
(
item
=>
`【
${
item
.
batchArrangeNum
}
】
${
item
.
reason
}
`
)
.
join
(
";"
);
this
.
$message
.
warning
(
`存在异常批次:
${
msg
}
`
);
});
},
beforeDestroy
()
{
clearInterval
(
this
.
pollTimer
);
ipcRenderer
.
removeAllListeners
(
"download:success"
);
ipcRenderer
.
removeAllListeners
(
"download:error"
);
ipcRenderer
.
removeAllListeners
(
"download:file-success"
);
ipcRenderer
.
removeAllListeners
(
"download:file-error"
);
ipcRenderer
.
removeAllListeners
(
"download:batch-invalid"
);
ipcRenderer
.
removeAllListeners
(
"download:all-finish"
);
},
methods
:
{
async
fetchBatchList
()
{
//定时任务
async
fetchBatchList
(
value
)
{
// 先判断:有下载任务在跑,本轮直接跳过
const
status
=
ipcRenderer
.
sendSync
(
"download:get
S
tatus"
);
const
status
=
ipcRenderer
.
sendSync
(
"download:get
-s
tatus"
);
if
(
status
.
isRunning
)
return
;
let
params
=
{
currentPage
:
this
.
pageInfo
.
currentPage
,
pageSize
:
this
.
pageInfo
.
pageSize
};
if
(
value
)
{
params
=
{
...
params
,
...
value
};
}
try
{
// const res = await getBatchListApi();
let
res
;
const
res
=
await
await
this
.
$api
.
post
(
"/productionSoftware"
,
params
);
if
(
res
.
code
===
200
)
{
this
.
tableData
=
res
.
data
||
[];
this
.
autoHandleDownload
();
this
.
tableData
=
res
.
data
.
records
||
[];
this
.
pageInfo
.
total
=
res
.
data
.
total
;
ipcRenderer
.
sendSync
(
"download:submit-batch-list"
,
this
.
tableData
);
}
}
catch
(
err
)
{
console
.
error
(
"批次列表拉取失败"
,
err
);
}
},
autoHandleDownload
()
{
// 1. 过滤出 status 为“是”的批次
const
finishList
=
this
.
tableData
.
filter
(
item
=>
item
.
status
===
"是"
);
if
(
finishList
.
length
===
0
)
return
;
// 2. 直接发给主进程,主进程会自动校验本地文件、过滤已存在的批次
ipcRenderer
.
sendSync
(
"download:addTasks"
,
finishList
);
},
handleSelect
(
rows
)
{
console
.
log
(
"选中行"
,
rows
);
this
.
selectionList
=
rows
;
},
handleSizeChange
(
size
)
{
this
.
pageInfo
.
pageSize
=
size
;
this
.
pageInfo
.
pageNum
=
1
;
this
.
pageInfo
.
currentPage
=
1
;
this
.
loadData
();
},
handleCurrentChange
(
page
)
{
this
.
pageInfo
.
pageNum
=
page
;
this
.
pageInfo
.
currentPage
=
page
;
this
.
loadData
();
},
//右键选择器
openMenu
(
e
)
{
e
.
preventDefault
();
const
template
=
[
...
...
@@ -174,12 +192,14 @@ export default {
const
menu
=
Menu
.
buildFromTemplate
(
template
);
menu
.
popup
({
x
:
e
.
clientX
,
y
:
e
.
clientY
});
},
//查看排版文件
async
goFliePage
(
value
,
type
)
{
console
.
log
(
value
);
let
{
data
}
=
await
this
.
$api
.
post
(
"/createBatchFolder"
,
{
basePath
:
this
.
dtfSetting
.
downloadDir
,
downloadDir
:
value
.
batch
No
,
downloadDir
:
value
.
batch
ArrangeNum
,
isArray
:
false
});
console
.
log
(
"goFliePage"
,
data
);
...
...
@@ -187,22 +207,26 @@ export default {
ipcRenderer
.
send
(
"open-folder-only"
,
data
);
}
},
changeTypesetting
()
{
if
(
this
.
dtfSetting
.
autoLayoutType
==
"none"
)
{
//打开手动排版弹窗
changeTypesetting
(
value
)
{
if
(
this
.
dtfSetting
.
autoLayoutStrategy
==
3
)
{
this
.
dialogVisible
=
true
;
this
.
templateBatchArrangeNum
=
value
;
}
else
{
this
.
$message
(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
}
},
submit
()
{
console
.
log
(
this
.
radio
);
//提交手动排版
async
submit
()
{
if
(
!
this
.
radio
)
{
return
this
.
$message
.
error
(
"请选择排版宽度"
);
}
if
(
this
.
dtfSetting
.
overWidthMode
===
"biggerSize"
&&
this
.
radio
==
60
)
{
if
(
this
.
dtfSetting
.
widthStrategy
==
3
&&
this
.
radio
==
60
)
{
this
.
$confirm
(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版"
,
"提示"
,
...
...
@@ -210,27 +234,56 @@ export default {
confirmButtonText
:
"确定"
,
type
:
"warning"
}
)
.
then
(()
=>
{
this
.
$message
(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
)
;
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
(
"排版进行中,请查看处理状态,排版完成的素材至文件夹排版素材查看"
);
}
},
//跳转批次详情页
toDetail
(
value
)
{
this
.
$router
.
push
({
name
:
"dtf-batch-detail"
,
params
:
{
batch
No
:
value
batch
ArrangeNum
:
value
}
});
},
downloadFile
()
{
this
.
$message
(
"下载进行中,下载完成的素材将在文件夹原素材中显示"
);
//下载批次原素材
async
downloadFile
(
value
)
{
try
{
const
{
data
}
=
await
this
.
$api
.
post
(
"/listMaterialsByBatch"
,
{
batchArrangeNum
:
value
});
const
res
=
await
this
.
$api
.
post
(
"/downloadMaterial"
,
{
urls
:
data
.
urls
,
targetPath
:
path
.
join
(
getDTFSetting
().
downloadDir
,
`批次号:
${
value
}
`
,
"原素材"
)
});
console
.
log
(
"listMaterialsByBatch-"
,
data
);
console
.
log
(
"downloadMaterial"
,
res
);
this
.
$message
(
"下载进行中,下载完成的素材将在文件夹原素材中显示"
);
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
}
};
...
...
@@ -238,7 +291,11 @@ export default {
<
template
>
<div
class=
"page"
@
contextmenu
.
prevent=
"openMenu"
>
<DTFhead
:user=
"user"
:factoryType=
"factoryType"
></DTFhead>
<DTFhead
:user=
"user"
:factoryType=
"factoryType"
@
search=
"fetchBatchList"
></DTFhead>
<BaseTable
v-if=
"tableList.length"
ref=
"tableRefs"
...
...
@@ -256,11 +313,16 @@ export default {
size=
"mini"
>
<!-- 自定义插槽示例 -->
<template
#
batch
No
=
"scope"
>
<a
href=
"javascript:;"
@
click=
"toDetail(scope.row.batch
No
)"
>
{{
scope
.
row
.
batch
No
<template
#
batch
ArrangeNum
=
"scope"
>
<a
href=
"javascript:;"
@
click=
"toDetail(scope.row.batch
ArrangeNum
)"
>
{{
scope
.
row
.
batch
ArrangeNum
}}
</a>
</
template
>
<
template
#
layoutStatus=
"scope"
>
<el-tag
:type=
"layoutStatus[scope.row?.layoutStatus].type"
>
{{
layoutStatus
[
scope
.
row
?.
layoutStatus
].
label
}}
</el-tag>
</
template
>
<
template
#
failReason=
"scope"
>
<div
style=
"white-space: pre-line"
>
<span
v-html=
"scope.row?.failReason"
></span>
...
...
@@ -272,21 +334,21 @@ export default {
<el-button
type=
"text"
class=
"btn flex btnBox"
@
click=
"downloadFile(scope.row)"
@
click=
"downloadFile(scope.row
.batchArrangeNum
)"
>
<img
src=
"../../assets/download.png"
width=
"30"
height=
"30"
/>
<div>
下载素材
</div></el-button
>
<el-button
type=
"text"
disabled
:disabled=
"scope.row.layoutStatus == 2"
class=
"btn flex btnBox"
@
click=
"changeTypesetting(scope.row)"
@
click=
"changeTypesetting(scope.row
.batchArrangeNum
)"
><img
src=
"../../assets/typesetting.png"
width=
"30"
height=
"30"
:class=
"
{ disabled:
false
}"
:class=
"
{ disabled:
scope.row.layoutStatus == 2
}"
/>
<div>
排版
</div>
</el-button>
...
...
src/views/dtf-setting/index.vue
View file @
1a6d16c7
...
...
@@ -21,26 +21,24 @@
<div
class=
"setting-group"
>
<div
class=
"group-title"
>
素材超排版宽度时:
</div>
<el-radio-group
v-model=
"form.overWidthMode"
>
<el-radio
label=
"autoScale"
>
自动缩小素材,适配烫画模宽度
</el-radio>
<el-radio
label=
"skip"
>
超出排版宽度不排版
</el-radio>
<el-radio
label=
"biggerSize"
>
超出排版宽度,使用更大规格排版
</el-radio
>
<el-radio-group
v-model=
"form.widthStrategy"
>
<el-radio
:label=
"1"
>
自动缩小素材,适配烫画模宽度
</el-radio>
<el-radio
:label=
"2"
>
超出排版宽度不排版
</el-radio>
<el-radio
:label=
"3"
>
超出排版宽度,使用更大规格排版
</el-radio>
</el-radio-group>
</div>
<div
class=
"setting-group"
>
<div
class=
"group-title"
>
单张排版最大素材数:
</div>
<el-radio-group
v-model=
"form.ma
xPicMode
"
>
<el-radio
label=
"batch
"
>
批次素材数
</el-radio>
<el-radio-group
v-model=
"form.ma
terialStrategy
"
>
<el-radio
:label=
"1
"
>
批次素材数
</el-radio>
<div
class=
"custom-num-row"
>
<el-radio
label=
"custom
"
>
自定义素材数(建议数量30):
</el-radio>
<el-radio
:label=
"2
"
>
自定义素材数(建议数量30):
</el-radio>
<el-input
v-model
.
number=
"form.
customPicNum
"
v-model
.
number=
"form.
materialCount
"
placeholder=
"请输入素材数"
style=
"width: 180px; margin-left: 8px"
:disabled=
"form.ma
xPicMode !== 'custom
'"
:disabled=
"form.ma
terialStrategy !== '2
'"
/>
</div>
</el-radio-group>
...
...
@@ -48,10 +46,10 @@
<div
class=
"setting-group"
>
<div
class=
"group-title"
>
自动排版设置
</div>
<el-radio-group
v-model=
"form.autoLayout
Type
"
>
<el-radio
label=
"40+2
"
>
40+2
</el-radio>
<el-radio
label=
"60
"
>
60
</el-radio>
<el-radio
label=
"none
"
>
不执行自动排版
</el-radio>
<el-radio-group
v-model=
"form.autoLayout
Strategy
"
>
<el-radio
:label=
"1
"
>
40+2
</el-radio>
<el-radio
:label=
"2
"
>
60
</el-radio>
<el-radio
:label=
"3
"
>
不执行自动排版
</el-radio>
</el-radio-group>
</div>
...
...
@@ -78,25 +76,19 @@
<
script
>
const
{
ipcRenderer
}
=
require
(
"electron"
);
const
{
getProductType
,
setProductType
,
setDTFSetting
,
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
const
{
setDTFSetting
,
getDTFSetting
}
=
require
(
"@/server/utils/store"
);
export
default
{
name
:
"dtf-setting"
,
data
()
{
return
{
// 表单配置,可从本地配置文件读取初始化
form
:
{
...
getDTFSetting
()
},
activeName
:
getProductType
()
form
:
{
...
getDTFSetting
()
}
};
},
mounted
()
{
// 页面加载读取本地配置回填表单
this
.
loadLocalConfig
();
//
this.loadLocalConfig();
},
methods
:
{
// 返回上一页
...
...
@@ -105,12 +97,13 @@ export default {
},
// 读取本地配置
async
loadLocalConfig
()
{
ipcRenderer
.
send
(
"read-layout-config"
);
ipcRenderer
.
once
(
"layout-config-data"
,
(
_
,
cfg
)
=>
{
if
(
cfg
)
{
this
.
form
=
Object
.
assign
({},
this
.
form
,
cfg
);
}
});
try
{
const
{
data
}
=
await
this
.
$api
.
post
(
"/productionConfig"
);
this
.
form
=
{
...
data
};
setDTFSetting
({
...
this
.
form
});
}
catch
(
error
)
{
console
.
log
(
error
);
}
},
// 选择文件夹弹窗
selectFolder
()
{
...
...
@@ -120,11 +113,8 @@ export default {
});
},
// 保存配置到本地文件
saveConfig
()
{
if
(
this
.
form
.
autoLayoutType
==
"60"
&&
this
.
form
.
overWidthMode
===
"biggerSize"
)
{
async
saveConfig
()
{
if
(
this
.
form
.
autoLayoutStrategy
==
2
&&
this
.
form
.
widthStrategy
==
3
)
{
this
.
$confirm
(
"由于排版设置中选择“按更大规格排版”仅适用于40+2,选60时,默认超60的规格不排版"
,
"提示"
,
...
...
@@ -132,17 +122,11 @@ export default {
confirmButtonText
:
"确定"
,
type
:
"warning"
}
).
then
(()
=>
{
this
.
$message
.
success
(
"排版设置保存成功!"
);
});
}
else
{
this
.
$message
.
success
(
"排版设置保存成功!"
);
);
}
await
this
.
$api
.
post
(
"/productionConfigUpdate"
,
this
.
form
);
setDTFSetting
({
...
this
.
form
});
},
tabClick
(
tab
)
{
if
(
tab
.
name
==
"DTG"
)
this
.
goBack
();
setProductType
(
tab
.
name
);
this
.
$message
.
success
(
"排版设置保存成功!"
);
}
}
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment