From 87cc601a2129889be1b386778f6f4ebb5ee9c39d Mon Sep 17 00:00:00 2001 From: cs Date: Tue, 10 Sep 2024 21:53:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global.d.ts | 8 + src/pages/message/my/index.tsx | 345 ++++++++++++++++++++++++++ src/pages/message/my/style.ts | 26 ++ src/pages/message/template/index.tsx | 446 ++++++++++++++++++++++++++++++++++ src/pages/message/template/style.ts | 26 ++ src/pages/system/message/index.tsx | 454 ----------------------------------- src/pages/system/message/style.ts | 26 -- src/service/message/message.ts | 17 -- src/service/message/my.ts | 13 + src/service/message/template.ts | 17 ++ src/store/message/my.ts | 71 ++++++ src/store/message/template.ts | 84 +++++++ src/store/system/message.ts | 85 ------- src/types/message/my.ts | 11 + src/types/message/template.ts | 9 + src/types/system/message.ts | 25 -- 16 files changed, 1056 insertions(+), 607 deletions(-) create mode 100644 src/pages/message/my/index.tsx create mode 100644 src/pages/message/my/style.ts create mode 100644 src/pages/message/template/index.tsx create mode 100644 src/pages/message/template/style.ts delete mode 100644 src/pages/system/message/index.tsx delete mode 100644 src/pages/system/message/style.ts delete mode 100644 src/service/message/message.ts create mode 100644 src/service/message/my.ts create mode 100644 src/service/message/template.ts create mode 100644 src/store/message/my.ts create mode 100644 src/store/message/template.ts delete mode 100644 src/store/system/message.ts create mode 100644 src/types/message/my.ts create mode 100644 src/types/message/template.ts delete mode 100644 src/types/system/message.ts diff --git a/src/global.d.ts b/src/global.d.ts index 44baa73..dedf0fe 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -33,6 +33,14 @@ export type IPageResult = IPage & { total: number } +export type IListAllResult = { + list: T[] +} + +export type IBoolResult = { + success: boolean +} + export type IApiResult = { code: number; data: T; diff --git a/src/pages/message/my/index.tsx b/src/pages/message/my/index.tsx new file mode 100644 index 0000000..503eeeb --- /dev/null +++ b/src/pages/message/my/index.tsx @@ -0,0 +1,345 @@ +import {useTranslation} from '@/i18n.ts' +import {Badge, Button, Divider, Form, Input, Space, Tooltip} from 'antd' +import {useAtom, useAtomValue} from 'jotai' +import React, {useEffect, useMemo, useState} from 'react' +import {BetaSchemaForm, ProColumns, ProFormColumnsType,} from '@ant-design/pro-components' +import ListPageLayout from '@/layout/ListPageLayout.tsx' +import {useStyle} from './style.ts' +import {FilterOutlined} from '@ant-design/icons' +import {getValueCount, unSetColumnRules} from '@/utils' +import {Table as ProTable} from '@/components/table' +import {msgListAtom, msgSearchAtom, saveMsgAtom} from "@/store/message/my.ts"; +import {templateAllListAtom} from "@/store/message/template.ts"; + +const i18nPrefix = 'msgMy.list' + +const MdwMessage = () => { + + const {styles, cx} = useStyle() + const {t} = useTranslation() + const [form] = Form.useForm() + const [filterForm] = Form.useForm() + const {mutate: saveOrUpdate, isPending: isSubmitting, isSuccess} = useAtomValue(saveMsgAtom) + const [search, setSearch] = useAtom(msgSearchAtom) + const {data, isFetching, isLoading, refetch} = useAtomValue(msgListAtom) + const {data: templateList} = useAtomValue(templateAllListAtom) + + const [open, setOpen] = useState(false) + const [openFilter, setFilterOpen] = useState(false) + const [searchKey, setSearchKey] = useState(search?.title) + + const drawerColumns = useMemo(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: {hidden: true} + }, + { + title: t(`${i18nPrefix}.columns.type`, '选择模板'), + dataIndex: 'type', + valueType: 'select', + fieldProps: { + options: [ + {label: '短信', value: 'SMS'}, + {label: '邮件', value: 'EMAIL'}, + {label: 'Telegram', value: 'TG'} + ], + allowClear: false, + }, + formItemProps: { + rules: [ + { + required: true, + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.title`, '标题'), + dataIndex: 'title', + valueType: 'text', + fieldProps: { + maxLength: 100, + showCount: true, + }, + }, + { + title: t(`${i18nPrefix}.columns.content`, '模板内容'), + dataIndex: 'content', + valueType: 'textarea', + fieldProps: { + defaultValue: "你好,我叫${.name}", + maxLength: 1000, + showCount: true, + rows: 15, + }, + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '模板内容必填') + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.field`, '识别到的变量'), + dataIndex: 'field', + renderFormItem: () => { + return ( + <> + { + templateList?.map((variable, index) => ( +
+ +
+ ))} + + ); + } + }, + { + title: t(`${i18nPrefix}.columns.dest`, '收件人(多个收件人用英文逗号隔开,如果类型是TG,则填token)'), + dataIndex: 'dest', + valueType: 'textarea', + fieldProps: { + maxLength: 1000, + showCount: true, + rows: 15, + placeholder: 'aaa@qq.com,bbb@gmail.com', + }, + }, + ] as ProColumns[] + }, [search]) + + const columns = useMemo(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: {hidden: true} + }, + { + title: t(`${i18nPrefix}.columns.name`, '模板名称'), + dataIndex: 'name', + }, + { + title: t(`${i18nPrefix}.columns.title`, '模板标题'), + dataIndex: 'title', + }, + { + title: t(`${i18nPrefix}.columns.content`, '模板内容'), + dataIndex: 'content', + }, + // { + // title: t(`${i18nPrefix}.columns.option`, '操作'), + // key: 'option', + // valueType: 'option', + // fixed: 'right', + // render: (_, record) => [ + // { + // deleteAppPackage(record.id) + // }} + // title={t('message.deleteConfirm')}> + // + // {t('actions.delete', '删除')} + // + // , + // ] + // } + ] as ProColumns[] + }, [search]) + + useEffect(() => { + + setSearchKey(search?.title) + filterForm.setFieldsValue(search) + + }, [search]) + + useEffect(() => { + if (isSuccess) { + setOpen(false) + } + }, [isSuccess]) + + return ( + + { + setSearch(prev => ({ + ...prev, + title: value + })) + }, + allowClear: true, + onChange: (e) => { + setSearchKey(e.target?.value) + }, + value: searchKey, + placeholder: t(`${i18nPrefix}.placeholder`, '输入模板名称') + }, + actions: [ + + + + ] + }} + scroll={{ + x: columns.length * 200, + y: 'calc(100vh - 290px)' + }} + search={false} + dateFormatter="string" + loading={isLoading || isFetching} + dataSource={data?.rows ?? []} + columns={columns} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: search.pageSize, + current: search.page, + onShowSizeChange: (current: number, size: number) => { + setSearch({ + ...search, + pageSize: size, + page: current + }) + }, + onChange: (current, pageSize) => { + setSearch(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + }} + /> + { + setOpen(open) + }} + loading={isSubmitting} + onValuesChange={() => { + + }} + onFinish={async (values) => { + saveOrUpdate(values) + }} + columns={drawerColumns as ProFormColumnsType[]}/> + { + setFilterOpen(open) + }} + layout={'vertical'} + scrollToFirstError={true} + layoutType={'DrawerForm'} + drawerProps={{ + maskClosable: false, + mask: false, + }} + submitter={{ + searchConfig: { + resetText: t(`${i18nPrefix}.filter.reset`, '清空'), + submitText: t(`${i18nPrefix}.filter.submit`, '查询'), + }, + onReset: () => { + filterForm.resetFields() + }, + render: (props,) => { + return ( +
+ + + + +
+ ) + }, + + }} + onValuesChange={() => { + + }} + + onFinish={async (values) => { + //处理,变成数组 + Object.keys(values).forEach(key => { + if (typeof values[key] === 'string' && values[key].includes(',')) { + values[key] = values[key].split(',') + } + }) + + setSearch(values) + + }} + columns={unSetColumnRules(columns.filter(item => !item.hideInSearch)) as ProFormColumnsType[]}/> +
+ ) +} + +export default MdwMessage \ No newline at end of file diff --git a/src/pages/message/my/style.ts b/src/pages/message/my/style.ts new file mode 100644 index 0000000..8774450 --- /dev/null +++ b/src/pages/message/my/style.ts @@ -0,0 +1,26 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-appPackage-list-page` + + const container = css` + .ant-table-cell{ + .ant-tag{ + padding-inline: 3px; + margin-inline-end: 3px; + } + } + .ant-table-empty { + .ant-table-body{ + height: calc(100vh - 350px) + } + } + .ant-pro-table-highlight{ + + } + ` + + return { + container: cx(prefix, props?.className, container), + } +}) \ No newline at end of file diff --git a/src/pages/message/template/index.tsx b/src/pages/message/template/index.tsx new file mode 100644 index 0000000..8a8077c --- /dev/null +++ b/src/pages/message/template/index.tsx @@ -0,0 +1,446 @@ +import {useTranslation} from '@/i18n.ts' +import {Badge, Button, Divider, Form, Popconfirm, Space, Tag, Tooltip} from 'antd' +import {useAtom, useAtomValue} from 'jotai' +import React, {useEffect, useMemo, useState} from 'react' +import Action from '@/components/action/Action.tsx' +import {BetaSchemaForm, ProColumns, ProFormColumnsType,} from '@ant-design/pro-components' +import ListPageLayout from '@/layout/ListPageLayout.tsx' +import {useStyle} from './style.ts' +import {FilterOutlined} from '@ant-design/icons' +import {getValueCount, unSetColumnRules} from '@/utils' +import {Table as ProTable} from '@/components/table' +import { + deleteTemplateAtom, + saveOrUpdateTemplateAtom, + templateAtom, + templateListAtom, + templateSearchAtom +} from "@/store/message/template.ts"; + +const i18nPrefix = 'mdwMessage.list' + +const MdwMessage = () => { + + const {styles, cx} = useStyle() + const {t} = useTranslation() + const [form] = Form.useForm() + const [filterForm] = Form.useForm() + const {mutate: saveOrUpdate, isPending: isSubmitting, isSuccess} = useAtomValue(saveOrUpdateTemplateAtom) + const [search, setSearch] = useAtom(templateSearchAtom) + const [currentTemplate, setAppPackage] = useAtom(templateAtom) + const {data, isFetching, isLoading, refetch} = useAtomValue(templateListAtom) + const {mutate: deleteAppPackage, isPending: isDeleting} = useAtomValue(deleteTemplateAtom) + + const [open, setOpen] = useState(false) + const [openFilter, setFilterOpen] = useState(false) + const [searchKey, setSearchKey] = useState(search?.title) + + const [templateField, setTemplateField] = useState([]); + + const [templateTitle, setTemplateTitle] = useState(''); + const [templateContent, setTemplateContent] = useState(''); + + const [templateType, setTemplateType] = useState('') + useEffect(() => { + setTemplateType(currentTemplate?.type) + + if (form.getFieldValue('id') === 0) { + setTemplateField(['name']); + } else { + setTemplateField(currentTemplate.fields.split(",")); + } + }, [open]); + + const handleChange = (title: string, content: string) => { + // 使用正则表达式匹配 ${var} 格式的变量 + const regex = /\${\.([a-zA-Z0-9_]+)}/g; + const matches = [...(title + content).matchAll(regex)]; + + // 提取变量名 + const variables = Array.from(new Set(matches.map(match => match[1]))); + setTemplateField(variables); + }; + + const handleContentChange = (e) => { + const value = e.target.value; + setTemplateContent(value) + handleChange(templateTitle, value) + }; + + const titheHandleContentChange = (e) => { + const value = e.target.value; + setTemplateTitle(value) + handleChange(value, templateContent) + }; + + const typeHandlerChange = (value) => { + console.log(value) + setTemplateType(value) + } + + const drawerColumns = useMemo(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: {hidden: true} + }, + { + title: t(`${i18nPrefix}.columns.name`, '模板名称'), + dataIndex: 'name', + valueType: 'text', + fieldProps: { + maxLength: 50, + showCount: true, + }, + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '模板名称必填') + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.type`, '模板类型'), + dataIndex: 'type', + valueType: 'select', + fieldProps: { + options: [ + {label: '短信', value: 'SMS'}, + {label: '邮件', value: 'EMAIL'}, + {label: 'Telegram', value: 'TG'} + ], + allowClear: false, + onChange: typeHandlerChange + }, + formItemProps: { + rules: [ + { + required: true, + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.title`, '模板标题'), + dataIndex: 'title', + valueType: 'text', + fieldProps: { + maxLength: 100, + showCount: true, + onChange: titheHandleContentChange, // 监听输入事件 + }, + formItemProps: { + tooltip: '支持邮件类型', + hidden: templateType != 'EMAIL', + rules: [ + { + required: templateType == 'EMAIL', + message: t('message.required', '模板内容必填') + } + ] + }, + }, + { + title: t(`${i18nPrefix}.columns.content`, '模板内容'), + dataIndex: 'content', + valueType: 'textarea', + fieldProps: { + defaultValue: "你好,我叫${.name}", + maxLength: 1000, + showCount: true, + rows: 15, + onChange: handleContentChange, // 监听输入事件 + }, + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '模板内容必填') + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.fields`, '识别到的变量'), + dataIndex: 'fields', + renderFormItem: () => { + return ( + <> + { + templateField.map((variable, index) => ( + + {variable} + + )) + } + + ); + } + }, + { + title: t(`${i18nPrefix}.columns.dest`, '收件人(多个收件人用英文逗号隔开,如果类型是TG,则填token)'), + dataIndex: 'dest', + valueType: 'textarea', + fieldProps: { + maxLength: 1000, + showCount: true, + rows: 15, + placeholder: 'aaa@qq.com,bbb@gmail.com', + }, + }, + ] as ProColumns[] + }, [isDeleting, currentTemplate, search, templateField, templateType]) + + const columns = useMemo(() => { + return [ + { + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: {hidden: true} + }, + { + title: t(`${i18nPrefix}.columns.name`, '模板名称'), + dataIndex: 'name', + }, + { + title: t(`${i18nPrefix}.columns.type`, '模板类型'), + dataIndex: 'type', + render: (_, record) => { + return
{record.type}
+ } + }, + { + title: t(`${i18nPrefix}.columns.title`, '模板标题'), + dataIndex: 'title', + }, + { + title: t(`${i18nPrefix}.columns.content`, '模板内容'), + dataIndex: 'content', + }, + { + title: t(`${i18nPrefix}.columns.option`, '操作'), + key: 'option', + valueType: 'option', + fixed: 'right', + render: (_, record) => [ + { + form.setFieldsValue(record) + setOpen(true) + }}>{t('actions.edit')}, + , + { + deleteAppPackage(record.id) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + , + ] + } + ] as ProColumns[] + }, [isDeleting, currentTemplate, search]) + + useEffect(() => { + + setSearchKey(search?.title) + filterForm.setFieldsValue(search) + + }, [search]) + + useEffect(() => { + if (isSuccess) { + setOpen(false) + } + }, [isSuccess]) + + return ( + + { + setSearch(prev => ({ + ...prev, + title: value + })) + }, + allowClear: true, + onChange: (e) => { + setSearchKey(e.target?.value) + }, + value: searchKey, + placeholder: t(`${i18nPrefix}.placeholder`, '输入模板名称') + }, + actions: [ + + + + ] + }} + scroll={{ + x: columns.length * 200, + y: 'calc(100vh - 290px)' + }} + search={false} + onRow={(record) => { + return { + className: cx({ + // 'ant-table-row-selected': currentAppPackage?.id === record.id + }), + onClick: () => { + setAppPackage(record) + } + } + }} + dateFormatter="string" + loading={isLoading || isFetching} + dataSource={data?.rows ?? []} + columns={columns} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: search.pageSize, + current: search.page, + onShowSizeChange: (current: number, size: number) => { + setSearch({ + ...search, + pageSize: size, + page: current + }) + }, + onChange: (current, pageSize) => { + setSearch(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + }} + /> + { + setOpen(open) + }} + loading={isSubmitting} + onValuesChange={() => { + + }} + onFinish={async (values) => { + saveOrUpdate({...values, 'fields': templateField.join()}) + }} + columns={drawerColumns as ProFormColumnsType[]}/> + { + setFilterOpen(open) + }} + layout={'vertical'} + scrollToFirstError={true} + layoutType={'DrawerForm'} + drawerProps={{ + maskClosable: false, + mask: false, + }} + submitter={{ + searchConfig: { + resetText: t(`${i18nPrefix}.filter.reset`, '清空'), + submitText: t(`${i18nPrefix}.filter.submit`, '查询'), + }, + onReset: () => { + filterForm.resetFields() + }, + render: (props,) => { + return ( +
+ + + + +
+ ) + }, + + }} + onValuesChange={() => { + + }} + + onFinish={async (values) => { + //处理,变成数组 + Object.keys(values).forEach(key => { + if (typeof values[key] === 'string' && values[key].includes(',')) { + values[key] = values[key].split(',') + } + }) + + setSearch(values) + + }} + columns={unSetColumnRules(columns.filter(item => !item.hideInSearch)) as ProFormColumnsType[]}/> +
+ ) +} + +export default MdwMessage \ No newline at end of file diff --git a/src/pages/message/template/style.ts b/src/pages/message/template/style.ts new file mode 100644 index 0000000..8774450 --- /dev/null +++ b/src/pages/message/template/style.ts @@ -0,0 +1,26 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-appPackage-list-page` + + const container = css` + .ant-table-cell{ + .ant-tag{ + padding-inline: 3px; + margin-inline-end: 3px; + } + } + .ant-table-empty { + .ant-table-body{ + height: calc(100vh - 350px) + } + } + .ant-pro-table-highlight{ + + } + ` + + return { + container: cx(prefix, props?.className, container), + } +}) \ No newline at end of file diff --git a/src/pages/system/message/index.tsx b/src/pages/system/message/index.tsx deleted file mode 100644 index 327c0e2..0000000 --- a/src/pages/system/message/index.tsx +++ /dev/null @@ -1,454 +0,0 @@ -import {useTranslation} from '@/i18n.ts' -import {Badge, Button, Divider, Form, Popconfirm, Space, Tag, Tooltip} from 'antd' -import {useAtom, useAtomValue} from 'jotai' -import React, {useEffect, useMemo, useState} from 'react' -import Action from '@/components/action/Action.tsx' -import {BetaSchemaForm, ProColumns, ProFormColumnsType,} from '@ant-design/pro-components' -import ListPageLayout from '@/layout/ListPageLayout.tsx' -import {useStyle} from './style' -import {FilterOutlined} from '@ant-design/icons' -import {getValueCount, unSetColumnRules} from '@/utils' -import {Table as ProTable} from '@/components/table' -import { - deleteTemplateAtom, - saveOrUpdateTemplateAtom, - templateAtom, - templateListAtom, - templateSearchAtom -} from "@/store/system/message.ts"; - -const i18nPrefix = 'mdwMessage.list' - -const MdwMessage = () => { - - const {styles, cx} = useStyle() - const {t} = useTranslation() - const [form] = Form.useForm() - const [filterForm] = Form.useForm() - const {mutate: saveOrUpdate, isPending: isSubmitting, isSuccess} = useAtomValue(saveOrUpdateTemplateAtom) - const [search, setSearch] = useAtom(templateSearchAtom) - const [currentTemplate, setAppPackage] = useAtom(templateAtom) - const {data, isFetching, isLoading, refetch} = useAtomValue(templateListAtom) - const {mutate: deleteAppPackage, isPending: isDeleting} = useAtomValue(deleteTemplateAtom) - - const [open, setOpen] = useState(false) - const [openFilter, setFilterOpen] = useState(false) - const [searchKey, setSearchKey] = useState(search?.title) - - const [templateField, setTemplateField] = useState(['name']); - const [titleTemplateField, setTitleTemplateField] = useState([]); - - const [allTemplateField, setAllTemplateField] = useState([]); - - useEffect(() => { - const combinedFields = [...templateField, ...titleTemplateField]; - const uniqueFields = Array.from(new Set(combinedFields)); - setAllTemplateField(uniqueFields); - }, [templateField, titleTemplateField]); - - const [templateType, setTemplateType] = useState('') - useEffect(() => { - setTemplateType(currentTemplate?.type) - }, [open]); - - const handleContentChange = (e) => { - const value = e.target.value; - - // 使用正则表达式匹配 ${var} 格式的变量 - const regex = /\${\.([a-zA-Z0-9_]+)}/g; - const matches = [...value.matchAll(regex)]; - - // 提取变量名 - const variables = Array.from(new Set(matches.map(match => match[1]))); - - // 更新变量状态 - setTemplateField(variables); - }; - - const titheHandleContentChange = (e) => { - const value = e.target.value; - - // 使用正则表达式匹配 ${var} 格式的变量 - const regex = /\${\.([a-zA-Z0-9_]+)}/g; - const matches = [...value.matchAll(regex)]; - - // 提取变量名 - const variables = Array.from(new Set(matches.map(match => match[1]))); - - // 更新变量状态 - setTitleTemplateField(variables); - - }; - - const typeHandlerChange = (value) => { - console.log(value) - setTemplateType(value) - } - - - const drawerColumns = useMemo(() => { - return [ - { - title: 'ID', - dataIndex: 'id', - hideInTable: true, - hideInSearch: true, - formItemProps: {hidden: true} - }, - { - title: t(`${i18nPrefix}.columns.name`, '模板名称'), - dataIndex: 'name', - valueType: 'text', - fieldProps: { - maxLength: 50, - showCount: true, - }, - formItemProps: { - rules: [ - { - required: true, - message: t('message.required', '模板名称必填') - } - ] - } - }, - { - title: t(`${i18nPrefix}.columns.type`, '模板类型'), - dataIndex: 'type', - valueType: 'select', - fieldProps: { - options: [ - {label: '短信', value: 'SMS'}, - {label: '邮件', value: 'EMAIL'}, - {label: 'Telegram', value: 'TG'} - ], - allowClear: false, - onChange: typeHandlerChange - }, - formItemProps: { - rules: [ - { - required: true, - } - ] - } - }, - { - title: t(`${i18nPrefix}.columns.title`, '模板标题'), - dataIndex: 'title', - valueType: 'text', - fieldProps: { - maxLength: 100, - showCount: true, - onChange: titheHandleContentChange, // 监听输入事件 - }, - formItemProps: { - tooltip: '支持邮件类型', - hidden: templateType != 'EMAIL', - rules: [ - { - required: templateType == 'EMAIL', - message: t('message.required', '模板内容必填') - } - ] - }, - }, - { - title: t(`${i18nPrefix}.columns.content`, '模板内容'), - dataIndex: 'content', - valueType: 'textarea', - fieldProps: { - defaultValue: "你好,我叫${.name}", - maxLength: 1000, - showCount: true, - rows: 15, - onChange: handleContentChange, // 监听输入事件 - }, - formItemProps: { - rules: [ - { - required: true, - message: t('message.required', '模板内容必填') - } - ] - } - }, - { - title: t(`${i18nPrefix}.columns.field`, '识别到的变量'), - dataIndex: 'field', - renderFormItem: () => { - return ( - <> - { - allTemplateField.map((variable, index) => ( - - {variable} - - )) - } - - ); - } - }, - { - title: t(`${i18nPrefix}.columns.dest`, '收件人(多个收件人用英文逗号隔开,如果类型是TG,则填token)'), - dataIndex: 'dest', - valueType: 'textarea', - fieldProps: { - maxLength: 1000, - showCount: true, - rows: 15, - placeholder: 'aaa@qq.com,bbb@gmail.com', - }, - }, - ] as ProColumns[] - }, [isDeleting, currentTemplate, search, allTemplateField, templateType]) - - const columns = useMemo(() => { - return [ - { - title: 'ID', - dataIndex: 'id', - hideInTable: true, - hideInSearch: true, - formItemProps: {hidden: true} - }, - { - title: t(`${i18nPrefix}.columns.name`, '模板名称'), - dataIndex: 'name', - }, - { - title: t(`${i18nPrefix}.columns.title`, '模板标题'), - dataIndex: 'title', - }, - { - title: t(`${i18nPrefix}.columns.content`, '模板内容'), - dataIndex: 'content', - }, - { - title: t(`${i18nPrefix}.columns.option`, '操作'), - key: 'option', - valueType: 'option', - fixed: 'right', - render: (_, record) => [ - { - form.setFieldsValue(record) - setOpen(true) - }}>{t('actions.edit')}, - , - { - deleteAppPackage(record.id) - }} - title={t('message.deleteConfirm')}> - - {t('actions.delete', '删除')} - - , - , - { - form.setFieldsValue(record) - setOpen(true) - }}>{t('actions.sendMsg', '发送消息')}, - ] - } - ] as ProColumns[] - }, [isDeleting, currentTemplate, search]) - - useEffect(() => { - - setSearchKey(search?.title) - filterForm.setFieldsValue(search) - - }, [search]) - - useEffect(() => { - if (isSuccess) { - setOpen(false) - } - }, [isSuccess]) - - return ( - - { - setSearch(prev => ({ - ...prev, - title: value - })) - }, - allowClear: true, - onChange: (e) => { - setSearchKey(e.target?.value) - }, - value: searchKey, - placeholder: t(`${i18nPrefix}.placeholder`, '输入模板名称') - }, - actions: [ - - - - ] - }} - scroll={{ - x: columns.length * 200, - y: 'calc(100vh - 290px)' - }} - search={false} - onRow={(record) => { - return { - className: cx({ - // 'ant-table-row-selected': currentAppPackage?.id === record.id - }), - onClick: () => { - setAppPackage(record) - } - } - }} - dateFormatter="string" - loading={isLoading || isFetching} - dataSource={data?.rows ?? []} - columns={columns} - options={{ - reload: () => { - refetch() - }, - }} - pagination={{ - total: data?.total, - pageSize: search.pageSize, - current: search.page, - onShowSizeChange: (current: number, size: number) => { - setSearch({ - ...search, - pageSize: size, - page: current - }) - }, - onChange: (current, pageSize) => { - setSearch(prev => { - return { - ...prev, - page: current, - pageSize: pageSize, - } - }) - }, - }} - /> - { - setOpen(open) - }} - loading={isSubmitting} - onValuesChange={() => { - - }} - onFinish={async (values) => { - saveOrUpdate({...values, 'fields': allTemplateField}) - }} - columns={drawerColumns as ProFormColumnsType[]}/> - { - setFilterOpen(open) - }} - layout={'vertical'} - scrollToFirstError={true} - layoutType={'DrawerForm'} - drawerProps={{ - maskClosable: false, - mask: false, - }} - submitter={{ - searchConfig: { - resetText: t(`${i18nPrefix}.filter.reset`, '清空'), - submitText: t(`${i18nPrefix}.filter.submit`, '查询'), - }, - onReset: () => { - filterForm.resetFields() - }, - render: (props,) => { - return ( -
- - - - -
- ) - }, - - }} - onValuesChange={() => { - - }} - - onFinish={async (values) => { - //处理,变成数组 - Object.keys(values).forEach(key => { - if (typeof values[key] === 'string' && values[key].includes(',')) { - values[key] = values[key].split(',') - } - }) - - setSearch(values) - - }} - columns={unSetColumnRules(columns.filter(item => !item.hideInSearch)) as ProFormColumnsType[]}/> -
- ) -} - -export default MdwMessage \ No newline at end of file diff --git a/src/pages/system/message/style.ts b/src/pages/system/message/style.ts deleted file mode 100644 index 8774450..0000000 --- a/src/pages/system/message/style.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createStyles } from '@/theme' - -export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { - const prefix = `${prefixCls}-${token?.proPrefix}-appPackage-list-page` - - const container = css` - .ant-table-cell{ - .ant-tag{ - padding-inline: 3px; - margin-inline-end: 3px; - } - } - .ant-table-empty { - .ant-table-body{ - height: calc(100vh - 350px) - } - } - .ant-pro-table-highlight{ - - } - ` - - return { - container: cx(prefix, props?.className, container), - } -}) \ No newline at end of file diff --git a/src/service/message/message.ts b/src/service/message/message.ts deleted file mode 100644 index 0e9a05c..0000000 --- a/src/service/message/message.ts +++ /dev/null @@ -1,17 +0,0 @@ -import request from '@/request.ts' -import { IApiResult, IPageResult } from '@/global' -import { IMsgFieldRes, IMsgTemplate } from '@/types/system/message.ts' -import { createCURD } from '@/service/base.ts' - -const mdwMessage = { - ...createCURD('/mdw-msg/template'), - list: async (params: any) => { - return await request.get>(`mdw-msg/template/list`, { ...params }) - }, - - fieldList: async (params: any) => { - return await request.get>(`mdw-msg/template/fieldList`, { ...params }) - }, -} - -export default mdwMessage \ No newline at end of file diff --git a/src/service/message/my.ts b/src/service/message/my.ts new file mode 100644 index 0000000..70962bf --- /dev/null +++ b/src/service/message/my.ts @@ -0,0 +1,13 @@ +import request from '@/request.ts' +import { IPageResult } from '@/global' +import { createCURD } from '@/service/base.ts' +import { IMsgMy } from '@/types/message/my.ts' + +const messageMy = { + ...createCURD('/mdw-msg/msg'), + list: async (params: any) => { + return await request.get>(`mdw-msg/msg/list`, { ...params }) + }, +} + +export default messageMy \ No newline at end of file diff --git a/src/service/message/template.ts b/src/service/message/template.ts new file mode 100644 index 0000000..3c5132b --- /dev/null +++ b/src/service/message/template.ts @@ -0,0 +1,17 @@ +import request from '@/request.ts' +import { IListAllResult, IPageResult } from '@/global' +import { IMsgTemplate } from '@/types/message/template.ts' +import { createCURD } from '@/service/base.ts' + +const messageTemplate = { + ...createCURD('/mdw-msg/template'), + list: async (params: any) => { + return await request.get>(`mdw-msg/template/list`, { ...params }) + }, + + listAll: async () => { + return await request.get>(`mdw-msg/template/listAll`) + }, +} + +export default messageTemplate \ No newline at end of file diff --git a/src/store/message/my.ts b/src/store/message/my.ts new file mode 100644 index 0000000..80e6073 --- /dev/null +++ b/src/store/message/my.ts @@ -0,0 +1,71 @@ +import { atom } from 'jotai/index' +import { IApiResult, IPage } from '@/global' +import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' +import messageTemplate from '@/service/message/template.ts' +import { message } from 'antd' +import { t } from 'i18next' +import { IMsgMy } from '@/types/message/my.ts' + +type SearchParams = IPage & { + key?: string + + [key: string]: any +} + +export const msgIdsAtom = atom(0) + +export const msgSearchAtom = atom({ + key: '', + pageSize: 10, + page: 1, +} as SearchParams) + +export const msgPageAtom = atom({ + pageSize: 10, + page: 1, +}) + +export const msgListAtom = atomWithQuery((get) => { + return { + queryKey: [ 'msgList', get(msgSearchAtom) ], + queryFn: async ({ queryKey: [ , params ] }) => { + const list = await messageTemplate.list(params as SearchParams) + return list.data + } + } +}) + +export const deleteMsgAtom = atomWithMutation((get) => { + return { + mutationKey: [ 'deleteMsg' ], + mutationFn: async (ids: number) => { + return await messageTemplate.delete(ids ?? get(msgIdsAtom) as number) + }, + onSuccess: (res) => { + message.success('message.deleteSuccess') + //更新列表 + get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(msgSearchAtom) ] }) + return res + } + } +}) + +export const saveMsgAtom = atomWithMutation((get) => { + + return { + mutationKey: [ 'saveMsg' ], + mutationFn: async (data) => { + if (data.id === 0) { + return await messageTemplate.add(data) + } + return await messageTemplate.update(data) + }, + onSuccess: (res) => { + message.success(t('message.saveSuccess', '保存成功')) + + get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(msgSearchAtom) ] }) + + return res + } + } +}) diff --git a/src/store/message/template.ts b/src/store/message/template.ts new file mode 100644 index 0000000..043b122 --- /dev/null +++ b/src/store/message/template.ts @@ -0,0 +1,84 @@ +import { atom } from 'jotai/index' +import { IApiResult, IPage } from '@/global' +import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' +import { IMsgTemplate } from '@/types/message/template.ts' +import messageTemplate from '@/service/message/template.ts' +import { message } from 'antd' +import { t } from 'i18next' + +type SearchParams = IPage & { + key?: string + + [key: string]: any +} + +export const templateIdsAtom = atom(0) + +export const templateAtom = atom(undefined as unknown as IMsgTemplate) + +export const templateSearchAtom = atom({ + key: '', + pageSize: 10, + page: 1, +} as SearchParams) + +export const templatePageAtom = atom({ + pageSize: 10, + page: 1, +}) + +export const templateListAtom = atomWithQuery((get) => { + return { + queryKey: [ 'templateList', get(templateSearchAtom) ], + queryFn: async ({ queryKey: [ , params ] }) => { + const list = await messageTemplate.list(params as SearchParams) + return list.data + } + } +}) + +export const templateAllListAtom = atomWithQuery(() => { + return { + queryKey: [ 'templateAllList'], + queryFn: async ({ queryKey: [ , ] }) => { + const list = await messageTemplate.listAll() + return list.data.list + } + } +}) + +export const deleteTemplateAtom = atomWithMutation((get) => { + return { + mutationKey: [ 'deleteTemplate' ], + mutationFn: async (ids: number) => { + return await messageTemplate.delete(ids ?? get(templateIdsAtom) as number) + }, + onSuccess: (res) => { + message.success('message.deleteSuccess') + //更新列表 + get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(templateSearchAtom) ] }) + return res + } + } +}) + +export const saveOrUpdateTemplateAtom = atomWithMutation((get) => { + + return { + mutationKey: [ 'updateTemplate' ], + mutationFn: async (data) => { + if (data.id === 0) { + return await messageTemplate.add(data) + } + return await messageTemplate.update(data) + }, + onSuccess: (res) => { + const isAdd = !!res.data?.id + message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) + + get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(templateSearchAtom) ] }) + + return res + } + } +}) diff --git a/src/store/system/message.ts b/src/store/system/message.ts deleted file mode 100644 index 7d95344..0000000 --- a/src/store/system/message.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { atom } from 'jotai/index' -import { IApiResult, IPage } from '@/global' -import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' -import { IMsgTemplate } from '@/types/system/message.ts' -import mdwMessage from '@/service/message/message.ts' -import { message } from 'antd' -import { t } from 'i18next' - -type SearchParams = IPage & { - key?: string - - [key: string]: any -} - -export const templateAIdsAtom = atom(0) - -export const templateAtom = atom(undefined as unknown as IMsgTemplate) - -export const templateSearchAtom = atom({ - key: '', - pageSize: 10, - page: 1, -} as SearchParams) - -export const templatePageAtom = atom({ - pageSize: 10, - page: 1, -}) - -export const templateListAtom = atomWithQuery((get) => { - return { - queryKey: [ 'templateList', get(templateSearchAtom) ], - queryFn: async ({ queryKey: [ , params ] }) => { - const list = await mdwMessage.list(params as SearchParams) - return list.data - } - } -}) - -export const templateFieldAtom = atomWithQuery(() => { - return { - queryKey: [ 'templateField' ], - queryFn: async ({ queryKey: [ , params ] }) => { - const list = await mdwMessage.fieldList(params) - return list.data - } - } -}) - -export const deleteTemplateAtom = atomWithMutation((get) => { - return { - mutationKey: [ 'deleteTemplate' ], - mutationFn: async (ids: number) => { - return await mdwMessage.delete(ids ?? get(templateAIdsAtom) as number) - }, - onSuccess: (res) => { - message.success('message.deleteSuccess') - //更新列表 - get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(templateSearchAtom) ] }) - return res - } - } -}) - -export const saveOrUpdateTemplateAtom = atomWithMutation((get) => { - - return { - mutationKey: [ 'updateTemplate' ], - mutationFn: async (data) => { - //data.status = data.status ? '1' : '0' - if (data.id === 0) { - return await mdwMessage.add(data) - } - return await mdwMessage.update(data) - }, - onSuccess: (res) => { - const isAdd = !!res.data?.id - message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) - - get(queryClientAtom).invalidateQueries({ queryKey: [ 'templateList', get(templateSearchAtom) ] }) - - return res - } - } -}) diff --git a/src/types/message/my.ts b/src/types/message/my.ts new file mode 100644 index 0000000..7e4c193 --- /dev/null +++ b/src/types/message/my.ts @@ -0,0 +1,11 @@ +export interface IMsgMy { + id: number + template_id: number + title: string + send_content: string + dest: string + type: string + status: number + error_message: string + send_at: number +} \ No newline at end of file diff --git a/src/types/message/template.ts b/src/types/message/template.ts new file mode 100644 index 0000000..25cf08c --- /dev/null +++ b/src/types/message/template.ts @@ -0,0 +1,9 @@ +export interface IMsgTemplate { + id: number + name: string + content: string + title: string + dest: string + type: string + fields: string +} diff --git a/src/types/system/message.ts b/src/types/system/message.ts deleted file mode 100644 index a94ac4f..0000000 --- a/src/types/system/message.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface IMsgTemplate { - id: number - name: string - content: string - title: string - dest: string - type: string -} - -export interface IMsgFieldRes { - list: IMsgField[] -} - - -export interface IMsgField { - template_id: string - field_key: string - field_value: string -} - -export interface IMdwMsgReq { - id: string - name: string - content: string -}