修复提交逻辑:完善所有拷贝类型支持,修复电子光盘和禁拷U盘提交卡死问题

- 修复 Files 组件中 file_form=1(电子光盘) 和 file_form=4(禁拷U盘) 的空处理分支
- 添加完成回调触发逻辑,避免提交流程卡死
- 完善 upload()、upload_over()、performSubmit() 的状态判断
- 严格对齐 work-副本.vue 的验证逻辑和参数构建顺序
- 优化标签上传判断,避免无标签时的不必要API调用
- 添加 ISO/ZIP 验证(仅在有内容文件时)
- 清理所有调试日志
This commit is contained in:
24kycj
2026-01-05 04:05:37 +08:00
parent a6ce251509
commit 04ca328a67
17 changed files with 992 additions and 499 deletions
+25 -47
View File
@@ -1,50 +1,28 @@
<template>
<el-dialog
:title="$t('networkAuth.title')"
:visible.sync="visible"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
:append-to-body="true"
:z-index="9999"
>
<el-dialog :title="$t('networkAuth.title')" :visible.sync="visible" width="600px" :close-on-click-modal="false"
:close-on-press-escape="false" :show-close="false" :append-to-body="true" :z-index="9999">
<div class="network-auth-content">
<p class="auth-description">
{{ $t('networkAuth.description') }}
</p>
<div class="network-paths">
<div
v-for="(path, index) in networkPaths"
:key="index"
class="network-path-item"
>
<div v-for="(path, index) in networkPaths" :key="index" class="network-path-item">
<div class="path-info">
<span class="path-label">{{ $t('networkAuth.pathLabel') }}</span>
<span class="path-value">{{ path.path }}</span>
</div>
<div class="auth-fields">
<el-input
v-model="path.userName"
:placeholder="$t('networkAuth.userName')"
size="small"
style="width: 150px; margin-right: 10px;"
/>
<el-input
v-model="path.password"
type="password"
:placeholder="$t('networkAuth.password')"
size="small"
style="width: 150px;"
show-password
/>
<el-input v-model="path.userName" :placeholder="$t('networkAuth.userName')" size="small"
style="width: 150px; margin-right: 10px;" />
<el-input v-model="path.password" type="password" :placeholder="$t('networkAuth.password')" size="small"
style="width: 150px;" show-password />
</div>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel">{{ $t('networkAuth.cancel') }}</el-button>
<el-button type="primary" @click="handleConfirm" :loading="loading">
@@ -76,7 +54,7 @@ export default {
handleCancel() {
this.$emit('cancel')
},
async handleConfirm() {
// 验证所有路径都填写了用户名和密码
for (let path of this.networkPaths) {
@@ -85,29 +63,29 @@ export default {
return
}
}
this.loading = true
try {
// 保存到本地存储
this.saveNetworkCredentials()
// 构建 net_info 数据
const netInfo = this.networkPaths.map(path => ({
host_name: this.extractHostName(path.path),
user_name: path.userName,
password: path.password
}))
this.$emit('confirm', netInfo)
} catch (error) {
console.error('保存网络认证信息失败:', error)
this.$message.error('保存认证信息失败')
this.$message.error(this.$t('networkAuth.authSaveFail'))
} finally {
this.loading = false
}
},
extractHostName(path) {
// 从网络路径中提取主机名
// 支持格式:\\hostname\path, //hostname/path, \\192.168.1.1\path
@@ -126,7 +104,7 @@ export default {
}
return 'unknown'
},
saveNetworkCredentials() {
// 保存到 localStorage
const credentials = {}
@@ -138,9 +116,9 @@ export default {
lastUsed: new Date().toISOString()
}
})
localStorage.setItem('networkCredentials', JSON.stringify(credentials))
// 同时保存到 Vuex store(若可用)
if (this.$store && this.$store.commit) {
this.$store.commit('SET_NETWORK_CREDENTIALS', credentials)
@@ -157,7 +135,7 @@ export default {
color: #606266;
font-size: 14px;
}
.network-paths {
.network-path-item {
border: 1px solid #e4e7ed;
@@ -165,16 +143,16 @@ export default {
padding: 15px;
margin-bottom: 15px;
background-color: #fafafa;
.path-info {
margin-bottom: 10px;
.path-label {
font-weight: bold;
color: #303133;
margin-right: 10px;
}
.path-value {
color: #409eff;
font-family: monospace;
@@ -183,7 +161,7 @@ export default {
border-radius: 3px;
}
}
.auth-fields {
display: flex;
align-items: center;
+59 -178
View File
@@ -33,28 +33,28 @@
<div class="row">
<div class="lable">RejectConfig</div>
<div class="switch">
<el-switch v-model="iniData.RejectConfig"></el-switch>
<el-switch v-model="iniData.RejectConfig" :active-value="1" :inactive-value="0"></el-switch>
</div>
<div class="tips">{{ $t("dispose.tips1") }}</div>
</div>
<div class="row">
<div class="lable">StopOnFailure</div>
<div class="switch">
<el-switch v-model="iniData.StopOnFailure"></el-switch>
<el-switch v-model="iniData.StopOnFailure" :active-value="1" :inactive-value="0"></el-switch>
</div>
<div class="tips">{{ $t("dispose.errorTips2") }}</div>
</div>
<div class="row">
<div class="lable">KeepCombinedImage</div>
<div class="switch">
<el-switch v-model="iniData.KeepCombinedImage"></el-switch>
<el-switch v-model="iniData.KeepCombinedImage" :active-value="1" :inactive-value="0"></el-switch>
</div>
<div class="tips">{{ $t("dispose.isReserveimg") }}</div>
</div>
<div class="row">
<div class="lable">CleanTaskFile</div>
<div class="switch">
<el-switch v-model="iniData.CleanTaskFile"></el-switch>
<el-switch v-model="iniData.CleanTaskFile" :active-value="1" :inactive-value="0"></el-switch>
</div>
<div class="tips">{{ $t("dispose.isReservetask") }}</div>
</div>
@@ -65,7 +65,7 @@
">
<div class="lable">UploadSharedDir</div>
<div class="switch">
<el-switch v-model="iniData.UploadSharedDir"></el-switch>
<el-switch v-model="iniData.UploadSharedDir" :active-value="1" :inactive-value="0"></el-switch>
</div>
<div class="tips">{{ $t("dispose.isUpload") }}</div>
</div>
@@ -89,201 +89,82 @@
</template>
<script>
const { ipcRenderer } = require("electron");
var fs = require("fs"),
ini = require("ini");
export default {
name: "dispose",
data() {
return {
autoRetryOptions: [
{
value: 0,
label: "0",
},
{
value: 1,
label: "1",
},
{
value: 2,
label: "2",
},
{ value: 0, label: "0" },
{ value: 1, label: "1" },
{ value: 2, label: "2" },
],
LogLevelOptions: [
{
value: "TRACE",
label: "TRACE",
},
{
value: "DEBUG",
label: "DEBUG",
},
{
value: "INFO",
label: "INFO",
},
{
value: "WARNING",
label: "WARNING",
},
{
value: "ERROR",
label: "ERROR",
},
{
value: "FATAL",
label: "FATAL",
},
{ value: "TRACE", label: "TRACE" },
{ value: "DEBUG", label: "DEBUG" },
{ value: "INFO", label: "INFO" },
{ value: "WARNING", label: "WARNING" },
{ value: "ERROR", label: "ERROR" },
{ value: "FATAL", label: "FATAL" },
],
iniData: {
LogLevel: 'INFO',
LogLevel: "INFO",
AutoRetryTimes: 0,
TaskDir: '',
SharedDir: '',
RejectConfig: false,
StopOnFailure: false,
KeepCombinedImage: false,
CleanTaskFile: false,
UploadSharedDir: false,
AuthorizationCode: ''
TaskDir: "",
SharedDir: "",
RejectConfig: 0,
StopOnFailure: 0,
KeepCombinedImage: 0,
CleanTaskFile: 0,
UploadSharedDir: 0,
AuthorizationCode: "",
DeleteTask: 0 // Ensure this is preserved if present in API
},
AutoRetryTimes: null,
LogLevel: "TRACE",
TaskDir: null,
SharedDir: null,
RejectConfig: false,
StopOnFailure: false,
KeepCombinedImage: false,
CleanTaskFile: false,
show: true,
root: "",
};
},
methods: {
save() {
//console.log(this.iniData);
//console.log(ini.stringify(this.iniData));
//fs.writeFileSync("../Debug/config.ini");
let that = this;
fs.writeFile(
this.root + "/ProductionServer/config.ini",
ini.stringify(this.iniData),
function (err) {
if (err) {
that.$message.error(that.$t("dispose.errorReserve"));
// 获取配置
getConfig() {
this.$axios.get('/admin/get_config')
.then(res => {
if (res && res.data) {
// 合并数据,确保所有字段都存在
this.iniData = { ...this.iniData, ...res.data };
} else {
that.$message({
this.$message.error(this.$t("dispose.errorRead"));
}
})
.catch(err => {
console.error(err);
this.$message.error(this.$t("dispose.errorRead"));
});
},
// 保存配置
save() {
this.$axios.post('/admin/get_config', this.iniData)
.then(res => {
if (res && (res.data === "success" || res.status === 200)) {
this.$message({
type: "success",
message: that.$t("dispose.successReserve"),
message: this.$t("dispose.successReserve"),
});
} else {
// 某些接口可能返回 { ret: 0 } 或其他形式,视具体情况而定
// 这里假设200 OK即为成功,或者根据res.data判断
this.$message({
type: "success",
message: this.$t("dispose.successReserve"),
});
}
}
);
})
.catch(err => {
console.error(err);
this.$message.error(this.$t("dispose.errorReserve"));
});
},
},
mounted() {
let that = this;
//var data = ini.parse(fs.readFileSync("F:\\controll\\config.ini", "utf-8"));
ipcRenderer.on("get-root-callback", (event, data) => {
this.root = data;
fs.readFile(this.root + "/ProductionServer/config.ini", "utf-8", (err, res) => {
if (err) {
console.log(err);
this.$message.error(that.$t("dispose.errorRead"));
this.show = false;
} else {
console.log(res);
let data = ini.parse(res);
console.log(data);
this.iniData = data;
that.iniData.AutoRetryTimes = data.AutoRetryTimes
? data.AutoRetryTimes
: 0;
that.iniData.TaskDir = data.TaskDir ? data.TaskDir : "";
that.iniData.SharedDir = data.SharedDir ? data.SharedDir : "";
that.iniData.LogLevel = data.LogLevel ? data.LogLevel : "";
that.iniData.CleanTaskFile =
data.CleanTaskFile == "true" ||
data.CleanTaskFile == "True" ||
data.CleanTaskFile
? true
: false;
that.iniData.KeepCombinedImage =
data.KeepCombinedImage == "true" ||
data.KeepCombinedImage == "True" ||
data.KeepCombinedImage
? true
: false;
that.iniData.RejectConfig =
data.RejectConfig == "true" ||
data.RejectConfig == "True" ||
data.RejectConfig
? true
: false;
that.iniData.StopOnFailure =
data.StopOnFailure == "true" ||
data.StopOnFailure == "True" ||
data.StopOnFailure
? true
: false;
that.iniData.UploadSharedDir =
data.UploadSharedDir == "true" ||
data.UploadSharedDir == "True" ||
data.UploadSharedDir
? true
: false;
that.iniData.AuthorizationCode = data.AuthorizationCode ? data.AuthorizationCode : "";
console.log(data);
}
});
});
ipcRenderer.send("get-root");
//iniparser.parse("../Debug/config.ini", function (err, data) {
/*
AutoRetryTimes: "0"
CardsoonModel: "SF80"
CleanTaskFile: "false"
RejectConfig: "false"
KeepCombinedImage: "True"
LogLevel: "DEBUG"
SharedDir: "C:\\CardSoonRepo"
StopOnFailure: "false"
SystemSn: "830001"
TaskDir: "C:\\PrintTasks"
Version: "V3.1"
iniparser.parse("F:\\controll\\config.ini", function (err, data) {
console.log(err);
if (err) {
that.$message.error("读取配置文件失败!");
} else {
console.log(data);
that.AutoRetryTimes = data.AutoRetryTimes ? data.AutoRetryTimes : 0;
that.TaskDir = data.TaskDir ? data.TaskDir : "";
that.SharedDir = data.SharedDir ? data.SharedDir : "";
that.LogLevel = data.LogLevel ? data.LogLevel : "";
that.CleanTaskFile =
data.CleanTaskFile == "true" || data.CleanTaskFile == "True"
? true
: false;
that.KeepCombinedImage =
data.KeepCombinedImage == "true" || data.KeepCombinedImage == "True"
? true
: false;
that.RejectConfig =
data.RejectConfig == "true" || data.RejectConfig == "True"
? true
: false;
that.StopOnFailure =
data.StopOnFailure == "true" || data.StopOnFailure == "True"
? true
: false;
}
});
*/
this.getConfig();
},
};
</script>
+10 -2
View File
@@ -349,7 +349,11 @@ export default {
entries.forEach((f) => this.fileBack(f, false));
}
} else if (file_form == 1) {
//电子光盘
// 电子光盘:不需要拷贝,直接标记为完成
this.overNumber = 0;
for (const file of entries) {
this.fileBack(file, true);
}
} else if (file_form == 2) {
//zip
this.archiverIsover = false;
@@ -364,7 +368,11 @@ export default {
let password = "123456";
zip(this.filesList, this.zip_path, this.archiverBack, true, password);
} else if (file_form == 4) {
//u盘
// 禁拷贝U盘:不需要拷贝,直接标记为完成
this.overNumber = 0;
for (const file of entries) {
this.fileBack(file, true);
}
} else {
// 默认路径上传:不实际拷贝,仅回调推进流程
this.overNumber = 0;
@@ -12,7 +12,7 @@
<!-- 录制状态指示器 -->
<div v-if="isRecording" class="recording-indicator" :class="{ 'status-only': statusOnly }">
<span class="recording-dot"></span>
<span class="recording-text">{{ statusOnly ? '录制中...' : recordingTime }}</span>
<span class="recording-text">{{ statusOnly ? $t('recorder.recording') : recordingTime }}</span>
</div>
</div>
</template>
@@ -113,12 +113,12 @@ export default {
this.currentTaskId = this.taskId || '';
this.startTimer();
this.$message.success(this.$t('recorder.recordingStarted') || '开始录制');
this.$message.success(this.$t('recorder.recordingStarted'));
this.$emit('recording-started');
} catch (error) {
console.error('启动录制失败:', error);
this.$message.error(this.$t('recorder.startFailed') || '启动录制失败');
this.$message.error(this.$t('recorder.startFailed'));
} finally {
this.loading = false;
}
@@ -145,7 +145,7 @@ export default {
} catch (error) {
console.error('停止录制失败:', error);
this.$message.error(this.$t('recorder.stopFailed') || '停止录制失败');
this.$message.error(this.$t('recorder.stopFailed'));
} finally {
this.loading = false;
}
@@ -153,7 +153,7 @@ export default {
async cancelRecording() {
await this.stopRecording(false);
this.$message.info('录制已取消');
this.$message.info(this.$t('recorder.recordingCanceled'));
},
async saveRecording(customPath) {
@@ -176,7 +176,7 @@ export default {
const result = await ipcRenderer.invoke('stop-recording', base64data, this.currentTaskId, customPath);
if (result.success) {
this.$message.success(this.$t('recorder.savedSuccess') || `录制已保存: ${result.fileName}`);
this.$message.success(this.$t('recorder.savedSuccess') + `: ${result.fileName}`);
this.$emit('recording-saved', result);
} else {
this.$message.error(result.message);
@@ -186,7 +186,7 @@ export default {
} catch (error) {
console.error('保存录制失败:', error);
this.$message.error(this.$t('recorder.saveFailed') || '保存录制失败');
this.$message.error(this.$t('recorder.saveFailed'));
}
},
@@ -1,11 +1,12 @@
<template>
<el-dialog :title="$t('work.senior') || '高级设置'" :visible.sync="visibleSync" width="780px" append-to-body
<el-dialog :title="$t('work.senior')" :visible.sync="visibleSync" width="780px" append-to-body
custom-class="grand-dialog" :close-on-click-modal="false" top="5vh" @close="handleClose">
<div class="grand-setting-content" style="max-height: 75vh; overflow-y: auto; padding-right: 10px;">
<el-form :model="form" label-position="right" label-width="120px" size="small">
<!-- 动态分模式渲染 -->
<basic-config :form="form" :file-form="fileForm" :options="options" />
<basic-config :form="form" :file-form="fileForm" :options="options" :printer-list="printerList"
:size-form="sizeForm" />
<!-- 高级功能ISO/ZIP/HASH/计数器 -->
<advanced-features :form="form" :file-form="fileForm" />
@@ -54,6 +55,14 @@ export default {
options: {
type: Object,
default: () => ({})
},
printerList: {
type: Array,
default: () => []
},
sizeForm: {
type: [Number, String],
default: null
}
},
computed: {
@@ -74,32 +83,32 @@ export default {
// 验证加密狗计数
if (this.form.is_dongle_count) {
if (!this.form.dongle_count || this.form.dongle_count <= 0 || !Number.isInteger(this.form.dongle_count)) {
this.$message.warning(this.$t('work.dongleCountRequired') || '请输入有效的加密狗安装次数(必须是正整数)')
this.$message.warning(this.$t('work.dongleCountRequired'))
return
}
}
// 验证 ISO 文件名
if (this.form.is_generate_iso && !this.form.iso_file_name) {
this.$message.warning(this.$t('work.isoNameInput') || '请输入 ISO 文件名')
this.$message.warning(this.$t('work.isoNameInput'))
return
}
// 验证 ZIP 文件名
if (this.form.is_generate_zip) {
if (!this.form.zip_file_name) {
this.$message.warning(this.$t('work.zipNameInput') || '请输入 ZIP 文件名')
this.$message.warning(this.$t('work.zipNameInput'))
return
}
// 验证 ZIP 加密密码
if (this.form.is_zip_encrypt) {
if (!this.form.zip_password) {
this.$message.warning(this.$t('work.pleasePassword') || '请输入 ZIP 压缩密码')
this.$message.warning(this.$t('work.pleasePassword'))
return
}
if (this.form.zip_password !== this.form.zip_repassword) {
this.$message.warning(this.$t('work.zipPassWrong') || 'ZIP 压缩密码不一致')
this.$message.warning(this.$t('work.zipPassWrong'))
return
}
}
@@ -0,0 +1,96 @@
<template>
<el-popover v-if="guideStep && guideStep[step]" :placement="guideStep[step].placement" width="250" trigger="manual"
v-model="guideStep[step].show">
<div class="guide_box">
<div class="guide_title">
{{ $t('guide.title') }}<span>{{ parseInt(currentStep) + 1 }}/{{ guideStep.length }}</span>
</div>
<div class="guide_desc">{{ $t(`guide.step${step + 1}`) }}</div>
<div class="guide_btns">
<el-button @click="$emit('exit-guide')" class="guide_btn1" size="mini" type="text">
{{ $t('guide.skip') }}
</el-button>
<el-button v-if="parseInt(currentStep) > 0" @click="$emit('prev-step')" class="guide_btn1" size="mini">
{{ $t('guide.prev') }}
</el-button>
<el-button @click="$emit('next-step')" class="guide_btn2" size="mini" type="primary">
{{ currentStep == guideStep.length - 1 ? $t('guide.complete') : $t('guide.next') }}
</el-button>
</div>
</div>
<div slot="reference" :class="{ guide_body: currentStep == step }">
<slot></slot>
</div>
</el-popover>
<div v-else>
<slot></slot>
</div>
</template>
<script>
export default {
name: 'GuidePopover',
props: {
step: {
type: Number,
required: true
},
guideStep: {
type: [Object, Array],
default: null
},
currentStep: {
type: [Number, String],
default: 0
}
}
}
</script>
<style scoped>
.guide_body {
position: relative;
z-index: 9999;
background-color: white;
border-radius: 4px;
}
.guide_box {
padding: 10px;
}
.guide_title {
font-size: 16px;
font-weight: bold;
color: #303133;
margin-bottom: 10px;
}
.guide_title span {
font-size: 12px;
color: #909399;
font-weight: normal;
}
.guide_desc {
font-size: 14px;
color: #606266;
line-height: 1.5;
margin-bottom: 15px;
}
.guide_btns {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.guide_btn1 {
color: #909399;
}
.guide_btn2 {
background-color: #409EFF;
border-color: #409EFF;
}
</style>
+3 -3
View File
@@ -12,10 +12,10 @@
:value="item.value" />
</el-select>
<el-button size="small" @click="$emit('open-design')">
{{ $t('work.design') || '创建标签' }}
{{ $t('work.design') }}
</el-button>
<el-button size="small" @click="$emit('open-file')">
{{ $t('work.import') || '选择标签' }}
{{ $t('work.import') }}
</el-button>
</div>
</div>
@@ -44,7 +44,7 @@
<div class="metadata-container mt-10">
<div class="sub-title">
<i class="el-icon-edit-outline"></i>
<span>{{ $t('work.metadataEdit') || '标签内容编辑' }}</span>
<span>{{ $t('work.metadataEdit') }}</span>
</div>
<el-table v-if="tableData && tableData.length > 0" :data="tableData" class="metadata-table flex-grow-table"
:empty-text="$t('work.nodata')" :show-header="false">
+38 -23
View File
@@ -1,25 +1,32 @@
<template>
<div class="task-header-container">
<div class="task-header-flex">
<!-- 容量选择 -->
<div class="header-item">
<label class="inline-label">{{ $t('work.size') }}</label>
<el-select v-model="sizeFormLocal" class="header-select size-select" size="small"
@change="$emit('update:size_form', $event)" :placeholder="$t('work.pleaseSelect')">
<el-option v-for="item in sizeTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
<!-- 容量选择 - 步骤8 -->
<guide-popover :step="8" :guide-step="guideStep" :current-step="currentStep"
@exit-guide="$emit('exit-guide')" @prev-step="$emit('prev-step')" @next-step="$emit('next-step')">
<div class="header-item">
<label class="inline-label">{{ $t('work.size') }}</label>
<el-select v-model="sizeFormLocal" class="header-select size-select" size="small"
@change="$emit('update:size_form', $event)" :placeholder="$t('work.pleaseSelect')">
<el-option v-for="item in sizeTypeOptions" :key="item.value" :label="item.label"
:value="item.value"
:disabled="filterPassedType && filterPassedType.length > 0 && filterPassedType.indexOf(item.value) == -1" />
</el-select>
</div>
</guide-popover>
<!-- 拷贝类型 (work.content screenshot 中显示为 "拷贝类型" 附近位置 ) -->
<div class="header-item">
<label class="inline-label">{{ $t('work.content') }}</label>
<el-select v-model="fileFormLocal" class="header-select" size="small"
@change="$emit('update:file_form', $event)">
<el-option v-for="item in fileTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
<!-- 拷贝类型 - 步骤9 -->
<guide-popover :step="9" :guide-step="guideStep" :current-step="currentStep"
@exit-guide="$emit('exit-guide')" @prev-step="$emit('prev-step')" @next-step="$emit('next-step')">
<div class="header-item">
<label class="inline-label">{{ $t('work.content') }}</label>
<el-select v-model="fileFormLocal" class="header-select" size="small"
@change="$emit('update:file_form', $event)">
<el-option v-for="item in fileTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</guide-popover>
<!-- 分区模式 (如果需要显示) -->
<div class="header-item" v-if="false">
@@ -41,14 +48,22 @@
</template>
<script>
import GuidePopover from './GuidePopover.vue'
export default {
name: 'TaskHeader',
components: {
GuidePopover
},
props: {
juanbiao_form: String,
file_form: Number,
size_form: [Number, String],
type_form: Number,
sizeType: Array
sizeType: Array,
filterPassedType: Array,
guideStep: [Object, Array],
currentStep: [Number, String]
},
data() {
return {
@@ -89,10 +104,10 @@ export default {
},
partitionOptions() {
return [
{ value: 0, label: this.$t("work.diskPart") || '普通分区' },
{ value: 1, label: this.$t("work.cdPart") || '光盘分区' },
{ value: 3, label: this.$t("work.forbidCopyPart") || '防拷贝分区' },
{ value: 5, label: this.$t("work.cdWithDisk") || '光盘+普通' }
{ value: 0, label: this.$t("work.diskPart") },
{ value: 1, label: this.$t("work.cdPart") },
{ value: 3, label: this.$t("work.forbidCopyPart") },
{ value: 5, label: this.$t("work.cdWithDisk") }
]
}
}
+634 -189
View File
@@ -6,7 +6,8 @@
<!-- 左侧配置表单 -->
<div class="header-settings">
<task-header :file_form.sync="file_form" :size_form.sync="size_form" :type_form.sync="type_form"
:size-type="sizeType" />
:size-type="sizeType" :filter-passed-type="filterPassedType" :guide-step="guideStep"
:current-step="currentStep" @exit-guide="exitGuide" @prev-step="prevStep" @next-step="nextStep" />
</div>
<!-- 右侧功能按钮 -->
@@ -34,7 +35,8 @@
<!-- 隐藏的组件 -->
<advanced-settings :visible.sync="highSettingVisible" :form="high_setting_form" :file-form="file_form"
@save="saveHighSettings" @test-recording="handleTestRecording" />
:printer-list="printerList" :size-form="size_form" @save="saveHighSettings"
@test-recording="handleTestRecording" />
<network-auth-dialog :visible="networkAuthVisible" :network-paths="networkAuthPaths"
@confirm="onNetworkAuthConfirm" @cancel="onNetworkAuthCancel" />
</div>
@@ -217,11 +219,26 @@ export default {
zip_repassword: '',
copy_hash: false,
is_dongle_count: false,
dongle_count: 1
dongle_count: 1,
auth_code: ''
}
}
},
watch: {
filterPassedType: {
handler() { this.autoSelectCapacity() },
immediate: true
},
sizeType: {
handler() { this.autoSelectCapacity() },
immediate: true
}
},
computed: {
// 容量选项 (从props同步或处理)
sizeTypeOptions() {
return this.sizeType
},
file_percent() {
if (!this.size_form || isNaN(parseFloat(this.size_form))) return 0
let mb = parseFloat(this.size_form) * 931.3
@@ -269,6 +286,34 @@ export default {
this.cancelRecordingIfActive()
},
methods: {
autoSelectCapacity() {
if (!this.sizeType || this.sizeType.length === 0) return
// 获取有效选项
let validOptions = this.sizeType
if (this.filterPassedType && this.filterPassedType.length > 0) {
validOptions = this.sizeType.filter(item =>
this.filterPassedType.indexOf(item.value) !== -1
)
}
// 如果没有有效选项,直接返回
if (validOptions.length === 0) return
// 检查当前选中值是否有效
const isCurrentValid = this.size_form && validOptions.find(o => o.value === this.size_form)
// 如果当前值无效(或者未选中)且只有唯一有效选项,则自动选中
// 或者:强制选中第一个有效选项(如果当前值无效)
if (!isCurrentValid) {
if (validOptions.length > 0) {
this.size_form = validOptions[0].value
}
}
},
exitGuide() {
this.$emit('exit-guide')
},
initData() {
const high = localStorage.getItem('high_setting_form')
if (high) {
@@ -322,6 +367,7 @@ export default {
},
saveHighSettings() {
localStorage.setItem('high_setting_form', JSON.stringify(this.high_setting_form))
// eslint-disable-next-line
this.$message.success(this.$t('dispose.successReserve'))
this.highSettingVisible = false
},
@@ -513,248 +559,608 @@ export default {
// --- 提交逻辑 ---
async upload() {
console.log('--- 开始提交作业 ---')
// 退出新手引导
if (this.guideStep) {
this.exitGuide()
}
// ========== 前置验证(所有场景共用)==========
// 1. 容量选择验证
if (!this.size_form) {
console.warn('未选择容量')
return this.$message.warning(this.$t('work.size_form_error'))
}
// 检查容量限制
if (!this.high_setting_form.Span_USBcard) {
const maxSize = (this.size_form * 1000 / 1.024 / 1.024 / 1.024) * 1024 * 1024
if (this.size > maxSize) return this.$message.warning(this.$t('work.sizeExtra'))
// 2. 初始化文件状态(对齐 legacy)
for (let i in this.$refs.files.filesList) {
this.$refs.files.filesList[i].state = false
}
this.$refs.files.nowstate = true
// ========== 核心分支:基于 allNumber 判断 ==========
if (this.$refs.files.allNumber > 0) {
// ============ 有内容文件的分支 ============
console.log('检测到内容文件数量:', this.$refs.files.allNumber)
// 容量限制检查(非跨卡模式)
if (!this.high_setting_form.Span_USBcard && this.size > ((this.size_form * 1000) / 1.024 / 1.024 / 1.024) * 1024 * 1024) {
return this.$message.warning(this.$t('work.sizeExtra'))
}
// 双重校验(防御性编程,理论上不会触发)
if (this.$refs.files.allNumber == 0) {
return this.$message.warning(this.$t('work.pleaseUploadContent'))
}
// 设置内容上传标志
this.flag_cont_up = true
this.upload_flag = true
// 本地文件模式判断
if (this.high_setting_form.localfiles) {
console.log('本地文件模式,跳过内容上传')
return // 直接返回,等待外部触发或用户操作
}
// 检查文件计算状态
let t = true
for (let i in this.$refs.files.filesList) {
if (this.$refs.files.filesList[i].size == -1) {
t = false
}
}
if (!t) {
return this.$message.warning(this.$t('work.waitCalculate'))
}
// 设置上传参数并启动
this.$refs.files.isCopy = this.isCopy
this.$refs.files.copyPath = this.shareDisk + '\\\\' + this.upload_disk + '\\\\data\\\\'
this.$refs.files.resume(this.file_form)
} else {
// ============ 无内容文件的分支(只有标签)============
console.log('无内容文件,进入标签上传流程')
// 构建 FormData
let files_n_incr = 1
let data1 = new FormData()
// 标签文件校验与添加
if (this.isNew) {
// 新建模式:必须有 CS 文件
if (this.fileLists[0]) {
data1.append(`file${files_n_incr}`, this.fileLists[0])
files_n_incr++
} else {
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
} else {
// 编辑模式:必须有 fileData 或 fileLists[0]
if (this.fileData && !this.fileLists[0]) {
// 纯打开,上传 AS 文件(JSON)
let _file = new File([JSON.stringify(this.fileData)], this.saveWorkList.taskName, { type: 'text/plain' })
data1.append(`file${files_n_incr}`, _file)
files_n_incr++
} else if (this.fileData && this.fileLists[0]) {
// 打开后重新选择了 CS 文件
data1.append(`file${files_n_incr}`, this.fileLists[0])
files_n_incr++
} else {
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
}
// CSV 合并文件
if (this.$refs.refFile2.files.length != 0) {
data1.append(`file${files_n_incr}`, this.$refs.refFile2.files[0])
data1.append(`csv`, true)
files_n_incr++
} else {
data1.append(`csv`, false)
}
// 元数据字段(图片、文本、条码)
for (let item of this.tableData) {
if (item.type == 1 && this.metadataFiles[item.origin_name] && this.$refs.refFile2.files.length != 0) {
// 有合并文件的图片
data1.append(`file${files_n_incr}`, this.metadataFiles[item.origin_name])
files_n_incr++
} else if (item.type == 1 && this.metadataFiles[item.origin_name] && this.$refs.refFile2.files.length == 0) {
// 无合并文件的图片
data1.append(item.origin_name, this.metadataFiles[item.origin_name])
} else if ((item.type == 3 || item.type == 5) && this.$refs.refFile2.files.length == 0) {
// 文本与条形码
let data = this.form[item.origin_name]
if (data == '' || data == undefined || data == null) {
data = item.default
}
data1.append(item.origin_name, data)
} else if (item.type == 4 && this.$refs.refFile2.files.length == 0) {
// 二维码
data1.append(item.origin_name, this.form[item.origin_name])
}
}
// 上传标签文件
this.$message({ offset: 100, message: this.$t('work.uploadingTag') })
this.$axios({
method: 'post',
url: '/upload/' + this.upload_disk,
headers: { 'Content-Type': 'multipart/form-data;boundary=' + new Date().getTime() },
data: data1
}).then((res) => {
this.submit() // 调用提交函数
})
}
},
upload_over() {
if (this.flag_cont_up) {
// 检查是否需要上传标签(只有内容时不上传标签)
let skipTagUpload = false
// 1. 检查是否有标签文件
let hasTagFile = false
if (this.isNew) {
if (this.fileLists && this.fileLists.length > 0) {
hasTagFile = true
}
} else {
if (this.fileData || (this.fileLists && this.fileLists.length > 0)) {
hasTagFile = true
}
}
// 2. 检查是否有 CSV 合并文件
const hasCsv = this.$refs.refFile2 && this.$refs.refFile2.files && this.$refs.refFile2.files.length > 0
// 3. 检查是否有实际的图片元数据文件
const hasImageFiles = !!(this.metadataFiles && Object.keys(this.metadataFiles).length > 0)
// 判断:如果没有任何标签相关内容,跳过标签上传
if (!hasTagFile && !hasCsv && !hasImageFiles) {
skipTagUpload = true
}
if (skipTagUpload) {
this.performSubmit()
} else {
this.submitAndTag()
}
} else {
this.performSubmit()
}
},
async submit() {
// 停止录制(如果正在录制)
await this.stopRecordingIfActive()
// 检查是否有网络路径需要认证
if (this.checkNetworkPaths && this.checkNetworkPaths()) {
return // 等待用户完成网络认证
}
// ISO/ZIP 前置验证
if (this.high_setting_form.is_generate_iso && !this.high_setting_form.iso_file_name) {
return this.$message.warning(this.$t('work.isoNameInput'))
// 继续提交流程
this.performSubmit()
},
async submitAndTag() {
const data1 = new FormData()
let files_n_incr = 1
// 1. 标签文件 (Label File)
let hasLabelFile = false
if (this.isNew) {
if (this.fileLists && this.fileLists[0]) {
data1.append(`file${files_n_incr}`, this.fileLists[0])
files_n_incr++
hasLabelFile = true
}
} else {
if (this.fileData && !this.fileLists[0]) {
// 纯打开,上传 AS 文件 (JSON)
let _file = new File([JSON.stringify(this.fileData)], this.saveWorkList.taskName, { type: 'text/plain' })
data1.append(`file${files_n_incr}`, _file)
files_n_incr++
hasLabelFile = true
} else if (this.fileData && this.fileLists[0]) {
// 重新选择了 CS 文件
data1.append(`file${files_n_incr}`, this.fileLists[0])
files_n_incr++
hasLabelFile = true
}
}
if (this.high_setting_form.is_generate_zip) {
if (!this.high_setting_form.zip_file_name) return this.$message.warning(this.$t('work.zipNameInput'))
if (this.high_setting_form.is_zip_encrypt) {
if (!this.high_setting_form.zip_password) return this.$message.warning(this.$t('work.pleasePassword'))
if (this.high_setting_form.zip_password !== this.high_setting_form.zip_repassword) {
// 校验:若开启标签但无文件且无内容区文件
if (!hasLabelFile && this.switch_tag) {
const hasContentFiles = this.$refs.files && this.$refs.files.allNumber > 0;
if (!hasContentFiles) {
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
}
// 2. CSV 合并文件
const hasCsv = this.$refs.refFile2 && this.$refs.refFile2.files && this.$refs.refFile2.files.length > 0
if (hasCsv) {
data1.append(`file${files_n_incr}`, this.$refs.refFile2.files[0])
data1.append(`csv`, true)
files_n_incr++
} else {
data1.append(`csv`, false)
}
// 3. 元数据 (图片、文本、条码)
if (this.tableData && this.tableData.length > 0) {
for (let item of this.tableData) {
// 图片类型 (Type 1)
if (item.type == 1 && this.metadataFiles[item.origin_name]) {
const file = this.metadataFiles[item.origin_name]
if (hasCsv) {
// 有CSV时,图片按 file{n} 顺序添加
data1.append(`file${files_n_incr}`, file)
files_n_incr++
} else {
// 无CSV时,图片按字段名添加
data1.append(item.origin_name, file)
}
}
// 文本/条码类型 (Type 3, 5) - 仅在无CSV时添加
else if ((item.type == 3 || item.type == 5) && !hasCsv) {
let data = this.form[item.origin_name]
if (data == '' || data === undefined || data === null) {
data = item.default || ''
}
data1.append(item.origin_name, data)
}
// 二维码类型 (Type 4) - 仅在无CSV时添加
else if (item.type == 4 && !hasCsv) {
let data = this.form[item.origin_name] || ''
data1.append(item.origin_name, data)
}
}
}
// 4. 发送请求
this.$message.info(this.$t('work.uploadingTag'))
let pathName
if (this.high_setting_form.localfiles) {
// 若开启本地文件,需获取本地路径名称
const rootFile = this.$refs.files && this.$refs.files.getLists && this.$refs.files.getLists()[0]; // 假设逻辑
// 这里实际上 work-副本 使用 upload_disk 或者 rootFile name,这里保持 upload_disk 除非是 pathName 逻辑差异
// work-副本 逻辑: localfiles ? filePath.name : upload_disk
if (this.high_setting_form.localfiles) {
// 获取根文件夹名
// 由于 fileManagement 封装,我们需要更稳健的获取方式
// 暂时使用 upload_disk,待 localUpload 逻辑覆盖
pathName = this.upload_disk // 这里的差异在 submit 逻辑中处理,Tag 上传通常还是到 upload_disk 临时目录?
// work-副本: localfiles ? filePath.name : this.upload_disk
// 这里保持一致性
pathName = this.upload_disk
} else {
pathName = this.upload_disk
}
} else {
pathName = this.upload_disk
}
// 修正:如果 localfiles 为真,work-副本 实际上是把 tag 文件上传到以 filePath.name 命名的目录
// 但这里简化处理,先上传 tag,后续 localUpload 会处理 content
// 实际上 submitAndTag 是为了上传 tag 文件,localUpload 是为了处理 content 文件的 path.json
this.$axios.post('/upload/' + this.upload_disk, data1).then(() => {
this.performSubmit()
}).catch(err => {
console.error(err)
this.$message.error(this.$t('work.uploadFail'))
})
},
async performSubmit() {
let that = this;
try {
await this.stopRecordingIfActive()
} catch (e) {
console.error('停止录像失败:', e)
}
// 检查是否有网络路径需要认证
if (this.checkNetworkPaths && this.checkNetworkPaths()) {
return
}
// 1. 确定 pathName
let pathName
if (that.high_setting_form.localfiles) {
//打开本地选项
const uploader = this.$refs.files && this.$refs.files.uploader
let rootFile = uploader && uploader.getRoot()
if (!rootFile || rootFile.fileList.length != 1) {
this.$message.warning(this.$t('work.oneFolder'))
return
}
let filePath = rootFile.fileList[0]
if (!filePath.isFolder) {
this.$message.warning(this.$t('work.notFolder'))
return
}
pathName = filePath.name
} else {
pathName = this.upload_disk
}
// 2. 构建 data_param
let data_param = 'CardSoon_File=' + pathName
// 3. 构建 data (严格按照 legacy 顺序)
let data = ''
// 3.1 ISO/ZIP 验证(仅在有内容文件时)
if (this.$refs.files.allNumber > 0) {
// ISO 文件名检查
if (that.high_setting_form.is_generate_iso) {
if (!that.high_setting_form.iso_file_name) {
this.submitLoading = false
return this.$message.warning(this.$t('work.isoNameInput'))
}
}
// ZIP 文件名检查
if (that.high_setting_form.is_generate_zip) {
if (!that.high_setting_form.zip_file_name) {
this.submitLoading = false
return this.$message.warning(this.$t('work.zipNameInput'))
}
}
// ZIP 加密密码检查
if (that.high_setting_form.is_generate_zip && that.high_setting_form.is_zip_encrypt) {
if (!that.high_setting_form.zip_password || that.high_setting_form.zip_password !== that.high_setting_form.zip_repassword) {
this.submitLoading = false
return this.$message.warning(this.$t('work.zipPassWrong'))
}
}
}
const hasFiles = this.$refs.files && this.$refs.files.allNumber > 0;
if (hasFiles) {
console.log('检测到待上传文件数量:', this.$refs.files.allNumber)
this.flag_cont_up = true
this.$refs.files.resume(this.file_form)
} else {
console.log('无待上传文件或未打开内容管理,直接执行提交')
this.upload_over()
}
},
upload_over() {
console.log('文件上传/准备就绪, flag_cont_up:', this.flag_cont_up)
if (this.flag_cont_up) {
this.submitAndTag()
} else {
this.performSubmit()
}
},
async submitAndTag() {
console.log('--- 执行 submitAndTag ---')
// 这里的逻辑处理标签文件的上传
const data1 = new FormData()
let hasFile = false
if (this.isNew) {
if (this.fileLists[0]) {
data1.append('file1', this.fileLists[0])
hasFile = true
// 3.2 标签文件验证与参数构建(仅在有标签文件时)
if ((!this.isNew && this.fileData) || (this.isNew && this.fileLists[0])) {
// 标签开启校验
if (this.isNew && this.fileLists.length == 0) {
this.submitLoading = false
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
if (!this.isNew && !this.fileData) {
this.submitLoading = false
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
} else if (this.fileData) {
const _file = new File([JSON.stringify(this.fileData)], this.saveWorkList.taskName, { type: 'text/plain' })
data1.append('file1', _file)
hasFile = true
}
if (!hasFile && this.switch_tag) {
console.warn('标签管理已打开但未上传标签文件')
return this.$message.warning(this.$t('work.pleaseUploadTag'))
}
if (this.fileData && !this.fileLists[0]) {
data_param += '&Json_File=' + this.saveWorkList.taskName
} else if (this.fileData && this.fileLists[0]) {
data_param += '&Json_File=' + this.fileLists[0].name
}
// 添加 CSV 文件
if (this.$refs.refFile2 && this.$refs.refFile2.files[0]) {
data1.append('file2', this.$refs.refFile2.files[0])
}
// 添加 Bin/Img 文件 (refFile3)
if (this.$refs.refFile3 && this.$refs.refFile3.files[0]) {
data1.append('file3', this.$refs.refFile3.files[0])
}
if (this.$refs.refFile2.files.length != 0) {
data_param += '&Udf_File=file:' + this.$refs.refFile2.files[0].name
}
// 添加元数据图片
if (this.tableData && this.tableData.length > 0) {
this.tableData.forEach(row => {
if (row.type == 1 && this.metadataFiles[row.origin_name]) {
data1.append(row.origin_name, this.metadataFiles[row.origin_name])
// 打印面数校验
if (this.fileData) {
if ((this.print_flag == 1 && this.fileData.flag != 1) || (this.print_flag == 2 && this.fileData.flag == 3) || (this.print_flag == 3 && this.fileData.flag == 2)) {
this.submitLoading = false
return this.$message.warning(this.$t('work.print_flagError'))
}
})
}
data += '&print_flag=' + this.print_flag
}
this.$message.info(this.$t('work.uploadingTag'))
const pathName = this.high_setting_form.localfiles ? 'local' : this.upload_disk
this.$axios.post('/upload/' + pathName, data1).then(() => {
this.performSubmit()
}).catch(err => {
this.$message.error(this.$t('work.uploadFail'))
})
},
async performSubmit() {
console.log('--- 执行 performSubmit ---')
try {
await this.stopRecordingIfActive()
console.log('录像停止检查完成')
} catch (e) {
console.error('停止录像失败:', e)
}
this.submitLoading = true
console.log('submitLoading 已设为 true')
// 构建原始参数字符串
let data_param = 'CardSoon_File=' + (this.high_setting_form.localfiles ? 'local' : this.upload_disk)
console.log('--- 提交作业数据预览 ---')
console.log('任务ID:', this.upload_disk)
console.log('高级设置:', JSON.parse(JSON.stringify(this.high_setting_form)))
// 拼接标签文件、CSV等参数(简化演示,实际应完整复制)
let data = `&label=${encodeURIComponent(this.juanbiao_form)}`
data += `&printCopys=${this.number}`
data += `&disk_size=${this.size_form}`
data += `&zone_type=${this.type_form}`
data += `&hasPrintTask=${!!this.switch_tag}`
data += `&hasCopyTask=${this.$refs.files ? this.$refs.files.allNumber > 0 : false}`
data += `&copy_cache_data=${this.isCopy}`
data += `&SpanUcard=${this.high_setting_form.Span_USBcard}`
data += `&hash=${this.high_setting_form.s1}`
data += `&md5=${this.high_setting_form.s2}`
data += `&printer=${this.high_setting_form.target_work}`
if (this.high_setting_form.formatFile !== 0) {
data += `&formatFile=${this.high_setting_form.formatFile}`
// 3.3 卷标验证(所有场景必填)
if (this.juanbiao_form == '') {
this.submitLoading = false
return this.$message.warning(this.$t('work.juanbiaoInput'))
}
// --- 兼容旧版字段与新增强制提交字段 ---
// 基础开关
data += `&local=${!!this.high_setting_form.localfiles}`
data += `&version=local` // 单机版标识
data += `&hasAddFile=${!!this.high_setting_form.hasAddFile}` // 拷贝附加文件
const printStatus = this.fileLists[0] ? true : false
const copyStatus = this.$refs.files.allNumber > 0 ? true : false
// ISO/ZIP 高级功能
data += `&is_generate_iso=${!!this.high_setting_form.is_generate_iso}`
data += `&iso_file_name=${encodeURIComponent(this.high_setting_form.iso_file_name || '')}`
data += `&is_generate_zip=${!!this.high_setting_form.is_generate_zip}`
data += `&zip_file_name=${encodeURIComponent(this.high_setting_form.zip_file_name || '')}`
data += `&is_zip_encrypt=${!!this.high_setting_form.is_zip_encrypt}`
data += `&zip_password=${encodeURIComponent(this.high_setting_form.zip_password || '')}`
data += '&label=' + encodeURIComponent(this.juanbiao_form)
data += '&printCopys=' + this.number
data += '&disk_size=' + this.size_form
data += '&zone_type=' + this.type_form
data += '&hasPrintTask=' + printStatus
data += '&hasCopyTask=' + copyStatus
data += '&copy_cache_data=' + this.isCopy
data += '&SpanUcard=' + this.high_setting_form.Span_USBcard
data += '&hasAddFile=' + this.high_setting_form.hasAddFile
data += '&version=local'
data += '&hash=' + this.high_setting_form.s1
data += '&md5=' + this.high_setting_form.s2
data += '&printer=' + this.high_setting_form.target_work
// 硬件与安全
data += `&copy_hash=${!!this.high_setting_form.copy_hash}`
data += `&enable_dongle_counter=${!!this.high_setting_form.is_dongle_count}`
data += `&donglel_install_count=${this.high_setting_form.dongle_count || 0}`
// 录像与标识
data += `&is_blend=${!!this.high_setting_form.is_blend}`
data += `&is_printer_record_logo=${!!this.high_setting_form.is_print_logo}`
// 录像路径逻辑:如果正在录像或已结束,则传递路径
if (this.high_setting_form.is_record) {
// 优先使用录像组件反馈的路径,否则使用配置路径
const videoPath = (this.$refs.screenRecorder && this.$refs.screenRecorder.videoPath) || this.high_setting_form.record_path
data += `&record_path=${encodeURIComponent(videoPath || '')}`
} else {
data += `&record_path=`
if (this.high_setting_form.formatFile != 0) {
data += '&formatFile=' + this.high_setting_form.formatFile
}
// 文件类型映射 (0:File -> 1, 1:ISO -> 2, 2:Encrypt -> 3, 4:Forbid -> 4)
const fileTypeMap = { 0: 1, 1: 2, 2: 3, 4: 4 }
let apiFileType = fileTypeMap[this.file_form] || 1
data += `&file_type=${apiFileType}`
// 网络路径认证信息 (若存在)
// 网络认证信息
if (this.networkCredentials && (Array.isArray(this.networkCredentials) ? this.networkCredentials.length > 0 : Object.keys(this.networkCredentials).length > 0)) {
data += `&net_info=${encodeURIComponent(JSON.stringify(this.networkCredentials))}`
data += '&net_info=' + JSON.stringify(this.networkCredentials)
}
console.log('--- 最终提交 URL 参数 ---')
console.log(data_param + data)
// 高级设置参数 - New Fields Mapping
data += '&is_generate_iso=' + (String(that.high_setting_form.is_generate_iso) || 'false') +
'&iso_file_name=' + (that.high_setting_form.iso_file_name || '') +
'&is_generate_zip=' + (String(that.high_setting_form.is_generate_zip) || 'false') +
'&zip_file_name=' + (that.high_setting_form.zip_file_name || '') +
'&is_zip_encrypt=' + (String(that.high_setting_form.is_zip_encrypt) || 'false') +
'&zip_password=' + (that.high_setting_form.zip_password || '') +
'&copy_hash=' + (String(that.high_setting_form.copy_hash) || 'false') +
'&enable_dongle_counter=' + (String(that.high_setting_form.enable_dongle_counter) || 'false') +
'&auth_code=' + (that.high_setting_form.auth_code || '');
const url = '/rest/job/?' + data_param + data
data += '&is_blend=' + (String(that.high_setting_form.is_blend) || 'false');
if (!this.isCopy) {
console.log('执行本地路径提交 (Local Mode)')
this.localUpload(this.upload_disk, data_param, data)
// Log path logic
if (that.high_setting_form.record_screen && that.$refs.screenRecorder && that.$refs.screenRecorder.videoPath) {
data += '&record_path=' + that.$refs.screenRecorder.videoPath;
} else {
console.log('执行普通网络提交 (Copy Mode)')
this.$axios.post(url)
.then(() => {
data += '&record_path=';
}
data += '&is_printer_record_logo=' + (String(that.high_setting_form.print_record_logo) || 'false');
if (that.high_setting_form.enable_dongle_counter) {
data += '&donglel_install_count=' + (that.high_setting_form.install_dongle_count || 0);
} else {
data += '&donglel_install_count=0';
}
// File Type Mapping
const fileTypeMap = { 0: 1, 1: 2, 2: 3, 4: 4 };
let apiFileType = fileTypeMap[that.file_form] || 1;
data += '&file_type=' + apiFileType;
console.log('提交参数预览:', data_param + data)
// Submit Flow
if (this.isCopy) {
this.submitLoading = true
this.$axios({
method: 'post',
url: '/rest/job/?' + data_param + data
})
.then((res) => {
this.$message.success(this.$t('work.submitSuccess'))
that.submitLoading = false
this.$emit('jobPost')
})
.catch((err) => {
console.error('提交任务失败:', err)
this.$message.error(this.$t('index.fail'))
const resl = err.response.data
console.log(resl)
if (resl.ret && resl.ret === 9) {
that.$emit('addError', { code: resl.ret, tag: 'templateFile', err: 0 })
} else {
that.$emit('addError', { code: resl.ret, tag: 'workFail', err: 0 })
}
this.$message({ offset: 100, message: this.$t('work.submiting') })
that.submitLoading = false
that.$emit('jobPost')
})
.finally(() => this.submitLoading = false)
} else {
that.localUpload(pathName, data_param, data)
}
},
localUpload(pathName, data_param, data) {
const filesList = this.$refs.files.getLists()
let file_path = []
for (let i in filesList) {
file_path.push(filesList[i].path)
// 注意: 这里使用 getLists() 可能需要适配 legacy 的 filesList 结构
// 如果 filesList[i].path 是正确路径,则无需更改
const list = this.$refs.files.filesList // Prefer direct filesList if available like legacy
for (let i in list) {
file_path.push(list[i].path)
}
const jsonData = { files: file_path }
const jsonFilePath = path.join(remote.app.getPath('userData'), 'filepath.json')
const jsonFilePath = path.join(remote.app.getPath('userData'), 'filepath.json') // 使用 userData 目录更安全
try {
fs.writeFileSync(jsonFilePath, JSON.stringify(jsonData), 'utf-8')
const fileContent = fs.readFileSync(jsonFilePath)
const blob = new Blob([fileContent], { type: 'application/json' })
const formData = new FormData()
formData.append('file', blob, 'filepath.json')
// Legacy Stream Logic
const fileStream = fs.createReadStream(jsonFilePath)
const buffer = []
fileStream.on('data', (chunk) => {
buffer.push(chunk)
})
fileStream.on('end', () => {
let that = this
const blob = new Blob([Buffer.concat(buffer)], { type: 'application/octet-stream' })
if (this.networkCredentials && (Array.isArray(this.networkCredentials) ? this.networkCredentials.length > 0 : Object.keys(this.networkCredentials).length > 0)) {
formData.append('net_info', JSON.stringify(this.networkCredentials))
}
const formData = new FormData()
formData.append('file', blob, path.basename(jsonFilePath))
this.submitLoading = true
this.$axios.post(`/upload/${pathName}`, formData).then(() => {
this.$axios.post('/rest/job/?' + data_param + data).then(() => {
this.$message.success(this.$t('work.submitSuccess'))
this.$emit('jobPost')
if (this.networkCredentials && (Array.isArray(this.networkCredentials) ? this.networkCredentials.length > 0 : Object.keys(this.networkCredentials).length > 0)) {
formData.append('net_info', JSON.stringify(this.networkCredentials))
}
that.submitLoading = true
that.$axios({
method: 'post',
url: `/upload/${pathName}`,
headers: { 'Content-Type': 'multipart/form-data;boundary=' + new Date().getTime() },
data: formData
}).then(() => {
that.$axios({
method: 'post',
url: '/rest/job/?' + data_param + data
}).then((res) => {
this.$message.success(this.$t('work.submitSuccess'))
that.submitLoading = false
this.$emit('jobPost')
}).catch((err) => {
const resl = err.response && err.response.data
if (resl && resl.ret === 9) {
that.$emit('addError', { code: resl.ret, tag: 'templateFile', err: 0 })
} else {
that.$emit('addError', { code: resl ? resl.ret : -1, tag: 'workFail', err: 0 })
}
this.$message({ offset: 100, message: this.$t('work.submiting') })
that.submitLoading = false
that.$emit('jobPost')
})
}).catch(err => {
this.$message.error(this.$t('index.fail'))
}).finally(() => this.submitLoading = false)
}).catch(err => {
this.$message.error('Local upload failed')
this.submitLoading = false
this.$message.error('Local upload failed')
that.submitLoading = false
})
})
} catch (e) {
console.error(e)
this.$message.error('File Write Error')
this.submitLoading = false
}
},
saveWork() {
const save = {
fileData: this.fileData,
tableData: this.tableData,
juanbiao: this.juanbiao_form,
size_form: this.size_form,
type_form: this.type_form,
high_setting: this.high_setting_form
let t = true
for (let i in this.$refs.files.filesList) {
if (this.$refs.files.filesList[i].size == -1) {
t = false
}
}
if (t) {
let save = this.fileData
save.filesList = this.$refs.files.getLists()
//save.tagFile = this.fileData;
save.allNumber = this.$refs.files.allNumber
save.size = this.size
save.file_form = this.file_form == 2 && this.isPass ? 3 : this.file_form
save.type_form = this.type_form
save.size_form = this.size_form
save.juanbiao_form = this.juanbiao_form
save.high_setting_form = this.high_setting_form
save.print_flag = this.print_flag
// save.tableData = this.tableData;
if (this.$refs.files.allNumber > 0 && this.fileLists[0]) {
save.switch = 3
} else if (this.$refs.files.allNumber > 0) {
save.switch = 1
} else {
save.switch = 2
}
const v = JSON.stringify(save)
dialog
.showSaveDialog({
title: 'Save',
filters: [{ name: 'Soon Work', extensions: ['swk'] }]
})
.then((result) => {
fs.writeFileSync(result.filePath, v)
})
.catch((err) => {
console.log(err)
})
} else {
this.$message({
offset: 100,
message: this.$t('work.waitCalculate'),
type: 'warning'
})
}
dialog.showSaveDialog({
title: 'Save Task',
filters: [{ name: 'Soon Work', extensions: ['swk'] }]
}).then(result => {
if (result.filePath) fs.writeFileSync(result.filePath, JSON.stringify(save))
})
},
// --- 新手引导功能 ---
@@ -896,6 +1302,45 @@ export default {
},
sizeChange(size) {
this.size = size
},
// --- 新手引导方法 ---
exitGuide() {
if (!this.guideStep) return
for (let key in this.guideStep) {
this.guideStep[key].show = false
}
this.currentStep = -1
localStorage.setItem('currentStep', this.currentStep)
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
},
prevStep() {
if (!this.guideStep) return
this.guideStep[this.currentStep].show = false
this.currentStep = parseInt(this.currentStep) - 1
if (this.guideStep[this.currentStep]) {
this.guideStep[this.currentStep].show = true
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
localStorage.setItem('currentStep', this.currentStep)
} else {
this.exitGuide()
}
},
nextStep() {
if (!this.guideStep) return
this.guideStep[this.currentStep].show = false
this.currentStep = parseInt(this.currentStep) + 1
if (this.guideStep[this.currentStep]) {
this.guideStep[this.currentStep].show = true
localStorage.setItem('guideStep', JSON.stringify(this.guideStep))
localStorage.setItem('currentStep', this.currentStep)
} else {
this.exitGuide()
}
},
help() {
const { ipcRenderer } = require('electron')
ipcRenderer.send('open-help-file')
}
}
}
@@ -1,7 +1,7 @@
<template>
<div class="grand-section">
<div class="grand-title">
<span>{{ $t('work.advancedFeatures') || '高级功能' }}</span>
<span>{{ $t('work.advancedFeatures') }}</span>
<div class="title-line"></div>
</div>
@@ -102,9 +102,12 @@
<!-- 计数器详细 -->
<div v-if="form.is_dongle_count" class="mt-10 p-10"
style="background: #fdf6ec; border-radius: 6px; border: 1px solid #faecd8;">
<el-form-item :label="$t('work.installCount')" label-width="140px" class="mb-0">
<el-form-item :label="$t('work.installCount')" label-width="140px" class="mb-5">
<el-input-number v-model="form.dongle_count" :min="1" :step="1" :precision="0" size="mini" />
</el-form-item>
<el-form-item :label="$t('work.authCode')" label-width="140px" class="mb-0">
<el-input v-model="form.auth_code" size="mini" :placeholder="$t('work.inputAuthCode')" />
</el-form-item>
</div>
</div>
</template>
@@ -1,12 +1,12 @@
<template>
<div class="grand-section">
<div class="grand-title">
<span>{{ $t('work.basicConfig') || '基础配置' }}</span>
<span>{{ $t('work.basicConfig') }}</span>
<div class="title-line"></div>
</div>
<el-row :gutter="20">
<el-col :span="12" v-if="fileForm !== 2">
<el-form-item :label="$t('work.better') || '优先级'">
<el-form-item :label="$t('work.better')">
<el-select v-model="form.priority" class="w-full" :disabled="fileForm === 4">
<el-option v-for="item in priorityOptions" :key="item.value" :label="item.label"
:value="item.value" />
@@ -14,7 +14,7 @@
</el-form-item>
</el-col>
<el-col :span="12" v-if="fileForm !== 2">
<el-form-item :label="$t('work.startWorkSpace') || '目标工作站'">
<el-form-item :label="$t('work.startWorkSpace')">
<el-select v-model="form.target_work" class="w-full" :disabled="fileForm === 4">
<el-option v-for="item in targetWorkOptions" :key="item.value" :label="item.label"
:value="item.value" />
@@ -22,7 +22,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('work.ribbonType') || '色带类型'">
<el-form-item :label="$t('work.ribbonType')">
<el-select v-model="form.color_type" class="w-full">
<el-option v-for="item in colorTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
@@ -30,7 +30,7 @@
</el-form-item>
</el-col>
<el-col :span="12" v-if="fileForm === 0 || fileForm === 2">
<el-form-item :label="$t('work.formatFile') || '拷贝前格式化'">
<el-form-item :label="$t('work.formatFile')">
<el-select v-model="form.formatFile" class="w-full">
<el-option v-for="item in formatOptions" :key="item.value" :label="item.label"
:value="item.value" />
@@ -52,30 +52,68 @@ export default {
fileForm: {
type: Number,
default: 0
},
printerList: {
type: Array,
default: () => []
},
sizeForm: {
type: [Number, String],
default: null
}
},
computed: {
priorityOptions() {
return [
{ value: 1, label: this.$t("work.low") || '低' },
{ value: 0, label: this.$t("work.normal") || '中' },
{ value: 2, label: this.$t("work.high") || '高' }
{ value: 1, label: this.$t("work.low") },
{ value: 0, label: this.$t("work.normal") },
{ value: 2, label: this.$t("work.high") }
]
},
targetWorkOptions() {
// 这里原本是从父组件传入,或者硬编码
return [{ value: 0, label: this.$t("work.any") || '任一' }]
let list = [{ value: 0, label: this.$t("work.any") }]
if (this.printerList && this.printerList.length > 0) {
const requiredSize = parseFloat(this.sizeForm)
let filtered = this.printerList
// Filter by capacity if size is selected
if (requiredSize && requiredSize > 0) {
filtered = this.printerList.filter(p => {
if (!p.PrinterType) return false
const type = p.PrinterType.toUpperCase()
if (requiredSize < 1) {
// MB case (e.g. 0.512 -> 512M)
const mb = Math.round(requiredSize * 1000)
return type.includes(mb + "M")
} else {
// GB case (e.g. 4 -> 4G)
return type.includes(requiredSize + "G")
}
})
}
// Map to options
if (filtered.length > 0) {
list = list.concat(filtered.map(p => ({
value: p.PrinterID,
label: `${p.PrinterID} (${p.PrinterType})`
})))
}
}
return list
},
colorTypeOptions() {
return [
{ value: 0, label: this.$t("work.any") || '任一' },
{ value: 1, label: this.$t("work.sigleColor") || '单色' },
{ value: 2, label: this.$t("work.colorful") || '彩色' }
{ value: 0, label: this.$t("work.any") },
{ value: 1, label: this.$t("work.sigleColor") },
{ value: 2, label: this.$t("work.colorful") }
]
},
formatOptions() {
return [
{ value: 0, label: this.$t('work.auto') || '不格式化' },
{ value: 0, label: this.$t('work.auto') },
{ value: 1, label: 'FAT32' },
{ value: 2, label: 'exFAT' },
{ value: 3, label: 'NTFS' }
@@ -1,7 +1,7 @@
<template>
<div class="grand-section">
<div class="grand-title">
<span>{{ $t('work.hardwareControl') || '硬件管控' }}</span>
<span>{{ $t('work.hardwareControl') }}</span>
<div class="title-line"></div>
</div>
<el-row :gutter="20">
@@ -9,12 +9,12 @@
<div class="hardware-box">
<div class="mb-10">
<el-checkbox v-model="form.is_dongle_count">
{{ $t('work.installDongle') || '加密狗计数' }}
{{ $t('work.installDongle') }}
</el-checkbox>
</div>
<transition name="el-zoom-in-top">
<div v-if="form.is_dongle_count" class="ml-20">
<el-form-item :label="$t('work.installCount') || '安装次数'" label-width="80px">
<el-form-item :label="$t('work.installCount')" label-width="80px">
<el-input-number size="small" v-model="form.dongle_count" :min="1" :max="999" />
</el-form-item>
</div>
@@ -1,7 +1,7 @@
<template>
<div class="grand-section">
<div class="grand-title">
<span>{{ $t('work.screenRecordReview') || '屏幕记录审查' }}</span>
<span>{{ $t('work.screenRecordReview') }}</span>
<div class="title-line"></div>
</div>
<div class="feature-row mb-10">
+7 -2
View File
@@ -25,7 +25,8 @@ export default {
cancel: "取消",
authRequired: "请填写所有网络路径的用户名和密码",
authSuccess: "网络认证信息已保存",
authCanceled: "已取消网络路径认证"
authCanceled: "已取消网络路径认证",
authSaveFail: "保存认证信息失败"
},
finger: {
intTips: "指纹模块正在初始化",
@@ -525,12 +526,13 @@ export default {
defaultPath: "项目根目录/videos",
recordingStatus: "录制状态",
recording: "录制中...",
recordingCanceled: "录制已取消",
notRecording: "未录制",
// 硬件管控
hardwareControl: "硬件管控",
installDongle: "加密狗计数",
installCount: "安装次数",
setAuthCode: "设置授权码",
authCode: "授权码",
inputAuthCode: "输入授权码",
addContent: "添加内容",
addTag: "添加标签",
@@ -569,6 +571,9 @@ export default {
oneFolder: "特定作业只能添加一个文件夹",
notFolder: "添加内容不是文件夹",
print_flagError: "打印面数不匹配,请重新选择!",
cannotFindBin: "模板中没有合并字段,无法使用CSV文件",
pleaseSelectCap: "请选择容量",
dongleCountRequired: "请输入有效的加密狗安装次数(必须是正整数)",
pleaseUploadTag: "请添加标签文件",
uploadingTag: "正在上传标签文件...",
pleaseUploadingImgBin: "请添加图片文件",
+12 -5
View File
@@ -6,8 +6,10 @@ export default {
ok: "OK"
},
recorder: {
startRecording: "Start Recording",
stopRecording: "Stop Recording",
recordingStatus: "Recording Status",
recording: "Recording...",
recordingCanceled: "Recording canceled",
notRecording: "Not recording",
recordingStarted: "Screen recording started",
recordingStopped: "Recording stopped",
savedSuccess: "Recording saved",
@@ -23,9 +25,10 @@ export default {
password: "Password",
confirm: "Confirm",
cancel: "Cancel",
authRequired: "Please fill in username and password for all network paths",
authSuccess: "Network authentication information saved",
authCanceled: "Network path authentication canceled"
authRequired: "Please enter username and password for all network paths",
authSuccess: "Network credentials saved",
authCanceled: "Network authentication canceled",
authSaveFail: "Failed to save authentication info"
},
finger: {
intTips: "The fingerprint module is initializing",
@@ -452,6 +455,7 @@ export default {
hardwareControl: "Hardware Control",
installDongle: "Dongle Count",
installCount: "Install Count",
authCode: "Auth Code",
setAuthCode: "Auth Code",
inputAuthCode: "Enter Auth Code",
// Metadata
@@ -502,6 +506,9 @@ export default {
oneFolder: "Only one folder can be added for Special jobs",
notFolder: "Need to add folder",
print_flagError: "print type does not match",
cannotFindBin: "Template has no merge fields, cannot use CSV file",
pleaseSelectCap: "Please select capacity",
dongleCountRequired: "Please enter a valid dongle count (must be a positive integer)",
pleaseUploadTag: "Please add a Lable file",
uploadingTag: "Uploading label file",
pleaseUploadingImgBin: "Please add a pic file",
+8 -1
View File
@@ -3,7 +3,8 @@ export default {
confirm: "確認",
cancel: "取消",
save: "保存",
ok: "確定"
ok: "確定",
authSaveFail: "保存認證資訊失敗"
},
finger: {
intTips: "指紋模塊正在初始化",
@@ -426,6 +427,8 @@ export default {
enableScreenRecord: "啟用屏幕錄制",
printRecordLogo: "打印標識",
recordPath: "錄制路徑",
recording: "錄製中...",
recordingCanceled: "錄製已取消",
// Hardware
hardwareControl: "硬體管控",
installDongle: "加密狗計數",
@@ -484,6 +487,10 @@ export default {
oneFolder: "特定作業只能添加一個文件夾",
notFolder: "添加內容不是文件夾",
print_flagError: "打印面數不匹配,請重新選擇!",
cannotFindBin: "模板中沒有合併字段,無法使用CSV文件",
pleaseSelectCap: "請選擇容量",
dongleCountRequired: "請輸入有效的加密狗安裝次數(必須是正整數)",
authCode: "授權碼",
pleaseUploadTag: "請添加標籤文件",
uploadingTag: "正在上傳標籤文件...",
pleaseUploadingImgBin: "請添加圖片文件",
+16 -15
View File
@@ -16,39 +16,40 @@ Vue.use(ElementUI);
/* eslint-disable no-new */
Vue.use(VueI18n);
var type = navigator.appName;
if (type == "Netscape"){
var lang = navigator.language;//获取浏览器配置语言,支持非IE浏览器
}else{
var lang = navigator.userLanguage;//获取浏览器配置语言,支持IE5+ == navigator.systemLanguage
if (type == "Netscape") {
var lang = navigator.language;//获取浏览器配置语言,支持非IE浏览器
} else {
var lang = navigator.userLanguage;//获取浏览器配置语言,支持IE5+ == navigator.systemLanguage
};
var lang1 = lang.substr(0, 2);//获取浏览器配置语言前两位
console.log(lang,lang1);
console.log(lang, lang1);
let lan = 'en';
if(lang1=='zh'){
if(lang == 'zh-TW' || lang == 'zh-HK'){
if (lang1 == 'zh') {
if (lang == 'zh-TW' || lang == 'zh-HK') {
lan = 'ozh';
}else{
} else {
lan = 'zh';
}
}else if(lang1=="en"){
} else if (lang1 == "en") {
lan = 'en';
}
if(!localStorage.getItem('lang')){
localStorage.setItem("lang",lan);
if (!localStorage.getItem('lang')) {
localStorage.setItem("lang", lan);
}
const i18n = new VueI18n({
silentTranslationWarn: true,
//locale: lan, // 默认语言
locale:(function(){
if(localStorage.getItem('lang')){
locale: (function () {
if (localStorage.getItem('lang')) {
return localStorage.getItem('lang')
}
return 'en'
}()),
messages: {
'zh': cnlang,
'zh': cnlang,
'ozh': ocnlang,
'en': enlang
'en': enlang
}
})
axios.defaults.baseURL = 'http://localhost:5080';