diff --git a/src/components/DrawerForm/index.tsx b/src/components/DrawerForm/index.tsx index 2c31598..f213dd7 100644 --- a/src/components/DrawerForm/index.tsx +++ b/src/components/DrawerForm/index.tsx @@ -1,9 +1,9 @@ -import React, { useImperativeHandle, forwardRef } from "react"; -import { DrawerForm } from "@ant-design/pro-components"; -import type { ProFormColumnsType } from "@ant-design/pro-components"; -import { BetaSchemaForm } from "@ant-design/pro-components"; -import { Button, Drawer, Space, Typography } from "antd"; -import { CloseOutlined } from "@ant-design/icons"; +import { CloseOutlined } from '@ant-design/icons'; +import type { ProFormColumnsType } from '@ant-design/pro-components'; +import { BetaSchemaForm, DrawerForm } from '@ant-design/pro-components'; +import { Button, type ColProps, Drawer, Space, Typography } from 'antd'; +import React, { forwardRef, useImperativeHandle } from 'react'; + const { Title } = Typography; interface ConfigurableDrawerFormProps { title?: string; @@ -11,6 +11,8 @@ interface ConfigurableDrawerFormProps { onSubmit?: (values: any) => Promise; initialValues?: Record; width?: number; + labelCol?: ColProps; + wrapperCol?: ColProps; } export interface ConfigurableDrawerFormRef { @@ -21,102 +23,115 @@ export interface ConfigurableDrawerFormRef { const ConfigurableDrawerForm = forwardRef< ConfigurableDrawerFormRef, ConfigurableDrawerFormProps ->(({ title = "表单", columns, onSubmit, initialValues, width = 600 }, ref) => { - const [open, setOpen] = React.useState(false); - const [formData, setFormData] = React.useState(initialValues || {}); - const [loading, setLoading] = React.useState(false); - // 添加表单实例引用 - const formRef = React.useRef(null); - useImperativeHandle(ref, () => ({ - open: (data) => { - if (data) { - setFormData(data); - } - console.log("open"); - setOpen(true); +>( + ( + { + title = '表单', + labelCol = { span: 4 }, + wrapperCol = { span: 20 }, + columns, + onSubmit, + initialValues, + width = 600, }, - close: () => setOpen(false), - })); - - const handleSubmit = async () => { - try { - if (onSubmit) { - await formRef.current?.validateFields(); - setLoading(true); - const values = formRef.current?.getFieldsValue(); - const success = await onSubmit(values); - if (success) { - setOpen(false); - return true; + ref, + ) => { + const [open, setOpen] = React.useState(false); + const [formData, setFormData] = React.useState(initialValues || {}); + const [loading, setLoading] = React.useState(false); + // 添加表单实例引用 + const formRef = React.useRef(null); + useImperativeHandle(ref, () => ({ + open: (data) => { + if (data) { + setFormData(data); } - return false; + console.log('open'); + setOpen(true); + }, + close: () => setOpen(false), + })); + + const handleSubmit = async () => { + try { + if (onSubmit) { + await formRef.current?.validateFields(); + setLoading(true); + const values = formRef.current?.getFieldsValue(); + const success = await onSubmit(values); + if (success) { + setOpen(false); + return true; + } + return false; + } + return true; + } finally { + setLoading(false); } - return true; - } finally { - setLoading(false); - } - }; - const customHeader = ( -
- - {title} - - - - - } - > - {/* {customHeader} */} - - - ); -}); + > + + {title} + +
+ ); + return ( + setOpen(false)} + width={width} + footer={ + + + + + } + > + {/* {customHeader} */} + + + ); + }, +); export default ConfigurableDrawerForm; diff --git a/src/pages/system/messages/mail/account/config.tsx b/src/pages/system/messages/mail/account/config.tsx new file mode 100644 index 0000000..76a71db --- /dev/null +++ b/src/pages/system/messages/mail/account/config.tsx @@ -0,0 +1,178 @@ +import type { + ProColumns, + ProDescriptionsItemProps, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { MailAccountVO } from '@/services/system/message/mail/account'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '邮箱', + dataIndex: 'mail', + width: 100, + }, + { + title: '用户名', + dataIndex: 'username', + }, + { + title: 'SMTP 服务器域名', + dataIndex: 'host', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: 'SMTP 服务器端口', + dataIndex: 'port', + + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '是否开启 SSL', + dataIndex: 'sslEnable', + valueType: 'radio', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '是否开启 STARTTLS', + dataIndex: 'starttlsEnable', + valueType: 'radio', + hideInSearch: true, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + hideInSearch: true, // 在搜索表单中隐藏 + render: (_, record: MailAccountVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (): ProFormColumnsType[] => [ + { + title: '邮箱', + dataIndex: 'mail', + }, + { + title: '用户名', + dataIndex: 'username', + formItemProps: { + rules: [ + { + required: true, + message: '请输入用户名', + }, + ], + }, + }, + { + title: '密码', + dataIndex: 'password', + formItemProps: { + rules: [ + { + required: true, + message: '请输入密码', + }, + ], + }, + }, + { + title: 'SMTP 服务器域名', + dataIndex: 'host', + + formItemProps: { + rules: [ + { + required: true, + message: '请输入SMTP 服务器域名', + }, + ], + }, + }, + { + title: 'SMTP 服务器端口', + dataIndex: 'port', + valueType: 'digit', + fieldProps: { + defaultValue: 465, + }, + formItemProps: { + rules: [ + { + required: true, + message: 'SMTP 服务器端口', + }, + ], + }, + }, + { + title: '是否开启 SSL', + dataIndex: 'sslEnable', + valueType: 'select', + fieldProps: { + placeholder: '请选择是否开启 SSL', + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请选择是否开启 SSL', + }, + ], + }, + }, + { + title: '是否开启 STARTTLS', + dataIndex: 'starttlsEnable', + valueType: 'select', + fieldProps: { + placeholder: '请选择是否开启 STARTTLS', + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请选择是否开启 STARTTLS', + }, + ], + }, + }, +]; + +export const formTestColumns = (type: string): ProFormColumnsType[] => [ + { + title: '模板内容', + dataIndex: 'content', + valueType: 'textarea', + }, + { + title: '手机号', + dataIndex: 'mobile', + }, +]; +// { +// title: "模板内容", +// dataIndex: "content", +// valueType: "textarea", +// }, diff --git a/src/pages/system/messages/mail/account/index.tsx b/src/pages/system/messages/mail/account/index.tsx new file mode 100644 index 0000000..7cfd5a9 --- /dev/null +++ b/src/pages/system/messages/mail/account/index.tsx @@ -0,0 +1,152 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { + ActionType, + ProColumns, + ProCoreActionType, +} from '@ant-design/pro-components'; +import { message, Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createMailAccount, + deleteMailAccount, + getMailAccount, + getMailAccountPage, + type MailAccountVO, + updateMailAccount, +} from '@/services/system/message/mail/account'; +import { baseTenantColumns, formColumns, formTestColumns } from './config'; + +const SyStemMessageSmsTemplate = () => { + const tableRef = useRef(null); + const configurableDrawerRef = useRef(null); + const [type, setType] = useState<'create' | 'update' | 'test'>('create'); + const testRef = useRef(null); + const [currentItem, setCurrentItem] = useState(); + const onFetch = async ( + params: MailAccountVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getMailAccountPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleSend = async (record: MailAccountVO) => { + setType('test'); + setCurrentItem(record); + testRef.current?.open(record); + }; + + const handleEdit = (record: MailAccountVO) => { + setType('update'); + setCurrentItem(record); + + configurableDrawerRef.current?.open(record); + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open(); + }; + const handleSubmit = useCallback( + async (values: MailAccountVO) => { + if (type === 'create') { + await createMailAccount(values); + message.success('创建成功'); + } else { + await updateMailAccount({ + ...values, + id: currentItem!.id, + }); + message.success('编辑成功'); + } + tableRef.current?.reload(); + return true; + }, + [type, currentItem], + ); + + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + text: React.ReactNode, + record: MailAccountVO, + _: number, + action: ProCoreActionType | undefined, + ) => [ + handleEdit(record)}> + 编辑 + , + handleSend(record)}> + 详情 + , + { + await deleteMailAccount(record.id); + message.success('删除成功'); + action?.reload?.(); + }} + okText="是" + cancelText="否" + > + + 删除 + + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + toolbarActions={toolbarActions} + showSelection={false} + /> + + + ); +}; + +export default SyStemMessageSmsTemplate; diff --git a/src/pages/system/messages/mail/log/config.tsx b/src/pages/system/messages/mail/log/config.tsx new file mode 100644 index 0000000..59f2d12 --- /dev/null +++ b/src/pages/system/messages/mail/log/config.tsx @@ -0,0 +1,171 @@ +import type { + ProColumns, + ProDescriptionsItemProps, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { SmsLogVO } from '@/services/system/message/sms/log'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '编号', + dataIndex: 'id', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '手机号', + dataIndex: 'mobile', + }, + { + title: '短信内容', + dataIndex: 'templateContent', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '发送状态', + dataIndex: 'sendStatus', + }, + { + title: '发送人名称', + dataIndex: 'templateNickname', + hideInSearch: true, // 在搜索表单中隐藏 + }, + + { + title: '接收状态', + dataIndex: 'receiveStatus', + }, + { + title: '短信渠道', + dataIndex: 'channelId', + }, + { + title: '模板编号', + dataIndex: 'templateId', + }, + { + title: '短信类型', + dataIndex: 'templateType', + hideInSearch: true, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + hideInSearch: true, + search: { + transform: (value) => { + return { + [`createTime[0]`]: dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + [`createTime[1]`]: dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: SmsLogVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, + { + title: '接收时间', + dataIndex: 'receiveTime', + valueType: 'dateRange', + hideInTable: true, + search: { + transform: (value) => { + return { + [`createTime[0]`]: dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + [`createTime[1]`]: dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: SmsLogVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const descriptionsColumns = (): ProDescriptionsItemProps< + Record, + 'text' +>[] => [ + { + title: '日志主键', + key: 'id', + dataIndex: 'id', + }, + { + title: '短信渠道', + key: 'channelId', + dataIndex: 'channelId', + }, + { + title: '短信模板', + key: 'templateCode', + dataIndex: 'templateCode', + }, + { + title: 'API 的模板编号', + key: 'apiTemplateId', + dataIndex: 'apiTemplateId', + }, + { + title: '用户信息', + key: 'mobile', + dataIndex: 'mobile', + }, + { + title: '短信内容', + key: 'templateContent', + dataIndex: 'templateContent', + }, + { + title: '短信参数', + key: 'templateParams', + dataIndex: 'templateParams', + }, + { + title: '创建时间', + key: 'createTime', + dataIndex: 'createTime', + }, + { + title: '发送状态', + key: 'sendStatus', + dataIndex: 'sendStatus', + }, + { + title: '发送时间', + key: 'sendTime', + dataIndex: 'sendTime', + }, + { + title: 'API 发送结果', + key: 'apiSendMsg', + dataIndex: 'apiSendMsg', + }, + { + title: 'API 短信编号', + key: 'apiSerialNo', + dataIndex: 'apiSerialNo', + }, + { + title: 'API 请求编号', + key: 'apiRequestId', + dataIndex: 'apiRequestId', + }, + { + title: 'API 接收状态', + key: 'receiveStatus', + dataIndex: 'receiveStatus', + }, + { + title: 'API 接收结果', + key: 'apiReceiveMsg', + dataIndex: 'apiReceiveMsg', + }, +]; diff --git a/src/pages/system/messages/mail/log/index.tsx b/src/pages/system/messages/mail/log/index.tsx new file mode 100644 index 0000000..39a5a06 --- /dev/null +++ b/src/pages/system/messages/mail/log/index.tsx @@ -0,0 +1,70 @@ +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import React, { useRef } from 'react'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import ModalDescriptions, { + type DescriptionsFormRef, +} from '@/components/ModalDescriptions'; +import { + getSmsLogPage, + type SmsLogVO, +} from '@/services/system/message/sms/log'; +import { baseTenantColumns, descriptionsColumns } from './config'; + +const SyStemMessageNotifyMessage = () => { + const tableRef = useRef(null); + const descriptionsRef = useRef(null); + const onFetch = async ( + params: SmsLogVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getSmsLogPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleDetail = (record: SmsLogVO) => { + descriptionsRef.current?.open(record); + }; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 80, + render: (text: React.ReactNode, record: SmsLogVO) => [ + handleDetail(record)}> + 详情 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + showSelection={false} + /> + + + ); +}; + +export default SyStemMessageNotifyMessage; diff --git a/src/pages/system/messages/mail/template/config.tsx b/src/pages/system/messages/mail/template/config.tsx new file mode 100644 index 0000000..d98f7e2 --- /dev/null +++ b/src/pages/system/messages/mail/template/config.tsx @@ -0,0 +1,171 @@ +import type { + ProColumns, + ProDescriptionsItemProps, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { SmsTemplateVO } from '@/services/system/message/sms/template'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '模板编码', + dataIndex: 'code', + width: 100, + }, + { + title: '模板名称', + dataIndex: 'name', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '模板内容', + dataIndex: 'content', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '短信类型', + dataIndex: 'type', + }, + { + title: '状态', + dataIndex: 'status', + }, + { + title: '备注', + dataIndex: 'remark', + hideInSearch: true, + }, + { + title: '短信 API 的模板编号', + dataIndex: 'apiTemplateId', + }, + { + title: '短信渠道', + dataIndex: 'channelCode', + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + render: (_, record: SmsTemplateVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (type: string): ProFormColumnsType[] => [ + { + title: '短信渠道编号', + dataIndex: 'channelId', + valueType: 'select', + formItemProps: { + rules: [ + { + required: true, + message: '请选择短信渠道编号', + }, + ], + }, + }, + { + title: '短信类型', + dataIndex: 'type', + valueType: 'select', + formItemProps: { + rules: [ + { + required: true, + message: '请选择短信类型', + }, + ], + }, + }, + { + title: '模板编号', + dataIndex: 'code', + formItemProps: { + rules: [ + { + required: true, + message: '请输入模板编号', + }, + ], + }, + }, + { + title: '模板名称', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入模板名称', + }, + ], + }, + }, + { + title: '模板内容', + dataIndex: 'type', + + formItemProps: { + rules: [ + { + required: true, + message: '请输入模板内容', + }, + ], + }, + }, + { + title: '开启状态', + dataIndex: 'status', + valueType: 'select', + + fieldProps: { + placeholder: '请选择开启状态', + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请选择状态', + }, + ], + }, + }, + { + title: '短信 API 模板编号', + dataIndex: 'apiTemplateId', + fieldProps: { + placeholder: '请输入短信 API 的模板编号', + }, + }, + { + title: '备注', + dataIndex: 'remark', + fieldProps: { + placeholder: '请输入备注', + }, + }, +]; + +export const formTestColumns = (type: string): ProFormColumnsType[] => [ + { + title: '模板内容', + dataIndex: 'content', + valueType: 'textarea', + }, + { + title: '手机号', + dataIndex: 'mobile', + }, +]; diff --git a/src/pages/system/messages/mail/template/index.tsx b/src/pages/system/messages/mail/template/index.tsx new file mode 100644 index 0000000..97659a7 --- /dev/null +++ b/src/pages/system/messages/mail/template/index.tsx @@ -0,0 +1,174 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { + ActionType, + ProColumns, + ProCoreActionType, +} from '@ant-design/pro-components'; +import { message, Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createSmsTemplate, + deleteSmsTemplate, + getSmsTemplatePage, + type SmsTemplateVO, + sendSms, + updateSmsTemplate, +} from '@/services/system/message/sms/template'; +import { baseTenantColumns, formColumns, formTestColumns } from './config'; + +const SyStemMessageSmsTemplate = () => { + const tableRef = useRef(null); + const configurableDrawerRef = useRef(null); + const [type, setType] = useState<'create' | 'update' | 'test'>('create'); + const testRef = useRef(null); + const [currentItem, setCurrentItem] = useState(); + const onFetch = async ( + params: SmsTemplateVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getSmsTemplatePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleSend = async (record: SmsTemplateVO) => { + setType('test'); + setCurrentItem(record); + testRef.current?.open(record); + }; + + const handleEdit = (record: SmsTemplateVO) => { + setType('update'); + setCurrentItem(record); + + configurableDrawerRef.current?.open(record); + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open(); + }; + const handleSubmit = useCallback( + async (values: SmsTemplateVO) => { + if (type === 'create') { + await createSmsTemplate(values); + message.success('创建成功'); + } else { + await updateSmsTemplate({ + ...values, + id: currentItem!.id, + }); + message.success('编辑成功'); + } + tableRef.current?.reload(); + return true; + }, + [type, currentItem], + ); + + const handleTestSubmit = useCallback( + async (values: SmsTemplateVO) => { + if (currentItem?.status === 1) { + return message.error('请先开启状态'); + } + const res = await sendSms({ + ...currentItem, + ...values, + mobile: '', + templateParams: new Map(), + templateCode: currentItem!.code, + }); + message.success('提交发送成功!发送结果,见发送日志编号:' + res); + tableRef.current?.reload(); + return true; + }, + [type, currentItem], + ); + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + text: React.ReactNode, + record: SmsTemplateVO, + _: number, + action: ProCoreActionType | undefined, + ) => [ + handleEdit(record)}> + 编辑 + , + handleSend(record)}> + 测试 + , + { + await deleteSmsTemplate(record.id); + message.success('删除成功'); + action?.reload?.(); + }} + okText="是" + cancelText="否" + > + + 删除 + + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + toolbarActions={toolbarActions} + showSelection={false} + /> + + + + ); +}; + +export default SyStemMessageSmsTemplate; diff --git a/src/services/system/message/mail/account.tsx b/src/services/system/message/mail/account.tsx index e7730cd..aeefc02 100644 --- a/src/services/system/message/mail/account.tsx +++ b/src/services/system/message/mail/account.tsx @@ -6,6 +6,7 @@ export interface MailAccountVO { username: string; password: string; host: string; + createTime: Date; port: number; sslEnable: boolean; starttlsEnable: boolean;