From df0d592ee4596b9e9c2e4bf0e6aeb5248a0be750 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 18:57:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A3=9E=E4=B9=A6=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=B7=BB=E5=8A=A0=E6=93=8D=E4=BD=9C=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 功能: 1. 审批通知卡片添加"通过"和"拒绝"按钮 2. 点击按钮跳转到审批详情页,自动弹出审批确认框 3. 保留"查看详情"按钮 实现: - FeishuNotificationService.buildApprovalCard() - 添加审批操作按钮 - ApprovalService.notifyNextApprovers() - 传递 order_id 参数 - approval/detail.tsx - 处理 URL 中的 action 参数 按钮样式: - 通过按钮:绿色 (primary) - 拒绝按钮:红色 (danger) - 查看详情:默认样式 相关文件: - backend/app/service/FeishuNotificationService.php - backend/app/service/ApprovalService.php - frontend/src/pages/approval/detail.tsx Co-Authored-By: Claude Opus 4.6 --- backend/app/service/ApprovalService.php | 1 + .../app/service/FeishuNotificationService.php | 63 ++++++++++++++----- frontend/src/pages/approval/detail.tsx | 21 ++++++- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/backend/app/service/ApprovalService.php b/backend/app/service/ApprovalService.php index 82d786f..40a2a85 100644 --- a/backend/app/service/ApprovalService.php +++ b/backend/app/service/ApprovalService.php @@ -482,6 +482,7 @@ class ApprovalService 'type' => $businessTypeName, 'content' => "**申请标题**: {$order->title}\n**申请人**: {$order->applicant->name}", 'url' => $detailUrl, + 'order_id' => $order->id, ]); error_log("✅ 审批人通知成功: approver_id={$approverId}"); } catch (\Exception $e) { diff --git a/backend/app/service/FeishuNotificationService.php b/backend/app/service/FeishuNotificationService.php index 32650b9..7fcac3a 100644 --- a/backend/app/service/FeishuNotificationService.php +++ b/backend/app/service/FeishuNotificationService.php @@ -118,8 +118,7 @@ class FeishuNotificationService * - type: 申请类型 * - content: 申请内容摘要 * - url: 详情链接 - * - approve_url: 通过按钮链接(可选) - * - reject_url: 拒绝按钮链接(可选) + * - order_id: 审批单ID(用于构建审批操作按钮) * @return bool */ public function sendApprovalNotification(array $params): bool @@ -284,10 +283,12 @@ class FeishuNotificationService $type = $params['type'] ?? '申请'; $content = $params['content'] ?? ''; $url = $params['url'] ?? ''; + $orderId = $params['order_id'] ?? null; - // 简化版卡片,先测试 + // 构建卡片元素 $elements = []; + // 内容区域 if ($content) { $elements[] = [ 'tag' => 'div', @@ -306,20 +307,54 @@ class FeishuNotificationService ]; } + // 操作按钮区域 + $actions = []; + + // 如果有审批单ID,添加审批操作按钮 + if ($orderId) { + // 通过按钮(绿色) + $actions[] = [ + 'tag' => 'button', + 'text' => [ + 'content' => '✓ 通过', + 'tag' => 'plain_text', + ], + 'type' => 'primary', + 'size' => 'medium', + 'url' => "https://xsm.dev.hnxinyouhe.com/approval/detail/{$orderId}?action=approve", + ]; + + // 拒绝按钮(红色) + $actions[] = [ + 'tag' => 'button', + 'text' => [ + 'content' => '✗ 拒绝', + 'tag' => 'plain_text', + ], + 'type' => 'danger', + 'size' => 'medium', + 'url' => "https://xsm.dev.hnxinyouhe.com/approval/detail/{$orderId}?action=reject", + ]; + } + + // 查看详情按钮 if ($url) { + $actions[] = [ + 'tag' => 'button', + 'text' => [ + 'content' => '查看详情', + 'tag' => 'plain_text', + ], + 'type' => 'default', + 'size' => 'medium', + 'url' => $url, + ]; + } + + if (!empty($actions)) { $elements[] = [ 'tag' => 'action', - 'actions' => [ - [ - 'tag' => 'button', - 'text' => [ - 'content' => '查看详情', - 'tag' => 'plain_text', - ], - 'type' => 'default', - 'url' => $url, - ], - ], + 'actions' => $actions, ]; } diff --git a/frontend/src/pages/approval/detail.tsx b/frontend/src/pages/approval/detail.tsx index b5f9df5..d8555c7 100644 --- a/frontend/src/pages/approval/detail.tsx +++ b/frontend/src/pages/approval/detail.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { useNavigate, useParams, useLocation } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { Card, Tag, Button, Dialog, Toast, TextArea } from 'antd-mobile' import { @@ -98,6 +98,7 @@ export default function ApprovalDetail() { const { t } = useTranslation() const navigate = useNavigate() const { id } = useParams() + const location = useLocation() const { user } = useUserStore() const [detail, setDetail] = useState(null) @@ -126,6 +127,24 @@ export default function ApprovalDetail() { loadDetail() }, [id]) + // 处理 URL 中的 action 参数(来自飞书消息按钮) + useEffect(() => { + if (!detail || !canApprove) return + + const searchParams = new URLSearchParams(location.search) + const action = searchParams.get('action') + + if (action === 'approve' || action === 'reject') { + // 自动打开审批对话框 + setActionType(action) + setComment('') + setCommentVisible(true) + + // 清除 URL 参数,避免重复触发 + window.history.replaceState({}, '', `/approval/detail/${id}`) + } + }, [detail, location.search]) + // 撤销申请 const handleCancel = () => { Dialog.confirm({