SendMailModal.jsx 9.85 KB
import React, { useState, useEffect } from 'react'
import { Modal, Form, Input, Button, Space, message, Tag, Alert, Radio, Tooltip } from 'antd'
import { SendOutlined, MailOutlined, PlusOutlined, UserAddOutlined, TeamOutlined, UserOutlined, CloseOutlined, ImportOutlined } from '@ant-design/icons'
import request from '../utils/request'

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/

export default function SendMailModal({ open, onClose, script, onSent }) {
  const [form] = Form.useForm()
  const [sending, setSending] = useState(false)
  // 收件人列表
  const [recipients, setRecipients] = useState([])
  // 单个添加输入框
  const [singleInput, setSingleInput] = useState('')
  // 批量导入输入框
  const [batchInput, setBatchInput] = useState('')
  const [showBatchInput, setShowBatchInput] = useState(false)
  // 发送模式: batch=群发(一封邮件所有人), individual=逐个发送(每人一封)
  const [sendMode, setSendMode] = useState('batch')

  useEffect(() => {
    if (open && script) {
      form.resetFields()
      // 从脚本预设的收件人初始化列表
      const savedEmails = (script.recipientEmails || '').split(',').map(e => e.trim()).filter(e => e && emailRegex.test(e))
      setRecipients(savedEmails)
      setSingleInput('')
      setBatchInput('')
      setShowBatchInput(false)
      setSendMode('batch')
      setTimeout(() => {
        form.setFieldsValue({
          emailSubject: script.emailSubject || '',
        })
      }, 0)
    }
  }, [open, script])

  // 添加单个收件人
  const handleAddSingle = () => {
    const email = singleInput.trim()
    if (!email) return
    if (!emailRegex.test(email)) {
      message.warning(`邮箱格式不正确: ${email}`)
      return
    }
    if (recipients.includes(email)) {
      message.info('该收件人已在列表中')
      return
    }
    setRecipients([...recipients, email])
    setSingleInput('')
  }

  // 批量导入收件人
  const handleBatchImport = () => {
    if (!batchInput.trim()) return
    const emails = batchInput.split(/[,;\n\s]+/).map(e => e.trim()).filter(e => e)
    const invalidEmails = emails.filter(e => !emailRegex.test(e))
    if (invalidEmails.length > 0) {
      message.warning(`以下邮箱格式不正确: ${invalidEmails.join(', ')}`)
    }
    const validEmails = emails.filter(e => emailRegex.test(e))
    // 去重
    const newEmails = validEmails.filter(e => !recipients.includes(e))
    if (newEmails.length > 0) {
      setRecipients([...recipients, ...newEmails])
      message.success(`成功添加 ${newEmails.length} 个收件人`)
    } else {
      message.info('没有新的收件人可添加')
    }
    setBatchInput('')
    setShowBatchInput(false)
  }

  // 删除收件人
  const handleRemoveRecipient = (email) => {
    setRecipients(recipients.filter(r => r !== email))
  }

  // 清空所有收件人
  const handleClearAll = () => {
    setRecipients([])
  }

  // 从脚本预设导入
  const handleImportFromScript = () => {
    const savedEmails = (script.recipientEmails || '').split(',').map(e => e.trim()).filter(e => e && emailRegex.test(e))
    const newEmails = savedEmails.filter(e => !recipients.includes(e))
    if (newEmails.length > 0) {
      setRecipients([...recipients, ...newEmails])
      message.success(`从脚本配置导入 ${newEmails.length} 个收件人`)
    } else {
      message.info('脚本配置中的收件人已全部在列表中')
    }
  }

  const handleSend = async () => {
    if (recipients.length === 0) {
      message.warning('请至少添加一个收件人')
      return
    }
    try {
      const values = await form.validateFields()
      setSending(true)

      const res = await request.post(`/sql-manage/${script.id}/send-mail`, {
        recipientEmails: recipients.join(','),
        emailSubject: values.emailSubject,
        sendMode,
      })

      if (res.success) {
        message.success(res.message)
        if (onSent) onSent()
        onClose()
      } else {
        message.error(res.message)
      }
    } catch (e) {
      if (e.errorFields) return
      message.error(e.message || '发送失败')
    } finally {
      setSending(false)
    }
  }

  if (!script) return null

  return (
    <Modal
      title={
        <Space>
          <SendOutlined />
          <span>手动发送邮件</span>
          <Tag color="blue">{script.name}</Tag>
        </Space>
      }
      open={open}
      onCancel={onClose}
      width={620}
      footer={
        <Space>
          <Button onClick={onClose}>取消</Button>
          <Button
            type="primary"
            icon={<SendOutlined />}
            loading={sending}
            onClick={handleSend}
            disabled={recipients.length === 0}
          >
            {sendMode === 'batch'
              ? `群发邮件(${recipients.length}人)`
              : `逐个发送(${recipients.length}封)`
            }
          </Button>
        </Space>
      }
    >
      <Alert
        type="info"
        showIcon
        style={{ marginBottom: 16 }}
        message="手动发送将执行脚本SQL,生成Excel附件并发送邮件到指定收件人。收件人可与脚本配置的不同。"
      />

      {/* 发送模式选择 */}
      <div style={{ marginBottom: 16 }}>
        <div style={{ marginBottom: 8, fontWeight: 500 }}>发送模式</div>
        <Radio.Group value={sendMode} onChange={e => setSendMode(e.target.value)}>
          <Radio value="batch">
            <Space size={4}>
              <TeamOutlined />
              <span>群发</span>
            </Space>
            <span style={{ fontSize: 12, color: '#999', marginLeft: 4 }}>— 一封邮件发送给所有人</span>
          </Radio>
          <Radio value="individual">
            <Space size={4}>
              <UserOutlined />
              <span>逐个发送</span>
            </Space>
            <span style={{ fontSize: 12, color: '#999', marginLeft: 4 }}>— 每人单独收到一封邮件</span>
          </Radio>
        </Radio.Group>
      </div>

      {/* 收件人管理 */}
      <div style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
          <span style={{ fontWeight: 500 }}>
            收件人 {recipients.length > 0 && <Tag color="blue" style={{ marginLeft: 4 }}>{recipients.length}</Tag>}
          </span>
          <Space size={4}>
            {script.recipientEmails && (
              <Tooltip title="从脚本配置的收件人导入">
                <Button size="small" type="dashed" icon={<ImportOutlined />} onClick={handleImportFromScript}>
                  导入预设
                </Button>
              </Tooltip>
            )}
            {recipients.length > 0 && (
              <Button size="small" type="dashed" danger onClick={handleClearAll}>
                清空
              </Button>
            )}
          </Space>
        </div>

        {/* 逐个添加 */}
        <Space.Compact style={{ width: '100%', marginBottom: 8 }}>
          <Input
            placeholder="输入邮箱地址,回车添加"
            value={singleInput}
            onChange={e => setSingleInput(e.target.value)}
            onPressEnter={handleAddSingle}
            prefix={<UserAddOutlined style={{ color: '#bbb' }} />}
          />
          <Button type="primary" icon={<PlusOutlined />} onClick={handleAddSingle}>
            添加
          </Button>
        </Space.Compact>

        {/* 批量导入切换 */}
        {!showBatchInput ? (
          <Button
            size="small"
            type="dashed"
            icon={<TeamOutlined />}
            onClick={() => setShowBatchInput(true)}
            style={{ marginBottom: 8 }}
          >
            批量导入
          </Button>
        ) : (
          <div style={{ marginBottom: 8 }}>
            <Input.TextArea
              placeholder="输入多个邮箱,用逗号、分号或换行分隔"
              value={batchInput}
              onChange={e => setBatchInput(e.target.value)}
              autoSize={{ minRows: 2, maxRows: 4 }}
            />
            <Space style={{ marginTop: 4 }}>
              <Button size="small" type="primary" onClick={handleBatchImport}>确认导入</Button>
              <Button size="small" onClick={() => { setShowBatchInput(false); setBatchInput('') }}>取消</Button>
            </Space>
          </div>
        )}

        {/* 收件人标签列表 */}
        {recipients.length > 0 && (
          <div style={{
            padding: '8px 12px',
            background: '#fafafa',
            border: '1px solid #f0f0f0',
            borderRadius: 6,
            maxHeight: 180,
            overflowY: 'auto',
          }}>
            <Space size={[8, 8]} wrap>
              {recipients.map((email) => (
                <Tag
                  key={email}
                  color="blue"
                  icon={<MailOutlined />}
                  closable
                  onClose={() => handleRemoveRecipient(email)}
                  closeIcon={<CloseOutlined style={{ fontSize: 10 }} />}
                >
                  {email}
                </Tag>
              ))}
            </Space>
          </div>
        )}
      </div>

      <Form form={form} layout="vertical">
        <Form.Item
          name="emailSubject"
          label="邮件主题"
          tooltip="为空则使用脚本配置的主题或自动生成"
        >
          <Input placeholder="例如:月度终端支付明细报表(自定义)" />
        </Form.Item>

        <div style={{ padding: '8px 12px', background: '#f6f6f6', borderRadius: 6, fontSize: 12, color: '#888' }}>
          <div>邮件将自动附带Excel附件,包含脚本查询结果数据。</div>
          {script.purpose && (
            <div style={{ marginTop: 4 }}>
              <span style={{ color: '#666' }}>脚本用途:</span>{script.purpose}
            </div>
          )}
        </div>
      </Form>
    </Modal>
  )
}