@liujitcn/kratos-admin-system 0.0.28 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +19 -2
  2. package/dist/package/declarations/src/api/base/notification.d.ts +24 -0
  3. package/dist/package/declarations/src/api/system/base_audit_log.d.ts +55 -0
  4. package/dist/package/declarations/src/api/system/base_dashboard.d.ts +14 -0
  5. package/dist/package/declarations/src/api/system/base_file.d.ts +13 -0
  6. package/dist/package/declarations/src/api/system/base_i18n.d.ts +5 -5
  7. package/dist/package/declarations/src/api/system/base_login_policy.d.ts +10 -0
  8. package/dist/package/declarations/src/api/system/base_message.d.ts +24 -0
  9. package/dist/package/declarations/src/api/system/base_message_category.d.ts +21 -0
  10. package/dist/package/declarations/src/api/system/base_session.d.ts +11 -0
  11. package/dist/package/declarations/src/api/system/oauth_client.d.ts +24 -0
  12. package/dist/package/declarations/src/components/AuditLogTable.vue.d.ts +45 -0
  13. package/dist/package/declarations/src/components/DynamicI18nCell.vue.d.ts +16 -0
  14. package/dist/package/declarations/src/components/DynamicI18nEditor.vue.d.ts +20 -0
  15. package/dist/package/declarations/src/components/auditLog.d.ts +9 -0
  16. package/dist/package/declarations/src/components/dynamicI18n.d.ts +38 -0
  17. package/dist/package/declarations/src/locales/generated.d.ts +1364 -68
  18. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +149 -0
  19. package/dist/package/declarations/src/rpc/base/v1/notification.d.ts +281 -0
  20. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +25 -0
  21. package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +23 -0
  22. package/dist/package/declarations/src/rpc/system/admin/v1/base_audit_log.d.ts +640 -0
  23. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +3 -3
  24. package/dist/package/declarations/src/rpc/system/admin/v1/base_dashboard.d.ts +60 -0
  25. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +4 -4
  26. package/dist/package/declarations/src/rpc/system/admin/v1/base_file.d.ts +73 -0
  27. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +27 -23
  28. package/dist/package/declarations/src/rpc/system/admin/v1/base_job.d.ts +5 -0
  29. package/dist/package/declarations/src/rpc/system/admin/v1/base_login_policy.d.ts +51 -0
  30. package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +2 -2
  31. package/dist/package/declarations/src/rpc/system/admin/v1/base_message.d.ts +193 -0
  32. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_category.d.ts +125 -0
  33. package/dist/package/declarations/src/rpc/system/admin/v1/base_session.d.ts +33 -0
  34. package/dist/package/declarations/src/rpc/system/admin/v1/base_user.d.ts +3 -3
  35. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +1 -1
  36. package/dist/package/declarations/src/rpc/system/admin/v1/oauth_client.d.ts +145 -0
  37. package/dist/package/declarations/src/views/base/api-log/index.vue.d.ts +3 -0
  38. package/dist/package/declarations/src/views/base/dashboard/index.vue.d.ts +3 -0
  39. package/dist/package/declarations/src/views/base/data-access-log/index.vue.d.ts +3 -0
  40. package/dist/package/declarations/src/views/base/file/index.vue.d.ts +3 -0
  41. package/dist/package/declarations/src/views/base/login-log/index.vue.d.ts +3 -0
  42. package/dist/package/declarations/src/views/base/login-policy/index.vue.d.ts +3 -0
  43. package/dist/package/declarations/src/views/base/message/index.vue.d.ts +3 -0
  44. package/dist/package/declarations/src/views/base/message-category/index.vue.d.ts +3 -0
  45. package/dist/package/declarations/src/views/base/oauth-client/index.vue.d.ts +3 -0
  46. package/dist/package/declarations/src/views/base/operation-log/index.vue.d.ts +3 -0
  47. package/dist/package/declarations/src/views/base/permission-log/index.vue.d.ts +3 -0
  48. package/dist/package/declarations/src/views/base/policy-evaluation-log/index.vue.d.ts +3 -0
  49. package/dist/package/declarations/src/views/message/inbox/index.vue.d.ts +3 -0
  50. package/dist/package/declarations/src/views/profile/components/session.vue.d.ts +3 -0
  51. package/dist/package/src/api/base/notification.ts +69 -0
  52. package/dist/package/src/api/base/sse.ts +4 -4
  53. package/dist/package/src/api/system/auth.ts +1 -0
  54. package/dist/package/src/api/system/base_audit_log.ts +168 -0
  55. package/dist/package/src/api/system/base_dashboard.ts +55 -0
  56. package/dist/package/src/api/system/base_file.ts +43 -0
  57. package/dist/package/src/api/system/base_i18n.ts +12 -12
  58. package/dist/package/src/api/system/base_login_policy.ts +32 -0
  59. package/dist/package/src/api/system/base_message.ts +70 -0
  60. package/dist/package/src/api/system/base_message_category.ts +57 -0
  61. package/dist/package/src/api/system/base_session.ts +33 -0
  62. package/dist/package/src/api/system/oauth_client.ts +102 -0
  63. package/dist/package/src/components/AuditLogTable.vue +105 -0
  64. package/dist/package/src/components/{DynamicTranslationCell.vue → DynamicI18nCell.vue} +50 -50
  65. package/dist/package/src/components/{DynamicTranslationEditor.vue → DynamicI18nEditor.vue} +45 -45
  66. package/dist/package/src/components/Notification.vue +196 -0
  67. package/dist/package/src/components/auditLog.ts +46 -0
  68. package/dist/package/src/components/{dynamicTranslation.ts → dynamicI18n.ts} +15 -15
  69. package/dist/package/src/locales/README.md +5 -3
  70. package/dist/package/src/locales/en-US.json +345 -21
  71. package/dist/package/src/locales/ja-JP.json +347 -23
  72. package/dist/package/src/locales/zh-CN.json +343 -19
  73. package/dist/package/src/locales/zh-TW.json +346 -22
  74. package/dist/package/src/module.ts +5 -2
  75. package/dist/package/src/rpc/base/v1/login.ts +172 -0
  76. package/dist/package/src/rpc/base/v1/notification.ts +315 -0
  77. package/dist/package/src/rpc/base/v1/oauth.ts +25 -0
  78. package/dist/package/src/rpc/base/v1/oauth_client.ts +33 -0
  79. package/dist/package/src/rpc/system/admin/v1/base_audit_log.ts +737 -0
  80. package/dist/package/src/rpc/system/admin/v1/base_config.ts +3 -3
  81. package/dist/package/src/rpc/system/admin/v1/base_dashboard.ts +81 -0
  82. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +4 -4
  83. package/dist/package/src/rpc/system/admin/v1/base_file.ts +88 -0
  84. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +27 -23
  85. package/dist/package/src/rpc/system/admin/v1/base_job.ts +5 -0
  86. package/dist/package/src/rpc/system/admin/v1/base_login_policy.ts +63 -0
  87. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +2 -2
  88. package/dist/package/src/rpc/system/admin/v1/base_message.ts +236 -0
  89. package/dist/package/src/rpc/system/admin/v1/base_message_category.ts +149 -0
  90. package/dist/package/src/rpc/system/admin/v1/base_session.ts +44 -0
  91. package/dist/package/src/rpc/system/admin/v1/base_user.ts +3 -3
  92. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +1 -1
  93. package/dist/package/src/rpc/system/admin/v1/oauth_client.ts +174 -0
  94. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +2 -2
  95. package/dist/package/src/views/ai/chat/message.ts +7 -3
  96. package/dist/package/src/views/base/api-log/index.vue +83 -0
  97. package/dist/package/src/views/base/config/index.vue +41 -41
  98. package/dist/package/src/views/base/dashboard/index.vue +200 -0
  99. package/dist/package/src/views/base/data-access-log/index.vue +84 -0
  100. package/dist/package/src/views/base/dict/index.vue +23 -23
  101. package/dist/package/src/views/base/dict/item.vue +25 -25
  102. package/dist/package/src/views/base/file/index.vue +119 -0
  103. package/dist/package/src/views/base/job/index.vue +62 -7
  104. package/dist/package/src/views/base/language/index.vue +2 -1
  105. package/dist/package/src/views/base/login-log/index.vue +92 -0
  106. package/dist/package/src/views/base/login-policy/index.vue +219 -0
  107. package/dist/package/src/views/base/menu/index.vue +23 -23
  108. package/dist/package/src/views/base/message/index.vue +582 -0
  109. package/dist/package/src/views/base/message-category/index.vue +356 -0
  110. package/dist/package/src/views/base/oauth-client/index.vue +411 -0
  111. package/dist/package/src/views/base/operation-log/index.vue +82 -0
  112. package/dist/package/src/views/base/permission-log/index.vue +85 -0
  113. package/dist/package/src/views/base/policy-evaluation-log/index.vue +82 -0
  114. package/dist/package/src/views/message/inbox/index.vue +249 -0
  115. package/dist/package/src/views/profile/components/base.vue +6 -5
  116. package/dist/package/src/views/profile/components/password.vue +2 -2
  117. package/dist/package/src/views/profile/components/security.vue +475 -62
  118. package/dist/package/src/views/profile/components/session.vue +153 -0
  119. package/dist/package/src/views/profile/index.vue +35 -22
  120. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +9 -9
  121. package/dist/package/src/views/tool/code-gen/columns/index.vue +1 -1
  122. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +8 -8
  123. package/dist/package/src/views/tool/code-gen/config.ts +1 -0
  124. package/dist/package/src/views/tool/code-gen/table/index.vue +7 -7
  125. package/dist/package/src/views/tool/ops-monitoring/index.vue +25 -21
  126. package/package.json +2 -2
  127. package/dist/package/declarations/src/api/system/base_log.d.ts +0 -9
  128. package/dist/package/declarations/src/components/DynamicTranslationCell.vue.d.ts +0 -16
  129. package/dist/package/declarations/src/components/DynamicTranslationEditor.vue.d.ts +0 -20
  130. package/dist/package/declarations/src/components/dynamicTranslation.d.ts +0 -38
  131. package/dist/package/declarations/src/rpc/system/admin/v1/base_log.d.ts +0 -87
  132. package/dist/package/src/api/system/base_log.ts +0 -32
  133. package/dist/package/src/rpc/system/admin/v1/base_log.ts +0 -101
  134. package/dist/package/src/views/base/log/index.vue +0 -339
  135. /package/dist/package/declarations/src/{views/base/log/index.vue.d.ts → components/Notification.vue.d.ts} +0 -0
@@ -0,0 +1,582 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable ref="proTable" row-key="id" :columns="columns" :header-actions="headerActions" :request-api="requestTable" />
4
+ <FormDialog
5
+ v-model="dialog.visible"
6
+ ref="formDialogRef"
7
+ :title="t(dialog.titleKey)"
8
+ width="900px"
9
+ :model="formState"
10
+ :fields="formFields"
11
+ :rules="rules"
12
+ @confirm="handleSubmit"
13
+ @close="handleClose"
14
+ >
15
+ <template #audiences>
16
+ <div class="audiences-editor">
17
+ <div v-for="(audience, index) in formState.audiences" :key="index" class="audience-row">
18
+ <el-select v-model="audience.type" class="audience-type" @change="onAudienceTypeChange(audience)">
19
+ <el-option
20
+ v-for="option in audienceOptions"
21
+ :key="String(option.value)"
22
+ :label="option.label"
23
+ :value="option.value"
24
+ />
25
+ </el-select>
26
+ <el-input-number
27
+ v-model="audience.id"
28
+ class="audience-id"
29
+ :min="audience.type === MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT ? 0 : 1"
30
+ :precision="0"
31
+ :disabled="audience.type === MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT"
32
+ />
33
+ <el-switch
34
+ v-if="audience.type === MessageAudienceType.MESSAGE_AUDIENCE_TYPE_DEPT"
35
+ v-model="audience.include_children"
36
+ />
37
+ <el-button v-if="formState.audiences.length > 1" link type="danger" @click="removeAudience(index)">
38
+ <el-icon><Delete /></el-icon>
39
+ </el-button>
40
+ </div>
41
+ <el-button link type="primary" @click="addAudience">
42
+ <el-icon><CirclePlus /></el-icon>
43
+ {{ t("common.action.create") }}
44
+ </el-button>
45
+ </div>
46
+ </template>
47
+ </FormDialog>
48
+ <el-dialog v-model="detail.visible" :title="t('system.base.message.detail.title')" width="900px">
49
+ <template v-if="detail.data">
50
+ <el-descriptions :column="2" border>
51
+ <el-descriptions-item :label="t('system.base.message.field.title')">{{
52
+ detail.data.base_message?.title
53
+ }}</el-descriptions-item>
54
+ <el-descriptions-item :label="t('common.field.status')">{{
55
+ optionLabel(statusOptions, detail.data.base_message?.status)
56
+ }}</el-descriptions-item>
57
+ <el-descriptions-item :label="t('system.base.message.field.delivered_total')">{{
58
+ detail.data.base_message?.delivered_total
59
+ }}</el-descriptions-item>
60
+ <el-descriptions-item :label="t('system.base.message.field.recipient_total')">{{
61
+ detail.data.base_message?.recipient_total
62
+ }}</el-descriptions-item>
63
+ </el-descriptions>
64
+ <el-table :data="detail.data.dispatches" style="margin-top: 16px" size="small">
65
+ <el-table-column prop="id" label="ID" width="90" />
66
+ <el-table-column prop="status" :label="t('common.field.status')" width="120" />
67
+ <el-table-column prop="matched_total" :label="t('system.base.message.field.matched_total')" width="110" />
68
+ <el-table-column prop="inserted_total" :label="t('system.base.message.field.inserted_total')" width="110" />
69
+ <el-table-column prop="attempt_count" :label="t('system.base.message.field.attempt_count')" width="90" />
70
+ <el-table-column
71
+ prop="last_error"
72
+ :label="t('system.base.message.field.last_error')"
73
+ min-width="220"
74
+ show-overflow-tooltip
75
+ />
76
+ <el-table-column :label="t('common.field.operation')" width="90">
77
+ <template #default="scope">
78
+ <el-button
79
+ v-if="scope.row.status === MessageDispatchStatus.MESSAGE_DISPATCH_STATUS_FAILED && BUTTONS['base:message:retry']"
80
+ link
81
+ type="primary"
82
+ @click="retryDispatch(scope.row.id)"
83
+ >
84
+ {{ t("system.base.message.action.retry") }}
85
+ </el-button>
86
+ </template>
87
+ </el-table-column>
88
+ </el-table>
89
+ </template>
90
+ </el-dialog>
91
+ </div>
92
+ </template>
93
+
94
+ <script setup lang="ts">
95
+ import { computed, onMounted, reactive, ref, watch } from "vue";
96
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
97
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
98
+ import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
99
+ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
100
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
101
+ import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
102
+ import { DEFAULT_TENANT_CODE, requestTenantOptions } from "@liujitcn/kratos-admin-core/tenant";
103
+ import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
104
+ import { t } from "@liujitcn/kratos-admin-core";
105
+ import { defBaseMessageService } from "@liujitcn/kratos-admin-system/api/system/base_message";
106
+ import { defBaseMessageCategoryService } from "@liujitcn/kratos-admin-system/api/system/base_message_category";
107
+ import { defBaseTenantService } from "@liujitcn/kratos-admin-system/api/system/base_tenant";
108
+ import type {
109
+ BaseMessage,
110
+ BaseMessageDetail,
111
+ BaseMessageForm,
112
+ BaseMessageAudienceForm,
113
+ PageBaseMessageRequest
114
+ } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_message";
115
+ import {
116
+ MessageActionType,
117
+ MessageAudienceType,
118
+ MessageContentFormat,
119
+ MessageDispatchStatus,
120
+ MessagePriority,
121
+ MessageStatus
122
+ } from "@liujitcn/kratos-admin-system/rpc/base/v1/notification";
123
+
124
+ defineOptions({ name: "BaseMessage", inheritAttrs: false });
125
+
126
+ /** 消息管理页面表单状态。 */
127
+ type MessageFormState = Omit<BaseMessageForm, "tenant_id" | "category_id"> & {
128
+ /** 租户ID。 */
129
+ tenant_id?: number;
130
+ /** 消息分类ID。 */
131
+ category_id?: number;
132
+ /** 单项受众类型。 */
133
+ audience_type: MessageAudienceType;
134
+ /** 单项受众ID。 */
135
+ audience_id: number;
136
+ /** 是否包含子部门。 */
137
+ include_children: boolean;
138
+ };
139
+
140
+ const { BUTTONS } = useAuthButtons();
141
+ const userStore = useUserStore();
142
+ const isDefaultTenant = computed(() => userStore.userInfo.tenant_code === DEFAULT_TENANT_CODE);
143
+ const proTable = ref<ProTableInstance>();
144
+ const formDialogRef = ref<InstanceType<typeof FormDialog>>();
145
+ const categoryOptions = ref<ProFormOption[]>([]);
146
+ const tenantOptions = ref<ProFormOption[]>([]);
147
+ const dialog = reactive({ visible: false, titleKey: "common.action.create" });
148
+ const detail = reactive<{ visible: boolean; data?: BaseMessageDetail }>({ visible: false });
149
+ const formState = reactive<MessageFormState>(defaultForm());
150
+
151
+ const priorityOptions = computed<ProFormOption[]>(() => [
152
+ { label: t("system.base.message.priority.normal"), value: MessagePriority.MESSAGE_PRIORITY_NORMAL },
153
+ { label: t("system.base.message.priority.important"), value: MessagePriority.MESSAGE_PRIORITY_IMPORTANT },
154
+ { label: t("system.base.message.priority.urgent"), value: MessagePriority.MESSAGE_PRIORITY_URGENT }
155
+ ]);
156
+ const actionTypeOptions = computed<ProFormOption[]>(() => [
157
+ { label: t("system.base.message.action_type.none"), value: MessageActionType.MESSAGE_ACTION_TYPE_UNSPECIFIED },
158
+ { label: t("system.base.message.action_type.view_key"), value: MessageActionType.MESSAGE_ACTION_TYPE_VIEW_KEY }
159
+ ]);
160
+ const audienceOptions = computed<ProFormOption[]>(() => [
161
+ { label: t("system.base.message.audience.tenant"), value: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT },
162
+ { label: t("system.base.message.audience.user"), value: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_USER },
163
+ { label: t("system.base.message.audience.role"), value: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_ROLE },
164
+ { label: t("system.base.message.audience.dept"), value: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_DEPT },
165
+ { label: t("system.base.message.audience.post"), value: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_POST }
166
+ ]);
167
+ const statusOptions = computed<ProFormOption[]>(() => [
168
+ { label: t("system.base.message.status.draft"), value: MessageStatus.MESSAGE_STATUS_DRAFT },
169
+ { label: t("system.base.message.status.scheduled"), value: MessageStatus.MESSAGE_STATUS_SCHEDULED },
170
+ { label: t("system.base.message.status.publishing"), value: MessageStatus.MESSAGE_STATUS_PUBLISHING },
171
+ { label: t("system.base.message.status.published"), value: MessageStatus.MESSAGE_STATUS_PUBLISHED },
172
+ { label: t("system.base.message.status.revoked"), value: MessageStatus.MESSAGE_STATUS_REVOKED }
173
+ ]);
174
+ onMounted(() => void loadTenantOptions());
175
+ watch(
176
+ () => formState.action_type,
177
+ value => {
178
+ if (value === MessageActionType.MESSAGE_ACTION_TYPE_UNSPECIFIED) formState.action_target = "";
179
+ }
180
+ );
181
+
182
+ const rules = computed(() => ({
183
+ tenant_id: isDefaultTenant.value
184
+ ? [
185
+ {
186
+ required: true,
187
+ message: t("common.validation.required_select", { field: t("common.field.tenant") }),
188
+ trigger: "change"
189
+ }
190
+ ]
191
+ : [],
192
+ category_id: [
193
+ {
194
+ required: true,
195
+ message: t("common.validation.required_select", { field: t("system.base.message.field.category") }),
196
+ trigger: "change"
197
+ }
198
+ ],
199
+ title: [
200
+ {
201
+ required: true,
202
+ message: t("common.validation.required_input", { field: t("system.base.message.field.title") }),
203
+ trigger: "blur"
204
+ }
205
+ ],
206
+ content: [
207
+ {
208
+ required: true,
209
+ message: t("common.validation.required_input", { field: t("system.base.message.field.content") }),
210
+ trigger: "blur"
211
+ }
212
+ ],
213
+ audiences: [{ required: true, message: t("system.base.message.validation.audience_id"), trigger: "change" }]
214
+ }));
215
+
216
+ const formFields = computed<ProFormField[]>(() => [
217
+ {
218
+ prop: "tenant_id",
219
+ label: t("common.field.tenant"),
220
+ labelTooltip: t("system.base.message.tooltip.tenant"),
221
+ component: "select",
222
+ visible: () => isDefaultTenant.value,
223
+ options: tenantOptions.value,
224
+ props: { filterable: true, disabled: Boolean(formState.id), placeholder: t("system.base.message.placeholder.tenant") }
225
+ },
226
+ {
227
+ prop: "category_id",
228
+ label: t("system.base.message.field.category"),
229
+ labelTooltip: t("system.base.message.tooltip.category"),
230
+ component: "select",
231
+ options: categoryOptions.value,
232
+ props: { filterable: true, placeholder: t("system.base.message.placeholder.category") }
233
+ },
234
+ {
235
+ prop: "title",
236
+ label: t("system.base.message.field.title"),
237
+ labelTooltip: t("system.base.message.tooltip.title"),
238
+ component: "input",
239
+ props: { maxlength: 200, showWordLimit: true, placeholder: t("system.base.message.placeholder.title") },
240
+ colSpan: 24
241
+ },
242
+ {
243
+ prop: "content",
244
+ label: t("system.base.message.field.content"),
245
+ labelTooltip: t("system.base.message.tooltip.content"),
246
+ component: "rich-text",
247
+ props: { height: "320px", editorConfig: { placeholder: t("system.base.message.placeholder.content") } },
248
+ colSpan: 24
249
+ },
250
+ {
251
+ prop: "priority",
252
+ label: t("system.base.message.field.priority"),
253
+ labelTooltip: t("system.base.message.tooltip.priority"),
254
+ component: "select",
255
+ options: priorityOptions.value
256
+ },
257
+ {
258
+ prop: "audiences",
259
+ label: t("system.base.message.field.audience_type"),
260
+ labelTooltip: t("system.base.message.tooltip.audience_type"),
261
+ component: "slot",
262
+ slotName: "audiences",
263
+ colSpan: 24,
264
+ rules: [{ required: true, message: t("system.base.message.validation.audience_id"), trigger: "change" }]
265
+ },
266
+ {
267
+ prop: "action_type",
268
+ label: t("system.base.message.field.action_type"),
269
+ labelTooltip: t("system.base.message.tooltip.action_type"),
270
+ component: "select",
271
+ options: actionTypeOptions.value
272
+ },
273
+ {
274
+ prop: "action_target",
275
+ label: t("system.base.message.field.action_target"),
276
+ labelTooltip: t("system.base.message.tooltip.action_target"),
277
+ component: "input",
278
+ visible: () => formState.action_type === MessageActionType.MESSAGE_ACTION_TYPE_VIEW_KEY,
279
+ props: { placeholder: t("system.base.message.placeholder.action_target") },
280
+ colSpan: 24
281
+ },
282
+ {
283
+ prop: "scheduled_at",
284
+ label: t("system.base.message.field.scheduled_at"),
285
+ component: "date-picker",
286
+ props: {
287
+ type: "datetime",
288
+ valueFormat: "YYYY-MM-DD HH:mm:ss",
289
+ placeholder: t("system.base.message.placeholder.scheduled_at")
290
+ }
291
+ },
292
+ {
293
+ prop: "expires_at",
294
+ label: t("system.base.message.field.expires_at"),
295
+ component: "date-picker",
296
+ props: { type: "datetime", valueFormat: "YYYY-MM-DD HH:mm:ss", placeholder: t("system.base.message.placeholder.expires_at") }
297
+ }
298
+ ]);
299
+
300
+ const columns = computed<ColumnProps[]>(() => [
301
+ { type: "selection", width: 55 },
302
+ ...(isDefaultTenant.value
303
+ ? ([
304
+ {
305
+ prop: "tenant_id",
306
+ label: t("common.field.tenant"),
307
+ minWidth: 120,
308
+ search: { el: "select" },
309
+ enum: requestTenantOptions
310
+ }
311
+ ] satisfies ColumnProps[])
312
+ : []),
313
+ { prop: "title", label: t("system.base.message.field.title"), minWidth: 220, search: { el: "input" } },
314
+ { prop: "category_name", label: t("system.base.message.field.category"), minWidth: 130 },
315
+ {
316
+ prop: "priority",
317
+ label: t("system.base.message.field.priority"),
318
+ width: 100,
319
+ render: scope => optionLabel(priorityOptions.value, (scope.row as BaseMessage).priority)
320
+ },
321
+ {
322
+ prop: "status",
323
+ label: t("common.field.status"),
324
+ width: 110,
325
+ search: { el: "select", enum: statusOptions.value },
326
+ render: scope => optionLabel(statusOptions.value, (scope.row as BaseMessage).status)
327
+ },
328
+ { prop: "delivered_total", label: t("system.base.message.field.delivered_total"), width: 100, align: "right" },
329
+ { prop: "created_at", label: t("common.field.created_at"), minWidth: 180 },
330
+ {
331
+ prop: "operation",
332
+ label: t("common.field.operation"),
333
+ width: 260,
334
+ fixed: "right",
335
+ cellType: "actions",
336
+ actions: [
337
+ {
338
+ label: t("common.action.edit"),
339
+ link: true,
340
+ icon: EditPen,
341
+ hidden: scope =>
342
+ !BUTTONS.value["base:message:update"] || (scope.row as BaseMessage).status !== MessageStatus.MESSAGE_STATUS_DRAFT,
343
+ onClick: scope => openDialog((scope.row as BaseMessage).id)
344
+ },
345
+ {
346
+ label: t("system.base.message.detail.action"),
347
+ link: true,
348
+ icon: View,
349
+ onClick: scope => openDetail((scope.row as BaseMessage).id)
350
+ },
351
+ {
352
+ label: t("system.base.message.action.publish"),
353
+ type: "primary",
354
+ link: true,
355
+ icon: Promotion,
356
+ hidden: scope =>
357
+ !BUTTONS.value["base:message:publish"] || (scope.row as BaseMessage).status !== MessageStatus.MESSAGE_STATUS_DRAFT,
358
+ onClick: scope => publishMessage(scope.row as BaseMessage)
359
+ },
360
+ {
361
+ label: t("system.base.message.action.cancel_schedule"),
362
+ link: true,
363
+ icon: RefreshRight,
364
+ hidden: scope =>
365
+ !BUTTONS.value["base:message:schedule"] || (scope.row as BaseMessage).status !== MessageStatus.MESSAGE_STATUS_SCHEDULED,
366
+ onClick: scope => cancelSchedule(scope.row as BaseMessage)
367
+ },
368
+ {
369
+ label: t("system.base.message.action.revoke"),
370
+ type: "danger",
371
+ link: true,
372
+ icon: SwitchButton,
373
+ hidden: scope =>
374
+ !BUTTONS.value["base:message:revoke"] ||
375
+ ![MessageStatus.MESSAGE_STATUS_PUBLISHING, MessageStatus.MESSAGE_STATUS_PUBLISHED].includes(
376
+ (scope.row as BaseMessage).status
377
+ ),
378
+ onClick: scope => revokeMessage(scope.row as BaseMessage)
379
+ },
380
+ {
381
+ label: t("common.action.delete"),
382
+ type: "danger",
383
+ link: true,
384
+ icon: Delete,
385
+ hidden: scope =>
386
+ !BUTTONS.value["base:message:delete"] || (scope.row as BaseMessage).status !== MessageStatus.MESSAGE_STATUS_DRAFT,
387
+ onClick: scope => handleDelete(scope.row as BaseMessage)
388
+ }
389
+ ]
390
+ }
391
+ ]);
392
+
393
+ const headerActions = computed<HeaderActionProps[]>(() => [
394
+ {
395
+ label: t("common.action.create"),
396
+ type: "primary",
397
+ icon: CirclePlus,
398
+ hidden: !BUTTONS.value["base:message:create"],
399
+ onClick: () => openDialog()
400
+ },
401
+ {
402
+ label: t("common.action.batch_delete"),
403
+ type: "danger",
404
+ icon: Delete,
405
+ hidden: !BUTTONS.value["base:message:delete"],
406
+ disabled: scope => !scope.isSelected,
407
+ onClick: scope => handleDelete(scope.selectedListIds as number[])
408
+ }
409
+ ]);
410
+
411
+ /** 创建默认消息表单。 */
412
+ function defaultForm(): MessageFormState {
413
+ return {
414
+ id: 0,
415
+ tenant_id: undefined,
416
+ category_id: undefined,
417
+ title: "",
418
+ content: "",
419
+ content_format: MessageContentFormat.MESSAGE_CONTENT_FORMAT_RICH_TEXT,
420
+ priority: MessagePriority.MESSAGE_PRIORITY_NORMAL,
421
+ action_type: MessageActionType.MESSAGE_ACTION_TYPE_UNSPECIFIED,
422
+ action_target: "",
423
+ action_params: "",
424
+ scheduled_at: "",
425
+ expires_at: "",
426
+ audiences: [defaultAudience()],
427
+ version: 1,
428
+ audience_type: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT,
429
+ audience_id: 0,
430
+ include_children: false
431
+ };
432
+ }
433
+
434
+ /** 创建默认受众行。 */
435
+ function defaultAudience(): BaseMessageAudienceForm {
436
+ return { type: MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT, id: 0, include_children: false };
437
+ }
438
+
439
+ /** 添加一条消息受众。 */
440
+ function addAudience() {
441
+ formState.audiences.push(defaultAudience());
442
+ }
443
+
444
+ /** 删除一条消息受众。 */
445
+ function removeAudience(index: number) {
446
+ if (formState.audiences.length > 1) formState.audiences.splice(index, 1);
447
+ }
448
+
449
+ /** 切换受众类型时清理不适用的字段。 */
450
+ function onAudienceTypeChange(audience: BaseMessageAudienceForm) {
451
+ if (audience.type === MessageAudienceType.MESSAGE_AUDIENCE_TYPE_TENANT) audience.id = 0;
452
+ if (audience.type !== MessageAudienceType.MESSAGE_AUDIENCE_TYPE_DEPT) audience.include_children = false;
453
+ }
454
+
455
+ /** 请求消息表格数据。 */
456
+ async function requestTable(params: Record<string, unknown>) {
457
+ const data = await defBaseMessageService.PageBaseMessage(
458
+ buildPageRequest<PageBaseMessageRequest>(params as unknown as PageBaseMessageRequest)
459
+ );
460
+ return { data: { list: data.base_messages ?? [], total: data.total } };
461
+ }
462
+
463
+ /** 加载租户选项。 */
464
+ async function loadTenantOptions() {
465
+ if (!isDefaultTenant.value) return;
466
+ const result = await defBaseTenantService.OptionBaseTenant({ keyword: "" });
467
+ tenantOptions.value = result.list.map(item => ({ label: item.label, value: item.value }));
468
+ await loadCategoryOptions();
469
+ }
470
+
471
+ /** 加载消息分类选项。 */
472
+ async function loadCategoryOptions() {
473
+ const result = await defBaseMessageCategoryService.OptionBaseMessageCategory({});
474
+ categoryOptions.value = result.list.map(item => ({ label: item.label, value: item.value, disabled: item.disabled }));
475
+ }
476
+
477
+ /** 打开消息草稿表单。 */
478
+ async function openDialog(id?: number) {
479
+ await loadTenantOptions();
480
+ Object.assign(formState, defaultForm());
481
+ dialog.titleKey = id ? "common.action.edit" : "common.action.create";
482
+ if (id) {
483
+ const detail = await defBaseMessageService.GetBaseMessage({ id });
484
+ Object.assign(formState, detail.form);
485
+ formState.audiences = detail.form?.audiences?.map(item => ({ ...item })) ?? [defaultAudience()];
486
+ }
487
+ await loadCategoryOptions();
488
+ dialog.visible = true;
489
+ }
490
+
491
+ /** 提交消息草稿。 */
492
+ async function handleSubmit() {
493
+ const audiences = formState.audiences.length > 0 ? formState.audiences.map(item => ({ ...item })) : [defaultAudience()];
494
+ const payload: BaseMessageForm = {
495
+ ...formState,
496
+ tenant_id: formState.tenant_id ?? 0,
497
+ category_id: formState.category_id ?? 0,
498
+ content_format: MessageContentFormat.MESSAGE_CONTENT_FORMAT_RICH_TEXT,
499
+ audiences
500
+ };
501
+ if (payload.id) await defBaseMessageService.UpdateBaseMessage({ base_message: payload });
502
+ else await defBaseMessageService.CreateBaseMessage({ base_message: payload });
503
+ ElMessage.success(t("common.message.operation_success"));
504
+ dialog.visible = false;
505
+ proTable.value?.getTableList();
506
+ }
507
+
508
+ /** 打开消息详情并加载投递进度。 */
509
+ async function openDetail(id: number) {
510
+ detail.data = await defBaseMessageService.GetBaseMessage({ id });
511
+ detail.visible = true;
512
+ }
513
+
514
+ /** 重试失败的消息投递任务并刷新详情。 */
515
+ async function retryDispatch(id: number) {
516
+ await defBaseMessageService.RetryBaseMessageDispatch({ id });
517
+ if (detail.data?.base_message?.id)
518
+ detail.data = await defBaseMessageService.GetBaseMessage({ id: detail.data.base_message.id });
519
+ ElMessage.success(t("common.message.operation_success"));
520
+ }
521
+
522
+ /** 删除草稿消息。 */
523
+ async function handleDelete(value: BaseMessage | BaseMessage[] | number | number[]) {
524
+ const ids = normalizeSelectedIds(value as Parameters<typeof normalizeSelectedIds>[0]);
525
+ await ElMessageBox.confirm(t("common.confirm.delete"), t("common.tips"), { type: "warning" });
526
+ await defBaseMessageService.DeleteBaseMessage({ id: ids.join(",") });
527
+ ElMessage.success(t("common.message.operation_success"));
528
+ proTable.value?.getTableList();
529
+ }
530
+
531
+ /** 发布消息。 */
532
+ async function publishMessage(row: BaseMessage) {
533
+ await ElMessageBox.confirm(t("system.base.message.confirm.publish"), t("common.tips"), { type: "warning" });
534
+ await defBaseMessageService.PublishBaseMessage({ id: row.id });
535
+ proTable.value?.getTableList();
536
+ }
537
+
538
+ /** 取消定时发布。 */
539
+ async function cancelSchedule(row: BaseMessage) {
540
+ await defBaseMessageService.CancelBaseMessageSchedule({ id: row.id });
541
+ proTable.value?.getTableList();
542
+ }
543
+
544
+ /** 撤回消息。 */
545
+ async function revokeMessage(row: BaseMessage) {
546
+ await ElMessageBox.confirm(t("system.base.message.confirm.revoke"), t("common.tips"), { type: "warning" });
547
+ await defBaseMessageService.RevokeBaseMessage({ id: row.id });
548
+ proTable.value?.getTableList();
549
+ }
550
+
551
+ /** 关闭消息表单。 */
552
+ function handleClose() {
553
+ Object.assign(formState, defaultForm());
554
+ formDialogRef.value?.resetFields();
555
+ }
556
+
557
+ /** 查询选项显示名称。 */
558
+ function optionLabel(options: ProFormOption[], value: unknown) {
559
+ return options.find(item => item.value === value)?.label ?? "";
560
+ }
561
+ </script>
562
+
563
+ <style scoped>
564
+ .audiences-editor {
565
+ width: 100%;
566
+ }
567
+
568
+ .audience-row {
569
+ display: flex;
570
+ align-items: center;
571
+ gap: 8px;
572
+ margin-bottom: 8px;
573
+ }
574
+
575
+ .audience-type {
576
+ width: 180px;
577
+ }
578
+
579
+ .audience-id {
580
+ width: 160px;
581
+ }
582
+ </style>