Skip to content

Commit 0380131

Browse files
committed
feat: support chart group
1 parent 41b7e0e commit 0380131

File tree

23 files changed

+907
-30
lines changed

23 files changed

+907
-30
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
"highlight.js": "^10.5.0",
121121
"immer": "~10.1.1",
122122
"lodash-es": "^4.17.21",
123+
"moment": "^2.30.1",
123124
"rc-drawer": "~5.1.0",
124125
"rc-virtual-list": "^3.4.13",
125126
"react-draggable": "~4.4.6",

pnpm-lock.yaml

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chat/content/index.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ const Content = forwardRef<IContentRef, IContentProps>(function (
5151
};
5252

5353
const dataValid = !!(Array.isArray(data) && data.length);
54-
const lastPrompt = data[data.length - 1];
55-
const lastMessage = dataValid
56-
? lastPrompt.messages?.[lastPrompt.messages.length - 1]
57-
: undefined;
54+
const lastMessage = dataValid ? data.at(-1)?.messages?.at(-1) : undefined;
5855

5956
useLayoutEffect(() => {
6057
const handleScroll = () => {

src/chat/demos/basic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default function () {
3838
}, []);
3939

4040
return (
41-
<div style={{ width: '100%', height: 400, marginBottom: 56 }}>
41+
<div style={{ width: '100%', height: 400, display: 'flex', flexDirection: 'column' }}>
4242
<Chat
4343
chat={chat}
4444
codeBlock={{

src/chat/demos/global-state/group.tsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { LikeOutlined } from '@ant-design/icons';
3+
import { Button } from 'antd';
4+
import { Chat, Flex } from 'dt-react-component';
5+
import { cloneDeep } from 'lodash-es';
6+
7+
import { ConversationProperties } from '../../entity';
8+
import { mockSSE } from '../mockSSE';
9+
10+
export default function () {
11+
const chat = Chat.useChat();
12+
const [value, setValue] = useState<string | undefined>('');
13+
14+
const [convert, setConvert] = useState(false);
15+
16+
const [data, setData] = React.useState<ConversationProperties[]>([]);
17+
18+
const [selectId, setSelectId] = React.useState<string | undefined>('1');
19+
const [expand, setIsExpand] = React.useState(true);
20+
21+
const handleSelectChat = (conversation: ConversationProperties) => {
22+
setSelectId(conversation.id);
23+
};
24+
25+
const handleRename = (_conversation: ConversationProperties, _value: string) => {
26+
return Promise.resolve(true);
27+
};
28+
29+
const handleClearChat = (conversation: ConversationProperties) => {
30+
const list = cloneDeep(data).filter((i) => i.id !== conversation.id);
31+
if (conversation.id === chat.conversation.get()?.id) {
32+
chat.conversation.remove();
33+
if (list.length) {
34+
handleSelectChat(list[0]);
35+
chat.conversation.create({ ...list[0] });
36+
}
37+
}
38+
setData(list);
39+
};
40+
const handleNewChat = () => {
41+
chat.conversation.remove();
42+
chat.conversation.create({ id: new Date().valueOf().toString() });
43+
};
44+
45+
const addData = (str: string) => {
46+
setData((prev) => {
47+
const idx = prev.length + 1;
48+
return [
49+
...prev,
50+
{
51+
id: chat.conversation.get()!.id,
52+
title: str,
53+
assistantId: idx.toString(),
54+
createdAt: new Date().valueOf(),
55+
updatedAt: new Date().valueOf(),
56+
},
57+
];
58+
});
59+
handleSelectChat(chat.conversation.get()!);
60+
};
61+
62+
const handleSubmit = (raw = value) => {
63+
const val = raw?.trim();
64+
if (chat.loading() || !val) return;
65+
setValue('');
66+
const promptId = new Date().valueOf().toString();
67+
const messageId = (new Date().valueOf() + 1).toString();
68+
chat.prompt.create({ id: promptId, title: val });
69+
chat.message.create(promptId, { id: messageId, content: '' });
70+
mockSSE({
71+
message: val,
72+
onopen() {
73+
chat.start(promptId, messageId);
74+
addData(val);
75+
},
76+
onmessage(str) {
77+
chat.push(promptId, messageId, str);
78+
},
79+
onstop() {
80+
chat.close(promptId, messageId);
81+
},
82+
});
83+
};
84+
85+
useEffect(() => {
86+
chat.conversation.create({ id: new Date().valueOf().toString() });
87+
}, []);
88+
89+
return (
90+
<div style={{ width: '100%', height: 600, display: 'flex', flexDirection: 'column' }}>
91+
<Chat
92+
chat={chat}
93+
codeBlock={{
94+
convert,
95+
}}
96+
messageIcons={() => <LikeOutlined className="dtc__message__icon" />}
97+
components={{
98+
a: ({ children }) => (
99+
<Button
100+
type="primary"
101+
size="small"
102+
ghost
103+
onClick={() => setConvert((p) => !p)}
104+
>
105+
{children}
106+
</Button>
107+
),
108+
}}
109+
>
110+
<Chat.Group
111+
loading={false}
112+
expand={expand}
113+
fullscreen
114+
data={data}
115+
openFloat={false}
116+
listProps={{
117+
selectId,
118+
onRename: handleRename,
119+
onDelete: handleClearChat,
120+
onItemClick: handleSelectChat,
121+
}}
122+
dialogButtonProps={{
123+
onClick: handleNewChat,
124+
}}
125+
onExpandChange={setIsExpand}
126+
>
127+
<Chat.Content
128+
data={chat.conversation.get()?.prompts || []}
129+
placeholder={
130+
<div className="dtc__aigc__content__inner__holder">
131+
<Chat.Welcome
132+
title="dt-react-component"
133+
description="React UI component library based on antd package"
134+
/>
135+
<br />
136+
<Flex vertical align="start" gap="4px">
137+
<Chat.Tag onClick={() => handleSubmit('请告诉我一首诗')}>
138+
返回一首诗
139+
</Chat.Tag>
140+
<Chat.Tag onClick={() => handleSubmit('生成 CodeBlock')}>
141+
生成 CodeBlock
142+
</Chat.Tag>
143+
</Flex>
144+
</div>
145+
}
146+
/>
147+
<Chat.Input
148+
value={value}
149+
onChange={setValue}
150+
onPressEnter={() => handleSubmit()}
151+
onPressShiftEnter={() => setValue((v) => v + '\n')}
152+
button={{
153+
disabled: chat.loading() || !value?.trim(),
154+
}}
155+
placeholder="请输入想咨询的内容…"
156+
/>
157+
</Chat.Group>
158+
</Chat>
159+
</div>
160+
);
161+
}

src/chat/demos/global-state/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ function AI({ data, onSubmit }: { data?: Conversation; onSubmit?: (str?: string)
127127
const [value, setValue] = useState<string | undefined>('');
128128

129129
return (
130-
<div style={{ width: '100%', height: 400, marginBottom: 46 }}>
130+
<div style={{ width: '100%', height: 400, display: 'flex', flexDirection: 'column' }}>
131131
<Chat chat={chat}>
132132
<Chat.Content
133133
data={data?.prompts || []}

src/chat/demos/group.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from 'react';
2+
3+
import Button from '../button';
4+
import { ConversationProperties } from '../entity';
5+
import Group from '../group';
6+
7+
export default function (props: { children: React.ReactNode }) {
8+
console.log(props);
9+
const [data, setData] = React.useState<ConversationProperties[]>([]);
10+
11+
const [selectId, setSelectId] = React.useState<string | undefined>('1');
12+
const [expand, setIsExpand] = React.useState(true);
13+
14+
const handleSelectChat = (conversation: ConversationProperties) => {
15+
setSelectId(conversation.id);
16+
};
17+
18+
const handleRename = (_conversation: ConversationProperties, _value: string) => {
19+
return Promise.resolve(true);
20+
};
21+
22+
const handleClearChat = (conversation: ConversationProperties) => {
23+
console.log(conversation);
24+
};
25+
const handleNewChat = () => {
26+
setData((prev) => {
27+
const idx = prev.length + 1;
28+
return [
29+
...prev,
30+
{
31+
id: idx.toString(),
32+
title: `对话${idx}`,
33+
assistantId: idx.toString(),
34+
createdAt: new Date().valueOf(),
35+
updatedAt: new Date().valueOf(),
36+
},
37+
];
38+
});
39+
};
40+
41+
return (
42+
<div style={{ overflow: 'hidden', height: 600 }}>
43+
<Button type="default" onClick={() => setIsExpand((p) => !p)}>
44+
toggle
45+
</Button>
46+
<Group
47+
loading={false}
48+
expand={expand}
49+
fullscreen
50+
data={data}
51+
openFloat={false}
52+
listProps={{
53+
selectId,
54+
onRename: handleRename,
55+
onDelete: handleClearChat,
56+
onItemClick: handleSelectChat,
57+
}}
58+
dialogButtonProps={{
59+
onClick: handleNewChat,
60+
}}
61+
onExpandChange={setIsExpand}
62+
>
63+
{props.children}
64+
</Group>
65+
</div>
66+
);
67+
}

src/chat/entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type ConversationProperties = {
2929
id: string;
3030
assistantId?: string;
3131
createdAt?: Timestamp;
32+
updatedAt?: Timestamp;
3233
title?: string;
3334
prompts?: Prompt[];
3435
};
@@ -58,6 +59,7 @@ export abstract class Conversation {
5859
// 后端 Id
5960
assistantId?: string;
6061
createdAt: Timestamp;
62+
updatedAt: Timestamp;
6163
title?: string;
6264
prompts: Prompt[];
6365

@@ -67,6 +69,7 @@ export abstract class Conversation {
6769
this.id = props.id;
6870
this.assistantId = props.assistantId;
6971
this.createdAt = props.createdAt || new Date().valueOf();
72+
this.updatedAt = props.updatedAt || new Date().valueOf();
7073
this.title = props.title;
7174
this.prompts = props.prompts || [];
7275
}

src/chat/group/Item/index.scss

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.dtc-aigc-dialog-list {
2+
&-item {
3+
line-height: 32px;
4+
height: 32px;
5+
padding: 0 16px;
6+
border-radius: 4px;
7+
margin-top: 4px;
8+
color: #3D446E;
9+
display: flex;
10+
align-items: center;
11+
justify-content: space-between;
12+
cursor: pointer;
13+
&:hover {
14+
cursor: pointer;
15+
background-color: #EBECF0;
16+
.anticon-more {
17+
display: block;
18+
}
19+
}
20+
.anticon-more {
21+
display: none;
22+
}
23+
&.dtc-aigc-dialog-list-item__selected {
24+
background-color: #E8F1FF;
25+
color: #1D78FF;
26+
}
27+
&-input {
28+
height: 24px;
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)