| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import React, { useState, useEffect } from 'react'; |
| import { API, isMobile, showError, showSuccess } from '../../helpers'; |
| import Title from '@douyinfe/semi-ui/lib/es/typography/title'; |
| import { |
| Button, |
| Input, |
| SideSheet, |
| Space, |
| Spin, |
| TextArea, |
| RadioGroup, |
| Radio, |
| Table, |
| Tag, |
| Descriptions |
| } from '@douyinfe/semi-ui'; |
| import { useTranslation } from 'react-i18next'; |
|
|
| const EditMessage = (props) => { |
| const { t } = useTranslation(); |
| |
| const [inputs, setInputs] = useState({ |
| title: '', |
| content: '', |
| format: 'markdown', |
| }); |
| |
| const [loading, setLoading] = useState(false); |
| const [messageDetails, setMessageDetails] = useState(null); |
| const [recipients, setRecipients] = useState([]); |
| const [recipientLoading, setRecipientLoading] = useState(false); |
| |
| const { title, content, format } = inputs; |
|
|
| const handleInputChange = (name, value) => { |
| setInputs((inputs) => ({ ...inputs, [name]: value })); |
| }; |
|
|
| const loadMessageDetails = async (messageId) => { |
| if (!messageId) return; |
| |
| setLoading(true); |
| try { |
| const res = await API.get(`/api/admin/messages/${messageId}`); |
| const { success, message, data } = res.data; |
| if (success) { |
| setMessageDetails(data); |
| setInputs({ |
| title: data.title || '', |
| content: data.content || '', |
| format: data.format || 'markdown', |
| }); |
| loadRecipients(messageId); |
| } else { |
| showError(message); |
| } |
| } catch (error) { |
| showError(error.message); |
| } |
| setLoading(false); |
| }; |
|
|
| const loadRecipients = async (messageId) => { |
| setRecipientLoading(true); |
| try { |
| const res = await API.get(`/api/admin/messages/${messageId}/recipients`); |
| const { success, message, data } = res.data; |
| if (success) { |
| setRecipients(data || []); |
| } else { |
| showError(message); |
| } |
| } catch (error) { |
| showError(error.message); |
| } |
| setRecipientLoading(false); |
| }; |
|
|
| useEffect(() => { |
| if (props.visible && props.editingMessage?.id) { |
| loadMessageDetails(props.editingMessage.id); |
| } |
| }, [props.visible, props.editingMessage?.id]); |
|
|
| const recipientColumns = [ |
| { |
| title: 'ID', |
| dataIndex: 'user_id', |
| width: 80, |
| }, |
| { |
| title: t('用户名'), |
| dataIndex: 'username', |
| }, |
| { |
| title: t('显示名称'), |
| dataIndex: 'display_name', |
| render: (text) => text || '-', |
| }, |
| { |
| title: t('阅读状态'), |
| dataIndex: 'read_at', |
| width: 120, |
| render: (text) => ( |
| <Tag color={text ? 'green' : 'orange'} size='small'> |
| {text ? t('已读') : t('未读')} |
| </Tag> |
| ), |
| }, |
| { |
| title: t('阅读时间'), |
| dataIndex: 'read_at', |
| width: 180, |
| render: (text) => { |
| return text ? new Date(text).toLocaleString() : '-'; |
| }, |
| }, |
| ]; |
|
|
| const submit = async () => { |
| setLoading(true); |
| |
| if (!inputs.title.trim()) { |
| showError(t('请输入消息标题')); |
| setLoading(false); |
| return; |
| } |
| |
| if (!inputs.content.trim()) { |
| showError(t('请输入消息内容')); |
| setLoading(false); |
| return; |
| } |
|
|
| const messageData = { |
| title: inputs.title.trim(), |
| content: inputs.content.trim(), |
| format: inputs.format, |
| }; |
|
|
| try { |
| const res = await API.put(`/api/admin/messages/${props.editingMessage.id}`, messageData); |
| const { success, message } = res.data; |
| if (success) { |
| showSuccess(t('消息更新成功')); |
| props.refresh(); |
| props.handleClose(); |
| } else { |
| showError(message); |
| } |
| } catch (error) { |
| showError(error.message); |
| } |
| setLoading(false); |
| }; |
|
|
| const handleCancel = () => { |
| setInputs({ |
| title: '', |
| content: '', |
| format: 'markdown', |
| }); |
| setMessageDetails(null); |
| setRecipients([]); |
| props.handleClose(); |
| }; |
|
|
| return ( |
| <> |
| <SideSheet |
| placement={'right'} |
| title={<Title level={3}>{t('编辑消息')}</Title>} |
| headerStyle={{ borderBottom: '1px solid var(--semi-color-border)' }} |
| bodyStyle={{ borderBottom: '1px solid var(--semi-color-border)' }} |
| visible={props.visible} |
| footer={ |
| <div style={{ display: 'flex', justifyContent: 'flex-end' }}> |
| <Space> |
| <Button theme='solid' size={'large'} onClick={submit} loading={loading}> |
| {t('更新消息')} |
| </Button> |
| <Button |
| theme='solid' |
| size={'large'} |
| type={'tertiary'} |
| onClick={handleCancel} |
| > |
| {t('取消')} |
| </Button> |
| </Space> |
| </div> |
| } |
| closeIcon={null} |
| onCancel={() => handleCancel()} |
| width={isMobile() ? '100%' : 900} |
| > |
| <Spin spinning={loading}> |
| <div style={{ padding: '20px 0' }}> |
| {messageDetails && ( |
| <div style={{ marginBottom: 24 }}> |
| <Descriptions |
| data={[ |
| { key: t('消息ID'), value: messageDetails.id }, |
| { key: t('创建时间'), value: new Date(messageDetails.created_at).toLocaleString() }, |
| { key: t('收件人数'), value: recipients.length }, |
| { key: t('已读人数'), value: recipients.filter(r => r.read_at).length }, |
| ]} |
| row |
| size="small" |
| /> |
| </div> |
| )} |
| |
| <Input |
| label={t('消息标题')} |
| placeholder={t('请输入消息标题')} |
| value={title} |
| onChange={(value) => handleInputChange('title', value)} |
| style={{ marginBottom: 20 }} |
| /> |
| |
| <div style={{ marginBottom: 20 }}> |
| <label style={{ display: 'block', marginBottom: 8, fontWeight: 600 }}> |
| {t('内容格式')} |
| </label> |
| <RadioGroup |
| type="button" |
| value={format} |
| onChange={(e) => handleInputChange('format', e.target.value)} |
| > |
| <Radio value="markdown">Markdown</Radio> |
| <Radio value="html">HTML</Radio> |
| </RadioGroup> |
| </div> |
| |
| <div style={{ marginBottom: 20 }}> |
| <label style={{ display: 'block', marginBottom: 8, fontWeight: 600 }}> |
| {t('消息内容')} |
| </label> |
| <TextArea |
| placeholder={format === 'html' ? t('请输入HTML格式的消息内容') : t('请输入Markdown格式的消息内容')} |
| value={content} |
| onChange={(value) => handleInputChange('content', value)} |
| rows={8} |
| style={{ fontFamily: 'monospace' }} |
| /> |
| </div> |
| |
| <div> |
| <label style={{ display: 'block', marginBottom: 8, fontWeight: 600 }}> |
| {t('收件人列表')} |
| </label> |
| |
| <Table |
| columns={recipientColumns} |
| dataSource={recipients} |
| loading={recipientLoading} |
| pagination={{ |
| pageSize: 10, |
| showSizeChanger: false, |
| size: 'small', |
| }} |
| size="small" |
| style={{ maxHeight: 400, overflow: 'auto' }} |
| /> |
| </div> |
| </div> |
| </Spin> |
| </SideSheet> |
| </> |
| ); |
| }; |
|
|
| export default EditMessage; |