bi-sdk-react 0.0.4 → 0.0.6
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/dist/es/css/bi-sdk.css +1 -1
- package/dist/es/js/bi-sdk.es.js +296 -63
- package/dist/types/components/PageDesigner.d.ts +9 -1
- package/dist/types/components/context/DesignerContext.d.ts +5 -2
- package/dist/types/components/context/EnvContext.d.ts +2 -1
- package/dist/types/components/icon/IconFont.d.ts +2 -1
- package/dist/types/components/layout/PageCanvas.d.ts +2 -0
- package/dist/types/components/panel/AiPanel.d.ts +4 -0
- package/dist/types/components/panel/ChatInput.d.ts +13 -6
- package/dist/types/components/panel/DatasetPanel.d.ts +11 -0
- package/dist/types/components/panel/PaneHeader.d.ts +1 -0
- package/dist/types/components/panel/PropertiesPanel.d.ts +3 -1
- package/dist/types/components/plugins/@antd/item-props/EchartsProps.d.ts +2 -2
- package/dist/types/components/plugins/@antd/item-props/TextProps.d.ts +1 -0
- package/dist/types/components/plugins/@antd/items/TableRender.d.ts +1 -0
- package/dist/types/components/plugins/@antd/items/TextRender.d.ts +1 -0
- package/dist/types/components/typing.d.ts +102 -2
- package/dist/types/components/utils.d.ts +1 -0
- package/dist/umd/css/bi-sdk.css +1 -1
- package/dist/umd/js/bi-sdk.umd.min.js +300 -67
- package/package.json +3 -2
- package/src/components/PageDesigner.tsx +231 -37
- package/src/components/context/DesignerContext.tsx +15 -3
- package/src/components/context/EnvContext.tsx +4 -1
- package/src/components/icon/IconFont.tsx +15 -11
- package/src/components/layout/PageCanvas.tsx +4 -2
- package/src/components/layout/PageItem.tsx +1 -1
- package/src/components/panel/AiPanel.tsx +656 -43
- package/src/components/panel/ChatInput.tsx +259 -147
- package/src/components/panel/DatasetPanel.tsx +65 -0
- package/src/components/panel/PaneHeader.tsx +3 -2
- package/src/components/panel/PropertiesPanel.tsx +332 -125
- package/src/components/plugins/@antd/index.ts +12 -8
- package/src/components/plugins/@antd/item-props/EchartsProps.tsx +52 -22
- package/src/components/plugins/@antd/item-props/HtmlProps.tsx +8 -9
- package/src/components/plugins/@antd/item-props/TextProps.tsx +13 -1
- package/src/components/plugins/@antd/items/EchartsRender.tsx +9 -1
- package/src/components/plugins/@antd/items/HtmlRender.tsx +13 -1
- package/src/components/plugins/@antd/items/ListRender.tsx +18 -1
- package/src/components/plugins/@antd/items/TableRender.tsx +16 -1
- package/src/components/plugins/@antd/items/TextRender.tsx +3 -1
- package/src/components/styles.css +20 -0
- package/src/components/typing.ts +117 -2
- package/src/components/utils.ts +40 -0
- package/src/example.tsx +344 -13
package/src/example.tsx
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { CloseOutlined, EditOutlined, PlusOutlined } from "@ant-design/icons";
|
|
2
2
|
import { Button, Col, Divider, Drawer, Form, Input, Row, Table } from "antd";
|
|
3
|
-
import React, {
|
|
3
|
+
import React, {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useEffect,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from "react";
|
|
4
10
|
import styled from "styled-components";
|
|
5
11
|
import { PageDesigner } from "./components";
|
|
6
12
|
import { schema } from "./components/example";
|
|
7
13
|
import { plugins } from "./components/plugins/@antd";
|
|
14
|
+
import { DatasetSelectorFunction, FieldType } from "./components/typing";
|
|
15
|
+
import { uuid } from "./components/utils";
|
|
8
16
|
|
|
9
17
|
type Agent = { id: string | number; name: string };
|
|
10
18
|
|
|
@@ -32,7 +40,20 @@ const EditDrawer = styled(Drawer)`
|
|
|
32
40
|
export const Example: React.FC = () => {
|
|
33
41
|
const editDrawerRef = useRef<any>(null);
|
|
34
42
|
const [editVisible, setEditVisible] = useState(false);
|
|
35
|
-
const [agentList, setAgentList] = useState<Agent[]>([
|
|
43
|
+
const [agentList, setAgentList] = useState<Agent[]>([
|
|
44
|
+
{
|
|
45
|
+
id: 1,
|
|
46
|
+
name: "教师数据集",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 2,
|
|
50
|
+
name: "学生数据集",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 3,
|
|
54
|
+
name: "教学数据集",
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
36
57
|
const [params, setParams] = useState<{ name: string }>({ name: "" });
|
|
37
58
|
const columns = [
|
|
38
59
|
{ title: "名称", dataIndex: "name", key: "name" },
|
|
@@ -61,16 +82,6 @@ export const Example: React.FC = () => {
|
|
|
61
82
|
}, 0);
|
|
62
83
|
};
|
|
63
84
|
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
const initAgentList = async () => {
|
|
66
|
-
try {
|
|
67
|
-
setAgentList([]);
|
|
68
|
-
} catch {
|
|
69
|
-
setAgentList([]);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
initAgentList();
|
|
73
|
-
}, []);
|
|
74
85
|
return (
|
|
75
86
|
<div style={{ padding: 20 }}>
|
|
76
87
|
<Row justify="space-around">
|
|
@@ -117,20 +128,340 @@ export const Example: React.FC = () => {
|
|
|
117
128
|
}}
|
|
118
129
|
>
|
|
119
130
|
<PageDesigner
|
|
131
|
+
pageId="1"
|
|
120
132
|
ref={editDrawerRef}
|
|
121
133
|
agentList={agentList}
|
|
122
134
|
plugins={plugins}
|
|
123
135
|
headerExtra={
|
|
124
136
|
<>
|
|
125
|
-
<Divider
|
|
137
|
+
<Divider orientation="vertical" />
|
|
126
138
|
<a className="toolbar" onClick={() => setEditVisible(false)}>
|
|
127
139
|
<CloseOutlined />
|
|
128
140
|
</a>
|
|
129
141
|
</>
|
|
130
142
|
}
|
|
143
|
+
datasourceEnable={false}
|
|
144
|
+
datasetPanel={DatasetPanel}
|
|
145
|
+
datasetSelector={datasetSelector()}
|
|
146
|
+
fetch={{
|
|
147
|
+
ai: {
|
|
148
|
+
chat: async (bizType, bizId, conversationId, request) =>
|
|
149
|
+
new Promise((resolve) => {
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
resolve({ ...example.chatResponse, id: uuid() });
|
|
152
|
+
}, 5000);
|
|
153
|
+
}),
|
|
154
|
+
conversationList: async (bizType, bizId) =>
|
|
155
|
+
Promise.resolve(example.conversationList),
|
|
156
|
+
messageList: async (bizType, bizId, conversationId) =>
|
|
157
|
+
Promise.resolve(
|
|
158
|
+
example.messageList.filter(
|
|
159
|
+
(m) => m.conversationId === conversationId
|
|
160
|
+
)
|
|
161
|
+
),
|
|
162
|
+
removeConversation: async (bizType, bizId, conversationId) =>
|
|
163
|
+
Promise.resolve(true),
|
|
164
|
+
removeMessage: async (bizType, bizId, messageId) =>
|
|
165
|
+
Promise.resolve(true),
|
|
166
|
+
},
|
|
167
|
+
upload: async (file) =>
|
|
168
|
+
Promise.resolve({ id: uuid(), name: file.name }),
|
|
169
|
+
}}
|
|
131
170
|
/>
|
|
132
171
|
</EditDrawer>
|
|
133
172
|
)}
|
|
134
173
|
</div>
|
|
135
174
|
);
|
|
136
175
|
};
|
|
176
|
+
|
|
177
|
+
const DatasetPanel = forwardRef<{ handleAdd: () => void }, any>(({}, ref) => {
|
|
178
|
+
useImperativeHandle(ref, () => ({
|
|
179
|
+
handleAdd: () => {
|
|
180
|
+
console.log("handleAdd");
|
|
181
|
+
},
|
|
182
|
+
}));
|
|
183
|
+
return <>dataset panel</>;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
function datasetSelector(): DatasetSelectorFunction {
|
|
187
|
+
return (onSelect) => {
|
|
188
|
+
return (
|
|
189
|
+
<a
|
|
190
|
+
onClick={() =>
|
|
191
|
+
onSelect({
|
|
192
|
+
id: "1",
|
|
193
|
+
name: "数据集1",
|
|
194
|
+
output: {
|
|
195
|
+
fields: [
|
|
196
|
+
{ key: "name", name: "名称", type: FieldType.STRING },
|
|
197
|
+
{ key: "age", name: "年龄", type: FieldType.NUMBER },
|
|
198
|
+
{
|
|
199
|
+
key: "gender",
|
|
200
|
+
name: "性别",
|
|
201
|
+
type: FieldType.STRING,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: "project",
|
|
205
|
+
name: "项目",
|
|
206
|
+
type: FieldType.ARRAY,
|
|
207
|
+
schema: {
|
|
208
|
+
fields: [
|
|
209
|
+
{
|
|
210
|
+
key: "name",
|
|
211
|
+
name: "项目名称",
|
|
212
|
+
type: FieldType.STRING,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: "startDate",
|
|
216
|
+
name: "开始日期",
|
|
217
|
+
type: FieldType.STRING,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
key: "endDate",
|
|
221
|
+
name: "结束日期",
|
|
222
|
+
type: FieldType.STRING,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
key: "deptInfo",
|
|
229
|
+
name: "单位信息",
|
|
230
|
+
type: FieldType.OBJECT,
|
|
231
|
+
schema: {
|
|
232
|
+
fields: [
|
|
233
|
+
{
|
|
234
|
+
key: "name",
|
|
235
|
+
name: "单位名称",
|
|
236
|
+
type: FieldType.STRING,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
key: "code",
|
|
240
|
+
name: "单位编码",
|
|
241
|
+
type: FieldType.STRING,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
>
|
|
251
|
+
数据集1
|
|
252
|
+
</a>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const nowStr = new Date().toISOString();
|
|
258
|
+
const y = new Date();
|
|
259
|
+
y.setDate(y.getDate() - 1);
|
|
260
|
+
const yStr = y.toISOString();
|
|
261
|
+
const m = new Date();
|
|
262
|
+
m.setDate(3);
|
|
263
|
+
const mStr = m.toISOString();
|
|
264
|
+
const o = new Date();
|
|
265
|
+
o.setFullYear(o.getFullYear() - 1);
|
|
266
|
+
const oStr = o.toISOString();
|
|
267
|
+
const example = {
|
|
268
|
+
conversationList: [
|
|
269
|
+
{
|
|
270
|
+
id: "c1",
|
|
271
|
+
name: "学业表现概览(本学期)",
|
|
272
|
+
createdAt: nowStr,
|
|
273
|
+
isActived: true,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: "c2",
|
|
277
|
+
name: "出勤与纪律(本周)",
|
|
278
|
+
createdAt: yStr,
|
|
279
|
+
isActived: false,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "c3",
|
|
283
|
+
name: "课程满意度(本月)",
|
|
284
|
+
createdAt: mStr,
|
|
285
|
+
isActived: false,
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: "c4",
|
|
289
|
+
name: "教师教学概览(上学期)",
|
|
290
|
+
createdAt: oStr,
|
|
291
|
+
isActived: false,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
messageList: [
|
|
295
|
+
{
|
|
296
|
+
id: "m1",
|
|
297
|
+
conversationId: "c1",
|
|
298
|
+
question:
|
|
299
|
+
"制作学业表现看板:顶部选择学期与年级;左侧展示各科平均分与分数段;右侧显示班级排名TOP5与优秀率趋势。整体风格简洁,重点信息强化。",
|
|
300
|
+
answer: {
|
|
301
|
+
answer:
|
|
302
|
+
"已生成草稿:包含学期与年级筛选、科目均分柱状、分数段分布、班级TOP5榜单及优秀率折线,主色清爽,关键数字加粗。",
|
|
303
|
+
plans: [
|
|
304
|
+
{ name: "计划1", description: "计划1描述" },
|
|
305
|
+
{ name: "计划2", description: "计划2描述" },
|
|
306
|
+
],
|
|
307
|
+
extra: [
|
|
308
|
+
{ id: "e1", element: "科目均分", action: "新增", area: "左侧" },
|
|
309
|
+
{ id: "e2", element: "分数段分布", action: "新增", area: "左侧" },
|
|
310
|
+
{ id: "e3", element: "班级排名TOP5", action: "新增", area: "右侧" },
|
|
311
|
+
{ id: "e4", element: "优秀率趋势", action: "新增", area: "右侧" },
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
files: [
|
|
315
|
+
{ id: "f-excel-c1-1", name: "本学期成绩汇总.xlsx", extension: "xlsx" },
|
|
316
|
+
{ id: "f-audio-c1-1", name: "教学反馈录音.m4a", extension: "m4a" },
|
|
317
|
+
],
|
|
318
|
+
agents: [
|
|
319
|
+
{ id: "a-performance", name: "学业表现助理" },
|
|
320
|
+
{ id: "a-analytics", name: "数据分析助理" },
|
|
321
|
+
],
|
|
322
|
+
createdAt: nowStr,
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "m2",
|
|
326
|
+
conversationId: "c1",
|
|
327
|
+
question:
|
|
328
|
+
"补充信息区:在看板顶部显示学生总人数与及格率;底部增加异常波动提醒区,突出近一周变化。",
|
|
329
|
+
answer: {
|
|
330
|
+
answer:
|
|
331
|
+
"已更新草稿:加入人数与及格率标签,底部提醒区高亮近一周波动,便于快速关注。",
|
|
332
|
+
plans: [
|
|
333
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
334
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
335
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
336
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
337
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
338
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
339
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
340
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
341
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
342
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
343
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
344
|
+
{ name: "计划3计划3计划3计划3计划3计划3", description: "计划3描述" },
|
|
345
|
+
],
|
|
346
|
+
extra: [
|
|
347
|
+
{ id: "e5", element: "学生总人数标签", action: "新增", area: "顶部" },
|
|
348
|
+
{ id: "e6", element: "及格率标签", action: "新增", area: "顶部" },
|
|
349
|
+
{ id: "e7", element: "异常波动提醒区", action: "新增", area: "底部" },
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
files: [
|
|
353
|
+
{ id: "f-excel-c1-2", name: "年级学生清单.xlsx", extension: "xlsx" },
|
|
354
|
+
{ id: "f-audio-c1-2", name: "家长会语音纪要.mp3", extension: "mp3" },
|
|
355
|
+
],
|
|
356
|
+
agents: [
|
|
357
|
+
{ id: "a-performance", name: "学业表现助理" },
|
|
358
|
+
{ id: "a-alert", name: "预警提醒助理" },
|
|
359
|
+
],
|
|
360
|
+
createdAt: nowStr,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "m3",
|
|
364
|
+
conversationId: "c2",
|
|
365
|
+
question:
|
|
366
|
+
"制作出勤与纪律页:上方日期范围;中间左侧出勤率每日走势;右侧迟到与请假占比;下方列出异常班级与提醒。",
|
|
367
|
+
answer: {
|
|
368
|
+
answer:
|
|
369
|
+
"已生成草稿:曲线展示出勤率,环形占比呈现迟到与请假,卡片列出异常班级与提醒事项。",
|
|
370
|
+
plans: [
|
|
371
|
+
{ name: "计划4", description: "计划4描述" },
|
|
372
|
+
],
|
|
373
|
+
extra: [
|
|
374
|
+
{ id: "e8", element: "出勤率走势", action: "新增", area: "中间左侧" },
|
|
375
|
+
{
|
|
376
|
+
id: "e9",
|
|
377
|
+
element: "迟到与请假占比",
|
|
378
|
+
action: "新增",
|
|
379
|
+
area: "中间右侧",
|
|
380
|
+
},
|
|
381
|
+
{ id: "e10", element: "异常班级卡片", action: "新增", area: "底部" },
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
files: [
|
|
385
|
+
{ id: "f-excel-c2-1", name: "本周出勤记录.xlsx", extension: "xlsx" },
|
|
386
|
+
{ id: "f-audio-c2-1", name: "晨会纪律通报.wav", extension: "wav" },
|
|
387
|
+
],
|
|
388
|
+
agents: [
|
|
389
|
+
{ id: "a-attendance", name: "出勤助理" },
|
|
390
|
+
{ id: "a-discipline", name: "纪律管理助理" },
|
|
391
|
+
],
|
|
392
|
+
createdAt: yStr,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "m4",
|
|
396
|
+
conversationId: "c3",
|
|
397
|
+
question:
|
|
398
|
+
"制作课程满意度页:顶部选择课程与授课教师;中间评分分布与意见词语聚合;右侧列出改善建议清单。",
|
|
399
|
+
answer: {
|
|
400
|
+
answer:
|
|
401
|
+
"已生成草稿:评分分布清晰,热门意见词语聚合展示,建议清单分组呈现,便于教学改进。",
|
|
402
|
+
plans: [
|
|
403
|
+
{ name: "计划5", description: "计划5描述" },
|
|
404
|
+
],
|
|
405
|
+
extra: [
|
|
406
|
+
{ id: "e11", element: "评分分布", action: "新增", area: "中间" },
|
|
407
|
+
{ id: "e12", element: "意见词语聚合", action: "新增", area: "中间" },
|
|
408
|
+
{ id: "e13", element: "改善建议清单", action: "新增", area: "右侧" },
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
files: [
|
|
412
|
+
{ id: "f-excel-c3-1", name: "课程评价问卷.xlsx", extension: "xlsx" },
|
|
413
|
+
{ id: "f-audio-c3-1", name: "学生访谈片段.m4a", extension: "m4a" },
|
|
414
|
+
],
|
|
415
|
+
agents: [
|
|
416
|
+
{ id: "a-satisfaction", name: "满意度分析助理" },
|
|
417
|
+
{ id: "a-teacher", name: "教学改进助理" },
|
|
418
|
+
],
|
|
419
|
+
createdAt: mStr,
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
id: "m5",
|
|
423
|
+
conversationId: "c4",
|
|
424
|
+
question:
|
|
425
|
+
"制作教师教学概览:左侧本学期授课时长与班级覆盖;右侧课后作业批改及时率与反馈质量,并在顶部展示总览卡片。",
|
|
426
|
+
answer: {
|
|
427
|
+
answer:
|
|
428
|
+
"已生成草稿:时长条形展示、覆盖范围统计、及时率仪表与反馈质量等级卡片,顶部总览卡片汇总核心信息。",
|
|
429
|
+
plans: [
|
|
430
|
+
{ name: "计划6", description: "计划6描述" },
|
|
431
|
+
],
|
|
432
|
+
extra: [
|
|
433
|
+
{ id: "e14", element: "授课时长", action: "新增", area: "左侧" },
|
|
434
|
+
{ id: "e15", element: "班级覆盖", action: "新增", area: "左侧" },
|
|
435
|
+
{ id: "e16", element: "批改及时率", action: "新增", area: "右侧" },
|
|
436
|
+
{ id: "e17", element: "反馈质量", action: "新增", area: "右侧" },
|
|
437
|
+
{ id: "e18", element: "总览卡片", action: "新增", area: "顶部" },
|
|
438
|
+
],
|
|
439
|
+
},
|
|
440
|
+
files: [
|
|
441
|
+
{ id: "f-excel-c4-1", name: "教师授课统计.xlsx", extension: "xlsx" },
|
|
442
|
+
{ id: "f-audio-c4-1", name: "教研组会议录音.mp3", extension: "mp3" },
|
|
443
|
+
],
|
|
444
|
+
agents: [
|
|
445
|
+
{ id: "a-teacher", name: "教师教学助理" },
|
|
446
|
+
{ id: "a-analytics", name: "数据分析助理" },
|
|
447
|
+
],
|
|
448
|
+
createdAt: oStr,
|
|
449
|
+
},
|
|
450
|
+
],
|
|
451
|
+
chatResponse: {
|
|
452
|
+
id: "1",
|
|
453
|
+
answer: {
|
|
454
|
+
answer:
|
|
455
|
+
"已更新草稿:加入人数与及格率标签,底部提醒区高亮近一周波动,便于快速关注。",
|
|
456
|
+
plans: [
|
|
457
|
+
{ name: "计划3", description: "计划3描述" },
|
|
458
|
+
],
|
|
459
|
+
extra: [
|
|
460
|
+
{ id: "e1", element: "学生总人数标签", action: "新增", area: "顶部" },
|
|
461
|
+
{ id: "e2", element: "及格率标签", action: "新增", area: "顶部" },
|
|
462
|
+
{ id: "e3", element: "异常波动提醒区", action: "新增", area: "底部" },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
createdAt: nowStr,
|
|
466
|
+
},
|
|
467
|
+
};
|