feat: 实现默认审批人和抄送人功能
- 后端新增 getDefaultApprovers 接口,从审批流程配置中获取默认审批人和抄送人 - 前端申请页面加载时自动获取并显示默认审批人和抄送人 - 添加默认审批流程配置:1个审批人(闫继光) + 2个抄送人(孟启、程丽敏) - 移除后端 support 目录下的自定义 Db 和 Model 类,使用 Webman 官方实现 - 前端选择页面优化:优先从 sessionStorage 恢复,否则加载后端默认配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,8 @@ import { useTranslation } from 'react-i18next'
|
||||
import { DatePicker, Input, Toast, Dialog, TextArea, Selector } from 'antd-mobile'
|
||||
import { AddOutline, DeleteOutline } from 'antd-mobile-icons'
|
||||
import dayjs from 'dayjs'
|
||||
import { businessTripApi } from '../../services/api'
|
||||
import { businessTripApi, userApi } from '../../services/api'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import styles from './apply.module.css'
|
||||
|
||||
interface ItineraryItem {
|
||||
@@ -64,6 +65,7 @@ export default function BusinessTripApply() {
|
||||
])
|
||||
const [approvers, setApprovers] = useState<ApproverItem[]>([])
|
||||
const [ccUsers, setCcUsers] = useState<ApproverItem[]>([])
|
||||
const [defaultLoaded, setDefaultLoaded] = useState(false)
|
||||
|
||||
// 手动保存草稿的函数
|
||||
const saveDraft = () => {
|
||||
@@ -84,6 +86,40 @@ export default function BusinessTripApply() {
|
||||
// 使用 ref 来跟踪是否已经恢复过草稿
|
||||
const hasRestoredRef = useRef(false)
|
||||
|
||||
// 加载默认审批人和抄送人(仅在首次加载且没有草稿数据时)
|
||||
useEffect(() => {
|
||||
const loadDefaultApprovers = async () => {
|
||||
// 如果已经有存储的草稿数据,不加载默认值
|
||||
if (sessionStorage.getItem('businessTripDraft')) {
|
||||
return
|
||||
}
|
||||
// 如果已经从选择页面返回,不加载默认值
|
||||
if (location.state?.fromSelection) {
|
||||
return
|
||||
}
|
||||
// 如果已经有存储的人员数据,不加载默认值
|
||||
if (sessionStorage.getItem('selectedApprovers') || sessionStorage.getItem('selectedCc')) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const defaultData = await businessTripApi.getDefaultApprovers()
|
||||
if (defaultData.approvers && defaultData.approvers.length > 0) {
|
||||
setApprovers(defaultData.approvers)
|
||||
sessionStorage.setItem('selectedApprovers', JSON.stringify(defaultData.approvers))
|
||||
}
|
||||
if (defaultData.cc_users && defaultData.cc_users.length > 0) {
|
||||
setCcUsers(defaultData.cc_users)
|
||||
sessionStorage.setItem('selectedCc', JSON.stringify(defaultData.cc_users))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load default approvers:', error)
|
||||
}
|
||||
}
|
||||
|
||||
loadDefaultApprovers()
|
||||
}, [])
|
||||
|
||||
// 处理从选择页面返回的数据和草稿恢复
|
||||
useEffect(() => {
|
||||
const fromSelection = location.state?.fromSelection
|
||||
|
||||
@@ -3,7 +3,7 @@ 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-approver.module.css'
|
||||
|
||||
interface UserItem {
|
||||
@@ -34,16 +34,45 @@ export default function SelectUserPage() {
|
||||
const state = location.state as LocationState
|
||||
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
// 只从 sessionStorage 获取对应类型的数据
|
||||
const getInitialSelected = () => {
|
||||
const data = sessionStorage.getItem('selectedApprovers')
|
||||
return data ? JSON.parse(data) : []
|
||||
}
|
||||
const [selectedUsers, setSelectedUsers] = useState<UserItem[]>(getInitialSelected())
|
||||
const [selectedUsers, setSelectedUsers] = useState<UserItem[]>([])
|
||||
const [allUsers, setAllUsers] = useState<UserItem[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [page, setPage] = useState(1)
|
||||
const [defaultLoaded, setDefaultLoaded] = useState(false)
|
||||
|
||||
// 获取初始选中的用户(从 sessionStorage 或后端默认配置)
|
||||
const loadInitialSelected = async () => {
|
||||
const type = location.state?.type || 'approver'
|
||||
const storageKey = type === 'approver' ? 'selectedApprovers' : 'selectedCc'
|
||||
|
||||
// 先尝试从 sessionStorage 获取
|
||||
const storedData = sessionStorage.getItem(storageKey)
|
||||
if (storedData) {
|
||||
try {
|
||||
const parsed = JSON.parse(storedData)
|
||||
if (parsed.length > 0) {
|
||||
setSelectedUsers(parsed)
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse stored data:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有存储的数据,从后端获取默认配置
|
||||
try {
|
||||
const defaultData = await businessTripApi.getDefaultApprovers()
|
||||
const defaultUsers = type === 'approver'
|
||||
? (defaultData.approvers || [])
|
||||
: (defaultData.cc_users || [])
|
||||
setSelectedUsers(defaultUsers)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load default approvers:', error)
|
||||
// 获取失败时设置为空数组
|
||||
setSelectedUsers([])
|
||||
}
|
||||
}
|
||||
|
||||
// 加载用户列表
|
||||
const loadUsers = async (pageNum: number = 1, isRefresh: boolean = false) => {
|
||||
@@ -89,6 +118,7 @@ export default function SelectUserPage() {
|
||||
// 初始加载
|
||||
useEffect(() => {
|
||||
loadUsers(1, true)
|
||||
loadInitialSelected()
|
||||
}, [])
|
||||
|
||||
const isUserSelected = (userId: number) => {
|
||||
|
||||
@@ -72,6 +72,9 @@ export const businessTripApi = {
|
||||
|
||||
getDetail: (id: number) => api.get(`/business-trip/detail/${id}`),
|
||||
|
||||
// 获取默认审批人和抄送人
|
||||
getDefaultApprovers: () => api.get('/business-trip/default-approvers'),
|
||||
|
||||
create: (data: any) => api.post('/business-trip/create', data),
|
||||
|
||||
update: (id: number, data: any) =>
|
||||
|
||||
Reference in New Issue
Block a user