feat: 飞书审批消息添加操作按钮
功能: 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ApprovalDetail | null>(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({
|
||||
|
||||
Reference in New Issue
Block a user