From 6b68eb254eaa2fe353760f78a4dd9ccecea12be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E5=B3=B0?= Date: Thu, 23 Apr 2026 16:42:10 +0800 Subject: [PATCH] up --- .workbuddy/memory/2026-04-23.md | 26 ++++++ backend/my_work/routers/apiPurchase.go | 48 +++++++++++ backend/my_work/routers/apiWorkOrder.go | 54 ++++++++++++- frontend/ops_vue_js/src/api/purchase.js | 5 ++ frontend/ops_vue_js/src/api/work_order.js | 5 ++ frontend/ops_vue_js/src/i18n/en.json | 5 +- frontend/ops_vue_js/src/i18n/zh-CN.json | 5 +- .../src/views/purchase/ShowOrder.vue | 63 ++++++++++++++- .../src/views/work_order/AddEditWorkOrder.vue | 4 +- .../src/views/work_order/ShowWorkOrder.vue | 81 ++++++++++++++++--- 10 files changed, 280 insertions(+), 16 deletions(-) diff --git a/.workbuddy/memory/2026-04-23.md b/.workbuddy/memory/2026-04-23.md index 1b4cae6..e98b5a9 100644 --- a/.workbuddy/memory/2026-04-23.md +++ b/.workbuddy/memory/2026-04-23.md @@ -47,3 +47,29 @@ - 前端:状态选 `parts_ordered` 时显示采购订单搜索框(防抖300ms),输入框获取焦点自动搜索 - 时间线每条 commit 下方展示关联的采购订单链接(点击跳转到采购详情页) +## 今日功能迭代 + +**ConfirmDialog 组件 v-model 修复**: +- 组件使用 `v-if="modelValue"` 控制弹窗显示,但外部只用了 `v-if` 控制组件存在,没有传入 `modelValue` prop +- 修复:在所有使用 ConfirmDialog 的地方改用 `v-model="xxx"` 绑定 +- 涉及文件:ShowWorkOrder.vue、AddEditWorkOrder.vue、ShowOrder.vue + +**ShowWorkOrder.vue 进度删除功能**: +- 新增删除按钮(新样式:带边框和背景色的文字按钮) +- 每条 commit 加边框和背景色,便于区分 +- 最新状态不显示删除按钮 +- 权限判断:工单创建者 OR 进度创建者 OR 管理员 +- 删除后前端直接移除该 commit,保持滚动位置 + +**useDropzone v-model 问题修复**: +- useDropzone 组件没有实现 v-model,是通过 `return_files()` 方法暴露文件 +- 修复:添加 `ref="commitDropzoneRef"`,通过 `commitDropzoneRef.value?.return_files()` 获取文件 +- 只筛选 `is_upload === true` 的文件获取 hash + +**采购订单状态记录删除功能**: +- 后端:apiPurchase.go 新增 `/delete_commit` 接口,权限判断同工单 +- 前端:ShowOrder.vue 新增删除按钮,样式和逻辑同工单页面 +- 新增翻译:purchase.confirm_delete_commit + + + diff --git a/backend/my_work/routers/apiPurchase.go b/backend/my_work/routers/apiPurchase.go index f0929f7..f54f9ce 100644 --- a/backend/my_work/routers/apiPurchase.go +++ b/backend/my_work/routers/apiPurchase.go @@ -350,6 +350,54 @@ func ApiPurchase(r *gin.RouterGroup) { ReturnJson(ctx, "apiOK", nil) }) + // 删除状态记录 + r.POST("/delete_commit", func(ctx *gin.Context) { + isAuth, user, data := AuthenticationAuthority(ctx) + if !isAuth { + ReturnJson(ctx, "userCookieError", nil) + return + } + + type FromDeleteCommit struct { + OrderID uint `json:"orderId"` + CommitID uint `json:"commitId"` + } + var from FromDeleteCommit + if err := decodeJSON(data, &from); err != nil || from.OrderID == 0 || from.CommitID == 0 { + ReturnJson(ctx, "jsonErr", nil) + return + } + + // 获取订单信息 + var order TabPurchaseOrder + if err := models.DB.Where("id = ?", from.OrderID).First(&order).Error; err != nil { + ReturnJson(ctx, "order_not_found", nil) + return + } + + // 获取进度信息 + var commit TabPurchaseCommit + if err := models.DB.Where("id = ? AND order_id = ?", from.CommitID, from.OrderID).First(&commit).Error; err != nil { + ReturnJson(ctx, "commit_not_found", nil) + return + } + + // 权限判断:订单创建者 或 进度创建者 或 管理员 + isOrderCreator := user.ID == order.UserID + isCommitCreator := user.ID == commit.UserID + isAdmin := slices.Contains(purchaseAdmins, user.ID) + + if !isOrderCreator && !isCommitCreator && !isAdmin { + ReturnJson(ctx, "no_permission", nil) + return + } + + // 删除进度 + models.DB.Where("id = ?", from.CommitID).Delete(&TabPurchaseCommit{}) + + ReturnJson(ctx, "apiOK", nil) + }) + r.POST("/getorders", func(ctx *gin.Context) { isAuth, _, data := AuthenticationAuthority(ctx) if isAuth { diff --git a/backend/my_work/routers/apiWorkOrder.go b/backend/my_work/routers/apiWorkOrder.go index 8b9d076..3513e9b 100644 --- a/backend/my_work/routers/apiWorkOrder.go +++ b/backend/my_work/routers/apiWorkOrder.go @@ -354,7 +354,7 @@ func ApiWorkOrder(r *gin.RouterGroup) { // commits var commits []TabWorkOrderCommit - models.DB.Where("work_order_id = ?", from.ID).Order("created_at ASC").Find(&commits) + models.DB.Where("work_order_id = ?", from.ID).Order("created_at DESC").Find(&commits) // 为每条 commit 附加图片和采购订单 type CommitWithPhotos struct { @@ -566,6 +566,58 @@ func ApiWorkOrder(r *gin.RouterGroup) { ReturnJson(ctx, "apiOK", nil) }) + // 删除进度 + r.POST("/delete_commit", func(ctx *gin.Context) { + isAuth, user, data := AuthenticationAuthority(ctx) + if !isAuth { + ReturnJson(ctx, "userCookieError", nil) + return + } + + type FromDeleteCommit struct { + WorkOrderID uint `json:"workOrderId"` + CommitID uint `json:"commitId"` + } + var from FromDeleteCommit + if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CommitID == 0 { + ReturnJson(ctx, "jsonErr", nil) + return + } + + // 获取工单信息 + var order TabWorkOrder + if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil { + ReturnJson(ctx, "order_not_found", nil) + return + } + + // 获取进度信息 + var commit TabWorkOrderCommit + if err := models.DB.Where("id = ? AND work_order_id = ?", from.CommitID, from.WorkOrderID).First(&commit).Error; err != nil { + ReturnJson(ctx, "commit_not_found", nil) + return + } + + // 权限判断:工单创建者 或 进度创建者 或 管理员 + isOrderCreator := user.ID == order.UserID + isCommitCreator := user.ID == commit.UserID + isAdmin := slices.Contains(workOrderAdmins, user.ID) + + if !isOrderCreator && !isCommitCreator && !isAdmin { + ReturnJson(ctx, "no_permission", nil) + return + } + + // 删除关联的采购订单绑定 + models.DB.Where("commit_id = ?", from.CommitID).Delete(&TabWorkOrderPurchaseOrderBind{}) + // 删除关联的图片 + models.DB.Where("commit_id = ?", from.CommitID).Delete(&TabWorkOrderCommitFileBind{}) + // 删除进度记录 + models.DB.Where("id = ?", from.CommitID).Delete(&commit) + + ReturnJson(ctx, "apiOK", nil) + }) + // 获取工单数量统计 r.POST("/count", func(ctx *gin.Context) { isAuth, _, _ := AuthenticationAuthority(ctx) diff --git a/frontend/ops_vue_js/src/api/purchase.js b/frontend/ops_vue_js/src/api/purchase.js index 3602d69..165e1d6 100644 --- a/frontend/ops_vue_js/src/api/purchase.js +++ b/frontend/ops_vue_js/src/api/purchase.js @@ -35,4 +35,9 @@ export const purchaseApi = { deleteOrder(id) { return api.post('/purchase/deleteorder', { id }) }, + + /** 删除状态记录 */ + deleteCommit(orderId, commitId) { + return api.post('/purchase/delete_commit', { orderId, commitId }) + }, } diff --git a/frontend/ops_vue_js/src/api/work_order.js b/frontend/ops_vue_js/src/api/work_order.js index 74c41fb..a65d20b 100644 --- a/frontend/ops_vue_js/src/api/work_order.js +++ b/frontend/ops_vue_js/src/api/work_order.js @@ -40,4 +40,9 @@ export const workOrderApi = { searchPurchaseOrders(search = '', limit = 5) { return api.post('/work_order/search_purchase_orders', { search, limit }) }, + + /** 删除进度 */ + deleteCommit(workOrderId, commitId) { + return api.post('/work_order/delete_commit', { workOrderId, commitId }) + }, } diff --git a/frontend/ops_vue_js/src/i18n/en.json b/frontend/ops_vue_js/src/i18n/en.json index 76b170b..e889665 100644 --- a/frontend/ops_vue_js/src/i18n/en.json +++ b/frontend/ops_vue_js/src/i18n/en.json @@ -108,7 +108,8 @@ "upload_photos": "Upload Photos", "commit_create": "Order created", "edit_order": "Edit Order", - "submit_changes":"Submit changes" + "submit_changes":"Submit changes", + "confirm_delete_commit": "Are you sure you want to delete this progress?" }, "work_order": { "list_title": "Work Order List", @@ -146,6 +147,7 @@ "back_to_list": "Back to List", "not_found": "Work order not found", "confirm_delete": "Are you sure you want to delete this work order? This action cannot be undone.", + "confirm_delete_commit": "Are you sure you want to delete this progress?", "submit": "Submit", "save_changes": "Save Changes" }, @@ -292,6 +294,7 @@ "cancel": "Cancel", "save_success": "Saved successfully", "submit": "Submit", + "submitting": "Submitting...", "loading": "Loading..." }, "settings": { diff --git a/frontend/ops_vue_js/src/i18n/zh-CN.json b/frontend/ops_vue_js/src/i18n/zh-CN.json index 7c32851..865dbdd 100644 --- a/frontend/ops_vue_js/src/i18n/zh-CN.json +++ b/frontend/ops_vue_js/src/i18n/zh-CN.json @@ -108,7 +108,8 @@ "upload_photos": "上传图片", "commit_create": "订单创建", "edit_order": "编辑订单", - "submit_changes":"提交修改" + "submit_changes":"提交修改", + "confirm_delete_commit": "确定要删除此进度吗?" }, "work_order": { "list_title": "工单列表", @@ -146,6 +147,7 @@ "back_to_list": "返回列表", "not_found": "工单不存在", "confirm_delete": "确定要删除此工单吗?此操作不可撤销。", + "confirm_delete_commit": "确定要删除此进度吗?", "submit": "提交", "save_changes": "保存修改" }, @@ -292,6 +294,7 @@ "delete_ok": "删除成功", "save_success": "保存成功", "submit": "提交", + "submitting": "提交中...", "loading": "加载中..." }, "settings": { diff --git a/frontend/ops_vue_js/src/views/purchase/ShowOrder.vue b/frontend/ops_vue_js/src/views/purchase/ShowOrder.vue index d604f88..656aa2f 100644 --- a/frontend/ops_vue_js/src/views/purchase/ShowOrder.vue +++ b/frontend/ops_vue_js/src/views/purchase/ShowOrder.vue @@ -7,6 +7,7 @@ import { usePageTitle } from "@/composables/usePageTitle"; import { purchaseApi } from "@/api/purchase"; import { useUserStore } from "@/stores/user"; import { useUsersStore } from "@/stores/users"; +import ConfirmDialog from "@/components/ConfirmDialog.vue"; import { IconChevronLeft, IconExternalLink, @@ -44,6 +45,47 @@ const pendingComment = ref(""); const pendingPhotos = ref([]); // { hash, url, uploading, error } const photoInputRef = ref(null); +// 删除进度相关 +const showDeleteConfirm = ref(false); +const pendingDeleteCommitId = ref(null); + +// 判断是否可以删除进度 +function canDeleteCommit(commit, index) { + // 最新状态(第0条)不显示删除按钮 + if (index === 0) return false; + // 订单创建者 + if (order.value?.UserID === userStore.user?.ID) return true; + // 进度创建者 + if (commit.userId === userStore.user?.ID) return true; + // 管理员 + if (userStore.user?.Type === 'admin') return true; + return false; +} + +function handleDeleteCommit(commitId) { + pendingDeleteCommitId.value = commitId; + showDeleteConfirm.value = true; +} + +async function confirmDeleteCommit() { + if (!pendingDeleteCommitId.value) return; + try { + const { errCode } = await purchaseApi.deleteCommit(orderId.value, pendingDeleteCommitId.value); + if (errCode === 0) { + toast.success(t("message.delete_ok")); + // 前端直接移除该 commit,保持滚动位置 + commits.value = commits.value.filter(c => c.id !== pendingDeleteCommitId.value); + } else { + toast.error(t("message.server_error")); + } + } catch { + toast.error(t("message.server_error")); + } finally { + pendingDeleteCommitId.value = null; + showDeleteConfirm.value = false; + } +} + // 状态选项 const statusOptions = [ { value: "pending", labelKey: "status_pending", color: "yellow" }, @@ -644,9 +686,9 @@ onMounted(fetchOrder); class="divide-y divide-gray-50 px-6 py-2 dark:divide-dk-muted/50" >
@@ -697,6 +739,15 @@ onMounted(fetchOrder); {{ formatDate(commit.createdAt) }} + +

+ + +