Browse Source

chore: cleanup

dev
刘凯 1 year ago
parent
commit
e019c83643
  1. 11
      src/api/system/area/index.ts
  2. 36
      src/api/system/dict/data.ts
  3. 36
      src/api/system/dict/type.ts
  4. 46
      src/api/system/dict/types.ts
  5. 49
      src/api/system/errorCode/index.ts
  6. 30
      src/api/system/loginLog/index.ts
  7. 31
      src/api/system/mail/account.ts
  8. 11
      src/api/system/mail/log.ts
  9. 54
      src/api/system/mail/template.ts
  10. 42
      src/api/system/notice/index.ts
  11. 32
      src/api/system/notify/message.ts
  12. 63
      src/api/system/notify/template.ts
  13. 51
      src/api/system/oauth2/client.ts
  14. 28
      src/api/system/oauth2/token.ts
  15. 41
      src/api/system/operatelog/index.ts
  16. 42
      src/api/system/permission/index.ts
  17. 58
      src/api/system/post/index.ts
  18. 64
      src/api/system/sensitiveWord/index.ts
  19. 50
      src/api/system/sms/smsChannel/index.ts
  20. 55
      src/api/system/sms/smsLog/index.ts
  21. 94
      src/api/system/sms/smsTemplate/index.ts
  22. 49
      src/api/system/tenantPackage/index.ts
  23. 4
      src/components/DictTag/index.ts
  24. 72
      src/components/DictTag/src/DictTag.vue
  25. 13
      src/components/Table/src/hooks/useRender.ts
  26. 3
      src/enums/cacheEnum.ts
  27. 4
      src/router/guard/permissionGuard.ts
  28. 67
      src/store/modules/dict.ts
  29. 24
      src/store/modules/userMessage.ts
  30. 5
      src/types/store.d.ts
  31. 97
      src/utils/dict.ts
  32. 42
      src/views/system/area/AreaModal.vue
  33. 29
      src/views/system/area/area.data.ts
  34. 53
      src/views/system/area/index.vue
  35. 102
      src/views/system/dict/DictData.vue
  36. 63
      src/views/system/dict/DictDataModal.vue
  37. 58
      src/views/system/dict/DictTypeModal.vue
  38. 206
      src/views/system/dict/dict.data.ts
  39. 89
      src/views/system/dict/dict.type.ts
  40. 90
      src/views/system/dict/index.vue
  41. 58
      src/views/system/errorCode/ErrorCodeModal.vue
  42. 115
      src/views/system/errorCode/errorCode.data.ts
  43. 94
      src/views/system/errorCode/index.vue
  44. 46
      src/views/system/loginlog/index.vue
  45. 83
      src/views/system/loginlog/loginLog.data.ts
  46. 58
      src/views/system/mail/account/AccountModal.vue
  47. 121
      src/views/system/mail/account/account.data.ts
  48. 78
      src/views/system/mail/account/index.vue
  49. 28
      src/views/system/mail/log/MailLogModal.vue
  50. 55
      src/views/system/mail/log/index.vue
  51. 192
      src/views/system/mail/log/mailLog.data.ts
  52. 107
      src/views/system/mail/template/SendMailModal.vue
  53. 58
      src/views/system/mail/template/TemplateModal.vue
  54. 96
      src/views/system/mail/template/index.vue
  55. 220
      src/views/system/mail/template/template.data.ts
  56. 58
      src/views/system/notice/NoticeModal.vue
  57. 78
      src/views/system/notice/index.vue
  58. 93
      src/views/system/notice/notice.data.ts
  59. 27
      src/views/system/notify/components/MessageInfoModal.vue
  60. 96
      src/views/system/notify/components/message.data.ts
  61. 56
      src/views/system/notify/message/index.vue
  62. 204
      src/views/system/notify/message/message.data.ts
  63. 125
      src/views/system/notify/my/index.vue
  64. 112
      src/views/system/notify/my/my.data.ts
  65. 88
      src/views/system/notify/template/SendNotifyModal.vue
  66. 58
      src/views/system/notify/template/TemplateModal.vue
  67. 97
      src/views/system/notify/template/index.vue
  68. 181
      src/views/system/notify/template/template.data.ts
  69. 58
      src/views/system/oauth2/client/ClientModal.vue
  70. 212
      src/views/system/oauth2/client/client.data.ts
  71. 78
      src/views/system/oauth2/client/index.vue
  72. 60
      src/views/system/oauth2/token/index.vue
  73. 69
      src/views/system/oauth2/token/token.data.ts
  74. 25
      src/views/system/operatelog/LogInfoModal.vue
  75. 74
      src/views/system/operatelog/index.vue
  76. 202
      src/views/system/operatelog/operateLog.data.ts
  77. 58
      src/views/system/post/PostModal.vue
  78. 94
      src/views/system/post/index.vue
  79. 112
      src/views/system/post/post.data.ts
  80. 58
      src/views/system/sensitiveWord/SensitiveWordModal.vue
  81. 99
      src/views/system/sensitiveWord/index.vue
  82. 113
      src/views/system/sensitiveWord/sensitiveWord.data.ts
  83. 58
      src/views/system/sms/channel/SmsChannelModal.vue
  84. 79
      src/views/system/sms/channel/index.vue
  85. 138
      src/views/system/sms/channel/smsChannel.data.ts
  86. 47
      src/views/system/sms/log/index.vue
  87. 148
      src/views/system/sms/log/smsLog.data.ts
  88. 88
      src/views/system/sms/template/SendSmsModal.vue
  89. 58
      src/views/system/sms/template/SmsTemplateModal.vue
  90. 107
      src/views/system/sms/template/index.vue
  91. 209
      src/views/system/sms/template/smsTemplate.data.ts
  92. 128
      src/views/system/tenantPackage/TenantPackageModal.vue
  93. 83
      src/views/system/tenantPackage/index.vue
  94. 89
      src/views/system/tenantPackage/tenantPackage.data.ts

11
src/api/system/area/index.ts

@ -1,11 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 获得地区树
export function getAreaTree() {
return defHttp.get({ url: '/system/area/tree' })
}
// 获得 IP 对应的地区名
export function getAreaByIp(ip: string) {
return defHttp.get({ url: `/system/area/get-by-ip?ip=${ip}` })
}

36
src/api/system/dict/data.ts

@ -1,36 +0,0 @@
import type { DictDataExportReqVO, DictDataPageReqVO, DictDataVO } from './types'
import { defHttp } from '@/utils/http/axios'
// 查询字典数据(精简)列表
export function listSimpleDictData() {
return defHttp.get({ url: '/system/dict-data/list-all-simple' })
}
// 查询字典数据列表
export function getDictDataPage(params: DictDataPageReqVO) {
return defHttp.get({ url: '/system/dict-data/page', params })
}
// 查询字典数据详情
export function getDictData(id: number) {
return defHttp.get({ url: `/system/dict-data/get?id=${id}` })
}
// 新增字典数据
export function createDictData(data: DictDataVO) {
return defHttp.post({ url: '/system/dict-data/create', data })
}
// 修改字典数据
export function updateDictData(data: DictDataVO) {
return defHttp.put({ url: '/system/dict-data/update', data })
}
// 删除字典数据
export function deleteDictData(id: number) {
return defHttp.delete({ url: `/system/dict-data/delete?id=${id}` })
}
// 导出字典类型数据
export function exportDictData(params: DictDataExportReqVO) {
return defHttp.get({ url: '/system/dict-data/export', params })
}

36
src/api/system/dict/type.ts

@ -1,36 +0,0 @@
import type { DictTypeExportReqVO, DictTypePageReqVO, DictTypeVO } from './types'
import { defHttp } from '@/utils/http/axios'
// 查询字典(精简)列表
export function listSimpleDictType() {
return defHttp.get({ url: '/system/dict-type/list-all-simple' })
}
// 查询字典列表
export function getDictTypePage(params: DictTypePageReqVO) {
return defHttp.get({ url: '/system/dict-type/page', params })
}
// 查询字典详情
export function getDictType(id: number) {
return defHttp.get({ url: `/system/dict-type/get?id=${id}` })
}
// 新增字典
export function createDictType(data: DictTypeVO) {
return defHttp.post({ url: '/system/dict-type/create', data })
}
// 修改字典
export function updateDictType(data: DictTypeVO) {
return defHttp.put({ url: '/system/dict-type/update', data })
}
// 删除字典
export function deleteDictType(id: number) {
return defHttp.delete({ url: `/system/dict-type/delete?id=${id}` })
}
// 导出字典类型
export function exportDictType(params: DictTypeExportReqVO) {
return defHttp.get({ url: '/system/dict-type/export', params })
}

46
src/api/system/dict/types.ts

@ -1,46 +0,0 @@
export interface DictTypeVO {
id: number
name: string
type: string
status: number
remark: string
createTime: Date
}
export interface DictTypePageReqVO {
name: string
type: string
status: number
createTime: Date[]
}
export interface DictTypeExportReqVO {
name: string
type: string
status: number
createTime: Date[]
}
export interface DictDataVO {
id: number
sort: number
label: string
value: string
dictType: string
status: number
colorType: string
cssClass: string
remark: string
createTime: Date
}
export interface DictDataPageReqVO {
label: string
dictType: string
status: number
}
export interface DictDataExportReqVO {
label: string
dictType: string
status: number
}

49
src/api/system/errorCode/index.ts

@ -1,49 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface ErrorCodeVO {
id: number
type: number
applicationName: string
code: number
message: string
memo: string
createTime: Date
}
export interface ErrorCodePageReqVO extends PageParam {
type?: number
applicationName?: string
code?: number
message?: string
createTime?: Date[]
}
// 查询错误码列表
export function getErrorCodePage(params: ErrorCodePageReqVO) {
return defHttp.get({ url: '/system/error-code/page', params })
}
// 查询错误码详情
export function getErrorCode(id: number) {
return defHttp.get({ url: `/system/error-code/get?id=${id}` })
}
// 新增错误码
export function createErrorCode(data: ErrorCodeVO) {
return defHttp.post({ url: '/system/error-code/create', data })
}
// 修改错误码
export function updateErrorCode(data: ErrorCodeVO) {
return defHttp.put({ url: '/system/error-code/update', data })
}
// 删除错误码
export function deleteErrorCode(id: number) {
return defHttp.delete({ url: `/system/error-code/delete?id=${id}` })
}
// 导出错误码
export function excelErrorCode(params: ErrorCodePageReqVO) {
return defHttp.download({ url: '/system/error-code/export-excel', params }, '错误码.xls')
}

30
src/api/system/loginLog/index.ts

@ -1,30 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface LoginLogVO {
id: number
logType: number
traceId: number
userId: number
userType: number
username: string
status: number
userIp: string
userAgent: string
createTime: Date
}
export interface LoginLogReqVO extends PageParam {
userIp?: string
username?: string
status?: boolean
createTime?: Date[]
}
// 查询登录日志列表
export function getLoginLogPage(params: LoginLogReqVO) {
return defHttp.get({ url: '/system/login-log/page', params })
}
// 导出登录日志
export function exportLoginLog(params: LoginLogReqVO) {
return defHttp.download({ url: '/system/login-log/export', params }, '登录日志.xls')
}

31
src/api/system/mail/account.ts

@ -1,31 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 创建邮箱账号
export function createMailAccount(data) {
return defHttp.post({ url: '/system/mail-account/create', data })
}
// 更新邮箱账号
export function updateMailAccount(data) {
return defHttp.put({ url: '/system/mail-account/update', data })
}
// 删除邮箱账号
export function deleteMailAccount(id: number) {
return defHttp.delete({ url: `/system/mail-account/delete?id=${id}` })
}
// 获得邮箱账号
export function getMailAccount(id: number) {
return defHttp.get({ url: `/system/mail-account/get?id=${id}` })
}
// 获得邮箱账号分页
export function getMailAccountPage(params) {
return defHttp.get({ url: '/system/mail-account/page', params })
}
// 获取邮箱账号的精简信息列表
export function getSimpleMailAccountList() {
return defHttp.get({ url: '/system/mail-account/list-all-simple' })
}

11
src/api/system/mail/log.ts

@ -1,11 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 获得邮件日志
export function getMailLog(id: number) {
return defHttp.get({ url: `/system/mail-log/get?id=${id}` })
}
// 获得邮件日志分页
export function getMailAccountPage(params) {
return defHttp.get({ url: '/system/mail-log/page', params })
}

54
src/api/system/mail/template.ts

@ -1,54 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 创建邮件模版
export function createMailTemplate(data) {
return defHttp.post({ url: '/system/mail-template/create', data })
}
// 更新邮件模版
export function updateMailTemplate(data) {
return defHttp.put({ url: '/system/mail-template/update', data })
}
// 删除邮件模版
export function deleteMailTemplate(id: number) {
return defHttp.delete({ url: `/system/mail-template/delete?id=${id}` })
}
// 获得邮件模版
export function getMailTemplate(id: number) {
return defHttp.get({ url: `/system/mail-template/get?id=${id}` })
}
// 获得邮件模版分页
export function getMailTemplatePage(params) {
return defHttp.get({ url: '/system/mail-template/page', params })
}
// 邮件模板
export interface MailTemplate {
name: string // 标题
code: string // 编码
accountId: number
nickname: string // 发送人
title: string // 标题
content: string // 内容
status: number //
remark?: any // 备注
id: number
params: string[] // 模板里的参数
createTime: number
}
export interface SendMailParams {
mail: string
templateCode: string
templateParams: {
[key: string]: any
}
}
// 发送测试邮件
export function sendMail(data: SendMailParams) {
return defHttp.post({ url: '/system/mail-template/send-mail', data })
}

42
src/api/system/notice/index.ts

@ -1,42 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface NoticeVO {
id: number
title: string
type: number
content: string
status: number
remark: string
creator: string
createTime: Date
}
export interface NoticePageReqVO extends PageParam {
title?: string
status?: number
}
// 查询公告列表
export function getNoticePage(params: NoticePageReqVO) {
return defHttp.get({ url: '/system/notice/page', params })
}
// 查询公告详情
export function getNotice(id: number) {
return defHttp.get({ url: `/system/notice/get?id=${id}` })
}
// 新增公告
export function createNotice(data: NoticeVO) {
return defHttp.post({ url: '/system/notice/create', data })
}
// 修改公告
export function updateNotice(data: NoticeVO) {
return defHttp.put({ url: '/system/notice/update', data })
}
// 删除公告
export function deleteNotice(id: number) {
return defHttp.delete({ url: `/system/notice/delete?id=${id}` })
}

32
src/api/system/notify/message.ts

@ -1,32 +0,0 @@
import qs from 'qs'
import { defHttp } from '@/utils/http/axios'
// 获得站内信分页
export function getNotifyMessagePage(params) {
return defHttp.get({ url: '/system/notify-message/page', params })
}
// 获得我的站内信分页
export function getMyNotifyMessagePage(params) {
return defHttp.get({ url: '/system/notify-message/my-page', params })
}
// 批量标记已读
export function updateNotifyMessageRead(ids: number[]) {
return defHttp.put({ url: `/system/notify-message/update-read?${qs.stringify({ ids }, { indices: false })}` })
}
// 标记所有站内信为已读
export function updateAllNotifyMessageRead() {
return defHttp.put({ url: '/system/notify-message/update-all-read' })
}
// 获取当前用户的最新站内信列表
export function getUnreadNotifyMessageList() {
return defHttp.get({ url: '/system/notify-message/get-unread-list' })
}
// 获得当前用户的未读站内信数量
export function getUnreadNotifyMessageCount() {
return defHttp.get<number>({ url: '/system/notify-message/get-unread-count' })
}

63
src/api/system/notify/template.ts

@ -1,63 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 创建站内信模板
export function createNotifyTemplate(data) {
return defHttp.post({ url: '/system/notify-template/create', data })
}
// 更新站内信模板
export function updateNotifyTemplate(data) {
return defHttp.put({ url: '/system/notify-template/update', data })
}
// 删除站内信模板
export function deleteNotifyTemplate(id: number) {
return defHttp.delete({ url: `/system/notify-template/delete?id=${id}` })
}
// 获得站内信模板
export function getNotifyTemplate(id: number) {
return defHttp.get({ url: `/system/notify-template/get?id=${id}` })
}
// 获得站内信模板分页
export function getNotifyTemplatePage(params) {
return defHttp.get({ url: '/system/notify-template/page', params })
}
// 获取岗位精简信息列表
export function listSimplePosts() {
return defHttp.get({ url: '/system/post/list-all-simple' })
}
// 导出站内信模板 Excel
export function exportNotifyTemplateExcel(params) {
return defHttp.download({ url: '/system/notify-template/export-excel', params }, '导出站内信模板.xls')
}
export interface SendNotifyParam {
userId: number
templateCode: string
templateParams: {
[key: string]: string
}
}
export interface NotifyTemplate {
name: string
code: string
type: number
nickname: string
content: string
status: number
remark?: any
id: number
params: string[]
createTime: number
key: string
}
// 发送
export function sendNotify(data: SendNotifyParam) {
return defHttp.post({ url: '/system/notify-template/send-notify', data })
}

51
src/api/system/oauth2/client.ts

@ -1,51 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface OAuth2ClientVO {
id: number
clientId: string
secret: string
name: string
logo: string
description: string
status: number
accessTokenValiditySeconds: number
refreshTokenValiditySeconds: number
redirectUris: string[]
autoApprove: boolean
authorizedGrantTypes: string[]
scopes: string[]
authorities: string[]
resourceIds: string[]
additionalInformation: string
isAdditionalInformationJson: boolean
createTime: Date
}
export interface OAuth2ClientPageReqVO extends PageParam {
name?: string
status?: number
}
// 查询 OAuth2列表
export function getOAuth2ClientPage(params: OAuth2ClientPageReqVO) {
return defHttp.get({ url: '/system/oauth2-client/page', params })
}
// 查询 OAuth2详情
export function getOAuth2Client(id: number) {
return defHttp.get({ url: `/system/oauth2-client/get?id=${id}` })
}
// 新增 OAuth2
export function createOAuth2Client(data: OAuth2ClientVO) {
return defHttp.post({ url: '/system/oauth2-client/create', data })
}
// 修改 OAuth2
export function updateOAuth2Client(data: OAuth2ClientVO) {
return defHttp.put({ url: '/system/oauth2-client/update', data })
}
// 删除 OAuth2
export function deleteOAuth2Client(id: number) {
return defHttp.delete({ url: `/system/oauth2-client/delete?id=${id}` })
}

28
src/api/system/oauth2/token.ts

@ -1,28 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface OAuth2TokenVO {
id: number
accessToken: string
refreshToken: string
userId: number
userType: number
clientId: string
createTime: Date
expiresTime: Date
}
export interface OAuth2TokenPageReqVO extends PageParam {
userId?: number
userType?: number
clientId?: string
}
// 查询 token列表
export function getAccessTokenPage(params: OAuth2TokenPageReqVO) {
return defHttp.get({ url: '/system/oauth2-token/page', params })
}
// 删除 token
export function deleteAccessToken(accessToken: number) {
return defHttp.delete({ url: `/system/oauth2-token/delete?accessToken=${accessToken}` })
}

41
src/api/system/operatelog/index.ts

@ -1,41 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface OperateLogVO {
id: number
userNickname: string
traceId: string
userId: number
module: string
name: string
type: number
content: string
exts: Map<string, object>
defHttpMethod: string
defHttpUrl: string
userIp: string
userAgent: string
javaMethod: string
javaMethodArgs: string
startTime: Date
duration: number
resultCode: number
resultMsg: string
resultData: string
}
export interface OperateLogPageReqVO extends PageParam {
module?: string
userNickname?: string
type?: number
success?: boolean
startTime?: Date[]
}
// 查询操作日志列表
export function getOperateLogPage(params: OperateLogPageReqVO) {
return defHttp.get({ url: '/system/operate-log/page', params })
}
// 导出操作日志
export function exportOperateLog(params: OperateLogPageReqVO) {
return defHttp.download({ url: '/system/operate-log/export', params }, '操作日志.xls')
}

42
src/api/system/permission/index.ts

@ -1,42 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface PermissionAssignUserRoleReqVO {
userId: number
roleIds: number[]
}
export interface PermissionAssignRoleMenuReqVO {
roleId: number
menuIds: number[]
}
export interface PermissionAssignRoleDataScopeReqVO {
roleId: number
dataScope: number
dataScopeDeptIds: number[]
}
// 查询角色拥有的菜单权限
export function listRoleMenus(roleId: number) {
return defHttp.get({ url: `/system/permission/list-role-menus?roleId=${roleId}` })
}
// 赋予角色菜单权限
export function assignRoleMenu(data: PermissionAssignRoleMenuReqVO) {
return defHttp.post({ url: '/system/permission/assign-role-menu', data })
}
// 赋予角色数据权限
export function assignRoleDataScope(data: PermissionAssignRoleDataScopeReqVO) {
return defHttp.post({ url: '/system/permission/assign-role-data-scope', data })
}
// 查询用户拥有的角色数组
export function listUserRoles(userId: number) {
return defHttp.get({ url: `/system/permission/list-user-roles?userId=${userId}` })
}
// 赋予用户角色
export function assignUserRole(data: PermissionAssignUserRoleReqVO) {
return defHttp.post({ url: '/system/permission/assign-user-role', data })
}

58
src/api/system/post/index.ts

@ -1,58 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface PostVO {
id?: number
name: string
code: string
sort: number
status: number
remark: string
createTime?: Date
}
export interface PostPageReqVO extends PageParam {
code?: string
name?: string
status?: number
}
export interface PostExportReqVO {
code?: string
name?: string
status?: number
}
// 查询岗位列表
export function getPostPage(params: PostPageReqVO) {
return defHttp.get<PageResult<PostVO>>({ url: '/system/post/page', params })
}
// 获取岗位精简信息列表
export function listSimplePosts() {
return defHttp.get({ url: '/system/post/list-all-simple' })
}
// 查询岗位详情
export function getPost(id: number) {
return defHttp.get({ url: `/system/post/get?id=${id}` })
}
// 新增岗位
export function createPost(data) {
return defHttp.post({ url: '/system/post/create', data })
}
// 修改岗位
export function updatePost(data) {
return defHttp.put({ url: '/system/post/update', data })
}
// 删除岗位
export function deletePost(id: number) {
return defHttp.delete({ url: `/system/post/delete?id=${id}` })
}
// 导出岗位
export function exportPost(params: PostExportReqVO) {
return defHttp.download({ url: '/system/post/export', params }, '导出岗位.xls')
}

64
src/api/system/sensitiveWord/index.ts

@ -1,64 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface SensitiveWordVO {
id: number
name: string
status: number
description: string
tags: string[]
createTime: Date
}
export interface SensitiveWordPageReqVO extends PageParam {
name?: string
tag?: string
status?: number
createTime?: Date[]
}
export interface SensitiveWordExportReqVO {
name?: string
tag?: string
status?: number
createTime?: Date[]
}
// 查询敏感词列表
export function getSensitiveWordPage(params: SensitiveWordPageReqVO) {
return defHttp.get({ url: '/system/sensitive-word/page', params })
}
// 查询敏感词详情
export function getSensitiveWord(id: number) {
return defHttp.get({ url: `/system/sensitive-word/get?id=${id}` })
}
// 新增敏感词
export function createSensitiveWord(data: SensitiveWordVO) {
return defHttp.post({ url: '/system/sensitive-word/create', data })
}
// 修改敏感词
export function updateSensitiveWord(data: SensitiveWordVO) {
return defHttp.put({ url: '/system/sensitive-word/update', data })
}
// 删除敏感词
export function deleteSensitiveWord(id: number) {
return defHttp.delete({ url: `/system/sensitive-word/delete?id=${id}` })
}
// 导出敏感词
export function exportSensitiveWord(params: SensitiveWordExportReqVO) {
return defHttp.download({ url: '/system/sensitive-word/export-excel', params }, '导出敏感词.xls')
}
// 获取所有敏感词的标签数组
export function getSensitiveWordTags() {
return defHttp.get({ url: '/system/sensitive-word/get-tags' })
}
// 获得文本所包含的不合法的敏感词数组
export function validateText(id: number) {
return defHttp.get({ url: `/system/sensitive-word/validate-text?${id}` })
}

50
src/api/system/sms/smsChannel/index.ts

@ -1,50 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface SmsChannelVO {
id: number
code: string
status: number
signature: string
remark: string
apiKey: string
apiSecret: string
callbackUrl: string
createTime: Date
}
export interface SmsChannelPageReqVO extends PageParam {
signature?: string
code?: string
status?: number
createTime?: Date[]
}
// 查询短信渠道列表
export function getSmsChannelPage(params: SmsChannelPageReqVO) {
return defHttp.get({ url: '/system/sms-channel/page', params })
}
// 获得短信渠道精简列表
export function getSimpleSmsChannels() {
return defHttp.get({ url: '/system/sms-channel/list-all-simple' })
}
// 查询短信渠道详情
export function getSmsChannel(id: number) {
return defHttp.get({ url: `/system/sms-channel/get?id=${id}` })
}
// 新增短信渠道
export function createSmsChannel(data: SmsChannelVO) {
return defHttp.post({ url: '/system/sms-channel/create', data })
}
// 修改短信渠道
export function updateSmsChannel(data: SmsChannelVO) {
return defHttp.put({ url: '/system/sms-channel/update', data })
}
// 删除短信渠道
export function deleteSmsChannel(id: number) {
return defHttp.delete({ url: `/system/sms-channel/delete?id=${id}` })
}

55
src/api/system/sms/smsLog/index.ts

@ -1,55 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface SmsLogVO {
id: number
channelId: number
channelCode: string
templateId: number
templateCode: string
templateType: number
templateContent: string
templateParams: Map<string, object>
mobile: string
userId: number
userType: number
sendStatus: number
sendTime: Date
apiSendCode: string
apiSendMsg: string
apidefHttpId: string
apiSerialNo: string
receiveStatus: number
receiveTime: Date
apiReceiveCode: string
apiReceiveMsg: string
createTime: Date
}
export interface SmsLogPageReqVO extends PageParam {
channelId?: number
templateId?: number
mobile?: string
sendStatus?: number
sendTime?: Date[]
receiveStatus?: number
receiveTime?: Date[]
}
export interface SmsLogExportReqVO {
channelId?: number
templateId?: number
mobile?: string
sendStatus?: number
sendTime?: Date[]
receiveStatus?: number
receiveTime?: Date[]
}
// 查询短信日志列表
export function getSmsLogPage(params: SmsLogPageReqVO) {
return defHttp.get({ url: '/system/sms-log/page', params })
}
// 导出短信日志
export function exportSmsLog(params: SmsLogExportReqVO) {
return defHttp.download({ url: '/system/sms-log/export', params }, '短信日志.xls')
}

94
src/api/system/sms/smsTemplate/index.ts

@ -1,94 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface SmsTemplateVO {
id: number
type: number
status: number
code: string
name: string
content: string
remark: string
apiTemplateId: string
channelId: number
channelCode: string
params: string[]
createTime: Date
}
export interface SendSmsReqVO {
mobile: string
templateCode: string
templateParams: {
[key: string]: any
}
}
export interface SmsTemplatePageReqVO {
type?: number
status?: number
code?: string
content?: string
apiTemplateId?: string
channelId?: number
createTime?: Date[]
}
export interface SmsTemplateExportReqVO {
type?: number
status?: number
code?: string
content?: string
apiTemplateId?: string
channelId?: number
createTime?: Date[]
}
// 查询短信模板列表
export function getSmsTemplatePage(params: SmsTemplatePageReqVO) {
return defHttp.get({ url: '/system/sms-template/page', params })
}
// 查询短信模板详情
export function getSmsTemplate(id: number) {
return defHttp.get({ url: `/system/sms-template/get?id=${id}` })
}
// 新增短信模板
export function createSmsTemplate(data: SmsTemplateVO) {
return defHttp.post({ url: '/system/sms-template/create', data })
}
// 修改短信模板
export function updateSmsTemplate(data: SmsTemplateVO) {
return defHttp.put({ url: '/system/sms-template/update', data })
}
// 删除短信模板
export function deleteSmsTemplate(id: number) {
return defHttp.delete({ url: `/system/sms-template/delete?id=${id}` })
}
// 邮件模板
export interface SmsTemplate {
name: string // 标题
code: string // 编码
accountId: number
nickname: string // 发送人
title: string // 标题
content: string // 内容
status: number //
remark?: any // 备注
id: number
params: string[] // 模板里的参数
createTime: number
}
// 发送短信
export function sendSms(data: SendSmsReqVO) {
return defHttp.post({ url: '/system/sms-template/send-sms', data })
}
// 导出短信模板
export function exportSmsTemplate(params: SmsTemplateExportReqVO) {
return defHttp.download({ url: '/system/sms-template/export-excel', params }, '短信模板.xls')
}

49
src/api/system/tenantPackage/index.ts

@ -1,49 +0,0 @@
import { defHttp } from '@/utils/http/axios'
export interface TenantPackageVO {
id: number
name: string
status: number
remark: string
creator: string
updater: string
updateTime: string
menuIds: number[]
createTime: Date
}
export interface TenantPackagePageReqVO extends PageParam {
name?: string
status?: number
remark?: string
createTime?: Date[]
}
// 查询租户套餐列表
export function getTenantPackagePage(params: TenantPackagePageReqVO) {
return defHttp.get({ url: '/system/tenant-package/page', params })
}
// 获得租户
export function getTenantPackage(id: number) {
return defHttp.get({ url: `/system/tenant-package/get?id=${id}` })
}
// 新增租户套餐
export function createTenantPackage(data: TenantPackageVO) {
return defHttp.post({ url: '/system/tenant-package/create', data })
}
// 修改租户套餐
export function updateTenantPackage(data: TenantPackageVO) {
return defHttp.put({ url: '/system/tenant-package/update', data })
}
// 删除租户套餐
export function deleteTenantPackage(id: number) {
return defHttp.delete({ url: `/system/tenant-package/delete?id=${id}` })
}
// 获取租户套餐精简信息列表
export function getTenantPackageList() {
return defHttp.get({ url: '/system/tenant-package/get-simple-list' })
}

4
src/components/DictTag/index.ts

@ -1,4 +0,0 @@
import dictTag from './src/DictTag.vue'
import { withInstall } from '@/utils'
export const DictTag = withInstall(dictTag)

72
src/components/DictTag/src/DictTag.vue

@ -1,72 +0,0 @@
<script lang="tsx">
import type { PropType } from 'vue'
import { defineComponent, ref } from 'vue'
import { Tag } from 'ant-design-vue'
import { isHexColor } from '@/utils/color'
import type { DictDataType } from '@/utils/dict'
import { getDictOpts } from '@/utils/dict'
import { propTypes } from '@/utils/propTypes'
export default defineComponent({
name: 'DictTag',
props: {
type: {
type: String as PropType<string>,
required: true,
},
value: propTypes.oneOfType([propTypes.string, propTypes.number, propTypes.bool]),
icon: { type: String },
},
setup(props) {
const dictData = ref<DictDataType>()
const getDictObj = (dictType: string, value: string) => {
let dictOptions: DictDataType[] = []
dictOptions = getDictOpts(dictType)
if (dictOptions && dictOptions.length === 0)
return
dictOptions.forEach((dict: DictDataType) => {
if (dict.value === value) {
if (`${dict.colorType}` === 'primary')
dict.colorType = 'processing'
else if (`${dict.colorType}` === 'danger')
dict.colorType = 'error'
else if (`${dict.colorType}` === 'info')
dict.colorType = 'default'
dictData.value = dict
}
})
}
const rederDictTag = () => {
if (!props.type)
return null
//
if (props.value === undefined || props.value === null)
return null
getDictObj(props.type, props.value.toString())
if (dictData.value === undefined)
return null
// && isHexColor(dictData.value?.cssClass)
return (
<Tag
color={
dictData.value?.colorType
? dictData.value?.colorType
: dictData.value?.cssClass && isHexColor(dictData.value?.cssClass)
? dictData.value?.cssClass
: ''
}
>
{dictData.value?.label || ''}
</Tag>
)
}
return () => rederDictTag()
},
})
</script>

13
src/components/Table/src/hooks/useRender.ts

@ -3,7 +3,6 @@ import dayjs from 'dayjs'
import { Button, Tag } from 'ant-design-vue' import { Button, Tag } from 'ant-design-vue'
import TableImg from '../components/TableImg.vue' import TableImg from '../components/TableImg.vue'
import { isArray, isString } from '@/utils/is' import { isArray, isString } from '@/utils/is'
import { DictTag } from '@/components/DictTag'
import { JsonPreview } from '@/components/CodeEditor' import { JsonPreview } from '@/components/CodeEditor'
export const useRender = { export const useRender = {
@ -87,18 +86,6 @@ export const useRender = {
else else
return dayjs(text).format(format) return dayjs(text).format(format)
}, },
/**
*
* @param text
* @param dictType
* @returns
*/
renderDict: (text: string, dictType: string) => {
if (dictType)
return h(DictTag, { type: dictType, value: text })
return ''
},
/** /**
* icon * icon
* @param text icon * @param text icon

3
src/enums/cacheEnum.ts

@ -16,9 +16,6 @@ export const ROLES_KEY = 'ROLES__KEY__'
// project config key // project config key
export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__' export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'
// dict info key
export const DICT_KEY = 'DICT__KEY__'
// lock info // lock info
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__' export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'

4
src/router/guard/permissionGuard.ts

@ -16,7 +16,6 @@ const LOGIN_PATH = PageEnum.BASE_LOGIN
const whitePathList: PageEnum[] = [LOGIN_PATH] const whitePathList: PageEnum[] = [LOGIN_PATH]
export function createPermissionGuard(router: Router) { export function createPermissionGuard(router: Router) {
// const dictStore = useDictStoreWithOut()
const userStore = useUserStoreWithOut() const userStore = useUserStoreWithOut()
const permissionStore = usePermissionStoreWithOut() const permissionStore = usePermissionStoreWithOut()
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
@ -78,9 +77,6 @@ export function createPermissionGuard(router: Router) {
return return
} }
// if (!dictStore.getIsSetDict)
// await dictStore.setDictMap()
// get userinfo while last fetch time is empty // get userinfo while last fetch time is empty
if (userStore.getLastUpdateTime === 0) { if (userStore.getLastUpdateTime === 0) {
try { try {

67
src/store/modules/dict.ts

@ -1,67 +0,0 @@
import { defineStore } from 'pinia'
import type { DictState } from '@/types/store'
import { store } from '@/store'
import { DICT_KEY } from '@/enums/cacheEnum'
import { createLocalStorage } from '@/utils/cache'
import { listSimpleDictData } from '@/api/system/dict/data'
import type { DictDataVO } from '@/api/system/dict/types'
const ls = createLocalStorage()
export const useDictStore = defineStore({
id: 'app-dict',
state: (): DictState => ({
dictMap: new Map<string, any>(),
isSetDict: false,
}),
getters: {
getDictMap(state): Recordable {
const dictMap = ls.get(DICT_KEY)
if (dictMap)
state.dictMap = dictMap
return state.dictMap
},
getIsSetDict(state): boolean {
return state.isSetDict
},
},
actions: {
async setDictMap() {
const dictMap = ls.get(DICT_KEY)
if (dictMap) {
this.dictMap = dictMap
this.isSetDict = true
}
else {
const res = await listSimpleDictData()
// 设置数据
const dictDataMap = new Map<string, any>()
res.forEach((dictData: DictDataVO) => {
// 获得 dictType 层级
const enumValueObj = dictDataMap[dictData.dictType]
if (!enumValueObj)
dictDataMap[dictData.dictType] = []
// 处理 dictValue 层级
dictDataMap[dictData.dictType].push({
value: dictData.value,
label: dictData.label,
colorType: dictData.colorType,
cssClass: dictData.cssClass,
})
})
this.dictMap = dictDataMap
this.isSetDict = true
ls.set(DICT_KEY, dictDataMap, 60) // 60 秒 过期
}
},
},
})
// Need to be used outside the setup
export function useDictStoreWithOut() {
return useDictStore(store)
}

24
src/store/modules/userMessage.ts

@ -1,24 +0,0 @@
import { defineStore } from 'pinia'
import { getUnreadNotifyMessageCount } from '@/api/system/notify/message'
interface MessageState {
unreadCount: number // 未读消息数量
}
export const useUserMessageStore = defineStore('userMessage', {
state: (): MessageState => ({
unreadCount: 0,
}),
getters: {
getUnreadCount(state) {
return state.unreadCount
},
},
actions: {
// 更新未读消息的数量
async updateUnreadCount() {
const count = await getUnreadNotifyMessageCount()
this.unreadCount = count
},
},
})

5
src/types/store.d.ts vendored

@ -45,8 +45,3 @@ export interface BeforeMiniState {
menuMode?: MenuModeEnum menuMode?: MenuModeEnum
menuType?: MenuTypeEnum menuType?: MenuTypeEnum
} }
export interface DictState {
dictMap: Map<string, any>
isSetDict: boolean
}

97
src/utils/dict.ts

@ -1,97 +0,0 @@
/**
*
*/
import { useDictStoreWithOut } from '@/store/modules/dict'
import type { StringLiteralsToType } from '@/types/utils'
const dictStore = useDictStoreWithOut()
/**
* dictType
*
* @param dictType
* @returns {*|Array}
*/
export interface DictDataType<T extends string | number | boolean = string> {
dictType: string
label: string
value: T
key?: any
colorType: string
cssClass: string
}
export function getDictDatas(dictType: string) {
return dictStore.getDictMap[dictType] || []
}
export function getDictOpts(dictType: string) {
/**
* Tag
* getDictOptions来处理
*
* bugfix:
* dictOption.push({
...dict,
value: parseInt(dict.value + '')
})
*/
return getDictDatas(dictType)
}
export function getDictOptions<T extends 'string' | 'number' | 'boolean' = 'string'>(dictType: string, valueType?: T) {
const dictOption: DictDataType<StringLiteralsToType<T>>[] = []
const dictOptions: DictDataType[] = getDictDatas(dictType)
if (dictOptions && dictOptions.length > 0) {
dictOptions.forEach((dict: DictDataType) => {
dictOption.push({
...dict,
key: dict.value,
value:
valueType === 'string'
? `${dict.value}`
: valueType === 'boolean'
? `${dict.value}` === 'true'
: Number.parseInt(`${dict.value}`),
} as DictDataType<StringLiteralsToType<T>>)
})
}
return dictOption
}
export function getDictObj(dictType: string, value: any) {
const dictOptions: DictDataType[] = getDictDatas(dictType)
if (dictOptions) {
dictOptions.forEach((dict: DictDataType) => {
if (dict.value === value.toString())
return dict
})
}
else {
return null
}
}
export enum DICT_TYPE {
USER_TYPE = 'user_type',
COMMON_STATUS = 'common_status',
SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id',
SYSTEM_USER_SEX = 'system_user_sex',
SYSTEM_MENU_TYPE = 'system_menu_type',
SYSTEM_ROLE_TYPE = 'system_role_type',
SYSTEM_DATA_SCOPE = 'system_data_scope',
SYSTEM_NOTICE_TYPE = 'system_notice_type',
SYSTEM_OPERATE_TYPE = 'system_operate_type',
SYSTEM_LOGIN_TYPE = 'system_login_type',
SYSTEM_LOGIN_RESULT = 'system_login_result',
SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code',
SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type',
SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status',
SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
INFRA_BOOLEAN_STRING = 'infra_boolean_string',
}

42
src/views/system/area/AreaModal.vue

@ -1,42 +0,0 @@
<script lang="ts" setup>
import { formSchema } from './area.data'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { getAreaByIp } from '@/api/system/area'
defineOptions({ name: 'SystemAreaModal' })
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps }] = useModalInner(async () => {
resetFields()
setModalProps({ confirmLoading: false })
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
const res = await getAreaByIp(values.ip)
if (res) {
values.result = res
setFieldsValue({ ...values })
}
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" title="IP 查询" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

29
src/views/system/area/area.data.ts

@ -1,29 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
align: 'left',
},
{
title: '名字',
dataIndex: 'name',
width: 180,
},
]
export const formSchema: FormSchema[] = [
{
label: 'IP',
field: 'ip',
required: true,
component: 'Input',
},
{
label: '地址',
field: 'result',
component: 'Input',
},
]

53
src/views/system/area/index.vue

@ -1,53 +0,0 @@
<script lang="ts" setup>
import AreaModal from './AreaModal.vue'
import { columns } from './area.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, useTable } from '@/components/Table'
import { getAreaTree } from '@/api/system/area'
defineOptions({ name: 'SystemArea' })
const { t } = useI18n()
const [registerModal, { openModal }] = useModal()
const [register, { expandAll, collapseAll, reload }] = useTable({
title: 'IP 地区列表',
api: getAreaTree,
columns,
rowKey: 'id',
isTreeTable: true,
pagination: false,
striped: false,
useSearchForm: false,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
canResize: true,
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
</script>
<template>
<div class="p-4">
<BasicTable @register="register">
<template #toolbar>
<a-button type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
IP 查询
</a-button>
<a-button @click="expandAll">
{{ t('component.tree.expandAll') }}
</a-button>
<a-button @click="collapseAll">
{{ t('component.tree.unExpandAll') }}
</a-button>
</template>
</BasicTable>
<AreaModal @register="registerModal" @success="reload()" />
</div>
</template>

102
src/views/system/dict/DictData.vue

@ -1,102 +0,0 @@
<script lang="ts" setup>
import { watch } from 'vue'
import DictDataModal from './DictDataModal.vue'
import { dataColumns, dataSearchFormSchema } from './dict.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteDictData, getDictDataPage } from '@/api/system/dict/data'
defineOptions({ name: 'SystemDictData' })
const props = defineProps({
searchInfo: {
type: Object as PropType<Recordable>,
default: () => ({}),
},
})
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '字典数据列表',
api: getDictDataPage,
columns: dataColumns,
formConfig: {
labelWidth: 120,
schemas: dataSearchFormSchema,
autoSubmitOnEnter: true,
},
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, {
record: props.searchInfo.dictType,
isUpdate: false,
})
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteDictData(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
watch(
() => props.searchInfo,
(val) => {
val && reload()
},
{ deep: true },
)
</script>
<template>
<div>
<BasicTable :search-info="searchInfo" @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:dict:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:dict:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:dict:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<DictDataModal @register="registerModal" @success="reload()" />
</div>
</template>

63
src/views/system/dict/DictDataModal.vue

@ -1,63 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { dataFormSchema } from './dict.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createDictData, getDictData, updateDictData } from '@/api/system/dict/data'
defineOptions({ name: 'SystemDictDataModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: dataFormSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getDictData(data.record.id)
setFieldsValue({ ...res })
}
else {
setFieldsValue({
dictType: data.record,
})
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateDictData(values)
else
await createDictData(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

58
src/views/system/dict/DictTypeModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { typeFormSchema } from './dict.type'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createDictType, getDictType, updateDictType } from '@/api/system/dict/type'
defineOptions({ name: 'SystemDictTypeModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: typeFormSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getDictType(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateDictType(values)
else
await createDictType(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

206
src/views/system/dict/dict.data.ts

@ -1,206 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
const options = [
{
value: '',
label: '无',
},
{
value: 'processing',
label: '主要',
},
{
value: 'success',
label: '成功',
},
{
value: 'default',
label: '默认',
},
{
value: 'warning',
label: '警告',
},
{
value: 'error',
label: '危险',
},
{
value: 'pink',
label: 'pink',
},
{
value: 'red',
label: 'red',
},
{
value: 'orange',
label: 'orange',
},
{
value: 'green',
label: 'green',
},
{
value: 'cyan',
label: 'cyan',
},
{
value: 'blue',
label: 'blue',
},
{
value: 'purple',
label: 'purple',
},
]
function previewOptions() {
return options.map((option) => {
const { value, label } = option
if (value === '')
return option
return {
label: useRender.renderTag(label, value),
value,
}
})
}
export const dataColumns: BasicColumn[] = [
{
title: '字典编码',
dataIndex: 'id',
width: 100,
},
{
title: '字典标签',
dataIndex: 'label',
width: 180,
},
{
title: '字典键值',
dataIndex: 'value',
width: 100,
},
{
title: '字典排序',
dataIndex: 'sort',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '颜色类型',
dataIndex: 'colorType',
width: 180,
},
{
title: 'CSS Class',
dataIndex: 'cssClass',
width: 180,
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const dataSearchFormSchema: FormSchema[] = [
{
label: '字典标签',
field: 'label',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
]
export const dataFormSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '字典类型',
field: 'dictType',
required: true,
component: 'Input',
dynamicDisabled: ({ values }) => !!values.dictType,
},
{
label: '数据标签',
field: 'label',
required: true,
component: 'Input',
},
{
label: '数据键值',
field: 'value',
required: true,
component: 'Input',
},
{
label: '显示排序',
field: 'sort',
required: true,
defaultValue: 0,
component: 'InputNumber',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '颜色类型',
field: 'colorType',
component: 'Select',
componentProps: {
options: previewOptions(),
},
},
{
label: 'CSS Class',
field: 'cssClass',
component: 'Input',
helpMessage: '输入hex模式的颜色, 例如#108ee9',
rules: [{ required: false, message: '输入正确的16进制颜色', pattern: /^#([0-9a-fA-F]{3}){1,2}$/, trigger: 'blur' }],
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]

89
src/views/system/dict/dict.type.ts

@ -1,89 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const typeColumns: BasicColumn[] = [
{
title: '字典编号',
dataIndex: 'id',
width: 100,
},
{
title: '字典名称',
dataIndex: 'name',
width: 180,
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const typeSearchFormSchema: FormSchema[] = [
{
label: '字典名称',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
]
export const typeFormSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '字典名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '字典类型',
field: 'type',
required: true,
component: 'Input',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]

90
src/views/system/dict/index.vue

@ -1,90 +0,0 @@
<script lang="ts" setup>
import { reactive } from 'vue'
import DictData from './DictData.vue'
import DictTypeModal from './DictTypeModal.vue'
import { typeColumns, typeSearchFormSchema } from './dict.type'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteDictType, getDictTypePage } from '@/api/system/dict/type'
defineOptions({ name: 'SystemDict' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const searchInfo = reactive<Recordable>({})
const [registerTable, { reload }] = useTable({
title: '字典分类列表',
api: getDictTypePage,
columns: typeColumns,
formConfig: {
labelWidth: 120,
schemas: typeSearchFormSchema,
},
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleRowClick(record) {
searchInfo.dictType = record.type
}
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteDictType(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div class="flex">
<BasicTable class="w-1/2" @register="registerTable" @row-click="handleRowClick">
<template #toolbar>
<a-button v-auth="['system:dict:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:dict:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:dict:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<DictData class="w-1/2" :search-info="searchInfo" />
<DictTypeModal @register="registerModal" @success="reload()" />
</div>
</template>

58
src/views/system/errorCode/ErrorCodeModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './errorCode.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createErrorCode, getErrorCode, updateErrorCode } from '@/api/system/errorCode'
defineOptions({ name: 'SystemErrorCodeModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getErrorCode(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateErrorCode(values)
else
await createErrorCode(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

115
src/views/system/errorCode/errorCode.data.ts

@ -1,115 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '类型',
dataIndex: 'type',
width: 80,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_ERROR_CODE_TYPE)
},
},
{
title: '应用名',
dataIndex: 'applicationName',
width: 200,
},
{
title: '错误码编码',
dataIndex: 'code',
width: 120,
},
{
title: '错误码提示',
dataIndex: 'message',
width: 300,
},
{
title: '备注',
dataIndex: 'memo',
width: 200,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '错误码类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_ERROR_CODE_TYPE),
},
colProps: { span: 8 },
},
{
label: '应用名',
field: 'applicationName',
component: 'Input',
colProps: { span: 8 },
},
{
label: '错误码编码',
field: 'code',
component: 'Input',
colProps: { span: 8 },
},
{
label: '错误码提示',
field: 'message',
component: 'Input',
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '应用名',
field: 'applicationName',
required: true,
component: 'Input',
},
{
label: '错误码编码',
field: 'code',
required: true,
component: 'Input',
},
{
label: '错误码提示',
field: 'message',
required: true,
component: 'Input',
},
{
label: '备注',
field: 'memo',
component: 'InputTextArea',
},
]

94
src/views/system/errorCode/index.vue

@ -1,94 +0,0 @@
<script lang="ts" setup>
import ErrorCodeModal from './ErrorCodeModal.vue'
import { columns, searchFormSchema } from './errorCode.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import type { ErrorCodePageReqVO } from '@/api/system/errorCode'
import { deleteErrorCode, excelErrorCode, getErrorCodePage } from '@/api/system/errorCode'
defineOptions({ name: 'SystemErrorCode' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '错误码列表',
api: getErrorCodePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await excelErrorCode(getForm().getFieldsValue() as ErrorCodePageReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await deleteErrorCode(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:error-code:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['system:error-code:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:error-code:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:error-code:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<ErrorCodeModal @register="registerModal" @success="reload()" />
</div>
</template>

46
src/views/system/loginlog/index.vue

@ -1,46 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './loginLog.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicTable, useTable } from '@/components/Table'
import type { LoginLogReqVO } from '@/api/system/loginLog'
import { exportLoginLog, getLoginLogPage } from '@/api/system/loginLog'
defineOptions({ name: 'SystemLoginLog' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerTable, { getForm }] = useTable({
title: '登录日志列表',
api: getLoginLogPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
})
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportLoginLog(getForm().getFieldsValue() as LoginLogReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:login-log:export']" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
</BasicTable>
</div>
</template>

83
src/views/system/loginlog/loginLog.data.ts

@ -1,83 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '访问编号',
dataIndex: 'id',
width: 100,
},
{
title: '日志类型',
dataIndex: 'logType',
width: 120,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_LOGIN_TYPE)
},
},
{
title: '用户名称',
dataIndex: 'username',
width: 120,
},
{
title: '登录地址',
dataIndex: 'userIp',
width: 120,
},
{
title: 'userAgent',
dataIndex: 'userAgent',
width: 400,
},
{
title: '结果',
dataIndex: 'result',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_LOGIN_RESULT)
},
},
{
title: '登录日期',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '登录地址',
field: 'userIp',
component: 'Input',
colProps: { span: 8 },
},
{
label: '用户名称',
field: 'username',
component: 'Input',
colProps: { span: 8 },
},
{
label: '结果',
field: 'result',
component: 'Select',
componentProps: {
options: [
{ label: '成功', value: 'true', key: 'true' },
{ label: '失败', value: 'false', key: 'false' },
],
},
colProps: { span: 8 },
},
{
label: '登录时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]

58
src/views/system/mail/account/AccountModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './account.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createMailAccount, getMailAccount, updateMailAccount } from '@/api/system/mail/account'
defineOptions({ name: 'SystemMailAccountModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getMailAccount(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateMailAccount(values)
else
await createMailAccount(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

121
src/views/system/mail/account/account.data.ts

@ -1,121 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '邮箱',
dataIndex: 'mail',
width: 180,
},
{
title: '用户名',
dataIndex: 'username',
width: 100,
},
{
title: 'SMTP 服务器域名',
dataIndex: 'host',
width: 120,
},
{
title: 'SMTP 服务器端口',
dataIndex: 'port',
width: 120,
},
{
title: '是否开启 SSL',
dataIndex: 'sslEnable',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '邮箱',
field: 'mail',
component: 'Input',
colProps: { span: 8 },
},
{
label: '用户名',
field: 'username',
component: 'Input',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '邮箱',
field: 'mail',
required: true,
component: 'Input',
helpMessage: '填写发件邮箱地址',
rules: [
{
required: true,
message: '请输入正确的邮箱地址',
pattern: /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/,
trigger: 'blur',
},
],
},
{
label: '用户名',
field: 'username',
required: true,
component: 'Input',
},
{
label: '密码/授权码',
field: 'password',
required: true,
component: 'InputPassword',
helpMessage: '填写邮件密码, 部分邮件商需要填写授权码',
},
{
label: 'SMTP 服务器域名',
field: 'host',
required: true,
component: 'Input',
},
{
label: 'SMTP 服务器端口',
field: 'port',
required: true,
component: 'InputNumber',
},
{
label: '是否开启 SSL',
field: 'sslEnable',
required: true,
defaultValue: false,
component: 'RadioButtonGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'),
},
},
]

78
src/views/system/mail/account/index.vue

@ -1,78 +0,0 @@
<script lang="ts" setup>
import AccountModal from './AccountModal.vue'
import { columns, searchFormSchema } from './account.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteMailAccount, getMailAccountPage } from '@/api/system/mail/account'
defineOptions({ name: 'SystemMailAccount' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '邮件列表',
api: getMailAccountPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteMailAccount(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:mail-account:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:mail-account:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:mail-account:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<AccountModal @register="registerModal" @success="reload()" />
</div>
</template>

28
src/views/system/mail/log/MailLogModal.vue

@ -1,28 +0,0 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { logSchema } from './mailLog.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import { Description, useDescription } from '@/components/Description/index'
defineOptions({ name: 'MailLogModal' })
const logData = ref()
const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => {
logData.value = record
})
const [registerDescription] = useDescription({
column: 1,
schema: logSchema,
data: logData,
labelStyle: {
width: '100px',
},
})
</script>
<template>
<BasicModal v-bind="$attrs" title="发送邮件详情" width="800px" @register="registerModalInner" @ok="closeModal">
<Description @register="registerDescription" />
</BasicModal>
</template>

55
src/views/system/mail/log/index.vue

@ -1,55 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './mailLog.data'
import MailLogModal from './MailLogModal.vue'
import { useI18n } from '@/hooks/web/useI18n'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { getMailAccountPage } from '@/api/system/mail/log'
import { useModal } from '@/components/Modal'
defineOptions({ name: 'SystemOperateLog' })
const { t } = useI18n()
const [registerTable] = useTable({
title: '邮件日志列表',
api: getMailAccountPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
const [registerModal, { openModal }] = useModal()
function handleShowInfo(record: Recordable) {
openModal(true, record)
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.VIEW,
label: t('action.detail'),
onClick: handleShowInfo.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
<MailLogModal @register="registerModal" />
</div>
</template>

192
src/views/system/mail/log/mailLog.data.ts

@ -1,192 +0,0 @@
import { h } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { getSimpleMailAccountList } from '@/api/system/mail/account'
import type { DescItem } from '@/components/Description/index'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '接收邮箱',
dataIndex: 'toMail',
width: 200,
},
{
title: '邮件标题',
dataIndex: 'templateTitle',
width: 180,
},
{
title: '发送状态',
dataIndex: 'sendStatus',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_MAIL_SEND_STATUS)
},
},
{
title: '邮箱账号',
dataIndex: 'fromMail',
width: 180,
},
{
title: '模板编号',
dataIndex: 'templateId',
width: 100,
},
{
title: '发送时间',
dataIndex: 'sendTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '接收邮箱',
field: 'toMail',
component: 'Input',
colProps: { span: 8 },
},
{
label: '邮箱账号',
field: 'accountId',
component: 'ApiSelect',
componentProps: {
api: () => getSimpleMailAccountList(),
labelField: 'mail',
valueField: 'id',
},
colProps: { span: 8 },
},
{
label: '模板编号',
field: 'templateId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '发送状态',
field: 'sendStatus',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS),
},
colProps: { span: 8 },
},
{
label: '用户编号',
field: 'userId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '用户类型',
field: 'userType',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.USER_TYPE),
},
colProps: { span: 8 },
},
{
label: '发送时间',
field: 'sendTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const logSchema: DescItem[] = [
{
field: 'sendStatus',
label: '发送状态',
labelMinWidth: 80,
render(value) {
return useRender.renderDict(value, DICT_TYPE.SYSTEM_MAIL_SEND_STATUS)
},
},
{
field: 'sendException',
label: '异常信息',
labelMinWidth: 80,
show: data => data && data.sendException && data.sendException.length > 0,
render(value) {
return h('span', { style: { fontWeight: 'bold' } }, value)
},
},
{
field: 'sendTime',
label: '发送时间',
render(value) {
return useRender.renderDate(value)
},
},
{
field: 'userId',
label: '用户类型',
render(_, data) {
const { userId, userType } = data
const uidTag = useRender.renderTag(`uid: ${userId}`)
const typeTag = useRender.renderDict(userType, DICT_TYPE.USER_TYPE)
return h('span', {}, [typeTag, uidTag])
},
},
{
field: 'fromMail',
label: '发件邮箱',
},
{
field: 'toMail',
label: '收件邮箱',
},
{
field: 'templateNickname',
label: '发件昵称',
},
{
field: 'templateTitle',
label: '邮件标题',
},
{
field: 'templateContent',
label: '邮件内容',
render(value) {
return h('div', { innerHTML: value })
},
},
{
field: 'templateParams',
label: '邮件参数',
render(value) {
return useRender.renderJsonPreview(value)
},
},
{
field: 'sendMessageId',
label: '返回ID',
},
{
field: 'templateCode',
label: '模板编码',
},
{
field: 'templateId',
label: '模板编号',
},
{
field: 'createTime',
label: '记录时间',
render(value) {
return useRender.renderDate(value)
},
},
]

107
src/views/system/mail/template/SendMailModal.vue

@ -1,107 +0,0 @@
<script lang="ts" setup>
import { baseSendSchemas, keyPrefix } from './template.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import type { FormSchema } from '@/components/Form'
import { BasicForm, useForm } from '@/components/Form'
import { sendMail } from '@/api/system/mail/template'
import type { MailTemplate } from '@/api/system/mail/template'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SendMailModal' })
const [register, { setFieldsValue, getFieldsValue, validateFields, resetFields, clearValidate, appendSchemaByField, removeSchemaByField }]
= useForm({
labelWidth: 120,
schemas: baseSendSchemas,
baseColProps: {
span: 24,
},
showActionButtonGroup: false,
})
//
let dyFields: string[] = []
const [innerRegister, { changeLoading, changeOkLoading, closeModal }] = useModalInner(async (data: MailTemplate) => {
//
await resetForm()
const dyschemas: FormSchema[] = []
data.params.forEach((item) => {
// content/mail
const field = keyPrefix + item
const dySchema: FormSchema = {
field,
label: `参数{${item}} `,
component: 'Input',
componentProps: {
placeholder: `输入{${item}}`,
},
required: true,
}
dyschemas.push(dySchema)
dyFields.push(field)
})
setFieldsValue(data)
//
appendSchemaByField(dyschemas, undefined)
})
function modalLoading(status: boolean) {
changeOkLoading(status)
changeLoading(status)
}
/**
* 移除动态生成的表单元素
*/
async function removeDySchemas() {
await removeSchemaByField(dyFields)
dyFields = []
}
const { createMessage } = useMessage()
async function submit() {
try {
modalLoading(true)
await validateFields()
const fields = getFieldsValue()
const data = {
mail: fields.mail,
templateCode: fields.code,
templateParams: {},
}
Object.keys(fields).forEach((key) => {
//
const fixedKeys = ['mail', 'code', 'content']
if (fixedKeys.includes(key))
return
// key
const realKey = key.split(keyPrefix)[1]
data.templateParams[realKey] = fields[key]
})
await sendMail(data)
createMessage.success(`发送邮件到[${fields.mail}]成功`)
closeModal()
}
catch (e) {
}
finally {
modalLoading(false)
}
}
async function resetForm() {
//
await removeDySchemas()
//
await resetFields()
await clearValidate()
}
</script>
<template>
<BasicModal v-bind="$attrs" title="发送邮件" width="600px" @register="innerRegister" @ok="submit" @cancel="resetForm">
<BasicForm @register="register" />
</BasicModal>
</template>

58
src/views/system/mail/template/TemplateModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './template.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createMailTemplate, getMailTemplate, updateMailTemplate } from '@/api/system/mail/template'
defineOptions({ name: 'SystemMailTemplateModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 100,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getMailTemplate(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateMailTemplate(values)
else
await createMailTemplate(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

96
src/views/system/mail/template/index.vue

@ -1,96 +0,0 @@
<script lang="ts" setup>
import TemplateModal from './TemplateModal.vue'
import SendMailModal from './SendMailModal.vue'
import { columns, searchFormSchema } from './template.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteMailTemplate, getMailTemplatePage } from '@/api/system/mail/template'
defineOptions({ name: 'SystemMailTemplate' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerTemplateModal, { openModal }] = useModal()
const [registerSendModal, { openModal: openSendModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '邮件模板列表',
api: getMailTemplatePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 200,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleSend(record: Recordable) {
openSendModal(true, record)
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteMailTemplate(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:mail-template:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.SEND,
label: t('action.send'),
auth: 'system:mail-template:send-mail',
onClick: handleSend.bind(null, record),
},
{
icon: IconEnum.EDIT,
label: t('action.edit'),
auth: 'system:mail-template:update',
onClick: handleEdit.bind(null, record),
},
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:mail-template:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<TemplateModal @register="registerTemplateModal" @success="reload()" />
<SendMailModal @register="registerSendModal" />
</div>
</template>

220
src/views/system/mail/template/template.data.ts

@ -1,220 +0,0 @@
import { h } from 'vue'
import { getSimpleMailAccountList } from '@/api/system/mail/account'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { ScrollContainer } from '@/components/Container'
export const columns: BasicColumn[] = [
{
title: '模板编码',
dataIndex: 'code',
width: 100,
},
{
title: '模板名称',
dataIndex: 'name',
width: 180,
},
{
title: '模板标题',
dataIndex: 'title',
width: 100,
},
{
title: '模板内容',
dataIndex: 'content',
width: 300,
},
{
title: '邮箱账号',
dataIndex: 'accountId',
width: 120,
},
{
title: '发送人名称',
dataIndex: 'nickname',
width: 100,
},
{
title: '开启状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '模板名称',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '模板编码',
field: 'code',
component: 'Input',
colProps: { span: 8 },
},
{
label: '发件邮箱',
field: 'accountId',
component: 'ApiSelect',
componentProps: {
api: () => getSimpleMailAccountList(),
fieldNames: {
label: 'mail',
key: 'id',
value: 'id',
},
},
colProps: { span: 8 },
},
{
label: '开启状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '模板名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '模板编码',
field: 'code',
required: true,
component: 'Input',
helpMessage: '建议使用下划线/数字/字母命名',
},
{
label: '发件邮箱',
field: 'accountId',
required: true,
component: 'ApiSelect',
componentProps: {
api: () => getSimpleMailAccountList(),
fieldNames: {
label: 'mail',
key: 'id',
value: 'id',
},
},
},
{
label: '发送人名称',
field: 'nickname',
required: true,
component: 'Input',
helpMessage: '发件人的名称, 如:系统发件人',
},
{
label: '模板标题',
field: 'title',
required: true,
component: 'Input',
helpMessage: '邮件的标题',
},
{
label: '模板内容',
field: 'content',
component: 'Editor',
required: true,
helpMessage: '{}括号中的内容作为模板参数',
},
{
label: '开启状态',
field: 'status',
component: 'Select',
defaultValue: 0,
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]
// 发送邮件
// 这里加上前缀 防止和表单其他字段重名
export const keyPrefix = 'key$-'
export const baseSendSchemas: FormSchema[] = [
{
field: 'code',
label: '编码',
component: 'Input',
show: () => false,
},
{
field: 'content',
component: 'Editor',
label: '模板内容 ',
required: false,
defaultValue: '',
render({ model }) {
let content: string = model.content
Object.keys(model).forEach((key) => {
if (!key.startsWith(keyPrefix))
return
const realKey = key.split(keyPrefix)[1]
content = content.replace(`{${realKey}}`, model[key])
})
return h(ScrollContainer, {
innerHTML: content,
style: { border: '1px solid #e8e8e8', borderRadius: '6px', padding: '10px' },
})
},
},
{
field: 'mail',
label: '收件邮箱 ',
component: 'Input',
componentProps: {
placeholder: '输入收件邮箱',
},
required: true,
rules: [
{
required: true,
pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
trigger: 'blur',
message: '邮箱格式不正确',
},
],
},
]

58
src/views/system/notice/NoticeModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './notice.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createNotice, getNotice, updateNotice } from '@/api/system/notice'
defineOptions({ name: 'SystemNoticeModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getNotice(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateNotice(values)
else
await createNotice(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

78
src/views/system/notice/index.vue

@ -1,78 +0,0 @@
<script lang="ts" setup>
import NoticeModal from './NoticeModal.vue'
import { columns, searchFormSchema } from './notice.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteNotice, getNoticePage } from '@/api/system/notice'
defineOptions({ name: 'SystemNotice' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '公告列表',
api: getNoticePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteNotice(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:notice:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:notice:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:notice:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<NoticeModal @register="registerModal" @success="reload()" />
</div>
</template>

93
src/views/system/notice/notice.data.ts

@ -1,93 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '公告编号',
dataIndex: 'id',
width: 100,
},
{
title: '公告标题',
dataIndex: 'title',
width: 180,
},
{
title: '公告类型',
dataIndex: 'type',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_NOTICE_TYPE)
},
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '公告标题',
field: 'title',
component: 'Input',
colProps: { span: 8 },
},
{
label: '公告状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '公告标题',
field: 'title',
required: true,
component: 'Input',
},
{
label: '公告类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_NOTICE_TYPE),
},
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '内容',
field: 'content',
component: 'Editor',
},
]

27
src/views/system/notify/components/MessageInfoModal.vue

@ -1,27 +0,0 @@
<script lang="ts" setup>
import { ref } from 'vue'
import type { MessageInfo } from './message.data'
import { infoSchema } from './message.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import { Description, useDescription } from '@/components/Description/index'
defineOptions({ name: 'MessageInfoModal' })
const data = ref<MessageInfo>()
const [innerRegister] = useModalInner((value: MessageInfo) => {
data.value = value
})
const [descriptionRegister] = useDescription({
column: 1,
schema: infoSchema,
data,
})
</script>
<template>
<BasicModal title="站内信详情" @register="innerRegister">
<Description @register="descriptionRegister" />
</BasicModal>
</template>

96
src/views/system/notify/components/message.data.ts

@ -1,96 +0,0 @@
import { h } from 'vue'
import { useRender } from '@/components/Table'
import { DICT_TYPE } from '@/utils/dict'
import { JsonPreview } from '@/components/CodeEditor'
import type { DescItem } from '@/components/Description/index'
// 站内信详情modal
export const infoSchema: DescItem[] = [
{
field: 'id',
label: '编号',
labelMinWidth: 50,
},
{
field: 'readStatus',
label: '是否已读',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.INFRA_BOOLEAN_STRING)
},
},
{
field: 'userType',
label: '用户类型',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.USER_TYPE)
},
},
{
field: 'userType',
label: '用户编号',
},
{
field: 'templateId',
label: '模板编号',
},
{
field: 'templateCode',
label: '模板编码',
},
{
field: 'templateNickname',
label: '发送人名称',
},
{
field: 'templateContent',
label: '模板内容',
},
{
field: 'templateParams',
label: '模板参数',
render: (value) => {
return h(JsonPreview, { data: value })
},
},
{
field: 'templateType',
label: '模板类型',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
},
},
{
field: 'readTime',
label: '阅读时间',
render: (value) => {
if (!value)
return useRender.renderTag('未阅读')
return useRender.renderDate(value)
},
},
{
field: 'createTime',
label: '创建时间',
render: (value) => {
return useRender.renderDate(value)
},
},
]
// 站内信详情
export interface MessageInfo {
userId: number
userType: number
templateId: number
templateCode: string
templateNickname: string
templateContent: string
templateType: number
templateParams: { [key: string]: string }
readStatus: boolean
readTime?: any
id: number
createTime: number
key: string
}

56
src/views/system/notify/message/index.vue

@ -1,56 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './message.data'
import { IconEnum } from '@/enums/appEnum'
import { useI18n } from '@/hooks/web/useI18n'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { getNotifyMessagePage } from '@/api/system/notify/message'
import MessageInfoModal from '@/views/system/notify/components/MessageInfoModal.vue'
import { useModal } from '@/components/Modal'
defineOptions({ name: 'SystemMessage' })
const { t } = useI18n()
const [registerTable] = useTable({
title: '站内信记录列表',
api: getNotifyMessagePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 100,
title: t('common.action'),
fixed: 'right',
key: 'action',
},
})
const [registerModal, { openModal }] = useModal()
function handleShowInfo(record: Recordable) {
openModal(true, record)
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: t('action.detail'),
icon: IconEnum.LOG,
onClick: handleShowInfo.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
<MessageInfoModal @register="registerModal" />
</div>
</template>

204
src/views/system/notify/message/message.data.ts

@ -1,204 +0,0 @@
import { h } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { JsonPreview } from '@/components/CodeEditor'
import type { DescItem } from '@/components/Description/index'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '用户编号',
dataIndex: 'userId',
width: 100,
},
{
title: '用户类型',
dataIndex: 'userType',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.USER_TYPE)
},
},
{
title: '模板编码',
dataIndex: 'templateCode',
width: 100,
},
{
title: '发送人名称',
dataIndex: 'templateNickname',
width: 120,
},
{
title: '模版内容',
dataIndex: 'templateContent',
width: 240,
},
{
title: '模版类型',
dataIndex: 'templateType',
width: 140,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
},
},
{
title: '是否已读',
dataIndex: 'readStatus',
width: 140,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING)
},
},
{
title: '阅读时间',
dataIndex: 'readTime',
width: 180,
customRender: ({ text }) => {
if (!text)
return useRender.renderTag('未阅读')
return useRender.renderDate(text)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '用户编号',
field: 'userId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '用户类型',
field: 'userType',
component: 'Input',
colProps: { span: 8 },
},
{
label: '模板编码',
field: 'templateCode',
component: 'Input',
colProps: { span: 8 },
},
{
label: '模版类型',
field: 'templateType',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
// 站内信详情modal
export const infoSchema: DescItem[] = [
{
field: 'id',
label: '编号',
labelMinWidth: 50,
},
{
field: 'readStatus',
label: '是否已读',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.INFRA_BOOLEAN_STRING)
},
},
{
field: 'userType',
label: '用户类型',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.USER_TYPE)
},
},
{
field: 'userType',
label: '用户编号',
},
{
field: 'templateId',
label: '模板编号',
},
{
field: 'templateCode',
label: '模板编码',
},
{
field: 'templateNickname',
label: '发送人名称',
},
{
field: 'templateContent',
label: '模板内容',
},
{
field: 'templateParams',
label: '模板参数',
render: (value) => {
return h(JsonPreview, { data: value })
},
},
{
field: 'templateType',
label: '模板类型',
render: (value) => {
return useRender.renderDict(value, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
},
},
{
field: 'readTime',
label: '阅读时间',
render: (value) => {
if (!value)
return useRender.renderTag('未阅读')
return useRender.renderDate(value)
},
},
{
field: 'createTime',
label: '创建时间',
render: (value) => {
return useRender.renderDate(value)
},
},
]
// 站内信详情
export interface MessageInfo {
userId: number
userType: number
templateId: number
templateCode: string
templateNickname: string
templateContent: string
templateType: number
templateParams: { [key: string]: string }
readStatus: boolean
readTime?: any
id: number
createTime: number
key: string
}

125
src/views/system/notify/my/index.vue

@ -1,125 +0,0 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { columns, searchFormSchema } from './my.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { getMyNotifyMessagePage, updateAllNotifyMessageRead, updateNotifyMessageRead } from '@/api/system/notify/message'
import MessageInfoModal from '@/views/system/notify/components/MessageInfoModal.vue'
import { useModal } from '@/components/Modal'
import { useUserMessageStore } from '@/store/modules/userMessage'
defineOptions({ name: 'SystemMyMessage' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const store = useUserMessageStore()
const [registerTable, { getSelectRowKeys, clearSelectedRowKeys, reload }] = useTable({
title: '我的站内信列表',
api: getMyNotifyMessagePage,
columns,
formConfig: { labelWidth: 130, schemas: searchFormSchema },
rowSelection: {
type: 'checkbox',
getCheckboxProps: (record: Recordable) => {
return {
// disabled
disabled: record.readStatus,
}
},
},
rowKey: 'id',
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
/**
* 已读按钮的disabled 未选中则disabled
*/
const readedDisabled = computed<boolean>(() => {
return getSelectRowKeys().length === 0
})
function handleUpdateList() {
const ids = getSelectRowKeys()
handleUpdate(ids)
}
async function handleUpdateSingle(record: Recordable) {
await handleUpdate([record.id])
}
function afterRead(msg: string) {
createMessage.success(msg)
//
store.updateUnreadCount()
//
reload()
//
clearSelectedRowKeys()
}
async function handleUpdate(ids) {
await updateNotifyMessageRead(ids)
afterRead('标记已读成功!')
}
async function handleUpdateAll() {
await updateAllNotifyMessageRead()
afterRead('全部已读成功!')
}
function handleInfo(record: any) {
openModal(true, record)
}
</script>
<template>
<div>
<BasicTable bordered @register="registerTable">
<template #toolbar>
<a-button pre-icon="i-solar:check-read-line-duotone" type="primary" :disabled="readedDisabled" @click="handleUpdateList">
标记已读
</a-button>
<a-button pre-icon="i-solar:check-read-linear" type="primary" @click="handleUpdateAll">
全部已读
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<!-- 阻止事件冒泡 勾选框 -->
<TableAction
stop-button-propagation
:actions="[
{
icon: IconEnum.EDIT,
label: '已读',
color: 'warning',
ifShow: () => {
return !record.readStatus
},
onClick: handleUpdateSingle.bind(null, record),
},
{
icon: IconEnum.LOG,
label: t('action.detail'),
onClick: handleInfo.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
<MessageInfoModal @register="registerModal" />
</div>
</template>

112
src/views/system/notify/my/my.data.ts

@ -1,112 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '发送人',
dataIndex: 'templateNickname',
width: 100,
},
{
title: '发送时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
{
title: '类型',
dataIndex: 'templateType',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
},
},
{
title: '内容',
dataIndex: 'templateContent',
width: 300,
},
{
title: '是否已读',
dataIndex: 'readStatus',
width: 100,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.INFRA_BOOLEAN_STRING)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '是否已读',
field: 'readStatus',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING),
},
colProps: { span: 8 },
},
{
label: '发送时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '模版编码',
field: 'code',
required: true,
component: 'Input',
},
{
label: '模板名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '发件人名称',
field: 'nickname',
required: true,
component: 'Input',
},
{
label: '模板内容',
field: 'content',
required: true,
component: 'InputTextArea',
},
{
label: '类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE),
},
},
{
label: '开启状态',
field: 'status',
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]

88
src/views/system/notify/template/SendNotifyModal.vue

@ -1,88 +0,0 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { baseSendSchemas } from './template.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import type { FormSchema } from '@/components/Form'
import { BasicForm, useForm } from '@/components/Form'
import { useMessage } from '@/hooks/web/useMessage'
import type { NotifyTemplate, SendNotifyParam } from '@/api/system/notify/template'
import { sendNotify } from '@/api/system/notify/template'
defineOptions({ name: 'SendNotifyModal' })
const { createMessage } = useMessage()
const reactiveSchemas: FormSchema[] = reactive([])
const templateCode = ref<string>('')
const [register, { setFieldsValue, getFieldsValue, validateFields, resetFields, clearValidate, setProps }] = useForm({
labelWidth: 100,
baseColProps: {
span: 24,
},
showActionButtonGroup: false,
})
const [innerRegister, { changeLoading, closeModal }] = useModalInner((data: NotifyTemplate) => {
resetForm()
data.params.forEach((item) => {
const dySchema: FormSchema = {
// content/userId
field: `key-${item}`,
label: `参数{${item}} `,
component: 'Input',
componentProps: {
placeholder: `输入{${item}}`,
},
required: true,
}
reactiveSchemas.push(dySchema)
})
const { content, code } = data
setFieldsValue({ content })
templateCode.value = code
})
async function submit() {
try {
setProps({ disabled: true })
changeLoading(true)
await validateFields()
const fields = getFieldsValue()
const data: SendNotifyParam = {
userId: fields.userId,
templateCode: templateCode.value,
templateParams: {},
}
Object.keys(fields).forEach((key) => {
if (key === 'content' || key === 'userId')
return
// - key
const realKey = key.split('-')[1]
data.templateParams[realKey] = fields[key]
})
await sendNotify(data)
createMessage.success('发送站内信成功')
closeModal()
}
finally {
setProps({ disabled: false })
changeLoading(false)
}
}
function resetForm() {
//
reactiveSchemas.splice(0, reactiveSchemas.length)
reactiveSchemas.push(...baseSendSchemas)
//
resetFields()
clearValidate()
}
</script>
<template>
<BasicModal v-bind="$attrs" title="发送站内信" @register="innerRegister" @ok="submit">
<BasicForm :schemas="reactiveSchemas" @register="register" />
</BasicModal>
</template>

58
src/views/system/notify/template/TemplateModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './template.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createNotifyTemplate, getNotifyTemplate, updateNotifyTemplate } from '@/api/system/notify/template'
defineOptions({ name: 'NotifyTemplateModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getNotifyTemplate(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateNotifyTemplate(values)
else
await createNotifyTemplate(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

97
src/views/system/notify/template/index.vue

@ -1,97 +0,0 @@
<script lang="ts" setup>
import TemplateModal from './TemplateModal.vue'
import { columns, searchFormSchema } from './template.data'
import SendNotifyModal from './SendNotifyModal.vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteNotifyTemplate, getNotifyTemplatePage } from '@/api/system/notify/template'
defineOptions({ name: 'SystemMessageTemplate' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerSendModal, { openModal: openSendModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '站内信模板列表',
api: getNotifyTemplatePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 200,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
function handleSend(record: Recordable) {
openSendModal(true, record)
}
async function handleDelete(record: Recordable) {
await deleteNotifyTemplate(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:notify-template:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.UPLOAD,
label: t('action.send'),
auth: 'system:notify-template:update',
onClick: handleSend.bind(null, record),
},
{
icon: IconEnum.EDIT,
label: t('action.edit'),
auth: 'system:notify-template:update',
onClick: handleEdit.bind(null, record),
},
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:notify-template:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<TemplateModal @register="registerModal" @success="reload()" />
<SendNotifyModal @register="registerSendModal" />
</div>
</template>

181
src/views/system/notify/template/template.data.ts

@ -1,181 +0,0 @@
import { h } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import { getListSimpleUsers } from '@/api/system/user/index'
import { ScrollContainer } from '@/components/Container'
export const columns: BasicColumn[] = [
{
title: '模板编码',
dataIndex: 'code',
width: 100,
},
{
title: '模板名称',
dataIndex: 'name',
width: 180,
},
{
title: '类型',
dataIndex: 'type',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)
},
},
{
title: '发送人名称',
dataIndex: 'nickname',
width: 100,
},
{
title: '模板内容',
dataIndex: 'content',
width: 300,
},
{
title: '开启状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '模板名称',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '模版编码',
field: 'code',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '模版编码',
field: 'code',
required: true,
component: 'Input',
},
{
label: '模板名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '发件人名称',
field: 'nickname',
required: true,
component: 'Input',
},
{
label: '模板内容',
field: 'content',
required: true,
component: 'InputTextArea',
},
{
label: '类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE),
},
},
{
label: '开启状态',
field: 'status',
required: true,
component: 'RadioGroup',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]
// 发送站内信
// 这里加上前缀 防止和表单其他字段重名
export const keyPrefix = 'key$-'
export const baseSendSchemas: FormSchema[] = [
{
field: 'content',
component: 'Editor',
label: '模板内容 ',
required: false,
defaultValue: '',
render({ model }) {
let content: string = model.content
Object.keys(model).forEach((key) => {
if (!key.startsWith(keyPrefix))
return
const realKey = key.split(keyPrefix)[1]
content = content.replace(`{${realKey}}`, model[key])
})
return h(ScrollContainer, {
innerHTML: content,
style: { border: '1px solid #e8e8e8', borderRadius: '6px', padding: '10px' },
})
},
},
{
field: 'userId',
component: 'ApiSelect',
label: '接收人 ',
required: true,
componentProps: {
api: getListSimpleUsers,
labelField: 'nickname',
valueField: 'id',
},
},
]

58
src/views/system/oauth2/client/ClientModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './client.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createOAuth2Client, getOAuth2Client, updateOAuth2Client } from '@/api/system/oauth2/client'
defineOptions({ name: 'SystemClientModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 160,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getOAuth2Client(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateOAuth2Client(values)
else
await createOAuth2Client(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

212
src/views/system/oauth2/client/client.data.ts

@ -1,212 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '客户端编号',
dataIndex: 'clientId',
width: 200,
},
{
title: '客户端密钥',
dataIndex: 'secret',
width: 100,
},
{
title: '应用名',
dataIndex: 'name',
width: 100,
},
{
title: '应用图标',
dataIndex: 'logo',
width: 120,
customRender: ({ text }) => {
return useRender.renderImg(text)
},
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '访问令牌的有效期',
dataIndex: 'accessTokenValiditySeconds',
width: 100,
customRender: ({ text }) => {
return useRender.renderText(text, '秒')
},
},
{
title: '刷新令牌的有效期',
dataIndex: 'refreshTokenValiditySeconds',
width: 100,
customRender: ({ text }) => {
return useRender.renderText(text, '秒')
},
},
{
title: '授权类型',
dataIndex: 'authorizedGrantTypes',
width: 180,
customRender: ({ text }) => {
return useRender.renderTags(text)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '应用名',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '客户端编号',
field: 'clientId',
required: true,
component: 'Input',
},
{
label: '客户端密钥',
field: 'secret',
required: true,
component: 'Input',
},
{
label: '应用名',
field: 'name',
required: true,
component: 'Input',
},
{
label: '应用图标',
field: 'logo',
required: true,
component: 'FileUpload',
componentProps: {
fileType: 'image',
maxCount: 1,
},
},
{
label: '应用描述',
field: 'description',
component: 'InputTextArea',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '访问令牌的有效期',
field: 'accessTokenValiditySeconds',
required: true,
defaultValue: 0,
component: 'InputNumber',
},
{
label: '刷新令牌的有效期',
field: 'refreshTokenValiditySeconds',
required: true,
defaultValue: 0,
component: 'InputNumber',
},
{
label: '授权类型',
field: 'authorizedGrantTypes',
required: true,
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE, 'string'),
mode: 'multiple',
},
},
{
label: '授权范围',
field: 'scopes',
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
{
label: '自动授权范围',
field: 'autoApproveScopes',
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
{
label: '可重定向的 URI 地址',
field: 'redirectUris',
required: true,
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
{
label: '权限',
field: 'authorities',
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
{
label: '资源',
field: 'resourceIds',
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
{
label: '附加信息',
field: 'additionalInformation',
component: 'InputTextArea',
},
]

78
src/views/system/oauth2/client/index.vue

@ -1,78 +0,0 @@
<script lang="ts" setup>
import ClientModal from './ClientModal.vue'
import { columns, searchFormSchema } from './client.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteOAuth2Client, getOAuth2ClientPage } from '@/api/system/oauth2/client'
defineOptions({ name: 'SystemClient' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '应用列表',
api: getOAuth2ClientPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteOAuth2Client(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:oauth2-client:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:oauth2-client:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:oauth2-client:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<ClientModal @register="registerModal" @success="reload()" />
</div>
</template>

60
src/views/system/oauth2/token/index.vue

@ -1,60 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './token.data'
import { useI18n } from '@/hooks/web/useI18n'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteAccessToken, getAccessTokenPage } from '@/api/system/oauth2/token'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SystemToken' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerTable, { reload }] = useTable({
title: 'Token列表',
api: getAccessTokenPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
async function handleDelete(record: Recordable) {
await deleteAccessToken(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.DELETE,
danger: true,
label: '强退',
auth: 'system:oauth2-token:delete',
popConfirm: {
title: '是否确认强退',
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
</div>
</template>

69
src/views/system/oauth2/token/token.data.ts

@ -1,69 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '访问令牌',
dataIndex: 'accessToken',
width: 300,
},
{
title: '刷新令牌',
dataIndex: 'refreshToken',
width: 300,
},
{
title: '用户编号',
dataIndex: 'userId',
width: 100,
},
{
title: '用户类型',
dataIndex: 'userType',
width: 120,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.USER_TYPE)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
{
title: '过期时间',
dataIndex: 'expiresTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '用户编号',
field: 'userId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '客户端编号',
field: 'clientId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '用户类型',
field: 'userType',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.USER_TYPE),
},
colProps: { span: 8 },
},
]

25
src/views/system/operatelog/LogInfoModal.vue

@ -1,25 +0,0 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { infoSchema } from './operateLog.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import { Description, useDescription } from '@/components/Description/index'
defineOptions({ name: 'OperLogInfoModal' })
const logData = ref()
const [registerModalInner, { closeModal }] = useModalInner((record: Recordable) => {
logData.value = record
})
const [registerDescription] = useDescription({
column: 1,
schema: infoSchema,
data: logData,
})
</script>
<template>
<BasicModal v-bind="$attrs" title="操作日志详情" width="800px" @register="registerModalInner" @ok="closeModal">
<Description @register="registerDescription" />
</BasicModal>
</template>

74
src/views/system/operatelog/index.vue

@ -1,74 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './operateLog.data'
import OperLogInfoModal from './LogInfoModal.vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import type { OperateLogPageReqVO } from '@/api/system/operatelog'
import { exportOperateLog, getOperateLogPage } from '@/api/system/operatelog'
import { useModal } from '@/components/Modal'
defineOptions({ name: 'SystemOperateLog' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerTable, { getForm }] = useTable({
title: '操作日志列表',
api: getOperateLogPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportOperateLog(getForm().getFieldsValue() as OperateLogPageReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
const [registerModal, { openModal }] = useModal()
function handleShowInfo(record: Recordable) {
openModal(true, record)
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.VIEW,
label: t('action.detail'),
onClick: handleShowInfo.bind(null, record),
},
]"
/>
</template>
</template>
</BasicTable>
<OperLogInfoModal @register="registerModal" />
</div>
</template>

202
src/views/system/operatelog/operateLog.data.ts

@ -1,202 +0,0 @@
import { h } from 'vue'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
import type { DescItem } from '@/components/Description/index'
export const columns: BasicColumn[] = [
{
title: '日志编号',
dataIndex: 'id',
width: 100,
},
{
title: '操作模块',
dataIndex: 'module',
width: 200,
},
{
title: '操作名',
dataIndex: 'name',
width: 180,
},
{
title: '操作类型',
dataIndex: 'type',
width: 120,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_OPERATE_TYPE)
},
},
{
title: '操作人',
dataIndex: 'userNickname',
width: 120,
},
// {
// title: 'userAgent',
// dataIndex: 'userAgent',
// width: 400
// },
{
title: '请求路径',
dataIndex: 'requestUrl',
},
{
title: '操作结果',
dataIndex: 'resultCode',
width: 180,
customRender: ({ text }) => {
return useRender.renderTag(text === 0 ? '成功' : '失败', text === 0 ? '#87d068' : '#f50')
},
},
{
title: '执行时长',
dataIndex: 'duration',
width: 180,
customRender: ({ text }) => {
return useRender.renderText(text, 'ms')
},
},
{
title: '操作日期',
dataIndex: 'startTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '系统模块',
field: 'title',
component: 'Input',
colProps: { span: 8 },
},
{
label: '操作人员',
field: 'operName',
component: 'Input',
colProps: { span: 8 },
},
{
label: '类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_OPERATE_TYPE),
},
colProps: { span: 8 },
},
{
label: '状态',
field: 'success',
component: 'Select',
componentProps: {
options: [
{ value: 1, key: true, label: '成功' },
{ value: 0, key: false, label: '失败' },
],
},
colProps: { span: 8 },
},
{
label: '操作时间',
field: 'startTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
const httpMethods = [
{ value: 'GET', color: '#108ee9' },
{ value: 'POST', color: '#2db7f5' },
{ value: 'PUT', color: 'warning' },
{ value: 'DELETE', color: '#f50' },
]
export const infoSchema: DescItem[] = [
{
field: 'module',
label: '操作模块',
},
{
field: 'name',
label: '操作名',
},
{
field: 'userNickname',
label: '操作人',
render(_, data) {
const { userNickname, userId } = data
// return useRender.renderText(userNickname, 'uid: ' + userId)
return useRender.renderTags([userNickname, `uid: ${userId}`])
},
},
{
field: 'resultCode',
label: '请求结果',
render(value) {
return useRender.renderTag(value === 0 ? '成功' : '失败', value === 0 ? '#87d068' : '#f50')
},
},
{
field: 'resultMsg',
label: '响应信息',
show(data) {
return data && data.resultMsg && data.resultMsg !== ''
},
render(value) {
return h('span', { style: { color: 'red', fontWeight: 'bold' } }, value)
},
},
{
field: 'userIp',
label: '请求ip',
},
{
field: 'startTime',
label: '请求时间',
render(value) {
return useRender.renderDate(value)
},
},
{
field: 'requestUrl',
label: '请求路径',
render(_, data) {
if (!data)
return ''
const { requestMethod, requestUrl } = data
const current = httpMethods.find(item => item.value === requestMethod.toUpperCase())
const methodTag = current ? useRender.renderTag(requestMethod, current.color) : requestMethod
return h('span', {}, [methodTag, requestUrl])
},
},
{
field: 'javaMethod',
label: '操作方法',
labelMinWidth: 80,
},
{
field: 'javaMethodArgs',
label: '请求参数',
render(value) {
return useRender.renderJsonPreview(value)
},
},
{
field: 'userAgent',
label: 'userAgent',
},
{
field: 'duration',
label: '请求耗时',
render(value) {
return useRender.renderText(value, 'ms')
},
},
]

58
src/views/system/post/PostModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './post.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createPost, getPost, updatePost } from '@/api/system/post'
defineOptions({ name: 'SystemPostModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getPost(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updatePost(values)
else
await createPost(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

94
src/views/system/post/index.vue

@ -1,94 +0,0 @@
<script lang="ts" setup>
import PostModal from './PostModal.vue'
import { columns, searchFormSchema } from './post.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import type { PostExportReqVO } from '@/api/system/post'
import { deletePost, exportPost, getPostPage } from '@/api/system/post'
defineOptions({ name: 'SystemPost' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '岗位列表',
api: getPostPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportPost(getForm().getFieldsValue() as PostExportReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await deletePost(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:post:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['system:post:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:post:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:post:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<PostModal @register="registerModal" @success="reload()" />
</div>
</template>

112
src/views/system/post/post.data.ts

@ -1,112 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '岗位编号',
dataIndex: 'id',
width: 100,
},
{
title: '岗位名称',
dataIndex: 'name',
width: 180,
},
{
title: '岗位编码',
dataIndex: 'code',
width: 100,
},
{
title: '岗位顺序',
dataIndex: 'sort',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '岗位名称',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '岗位编码',
field: 'code',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '岗位名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '岗位编码',
field: 'code',
required: true,
component: 'Input',
},
{
label: '岗位顺序',
field: 'sort',
required: true,
defaultValue: 0,
component: 'InputNumber',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]

58
src/views/system/sensitiveWord/SensitiveWordModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './sensitiveWord.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createSensitiveWord, getSensitiveWord, updateSensitiveWord } from '@/api/system/sensitiveWord'
defineOptions({ name: 'SystemSensitiveWordModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getSensitiveWord(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateSensitiveWord(values)
else
await createSensitiveWord(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

99
src/views/system/sensitiveWord/index.vue

@ -1,99 +0,0 @@
<script lang="ts" setup>
import SensitiveWordModal from './SensitiveWordModal.vue'
import { columns, searchFormSchema } from './sensitiveWord.data'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import type { SensitiveWordExportReqVO } from '@/api/system/sensitiveWord'
import { deleteSensitiveWord, exportSensitiveWord, getSensitiveWordPage } from '@/api/system/sensitiveWord'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SystemSensitiveWord' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '敏感词列表',
api: getSensitiveWordPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportSensitiveWord(getForm().getFieldsValue() as SensitiveWordExportReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await deleteSensitiveWord(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:sensitive-word:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['system:sensitive-word:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.EDIT,
label: t('action.edit'),
auth: 'system:sensitive-word:delete',
onClick: handleEdit.bind(null, record),
},
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:sensitive-word:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<SensitiveWordModal @register="registerModal" @success="reload()" />
</div>
</template>

113
src/views/system/sensitiveWord/sensitiveWord.data.ts

@ -1,113 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '敏感词',
dataIndex: 'name',
width: 180,
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '描述',
dataIndex: 'description',
width: 200,
},
{
title: '标签',
dataIndex: 'tags',
width: 180,
customRender: ({ text }) => {
return useRender.renderTags(text)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '敏感词',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '标签',
field: 'tag',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '敏感词',
field: 'name',
required: true,
component: 'Input',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
{
label: '标签',
field: 'tags',
required: true,
component: 'Select',
componentProps: {
mode: 'tags',
options: [],
},
},
]

58
src/views/system/sms/channel/SmsChannelModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './smsChannel.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createSmsChannel, getSmsChannel, updateSmsChannel } from '@/api/system/sms/smsChannel'
defineOptions({ name: 'SystemSmsChannelModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getSmsChannel(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateSmsChannel(values)
else
await createSmsChannel(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

79
src/views/system/sms/channel/index.vue

@ -1,79 +0,0 @@
<script lang="ts" setup>
import SmsChannelModal from './SmsChannelModal.vue'
import { columns, searchFormSchema } from './smsChannel.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteSmsChannel, getSmsChannelPage } from '@/api/system/sms/smsChannel'
defineOptions({ name: 'SystemSmsChannel' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '短信渠道列表',
api: getSmsChannelPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteSmsChannel(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:sms-channel:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:sms-channel:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:sms-channel:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<SmsChannelModal @register="registerModal" @success="reload()" />
</div>
</template>

138
src/views/system/sms/channel/smsChannel.data.ts

@ -1,138 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'id',
width: 100,
},
{
title: '短信签名',
dataIndex: 'signature',
width: 180,
},
{
title: '渠道编码',
dataIndex: 'code',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE)
},
},
{
title: '启用状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '短信 API 的账号',
dataIndex: 'apiKey',
width: 180,
},
{
title: '短信 API 的密钥',
dataIndex: 'apiSecret',
width: 180,
},
{
title: '短信发送回调 URL',
dataIndex: 'callbackUrl',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '短信签名',
field: 'signature',
component: 'Input',
colProps: { span: 8 },
},
{
label: '启用状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '短信签名',
field: 'signature',
required: true,
component: 'Input',
},
{
label: '渠道编码',
field: 'code',
component: 'Select',
required: true,
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'),
},
},
{
label: '启用状态',
field: 'status',
component: 'Select',
defaultValue: 0,
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
{
label: '短信 API 的账号',
field: 'apiKey',
required: true,
component: 'Input',
},
{
label: '短信 API 的密钥',
field: 'apiSecret',
component: 'Input',
},
{
label: '短信发送回调 URL',
field: 'callbackUrl',
component: 'Input',
},
]

47
src/views/system/sms/log/index.vue

@ -1,47 +0,0 @@
<script lang="ts" setup>
import { columns, searchFormSchema } from './smsLog.data'
import { BasicTable, useTable } from '@/components/Table'
import { IconEnum } from '@/enums/appEnum'
import type { SmsLogExportReqVO } from '@/api/system/sms/smsLog'
import { exportSmsLog, getSmsLogPage } from '@/api/system/sms/smsLog'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SystemSmsLog' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerTable, { getForm }] = useTable({
title: '短信日志列表',
api: getSmsLogPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
})
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportSmsLog(getForm().getFieldsValue() as SmsLogExportReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:sms-log:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
</BasicTable>
</div>
</template>

148
src/views/system/sms/log/smsLog.data.ts

@ -1,148 +0,0 @@
import { getSimpleSmsChannels } from '@/api/system/sms/smsChannel'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
let channelOptions: any[] = []
async function getchannelList() {
const res = await getSimpleSmsChannels()
channelOptions = res
}
await getchannelList()
export const columns: BasicColumn[] = [
{
title: '日志编号',
dataIndex: 'id',
width: 100,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
{
title: '手机号',
dataIndex: 'mobile',
width: 180,
customRender: ({ text, record }) => {
if (record.userType && record.userId)
return useRender.renderDict(record.userType, DICT_TYPE.USER_TYPE)
else return text
},
},
{
title: '短信内容',
dataIndex: 'templateContent',
width: 300,
},
{
title: '发送状态',
dataIndex: 'sendStatus',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_SEND_STATUS)
},
},
{
title: '接收状态',
dataIndex: 'receiveStatus',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS)
},
},
{
title: '短信渠道',
dataIndex: 'channelCode',
width: 180,
customRender: ({ text, record }) => {
if (!text)
return '未设置'
for (const channel of channelOptions) {
if (record.channelId === channel.id)
return channel.signature
}
return `找不到签名:${record.channelId}`
},
},
{
title: '模板编号',
dataIndex: 'templateId',
width: 120,
},
{
title: '短信类型',
dataIndex: 'templateType',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '手机号',
field: 'mobile',
component: 'Input',
colProps: { span: 8 },
},
{
label: '短信渠道',
field: 'channelId',
component: 'ApiSelect',
componentProps: {
api: getSimpleSmsChannels,
fieldNames: {
label: 'signature',
key: 'id',
value: 'id',
},
},
colProps: { span: 8 },
},
{
label: '模板编号',
field: 'templateId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '发送状态',
field: 'sendStatus',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_SEND_STATUS),
},
colProps: { span: 8 },
},
{
label: '发送时间',
field: 'sendTime',
component: 'RangePicker',
colProps: { span: 8 },
},
{
label: '接收状态',
field: 'receiveStatus',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS),
},
colProps: { span: 8 },
},
{
label: '接收时间',
field: 'receiveTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]

88
src/views/system/sms/template/SendSmsModal.vue

@ -1,88 +0,0 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { baseSendSchemas } from './smsTemplate.data'
import { BasicModal, useModalInner } from '@/components/Modal'
import type { FormSchema } from '@/components/Form'
import { BasicForm, useForm } from '@/components/Form'
import type { SmsTemplateVO } from '@/api/system/sms/smsTemplate'
import { sendSms } from '@/api/system/sms/smsTemplate'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SendSmsModal' })
const { createMessage } = useMessage()
const reactiveSchemas: FormSchema[] = reactive([])
const templateCode = ref<string>('')
const [register, { setFieldsValue, getFieldsValue, validateFields, resetFields, clearValidate, setProps }] = useForm({
labelWidth: 100,
baseColProps: {
span: 24,
},
showActionButtonGroup: false,
})
const [innerRegister, { changeLoading, closeModal }] = useModalInner((data: SmsTemplateVO) => {
resetForm()
data.params.forEach((item) => {
const dySchema: FormSchema = {
// content/mobile
field: `key-${item}`,
label: `参数{${item}} `,
component: 'Input',
componentProps: {
placeholder: `输入{${item}}`,
},
required: true,
}
reactiveSchemas.push(dySchema)
})
const { content, code } = data
setFieldsValue({ content })
templateCode.value = code
})
async function submit() {
try {
setProps({ disabled: true })
changeLoading(true)
await validateFields()
const fields = getFieldsValue()
const data = {
mobile: fields.mobile,
templateCode: templateCode.value,
templateParams: {},
}
Object.keys(fields).forEach((key) => {
if (key === 'content' || key === 'mobile')
return
// - key
const realKey = key.split('-')[1]
data.templateParams[realKey] = fields[key]
})
await sendSms(data)
createMessage.success(`发送短信到[${fields.mobile}]成功`)
closeModal()
}
finally {
setProps({ disabled: false })
changeLoading(false)
}
}
function resetForm() {
//
reactiveSchemas.splice(0, reactiveSchemas.length)
reactiveSchemas.push(...baseSendSchemas)
//
resetFields()
clearValidate()
}
</script>
<template>
<BasicModal v-bind="$attrs" title="测试发送短信" @register="innerRegister" @ok="submit">
<BasicForm :schemas="reactiveSchemas" @register="register" />
</BasicModal>
</template>

58
src/views/system/sms/template/SmsTemplateModal.vue

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { formSchema } from './smsTemplate.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createSmsTemplate, getSmsTemplate, updateSmsTemplate } from '@/api/system/sms/smsTemplate'
defineOptions({ name: 'SystemSmsTemplateModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getSmsTemplate(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate() as any
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateSmsTemplate(values)
else
await createSmsTemplate(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

107
src/views/system/sms/template/index.vue

@ -1,107 +0,0 @@
<script lang="ts" setup>
import SmsTemplateModal from './SmsTemplateModal.vue'
import SendSmsModal from './SendSmsModal.vue'
import { columns, searchFormSchema } from './smsTemplate.data'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import type { SmsTemplateExportReqVO } from '@/api/system/sms/smsTemplate'
import { deleteSmsTemplate, exportSmsTemplate, getSmsTemplatePage } from '@/api/system/sms/smsTemplate'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
defineOptions({ name: 'SystemSmsTemplate' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerSendModal, { openModal: openSendModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '短信模版列表',
api: getSmsTemplatePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 220,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleSendSms(record: Recordable) {
openSendModal(true, record)
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportSmsTemplate(getForm().getFieldsValue() as SmsTemplateExportReqVO)
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await deleteSmsTemplate(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:sms-template:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['system:sms-template:export']" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.TEST,
label: t('action.test'),
auth: 'system:sms-template:send-sms',
onClick: handleSendSms.bind(null, record),
},
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'system:sms-template:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:sms-template:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<SmsTemplateModal @register="registerModal" @success="reload()" />
<SendSmsModal @register="registerSendModal" />
</div>
</template>

209
src/views/system/sms/template/smsTemplate.data.ts

@ -1,209 +0,0 @@
import { h } from 'vue'
import { ScrollContainer } from '@/components/Container'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '模板编码',
dataIndex: 'code',
width: 180,
},
{
title: '模板名称',
dataIndex: 'name',
width: 100,
},
{
title: '模板内容',
dataIndex: 'content',
width: 300,
},
{
title: '短信类型',
dataIndex: 'type',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE)
},
},
{
title: '开启状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '短信 API 的模板编号',
dataIndex: 'apiTemplateId',
width: 180,
},
{
title: '短信渠道',
dataIndex: 'channelCode',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE)
},
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '短信类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE),
},
colProps: { span: 8 },
},
{
label: '开启状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '模板编码',
field: 'code',
component: 'Input',
colProps: { span: 8 },
},
{
label: '短信 API 的模板编号',
field: 'apiTemplateId',
component: 'Input',
colProps: { span: 8 },
},
{
label: '短信渠道',
field: 'channelId',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '短信渠道编号',
field: 'channelId',
required: true,
component: 'Input',
},
{
label: '短信类型',
field: 'type',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE),
},
},
{
label: '模板编号',
field: 'code',
required: true,
component: 'Input',
},
{
label: '模板名称',
field: 'name',
required: true,
component: 'Input',
},
{
label: '模板内容',
field: 'content',
required: true,
component: 'Input',
},
{
label: '开启状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
{
label: '短信 API 模板编号',
field: 'apiTemplateId',
required: true,
component: 'Input',
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
},
]
// 发送短信
// 这里加上前缀 防止和表单其他字段重名
export const keyPrefix = 'key$-'
export const baseSendSchemas: FormSchema[] = [
{
field: 'content',
component: 'Editor',
label: '模板内容 ',
required: false,
defaultValue: '',
render({ model }) {
let content: string = model.content
Object.keys(model).forEach((key) => {
if (!key.startsWith(keyPrefix))
return
const realKey = key.split(keyPrefix)[1]
content = content.replace(`{${realKey}}`, model[key])
})
return h(ScrollContainer, {
innerHTML: content,
style: { border: '1px solid #e8e8e8', borderRadius: '6px', padding: '10px' },
})
},
},
{
field: 'mobile',
label: '手机号 ',
component: 'Input',
componentProps: {
placeholder: '请输入手机号',
},
required: true,
},
]

128
src/views/system/tenantPackage/TenantPackageModal.vue

@ -1,128 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { without } from 'lodash-es'
import { formSchema } from './tenantPackage.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import type { CheckedEvent, CheckedKeys, TreeItem } from '@/components/Tree'
import { BasicTree } from '@/components/Tree'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createTenantPackage, getTenantPackage, updateTenantPackage } from '@/api/system/tenantPackage'
import { getMenuListWithoutButtons } from '@/api/system/menu'
import { handleTree } from '@/utils/tree'
defineOptions({ name: 'SystemTenantPackageModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const treeData = ref<TreeItem[]>([])
const menuKeys = ref<number[]>([])
const menuHalfKeys = ref<number[]>([])
//
const defaultExpandLevel = ref<number>(1)
// list
const parentIdSets = ref<Set<string>>(new Set())
const treeRef = ref()
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
menuReset()
setModalProps({ confirmLoading: false })
if (unref(treeData).length === 0) {
const res = await getMenuListWithoutButtons()
treeData.value = handleTree(res, 'id')
//
parentIdSets.value = new Set<string>(res.map(item => item.parentId))
}
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
const res = await getTenantPackage(data.record.id)
//
//
// ""
const excludeParentIds: string[] = without(res.menuIds, ...Array.from(parentIdSets.value))
// checkedKeys
menuKeys.value = res.menuIds
//
res.menuIds = excludeParentIds
await setFieldsValue({ ...res })
}
//
if (unref(treeRef))
unref(treeRef).filterByLevel(defaultExpandLevel.value)
})
async function handleSubmit() {
try {
const values = await validate() as any
values.menuIds = [...menuKeys.value, ...menuHalfKeys.value]
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateTenantPackage(values)
else
await createTenantPackage(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
}
finally {
setModalProps({ confirmLoading: false })
}
}
function menuReset() {
menuKeys.value = []
menuHalfKeys.value = []
}
/**
* 父子节点关联情况下 checkedKeys为选中的菜单 e.halfCheckedKeys为父节点数组
* 父子节点独立情况下 checkedKeys为{checked: number[], halfChecked: number[]} e.halfCheckedKeys为null
* @param checkedKeys 选中的菜单
* @param event event
*/
function menuCheck(checkedKeys: CheckedKeys, event: CheckedEvent) {
if (Array.isArray(checkedKeys)) {
menuKeys.value = checkedKeys
menuHalfKeys.value = event.halfCheckedKeys!
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm">
<template #menuIds="{ model, field }">
<BasicTree
v-if="treeData.length"
ref="treeRef"
v-model:checkedKeys="model[field]"
:tree-data="treeData"
:field-names="{ title: 'name', key: 'id' }"
checkable
toolbar
search
:show-strictly-button="false"
:selectable="false"
title="菜单分配"
@check="menuCheck"
/>
</template>
</BasicForm>
</BasicModal>
</template>

83
src/views/system/tenantPackage/index.vue

@ -1,83 +0,0 @@
<script lang="ts" setup>
import TenantPackageModal from './TenantPackageModal.vue'
import { columns, searchFormSchema } from './tenantPackage.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteTenantPackage, getTenantPackagePage } from '@/api/system/tenantPackage'
defineOptions({ name: 'SystemTenantPackage' })
const { t } = useI18n()
const { createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { reload }] = useTable({
title: '租户套餐列表',
api: getTenantPackagePage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
showIndexColumn: false,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleDelete(record: Recordable) {
await deleteTenantPackage(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['system:tenant-package:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: IconEnum.EDIT,
label: t('action.edit'),
auth: 'system:tenant-package:update',
onClick: handleEdit.bind(null, record),
},
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'system:tenant-package:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<TenantPackageModal @register="registerModal" @success="reload()" />
</div>
</template>

89
src/views/system/tenantPackage/tenantPackage.data.ts

@ -1,89 +0,0 @@
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
{
title: '套餐编号',
dataIndex: 'id',
width: 100,
},
{
title: '套餐名',
dataIndex: 'name',
width: 180,
},
{
title: '状态',
dataIndex: 'status',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
},
},
{
title: '备注',
dataIndex: 'remark',
width: 180,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
]
export const searchFormSchema: FormSchema[] = [
{
label: '套餐名',
field: 'name',
component: 'Input',
colProps: { span: 8 },
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
colProps: { span: 8 },
},
{
label: '创建时间',
field: 'createTime',
component: 'RangePicker',
colProps: { span: 8 },
},
]
export const formSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
{
label: '套餐名',
field: 'name',
required: true,
component: 'Input',
},
{
label: '菜单权限',
field: 'menuIds',
slot: 'menuIds',
},
{
label: '状态',
field: 'status',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.COMMON_STATUS),
},
},
]