/**
|
* @author 韩天尊
|
* @time 2024-01-15
|
* @version 1.0.0
|
* @description 管理端积分审核页面,展示待审核的积分申报记录
|
*/
|
import React, { useState, useEffect } from 'react';
|
import { useNavigate } from 'react-router-dom';
|
import { useAppContext } from '../context/AppContext';
|
import PageHeader from '../components/PageHeader';
|
import { adminAPI } from '../services/api';
|
import { AdminPointsReviewRecord } from '../types';
|
|
const AdminPointsReviewPage: React.FC = () => {
|
const navigate = useNavigate();
|
const { state } = useAppContext();
|
const [activeTab, setActiveTab] = useState('all');
|
const [selectedRecords, setSelectedRecords] = useState<number[]>([]);
|
const [reviewRecords, setReviewRecords] = useState<AdminPointsReviewRecord[]>([]);
|
const [loading, setLoading] = useState(true);
|
const [pagination, setPagination] = useState({
|
page: 1,
|
size: 10,
|
total: 0
|
});
|
const [showSuccessDialog, setShowSuccessDialog] = useState(false);
|
const [successMessage, setSuccessMessage] = useState('');
|
|
// 获取积分审核列表数据
|
const fetchReviewRecords = async (status?: string) => {
|
try {
|
setLoading(true);
|
const response = await adminAPI.getPointsReviewList({
|
page: pagination.page,
|
size: pagination.size,
|
status: status
|
});
|
|
if (response.code === 0) {
|
setReviewRecords(response.data.list || []);
|
setPagination(prev => ({
|
...prev,
|
total: response.data.total || 0
|
}));
|
}
|
} catch (error) {
|
console.error('获取积分审核列表失败:', error);
|
} finally {
|
setLoading(false);
|
}
|
};
|
|
// 初始化数据
|
useEffect(() => {
|
fetchReviewRecords();
|
}, [pagination.page]);
|
|
// 切换Tab时重新获取数据
|
useEffect(() => {
|
const statusMap: { [key: string]: string } = {
|
'all': '',
|
'pending': '0',
|
'reviewed': '1,2' // 已审核包括通过和拒绝
|
};
|
fetchReviewRecords(statusMap[activeTab]);
|
}, [activeTab]);
|
|
// 获取筛选后的记录
|
const getFilteredRecords = () => {
|
return reviewRecords;
|
};
|
|
// Tab切换处理
|
const handleTabSwitch = (tab: string) => {
|
setActiveTab(tab);
|
setSelectedRecords([]);
|
};
|
|
// 复选框选择处理
|
const handleRecordSelect = (recordId: number) => {
|
setSelectedRecords(prev => {
|
if (prev.includes(recordId)) {
|
return prev.filter(id => id !== recordId);
|
} else {
|
return [...prev, recordId];
|
}
|
});
|
};
|
|
// 全选/取消全选
|
const handleSelectAll = () => {
|
const filteredRecords = getFilteredRecords();
|
const pendingRecords = filteredRecords.filter(record => record.status === '0');
|
|
if (selectedRecords.length === pendingRecords.length && pendingRecords.length > 0) {
|
setSelectedRecords([]);
|
} else {
|
setSelectedRecords(pendingRecords.map(record => record.id));
|
}
|
};
|
|
// 批量审核处理
|
const handleBatchReview = async (action: 'approve' | 'reject') => {
|
if (selectedRecords.length === 0) {
|
alert('请选择要审核的记录');
|
return;
|
}
|
|
try {
|
const response = await adminAPI.batchReviewPointsDeclaration({
|
declarationIds: selectedRecords,
|
action: action === 'approve' ? '1' : '2',
|
comment: action === 'approve' ? '审核通过' : '审核拒绝'
|
});
|
|
if (response.code === 0) {
|
setSelectedRecords([]);
|
showSuccessMessage(`已${action === 'approve' ? '通过' : '拒绝'}选中的${selectedRecords.length}条审核记录`);
|
// 重新获取数据
|
fetchReviewRecords();
|
}
|
} catch (error) {
|
console.error('批量审核失败:', error);
|
alert('审核失败,请重试');
|
}
|
};
|
|
// 跳转到审核详情页面
|
const handleReviewDetail = (recordId: number) => {
|
navigate(`/admin-review-detail/${recordId}`);
|
};
|
|
// 显示成功弹框
|
const showSuccessMessage = (message: string) => {
|
setSuccessMessage(message);
|
setShowSuccessDialog(true);
|
};
|
|
// 关闭成功弹框
|
const closeSuccessDialog = () => {
|
setShowSuccessDialog(false);
|
setSuccessMessage('');
|
};
|
|
// 获取状态文本
|
const getStatusText = (status: string) => {
|
const statusMap: { [key: string]: string } = {
|
'0': '待审核',
|
'1': '已通过',
|
'2': '已拒绝',
|
'pending': '待审核',
|
'approved': '已通过',
|
'rejected': '已拒绝'
|
};
|
return statusMap[status] || status;
|
};
|
|
// 获取状态对应的CSS类名
|
const getStatusClass = (status: string) => {
|
const statusClassMap: { [key: string]: string } = {
|
'0': 'pending',
|
'1': 'approved',
|
'2': 'rejected',
|
'pending': 'pending',
|
'approved': 'approved',
|
'rejected': 'rejected'
|
};
|
return statusClassMap[status] || 'pending';
|
};
|
|
// 格式化时间
|
const formatTime = (timeStr: string) => {
|
if (!timeStr) return '';
|
try {
|
const date = new Date(timeStr);
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
const hours = String(date.getHours()).padStart(2, '0');
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
} catch (error) {
|
return timeStr;
|
}
|
};
|
|
const filteredRecords = getFilteredRecords();
|
const pendingRecords = filteredRecords.filter(record => record.status === '0');
|
|
return (
|
<div className="page admin-points-review-page">
|
<PageHeader title="积分审核" showBack={true} />
|
|
{/* Tab切换 */}
|
<div className="declaration-tabs">
|
<button
|
className={`declaration-tab ${activeTab === 'all' ? 'active' : ''}`}
|
onClick={() => handleTabSwitch('all')}
|
>
|
全部
|
</button>
|
<button
|
className={`declaration-tab ${activeTab === 'pending' ? 'active' : ''}`}
|
onClick={() => handleTabSwitch('pending')}
|
>
|
待审核
|
</button>
|
<button
|
className={`declaration-tab ${activeTab === 'reviewed' ? 'active' : ''}`}
|
onClick={() => handleTabSwitch('reviewed')}
|
>
|
已审核
|
</button>
|
</div>
|
|
{/* 全选区域 - 在全部Tab和待审核Tab中显示,且有待审核记录时显示 */}
|
{(activeTab === 'all' || activeTab === 'pending') && pendingRecords.length > 0 && (
|
<div className="batch-select-only">
|
<label className="checkbox-label">
|
<input
|
type="checkbox"
|
checked={selectedRecords.length === pendingRecords.length && pendingRecords.length > 0}
|
onChange={handleSelectAll}
|
/>
|
<span className="checkmark"></span>
|
全选 ({selectedRecords.length}/{pendingRecords.length})
|
</label>
|
</div>
|
)}
|
|
{/* 审核记录列表 */}
|
<div className="review-list">
|
{loading ? (
|
<div className="loading">
|
<i className="fas fa-spinner fa-spin"></i>
|
<p>加载中...</p>
|
</div>
|
) : filteredRecords.length === 0 ? (
|
<div className="empty-state">
|
<i className="fas fa-clipboard-list"></i>
|
<p>暂无审核记录</p>
|
</div>
|
) : (
|
filteredRecords.map((record) => (
|
<div key={record.id} className="review-item">
|
{record.status === '0' && (
|
<div className="review-checkbox">
|
<label className="checkbox-label">
|
<input
|
type="checkbox"
|
checked={selectedRecords.includes(record.id)}
|
onChange={() => handleRecordSelect(record.id)}
|
/>
|
<span className="checkmark"></span>
|
</label>
|
</div>
|
)}
|
<div
|
className="review-content"
|
onClick={() => handleReviewDetail(record.id)}
|
>
|
<div className="review-header">
|
<div className="review-name">{record.userName}</div>
|
<div className={`review-status ${getStatusClass(record.status)}`}>
|
{getStatusText(record.status)}
|
</div>
|
</div>
|
<div className="review-info">
|
<div className="review-title">{record.categoryDesc}</div>
|
<div className="review-points">+{record.points} 积分</div>
|
</div>
|
<div className="review-time">提交时间:{formatTime(record.createTime)}</div>
|
</div>
|
</div>
|
))
|
)}
|
</div>
|
|
{/* 批量操作按钮区域 - 在全部Tab和待审核Tab中,且有勾选时显示 */}
|
{(activeTab === 'all' || activeTab === 'pending') && selectedRecords.length > 0 && (
|
<div className="batch-actions-bottom">
|
<div className="batch-buttons">
|
<button
|
className="batch-btn approve"
|
onClick={() => handleBatchReview('approve')}
|
>
|
<i className="fas fa-check"></i>
|
批量通过
|
</button>
|
<button
|
className="batch-btn reject"
|
onClick={() => handleBatchReview('reject')}
|
>
|
<i className="fas fa-times"></i>
|
批量拒绝
|
</button>
|
</div>
|
</div>
|
)}
|
|
{/* 成功弹框 */}
|
{showSuccessDialog && (
|
<div className="success-dialog-overlay" onClick={closeSuccessDialog}>
|
<div className="success-dialog" onClick={(e) => e.stopPropagation()}>
|
<div className="success-dialog-header">
|
<div className="success-dialog-icon">
|
<i className="fas fa-check-circle"></i>
|
</div>
|
<h3 className="success-dialog-title">操作成功</h3>
|
</div>
|
<div className="success-dialog-content">
|
<p>{successMessage}</p>
|
</div>
|
<div className="success-dialog-actions">
|
<button
|
className="success-dialog-btn success-dialog-btn-confirm"
|
onClick={closeSuccessDialog}
|
>
|
确定
|
</button>
|
</div>
|
</div>
|
</div>
|
)}
|
</div>
|
);
|
};
|
|
export default AdminPointsReviewPage;
|