|
|
@@ -0,0 +1,313 @@
|
|
|
+import React, { useState, useEffect, useRef } from 'react';
|
|
|
+import { Search, Plus, RefreshCw, Settings, ArrowLeft, Edit2, Trash2 } from 'lucide-react';
|
|
|
+import { mailNotifyConfigApi, MailNotifyConfigVO } from '../../api/mailNotifyConfig';
|
|
|
+import { toast } from 'sonner';
|
|
|
+import { Modal, Table, Input, Button, Switch, Space } from 'antd';
|
|
|
+import { ExclamationCircleOutlined } from '@ant-design/icons';
|
|
|
+import { Button as UIButton } from '../ui/button';
|
|
|
+import type { ColumnsType } from 'antd/es/table';
|
|
|
+import EmailNotifyForm, { EmailNotifyFormRef } from './EmailNotifyForm';
|
|
|
+import MailTemplateManagement from '../mailTemplate/MailTemplateManagement';
|
|
|
+
|
|
|
+export default function EmailNotifyManagement() {
|
|
|
+ console.log('EmailNotifyManagement 组件开始渲染');
|
|
|
+ const [showTemplateManagement, setShowTemplateManagement] = useState(false);
|
|
|
+ const [loading, setLoading] = useState(true);
|
|
|
+ const [list, setList] = useState<MailNotifyConfigVO[]>([]);
|
|
|
+ const [total, setTotal] = useState(0);
|
|
|
+ const [queryParams, setQueryParams] = useState({
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ name: '',
|
|
|
+ templateName: '',
|
|
|
+ });
|
|
|
+
|
|
|
+ // 子组件引用
|
|
|
+ const formRef = useRef<EmailNotifyFormRef>(null);
|
|
|
+
|
|
|
+ // 获取列表
|
|
|
+ const getList = async (params?: typeof queryParams) => {
|
|
|
+ const currentParams = params || queryParams;
|
|
|
+ console.log('EmailNotifyManagement: 开始获取列表,参数:', currentParams);
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const response = await mailNotifyConfigApi.listIsMailNotifyConfigPage(currentParams);
|
|
|
+ console.log('EmailNotifyManagement: API 响应:', response);
|
|
|
+ setList(response.list || []);
|
|
|
+ setTotal(response.total || 0);
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('EmailNotifyManagement: 获取列表失败:', error);
|
|
|
+ toast.error(error.message || '获取邮件提醒列表失败');
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ 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 = {
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ name: '',
|
|
|
+ templateName: '',
|
|
|
+ };
|
|
|
+ setQueryParams(resetParams);
|
|
|
+ getList(resetParams);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 打开表单弹窗
|
|
|
+ const openForm = (type: string, id?: number) => {
|
|
|
+ formRef.current?.open(type, id);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除
|
|
|
+ const handleDelete = (id: number) => {
|
|
|
+ Modal.confirm({
|
|
|
+ title: '确认删除',
|
|
|
+ icon: <ExclamationCircleOutlined />,
|
|
|
+ content: '确定要删除这条邮件提醒配置吗?',
|
|
|
+ okText: '确定',
|
|
|
+ okType: 'danger',
|
|
|
+ cancelText: '取消',
|
|
|
+ onOk: async () => {
|
|
|
+ try {
|
|
|
+ await mailNotifyConfigApi.deleteIsMailNotifyConfig(id);
|
|
|
+ toast.success('删除成功');
|
|
|
+ await getList();
|
|
|
+ } catch (error: any) {
|
|
|
+ toast.error(error.message || '删除失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 状态修改
|
|
|
+ const handleStatusChange = async (checked: boolean, record: MailNotifyConfigVO) => {
|
|
|
+ try {
|
|
|
+ const data: MailNotifyConfigVO = {
|
|
|
+ ...record,
|
|
|
+ status: checked ? '1' : '0',
|
|
|
+ };
|
|
|
+ await mailNotifyConfigApi.updateIsMailNotifyConfig(data);
|
|
|
+ toast.success(checked ? '激活成功' : '取消激活成功');
|
|
|
+ await getList();
|
|
|
+ } catch (error: any) {
|
|
|
+ toast.error(error.message || '状态修改失败');
|
|
|
+ // 恢复原状态
|
|
|
+ await getList();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 格式化时间
|
|
|
+ const formattedTime = (totalSeconds?: number) => {
|
|
|
+ if (!totalSeconds) return '-';
|
|
|
+ const days = Math.floor(totalSeconds / (24 * 60 * 60));
|
|
|
+ const hours = Math.floor((totalSeconds % (24 * 60 * 60)) / (60 * 60));
|
|
|
+ const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
|
|
|
+ const seconds = totalSeconds % 60;
|
|
|
+ const parts = [];
|
|
|
+ if (days > 0) parts.push(`${days}天`);
|
|
|
+ if (hours > 0) parts.push(`${hours}小时`);
|
|
|
+ if (minutes > 0) parts.push(`${minutes}分钟`);
|
|
|
+ if (seconds > 0) parts.push(`${seconds}秒`);
|
|
|
+ return parts.length > 0 ? parts.join(' ') : '0秒';
|
|
|
+ };
|
|
|
+
|
|
|
+ // 表格列配置
|
|
|
+ const columns: ColumnsType<MailNotifyConfigVO> = [
|
|
|
+ {
|
|
|
+ title: '序号',
|
|
|
+ width: 80,
|
|
|
+ align: 'center',
|
|
|
+ render: (_: any, __: MailNotifyConfigVO, index: number) => {
|
|
|
+ return (queryParams.pageNo - 1) * queryParams.pageSize + index + 1;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '提醒事项',
|
|
|
+ dataIndex: 'name',
|
|
|
+ key: 'name',
|
|
|
+ align: 'center',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '是否激活',
|
|
|
+ dataIndex: 'status',
|
|
|
+ width: 120,
|
|
|
+ align: 'center',
|
|
|
+ render: (status: string | undefined, record: MailNotifyConfigVO) => {
|
|
|
+ const isChecked = status === '1';
|
|
|
+ return (
|
|
|
+ <Switch
|
|
|
+ checked={isChecked}
|
|
|
+ checkedChildren="ON"
|
|
|
+ unCheckedChildren="OFF"
|
|
|
+ onChange={(checked) => handleStatusChange(checked, record)}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '邮件模板编码',
|
|
|
+ dataIndex: 'templateCode',
|
|
|
+ key: 'templateCode',
|
|
|
+ align: 'center',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '提醒时长',
|
|
|
+ key: 'reminderTime',
|
|
|
+ align: 'center',
|
|
|
+ render: (_: any, record: MailNotifyConfigVO) => {
|
|
|
+ return formattedTime(record.reminderTime);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '操作',
|
|
|
+ width: 150,
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'right',
|
|
|
+ render: (_: any, record: MailNotifyConfigVO) => {
|
|
|
+ return (
|
|
|
+ <div className="flex items-center gap-2 justify-center">
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => openForm('update', record.configId)}
|
|
|
+ className="h-8 px-2"
|
|
|
+ >
|
|
|
+ <Edit2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">编辑</span>
|
|
|
+ </UIButton>
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleDelete(record.configId!)}
|
|
|
+ className="h-8 px-2 text-red-600 hover:text-red-700"
|
|
|
+ >
|
|
|
+ <Trash2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">删除</span>
|
|
|
+ </UIButton>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 如果显示邮件模板管理,直接返回模板管理组件(不显示邮件提醒的搜索栏)
|
|
|
+ if (showTemplateManagement) {
|
|
|
+ return <MailTemplateManagement onBack={() => setShowTemplateManagement(false)} />;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="space-y-6">
|
|
|
+ {/* 搜索栏 */}
|
|
|
+ <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-6">
|
|
|
+ <div className="flex items-center gap-4 flex-wrap">
|
|
|
+ <div className="flex items-center gap-2 flex-1 min-w-[200px]">
|
|
|
+ <label className="text-sm text-gray-700 whitespace-nowrap">提醒事项:</label>
|
|
|
+ <Input
|
|
|
+ placeholder="请输入提醒事项"
|
|
|
+ value={queryParams.name}
|
|
|
+ onChange={(e) => setQueryParams({ ...queryParams, name: e.target.value })}
|
|
|
+ onPressEnter={handleQuery}
|
|
|
+ className="flex-1 max-w-[240px]"
|
|
|
+ allowClear
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-2 flex-1 min-w-[200px]">
|
|
|
+ <label className="text-sm text-gray-700 whitespace-nowrap">邮件模板:</label>
|
|
|
+ <Input
|
|
|
+ placeholder="请输入邮件模板"
|
|
|
+ value={queryParams.templateName}
|
|
|
+ onChange={(e) => setQueryParams({ ...queryParams, templateName: e.target.value })}
|
|
|
+ onPressEnter={handleQuery}
|
|
|
+ className="flex-1 max-w-[240px]"
|
|
|
+ allowClear
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <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={<Settings className="w-4 h-4" />}
|
|
|
+ onClick={() => setShowTemplateManagement(true)}
|
|
|
+ >
|
|
|
+ 设置邮件模板
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 表格 */}
|
|
|
+ <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
+ <Table
|
|
|
+ columns={columns}
|
|
|
+ dataSource={list}
|
|
|
+ rowKey="configId"
|
|
|
+ loading={loading}
|
|
|
+ pagination={false}
|
|
|
+ scroll={{ x: 'max-content' }}
|
|
|
+ />
|
|
|
+ {/* 分页 */}
|
|
|
+ {!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 items-center gap-2">
|
|
|
+ <Button
|
|
|
+ size="small"
|
|
|
+ disabled={queryParams.pageNo === 1}
|
|
|
+ onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo - 1 })}
|
|
|
+ >
|
|
|
+ 上一页
|
|
|
+ </Button>
|
|
|
+ <span className="text-sm text-gray-600">
|
|
|
+ 第 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize)} 页
|
|
|
+ </span>
|
|
|
+ <Button
|
|
|
+ size="small"
|
|
|
+ disabled={queryParams.pageNo >= Math.ceil(total / queryParams.pageSize)}
|
|
|
+ onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo + 1 })}
|
|
|
+ >
|
|
|
+ 下一页
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 表单弹窗 */}
|
|
|
+ <EmailNotifyForm ref={formRef} onSuccess={getList} />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|