From 25df589eeab7610157f0097dba8cd4fc5832ea8a Mon Sep 17 00:00:00 2001 From: root Date: Sat, 28 Feb 2026 17:34:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E5=92=8C=E6=8A=84=E9=80=81=E4=BA=BA=E7=9B=B8=E5=85=B3=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复抄送人显示问题 - select-cc.tsx: 添加从后端获取默认抄送人的逻辑 - 之前只从 sessionStorage 获取,导致默认抄送人无法加载 2. 修复多人审批重复操作问题 - ApprovalService: approve() 方法添加重复审批检查 - 检查用户是否已在当前节点审批过,避免重复操作 - 恢复 isNodeCompleted() 从 form_data 获取审批人列表 3. 审批逻辑优化 - 从 form_data['approvers'] 获取用户实际选择的审批人 - 支持会签(and_approve)多人审批场景 Co-Authored-By: Claude Opus 4.6 --- backend/app/service/ApprovalService.php | 18 ++++++- .../src/pages/business-trip/select-cc.tsx | 47 +++++++++++++++---- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/backend/app/service/ApprovalService.php b/backend/app/service/ApprovalService.php index c55a485..221c184 100644 --- a/backend/app/service/ApprovalService.php +++ b/backend/app/service/ApprovalService.php @@ -87,6 +87,18 @@ class ApprovalService throw new \Exception('当前节点不存在'); } + // 检查当前用户是否已经在当前节点审批过 + $existingRecord = ApprovalRecord::where('order_id', $order->id) + ->where('node_id', $currentNode->id) + ->where('approver_id', $approverId) + ->whereIn('action', [ApprovalRecord::ACTION_APPROVE, ApprovalRecord::ACTION_REJECT]) + ->first(); + + if ($existingRecord) { + $actionText = $existingRecord->action === ApprovalRecord::ACTION_APPROVE ? '通过' : '拒绝'; + throw new \Exception("您已经{$actionText}过此申请,无需重复操作"); + } + // 记录审批操作 ApprovalRecord::create([ 'order_id' => $order->id, @@ -331,8 +343,10 @@ class ApprovalService ->where('action', 'approve') ->get(); - // 从节点的 approvers 字段获取审批人列表 - $approvers = $node->approvers ?? []; + // 从 form_data 获取实际审批人列表(用户可能重新选择过) + $order = ApprovalOrder::find($orderId); + $formData = $order->form_data ?? []; + $approvers = $formData['approvers'] ?? []; // 兼容两种格式:[{id:1, name:'...'}] 或 [1] if (!empty($approvers) && is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { diff --git a/frontend/src/pages/business-trip/select-cc.tsx b/frontend/src/pages/business-trip/select-cc.tsx index 8abcda4..b8dbf2e 100644 --- a/frontend/src/pages/business-trip/select-cc.tsx +++ b/frontend/src/pages/business-trip/select-cc.tsx @@ -1,9 +1,9 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { NavBar, SearchBar, Button, Toast, Empty } from 'antd-mobile' import { CheckOutline } from 'antd-mobile-icons' -import { userApi } from '../../services/api' +import { userApi, businessTripApi } from '../../services/api' import styles from './select-cc.module.css' interface UserItem { @@ -34,16 +34,46 @@ export default function SelectCcPage() { const state = location.state as LocationState const [searchValue, setSearchValue] = useState('') - // 只从 sessionStorage 获取对应类型的数据 - const getInitialSelected = () => { - const data = sessionStorage.getItem('selectedCc') - return data ? JSON.parse(data) : [] - } - const [selectedUsers, setSelectedUsers] = useState(getInitialSelected()) + const [selectedUsers, setSelectedUsers] = useState([]) const [allUsers, setAllUsers] = useState([]) const [loading, setLoading] = useState(false) const [hasMore, setHasMore] = useState(true) const [page, setPage] = useState(1) + const initialLoadedRef = useRef(false) + + // 获取初始选中的用户(从 sessionStorage 或后端默认配置) + const loadInitialSelected = async () => { + // 防止重复加载 + if (initialLoadedRef.current) return + + // 先尝试从 sessionStorage 获取 + const storedData = sessionStorage.getItem('selectedCc') + if (storedData) { + try { + const parsed = JSON.parse(storedData) + setSelectedUsers(parsed) + initialLoadedRef.current = true + return + } catch (e) { + console.error('Failed to parse stored data:', e) + } + } + + // 如果没有存储的数据,从后端获取默认配置 + try { + const defaultData = await businessTripApi.getDefaultApprovers() + const defaultUsers = defaultData.cc_users || [] + setSelectedUsers(defaultUsers) + // 将默认数据也保存到 sessionStorage + if (defaultUsers.length > 0) { + sessionStorage.setItem('selectedCc', JSON.stringify(defaultUsers)) + } + } catch (error: any) { + console.error('Failed to load default cc users:', error) + setSelectedUsers([]) + } + initialLoadedRef.current = true + } // 加载用户列表 const loadUsers = async (pageNum: number = 1, isRefresh: boolean = false) => { @@ -89,6 +119,7 @@ export default function SelectCcPage() { // 初始加载 useEffect(() => { loadUsers(1, true) + loadInitialSelected() }, []) const isUserSelected = (userId: number) => {