/* Copyright (c) 2025 Tethys Plex This file is part of Veloera. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import React, { useEffect, useState } from 'react'; import { API, showError, showSuccess } from '../../helpers'; import { Button, Form, Popconfirm, Space, Table, Tag, Tooltip, } from '@douyinfe/semi-ui'; import { ITEMS_PER_PAGE } from '../../constants'; import CreateMessage from './CreateMessage'; import EditMessage from './EditMessage'; import { useTranslation } from 'react-i18next'; const MessageList = () => { const { t } = useTranslation(); const columns = [ { title: 'ID', dataIndex: 'id', width: 80, }, { title: t('标题'), dataIndex: 'title', render: (text) => (
{text}
), }, { title: t('格式'), dataIndex: 'format', width: 100, render: (text) => ( {text?.toUpperCase()} ), }, { title: t('收件人数'), dataIndex: 'stats', width: 100, render: (stats) => ( {stats?.total_recipients || 0} ), }, { title: t('创建时间'), dataIndex: 'created_at', width: 180, render: (text) => { return new Date(text).toLocaleString(); }, }, { title: t('操作'), dataIndex: 'operate', width: 200, render: (_, record) => (
deleteMessage(record.id)} >
), }, ]; const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(true); const [activePage, setActivePage] = useState(1); const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); const [searchKeyword, setSearchKeyword] = useState(''); const [searching, setSearching] = useState(false); const [dateRange, setDateRange] = useState([]); const [messageCount, setMessageCount] = useState(ITEMS_PER_PAGE); const [showCreateMessage, setShowCreateMessage] = useState(false); const [showEditMessage, setShowEditMessage] = useState(false); const [editingMessage, setEditingMessage] = useState({ id: undefined, }); const setMessageFormat = (messages) => { for (let i = 0; i < messages.length; i++) { messages[i].key = messages[i].id; } setMessages(messages); }; const loadMessages = async (startIdx, pageSize) => { const res = await API.get(`/api/admin/messages?p=${startIdx}&page_size=${pageSize}`); const { success, message, data } = res.data; if (success) { const newPageData = data.items || []; setActivePage(data.page || 1); setMessageCount(data.total || 0); setMessageFormat(newPageData); } else { showError(message); } setLoading(false); }; useEffect(() => { loadMessages(0, pageSize) .then() .catch((reason) => { showError(reason); }); }, []); const deleteMessage = async (messageId) => { const res = await API.delete(`/api/admin/messages/${messageId}`); const { success, message } = res.data; if (success) { showSuccess(t('消息删除成功')); // Remove the deleted message from the list setMessages(messages.filter(msg => msg.id !== messageId)); setMessageCount(messageCount - 1); } else { showError(message); } }; const searchMessages = async ( startIdx, pageSize, searchKeyword, dateRange, ) => { if (searchKeyword === '' && dateRange.length === 0) { // if keyword is blank and no date range, load messages instead. await loadMessages(startIdx, pageSize); return; } setSearching(true); let params = new URLSearchParams({ p: startIdx, page_size: pageSize, }); if (searchKeyword) { params.append('keyword', searchKeyword); } if (dateRange.length === 2) { params.append('start_date', dateRange[0].toISOString()); params.append('end_date', dateRange[1].toISOString()); } const res = await API.get(`/api/admin/messages/search?${params.toString()}`); const { success, message, data } = res.data; if (success) { const newPageData = data.items || []; setActivePage(data.page || 1); setMessageCount(data.total || 0); setMessageFormat(newPageData); } else { showError(message); } setSearching(false); }; const handleKeywordChange = async (value) => { setSearchKeyword(value.trim()); }; const handlePageChange = (page) => { setActivePage(page); if (searchKeyword === '' && dateRange.length === 0) { loadMessages(page, pageSize).then(); } else { searchMessages(page, pageSize, searchKeyword, dateRange).then(); } }; const closeCreateMessage = () => { setShowCreateMessage(false); }; const closeEditMessage = () => { setShowEditMessage(false); setEditingMessage({ id: undefined, }); }; const refresh = async () => { setActivePage(1); if (searchKeyword === '' && dateRange.length === 0) { await loadMessages(1, pageSize); } else { await searchMessages(1, pageSize, searchKeyword, dateRange); } }; const handlePageSizeChange = async (size) => { localStorage.setItem('message-page-size', size + ''); setPageSize(size); setActivePage(1); loadMessages(1, size) .then() .catch((reason) => { showError(reason); }); }; return ( <>
{ searchMessages(1, pageSize, searchKeyword, dateRange); }} labelPosition='left' >
handleKeywordChange(value)} style={{ width: 200 }} /> { setDateRange(value || []); }} style={{ width: 300 }} />
t('第 {{start}} - {{end}} 条,共 {{total}} 条', { start: page.currentStart, end: page.currentEnd, total: messageCount, }), currentPage: activePage, pageSize: pageSize, total: messageCount, pageSizeOpts: [10, 20, 50, 100], showSizeChanger: true, onPageSizeChange: (size) => { handlePageSizeChange(size); }, onPageChange: handlePageChange, }} loading={loading} /> ); }; export default MessageList;