@baishuyun/ai-design 2.1.0-alpha.4 → 2.1.0
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.
- package/.storybook/preview.ts +2 -0
- package/CHANGELOG.md +179 -28
- package/package.json +8 -9
- package/scripts/post-build.cjs +32 -0
- package/src/components/agent-drawer.tsx +119 -0
- package/src/components/agent-helper/agent-entry-card/index.tsx +3 -8
- package/src/components/agent-helper/agent-helper-card/index.tsx +3 -3
- package/src/components/agent-helper/app-select/index.tsx +119 -84
- package/src/components/agent-helper/condition-input/index.tsx +18 -21
- package/src/components/agent-helper/condition-item/index.tsx +1 -0
- package/src/components/agent-helper/field-edit-preference-select/index.tsx +31 -4
- package/src/components/agent-helper/field-value-input/dept-select.tsx +447 -0
- package/src/components/agent-helper/field-value-input/index.tsx +3 -2
- package/src/components/agent-helper/field-value-input/picker-inputs.tsx +20 -18
- package/src/components/agent-helper/form-select/index.tsx +12 -25
- package/src/components/agent-helper/relational-operator-select/index.tsx +37 -23
- package/src/components/agent-helper/table/index.tsx +6 -2
- package/src/components/agent-helper/team-wide-form-select/data-adapter.ts +109 -0
- package/src/components/agent-helper/team-wide-form-select/index.tsx +289 -0
- package/src/components/agent-helper/team-wide-form-select/tree-panel.tsx +180 -0
- package/src/components/agent-helper/team-wide-form-select/types.ts +36 -0
- package/src/components/agent-helper/team-wide-form-select/use-form-tree.ts +108 -0
- package/src/components/agent-helper/trigger-select/index.tsx +1 -1
- package/src/components/agent-select.tsx +115 -35
- package/src/components/bot-avatar-name.tsx +1 -1
- package/src/components/bs-entry-select/build-tree.ts +0 -6
- package/src/components/bs-entry-select/entry-item.tsx +7 -11
- package/src/components/bs-entry-select/other-app-section.tsx +2 -4
- package/src/components/bs-entry-select/scroll-slider.tsx +6 -11
- package/src/components/bs-icons.tsx +752 -206
- package/src/components/card.tsx +55 -10
- package/src/components/circle-checker.tsx +18 -3
- package/src/components/collapsible-txt-msg.tsx +18 -13
- package/src/components/fake-bot-msg.tsx +18 -7
- package/src/components/field-checker-list-msg.tsx +4 -3
- package/src/components/fields-design-info-table.tsx +9 -26
- package/src/components/fields-portal.tsx +2 -2
- package/src/components/fields-previewer.tsx +1 -0
- package/src/components/font-icon.tsx +20 -9
- package/src/components/form-info-editor.tsx +21 -6
- package/src/components/legacy-slot.tsx +11 -16
- package/src/components/memoized-markdown.tsx +32 -19
- package/src/components/message-content.tsx +7 -3
- package/src/components/output-config-editor.tsx +39 -25
- package/src/components/primary-confirm-btn.tsx +6 -1
- package/src/components/scroll-to-bottom-btn.tsx +3 -2
- package/src/components/shadow-content.tsx +19 -0
- package/src/components/shadow-portal.tsx +23 -0
- package/src/components/ui/dialog.tsx +4 -1
- package/src/components/ui/drawer.tsx +3 -1
- package/src/components/ui/enhanced-drawer.tsx +134 -0
- package/src/components/ui/popover.tsx +3 -2
- package/src/components/ui/select.tsx +3 -2
- package/src/components/ui/tooltip.tsx +3 -2
- package/src/components/user-txt-msg-bubble.tsx +41 -19
- package/src/components/warning-msg.tsx +29 -12
- package/src/const/shadow.ts +17 -0
- package/src/const/ui.ts +2 -2
- package/src/hooks/use-column-appear.ts +1 -1
- package/src/hooks/use-portal-container.ts +6 -3
- package/src/hooks/use-shadow-boundary.ts +67 -0
- package/src/hooks/use-shadow.ts +24 -7
- package/src/index.ts +21 -0
- package/src/stories/AppSelect.stories.tsx +44 -0
- package/src/stories/FieldValueInput.stories.tsx +6 -3
- package/src/stories/FormSelect.stories.tsx +40 -0
- package/src/stories/IconfontPicker.stories.tsx +106 -0
- package/src/stories/RelationalOperatorSelect.stories.tsx +27 -0
- package/src/stories/TeamWideFormSelect.stories.tsx +362 -0
- package/src/stories/UserTextMsgBubble.stories.ts +28 -0
- package/src/style.css +28 -1
- package/style.css +172 -130
|
@@ -6,6 +6,7 @@ import { SquareChecker } from '../../square-checker';
|
|
|
6
6
|
import type { IAppGroup, IAppGroupItem } from '@baishuyun/types';
|
|
7
7
|
import { AppIcon } from '../../font-icon';
|
|
8
8
|
import { SearchIcon } from '../../bs-icons';
|
|
9
|
+
import classNames from 'classnames';
|
|
9
10
|
|
|
10
11
|
// ── Tree Node Type ────────────────────────────────────────────────
|
|
11
12
|
|
|
@@ -41,7 +42,10 @@ export function groupsToAppTreeNodes(groups: IAppGroup[]): AppTreeNode[] {
|
|
|
41
42
|
/** Folder/group icon (16x16) */
|
|
42
43
|
const FolderIcon = () => (
|
|
43
44
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
|
44
|
-
<path
|
|
45
|
+
<path
|
|
46
|
+
d="M13.9004 15H2.65039C2.25257 15 1.87104 14.842 1.58973 14.5607C1.30843 14.2794 1.15039 13.8978 1.15039 13.5V4.5C1.15039 4.10218 1.30843 3.72064 1.58973 3.43934C1.87104 3.15804 2.25257 3 2.65039 3H7.15039L8.65039 4.5H13.9004C14.0974 4.5 14.2924 4.5388 14.4744 4.61418C14.6564 4.68956 14.8218 4.80005 14.9611 4.93934C15.1003 5.07863 15.2108 5.24399 15.2862 5.42597C15.3616 5.60796 15.4004 5.80302 15.4004 6H2.65039V13.5L4.25539 7.5H16.0754C16.1927 7.49595 16.3093 7.51947 16.4158 7.56867C16.5223 7.61788 16.6158 7.69139 16.6888 7.78331C16.7617 7.87523 16.8121 7.98298 16.8358 8.0979C16.8595 8.21282 16.856 8.33171 16.8254 8.445L15.3704 13.875C15.2865 14.1999 15.0961 14.4873 14.8296 14.6913C14.5631 14.8952 14.2359 15.0039 13.9004 15Z"
|
|
47
|
+
fill="#6F81BD"
|
|
48
|
+
/>
|
|
45
49
|
</svg>
|
|
46
50
|
);
|
|
47
51
|
|
|
@@ -70,11 +74,20 @@ const FolderIcon = () => (
|
|
|
70
74
|
/** Collapse/expand toggle (18x18) — shown on expandable parent nodes */
|
|
71
75
|
const ToggleIcon = ({ expanded }: { expanded: boolean }) => (
|
|
72
76
|
<svg
|
|
73
|
-
width="18"
|
|
77
|
+
width="18"
|
|
78
|
+
height="18"
|
|
79
|
+
viewBox="0 0 18 18"
|
|
80
|
+
fill="none"
|
|
74
81
|
className={cn('shrink-0', transCls)}
|
|
75
82
|
style={{ transform: expanded ? 'rotate(0deg)' : 'rotate(-90deg)' }}
|
|
76
83
|
>
|
|
77
|
-
<path
|
|
84
|
+
<path
|
|
85
|
+
d="M6 8L9 11L12 8"
|
|
86
|
+
stroke="#666666"
|
|
87
|
+
strokeWidth="1.2"
|
|
88
|
+
strokeLinecap="round"
|
|
89
|
+
strokeLinejoin="round"
|
|
90
|
+
/>
|
|
78
91
|
</svg>
|
|
79
92
|
);
|
|
80
93
|
|
|
@@ -97,11 +110,20 @@ const MoreIcon = () => (
|
|
|
97
110
|
/** Dropdown chevron in trigger — matches Figma trigger icon */
|
|
98
111
|
const DropdownChevron = ({ open }: { open: boolean }) => (
|
|
99
112
|
<svg
|
|
100
|
-
width="16"
|
|
113
|
+
width="16"
|
|
114
|
+
height="16"
|
|
115
|
+
viewBox="0 0 16 16"
|
|
116
|
+
fill="none"
|
|
101
117
|
className={cn('shrink-0', transCls)}
|
|
102
118
|
style={{ transform: open ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
|
103
119
|
>
|
|
104
|
-
<path
|
|
120
|
+
<path
|
|
121
|
+
d="M4 6L8 10L12 6"
|
|
122
|
+
stroke="#666666"
|
|
123
|
+
strokeWidth="1.2"
|
|
124
|
+
strokeLinecap="round"
|
|
125
|
+
strokeLinejoin="round"
|
|
126
|
+
/>
|
|
105
127
|
</svg>
|
|
106
128
|
);
|
|
107
129
|
|
|
@@ -130,6 +152,7 @@ export interface AppSelectProps {
|
|
|
130
152
|
maxVisibleChips?: number;
|
|
131
153
|
/** Additional className */
|
|
132
154
|
className?: string;
|
|
155
|
+
contentCls?: string;
|
|
133
156
|
/** Show a "全部应用" (All Apps) item at the top. Only effective in single mode. */
|
|
134
157
|
showAll?: boolean;
|
|
135
158
|
/** Allow selecting an empty value as the first option. Only effective in single mode. */
|
|
@@ -137,6 +160,8 @@ export interface AppSelectProps {
|
|
|
137
160
|
/** Label for the empty option. Default "无". */
|
|
138
161
|
emptyLabel?: string;
|
|
139
162
|
popoverContentOnly?: boolean; // for testing: render only the popover content without the trigger
|
|
163
|
+
|
|
164
|
+
disableMaxHeight?: boolean;
|
|
140
165
|
}
|
|
141
166
|
|
|
142
167
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
@@ -171,6 +196,8 @@ export const AppSelect = ({
|
|
|
171
196
|
disabled = false,
|
|
172
197
|
maxVisibleChips = 4,
|
|
173
198
|
className,
|
|
199
|
+
contentCls,
|
|
200
|
+
disableMaxHeight,
|
|
174
201
|
}: AppSelectProps) => {
|
|
175
202
|
const [open, setOpen] = useState(false);
|
|
176
203
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -181,6 +208,11 @@ export const AppSelect = ({
|
|
|
181
208
|
const [triggerWidth, setTriggerWidth] = useState<number>(0);
|
|
182
209
|
const triggerBtnRef = useRef<HTMLButtonElement | null>(null);
|
|
183
210
|
|
|
211
|
+
const maxTreeHeightCls = classNames({
|
|
212
|
+
'max-h-[400px]': !disableMaxHeight,
|
|
213
|
+
'max-h-full': disableMaxHeight,
|
|
214
|
+
});
|
|
215
|
+
|
|
184
216
|
// Expanded node IDs (only nodes with children can be expanded)
|
|
185
217
|
const [expandedIds, setExpandedIds] = useState<Set<string>>(() => {
|
|
186
218
|
// Start with root nodes expanded
|
|
@@ -213,7 +245,7 @@ export const AppSelect = ({
|
|
|
213
245
|
|
|
214
246
|
// Selected labels for trigger
|
|
215
247
|
const selectedLabels = useMemo(() => {
|
|
216
|
-
const ids = Array.isArray(resolvedValue) ? resolvedValue :
|
|
248
|
+
const ids = Array.isArray(resolvedValue) ? resolvedValue : resolvedValue ? [resolvedValue] : [];
|
|
217
249
|
return ids.map((id) => nodeMap.get(id)?.label ?? id);
|
|
218
250
|
}, [resolvedValue, nodeMap]);
|
|
219
251
|
|
|
@@ -236,7 +268,10 @@ export const AppSelect = ({
|
|
|
236
268
|
const labelMatch = node?.label?.toLowerCase().includes(q);
|
|
237
269
|
const filteredChildren = node.children ? filterTree(node.children) : [];
|
|
238
270
|
if (labelMatch || filteredChildren.length > 0) {
|
|
239
|
-
result.push({
|
|
271
|
+
result.push({
|
|
272
|
+
...node,
|
|
273
|
+
children: filteredChildren.length > 0 ? filteredChildren : node.children,
|
|
274
|
+
});
|
|
240
275
|
}
|
|
241
276
|
}
|
|
242
277
|
return result;
|
|
@@ -249,11 +284,17 @@ export const AppSelect = ({
|
|
|
249
284
|
const setTriggerRef = useCallback((node: HTMLButtonElement | null) => {
|
|
250
285
|
triggerBtnRef.current = node;
|
|
251
286
|
if (node) setTriggerWidth(node.offsetWidth);
|
|
252
|
-
if (!node) {
|
|
287
|
+
if (!node) {
|
|
288
|
+
setPortalContainer(null);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
253
291
|
const rootNode = node.getRootNode();
|
|
254
292
|
if (rootNode instanceof ShadowRoot) {
|
|
255
293
|
const existing = rootNode.querySelector<HTMLElement>('[data-bs-app-select-portal]');
|
|
256
|
-
if (existing) {
|
|
294
|
+
if (existing) {
|
|
295
|
+
setPortalContainer(existing);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
257
298
|
const container = document.createElement('div');
|
|
258
299
|
container.setAttribute('data-bs-app-select-portal', 'true');
|
|
259
300
|
rootNode.appendChild(container);
|
|
@@ -354,9 +395,7 @@ export const AppSelect = ({
|
|
|
354
395
|
'flex items-center gap-[6px] w-full h-[34px] rounded-[4px] text-left',
|
|
355
396
|
'pr-[10px]',
|
|
356
397
|
transCls,
|
|
357
|
-
isSelected
|
|
358
|
-
? 'bg-[#F0F6FF] text-[#0265FF]'
|
|
359
|
-
: 'text-[#030303] hover:bg-[#EFF0F6]'
|
|
398
|
+
isSelected ? 'bg-[#F0F6FF] text-[#0265FF]' : 'text-[#030303] hover:bg-[#EFF0F6]'
|
|
360
399
|
)}
|
|
361
400
|
style={{ paddingLeft: `${leftPad}px`, paddingTop: '10px', paddingBottom: '10px' }}
|
|
362
401
|
>
|
|
@@ -376,7 +415,7 @@ export const AppSelect = ({
|
|
|
376
415
|
|
|
377
416
|
{/* SquareChecker for multi-select on leaf nodes — always visible */}
|
|
378
417
|
{mode === 'multiple' && !hasChildren && node.type !== 'group' && (
|
|
379
|
-
<span onClick={(e) => e.stopPropagation()} className=
|
|
418
|
+
<span onClick={(e) => e.stopPropagation()} className="grid place-items-center">
|
|
380
419
|
<SquareChecker
|
|
381
420
|
checked={isSelected}
|
|
382
421
|
onCheckedChange={() => handleSelect(node.id, node)}
|
|
@@ -388,9 +427,7 @@ export const AppSelect = ({
|
|
|
388
427
|
|
|
389
428
|
{/* Children (when expanded) */}
|
|
390
429
|
{hasChildren && isExpanded && (
|
|
391
|
-
<div>
|
|
392
|
-
{node.children!.map((child) => renderNodeRow(child, depth + 1))}
|
|
393
|
-
</div>
|
|
430
|
+
<div>{node.children!.map((child) => renderNodeRow(child, depth + 1))}</div>
|
|
394
431
|
)}
|
|
395
432
|
</div>
|
|
396
433
|
);
|
|
@@ -403,70 +440,70 @@ export const AppSelect = ({
|
|
|
403
440
|
const hasMultiSelection = mode === 'multiple' && multiSelectedLabels.length > 0;
|
|
404
441
|
|
|
405
442
|
const popoverContent = (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
</div>
|
|
467
|
-
)}
|
|
468
|
-
</div>
|
|
443
|
+
<div className={classNames('flex min-h-0 flex-col gap-[4px]', contentCls)}>
|
|
444
|
+
{/* Search */}
|
|
445
|
+
<div className="flex shrink-0 items-center gap-[6px] border border-[#E0E0E0] rounded-[4px] h-[30px] px-[10px] py-[5px]">
|
|
446
|
+
<SearchIcon />
|
|
447
|
+
<input
|
|
448
|
+
type="text"
|
|
449
|
+
value={searchQuery}
|
|
450
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
451
|
+
placeholder={searchPlaceholder}
|
|
452
|
+
className="flex-1 outline-none text-[14px] placeholder:text-[#999999] bg-transparent min-w-0 text-[#030303]"
|
|
453
|
+
/>
|
|
454
|
+
</div>
|
|
455
|
+
|
|
456
|
+
{/* Empty option */}
|
|
457
|
+
{allowEmpty && mode === 'single' && !searchQuery.trim() && (
|
|
458
|
+
<button
|
|
459
|
+
type="button"
|
|
460
|
+
onClick={() => {
|
|
461
|
+
updateValue('');
|
|
462
|
+
setOpen(false);
|
|
463
|
+
}}
|
|
464
|
+
className={cn(
|
|
465
|
+
'flex shrink-0 items-center w-full h-[34px] rounded-[4px] text-left',
|
|
466
|
+
'px-[10px]',
|
|
467
|
+
transCls,
|
|
468
|
+
'text-[#030303] hover:bg-[#EFF0F6]'
|
|
469
|
+
)}
|
|
470
|
+
>
|
|
471
|
+
<span className="text-[14px] leading-[normal]">{emptyLabel}</span>
|
|
472
|
+
</button>
|
|
473
|
+
)}
|
|
474
|
+
|
|
475
|
+
{/* "全部应用" item */}
|
|
476
|
+
{showAll && mode === 'single' && !searchQuery.trim() && (
|
|
477
|
+
<button
|
|
478
|
+
type="button"
|
|
479
|
+
onClick={() => {
|
|
480
|
+
updateValue(ALL_APPS_VALUE);
|
|
481
|
+
setOpen(false);
|
|
482
|
+
}}
|
|
483
|
+
className={cn(
|
|
484
|
+
'flex shrink-0 items-center w-full h-[34px] rounded-[4px] text-left',
|
|
485
|
+
'px-[10px]',
|
|
486
|
+
transCls,
|
|
487
|
+
selectedIds.has(ALL_APPS_VALUE)
|
|
488
|
+
? 'bg-[#F0F6FF] text-[#0265FF]'
|
|
489
|
+
: 'text-[#030303] hover:bg-[#EFF0F6]'
|
|
490
|
+
)}
|
|
491
|
+
>
|
|
492
|
+
<span className="text-[14px] leading-[normal]">全部应用</span>
|
|
493
|
+
</button>
|
|
494
|
+
)}
|
|
495
|
+
|
|
496
|
+
{/* Tree */}
|
|
497
|
+
<div className={classNames('min-h-0 overflow-y-auto', maxTreeHeightCls)}>
|
|
498
|
+
{filteredNodes.length > 0 ? (
|
|
499
|
+
filteredNodes.map((node) => renderNodeRow(node, 0))
|
|
500
|
+
) : (
|
|
501
|
+
<div className="flex items-center justify-center h-[60px] text-[12px] text-[#999999]">
|
|
502
|
+
{searchQuery.trim() ? '未找到相关应用' : '暂无应用'}
|
|
469
503
|
</div>
|
|
504
|
+
)}
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
470
507
|
);
|
|
471
508
|
|
|
472
509
|
if (popoverContentOnly) {
|
|
@@ -511,9 +548,7 @@ export const AppSelect = ({
|
|
|
511
548
|
<span className="text-[14px] text-[#030303] truncate leading-[normal]">
|
|
512
549
|
{label}
|
|
513
550
|
</span>
|
|
514
|
-
<span
|
|
515
|
-
className="cursor-pointer flex items-center shrink-0 hover:bg-[#EFF0F6]"
|
|
516
|
-
>
|
|
551
|
+
<span className="cursor-pointer flex items-center shrink-0 hover:bg-[#EFF0F6]">
|
|
517
552
|
<CloseIcon />
|
|
518
553
|
</span>
|
|
519
554
|
</span>
|
|
@@ -548,8 +583,8 @@ export const AppSelect = ({
|
|
|
548
583
|
align="start"
|
|
549
584
|
sideOffset={4}
|
|
550
585
|
portalContainer={portalContainer}
|
|
551
|
-
className="!border-0 rounded-[10px] shadow-[0px_0px_5px_rgba(0,0,0,0.04)] p-[4px]"
|
|
552
|
-
style={{ minWidth: triggerWidth || undefined }}
|
|
586
|
+
className="flex flex-col overflow-hidden !border-0 rounded-[10px] shadow-[0px_0px_5px_rgba(0,0,0,0.04)] p-[4px]"
|
|
587
|
+
style={{ minWidth: triggerWidth || undefined, maxHeight: 300 }}
|
|
553
588
|
>
|
|
554
589
|
{popoverContent}
|
|
555
590
|
</PopoverContent>
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { useState, useCallback, useRef } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
IField,
|
|
4
|
+
ICondition,
|
|
5
|
+
IConditionItem,
|
|
6
|
+
quantifier,
|
|
7
|
+
IFieldValueService,
|
|
8
|
+
FieldType,
|
|
9
|
+
} from '@baishuyun/types';
|
|
3
10
|
import { cn } from '../../../lib/utils';
|
|
4
11
|
import { transCls } from '../../../const/ui';
|
|
5
12
|
import { ConditonTriggerDescription } from '../condition-trigger-description';
|
|
@@ -23,12 +30,7 @@ function createEmptyItem(): IConditionItem {
|
|
|
23
30
|
};
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
const FilterFieldTypeBlacklist: Array<FieldType> = [
|
|
27
|
-
'button',
|
|
28
|
-
'separator',
|
|
29
|
-
'subform',
|
|
30
|
-
'upload',
|
|
31
|
-
];
|
|
33
|
+
const FilterFieldTypeBlacklist: Array<FieldType> = ['button', 'separator', 'subform'];
|
|
32
34
|
|
|
33
35
|
export interface ConditionInputProps {
|
|
34
36
|
targetName: string;
|
|
@@ -56,11 +58,11 @@ export const ConditionInput = ({
|
|
|
56
58
|
targetName,
|
|
57
59
|
quantifier: 'all',
|
|
58
60
|
items: [],
|
|
59
|
-
}
|
|
61
|
+
}
|
|
60
62
|
).current;
|
|
61
63
|
|
|
62
64
|
const fields = allFields.filter(
|
|
63
|
-
(f) => !FilterFieldTypeBlacklist.includes(f.widget?.type as FieldType)
|
|
65
|
+
(f) => !FilterFieldTypeBlacklist.includes(f.widget?.type as FieldType)
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
const [internalValue, setInternalValue] = useState<ICondition>(defaultCondition);
|
|
@@ -72,14 +74,14 @@ export const ConditionInput = ({
|
|
|
72
74
|
if (!isControlled) setInternalValue(next);
|
|
73
75
|
onChange?.(next);
|
|
74
76
|
},
|
|
75
|
-
[isControlled, onChange]
|
|
77
|
+
[isControlled, onChange]
|
|
76
78
|
);
|
|
77
79
|
|
|
78
80
|
const handleQuantifierChange = useCallback(
|
|
79
81
|
(nextQuantifier: quantifier) => {
|
|
80
82
|
updateValue({ ...currentValue, quantifier: nextQuantifier });
|
|
81
83
|
},
|
|
82
|
-
[currentValue, updateValue]
|
|
84
|
+
[currentValue, updateValue]
|
|
83
85
|
);
|
|
84
86
|
|
|
85
87
|
const handleAddCondition = useCallback(() => {
|
|
@@ -111,7 +113,7 @@ export const ConditionInput = ({
|
|
|
111
113
|
'text-[14px] leading-[normal] text-[#0265FF]',
|
|
112
114
|
'hover:opacity-80',
|
|
113
115
|
disabled && 'opacity-50 cursor-not-allowed',
|
|
114
|
-
transCls
|
|
116
|
+
transCls
|
|
115
117
|
)}
|
|
116
118
|
>
|
|
117
119
|
<AddIcon />
|
|
@@ -127,7 +129,7 @@ export const ConditionInput = ({
|
|
|
127
129
|
'text-[14px] leading-[normal] text-[#FF5631]',
|
|
128
130
|
'hover:opacity-80',
|
|
129
131
|
disabled && 'opacity-50 cursor-not-allowed',
|
|
130
|
-
transCls
|
|
132
|
+
transCls
|
|
131
133
|
)}
|
|
132
134
|
>
|
|
133
135
|
删除全部
|
|
@@ -150,10 +152,7 @@ ConditionInput.displayName = 'ConditionInput';
|
|
|
150
152
|
|
|
151
153
|
// ── BsConditionInput (IBsCondition string ↔ ICondition adapter) ──────────────
|
|
152
154
|
|
|
153
|
-
function parseBsCondition(
|
|
154
|
-
str: string | undefined,
|
|
155
|
-
targetName: string,
|
|
156
|
-
): ICondition {
|
|
155
|
+
function parseBsCondition(str: string | undefined, targetName: string): ICondition {
|
|
157
156
|
if (str) {
|
|
158
157
|
try {
|
|
159
158
|
const parsed = bsConditionParse2Condition(str);
|
|
@@ -198,9 +197,7 @@ export const BsConditionInput = ({
|
|
|
198
197
|
disabled = false,
|
|
199
198
|
className,
|
|
200
199
|
}: BsConditionInputProps) => {
|
|
201
|
-
const defaultBS = useRef<string>(
|
|
202
|
-
defaultValue ?? defaultBsConditionString(targetName),
|
|
203
|
-
).current;
|
|
200
|
+
const defaultBS = useRef<string>(defaultValue ?? defaultBsConditionString(targetName)).current;
|
|
204
201
|
|
|
205
202
|
const [internalValue, setInternalValue] = useState<string>(defaultBS);
|
|
206
203
|
const isControlled = controlledValue !== undefined;
|
|
@@ -214,7 +211,7 @@ export const BsConditionInput = ({
|
|
|
214
211
|
if (!isControlled) setInternalValue(bsStr);
|
|
215
212
|
onChange?.(bsStr);
|
|
216
213
|
},
|
|
217
|
-
[isControlled, onChange, targetName]
|
|
214
|
+
[isControlled, onChange, targetName]
|
|
218
215
|
);
|
|
219
216
|
|
|
220
217
|
return (
|
|
@@ -3,7 +3,7 @@ import { cn } from '../../../lib/utils';
|
|
|
3
3
|
import { transCls } from '../../../const/ui';
|
|
4
4
|
import { Popover, PopoverTrigger, PopoverContent } from '../../ui/popover';
|
|
5
5
|
import { FieldSelectPopover } from '../field-select';
|
|
6
|
-
import type { IField } from '@baishuyun/types';
|
|
6
|
+
import type { IField, ISubformField } from '@baishuyun/types';
|
|
7
7
|
import type { FormActionFieldEditPreference } from '@baishuyun/types';
|
|
8
8
|
|
|
9
9
|
// ── Inline SVG Icons ──────────────────────────────────────────────
|
|
@@ -62,6 +62,29 @@ function FieldChip({ label, onRemove }: { label: string; onRemove: () => void })
|
|
|
62
62
|
|
|
63
63
|
// ── Types ─────────────────────────────────────────────────────────
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 解析选中的字段:
|
|
67
|
+
* 顶层字段 nodeKey 无 '.',原样返回;
|
|
68
|
+
* 子表单子字段 nodeKey 形如 parent.childWidgetName,从父字段 items 中取出
|
|
69
|
+
* 子字段,构造 label 为子字段名、widgetName 为完整路径的独立 IField,
|
|
70
|
+
* 使 FieldChip 显示子字段名而非「子表单」字段名。
|
|
71
|
+
*/
|
|
72
|
+
function resolveSelectedField(field: IField, nodeKey: string): IField {
|
|
73
|
+
const dotIndex = nodeKey.indexOf('.');
|
|
74
|
+
if (dotIndex < 0) return field;
|
|
75
|
+
const childWidgetName = nodeKey.slice(dotIndex + 1);
|
|
76
|
+
const item = field.widget?.items?.find(
|
|
77
|
+
(it): it is ISubformField =>
|
|
78
|
+
'widget' in it && it.widget?.widgetName === childWidgetName
|
|
79
|
+
);
|
|
80
|
+
if (!item) return field;
|
|
81
|
+
return {
|
|
82
|
+
...field,
|
|
83
|
+
label: item.label,
|
|
84
|
+
widget: { ...item.widget, widgetName: nodeKey },
|
|
85
|
+
} as IField;
|
|
86
|
+
}
|
|
87
|
+
|
|
65
88
|
export interface FieldEditPreferenceSelectProps {
|
|
66
89
|
fields: IField[];
|
|
67
90
|
value?: FormActionFieldEditPreference;
|
|
@@ -118,12 +141,16 @@ export const FieldEditPreferenceSelect = ({
|
|
|
118
141
|
);
|
|
119
142
|
|
|
120
143
|
const handleAddField = useCallback(
|
|
121
|
-
(field: IField,
|
|
122
|
-
|
|
144
|
+
(field: IField, nodeKey: string) => {
|
|
145
|
+
// 子表单子字段解析为独立字段(label=子字段名,widgetName=完整路径)
|
|
146
|
+
const resolved = resolveSelectedField(field, nodeKey);
|
|
147
|
+
// 以 widgetName(子字段为完整路径)去重,避免同名子字段误判
|
|
148
|
+
const resolvedKey = resolved.widget?.widgetName ?? resolved.label;
|
|
149
|
+
if (selectedFields.some((f) => (f.widget?.widgetName ?? f.label) === resolvedKey)) {
|
|
123
150
|
setOpen(false);
|
|
124
151
|
return;
|
|
125
152
|
}
|
|
126
|
-
const newFields = [...selectedFields,
|
|
153
|
+
const newFields = [...selectedFields, resolved];
|
|
127
154
|
setValue({ type: 'edit_any_specified_field', fields: newFields });
|
|
128
155
|
setOpen(false);
|
|
129
156
|
},
|