1
1
import { type JSONContentZod } from "tentix-server/types" ;
2
- import { Loader2Icon , UploadIcon , LibraryBigIcon , XIcon } from "lucide-react" ;
2
+ import { Loader2Icon , UploadIcon , LibraryBigIcon , XIcon , FileTextIcon } from "lucide-react" ;
3
3
import React , { useRef , useState , useCallback , useMemo } from "react" ;
4
4
import {
5
5
SendIcon ,
@@ -14,6 +14,8 @@ import useLocalUser from "@hook/use-local-user.tsx";
14
14
import { collectFavoritedKnowledge } from "@lib/query" ;
15
15
import { useTranslation } from "i18n" ;
16
16
import type { TFunction } from "i18next" ;
17
+ import { cn } from "../../../lib/utils" ;
18
+ import { ContextOrganizerDialog } from "../../../modal/use-context-organizer-modal" ;
17
19
18
20
// 错误处理工具函数
19
21
const getErrorMessage = ( error : unknown , t : TFunction ) : string => {
@@ -139,14 +141,18 @@ export function StaffMessageInput({
139
141
const [ uploadProgress , setUploadProgress ] = useState < UploadProgress | null > (
140
142
null ,
141
143
) ;
144
+ const [ isContextOrganizerOpen , setIsContextOrganizerOpen ] = useState ( false ) ;
142
145
143
146
const editorRef = useRef < EditorRef > ( null ) ;
144
147
const { toast } = useToast ( ) ;
145
148
const { kbSelectionMode, clearKbSelection, selectedMessageIds } =
146
149
useChatStore ( ) ;
147
150
const { id : userId } = useLocalUser ( ) ;
151
+
152
+
153
+ const authToken = window . localStorage . getItem ( "token" ) ;
154
+ const ticketId = window . location . pathname . split ( '/' ) . pop ( ) ;
148
155
149
- // 分析消息内容中的文件情况
150
156
const analyzeFileContent = useCallback (
151
157
( content : JSONContentZod ) : FileStats => {
152
158
let count = 0 ;
@@ -257,6 +263,12 @@ export function StaffMessageInput({
257
263
] ,
258
264
) ;
259
265
266
+ // 工单整理按钮点击
267
+ const handleContextOrganizerClick = useCallback ( ( ) => {
268
+ if ( isLoading || uploadProgress ) return ;
269
+ setIsContextOrganizerOpen ( true ) ;
270
+ } , [ isLoading , uploadProgress ] ) ;
271
+
260
272
const editorProps = useMemo (
261
273
( ) => ( {
262
274
handleKeyDown : ( _ : unknown , event : KeyboardEvent ) => {
@@ -336,6 +348,7 @@ export function StaffMessageInput({
336
348
} ;
337
349
338
350
const isUploading = uploadProgress !== null ;
351
+ const isContextOrganizerDisabled = isLoading || isUploading ;
339
352
340
353
if ( kbSelectionMode ) {
341
354
const count = selectedMessageIds . size ;
@@ -410,45 +423,75 @@ export function StaffMessageInput({
410
423
}
411
424
412
425
return (
413
- < div className = "border-t relative" >
414
- { /* 上传进度指示器 */ }
415
- { renderUploadProgress ( ) }
416
-
417
- { /* 主要内容区域 - 动态调整顶部间距 */ }
418
- < form
419
- onSubmit = { handleSubmit }
420
- style = { {
421
- marginTop : progressBarHeight ,
422
- transition : "margin-top 0.3s ease-in-out" ,
423
- } }
424
- >
425
- < div className = "flex" >
426
- < StaffChatEditor
427
- ref = { editorRef }
428
- value = { newMessage }
429
- onChange = { ( value ) => {
430
- onTyping ?.( ) ;
431
- setNewMessage ( value as JSONContentZod ) ;
432
- } }
433
- throttleDelay = { 150 }
434
- editorContentClassName = "overflow-auto h-full"
435
- editable = { ! isUploading }
436
- editorClassName = "focus:outline-none p-4 h-full"
437
- className = "border-none"
438
- editorProps = { editorProps }
439
- />
440
- </ div >
441
-
442
- < Button
443
- type = "submit"
444
- size = "icon"
445
- className = "absolute right-3 bottom-4 flex justify-center items-center rounded-[10px] bg-zinc-900 z-20 h-9 w-9"
446
- disabled = { ! canSend }
426
+ < >
427
+ < div className = "border-t relative" >
428
+ { /* 上传进度指示器 */ }
429
+ { renderUploadProgress ( ) }
430
+
431
+ { /* 主要内容区域 - 动态调整顶部间距 */ }
432
+ < form
433
+ onSubmit = { handleSubmit }
434
+ style = { {
435
+ marginTop : progressBarHeight ,
436
+ transition : "margin-top 0.3s ease-in-out" ,
437
+ } }
447
438
>
448
- { renderSendButtonContent ( ) }
449
- < span className = "sr-only" > { t ( "send_message_shortcut" ) } </ span >
450
- </ Button >
451
- </ form >
452
- </ div >
439
+ < div className = "flex" >
440
+ < StaffChatEditor
441
+ ref = { editorRef }
442
+ value = { newMessage }
443
+ onChange = { ( value ) => {
444
+ onTyping ?.( ) ;
445
+ setNewMessage ( value as JSONContentZod ) ;
446
+ } }
447
+ throttleDelay = { 150 }
448
+ editorContentClassName = "overflow-auto h-full"
449
+ editable = { ! isUploading }
450
+ editorClassName = "focus:outline-none p-4 h-full"
451
+ ticketId = { ticketId || undefined }
452
+ authToken = { authToken || undefined }
453
+ className = "border-none"
454
+ editorProps = { editorProps }
455
+ />
456
+ </ div >
457
+
458
+ { /* 工具栏区域 */ }
459
+ < div className = "absolute right-14 bottom-4 flex items-center gap-2 z-20" >
460
+ { ticketId && authToken && (
461
+ < Button
462
+ variant = "ghost"
463
+ size = "sm"
464
+ onClick = { handleContextOrganizerClick }
465
+ disabled = { isContextOrganizerDisabled }
466
+ className = { cn (
467
+ "h-9 w-9 p-0 hover:bg-gray-100"
468
+ ) }
469
+ title = { t ( 'organize_ticket_context' ) }
470
+ >
471
+ < FileTextIcon className = "h-4 w-4" />
472
+ </ Button >
473
+ ) }
474
+ </ div >
475
+
476
+ < Button
477
+ type = "submit"
478
+ size = "icon"
479
+ className = "absolute right-3 bottom-4 flex justify-center items-center rounded-[10px] bg-zinc-900 z-20 h-9 w-9"
480
+ disabled = { ! canSend }
481
+ >
482
+ { renderSendButtonContent ( ) }
483
+ < span className = "sr-only" > { t ( "send_message_shortcut" ) } </ span >
484
+ </ Button >
485
+ </ form >
486
+ </ div >
487
+
488
+ { /* 工单整理对话框 */ }
489
+ < ContextOrganizerDialog
490
+ open = { isContextOrganizerOpen }
491
+ onOpenChange = { setIsContextOrganizerOpen }
492
+ ticketId = { ticketId || "" }
493
+ authToken = { authToken || "" }
494
+ />
495
+ </ >
453
496
) ;
454
497
}
0 commit comments