@hkyhy/marsun-components-core 0.1.102 → 0.1.113

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 (37) hide show
  1. package/dist/components/Filter/createKneFilterField.js +42 -39
  2. package/dist/components/Filter/createKneFilterField.js.map +1 -1
  3. package/dist/components/Form/SuperSelect.js +48 -45
  4. package/dist/components/Form/SuperSelect.js.map +1 -1
  5. package/dist/components/ReactFilter/fields/index.js +33 -26
  6. package/dist/components/ReactFilter/fields/index.js.map +1 -1
  7. package/dist/components/ReactFilter/renderSelectListItemContent.d.ts +8 -0
  8. package/dist/components/ReactFilter/renderSelectListItemContent.js +12 -0
  9. package/dist/components/ReactFilter/renderSelectListItemContent.js.map +1 -0
  10. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/MessageTemplateAdmin.d.ts +1 -1
  11. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/MessageTemplateAdmin.js +158 -137
  12. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/MessageTemplateAdmin.js.map +1 -1
  13. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/Modal/TemplateFormModal.js +218 -126
  14. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/Modal/TemplateFormModal.js.map +1 -1
  15. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/PushRulesPanel/index.d.ts +8 -1
  16. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/PushRulesPanel/index.js +310 -170
  17. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/PushRulesPanel/index.js.map +1 -1
  18. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/VariablesPanel/index.js +87 -76
  19. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/VariablesPanel/index.js.map +1 -1
  20. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/audienceSlots.d.ts +14 -0
  21. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/audienceSlots.js +31 -0
  22. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/audienceSlots.js.map +1 -0
  23. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/index.d.ts +3 -1
  24. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/style.module.scss.js +18 -6
  25. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/style.module.scss.js.map +1 -1
  26. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/types.d.ts +27 -0
  27. package/dist/components/Tools/MessageCenter/MessageTemplateAdmin/types.js.map +1 -1
  28. package/dist/components/Tools/MessageCenter/index.d.ts +3 -1
  29. package/dist/components/Tools/MessageCenter/utils/audienceUserSelectOptions.d.ts +16 -0
  30. package/dist/components/Tools/MessageCenter/utils/audienceUserSelectOptions.js +33 -0
  31. package/dist/components/Tools/MessageCenter/utils/audienceUserSelectOptions.js.map +1 -0
  32. package/dist/components/Tools/MessageCenter/utils/templateCode.js +11 -17
  33. package/dist/components/Tools/MessageCenter/utils/templateCode.js.map +1 -1
  34. package/dist/components/Tools/index.d.ts +1 -1
  35. package/dist/marsun-components-core.css +1 -1
  36. package/dist/styles/global.css +1 -1
  37. package/package.json +8 -1
@@ -1,191 +1,283 @@
1
- import { jsx as r, jsxs as c, Fragment as $ } from "react/jsx-runtime";
2
- import { RichTextField as q } from "../../../../FormInfo/RichTextField/index.js";
3
- import { normalizeTemplatePlaceholders as b, applyTemplateVars as H, stripHtmlToText as z } from "../../utils/templateCode.js";
4
- import { Typography as y, message as F, Select as B } from "antd";
5
- import { useState as I, useEffect as C, useMemo as x, useCallback as E } from "react";
6
- import j from "../style.module.scss.js";
7
- import { Input as A, Select as O } from "../../../../FormInfo/fields.js";
8
- import Q from "../../../../FormInfo/FormModal.js";
9
- import _ from "../../../../FormInfo/FormInfo.js";
10
- import G from "../../../../FormInfo/FormItem.js";
11
- const re = ({
12
- open: T,
13
- isCreate: s,
1
+ import { jsx as s, jsxs as y } from "react/jsx-runtime";
2
+ import { RichTextField as Q } from "../../../../FormInfo/RichTextField/index.js";
3
+ import { sanitizeInboxHtml as V } from "../../../Inbox/sanitizeInboxHtml.js";
4
+ import { normalizeTemplatePlaceholders as v, applyTemplateVars as D } from "../../utils/templateCode.js";
5
+ import { Typography as k, message as b, Modal as q, Collapse as G } from "antd";
6
+ import { useState as H, useEffect as U, useMemo as I, useCallback as i } from "react";
7
+ import { AUDIENCE_SLOT_OPTIONS as J, normalizeAudienceSlots as X, hasAudienceSelection as Y, ORG_TENANT_SLOT as Z } from "../audienceSlots.js";
8
+ import t from "../style.module.scss.js";
9
+ import { Input as A, Select as C } from "../../../../FormInfo/fields.js";
10
+ import W from "../../../../FormInfo/FormModal.js";
11
+ import ee from "../../../../FormInfo/FormInfo.js";
12
+ import oe from "../../../../FormInfo/FormItem.js";
13
+ const K = {
14
+ getPopupContainer: () => document.body,
15
+ styles: { popup: { root: { zIndex: 2e3 } } }
16
+ }, ye = ({
17
+ open: w,
18
+ isCreate: n,
14
19
  initial: e,
15
- catalog: n,
16
- roleOptions: K,
17
- variables: f,
18
- catalogError: a,
19
- previewVars: P,
20
- canWrite: m,
21
- onCancel: g,
22
- onSubmit: R,
23
- renderAudienceField: k
20
+ catalog: p,
21
+ roleOptions: S,
22
+ variables: T,
23
+ catalogError: u,
24
+ previewVars: F,
25
+ canWrite: r,
26
+ onCancel: R,
27
+ onSubmit: M,
28
+ renderAudienceField: a
24
29
  }) => {
25
- const [d, v] = I([]), [w, u] = I(!1);
26
- C(() => {
27
- if (!T || !e) {
28
- u(!1);
30
+ const [x, E] = H([]), [N, h] = H(!1);
31
+ U(() => {
32
+ if (!w || !e) {
33
+ h(!1);
29
34
  return;
30
35
  }
31
- v(e.audienceRoles || e.roles || []);
32
- const o = window.requestAnimationFrame(() => u(!0));
36
+ E(e.audienceRoles || e.roles || []);
37
+ const o = window.requestAnimationFrame(() => h(!0));
33
38
  return () => window.cancelAnimationFrame(o);
34
- }, [T, e]);
35
- const M = (e == null ? void 0 : e.id) || (e == null ? void 0 : e.code) || (s ? "create" : "edit"), S = x(() => e ? {
39
+ }, [w, e]);
40
+ const _ = (e == null ? void 0 : e.id) || (e == null ? void 0 : e.code) || (n ? "create" : "edit"), P = I(() => e ? {
36
41
  code: e.code,
37
42
  eventKey: e.eventKey,
38
43
  scenario: e.scenario || e.label || "",
39
- titleTemplate: b(
44
+ titleTemplate: v(
40
45
  e.titleTemplate || e.titlePreview || ""
41
46
  ),
42
- bodyTemplate: b(e.bodyTemplate || "")
43
- } : {}, [e]), D = a ? `目录加载失败,无法插入变量(${a})` : f.length === 0 ? "暂无 catalog 变量" : "", L = E((o) => {
44
- u(!!o);
45
- }, []), N = x(() => {
46
- if (!w)
47
+ bodyTemplate: v(e.bodyTemplate || ""),
48
+ audienceRoles: e.audienceRoles || e.roles || [],
49
+ audienceSlots: e.audienceSlots || []
50
+ } : {}, [e]), z = u ? `目录加载失败,无法插入变量(${u})` : T.length === 0 ? "暂无 catalog 变量" : "", B = i((o) => {
51
+ h(!!o);
52
+ }, []), $ = I(() => {
53
+ if (!N)
47
54
  return [
48
- /* @__PURE__ */ r(y.Text, { type: "secondary", children: "加载编辑器…" }, "loading")
55
+ /* @__PURE__ */ s(k.Text, { type: "secondary", children: "加载编辑器…" }, "loading")
49
56
  ];
50
57
  const o = [];
51
- return s || o.push(/* @__PURE__ */ r(A, { name: "code", label: "编号", disabled: !0 }, "code")), o.push(
52
- /* @__PURE__ */ r(
53
- O,
58
+ return n || o.push(/* @__PURE__ */ s(A, { name: "code", label: "编号", disabled: !0 }, "code")), o.push(
59
+ /* @__PURE__ */ s(
60
+ C,
54
61
  {
55
62
  name: "eventKey",
56
63
  label: "适用场景",
57
64
  rule: "REQ",
58
- disabled: !m,
65
+ disabled: !r,
59
66
  optionLabelProp: "label",
60
- options: n.map((t) => ({
61
- value: t.eventKey,
62
- label: t.label || t.eventKey
67
+ options: p.map((l) => ({
68
+ value: l.eventKey,
69
+ label: l.label || l.eventKey
63
70
  })),
64
- placeholder: n.length ? "选择场景" : "暂无事件目录"
71
+ placeholder: p.length ? "选择场景" : "暂无事件目录",
72
+ ...K
65
73
  },
66
74
  "eventKey"
67
75
  ),
68
- /* @__PURE__ */ r(
76
+ /* @__PURE__ */ s(
69
77
  A,
70
78
  {
71
79
  name: "titleTemplate",
72
80
  label: "标题模板",
73
- disabled: !m,
81
+ rule: "REQ",
82
+ disabled: !r,
74
83
  enableVariableMention: !0,
75
- variables: f,
76
- catalogError: a,
84
+ variables: T,
85
+ catalogError: u,
77
86
  placeholder: "点「插入变量」选择 catalog 变量",
78
- labelTips: s ? "新建默认停用。标题:点「插入变量」插入明文 {{key}}(可手改)。正文:原子色块,不可改字,整颗删除。" : "标题可手改 {{key}};正文为原子色块(不可改字、Backspace 整颗删)。"
87
+ labelTips: n ? "新建默认停用。标题:点「插入变量」插入明文 {{key}}(可手改)。正文:原子色块,不可改字,整颗删除。" : "标题可手改 {{key}};正文为原子色块(不可改字、Backspace 整颗删)。"
79
88
  },
80
89
  "titleTemplate"
81
90
  ),
82
- /* @__PURE__ */ r(
83
- q,
91
+ /* @__PURE__ */ s(
92
+ Q,
84
93
  {
85
94
  name: "bodyTemplate",
86
95
  label: "正文模板",
87
- disabled: !m,
96
+ disabled: !r,
88
97
  enableVariableMention: !0,
89
- variables: f,
90
- catalogError: a,
91
- editorKey: M,
98
+ variables: T,
99
+ catalogError: u,
100
+ editorKey: _,
92
101
  placeholder: "编辑正文,点「插入变量」",
93
102
  minHeight: 160
94
103
  },
95
104
  "bodyTemplate"
96
105
  )
106
+ ), a || o.push(
107
+ /* @__PURE__ */ s(
108
+ C,
109
+ {
110
+ name: "audienceRoles",
111
+ label: "受众角色",
112
+ mode: "multiple",
113
+ allowClear: !0,
114
+ disabled: !r,
115
+ options: S.map((l) => ({
116
+ value: l.code,
117
+ label: l.name || l.code
118
+ })),
119
+ optionFilterProp: "label",
120
+ maxTagCount: "responsive",
121
+ placeholder: S.length ? "下拉选择角色(可多选)" : "暂无角色数据",
122
+ notFoundContent: "暂无角色",
123
+ labelTips: "可与「通知范围」并存;至少选角色或槽位之一。emit 与显式 userIds 并集送达。",
124
+ ...K
125
+ },
126
+ "audienceRoles"
127
+ ),
128
+ /* @__PURE__ */ s(
129
+ C,
130
+ {
131
+ name: "audienceSlots",
132
+ label: "通知范围",
133
+ mode: "multiple",
134
+ allowClear: !0,
135
+ disabled: !r,
136
+ options: J.map((l) => ({
137
+ value: l.value,
138
+ label: l.label
139
+ })),
140
+ optionFilterProp: "label",
141
+ maxTagCount: "responsive",
142
+ placeholder: "可选:本人/领导/部门/分厂/租户或任务当事人",
143
+ notFoundContent: "暂无槽位",
144
+ labelTips: "依赖业务 emit.audienceContext;无上下文的槽不会发出。选「本租户」保存前会确认。",
145
+ ...K
146
+ },
147
+ "audienceSlots"
148
+ )
97
149
  ), o;
98
- }, [w, s, m, n, f, M, a]), V = x(
150
+ }, [
151
+ N,
152
+ n,
153
+ r,
154
+ p,
155
+ T,
156
+ _,
157
+ u,
158
+ a,
159
+ S
160
+ ]), j = I(
99
161
  () => ({
100
- data: S,
162
+ data: P,
101
163
  onSubmit: async (o) => {
102
- if (!m)
103
- return F.warning("当前为只读(业务未授予写权限)"), !1;
104
- const t = String(o.eventKey || "").trim();
105
- if (!t)
106
- return F.warning("请选择适用场景"), !1;
107
- const l = n.find((p) => p.eventKey === t), h = String((l == null ? void 0 : l.label) || o.scenario || t).trim();
164
+ if (!r)
165
+ return b.warning("当前为只读(业务未授予写权限)"), !1;
166
+ const l = String(o.eventKey || "").trim();
167
+ if (!l)
168
+ return b.warning("请选择适用场景"), !1;
169
+ const g = v(String(o.titleTemplate || ""));
170
+ if (!g.trim())
171
+ return b.warning("请填写标题模板"), !1;
172
+ const f = a ? x : Array.isArray(o.audienceRoles) ? o.audienceRoles : [], m = X(o.audienceSlots || []);
173
+ if (!Y({
174
+ roles: f,
175
+ slots: m,
176
+ userIds: null
177
+ }))
178
+ return b.warning("请选择受众角色或通知范围(至少一个)"), !1;
179
+ if (m.includes(Z) && !await new Promise((O) => {
180
+ q.confirm({
181
+ title: "确认通知本租户全员?",
182
+ content: "已选「本租户(全公司)」:将向当前租户内用户展开(≠跨租户)。确认保存?",
183
+ okText: "确认保存",
184
+ cancelText: "取消",
185
+ onOk: () => O(!0),
186
+ onCancel: () => O(!1)
187
+ });
188
+ }))
189
+ return !1;
190
+ const d = p.find((c) => c.eventKey === l), L = String((d == null ? void 0 : d.label) || o.scenario || l).trim();
108
191
  try {
109
- await R({
192
+ await M({
110
193
  ...e || {},
111
194
  code: o.code || (e == null ? void 0 : e.code),
112
- eventKey: t,
113
- scenario: h,
114
- label: h,
115
- messageType: (l == null ? void 0 : l.messageType) || (e == null ? void 0 : e.messageType) || "alert",
116
- titleTemplate: b(String(o.titleTemplate || "")),
117
- bodyTemplate: b(String(o.bodyTemplate || "")),
118
- audienceRoles: d,
119
- roles: d,
120
- enabled: s ? !1 : (e == null ? void 0 : e.enabled) !== !1,
195
+ eventKey: l,
196
+ scenario: L,
197
+ label: L,
198
+ messageType: (d == null ? void 0 : d.messageType) || (e == null ? void 0 : e.messageType) || "alert",
199
+ titleTemplate: g,
200
+ bodyTemplate: v(String(o.bodyTemplate || "")),
201
+ audienceRoles: f,
202
+ roles: f,
203
+ audienceSlots: m,
204
+ enabled: n ? !1 : (e == null ? void 0 : e.enabled) !== !1,
121
205
  channel: (e == null ? void 0 : e.channel) || "in_app"
122
- }), g();
123
- } catch (p) {
124
- return F.error(p instanceof Error ? p.message : String(p)), !1;
206
+ }), R();
207
+ } catch (c) {
208
+ return b.error(c instanceof Error ? c.message : String(c)), !1;
125
209
  }
126
210
  }
127
211
  }),
128
- [S, m, n, R, e, d, s, g]
212
+ [
213
+ P,
214
+ r,
215
+ p,
216
+ M,
217
+ e,
218
+ x,
219
+ n,
220
+ R,
221
+ a
222
+ ]
129
223
  );
130
- return /* @__PURE__ */ c(
131
- Q,
224
+ return /* @__PURE__ */ y(
225
+ W,
132
226
  {
133
- title: s ? "新建模板" : "编辑模板",
134
- open: T,
135
- onCancel: g,
227
+ title: n ? "新建模板" : "编辑模板",
228
+ open: w,
229
+ onCancel: R,
136
230
  width: 720,
231
+ size: "small",
232
+ className: "msg-center-form-modal",
137
233
  okText: "保存",
138
234
  autoClose: !1,
139
235
  focusable: { trap: !1 },
140
- afterOpenChange: L,
141
- formProps: V,
236
+ afterOpenChange: B,
237
+ formProps: j,
142
238
  children: [
143
- /* @__PURE__ */ r(_, { column: 1, list: N }),
144
- w ? /* @__PURE__ */ r(G, { children: (o) => {
145
- const t = (typeof o.getFormData == "function" ? o.getFormData() : void 0) ?? o.formData ?? S, l = H(String(t.titleTemplate || ""), P), h = H(
146
- z(String(t.bodyTemplate || "")),
147
- P
148
- );
149
- return /* @__PURE__ */ c($, { children: [
150
- D ? /* @__PURE__ */ r(y.Text, { type: "secondary", children: D }) : null,
151
- /* @__PURE__ */ c(y.Paragraph, { type: "secondary", className: j.preview, children: [
152
- "标题预览:",
153
- l || "—"
154
- ] }),
155
- /* @__PURE__ */ c(y.Paragraph, { type: "secondary", className: j.preview, children: [
156
- "正文预览:",
157
- h || "—"
158
- ] })
239
+ /* @__PURE__ */ s(ee, { column: 1, list: $ }),
240
+ a ? /* @__PURE__ */ y("div", { className: t.audienceBlock, children: [
241
+ /* @__PURE__ */ y(k.Text, { className: t.audienceLabel, children: [
242
+ /* @__PURE__ */ s("span", { style: { color: "#ff4d4f", marginRight: 4 }, children: "*" }),
243
+ "受众角色"
244
+ ] }),
245
+ a({ roles: x, onChange: E })
246
+ ] }) : null,
247
+ N ? /* @__PURE__ */ s(oe, { children: (o) => {
248
+ const l = (typeof o.getFormData == "function" ? o.getFormData() : void 0) ?? o.formData ?? P, g = D(String(l.titleTemplate || ""), F), f = D(String(l.bodyTemplate || ""), F), m = V(f);
249
+ return /* @__PURE__ */ y("div", { className: t.previewCollapse, children: [
250
+ z ? /* @__PURE__ */ s(k.Text, { type: "secondary", children: z }) : null,
251
+ /* @__PURE__ */ s(
252
+ G,
253
+ {
254
+ ghost: !0,
255
+ size: "small",
256
+ items: [
257
+ {
258
+ key: "preview",
259
+ label: "效果预览",
260
+ children: /* @__PURE__ */ y("div", { className: t.previewCard, children: [
261
+ /* @__PURE__ */ s("div", { className: t.previewTitle, children: g.trim() || /* @__PURE__ */ s("span", { className: t.previewEmpty, children: "(无标题)" }) }),
262
+ m ? /* @__PURE__ */ s(
263
+ "div",
264
+ {
265
+ className: t.previewBody,
266
+ dangerouslySetInnerHTML: { __html: m }
267
+ }
268
+ ) : /* @__PURE__ */ s("div", { className: `${t.previewBody} ${t.previewEmpty}`, children: "(无正文)" })
269
+ ] })
270
+ }
271
+ ]
272
+ }
273
+ )
159
274
  ] });
160
- } }) : null,
161
- /* @__PURE__ */ c("div", { style: { marginTop: 12 }, children: [
162
- /* @__PURE__ */ r(y.Text, { children: "受众角色" }),
163
- k ? k({ roles: d, onChange: v }) : /* @__PURE__ */ r(
164
- B,
165
- {
166
- mode: "multiple",
167
- allowClear: !0,
168
- showSearch: !0,
169
- style: { width: "100%", marginTop: 8 },
170
- value: d,
171
- disabled: !m,
172
- placeholder: K.length ? "下拉选择角色(可多选)" : "暂无角色数据",
173
- options: K.map((o) => ({
174
- value: o.code,
175
- label: o.name || o.code
176
- })),
177
- optionFilterProp: "label",
178
- maxTagCount: "responsive",
179
- onChange: (o) => v(o || []),
180
- notFoundContent: "暂无角色"
181
- }
182
- )
183
- ] })
275
+ } }) : null
184
276
  ]
185
277
  }
186
278
  );
187
279
  };
188
280
  export {
189
- re as TemplateFormModal
281
+ ye as TemplateFormModal
190
282
  };
191
283
  //# sourceMappingURL=TemplateFormModal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateFormModal.js","sources":["../../../../../../src/components/Tools/MessageCenter/MessageTemplateAdmin/Modal/TemplateFormModal.tsx"],"sourcesContent":["import { FormInfo, FormItem, FormModal, Input, Select } from '@/components/FormInfo';\nimport { RichTextField } from '@/components/FormInfo/RichTextField';\nimport {\n applyTemplateVars,\n normalizeTemplatePlaceholders,\n stripHtmlToText,\n} from '../../utils/templateCode';\nimport { Select as AntSelect, Typography, message } from 'antd';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport type {\n MessageAudienceRoleOption,\n MessageEventCatalogItem,\n MessageTemplateAdminItem,\n MessageTemplateVariable,\n} from '../types';\nimport styles from '../style.module.scss';\n\nexport type TemplateFormModalProps = {\n open: boolean;\n isCreate: boolean;\n initial: MessageTemplateAdminItem | null;\n catalog: MessageEventCatalogItem[];\n roleOptions: MessageAudienceRoleOption[];\n variables: MessageTemplateVariable[];\n /** catalog 请求失败时的短文案(与「变量为空」区分) */\n catalogError?: string;\n previewVars: Record<string, string>;\n canWrite: boolean;\n onCancel: () => void;\n onSubmit: (item: MessageTemplateAdminItem) => Promise<void>;\n renderAudienceField?: (ctx: {\n roles: string[];\n onChange: (roles: string[]) => void;\n }) => React.ReactNode;\n};\n\ntype FormShape = {\n code?: string;\n eventKey?: string;\n scenario?: string;\n titleTemplate?: string;\n bodyTemplate?: string;\n};\n\ntype FormApiLike = {\n formData?: FormShape;\n getFormData?: () => FormShape;\n};\n\n/**\n * 模板新建/编辑:FormModal + FormInfo;正文 RichTextField。\n * CKEditor 在 open 后微任务挂载,避免与 Modal focus/动画互抢;禁 mention uplift 死循环。\n */\nexport const TemplateFormModal: React.FC<TemplateFormModalProps> = ({\n open,\n isCreate,\n initial,\n catalog,\n roleOptions,\n variables,\n catalogError,\n previewVars,\n canWrite,\n onCancel,\n onSubmit,\n renderAudienceField,\n}) => {\n const [roles, setRoles] = useState<string[]>([]);\n const [editorReady, setEditorReady] = useState(false);\n\n useEffect(() => {\n if (!open || !initial) {\n setEditorReady(false);\n return;\n }\n setRoles(initial.audienceRoles || initial.roles || []);\n // 打开后再挂编辑器(afterOpenChange 兜底 + rAF)\n const id = window.requestAnimationFrame(() => setEditorReady(true));\n return () => window.cancelAnimationFrame(id);\n }, [open, initial]);\n\n const editorKey = initial?.id || initial?.code || (isCreate ? 'create' : 'edit');\n\n const formData = useMemo<FormShape>(() => {\n if (!initial) return {};\n return {\n code: initial.code,\n eventKey: initial.eventKey,\n scenario: initial.scenario || initial.label || '',\n titleTemplate: normalizeTemplatePlaceholders(\n initial.titleTemplate || initial.titlePreview || '',\n ),\n bodyTemplate: normalizeTemplatePlaceholders(initial.bodyTemplate || ''),\n };\n }, [initial]);\n\n const varsEmptyHint = catalogError\n ? `目录加载失败,无法插入变量(${catalogError})`\n : variables.length === 0\n ? '暂无 catalog 变量'\n : '';\n\n const handleAfterOpenChange = useCallback((nextOpen: boolean) => {\n if (nextOpen) setEditorReady(true);\n else setEditorReady(false);\n }, []);\n\n const fieldList = useMemo(() => {\n if (!editorReady) {\n return [\n <Typography.Text key=\"loading\" type=\"secondary\">\n 加载编辑器…\n </Typography.Text>,\n ];\n }\n const fields: React.ReactNode[] = [];\n if (!isCreate) {\n fields.push(<Input key=\"code\" name=\"code\" label=\"编号\" disabled />);\n }\n // 适用场景 = catalog 中文下拉(value=eventKey);禁自由输入英文 key\n fields.push(\n <Select\n key=\"eventKey\"\n name=\"eventKey\"\n label=\"适用场景\"\n rule=\"REQ\"\n disabled={!canWrite}\n optionLabelProp=\"label\"\n options={catalog.map((c) => ({\n value: c.eventKey,\n label: c.label || c.eventKey,\n }))}\n placeholder={catalog.length ? '选择场景' : '暂无事件目录'}\n />,\n <Input\n key=\"titleTemplate\"\n name=\"titleTemplate\"\n label=\"标题模板\"\n disabled={!canWrite}\n enableVariableMention\n variables={variables}\n catalogError={catalogError}\n placeholder=\"点「插入变量」选择 catalog 变量\"\n labelTips={\n isCreate\n ? '新建默认停用。标题:点「插入变量」插入明文 {{key}}(可手改)。正文:原子色块,不可改字,整颗删除。'\n : '标题可手改 {{key}};正文为原子色块(不可改字、Backspace 整颗删)。'\n }\n />,\n <RichTextField\n key=\"bodyTemplate\"\n name=\"bodyTemplate\"\n label=\"正文模板\"\n disabled={!canWrite}\n enableVariableMention\n variables={variables}\n catalogError={catalogError}\n editorKey={editorKey}\n placeholder=\"编辑正文,点「插入变量」\"\n minHeight={160}\n />,\n );\n return fields;\n }, [editorReady, isCreate, canWrite, catalog, variables, editorKey, catalogError]);\n\n const formProps = useMemo(\n () => ({\n data: formData,\n onSubmit: async (data: FormShape) => {\n if (!canWrite) {\n message.warning('当前为只读(业务未授予写权限)');\n return false;\n }\n const eventKey = String(data.eventKey || '').trim();\n if (!eventKey) {\n message.warning('请选择适用场景');\n return false;\n }\n const hit = catalog.find((c) => c.eventKey === eventKey);\n const scenario = String(hit?.label || data.scenario || eventKey).trim();\n try {\n await onSubmit({\n ...(initial || {}),\n code: data.code || initial?.code,\n eventKey,\n scenario,\n label: scenario,\n messageType: hit?.messageType || initial?.messageType || 'alert',\n titleTemplate: normalizeTemplatePlaceholders(String(data.titleTemplate || '')),\n bodyTemplate: normalizeTemplatePlaceholders(String(data.bodyTemplate || '')),\n audienceRoles: roles,\n roles,\n enabled: isCreate ? false : initial?.enabled !== false,\n channel: initial?.channel || 'in_app',\n });\n onCancel();\n } catch (e) {\n message.error(e instanceof Error ? e.message : String(e));\n return false;\n }\n },\n }),\n [formData, canWrite, catalog, onSubmit, initial, roles, isCreate, onCancel],\n );\n\n return (\n <FormModal\n title={isCreate ? '新建模板' : '编辑模板'}\n open={open}\n onCancel={onCancel}\n width={720}\n okText=\"保存\"\n autoClose={false}\n focusable={{ trap: false }}\n afterOpenChange={handleAfterOpenChange}\n formProps={formProps}\n >\n <FormInfo column={1} list={fieldList} />\n {editorReady ? (\n <FormItem>\n {(api: FormApiLike) => {\n const data =\n (typeof api.getFormData === 'function' ? api.getFormData() : undefined) ??\n api.formData ??\n formData;\n const titlePreview = applyTemplateVars(String(data.titleTemplate || ''), previewVars);\n const bodyPreview = applyTemplateVars(\n stripHtmlToText(String(data.bodyTemplate || '')),\n previewVars,\n );\n return (\n <>\n {varsEmptyHint ? (\n <Typography.Text type=\"secondary\">{varsEmptyHint}</Typography.Text>\n ) : null}\n <Typography.Paragraph type=\"secondary\" className={styles.preview}>\n 标题预览:{titlePreview || '—'}\n </Typography.Paragraph>\n <Typography.Paragraph type=\"secondary\" className={styles.preview}>\n 正文预览:{bodyPreview || '—'}\n </Typography.Paragraph>\n </>\n );\n }}\n </FormItem>\n ) : null}\n <div style={{ marginTop: 12 }}>\n <Typography.Text>受众角色</Typography.Text>\n {renderAudienceField ? (\n renderAudienceField({ roles, onChange: setRoles })\n ) : (\n <AntSelect\n mode=\"multiple\"\n allowClear\n showSearch\n style={{ width: '100%', marginTop: 8 }}\n value={roles}\n disabled={!canWrite}\n placeholder={roleOptions.length ? '下拉选择角色(可多选)' : '暂无角色数据'}\n options={roleOptions.map((r) => ({\n value: r.code,\n label: r.name || r.code,\n }))}\n optionFilterProp=\"label\"\n maxTagCount=\"responsive\"\n onChange={(v) => setRoles(v || [])}\n notFoundContent=\"暂无角色\"\n />\n )}\n </div>\n </FormModal>\n );\n};\n\nexport default TemplateFormModal;\n"],"names":["TemplateFormModal","open","isCreate","initial","catalog","roleOptions","variables","catalogError","previewVars","canWrite","onCancel","onSubmit","renderAudienceField","roles","setRoles","useState","editorReady","setEditorReady","useEffect","id","editorKey","formData","useMemo","normalizeTemplatePlaceholders","varsEmptyHint","handleAfterOpenChange","useCallback","nextOpen","fieldList","Typography","fields","jsx","Input","Select","c","RichTextField","formProps","data","message","eventKey","hit","scenario","e","jsxs","FormModal","FormInfo","FormItem","api","titlePreview","applyTemplateVars","bodyPreview","stripHtmlToText","Fragment","styles","AntSelect","r","v"],"mappings":"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"}
1
+ {"version":3,"file":"TemplateFormModal.js","sources":["../../../../../../src/components/Tools/MessageCenter/MessageTemplateAdmin/Modal/TemplateFormModal.tsx"],"sourcesContent":["import { FormInfo, FormItem, FormModal, Input, Select } from '@/components/FormInfo';\nimport { RichTextField } from '@/components/FormInfo/RichTextField';\nimport { sanitizeInboxHtml } from '@/components/Tools/Inbox/sanitizeInboxHtml';\nimport { applyTemplateVars, normalizeTemplatePlaceholders } from '../../utils/templateCode';\nimport { Collapse, Modal, Typography, message } from 'antd';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport type {\n MessageAudienceRoleOption,\n MessageEventCatalogItem,\n MessageTemplateAdminItem,\n MessageTemplateVariable,\n} from '../types';\nimport {\n AUDIENCE_SLOT_OPTIONS,\n ORG_TENANT_SLOT,\n hasAudienceSelection,\n normalizeAudienceSlots,\n} from '../audienceSlots';\nimport styles from '../style.module.scss';\n\n/** ReactModal 内 Select 弹出层须高于 Modal(≈1100) */\nconst selectInModalPopupProps = {\n getPopupContainer: () => document.body,\n styles: { popup: { root: { zIndex: 2000 } } },\n} as const;\n\nexport type TemplateFormModalProps = {\n open: boolean;\n isCreate: boolean;\n initial: MessageTemplateAdminItem | null;\n catalog: MessageEventCatalogItem[];\n roleOptions: MessageAudienceRoleOption[];\n variables: MessageTemplateVariable[];\n /** catalog 请求失败时的短文案(与「变量为空」区分) */\n catalogError?: string;\n previewVars: Record<string, string>;\n canWrite: boolean;\n onCancel: () => void;\n onSubmit: (item: MessageTemplateAdminItem) => Promise<void>;\n renderAudienceField?: (ctx: {\n roles: string[];\n onChange: (roles: string[]) => void;\n }) => React.ReactNode;\n};\n\ntype FormShape = {\n code?: string;\n eventKey?: string;\n scenario?: string;\n titleTemplate?: string;\n bodyTemplate?: string;\n audienceRoles?: string[];\n audienceSlots?: string[];\n};\n\ntype FormApiLike = {\n formData?: FormShape;\n getFormData?: () => FormShape;\n};\n\n/**\n * 模板新建/编辑:FormModal + FormInfo;正文 RichTextField。\n * CKEditor 在 open 后微任务挂载,避免与 Modal focus/动画互抢;禁 mention uplift 死循环。\n */\nexport const TemplateFormModal: React.FC<TemplateFormModalProps> = ({\n open,\n isCreate,\n initial,\n catalog,\n roleOptions,\n variables,\n catalogError,\n previewVars,\n canWrite,\n onCancel,\n onSubmit,\n renderAudienceField,\n}) => {\n /** 仅业务 DI 自定义受众插槽时使用;默认走 FormInfo Select */\n const [roles, setRoles] = useState<string[]>([]);\n const [editorReady, setEditorReady] = useState(false);\n\n useEffect(() => {\n if (!open || !initial) {\n setEditorReady(false);\n return;\n }\n setRoles(initial.audienceRoles || initial.roles || []);\n const id = window.requestAnimationFrame(() => setEditorReady(true));\n return () => window.cancelAnimationFrame(id);\n }, [open, initial]);\n\n const editorKey = initial?.id || initial?.code || (isCreate ? 'create' : 'edit');\n\n const formData = useMemo<FormShape>(() => {\n if (!initial) return {};\n return {\n code: initial.code,\n eventKey: initial.eventKey,\n scenario: initial.scenario || initial.label || '',\n titleTemplate: normalizeTemplatePlaceholders(\n initial.titleTemplate || initial.titlePreview || '',\n ),\n bodyTemplate: normalizeTemplatePlaceholders(initial.bodyTemplate || ''),\n audienceRoles: initial.audienceRoles || initial.roles || [],\n audienceSlots: initial.audienceSlots || [],\n };\n }, [initial]);\n\n const varsEmptyHint = catalogError\n ? `目录加载失败,无法插入变量(${catalogError})`\n : variables.length === 0\n ? '暂无 catalog 变量'\n : '';\n\n const handleAfterOpenChange = useCallback((nextOpen: boolean) => {\n if (nextOpen) setEditorReady(true);\n else setEditorReady(false);\n }, []);\n\n const fieldList = useMemo(() => {\n if (!editorReady) {\n return [\n <Typography.Text key=\"loading\" type=\"secondary\">\n 加载编辑器…\n </Typography.Text>,\n ];\n }\n const fields: React.ReactNode[] = [];\n if (!isCreate) {\n fields.push(<Input key=\"code\" name=\"code\" label=\"编号\" disabled />);\n }\n fields.push(\n <Select\n key=\"eventKey\"\n name=\"eventKey\"\n label=\"适用场景\"\n rule=\"REQ\"\n disabled={!canWrite}\n optionLabelProp=\"label\"\n options={catalog.map((c) => ({\n value: c.eventKey,\n label: c.label || c.eventKey,\n }))}\n placeholder={catalog.length ? '选择场景' : '暂无事件目录'}\n {...selectInModalPopupProps}\n />,\n <Input\n key=\"titleTemplate\"\n name=\"titleTemplate\"\n label=\"标题模板\"\n rule=\"REQ\"\n disabled={!canWrite}\n enableVariableMention\n variables={variables}\n catalogError={catalogError}\n placeholder=\"点「插入变量」选择 catalog 变量\"\n labelTips={\n isCreate\n ? '新建默认停用。标题:点「插入变量」插入明文 {{key}}(可手改)。正文:原子色块,不可改字,整颗删除。'\n : '标题可手改 {{key}};正文为原子色块(不可改字、Backspace 整颗删)。'\n }\n />,\n <RichTextField\n key=\"bodyTemplate\"\n name=\"bodyTemplate\"\n label=\"正文模板\"\n disabled={!canWrite}\n enableVariableMention\n variables={variables}\n catalogError={catalogError}\n editorKey={editorKey}\n placeholder=\"编辑正文,点「插入变量」\"\n minHeight={160}\n />,\n );\n if (!renderAudienceField) {\n fields.push(\n <Select\n key=\"audienceRoles\"\n name=\"audienceRoles\"\n label=\"受众角色\"\n mode=\"multiple\"\n allowClear\n disabled={!canWrite}\n options={roleOptions.map((r) => ({\n value: r.code,\n label: r.name || r.code,\n }))}\n optionFilterProp=\"label\"\n maxTagCount=\"responsive\"\n placeholder={roleOptions.length ? '下拉选择角色(可多选)' : '暂无角色数据'}\n notFoundContent=\"暂无角色\"\n labelTips=\"可与「通知范围」并存;至少选角色或槽位之一。emit 与显式 userIds 并集送达。\"\n {...selectInModalPopupProps}\n />,\n <Select\n key=\"audienceSlots\"\n name=\"audienceSlots\"\n label=\"通知范围\"\n mode=\"multiple\"\n allowClear\n disabled={!canWrite}\n options={AUDIENCE_SLOT_OPTIONS.map((o) => ({\n value: o.value,\n label: o.label,\n }))}\n optionFilterProp=\"label\"\n maxTagCount=\"responsive\"\n placeholder=\"可选:本人/领导/部门/分厂/租户或任务当事人\"\n notFoundContent=\"暂无槽位\"\n labelTips=\"依赖业务 emit.audienceContext;无上下文的槽不会发出。选「本租户」保存前会确认。\"\n {...selectInModalPopupProps}\n />,\n );\n }\n return fields;\n }, [\n editorReady,\n isCreate,\n canWrite,\n catalog,\n variables,\n editorKey,\n catalogError,\n renderAudienceField,\n roleOptions,\n ]);\n\n const formProps = useMemo(\n () => ({\n data: formData,\n onSubmit: async (data: FormShape) => {\n if (!canWrite) {\n message.warning('当前为只读(业务未授予写权限)');\n return false;\n }\n const eventKey = String(data.eventKey || '').trim();\n if (!eventKey) {\n message.warning('请选择适用场景');\n return false;\n }\n const titleTemplate = normalizeTemplatePlaceholders(String(data.titleTemplate || ''));\n if (!titleTemplate.trim()) {\n message.warning('请填写标题模板');\n return false;\n }\n const audienceRoles = renderAudienceField\n ? roles\n : Array.isArray(data.audienceRoles)\n ? data.audienceRoles\n : [];\n const audienceSlots = normalizeAudienceSlots(data.audienceSlots || []);\n if (\n !hasAudienceSelection({\n roles: audienceRoles,\n slots: audienceSlots,\n userIds: null,\n })\n ) {\n message.warning('请选择受众角色或通知范围(至少一个)');\n return false;\n }\n if (audienceSlots.includes(ORG_TENANT_SLOT)) {\n const ok = await new Promise<boolean>((resolve) => {\n Modal.confirm({\n title: '确认通知本租户全员?',\n content: '已选「本租户(全公司)」:将向当前租户内用户展开(≠跨租户)。确认保存?',\n okText: '确认保存',\n cancelText: '取消',\n onOk: () => resolve(true),\n onCancel: () => resolve(false),\n });\n });\n if (!ok) return false;\n }\n const hit = catalog.find((c) => c.eventKey === eventKey);\n const scenario = String(hit?.label || data.scenario || eventKey).trim();\n try {\n await onSubmit({\n ...(initial || {}),\n code: data.code || initial?.code,\n eventKey,\n scenario,\n label: scenario,\n messageType: hit?.messageType || initial?.messageType || 'alert',\n titleTemplate,\n bodyTemplate: normalizeTemplatePlaceholders(String(data.bodyTemplate || '')),\n audienceRoles,\n roles: audienceRoles,\n audienceSlots,\n enabled: isCreate ? false : initial?.enabled !== false,\n channel: initial?.channel || 'in_app',\n });\n onCancel();\n } catch (e) {\n message.error(e instanceof Error ? e.message : String(e));\n return false;\n }\n },\n }),\n [\n formData,\n canWrite,\n catalog,\n onSubmit,\n initial,\n roles,\n isCreate,\n onCancel,\n renderAudienceField,\n ],\n );\n\n return (\n <FormModal\n title={isCreate ? '新建模板' : '编辑模板'}\n open={open}\n onCancel={onCancel}\n width={720}\n size=\"small\"\n className=\"msg-center-form-modal\"\n okText=\"保存\"\n autoClose={false}\n focusable={{ trap: false }}\n afterOpenChange={handleAfterOpenChange}\n formProps={formProps}\n >\n <FormInfo column={1} list={fieldList} />\n {renderAudienceField ? (\n <div className={styles.audienceBlock}>\n <Typography.Text className={styles.audienceLabel}>\n <span style={{ color: '#ff4d4f', marginRight: 4 }}>*</span>\n 受众角色\n </Typography.Text>\n {renderAudienceField({ roles, onChange: setRoles })}\n </div>\n ) : null}\n {editorReady ? (\n <FormItem>\n {(api: FormApiLike) => {\n const data =\n (typeof api.getFormData === 'function' ? api.getFormData() : undefined) ??\n api.formData ??\n formData;\n const titlePreview = applyTemplateVars(String(data.titleTemplate || ''), previewVars);\n const bodyRaw = applyTemplateVars(String(data.bodyTemplate || ''), previewVars);\n const bodyHtml = sanitizeInboxHtml(bodyRaw);\n return (\n <div className={styles.previewCollapse}>\n {varsEmptyHint ? (\n <Typography.Text type=\"secondary\">{varsEmptyHint}</Typography.Text>\n ) : null}\n <Collapse\n ghost\n size=\"small\"\n items={[\n {\n key: 'preview',\n label: '效果预览',\n children: (\n <div className={styles.previewCard}>\n <div className={styles.previewTitle}>\n {titlePreview.trim() || (\n <span className={styles.previewEmpty}>(无标题)</span>\n )}\n </div>\n {bodyHtml ? (\n <div\n className={styles.previewBody}\n dangerouslySetInnerHTML={{ __html: bodyHtml }}\n />\n ) : (\n <div className={`${styles.previewBody} ${styles.previewEmpty}`}>\n (无正文)\n </div>\n )}\n </div>\n ),\n },\n ]}\n />\n </div>\n );\n }}\n </FormItem>\n ) : null}\n </FormModal>\n );\n};\n\nexport default TemplateFormModal;\n"],"names":["selectInModalPopupProps","TemplateFormModal","open","isCreate","initial","catalog","roleOptions","variables","catalogError","previewVars","canWrite","onCancel","onSubmit","renderAudienceField","roles","setRoles","useState","editorReady","setEditorReady","useEffect","id","editorKey","formData","useMemo","normalizeTemplatePlaceholders","varsEmptyHint","handleAfterOpenChange","useCallback","nextOpen","fieldList","Typography","fields","jsx","Input","Select","c","RichTextField","r","AUDIENCE_SLOT_OPTIONS","o","formProps","data","message","eventKey","titleTemplate","audienceRoles","audienceSlots","normalizeAudienceSlots","hasAudienceSelection","ORG_TENANT_SLOT","resolve","Modal","hit","scenario","e","jsxs","FormModal","FormInfo","styles","FormItem","api","titlePreview","applyTemplateVars","bodyRaw","bodyHtml","sanitizeInboxHtml","Collapse"],"mappings":";;;;;;;;;;;;AAqBA,MAAMA,IAA0B;AAAA,EAC9B,mBAAmB,MAAM,SAAS;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,MAAK,EAAE;AAC5C,GAwCaC,KAAsD,CAAC;AAAA,EAClE,MAAAC;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,qBAAAC;AACF,MAAM;AAEJ,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAmB,CAAA,CAAE,GACzC,CAACC,GAAaC,CAAc,IAAIF,EAAS,EAAK;AAEpD,EAAAG,EAAU,MAAM;AACd,QAAI,CAACjB,KAAQ,CAACE,GAAS;AACrB,MAAAc,EAAe,EAAK;AACpB;AAAA,IACF;AACA,IAAAH,EAASX,EAAQ,iBAAiBA,EAAQ,SAAS,CAAA,CAAE;AACrD,UAAMgB,IAAK,OAAO,sBAAsB,MAAMF,EAAe,EAAI,CAAC;AAClE,WAAO,MAAM,OAAO,qBAAqBE,CAAE;AAAA,EAC7C,GAAG,CAAClB,GAAME,CAAO,CAAC;AAElB,QAAMiB,KAAYjB,KAAA,gBAAAA,EAAS,QAAMA,KAAA,gBAAAA,EAAS,UAASD,IAAW,WAAW,SAEnEmB,IAAWC,EAAmB,MAC7BnB,IACE;AAAA,IACL,MAAMA,EAAQ;AAAA,IACd,UAAUA,EAAQ;AAAA,IAClB,UAAUA,EAAQ,YAAYA,EAAQ,SAAS;AAAA,IAC/C,eAAeoB;AAAA,MACbpB,EAAQ,iBAAiBA,EAAQ,gBAAgB;AAAA,IAAA;AAAA,IAEnD,cAAcoB,EAA8BpB,EAAQ,gBAAgB,EAAE;AAAA,IACtE,eAAeA,EAAQ,iBAAiBA,EAAQ,SAAS,CAAA;AAAA,IACzD,eAAeA,EAAQ,iBAAiB,CAAA;AAAA,EAAC,IAVtB,CAAA,GAYpB,CAACA,CAAO,CAAC,GAENqB,IAAgBjB,IAClB,iBAAiBA,CAAY,MAC7BD,EAAU,WAAW,IACnB,kBACA,IAEAmB,IAAwBC,EAAY,CAACC,MAAsB;AAC/D,MAAI,EAAAA,CAA6B;AAAA,EAEnC,GAAG,CAAA,CAAE,GAECC,IAAYN,EAAQ,MAAM;AAC9B,QAAI,CAACN;AACH,aAAO;AAAA,0BACJa,EAAW,MAAX,EAA8B,MAAK,aAAY,sBAA3B,SAErB;AAAA,MAAA;AAGJ,UAAMC,IAA4B,CAAA;AAClC,WAAK5B,KACH4B,EAAO,KAAK,gBAAAC,EAACC,GAAA,EAAiB,MAAK,QAAO,OAAM,MAAK,UAAQ,GAAA,GAAtC,MAAuC,CAAE,GAElEF,EAAO;AAAA,MACL,gBAAAC;AAAA,QAACE;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAK;AAAA,UACL,UAAU,CAACxB;AAAA,UACX,iBAAgB;AAAA,UAChB,SAASL,EAAQ,IAAI,CAAC8B,OAAO;AAAA,YAC3B,OAAOA,EAAE;AAAA,YACT,OAAOA,EAAE,SAASA,EAAE;AAAA,UAAA,EACpB;AAAA,UACF,aAAa9B,EAAQ,SAAS,SAAS;AAAA,UACtC,GAAGL;AAAA,QAAA;AAAA,QAXA;AAAA,MAAA;AAAA,MAaN,gBAAAgC;AAAA,QAACC;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAK;AAAA,UACL,UAAU,CAACvB;AAAA,UACX,uBAAqB;AAAA,UACrB,WAAAH;AAAA,UACA,cAAAC;AAAA,UACA,aAAY;AAAA,UACZ,WACEL,IACI,0DACA;AAAA,QAAA;AAAA,QAZF;AAAA,MAAA;AAAA,MAeN,gBAAA6B;AAAA,QAACI;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAM;AAAA,UACN,UAAU,CAAC1B;AAAA,UACX,uBAAqB;AAAA,UACrB,WAAAH;AAAA,UACA,cAAAC;AAAA,UACA,WAAAa;AAAA,UACA,aAAY;AAAA,UACZ,WAAW;AAAA,QAAA;AAAA,QATP;AAAA,MAAA;AAAA,IAUN,GAEGR,KACHkB,EAAO;AAAA,MACL,gBAAAC;AAAA,QAACE;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAK;AAAA,UACL,YAAU;AAAA,UACV,UAAU,CAACxB;AAAA,UACX,SAASJ,EAAY,IAAI,CAAC+B,OAAO;AAAA,YAC/B,OAAOA,EAAE;AAAA,YACT,OAAOA,EAAE,QAAQA,EAAE;AAAA,UAAA,EACnB;AAAA,UACF,kBAAiB;AAAA,UACjB,aAAY;AAAA,UACZ,aAAa/B,EAAY,SAAS,gBAAgB;AAAA,UAClD,iBAAgB;AAAA,UAChB,WAAU;AAAA,UACT,GAAGN;AAAA,QAAA;AAAA,QAfA;AAAA,MAAA;AAAA,MAiBN,gBAAAgC;AAAA,QAACE;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAM;AAAA,UACN,MAAK;AAAA,UACL,YAAU;AAAA,UACV,UAAU,CAACxB;AAAA,UACX,SAAS4B,EAAsB,IAAI,CAACC,OAAO;AAAA,YACzC,OAAOA,EAAE;AAAA,YACT,OAAOA,EAAE;AAAA,UAAA,EACT;AAAA,UACF,kBAAiB;AAAA,UACjB,aAAY;AAAA,UACZ,aAAY;AAAA,UACZ,iBAAgB;AAAA,UAChB,WAAU;AAAA,UACT,GAAGvC;AAAA,QAAA;AAAA,QAfA;AAAA,MAAA;AAAA,IAgBN,GAGG+B;AAAA,EACT,GAAG;AAAA,IACDd;AAAA,IACAd;AAAA,IACAO;AAAA,IACAL;AAAA,IACAE;AAAA,IACAc;AAAA,IACAb;AAAA,IACAK;AAAA,IACAP;AAAA,EAAA,CACD,GAEKkC,IAAYjB;AAAA,IAChB,OAAO;AAAA,MACL,MAAMD;AAAA,MACN,UAAU,OAAOmB,MAAoB;AACnC,YAAI,CAAC/B;AACH,iBAAAgC,EAAQ,QAAQ,iBAAiB,GAC1B;AAET,cAAMC,IAAW,OAAOF,EAAK,YAAY,EAAE,EAAE,KAAA;AAC7C,YAAI,CAACE;AACH,iBAAAD,EAAQ,QAAQ,SAAS,GAClB;AAET,cAAME,IAAgBpB,EAA8B,OAAOiB,EAAK,iBAAiB,EAAE,CAAC;AACpF,YAAI,CAACG,EAAc;AACjB,iBAAAF,EAAQ,QAAQ,SAAS,GAClB;AAET,cAAMG,IAAgBhC,IAClBC,IACA,MAAM,QAAQ2B,EAAK,aAAa,IAC9BA,EAAK,gBACL,CAAA,GACAK,IAAgBC,EAAuBN,EAAK,iBAAiB,CAAA,CAAE;AACrE,YACE,CAACO,EAAqB;AAAA,UACpB,OAAOH;AAAA,UACP,OAAOC;AAAA,UACP,SAAS;AAAA,QAAA,CACV;AAED,iBAAAJ,EAAQ,QAAQ,oBAAoB,GAC7B;AAET,YAAII,EAAc,SAASG,CAAe,KAWpC,CAVO,MAAM,IAAI,QAAiB,CAACC,MAAY;AACjD,UAAAC,EAAM,QAAQ;AAAA,YACZ,OAAO;AAAA,YACP,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,YAAY;AAAA,YACZ,MAAM,MAAMD,EAAQ,EAAI;AAAA,YACxB,UAAU,MAAMA,EAAQ,EAAK;AAAA,UAAA,CAC9B;AAAA,QACH,CAAC;AACQ,iBAAO;AAElB,cAAME,IAAM/C,EAAQ,KAAK,CAAC,MAAM,EAAE,aAAasC,CAAQ,GACjDU,IAAW,QAAOD,KAAA,gBAAAA,EAAK,UAASX,EAAK,YAAYE,CAAQ,EAAE,KAAA;AACjE,YAAI;AACF,gBAAM/B,EAAS;AAAA,YACb,GAAIR,KAAW,CAAA;AAAA,YACf,MAAMqC,EAAK,SAAQrC,KAAA,gBAAAA,EAAS;AAAA,YAC5B,UAAAuC;AAAA,YACA,UAAAU;AAAA,YACA,OAAOA;AAAA,YACP,cAAaD,KAAA,gBAAAA,EAAK,iBAAehD,KAAA,gBAAAA,EAAS,gBAAe;AAAA,YACzD,eAAAwC;AAAA,YACA,cAAcpB,EAA8B,OAAOiB,EAAK,gBAAgB,EAAE,CAAC;AAAA,YAC3E,eAAAI;AAAA,YACA,OAAOA;AAAA,YACP,eAAAC;AAAA,YACA,SAAS3C,IAAW,MAAQC,KAAA,gBAAAA,EAAS,aAAY;AAAA,YACjD,UAASA,KAAA,gBAAAA,EAAS,YAAW;AAAA,UAAA,CAC9B,GACDO,EAAA;AAAA,QACF,SAAS2C,GAAG;AACV,iBAAAZ,EAAQ,MAAMY,aAAa,QAAQA,EAAE,UAAU,OAAOA,CAAC,CAAC,GACjD;AAAA,QACT;AAAA,MACF;AAAA,IAAA;AAAA,IAEF;AAAA,MACEhC;AAAA,MACAZ;AAAA,MACAL;AAAA,MACAO;AAAA,MACAR;AAAA,MACAU;AAAA,MACAX;AAAA,MACAQ;AAAA,MACAE;AAAA,IAAA;AAAA,EACF;AAGF,SACE,gBAAA0C;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,OAAOrD,IAAW,SAAS;AAAA,MAC3B,MAAAD;AAAA,MACA,UAAAS;AAAA,MACA,OAAO;AAAA,MACP,MAAK;AAAA,MACL,WAAU;AAAA,MACV,QAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW,EAAE,MAAM,GAAA;AAAA,MACnB,iBAAiBe;AAAA,MACjB,WAAAc;AAAA,MAEA,UAAA;AAAA,QAAA,gBAAAR,EAACyB,IAAA,EAAS,QAAQ,GAAG,MAAM5B,GAAW;AAAA,QACrChB,IACC,gBAAA0C,EAAC,OAAA,EAAI,WAAWG,EAAO,eACrB,UAAA;AAAA,UAAA,gBAAAH,EAACzB,EAAW,MAAX,EAAgB,WAAW4B,EAAO,eACjC,UAAA;AAAA,YAAA,gBAAA1B,EAAC,QAAA,EAAK,OAAO,EAAE,OAAO,WAAW,aAAa,EAAA,GAAK,UAAA,IAAA,CAAC;AAAA,YAAO;AAAA,UAAA,GAE7D;AAAA,UACCnB,EAAoB,EAAE,OAAAC,GAAO,UAAUC,GAAU;AAAA,QAAA,EAAA,CACpD,IACE;AAAA,QACHE,IACC,gBAAAe,EAAC2B,IAAA,EACE,UAAA,CAACC,MAAqB;AACrB,gBAAMnB,KACH,OAAOmB,EAAI,eAAgB,aAAaA,EAAI,gBAAgB,WAC7DA,EAAI,YACJtC,GACIuC,IAAeC,EAAkB,OAAOrB,EAAK,iBAAiB,EAAE,GAAGhC,CAAW,GAC9EsD,IAAUD,EAAkB,OAAOrB,EAAK,gBAAgB,EAAE,GAAGhC,CAAW,GACxEuD,IAAWC,EAAkBF,CAAO;AAC1C,iBACE,gBAAAR,EAAC,OAAA,EAAI,WAAWG,EAAO,iBACpB,UAAA;AAAA,YAAAjC,sBACEK,EAAW,MAAX,EAAgB,MAAK,aAAa,aAAc,IAC/C;AAAA,YACJ,gBAAAE;AAAA,cAACkC;AAAA,cAAA;AAAA,gBACC,OAAK;AAAA,gBACL,MAAK;AAAA,gBACL,OAAO;AAAA,kBACL;AAAA,oBACE,KAAK;AAAA,oBACL,OAAO;AAAA,oBACP,UACE,gBAAAX,EAAC,OAAA,EAAI,WAAWG,EAAO,aACrB,UAAA;AAAA,sBAAA,gBAAA1B,EAAC,OAAA,EAAI,WAAW0B,EAAO,cACpB,UAAAG,EAAa,KAAA,KACZ,gBAAA7B,EAAC,QAAA,EAAK,WAAW0B,EAAO,cAAc,mBAAK,GAE/C;AAAA,sBACCM,IACC,gBAAAhC;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,WAAW0B,EAAO;AAAA,0BAClB,yBAAyB,EAAE,QAAQM,EAAA;AAAA,wBAAS;AAAA,sBAAA,IAG9C,gBAAAhC,EAAC,OAAA,EAAI,WAAW,GAAG0B,EAAO,WAAW,IAAIA,EAAO,YAAY,IAAI,UAAA,QAAA,CAEhE;AAAA,oBAAA,EAAA,CAEJ;AAAA,kBAAA;AAAA,gBAEJ;AAAA,cACF;AAAA,YAAA;AAAA,UACF,GACF;AAAA,QAEJ,GACF,IACE;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGV;"}
@@ -1,10 +1,15 @@
1
1
  import { default as React } from 'react';
2
- import { MessageAudienceRoleOption, MessageCrudFlags, MessageEventCatalogItem, MessageTemplateAdminItem, PushRuleAdminItem } from '../types';
2
+ import { MessageAudienceRoleOption, MessageAudienceUserOption, MessageCrudFlags, MessageEventCatalogItem, MessageTemplateAdminItem, PushRuleAdminItem } from '../types';
3
3
  export type PushRulesPanelProps = {
4
4
  crud: MessageCrudFlags;
5
5
  catalog: MessageEventCatalogItem[];
6
6
  templates: MessageTemplateAdminItem[];
7
7
  roleOptions: MessageAudienceRoleOption[];
8
+ userOptions?: MessageAudienceUserOption[];
9
+ /** 角色 options 加载失败短文案(勿静默空列表) */
10
+ audienceRolesError?: string;
11
+ /** 例外抄送(静态 userIds)options 加载失败短文案 */
12
+ audienceUsersError?: string;
8
13
  fetchPushRules: () => Promise<PushRuleAdminItem[]>;
9
14
  savePushRule: (item: PushRuleAdminItem) => Promise<void>;
10
15
  setPushRuleEnabled?: (item: PushRuleAdminItem, enabled: boolean) => Promise<void>;
@@ -13,6 +18,8 @@ export type PushRulesPanelProps = {
13
18
  };
14
19
  /**
15
20
  * 推送规则 CRUD:必含 eventKey + templateCode;channels 默认 in_app。
21
+ * 受众:角色多选 + 通知范围(audienceSlots)+ 例外抄送;roles∪slots∪userIds 至少一个。
22
+ * 任务当事人/组织槽依赖 emit.audienceContext;禁 Tree 盖壳。
16
23
  */
17
24
  export declare const PushRulesPanel: React.FC<PushRulesPanelProps>;
18
25
  export default PushRulesPanel;