| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- import React, { useState, useEffect, useRef } from 'react';
- import { Search, Plus, RefreshCw, Edit2, Trash2, Download } from 'lucide-react';
- import { postApi, PostVO, PostStatus, PageParam } from '../api/Post';
- import { toast } from 'sonner';
- import { formatDateTimeFull } from '../utils/formatTime';
- import { Modal, Button, Input, Space } from 'antd';
- import { ExclamationCircleOutlined } from '@ant-design/icons';
- import { Button as UIButton } from './ui/button';
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
- import PostForm, { PostFormRef } from './PostForm';
- export default function PostManagement() {
- const [loading, setLoading] = useState(true);
- const [list, setList] = useState<PostVO[]>([]);
- const [total, setTotal] = useState(0);
- const [queryParams, setQueryParams] = useState<PageParam>({
- pageNo: 1,
- pageSize: 10,
- name: undefined,
- code: undefined,
- userId: undefined, // 添加用户ID参数
- });
- const [exportLoading, setExportLoading] = useState(false);
- const formRef = useRef<PostFormRef>(null);
- // 从 sessionStorage 读取用户ID(如果存在)
- useEffect(() => {
- const userId = sessionStorage.getItem('selectedUserId');
- if (userId) {
- setQueryParams(prev => ({ ...prev, userId: Number(userId) }));
- // 读取后清除,避免下次进入时还使用旧的用户ID
- sessionStorage.removeItem('selectedUserId');
- }
- }, []);
- // 获取岗位列表
- const getList = async (params?: PageParam) => {
- const currentParams = params || queryParams;
- setLoading(true);
- try {
- console.log('PostManagement: 开始获取岗位列表', currentParams);
- const response = await postApi.getPostPage(currentParams);
- console.log('PostManagement: API 响应', response);
-
- // 处理响应数据
- let data;
- if (response && typeof response === 'object') {
- // 如果响应有 data 属性,使用 data;否则直接使用 response
- data = (response as any).data !== undefined ? (response as any).data : response;
- } else {
- data = response;
- }
-
- // 确保 data 是对象
- if (!data || typeof data !== 'object') {
- console.warn('PostManagement: 响应数据格式异常', data);
- setList([]);
- setTotal(0);
- return;
- }
-
- setList(data.list || []);
- setTotal(data.total || 0);
- console.log('PostManagement: 设置列表数据', data.list, '总数', data.total);
- } catch (error: any) {
- console.error('PostManagement: 获取岗位列表失败', error);
- // 即使请求失败,也设置空列表,避免一直显示加载中
- setList([]);
- setTotal(0);
-
- // 显示错误信息
- const errorMessage = error?.response?.data?.message ||
- error?.message ||
- '获取岗位列表失败,请检查网络连接或联系管理员';
- toast.error(errorMessage);
- } finally {
- setLoading(false);
- }
- };
- // 组件挂载和分页参数变化时获取数据
- useEffect(() => {
- console.log('PostManagement: useEffect 触发,queryParams =', queryParams);
- getList();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [queryParams.pageNo, queryParams.pageSize]);
- // 搜索
- const handleQuery = () => {
- const newParams = { ...queryParams, pageNo: 1 };
- setQueryParams(newParams);
- getList(newParams);
- };
- // 重置搜索
- const resetQuery = () => {
- const resetParams: PageParam = {
- pageNo: 1,
- pageSize: 10,
- name: undefined,
- code: undefined,
- };
- setQueryParams(resetParams);
- getList(resetParams);
- };
- // 打开表单
- const openForm = (type: string, id?: number) => {
- console.log('PostManagement: 打开表单', type, id, 'formRef.current =', formRef.current);
- if (formRef.current) {
- formRef.current.open(type, id);
- } else {
- console.error('PostManagement: formRef.current 为 null');
- toast.error('表单组件未初始化,请刷新页面重试');
- }
- };
- // 删除岗位
- const handleDelete = async (id: number, name: string) => {
- Modal.confirm({
- title: '确认删除',
- icon: <ExclamationCircleOutlined />,
- content: (
- <div>
- <p>确定要删除岗位 <strong>"{name}"</strong> 吗?</p>
- <p style={{ color: '#ff4d4f', marginTop: '8px' }}>删除后无法恢复,请谨慎操作!</p>
- </div>
- ),
- okText: '确定删除',
- okType: 'danger',
- cancelText: '取消',
- onOk: async () => {
- try {
- await postApi.deletePost(id);
- toast.success('删除成功');
- await getList();
- } catch (error: any) {
- toast.error(error.message || '删除失败');
- }
- },
- });
- };
- // 导出岗位
- const handleExport = async () => {
- Modal.confirm({
- title: '确认导出',
- icon: <ExclamationCircleOutlined />,
- content: '确定要导出岗位数据吗?',
- okText: '确定导出',
- cancelText: '取消',
- onOk: async () => {
- setExportLoading(true);
- try {
- const response = await postApi.exportPost(queryParams);
- const blob = (response as any)?.data || response;
- const url = window.URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = '岗位列表.xls';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(url);
- toast.success('导出成功');
- } catch (error: any) {
- toast.error(error.message || '导出失败');
- } finally {
- setExportLoading(false);
- }
- },
- });
- };
- // 获取状态标签
- const getStatusLabel = (status: number) => {
- return status === PostStatus.ENABLE ? '启用' : '禁用';
- };
- // 获取状态样式
- const getStatusStyle = (status: number) => {
- return status === PostStatus.ENABLE
- ? 'bg-green-100 text-green-700'
- : 'bg-gray-100 text-gray-700';
- };
- return (
- <div className="space-y-4">
- {/* 搜索栏 */}
- <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-5">
- <div className="flex items-center justify-between gap-4 flex-wrap">
- {/* 搜索输入框 */}
- <div className="flex items-center gap-3 flex-wrap flex-1">
- <div className="flex items-center gap-3">
- <label className="text-sm font-medium text-gray-700 whitespace-nowrap">岗位名称:</label>
- <Input
- value={queryParams.name || ''}
- onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
- onPressEnter={handleQuery}
- placeholder="请输入岗位名称"
- style={{ width: 192 }}
- allowClear
- />
- </div>
- <div className="flex items-center gap-3">
- <label className="text-sm font-medium text-gray-700 whitespace-nowrap">岗位编码:</label>
- <Input
- value={queryParams.code || ''}
- onChange={(e) => setQueryParams(prev => ({ ...prev, code: e.target.value }))}
- onPressEnter={handleQuery}
- placeholder="请输入岗位编码"
- style={{ width: 192 }}
- allowClear
- />
- </div>
- </div>
- {/* 操作按钮组 */}
- <Space>
- <Button
- type="primary"
- icon={<Search className="w-4 h-4" />}
- onClick={handleQuery}
- >
- 搜索
- </Button>
-
- <Button
- icon={<RefreshCw className="w-4 h-4" />}
- onClick={resetQuery}
- >
- 重置
- </Button>
-
- <Button
- type="primary"
- icon={<Plus className="w-4 h-4" />}
- onClick={() => openForm('create')}
- >
- 新增
- </Button>
-
- <Button
- icon={<Download className="w-4 h-4" />}
- onClick={handleExport}
- loading={exportLoading}
- >
- 导出
- </Button>
- </Space>
- </div>
- </div>
- {/* 表格 */}
- <div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead className="w-[80px] text-center">岗位编号</TableHead>
- <TableHead className="w-[150px]">岗位名称</TableHead>
- <TableHead className="w-[150px]">岗位编码</TableHead>
- <TableHead className="w-[100px] text-center">岗位顺序</TableHead>
- <TableHead>岗位备注</TableHead>
- <TableHead className="w-[100px] text-center">状态</TableHead>
- <TableHead className="w-[180px] text-center">创建时间</TableHead>
- <TableHead className="w-[160px] text-center">操作</TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {loading ? (
- <TableRow>
- <TableCell colSpan={8} className="text-center py-8 text-gray-500">
- 加载中...
- </TableCell>
- </TableRow>
- ) : list.length === 0 ? (
- <TableRow>
- <TableCell colSpan={8} className="text-center py-8 text-gray-500">
- 暂无数据
- </TableCell>
- </TableRow>
- ) : (
- list.map((row, index) => (
- <TableRow key={row.id} className="hover:bg-gray-50">
- <TableCell className="text-center">{row.id}</TableCell>
- <TableCell className="font-medium">{row.name}</TableCell>
- <TableCell>{row.code}</TableCell>
- <TableCell className="text-center">{row.sort}</TableCell>
- <TableCell className="text-sm text-gray-600">{row.remark || '-'}</TableCell>
- <TableCell className="text-center">
- <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusStyle(row.status)}`}>
- {getStatusLabel(row.status)}
- </span>
- </TableCell>
- <TableCell className="text-center text-sm text-gray-600">
- {formatDateTimeFull(row.createTime)}
- </TableCell>
- <TableCell>
- <div className="flex items-center gap-2 justify-center">
- <UIButton
- variant="ghost"
- size="sm"
- onClick={() => openForm('update', row.id)}
- className="h-8 px-2"
- >
- <Edit2 className="w-4 h-4" />
- </UIButton>
- <UIButton
- variant="ghost"
- size="sm"
- onClick={() => handleDelete(row.id!, row.name)}
- className="h-8 px-2 text-red-600 hover:text-red-700"
- >
- <Trash2 className="w-4 h-4" />
- </UIButton>
- </div>
- </TableCell>
- </TableRow>
- ))
- )}
- </TableBody>
- </Table>
- </div>
- {/* 分页 */}
- {!loading && list.length > 0 && (
- <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
- <div className="flex items-center justify-between">
- <div className="text-sm text-gray-600">
- 共 <span className="text-blue-600 font-medium">{total}</span> 条记录
- </div>
- <div className="flex gap-2">
- <Button
- onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! - 1 })}
- disabled={queryParams.pageNo! <= 1}
- >
- 上一页
- </Button>
- <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
- {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
- </span>
- <Button
- onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! + 1 })}
- disabled={queryParams.pageNo! >= Math.ceil(total / queryParams.pageSize!)}
- >
- 下一页
- </Button>
- </div>
- </div>
- </div>
- )}
- {/* 表单弹窗 */}
- <PostForm ref={formRef} onSuccess={getList} />
- </div>
- );
- }
|