@qiqb/message-domain 0.0.0-stage → 0.1.3

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 (57) hide show
  1. package/README.md +213 -2
  2. package/build/components/DefaultMessageReminderRenderer.d.ts +10 -0
  3. package/build/components/DefaultMessageReminderRenderer.js +29 -0
  4. package/build/components/MessageNavigation.d.ts +7 -0
  5. package/build/components/MessageNavigation.js +192 -0
  6. package/build/components/MessageReminderDisplay.d.ts +21 -0
  7. package/build/components/MessageReminderDisplay.js +353 -0
  8. package/build/components/MessageWorkspace.d.ts +14 -0
  9. package/build/components/MessageWorkspace.js +26 -0
  10. package/build/components/message-navigation.less +89 -0
  11. package/build/components/message-reminder-display.less +367 -0
  12. package/build/components/message-workspace.less +150 -0
  13. package/build/index.d.ts +20 -0
  14. package/build/index.js +17 -0
  15. package/build/pages/inbox/InboxPage.d.ts +2 -0
  16. package/build/pages/inbox/InboxPage.js +257 -0
  17. package/build/pages/inbox/inbox.less +293 -0
  18. package/build/pages/subscription/PersonalSubscriptionPage.d.ts +1 -0
  19. package/build/pages/subscription/PersonalSubscriptionPage.js +6 -0
  20. package/build/pages/subscription/RoleSubscriptionPage.d.ts +1 -0
  21. package/build/pages/subscription/RoleSubscriptionPage.js +5 -0
  22. package/build/pages/subscription/SubscriptionForm.d.ts +16 -0
  23. package/build/pages/subscription/SubscriptionForm.js +111 -0
  24. package/build/pages/subscription/SubscriptionPage.d.ts +5 -0
  25. package/build/pages/subscription/SubscriptionPage.js +273 -0
  26. package/build/pages/subscription/subscription-form.less +341 -0
  27. package/build/pages/subscription/subscription.less +250 -0
  28. package/build/pages/topic/TopicPage.d.ts +1 -0
  29. package/build/pages/topic/TopicPage.js +275 -0
  30. package/build/routes.cjs +38 -0
  31. package/build/routes.d.cts +22 -0
  32. package/build/routes.d.ts +22 -0
  33. package/build/routes.js +34 -0
  34. package/build/runtime/MessageReminderLayout.d.ts +14 -0
  35. package/build/runtime/MessageReminderLayout.js +25 -0
  36. package/build/runtime/MessageReminders.d.ts +16 -0
  37. package/build/runtime/MessageReminders.js +364 -0
  38. package/build/runtime/index.d.ts +77 -0
  39. package/build/runtime/index.js +103 -0
  40. package/build/runtime/message-reminder-layout.less +40 -0
  41. package/build/runtime/messageRealtime.d.ts +15 -0
  42. package/build/runtime/messageRealtime.js +99 -0
  43. package/build/runtime/reminderEvents.d.ts +18 -0
  44. package/build/runtime/reminderEvents.js +53 -0
  45. package/build/runtime/reminderOwnership.d.ts +35 -0
  46. package/build/runtime/reminderOwnership.js +135 -0
  47. package/build/runtime/reminderPresentation.d.ts +34 -0
  48. package/build/runtime/reminderPresentation.js +57 -0
  49. package/build/runtime/reminderQuery.d.ts +18 -0
  50. package/build/runtime/reminderQuery.js +33 -0
  51. package/build/runtime/reminderQueue.d.ts +25 -0
  52. package/build/runtime/reminderQueue.js +88 -0
  53. package/build/services/MessageController.d.ts +108 -0
  54. package/build/services/MessageController.js +42 -0
  55. package/build/types/index.d.ts +12 -0
  56. package/build/types/index.js +1 -0
  57. package/package.json +95 -4
@@ -0,0 +1,275 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EyeOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons';
3
+ import { PageContainer, ProTable, } from '@ant-design/pro-components';
4
+ import { Alert, Button, Col, Form, Input, Modal, Popconfirm, Row, Select, Space, Switch, Tag, Typography, message, } from 'antd';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+ import { useAccess } from '../../runtime/index.js';
7
+ import { dispatchMessageReminderPreview } from '../../runtime/reminderEvents.js';
8
+ import { getMessageSubscriptionModes, pageMessageTopics, removeMessageTopic, saveMessageTopic, showMessageError, syncMessageTopicSubscriptions, } from '../../services/MessageController.js';
9
+ // 两组设置各自定义完整选项,不按级别禁用提醒方式,让业务自由搭配。
10
+ const reminderModeLabels = {
11
+ FULLSCREEN: '全屏弹框',
12
+ CAROUSEL: '轮播条',
13
+ FLOATING: '浮动通知',
14
+ };
15
+ const messageLevelLabels = {
16
+ NORMAL: '普通',
17
+ IMPORTANT: '重要',
18
+ SEVERE: '严重',
19
+ };
20
+ const messageLevelColors = {
21
+ NORMAL: 'default',
22
+ IMPORTANT: 'orange',
23
+ SEVERE: 'red',
24
+ };
25
+ export default function TopicPage() {
26
+ const access = useAccess();
27
+ const actionRef = useRef();
28
+ const [form] = Form.useForm();
29
+ const [open, setOpen] = useState(false);
30
+ const [editing, setEditing] = useState();
31
+ const [saving, setSaving] = useState(false);
32
+ const [removingId, setRemovingId] = useState();
33
+ const [syncingId, setSyncingId] = useState();
34
+ const [subscriptionModes, setSubscriptionModes] = useState([]);
35
+ const [modesLoading, setModesLoading] = useState(true);
36
+ const [modesFailed, setModesFailed] = useState(false);
37
+ const modesVersion = useRef(0);
38
+ const syncId = useRef();
39
+ const mounted = useRef(true);
40
+ const canSave = access.verifyElementPermission('MessageTopicSaveBtn');
41
+ const canRemove = access.verifyElementPermission('MessageTopicRemoveBtn');
42
+ const loadSubscriptionModes = useCallback(async () => {
43
+ const version = ++modesVersion.current;
44
+ setModesLoading(true);
45
+ setModesFailed(false);
46
+ try {
47
+ const { data } = await getMessageSubscriptionModes();
48
+ if (version !== modesVersion.current)
49
+ return;
50
+ // 订阅策略由服务端提供,新增扩展模式时无需修改前端枚举。
51
+ setSubscriptionModes(data || []);
52
+ }
53
+ catch (error) {
54
+ if (version !== modesVersion.current)
55
+ return;
56
+ setModesFailed(true);
57
+ showMessageError(error, '加载订阅模式失败');
58
+ }
59
+ finally {
60
+ if (version === modesVersion.current)
61
+ setModesLoading(false);
62
+ }
63
+ }, []);
64
+ useEffect(() => {
65
+ mounted.current = true;
66
+ void loadSubscriptionModes();
67
+ return () => {
68
+ mounted.current = false;
69
+ modesVersion.current += 1;
70
+ };
71
+ }, [loadSubscriptionModes]);
72
+ const editTopic = (record) => {
73
+ setEditing(record);
74
+ form.resetFields();
75
+ form.setFieldsValue({
76
+ ...(record || {
77
+ enabled: true,
78
+ }),
79
+ subscriptionMode: record?.subscriptionMode || 'ALL_ACCOUNTS',
80
+ // 编辑历史主题时显式回填默认值,避免缺字段导致表单空选或保存丢失默认配置。
81
+ reminderMode: record?.reminderMode || 'FLOATING',
82
+ messageLevel: record?.messageLevel || 'NORMAL',
83
+ });
84
+ setOpen(true);
85
+ };
86
+ const save = async () => {
87
+ if (!canSave || modesLoading || modesFailed)
88
+ return;
89
+ // 校验失败留在表单内提示;服务失败保留已输入内容,便于修正后重试。
90
+ const values = await form.validateFields().catch(() => undefined);
91
+ if (!values)
92
+ return;
93
+ setSaving(true);
94
+ try {
95
+ await saveMessageTopic({
96
+ ...values,
97
+ id: editing?.id,
98
+ reminderMode: values.reminderMode || 'FLOATING',
99
+ messageLevel: values.messageLevel || 'NORMAL',
100
+ subscriptionMode: values.subscriptionMode || 'ALL_ACCOUNTS',
101
+ });
102
+ message.success('消息主题已保存');
103
+ setOpen(false);
104
+ actionRef.current?.reload();
105
+ }
106
+ catch (error) {
107
+ showMessageError(error, '保存消息主题失败');
108
+ }
109
+ finally {
110
+ setSaving(false);
111
+ }
112
+ };
113
+ const syncSubscriptions = async (record) => {
114
+ if (!canSave || !mounted.current || syncId.current)
115
+ return;
116
+ // 同步可能创建多条账户订阅,连续点击由 ref 拦截,按钮状态用于反馈进度。
117
+ syncId.current = record.id;
118
+ setSyncingId(record.id);
119
+ try {
120
+ const { data } = await syncMessageTopicSubscriptions(record.id);
121
+ if (!mounted.current)
122
+ return;
123
+ message.success(`订阅已同步,新增 ${data?.createdCount || 0} 条账户订阅`);
124
+ }
125
+ catch (error) {
126
+ if (mounted.current)
127
+ showMessageError(error, '同步消息订阅失败');
128
+ }
129
+ finally {
130
+ syncId.current = undefined;
131
+ if (mounted.current)
132
+ setSyncingId(undefined);
133
+ }
134
+ };
135
+ const previewReminder = async () => {
136
+ if (saving)
137
+ return;
138
+ // 预览只校验提醒选项,主题尚未填写完整时也能比较不同展示组合。
139
+ const presentation = await form
140
+ .validateFields(['reminderMode', 'messageLevel'])
141
+ .catch(() => undefined);
142
+ if (!presentation)
143
+ return;
144
+ const values = form.getFieldsValue();
145
+ const reminderMode = presentation.reminderMode || 'FLOATING';
146
+ const messageLevel = presentation.messageLevel || 'NORMAL';
147
+ const topicName = values.name?.trim() || '示例消息主题';
148
+ // 优先使用浏览器 UUID;内网 HTTP 环境可能不提供该方法,仍允许生成本地预览标识。
149
+ const previewToken = globalThis.crypto?.randomUUID?.() ||
150
+ `${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
151
+ const previewId = `preview:${previewToken}`;
152
+ const now = new Date().toISOString();
153
+ // 示例只交给全局展示组件;独立标识用于避免点击预览时读取或确认真实收件记录。
154
+ dispatchMessageReminderPreview({
155
+ id: previewId,
156
+ receiptId: previewId,
157
+ messageId: previewId,
158
+ topicCode: values.code?.trim() || 'PREVIEW_TOPIC',
159
+ topicName,
160
+ sourceSystem: values.sourceSystem?.trim() || 'Platform',
161
+ title: `提醒预览 · ${topicName}`,
162
+ content: `这是一条${messageLevelLabels[messageLevel]}级别的示例消息,采用${reminderModeLabels[reminderMode]}展示。预览不会保存配置、发布消息或改变收件状态。`,
163
+ createTime: now,
164
+ occurredAt: now,
165
+ reminderMode,
166
+ messageLevel,
167
+ });
168
+ };
169
+ const columns = [
170
+ { title: '主题编码', dataIndex: 'code', copyable: true, width: 190 },
171
+ { title: '主题名称', dataIndex: 'name', width: 190 },
172
+ { title: '来源系统', dataIndex: 'sourceSystem', width: 130 },
173
+ {
174
+ title: '提醒方式',
175
+ dataIndex: 'reminderMode',
176
+ width: 110,
177
+ search: false,
178
+ render: (_, record) => reminderModeLabels[record.reminderMode || 'FLOATING'],
179
+ },
180
+ {
181
+ title: '消息级别',
182
+ dataIndex: 'messageLevel',
183
+ width: 100,
184
+ search: false,
185
+ render: (_, record) => {
186
+ const level = record.messageLevel || 'NORMAL';
187
+ return (_jsx(Tag, { color: messageLevelColors[level], children: messageLevelLabels[level] }));
188
+ },
189
+ },
190
+ {
191
+ title: '启用状态',
192
+ dataIndex: 'enabled',
193
+ valueType: 'select',
194
+ valueEnum: { true: '启用', false: '停用' },
195
+ render: (_, record) => (_jsx(Tag, { color: record.enabled ? 'success' : 'default', children: record.enabled ? '启用' : '停用' })),
196
+ },
197
+ {
198
+ title: '订阅模式',
199
+ dataIndex: 'subscriptionMode',
200
+ search: false,
201
+ render: (_, record) => {
202
+ const mode = record.subscriptionMode || 'ALL_ACCOUNTS';
203
+ return (subscriptionModes.find((item) => item.code === mode)?.name || mode);
204
+ },
205
+ },
206
+ {
207
+ title: '说明',
208
+ dataIndex: 'description',
209
+ search: false,
210
+ ellipsis: true,
211
+ },
212
+ {
213
+ title: '操作',
214
+ valueType: 'option',
215
+ width: 260,
216
+ render: (_, record) => (_jsxs(Space, { children: [canSave && _jsx("a", { onClick: () => editTopic(record), children: "\u7F16\u8F91" }), canSave && (_jsx(Button, { size: "small", type: "link", icon: _jsx(SyncOutlined, {}), loading: syncingId === record.id, disabled: !!syncingId || removingId === record.id, onClick: () => void syncSubscriptions(record), children: "\u540C\u6B65\u8BA2\u9605" })), canRemove && (_jsx(Popconfirm, { title: "\u5220\u9664\u8BE5\u6D88\u606F\u4E3B\u9898\uFF1F", description: "\u5DF2\u6709\u8BA2\u9605\u6216\u6D88\u606F\u7684\u4E3B\u9898\u53EF\u80FD\u4E0D\u5141\u8BB8\u5220\u9664\u3002", onConfirm: async () => {
217
+ setRemovingId(record.id);
218
+ try {
219
+ await removeMessageTopic(record.id);
220
+ message.success('消息主题已删除');
221
+ actionRef.current?.reload();
222
+ }
223
+ catch (error) {
224
+ showMessageError(error, '删除消息主题失败');
225
+ }
226
+ finally {
227
+ setRemovingId(undefined);
228
+ }
229
+ }, children: _jsx(Button, { size: "small", type: "link", danger: true, loading: removingId === record.id, disabled: syncingId === record.id, children: "\u5220\u9664" }) }))] })),
230
+ },
231
+ ];
232
+ return (_jsxs(PageContainer, { title: "\u6D88\u606F\u4E3B\u9898", children: [modesFailed && (_jsx(Alert, { type: "error", showIcon: true, message: "\u8BA2\u9605\u6A21\u5F0F\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u540E\u4FDD\u5B58\u4E3B\u9898\u3002", action: _jsx(Button, { size: "small", onClick: () => void loadSubscriptionModes(), children: "\u91CD\u8BD5" }) })), _jsx(ProTable, { rowKey: "id", actionRef: actionRef, columns: columns,
233
+ // 列表保留主题管理字段,窄屏通过横向滚动展示,避免压缩新增配置的中文标签。
234
+ scroll: { x: 1440 }, pagination: { defaultPageSize: 20, showSizeChanger: true }, toolBarRender: () => canSave
235
+ ? [
236
+ _jsx(Button, { type: "primary", icon: _jsx(PlusOutlined, {}), onClick: () => editTopic(), children: "\u65B0\u589E\u4E3B\u9898" }, "create"),
237
+ ]
238
+ : [], request: async (params) => {
239
+ try {
240
+ const { data } = await pageMessageTopics({
241
+ ...params,
242
+ enabled: params.enabled === undefined
243
+ ? undefined
244
+ : params.enabled === true || params.enabled === 'true',
245
+ });
246
+ return {
247
+ data: data?.records || [],
248
+ total: data?.total || 0,
249
+ success: true,
250
+ };
251
+ }
252
+ catch (error) {
253
+ showMessageError(error, '加载消息主题失败');
254
+ return { data: [], total: 0, success: false };
255
+ }
256
+ } }), _jsx(Modal, { title: editing ? '编辑消息主题' : '新增消息主题', open: open, width: 680, onCancel: () => {
257
+ if (!saving)
258
+ setOpen(false);
259
+ }, onOk: save, confirmLoading: saving, okButtonProps: { disabled: !canSave || modesLoading || modesFailed }, cancelButtonProps: { disabled: saving }, destroyOnHidden: true, children: _jsxs(Form, { form: form, layout: "vertical", children: [_jsx(Form.Item, { name: "code", label: "\u4E3B\u9898\u7F16\u7801", rules: [
260
+ { required: true, whitespace: true, message: '请输入主题编码' },
261
+ ], children: _jsx(Input, { disabled: !!editing, placeholder: "\u4F8B\u5982 ERP_APPROVAL_NOTICE", maxLength: 64 }) }), _jsx(Form.Item, { name: "name", label: "\u4E3B\u9898\u540D\u79F0", rules: [
262
+ { required: true, whitespace: true, message: '请输入主题名称' },
263
+ ], children: _jsx(Input, { maxLength: 128 }) }), _jsx(Form.Item, { name: "sourceSystem", label: "\u6765\u6E90\u7CFB\u7EDF", rules: [
264
+ { required: true, whitespace: true, message: '请输入来源系统' },
265
+ ], children: _jsx(Input, { disabled: !!editing, placeholder: "\u4F8B\u5982 ERP\u3001OA\u3001CRM", maxLength: 64 }) }), _jsx(Form.Item, { name: "enabled", label: "\u542F\u7528", valuePropName: "checked", children: _jsx(Switch, {}) }), _jsx(Form.Item, { name: "subscriptionMode", label: "\u8BA2\u9605\u6A21\u5F0F", extra: "\u5168\u91CF\u8D26\u6237\u8BA2\u9605\u4F1A\u5728\u521B\u5EFA\u4E3B\u9898\u65F6\u7ED1\u5B9A\u6709\u6548\u8D26\u6237\uFF1B\u89D2\u8272\u8BA2\u9605\u7531\u89D2\u8272\u914D\u7F6E\u4E0E\u8D26\u6237\u7ED1\u5B9A\u5173\u7CFB\u786E\u5B9A\u3002\u540C\u6B65\u8BA2\u9605\u53EF\u8865\u9F50\u6211\u7684\u8BA2\u9605\u3002", rules: [{ required: true, message: '请选择订阅模式' }], children: _jsx(Select, { loading: modesLoading, disabled: modesLoading || modesFailed, options: subscriptionModes.map((item) => ({
266
+ value: item.code,
267
+ label: item.name,
268
+ })) }) }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, sm: 12, children: _jsx(Form.Item, { name: "reminderMode", label: "\u63D0\u9192\u65B9\u5F0F", children: _jsx(Select, { options: Object.entries(reminderModeLabels).map(([value, label]) => ({
269
+ value,
270
+ label,
271
+ })) }) }) }), _jsx(Col, { xs: 24, sm: 12, children: _jsx(Form.Item, { name: "messageLevel", label: "\u6D88\u606F\u7EA7\u522B", children: _jsx(Select, { options: Object.entries(messageLevelLabels).map(([value, label]) => ({
272
+ value,
273
+ label,
274
+ })) }) }) })] }), _jsxs(Space, { wrap: true, style: { marginBottom: 20 }, children: [_jsx(Button, { htmlType: "button", icon: _jsx(EyeOutlined, {}), disabled: saving, onClick: () => void previewReminder(), children: "\u9884\u89C8\u63D0\u9192" }), _jsx(Typography.Text, { type: "secondary", children: "\u4EC5\u5C55\u793A\u793A\u4F8B\uFF0C\u4E0D\u4FDD\u5B58\u914D\u7F6E\u6216\u4EA7\u751F\u65B0\u6D88\u606F\u3002" })] }), _jsx(Form.Item, { name: "description", label: "\u8BF4\u660E", children: _jsx(Input.TextArea, { rows: 3, maxLength: 1000, showCount: true }) })] }) })] }));
275
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.messageRoutes = exports.MESSAGE_BASE_PATH = exports.MESSAGE_PROJECT_CODE = void 0;
4
+ exports.createMessageRoutes = createMessageRoutes;
5
+ exports.MESSAGE_PROJECT_CODE = 'qiqb_platform';
6
+ /** 可选领域的前端前缀与后端 /qiqb_platform 前缀分别维护。 */
7
+ exports.MESSAGE_BASE_PATH = '/p/message';
8
+ /** 纯配置工厂不依赖 React/宿主运行时,可在 Umi、React Router 等路由装配阶段调用。 */
9
+ function createMessageRoutes({ projectCode = exports.MESSAGE_PROJECT_CODE, basePath, pathPrefix = exports.MESSAGE_BASE_PATH, } = {}) {
10
+ const base = (basePath || pathPrefix).replace(/\/$/, '');
11
+ return [
12
+ {
13
+ name: '消息主题',
14
+ path: `${base}/topics`,
15
+ permissionCode: 'MessageTopicPage',
16
+ component: '@qiqb/message-domain/pages/topic/TopicPage',
17
+ },
18
+ {
19
+ name: '我的订阅',
20
+ path: `${base}/my-subscriptions`,
21
+ permissionCode: 'MessageSubscriptionPage',
22
+ component: '@qiqb/message-domain/pages/subscription/PersonalSubscriptionPage',
23
+ },
24
+ {
25
+ name: '角色订阅',
26
+ path: `${base}/role-subscriptions`,
27
+ permissionCode: 'MessageRoleSubscriptionPage',
28
+ component: '@qiqb/message-domain/pages/subscription/RoleSubscriptionPage',
29
+ },
30
+ {
31
+ name: '我的消息',
32
+ path: `${base}/inbox`,
33
+ permissionCode: 'MessageInboxPage',
34
+ component: '@qiqb/message-domain/pages/inbox/InboxPage',
35
+ },
36
+ ].map((route) => ({ ...route, projectCode, hideInMenu: true }));
37
+ }
38
+ exports.messageRoutes = createMessageRoutes();
@@ -0,0 +1,22 @@
1
+ export interface MessageRouteDefinition {
2
+ name: string;
3
+ path: string;
4
+ hideInMenu: boolean;
5
+ permissionCode: string;
6
+ projectCode: string;
7
+ component: string;
8
+ }
9
+ export interface MessageRouteOptions {
10
+ /** 路由授权仍按使用方项目隔离;BIP 可传入 prokeeper_bip。 */
11
+ projectCode?: string;
12
+ /** 可选领域默认挂在 /p 下;宿主覆盖时同步配置 runtime.inboxPath。 */
13
+ basePath?: string;
14
+ /** 兼容早期装配示例,优先使用 basePath。 */
15
+ pathPrefix?: string;
16
+ }
17
+ export declare const MESSAGE_PROJECT_CODE = "qiqb_platform";
18
+ /** 可选领域的前端前缀与后端 /qiqb_platform 前缀分别维护。 */
19
+ export declare const MESSAGE_BASE_PATH = "/p/message";
20
+ /** 纯配置工厂不依赖 React/宿主运行时,可在 Umi、React Router 等路由装配阶段调用。 */
21
+ export declare function createMessageRoutes({ projectCode, basePath, pathPrefix, }?: MessageRouteOptions): MessageRouteDefinition[];
22
+ export declare const messageRoutes: MessageRouteDefinition[];
@@ -0,0 +1,22 @@
1
+ export interface MessageRouteDefinition {
2
+ name: string;
3
+ path: string;
4
+ hideInMenu: boolean;
5
+ permissionCode: string;
6
+ projectCode: string;
7
+ component: string;
8
+ }
9
+ export interface MessageRouteOptions {
10
+ /** 路由授权仍按使用方项目隔离;BIP 可传入 prokeeper_bip。 */
11
+ projectCode?: string;
12
+ /** 可选领域默认挂在 /p 下;宿主覆盖时同步配置 runtime.inboxPath。 */
13
+ basePath?: string;
14
+ /** 兼容早期装配示例,优先使用 basePath。 */
15
+ pathPrefix?: string;
16
+ }
17
+ export declare const MESSAGE_PROJECT_CODE = "qiqb_platform";
18
+ /** 可选领域的前端前缀与后端 /qiqb_platform 前缀分别维护。 */
19
+ export declare const MESSAGE_BASE_PATH = "/p/message";
20
+ /** 纯配置工厂不依赖 React/宿主运行时,可在 Umi、React Router 等路由装配阶段调用。 */
21
+ export declare function createMessageRoutes({ projectCode, basePath, pathPrefix, }?: MessageRouteOptions): MessageRouteDefinition[];
22
+ export declare const messageRoutes: MessageRouteDefinition[];
@@ -0,0 +1,34 @@
1
+ export const MESSAGE_PROJECT_CODE = 'qiqb_platform';
2
+ /** 可选领域的前端前缀与后端 /qiqb_platform 前缀分别维护。 */
3
+ export const MESSAGE_BASE_PATH = '/p/message';
4
+ /** 纯配置工厂不依赖 React/宿主运行时,可在 Umi、React Router 等路由装配阶段调用。 */
5
+ export function createMessageRoutes({ projectCode = MESSAGE_PROJECT_CODE, basePath, pathPrefix = MESSAGE_BASE_PATH, } = {}) {
6
+ const base = (basePath || pathPrefix).replace(/\/$/, '');
7
+ return [
8
+ {
9
+ name: '消息主题',
10
+ path: `${base}/topics`,
11
+ permissionCode: 'MessageTopicPage',
12
+ component: '@qiqb/message-domain/pages/topic/TopicPage',
13
+ },
14
+ {
15
+ name: '我的订阅',
16
+ path: `${base}/my-subscriptions`,
17
+ permissionCode: 'MessageSubscriptionPage',
18
+ component: '@qiqb/message-domain/pages/subscription/PersonalSubscriptionPage',
19
+ },
20
+ {
21
+ name: '角色订阅',
22
+ path: `${base}/role-subscriptions`,
23
+ permissionCode: 'MessageRoleSubscriptionPage',
24
+ component: '@qiqb/message-domain/pages/subscription/RoleSubscriptionPage',
25
+ },
26
+ {
27
+ name: '我的消息',
28
+ path: `${base}/inbox`,
29
+ permissionCode: 'MessageInboxPage',
30
+ component: '@qiqb/message-domain/pages/inbox/InboxPage',
31
+ },
32
+ ].map((route) => ({ ...route, projectCode, hideInMenu: true }));
33
+ }
34
+ export const messageRoutes = createMessageRoutes();
@@ -0,0 +1,14 @@
1
+ import type { PropsWithChildren } from 'react';
2
+ import './message-reminder-layout.less';
3
+ /**
4
+ * 宿主只提供内容区槽位,消息领域继续负责查询与展示。
5
+ * Provider 覆盖业务页面和全局提醒入口,使 body 中的提醒入口能找到内容区的真实布局位置。
6
+ */
7
+ export declare function MessageReminderProvider({ children }: PropsWithChildren): import("react").JSX.Element;
8
+ /** 没有页面槽位时保留排队的消息,避免将不可见的提醒记为已经展示。 */
9
+ export declare function useMessageReminderTarget(): HTMLElement | undefined;
10
+ /**
11
+ * 放在宿主页头下方,并包住完整内容区,包含页面自己的二级菜单。
12
+ * 消息栏使用内容区的全部宽度;空槽位不占空间,有消息时菜单与页面整体顺序排在其后。
13
+ */
14
+ export declare function MessageReminderLayout({ children }: PropsWithChildren): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useMemo, useState } from 'react';
3
+ import './message-reminder-layout.less';
4
+ const LayoutContext = createContext(undefined);
5
+ /**
6
+ * 宿主只提供内容区槽位,消息领域继续负责查询与展示。
7
+ * Provider 覆盖业务页面和全局提醒入口,使 body 中的提醒入口能找到内容区的真实布局位置。
8
+ */
9
+ export function MessageReminderProvider({ children }) {
10
+ const [target, setTarget] = useState(null);
11
+ const value = useMemo(() => ({ target, setTarget }), [target]);
12
+ return (_jsx(LayoutContext.Provider, { value: value, children: children }));
13
+ }
14
+ /** 没有页面槽位时保留排队的消息,避免将不可见的提醒记为已经展示。 */
15
+ export function useMessageReminderTarget() {
16
+ return useContext(LayoutContext)?.target || undefined;
17
+ }
18
+ /**
19
+ * 放在宿主页头下方,并包住完整内容区,包含页面自己的二级菜单。
20
+ * 消息栏使用内容区的全部宽度;空槽位不占空间,有消息时菜单与页面整体顺序排在其后。
21
+ */
22
+ export function MessageReminderLayout({ children }) {
23
+ const context = useContext(LayoutContext);
24
+ return (_jsxs("div", { className: "qiqb-message-reminder-layout", children: [context && (_jsx("div", { className: "qiqb-message-reminder-slot", ref: context.setTarget })), children] }));
25
+ }
@@ -0,0 +1,16 @@
1
+ import type { MessageReminderRenderer } from './reminderPresentation.js';
2
+ import { InboxMessage } from '../services/MessageController.js';
3
+ export interface MessageRemindersProps {
4
+ /** 登录账号变化会取消旧请求、释放 owner 并重新建立隔离队列。 */
5
+ accountId?: string;
6
+ onOpen?: (record: InboxMessage) => void;
7
+ /** 平台宿主可传 100;业务微应用默认 0,同一消息源同一账号只保留最高优先级入口。 */
8
+ priority?: number;
9
+ /** 当前入口的外观覆盖,优先于 runtime.reminderRenderer;所有者优先级仍由 priority 控制。 */
10
+ renderer?: MessageReminderRenderer;
11
+ }
12
+ /**
13
+ * 请求、账号和导航由宿主提供;当前文档通过跨 bundle owner 选举避免重复正式提醒。
14
+ * namespace 内的预览单独保存,非 owner 也可预览,且预览不会进入正式队列或调用阅读接口。
15
+ */
16
+ export default function MessageReminders({ accountId, onOpen, priority, renderer, }: MessageRemindersProps): import("react").JSX.Element;