@gientech/modual 1.2.8 → 1.2.9-fix
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/README.md +593 -79
- package/USAGE.md +56 -0
- package/dist/README.md +593 -79
- package/dist/assets/GientechStreamReader-C21-q_Qv.js +449 -0
- package/dist/assets/chevron-down-DjLtKwcs.js +280 -0
- package/dist/assets/databse.svg +6 -0
- package/dist/assets/graph.svg +4 -0
- package/dist/assets/homeBg.png +0 -0
- package/dist/assets/index-BMz4lcjQ.js +1 -0
- package/dist/assets/index-C3Viu8Oj.js +1 -0
- package/dist/assets/index-C9GlPyHu.js +13 -0
- package/dist/assets/index-CRbX3ZA1.js +1 -0
- package/dist/assets/index-CTwzi_v2.js +21 -0
- package/dist/assets/index-DQlLDleQ.js +11 -0
- package/dist/assets/index-DRU1P9R0.js +1150 -0
- package/dist/assets/index-Dqej68NT.js +585 -0
- package/dist/assets/index-ECprhahs.js +157 -0
- package/dist/assets/index-i7qcZOwY.js +1088 -0
- package/dist/assets/knowledge.svg +4 -0
- package/dist/assets/left.jpg +0 -0
- package/dist/assets/logoImg.png +0 -0
- package/dist/assets/{plus-omCUN0e3.js → plus-CvJRSbOe.js} +1 -1
- package/dist/assets/sensitive.svg +5 -0
- package/dist/assets/style.css +1 -1
- package/dist/assets/style3.css +1 -1
- package/dist/assets/worker-BbpylX7l.js +13 -0
- package/dist/assets/{x-vPcWt3fC.js → x-DKPeLdlu.js} +1 -1
- package/dist/assistantConfig.d.ts +31 -0
- package/dist/assistantConfig.js +1 -0
- package/dist/chat.d.ts +25 -1
- package/dist/chat.js +563 -369
- package/dist/database.js +2 -2
- package/dist/databaseId.js +1 -11
- package/dist/databaseTable.js +2 -2
- package/dist/index.d.ts +85 -0
- package/dist/index.js +1 -0
- package/dist/modelManage.js +1 -1
- package/dist/package.json +13 -1
- package/dist/sensitive.js +1 -1
- package/dist/streamFilesReader.d.ts +3 -0
- package/dist/streamFilesReader.js +1 -442
- package/doc_assets//346/226/271/346/241/210//344/274/230/345/214/226/346/226/271/346/241/210-/345/244/232/344/274/232/350/257/235SSE/350/277/236/346/216/245/347/256/241/347/220/206.md +504 -0
- package/package.json +125 -99
- package/package.json.demo-backup +109 -0
- package/scripts/README.md +133 -133
- package/scripts/build-demo.js +88 -88
- package/scripts/demo-selector.js +216 -216
- package/scripts/preview-demo.js +130 -130
- package/scripts/run-demo.bat +34 -34
- package/src/assets/img/close.png +0 -0
- package/src/assets/img/database.png +0 -0
- package/src/assets/img/downLoad.png +0 -0
- package/src/assets/img/graphIcon.png +0 -0
- package/src/assets/img/pdf.png +0 -0
- package/src/assets/img/singleQa.png +0 -0
- package/src/assets/img/webSearch.png +0 -0
- package/src/examples/ConversationAssistantPage/index.tsx +37 -0
- package/src/examples/Demo/index.tsx +12 -0
- package/src/examples/chat/components/DrawerGraphPreview.tsx +78 -0
- package/src/examples/chat/index.tsx +112 -99
- package/src/examples/chat/logo03.png +0 -0
- package/src/examples/gientechStreamFilesReader/index.tsx +4 -69
- package/src/lib_enter.ts +11 -6
- package/src/modules/assistantConfig/assets/databse.svg +6 -0
- package/src/modules/assistantConfig/assets/empty.png +0 -0
- package/src/modules/assistantConfig/assets/graph.svg +4 -0
- package/src/modules/assistantConfig/assets/knowledge.svg +4 -0
- package/src/modules/assistantConfig/assets/sensitive.svg +5 -0
- package/src/modules/assistantConfig/components/Database.tsx +144 -0
- package/src/modules/assistantConfig/components/Graph.tsx +156 -0
- package/src/modules/assistantConfig/components/Knowledge.tsx +266 -0
- package/src/modules/assistantConfig/components/NotFoundContent.tsx +21 -0
- package/src/modules/assistantConfig/components/Paragraph.tsx +51 -0
- package/src/modules/assistantConfig/components/ParamsItem.tsx +39 -0
- package/src/modules/assistantConfig/components/ResourceBinderItem.tsx +132 -0
- package/src/modules/assistantConfig/components/SearchableSelector.tsx +500 -0
- package/src/modules/assistantConfig/components/Sensitive.tsx +179 -0
- package/src/modules/assistantConfig/components/SliderInput.tsx +65 -0
- package/src/modules/assistantConfig/constants.tsx +74 -0
- package/src/modules/assistantConfig/index.tsx +700 -0
- package/src/modules/assistantConfig/server.ts +262 -0
- package/src/modules/chat/Conversations/List.tsx +76 -9
- package/src/modules/chat/Conversations/index.tsx +37 -19
- package/src/modules/chat/ReferenceBar.tsx +592 -0
- package/src/modules/chat/constants.tsx +29 -6
- package/src/modules/chat/data.txt +82 -0
- package/src/modules/chat/index.tsx +357 -113
- package/src/modules/chat/referenceCom/DeleteModal.tsx +75 -0
- package/src/modules/chat/referenceCom/DrawerContent.tsx +136 -0
- package/src/modules/chat/referenceCom/DrawerDatabase.tsx +110 -0
- package/src/modules/chat/referenceCom/DrawerGraphPreview.tsx +86 -0
- package/src/modules/chat/referenceCom/DrawerPreview.tsx +73 -0
- package/src/modules/chat/referenceCom/DrawerTitle.tsx +26 -0
- package/src/modules/chat/referenceCom/RenameModal.tsx +86 -0
- package/src/modules/chat/referenceCom/TagCom.tsx +30 -0
- package/src/modules/chat/style.less +3 -0
- package/src/modules/chat/utils/index.ts +326 -0
- package/src/modules/database/CreateModal.tsx +1 -1
- package/src/modules/headlessChat/index.tsx +1 -3
- package/src/modules/nodegraph/index.tsx +1 -0
- package/src/modules/search/components/ResultContent.tsx +2 -2
- package/src/modules/streamFilesReader/GientechStreamReader.tsx +436 -367
- package/src/modules/streamFilesReader/index.tsx +1 -1
- package/src/utils/gientechCommon/components/AppLoading.tsx +10 -10
- package/src/utils/gientechCommon/components/Messages/GientechNewChatWelcome.tsx +312 -27
- package/src/utils/gientechCommon/hooks/AichatUseController.tsx +84 -6
- package/src/utils/testconfigs/index.ts +7 -1
- package/stats.html +1 -1
- package/vite.config.ts +69 -20
- package/dist/assets/_commonjsHelpers-gnU0ypJ3.js +0 -1
- package/dist/assets/circle-alert-g2Y6zAjt.js +0 -6
- package/dist/assets/index-97TKgPKE.js +0 -1284
- package/dist/assets/index-CEK88UzR.js +0 -26
- package/dist/assets/index-DIm7RgkM.js +0 -1709
- package/dist/assets/styled-components.browser.esm-DPkS13KC.js +0 -2
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
interface DatabaseApiProps {
|
|
4
|
+
url?: string;
|
|
5
|
+
token?: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
eventsEmit?: (name: string, data: any) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const AxiosInstance = ({ url, token, role, eventsEmit }: DatabaseApiProps) => {
|
|
11
|
+
const axios$ = axios.create({
|
|
12
|
+
baseURL: url,
|
|
13
|
+
// 配置请求超时时间
|
|
14
|
+
timeout: 1000000,
|
|
15
|
+
});
|
|
16
|
+
// 请求拦截
|
|
17
|
+
axios$.interceptors.request.use((config: any) => {
|
|
18
|
+
let target_cookie = token;
|
|
19
|
+
|
|
20
|
+
const noA = ['user/login', 'user/kaptcha', 'user/regist'];
|
|
21
|
+
// dangerous behaviour
|
|
22
|
+
if (!target_cookie && !noA.some((item: string) => config.url.includes(item))) {
|
|
23
|
+
// 通过eventsEmit抛出认证错误事件,让调用层处理
|
|
24
|
+
if (eventsEmit) {
|
|
25
|
+
eventsEmit('request:error_auth', {
|
|
26
|
+
errorMsg: '未登录或token已过期',
|
|
27
|
+
redirect: '/login',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return Promise.reject(new Error('未登录或token已过期'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
config.headers.Authorization = target_cookie || '';
|
|
34
|
+
// 这里传中文会报错
|
|
35
|
+
config.headers.userName = role === '未登录用户' ? '' : role;
|
|
36
|
+
return config;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 返回拦截
|
|
40
|
+
axios$.interceptors.response.use(
|
|
41
|
+
response => {
|
|
42
|
+
// 获取接口返回结果
|
|
43
|
+
const noA = ['user/kaptcha'];
|
|
44
|
+
const res = response.data;
|
|
45
|
+
if (noA.some((item: string) => response.config.url && response.config.url.includes(item))) {
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
if (res.size && res.type) {
|
|
49
|
+
return res;
|
|
50
|
+
}
|
|
51
|
+
const { success, errorMsg, errorCode } = res;
|
|
52
|
+
|
|
53
|
+
if (success) {
|
|
54
|
+
return res;
|
|
55
|
+
} else {
|
|
56
|
+
// 通过eventsEmit抛出业务错误事件,让调用层处理
|
|
57
|
+
if (eventsEmit) {
|
|
58
|
+
eventsEmit('request:error_business', {
|
|
59
|
+
errorCode,
|
|
60
|
+
errorMsg: errorMsg || '请求出错,请稍后再试',
|
|
61
|
+
response: res,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
error => {
|
|
67
|
+
// 主动取消请求,不给报错信息
|
|
68
|
+
if (error.message === 'canceled') {
|
|
69
|
+
return Promise.reject(error);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 通过eventsEmit抛出网络错误事件,让调用层处理
|
|
73
|
+
if (eventsEmit) {
|
|
74
|
+
let errorType = 'request:error_network';
|
|
75
|
+
let errorData: any = {
|
|
76
|
+
errorMsg: '网络请求失败',
|
|
77
|
+
error: error,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (error?.response?.status === 401) {
|
|
81
|
+
errorType = 'request:error_auth';
|
|
82
|
+
errorData = {
|
|
83
|
+
errorMsg: '没有足够权限',
|
|
84
|
+
error: error,
|
|
85
|
+
redirect: '/login',
|
|
86
|
+
};
|
|
87
|
+
} else if (
|
|
88
|
+
error?.response?.data?.errorCode === '30013' ||
|
|
89
|
+
error?.response?.data?.errorCode === '30023'
|
|
90
|
+
) {
|
|
91
|
+
errorType = 'request:error_business';
|
|
92
|
+
errorData = {
|
|
93
|
+
errorCode: error?.response?.data?.errorCode,
|
|
94
|
+
errorMsg: error?.response?.data?.errorMsg || '请求出错,请稍后再试',
|
|
95
|
+
error: error,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
eventsEmit(errorType, errorData);
|
|
100
|
+
}
|
|
101
|
+
return Promise.reject(error);
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const getConfigById = async (id: any) => {
|
|
106
|
+
try {
|
|
107
|
+
const res: any = await axios$.get(`/qa/search/config/getById?configId=${id}`);
|
|
108
|
+
const { success, data } = res;
|
|
109
|
+
if (success) return data;
|
|
110
|
+
return false;
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error('getConfigById', e);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const fetchDatabases = async () => {
|
|
117
|
+
try {
|
|
118
|
+
const res: any = await axios$.get(`/index/database/queryAllDBList`);
|
|
119
|
+
const { success, data } = res;
|
|
120
|
+
if (success) return data;
|
|
121
|
+
return false;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error('fetchDatabases', e);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const fetchConfigItemLimit = async () => {
|
|
128
|
+
try {
|
|
129
|
+
const res: any = await axios$.get(`/qa/search/config/configItemLimit`);
|
|
130
|
+
const { success, data } = res;
|
|
131
|
+
if (success) return data;
|
|
132
|
+
return false;
|
|
133
|
+
} catch (e) {
|
|
134
|
+
console.error('fetchConfigItemLimit error', e);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const fetchSensitiveLibrary = async (params: any) => {
|
|
139
|
+
try {
|
|
140
|
+
const res: any = await axios$.get(
|
|
141
|
+
`/sensitive/library?${new URLSearchParams(params).toString()}`
|
|
142
|
+
);
|
|
143
|
+
const { success, data } = res;
|
|
144
|
+
if (success) return data;
|
|
145
|
+
return false;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
console.log('fetchSensitiveLibrary error', e);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const fetchModels = async (params: any) => {
|
|
153
|
+
try {
|
|
154
|
+
const res: any = await axios$.get(
|
|
155
|
+
`/index/modelInfo/allPage?${new URLSearchParams(params).toString()}`
|
|
156
|
+
);
|
|
157
|
+
const { success, data } = res;
|
|
158
|
+
if (success) return data;
|
|
159
|
+
return false;
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.log('fetchModels error', e);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const fetchKnowledgeBase = async (createBy?: string) => {
|
|
167
|
+
try {
|
|
168
|
+
const urlParams = createBy ? `?userName=${createBy}` : '';
|
|
169
|
+
console.log('+++', urlParams);
|
|
170
|
+
|
|
171
|
+
const res: any = await axios$.get(`/index/knowledgeBase/searchList${urlParams}`);
|
|
172
|
+
const { success, data } = res;
|
|
173
|
+
if (success) return data;
|
|
174
|
+
return false;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.log('e', error);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const fetchGraphs = async () => {
|
|
181
|
+
try {
|
|
182
|
+
const res: any = await axios$.get(
|
|
183
|
+
`/index/graph/platform/querySnapshotList?pageSize=999&pageNo=1`
|
|
184
|
+
);
|
|
185
|
+
const { success, data } = res;
|
|
186
|
+
if (success) return data;
|
|
187
|
+
return false;
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.error('fetchGraphs', e);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const addConversationAssistant = async (params: any) => {
|
|
194
|
+
try {
|
|
195
|
+
const res: any = await axios$.post(`/qa/search/config/add`, {
|
|
196
|
+
...params,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const { success, data } = res;
|
|
200
|
+
if (success) return data;
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.log('addConversationAssistant error:', e);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const updateConversationAssistant = async (id: string | number, params: any) => {
|
|
208
|
+
try {
|
|
209
|
+
const res: any = await axios$.put(`/qa/search/config/update`, {
|
|
210
|
+
id,
|
|
211
|
+
...params,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const { success, data } = res;
|
|
215
|
+
if (success) return data;
|
|
216
|
+
} catch (e) {
|
|
217
|
+
console.log('updateConversationAssistant error:', e);
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const getBizLabelLabels = async (params: any) => {
|
|
223
|
+
try {
|
|
224
|
+
const res: any = await axios$.post(`/index/bizLabel/labels`, params);
|
|
225
|
+
|
|
226
|
+
const { success, data } = res;
|
|
227
|
+
if (success) return data;
|
|
228
|
+
} catch (e) {
|
|
229
|
+
console.log('updateConversationAssistant error:', e);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const checkName = async (name: string, id?: string | number) => {
|
|
235
|
+
try {
|
|
236
|
+
const res: any = await axios$.get(
|
|
237
|
+
`/qa/search/config/checkName?configName=${name}${id ? `&configId=${id}` : ''}`
|
|
238
|
+
);
|
|
239
|
+
const { success, data } = res;
|
|
240
|
+
if (success) return data;
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.log('updateConversationAssistant error:', e);
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
getConfigById,
|
|
249
|
+
fetchDatabases,
|
|
250
|
+
fetchModels,
|
|
251
|
+
fetchSensitiveLibrary,
|
|
252
|
+
fetchConfigItemLimit,
|
|
253
|
+
fetchKnowledgeBase,
|
|
254
|
+
fetchGraphs,
|
|
255
|
+
checkName,
|
|
256
|
+
getBizLabelLabels,
|
|
257
|
+
addConversationAssistant,
|
|
258
|
+
updateConversationAssistant,
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export default AxiosInstance;
|
|
@@ -14,6 +14,8 @@ export interface GientechConversationBarListProps {
|
|
|
14
14
|
assistantList?: any[];
|
|
15
15
|
height?: number;
|
|
16
16
|
styles?: any;
|
|
17
|
+
hasMore?: boolean;
|
|
18
|
+
loadingMore?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function GientechConversationBarList(
|
|
@@ -29,6 +31,8 @@ export function GientechConversationBarList(
|
|
|
29
31
|
assistantList,
|
|
30
32
|
height = 400,
|
|
31
33
|
styles = {},
|
|
34
|
+
hasMore = false,
|
|
35
|
+
loadingMore = false,
|
|
32
36
|
} = props;
|
|
33
37
|
|
|
34
38
|
// 用于动画的 flipKey,包含 sessionId 和 gmtModified
|
|
@@ -57,15 +61,26 @@ export function GientechConversationBarList(
|
|
|
57
61
|
return arr;
|
|
58
62
|
}, [data, sortRules, flipKey]);
|
|
59
63
|
|
|
64
|
+
// 代表当前扁平顺序与类型的签名,用于监测重排
|
|
65
|
+
const layoutKey = useMemo(() => {
|
|
66
|
+
return flatList
|
|
67
|
+
.map(i => (i.type === 'group' ? `g:${i.label}` : `i:${i.data!.sessionId}`))
|
|
68
|
+
.join('|');
|
|
69
|
+
}, [flatList]);
|
|
70
|
+
|
|
60
71
|
const ITEM_HEIGHT = 56;
|
|
61
72
|
const ITEM_GAP = 14;
|
|
73
|
+
const FOOTER_HEIGHT = 36;
|
|
62
74
|
|
|
63
|
-
const getItemSize = (index: number) =>
|
|
64
|
-
|
|
75
|
+
const getItemSize = (index: number) => {
|
|
76
|
+
// 最后一项为 Footer
|
|
77
|
+
if (index === flatList.length) return FOOTER_HEIGHT;
|
|
78
|
+
return flatList[index].type === 'group' ? 32 : ITEM_HEIGHT + ITEM_GAP;
|
|
79
|
+
};
|
|
65
80
|
|
|
66
81
|
// 新增:List ref
|
|
67
82
|
const listRef = useRef<any>(null);
|
|
68
|
-
// 新增:当前激活会话在 flatList 的 index
|
|
83
|
+
// 新增:当前激活会话在 flatList 的 index(仅依赖 activedItem 与数据映射)
|
|
69
84
|
const activeIndex = useMemo(() => {
|
|
70
85
|
return flatList.findIndex(item => item.type === 'item' && item.data?.sessionId === activedItem);
|
|
71
86
|
}, [flatList, activedItem]);
|
|
@@ -73,19 +88,39 @@ export function GientechConversationBarList(
|
|
|
73
88
|
const firstItemIndex = useMemo(() => {
|
|
74
89
|
return flatList.findIndex(item => item.type === 'item');
|
|
75
90
|
}, [flatList]);
|
|
76
|
-
//
|
|
91
|
+
// 仅在激活项变化时,且激活项恰好是第一项时滚动到顶部;加载更多时不触发
|
|
92
|
+
const lastActivedRef = useRef<string | undefined>(undefined);
|
|
77
93
|
useEffect(() => {
|
|
78
|
-
|
|
94
|
+
const last = lastActivedRef.current;
|
|
95
|
+
if (activedItem !== last && activeIndex > -1 && activeIndex === firstItemIndex && listRef.current) {
|
|
79
96
|
listRef.current.scrollToItem(activeIndex, 'start');
|
|
80
97
|
}
|
|
81
|
-
|
|
98
|
+
lastActivedRef.current = activedItem;
|
|
99
|
+
}, [activedItem, activeIndex, firstItemIndex]);
|
|
100
|
+
|
|
101
|
+
// 轻量刷新缓存:仅在数据长度变化时刷新末尾,避免重置滚动位置
|
|
102
|
+
const prevCountRef = useRef<number>(0);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (listRef.current) {
|
|
105
|
+
const prev = prevCountRef.current;
|
|
106
|
+
const curr = flatList.length;
|
|
107
|
+
prevCountRef.current = curr;
|
|
108
|
+
if (curr !== prev) {
|
|
109
|
+
// 如果数据量变化很大(比如从少量恢复到大量,可能是搜索清空),强制重新计算所有尺寸
|
|
110
|
+
const changeRatio = prev > 0 ? Math.abs(curr - prev) / prev : 1;
|
|
111
|
+
const shouldForceReset = changeRatio > 0.5 || (prev < 10 && curr > 20); // 变化超过50%或从少量恢复到大量
|
|
112
|
+
listRef.current.resetAfterIndex(0, shouldForceReset);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}, [flatList.length]);
|
|
82
116
|
|
|
83
|
-
//
|
|
117
|
+
// 当分组/顺序发生变化时,重置尺寸缓存,避免位置叠加
|
|
84
118
|
useEffect(() => {
|
|
85
119
|
if (listRef.current) {
|
|
120
|
+
// 强制重新计算所有尺寸,避免重叠
|
|
86
121
|
listRef.current.resetAfterIndex(0, true);
|
|
87
122
|
}
|
|
88
|
-
}, [
|
|
123
|
+
}, [layoutKey]);
|
|
89
124
|
|
|
90
125
|
// hooks全部调用完后再判断是否为空
|
|
91
126
|
if (flatList.length === 0) {
|
|
@@ -101,15 +136,47 @@ export function GientechConversationBarList(
|
|
|
101
136
|
height={height}
|
|
102
137
|
width={'100%'}
|
|
103
138
|
|
|
104
|
-
itemCount={flatList.length}
|
|
139
|
+
itemCount={flatList.length + 1}
|
|
105
140
|
itemSize={getItemSize}
|
|
106
141
|
itemKey={index => {
|
|
142
|
+
if (index === flatList.length) return 'footer';
|
|
107
143
|
const item = flatList[index];
|
|
108
144
|
return item.type === 'item' ? item.data!.sessionId : `group-${item.label}`;
|
|
109
145
|
}}
|
|
146
|
+
onItemsRendered={({ visibleStopIndex }) => {
|
|
147
|
+
// 接近底部自动加载
|
|
148
|
+
const threshold = 3;
|
|
149
|
+
if (
|
|
150
|
+
hasMore &&
|
|
151
|
+
!loadingMore &&
|
|
152
|
+
visibleStopIndex >= flatList.length - 1 - threshold &&
|
|
153
|
+
typeof eventsEmit === 'function'
|
|
154
|
+
) {
|
|
155
|
+
eventsEmit('conversations:load_more');
|
|
156
|
+
}
|
|
157
|
+
}}
|
|
110
158
|
style={{ overflowX: 'hidden' }}
|
|
111
159
|
>
|
|
112
160
|
{({ index, style }) => {
|
|
161
|
+
// Footer 行
|
|
162
|
+
if (index === flatList.length) {
|
|
163
|
+
const colors = styles?.colors || {};
|
|
164
|
+
return (
|
|
165
|
+
<div
|
|
166
|
+
style={{
|
|
167
|
+
...style,
|
|
168
|
+
height: FOOTER_HEIGHT,
|
|
169
|
+
lineHeight: `${FOOTER_HEIGHT}px`,
|
|
170
|
+
textAlign: 'center',
|
|
171
|
+
fontSize: '10px',
|
|
172
|
+
color: colors.textMuted || '#9ca3af',
|
|
173
|
+
background: colors.groupBg || 'white',
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
{loadingMore ? '加载中...' : hasMore ? '' : '没有更多了'}
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
113
180
|
const item = flatList[index];
|
|
114
181
|
if (item.type === 'group') {
|
|
115
182
|
return (
|
|
@@ -15,6 +15,8 @@ export interface GientechConversationPanelProps {
|
|
|
15
15
|
eventsEmit?: (eventName: string, data?: any) => void;
|
|
16
16
|
assistantList?: any[];
|
|
17
17
|
styles?: any; // 新增,支持动态主题样式
|
|
18
|
+
hasMore?: boolean;
|
|
19
|
+
loadingMore?: boolean;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
// Header美化:分区更明显,搜索框更圆润,按钮更突出,底部分割线阴影
|
|
@@ -31,6 +33,20 @@ const Header: React.FC<{
|
|
|
31
33
|
const primary = colors.primary || '#2563eb';
|
|
32
34
|
const headerBg = themeStyles.headerBg || colors.sidebarBg || colors.background || '#fff';
|
|
33
35
|
const [searchOpen, setSearchOpen] = React.useState(false);
|
|
36
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
37
|
+
|
|
38
|
+
// 搜索展开时自动聚焦
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
if (searchOpen && inputRef.current) {
|
|
41
|
+
inputRef.current.focus();
|
|
42
|
+
}
|
|
43
|
+
}, [searchOpen]);
|
|
44
|
+
|
|
45
|
+
const handleCloseSearch = () => {
|
|
46
|
+
setSearchOpen(false);
|
|
47
|
+
setSearch(''); // 关闭时清空搜索内容
|
|
48
|
+
};
|
|
49
|
+
|
|
34
50
|
return (
|
|
35
51
|
<div
|
|
36
52
|
className="flex items-center gap-2 px-4 py-3 sticky top-0 z-10 min-w-0"
|
|
@@ -65,6 +81,7 @@ const Header: React.FC<{
|
|
|
65
81
|
overflow: 'hidden',
|
|
66
82
|
flexShrink: 0,
|
|
67
83
|
transition: 'width .22s ease, min-width .22s ease, padding .22s ease, opacity .18s ease',
|
|
84
|
+
pointerEvents: searchOpen ? 'none' : 'auto',
|
|
68
85
|
}}
|
|
69
86
|
>
|
|
70
87
|
<div className="flex items-center gap-2">
|
|
@@ -76,12 +93,11 @@ const Header: React.FC<{
|
|
|
76
93
|
{/* 搜索区:折叠图标/展开输入框 */}
|
|
77
94
|
{!searchOpen ? (
|
|
78
95
|
<button
|
|
79
|
-
className="ml-auto flex items-center justify-center transition-colors"
|
|
96
|
+
className="ml-auto flex items-center justify-center transition-colors hover:bg-gray-50"
|
|
80
97
|
style={{
|
|
81
98
|
width: 36,
|
|
82
99
|
height: 36,
|
|
83
100
|
borderRadius: '90px',
|
|
84
|
-
// background: colors.inputBg || '#f4f4f5',
|
|
85
101
|
border: `1px solid ${border}`,
|
|
86
102
|
color: primary || '#222',
|
|
87
103
|
flexShrink: 0,
|
|
@@ -92,42 +108,41 @@ const Header: React.FC<{
|
|
|
92
108
|
<Search size={18} />
|
|
93
109
|
</button>
|
|
94
110
|
) : (
|
|
95
|
-
<div className="flex-1 min-w-0
|
|
111
|
+
<div className="flex-1 min-w-0 relative">
|
|
96
112
|
<input
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
ref={inputRef}
|
|
114
|
+
className="w-full px-3 py-2 pr-10 text-sm transition-all"
|
|
115
|
+
placeholder="搜索历史记录"
|
|
99
116
|
value={value}
|
|
100
117
|
onChange={e => setSearch(e.target.value)}
|
|
101
|
-
onBlur={() => setSearchOpen(false)}
|
|
102
118
|
onKeyDown={e => {
|
|
103
|
-
if (e.key === '
|
|
104
|
-
|
|
119
|
+
if (e.key === 'Escape') {
|
|
120
|
+
handleCloseSearch();
|
|
105
121
|
}
|
|
106
122
|
}}
|
|
107
123
|
style={{
|
|
108
124
|
minWidth: 0,
|
|
109
125
|
width: '100%',
|
|
110
|
-
maxWidth: '100%',
|
|
111
126
|
borderRadius: radius,
|
|
112
127
|
background: colors.inputBg || '#f4f4f5',
|
|
113
128
|
border: `1px solid ${border}`,
|
|
114
129
|
color: colors.text || '#222',
|
|
115
130
|
outline: 'none',
|
|
131
|
+
paddingRight: 40, // 为关闭按钮留出空间
|
|
116
132
|
}}
|
|
117
133
|
/>
|
|
134
|
+
{/* 关闭按钮:在搜索栏内部最右侧尾部 */}
|
|
118
135
|
<button
|
|
119
|
-
className="flex items-center justify-center transition-colors"
|
|
136
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center justify-center transition-colors hover:bg-gray-200 rounded-full"
|
|
120
137
|
style={{
|
|
121
|
-
width:
|
|
122
|
-
height:
|
|
123
|
-
|
|
124
|
-
// background: colors.inputBg || '#f4f4f5',
|
|
125
|
-
border: `1px solid ${border}`,
|
|
126
|
-
color: colors.text || '#222',
|
|
138
|
+
width: 20,
|
|
139
|
+
height: 20,
|
|
140
|
+
color: colors.text || '#666',
|
|
127
141
|
flexShrink: 0,
|
|
128
142
|
}}
|
|
129
|
-
onClick={
|
|
130
|
-
title="
|
|
143
|
+
onClick={handleCloseSearch}
|
|
144
|
+
title="关闭搜索"
|
|
145
|
+
onMouseDown={e => e.preventDefault()} // 防止触发 input 的 blur
|
|
131
146
|
>
|
|
132
147
|
<X size={16} />
|
|
133
148
|
</button>
|
|
@@ -138,7 +153,7 @@ const Header: React.FC<{
|
|
|
138
153
|
};
|
|
139
154
|
|
|
140
155
|
export default function GientechConversationPanel(props: GientechConversationPanelProps) {
|
|
141
|
-
const { data, sortRules, activedItem,eventsEmit,assistantList, styles = {} } = props;
|
|
156
|
+
const { data, sortRules, activedItem,eventsEmit,assistantList, styles = {}, hasMore = false, loadingMore = false } = props;
|
|
142
157
|
const [search, setSearch] = useState('');
|
|
143
158
|
|
|
144
159
|
// 本地搜索过滤
|
|
@@ -198,6 +213,8 @@ export default function GientechConversationPanel(props: GientechConversationPan
|
|
|
198
213
|
sortRules={sortRules}
|
|
199
214
|
height={listHeight}
|
|
200
215
|
styles={styles}
|
|
216
|
+
hasMore={hasMore}
|
|
217
|
+
loadingMore={loadingMore}
|
|
201
218
|
/>
|
|
202
219
|
{/* 顶部阴影提示 */}
|
|
203
220
|
<div
|
|
@@ -207,6 +224,7 @@ export default function GientechConversationPanel(props: GientechConversationPan
|
|
|
207
224
|
}}
|
|
208
225
|
/>
|
|
209
226
|
</div>
|
|
227
|
+
{/* Footer 文案改由虚拟列表内部渲染,这里无需再显示 */}
|
|
210
228
|
</div>
|
|
211
229
|
);
|
|
212
230
|
}
|