commit
						c03845ba99
					
				|  | @ -1,10 +1,10 @@ | ||||||
| import type { PageResult } from '@vben/request'; | import type { PageParam, PageResult } from '@vben/request'; | ||||||
| 
 | 
 | ||||||
| import { requestClient } from '#/api/request'; | import { requestClient } from '#/api/request'; | ||||||
| 
 | 
 | ||||||
| // TODO @puhui999:代码风格的统一
 |  | ||||||
| export namespace SystemMailAccountApi { | export namespace SystemMailAccountApi { | ||||||
|   export interface MailAccountVO { |   /** 邮箱信息 */ | ||||||
|  |   export interface MailAccount { | ||||||
|     id: number; |     id: number; | ||||||
|     mail: string; |     mail: string; | ||||||
|     username: string; |     username: string; | ||||||
|  | @ -19,16 +19,17 @@ export namespace SystemMailAccountApi { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 查询邮箱账号列表
 | /** 查询邮箱账号列表 */ | ||||||
| export const getMailAccountPage = async (params: any) => { | export const getMailAccountPage = async (params: PageParam) => { | ||||||
|   return await requestClient.get< |   return await requestClient.get<PageResult<SystemMailAccountApi.MailAccount>>( | ||||||
|     PageResult<SystemMailAccountApi.MailAccountVO> |     '/system/mail-account/page', | ||||||
|   >('/system/mail-account/page', { params }); |     { params }, | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 查询邮箱账号详情
 | /** 查询邮箱账号详情 */ | ||||||
| export const getMailAccount = async (id: number) => { | export const getMailAccount = async (id: number) => { | ||||||
|   return await requestClient.get<SystemMailAccountApi.MailAccountVO>( |   return await requestClient.get<SystemMailAccountApi.MailAccount>( | ||||||
|     '/system/mail-account/get', |     '/system/mail-account/get', | ||||||
|     { |     { | ||||||
|       params: { id }, |       params: { id }, | ||||||
|  | @ -36,41 +37,41 @@ export const getMailAccount = async (id: number) => { | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 新增邮箱账号
 | /** 新增邮箱账号 */ | ||||||
| export const createMailAccount = async ( | export const createMailAccount = async ( | ||||||
|   data: SystemMailAccountApi.MailAccountVO, |   data: SystemMailAccountApi.MailAccount, | ||||||
| ) => { | ) => { | ||||||
|   return await requestClient.post<SystemMailAccountApi.MailAccountVO>( |   return await requestClient.post<SystemMailAccountApi.MailAccount>( | ||||||
|     '/system/mail-account/create', |     '/system/mail-account/create', | ||||||
|     data, |     data, | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 修改邮箱账号
 | /** 修改邮箱账号 */ | ||||||
| export const updateMailAccount = async ( | export const updateMailAccount = async ( | ||||||
|   data: SystemMailAccountApi.MailAccountVO, |   data: SystemMailAccountApi.MailAccount, | ||||||
| ) => { | ) => { | ||||||
|   return await requestClient.put<SystemMailAccountApi.MailAccountVO>( |   return await requestClient.put<SystemMailAccountApi.MailAccount>( | ||||||
|     '/system/mail-account/update', |     '/system/mail-account/update', | ||||||
|     data, |     data, | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 删除邮箱账号
 | /** 删除邮箱账号 */ | ||||||
| export const deleteMailAccount = async (id: number) => { | export const deleteMailAccount = async (id: number) => { | ||||||
|   return await requestClient.delete<boolean>('/system/mail-account/delete', { |   return await requestClient.delete<boolean>('/system/mail-account/delete', { | ||||||
|     params: { id }, |     params: { id }, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 获得邮箱账号精简列表
 | /** 获得邮箱账号精简列表 */ | ||||||
| export const getSimpleMailAccountList = async () => { | export const getSimpleMailAccountList = async () => { | ||||||
|   return await requestClient.get<SystemMailAccountApi.MailAccountVO[]>( |   return await requestClient.get<SystemMailAccountApi.MailAccount[]>( | ||||||
|     '/system/mail-account/simple-list', |     '/system/mail-account/simple-list', | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 测试邮箱连接
 | /** 测试邮箱连接 */ | ||||||
| export const testMailAccount = async (id: number) => { | export const testMailAccount = async (id: number) => { | ||||||
|   return await requestClient.post<boolean>('/system/mail-account/test', null, { |   return await requestClient.post<boolean>('/system/mail-account/test', null, { | ||||||
|     params: { id }, |     params: { id }, | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import type { PageResult } from '@vben/request'; | import type { PageParam, PageResult } from '@vben/request'; | ||||||
| 
 | 
 | ||||||
| import { requestClient } from '#/api/request'; | import { requestClient } from '#/api/request'; | ||||||
| 
 | 
 | ||||||
| // TODO @puhui999:代码风格的统一
 |  | ||||||
| export namespace SystemMailLogApi { | export namespace SystemMailLogApi { | ||||||
|   export interface MailLogVO { |   /** 邮件日志 */ | ||||||
|  |   export interface MailLog { | ||||||
|     id: number; |     id: number; | ||||||
|     userId: number; |     userId: number; | ||||||
|     userType: number; |     userType: number; | ||||||
|  | @ -18,24 +18,24 @@ export namespace SystemMailLogApi { | ||||||
|     templateContent: string; |     templateContent: string; | ||||||
|     templateParams: string; |     templateParams: string; | ||||||
|     sendStatus: number; |     sendStatus: number; | ||||||
|     sendTime: Date; |     sendTime: string; | ||||||
|     sendMessageId: string; |     sendMessageId: string; | ||||||
|     sendException: string; |     sendException: string; | ||||||
|     createTime: Date; |     createTime: string; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 查询邮件日志列表
 | /** 查询邮件日志列表 */ | ||||||
| export const getMailLogPage = async (params: any) => { | export const getMailLogPage = async (params: PageParam) => { | ||||||
|   return await requestClient.get<PageResult<SystemMailLogApi.MailLogVO>>( |   return await requestClient.get<PageResult<SystemMailLogApi.MailLog>>( | ||||||
|     '/system/mail-log/page', |     '/system/mail-log/page', | ||||||
|     { params }, |     { params }, | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 查询邮件日志详情
 | /** 查询邮件日志详情 */ | ||||||
| export const getMailLog = async (id: number) => { | export const getMailLog = async (id: number) => { | ||||||
|   return await requestClient.get<SystemMailLogApi.MailLogVO>( |   return await requestClient.get<SystemMailLogApi.MailLog>( | ||||||
|     '/system/mail-log/get', |     '/system/mail-log/get', | ||||||
|     { |     { | ||||||
|       params: { id }, |       params: { id }, | ||||||
|  | @ -43,14 +43,14 @@ export const getMailLog = async (id: number) => { | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 重新发送邮件
 | /** 重新发送邮件 */ | ||||||
| export const resendMail = async (id: number) => { | export const resendMail = async (id: number) => { | ||||||
|   return await requestClient.put<boolean>('/system/mail-log/resend', null, { |   return await requestClient.put<boolean>('/system/mail-log/resend', null, { | ||||||
|     params: { id }, |     params: { id }, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 批量删除邮件日志
 | /** 批量删除邮件日志 */ | ||||||
| export const deleteMailLogs = async (ids: number[]) => { | export const deleteMailLogs = async (ids: number[]) => { | ||||||
|   return await requestClient.delete<boolean>('/system/mail-log/delete', { |   return await requestClient.delete<boolean>('/system/mail-log/delete', { | ||||||
|     data: { ids }, |     data: { ids }, | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import type { PageResult } from '@vben/request'; | import type { PageParam, PageResult } from '@vben/request'; | ||||||
| 
 | 
 | ||||||
| import { requestClient } from '#/api/request'; | import { requestClient } from '#/api/request'; | ||||||
| 
 | 
 | ||||||
| // TODO @puhui999:代码风格的统一
 |  | ||||||
| export namespace SystemMailTemplateApi { | export namespace SystemMailTemplateApi { | ||||||
|   export interface MailTemplateVO { |   /** 邮件模版信息 */ | ||||||
|  |   export interface MailTemplate { | ||||||
|     id: number; |     id: number; | ||||||
|     name: string; |     name: string; | ||||||
|     code: string; |     code: string; | ||||||
|  | @ -12,29 +12,30 @@ export namespace SystemMailTemplateApi { | ||||||
|     nickname: string; |     nickname: string; | ||||||
|     title: string; |     title: string; | ||||||
|     content: string; |     content: string; | ||||||
|     params: string; |     params: string[]; | ||||||
|     status: number; |     status: number; | ||||||
|     remark: string; |     remark: string; | ||||||
|     createTime: Date; |     createTime: Date; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   export interface MailSendReqVO { |   /** 邮件发送信息 */ | ||||||
|  |   export interface MailSendReq { | ||||||
|     mail: string; |     mail: string; | ||||||
|     templateCode: string; |     templateCode: string; | ||||||
|     templateParams: Record<string, any>; |     templateParams: Record<string, any>; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 查询邮件模版列表
 | /** 查询邮件模版列表 */ | ||||||
| export const getMailTemplatePage = async (params: any) => { | export const getMailTemplatePage = async (params: PageParam) => { | ||||||
|   return await requestClient.get< |   return await requestClient.get< | ||||||
|     PageResult<SystemMailTemplateApi.MailTemplateVO> |     PageResult<SystemMailTemplateApi.MailTemplate> | ||||||
|   >('/system/mail-template/page', { params }); |   >('/system/mail-template/page', { params }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 查询邮件模版详情
 | /** 查询邮件模版详情 */ | ||||||
| export const getMailTemplate = async (id: number) => { | export const getMailTemplate = async (id: number) => { | ||||||
|   return await requestClient.get<SystemMailTemplateApi.MailTemplateVO>( |   return await requestClient.get<SystemMailTemplateApi.MailTemplate>( | ||||||
|     '/system/mail-template/get', |     '/system/mail-template/get', | ||||||
|     { |     { | ||||||
|       params: { id }, |       params: { id }, | ||||||
|  | @ -42,35 +43,35 @@ export const getMailTemplate = async (id: number) => { | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 新增邮件模版
 | /** 新增邮件模版 */ | ||||||
| export const createMailTemplate = async ( | export const createMailTemplate = async ( | ||||||
|   data: SystemMailTemplateApi.MailTemplateVO, |   data: SystemMailTemplateApi.MailTemplate, | ||||||
| ) => { | ) => { | ||||||
|   return await requestClient.post<SystemMailTemplateApi.MailTemplateVO>( |   return await requestClient.post<SystemMailTemplateApi.MailTemplate>( | ||||||
|     '/system/mail-template/create', |     '/system/mail-template/create', | ||||||
|     data, |     data, | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 修改邮件模版
 | /** 修改邮件模版 */ | ||||||
| export const updateMailTemplate = async ( | export const updateMailTemplate = async ( | ||||||
|   data: SystemMailTemplateApi.MailTemplateVO, |   data: SystemMailTemplateApi.MailTemplate, | ||||||
| ) => { | ) => { | ||||||
|   return await requestClient.put<SystemMailTemplateApi.MailTemplateVO>( |   return await requestClient.put<SystemMailTemplateApi.MailTemplate>( | ||||||
|     '/system/mail-template/update', |     '/system/mail-template/update', | ||||||
|     data, |     data, | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 删除邮件模版
 | /** 删除邮件模版 */ | ||||||
| export const deleteMailTemplate = async (id: number) => { | export const deleteMailTemplate = async (id: number) => { | ||||||
|   return await requestClient.delete<boolean>('/system/mail-template/delete', { |   return await requestClient.delete<boolean>('/system/mail-template/delete', { | ||||||
|     params: { id }, |     params: { id }, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 发送邮件
 | /** 发送邮件 */ | ||||||
| export const sendMail = async (data: SystemMailTemplateApi.MailSendReqVO) => { | export const sendMail = async (data: SystemMailTemplateApi.MailSendReq) => { | ||||||
|   return await requestClient.post<boolean>( |   return await requestClient.post<boolean>( | ||||||
|     '/system/mail-template/send-mail', |     '/system/mail-template/send-mail', | ||||||
|     data, |     data, | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | import type { PageParam, PageResult } from '@vben/request'; | ||||||
|  | 
 | ||||||
|  | import { requestClient } from '#/api/request'; | ||||||
|  | 
 | ||||||
|  | export namespace SystemNotifyMessageApi { | ||||||
|  |   /** 站内信消息信息 */ | ||||||
|  |   export interface NotifyMessage { | ||||||
|  |     id: number; | ||||||
|  |     userId: number; | ||||||
|  |     userType: number; | ||||||
|  |     templateId: number; | ||||||
|  |     templateCode: string; | ||||||
|  |     templateNickname: string; | ||||||
|  |     templateContent: string; | ||||||
|  |     templateType: number; | ||||||
|  |     templateParams: string; | ||||||
|  |     readStatus: boolean; | ||||||
|  |     readTime: string; | ||||||
|  |     createTime: string; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查询站内信消息列表 */ | ||||||
|  | export function getNotifyMessagePage(params: PageParam) { | ||||||
|  |   return requestClient.get<PageResult<SystemNotifyMessageApi.NotifyMessage>>( | ||||||
|  |     '/system/notify-message/page', | ||||||
|  |     { params }, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 获得我的站内信分页 */ | ||||||
|  | export function getMyNotifyMessagePage(params: PageParam) { | ||||||
|  |   return requestClient.get<PageResult<SystemNotifyMessageApi.NotifyMessage>>( | ||||||
|  |     '/system/notify-message/my-page', | ||||||
|  |     { params }, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 批量标记已读 */ | ||||||
|  | export function updateNotifyMessageRead(ids: number[]) { | ||||||
|  |   return requestClient.put('/system/notify-message/update-read', { ids }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 标记所有站内信为已读 */ | ||||||
|  | export function updateAllNotifyMessageRead() { | ||||||
|  |   return requestClient.put('/system/notify-message/update-all-read'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 获取当前用户的最新站内信列表 */ | ||||||
|  | export function getUnreadNotifyMessageList() { | ||||||
|  |   return requestClient.get<SystemNotifyMessageApi.NotifyMessage[]>( | ||||||
|  |     '/system/notify-message/get-unread-list', | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 获得当前用户的未读站内信数量 */ | ||||||
|  | export function getUnreadNotifyMessageCount() { | ||||||
|  |   return requestClient.get<number>('/system/notify-message/get-unread-count'); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,71 @@ | ||||||
|  | import type { PageParam, PageResult } from '@vben/request'; | ||||||
|  | 
 | ||||||
|  | import { requestClient } from '#/api/request'; | ||||||
|  | 
 | ||||||
|  | export namespace SystemNotifyTemplateApi { | ||||||
|  |   /** 站内信模板信息 */ | ||||||
|  |   export interface NotifyTemplate { | ||||||
|  |     id?: number; | ||||||
|  |     name: string; | ||||||
|  |     nickname: string; | ||||||
|  |     code: string; | ||||||
|  |     content: string; | ||||||
|  |     type?: number; | ||||||
|  |     params: string[]; | ||||||
|  |     status: number; | ||||||
|  |     remark: string; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** 发送站内信请求 */ | ||||||
|  |   export interface NotifySendReq { | ||||||
|  |     userId: number; | ||||||
|  |     templateCode: string; | ||||||
|  |     templateParams: Record<string, any>; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查询站内信模板列表 */ | ||||||
|  | export function getNotifyTemplatePage(params: PageParam) { | ||||||
|  |   return requestClient.get<PageResult<SystemNotifyTemplateApi.NotifyTemplate>>( | ||||||
|  |     '/system/notify-template/page', | ||||||
|  |     { params }, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查询站内信模板详情 */ | ||||||
|  | export function getNotifyTemplate(id: number) { | ||||||
|  |   return requestClient.get<SystemNotifyTemplateApi.NotifyTemplate>( | ||||||
|  |     `/system/notify-template/get?id=${id}`, | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 新增站内信模板 */ | ||||||
|  | export function createNotifyTemplate( | ||||||
|  |   data: SystemNotifyTemplateApi.NotifyTemplate, | ||||||
|  | ) { | ||||||
|  |   return requestClient.post('/system/notify-template/create', data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 修改站内信模板 */ | ||||||
|  | export function updateNotifyTemplate( | ||||||
|  |   data: SystemNotifyTemplateApi.NotifyTemplate, | ||||||
|  | ) { | ||||||
|  |   return requestClient.put('/system/notify-template/update', data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 删除站内信模板 */ | ||||||
|  | export function deleteNotifyTemplate(id: number) { | ||||||
|  |   return requestClient.delete(`/system/notify-template/delete?id=${id}`); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 导出站内信模板 */ | ||||||
|  | export function exportNotifyTemplate(params: any) { | ||||||
|  |   return requestClient.download('/system/notify-template/export-excel', { | ||||||
|  |     params, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 发送站内信 */ | ||||||
|  | export function sendNotify(data: SystemNotifyTemplateApi.NotifySendReq) { | ||||||
|  |   return requestClient.post('/system/notify-template/send-notify', data); | ||||||
|  | } | ||||||
|  | @ -27,12 +27,16 @@ export function getSmsChannelPage(params: PageParam) { | ||||||
| 
 | 
 | ||||||
| /** 获得短信渠道精简列表 */ | /** 获得短信渠道精简列表 */ | ||||||
| export function getSimpleSmsChannelList() { | export function getSimpleSmsChannelList() { | ||||||
|   return requestClient.get<SystemSmsChannelApi.SmsChannel[]>('/system/sms-channel/simple-list'); |   return requestClient.get<SystemSmsChannelApi.SmsChannel[]>( | ||||||
|  |     '/system/sms-channel/simple-list', | ||||||
|  |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 查询短信渠道详情 */ | /** 查询短信渠道详情 */ | ||||||
| export function getSmsChannel(id: number) { | export function getSmsChannel(id: number) { | ||||||
|   return requestClient.get<SystemSmsChannelApi.SmsChannel>(`/system/sms-channel/get?id=${id}`); |   return requestClient.get<SystemSmsChannelApi.SmsChannel>( | ||||||
|  |     `/system/sms-channel/get?id=${id}`, | ||||||
|  |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 新增短信渠道 */ | /** 新增短信渠道 */ | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import { requestClient } from '#/api/request'; | ||||||
| 
 | 
 | ||||||
| export namespace SystemSmsLogApi { | export namespace SystemSmsLogApi { | ||||||
|   /** 短信日志信息 */ |   /** 短信日志信息 */ | ||||||
|   export interface SmsLogVO { |   export interface SmsLog { | ||||||
|     id?: number; |     id?: number; | ||||||
|     channelId?: number; |     channelId?: number; | ||||||
|     channelCode: string; |     channelCode: string; | ||||||
|  | @ -18,22 +18,25 @@ export namespace SystemSmsLogApi { | ||||||
|     userId?: number; |     userId?: number; | ||||||
|     userType?: number; |     userType?: number; | ||||||
|     sendStatus?: number; |     sendStatus?: number; | ||||||
|     sendTime?: Date; |     sendTime?: string; | ||||||
|     apiSendCode: string; |     apiSendCode: string; | ||||||
|     apiSendMsg: string; |     apiSendMsg: string; | ||||||
|     apiRequestId: string; |     apiRequestId: string; | ||||||
|     apiSerialNo: string; |     apiSerialNo: string; | ||||||
|     receiveStatus?: number; |     receiveStatus?: number; | ||||||
|     receiveTime?: Date; |     receiveTime?: string; | ||||||
|     apiReceiveCode: string; |     apiReceiveCode: string; | ||||||
|     apiReceiveMsg: string; |     apiReceiveMsg: string; | ||||||
|     createTime?: Date; |     createTime: string; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 查询短信日志列表 */ | /** 查询短信日志列表 */ | ||||||
| export function getSmsLogPage(params: PageParam) { | export function getSmsLogPage(params: PageParam) { | ||||||
|   return requestClient.get<PageResult<SystemSmsLogApi.SmsLogVO>>('/system/sms-log/page', { params }); |   return requestClient.get<PageResult<SystemSmsLogApi.SmsLog>>( | ||||||
|  |     '/system/sms-log/page', | ||||||
|  |     { params }, | ||||||
|  |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 导出短信日志 */ | /** 导出短信日志 */ | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import type { PageResult } from '@vben/request'; | import type { PageParam, PageResult } from '@vben/request'; | ||||||
| 
 | 
 | ||||||
| import { requestClient } from '#/api/request'; | import { requestClient } from '#/api/request'; | ||||||
| 
 | 
 | ||||||
|  | @ -20,7 +20,7 @@ export namespace SystemSmsTemplateApi { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** 发送短信请求 */ |   /** 发送短信请求 */ | ||||||
|   export interface SmsSendReqVO { |   export interface SmsSendReq { | ||||||
|     mobile: string; |     mobile: string; | ||||||
|     templateCode: string; |     templateCode: string; | ||||||
|     templateParams: Record<string, any>; |     templateParams: Record<string, any>; | ||||||
|  | @ -28,7 +28,7 @@ export namespace SystemSmsTemplateApi { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 查询短信模板列表 */ | /** 查询短信模板列表 */ | ||||||
| export function getSmsTemplatePage(params: any) { | export function getSmsTemplatePage(params: PageParam) { | ||||||
|   return requestClient.get<PageResult<SystemSmsTemplateApi.SmsTemplate>>( |   return requestClient.get<PageResult<SystemSmsTemplateApi.SmsTemplate>>( | ||||||
|     '/system/sms-template/page', |     '/system/sms-template/page', | ||||||
|     { params }, |     { params }, | ||||||
|  | @ -37,7 +37,9 @@ export function getSmsTemplatePage(params: any) { | ||||||
| 
 | 
 | ||||||
| /** 查询短信模板详情 */ | /** 查询短信模板详情 */ | ||||||
| export function getSmsTemplate(id: number) { | export function getSmsTemplate(id: number) { | ||||||
|   return requestClient.get<SystemSmsTemplateApi.SmsTemplate>(`/system/sms-template/get?id=${id}`); |   return requestClient.get<SystemSmsTemplateApi.SmsTemplate>( | ||||||
|  |     `/system/sms-template/get?id=${id}`, | ||||||
|  |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 新增短信模板 */ | /** 新增短信模板 */ | ||||||
|  | @ -57,10 +59,12 @@ export function deleteSmsTemplate(id: number) { | ||||||
| 
 | 
 | ||||||
| /** 导出短信模板 */ | /** 导出短信模板 */ | ||||||
| export function exportSmsTemplate(params: any) { | export function exportSmsTemplate(params: any) { | ||||||
|   return requestClient.download('/system/sms-template/export-excel', { params }); |   return requestClient.download('/system/sms-template/export-excel', { | ||||||
|  |     params, | ||||||
|  |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 发送短信 */ | /** 发送短信 */ | ||||||
| export function sendSms(data: SystemSmsTemplateApi.SmsSendReqVO) { | export function sendSms(data: SystemSmsTemplateApi.SmsSendReq) { | ||||||
|   return requestClient.post('/system/sms-template/send-sms', data); |   return requestClient.post('/system/sms-template/send-sms', data); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,191 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailAccountApi } from '#/api/system/mail/account'; | ||||||
|  | 
 | ||||||
|  | import { z } from '#/adapter/form'; | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 新增/修改的表单 */ | ||||||
|  | export function useFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'id', | ||||||
|  |       label: 'id', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'mail', | ||||||
|  |       label: '邮箱', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'username', | ||||||
|  |       label: '用户名', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'password', | ||||||
|  |       label: '密码', | ||||||
|  |       component: 'InputPassword', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'host', | ||||||
|  |       label: 'SMTP 服务器域名', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'port', | ||||||
|  |       label: 'SMTP 服务器端口', | ||||||
|  |       component: 'InputNumber', | ||||||
|  |       componentProps: { | ||||||
|  |         min: 0, | ||||||
|  |         max: 65_535, | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'sslEnable', | ||||||
|  |       label: '是否开启 SSL', | ||||||
|  |       component: 'RadioGroup', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), | ||||||
|  |         buttonStyle: 'solid', | ||||||
|  |         optionType: 'button', | ||||||
|  |       }, | ||||||
|  |       rules: z.boolean().default(true), | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'starttlsEnable', | ||||||
|  |       label: '是否开启 STARTTLS', | ||||||
|  |       component: 'RadioGroup', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), | ||||||
|  |         buttonStyle: 'solid', | ||||||
|  |         optionType: 'button', | ||||||
|  |       }, | ||||||
|  |       rules: z.boolean().default(false), | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'remark', | ||||||
|  |       label: '备注', | ||||||
|  |       component: 'Textarea', | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'mail', | ||||||
|  |       label: '邮箱', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'username', | ||||||
|  |       label: '用户名', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '创建时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemMailAccountApi.MailAccount>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'id', | ||||||
|  |       title: '编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'mail', | ||||||
|  |       title: '邮箱', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'username', | ||||||
|  |       title: '用户名', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'host', | ||||||
|  |       title: 'SMTP 服务器域名', | ||||||
|  |       minWidth: 150, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'port', | ||||||
|  |       title: 'SMTP 服务器端口', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'sslEnable', | ||||||
|  |       title: '是否开启 SSL', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'starttlsEnable', | ||||||
|  |       title: '是否开启 STARTTLS', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'remark', | ||||||
|  |       title: '备注', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '创建时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 220, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'mail', | ||||||
|  |           nameTitle: '邮箱账号', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           'edit', // 默认的编辑按钮
 | ||||||
|  |           'delete', // 默认的删除按钮
 | ||||||
|  |           { | ||||||
|  |             code: 'test', | ||||||
|  |             text: '测试连接', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,143 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailAccountApi } from '#/api/system/mail/account'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Plus } from '@vben/icons'; | ||||||
|  | 
 | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { | ||||||
|  |   deleteMailAccount, | ||||||
|  |   getMailAccountPage, | ||||||
|  |   testMailAccount, | ||||||
|  | } from '#/api/system/mail/account'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import Form from './modules/form.vue'; | ||||||
|  | 
 | ||||||
|  | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Form, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 创建邮箱账号 */ | ||||||
|  | function onCreate() { | ||||||
|  |   formModalApi.setData(null).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 编辑邮箱账号 */ | ||||||
|  | function onEdit(row: SystemMailAccountApi.MailAccount) { | ||||||
|  |   formModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 测试邮箱连接 */ | ||||||
|  | async function onTest(row: SystemMailAccountApi.MailAccount) { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: '正在测试邮箱连接...', | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await testMailAccount(row.id as number); | ||||||
|  |     message.success({ | ||||||
|  |       content: '邮箱连接测试成功', | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 删除邮箱账号 */ | ||||||
|  | async function onDelete(row: SystemMailAccountApi.MailAccount) { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: $t('ui.actionMessage.deleting', [row.mail]), | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await deleteMailAccount(row.id as number); | ||||||
|  |     message.success({ | ||||||
|  |       content: $t('ui.actionMessage.deleteSuccess', [row.mail]), | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemMailAccountApi.MailAccount>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'delete': { | ||||||
|  |       onDelete(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'edit': { | ||||||
|  |       onEdit(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'test': { | ||||||
|  |       onTest(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getMailAccountPage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemMailAccountApi.MailAccount>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <FormModal @success="onRefresh" /> | ||||||
|  |     <Grid table-title="邮箱账号列表"> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button type="primary" @click="onCreate"> | ||||||
|  |           <Plus class="size-5" /> | ||||||
|  |           {{ $t('ui.actionTitle.create', ['邮箱账号']) }} | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,84 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemMailAccountApi } from '#/api/system/mail/account'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { | ||||||
|  |   createMailAccount, | ||||||
|  |   getMailAccount, | ||||||
|  |   updateMailAccount, | ||||||
|  | } from '#/api/system/mail/account'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | 
 | ||||||
|  | import { useFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const formData = ref<SystemMailAccountApi.MailAccount>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return formData.value?.id | ||||||
|  |     ? $t('ui.actionTitle.edit', ['邮箱账号']) | ||||||
|  |     : $t('ui.actionTitle.create', ['邮箱账号']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: useFormSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 提交表单 | ||||||
|  |     const data = | ||||||
|  |       (await formApi.getValues()) as SystemMailAccountApi.MailAccount; | ||||||
|  |     try { | ||||||
|  |       await (formData.value?.id | ||||||
|  |         ? updateMailAccount(data) | ||||||
|  |         : createMailAccount(data)); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       message.success({ | ||||||
|  |         content: $t('ui.actionMessage.operationSuccess'), | ||||||
|  |         key: 'action_process_msg', | ||||||
|  |       }); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemMailAccountApi.MailAccount>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       formData.value = await getMailAccount(data.id); | ||||||
|  |       // 设置到 values | ||||||
|  |       await formApi.setValues(formData.value); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <Form class="mx-4" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,167 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailLogApi } from '#/api/system/mail/log'; | ||||||
|  | 
 | ||||||
|  | import { getSimpleMailAccountList } from '#/api/system/mail/account'; | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'toMail', | ||||||
|  |       label: '收件邮箱', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'accountId', | ||||||
|  |       label: '邮箱账号', | ||||||
|  |       component: 'ApiSelect', | ||||||
|  |       componentProps: { | ||||||
|  |         api: async () => await getSimpleMailAccountList(), | ||||||
|  |         labelField: 'mail', | ||||||
|  |         valueField: 'id', | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateId', | ||||||
|  |       label: '模板编号', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'sendStatus', | ||||||
|  |       label: '发送状态', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, 'number'), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'sendTime', | ||||||
|  |       label: '发送时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'userId', | ||||||
|  |       label: '用户编号', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'userType', | ||||||
|  |       label: '用户类型', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemMailLogApi.MailLog>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'id', | ||||||
|  |       title: '编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '创建时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'toMail', | ||||||
|  |       title: '收件邮箱', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'fromMail', | ||||||
|  |       title: '发送邮箱', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateTitle', | ||||||
|  |       title: '邮件标题', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateContent', | ||||||
|  |       title: '邮件内容', | ||||||
|  |       minWidth: 300, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'sendStatus', | ||||||
|  |       title: '发送状态', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'sendTime', | ||||||
|  |       title: '发送时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateId', | ||||||
|  |       title: '模板编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateCode', | ||||||
|  |       title: '模板编码', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'userId', | ||||||
|  |       title: '用户编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'userType', | ||||||
|  |       title: '用户类型', | ||||||
|  |       minWidth: 100, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.USER_TYPE }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 120, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'toMail', | ||||||
|  |           nameTitle: '邮件日志', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           { | ||||||
|  |             code: 'view', | ||||||
|  |             text: '查看', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             code: 'resend', | ||||||
|  |             text: '重发', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,105 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailLogApi } from '#/api/system/mail/log'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { getMailLogPage, resendMail } from '#/api/system/mail/log'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import Form from './modules/form.vue'; | ||||||
|  | 
 | ||||||
|  | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Form, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查看邮件日志详情 */ | ||||||
|  | function onView(row: SystemMailLogApi.MailLog) { | ||||||
|  |   formModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 重新发送邮件 */ | ||||||
|  | async function onResend(row: SystemMailLogApi.MailLog) { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: '重新发送邮件中...', | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await resendMail(row.id as number); | ||||||
|  |     message.success({ | ||||||
|  |       content: '重新发送邮件成功', | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemMailLogApi.MailLog>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'resend': { | ||||||
|  |       onResend(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'view': { | ||||||
|  |       onView(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getMailLogPage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemMailLogApi.MailLog>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <FormModal @success="onRefresh" /> | ||||||
|  |     <Grid table-title="邮件日志列表"> | ||||||
|  |       <template #toolbar-tools> </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemMailLogApi } from '#/api/system/mail/log'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Descriptions, Tag } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | import { formatDateTime } from '@vben/utils'; | ||||||
|  | import { DICT_TYPE, getDictLabel } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | const formData = ref<SystemMailLogApi.MailLog>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return '邮件日志详情'; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemMailLogApi.MailLog>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       formData.value = data; | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <div class="p-4"> | ||||||
|  |       <Descriptions :column="2" bordered> | ||||||
|  |         <Descriptions.Item label="编号">{{ formData?.id }}</Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="创建时间"> | ||||||
|  |           {{ formatDateTime(formData?.createTime || '') }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="收件邮箱"> | ||||||
|  |           {{ formData?.toMail }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="发送邮箱"> | ||||||
|  |           {{ formData?.fromMail }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="用户编号"> | ||||||
|  |           {{ formData?.userId }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="用户类型"> | ||||||
|  |           {{ formData?.userType }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="模板编号"> | ||||||
|  |           {{ formData?.templateId }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="模板编码"> | ||||||
|  |           {{ formData?.templateCode }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="邮件标题" :span="2"> | ||||||
|  |           {{ formData?.templateTitle }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="邮件内容" :span="2"> | ||||||
|  |           <div v-html="formData?.templateContent"></div> | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="发送状态"> | ||||||
|  |           <!-- TODO @芋艿: 数据字典--> | ||||||
|  |           <Tag color="processing"> | ||||||
|  |             {{ | ||||||
|  |               getDictLabel( | ||||||
|  |                 DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, | ||||||
|  |                 formData?.sendStatus, | ||||||
|  |               ) | ||||||
|  |             }} | ||||||
|  |           </Tag> | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="发送时间"> | ||||||
|  |           {{ formatDateTime(formData?.sendTime || '') }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="发送消息编号"> | ||||||
|  |           {{ formData?.sendMessageId }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |         <Descriptions.Item label="发送异常"> | ||||||
|  |           {{ formData?.sendException }} | ||||||
|  |         </Descriptions.Item> | ||||||
|  |       </Descriptions> | ||||||
|  |     </div> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,237 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailTemplateApi } from '#/api/system/mail/template'; | ||||||
|  | 
 | ||||||
|  | import { z } from '#/adapter/form'; | ||||||
|  | import { getSimpleMailAccountList } from '#/api/system/mail/account'; | ||||||
|  | import { CommonStatusEnum } from '#/utils/constants'; | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 新增/修改的表单 */ | ||||||
|  | export function useFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'id', | ||||||
|  |       label: 'id', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'name', | ||||||
|  |       label: '模板名称', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'code', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'accountId', | ||||||
|  |       label: '邮箱账号', | ||||||
|  |       component: 'ApiSelect', | ||||||
|  |       componentProps: { | ||||||
|  |         api: async () => await getSimpleMailAccountList(), | ||||||
|  |         class: 'w-full', | ||||||
|  |         labelField: 'mail', | ||||||
|  |         valueField: 'id', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'nickname', | ||||||
|  |       label: '发送人名称', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'title', | ||||||
|  |       label: '模板标题', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'content', | ||||||
|  |       label: '模板内容', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         height: 300, | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'status', | ||||||
|  |       label: '开启状态', | ||||||
|  |       component: 'RadioGroup', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         buttonStyle: 'solid', | ||||||
|  |         optionType: 'button', | ||||||
|  |       }, | ||||||
|  |       rules: z.number().default(CommonStatusEnum.ENABLE), | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'remark', | ||||||
|  |       label: '备注', | ||||||
|  |       component: 'Textarea', | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'status', | ||||||
|  |       label: '开启状态', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'code', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'name', | ||||||
|  |       label: '模板名称', | ||||||
|  |       component: 'Input', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'accountId', | ||||||
|  |       label: '邮箱账号', | ||||||
|  |       component: 'ApiSelect', | ||||||
|  |       componentProps: { | ||||||
|  |         api: async () => await getSimpleMailAccountList(), | ||||||
|  |         labelField: 'mail', | ||||||
|  |         valueField: 'id', | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '创建时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 发送邮件表单 */ | ||||||
|  | export function useSendMailFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'content', | ||||||
|  |       label: '模板内容', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         disabled: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'mail', | ||||||
|  |       label: '收件邮箱', | ||||||
|  |       component: 'Input', | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateParams', | ||||||
|  |       label: '模板参数', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemMailTemplateApi.MailTemplate>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'id', | ||||||
|  |       title: '编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'name', | ||||||
|  |       title: '模板名称', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'code', | ||||||
|  |       title: '模板编码', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'title', | ||||||
|  |       title: '模板标题', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'accountId', | ||||||
|  |       title: '邮箱账号', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'nickname', | ||||||
|  |       title: '发送人名称', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'status', | ||||||
|  |       title: '开启状态', | ||||||
|  |       minWidth: 100, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.COMMON_STATUS }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'remark', | ||||||
|  |       title: '备注', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '创建时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 300, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'name', | ||||||
|  |           nameTitle: '邮件模板', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           'edit', // 默认的编辑按钮
 | ||||||
|  |           'delete', // 默认的删除按钮
 | ||||||
|  |           { | ||||||
|  |             code: 'mail-send', | ||||||
|  |             text: '发送邮件', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,136 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemMailTemplateApi } from '#/api/system/mail/template'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Plus } from '@vben/icons'; | ||||||
|  | 
 | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { | ||||||
|  |   deleteMailTemplate, | ||||||
|  |   getMailTemplatePage, | ||||||
|  | } from '#/api/system/mail/template'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import Form from './modules/form.vue'; | ||||||
|  | import SendForm from './modules/send-form.vue'; | ||||||
|  | 
 | ||||||
|  | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Form, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [SendModal, sendModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: SendForm, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 创建邮件模板 */ | ||||||
|  | function onCreate() { | ||||||
|  |   formModalApi.setData(null).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 编辑邮件模板 */ | ||||||
|  | function onEdit(row: SystemMailTemplateApi.MailTemplate) { | ||||||
|  |   formModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 发送测试邮件 */ | ||||||
|  | function onSend(row: SystemMailTemplateApi.MailTemplate) { | ||||||
|  |   sendModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 删除邮件模板 */ | ||||||
|  | async function onDelete(row: SystemMailTemplateApi.MailTemplate) { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: $t('ui.actionMessage.deleting', [row.name]), | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await deleteMailTemplate(row.id as number); | ||||||
|  |     message.success({ | ||||||
|  |       content: $t('ui.actionMessage.deleteSuccess', [row.name]), | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemMailTemplateApi.MailTemplate>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'delete': { | ||||||
|  |       onDelete(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'edit': { | ||||||
|  |       onEdit(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'mail-send': { | ||||||
|  |       onSend(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getMailTemplatePage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemMailTemplateApi.MailTemplate>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <FormModal @success="onRefresh" /> | ||||||
|  |     <SendModal /> | ||||||
|  |     <Grid table-title="邮件模板列表"> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button type="primary" @click="onCreate"> | ||||||
|  |           <Plus class="size-5" /> | ||||||
|  |           {{ $t('ui.actionTitle.create', ['邮件模板']) }} | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,82 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemMailTemplateApi } from '#/api/system/mail/template'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { | ||||||
|  |   createMailTemplate, | ||||||
|  |   getMailTemplate, | ||||||
|  |   updateMailTemplate, | ||||||
|  | } from '#/api/system/mail/template'; | ||||||
|  | 
 | ||||||
|  | import { useFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const formData = ref<SystemMailTemplateApi.MailTemplate>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return formData.value?.id | ||||||
|  |     ? $t('ui.actionTitle.edit', ['邮件模板']) | ||||||
|  |     : $t('ui.actionTitle.create', ['邮件模板']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: useFormSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 提交表单 | ||||||
|  |     const data = | ||||||
|  |       (await formApi.getValues()) as SystemMailTemplateApi.MailTemplate; | ||||||
|  |     try { | ||||||
|  |       await (formData.value?.id | ||||||
|  |         ? updateMailTemplate(data) | ||||||
|  |         : createMailTemplate(data)); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       message.success({ | ||||||
|  |         content: $t('ui.actionMessage.operationSuccess'), | ||||||
|  |         key: 'action_process_msg', | ||||||
|  |       }); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemMailTemplateApi.MailTemplate>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       formData.value = await getMailTemplate(data.id); | ||||||
|  |       // 设置到 values | ||||||
|  |       await formApi.setValues(formData.value); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <Form class="mx-4" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,114 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemMailTemplateApi } from '#/api/system/mail/template'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { sendMail } from '#/api/system/mail/template'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | 
 | ||||||
|  | import { useSendMailFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const templateData = ref<SystemMailTemplateApi.MailTemplate>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return $t('ui.actionTitle.send', ['邮件']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // 动态构建表单 | ||||||
|  | const buildSchema = () => { | ||||||
|  |   const schema = useSendMailFormSchema(); | ||||||
|  | 
 | ||||||
|  |   // 添加参数字段 | ||||||
|  |   if (templateData.value?.params && templateData.value.params.length > 0) { | ||||||
|  |     templateData.value.params?.forEach((param) => { | ||||||
|  |       schema.push({ | ||||||
|  |         component: 'Input', | ||||||
|  |         fieldName: `param_${param}`, | ||||||
|  |         label: `参数 ${param}`, | ||||||
|  |         rules: 'required', | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return schema; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: buildSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 获取表单数据 | ||||||
|  |     const values = await formApi.getValues(); | ||||||
|  | 
 | ||||||
|  |     // 提取参数 | ||||||
|  |     const paramsObj: Record<string, string> = {}; | ||||||
|  |     if (templateData.value?.params) { | ||||||
|  |       templateData.value.params.forEach((param) => { | ||||||
|  |         paramsObj[param] = values[`param_${param}`]; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 构建发送邮件请求 | ||||||
|  |     const data: SystemMailTemplateApi.MailSendReq = { | ||||||
|  |       mail: values.mail, | ||||||
|  |       templateCode: templateData.value?.code || '', | ||||||
|  |       templateParams: paramsObj, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       await sendMail(data); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       message.success({ | ||||||
|  |         content: '邮件发送成功', | ||||||
|  |         key: 'action_process_msg', | ||||||
|  |       }); | ||||||
|  |     } catch (error) { | ||||||
|  |       console.error('发送邮件失败', error); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 获取数据 | ||||||
|  |     const data = modalApi.getData<SystemMailTemplateApi.MailTemplate>(); | ||||||
|  |     if (!data) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     templateData.value = data; | ||||||
|  |     // 更新表单结构 | ||||||
|  |     const schema = buildSchema(); | ||||||
|  |     formApi.setState({ schema }); | ||||||
|  | 
 | ||||||
|  |     // 设置表单初始值,包括模板内容 | ||||||
|  |     await formApi.setValues({ | ||||||
|  |       content: data.content, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <Form class="mx-4" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,158 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'userId', | ||||||
|  |       label: '用户编号', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请输入用户编号', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'userType', | ||||||
|  |       label: '用户类型', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), | ||||||
|  |         placeholder: '请选择用户类型', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateCode', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请输入模板编码', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateType', | ||||||
|  |       label: '模版类型', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions( | ||||||
|  |           DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, | ||||||
|  |           'number', | ||||||
|  |         ), | ||||||
|  |         placeholder: '请选择模版类型', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '创建时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'id', | ||||||
|  |       title: '编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'userType', | ||||||
|  |       title: '用户类型', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.USER_TYPE }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'userId', | ||||||
|  |       title: '用户编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateCode', | ||||||
|  |       title: '模板编码', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateNickname', | ||||||
|  |       title: '发送人名称', | ||||||
|  |       minWidth: 180, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateContent', | ||||||
|  |       title: '模版内容', | ||||||
|  |       minWidth: 200, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateParams', | ||||||
|  |       title: '模版参数', | ||||||
|  |       minWidth: 180, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateType', | ||||||
|  |       title: '模版类型', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'readStatus', | ||||||
|  |       title: '是否已读', | ||||||
|  |       minWidth: 100, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'readTime', | ||||||
|  |       title: '阅读时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '创建时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 180, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'id', | ||||||
|  |           nameTitle: '站内信', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           { | ||||||
|  |             code: 'view', | ||||||
|  |             text: '查看', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { getNotifyMessagePage } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import Detail from './modules/detail.vue'; | ||||||
|  | 
 | ||||||
|  | const [DetailModal, detailModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Detail, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查看站内信详情 */ | ||||||
|  | function onView(row: SystemNotifyMessageApi.NotifyMessage) { | ||||||
|  |   detailModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemNotifyMessageApi.NotifyMessage>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'view': { | ||||||
|  |       onView(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getNotifyMessagePage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemNotifyMessageApi.NotifyMessage>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <DetailModal @success="onRefresh" /> | ||||||
|  |     <Grid table-title="站内信列表" /> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Descriptions, Tag } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { ref } from 'vue'; | ||||||
|  | import { formatDateTime } from '@vben/utils'; | ||||||
|  | import { DICT_TYPE, getDictLabel } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | const messageData = ref<SystemNotifyMessageApi.NotifyMessage>(); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemNotifyMessageApi.NotifyMessage>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       messageData.value = data; | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal title="站内信详情"> | ||||||
|  |     <Descriptions bordered :column="1" size="middle" class="mx-4"> | ||||||
|  |       <Descriptions.Item label="编号">{{ messageData?.id }}</Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="用户类型"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ getDictLabel(DICT_TYPE.USER_TYPE, messageData?.userType) }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="用户编号"> | ||||||
|  |         {{ messageData?.userId }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版编号"> | ||||||
|  |         {{ messageData?.templateId }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模板编码"> | ||||||
|  |         {{ messageData?.templateCode }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="发送人名称"> | ||||||
|  |         {{ messageData?.templateNickname }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版内容"> | ||||||
|  |         {{ messageData?.templateContent }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版参数"> | ||||||
|  |         {{ messageData?.templateParams }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版类型"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ | ||||||
|  |             getDictLabel( | ||||||
|  |               DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, | ||||||
|  |               messageData?.templateType, | ||||||
|  |             ) | ||||||
|  |           }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="是否已读"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ | ||||||
|  |             getDictLabel( | ||||||
|  |               DICT_TYPE.INFRA_BOOLEAN_STRING, | ||||||
|  |               messageData?.readStatus, | ||||||
|  |             ) | ||||||
|  |           }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="阅读时间"> | ||||||
|  |         {{ formatDateTime(messageData?.readTime || '') }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="创建时间"> | ||||||
|  |         {{ formatDateTime(messageData?.createTime || '') }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |     </Descriptions> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,98 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'readStatus', | ||||||
|  |       label: '是否已读', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), | ||||||
|  |         placeholder: '请选择是否已读', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '发送时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'templateNickname', | ||||||
|  |       title: '发送人', | ||||||
|  |       minWidth: 180, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '发送时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateType', | ||||||
|  |       title: '类型', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'templateContent', | ||||||
|  |       title: '消息内容', | ||||||
|  |       minWidth: 300, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'readStatus', | ||||||
|  |       title: '是否已读', | ||||||
|  |       minWidth: 100, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'readTime', | ||||||
|  |       title: '阅读时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 180, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'id', | ||||||
|  |           nameTitle: '站内信', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           { | ||||||
|  |             code: 'view', | ||||||
|  |             text: '查看', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,163 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { MdiCheckboxMarkedCircleOutline } from '@vben/icons'; | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | import Detail from './modules/detail.vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { | ||||||
|  |   getMyNotifyMessagePage, | ||||||
|  |   updateAllNotifyMessageRead, | ||||||
|  |   updateNotifyMessageRead, | ||||||
|  | } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | 
 | ||||||
|  | const [DetailModal, detailModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Detail, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 查看站内信详情 */ | ||||||
|  | function onView(row: SystemNotifyMessageApi.NotifyMessage) { | ||||||
|  |   // 标记已读 | ||||||
|  |   if (!row.readStatus) { | ||||||
|  |     handleReadOne(row.id); | ||||||
|  |   } | ||||||
|  |   detailModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 标记一条站内信已读 */ | ||||||
|  | async function handleReadOne(id: number) { | ||||||
|  |   await updateNotifyMessageRead([id]); | ||||||
|  |   onRefresh(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 标记选中的站内信为已读 */ | ||||||
|  | async function onMarkRead() { | ||||||
|  |   const rows = gridApi.grid.getCheckboxRecords(); | ||||||
|  |   if (!rows || rows.length === 0) { | ||||||
|  |     message.warning({ | ||||||
|  |       content: '请选择需要标记的站内信', | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const ids = rows.map((row: SystemNotifyMessageApi.NotifyMessage) => row.id); | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: '正在标记已读...', | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   try { | ||||||
|  |     await updateNotifyMessageRead(ids); | ||||||
|  |     message.success({ | ||||||
|  |       content: '标记已读成功', | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 标记所有站内信为已读 */ | ||||||
|  | async function onMarkAllRead() { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: '正在标记全部已读...', | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   try { | ||||||
|  |     await updateAllNotifyMessageRead(); | ||||||
|  |     message.success({ | ||||||
|  |       content: '全部标记已读成功', | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemNotifyMessageApi.NotifyMessage>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'view': { | ||||||
|  |       onView(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 是否允许勾选的过滤函数(只允许未读的消息被选择) | ||||||
|  | function checkboxConfig(params: { row: SystemNotifyMessageApi.NotifyMessage }) { | ||||||
|  |   return !params.row.readStatus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getMyNotifyMessagePage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |     checkboxConfig: { | ||||||
|  |       checkMethod: checkboxConfig, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemNotifyMessageApi.NotifyMessage>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <DetailModal @success="onRefresh" /> | ||||||
|  |     <Grid table-title="我的站内信"> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button type="primary" @click="onMarkRead"> | ||||||
|  |           <MdiCheckboxMarkedCircleOutline /> | ||||||
|  |           标记已读 | ||||||
|  |         </Button> | ||||||
|  |         <Button type="primary" class="ml-2" @click="onMarkAllRead"> | ||||||
|  |           <MdiCheckboxMarkedCircleOutline /> | ||||||
|  |           全部已读 | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,91 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemNotifyMessageApi } from '#/api/system/notify/message'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Descriptions, Tag } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { ref } from 'vue'; | ||||||
|  | import { formatDateTime } from '@vben/utils'; | ||||||
|  | import { DICT_TYPE, getDictLabel } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | const messageData = ref<SystemNotifyMessageApi.NotifyMessage>(); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemNotifyMessageApi.NotifyMessage>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       messageData.value = data; | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal title="我的站内信"> | ||||||
|  |     <Descriptions bordered :column="1" size="middle" class="mx-4"> | ||||||
|  |       <Descriptions.Item label="编号">{{ messageData?.id }}</Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="用户类型"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ getDictLabel(DICT_TYPE.USER_TYPE, messageData?.userType) }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="用户编号"> | ||||||
|  |         {{ messageData?.userId }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版编号"> | ||||||
|  |         {{ messageData?.templateId }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模板编码"> | ||||||
|  |         {{ messageData?.templateCode }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="发送人名称"> | ||||||
|  |         {{ messageData?.templateNickname }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版内容"> | ||||||
|  |         {{ messageData?.templateContent }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版参数"> | ||||||
|  |         {{ messageData?.templateParams }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="模版类型"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ | ||||||
|  |             getDictLabel( | ||||||
|  |               DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, | ||||||
|  |               messageData?.templateType, | ||||||
|  |             ) | ||||||
|  |           }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="是否已读"> | ||||||
|  |         <!-- TODO @芋艿: 数据字典--> | ||||||
|  |         <Tag color="processing"> | ||||||
|  |           {{ | ||||||
|  |             getDictLabel( | ||||||
|  |               DICT_TYPE.INFRA_BOOLEAN_STRING, | ||||||
|  |               messageData?.readStatus, | ||||||
|  |             ) | ||||||
|  |           }} | ||||||
|  |         </Tag> | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="阅读时间"> | ||||||
|  |         {{ formatDateTime(messageData?.readTime || '') }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |       <Descriptions.Item label="创建时间"> | ||||||
|  |         {{ formatDateTime(messageData?.createTime || '') }} | ||||||
|  |       </Descriptions.Item> | ||||||
|  |     </Descriptions> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,268 @@ | ||||||
|  | import type { VbenFormSchema } from '#/adapter/form'; | ||||||
|  | import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyTemplateApi } from '#/api/system/notify/template'; | ||||||
|  | 
 | ||||||
|  | import { z } from '#/adapter/form'; | ||||||
|  | import { CommonStatusEnum } from '#/utils/constants'; | ||||||
|  | import { DICT_TYPE, getDictOptions } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | /** 新增/修改的表单 */ | ||||||
|  | export function useFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'id', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'name', | ||||||
|  |       label: '模板名称', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入模板名称', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'code', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入模板编码', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'nickname', | ||||||
|  |       label: '发送人名称', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入发送人名称', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'content', | ||||||
|  |       label: '模板内容', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入模板内容', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'type', | ||||||
|  |       label: '模板类型', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions( | ||||||
|  |           DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, | ||||||
|  |           'number', | ||||||
|  |         ), | ||||||
|  |         class: 'w-full', | ||||||
|  |         placeholder: '请选择模板类型', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'status', | ||||||
|  |       label: '状态', | ||||||
|  |       component: 'RadioGroup', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         buttonStyle: 'solid', | ||||||
|  |         optionType: 'button', | ||||||
|  |       }, | ||||||
|  |       rules: z.number().default(CommonStatusEnum.ENABLE), | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'remark', | ||||||
|  |       label: '备注', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入备注', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的搜索表单 */ | ||||||
|  | export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'name', | ||||||
|  |       label: '模板名称', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请输入模板名称', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'code', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请输入模板编码', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'status', | ||||||
|  |       label: '状态', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请选择状态', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'type', | ||||||
|  |       label: '模板类型', | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         options: getDictOptions( | ||||||
|  |           DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, | ||||||
|  |           'number', | ||||||
|  |         ), | ||||||
|  |         allowClear: true, | ||||||
|  |         placeholder: '请选择模板类型', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'createTime', | ||||||
|  |       label: '创建时间', | ||||||
|  |       component: 'RangePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 发送站内信表单 */ | ||||||
|  | export function useSendNotifyFormSchema(): VbenFormSchema[] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'content', | ||||||
|  |       label: '模板内容', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         disabled: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'userId', | ||||||
|  |       label: '用户编号', | ||||||
|  |       component: 'Input', | ||||||
|  |       componentProps: { | ||||||
|  |         placeholder: '请输入用户编号', | ||||||
|  |       }, | ||||||
|  |       rules: 'required', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateCode', | ||||||
|  |       label: '模板编码', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       fieldName: 'templateParams', | ||||||
|  |       label: '模板参数', | ||||||
|  |       component: 'Input', | ||||||
|  |       dependencies: { | ||||||
|  |         triggerFields: [''], | ||||||
|  |         show: () => false, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 列表的字段 */ | ||||||
|  | export function useGridColumns<T = SystemNotifyTemplateApi.NotifyTemplate>( | ||||||
|  |   onActionClick: OnActionClickFn<T>, | ||||||
|  | ): VxeTableGridOptions['columns'] { | ||||||
|  |   return [ | ||||||
|  |     { | ||||||
|  |       field: 'id', | ||||||
|  |       title: '编号', | ||||||
|  |       minWidth: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'name', | ||||||
|  |       title: '模板名称', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'code', | ||||||
|  |       title: '模板编码', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'nickname', | ||||||
|  |       title: '发送人名称', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'content', | ||||||
|  |       title: '模板内容', | ||||||
|  |       minWidth: 200, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'type', | ||||||
|  |       title: '模板类型', | ||||||
|  |       minWidth: 120, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'status', | ||||||
|  |       title: '状态', | ||||||
|  |       minWidth: 100, | ||||||
|  |       cellRender: { | ||||||
|  |         name: 'CellDict', | ||||||
|  |         props: { type: DICT_TYPE.COMMON_STATUS }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'remark', | ||||||
|  |       title: '备注', | ||||||
|  |       minWidth: 120, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'createTime', | ||||||
|  |       title: '创建时间', | ||||||
|  |       minWidth: 180, | ||||||
|  |       formatter: 'formatDateTime', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'operation', | ||||||
|  |       title: '操作', | ||||||
|  |       minWidth: 180, | ||||||
|  |       align: 'center', | ||||||
|  |       fixed: 'right', | ||||||
|  |       cellRender: { | ||||||
|  |         attrs: { | ||||||
|  |           nameField: 'name', | ||||||
|  |           nameTitle: '站内信模板', | ||||||
|  |           onClick: onActionClick, | ||||||
|  |         }, | ||||||
|  |         name: 'CellOperation', | ||||||
|  |         options: [ | ||||||
|  |           { code: 'edit', text: '编辑' }, | ||||||
|  |           { code: 'notify-send', text: '测试' }, | ||||||
|  |           { code: 'delete', text: '删除' }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,147 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
|  | import type { SystemNotifyTemplateApi } from '#/api/system/notify/template'; | ||||||
|  | 
 | ||||||
|  | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { Download, Plus } from '@vben/icons'; | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | import Form from './modules/form.vue'; | ||||||
|  | import SendForm from './modules/send-form.vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
|  | import { | ||||||
|  |   deleteNotifyTemplate, | ||||||
|  |   exportNotifyTemplate, | ||||||
|  |   getNotifyTemplatePage, | ||||||
|  | } from '#/api/system/notify/template'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | import { downloadByData } from '#/utils/download'; | ||||||
|  | 
 | ||||||
|  | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | 
 | ||||||
|  | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: Form, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [SendModal, sendModalApi] = useVbenModal({ | ||||||
|  |   connectedComponent: SendForm, | ||||||
|  |   destroyOnClose: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | /** 刷新表格 */ | ||||||
|  | function onRefresh() { | ||||||
|  |   gridApi.query(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 导出表格 */ | ||||||
|  | async function onExport() { | ||||||
|  |   const data = await exportNotifyTemplate(await gridApi.formApi.getValues()); | ||||||
|  |   downloadByData(data, '站内信模板.xls'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 创建站内信模板 */ | ||||||
|  | function onCreate() { | ||||||
|  |   formModalApi.setData(null).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 编辑站内信模板 */ | ||||||
|  | function onEdit(row: SystemNotifyTemplateApi.NotifyTemplate) { | ||||||
|  |   formModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 发送测试站内信 */ | ||||||
|  | function onSend(row: SystemNotifyTemplateApi.NotifyTemplate) { | ||||||
|  |   sendModalApi.setData(row).open(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 删除站内信模板 */ | ||||||
|  | async function onDelete(row: SystemNotifyTemplateApi.NotifyTemplate) { | ||||||
|  |   const hideLoading = message.loading({ | ||||||
|  |     content: $t('ui.actionMessage.deleting', [row.name]), | ||||||
|  |     duration: 0, | ||||||
|  |     key: 'action_process_msg', | ||||||
|  |   }); | ||||||
|  |   try { | ||||||
|  |     await deleteNotifyTemplate(row.id as number); | ||||||
|  |     message.success({ | ||||||
|  |       content: $t('ui.actionMessage.deleteSuccess', [row.name]), | ||||||
|  |       key: 'action_process_msg', | ||||||
|  |     }); | ||||||
|  |     onRefresh(); | ||||||
|  |   } finally { | ||||||
|  |     hideLoading(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 表格操作按钮的回调函数 */ | ||||||
|  | function onActionClick({ | ||||||
|  |   code, | ||||||
|  |   row, | ||||||
|  | }: OnActionClickParams<SystemNotifyTemplateApi.NotifyTemplate>) { | ||||||
|  |   switch (code) { | ||||||
|  |     case 'delete': { | ||||||
|  |       onDelete(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'edit': { | ||||||
|  |       onEdit(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 'notify-send': { | ||||||
|  |       onSend(row); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|  |   formOptions: { | ||||||
|  |     schema: useGridFormSchema(), | ||||||
|  |   }, | ||||||
|  |   gridOptions: { | ||||||
|  |     columns: useGridColumns(onActionClick), | ||||||
|  |     height: 'auto', | ||||||
|  |     keepSource: true, | ||||||
|  |     proxyConfig: { | ||||||
|  |       ajax: { | ||||||
|  |         query: async ({ page }, formValues) => { | ||||||
|  |           return await getNotifyTemplatePage({ | ||||||
|  |             pageNo: page.currentPage, | ||||||
|  |             pageSize: page.pageSize, | ||||||
|  |             ...formValues, | ||||||
|  |           }); | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     rowConfig: { | ||||||
|  |       keyField: 'id', | ||||||
|  |     }, | ||||||
|  |     toolbarConfig: { | ||||||
|  |       refresh: { code: 'query' }, | ||||||
|  |       search: true, | ||||||
|  |     }, | ||||||
|  |   } as VxeTableGridOptions<SystemNotifyTemplateApi.NotifyTemplate>, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <FormModal @success="onRefresh" /> | ||||||
|  |     <SendModal /> | ||||||
|  |     <Grid table-title="站内信模板列表"> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button type="primary" @click="onCreate"> | ||||||
|  |           <Plus class="size-5" /> | ||||||
|  |           {{ $t('ui.actionTitle.create', ['站内信模板']) }} | ||||||
|  |         </Button> | ||||||
|  |         <Button type="primary" class="ml-2" @click="onExport"> | ||||||
|  |           <Download class="size-5" /> | ||||||
|  |           {{ $t('ui.actionTitle.export') }} | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,85 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemNotifyTemplateApi } from '#/api/system/notify/template'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { | ||||||
|  |   createNotifyTemplate, | ||||||
|  |   getNotifyTemplate, | ||||||
|  |   updateNotifyTemplate, | ||||||
|  | } from '#/api/system/notify/template'; | ||||||
|  | 
 | ||||||
|  | import { useFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const formData = ref<SystemNotifyTemplateApi.NotifyTemplate>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return formData.value?.id | ||||||
|  |     ? $t('ui.actionTitle.edit', ['站内信模板']) | ||||||
|  |     : $t('ui.actionTitle.create', ['站内信模板']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: useFormSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  |   commonConfig: { | ||||||
|  |     labelWidth: 120, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 提交表单 | ||||||
|  |     const data = | ||||||
|  |       (await formApi.getValues()) as SystemNotifyTemplateApi.NotifyTemplate; | ||||||
|  |     try { | ||||||
|  |       await (formData.value?.id | ||||||
|  |         ? updateNotifyTemplate(data) | ||||||
|  |         : createNotifyTemplate(data)); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       message.success({ | ||||||
|  |         content: $t('ui.actionMessage.operationSuccess'), | ||||||
|  |         key: 'action_process_msg', | ||||||
|  |       }); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 加载数据 | ||||||
|  |     const data = modalApi.getData<SystemNotifyTemplateApi.NotifyTemplate>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     try { | ||||||
|  |       formData.value = await getNotifyTemplate(data.id as number); | ||||||
|  |       // 设置到 values | ||||||
|  |       await formApi.setValues(formData.value); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <Form class="mx-4" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,116 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { SystemNotifyTemplateApi } from '#/api/system/notify/template'; | ||||||
|  | 
 | ||||||
|  | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | import { useVbenForm } from '#/adapter/form'; | ||||||
|  | import { sendNotify } from '#/api/system/notify/template'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
|  | 
 | ||||||
|  | import { useSendNotifyFormSchema } from '../data'; | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits(['success']); | ||||||
|  | const templateData = ref<SystemNotifyTemplateApi.NotifyTemplate>(); | ||||||
|  | const getTitle = computed(() => { | ||||||
|  |   return $t('ui.actionTitle.send', ['站内信']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // 动态构建表单 | ||||||
|  | const buildSchema = () => { | ||||||
|  |   const schema = useSendNotifyFormSchema(); | ||||||
|  | 
 | ||||||
|  |   // 添加参数字段 | ||||||
|  |   if (templateData.value?.params) { | ||||||
|  |     templateData.value.params.forEach((param) => { | ||||||
|  |       schema.push({ | ||||||
|  |         fieldName: `param_${param}`, | ||||||
|  |         label: `参数 ${param}`, | ||||||
|  |         component: 'Input', | ||||||
|  |         rules: 'required', | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return schema; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useVbenForm({ | ||||||
|  |   layout: 'horizontal', | ||||||
|  |   schema: buildSchema(), | ||||||
|  |   showDefaultActions: false, | ||||||
|  |   commonConfig: { | ||||||
|  |     labelWidth: 120, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const [Modal, modalApi] = useVbenModal({ | ||||||
|  |   async onConfirm() { | ||||||
|  |     const { valid } = await formApi.validate(); | ||||||
|  |     if (!valid) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     modalApi.lock(); | ||||||
|  |     // 获取表单数据 | ||||||
|  |     const values = await formApi.getValues(); | ||||||
|  | 
 | ||||||
|  |     // 提取参数 | ||||||
|  |     const paramsObj: Record<string, string> = {}; | ||||||
|  |     if (templateData.value?.params) { | ||||||
|  |       templateData.value.params.forEach((param) => { | ||||||
|  |         paramsObj[param] = values[`param_${param}`]; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 构建发送站内信请求 | ||||||
|  |     const data: SystemNotifyTemplateApi.NotifySendReq = { | ||||||
|  |       userId: values.userId, | ||||||
|  |       templateCode: templateData.value?.code || '', | ||||||
|  |       templateParams: paramsObj, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       await sendNotify(data); | ||||||
|  |       // 关闭并提示 | ||||||
|  |       await modalApi.close(); | ||||||
|  |       emit('success'); | ||||||
|  |       message.success({ | ||||||
|  |         content: $t('ui.actionMessage.operationSuccess'), | ||||||
|  |         key: 'action_process_msg', | ||||||
|  |       }); | ||||||
|  |     } catch (error) { | ||||||
|  |       console.error('发送站内信失败', error); | ||||||
|  |     } finally { | ||||||
|  |       modalApi.lock(false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   async onOpenChange(isOpen: boolean) { | ||||||
|  |     if (!isOpen) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // 获取数据 | ||||||
|  |     const data = modalApi.getData<SystemNotifyTemplateApi.NotifyTemplate>(); | ||||||
|  |     if (!data || !data.id) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     templateData.value = data; | ||||||
|  |     // 更新表单结构 | ||||||
|  |     const schema = buildSchema(); | ||||||
|  |     formApi.setState({ schema }); | ||||||
|  | 
 | ||||||
|  |     // 设置表单初始值 | ||||||
|  |     await formApi.setValues({ | ||||||
|  |       content: data.content, | ||||||
|  |       templateCode: data.code, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Modal :title="getTitle"> | ||||||
|  |     <Form class="mx-4" /> | ||||||
|  |   </Modal> | ||||||
|  | </template> | ||||||
|  | @ -15,7 +15,7 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|       componentProps: { |       componentProps: { | ||||||
|         allowClear: true, |         allowClear: true, | ||||||
|         placeholder: '请输入手机号', |         placeholder: '请输入手机号', | ||||||
|       } |       }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       fieldName: 'channelId', |       fieldName: 'channelId', | ||||||
|  | @ -36,7 +36,7 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|       componentProps: { |       componentProps: { | ||||||
|         allowClear: true, |         allowClear: true, | ||||||
|         placeholder: '请输入模板编号', |         placeholder: '请输入模板编号', | ||||||
|       } |       }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       fieldName: 'sendStatus', |       fieldName: 'sendStatus', | ||||||
|  | @ -80,7 +80,7 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 列表的字段 */ | /** 列表的字段 */ | ||||||
| export function useGridColumns<T = SystemSmsLogApi.SmsLogVO>( | export function useGridColumns<T = SystemSmsLogApi.SmsLog>( | ||||||
|   onActionClick: OnActionClickFn<T>, |   onActionClick: OnActionClickFn<T>, | ||||||
| ): VxeTableGridOptions['columns'] { | ): VxeTableGridOptions['columns'] { | ||||||
|   return [ |   return [ | ||||||
|  |  | ||||||
|  | @ -1,18 +1,22 @@ | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table'; | import type { | ||||||
|  |   OnActionClickParams, | ||||||
|  |   VxeTableGridOptions, | ||||||
|  | } from '#/adapter/vxe-table'; | ||||||
| import type { SystemSmsLogApi } from '#/api/system/sms/log'; | import type { SystemSmsLogApi } from '#/api/system/sms/log'; | ||||||
| 
 | 
 | ||||||
| import { Page, useVbenModal } from '@vben/common-ui'; | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
| import { Download } from '@vben/icons'; | import { Download } from '@vben/icons'; | ||||||
| import { Button } from 'ant-design-vue'; |  | ||||||
| import Form from './modules/form.vue'; |  | ||||||
| 
 | 
 | ||||||
| import { $t } from '#/locales'; | import { Button } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
| import { useVbenVxeGrid } from '#/adapter/vxe-table'; | import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||||
| import { exportSmsLog, getSmsLogPage } from '#/api/system/sms/log'; | import { exportSmsLog, getSmsLogPage } from '#/api/system/sms/log'; | ||||||
|  | import { $t } from '#/locales'; | ||||||
| import { downloadByData } from '#/utils/download'; | import { downloadByData } from '#/utils/download'; | ||||||
| 
 | 
 | ||||||
| import { useGridColumns, useGridFormSchema } from './data'; | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
|  | import Form from './modules/form.vue'; | ||||||
| 
 | 
 | ||||||
| const [FormModal, formModalApi] = useVbenModal({ | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|   connectedComponent: Form, |   connectedComponent: Form, | ||||||
|  | @ -31,7 +35,7 @@ async function onExport() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 查看短信日志详情 */ | /** 查看短信日志详情 */ | ||||||
| function onView(row: SystemSmsLogApi.SmsLogVO) { | function onView(row: SystemSmsLogApi.SmsLog) { | ||||||
|   formModalApi.setData(row).open(); |   formModalApi.setData(row).open(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +43,7 @@ function onView(row: SystemSmsLogApi.SmsLogVO) { | ||||||
| function onActionClick({ | function onActionClick({ | ||||||
|   code, |   code, | ||||||
|   row, |   row, | ||||||
| }: OnActionClickParams<SystemSmsLogApi.SmsLogVO>) { | }: OnActionClickParams<SystemSmsLogApi.SmsLog>) { | ||||||
|   switch (code) { |   switch (code) { | ||||||
|     case 'view': { |     case 'view': { | ||||||
|       onView(row); |       onView(row); | ||||||
|  | @ -74,7 +78,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|       refresh: { code: 'query' }, |       refresh: { code: 'query' }, | ||||||
|       search: true, |       search: true, | ||||||
|     }, |     }, | ||||||
|   } as VxeTableGridOptions<SystemSmsLogApi.SmsLogVO>, |   } as VxeTableGridOptions<SystemSmsLogApi.SmsLog>, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,16 @@ | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import type { SystemSmsLogApi } from '#/api/system/sms/log'; | import type { SystemSmsLogApi } from '#/api/system/sms/log'; | ||||||
| 
 | 
 | ||||||
| import { useVbenModal } from '@vben/common-ui'; |  | ||||||
| 
 |  | ||||||
| import { ref } from 'vue'; | import { ref } from 'vue'; | ||||||
| 
 | 
 | ||||||
| const formData = ref<SystemSmsLogApi.SmsLogVO>(); | import { useVbenModal } from '@vben/common-ui'; | ||||||
|  | import { formatDateTime } from '@vben/utils'; | ||||||
|  | 
 | ||||||
|  | import { Descriptions, Tag } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { DICT_TYPE, getDictLabel } from '#/utils/dict'; | ||||||
|  | 
 | ||||||
|  | const formData = ref<SystemSmsLogApi.SmsLog>(); | ||||||
| 
 | 
 | ||||||
| const [Modal, modalApi] = useVbenModal({ | const [Modal, modalApi] = useVbenModal({ | ||||||
|   async onOpenChange(isOpen: boolean) { |   async onOpenChange(isOpen: boolean) { | ||||||
|  | @ -13,7 +18,7 @@ const [Modal, modalApi] = useVbenModal({ | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     // 加载数据 |     // 加载数据 | ||||||
|     const data = modalApi.getData<SystemSmsLogApi.SmsLogVO>(); |     const data = modalApi.getData<SystemSmsLogApi.SmsLog>(); | ||||||
|     if (!data || !data.id) { |     if (!data || !data.id) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  | @ -27,97 +32,78 @@ const [Modal, modalApi] = useVbenModal({ | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <!-- TODO @puhui999:https://ant-design.antgroup.com/components/descriptions-cn 参考这个? --> |  | ||||||
| <template> | <template> | ||||||
|   <Modal title="短信日志详情"> |   <Modal title="短信日志详情"> | ||||||
|     <div class="p-4"> |     <div class="p-4"> | ||||||
|       <div class="grid grid-cols-1 gap-4 md:grid-cols-2"> |       <Descriptions | ||||||
|         <div class="form-item"> |         :column="{ xxl: 3, xl: 3, lg: 3, md: 2, sm: 2, xs: 1 }" | ||||||
|           <div class="form-label">编号:</div> |         bordered | ||||||
|           <div>{{ formData?.id }}</div> |       > | ||||||
|         </div> |         <Descriptions.Item label="创建时间"> | ||||||
|         <div class="form-item"> |           {{ formatDateTime(formData?.createTime || '') }} | ||||||
|           <!-- TODO @puhui:格式不对 --> |         </Descriptions.Item> | ||||||
|           <div class="form-label">创建时间:</div> |         <Descriptions.Item label="手机号"> | ||||||
|           <div>{{ formData?.createTime }}</div> |           {{ formData?.mobile }} | ||||||
|         </div> |         </Descriptions.Item> | ||||||
|         <div class="form-item"> |         <Descriptions.Item label="短信渠道"> | ||||||
|           <div class="form-label">手机号:</div> |           {{ formData?.channelCode }} | ||||||
|           <div>{{ formData?.mobile }}</div> |         </Descriptions.Item> | ||||||
|         </div> |         <Descriptions.Item label="模板编号"> | ||||||
|         <div class="form-item"> |           {{ formData?.templateId }} | ||||||
|           <div class="form-label">短信渠道:</div> |         </Descriptions.Item> | ||||||
|           <div>{{ formData?.channelCode }}</div> |         <Descriptions.Item label="模板类型"> | ||||||
|         </div> |           {{ formData?.templateType }} | ||||||
|         <div class="form-item"> |         </Descriptions.Item> | ||||||
|           <div class="form-label">模板编号:</div> |         <Descriptions.Item label="短信内容" :span="3"> | ||||||
|           <div>{{ formData?.templateId }}</div> |           {{ formData?.templateContent }} | ||||||
|         </div> |         </Descriptions.Item> | ||||||
|         <div class="form-item"> |         <Descriptions.Item label="发送状态"> | ||||||
|           <div class="form-label">模板类型:</div> |           <!-- TODO @芋艿: 数据字典--> | ||||||
|           <div>{{ formData?.templateType }}</div> |           <Tag color="processing"> | ||||||
|         </div> |             {{ | ||||||
|       </div> |               getDictLabel( | ||||||
| 
 |                 DICT_TYPE.SYSTEM_SMS_SEND_STATUS, | ||||||
|       <div class="mt-4"> |                 formData?.sendStatus, | ||||||
|         <div class="form-label">短信内容:</div> |               ) | ||||||
|         <div>{{ formData?.templateContent }}</div> |             }} | ||||||
|       </div> |           </Tag> | ||||||
| 
 |         </Descriptions.Item> | ||||||
|       <div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2"> |         <Descriptions.Item label="发送时间"> | ||||||
|         <div class="form-item"> |           {{ formatDateTime(formData?.sendTime || '') }} | ||||||
|           <!-- TODO @puhui:格式不对 --> |         </Descriptions.Item> | ||||||
|           <div class="form-label">发送状态:</div> |         <Descriptions.Item label="API发送编码"> | ||||||
|           <div>{{ formData?.sendStatus }}</div> |           {{ formData?.apiSendCode }} | ||||||
|         </div> |         </Descriptions.Item> | ||||||
|         <div class="form-item"> |         <Descriptions.Item label="API发送消息" :span="2"> | ||||||
|           <div class="form-label">发送时间:</div> |           {{ formData?.apiSendMsg }} | ||||||
|           <div>{{ formData?.sendTime }}</div> |         </Descriptions.Item> | ||||||
|         </div> |         <Descriptions.Item label="接收状态"> | ||||||
|         <div class="form-item"> |           <!-- TODO @芋艿: 数据字典--> | ||||||
|           <div class="form-label">API发送编码:</div> |           <Tag color="processing"> | ||||||
|           <div>{{ formData?.apiSendCode }}</div> |             {{ | ||||||
|         </div> |               getDictLabel( | ||||||
|         <div class="form-item"> |                 DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS, | ||||||
|           <div class="form-label">API发送消息:</div> |                 formData?.receiveStatus, | ||||||
|           <div>{{ formData?.apiSendMsg }}</div> |               ) | ||||||
|         </div> |             }} | ||||||
|         <div class="form-item"> |           </Tag> | ||||||
|           <!-- TODO @puhui:格式不对 --> |         </Descriptions.Item> | ||||||
|           <div class="form-label">接收状态:</div> |         <Descriptions.Item label="接收时间"> | ||||||
|           <div>{{ formData?.receiveStatus }}</div> |           {{ formatDateTime(formData?.receiveTime || '') }} | ||||||
|         </div> |         </Descriptions.Item> | ||||||
|         <div class="form-item"> |         <Descriptions.Item label="API接收编码"> | ||||||
|           <div class="form-label">接收时间:</div> |           {{ formData?.apiReceiveCode }} | ||||||
|           <div>{{ formData?.receiveTime }}</div> |         </Descriptions.Item> | ||||||
|         </div> |         <Descriptions.Item label="API接收消息" :span="2"> | ||||||
|         <div class="form-item"> |           {{ formData?.apiReceiveMsg }} | ||||||
|           <div class="form-label">API 接收编码:</div> |         </Descriptions.Item> | ||||||
|           <div>{{ formData?.apiReceiveCode }}</div> |         <Descriptions.Item label="API请求ID"> | ||||||
|         </div> |           {{ formData?.apiRequestId }} | ||||||
|         <div class="form-item"> |         </Descriptions.Item> | ||||||
|           <div class="form-label">API 接收消息:</div> |         <Descriptions.Item label="API序列号"> | ||||||
|           <div>{{ formData?.apiReceiveMsg }}</div> |           {{ formData?.apiSerialNo }} | ||||||
|         </div> |         </Descriptions.Item> | ||||||
|         <div class="form-item"> |       </Descriptions> | ||||||
|           <div class="form-label">API 请求 ID:</div> |  | ||||||
|           <div>{{ formData?.apiRequestId }}</div> |  | ||||||
|         </div> |  | ||||||
|         <div class="form-item"> |  | ||||||
|           <div class="form-label">API 序列号:</div> |  | ||||||
|           <div>{{ formData?.apiSerialNo }}</div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </div> |     </div> | ||||||
|   </Modal> |   </Modal> | ||||||
| </template> | </template> | ||||||
| 
 |  | ||||||
| <style scoped> |  | ||||||
| .form-item { |  | ||||||
|   @apply mb-2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .form-label { |  | ||||||
|   @apply font-medium; |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -95,7 +95,7 @@ export function useFormSchema(): VbenFormSchema[] { | ||||||
|       component: 'Textarea', |       component: 'Textarea', | ||||||
|       componentProps: { |       componentProps: { | ||||||
|         placeholder: '请输入备注', |         placeholder: '请输入备注', | ||||||
|       } |       }, | ||||||
|     }, |     }, | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|  | @ -130,7 +130,7 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|       componentProps: { |       componentProps: { | ||||||
|         allowClear: true, |         allowClear: true, | ||||||
|         placeholder: '请输入模板编码', |         placeholder: '请输入模板编码', | ||||||
|       } |       }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       fieldName: 'name', |       fieldName: 'name', | ||||||
|  | @ -139,7 +139,7 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
|       componentProps: { |       componentProps: { | ||||||
|         allowClear: true, |         allowClear: true, | ||||||
|         placeholder: '请输入模板名称', |         placeholder: '请输入模板名称', | ||||||
|       } |       }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       fieldName: 'channelId', |       fieldName: 'channelId', | ||||||
|  | @ -168,6 +168,14 @@ export function useGridFormSchema(): VbenFormSchema[] { | ||||||
| /** 发送短信表单 */ | /** 发送短信表单 */ | ||||||
| export function useSendSmsFormSchema(): VbenFormSchema[] { | export function useSendSmsFormSchema(): VbenFormSchema[] { | ||||||
|   return [ |   return [ | ||||||
|  |     { | ||||||
|  |       fieldName: 'content', | ||||||
|  |       label: '模板内容', | ||||||
|  |       component: 'Textarea', | ||||||
|  |       componentProps: { | ||||||
|  |         disabled: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       fieldName: 'mobile', |       fieldName: 'mobile', | ||||||
|       label: '手机号码', |       label: '手机号码', | ||||||
|  |  | ||||||
|  | @ -1,10 +1,12 @@ | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import type { SystemSmsTemplateApi } from '#/api/system/sms/template'; | import type { SystemSmsTemplateApi } from '#/api/system/sms/template'; | ||||||
| 
 | 
 | ||||||
|  | import { ref } from 'vue'; | ||||||
|  | 
 | ||||||
| import { useVbenModal } from '@vben/common-ui'; | import { useVbenModal } from '@vben/common-ui'; | ||||||
| 
 | 
 | ||||||
| import { ref } from 'vue'; |  | ||||||
| import { message } from 'ant-design-vue'; | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
| import { useVbenForm } from '#/adapter/form'; | import { useVbenForm } from '#/adapter/form'; | ||||||
| import { sendSms } from '#/api/system/sms/template'; | import { sendSms } from '#/api/system/sms/template'; | ||||||
| 
 | 
 | ||||||
|  | @ -13,7 +15,6 @@ import { useSendSmsFormSchema } from '../data'; | ||||||
| const emit = defineEmits(['success']); | const emit = defineEmits(['success']); | ||||||
| const templateData = ref<SystemSmsTemplateApi.SmsTemplate>(); | const templateData = ref<SystemSmsTemplateApi.SmsTemplate>(); | ||||||
| 
 | 
 | ||||||
| // TODO @puhui999:貌似发送短信,少了参数展示 |  | ||||||
| // 动态构建表单 | // 动态构建表单 | ||||||
| const buildSchema = () => { | const buildSchema = () => { | ||||||
|   const schema = useSendSmsFormSchema(); |   const schema = useSendSmsFormSchema(); | ||||||
|  | @ -22,9 +23,12 @@ const buildSchema = () => { | ||||||
|   if (templateData.value?.params && templateData.value.params.length > 0) { |   if (templateData.value?.params && templateData.value.params.length > 0) { | ||||||
|     templateData.value.params.forEach((param) => { |     templateData.value.params.forEach((param) => { | ||||||
|       schema.push({ |       schema.push({ | ||||||
|         component: 'Input', |  | ||||||
|         fieldName: `param_${param}`, |         fieldName: `param_${param}`, | ||||||
|         label: `参数 ${param}`, |         label: `参数 ${param}`, | ||||||
|  |         component: 'Input', | ||||||
|  |         componentProps: { | ||||||
|  |           placeholder: '请输入', | ||||||
|  |         }, | ||||||
|         rules: 'required', |         rules: 'required', | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  | @ -58,7 +62,7 @@ const [Modal, modalApi] = useVbenModal({ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // 构建发送短信请求 |     // 构建发送短信请求 | ||||||
|     const data: SystemSmsTemplateApi.SmsSendReqVO = { |     const data: SystemSmsTemplateApi.SmsSendReq = { | ||||||
|       mobile: values.mobile, |       mobile: values.mobile, | ||||||
|       templateCode: templateData.value?.code || '', |       templateCode: templateData.value?.code || '', | ||||||
|       templateParams: paramsObj, |       templateParams: paramsObj, | ||||||
|  | @ -92,7 +96,12 @@ const [Modal, modalApi] = useVbenModal({ | ||||||
|     templateData.value = data; |     templateData.value = data; | ||||||
|     // 更新表单结构 |     // 更新表单结构 | ||||||
|     const schema = buildSchema(); |     const schema = buildSchema(); | ||||||
|     await formApi.updateSchema(schema); |     formApi.setState({ schema }); | ||||||
|  | 
 | ||||||
|  |     // 设置表单初始值,包括模板内容 | ||||||
|  |     await formApi.setValues({ | ||||||
|  |       content: data.content, | ||||||
|  |     }); | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -11,3 +11,7 @@ export const MdiGithub = createIconifyIcon('mdi:github'); | ||||||
| export const MdiGoogle = createIconifyIcon('mdi:google'); | export const MdiGoogle = createIconifyIcon('mdi:google'); | ||||||
| 
 | 
 | ||||||
| export const MdiQqchat = createIconifyIcon('mdi:qqchat'); | export const MdiQqchat = createIconifyIcon('mdi:qqchat'); | ||||||
|  | 
 | ||||||
|  | export const MdiCheckboxMarkedCircleOutline = createIconifyIcon( | ||||||
|  |   'mdi:checkbox-marked-circle-outline', | ||||||
|  | ); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 芋道源码
						芋道源码