diff --git a/track-uniapp/src/pages/scan/detail.vue b/track-uniapp/src/pages/scan/detail.vue
index 53573f5..1fdd460 100644
--- a/track-uniapp/src/pages/scan/detail.vue
+++ b/track-uniapp/src/pages/scan/detail.vue
@@ -96,7 +96,7 @@
备注 *
-
+
@@ -110,7 +110,7 @@
⏳
-
+
@@ -127,13 +127,13 @@
@tap="receiveTaskName = opt">{{ opt }}
-
+
-
+
@@ -148,7 +148,7 @@
交接备注 *
{{ transferForm.isWarehouse ? '产品将入库并从个人待办中移除' : '将创建新任务指派给 ' + (transferUserName || '—') }}
-
+
@@ -161,7 +161,7 @@
派发备注 *
-
+
@@ -197,15 +197,15 @@
-
+
@@ -337,7 +337,7 @@ export default {
closeRecordPopup() { this.recordPopup = { visible: false, task: null }; },
async handleChooseImage() { const maxSlots = 9 - (this.recordForm.images.length + this.recordForm.pendingCount); if (maxSlots <= 0) return; const chooseRes = await new Promise((resolve, reject) => { uni.chooseImage({ count: maxSlots, sizeType: ["compressed"], sourceType: ["camera", "album"], success: resolve, fail: reject }); }).catch(() => null); if (!chooseRes || !chooseRes.tempFilePaths || !chooseRes.tempFilePaths.length) return; let compressSkipCount = 0; const compressedPaths = []; for (const p of chooseRes.tempFilePaths) { try { const compressed = await new Promise((resolve, reject) => { uni.compressImage({ src: p, quality: 60, success: resolve, fail: reject }); }); compressedPaths.push(compressed.tempFilePath); } catch { compressSkipCount++; } } if (!compressedPaths.length) return; this.isUploading = true; this.recordForm.pendingCount += compressedPaths.length; for (const path of compressedPaths) { const url = await this.uploadFile(path); if (url) this.recordForm.images.push(url); this.recordForm.pendingCount--; } this.isUploading = false; },
uploadFile(filePath) { return new Promise((resolve) => { uni.uploadFile({ url: getBaseUrl() + "/upload/", filePath, name: "file", success(res) { try { const data = JSON.parse(res.data); resolve(data.url || null); } catch { resolve(null); } }, fail: () => resolve(null) }); }); },
- removeRecordImage(i) { this.openConfirmDlg({ title: "删除图片", content: "确定删除这张图片吗?", action: () => { this.recordForm.images.splice(i, 1); } }); },
+ removeRecordImage(i) { this.openConfirmDlg({ title: "删除图片", content: "确定删除这张图片吗?", countdown: true, action: () => { this.recordForm.images.splice(i, 1); } }); },
previewRecordImage(i) { uni.previewImage({ urls: this.recordForm.images, current: i }); },
async doSaveRecord() { if (this.isUploading) return; this.recordSaving = true; try { const payload = { remark: this.recordForm.remark.trim(), images: this.recordForm.images }; if (this.recordForm.recordId) await put(`/records/${this.recordForm.recordId}`, payload); else await patch(`/tasks/${this.recordPopup.task.id}/records`, payload); uni.showToast({ title: "已保存", icon: "success" }); this.closeRecordPopup(); this.doQuery(this.product.serial_number); } catch {} finally { this.recordSaving = false; } },
@@ -352,26 +352,27 @@ export default {
this.spawnForm = { assignee_id: "", remark: "" };
},
handleViewRecords(task) { uni.navigateTo({ url: `/pages/scan/records?taskId=${task.id}` }); },
- async doDeleteRecord(record) { this.openConfirmDlg({ title: "删除记录", content: "确定删除这条记录吗?删除后不可恢复。", action: async () => { try { await request({ url: `/records/${record.id}`, method: "DELETE" }); uni.showToast({ title: "记录已删除", icon: "success" }); this.doQuery(this.product.serial_number); } catch (e) { uni.showToast({ title: (e && e.data && e.data.detail) || "删除失败", icon: "none" }); } } }); },
- confirmEndBranch(task) { this.openConfirmDlg({ title: "结束分支", content: `确定结束「${task.task_name}」吗?`, action: () => this.doEndBranch(task) }); },
+ async doDeleteRecord(record) { this.openConfirmDlg({ title: "删除记录", content: "确定删除这条记录吗?删除后不可恢复。", countdown: true, action: async () => { try { await request({ url: `/records/${record.id}`, method: "DELETE" }); uni.showToast({ title: "记录已删除", icon: "success" }); this.doQuery(this.product.serial_number); } catch (e) { uni.showToast({ title: (e && e.data && e.data.detail) || "删除失败", icon: "none" }); } } }); },
+ confirmEndBranch(task) { this.openConfirmDlg({ title: "结束分支", content: `确定结束「${task.task_name}」吗?`, countdown: true, action: () => this.doEndBranch(task) }); },
confirmRecall(task) { this.openConfirmDlg({ title: "撤回转交", content: `确定撤回「${task.task_name}」吗?撤回后任务将回到您的手中。`, action: () => this.doRecall(task) }); },
async doRecall(task) { try { const opId = this.currentUsername || this.currentUserId; await post(`/tasks/${task.id}/recall?operator_id=${encodeURIComponent(opId)}`); uni.showToast({ title: "已撤回", icon: "success" }); this.doQuery(this.product.serial_number); } catch {} },
async doEndBranch(task) { try { const opId = this.currentUsername || this.currentUserId; await post(`/tasks/${task.id}/end?operator_id=${encodeURIComponent(opId)}`); uni.showToast({ title: "分支已结束", icon: "success" }); this.doQuery(this.product.serial_number); } catch {} },
closeActionPopup() { this.actionPopup = { visible: false, type: "", task: null }; },
// ═══ 双重确认倒计时(防误触) ═══
- // 第一次点击进入 5 秒倒计时(期间再次点击 = 取消反悔),倒计时结束再点击才真正执行
+ // 首次点击进入 5 秒倒计时:期间确认按钮虚化禁用(点不了),只有「取消」可用;
+ // 5 秒结束后确认按钮解锁,点击才真正执行。
confirmBtn(key, doAction) {
if (this.confirming === key) {
if (this.confirmCount <= 0) {
+ // 倒计时结束,点击执行
this.clearConfirm();
doAction();
- } else {
- // 倒计时中再次点击 → 取消(反悔)
- this.clearConfirm();
}
+ // 倒计时中:按钮已禁用,忽略点击
return;
}
+ // 首次点击,开始倒计时
this.clearConfirm();
this.confirming = key;
this.confirmCount = 5;
@@ -387,27 +388,30 @@ export default {
this.confirmCount = 5;
},
confirmLabel(key, baseText) {
- if (this.confirming === key) return this.confirmCount > 0 ? `再次确认 (${this.confirmCount}s)` : "确认执行";
+ if (this.confirming === key && this.confirmCount > 0) return `${baseText} (${this.confirmCount}s)`;
return baseText;
},
- // 确认框类操作(结束分支/删除记录/撤回转交):弹倒计时确认弹窗
- openConfirmDlg({ title, content, action }) {
+ // 确认框类操作(结束分支/删除记录/删除图片/撤回转交)
+ // countdown=true 时确认按钮需要 5 秒等待(期间禁用);false 时立即确认
+ openConfirmDlg({ title, content, action, countdown = false }) {
this.clearConfirm();
- this.confirmDlg = { visible: true, title, content, action };
- this.confirming = "dlg";
- this.confirmCount = 5;
- this.confirmTimer = setInterval(() => {
- this.confirmCount -= 1;
- if (this.confirmCount <= 0) clearInterval(this.confirmTimer);
- }, 1000);
+ this.confirmDlg = { visible: true, title, content, action, countdown };
+ if (countdown) {
+ this.confirming = "dlg";
+ this.confirmCount = 5;
+ this.confirmTimer = setInterval(() => {
+ this.confirmCount -= 1;
+ if (this.confirmCount <= 0) clearInterval(this.confirmTimer);
+ }, 1000);
+ }
},
confirmDlgConfirm() {
- this.confirmBtn("dlg", () => {
- const action = this.confirmDlg.action;
- this.confirmDlg.visible = false;
- this.confirmDlg.action = null;
- if (action) action();
- });
+ if (this.confirming === "dlg" && this.confirmCount > 0) return; // 倒计时中:忽略
+ if (this.confirming === "dlg") this.clearConfirm();
+ const action = this.confirmDlg.action;
+ this.confirmDlg.visible = false;
+ this.confirmDlg.action = null;
+ if (action) action();
},
confirmDlgCancel() {
this.clearConfirm();
diff --git a/track-uniapp/src/pages/scan/records.vue b/track-uniapp/src/pages/scan/records.vue
index 7bdca05..6e6c227 100644
--- a/track-uniapp/src/pages/scan/records.vue
+++ b/track-uniapp/src/pages/scan/records.vue
@@ -54,10 +54,10 @@
{{ confirmDlg.title }}
{{ confirmDlg.content }}
- ⚠️ 请再次确认:倒计时结束点击「确认删除」才会生效
+ ⚠️ 5 秒确认等待中:请核对信息,倒计时结束后确认按钮才可点击
-
+
@@ -184,40 +184,22 @@ export default {
this.confirmCount = 5;
},
confirmLabel(baseText) {
- if (this.confirming === "dlg") return this.confirmCount > 0 ? `再次确认 (${this.confirmCount}s)` : "确认删除";
+ if (this.confirming === "dlg" && this.confirmCount > 0) return `${baseText} (${this.confirmCount}s)`;
return baseText;
},
confirmDlgConfirm() {
- this.confirmBtn(() => {
- const rec = this.confirmDlg.target;
- this.confirmDlg.visible = false;
- this.confirmDlg.target = null;
- if (rec) this.deleteRecord(rec);
- });
+ if (this.confirming === "dlg" && this.confirmCount > 0) return; // 倒计时中:忽略
+ if (this.confirming === "dlg") this.clearConfirm();
+ const rec = this.confirmDlg.target;
+ this.confirmDlg.visible = false;
+ this.confirmDlg.target = null;
+ if (rec) this.deleteRecord(rec);
},
confirmDlgCancel() {
this.clearConfirm();
this.confirmDlg.visible = false;
this.confirmDlg.target = null;
},
- confirmBtn(doAction) {
- if (this.confirming === "dlg") {
- if (this.confirmCount <= 0) {
- this.clearConfirm();
- doAction();
- } else {
- this.clearConfirm();
- }
- return;
- }
- this.clearConfirm();
- this.confirming = "dlg";
- this.confirmCount = 5;
- this.confirmTimer = setInterval(() => {
- this.confirmCount -= 1;
- if (this.confirmCount <= 0) clearInterval(this.confirmTimer);
- }, 1000);
- },
async deleteRecord(rec) {
try {
await request({ url: `/records/${rec.id}`, method: "DELETE" });
@@ -272,4 +254,5 @@ export default {
.dlg-btn::after { border: none; }
.dlg-cancel { background: #f3f4f6; color: #6b7280; }
.dlg-danger { background: #dc2626; color: #fff; }
+.dlg-danger[disabled] { opacity: 0.5; }