Browse Source

我的消息

main
cs 1 week ago
parent
commit
4dafc2062c
  1. 181
      src/pages/message/my/index.tsx
  2. 10
      src/pages/message/template/index.tsx
  3. 15
      src/store/message/my.ts

181
src/pages/message/my/index.tsx

@ -1,5 +1,5 @@
import {useTranslation} from '@/i18n.ts' import {useTranslation} from '@/i18n.ts'
import {Badge, Button, Divider, Form, Input, Space, Tooltip} from 'antd'
import {Badge, Button, Divider, Form, Input, Select, Space, Tooltip} from 'antd'
import {useAtom, useAtomValue} from 'jotai' import {useAtom, useAtomValue} from 'jotai'
import React, {useEffect, useMemo, useState} from 'react' import React, {useEffect, useMemo, useState} from 'react'
import {BetaSchemaForm, ProColumns, ProFormColumnsType,} from '@ant-design/pro-components' import {BetaSchemaForm, ProColumns, ProFormColumnsType,} from '@ant-design/pro-components'
@ -10,6 +10,7 @@ import {getValueCount, unSetColumnRules} from '@/utils'
import {Table as ProTable} from '@/components/table' import {Table as ProTable} from '@/components/table'
import {msgListAtom, msgSearchAtom, saveMsgAtom} from "@/store/message/my.ts"; import {msgListAtom, msgSearchAtom, saveMsgAtom} from "@/store/message/my.ts";
import {templateAllListAtom} from "@/store/message/template.ts"; import {templateAllListAtom} from "@/store/message/template.ts";
import {IMsgTemplate} from "@/types/message/template.ts";
const i18nPrefix = 'msgMy.list' const i18nPrefix = 'msgMy.list'
@ -26,8 +27,77 @@ const MdwMessage = () => {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [openFilter, setFilterOpen] = useState(false) const [openFilter, setFilterOpen] = useState(false)
const [currentTemplate, setCurrentTemplate] = useState<IMsgTemplate>()
const [searchKey, setSearchKey] = useState(search?.title) const [searchKey, setSearchKey] = useState(search?.title)
const [templateType, setTemplateType] = useState('')
const typeHandlerChange = (value: string) => {
if (value !== 'EMAIL') {
setTemplateTitle('')
form.setFieldsValue({'title': undefined})
}
setTemplateType(value)
}
const templateChange = (index: number) => {
if (templateList && index !== undefined) {
// key转换
const result = templateList[index].fields.split(',').map(item => {
return {
field_key: item,
field_value: ''
};
});
setCurrentTemplate(templateList[index])
form.setFieldsValue({...templateList[index], 'fieldList': result})
setTemplateType(templateList[index].type)
setTemplateTitle(templateList[index].title)
setTemplateContent(templateList[index].content)
} else {
form.resetFields()
setTemplateType('')
setCurrentTemplate(undefined)
}
}
const [templateTitle, setTemplateTitle] = useState('');
const [templateContent, setTemplateContent] = useState('');
useEffect(() => {
handleChange()
}, [templateTitle, templateContent]);
const handleContentChange = (e) => {
const value = e.target.value;
setTemplateContent(value)
};
const titheHandleContentChange = (e) => {
const value = e.target.value;
setTemplateTitle(value)
};
const handleChange = () => {
// 使用正则表达式匹配 ${var} 格式的变量
const regex = /\${\.([a-zA-Z0-9_]+)}/g;
const matches = [...(templateTitle + templateContent).matchAll(regex)];
// 提取变量名
const variables = Array.from(new Set(matches.map(match => match[1])));
const result = variables.map(item => {
return {
field_key: item,
field_value: ''
};
});
form.setFieldsValue({'fieldList': result})
};
const handleInputChange = (index, e) => {
form.getFieldValue("fieldList")[index].field_value = e.target.value
}
const drawerColumns = useMemo(() => { const drawerColumns = useMemo(() => {
return [ return [
{ {
@ -38,7 +108,25 @@ const MdwMessage = () => {
formItemProps: {hidden: true} formItemProps: {hidden: true}
}, },
{ {
title: t(`${i18nPrefix}.columns.type`, '选择模板'),
title: t(`${i18nPrefix}.columns.template`, '选择模板'),
valueType: 'select',
fieldProps: {
allowClear: true,
},
renderFormItem: () => {
return <Select onChange={templateChange}>
{
templateList?.map((template, index) => (
<Select.Option key={index} value={index}>
{template.name}
</Select.Option>
))
}
</Select>
}
},
{
title: t(`${i18nPrefix}.columns.type`, '消息类型'),
dataIndex: 'type', dataIndex: 'type',
valueType: 'select', valueType: 'select',
fieldProps: { fieldProps: {
@ -48,6 +136,7 @@ const MdwMessage = () => {
{label: 'Telegram', value: 'TG'} {label: 'Telegram', value: 'TG'}
], ],
allowClear: false, allowClear: false,
onChange: typeHandlerChange
}, },
formItemProps: { formItemProps: {
rules: [ rules: [
@ -64,46 +153,60 @@ const MdwMessage = () => {
fieldProps: { fieldProps: {
maxLength: 100, maxLength: 100,
showCount: true, showCount: true,
onChange: titheHandleContentChange,
},
formItemProps: {
tooltip: '支持邮件类型',
hidden: templateType != 'EMAIL',
rules: [
{
required: templateType == 'EMAIL',
message: t('message.required', '模板内容必填')
}
]
}, },
}, },
{ {
title: t(`${i18nPrefix}.columns.content`, '模板内容'),
title: t(`${i18nPrefix}.columns.content`, '正文'),
dataIndex: 'content', dataIndex: 'content',
valueType: 'textarea', valueType: 'textarea',
fieldProps: { fieldProps: {
defaultValue: "你好,我叫${.name}",
maxLength: 1000, maxLength: 1000,
showCount: true, showCount: true,
rows: 15, rows: 15,
onChange: handleContentChange,
}, },
formItemProps: { formItemProps: {
rules: [ rules: [
{ {
required: true, required: true,
message: t('message.required', '模板内容必填')
message: t('message.required', '内容必填')
} }
] ]
} }
}, },
{ {
title: t(`${i18nPrefix}.columns.field`, '识别到的变量'),
dataIndex: 'field',
renderFormItem: () => {
title: t(`${i18nPrefix}.columns.fieldList`, '填充变量'),
dataIndex: 'fieldList',
formItemProps: {
hidden: currentTemplate === undefined,
},
renderFormItem: (_, config) => {
return ( return (
<> <>
{ {
templateList?.map((variable, index) => (
config.value?.map((variable, index) => (
<div key={index} style={{marginBottom: 8}}> <div key={index} style={{marginBottom: 8}}>
<Input <Input
addonBefore={`${variable.name}:`}
defaultValue=""
placeholder={`请输入${variable}`}
addonBefore={variable.field_key}
onChange={(e) => handleInputChange(index, e)}
/> />
</div> </div>
))}
))
}
</> </>
); );
}
},
}, },
{ {
title: t(`${i18nPrefix}.columns.dest`, '收件人(多个收件人用英文逗号隔开,如果类型是TG,则填token)'), title: t(`${i18nPrefix}.columns.dest`, '收件人(多个收件人用英文逗号隔开,如果类型是TG,则填token)'),
@ -112,12 +215,20 @@ const MdwMessage = () => {
fieldProps: { fieldProps: {
maxLength: 1000, maxLength: 1000,
showCount: true, showCount: true,
rows: 15,
rows: 5,
placeholder: '[email protected],[email protected]', placeholder: '[email protected],[email protected]',
}, },
formItemProps: {
rules: [
{
required: true,
message: t('message.required', '内容必填')
}
]
}
}, },
] as ProColumns[] ] as ProColumns[]
}, [search])
}, [search, templateType, templateList])
const columns = useMemo(() => { const columns = useMemo(() => {
return [ return [
@ -129,17 +240,37 @@ const MdwMessage = () => {
formItemProps: {hidden: true} formItemProps: {hidden: true}
}, },
{ {
title: t(`${i18nPrefix}.columns.name`, '模板名称'),
dataIndex: 'name',
title: t(`${i18nPrefix}.columns.type`, '类型'),
dataIndex: 'type',
render: (_, record) => {
return <div>{record.type}</div>
}
}, },
{ {
title: t(`${i18nPrefix}.columns.title`, '模板标题'),
title: t(`${i18nPrefix}.columns.title`, '标题'),
dataIndex: 'title', dataIndex: 'title',
}, },
{ {
title: t(`${i18nPrefix}.columns.content`, '模板内容'),
title: t(`${i18nPrefix}.columns.content`, '正文'),
dataIndex: 'content', dataIndex: 'content',
}, },
{
title: t(`${i18nPrefix}.columns.dest`, '收件人'),
dataIndex: 'dest',
},
{
title: t(`${i18nPrefix}.columns.status`, '状态'),
dataIndex: 'status',
render: (_text, record) => {
return <Badge
status={['default', 'processing', 'success', 'error'][record.status] as any}
text={['未处理', '发送中', '发送成功', '发送失败'][record.status]}/>
}
},
{
title: t(`${i18nPrefix}.columns.send_at`, '发送时间'),
dataIndex: 'send_at',
},
// { // {
// title: t(`${i18nPrefix}.columns.option`, '操作'), // title: t(`${i18nPrefix}.columns.option`, '操作'),
// key: 'option', // key: 'option',
@ -160,7 +291,7 @@ const MdwMessage = () => {
// ] // ]
// } // }
] as ProColumns[] ] as ProColumns[]
}, [search])
}, [search, currentTemplate])
useEffect(() => { useEffect(() => {
@ -179,7 +310,7 @@ const MdwMessage = () => {
<ListPageLayout className={styles.container}> <ListPageLayout className={styles.container}>
<ProTable <ProTable
rowKey="id" rowKey="id"
headerTitle={t(`${i18nPrefix}.title`, '消息模板管理')}
headerTitle={t(`${i18nPrefix}.title`, '消息管理')}
toolbar={{ toolbar={{
search: { search: {
loading: isFetching && !!search?.title, loading: isFetching && !!search?.title,
@ -215,7 +346,7 @@ const MdwMessage = () => {
}) })
setOpen(true) setOpen(true)
}} }}
type={'primary'}>{t(`${i18nPrefix}.add`, '添加模板')}</Button>
type={'primary'}>{t(`${i18nPrefix}.add`, '发送消息')}</Button>
] ]
}} }}
scroll={{ scroll={{
@ -261,7 +392,7 @@ const MdwMessage = () => {
form={form} form={form}
layout={'vertical'} layout={'vertical'}
scrollToFirstError={true} scrollToFirstError={true}
title={t(`${i18nPrefix}.title_${form.getFieldValue('id') !== 0 ? 'edit' : 'add'}`, form.getFieldValue('id') !== 0 ? '模板编辑' : '模板添加')}
title={t(`${i18nPrefix}.title_add}`, '发送消息')}
layoutType={'DrawerForm'} layoutType={'DrawerForm'}
open={open} open={open}
drawerProps={{ drawerProps={{
@ -275,7 +406,7 @@ const MdwMessage = () => {
}} }}
onFinish={async (values) => { onFinish={async (values) => {
saveOrUpdate(values)
saveOrUpdate({...values, "template_id": currentTemplate?.id})
}} }}
columns={drawerColumns as ProFormColumnsType[]}/> columns={drawerColumns as ProFormColumnsType[]}/>
<BetaSchemaForm <BetaSchemaForm

10
src/pages/message/template/index.tsx

@ -51,26 +51,28 @@ const MdwMessage = () => {
} }
}, [open]); }, [open]);
const handleChange = (title: string, content: string) => {
const handleChange = () => {
// 使用正则表达式匹配 ${var} 格式的变量 // 使用正则表达式匹配 ${var} 格式的变量
const regex = /\${\.([a-zA-Z0-9_]+)}/g; const regex = /\${\.([a-zA-Z0-9_]+)}/g;
const matches = [...(title + content).matchAll(regex)];
const matches = [...(templateTitle + templateContent).matchAll(regex)];
// 提取变量名 // 提取变量名
const variables = Array.from(new Set(matches.map(match => match[1]))); const variables = Array.from(new Set(matches.map(match => match[1])));
setTemplateField(variables); setTemplateField(variables);
}; };
useEffect(() => {
handleChange()
}, [templateTitle, templateContent]);
const handleContentChange = (e) => { const handleContentChange = (e) => {
const value = e.target.value; const value = e.target.value;
setTemplateContent(value) setTemplateContent(value)
handleChange(templateTitle, value)
}; };
const titheHandleContentChange = (e) => { const titheHandleContentChange = (e) => {
const value = e.target.value; const value = e.target.value;
setTemplateTitle(value) setTemplateTitle(value)
handleChange(value, templateContent)
}; };
const typeHandlerChange = (value) => { const typeHandlerChange = (value) => {

15
src/store/message/my.ts

@ -1,10 +1,10 @@
import { atom } from 'jotai/index' import { atom } from 'jotai/index'
import { IApiResult, IPage } from '@/global' import { IApiResult, IPage } from '@/global'
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'
import messageTemplate from '@/service/message/template.ts'
import { message } from 'antd' import { message } from 'antd'
import { t } from 'i18next' import { t } from 'i18next'
import { IMsgMy } from '@/types/message/my.ts' import { IMsgMy } from '@/types/message/my.ts'
import messageMy from '@/service/message/my.ts'
type SearchParams = IPage & { type SearchParams = IPage & {
key?: string key?: string
@ -29,7 +29,7 @@ export const msgListAtom = atomWithQuery((get) => {
return { return {
queryKey: [ 'msgList', get(msgSearchAtom) ], queryKey: [ 'msgList', get(msgSearchAtom) ],
queryFn: async ({ queryKey: [ , params ] }) => { queryFn: async ({ queryKey: [ , params ] }) => {
const list = await messageTemplate.list(params as SearchParams)
const list = await messageMy.list(params as SearchParams)
return list.data return list.data
} }
} }
@ -39,12 +39,12 @@ export const deleteMsgAtom = atomWithMutation((get) => {
return { return {
mutationKey: [ 'deleteMsg' ], mutationKey: [ 'deleteMsg' ],
mutationFn: async (ids: number) => { mutationFn: async (ids: number) => {
return await messageTemplate.delete(ids ?? get(msgIdsAtom) as number)
return await messageMy.delete(ids ?? get(msgIdsAtom) as number)
}, },
onSuccess: (res) => { onSuccess: (res) => {
message.success('message.deleteSuccess') message.success('message.deleteSuccess')
//更新列表 //更新列表
get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(msgSearchAtom) ] })
get(queryClientAtom).invalidateQueries({ queryKey: [ 'msgList', get(msgSearchAtom) ] })
return res return res
} }
} }
@ -55,15 +55,12 @@ export const saveMsgAtom = atomWithMutation<IApiResult, IMsgMy>((get) => {
return { return {
mutationKey: [ 'saveMsg' ], mutationKey: [ 'saveMsg' ],
mutationFn: async (data) => { mutationFn: async (data) => {
if (data.id === 0) {
return await messageTemplate.add(data)
}
return await messageTemplate.update(data)
return await messageMy.add(data)
}, },
onSuccess: (res) => { onSuccess: (res) => {
message.success(t('message.saveSuccess', '保存成功')) message.success(t('message.saveSuccess', '保存成功'))
get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(msgSearchAtom) ] })
get(queryClientAtom).invalidateQueries({ queryKey: [ 'msgList', get(msgSearchAtom) ] })
return res return res
} }

Loading…
Cancel
Save