8d78e18267
- Frontend: React 18 + TypeScript + Vite + Ant Design Mobile - Backend: PHP 8.5 + Webman 2.x + MySQL + Redis - i18n support: Chinese, English, Russian, Spanish, French - Approval workflow engine - Docker development environment - Feishu integration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
|
import { getApprovalDetail } from '../services/approval'
|
|
import ApprovalFlow from './ApprovalFlow'
|
|
import ApprovalAction from './ApprovalAction'
|
|
import styles from './ApprovalDetail.module.css'
|
|
|
|
interface ApprovalDetailProps {
|
|
id: string
|
|
}
|
|
|
|
export default function ApprovalDetail({ id }: ApprovalDetailProps) {
|
|
const { data, isLoading, error } = useQuery({
|
|
queryKey: ['approval-detail', id],
|
|
queryFn: () => getApprovalDetail(id),
|
|
})
|
|
|
|
if (isLoading) {
|
|
return <div className={styles.loading}>加载中...</div>
|
|
}
|
|
|
|
if (error) {
|
|
return <div className={styles.error}>加载失败: {error.message}</div>
|
|
}
|
|
|
|
const order = data?.data
|
|
|
|
if (!order) {
|
|
return <div className={styles.empty}>未找到审批详情</div>
|
|
}
|
|
|
|
const getStatusText = (status: string) => {
|
|
const statusMap: Record<string, string> = {
|
|
draft: '草稿',
|
|
pending: '审批中',
|
|
approved: '已通过',
|
|
rejected: '已拒绝',
|
|
cancelled: '已撤销',
|
|
}
|
|
return statusMap[status] || status
|
|
}
|
|
|
|
return (
|
|
<div className={styles.detail}>
|
|
{/* 标题 */}
|
|
<div className={styles.header}>
|
|
<h1 className={styles.title}>{order.title}</h1>
|
|
<span className={styles.status}>{getStatusText(order.status)}</span>
|
|
</div>
|
|
|
|
{/* 审批流程 */}
|
|
{order.flow && (
|
|
<div className={styles.section}>
|
|
<h2 className={styles.sectionTitle}>审批流程</h2>
|
|
<ApprovalFlow nodes={order.flow.nodes || []} />
|
|
</div>
|
|
)}
|
|
|
|
{/* 表单数据 */}
|
|
{order.formData && Object.keys(order.formData).length > 0 && (
|
|
<div className={styles.section}>
|
|
<h2 className={styles.sectionTitle}>申请内容</h2>
|
|
<div className={styles.formData}>
|
|
{Object.entries(order.formData).map(([key, value]) => (
|
|
<div key={key} className={styles.formRow}>
|
|
<span className={styles.formLabel}>{key}:</span>
|
|
<span className={styles.formValue}>{String(value)}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 审批记录 */}
|
|
{order.timeline && order.timeline.length > 0 && (
|
|
<div className={styles.section}>
|
|
<h2 className={styles.sectionTitle}>审批记录</h2>
|
|
<div className={styles.timeline}>
|
|
{order.timeline.map((record: any) => (
|
|
<div key={record.id} className={styles.timelineItem}>
|
|
<div className={styles.timelineDot} />
|
|
<div className={styles.timelineContent}>
|
|
<div className={styles.timelineHeader}>
|
|
<span className={styles.timelineUser}>{record.approver?.name}</span>
|
|
<span className={styles.timelineAction}>
|
|
{record.action === 'approve' && '通过'}
|
|
{record.action === 'reject' && '拒绝'}
|
|
{record.action === 'submit' && '提交'}
|
|
{record.action === 'cancel' && '撤销'}
|
|
</span>
|
|
</div>
|
|
{record.comment && (
|
|
<div className={styles.timelineComment}>{record.comment}</div>
|
|
)}
|
|
<div className={styles.timelineTime}>
|
|
{new Date(record.createdAt).toLocaleString('zh-CN')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 操作按钮 */}
|
|
<ApprovalAction orderId={order.id} status={order.status} />
|
|
</div>
|
|
)
|
|
}
|