@baishuyun/chat-sdk 0.0.9 → 0.0.10
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/CHANGELOG.md +8 -0
- package/dist/chat-sdk.js +13923 -13642
- package/dist/chat-sdk.js.map +1 -1
- package/dist/chat-sdk.umd.cjs +159 -159
- package/dist/chat-sdk.umd.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/package.json +4 -4
- package/src/chat.tsx +9 -0
- package/src/components/biz-comp/FakeBotMsg.tsx +6 -1
- package/src/components/biz-comp/chat-client.tsx +1 -0
- package/src/components/biz-comp/conversation.tsx +24 -45
- package/src/components/biz-comp/dash-widget-icon.tsx +17 -0
- package/src/components/biz-comp/field-icon.tsx +7 -2
- package/src/components/biz-comp/highlight-msg.tsx +3 -0
- package/src/components/biz-comp/messages.tsx +5 -4
- package/src/components/biz-comp/multi-modal-input/clear-btn.tsx +1 -0
- package/src/components/biz-comp/multi-modal-input/index.tsx +7 -3
- package/src/components/biz-comp/suggestions.tsx +5 -5
- package/src/components/bs-ui/border-color-animation.tsx +4 -1
- package/src/components/bs-ui/bs-icons.tsx +70 -0
- package/src/components/bs-ui/card.tsx +8 -10
- package/src/components/bs-ui/chat-area-header.tsx +5 -3
- package/src/components/bs-ui/collapsible-txt-msg.tsx +21 -6
- package/src/components/bs-ui/fields-generating-indicator.tsx +76 -31
- package/src/components/bs-ui/fields-previewer.tsx +4 -0
- package/src/components/bs-ui/form-info-editor.tsx +21 -7
- package/src/components/bs-ui/icon-btn.tsx +7 -2
- package/src/components/bs-ui/primary-confirm-btn.tsx +14 -7
- package/src/components/bs-ui/primary-entry-btn.tsx +14 -4
- package/src/components/bs-ui/scroll-to-bottom-btn.tsx +28 -0
- package/src/const/index.ts +26 -2
- package/src/hooks/use-frame-mode.ts +1 -0
- package/src/hooks/use-scroll-to-bottom.ts +127 -0
- package/src/plugins/mcp-form-builder-plugin/components/create-form-confirm.tsx +1 -1
- package/src/plugins/mcp-form-builder-plugin/index.ts +1 -0
- package/src/plugins/report-query-plugin/components/query-msg-part.tsx +30 -3
- package/src/plugins/report-query-plugin/components/result-cards/DataTableCard.tsx +12 -3
- package/src/plugins/report-query-plugin/components/result-cards/DataTableFields.tsx +1 -1
- package/src/plugins/report-query-plugin/components/result-cards/FilterCondition.tsx +11 -11
- package/src/plugins/report-query-plugin/index.ts +4 -0
- package/src/plugins/report-query-plugin/utils/field-enhance.ts +39 -0
- package/src/plugins/report-query-plugin/utils/get-field-group.ts +691 -0
- package/src/plugins/report-query-plugin/utils/get-field-icon.ts +26 -0
- package/src/plugins/report-query-plugin/utils/get-group-rule.ts +21 -0
- package/src/plugins/report-query-plugin/utils.tsx +40 -50
- package/src/stories/DashWidgetIcon.stories.tsx +132 -0
- package/src/stories/IconBtn.stories.tsx +14 -8
- package/src/stories/{PrimaryConfirmBtn.stories.ts → PrimaryConfirmBtn.stories.tsx} +16 -0
- package/src/stories/ScrollToBottom.stories.tsx +24 -0
- package/src/stories/SplitLine.stories.tsx +4 -4
- package/src/stories/fields-generating.stories.tsx +19 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils';
|
|
2
|
+
import { transCls } from '@/const/ui';
|
|
3
|
+
import { ScrollArrowDownIcon } from './bs-icons';
|
|
4
|
+
|
|
5
|
+
export interface ScrollToBottomProps {
|
|
6
|
+
onClick?: () => void;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ScrollToBottomBtn = ({ onClick, className }: ScrollToBottomProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<button
|
|
13
|
+
type="button"
|
|
14
|
+
onClick={onClick}
|
|
15
|
+
className={cn(
|
|
16
|
+
'inline-flex items-center justify-center',
|
|
17
|
+
'w-[36px] h-[36px] rounded-full',
|
|
18
|
+
'bg-white text-[#666] shadow-[0px_0px_14px_0px_rgba(0,0,0,0.1)]',
|
|
19
|
+
'hover:shadow-[0px_14px_14px_0px_rgba(0,0,0,0.14)]',
|
|
20
|
+
'cursor-pointer hover:text-[#333] aspect-square',
|
|
21
|
+
transCls,
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
>
|
|
25
|
+
<ScrollArrowDownIcon />
|
|
26
|
+
</button>
|
|
27
|
+
);
|
|
28
|
+
};
|
package/src/const/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldType } from '@baishuyun/types';
|
|
1
|
+
import { FieldType, IDashWidgetType } from '@baishuyun/types';
|
|
2
2
|
|
|
3
3
|
export const FieldTypeNameMap: Record<FieldType, string> = {
|
|
4
4
|
text: '输入框',
|
|
@@ -30,7 +30,7 @@ export const FieldTypeNameMap: Record<FieldType, string> = {
|
|
|
30
30
|
subform: '子表单',
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const FIELD_ICON_CODE_MAP: Record<FieldType, string> = {
|
|
33
|
+
export const FIELD_ICON_CODE_MAP: Record<FieldType & 'formula', string> = {
|
|
34
34
|
address: '',
|
|
35
35
|
subform: '',
|
|
36
36
|
password: '', // text
|
|
@@ -59,3 +59,27 @@ export const FIELD_ICON_CODE_MAP: Record<FieldType, string> = {
|
|
|
59
59
|
deptgroup: '',
|
|
60
60
|
unknown: '',
|
|
61
61
|
};
|
|
62
|
+
|
|
63
|
+
export const DASH_WIDGET_ICON_CODE_MAP: Record<IDashWidgetType, string> = {
|
|
64
|
+
metric_table: '', // 指标图
|
|
65
|
+
gauge_chart: '', // 仪表盘
|
|
66
|
+
column_chart: '', // 柱形图
|
|
67
|
+
funnel_chart: '', // 漏斗图
|
|
68
|
+
line_chart: '', // 折线图
|
|
69
|
+
pie_chart: '', // 饼图
|
|
70
|
+
area_chart: '', // 面积图
|
|
71
|
+
radar_chart: '', // 雷达图
|
|
72
|
+
bar_chart: '', // 条形图
|
|
73
|
+
gantt: '', // 甘特图
|
|
74
|
+
multi_axes_chart: '', // 双轴图
|
|
75
|
+
data_table: '', // 数据表
|
|
76
|
+
background_table: '', // 数据表(同 data_table)
|
|
77
|
+
pivot_table: '', // 透视图
|
|
78
|
+
map_chart: '', // 地图
|
|
79
|
+
newMap: '', // 地图
|
|
80
|
+
card: '', // 卡片
|
|
81
|
+
rili: '', // 日历(日期)
|
|
82
|
+
description: '', // 文本组件
|
|
83
|
+
image: '', // 图片组件
|
|
84
|
+
gauge_imageMore: '', // 仪表盘(同 gauge_chart)
|
|
85
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useScrollToBottom() {
|
|
4
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
5
|
+
const endRef = useRef<HTMLDivElement>(null);
|
|
6
|
+
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
7
|
+
const isAtBottomRef = useRef(true);
|
|
8
|
+
const isUserScrollingRef = useRef(false);
|
|
9
|
+
|
|
10
|
+
// Keep ref in sync with state
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
isAtBottomRef.current = isAtBottom;
|
|
13
|
+
}, [isAtBottom]);
|
|
14
|
+
|
|
15
|
+
const checkIfAtBottom = useCallback(() => {
|
|
16
|
+
if (!containerRef.current) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
|
20
|
+
return scrollTop + clientHeight >= scrollHeight - 100;
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
const scrollToBottom = useCallback((behavior: ScrollBehavior = 'smooth') => {
|
|
24
|
+
if (!containerRef.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
containerRef.current.scrollTo({
|
|
28
|
+
top: containerRef.current.scrollHeight,
|
|
29
|
+
behavior,
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
// Handle user scroll events
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const container = containerRef.current;
|
|
36
|
+
if (!container) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let scrollTimeout: ReturnType<typeof setTimeout>;
|
|
41
|
+
|
|
42
|
+
const handleScroll = () => {
|
|
43
|
+
// Mark as user scrolling
|
|
44
|
+
isUserScrollingRef.current = true;
|
|
45
|
+
clearTimeout(scrollTimeout);
|
|
46
|
+
|
|
47
|
+
// Update isAtBottom state
|
|
48
|
+
const atBottom = checkIfAtBottom();
|
|
49
|
+
setIsAtBottom(atBottom);
|
|
50
|
+
isAtBottomRef.current = atBottom;
|
|
51
|
+
|
|
52
|
+
// Reset user scrolling flag after scroll ends
|
|
53
|
+
scrollTimeout = setTimeout(() => {
|
|
54
|
+
isUserScrollingRef.current = false;
|
|
55
|
+
}, 150);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
container.addEventListener('scroll', handleScroll, { passive: true });
|
|
59
|
+
return () => {
|
|
60
|
+
container.removeEventListener('scroll', handleScroll);
|
|
61
|
+
clearTimeout(scrollTimeout);
|
|
62
|
+
};
|
|
63
|
+
}, [checkIfAtBottom]);
|
|
64
|
+
|
|
65
|
+
// Auto-scroll when content changes
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const container = containerRef.current;
|
|
68
|
+
if (!container) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const scrollIfNeeded = () => {
|
|
73
|
+
// Only auto-scroll if user was at bottom and isn't actively scrolling
|
|
74
|
+
if (isAtBottomRef.current && !isUserScrollingRef.current) {
|
|
75
|
+
requestAnimationFrame(() => {
|
|
76
|
+
container.scrollTo({
|
|
77
|
+
top: container.scrollHeight,
|
|
78
|
+
behavior: 'instant',
|
|
79
|
+
});
|
|
80
|
+
setIsAtBottom(true);
|
|
81
|
+
isAtBottomRef.current = true;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Watch for DOM changes
|
|
87
|
+
const mutationObserver = new MutationObserver(scrollIfNeeded);
|
|
88
|
+
mutationObserver.observe(container, {
|
|
89
|
+
childList: true,
|
|
90
|
+
subtree: true,
|
|
91
|
+
characterData: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Watch for size changes
|
|
95
|
+
const resizeObserver = new ResizeObserver(scrollIfNeeded);
|
|
96
|
+
resizeObserver.observe(container);
|
|
97
|
+
|
|
98
|
+
// Also observe children for size changes
|
|
99
|
+
for (const child of Array.from(container.children)) {
|
|
100
|
+
resizeObserver.observe(child);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return () => {
|
|
104
|
+
mutationObserver.disconnect();
|
|
105
|
+
resizeObserver.disconnect();
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
109
|
+
function onViewportEnter() {
|
|
110
|
+
setIsAtBottom(true);
|
|
111
|
+
isAtBottomRef.current = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function onViewportLeave() {
|
|
115
|
+
setIsAtBottom(false);
|
|
116
|
+
isAtBottomRef.current = false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
containerRef,
|
|
121
|
+
endRef,
|
|
122
|
+
isAtBottom,
|
|
123
|
+
scrollToBottom,
|
|
124
|
+
onViewportEnter,
|
|
125
|
+
onViewportLeave,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -7,18 +7,45 @@ import { usePluginCtx } from '@/hooks/use-plugin-ctx';
|
|
|
7
7
|
import { ReportQueryPlugin } from '@/index';
|
|
8
8
|
import { PLUGIN_NAME } from '../const';
|
|
9
9
|
import { IReportQueryContext } from '../types';
|
|
10
|
+
import { FakeBotMessage } from '@/components/biz-comp/FakeBotMsg';
|
|
11
|
+
import { HighlightMsg } from '@/components/biz-comp/highlight-msg';
|
|
12
|
+
import { CollapsibleTxtMsg } from '@/components/bs-ui/collapsible-txt-msg';
|
|
13
|
+
import { AggregateTableIcon } from '@/components/bs-ui/bs-icons';
|
|
10
14
|
|
|
11
15
|
export const QueryMsgPart = ({ part, ...rest }: MsgPartCompProps) => {
|
|
12
16
|
const textPart = part as TextUIPart;
|
|
13
17
|
const ctx = usePluginCtx<IReportQueryContext, ReportQueryPlugin>(PLUGIN_NAME);
|
|
14
18
|
|
|
19
|
+
if (!ctx) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
if (IsQueryResultPart(part)) {
|
|
16
24
|
const result = GetQueryResult(part);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
|
|
26
|
+
if (!result) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (result.isAggregate) {
|
|
31
|
+
return (
|
|
32
|
+
<FakeBotMessage headless className="w-full">
|
|
33
|
+
<HighlightMsg>已为你创建聚合表</HighlightMsg>
|
|
34
|
+
<CollapsibleTxtMsg
|
|
35
|
+
icon={
|
|
36
|
+
<span className="text-[#0265ff]">
|
|
37
|
+
<AggregateTableIcon />
|
|
38
|
+
</span>
|
|
39
|
+
}
|
|
40
|
+
title={result.title!}
|
|
41
|
+
disableExpand
|
|
42
|
+
/>
|
|
43
|
+
</FakeBotMessage>
|
|
44
|
+
);
|
|
20
45
|
}
|
|
21
46
|
|
|
47
|
+
ctx.queryResult = result;
|
|
48
|
+
ctx.queryResult.appID = ctx.appInfo?.appId || '';
|
|
22
49
|
return <DataTableCard {...(result || {})}></DataTableCard>;
|
|
23
50
|
}
|
|
24
51
|
|
|
@@ -10,6 +10,7 @@ import { ConditionField, DataSourceField, DimensionField, MetricField } from './
|
|
|
10
10
|
import { CardFieldsWrapper } from '@/components/bs-ui/card-field';
|
|
11
11
|
import { FilterCondition } from './FilterCondition';
|
|
12
12
|
import { SplitLine } from '@/components/bs-ui/split-line';
|
|
13
|
+
import { DashWidgetIcon } from '@/components/biz-comp/dash-widget-icon';
|
|
13
14
|
|
|
14
15
|
export const DataTableCard = (props: IQueryResult) => {
|
|
15
16
|
const ctx = usePluginCtx<IReportQueryContext, ReportQueryPlugin>(PLUGIN_NAME);
|
|
@@ -28,20 +29,28 @@ export const DataTableCard = (props: IQueryResult) => {
|
|
|
28
29
|
const hasMetrics = props.metrics && props.metrics.length > 0;
|
|
29
30
|
|
|
30
31
|
return (
|
|
31
|
-
<CollapsibleTxtMsg
|
|
32
|
+
<CollapsibleTxtMsg
|
|
33
|
+
title={props?.title || ''}
|
|
34
|
+
icon={<DashWidgetIcon type={props.type} />}
|
|
35
|
+
defaultOpen
|
|
36
|
+
>
|
|
32
37
|
<CardFieldsWrapper>
|
|
33
38
|
{name && <DataSourceField>{name}</DataSourceField>}
|
|
34
39
|
|
|
35
40
|
{props.fields?.length ? (
|
|
36
|
-
<DimensionField>{props.fields
|
|
41
|
+
<DimensionField>{props.fields?.map((field) => field.title).join('、')}</DimensionField>
|
|
37
42
|
) : null}
|
|
38
43
|
|
|
39
44
|
{props.xFields?.length ? (
|
|
40
45
|
<DimensionField isCol>
|
|
41
|
-
{props.xFields
|
|
46
|
+
{props.xFields?.map((field) => field.title).join('、')}
|
|
42
47
|
</DimensionField>
|
|
43
48
|
) : null}
|
|
44
49
|
|
|
50
|
+
{props.yFields?.length ? (
|
|
51
|
+
<DimensionField>{props.yFields.map((field) => field.title).join('、')}</DimensionField>
|
|
52
|
+
) : null}
|
|
53
|
+
|
|
45
54
|
{props.filter?.conds?.length ? (
|
|
46
55
|
<ConditionField>
|
|
47
56
|
<FilterCondition {...props.filter} />
|
|
@@ -35,7 +35,7 @@ export const MetricField = ({
|
|
|
35
35
|
return (
|
|
36
36
|
<div className={cn('flex items-center justify-between', className)}>
|
|
37
37
|
<div className="flex min-w-0 items-center gap-[6px]">
|
|
38
|
-
<span className="shrink-0 text-[#
|
|
38
|
+
<span className="shrink-0 text-[#6f829e]">
|
|
39
39
|
<FieldIcon type={metric.type} />
|
|
40
40
|
</span>
|
|
41
41
|
<span className="truncate text-sm leading-normal text-[#030303]">{metric.title}</span>
|
|
@@ -333,24 +333,24 @@ const filterToDesc = (field: IFilterField): ReactNode => {
|
|
|
333
333
|
// 为空/不为空 不需要显示值
|
|
334
334
|
if (method === 'empty' || method === 'not_empty') {
|
|
335
335
|
return (
|
|
336
|
-
<
|
|
336
|
+
<span className="leading-[normal]">
|
|
337
337
|
<span className="text-[#030303]">{title}</span>
|
|
338
|
-
<span className="text-[#4d609f]"> 
|
|
338
|
+
<span className="text-[#4d609f]"> </span>
|
|
339
339
|
{renderMethodLabel(methodLabel)}
|
|
340
|
-
</
|
|
340
|
+
</span>
|
|
341
341
|
);
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
return (
|
|
345
|
-
<
|
|
345
|
+
<span className="leading-[normal]">
|
|
346
346
|
<span className="text-[#030303]">{title}</span>
|
|
347
|
-
<span className="text-[#4d609f]"> 
|
|
347
|
+
<span className="text-[#4d609f]"> </span>
|
|
348
348
|
{prefix && <span className="text-[#666]">{prefix}</span>}
|
|
349
349
|
{renderMethodLabel(methodLabel)}
|
|
350
|
-
<span className="text-[#4d609f]"> 
|
|
350
|
+
<span className="text-[#4d609f]"> </span>
|
|
351
351
|
<span className="text-[#0265ff]">{valueStr}</span>
|
|
352
352
|
{suffix && <span className="text-[#666]"> {suffix}</span>}
|
|
353
|
-
</
|
|
353
|
+
</span>
|
|
354
354
|
);
|
|
355
355
|
};
|
|
356
356
|
|
|
@@ -362,13 +362,13 @@ export const FilterCondition = ({ conds, rel }: IQueryFilter): ReactNode => {
|
|
|
362
362
|
const relLabel = rel === 'or' ? '或' : '且';
|
|
363
363
|
|
|
364
364
|
return (
|
|
365
|
-
<
|
|
365
|
+
<span className="font-['Alibaba_PuHuiTi_2.0:55_Regular',sans-serif] text-[14px] text-[#030303] whitespace-pre-wrap">
|
|
366
366
|
{conds.map((field, index) => (
|
|
367
|
-
<
|
|
367
|
+
<span key={`${field.name}-${index}`} className="leading-[normal]">
|
|
368
368
|
{filterToDesc(field)}
|
|
369
369
|
{index < conds.length - 1 && <span className="text-[#999]"> {relLabel} </span>}
|
|
370
|
-
</
|
|
370
|
+
</span>
|
|
371
371
|
))}
|
|
372
|
-
</
|
|
372
|
+
</span>
|
|
373
373
|
);
|
|
374
374
|
};
|
|
@@ -68,6 +68,10 @@ export class ReportQueryPlugin extends ChatAreaPlugin<IReportQueryContext> {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
public setForms = (forms: Array<IDashForm>) => {
|
|
71
|
+
if (!forms || forms.length === 0) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
const formMap = new Map<string, IDashForm>();
|
|
72
76
|
forms.forEach((form) => {
|
|
73
77
|
formMap.set(form.entryId, form);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldType,
|
|
3
|
+
IDashWidgetType,
|
|
4
|
+
IQueryResultMetric,
|
|
5
|
+
IQueryResultXField,
|
|
6
|
+
} from '@baishuyun/types';
|
|
7
|
+
import { GetXFieldGroupStr } from './get-field-group';
|
|
8
|
+
import { getFieldIcon } from './get-field-icon';
|
|
9
|
+
import { getGroupRule } from './get-group-rule';
|
|
10
|
+
|
|
11
|
+
export const XFieldEnhance = (
|
|
12
|
+
field: IQueryResultXField,
|
|
13
|
+
widgetType: IDashWidgetType,
|
|
14
|
+
index: number
|
|
15
|
+
) => {
|
|
16
|
+
return {
|
|
17
|
+
...field,
|
|
18
|
+
id: index,
|
|
19
|
+
icon: getFieldIcon(field.type as FieldType),
|
|
20
|
+
group: GetXFieldGroupStr(widgetType, field),
|
|
21
|
+
groupRule: getGroupRule(field.type as FieldType, widgetType),
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const MetricEnhance = (
|
|
26
|
+
field: IQueryResultMetric,
|
|
27
|
+
widgetType: IDashWidgetType,
|
|
28
|
+
index: number
|
|
29
|
+
) => {
|
|
30
|
+
return {
|
|
31
|
+
...field,
|
|
32
|
+
id: index,
|
|
33
|
+
icon: getFieldIcon(field.type as FieldType),
|
|
34
|
+
group: GetXFieldGroupStr(widgetType, {
|
|
35
|
+
type: field.type,
|
|
36
|
+
name: field.name || field.title,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
};
|